export interface PositionDistributionData { id?: bigint; userId: bigint; provinceCode: string | null; cityCode: string | null; treeCount: number; } export interface PlantingPositionData { id?: bigint; userId: bigint; totalTreeCount: number; effectiveTreeCount: number; pendingTreeCount: number; firstMiningStartAt?: Date | null; distributions?: PositionDistributionData[]; } export class PositionDistribution { private _id: bigint | null; private readonly _userId: bigint; private readonly _provinceCode: string | null; private readonly _cityCode: string | null; private _treeCount: number; constructor( userId: bigint, provinceCode: string | null, cityCode: string | null, treeCount: number = 0, id?: bigint, ) { this._id = id || null; this._userId = userId; this._provinceCode = provinceCode; this._cityCode = cityCode; this._treeCount = treeCount; } get id(): bigint | null { return this._id; } get userId(): bigint { return this._userId; } get provinceCode(): string | null { return this._provinceCode; } get cityCode(): string | null { return this._cityCode; } get treeCount(): number { return this._treeCount; } addTrees(count: number): void { if (count <= 0) { throw new Error('添加数量必须大于0'); } this._treeCount += count; } setId(id: bigint): void { this._id = id; } matchesLocation(provinceCode: string | null, cityCode: string | null): boolean { return this._provinceCode === provinceCode && this._cityCode === cityCode; } } export class PlantingPosition { private _id: bigint | null; private readonly _userId: bigint; private _totalTreeCount: number; private _effectiveTreeCount: number; private _pendingTreeCount: number; private _firstMiningStartAt: Date | null; private _distributions: PositionDistribution[]; private constructor( userId: bigint, totalTreeCount: number = 0, effectiveTreeCount: number = 0, pendingTreeCount: number = 0, ) { this._id = null; this._userId = userId; this._totalTreeCount = totalTreeCount; this._effectiveTreeCount = effectiveTreeCount; this._pendingTreeCount = pendingTreeCount; this._firstMiningStartAt = null; this._distributions = []; } // Getters get id(): bigint | null { return this._id; } get userId(): bigint { return this._userId; } get totalTreeCount(): number { return this._totalTreeCount; } get effectiveTreeCount(): number { return this._effectiveTreeCount; } get pendingTreeCount(): number { return this._pendingTreeCount; } get firstMiningStartAt(): Date | null { return this._firstMiningStartAt; } get distributions(): ReadonlyArray { return this._distributions; } /** * 工厂方法:创建新持仓 */ static create(userId: bigint): PlantingPosition { return new PlantingPosition(userId); } /** * 添加认种 * 支付完成后直接生效,不再等待底池注入 */ addPlanting( treeCount: number, provinceCode: string | null, cityCode: string | null, ): void { if (treeCount <= 0) { throw new Error('认种数量必须大于0'); } // 更新总数和有效数(直接生效) this._totalTreeCount += treeCount; this._effectiveTreeCount += treeCount; // 设置首次挖矿开始时间 if (!this._firstMiningStartAt) { this._firstMiningStartAt = new Date(); } // 更新省市分布 let distribution = this._distributions.find((d) => d.matchesLocation(provinceCode, cityCode), ); if (distribution) { distribution.addTrees(treeCount); } else { distribution = new PositionDistribution( this._userId, provinceCode, cityCode, treeCount, ); this._distributions.push(distribution); } } /** * 激活挖矿(底池注入后) */ activateMining(treeCount: number): void { if (treeCount > this._pendingTreeCount) { throw new Error('激活数量不能超过待生效数量'); } this._pendingTreeCount -= treeCount; this._effectiveTreeCount += treeCount; if (!this._firstMiningStartAt) { this._firstMiningStartAt = new Date(); } } /** * 设置ID */ setId(id: bigint): void { this._id = id; } /** * 从数据库重建 */ static reconstitute(data: PlantingPositionData): PlantingPosition { const position = new PlantingPosition( data.userId, data.totalTreeCount, data.effectiveTreeCount, data.pendingTreeCount, ); if (data.id) { position._id = data.id; } position._firstMiningStartAt = data.firstMiningStartAt || null; if (data.distributions) { position._distributions = data.distributions.map( (d) => new PositionDistribution( d.userId, d.provinceCode, d.cityCode, d.treeCount, d.id, ), ); } return position; } /** * 转换为可持久化的数据对象 */ toPersistence(): PlantingPositionData { return { id: this._id || undefined, userId: this._userId, totalTreeCount: this._totalTreeCount, effectiveTreeCount: this._effectiveTreeCount, pendingTreeCount: this._pendingTreeCount, firstMiningStartAt: this._firstMiningStartAt, distributions: this._distributions.map((d) => ({ id: d.id || undefined, userId: d.userId, provinceCode: d.provinceCode, cityCode: d.cityCode, treeCount: d.treeCount, })), }; } }