diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/BdLeaderSalarySettlementServiceImpl.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/BdLeaderSalarySettlementServiceImpl.java index e44a2fff..f82d4b6a 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/BdLeaderSalarySettlementServiceImpl.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/BdLeaderSalarySettlementServiceImpl.java @@ -3,6 +3,9 @@ package com.red.circle.other.app.service; import com.red.circle.common.business.core.enums.ReceiptType; import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; import com.red.circle.other.app.dto.clientobject.BdLeaderSalarySettlementCO; +import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.other.domain.model.user.UserProfile; +import com.red.circle.other.infra.database.mongo.entity.bd.detail.BdLeaderTeamMemberDetail; import com.red.circle.other.infra.enums.BdSettlementStatus; import com.red.circle.other.infra.database.mongo.entity.bd.BdLeaderSalaryPolicy; import com.red.circle.other.infra.database.mongo.entity.bd.BdLeaderSalarySettlementRecord; @@ -11,7 +14,9 @@ import com.red.circle.other.infra.database.mongo.service.bd.BdLeaderSalaryPolicy import com.red.circle.other.infra.database.mongo.service.bd.BdLeaderSalarySettlementRecordService; import com.red.circle.other.infra.database.mongo.service.bd.BdSalarySettlementRecordService; import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentBaseInfo; +import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentTeam; import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService; +import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentTeamService; import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.sequence.IdWorkerUtils; @@ -27,8 +32,7 @@ import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.math.RoundingMode; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; /** @@ -43,11 +47,13 @@ import java.util.stream.Collectors; public class BdLeaderSalarySettlementServiceImpl implements BdLeaderSalarySettlementService { private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService; + private final BusinessDevelopmentTeamService businessDevelopmentTeamService; private final BdSalarySettlementRecordService bdSalarySettlementRecordService; private final BdLeaderSalaryPolicyService bdLeaderSalaryPolicyService; private final BdLeaderSalarySettlementRecordService bdLeaderSalarySettlementRecordService; private final UserBankBalanceClient userBankBalanceClient; private final UserBankRunningWaterClient userBankRunningWaterClient; + private final UserProfileGateway userProfileGateway; @Override public void processAllBdLeaderSettlement(Integer billBelong, String billTitle) { @@ -114,15 +120,50 @@ public class BdLeaderSalarySettlementServiceImpl implements BdLeaderSalarySettle record.setBizNo(bizNo); record.setStatus(BdSettlementStatus.PENDING); - // 4. 统计所有 BD 的工资总和 + // 4. 统计所有 BD 的工资总和并收集成员明细 + List memberDetails = new ArrayList<>(); BigDecimal totalBdSalary = BigDecimal.ZERO; + for (Long bdUserId : bdUserIds) { BdSalarySettlementRecord bdRecord = bdSalarySettlementRecordService.getByUserIdAndBillBelong(bdUserId, billBelong); - if (bdRecord != null && bdRecord.getSettlementSalary() != null) { - totalBdSalary = totalBdSalary.add(bdRecord.getSettlementSalary()); + + if (bdRecord != null) { + // 统计工资 + if (bdRecord.getSettlementSalary() != null) { + totalBdSalary = totalBdSalary.add(bdRecord.getSettlementSalary()); + } + + // 收集成员明细 + BdLeaderTeamMemberDetail detail = new BdLeaderTeamMemberDetail(); + detail.setBdUserId(bdUserId); + detail.setSalary(bdRecord.getSettlementSalary() != null ? + bdRecord.getSettlementSalary().setScale(2, RoundingMode.DOWN) : BigDecimal.ZERO); + detail.setAgencyCount(bdRecord.getAgencyNumber() != null ? bdRecord.getAgencyNumber() : 0); + + memberDetails.add(detail); } } + record.setTeamSalaryAmount(totalBdSalary.setScale(2, RoundingMode.DOWN)); + + // 批量查询 BD 用户信息 + if (!CollectionUtils.isEmpty(memberDetails)) { + Set bdUserIdSet = new HashSet<>(bdUserIds); + Map userProfileMap = userProfileGateway.mapByUserIds(bdUserIdSet); + + for (BdLeaderTeamMemberDetail detail : memberDetails) { + UserProfile userProfile = userProfileMap.get(detail.getBdUserId()); + if (userProfile != null) { + detail.setBdUserName(userProfile.getUserNickname()); + detail.setBdUserAvatar(userProfile.getUserAvatar()); + } + } + + // 按工资降序排列 + memberDetails.sort((a, b) -> b.getSalary().compareTo(a.getSalary())); + } + + record.setMemberDetails(memberDetails); // 5. 匹配政策并计算工资 calculateLeaderSalary(record); diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/BdSalarySettlementServiceImpl.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/BdSalarySettlementServiceImpl.java index 83ceae3c..ba6558ae 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/BdSalarySettlementServiceImpl.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/BdSalarySettlementServiceImpl.java @@ -4,6 +4,9 @@ import com.red.circle.common.business.core.enums.ReceiptType; import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; import com.red.circle.other.app.dto.clientobject.BdSalarySettlementCO; import com.red.circle.other.app.dto.clientobject.BdTeamStatisticsCO; +import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.other.domain.model.user.UserProfile; +import com.red.circle.other.infra.database.mongo.entity.bd.detail.BdTeamMemberDetail; import com.red.circle.other.infra.enums.BdPolicyType; import com.red.circle.other.infra.enums.BdSettlementStatus; import com.red.circle.other.infra.database.mongo.entity.bd.BdSalaryPolicy; @@ -52,6 +55,7 @@ public class BdSalarySettlementServiceImpl implements BdSalarySettlementService private final UserBankBalanceClient userBankBalanceClient; private final UserBankRunningWaterClient userBankRunningWaterClient; private final InAppPurchaseDetailsClientApi inAppPurchaseDetailsClientApi; + private final UserProfileGateway userProfileGateway; @Override @@ -107,13 +111,17 @@ public class BdSalarySettlementServiceImpl implements BdSalarySettlementService // 3. 统计 BD 团队数据 BdTeamStatisticsCO teamStatistics = statisticsBdTeam(bdUserId, billBelong); - // 4. 匹配政策并计算工资 - BdSalarySettlementRecord record = calculateSalary(bdUserId, billBelong, billTitle, teamStatistics, bizNo); + // 4. 收集团队成员明细数据 + List memberDetails = collectTeamMemberDetails(bdUserId, billBelong, teamStatistics); - // 5. 保存结算记录 + // 5. 匹配政策并计算工资 + BdSalarySettlementRecord record = calculateSalary(bdUserId, billBelong, billTitle, teamStatistics, bizNo); + record.setMemberDetails(memberDetails); + + // 6. 保存结算记录 bdSalarySettlementRecordService.create(record); - // 6. 如果工资大于 0,则发放工资 + // 7. 如果工资大于 0,则发放工资 if (record.getSettlementSalary() != null && record.getSettlementSalary().compareTo(BigDecimal.ZERO) > 0) { try { // 调用钱包服务发放工资 @@ -138,6 +146,118 @@ public class BdSalarySettlementServiceImpl implements BdSalarySettlementService return convertToCO(record); } + /** + * 收集团队成员明细数据. + */ + private List collectTeamMemberDetails(Long bdUserId, Integer billBelong, + BdTeamStatisticsCO teamStatistics) { + List memberDetails = new ArrayList<>(); + + // 查询 BD 下的所有 Agent 团队 + List bdTeams = businessDevelopmentTeamService.listByBdUserId(bdUserId); + + if (CollectionUtils.isEmpty(bdTeams)) { + return memberDetails; + } + + // 收集所有用户ID + Set allUserIds = new HashSet<>(); + Map userHostCountMap = new HashMap<>(); + Map userSalaryMap = new HashMap<>(); + + // 遍历每个团队,统计每个用户的数据 + for (BusinessDevelopmentTeam bdTeam : bdTeams) { + // 查询该团队的成员目标记录 + List memberTargets = + teamMemberTargetService.listByBillBelong(bdTeam.getTeamId(), billBelong); + + if (CollectionUtils.isEmpty(memberTargets)) { + continue; + } + + // 统计每个用户的数据 + for (com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget target : memberTargets) { + Long userId = target.getUserId(); + allUserIds.add(userId); + + // 统计 Host 数量(一个团队 = 1个 Host) + userHostCountMap.merge(userId, 1, Integer::sum); + + // 统计工资(从结算结果中获取) + if (target.getSettlementResult() != null && target.getSettlementResult().getTotalSalary() != null) { + BigDecimal salary = target.getSettlementResult().getTotalSalary(); + userSalaryMap.merge(userId, salary, BigDecimal::add); + } + } + } + + if (CollectionUtils.isEmpty(allUserIds)) { + return memberDetails; + } + + // 批量查询用户信息 + Map userProfileMap = userProfileGateway.mapByUserIds(allUserIds); + + // 计算结算周期的开始和结束时间 + int year = billBelong / 100; + int month = billBelong % 100; + java.time.LocalDateTime startTime = java.time.LocalDateTime.of(year, month, 1, 0, 0, 0); + java.time.LocalDateTime endTime = startTime.plusMonths(1).minusSeconds(1); + + // 批量查询用户充值统计 + Map userRechargeMap = new HashMap<>(); + try { + com.red.circle.framework.dto.ResultResponse> response = + inAppPurchaseDetailsClientApi.batchUserRechargeStatistics( + new ArrayList<>(allUserIds), + SysOriginPlatformEnum.LIKEI.name(), + startTime, + endTime + ); + + if (response != null && response.getStatus() && !CollectionUtils.isEmpty(response.getBody())) { + response.getBody().forEach(dto -> { + if (dto.getTotalAmount() != null) { + userRechargeMap.put(dto.getUserId(), dto.getTotalAmount()); + } + }); + } + } catch (Exception e) { + log.error("查询用户充值统计失败", e); + } + + // 组装成员明细数据 + for (Long userId : allUserIds) { + BdTeamMemberDetail detail = new BdTeamMemberDetail(); + detail.setUserId(userId); + + // 设置用户信息 + UserProfile userProfile = userProfileMap.get(userId); + if (userProfile != null) { + detail.setUserName(userProfile.getUserNickname()); + detail.setUserAvatar(userProfile.getUserAvatar()); + } + + // 设置工资 + BigDecimal salary = userSalaryMap.getOrDefault(userId, BigDecimal.ZERO); + detail.setSalary(salary.setScale(2, RoundingMode.DOWN)); + + // 设置充值 + BigDecimal recharge = userRechargeMap.getOrDefault(userId, BigDecimal.ZERO); + detail.setRecharge(recharge.setScale(2, RoundingMode.DOWN)); + + // 设置 Host 数量 + detail.setHostCount(userHostCountMap.getOrDefault(userId, 0)); + + memberDetails.add(detail); + } + + // 按工资降序排列 + memberDetails.sort((a, b) -> b.getSalary().compareTo(a.getSalary())); + + return memberDetails; + } + /** * 统计 BD 的团队数据. */ diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/BdLeaderSalarySettlementRecord.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/BdLeaderSalarySettlementRecord.java index ec7c7472..bdcd6d0c 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/BdLeaderSalarySettlementRecord.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/BdLeaderSalarySettlementRecord.java @@ -2,6 +2,7 @@ package com.red.circle.other.infra.database.mongo.entity.bd; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.red.circle.other.infra.database.mongo.entity.bd.detail.BdLeaderTeamMemberDetail; import com.red.circle.other.infra.enums.BdSettlementStatus; import lombok.Data; import lombok.experimental.Accessors; @@ -12,6 +13,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.sql.Timestamp; +import java.util.List; /** * BD Leader 工资结算记录. @@ -115,6 +117,11 @@ public class BdLeaderSalarySettlementRecord implements Serializable { */ private String bizNo; + /** + * 团队成员明细列表(BD 维度). + */ + private List memberDetails; + /** * 创建时间. */ diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/BdSalarySettlementRecord.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/BdSalarySettlementRecord.java index 327da25c..0868b97b 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/BdSalarySettlementRecord.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/BdSalarySettlementRecord.java @@ -2,6 +2,7 @@ package com.red.circle.other.infra.database.mongo.entity.bd; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.red.circle.other.infra.database.mongo.entity.bd.detail.BdTeamMemberDetail; import com.red.circle.other.infra.enums.BdPolicyType; import com.red.circle.other.infra.enums.BdSettlementStatus; import lombok.Data; @@ -13,6 +14,7 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.sql.Timestamp; +import java.util.List; /** * BD 工资结算记录. @@ -111,6 +113,11 @@ public class BdSalarySettlementRecord implements Serializable { */ private String bizNo; + /** + * 团队成员明细列表(Agent 维度). + */ + private List memberDetails; + /** * 创建时间. */ diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/detail/BdLeaderTeamMemberDetail.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/detail/BdLeaderTeamMemberDetail.java new file mode 100644 index 00000000..a77bc385 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/detail/BdLeaderTeamMemberDetail.java @@ -0,0 +1,50 @@ +package com.red.circle.other.infra.database.mongo.entity.bd.detail; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * BD Leader 团队成员明细(BD 维度). + * + * @author AI Assistant + * @since 2025-10-31 + */ +@Data +@Accessors(chain = true) +public class BdLeaderTeamMemberDetail implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * BD 用户 ID. + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long bdUserId; + + /** + * BD 用户名称. + */ + private String bdUserName; + + /** + * BD 用户头像. + */ + private String bdUserAvatar; + + /** + * 工资金额(美元). + */ + private BigDecimal salary; + + /** + * Agency 数量. + */ + private Integer agencyCount; +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/detail/BdTeamMemberDetail.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/detail/BdTeamMemberDetail.java new file mode 100644 index 00000000..f11b45fc --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/bd/detail/BdTeamMemberDetail.java @@ -0,0 +1,55 @@ +package com.red.circle.other.infra.database.mongo.entity.bd.detail; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * BD 团队成员明细(Agent 维度). + * + * @author AI Assistant + * @since 2025-10-31 + */ +@Data +@Accessors(chain = true) +public class BdTeamMemberDetail implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户 ID. + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long userId; + + /** + * 用户名称. + */ + private String userName; + + /** + * 用户头像. + */ + private String userAvatar; + + /** + * 工资金额(美元). + */ + private BigDecimal salary; + + /** + * 充值金额(美元). + */ + private BigDecimal recharge; + + /** + * Host 数量. + */ + private Integer hostCount; +}