refactor(authorization): 简化自助申请授权逻辑,移除待审核状态

自助申请授权直接生效,无需审核流程:
- 移除 pendingApplications 字段(不再需要待审核列表)
- 简化响应 DTO:applicationId -> authorizationId, 移除 status/reviewedAt/reviewNote
- 新增 benefitsActivated 字段表示是否已激活权益
- 更新前端 SelfApplyAuthorizationResponse 和 UserAuthorizationStatusResponse

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-22 06:13:14 -08:00
parent 13df91a55e
commit 05c8e1d6aa
4 changed files with 35 additions and 94 deletions

View File

@ -286,7 +286,8 @@
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(admin-service\\): 修复 Kafka topic 订阅不匹配问题\n\n问题admin-web 用户管理页面无数据\n原因admin-service 订阅的是 ''identity.events''\n 但 identity-service 发送到的是具体的 topic 如 ''identity.UserAccountCreated''\n\n修复将订阅的 topics 改为与 identity-service 的 IDENTITY_TOPICS 一致:\n- identity.UserAccountCreated\n- identity.UserAccountAutoCreated\n- identity.PhoneBound\n- identity.KYCSubmitted\n- identity.KYCVerified\n- identity.KYCRejected\n- identity.UserAccountFrozen\n- identity.UserAccountDeactivated\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>\nEOF\n\\)\")", "Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(admin-service\\): 修复 Kafka topic 订阅不匹配问题\n\n问题admin-web 用户管理页面无数据\n原因admin-service 订阅的是 ''identity.events''\n 但 identity-service 发送到的是具体的 topic 如 ''identity.UserAccountCreated''\n\n修复将订阅的 topics 改为与 identity-service 的 IDENTITY_TOPICS 一致:\n- identity.UserAccountCreated\n- identity.UserAccountAutoCreated\n- identity.PhoneBound\n- identity.KYCSubmitted\n- identity.KYCVerified\n- identity.KYCRejected\n- identity.UserAccountFrozen\n- identity.UserAccountDeactivated\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>\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 = ''0x5b25ae3ac4ad6291ef67aceaf657b62a200d8bf8'';\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 const amount = BigInt\\(1000000\\) * BigInt\\(1000000\\);\n \n console.log\\(''Transferring 1,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(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 = ''0x5b25ae3ac4ad6291ef67aceaf657b62a200d8bf8'';\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 const amount = BigInt\\(1000000\\) * BigInt\\(1000000\\);\n \n console.log\\(''Transferring 1,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(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 = ''0xe9f7dafeb225bd3c88bcad2cce35c6512c9b2987'';\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(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 = ''0xe9f7dafeb225bd3c88bcad2cce35c6512c9b2987'';\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(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 = ''0x631c1c09c5d481d6d2c4a75461a8b46af54eb846'';\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 const amount = BigInt\\(1000000\\) * BigInt\\(1000000\\);\n \n console.log\\(''Transferring 1,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(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 = ''0x631c1c09c5d481d6d2c4a75461a8b46af54eb846'';\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 const amount = BigInt\\(1000000\\) * BigInt\\(1000000\\);\n \n console.log\\(''Transferring 1,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(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 = ''0xa00ac1347f045676F8fc9791595e603810994d67'';\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\")"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

View File

@ -78,29 +78,24 @@ export class SelfApplyAuthorizationDto {
/** /**
* DTO * DTO
*
*/ */
export class SelfApplyAuthorizationResponseDto { export class SelfApplyAuthorizationResponseDto {
@ApiProperty({ description: '申请ID' }) @ApiProperty({ description: '授权ID' })
applicationId: string authorizationId: string
@ApiProperty({ description: '申请状态', enum: ['PENDING', 'APPROVED', 'REJECTED'] }) @ApiProperty({ description: '授权类型' })
status: 'PENDING' | 'APPROVED' | 'REJECTED'
@ApiProperty({ description: '申请类型' })
type: SelfApplyAuthorizationType type: SelfApplyAuthorizationType
@ApiProperty({ description: '申请时间' }) @ApiProperty({ description: '授权时间' })
appliedAt: Date grantedAt: Date
@ApiPropertyOptional({ description: '审核时间' }) @ApiProperty({ description: '是否已激活权益(达到初始考核目标)' })
reviewedAt?: Date benefitsActivated: boolean
@ApiPropertyOptional({ description: '审核备注' })
reviewNote?: string
} }
/** /**
* DTO * DTO
*/ */
export class UserAuthorizationStatusResponseDto { export class UserAuthorizationStatusResponseDto {
@ApiProperty({ description: '是否已认种' }) @ApiProperty({ description: '是否已认种' })
@ -111,11 +106,4 @@ export class UserAuthorizationStatusResponseDto {
@ApiProperty({ description: '已拥有的授权类型列表' }) @ApiProperty({ description: '已拥有的授权类型列表' })
existingAuthorizations: string[] existingAuthorizations: string[]
@ApiProperty({ description: '待审核的申请列表' })
pendingApplications: {
applicationId: string
type: SelfApplyAuthorizationType
appliedAt: Date
}[]
} }

View File

@ -2766,7 +2766,8 @@ export class AuthorizationApplicationService {
// ============================================ // ============================================
/** /**
* *
*
*/ */
async getUserAuthorizationStatus( async getUserAuthorizationStatus(
accountSequence: string, accountSequence: string,
@ -2784,18 +2785,10 @@ export class AuthorizationApplicationService {
.filter(auth => auth.status !== AuthorizationStatus.REVOKED) .filter(auth => auth.status !== AuthorizationStatus.REVOKED)
.map(auth => this.mapRoleTypeToDisplayName(auth.roleType)) .map(auth => this.mapRoleTypeToDisplayName(auth.roleType))
// 3. 获取待审核的申请(暂时返回空,待实现申请审核功能)
const pendingApplications: {
applicationId: string
type: SelfApplyAuthorizationType
appliedAt: Date
}[] = []
return { return {
hasPlanted, hasPlanted,
plantedCount, plantedCount,
existingAuthorizations, existingAuthorizations,
pendingApplications,
} }
} }
@ -2891,12 +2884,10 @@ export class AuthorizationApplicationService {
authorization.clearDomainEvents() authorization.clearDomainEvents()
return { return {
applicationId: authorization.authorizationId.value, authorizationId: authorization.authorizationId.value,
status: 'APPROVED',
type: SelfApplyAuthorizationType.COMMUNITY, type: SelfApplyAuthorizationType.COMMUNITY,
appliedAt: new Date(), grantedAt: new Date(),
reviewedAt: new Date(), benefitsActivated: authorization.isBenefitActivated(),
reviewNote: '自助申请自动审核通过',
} }
} }
@ -2935,12 +2926,10 @@ export class AuthorizationApplicationService {
authorization.clearDomainEvents() authorization.clearDomainEvents()
return { return {
applicationId: authorization.authorizationId.value, authorizationId: authorization.authorizationId.value,
status: 'APPROVED',
type: SelfApplyAuthorizationType.CITY_TEAM, type: SelfApplyAuthorizationType.CITY_TEAM,
appliedAt: new Date(), grantedAt: new Date(),
reviewedAt: new Date(), benefitsActivated: authorization.isBenefitActivated(),
reviewNote: '自助申请自动审核通过',
} }
} }
@ -2979,12 +2968,10 @@ export class AuthorizationApplicationService {
authorization.clearDomainEvents() authorization.clearDomainEvents()
return { return {
applicationId: authorization.authorizationId.value, authorizationId: authorization.authorizationId.value,
status: 'APPROVED',
type: SelfApplyAuthorizationType.PROVINCE_TEAM, type: SelfApplyAuthorizationType.PROVINCE_TEAM,
appliedAt: new Date(), grantedAt: new Date(),
reviewedAt: new Date(), benefitsActivated: authorization.isBenefitActivated(),
reviewNote: '自助申请自动审核通过',
} }
} }

View File

@ -343,41 +343,16 @@ extension SelfApplyAuthorizationTypeExtension on SelfApplyAuthorizationType {
} }
} }
/// ///
class PendingApplication {
final String applicationId;
final String type;
final DateTime appliedAt;
PendingApplication({
required this.applicationId,
required this.type,
required this.appliedAt,
});
factory PendingApplication.fromJson(Map<String, dynamic> json) {
return PendingApplication(
applicationId: json['applicationId'] ?? '',
type: json['type'] ?? '',
appliedAt: json['appliedAt'] != null
? DateTime.parse(json['appliedAt'])
: DateTime.now(),
);
}
}
///
class UserAuthorizationStatusResponse { class UserAuthorizationStatusResponse {
final bool hasPlanted; final bool hasPlanted;
final int plantedCount; final int plantedCount;
final List<String> existingAuthorizations; final List<String> existingAuthorizations;
final List<PendingApplication> pendingApplications;
UserAuthorizationStatusResponse({ UserAuthorizationStatusResponse({
required this.hasPlanted, required this.hasPlanted,
required this.plantedCount, required this.plantedCount,
required this.existingAuthorizations, required this.existingAuthorizations,
required this.pendingApplications,
}); });
factory UserAuthorizationStatusResponse.fromJson(Map<String, dynamic> json) { factory UserAuthorizationStatusResponse.fromJson(Map<String, dynamic> json) {
@ -387,43 +362,33 @@ class UserAuthorizationStatusResponse {
existingAuthorizations: (json['existingAuthorizations'] as List<dynamic>?) existingAuthorizations: (json['existingAuthorizations'] as List<dynamic>?)
?.map((e) => e.toString()) ?.map((e) => e.toString())
.toList() ?? [], .toList() ?? [],
pendingApplications: (json['pendingApplications'] as List<dynamic>?)
?.map((e) => PendingApplication.fromJson(e as Map<String, dynamic>))
.toList() ?? [],
); );
} }
} }
/// ///
///
class SelfApplyAuthorizationResponse { class SelfApplyAuthorizationResponse {
final String applicationId; final String authorizationId;
final String status;
final String type; final String type;
final DateTime appliedAt; final DateTime grantedAt;
final DateTime? reviewedAt; final bool benefitsActivated;
final String? reviewNote;
SelfApplyAuthorizationResponse({ SelfApplyAuthorizationResponse({
required this.applicationId, required this.authorizationId,
required this.status,
required this.type, required this.type,
required this.appliedAt, required this.grantedAt,
this.reviewedAt, required this.benefitsActivated,
this.reviewNote,
}); });
factory SelfApplyAuthorizationResponse.fromJson(Map<String, dynamic> json) { factory SelfApplyAuthorizationResponse.fromJson(Map<String, dynamic> json) {
return SelfApplyAuthorizationResponse( return SelfApplyAuthorizationResponse(
applicationId: json['applicationId'] ?? '', authorizationId: json['authorizationId'] ?? '',
status: json['status'] ?? 'PENDING',
type: json['type'] ?? '', type: json['type'] ?? '',
appliedAt: json['appliedAt'] != null grantedAt: json['grantedAt'] != null
? DateTime.parse(json['appliedAt']) ? DateTime.parse(json['grantedAt'])
: DateTime.now(), : DateTime.now(),
reviewedAt: json['reviewedAt'] != null benefitsActivated: json['benefitsActivated'] ?? false,
? DateTime.parse(json['reviewedAt'])
: null,
reviewNote: json['reviewNote'],
); );
} }
} }
@ -689,7 +654,7 @@ class AuthorizationService {
if (data != null) { if (data != null) {
final result = SelfApplyAuthorizationResponse.fromJson(data); final result = SelfApplyAuthorizationResponse.fromJson(data);
debugPrint('自助申请授权成功: applicationId=${result.applicationId}'); debugPrint('自助申请授权成功: authorizationId=${result.authorizationId}, benefitsActivated=${result.benefitsActivated}');
return result; return result;
} }
throw Exception('申请响应数据格式错误'); throw Exception('申请响应数据格式错误');