结算修改为累计
This commit is contained in:
parent
85ba70778f
commit
5ce1a14341
@ -41,4 +41,9 @@ public class TeamBillCmd implements Serializable {
|
||||
*/
|
||||
private Long operationBackUser;
|
||||
|
||||
// ===== 新增:批次模式字段 =====
|
||||
private Boolean batchMode;
|
||||
private Integer settlementBatch;
|
||||
private Integer monthlyBillBelong;
|
||||
|
||||
}
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
|
||||
/**
|
||||
* 结算结果.
|
||||
*/
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
@ -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)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user