refactor!: 重构账户序列号格式 (BREAKING CHANGE)

将 accountSequence 从数字类型改为字符串类型,新格式为:
- 普通用户: D + YYMMDD + 5位序号 (例: D2512120001)
- 系统账户: S + 10位序号 (例: S0000000001)

主要变更:
- identity-service: AccountSequence 值对象改为字符串类型
- identity-service: 序列号生成器改为按日期重置计数
- 所有服务: Prisma schema 字段类型从 BigInt/Int 改为 String
- 所有服务: DTO、Command、Event 中的类型定义更新
- Flutter 前端: 相关数据模型类型更新

涉及服务:
- identity-service (核心变更)
- referral-service
- authorization-service
- wallet-service
- reward-service
- blockchain-service
- backup-service
- planting-service
- mpc-service
- admin-service
- mobile-app (Flutter)

注意: 此为破坏性变更,需要清空数据库并重新运行 migration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-12 09:11:18 -08:00
parent 8148d1d127
commit 4be9c1fb82
163 changed files with 5050 additions and 4917 deletions

View File

@ -32,7 +32,20 @@
"Bash(node -e \"\nconst { ethers } = require(''ethers'');\n\nconst KAVA_TESTNET_RPC = ''https://evm.testnet.kava.io'';\nconst privateKey = ''0xd42a6e6021ebd884f3f179d3793a32e97b9f1001db6ff44441ec455d748b9aa6'';\nconst USDT_CONTRACT = ''0xc12f6A4A7Fd0965085B044A67a39CcA2ff7fe0dF'';\nconst TO_ADDRESS = ''0xd110112e057d269b41f7dc7dbf1f8eabb896f51a'';\n\nasync function transfer() {\n const provider = new ethers.JsonRpcProvider(KAVA_TESTNET_RPC);\n const wallet = new ethers.Wallet(privateKey, provider);\n \n const abi = [''function transfer(address to, uint256 amount) returns (bool)'', ''function balanceOf(address) view returns (uint256)''];\n const contract = new ethers.Contract(USDT_CONTRACT, abi, wallet);\n \n // 300,000 USDT = 300000 * 1e6 (6 decimals)\n const amount = BigInt(300000) * BigInt(1000000);\n \n console.log(''Transferring 300,000 USDT to'', TO_ADDRESS);\n const tx = await contract.transfer(TO_ADDRESS, amount, { gasLimit: 100000 });\n console.log(''TX Hash:'', tx.hash);\n await tx.wait();\n \n const newBalance = await contract.balanceOf(TO_ADDRESS);\n console.log(''New balance:'', Number(newBalance) / 1e6, ''USDT'');\n}\n\ntransfer().catch(e => console.error(''Error:'', e.message));\n\")", "Bash(node -e \"\nconst { ethers } = require(''ethers'');\n\nconst KAVA_TESTNET_RPC = ''https://evm.testnet.kava.io'';\nconst privateKey = ''0xd42a6e6021ebd884f3f179d3793a32e97b9f1001db6ff44441ec455d748b9aa6'';\nconst USDT_CONTRACT = ''0xc12f6A4A7Fd0965085B044A67a39CcA2ff7fe0dF'';\nconst TO_ADDRESS = ''0xd110112e057d269b41f7dc7dbf1f8eabb896f51a'';\n\nasync function transfer() {\n const provider = new ethers.JsonRpcProvider(KAVA_TESTNET_RPC);\n const wallet = new ethers.Wallet(privateKey, provider);\n \n const abi = [''function transfer(address to, uint256 amount) returns (bool)'', ''function balanceOf(address) view returns (uint256)''];\n const contract = new ethers.Contract(USDT_CONTRACT, abi, wallet);\n \n // 300,000 USDT = 300000 * 1e6 (6 decimals)\n const amount = BigInt(300000) * BigInt(1000000);\n \n console.log(''Transferring 300,000 USDT to'', TO_ADDRESS);\n const tx = await contract.transfer(TO_ADDRESS, amount, { gasLimit: 100000 });\n console.log(''TX Hash:'', tx.hash);\n await tx.wait();\n \n const newBalance = await contract.balanceOf(TO_ADDRESS);\n console.log(''New balance:'', Number(newBalance) / 1e6, ''USDT'');\n}\n\ntransfer().catch(e => console.error(''Error:'', e.message));\n\")",
"Bash(node -e \"\nconst { ethers } = require(''ethers'');\n\nconst KAVA_TESTNET_RPC = ''https://evm.testnet.kava.io'';\nconst privateKey = ''0xd42a6e6021ebd884f3f179d3793a32e97b9f1001db6ff44441ec455d748b9aa6'';\nconst USDT_CONTRACT = ''0xc12f6A4A7Fd0965085B044A67a39CcA2ff7fe0dF'';\nconst TO_ADDRESS = ''0x6a664488d000e094baa8a055961921bf495c1152'';\n\nasync function transfer() {\n const provider = new ethers.JsonRpcProvider(KAVA_TESTNET_RPC);\n const wallet = new ethers.Wallet(privateKey, provider);\n \n const abi = [''function transfer(address to, uint256 amount) returns (bool)'', ''function balanceOf(address) view returns (uint256)''];\n const contract = new ethers.Contract(USDT_CONTRACT, abi, wallet);\n \n // 880,000 USDT = 880000 * 1e6 (6 decimals)\n const amount = BigInt(880000) * BigInt(1000000);\n \n console.log(''Transferring 880,000 USDT to'', TO_ADDRESS);\n const tx = await contract.transfer(TO_ADDRESS, amount, { gasLimit: 100000 });\n console.log(''TX Hash:'', tx.hash);\n await tx.wait();\n \n const newBalance = await contract.balanceOf(TO_ADDRESS);\n console.log(''New balance:'', Number(newBalance) / 1e6, ''USDT'');\n}\n\ntransfer().catch(e => console.error(''Error:'', e.message));\n\")", "Bash(node -e \"\nconst { ethers } = require(''ethers'');\n\nconst KAVA_TESTNET_RPC = ''https://evm.testnet.kava.io'';\nconst privateKey = ''0xd42a6e6021ebd884f3f179d3793a32e97b9f1001db6ff44441ec455d748b9aa6'';\nconst USDT_CONTRACT = ''0xc12f6A4A7Fd0965085B044A67a39CcA2ff7fe0dF'';\nconst TO_ADDRESS = ''0x6a664488d000e094baa8a055961921bf495c1152'';\n\nasync function transfer() {\n const provider = new ethers.JsonRpcProvider(KAVA_TESTNET_RPC);\n const wallet = new ethers.Wallet(privateKey, provider);\n \n const abi = [''function transfer(address to, uint256 amount) returns (bool)'', ''function balanceOf(address) view returns (uint256)''];\n const contract = new ethers.Contract(USDT_CONTRACT, abi, wallet);\n \n // 880,000 USDT = 880000 * 1e6 (6 decimals)\n const amount = BigInt(880000) * BigInt(1000000);\n \n console.log(''Transferring 880,000 USDT to'', TO_ADDRESS);\n const tx = await contract.transfer(TO_ADDRESS, amount, { gasLimit: 100000 });\n console.log(''TX Hash:'', tx.hash);\n await tx.wait();\n \n const newBalance = await contract.balanceOf(TO_ADDRESS);\n console.log(''New balance:'', Number(newBalance) / 1e6, ''USDT'');\n}\n\ntransfer().catch(e => console.error(''Error:'', e.message));\n\")",
"Bash(node -e \"\nconst { ethers } = require(''ethers'');\n\nconst KAVA_TESTNET_RPC = ''https://evm.testnet.kava.io'';\nconst privateKey = ''0xd42a6e6021ebd884f3f179d3793a32e97b9f1001db6ff44441ec455d748b9aa6'';\nconst USDT_CONTRACT = ''0xc12f6A4A7Fd0965085B044A67a39CcA2ff7fe0dF'';\nconst TO_ADDRESS = ''0x53fd262ef1a707b80f87581cc64e09800fdbd690'';\n\nasync function transfer() {\n const provider = new ethers.JsonRpcProvider(KAVA_TESTNET_RPC);\n const wallet = new ethers.Wallet(privateKey, provider);\n \n const abi = [''function transfer(address to, uint256 amount) returns (bool)'', ''function balanceOf(address) view returns (uint256)''];\n const contract = new ethers.Contract(USDT_CONTRACT, abi, wallet);\n \n // 360,000 USDT = 360000 * 1e6 (6 decimals)\n const amount = BigInt(360000) * BigInt(1000000);\n \n console.log(''Transferring 360,000 USDT to'', TO_ADDRESS);\n const tx = await contract.transfer(TO_ADDRESS, amount, { gasLimit: 100000 });\n console.log(''TX Hash:'', tx.hash);\n await tx.wait();\n \n const newBalance = await contract.balanceOf(TO_ADDRESS);\n console.log(''New balance:'', Number(newBalance) / 1e6, ''USDT'');\n}\n\ntransfer().catch(e => console.error(''Error:'', e.message));\n\")", "Bash(node -e \"\nconst { ethers } = require(''ethers'');\n\nconst KAVA_TESTNET_RPC = ''https://evm.testnet.kava.io'';\nconst privateKey = ''0xd42a6e6021ebd884f3f179d3793a32e97b9f1001db6ff44441ec455d748b9aa6'';\nconst USDT_CONTRACT = ''0xc12f6A4A7Fd0965085B044A67a39CcA2ff7fe0dF'';\nconst TO_ADDRESS = ''0x53fd262ef1a707b80f87581cc64e09800fdbd690'';\n\nasync function transfer() {\n const provider = new ethers.JsonRpcProvider(KAVA_TESTNET_RPC);\n const wallet = new ethers.Wallet(privateKey, provider);\n \n const abi = [''function transfer(address to, uint256 amount) returns (bool)'', ''function balanceOf(address) view returns (uint256)''];\n const contract = new ethers.Contract(USDT_CONTRACT, abi, wallet);\n \n // 360,000 USDT = 360000 * 1e6 (6 decimals)\n const amount = BigInt(360000) * BigInt(1000000);\n \n console.log(''Transferring 360,000 USDT to'', TO_ADDRESS);\n const tx = await contract.transfer(TO_ADDRESS, amount, { gasLimit: 100000 });\n console.log(''TX Hash:'', tx.hash);\n await tx.wait();\n \n const newBalance = await contract.balanceOf(TO_ADDRESS);\n console.log(''New balance:'', Number(newBalance) / 1e6, ''USDT'');\n}\n\ntransfer().catch(e => console.error(''Error:'', e.message));\n\")",
"Bash(docker exec:*)" "Bash(docker exec:*)",
"Bash(node -e:*)",
"Bash(dir /s /b c:UsersdongDesktoprwadurianbackendservicesreward-servicesrc*.ts)",
"Bash(git tag:*)",
"Bash(dir:*)",
"Bash(grep:*)",
"Bash(npx prisma format)",
"Bash(DATABASE_URL=\"postgresql://dummy:dummy@localhost:5432/dummy\" npx prisma generate:*)",
"Bash(npx prisma generate)",
"Bash(for file in grant-*.dto.ts)",
"Bash(do sed -i '/@ApiProperty.*账户序列号/,/accountSequence:/ s/@IsNumber()/@IsString()/' \"$file\")",
"Bash(done)",
"Bash(git diff:*)",
"Bash(npm install:*)"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

View File

