242 lines
5.8 KiB
TypeScript
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,
|
|
})),
|
|
};
|
|
}
|
|
}
|