结算修改为累计

This commit is contained in:
tianfeng 2025-11-13 17:59:08 +08:00
parent 85ba70778f
commit 5ce1a14341
10 changed files with 518 additions and 107 deletions

View File

@ -41,4 +41,9 @@ public class TeamBillCmd implements Serializable {
*/
private Long operationBackUser;
// ===== 新增批次模式字段 =====
private Boolean batchMode;
private Integer settlementBatch;
private Integer monthlyBillBelong;
}

View File

@ -22,6 +22,7 @@ import com.red.circle.other.infra.database.rds.entity.team.BillDiamondBalance;
import com.red.circle.other.infra.database.rds.service.team.BillDiamondBalanceService;
import com.red.circle.other.inner.endpoint.team.target.TeamProfileClient;
import com.red.circle.other.inner.enums.team.*;
import com.red.circle.other.inner.enums.team.TeamMemberTargetIndex;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamRemark;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSetting;
@ -148,17 +149,24 @@ public class TeamBillSettleListener implements MessageListener {
return;
}
List<TeamMemberTarget> teamMemberTargets = teamMemberTargetService.listByBillBelong(
teamBillCycle.getTeamId(),
teamBillCycle.getBillBelong());
log.warn("团队账单结算teamMemberTargets."+teamMemberTargets);
Integer billBelong = teamBillCycle.getBillBelong();
Integer batchNumber = TeamBillCycleUtils.extractBatch(billBelong);
List<TeamMemberTarget> teamMemberTargets;
// teamMemberTargets.forEach(teamMemberTarget -> {
// TeamMember teamMember = teamMemberService.getByMemberId(teamMemberTarget.getUserId());
// if (Objects.nonNull(teamMember) && TeamMemberRole.OWN.eq(teamMember.getRole())) {
// teamMemberTarget.setIsOwn(true);
// }
// });
if (batchNumber != null && batchNumber == 2) {
teamMemberTargets = teamMemberTargetService.listByMonthlyBillBelong(
teamBillCycle.getTeamId(),
TeamBillCycleUtils.extractMonth(billBelong)
);
} else {
// 上半月结算直接查询当前批次数据
teamMemberTargets = teamMemberTargetService.listByBillBelong(
teamBillCycle.getTeamId(),
billBelong
);
}
log.warn("团队账单结算teamMemberTargets."+teamMemberTargets);
// 获得区域配置细信息
SysRegionConfig regionConfig = sysRegionConfigService.getById(teamProfile.getRegion());

View File

@ -5,6 +5,7 @@ import com.red.circle.mq.business.model.event.team.TeamBillSettleEvent;
import com.red.circle.mq.rocket.business.producer.TeamSalaryMqMessage;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamProfile;
import com.red.circle.other.infra.database.mongo.service.gift.GiftGiveRunningWaterService;
import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamBillCycleService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService;
import com.red.circle.other.infra.utils.ZonedDateTimeUtils;
@ -13,7 +14,9 @@ import com.red.circle.tool.core.date.TimestampUtils;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -57,50 +60,6 @@ public class TeamBillTask {
processTeamBill();
}
// /**
// * 10分钟 检查是否存在没有补目标用户.
// */
// @Scheduled(cron = "0 */10 * * * ?")
// @TaskCacheLock(key = "CHECK_NOT_COUNT_ANCHOR_TARGET", expireSecond = 50)
// public void checkNotCountAnchorGiftTarget() {
//
// List<GiftGiveRunningWater> giftGiveRunningWaters = giftGiveRunningWaterService.listThisMonthAnchorNotCountTarget(
// 1000);
// if (CollectionUtils.isEmpty(giftGiveRunningWaters)) {
// return;
// }
//
// for (GiftGiveRunningWater runningWater : giftGiveRunningWaters) {
// for (GiftAcceptUser acceptUser : runningWater.getAcceptUsers()) {
//
// if (!Objects.equals(acceptUser.getAnchor(), Boolean.TRUE)) {
// continue;
// }
//
// if (BigDecimalUtils.isNullOrLteZero(acceptUser.getTargetAmount())) {
// giftGiveRunningWaterService.updateAcceptUserCountTargetAmountTrue(
// runningWater.getId(),
// 0L,
// acceptUser.getAcceptUserId());
// continue;
// }
//
// // 累计目标
// TeamTargetBillRes teamTargetBillRes = teamTargetManager
// .incrementGiftValue(acceptUser.getAcceptUserId(),
// acceptUser.getTargetAmount());
// if (teamTargetBillRes.getSuccess()) {
// // 标记已被统计
// giftGiveRunningWaterService.updateAcceptUserCountTargetAmountTrue(
// runningWater.getId(),
// teamTargetBillRes.getBillId(),
// acceptUser.getAcceptUserId());
// }
//
// }
// }
// }
//
private void processTeamBill() {
log.warn("开始执行账单处理:{},{}", ZonedDateTimeUtils.nowAsiaRiyadhToInt(), LocalDateTime.now());
@ -112,25 +71,25 @@ public class TeamBillTask {
log.warn("结束账单处理");
}
/**
* 创建本月账单发出结算信号.
*/
private void consumeProcessPayOutBill() {
teamProfileService.scanStatusAvailable(teamProfiles -> {
/**
* 创建本月账单发出结算信号.
*/
private void consumeProcessPayOutBill() {
teamProfileService.scanStatusAvailable(teamProfiles -> {
for (TeamProfile teamProfile : teamProfiles) {
TeamBillCmd cmd = new TeamBillCmd()
.setSysOrigin(teamProfile.getSysOrigin())
.setTeamId(teamProfile.getId())
.setRegion(teamProfile.getRegion())
.setOperationTime(TimestampUtils.now())
.setOperationBackUser(teamProfile.getCreateUser());
for (TeamProfile teamProfile : teamProfiles) {
teamBillCycleService.createIfAbsent(new TeamBillCmd()
.setSysOrigin(teamProfile.getSysOrigin())
.setTeamId(teamProfile.getId())
.setRegion(teamProfile.getRegion())
.setOperationTime(TimestampUtils.now())
.setOperationBackUser(teamProfile.getCreateUser()));
otherMqMessage.teamBillSettle(new TeamBillSettleEvent().setTeamId(teamProfile.getId()));
}
}, 5000);
}
teamBillCycleService.createIfAbsent(cmd);
otherMqMessage.teamBillSettle(new TeamBillSettleEvent()
.setTeamId(teamProfile.getId()));
}
}, 5000);
}
}

View File

@ -0,0 +1,50 @@
package com.red.circle.other.infra.database.mongo.dto.team;
import com.red.circle.other.inner.enums.team.TeamBillCycleStatus;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
public class SettlementBatch {
/**
* 批次号用于标识当前是当月的第几个结算批次
* <ul>
* <li>{@code 1} 表示上半月1日 - 15日</li>
* <li>{@code 2} 表示下半月16日 - 月末</li>
* </ul>
*/
private Integer batchNumber;
/**
* 批次标题用于展示用途格式如"2025年11月上半月"
*/
private String batchTitle;
/**
* 本批次统计周期的开始日期包含例如{@code 2025-11-01}
*/
private LocalDate periodStart;
/**
* 本批次统计周期的结束日期包含例如{@code 2025-11-15}上半月 {@code 2025-11-30}下半月
*/
private LocalDate periodEnd;
/**
* 实际执行结算操作的时间戳精确到秒例如{@code 2025-11-16T10:30:00}
*/
private LocalDateTime settlementTime;
/**
* 本批次的结算结果详情包含金额条目数等汇总信息
*/
private TeamBillSettleResult batchResult;
/**
* 本批次的结算状态待结算结算中已结算结算失败等
*/
private TeamBillCycleStatus batchStatus;
}

View File

@ -2,12 +2,14 @@ package com.red.circle.other.infra.database.mongo.entity.team.team;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.other.infra.database.mongo.dto.team.SettlementBatch;
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillSettleResult;
import com.red.circle.other.inner.model.dto.agency.agency.TeamRemark;
import com.red.circle.other.inner.enums.team.TeamBillCycleStatus;
import java.io.Serial;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.util.List;
import lombok.Data;
import lombok.experimental.Accessors;
@ -54,6 +56,33 @@ public class TeamBillCycle implements Serializable {
*/
private TeamBillCycleStatus status;
// ===== 新增字段用于批次模式=====
/**
* 是否启用批次模式.
* true: 月度账单模式两次结算共享一个账期
* false: 传统半月账单模式每次结算独立账期
* 默认false兼容历史数据
*/
private Boolean batchMode;
/**
* 结算批次号仅批次模式下有效.
* 1 = 上半月1-15号
* 2 = 下半月16-月末
* null: 传统模式兼容历史数据
*/
private Integer settlementBatch;
/**
* 月度账期仅批次模式下有效.
* 格式yyyyMM ( 202511)
* 用于前端按月聚合查询
* null: 传统模式兼容历史数据
*/
private Integer monthlyBillBelong;
/**
* 结算结果.
*/

View File

@ -540,4 +540,83 @@ public final class TeamBillCycleUtils {
return strings[0] + "-" + strings[1];
}
/**
* 获取月度账期用于批次模式
* 返回格式202511
*/
public static Integer getMonthlyBillBelong() {
LocalDate now = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
return Integer.parseInt(now.format(DateTimeFormatter.ofPattern("yyyyMM")));
}
/**
* 获取当前结算批次
* 返回1 = 上半月, 2 = 下半月
*/
public static Integer getCurrentSettlementBatch() {
LocalDate now = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
return now.getDayOfMonth() < 16 ? 1 : 2;
}
/**
* 判断是否启用批次模式通过区域配置
*/
public static Boolean isBatchModeEnabled(String region) {
// 从配置中读取或者硬编码
// 例如return "SA".equals(region); // 沙特地区启用批次模式
return true;
}
/**
* billBelong 提取月份兼容新旧格式
* 20251101 -> 202511
* 20251116 -> 202511
*/
public static Integer extractMonth(Integer billBelong) {
if (billBelong == null) {
return null;
}
String billStr = String.valueOf(billBelong);
if (billStr.length() == 6) {
// 已经是月度格式
return billBelong;
}
// 提取年月部分
return Integer.parseInt(billStr.substring(0, 6));
}
/**
* billBelong 提取批次号兼容新旧格式
* 20251101 -> 1
* 20251116 -> 2
*/
public static Integer extractBatch(Integer billBelong) {
if (billBelong == null) {
return null;
}
String billStr = String.valueOf(billBelong);
if (billStr.length() != 8) {
return null;
}
int day = Integer.parseInt(billStr.substring(6, 8));
return day == 1 ? 1 : 2;
}
/**
* 生成批次标题
* 202511, 1 -> "2025年11月上半月"
* 202511, 2 -> "2025年11月下半月"
*/
public static String generateBatchTitle(Integer monthlyBillBelong, Integer batchNumber) {
if (monthlyBillBelong == null) {
return "";
}
String billStr = String.valueOf(monthlyBillBelong);
int year = Integer.parseInt(billStr.substring(0, 4));
int month = Integer.parseInt(billStr.substring(4, 6));
String batchDesc = batchNumber == 1 ? "上半月" : "下半月";
return String.format("%d年%d月%s", year, month, batchDesc);
}
}

View File

@ -216,4 +216,36 @@ public interface TeamMemberTargetService {
TeamSalaryStatisticsCO statisticsTeamSalaryByTeamIdAndBillBelong(
Long teamId, Integer billBelong);
/**
* 按月查询团队成员目标聚合半月数据.
* 用于前端按月展示会自动聚合同一个月的多条记录
*
* @param teamId 团队ID
* @param monthlyBillBelong 月度账期202511
* @return 该月所有成员目标列表可能包含两个批次的数据
*/
List<TeamMemberTarget> listByMonthlyBillBelong(Long teamId, Integer monthlyBillBelong);
/**
* 按月查询单个成员目标聚合半月数据.
* 用于查询指定用户的月度数据
*
* @param teamId 团队ID
* @param userId 用户ID
* @param monthlyBillBelong 月度账期202511
* @return 该月该成员的所有目标记录通常1-2条
*/
List<TeamMemberTarget> listMemberMonthlyTarget(Long teamId, Long userId, Integer monthlyBillBelong);
/**
* 按月统计成员目标聚合后返回月度汇总.
* 用于前端展示月度累计数据会自动合并同一个月的多条记录
*
* @param teamId 团队ID
* @param userId 用户ID
* @param monthlyBillBelong 月度账期202511
* @return 月度汇总后的成员目标dailyTargets 包含整月数据settlementResult 为累计值
*/
TeamMemberTarget aggregateMonthlyTarget(Long teamId, Long userId, Integer monthlyBillBelong);
}

View File

@ -8,6 +8,7 @@ import com.red.circle.component.mongodb.config.MongoPrimaryService;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillSettleResult;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamBillCycle;
import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamBillCycleService;
import com.red.circle.other.inner.model.cmd.team.TeamBillCmd;
import com.red.circle.other.inner.model.cmd.team.TeamBillCycleBackQryCmd;
@ -30,6 +31,7 @@ import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
@ -47,6 +49,7 @@ import static com.red.circle.other.infra.database.mongo.service.team.TeamBillCyc
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class TeamBillCycleServiceImpl implements TeamBillCycleService {
private final MongoTemplate mongoTemplate;
@ -220,27 +223,114 @@ public class TeamBillCycleServiceImpl implements TeamBillCycleService {
}
@Override
public void createIfAbsent(TeamBillCmd param) {
if (existsPrimaryBillBelong(param.getTeamId())) {
return;
/**
* 创建账单兼容批次模式
*/
@Override
public void createIfAbsent(TeamBillCmd cmd) {
// 计算 billBelong保持原有逻辑
Integer billBelong = TeamBillCycleUtils.getCalcBillBelong();
// 检查是否已存在
boolean existing = existsPrimaryBillBelong(cmd.getTeamId(), billBelong);
if (existing) {
return;
}
// 创建账单
TeamBillCycle bill = new TeamBillCycle()
.setId(IdWorkerUtils.getId())
.setSysOrigin(cmd.getSysOrigin())
.setTeamId(cmd.getTeamId())
.setRegion(cmd.getRegion())
.setBillBelong(billBelong)
.setBillTitle(generateBillTitle(billBelong))
.setStatus(TeamBillCycleStatus.UNPAID)
.setCreateTime(cmd.getOperationTime())
.setCreateUser(cmd.getOperationBackUser());
Boolean batchMode = TeamBillCycleUtils.isBatchModeEnabled(cmd.getRegion());
Integer currentBatch = TeamBillCycleUtils.getCurrentSettlementBatch();
Integer monthlyBillBelong = TeamBillCycleUtils.getMonthlyBillBelong();
// ===== 新增如果启用批次模式添加批次信息 =====
if (batchMode) {
bill.setBatchMode(true);
bill.setSettlementBatch(currentBatch);
bill.setMonthlyBillBelong(monthlyBillBelong);
// 覆盖账单标题增加批次说明
String batchTitle = TeamBillCycleUtils.generateBatchTitle(
cmd.getMonthlyBillBelong(),
cmd.getSettlementBatch()
);
bill.setBillTitle(batchTitle);
}
mongoTemplate.save(bill);
log.info("创建账单成功:{}", bill);
}
mongoTemplate.save(new TeamBillCycle()
.setId(IdWorkerUtils.getId())
.setSysOrigin(param.getSysOrigin())
.setTeamId(param.getTeamId())
.setStatus(TeamBillCycleStatus.UNPAID)
.setRegion(param.getRegion())
.setInternalRemarks(Lists.newArrayList())
.setSettleResult(null)
.setBillBelong(getCalcBillBelong())
.setBillTitle(getCalcBillBelong().toString())
.setCreateTime(param.getOperationTime())
.setUpdateTime(TimestampUtils.now())
.setCreateUser(param.getOperationBackUser())
.setUpdateUser(param.getOperationBackUser())
);
}
private String generateBillTitle(Integer billBelong) {
String[] dateRange = TeamBillCycleUtils.parseBillBelongToDateRange(billBelong);
return dateRange[0] + " - " + dateRange[1];
}
/**
* 创建或更新账单批次
* 关键逻辑
* - 第1批次创建新账单
* - 第2批次更新现有账单
*/
/*public void createOrUpdateBatch(TeamBillCmd cmd) {
// 查找当前月是否已有账单
TeamBillCycle existing = findByTeamAndBillBelong(
cmd.getTeamId(),
cmd.getBillBelong()
);
if (existing == null) {
// 第1批次创建新账单
TeamBillCycle newBill = new TeamBillCycle()
.setId(IdWorkerUtils.getId())
.setSysOrigin(cmd.getSysOrigin())
.setTeamId(cmd.getTeamId())
.setRegion(cmd.getRegion())
.setBillBelong(cmd.getBillBelong()) // 月度格式202511
.setBillTitle(parseBillTitle(cmd.getBillBelong())) // "2025年11月"
.setSettlementBatch(cmd.getSettlementBatch())
.setBatchStartDate(cmd.getBatchStartDate())
.setBatchEndDate(cmd.getBatchEndDate())
.setStatus(TeamBillCycleStatus.PAY_OUT)
.setCreateTime(cmd.getOperationTime())
.setCreateUser(cmd.getOperationBackUser());
save(newBill);
log.info("创建账单billBelong={}, batch={}",
cmd.getBillBelong(), cmd.getSettlementBatch());
} else {
// 第2批次更新现有账单
existing.setSettlementBatch(cmd.getSettlementBatch());
existing.setBatchStartDate(cmd.getBatchStartDate());
existing.setBatchEndDate(cmd.getBatchEndDate());
existing.setStatus(TeamBillCycleStatus.PAY_OUT);
existing.setUpdateTime(cmd.getOperationTime());
existing.setUpdateUser(cmd.getOperationBackUser());
updateById(existing);
log.info("更新账单billBelong={}, batch={}",
cmd.getBillBelong(), cmd.getSettlementBatch());
}
}
*//**
* 解析账单标题
*//*
private String parseBillTitle(Integer billBelong) {
String billStr = String.valueOf(billBelong);
int year = Integer.parseInt(billStr.substring(0, 4));
int month = Integer.parseInt(billStr.substring(4, 6));
return String.format("%d年%d月", year, month);
}*/
@Override
public List<TeamBillCycle> listLastMonthByTeamIds(Set<Long> teamIds) {
@ -297,11 +387,11 @@ public class TeamBillCycleServiceImpl implements TeamBillCycleService {
return cycleList.get(0);
}
private boolean existsPrimaryBillBelong(Long teamId) {
private boolean existsPrimaryBillBelong(Long teamId, Integer billBelong) {
return mongoPrimaryService.exists("team_bill_cycle",
Filters.and(
Filters.eq("teamId", teamId),
Filters.eq("billBelong", getCalcBillBelong())
Filters.eq("billBelong", billBelong)
)
);
}

View File

@ -659,4 +659,175 @@ public class TeamMemberTargetServiceImpl implements TeamMemberTargetService {
return statistics;
}
@Override
public List<TeamMemberTarget> listByMonthlyBillBelong(Long teamId, Integer monthlyBillBelong) {
// 构造查询条件billBelong 前6位匹配月度账期
// 例如monthlyBillBelong = 202511匹配 20251101 20251116
Criteria criteria = Criteria.where("teamId").is(teamId)
.and("history").is(Boolean.FALSE)
.and("billBelong").regex("^" + monthlyBillBelong);
return mongoTemplate.find(Query.query(criteria)
.with(Sort.by(Order.asc("billBelong"))),
TeamMemberTarget.class);
}
@Override
public List<TeamMemberTarget> listMemberMonthlyTarget(Long teamId, Long userId, Integer monthlyBillBelong) {
// 查询指定用户在指定月份的所有目标记录
Criteria criteria = Criteria.where("teamId").is(teamId)
.and("userId").is(userId)
.and("history").is(Boolean.FALSE)
.and("billBelong").regex("^" + monthlyBillBelong);
return mongoTemplate.find(Query.query(criteria)
.with(Sort.by(Order.asc("billBelong"))),
TeamMemberTarget.class);
}
@Override
public TeamMemberTarget aggregateMonthlyTarget(Long teamId, Long userId, Integer monthlyBillBelong) {
// 1. 查询该月的所有记录可能1条或2条
List<TeamMemberTarget> monthlyTargets = listMemberMonthlyTarget(teamId, userId, monthlyBillBelong);
if (CollectionUtils.isEmpty(monthlyTargets)) {
return null;
}
// 2. 如果只有一条记录直接返回
if (monthlyTargets.size() == 1) {
return monthlyTargets.get(0);
}
// 3. 多条记录时聚合数据
TeamMemberTarget aggregated = new TeamMemberTarget();
TeamMemberTarget first = monthlyTargets.get(0);
// 复制基础信息使用第一条记录的信息
aggregated.setId(first.getId());
aggregated.setTimeId(first.getTimeId());
aggregated.setSysOrigin(first.getSysOrigin());
aggregated.setTeamId(first.getTeamId());
aggregated.setUserId(first.getUserId());
aggregated.setIsOwn(first.getIsOwn());
aggregated.setBillBelong(monthlyBillBelong); // 设置为月度格式
aggregated.setBillTitle(generateMonthlyBillTitle(monthlyBillBelong));
aggregated.setHistory(Boolean.FALSE);
aggregated.setCreateTime(first.getCreateTime());
aggregated.setUpdateTime(monthlyTargets.get(monthlyTargets.size() - 1).getUpdateTime());
// 4. 合并每日目标数据dailyTargets
List<TeamMemberTargetIndex> allDailyTargets = monthlyTargets.stream()
.flatMap(target -> target.getDailyTargets().stream())
.sorted((d1, d2) -> d1.getDateNumber().compareTo(d2.getDateNumber()))
.collect(Collectors.toList());
aggregated.setDailyTargets(allDailyTargets);
// 5. 聚合结算结果如果有多条取总和
List<TeamMemberTargetSettlementResult> settlementResults = monthlyTargets.stream()
.map(TeamMemberTarget::getSettlementResult)
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(settlementResults)) {
TeamMemberTargetSettlementResult aggregatedResult = new TeamMemberTargetSettlementResult();
// 取最高等级
aggregatedResult.setLevel(settlementResults.stream()
.map(TeamMemberTargetSettlementResult::getLevel)
.filter(Objects::nonNull)
.max(Integer::compareTo)
.orElse(null));
// 判断是否满足等级都满足才算满足
aggregatedResult.setSatisfyLevel(settlementResults.stream()
.allMatch(r -> Boolean.TRUE.equals(r.getSatisfyLevel())));
// 累加金额
aggregatedResult.setMemberSalary(settlementResults.stream()
.map(TeamMemberTargetSettlementResult::getMemberSalary)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add));
aggregatedResult.setOwnSalary(settlementResults.stream()
.map(TeamMemberTargetSettlementResult::getOwnSalary)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add));
aggregatedResult.setTotalSalary(settlementResults.stream()
.map(TeamMemberTargetSettlementResult::getTotalSalary)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add));
// 累加业绩数据这些字段已经在 dailyTargets 这里主要用于快速查看
aggregatedResult.setAcceptGiftValue(aggregated.sumAcceptGiftValue());
aggregatedResult.setGiveGiftValue(aggregated.sumGiveGiftValue());
aggregatedResult.setRoomValue(aggregated.sumRoomValue());
aggregatedResult.setOnlineTime(aggregated.sumAllTargetTime());
// 道具奖励取任一非空值
aggregatedResult.setPropsRewards(settlementResults.stream()
.map(TeamMemberTargetSettlementResult::getPropsRewards)
.filter(Objects::nonNull)
.findFirst()
.orElse(null));
// 道具领取状态任一已领取则算已领取
aggregatedResult.setPropsReceived(settlementResults.stream()
.anyMatch(r -> Boolean.TRUE.equals(r.getPropsReceived())));
aggregated.setSettlementResult(aggregatedResult);
}
// 6. 状态取最终状态按优先级CREDITED > UNDONE > AUTO > NOT_RECORDED
TeamMemberTargetStatus finalStatus = monthlyTargets.stream()
.map(TeamMemberTarget::getStatus)
.filter(Objects::nonNull)
.max((s1, s2) -> compareStatus(s1, s2))
.orElse(TeamMemberTargetStatus.NOT_RECORDED);
aggregated.setStatus(finalStatus);
// 7. 合并备注
List<String> allRemarks = monthlyTargets.stream()
.map(TeamMemberTarget::getRemarks)
.filter(CollectionUtils::isNotEmpty)
.flatMap(List::stream)
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(allRemarks)) {
aggregated.setRemarks(allRemarks);
}
return aggregated;
}
/**
* 生成月度账单标题
* 202511 -> "2025年11月"
*/
private String generateMonthlyBillTitle(Integer monthlyBillBelong) {
String billStr = String.valueOf(monthlyBillBelong);
int year = Integer.parseInt(billStr.substring(0, 4));
int month = Integer.parseInt(billStr.substring(4, 6));
return String.format("%d年%d月", year, month);
}
/**
* 比较状态优先级
* 返回值: 1 表示 s1 优先级高, -1 表示 s2 优先级高, 0 表示相等
*/
private int compareStatus(TeamMemberTargetStatus s1, TeamMemberTargetStatus s2) {
// 定义优先级顺序CREDITED > UNDONE > AUTO > NOT_RECORDED
Map<TeamMemberTargetStatus, Integer> priority = Map.of(
TeamMemberTargetStatus.CREDITED, 4,
TeamMemberTargetStatus.UNDONE, 3,
TeamMemberTargetStatus.AUTO, 2,
TeamMemberTargetStatus.NOT_RECORDED, 1
);
int p1 = priority.getOrDefault(s1, 0);
int p2 = priority.getOrDefault(s2, 0);
return Integer.compare(p1, p2);
}
}

View File

@ -68,19 +68,7 @@ public class TeamMonthBillTest {
*/
@Test
public void processTeamBillSettle() {
String json = " [\n" +
"\"1970912960966479874\",\n" +
"\"1965724731682754561\",\n" +
"\"1982985325435207682\",\n" +
"\"1978213327176196097\",\n" +
"\"1981316214519701506\",\n" +
"\"1980616977268658178\",\n" +
"\"1982190084063019010\",\n" +
"\"1982431621845925889\",\n" +
"\"1982372655623356418\",\n" +
"\"1980617113277353986\",\n" +
"\"1978447372520308737\"\n" +
" ]";
String json = "['1984235784268349441']";
List<String> teamList = JSON.parseArray(json, String.class);
for (String teamId : teamList) {
Long id = Long.parseLong(teamId);