diff --git a/backend/services/authorization-service/src/api/dto/response/authorization.response.ts b/backend/services/authorization-service/src/api/dto/response/authorization.response.ts index 5843c207..781703dc 100644 --- a/backend/services/authorization-service/src/api/dto/response/authorization.response.ts +++ b/backend/services/authorization-service/src/api/dto/response/authorization.response.ts @@ -35,6 +35,15 @@ export class AuthorizationResponse { @ApiProperty({ description: '是否豁免占比考核' }) exemptFromPercentageCheck: boolean + @ApiProperty({ description: '初始考核目标(社区10,市100,省500)' }) + initialTargetTreeCount: number + + @ApiProperty({ description: '当前团队认种数量' }) + currentTreeCount: number + + @ApiProperty({ description: '月度考核目标' }) + monthlyTargetTreeCount: number + @ApiProperty({ description: '创建时间' }) createdAt: Date diff --git a/backend/services/authorization-service/src/application/dto/authorization.dto.ts b/backend/services/authorization-service/src/application/dto/authorization.dto.ts index b969139c..7e0a95f1 100644 --- a/backend/services/authorization-service/src/application/dto/authorization.dto.ts +++ b/backend/services/authorization-service/src/application/dto/authorization.dto.ts @@ -12,6 +12,10 @@ export interface AuthorizationDTO { currentMonthIndex: number requireLocalPercentage: number exemptFromPercentageCheck: boolean + // 考核进度字段 + initialTargetTreeCount: number // 初始考核目标(社区10,市100,省500) + currentTreeCount: number // 当前团队认种数量 + monthlyTargetTreeCount: number // 月度考核目标(社区固定10) createdAt: Date updatedAt: Date } 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 368b7292..021e9f98 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 @@ -322,7 +322,11 @@ export class AuthorizationApplicationService { UserId.create(userId), ) - return authorizations.map((auth) => this.toAuthorizationDTO(auth)) + // 查询用户团队统计数据 + const teamStats = await this.statsRepository.findByUserId(UserId.create(userId).value) + const currentTreeCount = teamStats?.totalTeamPlantingCount || 0 + + return authorizations.map((auth) => this.toAuthorizationDTO(auth, currentTreeCount)) } /** @@ -333,7 +337,13 @@ export class AuthorizationApplicationService { AuthorizationId.create(authorizationId), ) - return authorization ? this.toAuthorizationDTO(authorization) : null + if (!authorization) return null + + // 查询用户团队统计数据 + const teamStats = await this.statsRepository.findByUserId(authorization.userId.value) + const currentTreeCount = teamStats?.totalTeamPlantingCount || 0 + + return this.toAuthorizationDTO(authorization, currentTreeCount) } /** @@ -426,7 +436,17 @@ export class AuthorizationApplicationService { return 0 } - private toAuthorizationDTO(auth: AuthorizationRole): AuthorizationDTO { + private toAuthorizationDTO(auth: AuthorizationRole, currentTreeCount: number): AuthorizationDTO { + // 获取月度考核目标(社区固定10,其他类型根据阶梯规则) + let monthlyTargetTreeCount = 0 + if (auth.roleType === RoleType.COMMUNITY) { + monthlyTargetTreeCount = 10 // 社区固定每月新增10棵 + } else if (auth.benefitActive && auth.currentMonthIndex > 0) { + // 省/市公司使用阶梯目标 + const target = LadderTargetRule.getTarget(auth.roleType, auth.currentMonthIndex) + monthlyTargetTreeCount = target.monthlyTarget + } + return { authorizationId: auth.authorizationId.value, userId: auth.userId.value, @@ -439,6 +459,10 @@ export class AuthorizationApplicationService { currentMonthIndex: auth.currentMonthIndex, requireLocalPercentage: auth.requireLocalPercentage, exemptFromPercentageCheck: auth.exemptFromPercentageCheck, + // 考核进度字段 + initialTargetTreeCount: auth.getInitialTarget(), + currentTreeCount, + monthlyTargetTreeCount, createdAt: auth.createdAt, updatedAt: auth.updatedAt, } diff --git a/frontend/mobile-app/lib/core/services/authorization_service.dart b/frontend/mobile-app/lib/core/services/authorization_service.dart index 730c9258..f8d89699 100644 --- a/frontend/mobile-app/lib/core/services/authorization_service.dart +++ b/frontend/mobile-app/lib/core/services/authorization_service.dart @@ -98,6 +98,10 @@ class AuthorizationResponse { final int currentMonthIndex; final double requireLocalPercentage; final bool exemptFromPercentageCheck; + // 考核进度字段 + final int initialTargetTreeCount; // 初始考核目标(社区10,市100,省500) + final int currentTreeCount; // 当前团队认种数量 + final int monthlyTargetTreeCount; // 月度考核目标 final DateTime createdAt; final DateTime updatedAt; @@ -113,6 +117,9 @@ class AuthorizationResponse { required this.currentMonthIndex, required this.requireLocalPercentage, required this.exemptFromPercentageCheck, + required this.initialTargetTreeCount, + required this.currentTreeCount, + required this.monthlyTargetTreeCount, required this.createdAt, required this.updatedAt, }); @@ -130,6 +137,9 @@ class AuthorizationResponse { currentMonthIndex: json['currentMonthIndex'] ?? 0, requireLocalPercentage: (json['requireLocalPercentage'] ?? 0).toDouble(), exemptFromPercentageCheck: json['exemptFromPercentageCheck'] ?? false, + initialTargetTreeCount: json['initialTargetTreeCount'] ?? 0, + currentTreeCount: json['currentTreeCount'] ?? 0, + monthlyTargetTreeCount: json['monthlyTargetTreeCount'] ?? 0, createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt']) : DateTime.now(), diff --git a/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart b/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart index c92ee17d..e008a22a 100644 --- a/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart +++ b/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart @@ -50,10 +50,13 @@ class _ProfilePageState extends ConsumerState { // 直推数据(从 referral-service 获取) List> _referrals = []; - // 社区考核数据 - final int _communityLevel = 3; - final int _currentPlanting = 12; - final int _requiredPlanting = 50; + // 社区考核数据(从 authorization-service 获取) + bool _hasCommunityAuth = false; // 是否有社区授权 + bool _communityBenefitActive = false; // 社区权益是否激活 + int _communityCurrentTreeCount = 0; // 当前团队认种数量 + int _communityInitialTarget = 10; // 初始考核目标(社区固定10) + int _communityMonthlyTarget = 10; // 月度考核目标 + int _communityMonthIndex = 0; // 当前考核月份 // 收益数据(从 wallet-service 获取) double _pendingUsdt = 0.0; @@ -283,9 +286,15 @@ class _ProfilePageState extends ConsumerState { /// 加载授权数据 (from authorization-service) Future _loadAuthorizationData() async { try { + debugPrint('[ProfilePage] 开始加载授权数据...'); final authorizationService = ref.read(authorizationServiceProvider); final summary = await authorizationService.getMyAuthorizationSummary(); + debugPrint('[ProfilePage] 授权数据加载成功:'); + debugPrint('[ProfilePage] 社区授权: ${summary.community != null}'); + debugPrint('[ProfilePage] 市公司授权: ${summary.cityCompany != null}'); + debugPrint('[ProfilePage] 省公司授权: ${summary.provinceCompany != null}'); + if (mounted) { setState(() { _community = summary.communityName ?? '--'; @@ -293,10 +302,29 @@ class _ProfilePageState extends ConsumerState { _provinceCompany = summary.provinceCompanyName ?? '--'; // 上级社区和下级社区暂时无法从当前API获取 // 后续需要扩展API或从推荐链中计算 + + // 更新社区考核数据 + if (summary.community != null) { + _hasCommunityAuth = true; + _communityBenefitActive = summary.community!.benefitActive; + _communityCurrentTreeCount = summary.community!.currentTreeCount; + _communityInitialTarget = summary.community!.initialTargetTreeCount; + _communityMonthlyTarget = summary.community!.monthlyTargetTreeCount; + _communityMonthIndex = summary.community!.currentMonthIndex; + debugPrint('[ProfilePage] 社区考核数据:'); + debugPrint('[ProfilePage] 权益激活: $_communityBenefitActive'); + debugPrint('[ProfilePage] 当前数量: $_communityCurrentTreeCount'); + debugPrint('[ProfilePage] 初始目标: $_communityInitialTarget'); + debugPrint('[ProfilePage] 月度目标: $_communityMonthlyTarget'); + debugPrint('[ProfilePage] 考核月份: $_communityMonthIndex'); + } else { + _hasCommunityAuth = false; + } }); } - } catch (e) { + } catch (e, stackTrace) { debugPrint('[ProfilePage] 加载授权数据失败: $e'); + debugPrint('[ProfilePage] 堆栈: $stackTrace'); // 失败时保持默认数据 } } @@ -1591,6 +1619,94 @@ class _ProfilePageState extends ConsumerState { /// 构建社区权益考核 Widget _buildCommunityAssessment() { + // 如果没有社区授权,显示红色提示信息 + if (!_hasCommunityAuth) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0x15F44336), // 浅红色背景 + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: const Color(0x33F44336), // 红色边框 + width: 1, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + '社区权益考核', + style: TextStyle( + fontSize: 18, + fontFamily: 'Inter', + fontWeight: FontWeight.w700, + height: 1.56, + color: Color(0xFF5D4037), + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: const Color(0x20F44336), + borderRadius: BorderRadius.circular(4), + ), + child: const Text( + '未授权', + style: TextStyle( + fontSize: 12, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + color: Color(0xFFF44336), // 红色 + ), + ), + ), + ], + ), + const SizedBox(height: 15), + const Text( + '暂无社区授权', + style: TextStyle( + fontSize: 14, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + height: 1.43, + color: Color(0xFFF44336), // 红色 + ), + ), + const SizedBox(height: 8), + const Text( + '请联系管理员申请社区授权', + style: TextStyle( + fontSize: 12, + fontFamily: 'Inter', + height: 1.5, + color: Color(0x99F44336), // 浅红色 + ), + ), + ], + ), + ); + } + + // 计算进度百分比(用于进度条) + final progressRatio = _communityInitialTarget > 0 + ? (_communityCurrentTreeCount / _communityInitialTarget).clamp(0.0, 1.0) + : 0.0; + + // 确定状态文字和颜色 + final String statusText; + final Color statusColor; + if (_communityBenefitActive) { + statusText = '已激活'; + statusColor = const Color(0xFF4CAF50); // 绿色 + } else { + statusText = '待激活'; + statusColor = const Color(0xFFFF9800); // 橙色 + } + return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( @@ -1604,51 +1720,75 @@ class _ProfilePageState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - '社区权益考核', - style: TextStyle( - fontSize: 18, - fontFamily: 'Inter', - fontWeight: FontWeight.w700, - height: 1.56, - color: Color(0xFF5D4037), - ), - ), - const SizedBox(height: 15), - // 社区等级 + // 标题和状态 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( - '社区等级', + '社区权益考核', style: TextStyle( - fontSize: 14, - fontFamily: 'Inter', - fontWeight: FontWeight.w500, - height: 1.43, - color: Color(0xCC5D4037), - ), - ), - Text( - 'Level $_communityLevel', - style: const TextStyle( - fontSize: 14, + fontSize: 18, fontFamily: 'Inter', fontWeight: FontWeight.w700, - height: 1.43, + height: 1.56, color: Color(0xFF5D4037), ), ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: statusColor.withOpacity(0.15), + borderRadius: BorderRadius.circular(4), + ), + child: Text( + statusText, + style: TextStyle( + fontSize: 12, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + color: statusColor, + ), + ), + ), ], ), - const SizedBox(height: 16), - // 种植数量 + const SizedBox(height: 15), + // 考核月份(仅在权益激活后显示) + if (_communityBenefitActive && _communityMonthIndex > 0) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + '当前考核月份', + style: TextStyle( + fontSize: 14, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + height: 1.43, + color: Color(0xCC5D4037), + ), + ), + Text( + '第 $_communityMonthIndex 月', + style: const TextStyle( + fontSize: 14, + fontFamily: 'Inter', + fontWeight: FontWeight.w700, + height: 1.43, + color: Color(0xFF5D4037), + ), + ), + ], + ), + const SizedBox(height: 16), + ], + // 团队认种数量 / 目标 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( - '种植数量 / 等级要求', - style: TextStyle( + Text( + _communityBenefitActive ? '团队认种 / 月度目标' : '团队认种 / 激活目标', + style: const TextStyle( fontSize: 14, fontFamily: 'Inter', fontWeight: FontWeight.w500, @@ -1657,7 +1797,7 @@ class _ProfilePageState extends ConsumerState { ), ), Text( - '$_currentPlanting / $_requiredPlanting', + '$_communityCurrentTreeCount / ${_communityBenefitActive ? _communityMonthlyTarget : _communityInitialTarget}', style: const TextStyle( fontSize: 14, fontFamily: 'Inter', @@ -1678,7 +1818,7 @@ class _ProfilePageState extends ConsumerState { ), child: FractionallySizedBox( alignment: Alignment.centerLeft, - widthFactor: _currentPlanting / _requiredPlanting, + widthFactor: progressRatio, child: Container( decoration: BoxDecoration( color: const Color(0xFFD4AF37), @@ -1690,8 +1830,8 @@ class _ProfilePageState extends ConsumerState { const SizedBox(height: 16), // 社区贡献奖励 Row( - children: const [ - Text( + children: [ + const Text( '社区贡献奖励', style: TextStyle( fontSize: 14, @@ -1701,16 +1841,20 @@ class _ProfilePageState extends ConsumerState { color: Color(0xCC5D4037), ), ), - SizedBox(width: 39), + const SizedBox(width: 39), Expanded( child: Text( - '有资格获得 3% 的社区福利', + _communityBenefitActive + ? '有资格获得 3% 的社区福利' + : '需团队认种达到 $_communityInitialTarget 棵激活', style: TextStyle( fontSize: 14, fontFamily: 'Inter', fontWeight: FontWeight.w500, height: 1.43, - color: Color(0xFFD4AF37), + color: _communityBenefitActive + ? const Color(0xFFD4AF37) + : const Color(0x995D4037), ), ), ),