feat(industrial): add IndustrialListing CRUD endpoints + Typesense indexing
Wire full DDD stack for IndustrialListing: domain entity, repository interface, CQRS commands/queries with handlers, Prisma repository, Typesense sync on create/update/delete, controller with 5 REST endpoints, and validated DTOs. Register all providers in IndustrialModule. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
import { type IndustrialLeaseType, type IndustrialListingStatus, type IndustrialPropertyType } from '@prisma/client';
|
||||
import { AggregateRoot } from '@modules/shared';
|
||||
|
||||
export interface IndustrialListingProps {
|
||||
parkId: string;
|
||||
agentId: string | null;
|
||||
sellerId: string;
|
||||
propertyType: IndustrialPropertyType;
|
||||
leaseType: IndustrialLeaseType;
|
||||
status: IndustrialListingStatus;
|
||||
title: string;
|
||||
description: string | null;
|
||||
areaM2: number;
|
||||
ceilingHeightM: number | null;
|
||||
floorLoadTonM2: number | null;
|
||||
columnSpacingM: number | null;
|
||||
dockCount: number | null;
|
||||
craneCapacityTon: number | null;
|
||||
hasMezzanine: boolean;
|
||||
hasOfficeArea: boolean;
|
||||
officeAreaM2: number | null;
|
||||
priceUsdM2: number | null;
|
||||
pricingUnit: string | null;
|
||||
totalLeasePrice: number | null;
|
||||
managementFee: number | null;
|
||||
depositMonths: number | null;
|
||||
minLeaseYears: number | null;
|
||||
maxLeaseYears: number | null;
|
||||
leaseExpiry: Date | null;
|
||||
availableFrom: Date | null;
|
||||
powerCapacityKva: number | null;
|
||||
waterSupplyM3Day: number | null;
|
||||
media: Record<string, unknown>[] | null;
|
||||
viewCount: number;
|
||||
inquiryCount: number;
|
||||
publishedAt: Date | null;
|
||||
}
|
||||
|
||||
export class IndustrialListingEntity extends AggregateRoot<string> {
|
||||
private _parkId: string;
|
||||
private _agentId: string | null;
|
||||
private _sellerId: string;
|
||||
private _propertyType: IndustrialPropertyType;
|
||||
private _leaseType: IndustrialLeaseType;
|
||||
private _status: IndustrialListingStatus;
|
||||
private _title: string;
|
||||
private _description: string | null;
|
||||
private _areaM2: number;
|
||||
private _ceilingHeightM: number | null;
|
||||
private _floorLoadTonM2: number | null;
|
||||
private _columnSpacingM: number | null;
|
||||
private _dockCount: number | null;
|
||||
private _craneCapacityTon: number | null;
|
||||
private _hasMezzanine: boolean;
|
||||
private _hasOfficeArea: boolean;
|
||||
private _officeAreaM2: number | null;
|
||||
private _priceUsdM2: number | null;
|
||||
private _pricingUnit: string | null;
|
||||
private _totalLeasePrice: number | null;
|
||||
private _managementFee: number | null;
|
||||
private _depositMonths: number | null;
|
||||
private _minLeaseYears: number | null;
|
||||
private _maxLeaseYears: number | null;
|
||||
private _leaseExpiry: Date | null;
|
||||
private _availableFrom: Date | null;
|
||||
private _powerCapacityKva: number | null;
|
||||
private _waterSupplyM3Day: number | null;
|
||||
private _media: Record<string, unknown>[] | null;
|
||||
private _viewCount: number;
|
||||
private _inquiryCount: number;
|
||||
private _publishedAt: Date | null;
|
||||
|
||||
constructor(id: string, props: IndustrialListingProps, createdAt: Date, updatedAt: Date) {
|
||||
super(id, createdAt, updatedAt);
|
||||
this._parkId = props.parkId;
|
||||
this._agentId = props.agentId;
|
||||
this._sellerId = props.sellerId;
|
||||
this._propertyType = props.propertyType;
|
||||
this._leaseType = props.leaseType;
|
||||
this._status = props.status;
|
||||
this._title = props.title;
|
||||
this._description = props.description;
|
||||
this._areaM2 = props.areaM2;
|
||||
this._ceilingHeightM = props.ceilingHeightM;
|
||||
this._floorLoadTonM2 = props.floorLoadTonM2;
|
||||
this._columnSpacingM = props.columnSpacingM;
|
||||
this._dockCount = props.dockCount;
|
||||
this._craneCapacityTon = props.craneCapacityTon;
|
||||
this._hasMezzanine = props.hasMezzanine;
|
||||
this._hasOfficeArea = props.hasOfficeArea;
|
||||
this._officeAreaM2 = props.officeAreaM2;
|
||||
this._priceUsdM2 = props.priceUsdM2;
|
||||
this._pricingUnit = props.pricingUnit;
|
||||
this._totalLeasePrice = props.totalLeasePrice;
|
||||
this._managementFee = props.managementFee;
|
||||
this._depositMonths = props.depositMonths;
|
||||
this._minLeaseYears = props.minLeaseYears;
|
||||
this._maxLeaseYears = props.maxLeaseYears;
|
||||
this._leaseExpiry = props.leaseExpiry;
|
||||
this._availableFrom = props.availableFrom;
|
||||
this._powerCapacityKva = props.powerCapacityKva;
|
||||
this._waterSupplyM3Day = props.waterSupplyM3Day;
|
||||
this._media = props.media;
|
||||
this._viewCount = props.viewCount;
|
||||
this._inquiryCount = props.inquiryCount;
|
||||
this._publishedAt = props.publishedAt;
|
||||
}
|
||||
|
||||
get parkId() { return this._parkId; }
|
||||
get agentId() { return this._agentId; }
|
||||
get sellerId() { return this._sellerId; }
|
||||
get propertyType() { return this._propertyType; }
|
||||
get leaseType() { return this._leaseType; }
|
||||
get status() { return this._status; }
|
||||
get title() { return this._title; }
|
||||
get description() { return this._description; }
|
||||
get areaM2() { return this._areaM2; }
|
||||
get ceilingHeightM() { return this._ceilingHeightM; }
|
||||
get floorLoadTonM2() { return this._floorLoadTonM2; }
|
||||
get columnSpacingM() { return this._columnSpacingM; }
|
||||
get dockCount() { return this._dockCount; }
|
||||
get craneCapacityTon() { return this._craneCapacityTon; }
|
||||
get hasMezzanine() { return this._hasMezzanine; }
|
||||
get hasOfficeArea() { return this._hasOfficeArea; }
|
||||
get officeAreaM2() { return this._officeAreaM2; }
|
||||
get priceUsdM2() { return this._priceUsdM2; }
|
||||
get pricingUnit() { return this._pricingUnit; }
|
||||
get totalLeasePrice() { return this._totalLeasePrice; }
|
||||
get managementFee() { return this._managementFee; }
|
||||
get depositMonths() { return this._depositMonths; }
|
||||
get minLeaseYears() { return this._minLeaseYears; }
|
||||
get maxLeaseYears() { return this._maxLeaseYears; }
|
||||
get leaseExpiry() { return this._leaseExpiry; }
|
||||
get availableFrom() { return this._availableFrom; }
|
||||
get powerCapacityKva() { return this._powerCapacityKva; }
|
||||
get waterSupplyM3Day() { return this._waterSupplyM3Day; }
|
||||
get media() { return this._media; }
|
||||
get viewCount() { return this._viewCount; }
|
||||
get inquiryCount() { return this._inquiryCount; }
|
||||
get publishedAt() { return this._publishedAt; }
|
||||
|
||||
updateDetails(props: Partial<Omit<IndustrialListingProps, 'parkId' | 'sellerId'>>): void {
|
||||
if (props.agentId !== undefined) this._agentId = props.agentId;
|
||||
if (props.propertyType !== undefined) this._propertyType = props.propertyType;
|
||||
if (props.leaseType !== undefined) this._leaseType = props.leaseType;
|
||||
if (props.status !== undefined) this._status = props.status;
|
||||
if (props.title !== undefined) this._title = props.title;
|
||||
if (props.description !== undefined) this._description = props.description;
|
||||
if (props.areaM2 !== undefined) this._areaM2 = props.areaM2;
|
||||
if (props.ceilingHeightM !== undefined) this._ceilingHeightM = props.ceilingHeightM;
|
||||
if (props.floorLoadTonM2 !== undefined) this._floorLoadTonM2 = props.floorLoadTonM2;
|
||||
if (props.columnSpacingM !== undefined) this._columnSpacingM = props.columnSpacingM;
|
||||
if (props.dockCount !== undefined) this._dockCount = props.dockCount;
|
||||
if (props.craneCapacityTon !== undefined) this._craneCapacityTon = props.craneCapacityTon;
|
||||
if (props.hasMezzanine !== undefined) this._hasMezzanine = props.hasMezzanine;
|
||||
if (props.hasOfficeArea !== undefined) this._hasOfficeArea = props.hasOfficeArea;
|
||||
if (props.officeAreaM2 !== undefined) this._officeAreaM2 = props.officeAreaM2;
|
||||
if (props.priceUsdM2 !== undefined) this._priceUsdM2 = props.priceUsdM2;
|
||||
if (props.pricingUnit !== undefined) this._pricingUnit = props.pricingUnit;
|
||||
if (props.totalLeasePrice !== undefined) this._totalLeasePrice = props.totalLeasePrice;
|
||||
if (props.managementFee !== undefined) this._managementFee = props.managementFee;
|
||||
if (props.depositMonths !== undefined) this._depositMonths = props.depositMonths;
|
||||
if (props.minLeaseYears !== undefined) this._minLeaseYears = props.minLeaseYears;
|
||||
if (props.maxLeaseYears !== undefined) this._maxLeaseYears = props.maxLeaseYears;
|
||||
if (props.leaseExpiry !== undefined) this._leaseExpiry = props.leaseExpiry;
|
||||
if (props.availableFrom !== undefined) this._availableFrom = props.availableFrom;
|
||||
if (props.powerCapacityKva !== undefined) this._powerCapacityKva = props.powerCapacityKva;
|
||||
if (props.waterSupplyM3Day !== undefined) this._waterSupplyM3Day = props.waterSupplyM3Day;
|
||||
if (props.media !== undefined) this._media = props.media;
|
||||
this.updatedAt = new Date();
|
||||
}
|
||||
|
||||
softDelete(): void {
|
||||
this._status = 'EXPIRED' as IndustrialListingStatus;
|
||||
this.updatedAt = new Date();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user