diff --git a/backend/services/admin-service/src/api/controllers/user-detail.controller.ts b/backend/services/admin-service/src/api/controllers/user-detail.controller.ts index 3c424e44..cdf9a294 100644 --- a/backend/services/admin-service/src/api/controllers/user-detail.controller.ts +++ b/backend/services/admin-service/src/api/controllers/user-detail.controller.ts @@ -343,9 +343,10 @@ export class UserDetailController { throw new NotFoundException(`用户 ${accountSequence} 不存在`); } - const [roles, assessments, systemLedger] = await Promise.all([ + const [roles, assessments, benefitAssessments, systemLedger] = await Promise.all([ this.userDetailRepository.getAuthorizationRoles(accountSequence), this.userDetailRepository.getMonthlyAssessments(accountSequence), + this.userDetailRepository.getBenefitAssessments(accountSequence), this.userDetailRepository.getSystemAccountLedger(accountSequence), ]); @@ -385,6 +386,28 @@ export class UserDetailController { completedAt: assessment.completedAt?.toISOString() || null, assessedAt: assessment.assessedAt?.toISOString() || null, })), + // [2026-01-08] 新增:权益考核记录 + benefitAssessments: benefitAssessments.map((ba) => ({ + id: ba.id, + authorizationId: ba.authorizationId, + roleType: ba.roleType, + regionCode: ba.regionCode, + regionName: ba.regionName, + assessmentMonth: ba.assessmentMonth, + monthIndex: ba.monthIndex, + monthlyTarget: ba.monthlyTarget, + cumulativeTarget: ba.cumulativeTarget, + treesCompleted: ba.treesCompleted, + treesRequired: ba.treesRequired, + benefitActionTaken: ba.benefitActionTaken, + previousBenefitStatus: ba.previousBenefitStatus, + newBenefitStatus: ba.newBenefitStatus, + newValidUntil: ba.newValidUntil?.toISOString() || null, + result: ba.result, + remarks: ba.remarks, + assessedAt: ba.assessedAt.toISOString(), + createdAt: ba.createdAt.toISOString(), + })), systemAccountLedger: systemLedger.map((ledger) => ({ ledgerId: ledger.ledgerId.toString(), accountId: ledger.accountId.toString(), diff --git a/backend/services/admin-service/src/api/dto/response/user-detail.dto.ts b/backend/services/admin-service/src/api/dto/response/user-detail.dto.ts index 49045348..96b30052 100644 --- a/backend/services/admin-service/src/api/dto/response/user-detail.dto.ts +++ b/backend/services/admin-service/src/api/dto/response/user-detail.dto.ts @@ -256,11 +256,38 @@ export class SystemAccountLedgerItemDto { createdAt!: string; } +/** + * [2026-01-08] 权益考核记录 + * 独立于火柴人排名(MonthlyAssessment),专门记录权益有效性考核历史 + */ +export class BenefitAssessmentDto { + id!: string; + authorizationId!: string; + roleType!: string; + regionCode!: string; + regionName!: string; + assessmentMonth!: string; + monthIndex!: number; + monthlyTarget!: number; + cumulativeTarget!: number; + treesCompleted!: number; + treesRequired!: number; + benefitActionTaken!: string; // ACTIVATED, RENEWED, DEACTIVATED, NO_CHANGE + previousBenefitStatus!: boolean; + newBenefitStatus!: boolean; + newValidUntil!: string | null; + result!: string; + remarks!: string | null; + assessedAt!: string; + createdAt!: string; +} + /** * 授权详情响应 */ export class AuthorizationDetailResponseDto { roles!: AuthorizationRoleDto[]; assessments!: MonthlyAssessmentDto[]; + benefitAssessments!: BenefitAssessmentDto[]; // [2026-01-08] 新增:权益考核记录 systemAccountLedger!: SystemAccountLedgerItemDto[]; } diff --git a/backend/services/admin-service/src/domain/repositories/user-detail-query.repository.ts b/backend/services/admin-service/src/domain/repositories/user-detail-query.repository.ts index 7b407052..fda72c42 100644 --- a/backend/services/admin-service/src/domain/repositories/user-detail-query.repository.ts +++ b/backend/services/admin-service/src/domain/repositories/user-detail-query.repository.ts @@ -173,6 +173,29 @@ export interface SystemAccountLedger { createdAt: Date; } +// [2026-01-08] 新增:权益考核记录,独立于火柴人排名 +export interface BenefitAssessment { + id: string; + authorizationId: string; + roleType: string; + regionCode: string; + regionName: string; + assessmentMonth: string; + monthIndex: number; + monthlyTarget: number; + cumulativeTarget: number; + treesCompleted: number; + treesRequired: number; + benefitActionTaken: string; // ACTIVATED, RENEWED, DEACTIVATED, NO_CHANGE + previousBenefitStatus: boolean; + newBenefitStatus: boolean; + newValidUntil: Date | null; + result: string; + remarks: string | null; + assessedAt: Date; + createdAt: Date; +} + // ============================================================================ // 仓储接口 // ============================================================================ @@ -246,6 +269,12 @@ export interface IUserDetailQueryRepository { */ getSystemAccountLedger(accountSequence: string): Promise; + /** + * [2026-01-08] 获取权益考核记录 + * 独立于火柴人排名(MonthlyAssessment),专门记录权益有效性考核历史 + */ + getBenefitAssessments(accountSequence: string): Promise; + /** * 获取用户个人认种量(有效树数) */ diff --git a/backend/services/admin-service/src/infrastructure/persistence/repositories/user-detail-query.repository.impl.ts b/backend/services/admin-service/src/infrastructure/persistence/repositories/user-detail-query.repository.impl.ts index d03b862b..bdcc0d94 100644 --- a/backend/services/admin-service/src/infrastructure/persistence/repositories/user-detail-query.repository.impl.ts +++ b/backend/services/admin-service/src/infrastructure/persistence/repositories/user-detail-query.repository.impl.ts @@ -15,6 +15,7 @@ import { AuthorizationRole, MonthlyAssessment, SystemAccountLedger, + BenefitAssessment, } from '../../../domain/repositories/user-detail-query.repository'; @Injectable() @@ -478,6 +479,36 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository return []; } + // [2026-01-08] 新增:获取权益考核记录 + async getBenefitAssessments(accountSequence: string): Promise { + const assessments = await this.prisma.benefitAssessmentQueryView.findMany({ + where: { accountSequence }, + orderBy: [{ assessmentMonth: 'desc' }, { createdAt: 'desc' }], + }); + + return assessments.map((assessment) => ({ + id: assessment.id, + authorizationId: assessment.authorizationId, + roleType: assessment.roleType, + regionCode: assessment.regionCode, + regionName: assessment.regionName, + assessmentMonth: assessment.assessmentMonth, + monthIndex: assessment.monthIndex, + monthlyTarget: assessment.monthlyTarget, + cumulativeTarget: assessment.cumulativeTarget, + treesCompleted: assessment.treesCompleted, + treesRequired: assessment.treesRequired, + benefitActionTaken: assessment.benefitActionTaken, + previousBenefitStatus: assessment.previousBenefitStatus, + newBenefitStatus: assessment.newBenefitStatus, + newValidUntil: assessment.newValidUntil, + result: assessment.result, + remarks: assessment.remarks, + assessedAt: assessment.assessedAt, + createdAt: assessment.createdAt, + })); + } + async getPersonalAdoptionCount(accountSequence: string): Promise { // 统计用户的认种订单数量(状态为 MINING_ENABLED) const count = await this.prisma.plantingOrderQueryView.count({ diff --git a/frontend/admin-web/src/app/(dashboard)/users/[id]/page.tsx b/frontend/admin-web/src/app/(dashboard)/users/[id]/page.tsx index 5652e412..b1308f9c 100644 --- a/frontend/admin-web/src/app/(dashboard)/users/[id]/page.tsx +++ b/frontend/admin-web/src/app/(dashboard)/users/[id]/page.tsx @@ -84,10 +84,10 @@ const plantingStatusLabels: Record = { const roleTypeLabels: Record = { COMMUNITY: '社区权益', COMMUNITY_PARTNER: '社区权益', - AUTH_PROVINCE_COMPANY: '省区域', - PROVINCE_COMPANY: '省团队', - AUTH_CITY_COMPANY: '市区域', - CITY_COMPANY: '市团队', + AUTH_PROVINCE_COMPANY: '省团队', // 授权省公司 = 省团队权益 + PROVINCE_COMPANY: '省区域', // 正式省公司 = 省区域权益 + AUTH_CITY_COMPANY: '市团队', // 授权市公司 = 市团队权益(40U) + CITY_COMPANY: '市区域', // 正式市公司 = 市区域权益(35U+2%算力) }; const authStatusLabels: Record = { @@ -104,6 +104,14 @@ const assessmentResultLabels: Record = { BYPASSED: '豁免', }; +// 权益操作类型标签 +const benefitActionLabels: Record = { + ACTIVATED: '已激活', + RENEWED: '已续期', + DEACTIVATED: '已停用', + NO_CHANGE: '无变化', +}; + /** * 将区域代码转换为名称 * @param code 区域代码,如 450000(省)或 451200(市) @@ -777,6 +785,80 @@ export default function UserDetailPage() { })()} + {/* 权益考核记录 */} +
+

