feat(authorization): 实现市/省团队自动升级为市/省区域机制
- 添加 findAllActiveAuthProvinceCompanies 和 findAllActiveAuthCityCompanies 仓储方法 - 在认种事件处理中添加 checkAllTeamAutoUpgrade 检查所有已激活团队 - 市团队达到1万棵自动升级为市区域(CITY_COMPANY) - 省团队达到5万棵自动升级为省区域(PROVINCE_COMPANY) - 保持原有手动授权功能不变 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0a64024773
commit
bf63852a0f
|
|
@ -116,4 +116,18 @@ export interface IAuthorizationRoleRepository {
|
|||
* 用于审计和历史记录查询
|
||||
*/
|
||||
findByIdIncludeDeleted(authorizationId: AuthorizationId): Promise<AuthorizationRole | null>
|
||||
|
||||
// ============ 自动升级检查方法 ============
|
||||
|
||||
/**
|
||||
* 查找所有权益已激活的省团队(AUTH_PROVINCE_COMPANY)授权
|
||||
* 用于自动升级检查:省团队 → 省区域
|
||||
*/
|
||||
findAllActiveAuthProvinceCompanies(): Promise<AuthorizationRole[]>
|
||||
|
||||
/**
|
||||
* 查找所有权益已激活的市团队(AUTH_CITY_COMPANY)授权
|
||||
* 用于自动升级检查:市团队 → 市区域
|
||||
*/
|
||||
findAllActiveAuthCityCompanies(): Promise<AuthorizationRole[]>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,9 +183,13 @@ export class EventConsumerController {
|
|||
}
|
||||
}
|
||||
|
||||
// 4. 检查自动升级条件(省区域/市区域)
|
||||
// 4. 检查自动升级条件(省区域/市区域)- 仅针对当前用户的祖先链
|
||||
await this.checkAutoUpgrade(teamStats)
|
||||
|
||||
// 5. 检查所有已激活市/省团队的自动升级条件
|
||||
// 每次认种都可能导致某个市/省团队达到升级阈值
|
||||
await this.checkAllTeamAutoUpgrade()
|
||||
|
||||
this.logger.log(`[PLANTING] Completed processing tree planted event for user ${userId}`)
|
||||
} catch (error) {
|
||||
this.logger.error(`[PLANTING] Error processing tree planted for user ${userId}:`, error)
|
||||
|
|
@ -486,4 +490,198 @@ export class EventConsumerController {
|
|||
|
||||
this.logger.log(`[AUTO-UPGRADE] Successfully auto upgraded user ${accountSequence} to city company: ${cityName}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查所有已激活市/省团队的自动升级条件
|
||||
* 业务规则:
|
||||
* - 已激活权益的省团队(AUTH_PROVINCE_COMPANY)用户,如果团队认种数达到5万棵,自动升级为省区域(PROVINCE_COMPANY)
|
||||
* - 已激活权益的市团队(AUTH_CITY_COMPANY)用户,如果团队认种数达到1万棵,自动升级为市区域(CITY_COMPANY)
|
||||
*/
|
||||
private async checkAllTeamAutoUpgrade(): Promise<void> {
|
||||
this.logger.debug('[TEAM-AUTO-UPGRADE] Starting check for all active team authorizations')
|
||||
|
||||
// 并行检查省团队和市团队
|
||||
await Promise.all([
|
||||
this.checkAllAuthProvinceUpgrade(),
|
||||
this.checkAllAuthCityUpgrade(),
|
||||
])
|
||||
|
||||
this.logger.debug('[TEAM-AUTO-UPGRADE] Completed check for all active team authorizations')
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查所有已激活省团队的自动升级条件
|
||||
*/
|
||||
private async checkAllAuthProvinceUpgrade(): Promise<void> {
|
||||
// 1. 获取所有权益已激活的省团队授权
|
||||
const activeAuthProvinces = await this.authorizationRepository.findAllActiveAuthProvinceCompanies()
|
||||
if (activeAuthProvinces.length === 0) {
|
||||
this.logger.debug('[TEAM-AUTO-UPGRADE] No active auth province companies found')
|
||||
return
|
||||
}
|
||||
|
||||
this.logger.debug(`[TEAM-AUTO-UPGRADE] Found ${activeAuthProvinces.length} active auth province companies`)
|
||||
|
||||
// 2. 逐个检查是否达到升级阈值
|
||||
for (const authProvince of activeAuthProvinces) {
|
||||
await this.checkAuthProvinceUpgrade(authProvince)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查单个省团队是否可以升级为省区域
|
||||
*/
|
||||
private async checkAuthProvinceUpgrade(authProvince: AuthorizationRole): Promise<void> {
|
||||
const accountSequence = authProvince.userId.accountSequence
|
||||
const provinceCode = authProvince.regionCode.value
|
||||
const provinceName = authProvince.regionName
|
||||
|
||||
// 1. 获取该用户的团队统计
|
||||
const teamStats = await this.statsRepository.findByAccountSequence(accountSequence)
|
||||
if (!teamStats) {
|
||||
this.logger.debug(`[TEAM-AUTO-UPGRADE] No team stats found for auth province ${accountSequence}`)
|
||||
return
|
||||
}
|
||||
|
||||
const totalTeamCount = teamStats.totalTeamPlantingCount
|
||||
|
||||
// 2. 检查是否达到省区域升级阈值(5万棵)
|
||||
if (totalTeamCount < EventConsumerController.PROVINCE_UPGRADE_THRESHOLD) {
|
||||
this.logger.debug(`[TEAM-AUTO-UPGRADE] Auth province ${accountSequence} has ${totalTeamCount} trees, not reaching ${EventConsumerController.PROVINCE_UPGRADE_THRESHOLD} threshold`)
|
||||
return
|
||||
}
|
||||
|
||||
this.logger.log(`[TEAM-AUTO-UPGRADE] Auth province ${accountSequence} reached ${totalTeamCount} trees, checking upgrade eligibility`)
|
||||
|
||||
// 3. 检查该用户是否已有省区域授权
|
||||
const existingProvinceCompany = await this.authorizationRepository.findByAccountSequenceAndRoleType(
|
||||
accountSequence,
|
||||
RoleType.PROVINCE_COMPANY,
|
||||
)
|
||||
if (existingProvinceCompany && existingProvinceCompany.status !== AuthorizationStatus.REVOKED) {
|
||||
this.logger.debug(`[TEAM-AUTO-UPGRADE] User ${accountSequence} already has province company authorization`)
|
||||
return
|
||||
}
|
||||
|
||||
// 4. 检查该用户是否已有市区域授权(互斥)
|
||||
const existingCityCompany = await this.authorizationRepository.findByAccountSequenceAndRoleType(
|
||||
accountSequence,
|
||||
RoleType.CITY_COMPANY,
|
||||
)
|
||||
if (existingCityCompany && existingCityCompany.status !== AuthorizationStatus.REVOKED) {
|
||||
this.logger.debug(`[TEAM-AUTO-UPGRADE] User ${accountSequence} already has city company authorization, cannot upgrade to province`)
|
||||
return
|
||||
}
|
||||
|
||||
// 5. 检查该省是否已有省区域授权
|
||||
const existingProvinceRegion = await this.authorizationRepository.findProvinceCompanyByRegion(provinceCode)
|
||||
if (existingProvinceRegion) {
|
||||
this.logger.debug(`[TEAM-AUTO-UPGRADE] Province ${provinceName} already has province company authorization`)
|
||||
return
|
||||
}
|
||||
|
||||
// 6. 执行自动升级:省团队 → 省区域
|
||||
this.logger.log(`[TEAM-AUTO-UPGRADE] Auto upgrading auth province ${accountSequence} to province company: ${provinceName}`)
|
||||
|
||||
const userId = UserId.create(teamStats.userId, accountSequence)
|
||||
const authorization = AuthorizationRole.createAutoUpgradedProvinceCompany({
|
||||
userId,
|
||||
provinceCode,
|
||||
provinceName,
|
||||
})
|
||||
|
||||
await this.authorizationRepository.save(authorization)
|
||||
await this.eventPublisher.publishAll(authorization.domainEvents)
|
||||
authorization.clearDomainEvents()
|
||||
|
||||
this.logger.log(`[TEAM-AUTO-UPGRADE] Successfully auto upgraded auth province ${accountSequence} to province company: ${provinceName}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查所有已激活市团队的自动升级条件
|
||||
*/
|
||||
private async checkAllAuthCityUpgrade(): Promise<void> {
|
||||
// 1. 获取所有权益已激活的市团队授权
|
||||
const activeAuthCities = await this.authorizationRepository.findAllActiveAuthCityCompanies()
|
||||
if (activeAuthCities.length === 0) {
|
||||
this.logger.debug('[TEAM-AUTO-UPGRADE] No active auth city companies found')
|
||||
return
|
||||
}
|
||||
|
||||
this.logger.debug(`[TEAM-AUTO-UPGRADE] Found ${activeAuthCities.length} active auth city companies`)
|
||||
|
||||
// 2. 逐个检查是否达到升级阈值
|
||||
for (const authCity of activeAuthCities) {
|
||||
await this.checkAuthCityUpgrade(authCity)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查单个市团队是否可以升级为市区域
|
||||
*/
|
||||
private async checkAuthCityUpgrade(authCity: AuthorizationRole): Promise<void> {
|
||||
const accountSequence = authCity.userId.accountSequence
|
||||
const cityCode = authCity.regionCode.value
|
||||
const cityName = authCity.regionName
|
||||
|
||||
// 1. 获取该用户的团队统计
|
||||
const teamStats = await this.statsRepository.findByAccountSequence(accountSequence)
|
||||
if (!teamStats) {
|
||||
this.logger.debug(`[TEAM-AUTO-UPGRADE] No team stats found for auth city ${accountSequence}`)
|
||||
return
|
||||
}
|
||||
|
||||
const totalTeamCount = teamStats.totalTeamPlantingCount
|
||||
|
||||
// 2. 检查是否达到市区域升级阈值(1万棵)
|
||||
if (totalTeamCount < EventConsumerController.CITY_UPGRADE_THRESHOLD) {
|
||||
this.logger.debug(`[TEAM-AUTO-UPGRADE] Auth city ${accountSequence} has ${totalTeamCount} trees, not reaching ${EventConsumerController.CITY_UPGRADE_THRESHOLD} threshold`)
|
||||
return
|
||||
}
|
||||
|
||||
this.logger.log(`[TEAM-AUTO-UPGRADE] Auth city ${accountSequence} reached ${totalTeamCount} trees, checking upgrade eligibility`)
|
||||
|
||||
// 3. 检查该用户是否已有市区域授权
|
||||
const existingCityCompany = await this.authorizationRepository.findByAccountSequenceAndRoleType(
|
||||
accountSequence,
|
||||
RoleType.CITY_COMPANY,
|
||||
)
|
||||
if (existingCityCompany && existingCityCompany.status !== AuthorizationStatus.REVOKED) {
|
||||
this.logger.debug(`[TEAM-AUTO-UPGRADE] User ${accountSequence} already has city company authorization`)
|
||||
return
|
||||
}
|
||||
|
||||
// 4. 检查该用户是否已有省区域授权(互斥)
|
||||
const existingProvinceCompany = await this.authorizationRepository.findByAccountSequenceAndRoleType(
|
||||
accountSequence,
|
||||
RoleType.PROVINCE_COMPANY,
|
||||
)
|
||||
if (existingProvinceCompany && existingProvinceCompany.status !== AuthorizationStatus.REVOKED) {
|
||||
this.logger.debug(`[TEAM-AUTO-UPGRADE] User ${accountSequence} already has province company authorization, cannot upgrade to city`)
|
||||
return
|
||||
}
|
||||
|
||||
// 5. 检查该市是否已有市区域授权
|
||||
const existingCityRegion = await this.authorizationRepository.findCityCompanyByRegion(cityCode)
|
||||
if (existingCityRegion) {
|
||||
this.logger.debug(`[TEAM-AUTO-UPGRADE] City ${cityName} already has city company authorization`)
|
||||
return
|
||||
}
|
||||
|
||||
// 6. 执行自动升级:市团队 → 市区域
|
||||
this.logger.log(`[TEAM-AUTO-UPGRADE] Auto upgrading auth city ${accountSequence} to city company: ${cityName}`)
|
||||
|
||||
const userId = UserId.create(teamStats.userId, accountSequence)
|
||||
const authorization = AuthorizationRole.createAutoUpgradedCityCompany({
|
||||
userId,
|
||||
cityCode,
|
||||
cityName,
|
||||
})
|
||||
|
||||
await this.authorizationRepository.save(authorization)
|
||||
await this.eventPublisher.publishAll(authorization.domainEvents)
|
||||
authorization.clearDomainEvents()
|
||||
|
||||
this.logger.log(`[TEAM-AUTO-UPGRADE] Successfully auto upgraded auth city ${accountSequence} to city company: ${cityName}`)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -447,6 +447,34 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi
|
|||
return record ? this.toDomain(record) : null
|
||||
}
|
||||
|
||||
// ============ 自动升级检查方法 ============
|
||||
|
||||
async findAllActiveAuthProvinceCompanies(): Promise<AuthorizationRole[]> {
|
||||
const records = await this.prisma.authorizationRole.findMany({
|
||||
where: {
|
||||
roleType: RoleType.AUTH_PROVINCE_COMPANY,
|
||||
status: AuthorizationStatus.AUTHORIZED,
|
||||
benefitActive: true,
|
||||
...this.notDeleted,
|
||||
},
|
||||
orderBy: { createdAt: 'asc' },
|
||||
})
|
||||
return records.map((record) => this.toDomain(record))
|
||||
}
|
||||
|
||||
async findAllActiveAuthCityCompanies(): Promise<AuthorizationRole[]> {
|
||||
const records = await this.prisma.authorizationRole.findMany({
|
||||
where: {
|
||||
roleType: RoleType.AUTH_CITY_COMPANY,
|
||||
status: AuthorizationStatus.AUTHORIZED,
|
||||
benefitActive: true,
|
||||
...this.notDeleted,
|
||||
},
|
||||
orderBy: { createdAt: 'asc' },
|
||||
})
|
||||
return records.map((record) => this.toDomain(record))
|
||||
}
|
||||
|
||||
private toDomain(record: any): AuthorizationRole {
|
||||
const props: AuthorizationRoleProps = {
|
||||
authorizationId: AuthorizationId.create(record.id),
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ describe('Domain Services Integration Tests', () => {
|
|||
findAllByUserIdIncludeDeleted: jest.fn(),
|
||||
findAllByAccountSequenceIncludeDeleted: jest.fn(),
|
||||
findByIdIncludeDeleted: jest.fn(),
|
||||
findAllActiveAuthProvinceCompanies: jest.fn(),
|
||||
findAllActiveAuthCityCompanies: jest.fn(),
|
||||
}
|
||||
|
||||
const mockMonthlyAssessmentRepository: jest.Mocked<IMonthlyAssessmentRepository> = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue