From 726e3d0fcf0fce1ef3e99f9473af08fb1d19953f Mon Sep 17 00:00:00 2001 From: hailin Date: Fri, 26 Dec 2025 02:20:05 -0800 Subject: [PATCH] =?UTF-8?q?fix(wallet-service):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=8C=BA=E5=9F=9F=E6=9D=83=E7=9B=8A=E5=88=86=E9=85=8D=E6=97=B6?= =?UTF-8?q?=20targetId=20=E4=B8=BA=E7=94=A8=E6=88=B7=E8=B4=A6=E6=88=B7?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=9A=84=20BigInt=20=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:allocateToRegionAccount 假设 targetId 总是纯数字格式的区域账户, 但实际上当权益分配给授权市/省公司用户时,targetId 是 D 开头的用户账户格式。 修复:判断 targetId 格式 - D 开头:用户账户,直接 findByAccountSequence - 纯数字:区域账户,使用 getOrCreate 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .claude/settings.local.json | 9 ++++++- .../services/wallet-application.service.ts | 25 +++++++++++++------ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index c668435b..159674f1 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \nEOF\n\\)\")" ], "deny": [], "ask": [] diff --git a/backend/services/wallet-service/src/application/services/wallet-application.service.ts b/backend/services/wallet-service/src/application/services/wallet-application.service.ts index a46a7546..9e2b8354 100644 --- a/backend/services/wallet-service/src/application/services/wallet-application.service.ts +++ b/backend/services/wallet-service/src/application/services/wallet-application.service.ts @@ -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 { 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);