权益考核记录

+ {(() => { + // 只显示用户实际拥有且未撤销角色的权益考核记录 + const activeRoleIds = new Set( + authData.roles + .filter(r => r.status !== 'REVOKED') + .map(r => r.id) + ); + const filteredBenefitAssessments = (authData.benefitAssessments || []).filter( + a => activeRoleIds.has(a.authorizationId) + ); + + // 创建角色ID到区域名称的映射 + const roleIdToRegion = new Map( + authData.roles.map(r => [r.id, r.regionName]) + ); + + return filteredBenefitAssessments.length === 0 ? ( +
暂无权益考核记录
+ ) : ( +
+
+
考核月份
+
角色
+
区域
+
完成/需求
+
权益操作
+
权益状态
+
有效期至
+
结果
+
+ {filteredBenefitAssessments.map((assessment) => ( +
+
{assessment.assessmentMonth}
+
+ {roleTypeLabels[assessment.roleType] || assessment.roleType} +
+
+ {roleIdToRegion.get(assessment.authorizationId) || assessment.regionName || getRegionName(assessment.regionCode)} +
+
+ {formatNumber(assessment.treesCompleted)} / {formatNumber(assessment.treesRequired)} +
+
+ + {benefitActionLabels[assessment.benefitActionTaken] || assessment.benefitActionTaken} + +
+
+ {assessment.previousBenefitStatus ? '有效' : '无效'} → {assessment.newBenefitStatus ? '有效' : '无效'} +
+
+ {assessment.newValidUntil ? formatDate(assessment.newValidUntil) : '-'} +
+
+ + {assessmentResultLabels[assessment.result] || assessment.result} + +
+
+ ))} +
+ ); + })()} +
+ {/* 系统账户流水(如果有) */} {authData.systemAccountLedger.length > 0 && (
diff --git a/frontend/admin-web/src/app/(dashboard)/users/[id]/user-detail.module.scss b/frontend/admin-web/src/app/(dashboard)/users/[id]/user-detail.module.scss index f11217e4..e29c0663 100644 --- a/frontend/admin-web/src/app/(dashboard)/users/[id]/user-detail.module.scss +++ b/frontend/admin-web/src/app/(dashboard)/users/[id]/user-detail.module.scss @@ -847,6 +847,35 @@ } } +// 权益操作类型标签 +.ledgerTable__benefitAction { + display: inline-block; + padding: $spacing-xs $spacing-sm; + border-radius: $border-radius-sm; + font-size: $font-size-xs; + font-weight: $font-weight-medium; + + &--activated { + background-color: rgba($success-color, 0.1); + color: $success-color; + } + + &--renewed { + background-color: rgba($primary-color, 0.1); + color: $primary-color; + } + + &--deactivated { + background-color: rgba($error-color, 0.1); + color: $error-color; + } + + &--no_change { + background-color: rgba($text-disabled, 0.2); + color: $text-secondary; + } +} + // ============================================================================ // 分页 // ============================================================================ diff --git a/frontend/admin-web/src/types/userDetail.types.ts b/frontend/admin-web/src/types/userDetail.types.ts index ec866ece..485b10ef 100644 --- a/frontend/admin-web/src/types/userDetail.types.ts +++ b/frontend/admin-web/src/types/userDetail.types.ts @@ -227,9 +227,33 @@ export interface SystemAccountLedgerItem { createdAt: string; } +// [2026-01-08] 新增:权益考核记录 +export interface BenefitAssessment { + id: string; + authorizationId: string; + roleType: string; + regionCode: string; + regionName: string; + assessmentMonth: string; + monthIndex: number; + monthlyTarget: number; + cumulativeTarget: number; + treesCompleted: number; + treesRequired: number; + benefitActionTaken: string; // ACTIVATED, RENEWED, DEACTIVATED, NO_CHANGE + previousBenefitStatus: boolean; + newBenefitStatus: boolean; + newValidUntil: string | null; + result: string; + remarks: string | null; + assessedAt: string; + createdAt: string; +} + export interface AuthorizationDetailResponse { roles: AuthorizationRole[]; assessments: MonthlyAssessment[]; + benefitAssessments: BenefitAssessment[]; // [2026-01-08] 新增 systemAccountLedger: SystemAccountLedgerItem[]; } @@ -320,3 +344,11 @@ export const ASSESSMENT_RESULT_LABELS: Record = { FAILED: '未通过', BYPASSED: '豁免', }; + +// [2026-01-08] 新增:权益操作类型标签 +export const BENEFIT_ACTION_LABELS: Record = { + ACTIVATED: '已激活', + RENEWED: '已续期', + DEACTIVATED: '已停用', + NO_CHANGE: '无变化', +};