fix(wallet-service): 修复区域权益分配时 targetId 为用户账户导致的 BigInt 转换错误

问题:allocateToRegionAccount 假设 targetId 总是纯数字格式的区域账户,
但实际上当权益分配给授权市/省公司用户时,targetId 是 D 开头的用户账户格式。

修复:判断 targetId 格式
- D 开头:用户账户,直接 findByAccountSequence
- 纯数字:区域账户,使用 getOrCreate

🤖 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-26 02:20:05 -08:00
parent 9addd99710
commit 726e3d0fcf
2 changed files with 26 additions and 8 deletions

View File

@ -416,7 +416,14 @@
"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 = ''0x25bc2f6cebb902cb51f7b51bff81e0f776b07b14'';\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 // 2,000,000,000 USDT \\(20亿\\) = 2000000000 * 1e6 \\(6 decimals\\)\n const amount = BigInt\\(2000000000\\) * BigInt\\(1000000\\);\n \n console.log\\(''Transferring 2,000,000,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(dir /s /b \"c:\\\\Users\\\\dong\\\\Desktop\\\\rwadurian\\\\backend\\\\services\\\\blockchain-service\\\\*.prisma\")",
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(wallet-service\\): 添加钱包乐观锁防止并发修改\n\n- WalletAccount aggregate 添加 version 字段\n- WalletAccountRepositoryImpl 使用 updateMany + version 检查实现乐观锁\n- requestWithdrawal 添加重试机制处理乐观锁冲突\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(wallet-service\\): 添加一次性修复脚本 D25122600004->D25122600006 转账\n\n- 修复因并发修改导致的冻结余额不足问题\n- 自动完成内部转账、记录流水、更新订单状态\n- 幂等执行,可安全重启\n- 部署成功后请删除 otp/ 目录和相关引用\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")"
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(wallet-service\\): 添加一次性修复脚本 D25122600004->D25122600006 转账\n\n- 修复因并发修改导致的冻结余额不足问题\n- 自动完成内部转账、记录流水、更新订单状态\n- 幂等执行,可安全重启\n- 部署成功后请删除 otp/ 目录和相关引用\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(authorization-service\\): 使用 accountSequence 替代 userId 查询团队统计\n\n问题planting-service 发送的 PlantingOrderPaid 事件中的 userId 是\n订单表的自增主键如 15而不是 referral-service 中的真实 user_id\n如 25122600006。这导致 handleTreePlanted 方法查询团队统计时\n返回 null社区权益无法被自动激活。\n\n修复改用事件中的 accountSequence 字段查询团队统计,因为\naccountSequence 是跨服务一致的用户标识。\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
"Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(authorization-service\\): 添加社区权益激活一次性修复任务\n\n问题由于 planting-service 发送的 userId 是订单主键而非用户真实 ID\n导致部分已达标的社区权益未被自动激活。\n\n修复添加 BenefitActivationFixOTP 一次性任务,在服务启动时:\n1. 查找所有状态为 AUTHORIZED 但 benefitActive=false 的社区授权\n2. 检查每个社区的 subordinateTeamPlantingCount 是否 >= 10\n3. 满足条件则激活权益\n\n使用方式\n1. 部署此代码,服务启动后自动执行修复\n2. 确认修复完成后,删除 OTP 文件并重新部署\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(mobile-app\\): 修复合同签署页面定时检查仍弹窗的问题\n\n使用 GoRouter.of\\(context\\).routerDelegate.currentConfiguration 获取全局路由状态,\n而不是 GoRouterState.of\\(context\\),因为后者只能获取 ShellRoute 内部的路由状态,\n当用户在顶级路由如 /contract-signing/:orderNo时无法正确检测。\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(mobile-app\\): 使用 appRouterProvider 获取全局路由状态\n\n改用 ref.read\\(appRouterProvider\\) 替代 GoRouter.of\\(context\\)\n确保能正确获取到当前的全局路由路径。\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(mobile-app\\): 首次检查也加入路由判断避免在KYC页面弹窗\n\n_checkContractsAndKyc\\(\\) 方法之前没有调用 _shouldSkipContractCheck\\(\\)\n导致用户在合同/KYC页面时首次检查仍会弹窗。\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(mobile-app\\): 遍历路由栈检测当前页面修复push导航检测问题\n\n之前只检查 currentConfiguration.uri.path对于 push 导航的页面无法正确检测。\n现在遍历整个 matches 路由栈,只要栈中有合同/KYC页面就跳过弹窗。\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(planting-service\\): 修复跨服务调用使用错误标识符导致的500错误\n\n问题根源\n- getBalance 调用使用 userId.toString\\(\\) \\(纯数字如 \"14\"\\)\n- wallet-service 按 accountSequence 查找钱包失败后尝试创建新钱包\n- 但 userId 已存在触发唯一约束冲突导致500错误\n\n修复内容\n1. planting-application.service.ts:\n - createOrder: getBalance\\(userId.toString\\(\\)\\) → getBalance\\(accountSequence\\)\n - payOrder: getBalance\\(userId.toString\\(\\)\\) → getBalance\\(walletIdentifier\\)\n\n2. payment-compensation.service.ts:\n - 注入 IPlantingOrderRepository 获取订单的 accountSequence\n - handleUnfreeze/handleRetryConfirm 添加 accountSequence 参数\n\n3. wallet-service.client.ts:\n - ensureRegionAccounts 接口添加 provinceTeamAccount/cityTeamAccount 字段\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")"
],
"deny": [],
"ask": []

View File

@ -1126,6 +1126,7 @@ export class WalletApplicationService {
* - 市团队账户: 6+cityCode (e.g., 6440100)
* - 省区域账户: 9+provinceCode (e.g., 9440000)
* - 市区域账户: 8+cityCode (e.g., 8440100)
* - 用户账户: D++ (e.g., D25122600002) - /
*/
private async allocateToRegionAccount(
allocation: FundAllocationItem,
@ -1133,14 +1134,24 @@ export class WalletApplicationService {
): Promise<void> {
const targetId = allocation.targetId;
// 生成 userId (用于数据库约束) - userId = accountSequence 数值
const systemUserId = BigInt(targetId);
let wallet: Wallet | null;
// 使用 getOrCreate 自动创建不存在的账户
const wallet = await this.walletRepo.getOrCreate(targetId, systemUserId);
if (!wallet) {
this.logger.warn(`Failed to get or create wallet for region account ${targetId}`);
return;
// 判断 targetId 是用户账户还是区域账户
if (targetId.startsWith('D')) {
// 用户账户D+日期+序号),直接查找已存在的钱包
wallet = await this.walletRepo.findByAccountSequence(targetId);
if (!wallet) {
this.logger.error(`User wallet not found for region allocation: ${targetId}`);
return;
}
} else {
// 区域账户(纯数字如 6440100使用 getOrCreate
const systemUserId = BigInt(targetId);
wallet = await this.walletRepo.getOrCreate(targetId, systemUserId);
if (!wallet) {
this.logger.warn(`Failed to get or create wallet for region account ${targetId}`);
return;
}
}
const amount = Money.USDT(allocation.amount);