From 16a6edaf154ccfeea60378fea74a8bf31bb261e8 Mon Sep 17 00:00:00 2001 From: hailin Date: Thu, 25 Dec 2025 23:00:38 -0800 Subject: [PATCH] =?UTF-8?q?feat(authorization-service):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=A4=BE=E5=8C=BA=E6=9D=83=E7=9B=8A=E6=BF=80=E6=B4=BB?= =?UTF-8?q?=E4=B8=80=E6=AC=A1=E6=80=A7=E4=BF=AE=E5=A4=8D=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:由于 planting-service 发送的 userId 是订单主键而非用户真实 ID, 导致部分已达标的社区权益未被自动激活。 修复:添加 BenefitActivationFixOTP 一次性任务,在服务启动时: 1. 查找所有状态为 AUTHORIZED 但 benefitActive=false 的社区授权 2. 检查每个社区的 subordinateTeamPlantingCount 是否 >= 10 3. 满足条件则激活权益 使用方式: 1. 部署此代码,服务启动后自动执行修复 2. 确认修复完成后,删除 OTP 文件并重新部署 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../authorization-service/src/app.module.ts | 3 +- .../schedulers/benefit-activation-fix.otp.ts | 136 ++++++++++++++++++ .../src/application/schedulers/index.ts | 1 + 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 backend/services/authorization-service/src/application/schedulers/benefit-activation-fix.otp.ts diff --git a/backend/services/authorization-service/src/app.module.ts b/backend/services/authorization-service/src/app.module.ts index 3a0d0210..44665663 100644 --- a/backend/services/authorization-service/src/app.module.ts +++ b/backend/services/authorization-service/src/app.module.ts @@ -24,7 +24,7 @@ import { ReferralServiceClient, IdentityServiceClient, RewardServiceClient } fro // Application import { AuthorizationApplicationService, REFERRAL_REPOSITORY, TEAM_STATISTICS_REPOSITORY } from '@/application/services' -import { MonthlyAssessmentScheduler } from '@/application/schedulers' +import { MonthlyAssessmentScheduler, BenefitActivationFixOTP } from '@/application/schedulers' // API import { @@ -98,6 +98,7 @@ const MockReferralRepository = { // Application Services AuthorizationApplicationService, MonthlyAssessmentScheduler, + BenefitActivationFixOTP, // OTP: 一次性修复任务,执行后删除 // Strategies JwtStrategy, diff --git a/backend/services/authorization-service/src/application/schedulers/benefit-activation-fix.otp.ts b/backend/services/authorization-service/src/application/schedulers/benefit-activation-fix.otp.ts new file mode 100644 index 00000000..13726560 --- /dev/null +++ b/backend/services/authorization-service/src/application/schedulers/benefit-activation-fix.otp.ts @@ -0,0 +1,136 @@ +import { Injectable, Inject, Logger, OnModuleInit } from '@nestjs/common' +import { RoleType, AuthorizationStatus } from '@/domain/enums' +import { + IAuthorizationRoleRepository, + AUTHORIZATION_ROLE_REPOSITORY, +} from '@/domain/repositories' +import { ITeamStatisticsRepository } from '@/domain/services' +import { EventPublisherService } from '@/infrastructure/kafka' +import { TEAM_STATISTICS_REPOSITORY } from '@/application/services' + +/** + * 一次性修复任务(OTP - One Time Program) + * + * 问题描述: + * planting-service 发送的 PlantingOrderPaid 事件中的 userId 是订单表的自增主键, + * 而不是 referral-service 中的真实 user_id。这导致 handleTreePlanted 方法 + * 查询团队统计时返回 null,社区权益无法被自动激活。 + * + * 修复逻辑: + * 1. 查找所有状态为 AUTHORIZED 但 benefitActive = false 的社区授权 + * 2. 检查每个社区的 subordinateTeamPlantingCount 是否 >= initialTarget (10) + * 3. 如果满足条件,激活权益 + * + * 使用方式: + * 1. 部署此代码 + * 2. 服务启动时自动执行修复 + * 3. 执行完成后删除此文件,重新部署 + */ +@Injectable() +export class BenefitActivationFixOTP implements OnModuleInit { + private readonly logger = new Logger(BenefitActivationFixOTP.name) + + constructor( + @Inject(AUTHORIZATION_ROLE_REPOSITORY) + private readonly authorizationRepository: IAuthorizationRoleRepository, + @Inject(TEAM_STATISTICS_REPOSITORY) + private readonly statsRepository: ITeamStatisticsRepository, + private readonly eventPublisher: EventPublisherService, + ) {} + + async onModuleInit(): Promise { + // 延迟 5 秒执行,确保所有依赖都已初始化 + setTimeout(() => { + this.executeFix().catch((error) => { + this.logger.error('[OTP] Fix execution failed:', error) + }) + }, 5000) + } + + private async executeFix(): Promise { + this.logger.log('[OTP] ========================================') + this.logger.log('[OTP] 开始执行社区权益激活修复任务...') + this.logger.log('[OTP] ========================================') + + try { + // 1. 查找所有未激活的社区授权 + const pendingCommunities = await this.authorizationRepository.findByRoleTypeAndStatus( + RoleType.COMMUNITY, + AuthorizationStatus.AUTHORIZED, + ) + + // 过滤出 benefitActive = false 的记录 + const unactivatedCommunities = pendingCommunities.filter( + (auth) => !auth.benefitActive, + ) + + this.logger.log(`[OTP] 找到 ${unactivatedCommunities.length} 个未激活权益的社区授权`) + + if (unactivatedCommunities.length === 0) { + this.logger.log('[OTP] 没有需要修复的记录,任务结束') + return + } + + let fixedCount = 0 + let skippedCount = 0 + + // 2. 逐个检查并修复 + for (const auth of unactivatedCommunities) { + const accountSequence = auth.userId.accountSequence + + try { + // 获取团队统计 + const teamStats = await this.statsRepository.findByAccountSequence(accountSequence) + + if (!teamStats) { + this.logger.warn(`[OTP] 未找到 ${accountSequence} 的团队统计数据,跳过`) + skippedCount++ + continue + } + + const subordinateTreeCount = teamStats.subordinateTeamPlantingCount + const initialTarget = auth.getInitialTarget() + + this.logger.debug( + `[OTP] ${accountSequence}: subordinateTreeCount=${subordinateTreeCount}, initialTarget=${initialTarget}`, + ) + + // 3. 检查是否满足激活条件 + if (subordinateTreeCount >= initialTarget) { + this.logger.log( + `[OTP] ${accountSequence} 满足激活条件 (${subordinateTreeCount}>=${initialTarget}),正在激活权益...`, + ) + + // 激活权益 + auth.activateBenefit() + await this.authorizationRepository.save(auth) + await this.eventPublisher.publishAll(auth.domainEvents) + auth.clearDomainEvents() + + fixedCount++ + this.logger.log(`[OTP] ${accountSequence} 权益激活成功`) + } else { + this.logger.debug( + `[OTP] ${accountSequence} 未满足激活条件 (${subordinateTreeCount}<${initialTarget}),跳过`, + ) + skippedCount++ + } + } catch (error) { + this.logger.error(`[OTP] 处理 ${accountSequence} 时发生错误:`, error) + skippedCount++ + } + } + + // 4. 输出修复结果 + this.logger.log('[OTP] ========================================') + this.logger.log('[OTP] 社区权益激活修复任务完成') + this.logger.log(`[OTP] 总计处理: ${unactivatedCommunities.length}`) + this.logger.log(`[OTP] 成功修复: ${fixedCount}`) + this.logger.log(`[OTP] 跳过/失败: ${skippedCount}`) + this.logger.log('[OTP] ========================================') + } catch (error) { + this.logger.error('[OTP] 修复任务执行失败:', error) + throw error + } + } +} diff --git a/backend/services/authorization-service/src/application/schedulers/index.ts b/backend/services/authorization-service/src/application/schedulers/index.ts index ac9e816c..3ad9dfba 100644 --- a/backend/services/authorization-service/src/application/schedulers/index.ts +++ b/backend/services/authorization-service/src/application/schedulers/index.ts @@ -1 +1,2 @@ export * from './monthly-assessment.scheduler' +export * from './benefit-activation-fix.otp'