Use monthly team billing cycle
This commit is contained in:
parent
d9ff507e5f
commit
9deeef5004
@ -9,7 +9,6 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -29,7 +28,7 @@ public class AdminTaskQueryCmdExe {
|
|||||||
|
|
||||||
public List<AdminTaskRecord> execute(AdminTaskQueryCmd cmd) {
|
public List<AdminTaskRecord> execute(AdminTaskQueryCmd cmd) {
|
||||||
LocalDate cycleStartDate = cmd.getCycleStartDate() != null ? cmd.getCycleStartDate() :
|
LocalDate cycleStartDate = cmd.getCycleStartDate() != null ? cmd.getCycleStartDate() :
|
||||||
LocalDate.parse(TeamBillCycleUtils.getCalcBillBelong().toString(), DateTimeFormatter.ofPattern("yyyyMMdd"));
|
TeamBillCycleUtils.getBillBelongStartDate(TeamBillCycleUtils.getCalcBillBelong());
|
||||||
List<AdminTaskRecord> records = adminTaskGateway.findByUserIdAndCycle(cmd.getUserId(), cycleStartDate);
|
List<AdminTaskRecord> records = adminTaskGateway.findByUserIdAndCycle(cmd.getUserId(), cycleStartDate);
|
||||||
Map<String, AdminTaskRecord> taskRecordMap = records.stream().collect(Collectors.toMap(AdminTaskRecord::getTaskType, e -> e));
|
Map<String, AdminTaskRecord> taskRecordMap = records.stream().collect(Collectors.toMap(AdminTaskRecord::getTaskType, e -> e));
|
||||||
|
|
||||||
@ -48,7 +47,7 @@ public class AdminTaskQueryCmdExe {
|
|||||||
*/
|
*/
|
||||||
public AdminTaskRecord findByUserIdAndTaskTypeAndCycle(Long userId, String taskType, LocalDate cycleStartDate) {
|
public AdminTaskRecord findByUserIdAndTaskTypeAndCycle(Long userId, String taskType, LocalDate cycleStartDate) {
|
||||||
LocalDate queryDate = cycleStartDate != null ? cycleStartDate :
|
LocalDate queryDate = cycleStartDate != null ? cycleStartDate :
|
||||||
LocalDate.parse(TeamBillCycleUtils.getCalcBillBelong().toString(), DateTimeFormatter.ofPattern("yyyyMMdd"));
|
TeamBillCycleUtils.getBillBelongStartDate(TeamBillCycleUtils.getCalcBillBelong());
|
||||||
return adminTaskGateway.findByUserIdAndTaskTypeAndCycle(userId, taskType, queryDate);
|
return adminTaskGateway.findByUserIdAndTaskTypeAndCycle(userId, taskType, queryDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -96,8 +96,9 @@ public class TeamMemberWorkQryExe {
|
|||||||
teamMemberTargetService.listByGeBillBelong(
|
teamMemberTargetService.listByGeBillBelong(
|
||||||
teamMember.getTeamId(), teamMember.getMemberId(), billBelong))
|
teamMember.getTeamId(), teamMember.getMemberId(), billBelong))
|
||||||
.map(target -> target.stream()
|
.map(target -> target.stream()
|
||||||
.collect(Collectors.toMap(TeamMemberTarget::getBillBelong, v -> v)))
|
.collect(Collectors.toMap(TeamMemberTarget::getBillBelong, v -> v, (v1, v2) -> v2)))
|
||||||
.orElseGet(Maps::newHashMap);
|
.orElseGet(Maps::newHashMap);
|
||||||
|
Map<Integer, TeamMemberTarget> monthlyTargetCache = Maps.newHashMap();
|
||||||
|
|
||||||
List<TeamBillCycle> unpaidBillCycles = teamBillCycles.stream()
|
List<TeamBillCycle> unpaidBillCycles = teamBillCycles.stream()
|
||||||
.filter(
|
.filter(
|
||||||
@ -116,13 +117,19 @@ public class TeamMemberWorkQryExe {
|
|||||||
.setMemberProfile(userProfile)
|
.setMemberProfile(userProfile)
|
||||||
.setTargets(teamBillCycles.stream()
|
.setTargets(teamBillCycles.stream()
|
||||||
.map(bill -> {
|
.map(bill -> {
|
||||||
|
Integer monthlyBillBelong = TeamBillCycleUtils.extractMonth(bill.getBillBelong());
|
||||||
TeamMemberWorkBillCO co = new TeamMemberWorkBillCO()
|
TeamMemberWorkBillCO co = new TeamMemberWorkBillCO()
|
||||||
.setId(bill.getId())
|
.setId(bill.getId())
|
||||||
.setBillBelong(bill.getBillBelong())
|
.setBillBelong(monthlyBillBelong)
|
||||||
.setBillTitle(TeamBillCycleUtils.parseBillBelongToDateRangeStr(bill.getBillBelong()))
|
.setBillTitle(TeamBillCycleUtils.parseBillBelongToDateRangeStr(monthlyBillBelong))
|
||||||
.setStatus(bill.getStatus().name());
|
.setStatus(bill.getStatus().name());
|
||||||
|
|
||||||
TeamMemberTarget target = teamMemberTargetMap.get(bill.getBillBelong());
|
TeamMemberTarget target = resolveMonthlyTarget(
|
||||||
|
teamMember.getTeamId(),
|
||||||
|
teamMember.getMemberId(),
|
||||||
|
bill,
|
||||||
|
teamMemberTargetMap,
|
||||||
|
monthlyTargetCache);
|
||||||
if (Objects.isNull(target)) {
|
if (Objects.isNull(target)) {
|
||||||
return co;
|
return co;
|
||||||
}
|
}
|
||||||
@ -187,6 +194,38 @@ public class TeamMemberWorkQryExe {
|
|||||||
.map(SysRegionConfig::getMetadata).orElseGet(CollectionUtils::newHashMap);
|
.map(SysRegionConfig::getMetadata).orElseGet(CollectionUtils::newHashMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TeamMemberTarget resolveMonthlyTarget(Long teamId,
|
||||||
|
Long memberId,
|
||||||
|
TeamBillCycle bill,
|
||||||
|
Map<Integer, TeamMemberTarget> targetMap,
|
||||||
|
Map<Integer, TeamMemberTarget> monthlyTargetCache) {
|
||||||
|
Integer billBelong = bill.getBillBelong();
|
||||||
|
Integer monthlyBillBelong = TeamBillCycleUtils.extractMonth(billBelong);
|
||||||
|
|
||||||
|
TeamMemberTarget cachedMonthlyTarget = monthlyTargetCache.get(monthlyBillBelong);
|
||||||
|
if (Objects.nonNull(cachedMonthlyTarget)) {
|
||||||
|
return cachedMonthlyTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
TeamMemberTarget aggregatedTarget = teamMemberTargetService.aggregateMonthlyTarget(
|
||||||
|
teamId, memberId, monthlyBillBelong);
|
||||||
|
if (Objects.nonNull(aggregatedTarget)) {
|
||||||
|
monthlyTargetCache.put(monthlyBillBelong, aggregatedTarget);
|
||||||
|
return aggregatedTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
TeamMemberTarget monthlyTarget = targetMap.get(monthlyBillBelong);
|
||||||
|
if (Objects.nonNull(monthlyTarget)) {
|
||||||
|
return monthlyTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
TeamMemberTarget exactTarget = targetMap.get(billBelong);
|
||||||
|
if (Objects.equals(billBelong, monthlyBillBelong)) {
|
||||||
|
return exactTarget;
|
||||||
|
}
|
||||||
|
return exactTarget;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否开启每日自动结算工资.
|
* 是否开启每日自动结算工资.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -8,12 +8,9 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 管理员工资结算定时任务.
|
* 管理员工资结算定时任务.
|
||||||
* 每月1号和16号 2点执行半月结算(在BD和BD Leader结算之后)
|
* 每月1号2点执行整月结算(在BD和BD Leader结算之后).
|
||||||
*
|
*
|
||||||
* @author AI Assistant
|
* @author AI Assistant
|
||||||
* @since 2025-12-13
|
* @since 2025-12-13
|
||||||
@ -26,9 +23,9 @@ public class AdminSalarySettlementTask {
|
|||||||
private final AdminSalarySettlementService adminSalarySettlementService;
|
private final AdminSalarySettlementService adminSalarySettlementService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每月1号和16号 2点执行半月结算.
|
* 每月1号2点执行上月整月结算.
|
||||||
*/
|
*/
|
||||||
@Scheduled(cron = "0 0 2 1,16 * ?", zone = "Asia/Riyadh")
|
@Scheduled(cron = "0 0 2 1 * ?", zone = "Asia/Riyadh")
|
||||||
@TaskCacheLock(key = "ADMIN_SALARY_SETTLEMENT", expireSecond = 86400)
|
@TaskCacheLock(key = "ADMIN_SALARY_SETTLEMENT", expireSecond = 86400)
|
||||||
public void processAdminSalarySettlement() {
|
public void processAdminSalarySettlement() {
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
@ -55,28 +52,10 @@ public class AdminSalarySettlementTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算需要结算的账期.
|
* 计算需要结算的上月整月账期.
|
||||||
* 规则:
|
|
||||||
* - 如果当前账期是本月1号(如20250101),则结算上个月16号的账期(如20241216)
|
|
||||||
* - 如果当前账期是本月16号(如20250116),则结算本月1号的账期(如20250101)
|
|
||||||
*/
|
*/
|
||||||
private Integer calculateSettlementBillBelong(Integer currentBillBelong) {
|
private Integer calculateSettlementBillBelong(Integer currentBillBelong) {
|
||||||
String billStr = String.valueOf(currentBillBelong);
|
return TeamBillCycleUtils.getPreviousMonthlyBillBelong(currentBillBelong);
|
||||||
int year = Integer.parseInt(billStr.substring(0, 4));
|
|
||||||
int month = Integer.parseInt(billStr.substring(4, 6));
|
|
||||||
int day = Integer.parseInt(billStr.substring(6, 8));
|
|
||||||
|
|
||||||
LocalDate settlementDate;
|
|
||||||
|
|
||||||
if (day == 1) {
|
|
||||||
// 当前是本月1号,结算上个月16号
|
|
||||||
settlementDate = LocalDate.of(year, month, 1).minusMonths(1).withDayOfMonth(16);
|
|
||||||
} else {
|
|
||||||
// 当前是本月16号,结算本月1号
|
|
||||||
settlementDate = LocalDate.of(year, month, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Integer.parseInt(settlementDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -8,13 +8,9 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BD Leader 工资结算定时任务.
|
* BD Leader 工资结算定时任务.
|
||||||
* 每月1号和16号 0点执行半月结算
|
* 每月1号0点执行整月结算.
|
||||||
*
|
*
|
||||||
* @author AI Assistant
|
* @author AI Assistant
|
||||||
* @since 2025-10-29
|
* @since 2025-10-29
|
||||||
@ -27,9 +23,9 @@ public class BdLeaderSalarySettlementTask {
|
|||||||
private final BdLeaderSalarySettlementService bdLeaderSalarySettlementService;
|
private final BdLeaderSalarySettlementService bdLeaderSalarySettlementService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每月1号和16号 1点半执行半月结算.
|
* 每月1号1点执行上月整月结算.
|
||||||
*/
|
*/
|
||||||
@Scheduled(cron = "0 0 1 1,16 * ?", zone = "Asia/Riyadh")
|
@Scheduled(cron = "0 0 1 1 * ?", zone = "Asia/Riyadh")
|
||||||
@TaskCacheLock(key = "BD_LEADER_SALARY_SETTLEMENT", expireSecond = 86400)
|
@TaskCacheLock(key = "BD_LEADER_SALARY_SETTLEMENT", expireSecond = 86400)
|
||||||
public void processBdLeaderSalarySettlement() {
|
public void processBdLeaderSalarySettlement() {
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
@ -56,28 +52,10 @@ public class BdLeaderSalarySettlementTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算需要结算的账期
|
* 计算需要结算的上月整月账期.
|
||||||
* 规则:
|
|
||||||
* - 如果当前账期是本月1号(如20250101),则结算上个月16号的账期(如20241216)
|
|
||||||
* - 如果当前账期是本月16号(如20250116),则结算本月1号的账期(如20250101)
|
|
||||||
*/
|
*/
|
||||||
private Integer calculateSettlementBillBelong(Integer currentBillBelong) {
|
private Integer calculateSettlementBillBelong(Integer currentBillBelong) {
|
||||||
String billStr = String.valueOf(currentBillBelong);
|
return TeamBillCycleUtils.getPreviousMonthlyBillBelong(currentBillBelong);
|
||||||
int year = Integer.parseInt(billStr.substring(0, 4));
|
|
||||||
int month = Integer.parseInt(billStr.substring(4, 6));
|
|
||||||
int day = Integer.parseInt(billStr.substring(6, 8));
|
|
||||||
|
|
||||||
LocalDate settlementDate;
|
|
||||||
|
|
||||||
if (day == 1) {
|
|
||||||
// 当前是本月1号,结算上个月16号
|
|
||||||
settlementDate = LocalDate.of(year, month, 1).minusMonths(1).withDayOfMonth(16);
|
|
||||||
} else {
|
|
||||||
// 当前是本月16号,结算本月1号
|
|
||||||
settlementDate = LocalDate.of(year, month, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Integer.parseInt(settlementDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -8,13 +8,9 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BD 工资结算定时任务.
|
* BD 工资结算定时任务.
|
||||||
* 每月1号和16号 0点执行半月结算
|
* 每月1号0点执行整月结算.
|
||||||
*
|
*
|
||||||
* @author AI Assistant
|
* @author AI Assistant
|
||||||
* @since 2025-10-29
|
* @since 2025-10-29
|
||||||
@ -27,9 +23,9 @@ public class BdSalarySettlementTask {
|
|||||||
private final BdSalarySettlementService bdSalarySettlementService;
|
private final BdSalarySettlementService bdSalarySettlementService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每月1号和16号 1点执行半月结算.
|
* 每月1号0点30分执行上月整月结算.
|
||||||
*/
|
*/
|
||||||
@Scheduled(cron = "0 30 0 1,16 * ?", zone = "Asia/Riyadh")
|
@Scheduled(cron = "0 30 0 1 * ?", zone = "Asia/Riyadh")
|
||||||
@TaskCacheLock(key = "BD_SALARY_SETTLEMENT", expireSecond = 86400)
|
@TaskCacheLock(key = "BD_SALARY_SETTLEMENT", expireSecond = 86400)
|
||||||
public void processBdSalarySettlement() {
|
public void processBdSalarySettlement() {
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
@ -56,28 +52,10 @@ public class BdSalarySettlementTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算需要结算的账期
|
* 计算需要结算的上月整月账期.
|
||||||
* 规则:
|
|
||||||
* - 如果当前账期是本月1号(如20250101),则结算上个月16号的账期(如20241216)
|
|
||||||
* - 如果当前账期是本月16号(如20250116),则结算本月1号的账期(如20250101)
|
|
||||||
*/
|
*/
|
||||||
private Integer calculateSettlementBillBelong(Integer currentBillBelong) {
|
private Integer calculateSettlementBillBelong(Integer currentBillBelong) {
|
||||||
String billStr = String.valueOf(currentBillBelong);
|
return TeamBillCycleUtils.getPreviousMonthlyBillBelong(currentBillBelong);
|
||||||
int year = Integer.parseInt(billStr.substring(0, 4));
|
|
||||||
int month = Integer.parseInt(billStr.substring(4, 6));
|
|
||||||
int day = Integer.parseInt(billStr.substring(6, 8));
|
|
||||||
|
|
||||||
LocalDate settlementDate;
|
|
||||||
|
|
||||||
if (day == 1) {
|
|
||||||
// 当前是本月1号,结算上个月16号
|
|
||||||
settlementDate = LocalDate.of(year, month, 1).minusMonths(1).withDayOfMonth(16);
|
|
||||||
} else {
|
|
||||||
// 当前是本月16号,结算本月1号
|
|
||||||
settlementDate = LocalDate.of(year, month, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Integer.parseInt(settlementDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -5,18 +5,13 @@ import com.red.circle.mq.business.model.event.team.TeamBillSettleEvent;
|
|||||||
import com.red.circle.mq.rocket.business.producer.TeamSalaryMqMessage;
|
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.entity.team.team.TeamProfile;
|
||||||
import com.red.circle.other.infra.database.mongo.service.gift.GiftGiveRunningWaterService;
|
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.TeamBillCycleService;
|
||||||
import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService;
|
import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService;
|
||||||
import com.red.circle.other.infra.utils.ZonedDateTimeUtils;
|
import com.red.circle.other.infra.utils.ZonedDateTimeUtils;
|
||||||
import com.red.circle.other.inner.model.cmd.team.TeamBillCmd;
|
import com.red.circle.other.inner.model.cmd.team.TeamBillCmd;
|
||||||
import com.red.circle.tool.core.date.TimestampUtils;
|
import com.red.circle.tool.core.date.TimestampUtils;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.YearMonth;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@ -42,20 +37,16 @@ public class TeamBillTask {
|
|||||||
private final GiftGiveRunningWaterService giftGiveRunningWaterService;
|
private final GiftGiveRunningWaterService giftGiveRunningWaterService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每月1号和16号 0点执行半月结算
|
* 每月1号0点执行整月结算,并创建新月账期.
|
||||||
*/
|
*/
|
||||||
@Scheduled(cron = "0 0 0 1,16 * ?", zone = "Asia/Riyadh")
|
@Scheduled(cron = "0 0 0 1 * ?", zone = "Asia/Riyadh")
|
||||||
@TaskCacheLock(key = "PROCESS_TEAM_BILL_HALF_MONTH", expireSecond = 86400)
|
@TaskCacheLock(key = "PROCESS_TEAM_BILL_MONTH", expireSecond = 86400)
|
||||||
public void processTeamHalfMonthBill() {
|
public void processTeamMonthBill() {
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
log.info("exec process_team_half_month_bill start");
|
log.info("exec process_team_month_bill start");
|
||||||
|
|
||||||
// 判断是上半月还是下半月
|
|
||||||
LocalDate now = LocalDate.now(ZoneId.of("Asia/Riyadh"));
|
|
||||||
String billPeriod = now.getDayOfMonth() == 1 ? "first_half" : "second_half";
|
|
||||||
|
|
||||||
processTeamBill();
|
processTeamBill();
|
||||||
log.info("exec process_team_half_month_bill end with {}",
|
log.info("exec process_team_month_bill end with {}",
|
||||||
System.currentTimeMillis() - startTime);
|
System.currentTimeMillis() - startTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -58,7 +57,7 @@ public class AdminTaskServiceImpl implements AdminTaskService {
|
|||||||
@Override
|
@Override
|
||||||
public AdminTaskSummaryCO queryTaskSummary(AdminTaskQueryCmd cmd) {
|
public AdminTaskSummaryCO queryTaskSummary(AdminTaskQueryCmd cmd) {
|
||||||
LocalDate cycleStartDate = cmd.getCycleStartDate() != null ? cmd.getCycleStartDate() :
|
LocalDate cycleStartDate = cmd.getCycleStartDate() != null ? cmd.getCycleStartDate() :
|
||||||
LocalDate.parse(TeamBillCycleUtils.getCalcBillBelong().toString(), DateTimeFormatter.ofPattern("yyyyMMdd"));
|
TeamBillCycleUtils.getBillBelongStartDate(TeamBillCycleUtils.getCalcBillBelong());
|
||||||
|
|
||||||
List<AdminTaskRecord> records = adminTaskQueryCmdExe.execute(cmd);
|
List<AdminTaskRecord> records = adminTaskQueryCmdExe.execute(cmd);
|
||||||
|
|
||||||
@ -110,7 +109,7 @@ public class AdminTaskServiceImpl implements AdminTaskService {
|
|||||||
@Override
|
@Override
|
||||||
public boolean isTaskCompleted(Long userId, String taskType, LocalDate cycleStartDate) {
|
public boolean isTaskCompleted(Long userId, String taskType, LocalDate cycleStartDate) {
|
||||||
LocalDate queryDate = cycleStartDate != null ? cycleStartDate :
|
LocalDate queryDate = cycleStartDate != null ? cycleStartDate :
|
||||||
LocalDate.parse(TeamBillCycleUtils.getCalcBillBelong().toString(), DateTimeFormatter.ofPattern("yyyyMMdd"));
|
TeamBillCycleUtils.getBillBelongStartDate(TeamBillCycleUtils.getCalcBillBelong());
|
||||||
AdminTaskRecord record = adminTaskQueryCmdExe.findByUserIdAndTaskTypeAndCycle(userId, taskType, queryDate);
|
AdminTaskRecord record = adminTaskQueryCmdExe.findByUserIdAndTaskTypeAndCycle(userId, taskType, queryDate);
|
||||||
return record != null && record.isCompleted();
|
return record != null && record.isCompleted();
|
||||||
}
|
}
|
||||||
@ -120,7 +119,7 @@ public class AdminTaskServiceImpl implements AdminTaskService {
|
|||||||
*/
|
*/
|
||||||
private void ensureTasksInitialized(Long userId, LocalDate cycleStartDate) {
|
private void ensureTasksInitialized(Long userId, LocalDate cycleStartDate) {
|
||||||
LocalDate queryDate = cycleStartDate != null ? cycleStartDate :
|
LocalDate queryDate = cycleStartDate != null ? cycleStartDate :
|
||||||
LocalDate.parse(TeamBillCycleUtils.getCalcBillBelong().toString(), DateTimeFormatter.ofPattern("yyyyMMdd"));
|
TeamBillCycleUtils.getBillBelongStartDate(TeamBillCycleUtils.getCalcBillBelong());
|
||||||
|
|
||||||
// 检查是否存在任务记录
|
// 检查是否存在任务记录
|
||||||
boolean exists = adminTaskGateway.existsByUserIdAndCycle(userId, queryDate);
|
boolean exists = adminTaskGateway.existsByUserIdAndCycle(userId, queryDate);
|
||||||
@ -146,14 +145,6 @@ public class AdminTaskServiceImpl implements AdminTaskService {
|
|||||||
* 计算周期结束日期
|
* 计算周期结束日期
|
||||||
*/
|
*/
|
||||||
private LocalDate calculateCycleEndDate(LocalDate cycleStartDate) {
|
private LocalDate calculateCycleEndDate(LocalDate cycleStartDate) {
|
||||||
int dayOfMonth = cycleStartDate.getDayOfMonth();
|
return cycleStartDate.withDayOfMonth(cycleStartDate.lengthOfMonth());
|
||||||
|
|
||||||
if (dayOfMonth == 1) {
|
|
||||||
// 1号开始,结束于15号
|
|
||||||
return LocalDate.of(cycleStartDate.getYear(), cycleStartDate.getMonth(), 15);
|
|
||||||
} else {
|
|
||||||
// 16号开始,结束于月末
|
|
||||||
return cycleStartDate.withDayOfMonth(cycleStartDate.lengthOfMonth());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -666,29 +666,14 @@ public final class TeamBillCycleUtils {
|
|||||||
|
|
||||||
|
|
||||||
public static Integer getCalcBillBelong() {
|
public static Integer getCalcBillBelong() {
|
||||||
LocalDate now = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
|
return getMonthlyBillBelong();
|
||||||
return calculateCurrentBillBelong(now);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算当前账期的 billBelong
|
* 计算当前整月账期的 billBelong,返回格式:202510.
|
||||||
* 规则:
|
|
||||||
* - 1-15号:当前账期是本月1号(20251001)
|
|
||||||
* - 16-月末:当前账期是本月16号(20251016)
|
|
||||||
*/
|
*/
|
||||||
private static Integer calculateCurrentBillBelong(LocalDate now) {
|
private static Integer calculateCurrentBillBelong(LocalDate now) {
|
||||||
int day = now.getDayOfMonth();
|
return Integer.parseInt(now.format(DateTimeFormatter.ofPattern("yyyyMM")));
|
||||||
LocalDate billDate;
|
|
||||||
|
|
||||||
if (day < 16) {
|
|
||||||
// 当前是上半月,账期开始日期是本月1号
|
|
||||||
billDate = now.withDayOfMonth(1);
|
|
||||||
} else {
|
|
||||||
// 当前是下半月,账期开始日期是本月16号
|
|
||||||
billDate = now.withDayOfMonth(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Integer.parseInt(billDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -758,9 +743,17 @@ public final class TeamBillCycleUtils {
|
|||||||
* 获取月度账期(用于批次模式)
|
* 获取月度账期(用于批次模式)
|
||||||
* 返回格式:202511
|
* 返回格式:202511
|
||||||
*/
|
*/
|
||||||
public static Integer getMonthlyBillBelong() {
|
public static Integer getMonthlyBillBelong() {
|
||||||
LocalDate now = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
|
LocalDate now = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
|
||||||
return Integer.parseInt(now.format(DateTimeFormatter.ofPattern("yyyyMM")));
|
return calculateCurrentBillBelong(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取上一个整月账期,返回格式:202510.
|
||||||
|
*/
|
||||||
|
public static Integer getPreviousMonthlyBillBelong(Integer billBelong) {
|
||||||
|
YearMonth yearMonth = parseYearMonth(billBelong);
|
||||||
|
return Integer.parseInt(yearMonth.minusMonths(1).format(DateTimeFormatter.ofPattern("yyyyMM")));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -776,9 +769,7 @@ public final class TeamBillCycleUtils {
|
|||||||
* 判断是否启用批次模式(通过区域配置)
|
* 判断是否启用批次模式(通过区域配置)
|
||||||
*/
|
*/
|
||||||
public static Boolean isBatchModeEnabled(String region) {
|
public static Boolean isBatchModeEnabled(String region) {
|
||||||
// 从配置中读取,或者硬编码
|
return false;
|
||||||
// 例如:return "SA".equals(region); // 沙特地区启用批次模式
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -833,30 +824,56 @@ public final class TeamBillCycleUtils {
|
|||||||
return String.format("%d年%d月(%s)", year, month, batchDesc);
|
return String.format("%d年%d月(%s)", year, month, batchDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LocalDateTime getBillBelongStartTime(Integer billBelong) {
|
public static LocalDate getBillBelongStartDate(Integer billBelong) {
|
||||||
// billBelong 格式:20251101 或 20251116
|
String billStr = String.valueOf(billBelong);
|
||||||
int year = billBelong / 10000; // 2025
|
if (billStr.length() == 6) {
|
||||||
int month = (billBelong / 100) % 100; // 11
|
return parseYearMonth(billBelong).atDay(1);
|
||||||
int day = billBelong % 100; // 01 或 16
|
}
|
||||||
|
if (billStr.length() == 8) {
|
||||||
|
return LocalDate.of(
|
||||||
|
Integer.parseInt(billStr.substring(0, 4)),
|
||||||
|
Integer.parseInt(billStr.substring(4, 6)),
|
||||||
|
Integer.parseInt(billStr.substring(6, 8)));
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unsupported billBelong: " + billBelong);
|
||||||
|
}
|
||||||
|
|
||||||
return LocalDateTime.of(year, month, day, 0, 0, 0);
|
public static LocalDate getBillBelongEndDate(Integer billBelong) {
|
||||||
|
String billStr = String.valueOf(billBelong);
|
||||||
|
if (billStr.length() == 6) {
|
||||||
|
return parseYearMonth(billBelong).atEndOfMonth();
|
||||||
|
}
|
||||||
|
if (billStr.length() == 8) {
|
||||||
|
LocalDate startDate = getBillBelongStartDate(billBelong);
|
||||||
|
return startDate.getDayOfMonth() == 1
|
||||||
|
? startDate.withDayOfMonth(15)
|
||||||
|
: startDate.withDayOfMonth(startDate.lengthOfMonth());
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unsupported billBelong: " + billBelong);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalDateTime getBillBelongStartTime(Integer billBelong) {
|
||||||
|
return getBillBelongStartDate(billBelong).atStartOfDay();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LocalDateTime getBillBelongEndTime(Integer billBelong) {
|
public static LocalDateTime getBillBelongEndTime(Integer billBelong) {
|
||||||
// billBelong 格式:20251101 或 20251116
|
return getBillBelongEndDate(billBelong).plusDays(1).atStartOfDay();
|
||||||
int year = billBelong / 10000; // 2025
|
|
||||||
int month = (billBelong / 100) % 100; // 11
|
|
||||||
int day = billBelong % 100; // 01 或 16
|
|
||||||
|
|
||||||
LocalDateTime startTime = LocalDateTime.of(year, month, day, 0, 0, 0);
|
|
||||||
|
|
||||||
if (day == 1) {
|
|
||||||
// 如果是1号,结束时间是当月16号0点
|
|
||||||
return LocalDateTime.of(year, month, 16, 0, 0, 0);
|
|
||||||
} else {
|
|
||||||
// 如果是16号,结束时间是下个月1号0点
|
|
||||||
return startTime.plusMonths(1).withDayOfMonth(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static YearMonth parseYearMonth(Integer billBelong) {
|
||||||
|
if (billBelong == null) {
|
||||||
|
throw new IllegalArgumentException("billBelong is null");
|
||||||
|
}
|
||||||
|
String billStr = String.valueOf(billBelong);
|
||||||
|
if (billStr.length() == 8) {
|
||||||
|
billStr = billStr.substring(0, 6);
|
||||||
|
}
|
||||||
|
if (billStr.length() != 6) {
|
||||||
|
throw new IllegalArgumentException("Unsupported billBelong: " + billBelong);
|
||||||
|
}
|
||||||
|
return YearMonth.of(
|
||||||
|
Integer.parseInt(billStr.substring(0, 4)),
|
||||||
|
Integer.parseInt(billStr.substring(4, 6)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,8 +22,10 @@ import com.red.circle.tool.core.sequence.IdWorkerUtils;
|
|||||||
import com.red.circle.tool.core.text.StringUtils;
|
import com.red.circle.tool.core.text.StringUtils;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -246,7 +248,6 @@ public class TeamBillCycleServiceImpl implements TeamBillCycleService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void createIfAbsent(TeamBillCmd cmd) {
|
public void createIfAbsent(TeamBillCmd cmd) {
|
||||||
// 计算 billBelong(保持原有逻辑)
|
|
||||||
Integer billBelong = TeamBillCycleUtils.getCalcBillBelong();
|
Integer billBelong = TeamBillCycleUtils.getCalcBillBelong();
|
||||||
|
|
||||||
// 检查是否已存在
|
// 检查是否已存在
|
||||||
@ -263,27 +264,12 @@ public class TeamBillCycleServiceImpl implements TeamBillCycleService {
|
|||||||
.setRegion(cmd.getRegion())
|
.setRegion(cmd.getRegion())
|
||||||
.setBillBelong(billBelong)
|
.setBillBelong(billBelong)
|
||||||
.setBillTitle(generateBillTitle(billBelong))
|
.setBillTitle(generateBillTitle(billBelong))
|
||||||
|
.setBatchMode(false)
|
||||||
|
.setMonthlyBillBelong(billBelong)
|
||||||
.setStatus(TeamBillCycleStatus.UNPAID)
|
.setStatus(TeamBillCycleStatus.UNPAID)
|
||||||
.setCreateTime(cmd.getOperationTime())
|
.setCreateTime(cmd.getOperationTime())
|
||||||
.setCreateUser(cmd.getOperationBackUser());
|
.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);
|
mongoTemplate.save(bill);
|
||||||
log.info("创建账单成功:{}", bill);
|
log.info("创建账单成功:{}", bill);
|
||||||
}
|
}
|
||||||
@ -377,8 +363,18 @@ public class TeamBillCycleServiceImpl implements TeamBillCycleService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TeamBillCycle getTeamUnpaidBill(Long teamId) {
|
public TeamBillCycle getTeamUnpaidBill(Long teamId) {
|
||||||
|
TeamBillCycle currentMonthBill = mongoTemplate.findOne(
|
||||||
|
Query.query(Criteria.where("teamId").is(teamId)
|
||||||
|
.and("status").is(TeamBillCycleStatus.UNPAID)
|
||||||
|
.and("billBelong").is(getCalcBillBelong()))
|
||||||
|
.with(Sort.by(Sort.Order.desc("id"))),
|
||||||
|
TeamBillCycle.class);
|
||||||
|
if (Objects.nonNull(currentMonthBill)) {
|
||||||
|
return currentMonthBill;
|
||||||
|
}
|
||||||
return mongoTemplate.findOne(Query.query(Criteria.where("teamId").is(teamId)
|
return mongoTemplate.findOne(Query.query(Criteria.where("teamId").is(teamId)
|
||||||
.and("status").is(TeamBillCycleStatus.UNPAID)),
|
.and("status").is(TeamBillCycleStatus.UNPAID))
|
||||||
|
.with(Sort.by(Sort.Order.desc("id"))),
|
||||||
TeamBillCycle.class);
|
TeamBillCycle.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,6 +392,14 @@ public class TeamBillCycleServiceImpl implements TeamBillCycleService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TeamBillCycle getLatestByTeamId(Long teamId) {
|
public TeamBillCycle getLatestByTeamId(Long teamId) {
|
||||||
|
TeamBillCycle currentMonthBill = mongoTemplate.findOne(
|
||||||
|
Query.query(Criteria.where("teamId").is(teamId)
|
||||||
|
.and("billBelong").is(getCalcBillBelong()))
|
||||||
|
.with(Sort.by(Sort.Order.desc("id"))),
|
||||||
|
TeamBillCycle.class);
|
||||||
|
if (Objects.nonNull(currentMonthBill)) {
|
||||||
|
return currentMonthBill;
|
||||||
|
}
|
||||||
|
|
||||||
List<TeamBillCycle> cycleList = listLatestBill(teamId, 1);
|
List<TeamBillCycle> cycleList = listLatestBill(teamId, 1);
|
||||||
if (CollectionUtils.isEmpty(cycleList)) {
|
if (CollectionUtils.isEmpty(cycleList)) {
|
||||||
@ -435,6 +439,13 @@ public class TeamBillCycleServiceImpl implements TeamBillCycleService {
|
|||||||
Integer normalizedYearMonth = yearMonth > 999999
|
Integer normalizedYearMonth = yearMonth > 999999
|
||||||
? Integer.valueOf(yearMonth.toString().substring(0, 6))
|
? Integer.valueOf(yearMonth.toString().substring(0, 6))
|
||||||
: yearMonth;
|
: yearMonth;
|
||||||
|
TeamBillCycle monthlyBill = mongoTemplate.findOne(
|
||||||
|
Query.query(Criteria.where("teamId").is(teamId).and("billBelong").is(normalizedYearMonth))
|
||||||
|
.with(Sort.by(Sort.Order.desc("id"))),
|
||||||
|
TeamBillCycle.class);
|
||||||
|
if (Objects.nonNull(monthlyBill)) {
|
||||||
|
return monthlyBill;
|
||||||
|
}
|
||||||
return mongoTemplate.findOne(
|
return mongoTemplate.findOne(
|
||||||
Query.query(getBillBelongCriteria(normalizedYearMonth).and("teamId").is(teamId))
|
Query.query(getBillBelongCriteria(normalizedYearMonth).and("teamId").is(teamId))
|
||||||
.with(Sort.by(Sort.Order.desc("billBelong"), Sort.Order.desc("id"))),
|
.with(Sort.by(Sort.Order.desc("billBelong"), Sort.Order.desc("id"))),
|
||||||
@ -492,11 +503,35 @@ public class TeamBillCycleServiceImpl implements TeamBillCycleService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<TeamBillCycle> listByGeBillBelong(Long teamId, Integer billBelong, Integer size) {
|
public List<TeamBillCycle> listByGeBillBelong(Long teamId, Integer billBelong, Integer size) {
|
||||||
|
int limit = Objects.nonNull(size) ? size : PageConstant.DEFAULT_LIMIT_SIZE;
|
||||||
Criteria criteria = Criteria.where("teamId").is(teamId).and("billBelong").gte(billBelong);
|
Criteria criteria = Criteria.where("teamId").is(teamId).and("billBelong").gte(billBelong);
|
||||||
return mongoTemplate.find(Query.query(criteria)
|
List<TeamBillCycle> billCycles = mongoTemplate.find(Query.query(criteria)
|
||||||
.with(Sort.by(Sort.Order.desc("id")))
|
.with(Sort.by(Sort.Order.desc("id")))
|
||||||
.limit(size),
|
.limit(limit * 3),
|
||||||
TeamBillCycle.class);
|
TeamBillCycle.class);
|
||||||
|
if (CollectionUtils.isEmpty(billCycles)) {
|
||||||
|
return billCycles;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Integer, TeamBillCycle> monthBillMap = new LinkedHashMap<>();
|
||||||
|
billCycles.stream()
|
||||||
|
.sorted(Comparator
|
||||||
|
.comparing((TeamBillCycle bill) -> TeamBillCycleUtils.extractMonth(bill.getBillBelong()))
|
||||||
|
.reversed()
|
||||||
|
.thenComparing(this::monthlyBillPriority)
|
||||||
|
.thenComparing(TeamBillCycle::getId, Comparator.reverseOrder()))
|
||||||
|
.forEach(bill -> monthBillMap.putIfAbsent(
|
||||||
|
TeamBillCycleUtils.extractMonth(bill.getBillBelong()), bill));
|
||||||
|
|
||||||
|
return new ArrayList<>(monthBillMap.values()).stream()
|
||||||
|
.limit(limit)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int monthlyBillPriority(TeamBillCycle bill) {
|
||||||
|
return Objects.equals(bill.getBillBelong(), TeamBillCycleUtils.extractMonth(bill.getBillBelong()))
|
||||||
|
? 0
|
||||||
|
: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -582,12 +617,12 @@ public class TeamBillCycleServiceImpl implements TeamBillCycleService {
|
|||||||
public void billStatusPayOut() {
|
public void billStatusPayOut() {
|
||||||
// 计算当前账期的 billBelong
|
// 计算当前账期的 billBelong
|
||||||
Integer currentBillBelong = getCalcBillBelong();
|
Integer currentBillBelong = getCalcBillBelong();
|
||||||
Integer billBelong = calculateSettlementBillBelong(currentBillBelong);
|
Integer billBelong = TeamBillCycleUtils.getPreviousMonthlyBillBelong(currentBillBelong);
|
||||||
|
|
||||||
// 将小于当前账期的 UNPAID 状态改为 PAY_OUT
|
// 只出整月账单,历史半月账单不再自动进入结算,避免重复结算。
|
||||||
mongoTemplate.updateMulti(
|
mongoTemplate.updateMulti(
|
||||||
Query.query(
|
Query.query(
|
||||||
Criteria.where("billBelong").lte(billBelong)
|
payableMonthlyBillBelongCriteria(billBelong)
|
||||||
.and("status").is(TeamBillCycleStatus.UNPAID)),
|
.and("status").is(TeamBillCycleStatus.UNPAID)),
|
||||||
new Update()
|
new Update()
|
||||||
.set("updateTime", TimestampUtils.now())
|
.set("updateTime", TimestampUtils.now())
|
||||||
@ -597,43 +632,20 @@ public class TeamBillCycleServiceImpl implements TeamBillCycleService {
|
|||||||
|
|
||||||
public void billStatusPayOutTest() {
|
public void billStatusPayOutTest() {
|
||||||
// 计算当前账期的 billBelong
|
// 计算当前账期的 billBelong
|
||||||
Integer currentBillBelong = 20251201;
|
Integer currentBillBelong = 202512;
|
||||||
Integer billBelong = calculateSettlementBillBelong(currentBillBelong);
|
Integer billBelong = TeamBillCycleUtils.getPreviousMonthlyBillBelong(currentBillBelong);
|
||||||
|
|
||||||
// 将小于当前账期的 UNPAID 状态改为 PAY_OUT
|
// 将小于当前账期的 UNPAID 状态改为 PAY_OUT
|
||||||
List<TeamBillCycle> teamBillCycles = mongoTemplate.find(
|
List<TeamBillCycle> teamBillCycles = mongoTemplate.find(
|
||||||
Query.query(
|
Query.query(
|
||||||
Criteria.where("billBelong").lte(billBelong)
|
payableMonthlyBillBelongCriteria(billBelong)
|
||||||
.and("status").is(TeamBillCycleStatus.UNPAID)),
|
.and("status").is(TeamBillCycleStatus.UNPAID)),
|
||||||
TeamBillCycle.class);
|
TeamBillCycle.class);
|
||||||
System.out.println();
|
System.out.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Criteria payableMonthlyBillBelongCriteria(Integer billBelong) {
|
||||||
|
return Criteria.where("billBelong").gte(100000).lte(billBelong);
|
||||||
/**
|
|
||||||
* 计算需要结算的账期
|
|
||||||
* 规则:
|
|
||||||
* - 如果当前账期是本月1号(如20250101),则结算上个月16号的账期(如20241216)
|
|
||||||
* - 如果当前账期是本月16号(如20250116),则结算本月1号的账期(如20250101)
|
|
||||||
*/
|
|
||||||
private Integer calculateSettlementBillBelong(Integer currentBillBelong) {
|
|
||||||
String billStr = String.valueOf(currentBillBelong);
|
|
||||||
int year = Integer.parseInt(billStr.substring(0, 4));
|
|
||||||
int month = Integer.parseInt(billStr.substring(4, 6));
|
|
||||||
int day = Integer.parseInt(billStr.substring(6, 8));
|
|
||||||
|
|
||||||
LocalDate settlementDate;
|
|
||||||
|
|
||||||
if (day == 1) {
|
|
||||||
// 当前是本月1号,结算上个月16号
|
|
||||||
settlementDate = LocalDate.of(year, month, 1).minusMonths(1).withDayOfMonth(16);
|
|
||||||
} else {
|
|
||||||
// 当前是本月16号,结算本月1号
|
|
||||||
settlementDate = LocalDate.of(year, month, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Integer.parseInt(settlementDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -18,12 +18,14 @@ import com.red.circle.tool.core.date.ZonedId;
|
|||||||
import com.red.circle.tool.core.sequence.IdWorkerUtils;
|
import com.red.circle.tool.core.sequence.IdWorkerUtils;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.Timestamp;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.temporal.TemporalAdjusters;
|
import java.time.temporal.TemporalAdjusters;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
@ -662,9 +664,14 @@ public class TeamMemberTargetServiceImpl implements TeamMemberTargetService {
|
|||||||
long min = prefix * 100L + 1; // 202511 → 20251101
|
long min = prefix * 100L + 1; // 202511 → 20251101
|
||||||
long max = (prefix + 1) * 100L - 1; // 202511 → 20251199
|
long max = (prefix + 1) * 100L - 1; // 202511 → 20251199
|
||||||
|
|
||||||
Criteria criteria = Criteria.where("teamId").is(teamId)
|
Criteria criteria = new Criteria().andOperator(
|
||||||
// .and("history").is(Boolean.FALSE)
|
Criteria.where("teamId").is(teamId),
|
||||||
.and("billBelong").gte(min).lte(max);
|
Criteria.where("history").is(Boolean.FALSE),
|
||||||
|
new Criteria().orOperator(
|
||||||
|
Criteria.where("billBelong").is(monthlyBillBelong),
|
||||||
|
Criteria.where("billBelong").gte(min).lte(max)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return mongoTemplate.find(Query.query(criteria)
|
return mongoTemplate.find(Query.query(criteria)
|
||||||
.with(Sort.by(Order.asc("billBelong"))),
|
.with(Sort.by(Order.asc("billBelong"))),
|
||||||
@ -678,10 +685,15 @@ public class TeamMemberTargetServiceImpl implements TeamMemberTargetService {
|
|||||||
long max = (prefix + 1) * 100L - 1; // 202511 → 20251199
|
long max = (prefix + 1) * 100L - 1; // 202511 → 20251199
|
||||||
|
|
||||||
// 查询指定用户在指定月份的所有目标记录
|
// 查询指定用户在指定月份的所有目标记录
|
||||||
Criteria criteria = Criteria.where("teamId").is(teamId)
|
Criteria criteria = new Criteria().andOperator(
|
||||||
.and("userId").is(userId)
|
Criteria.where("teamId").is(teamId),
|
||||||
// .and("history").is(Boolean.FALSE)
|
Criteria.where("userId").is(userId),
|
||||||
.and("billBelong").gte(min).lte(max);
|
Criteria.where("history").is(Boolean.FALSE),
|
||||||
|
new Criteria().orOperator(
|
||||||
|
Criteria.where("billBelong").is(monthlyBillBelong),
|
||||||
|
Criteria.where("billBelong").gte(min).lte(max)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return mongoTemplate.find(Query.query(criteria)
|
return mongoTemplate.find(Query.query(criteria)
|
||||||
.with(Sort.by(Order.asc("billBelong"))),
|
.with(Sort.by(Order.asc("billBelong"))),
|
||||||
@ -697,6 +709,13 @@ public class TeamMemberTargetServiceImpl implements TeamMemberTargetService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<TeamMemberTarget> exactMonthlyTargets = monthlyTargets.stream()
|
||||||
|
.filter(target -> Objects.equals(target.getBillBelong(), monthlyBillBelong))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (exactMonthlyTargets.size() == monthlyTargets.size()) {
|
||||||
|
return exactMonthlyTargets.get(exactMonthlyTargets.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
// 2. 如果只有一条记录,直接返回
|
// 2. 如果只有一条记录,直接返回
|
||||||
if (monthlyTargets.size() == 1) {
|
if (monthlyTargets.size() == 1) {
|
||||||
return monthlyTargets.get(0);
|
return monthlyTargets.get(0);
|
||||||
@ -720,11 +739,7 @@ public class TeamMemberTargetServiceImpl implements TeamMemberTargetService {
|
|||||||
aggregated.setUpdateTime(monthlyTargets.get(monthlyTargets.size() - 1).getUpdateTime());
|
aggregated.setUpdateTime(monthlyTargets.get(monthlyTargets.size() - 1).getUpdateTime());
|
||||||
|
|
||||||
// 4. 合并每日目标数据(dailyTargets)
|
// 4. 合并每日目标数据(dailyTargets)
|
||||||
List<TeamMemberTargetIndex> allDailyTargets = monthlyTargets.stream()
|
aggregated.setDailyTargets(mergeDailyTargets(monthlyTargets));
|
||||||
.flatMap(target -> target.getDailyTargets().stream())
|
|
||||||
.sorted((d1, d2) -> d1.getDateNumber().compareTo(d2.getDateNumber()))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
aggregated.setDailyTargets(allDailyTargets);
|
|
||||||
|
|
||||||
// 5. 聚合结算结果(如果有多条,取总和)
|
// 5. 聚合结算结果(如果有多条,取总和)
|
||||||
List<TeamMemberTargetSettlementResult> settlementResults = monthlyTargets.stream()
|
List<TeamMemberTargetSettlementResult> settlementResults = monthlyTargets.stream()
|
||||||
@ -836,6 +851,66 @@ public class TeamMemberTargetServiceImpl implements TeamMemberTargetService {
|
|||||||
return String.format("%d年%d月", year, month);
|
return String.format("%d年%d月", year, month);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<TeamMemberTargetIndex> mergeDailyTargets(List<TeamMemberTarget> monthlyTargets) {
|
||||||
|
Map<Integer, TeamMemberTargetIndex> dailyTargetMap = new TreeMap<>();
|
||||||
|
monthlyTargets.stream()
|
||||||
|
.map(TeamMemberTarget::getDailyTargets)
|
||||||
|
.filter(CollectionUtils::isNotEmpty)
|
||||||
|
.flatMap(List::stream)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(dailyTarget -> Objects.nonNull(dailyTarget.getDateNumber()))
|
||||||
|
.forEach(dailyTarget -> {
|
||||||
|
TeamMemberTargetIndex target = dailyTargetMap.computeIfAbsent(
|
||||||
|
dailyTarget.getDateNumber(),
|
||||||
|
dateNumber -> new TeamMemberTargetIndex()
|
||||||
|
.setDateNumber(dateNumber)
|
||||||
|
.setCreateTime(dailyTarget.getCreateTime())
|
||||||
|
.setUpdateTime(dailyTarget.getUpdateTime()));
|
||||||
|
|
||||||
|
target.setOwnOnlineTime(sumLong(target.getOwnOnlineTime(), dailyTarget.getOwnOnlineTime()));
|
||||||
|
target.setOtherOnlineTime(sumLong(target.getOtherOnlineTime(), dailyTarget.getOtherOnlineTime()));
|
||||||
|
target.setAcceptGiftValue(sumLong(target.getAcceptGiftValue(), dailyTarget.getAcceptGiftValue()));
|
||||||
|
target.setGiveGiftValue(sumLong(target.getGiveGiftValue(), dailyTarget.getGiveGiftValue()));
|
||||||
|
target.setRoomValue(sumLong(target.getRoomValue(), dailyTarget.getRoomValue()));
|
||||||
|
target.setOverFlowGiftValue(sumLong(target.getOverFlowGiftValue(), dailyTarget.getOverFlowGiftValue()));
|
||||||
|
target.setEffectiveDay(maxInteger(target.getEffectiveDay(), dailyTarget.getEffectiveDay()));
|
||||||
|
target.setCreateTime(minTimestamp(target.getCreateTime(), dailyTarget.getCreateTime()));
|
||||||
|
target.setUpdateTime(maxTimestamp(target.getUpdateTime(), dailyTarget.getUpdateTime()));
|
||||||
|
});
|
||||||
|
return List.copyOf(dailyTargetMap.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long sumLong(Long left, Long right) {
|
||||||
|
return (Objects.nonNull(left) ? left : 0L) + (Objects.nonNull(right) ? right : 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer maxInteger(Integer left, Integer right) {
|
||||||
|
if (Objects.isNull(left) && Objects.isNull(right)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Math.max(Objects.nonNull(left) ? left : 0, Objects.nonNull(right) ? right : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Timestamp minTimestamp(Timestamp left, Timestamp right) {
|
||||||
|
if (Objects.isNull(left)) {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
if (Objects.isNull(right)) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
return left.before(right) ? left : right;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Timestamp maxTimestamp(Timestamp left, Timestamp right) {
|
||||||
|
if (Objects.isNull(left)) {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
if (Objects.isNull(right)) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
return left.after(right) ? left : right;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 比较状态优先级
|
* 比较状态优先级
|
||||||
* 返回值: 1 表示 s1 优先级高, -1 表示 s2 优先级高, 0 表示相等
|
* 返回值: 1 表示 s1 优先级高, -1 表示 s2 优先级高, 0 表示相等
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user