diff --git a/.claude/settings.local.json b/.claude/settings.local.json index e4939a9b..ec169563 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -325,7 +325,10 @@ "Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(mobile-app\\): 修复提款页面 _feeRate 未定义错误\n\n将 _feeRate 改为 _feeConfig?.feeValue ?? 0.02,\n使用 FeeConfig 对象中的 feeValue 字段。\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\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 = ''0x0ec001ed6233b7959d7a251e2792621e4707c35f'';\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 // 100,000,000 USDT = 100000000 * 1e6 \\(6 decimals\\)\n const amount = BigInt\\(100000000\\) * BigInt\\(1000000\\);\n \n console.log\\(''Transferring 100,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(git commit -m \"$\\(cat <<''EOF''\nfix\\(mobile-app\\): 调整账本明细流水类型筛选顺序和标签\n\n- REWARD_SETTLED 标签从\"提取\"改为\"结算\"\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\\(blockchain-service\\): 过滤热钱包发出的转账避免内部转账重复入账\n\n内部转账时,wallet-service 已经处理了接收方入账,\n需要过滤掉 blockchain-service 扫描到的热钱包转出交易,\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\\(blockchain-service\\): 过滤热钱包发出的转账避免内部转账重复入账\n\n内部转账时,wallet-service 已经处理了接收方入账,\n需要过滤掉 blockchain-service 扫描到的热钱包转出交易,\n避免将其当作充值重复处理导致双倍入账\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\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'';\n\nasync function mint\\(\\) {\n const provider = new ethers.JsonRpcProvider\\(KAVA_TESTNET_RPC\\);\n const wallet = new ethers.Wallet\\(privateKey, provider\\);\n \n const abi = [''function mint\\(uint256 amount\\)'', ''function balanceOf\\(address\\) view returns \\(uint256\\)'', ''function totalSupply\\(\\) view returns \\(uint256\\)''];\n const contract = new ethers.Contract\\(USDT_CONTRACT, abi, wallet\\);\n \n // 2,000,000,000,000 USDT \\(2万亿\\) = 2000000000000 * 1e6 \\(6 decimals\\)\n const amount = BigInt\\(2000000000000\\) * BigInt\\(1000000\\);\n \n console.log\\(''Minting 2,000,000,000,000 USDT \\(2万亿\\)...''\\);\n const tx = await contract.mint\\(amount, { gasLimit: 100000 }\\);\n console.log\\(''TX Hash:'', tx.hash\\);\n await tx.wait\\(\\);\n \n const totalSupply = await contract.totalSupply\\(\\);\n const balance = await contract.balanceOf\\(wallet.address\\);\n console.log\\(''New Total Supply:'', Number\\(totalSupply\\) / 1e6, ''USDT''\\);\n console.log\\(''Deployer Balance:'', Number\\(balance\\) / 1e6, ''USDT''\\);\n}\n\nmint\\(\\).catch\\(e => console.error\\(''Error:'', e.message\\)\\);\n\")", + "Bash(npx prisma migrate diff:*)", + "Bash(git revert:*)" ], "deny": [], "ask": [] diff --git a/backend/services/authorization-service/src/application/services/authorization-application.service.ts b/backend/services/authorization-service/src/application/services/authorization-application.service.ts index c4bd83ae..c5afb05d 100644 --- a/backend/services/authorization-service/src/application/services/authorization-application.service.ts +++ b/backend/services/authorization-service/src/application/services/authorization-application.service.ts @@ -17,6 +17,7 @@ import { } from '@/domain/repositories' import { AuthorizationValidatorService, + AssessmentCalculatorService, IReferralRepository, ITeamStatisticsRepository, TeamStatistics, @@ -586,9 +587,11 @@ export class AuthorizationApplicationService { `[getStickmanRanking] 查询火柴人排名: month=${month}, roleType=${roleType}, regionCode=${regionCode}, currentUserId=${currentUserId}`, ) + const monthVO = Month.create(month) + // 查询全系统该角色类型的所有评估记录(不按区域过滤) - const assessments = await this.assessmentRepository.findRankingsByMonthAndRoleType( - Month.create(month), + let assessments = await this.assessmentRepository.findRankingsByMonthAndRoleType( + monthVO, roleType, ) @@ -596,11 +599,88 @@ export class AuthorizationApplicationService { `[getStickmanRanking] 查询到 ${assessments.length} 条评估记录`, ) + // 如果没有评估记录,尝试实时创建 if (assessments.length === 0) { - this.logger.warn( - `[getStickmanRanking] 没有找到任何评估记录,month=${month}, roleType=${roleType}`, + this.logger.log( + `[getStickmanRanking] 没有评估记录,尝试实时创建: month=${month}, roleType=${roleType}`, ) - return [] + + // 获取所有该角色类型的激活授权 + const activeAuths = await this.authorizationRepository.findAllActive(roleType) + this.logger.log( + `[getStickmanRanking] 找到 ${activeAuths.length} 个激活的 ${roleType} 授权`, + ) + + if (activeAuths.length === 0) { + this.logger.warn( + `[getStickmanRanking] 没有找到任何激活的授权,month=${month}, roleType=${roleType}`, + ) + return [] + } + + // 按区域分组并创建评估记录 + const regionGroups = new Map() + for (const auth of activeAuths) { + const region = auth.regionCode.value + if (!regionGroups.has(region)) { + regionGroups.set(region, []) + } + regionGroups.get(region)!.push(auth) + } + + this.logger.log( + `[getStickmanRanking] 按区域分组: ${regionGroups.size} 个区域`, + ) + + // 使用 AssessmentCalculatorService 为每个区域创建评估 + const calculatorService = new AssessmentCalculatorService() + const allNewAssessments: MonthlyAssessment[] = [] + + for (const [region, auths] of regionGroups) { + this.logger.log( + `[getStickmanRanking] 为区域 ${region} 创建评估,包含 ${auths.length} 个授权`, + ) + + const regionAssessments = await calculatorService.assessAndRankRegion( + roleType, + RegionCode.create(region), + monthVO, + this.authorizationRepository, + this.statsRepository, + this.assessmentRepository, + ) + + this.logger.log( + `[getStickmanRanking] 区域 ${region} 创建了 ${regionAssessments.length} 条评估记录`, + ) + + // 保存评估记录 + if (regionAssessments.length > 0) { + await this.assessmentRepository.saveAll(regionAssessments) + allNewAssessments.push(...regionAssessments) + } + } + + this.logger.log( + `[getStickmanRanking] 共创建 ${allNewAssessments.length} 条评估记录`, + ) + + // 重新查询 + assessments = await this.assessmentRepository.findRankingsByMonthAndRoleType( + monthVO, + roleType, + ) + + this.logger.log( + `[getStickmanRanking] 重新查询到 ${assessments.length} 条评估记录`, + ) + + if (assessments.length === 0) { + this.logger.warn( + `[getStickmanRanking] 创建评估后仍然没有记录,month=${month}, roleType=${roleType}`, + ) + return [] + } } // 批量获取用户信息 diff --git a/backend/services/authorization-service/test/domain-services.integration-spec.ts b/backend/services/authorization-service/test/domain-services.integration-spec.ts index a9b9a2b3..8df26743 100644 --- a/backend/services/authorization-service/test/domain-services.integration-spec.ts +++ b/backend/services/authorization-service/test/domain-services.integration-spec.ts @@ -55,6 +55,7 @@ describe('Domain Services Integration Tests', () => { findFirstByAuthorization: jest.fn(), findByMonthAndRegion: jest.fn(), findRankingsByMonthAndRegion: jest.fn(), + findRankingsByMonthAndRoleType: jest.fn(), findByAuthorization: jest.fn(), delete: jest.fn(), }