@ -13,8 +13,8 @@ datasource db {
// ============ 授权角色表 ============ // ============ 授权角色表 ============
model AuthorizationRole { model AuthorizationRole {
id String @id @default(uuid()) id String @id @default(uuid())
userId BigInt @map("user_id") userId String @map("user_id")
accountSequence BigInt @map("account_sequence") accountSequence String @map("account_sequence")
roleType RoleType @map("role_type") roleType RoleType @map("role_type")
regionCode String @map("region_code") regionCode String @map("region_code")
regionName String @map("region_name") regionName String @map("region_name")
@ -23,9 +23,9 @@ model AuthorizationRole {
// 授权信息 // 授权信息
authorizedAt DateTime? @map("authorized_at") authorizedAt DateTime? @map("authorized_at")
authorizedBy BigInt? @map("authorized_by") authorizedBy String? @map("authorized_by")
revokedAt DateTime? @map("revoked_at") revokedAt DateTime? @map("revoked_at")
revokedBy BigInt? @map("revoked_by") revokedBy String? @map("revoked_by")
revokeReason String? @map("revoke_reason") revokeReason String? @map("revoke_reason")
// 考核配置 // 考核配置
@ -65,8 +65,8 @@ model AuthorizationRole {
model MonthlyAssessment { model MonthlyAssessment {
id String @id @default(uuid()) id String @id @default(uuid())
authorizationId String @map("authorization_id") authorizationId String @map("authorization_id")
userId BigInt @map("user_id") userId String @map("user_id")
accountSequence BigInt @map("account_sequence") accountSequence String @map("account_sequence")
roleType RoleType @map("role_type") roleType RoleType @map("role_type")
regionCode String @map("region_code") regionCode String @map("region_code")
@ -101,7 +101,7 @@ model MonthlyAssessment {
// 豁免 // 豁免
isBypassed Boolean @default(false) @map("is_bypassed") isBypassed Boolean @default(false) @map("is_bypassed")
bypassedBy BigInt? @map("bypassed_by") bypassedBy String? @map("bypassed_by")
bypassedAt DateTime? @map("bypassed_at") bypassedAt DateTime? @map("bypassed_at")
// 时间戳 // 时间戳
@ -125,22 +125,22 @@ model MonthlyAssessment {
model MonthlyBypass { model MonthlyBypass {
id String @id @default(uuid()) id String @id @default(uuid())
authorizationId String @map("authorization_id") authorizationId String @map("authorization_id")
userId BigInt @map("user_id") userId String @map("user_id")
accountSequence BigInt @map("account_sequence") accountSequence String @map("account_sequence")
roleType RoleType @map("role_type") roleType RoleType @map("role_type")
bypassMonth String @map("bypass_month") // YYYY-MM bypassMonth String @map("bypass_month") // YYYY-MM
// 授权信息 // 授权信息
grantedBy BigInt @map("granted_by") grantedBy String @map("granted_by")
grantedAt DateTime @map("granted_at") grantedAt DateTime @map("granted_at")
reason String? reason String?
// 审批信息(三人授权) // 审批信息(三人授权)
approver1Id BigInt @map("approver1_id") approver1Id String @map("approver1_id")
approver1At DateTime @map("approver1_at") approver1At DateTime @map("approver1_at")
approver2Id BigInt? @map("approver2_id") approver2Id String? @map("approver2_id")
approver2At DateTime? @map("approver2_at") approver2At DateTime? @map("approver2_at")
approver3Id BigInt? @map("approver3_id") approver3Id String? @map("approver3_id")
approver3At DateTime? @map("approver3_at") approver3At DateTime? @map("approver3_at")
approvalStatus ApprovalStatus @default(PENDING) @map("approval_status") approvalStatus ApprovalStatus @default(PENDING) @map("approval_status")
@ -281,8 +281,8 @@ model RegionHeatMap {
// ============ 火柴人排名视图数据表 ============ // ============ 火柴人排名视图数据表 ============
model StickmanRanking { model StickmanRanking {
id String @id @default(uuid()) id String @id @default(uuid())
userId BigInt @map("user_id") userId String @map("user_id")
accountSequence BigInt @map("account_sequence") accountSequence String @map("account_sequence")
authorizationId String @map("authorization_id") authorizationId String @map("authorization_id")
roleType RoleType @map("role_type") roleType RoleType @map("role_type")
regionCode String @map("region_code") regionCode String @map("region_code")

View File

@ -30,7 +30,7 @@ export class AdminAuthorizationController {
@ApiOperation({ summary: '授权社区(管理员)' }) @ApiOperation({ summary: '授权社区(管理员)' })
@ApiResponse({ status: 201, description: '授权成功' }) @ApiResponse({ status: 201, description: '授权成功' })
async grantCommunity( async grantCommunity(
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
@Body() dto: GrantCommunityDto, @Body() dto: GrantCommunityDto,
): Promise<{ message: string }> { ): Promise<{ message: string }> {
const command = new GrantCommunityCommand( const command = new GrantCommunityCommand(
@ -50,7 +50,7 @@ export class AdminAuthorizationController {
@ApiOperation({ summary: '授权正式省公司(管理员)' }) @ApiOperation({ summary: '授权正式省公司(管理员)' })
@ApiResponse({ status: 201, description: '授权成功' }) @ApiResponse({ status: 201, description: '授权成功' })
async grantProvinceCompany( async grantProvinceCompany(
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
@Body() dto: GrantProvinceCompanyDto, @Body() dto: GrantProvinceCompanyDto,
): Promise<{ message: string }> { ): Promise<{ message: string }> {
const command = new GrantProvinceCompanyCommand( const command = new GrantProvinceCompanyCommand(
@ -71,7 +71,7 @@ export class AdminAuthorizationController {
@ApiOperation({ summary: '授权正式市公司(管理员)' }) @ApiOperation({ summary: '授权正式市公司(管理员)' })
@ApiResponse({ status: 201, description: '授权成功' }) @ApiResponse({ status: 201, description: '授权成功' })
async grantCityCompany( async grantCityCompany(
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
@Body() dto: GrantCityCompanyDto, @Body() dto: GrantCityCompanyDto,
): Promise<{ message: string }> { ): Promise<{ message: string }> {
const command = new GrantCityCompanyCommand( const command = new GrantCityCompanyCommand(
@ -93,7 +93,7 @@ export class AdminAuthorizationController {
@ApiResponse({ status: 201, description: '授权成功' }) @ApiResponse({ status: 201, description: '授权成功' })
@ApiResponse({ status: 400, description: '验证失败(如团队内已存在相同省份授权)' }) @ApiResponse({ status: 400, description: '验证失败(如团队内已存在相同省份授权)' })
async grantAuthProvinceCompany( async grantAuthProvinceCompany(
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
@Body() dto: GrantAuthProvinceCompanyDto, @Body() dto: GrantAuthProvinceCompanyDto,
): Promise<{ message: string }> { ): Promise<{ message: string }> {
const command = new GrantAuthProvinceCompanyCommand( const command = new GrantAuthProvinceCompanyCommand(
@ -115,7 +115,7 @@ export class AdminAuthorizationController {
@ApiResponse({ status: 201, description: '授权成功' }) @ApiResponse({ status: 201, description: '授权成功' })
@ApiResponse({ status: 400, description: '验证失败(如团队内已存在相同城市授权)' }) @ApiResponse({ status: 400, description: '验证失败(如团队内已存在相同城市授权)' })
async grantAuthCityCompany( async grantAuthCityCompany(
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
@Body() dto: GrantAuthCityCompanyDto, @Body() dto: GrantAuthCityCompanyDto,
): Promise<{ message: string }> { ): Promise<{ message: string }> {
const command = new GrantAuthCityCompanyCommand( const command = new GrantAuthCityCompanyCommand(

View File

@ -55,7 +55,7 @@ export class AuthorizationController {
@ApiOperation({ summary: '申请社区授权' }) @ApiOperation({ summary: '申请社区授权' })
@ApiResponse({ status: 201, type: ApplyAuthorizationResponse }) @ApiResponse({ status: 201, type: ApplyAuthorizationResponse })
async applyCommunityAuth( async applyCommunityAuth(
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
@Body() dto: ApplyCommunityAuthDto, @Body() dto: ApplyCommunityAuthDto,
): Promise<ApplyAuthorizationResponse> { ): Promise<ApplyAuthorizationResponse> {
const command = new ApplyCommunityAuthCommand(user.userId, user.accountSequence, dto.communityName) const command = new ApplyCommunityAuthCommand(user.userId, user.accountSequence, dto.communityName)
@ -66,7 +66,7 @@ export class AuthorizationController {
@ApiOperation({ summary: '申请授权省公司' }) @ApiOperation({ summary: '申请授权省公司' })
@ApiResponse({ status: 201, type: ApplyAuthorizationResponse }) @ApiResponse({ status: 201, type: ApplyAuthorizationResponse })
async applyAuthProvinceCompany( async applyAuthProvinceCompany(
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
@Body() dto: ApplyAuthProvinceDto, @Body() dto: ApplyAuthProvinceDto,
): Promise<ApplyAuthorizationResponse> { ): Promise<ApplyAuthorizationResponse> {
const command = new ApplyAuthProvinceCompanyCommand( const command = new ApplyAuthProvinceCompanyCommand(
@ -82,7 +82,7 @@ export class AuthorizationController {
@ApiOperation({ summary: '申请授权市公司' }) @ApiOperation({ summary: '申请授权市公司' })
@ApiResponse({ status: 201, type: ApplyAuthorizationResponse }) @ApiResponse({ status: 201, type: ApplyAuthorizationResponse })
async applyAuthCityCompany( async applyAuthCityCompany(
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
@Body() dto: ApplyAuthCityDto, @Body() dto: ApplyAuthCityDto,
): Promise<ApplyAuthorizationResponse> { ): Promise<ApplyAuthorizationResponse> {
const command = new ApplyAuthCityCompanyCommand(user.userId, user.accountSequence, dto.cityCode, dto.cityName) const command = new ApplyAuthCityCompanyCommand(user.userId, user.accountSequence, dto.cityCode, dto.cityName)
@ -93,7 +93,7 @@ export class AuthorizationController {
@ApiOperation({ summary: '获取我的授权列表' }) @ApiOperation({ summary: '获取我的授权列表' })
@ApiResponse({ status: 200, type: [AuthorizationResponse] }) @ApiResponse({ status: 200, type: [AuthorizationResponse] })
async getMyAuthorizations( async getMyAuthorizations(
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
): Promise<AuthorizationResponse[]> { ): Promise<AuthorizationResponse[]> {
return await this.applicationService.getUserAuthorizations(user.accountSequence) return await this.applicationService.getUserAuthorizations(user.accountSequence)
} }
@ -102,7 +102,7 @@ export class AuthorizationController {
@ApiOperation({ summary: '获取我的社区层级(上级社区和下级社区)' }) @ApiOperation({ summary: '获取我的社区层级(上级社区和下级社区)' })
@ApiResponse({ status: 200, type: CommunityHierarchyResponse }) @ApiResponse({ status: 200, type: CommunityHierarchyResponse })
async getMyCommunityHierarchy( async getMyCommunityHierarchy(
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
): Promise<CommunityHierarchyResponse> { ): Promise<CommunityHierarchyResponse> {
return await this.applicationService.getCommunityHierarchy(user.accountSequence) return await this.applicationService.getCommunityHierarchy(user.accountSequence)
} }
@ -136,7 +136,7 @@ export class AuthorizationController {
@ApiResponse({ status: 204 }) @ApiResponse({ status: 204 })
async revokeAuthorization( async revokeAuthorization(
@Param('id') id: string, @Param('id') id: string,
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
@Body() dto: RevokeAuthorizationDto, @Body() dto: RevokeAuthorizationDto,
): Promise<void> { ): Promise<void> {
const command = new RevokeAuthorizationCommand(id, user.accountSequence, dto.reason) const command = new RevokeAuthorizationCommand(id, user.accountSequence, dto.reason)
@ -150,7 +150,7 @@ export class AuthorizationController {
@ApiResponse({ status: 204 }) @ApiResponse({ status: 204 })
async grantMonthlyBypass( async grantMonthlyBypass(
@Param('id') id: string, @Param('id') id: string,
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
@Body() dto: GrantMonthlyBypassDto, @Body() dto: GrantMonthlyBypassDto,
): Promise<void> { ): Promise<void> {
const command = new GrantMonthlyBypassCommand(id, dto.month, user.accountSequence, dto.reason) const command = new GrantMonthlyBypassCommand(id, dto.month, user.accountSequence, dto.reason)
@ -164,7 +164,7 @@ export class AuthorizationController {
@ApiResponse({ status: 204 }) @ApiResponse({ status: 204 })
async exemptLocalPercentageCheck( async exemptLocalPercentageCheck(
@Param('id') id: string, @Param('id') id: string,
@CurrentUser() user: { userId: string; accountSequence: number }, @CurrentUser() user: { userId: string; accountSequence: string },
): Promise<void> { ): Promise<void> {
const command = new ExemptLocalPercentageCheckCommand(id, user.accountSequence) const command = new ExemptLocalPercentageCheckCommand(id, user.accountSequence)
await this.applicationService.exemptLocalPercentageCheck(command) await this.applicationService.exemptLocalPercentageCheck(command)

View File

@ -26,21 +26,21 @@ export class InternalAuthorizationController {
schema: { schema: {
type: 'object', type: 'object',
properties: { properties: {
accountSequence: { type: 'number', nullable: true }, accountSequence: { type: 'string', nullable: true },
}, },
}, },
}) })
async findNearestCommunity( async findNearestCommunity(
@Query('accountSequence') accountSequence: string, @Query('accountSequence') accountSequence: string,
): Promise<{ accountSequence: number | null }> { ): Promise<{ accountSequence: string | null }> {
this.logger.debug(`[INTERNAL] findNearestCommunity: accountSequence=${accountSequence}`) this.logger.debug(`[INTERNAL] findNearestCommunity: accountSequence=${accountSequence}`)
const result = await this.applicationService.findNearestAuthorizedCommunity( const result = await this.applicationService.findNearestAuthorizedCommunity(
Number(accountSequence), accountSequence,
) )
return { return {
accountSequence: result ? Number(result) : null, accountSequence: result,
} }
} }
@ -58,25 +58,25 @@ export class InternalAuthorizationController {
schema: { schema: {
type: 'object', type: 'object',
properties: { properties: {
accountSequence: { type: 'number', nullable: true }, accountSequence: { type: 'string', nullable: true },
}, },
}, },
}) })
async findNearestProvince( async findNearestProvince(
@Query('accountSequence') accountSequence: string, @Query('accountSequence') accountSequence: string,
@Query('provinceCode') provinceCode: string, @Query('provinceCode') provinceCode: string,
): Promise<{ accountSequence: number | null }> { ): Promise<{ accountSequence: string | null }> {
this.logger.debug( this.logger.debug(
`[INTERNAL] findNearestProvince: accountSequence=${accountSequence}, provinceCode=${provinceCode}`, `[INTERNAL] findNearestProvince: accountSequence=${accountSequence}, provinceCode=${provinceCode}`,
) )
const result = await this.applicationService.findNearestAuthorizedProvince( const result = await this.applicationService.findNearestAuthorizedProvince(
Number(accountSequence), accountSequence,
provinceCode, provinceCode,
) )
return { return {
accountSequence: result ? Number(result) : null, accountSequence: result ? result : null,
} }
} }
@ -94,25 +94,25 @@ export class InternalAuthorizationController {
schema: { schema: {
type: 'object', type: 'object',
properties: { properties: {
accountSequence: { type: 'number', nullable: true }, accountSequence: { type: 'string', nullable: true },
}, },
}, },
}) })
async findNearestCity( async findNearestCity(
@Query('accountSequence') accountSequence: string, @Query('accountSequence') accountSequence: string,
@Query('cityCode') cityCode: string, @Query('cityCode') cityCode: string,
): Promise<{ accountSequence: number | null }> { ): Promise<{ accountSequence: string | null }> {
this.logger.debug( this.logger.debug(
`[INTERNAL] findNearestCity: accountSequence=${accountSequence}, cityCode=${cityCode}`, `[INTERNAL] findNearestCity: accountSequence=${accountSequence}, cityCode=${cityCode}`,
) )
const result = await this.applicationService.findNearestAuthorizedCity( const result = await this.applicationService.findNearestAuthorizedCity(
Number(accountSequence), accountSequence,
cityCode, cityCode,
) )
return { return {
accountSequence: result ? Number(result) : null, accountSequence: result ? result : null,
} }
} }
@ -136,8 +136,8 @@ export class InternalAuthorizationController {
items: { items: {
type: 'object', type: 'object',
properties: { properties: {
accountSequence: { type: 'number', description: '接收者账号' }, accountSequence: { type: 'string', description: '接收者账号' },
treeCount: { type: 'number', description: '分配棵数' }, treeCount: { type: 'string', description: '分配棵数' },
reason: { type: 'string', description: '分配原因' }, reason: { type: 'string', description: '分配原因' },
}, },
}, },
@ -150,7 +150,7 @@ export class InternalAuthorizationController {
@Query('treeCount') treeCount: string, @Query('treeCount') treeCount: string,
): Promise<{ ): Promise<{
distributions: Array<{ distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
}> }>
@ -160,7 +160,7 @@ export class InternalAuthorizationController {
) )
return this.applicationService.getCommunityRewardDistribution( return this.applicationService.getCommunityRewardDistribution(
Number(accountSequence), accountSequence,
Number(treeCount), Number(treeCount),
) )
} }
@ -179,7 +179,7 @@ export class InternalAuthorizationController {
@Query('treeCount') treeCount: string, @Query('treeCount') treeCount: string,
): Promise<{ ): Promise<{
distributions: Array<{ distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
}> }>
@ -189,7 +189,7 @@ export class InternalAuthorizationController {
) )
return this.applicationService.getProvinceTeamRewardDistribution( return this.applicationService.getProvinceTeamRewardDistribution(
Number(accountSequence), accountSequence,
provinceCode, provinceCode,
Number(treeCount), Number(treeCount),
) )
@ -207,7 +207,7 @@ export class InternalAuthorizationController {
@Query('treeCount') treeCount: string, @Query('treeCount') treeCount: string,
): Promise<{ ): Promise<{
distributions: Array<{ distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
isSystemAccount: boolean isSystemAccount: boolean
@ -237,7 +237,7 @@ export class InternalAuthorizationController {
@Query('treeCount') treeCount: string, @Query('treeCount') treeCount: string,
): Promise<{ ): Promise<{
distributions: Array<{ distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
}> }>
@ -247,7 +247,7 @@ export class InternalAuthorizationController {
) )
return this.applicationService.getCityTeamRewardDistribution( return this.applicationService.getCityTeamRewardDistribution(
Number(accountSequence), accountSequence,
cityCode, cityCode,
Number(treeCount), Number(treeCount),
) )
@ -265,7 +265,7 @@ export class InternalAuthorizationController {
@Query('treeCount') treeCount: string, @Query('treeCount') treeCount: string,
): Promise<{ ): Promise<{
distributions: Array<{ distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
isSystemAccount: boolean isSystemAccount: boolean

View File

@ -8,9 +8,9 @@ export class GrantAuthCityCompanyDto {
userId: string userId: string
@ApiProperty({ description: '账户序列号' }) @ApiProperty({ description: '账户序列号' })
@IsNumber() @IsString()
@IsNotEmpty({ message: '账户序列号不能为空' }) @IsNotEmpty({ message: '账户序列号不能为空' })
accountSequence: number accountSequence: string
@ApiProperty({ description: '城市代码', example: '430100' }) @ApiProperty({ description: '城市代码', example: '430100' })
@IsString() @IsString()

View File

@ -8,9 +8,9 @@ export class GrantAuthProvinceCompanyDto {
userId: string userId: string
@ApiProperty({ description: '账户序列号' }) @ApiProperty({ description: '账户序列号' })
@IsNumber() @IsString()
@IsNotEmpty({ message: '账户序列号不能为空' }) @IsNotEmpty({ message: '账户序列号不能为空' })
accountSequence: number accountSequence: string
@ApiProperty({ description: '省份代码', example: '430000' }) @ApiProperty({ description: '省份代码', example: '430000' })
@IsString() @IsString()

View File

@ -8,9 +8,9 @@ export class GrantCityCompanyDto {
userId: string userId: string
@ApiProperty({ description: '账户序列号' }) @ApiProperty({ description: '账户序列号' })
@IsNumber() @IsString()
@IsNotEmpty({ message: '账户序列号不能为空' }) @IsNotEmpty({ message: '账户序列号不能为空' })
accountSequence: number accountSequence: string
@ApiProperty({ description: '城市代码', example: '430100' }) @ApiProperty({ description: '城市代码', example: '430100' })
@IsString() @IsString()

View File

@ -8,9 +8,9 @@ export class GrantCommunityDto {
userId: string userId: string
@ApiProperty({ description: '账户序列号' }) @ApiProperty({ description: '账户序列号' })
@IsNumber() @IsString()
@IsNotEmpty({ message: '账户序列号不能为空' }) @IsNotEmpty({ message: '账户序列号不能为空' })
accountSequence: number accountSequence: string
@ApiProperty({ description: '社区名称', example: '深圳社区' }) @ApiProperty({ description: '社区名称', example: '深圳社区' })
@IsString() @IsString()

View File

@ -8,9 +8,9 @@ export class GrantProvinceCompanyDto {
userId: string userId: string
@ApiProperty({ description: '账户序列号' }) @ApiProperty({ description: '账户序列号' })
@IsNumber() @IsString()
@IsNotEmpty({ message: '账户序列号不能为空' }) @IsNotEmpty({ message: '账户序列号不能为空' })
accountSequence: number accountSequence: string
@ApiProperty({ description: '省份代码', example: '430000' }) @ApiProperty({ description: '省份代码', example: '430000' })
@IsString() @IsString()

View File

@ -8,7 +8,7 @@ export class CommunityInfo {
authorizationId: string authorizationId: string
@ApiProperty({ description: '账户序列号' }) @ApiProperty({ description: '账户序列号' })
accountSequence: number accountSequence: string
@ApiProperty({ description: '社区名称' }) @ApiProperty({ description: '社区名称' })
communityName: string communityName: string
@ -45,7 +45,7 @@ export class CommunityHierarchyResponse {
*/ */
export const HEADQUARTERS_COMMUNITY: CommunityInfo = { export const HEADQUARTERS_COMMUNITY: CommunityInfo = {
authorizationId: 'headquarters', authorizationId: 'headquarters',
accountSequence: 0, accountSequence: '',
communityName: '总部社区', communityName: '总部社区',
userId: undefined, userId: undefined,
isHeadquarters: true, isHeadquarters: true,

View File

@ -1,7 +1,7 @@
export class ApplyAuthCityCompanyCommand { export class ApplyAuthCityCompanyCommand {
constructor( constructor(
public readonly userId: string, public readonly userId: string,
public readonly accountSequence: number, public readonly accountSequence: string,
public readonly cityCode: string, public readonly cityCode: string,
public readonly cityName: string, public readonly cityName: string,
) {} ) {}

View File

@ -1,7 +1,7 @@
export class ApplyAuthProvinceCompanyCommand { export class ApplyAuthProvinceCompanyCommand {
constructor( constructor(
public readonly userId: string, public readonly userId: string,
public readonly accountSequence: number, public readonly accountSequence: string,
public readonly provinceCode: string, public readonly provinceCode: string,
public readonly provinceName: string, public readonly provinceName: string,
) {} ) {}

View File

@ -1,7 +1,7 @@
export class ApplyCommunityAuthCommand { export class ApplyCommunityAuthCommand {
constructor( constructor(
public readonly userId: string, public readonly userId: string,
public readonly accountSequence: number, public readonly accountSequence: string,
public readonly communityName: string, public readonly communityName: string,
) {} ) {}
} }

View File

@ -1,6 +1,6 @@
export class ExemptLocalPercentageCheckCommand { export class ExemptLocalPercentageCheckCommand {
constructor( constructor(
public readonly authorizationId: string, public readonly authorizationId: string,
public readonly adminAccountSequence: number, public readonly adminAccountSequence: string,
) {} ) {}
} }

View File

@ -1,11 +1,11 @@
export class GrantAuthCityCompanyCommand { export class GrantAuthCityCompanyCommand {
constructor( constructor(
public readonly userId: string, public readonly userId: string,
public readonly accountSequence: number, public readonly accountSequence: string,
public readonly cityCode: string, public readonly cityCode: string,
public readonly cityName: string, public readonly cityName: string,
public readonly adminId: string, public readonly adminId: string,
public readonly adminAccountSequence: number, public readonly adminAccountSequence: string,
public readonly skipAssessment: boolean = false, public readonly skipAssessment: boolean = false,
) {} ) {}
} }

View File

@ -1,11 +1,11 @@
export class GrantAuthProvinceCompanyCommand { export class GrantAuthProvinceCompanyCommand {
constructor( constructor(
public readonly userId: string, public readonly userId: string,
public readonly accountSequence: number, public readonly accountSequence: string,
public readonly provinceCode: string, public readonly provinceCode: string,
public readonly provinceName: string, public readonly provinceName: string,
public readonly adminId: string, public readonly adminId: string,
public readonly adminAccountSequence: number, public readonly adminAccountSequence: string,
public readonly skipAssessment: boolean = false, public readonly skipAssessment: boolean = false,
) {} ) {}
} }

View File

@ -1,11 +1,11 @@
export class GrantCityCompanyCommand { export class GrantCityCompanyCommand {
constructor( constructor(
public readonly userId: string, public readonly userId: string,
public readonly accountSequence: number, public readonly accountSequence: string,
public readonly cityCode: string, public readonly cityCode: string,
public readonly cityName: string, public readonly cityName: string,
public readonly adminId: string, public readonly adminId: string,
public readonly adminAccountSequence: number, public readonly adminAccountSequence: string,
public readonly skipAssessment: boolean = false, public readonly skipAssessment: boolean = false,
) {} ) {}
} }

View File

@ -1,10 +1,10 @@
export class GrantCommunityCommand { export class GrantCommunityCommand {
constructor( constructor(
public readonly userId: string, public readonly userId: string,
public readonly accountSequence: number, public readonly accountSequence: string,
public readonly communityName: string, public readonly communityName: string,
public readonly adminId: string, public readonly adminId: string,
public readonly adminAccountSequence: number, public readonly adminAccountSequence: string,
public readonly skipAssessment: boolean = false, public readonly skipAssessment: boolean = false,
) {} ) {}
} }

View File

@ -2,7 +2,7 @@ export class GrantMonthlyBypassCommand {
constructor( constructor(
public readonly authorizationId: string, public readonly authorizationId: string,
public readonly month: string, public readonly month: string,
public readonly adminAccountSequence: number, public readonly adminAccountSequence: string,
public readonly reason?: string, public readonly reason?: string,
) {} ) {}
} }

View File

@ -1,11 +1,11 @@
export class GrantProvinceCompanyCommand { export class GrantProvinceCompanyCommand {
constructor( constructor(
public readonly userId: string, public readonly userId: string,
public readonly accountSequence: number, public readonly accountSequence: string,
public readonly provinceCode: string, public readonly provinceCode: string,
public readonly provinceName: string, public readonly provinceName: string,
public readonly adminId: string, public readonly adminId: string,
public readonly adminAccountSequence: number, public readonly adminAccountSequence: string,
public readonly skipAssessment: boolean = false, public readonly skipAssessment: boolean = false,
) {} ) {}
} }

View File

@ -1,7 +1,7 @@
export class RevokeAuthorizationCommand { export class RevokeAuthorizationCommand {
constructor( constructor(
public readonly authorizationId: string, public readonly authorizationId: string,
public readonly adminAccountSequence: number, public readonly adminAccountSequence: string,
public readonly reason: string, public readonly reason: string,
) {} ) {}
} }

View File

@ -3,7 +3,7 @@
*/ */
export interface CommunityInfoDTO { export interface CommunityInfoDTO {
authorizationId: string authorizationId: string
accountSequence: number accountSequence: string
communityName: string communityName: string
userId?: string userId?: string
isHeadquarters: boolean isHeadquarters: boolean

View File

@ -456,13 +456,13 @@ export class AuthorizationApplicationService {
/** /**
* *
*/ */
async getUserAuthorizations(accountSequence: number): Promise<AuthorizationDTO[]> { async getUserAuthorizations(accountSequence: string): Promise<AuthorizationDTO[]> {
const authorizations = await this.authorizationRepository.findByAccountSequence( const authorizations = await this.authorizationRepository.findByAccountSequence(
BigInt(accountSequence), accountSequence,
) )
// 查询用户团队统计数据 // 查询用户团队统计数据
const teamStats = await this.statsRepository.findByAccountSequence(BigInt(accountSequence)) const teamStats = await this.statsRepository.findByAccountSequence(accountSequence)
const currentTreeCount = teamStats?.totalTeamPlantingCount || 0 const currentTreeCount = teamStats?.totalTeamPlantingCount || 0
return authorizations.map((auth) => this.toAuthorizationDTO(auth, currentTreeCount)) return authorizations.map((auth) => this.toAuthorizationDTO(auth, currentTreeCount))
@ -613,24 +613,24 @@ export class AuthorizationApplicationService {
* - parentCommunity: 上级社区沿 * - parentCommunity: 上级社区沿
* - childCommunities: 下级社区 * - childCommunities: 下级社区
*/ */
async getCommunityHierarchy(accountSequence: number): Promise<CommunityHierarchyDTO> { async getCommunityHierarchy(accountSequence: string): Promise<CommunityHierarchyDTO> {
this.logger.debug(`[getCommunityHierarchy] accountSequence=${accountSequence}`) this.logger.debug(`[getCommunityHierarchy] accountSequence=${accountSequence}`)
// 1. 查询我的社区授权 // 1. 查询我的社区授权
const myCommunity = await this.authorizationRepository.findByAccountSequenceAndRoleType( const myCommunity = await this.authorizationRepository.findByAccountSequenceAndRoleType(
BigInt(accountSequence), accountSequence,
RoleType.COMMUNITY, RoleType.COMMUNITY,
) )
// 2. 获取我的祖先链(推荐链) // 2. 获取我的祖先链(推荐链)
const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(BigInt(accountSequence)) const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(accountSequence)
this.logger.debug(`[getCommunityHierarchy] ancestorPath: ${ancestorAccountSequences.join(',')}`) this.logger.debug(`[getCommunityHierarchy] ancestorPath: ${ancestorAccountSequences.join(',')}`)
// 3. 查找上级社区(在祖先链中找最近的有社区授权的用户) // 3. 查找上级社区(在祖先链中找最近的有社区授权的用户)
let parentCommunityAuth: AuthorizationRole | null = null let parentCommunityAuth: AuthorizationRole | null = null
if (ancestorAccountSequences.length > 0) { if (ancestorAccountSequences.length > 0) {
const ancestorCommunities = await this.authorizationRepository.findActiveCommunityByAccountSequences( const ancestorCommunities = await this.authorizationRepository.findActiveCommunityByAccountSequences(
ancestorAccountSequences.map((seq) => BigInt(seq)), ancestorAccountSequences,
) )
// 找最近的ancestorAccountSequences 是从直接推荐人到根节点的顺序) // 找最近的ancestorAccountSequences 是从直接推荐人到根节点的顺序)
@ -638,7 +638,7 @@ export class AuthorizationApplicationService {
// 按祖先链顺序找第一个匹配的 // 按祖先链顺序找第一个匹配的
for (const ancestorSeq of ancestorAccountSequences) { for (const ancestorSeq of ancestorAccountSequences) {
const found = ancestorCommunities.find( const found = ancestorCommunities.find(
(auth) => Number(auth.userId.accountSequence) === ancestorSeq, (auth) => auth.userId.accountSequence === ancestorSeq,
) )
if (found) { if (found) {
parentCommunityAuth = found parentCommunityAuth = found
@ -649,7 +649,7 @@ export class AuthorizationApplicationService {
} }
// 4. 获取我的团队成员 // 4. 获取我的团队成员
const teamMemberAccountSequences = await this.referralServiceClient.getTeamMembers(BigInt(accountSequence)) const teamMemberAccountSequences = await this.referralServiceClient.getTeamMembers(accountSequence)
this.logger.debug(`[getCommunityHierarchy] teamMembers count: ${teamMemberAccountSequences.length}`) this.logger.debug(`[getCommunityHierarchy] teamMembers count: ${teamMemberAccountSequences.length}`)
// 5. 查找下级社区(在团队成员中找最近的有社区授权的用户) // 5. 查找下级社区(在团队成员中找最近的有社区授权的用户)
@ -658,7 +658,7 @@ export class AuthorizationApplicationService {
let childCommunityAuths: AuthorizationRole[] = [] let childCommunityAuths: AuthorizationRole[] = []
if (teamMemberAccountSequences.length > 0) { if (teamMemberAccountSequences.length > 0) {
const teamCommunities = await this.authorizationRepository.findActiveCommunityByAccountSequences( const teamCommunities = await this.authorizationRepository.findActiveCommunityByAccountSequences(
teamMemberAccountSequences.map((seq) => BigInt(seq)), teamMemberAccountSequences,
) )
// 只保留"最近的"下级社区 // 只保留"最近的"下级社区
@ -667,7 +667,7 @@ export class AuthorizationApplicationService {
// 但按用户要求"只计算最近的那个",这里需要做过滤 // 但按用户要求"只计算最近的那个",这里需要做过滤
// 算法:如果某个社区 A 的祖先中有另一个社区 B 也在团队中,则 A 不是最近的 // 算法:如果某个社区 A 的祖先中有另一个社区 B 也在团队中,则 A 不是最近的
const communityAccountSeqs = new Set(teamCommunities.map((c) => Number(c.userId.accountSequence))) const communityAccountSeqs = new Set(teamCommunities.map((c) => c.userId.accountSequence))
for (const comm of teamCommunities) { for (const comm of teamCommunities) {
// 获取这个社区成员的祖先链 // 获取这个社区成员的祖先链
@ -696,7 +696,7 @@ export class AuthorizationApplicationService {
// 6. 构建响应 // 6. 构建响应
const HEADQUARTERS_COMMUNITY = { const HEADQUARTERS_COMMUNITY = {
authorizationId: 'headquarters', authorizationId: 'headquarters',
accountSequence: 0, accountSequence: '0',
communityName: '总部社区', communityName: '总部社区',
userId: undefined, userId: undefined,
isHeadquarters: true, isHeadquarters: true,
@ -706,7 +706,7 @@ export class AuthorizationApplicationService {
myCommunity: myCommunity && myCommunity.status === AuthorizationStatus.AUTHORIZED myCommunity: myCommunity && myCommunity.status === AuthorizationStatus.AUTHORIZED
? { ? {
authorizationId: myCommunity.authorizationId.value, authorizationId: myCommunity.authorizationId.value,
accountSequence: Number(myCommunity.userId.accountSequence), accountSequence: myCommunity.userId.accountSequence,
communityName: myCommunity.displayTitle, communityName: myCommunity.displayTitle,
userId: myCommunity.userId.value, userId: myCommunity.userId.value,
isHeadquarters: false, isHeadquarters: false,
@ -715,7 +715,7 @@ export class AuthorizationApplicationService {
parentCommunity: parentCommunityAuth parentCommunity: parentCommunityAuth
? { ? {
authorizationId: parentCommunityAuth.authorizationId.value, authorizationId: parentCommunityAuth.authorizationId.value,
accountSequence: Number(parentCommunityAuth.userId.accountSequence), accountSequence: parentCommunityAuth.userId.accountSequence,
communityName: parentCommunityAuth.displayTitle, communityName: parentCommunityAuth.displayTitle,
userId: parentCommunityAuth.userId.value, userId: parentCommunityAuth.userId.value,
isHeadquarters: false, isHeadquarters: false,
@ -723,7 +723,7 @@ export class AuthorizationApplicationService {
: HEADQUARTERS_COMMUNITY, : HEADQUARTERS_COMMUNITY,
childCommunities: childCommunityAuths.map((auth) => ({ childCommunities: childCommunityAuths.map((auth) => ({
authorizationId: auth.authorizationId.value, authorizationId: auth.authorizationId.value,
accountSequence: Number(auth.userId.accountSequence), accountSequence: auth.userId.accountSequence,
communityName: auth.displayTitle, communityName: auth.displayTitle,
userId: auth.userId.value, userId: auth.userId.value,
isHeadquarters: false, isHeadquarters: false,
@ -738,11 +738,11 @@ export class AuthorizationApplicationService {
* reward-service * reward-service
* @returns accountSequence of nearest community authorization holder, or null * @returns accountSequence of nearest community authorization holder, or null
*/ */
async findNearestAuthorizedCommunity(accountSequence: number): Promise<bigint | null> { async findNearestAuthorizedCommunity(accountSequence: string): Promise<string | null> {
this.logger.debug(`[findNearestAuthorizedCommunity] accountSequence=${accountSequence}`) this.logger.debug(`[findNearestAuthorizedCommunity] accountSequence=${accountSequence}`)
// 获取用户的祖先链(推荐链) // 获取用户的祖先链(推荐链)
const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(BigInt(accountSequence)) const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(accountSequence)
if (ancestorAccountSequences.length === 0) { if (ancestorAccountSequences.length === 0) {
return null return null
@ -750,7 +750,7 @@ export class AuthorizationApplicationService {
// 在祖先链中找最近的有社区授权的用户 // 在祖先链中找最近的有社区授权的用户
const ancestorCommunities = await this.authorizationRepository.findActiveCommunityByAccountSequences( const ancestorCommunities = await this.authorizationRepository.findActiveCommunityByAccountSequences(
ancestorAccountSequences.map((seq) => BigInt(seq)), ancestorAccountSequences,
) )
if (ancestorCommunities.length === 0) { if (ancestorCommunities.length === 0) {
@ -760,7 +760,7 @@ export class AuthorizationApplicationService {
// 按祖先链顺序找第一个匹配的 // 按祖先链顺序找第一个匹配的
for (const ancestorSeq of ancestorAccountSequences) { for (const ancestorSeq of ancestorAccountSequences) {
const found = ancestorCommunities.find( const found = ancestorCommunities.find(
(auth) => Number(auth.userId.accountSequence) === ancestorSeq, (auth) => auth.userId.accountSequence === ancestorSeq,
) )
if (found) { if (found) {
return found.userId.accountSequence return found.userId.accountSequence
@ -776,15 +776,15 @@ export class AuthorizationApplicationService {
* @returns accountSequence of nearest province authorization holder, or null * @returns accountSequence of nearest province authorization holder, or null
*/ */
async findNearestAuthorizedProvince( async findNearestAuthorizedProvince(
accountSequence: number, accountSequence: string,
provinceCode: string, provinceCode: string,
): Promise<bigint | null> { ): Promise<string | null> {
this.logger.debug( this.logger.debug(
`[findNearestAuthorizedProvince] accountSequence=${accountSequence}, provinceCode=${provinceCode}`, `[findNearestAuthorizedProvince] accountSequence=${accountSequence}, provinceCode=${provinceCode}`,
) )
// 获取用户的祖先链(推荐链) // 获取用户的祖先链(推荐链)
const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(BigInt(accountSequence)) const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(accountSequence)
if (ancestorAccountSequences.length === 0) { if (ancestorAccountSequences.length === 0) {
return null return null
@ -792,7 +792,7 @@ export class AuthorizationApplicationService {
// 在祖先链中找最近的有省公司授权且匹配省份代码的用户 // 在祖先链中找最近的有省公司授权且匹配省份代码的用户
const ancestorProvinces = await this.authorizationRepository.findActiveProvinceByAccountSequencesAndRegion( const ancestorProvinces = await this.authorizationRepository.findActiveProvinceByAccountSequencesAndRegion(
ancestorAccountSequences.map((seq) => BigInt(seq)), ancestorAccountSequences,
provinceCode, provinceCode,
) )
@ -803,7 +803,7 @@ export class AuthorizationApplicationService {
// 按祖先链顺序找第一个匹配的 // 按祖先链顺序找第一个匹配的
for (const ancestorSeq of ancestorAccountSequences) { for (const ancestorSeq of ancestorAccountSequences) {
const found = ancestorProvinces.find( const found = ancestorProvinces.find(
(auth) => Number(auth.userId.accountSequence) === ancestorSeq, (auth) => auth.userId.accountSequence === ancestorSeq,
) )
if (found) { if (found) {
return found.userId.accountSequence return found.userId.accountSequence
@ -819,15 +819,15 @@ export class AuthorizationApplicationService {
* @returns accountSequence of nearest city authorization holder, or null * @returns accountSequence of nearest city authorization holder, or null
*/ */
async findNearestAuthorizedCity( async findNearestAuthorizedCity(
accountSequence: number, accountSequence: string,
cityCode: string, cityCode: string,
): Promise<bigint | null> { ): Promise<string | null> {
this.logger.debug( this.logger.debug(
`[findNearestAuthorizedCity] accountSequence=${accountSequence}, cityCode=${cityCode}`, `[findNearestAuthorizedCity] accountSequence=${accountSequence}, cityCode=${cityCode}`,
) )
// 获取用户的祖先链(推荐链) // 获取用户的祖先链(推荐链)
const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(BigInt(accountSequence)) const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(accountSequence)
if (ancestorAccountSequences.length === 0) { if (ancestorAccountSequences.length === 0) {
return null return null
@ -835,7 +835,7 @@ export class AuthorizationApplicationService {
// 在祖先链中找最近的有市公司授权且匹配城市代码的用户 // 在祖先链中找最近的有市公司授权且匹配城市代码的用户
const ancestorCities = await this.authorizationRepository.findActiveCityByAccountSequencesAndRegion( const ancestorCities = await this.authorizationRepository.findActiveCityByAccountSequencesAndRegion(
ancestorAccountSequences.map((seq) => BigInt(seq)), ancestorAccountSequences,
cityCode, cityCode,
) )
@ -846,7 +846,7 @@ export class AuthorizationApplicationService {
// 按祖先链顺序找第一个匹配的 // 按祖先链顺序找第一个匹配的
for (const ancestorSeq of ancestorAccountSequences) { for (const ancestorSeq of ancestorAccountSequences) {
const found = ancestorCities.find( const found = ancestorCities.find(
(auth) => Number(auth.userId.accountSequence) === ancestorSeq, (auth) => auth.userId.accountSequence === ancestorSeq,
) )
if (found) { if (found) {
return found.userId.accountSequence return found.userId.accountSequence
@ -890,11 +890,11 @@ export class AuthorizationApplicationService {
* 4. * 4.
*/ */
async getCommunityRewardDistribution( async getCommunityRewardDistribution(
accountSequence: number, accountSequence: string,
treeCount: number, treeCount: number,
): Promise<{ ): Promise<{
distributions: Array<{ distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
}> }>
@ -903,10 +903,10 @@ export class AuthorizationApplicationService {
`[getCommunityRewardDistribution] accountSequence=${accountSequence}, treeCount=${treeCount}`, `[getCommunityRewardDistribution] accountSequence=${accountSequence}, treeCount=${treeCount}`,
) )
const HEADQUARTERS_ACCOUNT_SEQUENCE = 1 // 总部社区账号 const HEADQUARTERS_ACCOUNT_SEQUENCE = '1' // 总部社区账号
// 1. 获取用户的祖先链(推荐链) // 1. 获取用户的祖先链(推荐链)
const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(BigInt(accountSequence)) const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(accountSequence)
if (ancestorAccountSequences.length === 0) { if (ancestorAccountSequences.length === 0) {
// 无推荐链,全部给总部 // 无推荐链,全部给总部
@ -923,7 +923,7 @@ export class AuthorizationApplicationService {
// 2. 查找祖先链中所有社区授权(包括 benefitActive=false 的) // 2. 查找祖先链中所有社区授权(包括 benefitActive=false 的)
const ancestorCommunities = await this.authorizationRepository.findCommunityByAccountSequences( const ancestorCommunities = await this.authorizationRepository.findCommunityByAccountSequences(
ancestorAccountSequences.map((seq) => BigInt(seq)), ancestorAccountSequences,
) )
if (ancestorCommunities.length === 0) { if (ancestorCommunities.length === 0) {
@ -946,7 +946,7 @@ export class AuthorizationApplicationService {
for (let i = 0; i < ancestorAccountSequences.length; i++) { for (let i = 0; i < ancestorAccountSequences.length; i++) {
const ancestorSeq = ancestorAccountSequences[i] const ancestorSeq = ancestorAccountSequences[i]
const found = ancestorCommunities.find( const found = ancestorCommunities.find(
(auth) => Number(auth.userId.accountSequence) === ancestorSeq, (auth) => auth.userId.accountSequence === ancestorSeq,
) )
if (found) { if (found) {
nearestCommunity = found nearestCommunity = found
@ -974,7 +974,7 @@ export class AuthorizationApplicationService {
return { return {
distributions: [ distributions: [
{ {
accountSequence: Number(nearestCommunity.userId.accountSequence), accountSequence: nearestCommunity.userId.accountSequence,
treeCount, treeCount,
reason: '社区权益已激活', reason: '社区权益已激活',
}, },
@ -1004,17 +1004,17 @@ export class AuthorizationApplicationService {
) )
// 6. 查找上级社区(用于接收考核前的权益) // 6. 查找上级社区(用于接收考核前的权益)
let parentCommunityAccountSequence: number = HEADQUARTERS_ACCOUNT_SEQUENCE let parentCommunityAccountSequence: string = HEADQUARTERS_ACCOUNT_SEQUENCE
let parentCommunityReason = '上级为总部社区' let parentCommunityReason = '上级为总部社区'
// 从最近社区之后继续查找上级社区 // 从最近社区之后继续查找上级社区
for (let i = nearestCommunityIndex + 1; i < ancestorAccountSequences.length; i++) { for (let i = nearestCommunityIndex + 1; i < ancestorAccountSequences.length; i++) {
const ancestorSeq = ancestorAccountSequences[i] const ancestorSeq = ancestorAccountSequences[i]
const found = ancestorCommunities.find( const found = ancestorCommunities.find(
(auth) => Number(auth.userId.accountSequence) === ancestorSeq && auth.benefitActive, (auth) => auth.userId.accountSequence === ancestorSeq && auth.benefitActive,
) )
if (found) { if (found) {
parentCommunityAccountSequence = Number(found.userId.accountSequence) parentCommunityAccountSequence = found.userId.accountSequence
parentCommunityReason = '上级社区权益已激活' parentCommunityReason = '上级社区权益已激活'
break break
} }
@ -1022,7 +1022,7 @@ export class AuthorizationApplicationService {
// 7. 计算分配方案 // 7. 计算分配方案
const distributions: Array<{ const distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
}> = [] }> = []
@ -1031,7 +1031,7 @@ export class AuthorizationApplicationService {
// 已达标但权益未激活(可能是月度考核失败),全部给该社区 // 已达标但权益未激活(可能是月度考核失败),全部给该社区
// 注:这种情况下应该由系统自动激活权益,但这里作为兜底处理 // 注:这种情况下应该由系统自动激活权益,但这里作为兜底处理
distributions.push({ distributions.push({
accountSequence: Number(nearestCommunity.userId.accountSequence), accountSequence: nearestCommunity.userId.accountSequence,
treeCount, treeCount,
reason: '已达初始考核目标', reason: '已达初始考核目标',
}) })
@ -1066,7 +1066,7 @@ export class AuthorizationApplicationService {
const afterTargetCount = treeCount - remaining const afterTargetCount = treeCount - remaining
if (afterTargetCount > 0) { if (afterTargetCount > 0) {
distributions.push({ distributions.push({
accountSequence: Number(nearestCommunity.userId.accountSequence), accountSequence: nearestCommunity.userId.accountSequence,
treeCount: afterTargetCount, treeCount: afterTargetCount,
reason: `考核达标后权益生效`, reason: `考核达标后权益生效`,
}) })
@ -1097,12 +1097,12 @@ export class AuthorizationApplicationService {
* 4. * 4.
*/ */
async getProvinceTeamRewardDistribution( async getProvinceTeamRewardDistribution(
accountSequence: number, accountSequence: string,
provinceCode: string, provinceCode: string,
treeCount: number, treeCount: number,
): Promise<{ ): Promise<{
distributions: Array<{ distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
}> }>
@ -1111,10 +1111,10 @@ export class AuthorizationApplicationService {
`[getProvinceTeamRewardDistribution] accountSequence=${accountSequence}, provinceCode=${provinceCode}, treeCount=${treeCount}`, `[getProvinceTeamRewardDistribution] accountSequence=${accountSequence}, provinceCode=${provinceCode}, treeCount=${treeCount}`,
) )
const HEADQUARTERS_ACCOUNT_SEQUENCE = 1 const HEADQUARTERS_ACCOUNT_SEQUENCE = '1'
// 1. 获取用户的祖先链 // 1. 获取用户的祖先链
const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(BigInt(accountSequence)) const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(accountSequence)
if (ancestorAccountSequences.length === 0) { if (ancestorAccountSequences.length === 0) {
return { return {
@ -1127,7 +1127,7 @@ export class AuthorizationApplicationService {
// 2. 查找祖先链中所有授权省公司(包括 benefitActive=false // 2. 查找祖先链中所有授权省公司(包括 benefitActive=false
// 注意:省团队收益不再要求省份匹配,只要推荐链上有省团队授权即可获得收益 // 注意:省团队收益不再要求省份匹配,只要推荐链上有省团队授权即可获得收益
const ancestorAuthProvinces = await this.authorizationRepository.findAuthProvinceByAccountSequences( const ancestorAuthProvinces = await this.authorizationRepository.findAuthProvinceByAccountSequences(
ancestorAccountSequences.map((seq) => BigInt(seq)), ancestorAccountSequences,
) )
if (ancestorAuthProvinces.length === 0) { if (ancestorAuthProvinces.length === 0) {
@ -1145,7 +1145,7 @@ export class AuthorizationApplicationService {
for (let i = 0; i < ancestorAccountSequences.length; i++) { for (let i = 0; i < ancestorAccountSequences.length; i++) {
const ancestorSeq = ancestorAccountSequences[i] const ancestorSeq = ancestorAccountSequences[i]
const found = ancestorAuthProvinces.find( const found = ancestorAuthProvinces.find(
(auth) => Number(auth.userId.accountSequence) === ancestorSeq, (auth) => auth.userId.accountSequence === ancestorSeq,
) )
if (found) { if (found) {
nearestAuthProvince = found nearestAuthProvince = found
@ -1166,7 +1166,7 @@ export class AuthorizationApplicationService {
if (nearestAuthProvince.benefitActive) { if (nearestAuthProvince.benefitActive) {
return { return {
distributions: [ distributions: [
{ accountSequence: Number(nearestAuthProvince.userId.accountSequence), treeCount, reason: '省团队权益已激活' }, { accountSequence: nearestAuthProvince.userId.accountSequence, treeCount, reason: '省团队权益已激活' },
], ],
} }
} }
@ -1183,27 +1183,27 @@ export class AuthorizationApplicationService {
) )
// 6. 查找上级(用于接收考核前的权益) // 6. 查找上级(用于接收考核前的权益)
let parentAccountSequence: number = HEADQUARTERS_ACCOUNT_SEQUENCE let parentAccountSequence: string = HEADQUARTERS_ACCOUNT_SEQUENCE
let parentReason = '上级为总部社区' let parentReason = '上级为总部社区'
for (let i = nearestIndex + 1; i < ancestorAccountSequences.length; i++) { for (let i = nearestIndex + 1; i < ancestorAccountSequences.length; i++) {
const ancestorSeq = ancestorAccountSequences[i] const ancestorSeq = ancestorAccountSequences[i]
const found = ancestorAuthProvinces.find( const found = ancestorAuthProvinces.find(
(auth) => Number(auth.userId.accountSequence) === ancestorSeq && auth.benefitActive, (auth) => auth.userId.accountSequence === ancestorSeq && auth.benefitActive,
) )
if (found) { if (found) {
parentAccountSequence = Number(found.userId.accountSequence) parentAccountSequence = found.userId.accountSequence
parentReason = '上级授权省公司权益已激活' parentReason = '上级授权省公司权益已激活'
break break
} }
} }
// 7. 计算分配 // 7. 计算分配
const distributions: Array<{ accountSequence: number; treeCount: number; reason: string }> = [] const distributions: Array<{ accountSequence: string; treeCount: number; reason: string }> = []
if (currentTeamCount >= initialTarget) { if (currentTeamCount >= initialTarget) {
distributions.push({ distributions.push({
accountSequence: Number(nearestAuthProvince.userId.accountSequence), accountSequence: nearestAuthProvince.userId.accountSequence,
treeCount, treeCount,
reason: '已达初始考核目标', reason: '已达初始考核目标',
}) })
@ -1235,7 +1235,7 @@ export class AuthorizationApplicationService {
const afterTargetCount = treeCount - remaining const afterTargetCount = treeCount - remaining
if (afterTargetCount > 0) { if (afterTargetCount > 0) {
distributions.push({ distributions.push({
accountSequence: Number(nearestAuthProvince.userId.accountSequence), accountSequence: nearestAuthProvince.userId.accountSequence,
treeCount: afterTargetCount, treeCount: afterTargetCount,
reason: '考核达标后权益生效', reason: '考核达标后权益生效',
}) })
@ -1266,7 +1266,7 @@ export class AuthorizationApplicationService {
treeCount: number, treeCount: number,
): Promise<{ ): Promise<{
distributions: Array<{ distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
isSystemAccount: boolean isSystemAccount: boolean
@ -1277,7 +1277,7 @@ export class AuthorizationApplicationService {
) )
// 系统省账户ID格式: 9 + 省份代码 // 系统省账户ID格式: 9 + 省份代码
const systemProvinceAccountId = Number(`9${provinceCode.padStart(6, '0')}`) const systemProvinceAccountId = `9${provinceCode.padStart(6, '0')}`
// 查找该省份的正式省公司 // 查找该省份的正式省公司
const provinceCompany = await this.authorizationRepository.findProvinceCompanyByRegion(provinceCode) const provinceCompany = await this.authorizationRepository.findProvinceCompanyByRegion(provinceCode)
@ -1301,7 +1301,7 @@ export class AuthorizationApplicationService {
return { return {
distributions: [ distributions: [
{ {
accountSequence: Number(provinceCompany.userId.accountSequence), accountSequence: provinceCompany.userId.accountSequence,
treeCount, treeCount,
reason: '省区域权益已激活', reason: '省区域权益已激活',
isSystemAccount: false, isSystemAccount: false,
@ -1322,7 +1322,7 @@ export class AuthorizationApplicationService {
) )
const distributions: Array<{ const distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
isSystemAccount: boolean isSystemAccount: boolean
@ -1331,7 +1331,7 @@ export class AuthorizationApplicationService {
if (currentTeamCount >= initialTarget) { if (currentTeamCount >= initialTarget) {
// 已达标但权益未激活,全部给该省公司 // 已达标但权益未激活,全部给该省公司
distributions.push({ distributions.push({
accountSequence: Number(provinceCompany.userId.accountSequence), accountSequence: provinceCompany.userId.accountSequence,
treeCount, treeCount,
reason: '已达初始考核目标', reason: '已达初始考核目标',
isSystemAccount: false, isSystemAccount: false,
@ -1366,7 +1366,7 @@ export class AuthorizationApplicationService {
const afterTargetCount = treeCount - remaining const afterTargetCount = treeCount - remaining
if (afterTargetCount > 0) { if (afterTargetCount > 0) {
distributions.push({ distributions.push({
accountSequence: Number(provinceCompany.userId.accountSequence), accountSequence: provinceCompany.userId.accountSequence,
treeCount: afterTargetCount, treeCount: afterTargetCount,
reason: '考核达标后权益生效', reason: '考核达标后权益生效',
isSystemAccount: false, isSystemAccount: false,
@ -1394,12 +1394,12 @@ export class AuthorizationApplicationService {
* 4. * 4.
*/ */
async getCityTeamRewardDistribution( async getCityTeamRewardDistribution(
accountSequence: number, accountSequence: string,
cityCode: string, cityCode: string,
treeCount: number, treeCount: number,
): Promise<{ ): Promise<{
distributions: Array<{ distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
}> }>
@ -1408,10 +1408,10 @@ export class AuthorizationApplicationService {
`[getCityTeamRewardDistribution] accountSequence=${accountSequence}, cityCode=${cityCode}, treeCount=${treeCount}`, `[getCityTeamRewardDistribution] accountSequence=${accountSequence}, cityCode=${cityCode}, treeCount=${treeCount}`,
) )
const HEADQUARTERS_ACCOUNT_SEQUENCE = 1 const HEADQUARTERS_ACCOUNT_SEQUENCE = '1'
// 1. 获取用户的祖先链 // 1. 获取用户的祖先链
const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(BigInt(accountSequence)) const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(accountSequence)
if (ancestorAccountSequences.length === 0) { if (ancestorAccountSequences.length === 0) {
return { return {
@ -1424,7 +1424,7 @@ export class AuthorizationApplicationService {
// 2. 查找祖先链中所有授权市公司(包括 benefitActive=false // 2. 查找祖先链中所有授权市公司(包括 benefitActive=false
// 注意:市团队收益不再要求城市匹配,只要推荐链上有市团队授权即可获得收益 // 注意:市团队收益不再要求城市匹配,只要推荐链上有市团队授权即可获得收益
const ancestorAuthCities = await this.authorizationRepository.findAuthCityByAccountSequences( const ancestorAuthCities = await this.authorizationRepository.findAuthCityByAccountSequences(
ancestorAccountSequences.map((seq) => BigInt(seq)), ancestorAccountSequences,
) )
if (ancestorAuthCities.length === 0) { if (ancestorAuthCities.length === 0) {
@ -1442,7 +1442,7 @@ export class AuthorizationApplicationService {
for (let i = 0; i < ancestorAccountSequences.length; i++) { for (let i = 0; i < ancestorAccountSequences.length; i++) {
const ancestorSeq = ancestorAccountSequences[i] const ancestorSeq = ancestorAccountSequences[i]
const found = ancestorAuthCities.find( const found = ancestorAuthCities.find(
(auth) => Number(auth.userId.accountSequence) === ancestorSeq, (auth) => auth.userId.accountSequence === ancestorSeq,
) )
if (found) { if (found) {
nearestAuthCity = found nearestAuthCity = found
@ -1463,7 +1463,7 @@ export class AuthorizationApplicationService {
if (nearestAuthCity.benefitActive) { if (nearestAuthCity.benefitActive) {
return { return {
distributions: [ distributions: [
{ accountSequence: Number(nearestAuthCity.userId.accountSequence), treeCount, reason: '市团队权益已激活' }, { accountSequence: nearestAuthCity.userId.accountSequence, treeCount, reason: '市团队权益已激活' },
], ],
} }
} }
@ -1480,27 +1480,27 @@ export class AuthorizationApplicationService {
) )
// 6. 查找上级 // 6. 查找上级
let parentAccountSequence: number = HEADQUARTERS_ACCOUNT_SEQUENCE let parentAccountSequence: string = HEADQUARTERS_ACCOUNT_SEQUENCE
let parentReason = '上级为总部社区' let parentReason = '上级为总部社区'
for (let i = nearestIndex + 1; i < ancestorAccountSequences.length; i++) { for (let i = nearestIndex + 1; i < ancestorAccountSequences.length; i++) {
const ancestorSeq = ancestorAccountSequences[i] const ancestorSeq = ancestorAccountSequences[i]
const found = ancestorAuthCities.find( const found = ancestorAuthCities.find(
(auth) => Number(auth.userId.accountSequence) === ancestorSeq && auth.benefitActive, (auth) => auth.userId.accountSequence === ancestorSeq && auth.benefitActive,
) )
if (found) { if (found) {
parentAccountSequence = Number(found.userId.accountSequence) parentAccountSequence = found.userId.accountSequence
parentReason = '上级授权市公司权益已激活' parentReason = '上级授权市公司权益已激活'
break break
} }
} }
// 7. 计算分配 // 7. 计算分配
const distributions: Array<{ accountSequence: number; treeCount: number; reason: string }> = [] const distributions: Array<{ accountSequence: string; treeCount: number; reason: string }> = []
if (currentTeamCount >= initialTarget) { if (currentTeamCount >= initialTarget) {
distributions.push({ distributions.push({
accountSequence: Number(nearestAuthCity.userId.accountSequence), accountSequence: nearestAuthCity.userId.accountSequence,
treeCount, treeCount,
reason: '已达初始考核目标', reason: '已达初始考核目标',
}) })
@ -1532,7 +1532,7 @@ export class AuthorizationApplicationService {
const afterTargetCount = treeCount - remaining const afterTargetCount = treeCount - remaining
if (afterTargetCount > 0) { if (afterTargetCount > 0) {
distributions.push({ distributions.push({
accountSequence: Number(nearestAuthCity.userId.accountSequence), accountSequence: nearestAuthCity.userId.accountSequence,
treeCount: afterTargetCount, treeCount: afterTargetCount,
reason: '考核达标后权益生效', reason: '考核达标后权益生效',
}) })
@ -1563,7 +1563,7 @@ export class AuthorizationApplicationService {
treeCount: number, treeCount: number,
): Promise<{ ): Promise<{
distributions: Array<{ distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
isSystemAccount: boolean isSystemAccount: boolean
@ -1574,7 +1574,7 @@ export class AuthorizationApplicationService {
) )
// 系统市账户ID格式: 8 + 城市代码 // 系统市账户ID格式: 8 + 城市代码
const systemCityAccountId = Number(`8${cityCode.padStart(6, '0')}`) const systemCityAccountId = `8${cityCode.padStart(6, '0')}`
// 查找该城市的正式市公司 // 查找该城市的正式市公司
const cityCompany = await this.authorizationRepository.findCityCompanyByRegion(cityCode) const cityCompany = await this.authorizationRepository.findCityCompanyByRegion(cityCode)
@ -1598,7 +1598,7 @@ export class AuthorizationApplicationService {
return { return {
distributions: [ distributions: [
{ {
accountSequence: Number(cityCompany.userId.accountSequence), accountSequence: cityCompany.userId.accountSequence,
treeCount, treeCount,
reason: '市区域权益已激活', reason: '市区域权益已激活',
isSystemAccount: false, isSystemAccount: false,
@ -1619,7 +1619,7 @@ export class AuthorizationApplicationService {
) )
const distributions: Array<{ const distributions: Array<{
accountSequence: number accountSequence: string
treeCount: number treeCount: number
reason: string reason: string
isSystemAccount: boolean isSystemAccount: boolean
@ -1628,7 +1628,7 @@ export class AuthorizationApplicationService {
if (currentTeamCount >= initialTarget) { if (currentTeamCount >= initialTarget) {
// 已达标但权益未激活,全部给该市公司 // 已达标但权益未激活,全部给该市公司
distributions.push({ distributions.push({
accountSequence: Number(cityCompany.userId.accountSequence), accountSequence: cityCompany.userId.accountSequence,
treeCount, treeCount,
reason: '已达初始考核目标', reason: '已达初始考核目标',
isSystemAccount: false, isSystemAccount: false,
@ -1663,7 +1663,7 @@ export class AuthorizationApplicationService {
const afterTargetCount = treeCount - remaining const afterTargetCount = treeCount - remaining
if (afterTargetCount > 0) { if (afterTargetCount > 0) {
distributions.push({ distributions.push({
accountSequence: Number(cityCompany.userId.accountSequence), accountSequence: cityCompany.userId.accountSequence,
treeCount: afterTargetCount, treeCount: afterTargetCount,
reason: '考核达标后权益生效', reason: '考核达标后权益生效',
isSystemAccount: false, isSystemAccount: false,

View File

@ -7,7 +7,7 @@ describe('AuthorizationRole Aggregate', () => {
describe('createCommunityAuth', () => { describe('createCommunityAuth', () => {
it('should create community authorization', () => { it('should create community authorization', () => {
const auth = AuthorizationRole.createCommunityAuth({ const auth = AuthorizationRole.createCommunityAuth({
userId: UserId.create('user-1', BigInt(1)), userId: UserId.create('user-1', '1'),
communityName: '量子社区', communityName: '量子社区',
}) })
@ -24,7 +24,7 @@ describe('AuthorizationRole Aggregate', () => {
describe('createAuthProvinceCompany', () => { describe('createAuthProvinceCompany', () => {
it('should create auth province company authorization', () => { it('should create auth province company authorization', () => {
const auth = AuthorizationRole.createAuthProvinceCompany({ const auth = AuthorizationRole.createAuthProvinceCompany({
userId: UserId.create('user-1', BigInt(1)), userId: UserId.create('user-1', '1'),
provinceCode: '430000', provinceCode: '430000',
provinceName: '湖南省', provinceName: '湖南省',
}) })
@ -42,7 +42,7 @@ describe('AuthorizationRole Aggregate', () => {
describe('createAuthCityCompany', () => { describe('createAuthCityCompany', () => {
it('should create auth city company authorization', () => { it('should create auth city company authorization', () => {
const auth = AuthorizationRole.createAuthCityCompany({ const auth = AuthorizationRole.createAuthCityCompany({
userId: UserId.create('user-1', BigInt(1)), userId: UserId.create('user-1', '1'),
cityCode: '430100', cityCode: '430100',
cityName: '长沙市', cityName: '长沙市',
}) })
@ -57,9 +57,9 @@ describe('AuthorizationRole Aggregate', () => {
describe('createProvinceCompany', () => { describe('createProvinceCompany', () => {
it('should create official province company with active benefits', () => { it('should create official province company with active benefits', () => {
const adminId = AdminUserId.create('admin-1', BigInt(101)) const adminId = AdminUserId.create('admin-1', '101')
const auth = AuthorizationRole.createProvinceCompany({ const auth = AuthorizationRole.createProvinceCompany({
userId: UserId.create('user-1', BigInt(1)), userId: UserId.create('user-1', '1'),
provinceCode: '430000', provinceCode: '430000',
provinceName: '湖南省', provinceName: '湖南省',
adminId, adminId,
@ -76,7 +76,7 @@ describe('AuthorizationRole Aggregate', () => {
describe('activateBenefit', () => { describe('activateBenefit', () => {
it('should activate benefit and emit event', () => { it('should activate benefit and emit event', () => {
const auth = AuthorizationRole.createCommunityAuth({ const auth = AuthorizationRole.createCommunityAuth({
userId: UserId.create('user-1', BigInt(1)), userId: UserId.create('user-1', '1'),
communityName: '量子社区', communityName: '量子社区',
}) })
auth.clearDomainEvents() auth.clearDomainEvents()
@ -92,10 +92,10 @@ describe('AuthorizationRole Aggregate', () => {
it('should throw error if already active', () => { it('should throw error if already active', () => {
const auth = AuthorizationRole.createProvinceCompany({ const auth = AuthorizationRole.createProvinceCompany({
userId: UserId.create('user-1', BigInt(1)), userId: UserId.create('user-1', '1'),
provinceCode: '430000', provinceCode: '430000',
provinceName: '湖南省', provinceName: '湖南省',
adminId: AdminUserId.create('admin-1', BigInt(101)), adminId: AdminUserId.create('admin-1', '101'),
}) })
expect(() => auth.activateBenefit()).toThrow(DomainError) expect(() => auth.activateBenefit()).toThrow(DomainError)
@ -105,10 +105,10 @@ describe('AuthorizationRole Aggregate', () => {
describe('deactivateBenefit', () => { describe('deactivateBenefit', () => {
it('should deactivate benefit and reset month index', () => { it('should deactivate benefit and reset month index', () => {
const auth = AuthorizationRole.createProvinceCompany({ const auth = AuthorizationRole.createProvinceCompany({
userId: UserId.create('user-1', BigInt(1)), userId: UserId.create('user-1', '1'),
provinceCode: '430000', provinceCode: '430000',
provinceName: '湖南省', provinceName: '湖南省',
adminId: AdminUserId.create('admin-1', BigInt(101)), adminId: AdminUserId.create('admin-1', '101'),
}) })
auth.clearDomainEvents() auth.clearDomainEvents()
@ -124,14 +124,14 @@ describe('AuthorizationRole Aggregate', () => {
describe('revoke', () => { describe('revoke', () => {
it('should revoke authorization', () => { it('should revoke authorization', () => {
const auth = AuthorizationRole.createProvinceCompany({ const auth = AuthorizationRole.createProvinceCompany({
userId: UserId.create('user-1', BigInt(1)), userId: UserId.create('user-1', '1'),
provinceCode: '430000', provinceCode: '430000',
provinceName: '湖南省', provinceName: '湖南省',
adminId: AdminUserId.create('admin-1', BigInt(101)), adminId: AdminUserId.create('admin-1', '101'),
}) })
auth.clearDomainEvents() auth.clearDomainEvents()
auth.revoke(AdminUserId.create('admin-2', BigInt(102)), '违规操作') auth.revoke(AdminUserId.create('admin-2', '102'), '违规操作')
expect(auth.status).toBe(AuthorizationStatus.REVOKED) expect(auth.status).toBe(AuthorizationStatus.REVOKED)
expect(auth.benefitActive).toBe(false) expect(auth.benefitActive).toBe(false)
@ -142,14 +142,14 @@ describe('AuthorizationRole Aggregate', () => {
it('should throw error if already revoked', () => { it('should throw error if already revoked', () => {
const auth = AuthorizationRole.createProvinceCompany({ const auth = AuthorizationRole.createProvinceCompany({
userId: UserId.create('user-1', BigInt(1)), userId: UserId.create('user-1', '1'),
provinceCode: '430000', provinceCode: '430000',
provinceName: '湖南省', provinceName: '湖南省',
adminId: AdminUserId.create('admin-1', BigInt(101)), adminId: AdminUserId.create('admin-1', '101'),
}) })
auth.revoke(AdminUserId.create('admin-2', BigInt(102)), '违规操作') auth.revoke(AdminUserId.create('admin-2', '102'), '违规操作')
expect(() => auth.revoke(AdminUserId.create('admin-3', BigInt(103)), '再次撤销')).toThrow( expect(() => auth.revoke(AdminUserId.create('admin-3', '103'), '再次撤销')).toThrow(
DomainError, DomainError,
) )
}) })
@ -158,7 +158,7 @@ describe('AuthorizationRole Aggregate', () => {
describe('exemptLocalPercentageCheck', () => { describe('exemptLocalPercentageCheck', () => {
it('should exempt from percentage check', () => { it('should exempt from percentage check', () => {
const auth = AuthorizationRole.createAuthProvinceCompany({ const auth = AuthorizationRole.createAuthProvinceCompany({
userId: UserId.create('user-1', BigInt(1)), userId: UserId.create('user-1', '1'),
provinceCode: '430000', provinceCode: '430000',
provinceName: '湖南省', provinceName: '湖南省',
}) })
@ -166,7 +166,7 @@ describe('AuthorizationRole Aggregate', () => {
expect(auth.exemptFromPercentageCheck).toBe(false) expect(auth.exemptFromPercentageCheck).toBe(false)
expect(auth.needsLocalPercentageCheck()).toBe(true) expect(auth.needsLocalPercentageCheck()).toBe(true)
auth.exemptLocalPercentageCheck(AdminUserId.create('admin-1', BigInt(101))) auth.exemptLocalPercentageCheck(AdminUserId.create('admin-1', '101'))
expect(auth.exemptFromPercentageCheck).toBe(true) expect(auth.exemptFromPercentageCheck).toBe(true)
expect(auth.needsLocalPercentageCheck()).toBe(false) expect(auth.needsLocalPercentageCheck()).toBe(false)
@ -176,7 +176,7 @@ describe('AuthorizationRole Aggregate', () => {
describe('incrementMonthIndex', () => { describe('incrementMonthIndex', () => {
it('should increment month index', () => { it('should increment month index', () => {
const auth = AuthorizationRole.createCommunityAuth({ const auth = AuthorizationRole.createCommunityAuth({
userId: UserId.create('user-1', BigInt(1)), userId: UserId.create('user-1', '1'),
communityName: '量子社区', communityName: '量子社区',
}) })
auth.activateBenefit() auth.activateBenefit()

View File

@ -8,14 +8,14 @@ export interface IAuthorizationRoleRepository {
save(authorization: AuthorizationRole): Promise<void> save(authorization: AuthorizationRole): Promise<void>
findById(authorizationId: AuthorizationId): Promise<AuthorizationRole | null> findById(authorizationId: AuthorizationId): Promise<AuthorizationRole | null>
findByUserIdAndRoleType(userId: UserId, roleType: RoleType): Promise<AuthorizationRole | null> findByUserIdAndRoleType(userId: UserId, roleType: RoleType): Promise<AuthorizationRole | null>
findByAccountSequenceAndRoleType(accountSequence: bigint, roleType: RoleType): Promise<AuthorizationRole | null> findByAccountSequenceAndRoleType(accountSequence: string, roleType: RoleType): Promise<AuthorizationRole | null>
findByUserIdRoleTypeAndRegion( findByUserIdRoleTypeAndRegion(
userId: UserId, userId: UserId,
roleType: RoleType, roleType: RoleType,
regionCode: RegionCode, regionCode: RegionCode,
): Promise<AuthorizationRole | null> ): Promise<AuthorizationRole | null>
findByUserId(userId: UserId): Promise<AuthorizationRole[]> findByUserId(userId: UserId): Promise<AuthorizationRole[]>
findByAccountSequence(accountSequence: bigint): Promise<AuthorizationRole[]> findByAccountSequence(accountSequence: string): Promise<AuthorizationRole[]>
findActiveByRoleTypeAndRegion( findActiveByRoleTypeAndRegion(
roleType: RoleType, roleType: RoleType,
regionCode: RegionCode, regionCode: RegionCode,
@ -27,33 +27,33 @@ export interface IAuthorizationRoleRepository {
/** /**
* accountSequence * accountSequence
*/ */
findActiveCommunityByAccountSequences(accountSequences: bigint[]): Promise<AuthorizationRole[]> findActiveCommunityByAccountSequences(accountSequences: string[]): Promise<AuthorizationRole[]>
/** /**
* accountSequence * accountSequence
*/ */
findActiveProvinceByAccountSequencesAndRegion( findActiveProvinceByAccountSequencesAndRegion(
accountSequences: bigint[], accountSequences: string[],
provinceCode: string, provinceCode: string,
): Promise<AuthorizationRole[]> ): Promise<AuthorizationRole[]>
/** /**
* accountSequence * accountSequence
*/ */
findActiveCityByAccountSequencesAndRegion( findActiveCityByAccountSequencesAndRegion(
accountSequences: bigint[], accountSequences: string[],
cityCode: string, cityCode: string,
): Promise<AuthorizationRole[]> ): Promise<AuthorizationRole[]>
/** /**
* accountSequence benefitActive=false * accountSequence benefitActive=false
* *
*/ */
findCommunityByAccountSequences(accountSequences: bigint[]): Promise<AuthorizationRole[]> findCommunityByAccountSequences(accountSequences: string[]): Promise<AuthorizationRole[]>
/** /**
* accountSequence benefitActive=false * accountSequence benefitActive=false
* *
* @deprecated 使 findAuthProvinceByAccountSequences * @deprecated 使 findAuthProvinceByAccountSequences
*/ */
findAuthProvinceByAccountSequencesAndRegion( findAuthProvinceByAccountSequencesAndRegion(
accountSequences: bigint[], accountSequences: string[],
provinceCode: string, provinceCode: string,
): Promise<AuthorizationRole[]> ): Promise<AuthorizationRole[]>
/** /**
@ -62,19 +62,19 @@ export interface IAuthorizationRoleRepository {
* @deprecated 使 findAuthCityByAccountSequences * @deprecated 使 findAuthCityByAccountSequences
*/ */
findAuthCityByAccountSequencesAndRegion( findAuthCityByAccountSequencesAndRegion(
accountSequences: bigint[], accountSequences: string[],
cityCode: string, cityCode: string,
): Promise<AuthorizationRole[]> ): Promise<AuthorizationRole[]>
/** /**
* accountSequence () benefitActive=false * accountSequence () benefitActive=false
* *
*/ */
findAuthProvinceByAccountSequences(accountSequences: bigint[]): Promise<AuthorizationRole[]> findAuthProvinceByAccountSequences(accountSequences: string[]): Promise<AuthorizationRole[]>
/** /**
* accountSequence () benefitActive=false * accountSequence () benefitActive=false
* *
*/ */
findAuthCityByAccountSequences(accountSequences: bigint[]): Promise<AuthorizationRole[]> findAuthCityByAccountSequences(accountSequences: string[]): Promise<AuthorizationRole[]>
/** /**
* *
*/ */

View File

@ -6,7 +6,7 @@ import { IMonthlyAssessmentRepository, IAuthorizationRoleRepository } from '@/do
export interface TeamStatistics { export interface TeamStatistics {
userId: string userId: string
accountSequence: bigint accountSequence: string
totalTeamPlantingCount: number totalTeamPlantingCount: number
selfPlantingCount: number selfPlantingCount: number
/** 下级团队认种数(不包括自己)= totalTeamPlantingCount - selfPlantingCount */ /** 下级团队认种数(不包括自己)= totalTeamPlantingCount - selfPlantingCount */
@ -17,7 +17,7 @@ export interface TeamStatistics {
export interface ITeamStatisticsRepository { export interface ITeamStatisticsRepository {
findByUserId(userId: string): Promise<TeamStatistics | null> findByUserId(userId: string): Promise<TeamStatistics | null>
findByAccountSequence(accountSequence: bigint): Promise<TeamStatistics | null> findByAccountSequence(accountSequence: string): Promise<TeamStatistics | null>
} }
export class AssessmentCalculatorService { export class AssessmentCalculatorService {

View File

@ -3,7 +3,7 @@ import { DomainError } from '@/shared/exceptions'
export class UserId { export class UserId {
constructor( constructor(
public readonly value: string, public readonly value: string,
public readonly accountSequence: bigint, public readonly accountSequence: string,
) { ) {
if (!value) { if (!value) {
throw new DomainError('用户ID不能为空') throw new DomainError('用户ID不能为空')
@ -13,8 +13,8 @@ export class UserId {
} }
} }
static create(value: string, accountSequence: number | bigint): UserId { static create(value: string, accountSequence: string): UserId {
return new UserId(value, BigInt(accountSequence)) return new UserId(value, accountSequence)
} }
equals(other: UserId): boolean { equals(other: UserId): boolean {
@ -29,7 +29,7 @@ export class UserId {
export class AdminUserId { export class AdminUserId {
constructor( constructor(
public readonly value: string, public readonly value: string,
public readonly accountSequence: bigint, public readonly accountSequence: string,
) { ) {
if (!value) { if (!value) {
throw new DomainError('管理员ID不能为空') throw new DomainError('管理员ID不能为空')
@ -39,8 +39,8 @@ export class AdminUserId {
} }
} }
static create(value: string, accountSequence: number | bigint): AdminUserId { static create(value: string, accountSequence: string): AdminUserId {
return new AdminUserId(value, BigInt(accountSequence)) return new AdminUserId(value, accountSequence)
} }
equals(other: AdminUserId): boolean { equals(other: AdminUserId): boolean {

View File

@ -21,7 +21,7 @@ interface ReferralTeamStatsResponse {
* *
*/ */
interface ReferralChainResponse { interface ReferralChainResponse {
accountSequence: number; accountSequence: string;
userId: string | null; userId: string | null;
ancestorPath: string[]; ancestorPath: string[];
referrerId: string | null; referrerId: string | null;
@ -31,8 +31,8 @@ interface ReferralChainResponse {
* *
*/ */
interface TeamMembersResponse { interface TeamMembersResponse {
accountSequence: number; accountSequence: string;
teamMembers: number[]; teamMembers: string[];
} }
/** /**
@ -41,7 +41,7 @@ interface TeamMembersResponse {
class TeamStatisticsAdapter implements TeamStatistics { class TeamStatisticsAdapter implements TeamStatistics {
constructor( constructor(
public readonly userId: string, public readonly userId: string,
public readonly accountSequence: bigint, public readonly accountSequence: string,
public readonly totalTeamPlantingCount: number, public readonly totalTeamPlantingCount: number,
public readonly selfPlantingCount: number, public readonly selfPlantingCount: number,
private readonly provinceCityDistribution: Record<string, Record<string, number>> | null, private readonly provinceCityDistribution: Record<string, Record<string, number>> | null,
@ -110,7 +110,7 @@ export class ReferralServiceClient implements ITeamStatisticsRepository, OnModul
async findByUserId(userId: string): Promise<TeamStatistics | null> { async findByUserId(userId: string): Promise<TeamStatistics | null> {
if (!this.enabled) { if (!this.enabled) {
this.logger.debug('[DISABLED] Referral service integration is disabled'); this.logger.debug('[DISABLED] Referral service integration is disabled');
return this.createEmptyStats(userId, BigInt(0)); return this.createEmptyStats(userId, '0');
} }
try { try {
@ -122,7 +122,7 @@ export class ReferralServiceClient implements ITeamStatisticsRepository, OnModul
if (!response.data) { if (!response.data) {
this.logger.debug(`[HTTP] No stats found for userId: ${userId}`); this.logger.debug(`[HTTP] No stats found for userId: ${userId}`);
return this.createEmptyStats(userId, BigInt(0)); return this.createEmptyStats(userId, '0');
} }
const data = response.data; const data = response.data;
@ -130,7 +130,7 @@ export class ReferralServiceClient implements ITeamStatisticsRepository, OnModul
return new TeamStatisticsAdapter( return new TeamStatisticsAdapter(
data.userId, data.userId,
BigInt(data.accountSequence || 0), data.accountSequence || '0',
data.totalTeamPlantingCount, data.totalTeamPlantingCount,
data.selfPlantingCount || 0, data.selfPlantingCount || 0,
data.provinceCityDistribution, data.provinceCityDistribution,
@ -138,14 +138,14 @@ export class ReferralServiceClient implements ITeamStatisticsRepository, OnModul
} catch (error) { } catch (error) {
this.logger.error(`[HTTP] Failed to get stats for userId ${userId}:`, error); this.logger.error(`[HTTP] Failed to get stats for userId ${userId}:`, error);
// 返回空数据而不是抛出错误,避免影响主流程 // 返回空数据而不是抛出错误,避免影响主流程
return this.createEmptyStats(userId, BigInt(0)); return this.createEmptyStats(userId, '0');
} }
} }
/** /**
* accountSequence * accountSequence
*/ */
async findByAccountSequence(accountSequence: bigint): Promise<TeamStatistics | null> { async findByAccountSequence(accountSequence: string): Promise<TeamStatistics | null> {
if (!this.enabled) { if (!this.enabled) {
this.logger.debug('[DISABLED] Referral service integration is disabled'); this.logger.debug('[DISABLED] Referral service integration is disabled');
return this.createEmptyStats('', accountSequence); return this.createEmptyStats('', accountSequence);
@ -168,7 +168,7 @@ export class ReferralServiceClient implements ITeamStatisticsRepository, OnModul
return new TeamStatisticsAdapter( return new TeamStatisticsAdapter(
data.userId, data.userId,
BigInt(data.accountSequence || accountSequence.toString()), data.accountSequence || accountSequence,
data.totalTeamPlantingCount, data.totalTeamPlantingCount,
data.selfPlantingCount || 0, data.selfPlantingCount || 0,
data.provinceCityDistribution, data.provinceCityDistribution,
@ -183,7 +183,7 @@ export class ReferralServiceClient implements ITeamStatisticsRepository, OnModul
/** /**
* *
*/ */
private createEmptyStats(userId: string, accountSequence: bigint): TeamStatistics { private createEmptyStats(userId: string, accountSequence: string): TeamStatistics {
return new TeamStatisticsAdapter(userId, accountSequence, 0, 0, null); return new TeamStatisticsAdapter(userId, accountSequence, 0, 0, null);
} }
@ -191,7 +191,7 @@ export class ReferralServiceClient implements ITeamStatisticsRepository, OnModul
* *
* accountSequence * accountSequence
*/ */
async getReferralChain(accountSequence: bigint): Promise<number[]> { async getReferralChain(accountSequence: string): Promise<string[]> {
if (!this.enabled) { if (!this.enabled) {
this.logger.debug('[DISABLED] Referral service integration is disabled'); this.logger.debug('[DISABLED] Referral service integration is disabled');
return []; return [];
@ -208,9 +208,8 @@ export class ReferralServiceClient implements ITeamStatisticsRepository, OnModul
return []; return [];
} }
// ancestorPath 存储的是 userId (bigint string),我们需要映射到 accountSequence // ancestorPath 存储的是 accountSequence string
// 由于 referral-service 中 userId = BigInt(accountSequence),可以直接转换 return response.data.ancestorPath;
return response.data.ancestorPath.map((id) => Number(id));
} catch (error) { } catch (error) {
this.logger.error(`[HTTP] Failed to get referral chain for accountSequence ${accountSequence}:`, error); this.logger.error(`[HTTP] Failed to get referral chain for accountSequence ${accountSequence}:`, error);
return []; return [];
@ -220,7 +219,7 @@ export class ReferralServiceClient implements ITeamStatisticsRepository, OnModul
/** /**
* accountSequence * accountSequence
*/ */
async getTeamMembers(accountSequence: bigint): Promise<number[]> { async getTeamMembers(accountSequence: string): Promise<string[]> {
if (!this.enabled) { if (!this.enabled) {
this.logger.debug('[DISABLED] Referral service integration is disabled'); this.logger.debug('[DISABLED] Referral service integration is disabled');
return []; return [];

View File

@ -76,7 +76,7 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
): Promise<AuthorizationRole | null> { ): Promise<AuthorizationRole | null> {
const record = await this.prisma.authorizationRole.findFirst({ const record = await this.prisma.authorizationRole.findFirst({
where: { where: {
userId: BigInt(userId.value), userId: userId.value,
roleType: roleType, roleType: roleType,
}, },
}) })
@ -90,7 +90,7 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
): Promise<AuthorizationRole | null> { ): Promise<AuthorizationRole | null> {
const record = await this.prisma.authorizationRole.findFirst({ const record = await this.prisma.authorizationRole.findFirst({
where: { where: {
userId: BigInt(userId.value), userId: userId.value,
roleType: roleType, roleType: roleType,
regionCode: regionCode.value, regionCode: regionCode.value,
}, },
@ -99,7 +99,7 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
} }
async findByAccountSequenceAndRoleType( async findByAccountSequenceAndRoleType(
accountSequence: bigint, accountSequence: string,
roleType: RoleType, roleType: RoleType,
): Promise<AuthorizationRole | null> { ): Promise<AuthorizationRole | null> {
const record = await this.prisma.authorizationRole.findFirst({ const record = await this.prisma.authorizationRole.findFirst({
@ -113,13 +113,13 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
async findByUserId(userId: UserId): Promise<AuthorizationRole[]> { async findByUserId(userId: UserId): Promise<AuthorizationRole[]> {
const records = await this.prisma.authorizationRole.findMany({ const records = await this.prisma.authorizationRole.findMany({
where: { userId: BigInt(userId.value) }, where: { userId: userId.value },
orderBy: { createdAt: 'desc' }, orderBy: { createdAt: 'desc' },
}) })
return records.map((record) => this.toDomain(record)) return records.map((record) => this.toDomain(record))
} }
async findByAccountSequence(accountSequence: bigint): Promise<AuthorizationRole[]> { async findByAccountSequence(accountSequence: string): Promise<AuthorizationRole[]> {
const records = await this.prisma.authorizationRole.findMany({ const records = await this.prisma.authorizationRole.findMany({
where: { accountSequence: accountSequence }, where: { accountSequence: accountSequence },
orderBy: { createdAt: 'desc' }, orderBy: { createdAt: 'desc' },
@ -156,7 +156,7 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
async findPendingByUserId(userId: UserId): Promise<AuthorizationRole[]> { async findPendingByUserId(userId: UserId): Promise<AuthorizationRole[]> {
const records = await this.prisma.authorizationRole.findMany({ const records = await this.prisma.authorizationRole.findMany({
where: { where: {
userId: BigInt(userId.value), userId: userId.value,
status: AuthorizationStatus.PENDING, status: AuthorizationStatus.PENDING,
}, },
}) })
@ -177,7 +177,7 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
} }
async findActiveCommunityByAccountSequences( async findActiveCommunityByAccountSequences(
accountSequences: bigint[], accountSequences: string[],
): Promise<AuthorizationRole[]> { ): Promise<AuthorizationRole[]> {
if (accountSequences.length === 0) { if (accountSequences.length === 0) {
return [] return []
@ -197,7 +197,7 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
} }
async findActiveProvinceByAccountSequencesAndRegion( async findActiveProvinceByAccountSequencesAndRegion(
accountSequences: bigint[], accountSequences: string[],
provinceCode: string, provinceCode: string,
): Promise<AuthorizationRole[]> { ): Promise<AuthorizationRole[]> {
if (accountSequences.length === 0) { if (accountSequences.length === 0) {
@ -218,7 +218,7 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
} }
async findActiveCityByAccountSequencesAndRegion( async findActiveCityByAccountSequencesAndRegion(
accountSequences: bigint[], accountSequences: string[],
cityCode: string, cityCode: string,
): Promise<AuthorizationRole[]> { ): Promise<AuthorizationRole[]> {
if (accountSequences.length === 0) { if (accountSequences.length === 0) {
@ -239,7 +239,7 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
} }
async findCommunityByAccountSequences( async findCommunityByAccountSequences(
accountSequences: bigint[], accountSequences: string[],
): Promise<AuthorizationRole[]> { ): Promise<AuthorizationRole[]> {
if (accountSequences.length === 0) { if (accountSequences.length === 0) {
return [] return []
@ -258,7 +258,7 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
} }
async findAuthProvinceByAccountSequencesAndRegion( async findAuthProvinceByAccountSequencesAndRegion(
accountSequences: bigint[], accountSequences: string[],
provinceCode: string, provinceCode: string,
): Promise<AuthorizationRole[]> { ): Promise<AuthorizationRole[]> {
if (accountSequences.length === 0) { if (accountSequences.length === 0) {
@ -279,7 +279,7 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
} }
async findAuthCityByAccountSequencesAndRegion( async findAuthCityByAccountSequencesAndRegion(
accountSequences: bigint[], accountSequences: string[],
cityCode: string, cityCode: string,
): Promise<AuthorizationRole[]> { ): Promise<AuthorizationRole[]> {
if (accountSequences.length === 0) { if (accountSequences.length === 0) {
@ -300,7 +300,7 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
} }
async findAuthProvinceByAccountSequences( async findAuthProvinceByAccountSequences(
accountSequences: bigint[], accountSequences: string[],
): Promise<AuthorizationRole[]> { ): Promise<AuthorizationRole[]> {
if (accountSequences.length === 0) { if (accountSequences.length === 0) {
return [] return []
@ -320,7 +320,7 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
} }
async findAuthCityByAccountSequences( async findAuthCityByAccountSequences(
accountSequences: bigint[], accountSequences: string[],
): Promise<AuthorizationRole[]> { ): Promise<AuthorizationRole[]> {
if (accountSequences.length === 0) { if (accountSequences.length === 0) {
return [] return []
@ -364,16 +364,16 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
private toDomain(record: any): AuthorizationRole { private toDomain(record: any): AuthorizationRole {
const props: AuthorizationRoleProps = { const props: AuthorizationRoleProps = {
authorizationId: AuthorizationId.create(record.id), authorizationId: AuthorizationId.create(record.id),
userId: UserId.create(record.userId.toString(), record.accountSequence), userId: UserId.create(record.userId, record.accountSequence),
roleType: record.roleType as RoleType, roleType: record.roleType as RoleType,
regionCode: RegionCode.create(record.regionCode), regionCode: RegionCode.create(record.regionCode),
regionName: record.regionName, regionName: record.regionName,
status: record.status as AuthorizationStatus, status: record.status as AuthorizationStatus,
displayTitle: record.displayTitle, displayTitle: record.displayTitle,
authorizedAt: record.authorizedAt, authorizedAt: record.authorizedAt,
authorizedBy: record.authorizedBy ? AdminUserId.create(record.authorizedBy.toString(), record.authorizedBy) : null, authorizedBy: record.authorizedBy ? AdminUserId.create(record.authorizedBy, record.authorizedBy) : null,
revokedAt: record.revokedAt, revokedAt: record.revokedAt,
revokedBy: record.revokedBy ? AdminUserId.create(record.revokedBy.toString(), record.revokedBy) : null, revokedBy: record.revokedBy ? AdminUserId.create(record.revokedBy, record.revokedBy) : null,
revokeReason: record.revokeReason, revokeReason: record.revokeReason,
assessmentConfig: new AssessmentConfig( assessmentConfig: new AssessmentConfig(
record.initialTargetTreeCount, record.initialTargetTreeCount,

View File

@ -148,7 +148,7 @@ export class MonthlyAssessmentRepositoryImpl implements IMonthlyAssessmentReposi
async findByUserAndMonth(userId: UserId, month: Month): Promise<MonthlyAssessment[]> { async findByUserAndMonth(userId: UserId, month: Month): Promise<MonthlyAssessment[]> {
const records = await this.prisma.monthlyAssessment.findMany({ const records = await this.prisma.monthlyAssessment.findMany({
where: { where: {
userId: BigInt(userId.value), userId: userId.value,
assessmentMonth: month.value, assessmentMonth: month.value,
}, },
}) })
@ -214,7 +214,7 @@ export class MonthlyAssessmentRepositoryImpl implements IMonthlyAssessmentReposi
const props: MonthlyAssessmentProps = { const props: MonthlyAssessmentProps = {
assessmentId: AssessmentId.create(record.id), assessmentId: AssessmentId.create(record.id),
authorizationId: AuthorizationId.create(record.authorizationId), authorizationId: AuthorizationId.create(record.authorizationId),
userId: UserId.create(record.userId.toString(), record.accountSequence), userId: UserId.create(record.userId, record.accountSequence),
roleType: record.roleType as RoleType, roleType: record.roleType as RoleType,
regionCode: RegionCode.create(record.regionCode), regionCode: RegionCode.create(record.regionCode),
assessmentMonth: Month.create(record.assessmentMonth), assessmentMonth: Month.create(record.assessmentMonth),
@ -233,7 +233,7 @@ export class MonthlyAssessmentRepositoryImpl implements IMonthlyAssessmentReposi
rankingInRegion: record.rankingInRegion, rankingInRegion: record.rankingInRegion,
isFirstPlace: record.isFirstPlace, isFirstPlace: record.isFirstPlace,
isBypassed: record.isBypassed, isBypassed: record.isBypassed,
bypassedBy: record.bypassedBy ? AdminUserId.create(record.bypassedBy.toString(), record.bypassedBy) : null, bypassedBy: record.bypassedBy ? AdminUserId.create(record.bypassedBy, record.bypassedBy) : null,
bypassedAt: record.bypassedAt, bypassedAt: record.bypassedAt,
assessedAt: record.assessedAt, assessedAt: record.assessedAt,
createdAt: record.createdAt, createdAt: record.createdAt,

View File

@ -2,7 +2,7 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'
export interface CurrentUserData { export interface CurrentUserData {
userId: string userId: string
accountSequence?: number accountSequence?: string
walletAddress?: string walletAddress?: string
roles?: string[] roles?: string[]
} }

View File

@ -6,7 +6,7 @@ import { ConfigService } from '@nestjs/config'
export interface JwtPayload { export interface JwtPayload {
// Identity-service uses 'userId' field // Identity-service uses 'userId' field
userId: string userId: string
accountSequence?: number accountSequence?: string
deviceId?: string deviceId?: string
type?: string type?: string
// Legacy support for 'sub' field // Legacy support for 'sub' field

View File

@ -14,7 +14,7 @@ model BackupShare {
// 用户标识 (来自 identity-service) // 用户标识 (来自 identity-service)
userId BigInt @unique @map("user_id") userId BigInt @unique @map("user_id")
accountSequence BigInt @unique @map("account_sequence") accountSequence String @unique @map("account_sequence") // 格式: D + YYMMDD + 5位序号
// MPC 密钥信息 // MPC 密钥信息
publicKey String @unique @map("public_key") @db.VarChar(130) publicKey String @unique @map("public_key") @db.VarChar(130)

View File

@ -14,9 +14,8 @@ export class StoreShareDto {
userId: string; userId: string;
@IsNotEmpty() @IsNotEmpty()
@IsNumber() @IsString()
@Min(1) accountSequence: string;
accountSequence: number;
@IsNotEmpty() @IsNotEmpty()
@IsString() @IsString()

View File

@ -1,7 +1,7 @@
export class StoreBackupShareCommand { export class StoreBackupShareCommand {
constructor( constructor(
public readonly userId: string, public readonly userId: string,
public readonly accountSequence: number, public readonly accountSequence: string,
public readonly publicKey: string, public readonly publicKey: string,
public readonly encryptedShareData: string, public readonly encryptedShareData: string,
public readonly sourceService: string, public readonly sourceService: string,

View File

@ -53,7 +53,7 @@ export class StoreBackupShareHandler {
// Create domain entity // Create domain entity
const share = BackupShare.create({ const share = BackupShare.create({
userId, userId,
accountSequence: BigInt(command.accountSequence), accountSequence: command.accountSequence,
publicKey: command.publicKey, publicKey: command.publicKey,
encryptedShareData: encrypted, encryptedShareData: encrypted,
encryptionKeyId: keyId, encryptionKeyId: keyId,

View File

@ -9,7 +9,7 @@ export enum BackupShareStatus {
export interface BackupShareProps { export interface BackupShareProps {
shareId: bigint | null; shareId: bigint | null;
userId: bigint; userId: bigint;
accountSequence: bigint; accountSequence: string;
publicKey: string; publicKey: string;
partyIndex: number; partyIndex: number;
threshold: number; threshold: number;
@ -27,7 +27,7 @@ export interface BackupShareProps {
export class BackupShare { export class BackupShare {
private _shareId: bigint | null; private _shareId: bigint | null;
private readonly _userId: bigint; private readonly _userId: bigint;
private readonly _accountSequence: bigint; private readonly _accountSequence: string;
private readonly _publicKey: string; private readonly _publicKey: string;
private readonly _partyIndex: number; private readonly _partyIndex: number;
private readonly _threshold: number; private readonly _threshold: number;
@ -61,7 +61,7 @@ export class BackupShare {
static create(params: { static create(params: {
userId: bigint; userId: bigint;
accountSequence: bigint; accountSequence: string;
publicKey: string; publicKey: string;
encryptedShareData: string; encryptedShareData: string;
encryptionKeyId: string; encryptionKeyId: string;
@ -131,7 +131,7 @@ export class BackupShare {
get userId(): bigint { get userId(): bigint {
return this._userId; return this._userId;
} }
get accountSequence(): bigint { get accountSequence(): string {
return this._accountSequence; return this._accountSequence;
} }
get publicKey(): string { get publicKey(): string {

View File

@ -11,6 +11,6 @@ export interface BackupShareRepository {
userId: bigint, userId: bigint,
publicKey: string, publicKey: string,
): Promise<BackupShare | null>; ): Promise<BackupShare | null>;
findByAccountSequence(accountSequence: bigint): Promise<BackupShare | null>; findByAccountSequence(accountSequence: string): Promise<BackupShare | null>;
delete(shareId: bigint): Promise<void>; delete(shareId: bigint): Promise<void>;
} }

View File

@ -86,7 +86,7 @@ export class BackupShareRepositoryImpl implements BackupShareRepository {
} }
async findByAccountSequence( async findByAccountSequence(
accountSequence: bigint, accountSequence: string,
): Promise<BackupShare | null> { ): Promise<BackupShare | null> {
const record = await this.prisma.backupShare.findUnique({ const record = await this.prisma.backupShare.findUnique({
where: { accountSequence }, where: { accountSequence },

View File

@ -15,7 +15,7 @@ datasource db {
// 存储需要监听充值的地址(用户地址和系统账户地址) // 存储需要监听充值的地址(用户地址和系统账户地址)
// ============================================ // ============================================
model MonitoredAddress { model MonitoredAddress {
id BigInt @id @default(autoincrement()) @map("address_id") id BigInt @id @default(autoincrement()) @map("address_id")
chainType String @map("chain_type") @db.VarChar(20) // KAVA, BSC chainType String @map("chain_type") @db.VarChar(20) // KAVA, BSC
address String @db.VarChar(42) // 0x地址 address String @db.VarChar(42) // 0x地址
@ -24,7 +24,7 @@ model MonitoredAddress {
addressType String @default("USER") @map("address_type") @db.VarChar(20) addressType String @default("USER") @map("address_type") @db.VarChar(20)
// 用户地址关联 (addressType = USER 时使用) // 用户地址关联 (addressType = USER 时使用)
accountSequence BigInt? @map("account_sequence") // 跨服务关联标识 accountSequence String? @map("account_sequence") @db.VarChar(20) // 跨服务关联标识 (格式: D + YYMMDD + 5位序号)
userId BigInt? @map("user_id") // 保留兼容 userId BigInt? @map("user_id") // 保留兼容
// 系统账户关联 (addressType = SYSTEM 时使用) // 系统账户关联 (addressType = SYSTEM 时使用)
@ -74,11 +74,11 @@ model DepositTransaction {
status String @default("DETECTED") @db.VarChar(20) // DETECTED, CONFIRMING, CONFIRMED, NOTIFIED status String @default("DETECTED") @db.VarChar(20) // DETECTED, CONFIRMING, CONFIRMED, NOTIFIED
// 关联 - 使用 accountSequence 作为跨服务主键 // 关联 - 使用 accountSequence 作为跨服务主键
addressId BigInt @map("address_id") addressId BigInt @map("address_id")
addressType String @default("USER") @map("address_type") @db.VarChar(20) // USER 或 SYSTEM addressType String @default("USER") @map("address_type") @db.VarChar(20) // USER 或 SYSTEM
// 用户地址关联 // 用户地址关联
accountSequence BigInt? @map("account_sequence") // 跨服务关联标识 accountSequence String? @map("account_sequence") @db.VarChar(20) // 跨服务关联标识 (格式: D + YYMMDD + 5位序号)
userId BigInt? @map("user_id") // 保留兼容 userId BigInt? @map("user_id") // 保留兼容
// 系统账户关联(当 addressType = SYSTEM 时) // 系统账户关联(当 addressType = SYSTEM 时)
@ -174,26 +174,26 @@ model TransactionRequest {
// 与账户序列号关联,用于账户恢复验证 // 与账户序列号关联,用于账户恢复验证
// ============================================ // ============================================
model RecoveryMnemonic { model RecoveryMnemonic {
id BigInt @id @default(autoincrement()) id BigInt @id @default(autoincrement())
accountSequence Int @map("account_sequence") // 8位账户序列号 accountSequence String @map("account_sequence") @db.VarChar(20) // 账户序列号 (格式: D + YYMMDD + 5位序号)
publicKey String @map("public_key") @db.VarChar(130) // 关联的钱包公钥 publicKey String @map("public_key") @db.VarChar(130) // 关联的钱包公钥
// 助记词存储 (加密) // 助记词存储 (加密)
encryptedMnemonic String @map("encrypted_mnemonic") @db.Text // AES加密的助记词 encryptedMnemonic String @map("encrypted_mnemonic") @db.Text // AES加密的助记词
mnemonicHash String @map("mnemonic_hash") @db.VarChar(64) // SHA256哈希用于验证 mnemonicHash String @map("mnemonic_hash") @db.VarChar(64) // SHA256哈希用于验证
// 状态管理 // 状态管理
status String @default("ACTIVE") @db.VarChar(20) // ACTIVE, REVOKED, REPLACED status String @default("ACTIVE") @db.VarChar(20) // ACTIVE, REVOKED, REPLACED
isBackedUp Boolean @default(false) @map("is_backed_up") // 用户是否已备份 isBackedUp Boolean @default(false) @map("is_backed_up") // 用户是否已备份
// 挂失/更换相关 // 挂失/更换相关
revokedAt DateTime? @map("revoked_at") revokedAt DateTime? @map("revoked_at")
revokedReason String? @map("revoked_reason") @db.VarChar(200) revokedReason String? @map("revoked_reason") @db.VarChar(200)
replacedById BigInt? @map("replaced_by_id") // 被哪个新助记词替代 replacedById BigInt? @map("replaced_by_id") // 被哪个新助记词替代
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")
@@unique([accountSequence, status], name: "uk_account_active_mnemonic") // 一个账户只有一个ACTIVE助记词 @@unique([accountSequence, status], name: "uk_account_active_mnemonic") // 一个账户只有一个ACTIVE助记词
@@index([accountSequence], name: "idx_recovery_account") @@index([accountSequence], name: "idx_recovery_account")
@@index([publicKey], name: "idx_recovery_public_key") @@index([publicKey], name: "idx_recovery_public_key")
@@index([status], name: "idx_recovery_status") @@index([status], name: "idx_recovery_status")
@ -217,15 +217,15 @@ model OutboxEvent {
status String @default("PENDING") @db.VarChar(20) status String @default("PENDING") @db.VarChar(20)
// 重试信息 // 重试信息
retryCount Int @default(0) @map("retry_count") retryCount Int @default(0) @map("retry_count")
maxRetries Int @default(10) @map("max_retries") maxRetries Int @default(10) @map("max_retries")
lastError String? @map("last_error") @db.Text lastError String? @map("last_error") @db.Text
nextRetryAt DateTime? @map("next_retry_at") nextRetryAt DateTime? @map("next_retry_at")
// 时间戳 // 时间戳
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")
sentAt DateTime? @map("sent_at") sentAt DateTime? @map("sent_at")
ackedAt DateTime? @map("acked_at") ackedAt DateTime? @map("acked_at")
@@index([status, nextRetryAt], name: "idx_outbox_pending") @@index([status, nextRetryAt], name: "idx_outbox_pending")
@@index([aggregateType, aggregateId], name: "idx_outbox_aggregate") @@index([aggregateType, aggregateId], name: "idx_outbox_aggregate")

View File

@ -1,4 +1,4 @@
import { IsString, IsNumberString, IsInt } from 'class-validator'; import { IsString, IsNumberString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
export class DeriveAddressDto { export class DeriveAddressDto {
@ -6,9 +6,9 @@ export class DeriveAddressDto {
@IsNumberString() @IsNumberString()
userId: string; userId: string;
@ApiProperty({ description: '账户序列号 (8位数字)', example: 10000001 }) @ApiProperty({ description: '账户序列号 (格式: D + YYMMDD + 5位序号)', example: 'D2512110008' })
@IsInt() @IsString()
accountSequence: number; accountSequence: string;
@ApiProperty({ @ApiProperty({
description: '压缩公钥 (33 bytes, 0x02/0x03 开头)', description: '压缩公钥 (33 bytes, 0x02/0x03 开头)',

View File

@ -1,8 +1,8 @@
import { IsInt } from 'class-validator'; import { IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
export class MarkMnemonicBackupDto { export class MarkMnemonicBackupDto {
@ApiProperty({ description: '账户序列号 (8位数字)', example: 10000001 }) @ApiProperty({ description: '账户序列号 (格式: D + YYMMDD + 5位序号)', example: 'D2512110008' })
@IsInt() @IsString()
accountSequence: number; accountSequence: string;
} }

View File

@ -1,13 +1,13 @@
import { IsString, IsInt } from 'class-validator'; import { IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
export class VerifyMnemonicHashDto { export class VerifyMnemonicHashDto {
@ApiProperty({ @ApiProperty({
description: '账户序列号 (8位数字)', description: '账户序列号 (格式: D + YYMMDD + 5位序号)',
example: 10000001, example: 'D2512110008',
}) })
@IsInt() @IsString()
accountSequence: number; accountSequence: string;
@ApiProperty({ @ApiProperty({
description: '助记词 (12个单词空格分隔)', description: '助记词 (12个单词空格分隔)',

View File

@ -58,7 +58,7 @@ export class MpcKeygenCompletedHandler implements OnModuleInit {
const result = await this.addressDerivationService.deriveAndRegister({ const result = await this.addressDerivationService.deriveAndRegister({
userId: BigInt(userId), userId: BigInt(userId),
accountSequence: Number(accountSequence), accountSequence: accountSequence,
publicKey, publicKey,
}); });

View File

@ -18,13 +18,13 @@ import { ChainTypeEnum } from '@/domain/enums';
export interface DeriveAddressParams { export interface DeriveAddressParams {
userId: bigint; userId: bigint;
accountSequence: number; accountSequence: string;
publicKey: string; publicKey: string;
} }
export interface DeriveAddressResult { export interface DeriveAddressResult {
userId: bigint; userId: bigint;
accountSequence: number; accountSequence: string;
publicKey: string; publicKey: string;
addresses: DerivedAddress[]; addresses: DerivedAddress[];
} }
@ -146,7 +146,7 @@ export class AddressDerivationService {
*/ */
private async registerEvmAddressForMonitoring( private async registerEvmAddressForMonitoring(
userId: bigint, userId: bigint,
accountSequence: number, accountSequence: string,
derived: DerivedAddress, derived: DerivedAddress,
): Promise<void> { ): Promise<void> {
const chainType = ChainType.fromEnum(derived.chainType); const chainType = ChainType.fromEnum(derived.chainType);
@ -159,7 +159,7 @@ export class AddressDerivationService {
const monitored = MonitoredAddress.create({ const monitored = MonitoredAddress.create({
chainType, chainType,
address, address,
accountSequence: BigInt(accountSequence), accountSequence,
userId, userId,
}); });

View File

@ -60,7 +60,7 @@ export class DepositRepairService {
id: d.id?.toString() ?? '', id: d.id?.toString() ?? '',
txHash: d.txHash.toString(), txHash: d.txHash.toString(),
userId: d.userId.toString(), userId: d.userId.toString(),
accountSequence: d.accountSequence.toString(), accountSequence: d.accountSequence,
amount: d.amount.toFixed(6), amount: d.amount.toFixed(6),
confirmedAt: d.createdAt?.toISOString() ?? '', confirmedAt: d.createdAt?.toISOString() ?? '',
})), })),
@ -99,7 +99,7 @@ export class DepositRepairService {
amount: deposit.amount.raw.toString(), amount: deposit.amount.raw.toString(),
amountFormatted: deposit.amount.toFixed(8), amountFormatted: deposit.amount.toFixed(8),
confirmations: deposit.confirmations, confirmations: deposit.confirmations,
accountSequence: deposit.accountSequence.toString(), accountSequence: deposit.accountSequence,
userId: deposit.userId.toString(), userId: deposit.userId.toString(),
}); });

View File

@ -3,7 +3,7 @@ import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.servic
import { RecoveryMnemonicAdapter } from '@/infrastructure/blockchain/recovery-mnemonic.adapter'; import { RecoveryMnemonicAdapter } from '@/infrastructure/blockchain/recovery-mnemonic.adapter';
export interface VerifyMnemonicByAccountParams { export interface VerifyMnemonicByAccountParams {
accountSequence: number; accountSequence: string;
mnemonic: string; mnemonic: string;
} }
@ -64,7 +64,7 @@ export class MnemonicVerificationService {
* *
*/ */
async saveRecoveryMnemonic(params: { async saveRecoveryMnemonic(params: {
accountSequence: number; accountSequence: string;
publicKey: string; publicKey: string;
encryptedMnemonic: string; encryptedMnemonic: string;
mnemonicHash: string; mnemonicHash: string;
@ -88,7 +88,7 @@ export class MnemonicVerificationService {
/** /**
* *
*/ */
async markAsBackedUp(accountSequence: number): Promise<void> { async markAsBackedUp(accountSequence: string): Promise<void> {
await this.prisma.recoveryMnemonic.updateMany({ await this.prisma.recoveryMnemonic.updateMany({
where: { where: {
accountSequence, accountSequence,

View File

@ -17,7 +17,7 @@ export interface DepositTransactionProps {
confirmations: number; confirmations: number;
status: DepositStatus; status: DepositStatus;
addressId: bigint; addressId: bigint;
accountSequence: bigint; // 跨服务关联标识 accountSequence: string; // 跨服务关联标识 (格式: D + YYMMDD + 5位序号)
userId: bigint; // 保留兼容 userId: bigint; // 保留兼容
notifiedAt?: Date; notifiedAt?: Date;
notifyAttempts: number; notifyAttempts: number;
@ -74,7 +74,7 @@ export class DepositTransaction extends AggregateRoot<bigint> {
get addressId(): bigint { get addressId(): bigint {
return this.props.addressId; return this.props.addressId;
} }
get accountSequence(): bigint { get accountSequence(): string {
return this.props.accountSequence; return this.props.accountSequence;
} }
get userId(): bigint { get userId(): bigint {
@ -117,7 +117,7 @@ export class DepositTransaction extends AggregateRoot<bigint> {
blockTimestamp: Date; blockTimestamp: Date;
logIndex: number; logIndex: number;
addressId: bigint; addressId: bigint;
accountSequence: bigint; accountSequence: string;
userId: bigint; userId: bigint;
}): DepositTransaction { }): DepositTransaction {
const deposit = new DepositTransaction({ const deposit = new DepositTransaction({
@ -139,7 +139,7 @@ export class DepositTransaction extends AggregateRoot<bigint> {
amountFormatted: params.amount.toFixed(8), amountFormatted: params.amount.toFixed(8),
blockNumber: params.blockNumber.toString(), blockNumber: params.blockNumber.toString(),
blockTimestamp: params.blockTimestamp.toISOString(), blockTimestamp: params.blockTimestamp.toISOString(),
accountSequence: params.accountSequence.toString(), accountSequence: params.accountSequence,
userId: params.userId.toString(), userId: params.userId.toString(),
}), }),
); );
@ -188,7 +188,7 @@ export class DepositTransaction extends AggregateRoot<bigint> {
amount: this.props.amount.raw.toString(), amount: this.props.amount.raw.toString(),
amountFormatted: this.props.amount.toFixed(8), amountFormatted: this.props.amount.toFixed(8),
confirmations: this.props.confirmations, confirmations: this.props.confirmations,
accountSequence: this.props.accountSequence.toString(), accountSequence: this.props.accountSequence,
userId: this.props.userId.toString(), userId: this.props.userId.toString(),
}), }),
); );

View File

@ -5,7 +5,7 @@ export interface MonitoredAddressProps {
id?: bigint; id?: bigint;
chainType: ChainType; chainType: ChainType;
address: EvmAddress; address: EvmAddress;
accountSequence: bigint; // 跨服务关联标识 (全局唯一业务ID) accountSequence: string; // 跨服务关联标识 (格式: D + YYMMDD + 5位序号)
userId: bigint; // 保留兼容 userId: bigint; // 保留兼容
isActive: boolean; isActive: boolean;
createdAt?: Date; createdAt?: Date;
@ -30,7 +30,7 @@ export class MonitoredAddress extends AggregateRoot<bigint> {
get address(): EvmAddress { get address(): EvmAddress {
return this.props.address; return this.props.address;
} }
get accountSequence(): bigint { get accountSequence(): string {
return this.props.accountSequence; return this.props.accountSequence;
} }
get userId(): bigint { get userId(): bigint {
@ -52,7 +52,7 @@ export class MonitoredAddress extends AggregateRoot<bigint> {
static create(params: { static create(params: {
chainType: ChainType; chainType: ChainType;
address: EvmAddress; address: EvmAddress;
accountSequence: bigint; accountSequence: string;
userId: bigint; userId: bigint;
}): MonitoredAddress { }): MonitoredAddress {
return new MonitoredAddress({ return new MonitoredAddress({

View File

@ -2,7 +2,7 @@ import { DomainEvent } from './domain-event.base';
export interface WalletAddressCreatedPayload { export interface WalletAddressCreatedPayload {
userId: string; userId: string;
accountSequence: number; // 8位账户序列号 accountSequence: string; // 账户序列号 (格式: D + YYMMDD + 5位序号)
publicKey: string; publicKey: string;
addresses: { addresses: {
chainType: string; chainType: string;

View File

@ -23,7 +23,7 @@ export interface KeygenCompletedPayload {
threshold: string; threshold: string;
extraPayload?: { extraPayload?: {
userId: string; userId: string;
accountSequence: number; // 8位账户序列号用于关联恢复助记词 accountSequence: string; // 账户序列号 (格式: D + YYMMDD + 5位序号)
username: string; username: string;
delegateShare?: { delegateShare?: {
partyId: string; partyId: string;

View File

@ -5,7 +5,7 @@ import { ConfigService } from '@nestjs/config';
interface JwtPayload { interface JwtPayload {
userId: string; userId: string;
accountSequence: number; accountSequence: string;
deviceId: string; deviceId: string;
type: 'access' | 'refresh'; type: 'access' | 'refresh';
iat: number; iat: number;

View File

@ -9,13 +9,13 @@ datasource db {
model UserAccount { model UserAccount {
userId BigInt @id @default(autoincrement()) @map("user_id") userId BigInt @id @default(autoincrement()) @map("user_id")
accountSequence BigInt @unique @map("account_sequence") accountSequence String @unique @map("account_sequence") @db.VarChar(12) // 格式: D + YYMMDD + 5位序号
phoneNumber String? @unique @map("phone_number") @db.VarChar(20) phoneNumber String? @unique @map("phone_number") @db.VarChar(20)
nickname String @db.VarChar(100) nickname String @db.VarChar(100)
avatarUrl String? @map("avatar_url") @db.Text avatarUrl String? @map("avatar_url") @db.Text
inviterSequence BigInt? @map("inviter_sequence") inviterSequence String? @map("inviter_sequence") @db.VarChar(12) // 推荐人序列号
referralCode String @unique @map("referral_code") @db.VarChar(10) referralCode String @unique @map("referral_code") @db.VarChar(10)
kycStatus String @default("NOT_VERIFIED") @map("kyc_status") @db.VarChar(20) kycStatus String @default("NOT_VERIFIED") @map("kyc_status") @db.VarChar(20)
@ -102,8 +102,9 @@ model WalletAddress {
} }
model AccountSequenceGenerator { model AccountSequenceGenerator {
id Int @id @default(1) id Int @id @default(autoincrement())
currentSequence BigInt @default(0) @map("current_sequence") dateKey String @unique @map("date_key") @db.VarChar(6) // 格式: YYMMDD
currentSequence Int @default(0) @map("current_sequence") // 当日序号 (0-99999)
updatedAt DateTime @updatedAt @map("updated_at") updatedAt DateTime @updatedAt @map("updated_at")
@@map("account_sequence_generator") @@map("account_sequence_generator")

View File

@ -4,42 +4,35 @@ const prisma = new PrismaClient();
// ============================================ // ============================================
// 系统账户定义 // 系统账户定义
// 系统账户使用特殊序列号格式: S + 00000 + 序号 (S0000000001 ~ S0000000004)
// ============================================ // ============================================
const SYSTEM_ACCOUNTS = [ const SYSTEM_ACCOUNTS = [
{ {
userId: BigInt(1), userId: BigInt(1),
accountSequence: BigInt(1), accountSequence: 'S0000000001', // 总部社区
nickname: '总部社区', nickname: '总部社区',
referralCode: 'HQ000001', referralCode: 'HQ000001',
provinceCode: '000000',
cityCode: '000000',
status: 'SYSTEM', status: 'SYSTEM',
}, },
{ {
userId: BigInt(2), userId: BigInt(2),
accountSequence: BigInt(2), accountSequence: 'S0000000002', // 成本费账户
nickname: '成本费账户', nickname: '成本费账户',
referralCode: 'COST0002', referralCode: 'COST0002',
provinceCode: '000000',
cityCode: '000000',
status: 'SYSTEM', status: 'SYSTEM',
}, },
{ {
userId: BigInt(3), userId: BigInt(3),
accountSequence: BigInt(3), accountSequence: 'S0000000003', // 运营费账户
nickname: '运营费账户', nickname: '运营费账户',
referralCode: 'OPER0003', referralCode: 'OPER0003',
provinceCode: '000000',
cityCode: '000000',
status: 'SYSTEM', status: 'SYSTEM',
}, },
{ {
userId: BigInt(4), userId: BigInt(4),
accountSequence: BigInt(4), accountSequence: 'S0000000004', // RWAD底池账户
nickname: 'RWAD底池账户', nickname: 'RWAD底池账户',
referralCode: 'POOL0004', referralCode: 'POOL0004',
provinceCode: '000000',
cityCode: '000000',
status: 'SYSTEM', status: 'SYSTEM',
}, },
]; ];
@ -56,12 +49,19 @@ async function main() {
await prisma.userDevice.deleteMany(); await prisma.userDevice.deleteMany();
await prisma.userAccount.deleteMany(); await prisma.userAccount.deleteMany();
// 初始化账户序列号生成器 (从100000开始系统账户使用1-99) // 初始化账户序列号生成器 (新格式: D + YYMMDD + 5位序号)
await prisma.accountSequenceGenerator.deleteMany(); await prisma.accountSequenceGenerator.deleteMany();
const today = new Date();
const year = String(today.getFullYear()).slice(-2);
const month = String(today.getMonth() + 1).padStart(2, '0');
const day = String(today.getDate()).padStart(2, '0');
const dateKey = `${year}${month}${day}`;
await prisma.accountSequenceGenerator.create({ await prisma.accountSequenceGenerator.create({
data: { data: {
id: 1, id: 1,
currentSequence: BigInt(100000), // 普通用户从100000开始 dateKey: dateKey,
currentSequence: 0,
}, },
}); });
@ -73,12 +73,12 @@ async function main() {
update: account, update: account,
create: account, create: account,
}); });
console.log(` - Created system account: ${account.nickname} (userId=${account.userId})`); console.log(` - Created system account: ${account.nickname} (accountSequence=${account.accountSequence})`);
} }
console.log('Database seeded successfully!'); console.log('Database seeded successfully!');
console.log('- Initialized account sequence generator starting at 100000'); console.log(`- Initialized account sequence generator for date ${dateKey}`);
console.log(`- Created ${SYSTEM_ACCOUNTS.length} system accounts (userId 1-4)`); console.log(`- Created ${SYSTEM_ACCOUNTS.length} system accounts (S0000000001-S0000000004)`);
} }
main() main()

View File

@ -112,8 +112,8 @@ export class RemoveDeviceDto {
// Response DTOs // Response DTOs
export class AutoCreateAccountResponseDto { export class AutoCreateAccountResponseDto {
@ApiProperty({ example: 100001, description: '用户序列号 (唯一标识)' }) @ApiProperty({ example: 'D2512110001', description: '用户序列号 (格式: D + YYMMDD + 5位序号)' })
userSerialNum: number; userSerialNum: string;
@ApiProperty({ example: 'ABC123', description: '推荐码' }) @ApiProperty({ example: 'ABC123', description: '推荐码' })
referralCode: string; referralCode: string;
@ -135,8 +135,8 @@ export class RecoverAccountResponseDto {
@ApiProperty() @ApiProperty()
userId: string; userId: string;
@ApiProperty() @ApiProperty({ example: 'D2512110001', description: '账户序列号 (格式: D + YYMMDD + 5位序号)' })
accountSequence: number; accountSequence: string;
@ApiProperty() @ApiProperty()
nickname: string; nickname: string;
@ -188,8 +188,8 @@ export class LoginResponseDto {
@ApiProperty() @ApiProperty()
userId: string; userId: string;
@ApiProperty() @ApiProperty({ example: 'D2512110001', description: '账户序列号 (格式: D + YYMMDD + 5位序号)' })
accountSequence: number; accountSequence: string;
@ApiProperty() @ApiProperty()
accessToken: string; accessToken: string;
@ -216,8 +216,8 @@ export class MeResponseDto {
@ApiProperty() @ApiProperty()
userId: string; userId: string;
@ApiProperty({ description: '账户序列号' }) @ApiProperty({ example: 'D2512110001', description: '账户序列号 (格式: D + YYMMDD + 5位序号)' })
accountSequence: number; accountSequence: string;
@ApiProperty({ nullable: true }) @ApiProperty({ nullable: true })
phoneNumber: string | null; phoneNumber: string | null;
@ -234,8 +234,8 @@ export class MeResponseDto {
@ApiProperty({ description: '完整推荐链接' }) @ApiProperty({ description: '完整推荐链接' })
referralLink: string; referralLink: string;
@ApiProperty({ description: '推荐人序列号', nullable: true }) @ApiProperty({ example: 'D2512110001', description: '推荐人序列号', nullable: true })
inviterSequence: number | null; inviterSequence: string | null;
@ApiProperty({ description: '钱包地址列表' }) @ApiProperty({ description: '钱包地址列表' })
walletAddresses: Array<{ chainType: string; address: string }>; walletAddresses: Array<{ chainType: string; address: string }>;
@ -259,7 +259,7 @@ export class ReferralValidationResponseDto {
@ApiPropertyOptional({ description: '邀请人信息' }) @ApiPropertyOptional({ description: '邀请人信息' })
inviterInfo?: { inviterInfo?: {
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
nickname: string; nickname: string;
avatarUrl: string | null; avatarUrl: string | null;
}; };
@ -292,8 +292,8 @@ export class ReferralLinkResponseDto {
} }
export class InviteRecordDto { export class InviteRecordDto {
@ApiProperty() @ApiProperty({ example: 'D2512110001', description: '账户序列号 (格式: D + YYMMDD + 5位序号)' })
accountSequence: number; accountSequence: string;
@ApiProperty() @ApiProperty()
nickname: string; nickname: string;

View File

@ -1,10 +1,11 @@
import { IsString, IsOptional, IsNotEmpty, IsNumber } from 'class-validator'; import { IsString, IsOptional, IsNotEmpty, Matches } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class RecoverByMnemonicDto { export class RecoverByMnemonicDto {
@ApiProperty({ example: 10001 }) @ApiProperty({ example: 'D2512110001', description: '账户序列号 (格式: D + YYMMDD + 5位序号)' })
@IsNumber() @IsString()
accountSequence: number; @Matches(/^D\d{11}$/, { message: '账户序列号格式错误,应为 D + 年月日(6位) + 序号(5位)' })
accountSequence: string;
@ApiProperty({ example: 'abandon ability able about above absent absorb abstract absurd abuse access accident' }) @ApiProperty({ example: 'abandon ability able about above absent absorb abstract absurd abuse access accident' })
@IsString() @IsString()

View File

@ -1,10 +1,11 @@
import { IsString, IsOptional, IsNotEmpty, IsNumber, Matches } from 'class-validator'; import { IsString, IsOptional, IsNotEmpty, Matches } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class RecoverByPhoneDto { export class RecoverByPhoneDto {
@ApiProperty({ example: 10001 }) @ApiProperty({ example: 'D2512110001', description: '账户序列号 (格式: D + YYMMDD + 5位序号)' })
@IsNumber() @IsString()
accountSequence: number; @Matches(/^D\d{11}$/, { message: '账户序列号格式错误,应为 D + 年月日(6位) + 序号(5位)' })
accountSequence: string;
@ApiProperty({ example: '13800138000' }) @ApiProperty({ example: '13800138000' })
@IsString() @IsString()

View File

@ -20,8 +20,8 @@ export class UserProfileDto {
@ApiProperty() @ApiProperty()
userId: string; userId: string;
@ApiProperty() @ApiProperty({ example: 'D2512110001', description: '账户序列号 (格式: D + YYMMDD + 5位序号)' })
accountSequence: number; accountSequence: string;
@ApiProperty({ nullable: true }) @ApiProperty({ nullable: true })
phoneNumber: string | null; phoneNumber: string | null;

View File

@ -18,7 +18,7 @@ export class AutoCreateAccountCommand {
export class RecoverByMnemonicCommand { export class RecoverByMnemonicCommand {
constructor( constructor(
public readonly accountSequence: number, public readonly accountSequence: string, // 格式: D + YYMMDD + 5位序号
public readonly mnemonic: string, public readonly mnemonic: string,
public readonly newDeviceId: string, public readonly newDeviceId: string,
public readonly deviceName?: string, public readonly deviceName?: string,
@ -27,7 +27,7 @@ export class RecoverByMnemonicCommand {
export class RecoverByPhoneCommand { export class RecoverByPhoneCommand {
constructor( constructor(
public readonly accountSequence: number, public readonly accountSequence: string, // 格式: D + YYMMDD + 5位序号
public readonly phoneNumber: string, public readonly phoneNumber: string,
public readonly smsCode: string, public readonly smsCode: string,
public readonly newDeviceId: string, public readonly newDeviceId: string,
@ -150,7 +150,7 @@ export class GenerateReferralLinkCommand {
} }
export class GetWalletStatusQuery { export class GetWalletStatusQuery {
constructor(public readonly userSerialNum: number) {} constructor(public readonly userSerialNum: string) {} // 格式: D + YYMMDD + 5位序号
} }
export class MarkMnemonicBackedUpCommand { export class MarkMnemonicBackedUpCommand {
@ -173,7 +173,7 @@ export interface WalletStatusResult {
errorMessage?: string; // 失败原因 (failed 状态时返回) errorMessage?: string; // 失败原因 (failed 状态时返回)
} }
export interface AutoCreateAccountResult { export interface AutoCreateAccountResult {
userSerialNum: number; // 用户序列号 userSerialNum: string; // 用户序列号 (格式: D + YYMMDD + 5位序号)
referralCode: string; // 推荐码 referralCode: string; // 推荐码
username: string; // 随机用户名 username: string; // 随机用户名
avatarSvg: string; // 随机SVG头像 avatarSvg: string; // 随机SVG头像
@ -183,7 +183,7 @@ export interface AutoCreateAccountResult {
export interface RecoverAccountResult { export interface RecoverAccountResult {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
nickname: string; nickname: string;
avatarUrl: string | null; avatarUrl: string | null;
referralCode: string; referralCode: string;
@ -193,14 +193,14 @@ export interface RecoverAccountResult {
export interface AutoLoginResult { export interface AutoLoginResult {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
accessToken: string; accessToken: string;
refreshToken: string; refreshToken: string;
} }
export interface RegisterResult { export interface RegisterResult {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
referralCode: string; referralCode: string;
accessToken: string; accessToken: string;
refreshToken: string; refreshToken: string;
@ -208,14 +208,14 @@ export interface RegisterResult {
export interface LoginResult { export interface LoginResult {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
accessToken: string; accessToken: string;
refreshToken: string; refreshToken: string;
} }
export interface UserProfileDTO { export interface UserProfileDTO {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
phoneNumber: string | null; phoneNumber: string | null;
nickname: string; nickname: string;
avatarUrl: string | null; avatarUrl: string | null;
@ -238,7 +238,7 @@ export interface DeviceDTO {
export interface UserBriefDTO { export interface UserBriefDTO {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
nickname: string; nickname: string;
avatarUrl: string | null; avatarUrl: string | null;
} }
@ -247,7 +247,7 @@ export interface ReferralCodeValidationResult {
valid: boolean; valid: boolean;
referralCode?: string; referralCode?: string;
inviterInfo?: { inviterInfo?: {
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
nickname: string; nickname: string;
avatarUrl: string | null; avatarUrl: string | null;
}; };
@ -273,7 +273,7 @@ export interface ReferralStatsResult {
thisWeekInvites: number; // 本周邀请 thisWeekInvites: number; // 本周邀请
thisMonthInvites: number; // 本月邀请 thisMonthInvites: number; // 本月邀请
recentInvites: Array<{ // 最近邀请记录 recentInvites: Array<{ // 最近邀请记录
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
nickname: string; nickname: string;
avatarUrl: string | null; avatarUrl: string | null;
registeredAt: Date; registeredAt: Date;
@ -283,13 +283,13 @@ export interface ReferralStatsResult {
export interface MeResult { export interface MeResult {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
phoneNumber: string | null; phoneNumber: string | null;
nickname: string; nickname: string;
avatarUrl: string | null; avatarUrl: string | null;
referralCode: string; referralCode: string;
referralLink: string; // 完整推荐链接 referralLink: string; // 完整推荐链接
inviterSequence: number | null; // 推荐人序列号 inviterSequence: string | null; // 推荐人序列号 (格式: D + YYMMDD + 5位序号)
walletAddresses: Array<{ chainType: string; address: string }>; walletAddresses: Array<{ chainType: string; address: string }>;
kycStatus: string; kycStatus: string;
status: string; status: string;

View File

@ -1,6 +1,6 @@
export class RecoverByMnemonicCommand { export class RecoverByMnemonicCommand {
constructor( constructor(
public readonly accountSequence: number, public readonly accountSequence: string, // 格式: D + YYMMDD + 5位序号
public readonly mnemonic: string, public readonly mnemonic: string,
public readonly newDeviceId: string, public readonly newDeviceId: string,
public readonly deviceName?: string, public readonly deviceName?: string,

View File

@ -1,6 +1,6 @@
export class RecoverByPhoneCommand { export class RecoverByPhoneCommand {
constructor( constructor(
public readonly accountSequence: number, public readonly accountSequence: string, // 格式: D + YYMMDD + 5位序号
public readonly phoneNumber: string, public readonly phoneNumber: string,
public readonly smsCode: string, public readonly smsCode: string,
public readonly newDeviceId: string, public readonly newDeviceId: string,

View File

@ -7,7 +7,7 @@ import { ApplicationError } from '@/shared/exceptions/domain.exception';
export interface TokenPayload { export interface TokenPayload {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
deviceId: string; deviceId: string;
type: 'access' | 'refresh'; type: 'access' | 'refresh';
} }
@ -22,7 +22,7 @@ export class TokenService {
async generateTokenPair(payload: { async generateTokenPair(payload: {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
deviceId: string; deviceId: string;
}): Promise<{ accessToken: string; refreshToken: string }> { }): Promise<{ accessToken: string; refreshToken: string }> {
const accessToken = this.jwtService.sign( const accessToken = this.jwtService.sign(
@ -51,7 +51,7 @@ export class TokenService {
async verifyRefreshToken(token: string): Promise<{ async verifyRefreshToken(token: string): Promise<{
userId: string; userId: string;
accountSequence: number; accountSequence: string;
deviceId: string; deviceId: string;
}> { }> {
try { try {

View File

@ -147,9 +147,9 @@ export class UserAccount {
} }
static reconstruct(params: { static reconstruct(params: {
userId: string; accountSequence: number; devices: DeviceInfo[]; userId: string; accountSequence: string; devices: DeviceInfo[];
phoneNumber: string | null; nickname: string; avatarUrl: string | null; phoneNumber: string | null; nickname: string; avatarUrl: string | null;
inviterSequence: number | null; referralCode: string; inviterSequence: string | null; referralCode: string;
walletAddresses: WalletAddress[]; kycInfo: KYCInfo | null; walletAddresses: WalletAddress[]; kycInfo: KYCInfo | null;
kycStatus: KYCStatus; status: AccountStatus; kycStatus: KYCStatus; status: AccountStatus;
registeredAt: Date; lastLoginAt: Date | null; updatedAt: Date; registeredAt: Date; lastLoginAt: Date | null; updatedAt: Date;

View File

@ -14,9 +14,9 @@ export class UserAccountAutoCreatedEvent extends DomainEvent {
constructor( constructor(
public readonly payload: { public readonly payload: {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
initialDeviceId: string; initialDeviceId: string;
inviterSequence: number | null; inviterSequence: string | null; // 格式: D + YYMMDD + 5位序号
registeredAt: Date; registeredAt: Date;
}, },
) { ) {
@ -32,10 +32,10 @@ export class UserAccountCreatedEvent extends DomainEvent {
constructor( constructor(
public readonly payload: { public readonly payload: {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
phoneNumber: string; phoneNumber: string;
initialDeviceId: string; initialDeviceId: string;
inviterSequence: number | null; inviterSequence: string | null; // 格式: D + YYMMDD + 5位序号
registeredAt: Date; registeredAt: Date;
}, },
) { ) {
@ -51,7 +51,7 @@ export class DeviceAddedEvent extends DomainEvent {
constructor( constructor(
public readonly payload: { public readonly payload: {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
deviceId: string; deviceId: string;
deviceName: string; deviceName: string;
}, },
@ -177,7 +177,7 @@ export class MpcKeygenRequestedEvent extends DomainEvent {
public readonly payload: { public readonly payload: {
sessionId: string; sessionId: string;
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
username: string; username: string;
threshold: number; threshold: number;
totalParties: number; totalParties: number;

View File

@ -1,19 +1,58 @@
import { DomainError } from '@/shared/exceptions/domain.exception'; import { DomainError } from '@/shared/exceptions/domain.exception';
/**
*
* 格式: D + (2) + (2) + (2) + 5
* 示例: D2512110008 -> 202512118
*/
export class AccountSequence { export class AccountSequence {
constructor(public readonly value: number) { private static readonly PATTERN = /^D\d{11}$/;
if (value <= 0) throw new DomainError('账户序列号必须大于0');
constructor(public readonly value: string) {
if (!AccountSequence.PATTERN.test(value)) {
throw new DomainError(`账户序列号格式无效: ${value},应为 D + 年月日(6位) + 序号(5位)`);
}
} }
static create(value: number): AccountSequence { static create(value: string): AccountSequence {
return new AccountSequence(value); return new AccountSequence(value);
} }
static next(current: AccountSequence): AccountSequence { /**
return new AccountSequence(current.value + 1); *
* @param date
* @param dailySequence (0-99999)
*/
static generate(date: Date, dailySequence: number): AccountSequence {
if (dailySequence < 0 || dailySequence > 99999) {
throw new DomainError(`当日序号超出范围: ${dailySequence},应为 0-99999`);
}
const year = String(date.getFullYear()).slice(-2);
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const seq = String(dailySequence).padStart(5, '0');
return new AccountSequence(`D${year}${month}${day}${seq}`);
}
/**
* (YYMMDD)
*/
get dateString(): string {
return this.value.slice(1, 7);
}
/**
*
*/
get dailySequence(): number {
return parseInt(this.value.slice(7), 10);
} }
equals(other: AccountSequence): boolean { equals(other: AccountSequence): boolean {
return this.value === other.value; return this.value === other.value;
} }
toString(): string {
return this.value;
}
} }

View File

@ -32,23 +32,8 @@ export class UserId {
} }
// ============ AccountSequence ============ // ============ AccountSequence ============
export class AccountSequence { // 导出新格式的账户序列号 (D + YYMMDD + 5位序号)
constructor(public readonly value: number) { export { AccountSequence } from './account-sequence.vo';
if (value <= 0) throw new DomainError('账户序列号必须大于0');
}
static create(value: number): AccountSequence {
return new AccountSequence(value);
}
static next(current: AccountSequence): AccountSequence {
return new AccountSequence(current.value + 1);
}
equals(other: AccountSequence): boolean {
return this.value === other.value;
}
}
// ============ PhoneNumber ============ // ============ PhoneNumber ============
export class PhoneNumber { export class PhoneNumber {

View File

@ -26,7 +26,7 @@ export interface VerifyMnemonicResult {
} }
export interface VerifyMnemonicByAccountParams { export interface VerifyMnemonicByAccountParams {
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
mnemonic: string; mnemonic: string;
} }
@ -144,7 +144,7 @@ export class BlockchainClientService {
/** /**
* *
*/ */
async markMnemonicBackedUp(accountSequence: number): Promise<void> { async markMnemonicBackedUp(accountSequence: string): Promise<void> {
this.logger.log(`Marking mnemonic as backed up for account ${accountSequence}`); this.logger.log(`Marking mnemonic as backed up for account ${accountSequence}`);
try { try {

View File

@ -32,7 +32,7 @@ export interface KeygenCompletedPayload {
threshold: string; threshold: string;
extraPayload?: { extraPayload?: {
userId: string; userId: string;
accountSequence: number; // 8位账户序列 accountSequence: string; // 格式: D + YYMMDD + 5位序
username: string; username: string;
delegateShare?: { delegateShare?: {
partyId: string; partyId: string;

View File

@ -1,11 +1,11 @@
// Prisma Entity Types - 用于Mapper转换 // Prisma Entity Types - 用于Mapper转换
export interface UserAccountEntity { export interface UserAccountEntity {
userId: bigint; userId: bigint;
accountSequence: bigint; accountSequence: string; // 格式: D + YYMMDD + 5位序号
phoneNumber: string | null; phoneNumber: string | null;
nickname: string; nickname: string;
avatarUrl: string | null; avatarUrl: string | null;
inviterSequence: bigint | null; inviterSequence: string | null; // 格式: D + YYMMDD + 5位序号
referralCode: string; referralCode: string;
kycStatus: string; kycStatus: string;
realName: string | null; realName: string | null;

View File

@ -45,12 +45,12 @@ export class UserAccountMapper {
return UserAccount.reconstruct({ return UserAccount.reconstruct({
userId: entity.userId.toString(), userId: entity.userId.toString(),
accountSequence: Number(entity.accountSequence), accountSequence: entity.accountSequence, // 现在是字符串类型
devices, devices,
phoneNumber: entity.phoneNumber, phoneNumber: entity.phoneNumber,
nickname: entity.nickname, nickname: entity.nickname,
avatarUrl: entity.avatarUrl, avatarUrl: entity.avatarUrl,
inviterSequence: entity.inviterSequence ? Number(entity.inviterSequence) : null, inviterSequence: entity.inviterSequence, // 现在是字符串类型
referralCode: entity.referralCode, referralCode: entity.referralCode,
walletAddresses: wallets, walletAddresses: wallets,
kycInfo, kycInfo,

View File

@ -26,11 +26,11 @@ export class UserAccountRepositoryImpl implements UserAccountRepository {
// 新账户让数据库自动生成userId // 新账户让数据库自动生成userId
const created = await tx.userAccount.create({ const created = await tx.userAccount.create({
data: { data: {
accountSequence: BigInt(account.accountSequence.value), accountSequence: account.accountSequence.value,
phoneNumber: account.phoneNumber?.value || null, phoneNumber: account.phoneNumber?.value || null,
nickname: account.nickname, nickname: account.nickname,
avatarUrl: account.avatarUrl, avatarUrl: account.avatarUrl,
inviterSequence: account.inviterSequence ? BigInt(account.inviterSequence.value) : null, inviterSequence: account.inviterSequence?.value || null,
referralCode: account.referralCode.value, referralCode: account.referralCode.value,
kycStatus: account.kycStatus, kycStatus: account.kycStatus,
realName: account.kycInfo?.realName || null, realName: account.kycInfo?.realName || null,
@ -125,7 +125,7 @@ export class UserAccountRepositoryImpl implements UserAccountRepository {
async findByAccountSequence(sequence: AccountSequence): Promise<UserAccount | null> { async findByAccountSequence(sequence: AccountSequence): Promise<UserAccount | null> {
const data = await this.prisma.userAccount.findUnique({ const data = await this.prisma.userAccount.findUnique({
where: { accountSequence: BigInt(sequence.value) }, where: { accountSequence: sequence.value },
include: { devices: true, walletAddresses: true }, include: { devices: true, walletAddresses: true },
}); });
return data ? this.toDomain(data) : null; return data ? this.toDomain(data) : null;
@ -163,18 +163,38 @@ export class UserAccountRepositoryImpl implements UserAccountRepository {
async getMaxAccountSequence(): Promise<AccountSequence | null> { async getMaxAccountSequence(): Promise<AccountSequence | null> {
const result = await this.prisma.userAccount.aggregate({ _max: { accountSequence: true } }); const result = await this.prisma.userAccount.aggregate({ _max: { accountSequence: true } });
return result._max.accountSequence ? AccountSequence.create(Number(result._max.accountSequence)) : null; return result._max.accountSequence ? AccountSequence.create(result._max.accountSequence) : null;
} }
async getNextAccountSequence(): Promise<AccountSequence> { async getNextAccountSequence(): Promise<AccountSequence> {
const now = new Date();
const year = String(now.getFullYear()).slice(-2);
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const dateKey = `${year}${month}${day}`;
const result = await this.prisma.$transaction(async (tx) => { const result = await this.prisma.$transaction(async (tx) => {
const updated = await tx.accountSequenceGenerator.update({ // 尝试更新当日记录,如果不存在则创建
where: { id: 1 }, const existing = await tx.accountSequenceGenerator.findUnique({
data: { currentSequence: { increment: 1 } }, where: { dateKey },
}); });
return updated.currentSequence;
if (existing) {
const updated = await tx.accountSequenceGenerator.update({
where: { dateKey },
data: { currentSequence: { increment: 1 } },
});
return updated.currentSequence;
} else {
// 当日第一个用户,创建新记录
const created = await tx.accountSequenceGenerator.create({
data: { dateKey, currentSequence: 0 },
});
return created.currentSequence;
}
}); });
return AccountSequence.create(Number(result));
return AccountSequence.generate(now, result);
} }
async findUsers( async findUsers(
@ -247,12 +267,12 @@ export class UserAccountRepositoryImpl implements UserAccountRepository {
return UserAccount.reconstruct({ return UserAccount.reconstruct({
userId: data.userId.toString(), userId: data.userId.toString(),
accountSequence: Number(data.accountSequence), accountSequence: data.accountSequence,
devices, devices,
phoneNumber: data.phoneNumber, phoneNumber: data.phoneNumber,
nickname: data.nickname, nickname: data.nickname,
avatarUrl: data.avatarUrl, avatarUrl: data.avatarUrl,
inviterSequence: data.inviterSequence ? Number(data.inviterSequence) : null, inviterSequence: data.inviterSequence || null,
referralCode: data.referralCode, referralCode: data.referralCode,
walletAddresses: wallets, walletAddresses: wallets,
kycInfo, kycInfo,
@ -268,7 +288,7 @@ export class UserAccountRepositoryImpl implements UserAccountRepository {
async findByInviterSequence(inviterSequence: AccountSequence): Promise<UserAccount[]> { async findByInviterSequence(inviterSequence: AccountSequence): Promise<UserAccount[]> {
const data = await this.prisma.userAccount.findMany({ const data = await this.prisma.userAccount.findMany({
where: { inviterSequence: BigInt(inviterSequence.value) }, where: { inviterSequence: inviterSequence.value },
include: { devices: true, walletAddresses: true }, include: { devices: true, walletAddresses: true },
orderBy: { registeredAt: 'desc' }, orderBy: { registeredAt: 'desc' },
}); });

View File

@ -5,14 +5,14 @@ import { UnauthorizedException } from '@/shared/exceptions/domain.exception';
export interface JwtPayload { export interface JwtPayload {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
deviceId: string; deviceId: string;
type: 'access' | 'refresh'; type: 'access' | 'refresh';
} }
export interface CurrentUserData { export interface CurrentUserData {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
deviceId: string; deviceId: string;
} }

View File

@ -5,7 +5,7 @@ import { ConfigService } from '@nestjs/config';
export interface JwtPayload { export interface JwtPayload {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
deviceId: string; deviceId: string;
type: 'access' | 'refresh'; type: 'access' | 'refresh';
iat: number; iat: number;

View File

@ -3,7 +3,7 @@
*/ */
// 生成用户名: 榴莲女皇x号 // 生成用户名: 榴莲女皇x号
export function generateUsername(accountSequence: number): string { export function generateUsername(accountSequence: string): string {
return `榴莲女皇${accountSequence}`; return `榴莲女皇${accountSequence}`;
} }
@ -132,9 +132,9 @@ export function generateRandomAvatarSvg(): string {
/** /**
* *
* @param accountSequence * @param accountSequence (格式: D + YYMMDD + 5)
*/ */
export function generateIdentity(accountSequence: number): { username: string; avatarSvg: string } { export function generateIdentity(accountSequence: string): { username: string; avatarSvg: string } {
return { return {
username: generateUsername(accountSequence), username: generateUsername(accountSequence),
avatarSvg: generateRandomAvatarSvg(), avatarSvg: generateRandomAvatarSvg(),

View File

@ -81,7 +81,7 @@ export class KeygenRequestedHandler implements OnModuleInit {
try { try {
const deriveResult = await this.blockchainClient.deriveAddresses({ const deriveResult = await this.blockchainClient.deriveAddresses({
userId, userId,
accountSequence, // 8位账户序列号用于关联恢复助记词 accountSequence, // 账户序列号,格式: D + YYMMDD + 5位序号如 D2512110008
publicKey: result.publicKey, publicKey: result.publicKey,
}); });
derivedAddresses = deriveResult.addresses; derivedAddresses = deriveResult.addresses;
@ -131,7 +131,7 @@ export class KeygenRequestedHandler implements OnModuleInit {
// Add extra payload for identity-service // Add extra payload for identity-service
(completedEvent as any).extraPayload = { (completedEvent as any).extraPayload = {
userId, userId,
accountSequence, // 8位账户序列号用于关联恢复助记词 accountSequence, // 账户序列号,格式: D + YYMMDD + 5位序号如 D2512110008
username, username,
delegateShare: result.delegateShare, delegateShare: result.delegateShare,
derivedAddresses, // BSC, KAVA, DST addresses derivedAddresses, // BSC, KAVA, DST addresses

View File

@ -13,7 +13,7 @@ import * as jwt from 'jsonwebtoken';
export interface StoreBackupShareParams { export interface StoreBackupShareParams {
userId: string; userId: string;
accountSequence: number; accountSequence: string;
username: string; username: string;
publicKey: string; publicKey: string;
partyId: string; partyId: string;

View File

@ -11,7 +11,7 @@ import { firstValueFrom } from 'rxjs';
export interface DeriveAddressParams { export interface DeriveAddressParams {
userId: string; userId: string;
accountSequence: number; // 8位账户序列号用于关联恢复助记词 accountSequence: string; // 账户序列号,格式: D + YYMMDD + 5位序号如 D2512110008
publicKey: string; publicKey: string;
} }

View File

@ -18,7 +18,7 @@ export const MPC_CONSUME_TOPICS = {
export interface KeygenRequestedPayload { export interface KeygenRequestedPayload {
sessionId: string; sessionId: string;
userId: string; userId: string;
accountSequence: number; // 8位账户序列号用于关联恢复助记词 accountSequence: string; // 账户序列号,格式: D + YYMMDD + 5位序号如 D2512110008
username: string; username: string;
threshold: number; threshold: number;
totalParties: number; totalParties: number;

View File

@ -32,7 +32,7 @@ import {
import { JwtAuthGuard } from '../guards/jwt-auth.guard'; import { JwtAuthGuard } from '../guards/jwt-auth.guard';
interface AuthenticatedRequest { interface AuthenticatedRequest {
user: { id: string; accountSequence: number }; user: { id: string; accountSequence: string };
} }
@ApiTags('认种订单') @ApiTags('认种订单')

View File

@ -10,7 +10,7 @@ import * as jwt from 'jsonwebtoken';
export interface JwtPayload { export interface JwtPayload {
sub: string; sub: string;
userId: string; userId: string;
accountSequence: number; accountSequence: string;
iat: number; iat: number;
exp: number; exp: number;
} }

View File

@ -173,7 +173,7 @@ export class PlantingApplicationService {
async payOrder( async payOrder(
orderNo: string, orderNo: string,
userId: bigint, userId: bigint,
accountSequence?: number, accountSequence?: string,
): Promise<{ ): Promise<{
orderNo: string; orderNo: string;
status: string; status: string;
@ -207,20 +207,8 @@ export class PlantingApplicationService {
); );
} }
// 3. 获取推荐链上下文 (先获取,确保服务可用) // 注意:资金分配已移至 reward-service由其调用 authorization-service 进行考核后分配
const referralContext = await this.referralService.getReferralContext( // planting-service 只负责:冻结 → 扣款 → 发事件
accountSequence!,
selection.provinceCode,
selection.cityCode,
);
this.logger.log(`Referral context fetched: ${JSON.stringify(referralContext)}`);
// 4. 预计算资金分配 (纯内存计算,无副作用)
const allocations = this.fundAllocationService.calculateAllocations(
order,
referralContext,
);
this.logger.log(`Fund allocations calculated: ${allocations.length} targets`);
// ==================== 冻结阶段 ==================== // ==================== 冻结阶段 ====================
// 5. 冻结用户资金(幂等,可回滚) // 5. 冻结用户资金(幂等,可回滚)
@ -228,6 +216,7 @@ export class PlantingApplicationService {
try { try {
await this.walletService.freezeForPlanting({ await this.walletService.freezeForPlanting({
userId: userId.toString(), userId: userId.toString(),
accountSequence: accountSequence,
amount: order.totalAmount, amount: order.totalAmount,
orderId: order.orderNo, orderId: order.orderNo,
}); });
@ -243,9 +232,9 @@ export class PlantingApplicationService {
// ==================== 执行阶段 ==================== // ==================== 执行阶段 ====================
try { try {
// 6. 标记已支付并分配资金 (内存操作) // 6. 标记已支付 (内存操作)
// 注意:资金分配已移至 reward-service
order.markAsPaid(); order.markAsPaid();
order.allocateFunds(allocations);
// 7. 使用事务保存本地数据库的所有变更 + Outbox事件 // 7. 使用事务保存本地数据库的所有变更 + Outbox事件
// 这确保了订单状态、用户持仓、批次数据、以及事件发布的原子性 // 这确保了订单状态、用户持仓、批次数据、以及事件发布的原子性
@ -291,17 +280,18 @@ export class PlantingApplicationService {
// ==================== 确认阶段 ==================== // ==================== 确认阶段 ====================
// 9. 确认扣款(从冻结金额中正式扣除) // 9. 确认扣款(从冻结金额中正式扣除)
// 钱会进入"待分配"状态,由 reward-service 通过事件触发后执行真正的分配
await this.walletService.confirmPlantingDeduction({ await this.walletService.confirmPlantingDeduction({
userId: userId.toString(), userId: userId.toString(),
accountSequence: accountSequence,
orderId: order.orderNo, orderId: order.orderNo,
}); });
this.logger.log(`Wallet deduction confirmed for order ${order.orderNo}`); this.logger.log(`Wallet deduction confirmed for order ${order.orderNo}`);
// 10. 调用钱包服务执行资金分配 (外部调用,在事务外) // 注意:资金分配已移至 reward-service
await this.walletService.allocateFunds({ // reward-service 收到 planting.order.paid 事件后,会:
orderId: order.orderNo, // 1. 调用 authorization-service 获取考核后的分配方案
allocations: allocations.map((a) => a.toDTO()), // 2. 调用 wallet-service 执行真正的资金分配
});
this.logger.log(`Order paid successfully: ${order.orderNo}`); this.logger.log(`Order paid successfully: ${order.orderNo}`);
@ -311,7 +301,7 @@ export class PlantingApplicationService {
return { return {
orderNo: order.orderNo, orderNo: order.orderNo,
status: order.status, status: order.status,
allocations: allocations.map((a) => a.toDTO()), allocations: [], // 分配由 reward-service 执行
}; };
} catch (error) { } catch (error) {
// 执行阶段出错,需要解冻资金 // 执行阶段出错,需要解冻资金
@ -325,6 +315,7 @@ export class PlantingApplicationService {
try { try {
await this.walletService.unfreezeForPlanting({ await this.walletService.unfreezeForPlanting({
userId: userId.toString(), userId: userId.toString(),
accountSequence: accountSequence,
orderId: order.orderNo, orderId: order.orderNo,
}); });
this.logger.log(`Wallet unfrozen (rollback) for order ${order.orderNo}`); this.logger.log(`Wallet unfrozen (rollback) for order ${order.orderNo}`);

View File

@ -30,7 +30,7 @@ export class ReferralServiceClient {
* *
*/ */
async getReferralContext( async getReferralContext(
accountSequence: number, accountSequence: string,
provinceCode: string, provinceCode: string,
cityCode: string, cityCode: string,
): Promise<ReferralContext> { ): Promise<ReferralContext> {

View File

@ -24,17 +24,20 @@ export interface WalletBalance {
export interface FreezeForPlantingRequest { export interface FreezeForPlantingRequest {
userId: string; userId: string;
accountSequence?: string; // 跨服务关联标识(优先使用)
amount: number; amount: number;
orderId: string; orderId: string;
} }
export interface ConfirmPlantingDeductionRequest { export interface ConfirmPlantingDeductionRequest {
userId: string; userId: string;
accountSequence?: string; // 跨服务关联标识(优先使用)
orderId: string; orderId: string;
} }
export interface UnfreezeForPlantingRequest { export interface UnfreezeForPlantingRequest {
userId: string; userId: string;
accountSequence?: string; // 跨服务关联标识(优先使用)
orderId: string; orderId: string;
} }

View File

@ -14,7 +14,7 @@ datasource db {
model ReferralRelationship { model ReferralRelationship {
id BigInt @id @default(autoincrement()) @map("relationship_id") id BigInt @id @default(autoincrement()) @map("relationship_id")
userId BigInt @unique @map("user_id") userId BigInt @unique @map("user_id")
accountSequence Int @unique @map("account_sequence") // 8位账户序列号用于跨服务关联 accountSequence String @unique @map("account_sequence") @db.VarChar(12) // 格式: D + YYMMDD + 5位序号
// 推荐人信息 // 推荐人信息
referrerId BigInt? @map("referrer_id") // 直接推荐人 (null = 无推荐人/根节点) referrerId BigInt? @map("referrer_id") // 直接推荐人 (null = 无推荐人/根节点)
@ -113,7 +113,7 @@ model DirectReferral {
id BigInt @id @default(autoincrement()) @map("direct_referral_id") id BigInt @id @default(autoincrement()) @map("direct_referral_id")
referrerId BigInt @map("referrer_id") // 推荐人ID referrerId BigInt @map("referrer_id") // 推荐人ID
referralId BigInt @map("referral_id") // 被推荐人ID referralId BigInt @map("referral_id") // 被推荐人ID
referralSequence BigInt @map("referral_sequence") // 被推荐人序列号 referralSequence String @map("referral_sequence") @db.VarChar(12) // 被推荐人序列号 (格式: D + YYMMDD + 5位序号)
// 被推荐人信息快照 (冗余存储,避免跨服务查询) // 被推荐人信息快照 (冗余存储,避免跨服务查询)
referralNickname String? @map("referral_nickname") @db.VarChar(100) referralNickname String? @map("referral_nickname") @db.VarChar(100)

View File

@ -31,7 +31,7 @@ export class InternalReferralChainController {
schema: { schema: {
type: 'object', type: 'object',
properties: { properties: {
accountSequence: { type: 'number' }, accountSequence: { type: 'string', description: '格式: D + YYMMDD + 5位序号' },
userId: { type: 'string' }, userId: { type: 'string' },
ancestorPath: { ancestorPath: {
type: 'array', type: 'array',
@ -45,13 +45,13 @@ export class InternalReferralChainController {
async getReferralChain(@Param('accountSequence') accountSequence: string) { async getReferralChain(@Param('accountSequence') accountSequence: string) {
this.logger.debug(`[INTERNAL] getReferralChain: accountSequence=${accountSequence}`); this.logger.debug(`[INTERNAL] getReferralChain: accountSequence=${accountSequence}`);
const relationship = await this.referralRepo.findByAccountSequence(Number(accountSequence)); const relationship = await this.referralRepo.findByAccountSequence(accountSequence);
if (!relationship) { if (!relationship) {
this.logger.debug(`[INTERNAL] No referral found for accountSequence: ${accountSequence}`); this.logger.debug(`[INTERNAL] No referral found for accountSequence: ${accountSequence}`);
// 返回空的祖先链而不是抛出错误 // 返回空的祖先链而不是抛出错误
return { return {
accountSequence: Number(accountSequence), accountSequence: accountSequence,
userId: null, userId: null,
ancestorPath: [], ancestorPath: [],
referrerId: null, referrerId: null,
@ -80,10 +80,10 @@ export class InternalReferralChainController {
description: '批量推荐链数据', description: '批量推荐链数据',
}) })
async getBatchReferralChains(@Param('accountSequences') accountSequences: string) { async getBatchReferralChains(@Param('accountSequences') accountSequences: string) {
const sequences = accountSequences.split(',').map((s) => Number(s.trim())); const sequences = accountSequences.split(',').map((s) => s.trim());
this.logger.debug(`[INTERNAL] getBatchReferralChains: ${sequences.length} accounts`); this.logger.debug(`[INTERNAL] getBatchReferralChains: ${sequences.length} accounts`);
const results: Record<number, { userId: string | null; ancestorPath: string[]; referrerId: string | null }> = {}; const results: Record<string, { userId: string | null; ancestorPath: string[]; referrerId: string | null }> = {};
for (const seq of sequences) { for (const seq of sequences) {
const relationship = await this.referralRepo.findByAccountSequence(seq); const relationship = await this.referralRepo.findByAccountSequence(seq);
@ -118,10 +118,10 @@ export class InternalReferralChainController {
schema: { schema: {
type: 'object', type: 'object',
properties: { properties: {
accountSequence: { type: 'number' }, accountSequence: { type: 'string', description: '格式: D + YYMMDD + 5位序号' },
teamMembers: { teamMembers: {
type: 'array', type: 'array',
items: { type: 'number' }, items: { type: 'string' },
description: '团队成员accountSequence列表直接和间接下级', description: '团队成员accountSequence列表直接和间接下级',
}, },
}, },
@ -130,11 +130,11 @@ export class InternalReferralChainController {
async getTeamMembers(@Param('accountSequence') accountSequence: string) { async getTeamMembers(@Param('accountSequence') accountSequence: string) {
this.logger.debug(`[INTERNAL] getTeamMembers: accountSequence=${accountSequence}`); this.logger.debug(`[INTERNAL] getTeamMembers: accountSequence=${accountSequence}`);
const relationship = await this.referralRepo.findByAccountSequence(Number(accountSequence)); const relationship = await this.referralRepo.findByAccountSequence(accountSequence);
if (!relationship) { if (!relationship) {
return { return {
accountSequence: Number(accountSequence), accountSequence: accountSequence,
teamMembers: [], teamMembers: [],
}; };
} }
@ -143,7 +143,7 @@ export class InternalReferralChainController {
const directReferrals = await this.referralRepo.findDirectReferrals(relationship.userId); const directReferrals = await this.referralRepo.findDirectReferrals(relationship.userId);
// 递归获取所有下级成员的accountSequence // 递归获取所有下级成员的accountSequence
const teamMembers: number[] = []; const teamMembers: string[] = [];
const queue = [...directReferrals]; const queue = [...directReferrals];
while (queue.length > 0) { while (queue.length > 0) {
@ -156,7 +156,7 @@ export class InternalReferralChainController {
} }
return { return {
accountSequence: Number(accountSequence), accountSequence: accountSequence,
teamMembers, teamMembers,
}; };
} }

View File

@ -52,7 +52,7 @@ export class InternalTeamStatisticsController {
return { return {
userId: stats.userId.toString(), userId: stats.userId.toString(),
accountSequence: '0', // userId 查询时无法获取 accountSequence accountSequence: '0', // userId 查询时无法获取 accountSequence
totalTeamPlantingCount: stats.teamPlantingCount, // 团队总认种(含自己) totalTeamPlantingCount: stats.teamPlantingCount, // 团队总认种(含自己,只有下级
selfPlantingCount: stats.personalPlantingCount, // 自己的认种数 selfPlantingCount: stats.personalPlantingCount, // 自己的认种数
provinceCityDistribution: distribution.toJson(), provinceCityDistribution: distribution.toJson(),
}; };
@ -85,7 +85,7 @@ export class InternalTeamStatisticsController {
try { try {
// 需要先通过 accountSequence 查找 userId // 需要先通过 accountSequence 查找 userId
// 这里需要扩展 repository 方法 // 这里需要扩展 repository 方法
const stats = await this.findByAccountSequence(BigInt(accountSequence)); const stats = await this.findByAccountSequence(accountSequence);
if (!stats) { if (!stats) {
this.logger.debug(`[INTERNAL] No stats found for accountSequence: ${accountSequence}`); this.logger.debug(`[INTERNAL] No stats found for accountSequence: ${accountSequence}`);
@ -97,7 +97,7 @@ export class InternalTeamStatisticsController {
return { return {
userId: stats.userId.toString(), userId: stats.userId.toString(),
accountSequence: accountSequence, accountSequence: accountSequence,
totalTeamPlantingCount: stats.teamPlantingCount, // 团队总认种(含自己) totalTeamPlantingCount: stats.teamPlantingCount, // 团队总认种(含自己,只有下级
selfPlantingCount: stats.personalPlantingCount, // 自己的认种数 selfPlantingCount: stats.personalPlantingCount, // 自己的认种数
provinceCityDistribution: distribution.toJson(), provinceCityDistribution: distribution.toJson(),
}; };
@ -111,10 +111,17 @@ export class InternalTeamStatisticsController {
* accountSequence * accountSequence
* referral_relationships userId team_statistics * referral_relationships userId team_statistics
*/ */
private async findByAccountSequence(accountSequence: bigint) { private async findByAccountSequence(accountSequence: string) {
// 使用 repository 的 findByUserId但这里需要 accountSequence 到 userId 的映射 // 使用 repository 的 findByUserId但这里需要 accountSequence 到 userId 的映射
// 由于当前架构 accountSequence 和 userId 不一定相等,需要通过 referral_relationships 表查询 // 由于当前架构 accountSequence 和 userId 不一定相等,需要通过 referral_relationships 表查询
// 暂时尝试用 accountSequence 作为 userId 查询 // 暂时尝试将 accountSequence 字符串转换为 BigInt 作为 userId 查询
return this.teamStatsRepo.findByUserId(accountSequence); // 注意:这里的实现取决于业务逻辑,可能需要通过 referral_relationships 表查询
try {
// 尝试从 accountSequence 中提取数字部分或直接使用
// 这是临时方案,实际应该通过 referral_relationships 查询
return this.teamStatsRepo.findByUserId(BigInt(accountSequence.replace(/\D/g, '')));
} catch {
return null;
}
} }
} }

View File

@ -56,7 +56,7 @@ export class ReferralController {
@ApiOperation({ summary: '获取当前用户推荐信息' }) @ApiOperation({ summary: '获取当前用户推荐信息' })
@ApiResponse({ status: 200, type: ReferralInfoResponseDto }) @ApiResponse({ status: 200, type: ReferralInfoResponseDto })
async getMyReferralInfo(@CurrentUser('userId') userId: bigint): Promise<ReferralInfoResponseDto> { async getMyReferralInfo(@CurrentUser('userId') userId: bigint): Promise<ReferralInfoResponseDto> {
const query = new GetUserReferralInfoQuery(Number(userId)); const query = new GetUserReferralInfoQuery(userId.toString()); // 转换为字符串
return this.referralService.getUserReferralInfo(query); return this.referralService.getUserReferralInfo(query);
} }
@ -171,7 +171,7 @@ export class ReferralController {
@ApiParam({ name: 'userId', description: '用户ID' }) @ApiParam({ name: 'userId', description: '用户ID' })
@ApiResponse({ status: 200, type: ReferralInfoResponseDto }) @ApiResponse({ status: 200, type: ReferralInfoResponseDto })
async getUserReferralInfo(@Param('userId') userId: string): Promise<ReferralInfoResponseDto> { async getUserReferralInfo(@Param('userId') userId: string): Promise<ReferralInfoResponseDto> {
const query = new GetUserReferralInfoQuery(Number(userId)); const query = new GetUserReferralInfoQuery(userId); // userId 已经是字符串
return this.referralService.getUserReferralInfo(query); return this.referralService.getUserReferralInfo(query);
} }
} }
@ -199,16 +199,14 @@ export class InternalReferralController {
@Query('provinceCode') provinceCode: string, @Query('provinceCode') provinceCode: string,
@Query('cityCode') cityCode: string, @Query('cityCode') cityCode: string,
) { ) {
const accountSeqNum = Number(accountSequence);
// 1. 获取用户的推荐链 // 1. 获取用户的推荐链
const query = new GetUserReferralInfoQuery(accountSeqNum); const query = new GetUserReferralInfoQuery(accountSequence); // accountSequence 现在是字符串
const referralInfo = await this.referralService.getUserReferralInfo(query); const referralInfo = await this.referralService.getUserReferralInfo(query);
// 2. 并行查询授权信息(省/市/社区) // 2. 并行查询授权信息(省/市/社区)
// 使用 fallback 机制:如果 authorization-service 不可用,返回 null // 使用 fallback 机制:如果 authorization-service 不可用,返回 null
const authorizations = await this.authorizationClient.findAllNearestAuthorizations( const authorizations = await this.authorizationClient.findAllNearestAuthorizations(
accountSeqNum, accountSequence, // accountSequence 现在是字符串
provinceCode, provinceCode,
cityCode, cityCode,
); );
@ -227,9 +225,9 @@ export class InternalReferralController {
accountSequence, accountSequence,
referralChain: referralInfo.referrerId ? [referralInfo.referrerId] : [], referralChain: referralInfo.referrerId ? [referralInfo.referrerId] : [],
referrerId: referralInfo.referrerId, referrerId: referralInfo.referrerId,
nearestProvinceAuth: authorizations.nearestProvinceAuth?.toString() ?? null, nearestProvinceAuth: authorizations.nearestProvinceAuth ?? null, // 已经是字符串,不需要 toString()
nearestCityAuth: authorizations.nearestCityAuth?.toString() ?? null, nearestCityAuth: authorizations.nearestCityAuth ?? null, // 已经是字符串,不需要 toString()
nearestCommunity: authorizations.nearestCommunity?.toString() ?? null, nearestCommunity: authorizations.nearestCommunity ?? null, // 已经是字符串,不需要 toString()
}; };
} }
} }

View File

@ -14,9 +14,9 @@ export class CreateReferralDto {
@IsString() @IsString()
userId: string; userId: string;
@ApiProperty({ description: '账户序列号 (8位)', example: 10000001 }) @ApiProperty({ description: '账户序列号 (新格式: D + YYMMDD + 5位序号)', example: 'D2512110008' })
@IsInt() @IsString()
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
@ApiPropertyOptional({ description: '推荐码', example: 'RWA123ABC' }) @ApiPropertyOptional({ description: '推荐码', example: 'RWA123ABC' })
@IsOptional() @IsOptional()
@ -24,10 +24,10 @@ export class CreateReferralDto {
@Length(6, 20) @Length(6, 20)
referrerCode?: string; referrerCode?: string;
@ApiPropertyOptional({ description: '邀请人账户序列号', example: 10000001 }) @ApiPropertyOptional({ description: '邀请人账户序列号 (新格式: D + YYMMDD + 5位序号)', example: 'D2512110007' })
@IsOptional() @IsOptional()
@IsInt() @IsString()
inviterAccountSequence?: number; inviterAccountSequence?: string;
} }
export class GetDirectReferralsDto { export class GetDirectReferralsDto {
@ -84,8 +84,8 @@ export class DirectReferralResponseDto {
@ApiProperty({ description: '用户ID' }) @ApiProperty({ description: '用户ID' })
userId: string; userId: string;
@ApiProperty({ description: '账户序列号 (8位)' }) @ApiProperty({ description: '账户序列号 (新格式: D + YYMMDD + 5位序号)' })
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
@ApiProperty({ description: '推荐码' }) @ApiProperty({ description: '推荐码' })
referralCode: string; referralCode: string;

View File

@ -1,8 +1,8 @@
export class CreateReferralRelationshipCommand { export class CreateReferralRelationshipCommand {
constructor( constructor(
public readonly userId: bigint, public readonly userId: bigint,
public readonly accountSequence: number, public readonly accountSequence: string, // 格式: D + YYMMDD + 5位序号
public readonly referrerCode: string | null = null, public readonly referrerCode: string | null = null,
public readonly inviterAccountSequence: number | null = null, public readonly inviterAccountSequence: string | null = null, // 格式: D + YYMMDD + 5位序号
) {} ) {}
} }

View File

@ -8,8 +8,8 @@ import { CreateReferralRelationshipCommand } from '../commands';
*/ */
interface UserAccountCreatedPayload { interface UserAccountCreatedPayload {
userId: string; userId: string;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
inviterSequence: number | null; inviterSequence: string | null; // 格式: D + YYMMDD + 5位序号
registeredAt: string; registeredAt: string;
// UserAccountCreated 有 phoneNumber, UserAccountAutoCreated 没有 // UserAccountCreated 有 phoneNumber, UserAccountAutoCreated 没有
phoneNumber?: string; phoneNumber?: string;
@ -66,11 +66,13 @@ export class UserRegisteredHandler implements OnModuleInit {
// 使用 accountSequence 作为 userId因为 identity-service 的 userId 是内部自增ID // 使用 accountSequence 作为 userId因为 identity-service 的 userId 是内部自增ID
// 在事件发布时可能还是临时值 0而 accountSequence 是全局唯一的业务标识 // 在事件发布时可能还是临时值 0而 accountSequence 是全局唯一的业务标识
// 注意userId 仍然需要是 bigint这里我们需要从 accountSequence 字符串中提取数值部分或使用其他方式
// 暂时保持原有逻辑,但 accountSequence 本身现在是字符串类型
const command = new CreateReferralRelationshipCommand( const command = new CreateReferralRelationshipCommand(
BigInt(payload.accountSequence), // 使用 accountSequence 作为 userId BigInt(payload.userId), // 使用 userId
payload.accountSequence, payload.accountSequence, // 现在是字符串格式
null, // referrerCode - 不通过推荐码查找 null, // referrerCode - 不通过推荐码查找
payload.inviterSequence, // 通过 accountSequence 查找推荐人 payload.inviterSequence, // 通过 accountSequence 查找推荐人,现在是字符串格式
); );
const result = await this.referralService.createReferralRelationship(command); const result = await this.referralService.createReferralRelationship(command);

View File

@ -8,7 +8,7 @@ export class GetDirectReferralsQuery {
export interface DirectReferralResult { export interface DirectReferralResult {
userId: string; userId: string;
accountSequence: number; // 8位账户序列号显示用 accountSequence: string; // 格式: D + YYMMDD + 5位序号
referralCode: string; referralCode: string;
personalPlantingCount: number; // 个人认种量 personalPlantingCount: number; // 个人认种量
teamPlantingCount: number; // 团队认种量 teamPlantingCount: number; // 团队认种量

View File

@ -1,5 +1,5 @@
export class GetUserReferralInfoQuery { export class GetUserReferralInfoQuery {
constructor(public readonly accountSequence: number) {} constructor(public readonly accountSequence: string) {} // 格式: D + YYMMDD + 5位序号
} }
export interface UserReferralInfoResult { export interface UserReferralInfoResult {

View File

@ -4,7 +4,7 @@ import { DomainEvent, ReferralRelationshipCreatedEvent } from '../../events';
export interface ReferralRelationshipProps { export interface ReferralRelationshipProps {
id: bigint; id: bigint;
userId: bigint; userId: bigint;
accountSequence: number; // 8位账户序列号用于跨服务关联 accountSequence: string; // 格式: D + YYMMDD + 5位序号
referrerId: bigint | null; referrerId: bigint | null;
referralCode: string; referralCode: string;
referralChain: bigint[]; referralChain: bigint[];
@ -26,7 +26,7 @@ export class ReferralRelationship {
private constructor( private constructor(
private readonly _id: bigint, private readonly _id: bigint,
private readonly _userId: UserId, private readonly _userId: UserId,
private readonly _accountSequence: number, private readonly _accountSequence: string, // 格式: D + YYMMDD + 5位序号
private readonly _referrerId: UserId | null, private readonly _referrerId: UserId | null,
private readonly _referralCode: ReferralCode, private readonly _referralCode: ReferralCode,
private readonly _referralChain: ReferralChain, private readonly _referralChain: ReferralChain,
@ -41,7 +41,7 @@ export class ReferralRelationship {
get userId(): bigint { get userId(): bigint {
return this._userId.value; return this._userId.value;
} }
get accountSequence(): number { get accountSequence(): string {
return this._accountSequence; return this._accountSequence;
} }
get referrerId(): bigint | null { get referrerId(): bigint | null {
@ -68,7 +68,7 @@ export class ReferralRelationship {
*/ */
static create( static create(
userId: bigint, userId: bigint,
accountSequence: number, accountSequence: string, // 格式: D + YYMMDD + 5位序号
referrerId: bigint | null, referrerId: bigint | null,
parentReferralChain: bigint[] = [], parentReferralChain: bigint[] = [],
): ReferralRelationship { ): ReferralRelationship {

View File

@ -191,16 +191,16 @@ export class TeamStatistics {
/** /**
* *
* teamPlantingCount
*/ */
addPersonalPlanting(count: number, provinceCode: string, cityCode: string): void { addPersonalPlanting(count: number, provinceCode: string, cityCode: string): void {
this._personalPlantingCount += count; this._personalPlantingCount += count;
this._teamPlantingCount += count; // 不更新 _teamPlantingCount因为团队认种不包含自己
this._provinceCityDistribution = this._provinceCityDistribution.add( this._provinceCityDistribution = this._provinceCityDistribution.add(
provinceCode, provinceCode,
cityCode, cityCode,
count, count,
); );
this.recalculateLeaderboardScore();
this._lastCalculatedAt = new Date(); this._lastCalculatedAt = new Date();
this._updatedAt = new Date(); this._updatedAt = new Date();

View File

@ -16,8 +16,9 @@ export interface IReferralRelationshipRepository {
/** /**
* () * ()
* @param accountSequence 格式: D + YYMMDD + 5
*/ */
findByAccountSequence(accountSequence: number): Promise<ReferralRelationship | null>; findByAccountSequence(accountSequence: string): Promise<ReferralRelationship | null>;
/** /**
* *

View File

@ -12,7 +12,7 @@ export interface AuthorizationServiceResponse<T> {
} }
export interface NearestAuthorizationResult { export interface NearestAuthorizationResult {
accountSequence: number | null; accountSequence: string | null; // 格式: D + YYMMDD + 5位序号
} }
/** /**
@ -37,10 +37,10 @@ export class AuthorizationServiceClient {
/** /**
* *
* @param accountSequence accountSequence * @param accountSequence accountSequence (格式: D + YYMMDD + 5)
* @returns accountSequence null * @returns accountSequence null
*/ */
async findNearestCommunity(accountSequence: number): Promise<number | null> { async findNearestCommunity(accountSequence: string): Promise<string | null> {
try { try {
const response = await firstValueFrom( const response = await firstValueFrom(
this.httpService this.httpService
@ -74,14 +74,14 @@ export class AuthorizationServiceClient {
/** /**
* *
* @param accountSequence accountSequence * @param accountSequence accountSequence (格式: D + YYMMDD + 5)
* @param provinceCode * @param provinceCode
* @returns accountSequence null * @returns accountSequence null
*/ */
async findNearestProvince( async findNearestProvince(
accountSequence: number, accountSequence: string,
provinceCode: string, provinceCode: string,
): Promise<number | null> { ): Promise<string | null> {
try { try {
const response = await firstValueFrom( const response = await firstValueFrom(
this.httpService this.httpService
@ -115,14 +115,14 @@ export class AuthorizationServiceClient {
/** /**
* *
* @param accountSequence accountSequence * @param accountSequence accountSequence (格式: D + YYMMDD + 5)
* @param cityCode * @param cityCode
* @returns accountSequence null * @returns accountSequence null
*/ */
async findNearestCity( async findNearestCity(
accountSequence: number, accountSequence: string,
cityCode: string, cityCode: string,
): Promise<number | null> { ): Promise<string | null> {
try { try {
const response = await firstValueFrom( const response = await firstValueFrom(
this.httpService this.httpService
@ -159,13 +159,13 @@ export class AuthorizationServiceClient {
* *
*/ */
async findAllNearestAuthorizations( async findAllNearestAuthorizations(
accountSequence: number, accountSequence: string, // 格式: D + YYMMDD + 5位序号
provinceCode: string, provinceCode: string,
cityCode: string, cityCode: string,
): Promise<{ ): Promise<{
nearestCommunity: number | null; nearestCommunity: string | null;
nearestProvinceAuth: number | null; nearestProvinceAuth: string | null;
nearestCityAuth: number | null; nearestCityAuth: string | null;
}> { }> {
const [nearestCommunity, nearestProvinceAuth, nearestCityAuth] = const [nearestCommunity, nearestProvinceAuth, nearestCityAuth] =
await Promise.all([ await Promise.all([

View File

@ -46,7 +46,7 @@ export class ReferralRelationshipRepository implements IReferralRelationshipRepo
return ReferralRelationship.reconstitute(this.mapToProps(record)); return ReferralRelationship.reconstitute(this.mapToProps(record));
} }
async findByAccountSequence(accountSequence: number): Promise<ReferralRelationship | null> { async findByAccountSequence(accountSequence: string): Promise<ReferralRelationship | null> {
const record = await this.prisma.referralRelationship.findUnique({ const record = await this.prisma.referralRelationship.findUnique({
where: { accountSequence }, where: { accountSequence },
}); });
@ -100,7 +100,7 @@ export class ReferralRelationshipRepository implements IReferralRelationshipRepo
private mapToProps(record: { private mapToProps(record: {
id: bigint; id: bigint;
userId: bigint; userId: bigint;
accountSequence: number; accountSequence: string; // 格式: D + YYMMDD + 5位序号
referrerId: bigint | null; referrerId: bigint | null;
myReferralCode: string; myReferralCode: string;
ancestorPath: bigint[]; ancestorPath: bigint[];

View File

@ -52,13 +52,13 @@ export class TeamStatisticsRepository implements ITeamStatisticsRepository {
where: { userId: { in: referralIds } }, where: { userId: { in: referralIds } },
select: { userId: true, accountSequence: true }, select: { userId: true, accountSequence: true },
}); });
const sequenceMap = new Map<bigint, number>(); const sequenceMap = new Map<bigint, string>();
for (const rel of referralRelations) { for (const rel of referralRelations) {
sequenceMap.set(rel.userId, rel.accountSequence); sequenceMap.set(rel.userId, rel.accountSequence);
} }
for (const dr of data.directReferrals) { for (const dr of data.directReferrals) {
const accountSequence = sequenceMap.get(dr.referralId) ?? Number(dr.referralId); const accountSequence = sequenceMap.get(dr.referralId) ?? dr.referralId.toString();
await tx.directReferral.upsert({ await tx.directReferral.upsert({
where: { where: {
uk_referrer_referral: { uk_referrer_referral: {
@ -73,7 +73,7 @@ export class TeamStatisticsRepository implements ITeamStatisticsRepository {
create: { create: {
referrerId: data.userId, referrerId: data.userId,
referralId: dr.referralId, referralId: dr.referralId,
referralSequence: BigInt(accountSequence), referralSequence: accountSequence, // 现在是字符串类型,不需要 BigInt 转换
teamPlantingCount: dr.teamCount, teamPlantingCount: dr.teamCount,
}, },
}); });
@ -210,7 +210,7 @@ export class TeamStatisticsRepository implements ITeamStatisticsRepository {
create: { create: {
referrerId: update.userId, referrerId: update.userId,
referralId: update.fromDirectReferralId, referralId: update.fromDirectReferralId,
referralSequence: update.fromDirectReferralId, referralSequence: update.fromDirectReferralId.toString(), // 转换为字符串
teamPlantingCount: update.countDelta, teamPlantingCount: update.countDelta,
}, },
}); });

View File

@ -14,7 +14,7 @@ datasource db {
model RewardLedgerEntry { model RewardLedgerEntry {
id BigInt @id @default(autoincrement()) @map("entry_id") id BigInt @id @default(autoincrement()) @map("entry_id")
userId BigInt @map("user_id") // 接收奖励的用户ID userId BigInt @map("user_id") // 接收奖励的用户ID
accountSequence BigInt @map("account_sequence") // 账户序列号 accountSequence String @map("account_sequence") @db.VarChar(20) // 账户序列号
// === 奖励来源 === // === 奖励来源 ===
sourceOrderNo String @map("source_order_no") @db.VarChar(50) // 来源认种订单号(字符串格式如PLT1765391584505Q0Q6QD) sourceOrderNo String @map("source_order_no") @db.VarChar(50) // 来源认种订单号(字符串格式如PLT1765391584505Q0Q6QD)
@ -58,7 +58,7 @@ model RewardLedgerEntry {
model RewardSummary { model RewardSummary {
id BigInt @id @default(autoincrement()) @map("summary_id") id BigInt @id @default(autoincrement()) @map("summary_id")
userId BigInt @unique @map("user_id") userId BigInt @unique @map("user_id")
accountSequence BigInt @unique @map("account_sequence") // 账户序列号 accountSequence String @unique @map("account_sequence") @db.VarChar(20) // 账户序列号
// === 待领取收益 (24h倒计时) === // === 待领取收益 (24h倒计时) ===
pendingUsdt Decimal @default(0) @map("pending_usdt") @db.Decimal(20, 8) pendingUsdt Decimal @default(0) @map("pending_usdt") @db.Decimal(20, 8)
@ -124,7 +124,7 @@ model RightDefinition {
model SettlementRecord { model SettlementRecord {
id BigInt @id @default(autoincrement()) @map("settlement_id") id BigInt @id @default(autoincrement()) @map("settlement_id")
userId BigInt @map("user_id") userId BigInt @map("user_id")
accountSequence BigInt @map("account_sequence") // 账户序列号 accountSequence String @map("account_sequence") @db.VarChar(20) // 账户序列号
// === 结算金额 === // === 结算金额 ===
usdtAmount Decimal @map("usdt_amount") @db.Decimal(20, 8) usdtAmount Decimal @map("usdt_amount") @db.Decimal(20, 8)

Some files were not shown because too many files have changed in this diff Show More