feat(mining-wallet): add seed and auto-create province/city accounts
- Add prisma seed to initialize core system accounts (HQ, OP, FEE) and pool accounts - Auto-create province/city system accounts on-demand during contribution distribution - Province/city regions are also auto-created if not exist This ensures: 1. Core accounts exist after deployment (via seed) 2. Province/city accounts are created dynamically as orders come in Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
6594845d4c
commit
ef330a2687
|
|
@ -16,7 +16,8 @@
|
|||
"prisma:generate": "prisma generate",
|
||||
"prisma:migrate": "prisma migrate dev",
|
||||
"prisma:migrate:prod": "prisma migrate deploy",
|
||||
"prisma:studio": "prisma studio"
|
||||
"prisma:studio": "prisma studio",
|
||||
"prisma:seed": "ts-node prisma/seed.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^10.3.0",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
import { PrismaClient } from '@prisma/client';
|
||||
import Decimal from 'decimal.js';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
console.log('Seeding mining-wallet-service database...');
|
||||
|
||||
// 1. 初始化核心系统账户(总部、运营、手续费)
|
||||
const systemAccounts = [
|
||||
{ accountType: 'HEADQUARTERS', name: '总部账户', code: 'HQ' },
|
||||
{ accountType: 'OPERATION', name: '运营账户', code: 'OP' },
|
||||
{ accountType: 'FEE', name: '手续费账户', code: 'FEE' },
|
||||
];
|
||||
|
||||
for (const account of systemAccounts) {
|
||||
const existing = await prisma.systemAccount.findFirst({
|
||||
where: { code: account.code },
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
await prisma.systemAccount.create({
|
||||
data: {
|
||||
accountType: account.accountType as any,
|
||||
name: account.name,
|
||||
code: account.code,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
console.log(`Created system account: ${account.code}`);
|
||||
} else {
|
||||
console.log(`System account already exists: ${account.code}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 初始化池账户(积分股池、黑洞池、流通池)
|
||||
const poolAccounts = [
|
||||
{
|
||||
poolType: 'SHARE_POOL',
|
||||
name: '积分股池',
|
||||
balance: new Decimal('100000000'), // 1亿初始发行量
|
||||
description: '挖矿奖励的来源池,总发行量',
|
||||
},
|
||||
{
|
||||
poolType: 'BLACK_HOLE_POOL',
|
||||
name: '黑洞池',
|
||||
balance: new Decimal('0'),
|
||||
targetBurn: new Decimal('50000000'), // 目标销毁5000万
|
||||
description: '销毁的积分股,用于减少流通量',
|
||||
},
|
||||
{
|
||||
poolType: 'CIRCULATION_POOL',
|
||||
name: '流通池',
|
||||
balance: new Decimal('0'),
|
||||
description: '市场流通的积分股',
|
||||
},
|
||||
];
|
||||
|
||||
for (const pool of poolAccounts) {
|
||||
const existing = await prisma.poolAccount.findFirst({
|
||||
where: { poolType: pool.poolType as any },
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
await prisma.poolAccount.create({
|
||||
data: {
|
||||
poolType: pool.poolType as any,
|
||||
name: pool.name,
|
||||
balance: pool.balance,
|
||||
targetBurn: pool.targetBurn,
|
||||
remainingBurn: pool.targetBurn,
|
||||
description: pool.description,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
console.log(`Created pool account: ${pool.poolType}`);
|
||||
} else {
|
||||
console.log(`Pool account already exists: ${pool.poolType}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Seeding completed!');
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error('Seeding failed:', e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
|
|
@ -151,15 +151,25 @@ export class ContributionWalletService {
|
|||
};
|
||||
}
|
||||
|
||||
const systemAccount = await tx.systemAccount.findFirst({
|
||||
let systemAccount = await tx.systemAccount.findFirst({
|
||||
where: whereClause,
|
||||
});
|
||||
|
||||
// 如果找不到,尝试自动创建省/市级系统账户
|
||||
if (!systemAccount) {
|
||||
this.logger.warn(
|
||||
`System account not found: ${input.accountType}, province: ${input.provinceCode}, city: ${input.cityCode}`,
|
||||
systemAccount = await this.createSystemAccountIfNeeded(
|
||||
tx,
|
||||
input.accountType,
|
||||
input.provinceCode,
|
||||
input.cityCode,
|
||||
);
|
||||
return;
|
||||
|
||||
if (!systemAccount) {
|
||||
this.logger.warn(
|
||||
`Failed to create system account: ${input.accountType}, province: ${input.provinceCode}, city: ${input.cityCode}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const balanceBefore = new Decimal(
|
||||
|
|
@ -281,4 +291,110 @@ export class ContributionWalletService {
|
|||
};
|
||||
return `${typeMap[input.contributionType]}, 来源认种: ${input.sourceAdoptionId}, 认种人: ${input.sourceAccountSequence}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动创建省/市级系统账户(如果不存在)
|
||||
* 同时会创建对应的省/市区域记录
|
||||
*/
|
||||
private async createSystemAccountIfNeeded(
|
||||
tx: any,
|
||||
accountType: string,
|
||||
provinceCode?: string,
|
||||
cityCode?: string,
|
||||
): Promise<any | null> {
|
||||
// 只处理省/市级账户的自动创建
|
||||
if (accountType === 'PROVINCE' && provinceCode) {
|
||||
// 先找或创建省份
|
||||
let province = await tx.province.findUnique({
|
||||
where: { code: provinceCode },
|
||||
});
|
||||
|
||||
if (!province) {
|
||||
province = await tx.province.create({
|
||||
data: {
|
||||
code: provinceCode,
|
||||
name: `${provinceCode}省`, // 默认名称,后续可更新
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
});
|
||||
this.logger.log(`Auto-created province: ${provinceCode}`);
|
||||
}
|
||||
|
||||
// 创建省级系统账户
|
||||
const account = await tx.systemAccount.create({
|
||||
data: {
|
||||
accountType: 'PROVINCE',
|
||||
name: `${province.name}账户`,
|
||||
code: `PROV-${provinceCode}`,
|
||||
provinceId: province.id,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
this.logger.log(`Auto-created province system account: ${account.code}`);
|
||||
return account;
|
||||
}
|
||||
|
||||
if (accountType === 'CITY' && cityCode) {
|
||||
// 先找城市
|
||||
let city = await tx.city.findUnique({
|
||||
where: { code: cityCode },
|
||||
});
|
||||
|
||||
if (!city) {
|
||||
// 城市不存在,需要先有省份
|
||||
// 尝试从 cityCode 推断省份(如 GD-SZ 中的 GD)
|
||||
const parts = cityCode.split('-');
|
||||
const inferredProvinceCode = parts.length > 1 ? parts[0] : null;
|
||||
|
||||
if (!inferredProvinceCode) {
|
||||
this.logger.warn(`Cannot infer province from city code: ${cityCode}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 找或创建省份
|
||||
let province = await tx.province.findUnique({
|
||||
where: { code: inferredProvinceCode },
|
||||
});
|
||||
|
||||
if (!province) {
|
||||
province = await tx.province.create({
|
||||
data: {
|
||||
code: inferredProvinceCode,
|
||||
name: `${inferredProvinceCode}省`,
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
});
|
||||
this.logger.log(`Auto-created province: ${inferredProvinceCode}`);
|
||||
}
|
||||
|
||||
// 创建城市
|
||||
city = await tx.city.create({
|
||||
data: {
|
||||
code: cityCode,
|
||||
name: `${cityCode}市`, // 默认名称
|
||||
provinceId: province.id,
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
});
|
||||
this.logger.log(`Auto-created city: ${cityCode}`);
|
||||
}
|
||||
|
||||
// 创建市级系统账户
|
||||
const account = await tx.systemAccount.create({
|
||||
data: {
|
||||
accountType: 'CITY',
|
||||
name: `${city.name}账户`,
|
||||
code: `CITY-${cityCode}`,
|
||||
provinceId: city.provinceId,
|
||||
cityId: city.id,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
this.logger.log(`Auto-created city system account: ${account.code}`);
|
||||
return account;
|
||||
}
|
||||
|
||||
// 其他类型(HEADQUARTERS, OPERATION, FEE)不自动创建,需要在 seed 中初始化
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue