feat(pre-planting): 支持省市名称存储,参照正常认种处理方式
- PurchasePrePlantingDto 添加可选字段 provinceName/cityName, 与 SelectProvinceCityDto 保持一致,解决 NestJS forbidNonWhitelisted 400 错误 - pre_planting_positions 表新增 province_name/city_name 列(迁移) - PrePlantingPosition aggregate 增加 provinceName/cityName 字段 - addPortions() 接受并存储省市名称 - getPosition() 返回 provinceName/cityName 供续购时显示 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5aa17b05c5
commit
20b8d41212
|
|
@ -0,0 +1,3 @@
|
|||
-- Add province_name and city_name to pre_planting_positions
|
||||
ALTER TABLE "pre_planting_positions" ADD COLUMN IF NOT EXISTS "province_name" VARCHAR(50);
|
||||
ALTER TABLE "pre_planting_positions" ADD COLUMN IF NOT EXISTS "city_name" VARCHAR(50);
|
||||
|
|
@ -453,7 +453,9 @@ model PrePlantingPosition {
|
|||
|
||||
// 省市 (首次购买时选择,后续复用)
|
||||
provinceCode String? @map("province_code") @db.VarChar(10)
|
||||
provinceName String? @map("province_name") @db.VarChar(50)
|
||||
cityCode String? @map("city_code") @db.VarChar(10)
|
||||
cityName String? @map("city_name") @db.VarChar(50)
|
||||
|
||||
// 首次购买时间 (1年冻结起点)
|
||||
firstPurchaseAt DateTime? @map("first_purchase_at")
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ export class PrePlantingController {
|
|||
dto.portionCount,
|
||||
dto.provinceCode,
|
||||
dto.cityCode,
|
||||
dto.provinceName,
|
||||
dto.cityName,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { IsInt, IsString, Min, Max, IsNotEmpty } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsInt, IsString, Min, Max, IsNotEmpty, IsOptional, MaxLength } from 'class-validator';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
export class PurchasePrePlantingDto {
|
||||
@ApiProperty({ description: '购买份数', example: 1, minimum: 1, maximum: 5 })
|
||||
|
|
@ -13,8 +13,20 @@ export class PurchasePrePlantingDto {
|
|||
@IsNotEmpty()
|
||||
provinceCode: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '省名称', example: '广东省' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(50)
|
||||
provinceName?: string;
|
||||
|
||||
@ApiProperty({ description: '市代码', example: '4401' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
cityCode: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '市名称', example: '广州市' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(50)
|
||||
cityName?: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ export class PrePlantingApplicationService {
|
|||
portionCount: number,
|
||||
provinceCode: string,
|
||||
cityCode: string,
|
||||
provinceName?: string,
|
||||
cityName?: string,
|
||||
): Promise<{ orderNo: string; merged: boolean; mergeNo?: string }> {
|
||||
this.logger.log(
|
||||
`[PRE-PLANTING] Purchase request: userId=${userId}, portions=${portionCount}, ` +
|
||||
|
|
@ -98,7 +100,7 @@ export class PrePlantingApplicationService {
|
|||
}
|
||||
|
||||
// 增加份数
|
||||
position.addPortions(portionCount, provinceCode, cityCode);
|
||||
position.addPortions(portionCount, provinceCode, cityCode, provinceName, cityName);
|
||||
|
||||
// 标记订单为已支付
|
||||
order.markAsPaid(position.totalPortions, position.availablePortions);
|
||||
|
|
@ -278,7 +280,9 @@ export class PrePlantingApplicationService {
|
|||
mergedPortions: number;
|
||||
totalTreesMerged: number;
|
||||
provinceCode: string | null;
|
||||
provinceName: string | null;
|
||||
cityCode: string | null;
|
||||
cityName: string | null;
|
||||
firstPurchaseAt: Date | null;
|
||||
} | null> {
|
||||
return this.prisma.$transaction(async (tx) => {
|
||||
|
|
@ -290,7 +294,9 @@ export class PrePlantingApplicationService {
|
|||
mergedPortions: position.mergedPortions,
|
||||
totalTreesMerged: position.totalTreesMerged,
|
||||
provinceCode: position.provinceCode,
|
||||
provinceName: position.provinceName,
|
||||
cityCode: position.cityCode,
|
||||
cityName: position.cityName,
|
||||
firstPurchaseAt: position.firstPurchaseAt,
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ export interface PrePlantingPositionData {
|
|||
mergedPortions: number;
|
||||
totalTreesMerged: number;
|
||||
provinceCode?: string | null;
|
||||
provinceName?: string | null;
|
||||
cityCode?: string | null;
|
||||
cityName?: string | null;
|
||||
firstPurchaseAt?: Date | null;
|
||||
createdAt?: Date;
|
||||
}
|
||||
|
|
@ -23,7 +25,9 @@ export class PrePlantingPosition {
|
|||
private _mergedPortions: number;
|
||||
private _totalTreesMerged: number;
|
||||
private _provinceCode: string | null;
|
||||
private _provinceName: string | null;
|
||||
private _cityCode: string | null;
|
||||
private _cityName: string | null;
|
||||
private _firstPurchaseAt: Date | null;
|
||||
private readonly _createdAt: Date;
|
||||
|
||||
|
|
@ -36,7 +40,9 @@ export class PrePlantingPosition {
|
|||
this._mergedPortions = 0;
|
||||
this._totalTreesMerged = 0;
|
||||
this._provinceCode = null;
|
||||
this._provinceName = null;
|
||||
this._cityCode = null;
|
||||
this._cityName = null;
|
||||
this._firstPurchaseAt = null;
|
||||
this._createdAt = createdAt || new Date();
|
||||
}
|
||||
|
|
@ -53,7 +59,9 @@ export class PrePlantingPosition {
|
|||
position._mergedPortions = data.mergedPortions;
|
||||
position._totalTreesMerged = data.totalTreesMerged;
|
||||
position._provinceCode = data.provinceCode || null;
|
||||
position._provinceName = data.provinceName || null;
|
||||
position._cityCode = data.cityCode || null;
|
||||
position._cityName = data.cityName || null;
|
||||
position._firstPurchaseAt = data.firstPurchaseAt || null;
|
||||
return position;
|
||||
}
|
||||
|
|
@ -61,7 +69,7 @@ export class PrePlantingPosition {
|
|||
/**
|
||||
* 增加购买份数
|
||||
*/
|
||||
addPortions(count: number, provinceCode: string, cityCode: string): void {
|
||||
addPortions(count: number, provinceCode: string, cityCode: string, provinceName?: string, cityName?: string): void {
|
||||
this._totalPortions += count;
|
||||
this._availablePortions += count;
|
||||
|
||||
|
|
@ -70,6 +78,8 @@ export class PrePlantingPosition {
|
|||
this._firstPurchaseAt = new Date();
|
||||
this._provinceCode = provinceCode;
|
||||
this._cityCode = cityCode;
|
||||
this._provinceName = provinceName || null;
|
||||
this._cityName = cityName || null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +123,9 @@ export class PrePlantingPosition {
|
|||
get mergedPortions(): number { return this._mergedPortions; }
|
||||
get totalTreesMerged(): number { return this._totalTreesMerged; }
|
||||
get provinceCode(): string | null { return this._provinceCode; }
|
||||
get provinceName(): string | null { return this._provinceName; }
|
||||
get cityCode(): string | null { return this._cityCode; }
|
||||
get cityName(): string | null { return this._cityName; }
|
||||
get firstPurchaseAt(): Date | null { return this._firstPurchaseAt; }
|
||||
get createdAt(): Date { return this._createdAt; }
|
||||
|
||||
|
|
@ -127,7 +139,9 @@ export class PrePlantingPosition {
|
|||
mergedPortions: this._mergedPortions,
|
||||
totalTreesMerged: this._totalTreesMerged,
|
||||
provinceCode: this._provinceCode,
|
||||
provinceName: this._provinceName,
|
||||
cityCode: this._cityCode,
|
||||
cityName: this._cityName,
|
||||
firstPurchaseAt: this._firstPurchaseAt,
|
||||
createdAt: this._createdAt,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ export class PrePlantingPositionRepository {
|
|||
mergedPortions: data.mergedPortions,
|
||||
totalTreesMerged: data.totalTreesMerged,
|
||||
provinceCode: data.provinceCode || null,
|
||||
provinceName: data.provinceName || null,
|
||||
cityCode: data.cityCode || null,
|
||||
cityName: data.cityName || null,
|
||||
firstPurchaseAt: data.firstPurchaseAt || null,
|
||||
},
|
||||
});
|
||||
|
|
@ -38,7 +40,9 @@ export class PrePlantingPositionRepository {
|
|||
mergedPortions: data.mergedPortions,
|
||||
totalTreesMerged: data.totalTreesMerged,
|
||||
provinceCode: data.provinceCode || null,
|
||||
provinceName: data.provinceName || null,
|
||||
cityCode: data.cityCode || null,
|
||||
cityName: data.cityName || null,
|
||||
firstPurchaseAt: data.firstPurchaseAt || null,
|
||||
},
|
||||
});
|
||||
|
|
@ -97,7 +101,9 @@ export class PrePlantingPositionRepository {
|
|||
mergedPortions: record.mergedPortions,
|
||||
totalTreesMerged: record.totalTreesMerged,
|
||||
provinceCode: record.provinceCode,
|
||||
provinceName: record.provinceName,
|
||||
cityCode: record.cityCode,
|
||||
cityName: record.cityName,
|
||||
firstPurchaseAt: record.firstPurchaseAt,
|
||||
createdAt: record.createdAt,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue