rwadurian/backend/services/planting-service/src/domain/aggregates/planting-position.aggregate.ts

242 lines
5.8 KiB
TypeScript

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<PositionDistribution> {
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,
})),
};
}
}