diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/cmd/team/TeamManualSalaryPaymentQryCmd.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/cmd/team/TeamManualSalaryPaymentQryCmd.java index 55f0571b..e8cca771 100644 --- a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/cmd/team/TeamManualSalaryPaymentQryCmd.java +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/cmd/team/TeamManualSalaryPaymentQryCmd.java @@ -31,4 +31,14 @@ public class TeamManualSalaryPaymentQryCmd extends PageCommand { * 用户 id. */ private Long userId; + + /** + * 排序字段. + */ + private String sortField; + + /** + * 排序方向. + */ + private String sortOrder; } diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/agency/agency/TeamManualSalaryPaymentDTO.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/agency/agency/TeamManualSalaryPaymentDTO.java index 02a9954a..30e15bea 100644 --- a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/agency/agency/TeamManualSalaryPaymentDTO.java +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/agency/agency/TeamManualSalaryPaymentDTO.java @@ -82,7 +82,12 @@ public class TeamManualSalaryPaymentDTO implements Serializable { private Boolean satisfyLevel; /** - * 应发工资. + * 当前账期按积分档位计算出的工资. + */ + private BigDecimal expectedSalary; + + /** + * 待结算工资 = 当前账期应发工资 - 当前账期实收工资. */ private BigDecimal payableSalary; @@ -91,6 +96,11 @@ public class TeamManualSalaryPaymentDTO implements Serializable { */ private BigDecimal totalIssuedSalary; + /** + * 当前账期多发工资. + */ + private BigDecimal overIssuedSalary; + /** * 当前工资余额. */ diff --git a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/team/TeamSalaryBackServiceImpl.java b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/team/TeamSalaryBackServiceImpl.java index 3ae377bc..821f7c95 100644 --- a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/team/TeamSalaryBackServiceImpl.java +++ b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/team/TeamSalaryBackServiceImpl.java @@ -215,8 +215,10 @@ public class TeamSalaryBackServiceImpl implements TeamSalaryBackService { .setCurrentTarget(dto.getCurrentTarget()) .setPolicyLevel(dto.getPolicyLevel()) .setSatisfyLevel(dto.getSatisfyLevel()) + .setExpectedSalary(dto.getExpectedSalary()) .setPayableSalary(dto.getPayableSalary()) .setTotalIssuedSalary(dto.getTotalIssuedSalary()) + .setOverIssuedSalary(dto.getOverIssuedSalary()) .setCurrentSalaryBalance(dto.getCurrentSalaryBalance()) .setPaid(dto.getPaid()); } diff --git a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/clienobject/team/TeamManualSalaryCO.java b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/clienobject/team/TeamManualSalaryCO.java index 3856abd2..8cdfb263 100644 --- a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/clienobject/team/TeamManualSalaryCO.java +++ b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/clienobject/team/TeamManualSalaryCO.java @@ -39,8 +39,10 @@ public class TeamManualSalaryCO implements Serializable { private Long currentTarget; private Integer policyLevel; private Boolean satisfyLevel; + private BigDecimal expectedSalary; private BigDecimal payableSalary; private BigDecimal totalIssuedSalary; + private BigDecimal overIssuedSalary; private BigDecimal currentSalaryBalance; private Boolean paid; } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/TeamSalaryPaymentService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/TeamSalaryPaymentService.java index 51fc4b82..71b454be 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/TeamSalaryPaymentService.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/TeamSalaryPaymentService.java @@ -3,9 +3,10 @@ package com.red.circle.other.infra.database.mongo.service.team.team; import com.red.circle.framework.dto.PageResult; import com.red.circle.other.infra.database.mongo.dto.team.TeamSalaryPaymentDetails; import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager; -import com.red.circle.other.infra.database.mongo.entity.team.team.TeamSalaryPayment; -import java.util.List; -import java.util.Map; +import com.red.circle.other.infra.database.mongo.entity.team.team.TeamSalaryPayment; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * 团队工资实时支付记录. @@ -80,10 +81,16 @@ public interface TeamSalaryPaymentService { /** * 获得数据. */ - TeamSalaryPayment getOneById(String id); - - /** - * 基于父ID修改子记录状态为已退款. + TeamSalaryPayment getOneById(String id); + + /** + * 按用户和账期批量获得数据. + */ + Map mapByUserIdsAndDateNumbers(String sysOrigin, Set userIds, + Set dateNumbers); + + /** + * 基于父ID修改子记录状态为已退款. * * @param id ID * @param salarySendDetails 发送工资记录集合. diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/impl/TeamSalaryPaymentServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/impl/TeamSalaryPaymentServiceImpl.java index 191c19b9..aae98d55 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/impl/TeamSalaryPaymentServiceImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/impl/TeamSalaryPaymentServiceImpl.java @@ -13,10 +13,12 @@ import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentRemar import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.text.StringUtils; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoTemplate; @@ -178,13 +180,33 @@ public class TeamSalaryPaymentServiceImpl implements TeamSalaryPaymentService { } @Override - public TeamSalaryPayment getOneById(String id) { - - return mongoTemplate.findOne(Query.query(Criteria.where("id").is(id)), TeamSalaryPayment.class); - } - - @Override - public void updateDetails(String id, List salarySendDetails) { + public TeamSalaryPayment getOneById(String id) { + + return mongoTemplate.findOne(Query.query(Criteria.where("id").is(id)), TeamSalaryPayment.class); + } + + @Override + public Map mapByUserIdsAndDateNumbers(String sysOrigin, + Set userIds, Set dateNumbers) { + + if (StringUtils.isBlank(sysOrigin) || CollectionUtils.isEmpty(userIds) + || CollectionUtils.isEmpty(dateNumbers)) { + return Maps.newHashMap(); + } + + Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin) + .and("userId").in(userIds) + .and("dateNumber").in(dateNumbers); + + return mongoTemplate.find(Query.query(criteria), TeamSalaryPayment.class) + .stream() + .filter(payment -> StringUtils.isNotBlank(payment.getId())) + .collect(Collectors.toMap(TeamSalaryPayment::getId, payment -> payment, + (first, second) -> first)); + } + + @Override + public void updateDetails(String id, List salarySendDetails) { if (Objects.isNull(id) || CollectionUtils.isEmpty(salarySendDetails)) { return; diff --git a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/TeamManualSalaryPaymentClientServiceImpl.java b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/TeamManualSalaryPaymentClientServiceImpl.java index e2e4a4fa..3094ded6 100644 --- a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/TeamManualSalaryPaymentClientServiceImpl.java +++ b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/TeamManualSalaryPaymentClientServiceImpl.java @@ -44,6 +44,7 @@ import java.math.RoundingMode; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -65,6 +66,11 @@ public class TeamManualSalaryPaymentClientServiceImpl implements private static final String MANUAL_SALARY_LOCK_PREFIX = "TeamManualSalaryPayment:"; private static final int LOCK_SECONDS = 300; + private static final String SORT_FIELD_CURRENT_SALARY_BALANCE = "currentSalaryBalance"; + private static final String SORT_FIELD_PAYABLE_SALARY = "payableSalary"; + private static final String SORT_FIELD_TOTAL_ISSUED_SALARY = "totalIssuedSalary"; + private static final String SORT_ORDER_ASCEND = "ascend"; + private static final int LEGACY_HALF_MONTH_COMPAT_BILL_BELONG = 202605; private final RedisService redisService; private final TeamMemberService teamMemberService; @@ -81,6 +87,7 @@ public class TeamManualSalaryPaymentClientServiceImpl implements List rows = buildRows(cmd.getSysOrigin(), cmd.getCountryCode(), singletonUserId(cmd.getUserId()), TeamBillCycleUtils.getCalcBillBelong()); + rows = sortRows(rows, cmd.getSortField(), cmd.getSortOrder()); int cursor = 1; int limit = 20; @@ -132,18 +139,28 @@ public class TeamManualSalaryPaymentClientServiceImpl implements continue; } + BigDecimal issueSalary = policyPayment.getSalary().compareTo(BigDecimal.ZERO) > 0 + ? policyPayment.getSalary() + : BigDecimal.ZERO.setScale(2, RoundingMode.DOWN); BigDecimal beforeBalance = getAccurateBalance(row.getUserId()); String detailsId = IdWorkerUtils.getIdStr(); - Boolean incrSuccess = ResponseAssert.requiredSuccess(userBankBalanceClient.incr(new UserBankBalanceIncrCmd() - .setUserId(row.getUserId()) - .setSysOrigin(row.getSysOrigin()) - .setAmount(PennyAmount.ofDollar(policyPayment.getSalary())))); - ResponseAssert.isTrue(CommonErrorCode.OPERATION_CONFLICT, Boolean.TRUE.equals(incrSuccess)); + if (issueSalary.compareTo(BigDecimal.ZERO) > 0) { + Boolean incrSuccess = ResponseAssert.requiredSuccess(userBankBalanceClient.incr(new UserBankBalanceIncrCmd() + .setUserId(row.getUserId()) + .setSysOrigin(row.getSysOrigin()) + .setAmount(PennyAmount.ofDollar(issueSalary)))); + ResponseAssert.isTrue(CommonErrorCode.OPERATION_CONFLICT, Boolean.TRUE.equals(incrSuccess)); + } BigDecimal afterBalance = getAccurateBalance(row.getUserId()); - addRunningWater(detailsId, row.getUserId(), row.getSysOrigin(), - policyPayment.getSalary(), afterBalance, cmd.getOperationUserId()); + if (issueSalary.compareTo(BigDecimal.ZERO) > 0) { + addRunningWater(detailsId, row.getUserId(), row.getSysOrigin(), + issueSalary, afterBalance, cmd.getOperationUserId()); + } + String settlementRemark = issueSalary.compareTo(BigDecimal.ZERO) > 0 + ? "后台手动发放主播工资" + : "后台手动结算主播工资,实收已大于等于应发,本次发放0"; teamSalaryPaymentService.paymentSalary(payment.getId(), new TeamSalaryPaymentDetails() .setId(detailsId) @@ -155,12 +172,12 @@ public class TeamManualSalaryPaymentClientServiceImpl implements .setTeamBillId(Long.valueOf(billBelong)) .setTeamRegionId(row.getRegion()) .setRemarks(List.of(new TeamSalaryPaymentRemark() - .setRemark("后台手动发放主播工资") + .setRemark(settlementRemark) .setCreateTime(TimestampUtils.now()))) .setAnchor(Boolean.TRUE) .setHostSalaryToAgent(Boolean.FALSE) .setTarget(row.getCurrentTarget()) - .setSalary(policyPayment.getSalary()) + .setSalary(issueSalary) .setPolicyLevel(policyPayment.getLevel()) .setPolicy(policyPayment.getPolicy())); @@ -177,7 +194,7 @@ public class TeamManualSalaryPaymentClientServiceImpl implements .setBillBelong(billBelong) .setBeforeSalaryBalance(beforeBalance) .setAfterSalaryBalance(afterBalance) - .setIssuedSalary(policyPayment.getSalary()) + .setIssuedSalary(issueSalary) .setCurrentTarget(row.getCurrentTarget()) .setPolicyLevel(policyPayment.getLevel())); } @@ -215,17 +232,55 @@ public class TeamManualSalaryPaymentClientServiceImpl implements .collect(Collectors.toSet()); Map memberMap = teamMemberService.mapByMemberIds(memberIds); Map teamProfileMap = teamProfileService.mapProfileByIds(teamIds); + Map salaryPaymentMap = + teamSalaryPaymentService.mapByUserIdsAndDateNumbers(sysOrigin, memberIds, + getMergedSalaryPaymentBillBelongs(billBelong)); Map policyCache = CollectionUtils.newHashMap(); return targets.stream() .map(target -> buildRow(target, memberMap, teamProfileMap, policyCache, billBelong, - countryCode)) + countryCode, salaryPaymentMap)) .filter(Objects::nonNull) .sorted(Comparator.comparing(ManualSalaryRow::getCurrentTarget).reversed() .thenComparing(ManualSalaryRow::getUserId)) .collect(Collectors.toList()); } + private List sortRows(List rows, String sortField, + String sortOrder) { + if (CollectionUtils.isEmpty(rows) || StringUtils.isBlank(sortField) + || StringUtils.isBlank(sortOrder)) { + return rows; + } + Comparator comparator = getSalarySortComparator(sortField); + if (Objects.isNull(comparator)) { + return rows; + } + if (!SORT_ORDER_ASCEND.equalsIgnoreCase(sortOrder)) { + comparator = comparator.reversed(); + } + return rows.stream() + .sorted(comparator.thenComparing(ManualSalaryRow::getUserId)) + .collect(Collectors.toList()); + } + + private Comparator getSalarySortComparator(String sortField) { + if (SORT_FIELD_PAYABLE_SALARY.equals(sortField)) { + return Comparator.comparing(row -> decimalValue(row.getPayableSalary())); + } + if (SORT_FIELD_TOTAL_ISSUED_SALARY.equals(sortField)) { + return Comparator.comparing(row -> decimalValue(row.getTotalIssuedSalary())); + } + if (SORT_FIELD_CURRENT_SALARY_BALANCE.equals(sortField)) { + return Comparator.comparing(row -> decimalValue(row.getCurrentSalaryBalance())); + } + return null; + } + + private BigDecimal decimalValue(BigDecimal value) { + return Objects.isNull(value) ? BigDecimal.ZERO : value; + } + private Set singletonUserId(Long userId) { if (Objects.isNull(userId)) { return Set.of(); @@ -242,9 +297,36 @@ public class TeamManualSalaryPaymentClientServiceImpl implements .collect(Collectors.toSet()); } + private Set getMergedSalaryPaymentBillBelongs(Integer billBelong) { + if (Objects.isNull(billBelong)) { + return Set.of(); + } + Set billBelongs = new HashSet<>(); + billBelongs.add(billBelong); + Integer month = TeamBillCycleUtils.extractMonth(billBelong); + if (Objects.equals(month, LEGACY_HALF_MONTH_COMPAT_BILL_BELONG)) { + billBelongs.add(month); + billBelongs.add(month * 100 + 1); + billBelongs.add(month * 100 + 16); + } + return billBelongs; + } + + private List listCyclePayments( + Map salaryPaymentMap, Long userId, Integer billBelong) { + if (Objects.isNull(userId) || CollectionUtils.isEmpty(salaryPaymentMap)) { + return List.of(); + } + return getMergedSalaryPaymentBillBelongs(billBelong).stream() + .map(item -> salaryPaymentMap.get(paymentId(userId, item))) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + private ManualSalaryRow buildRow(TeamMemberTarget target, Map memberMap, Map teamProfileMap, Map policyCache, - Integer billBelong, String requestedCountryCode) { + Integer billBelong, String requestedCountryCode, + Map salaryPaymentMap) { TeamMember member = memberMap.get(target.getUserId()); TeamProfile teamProfile = teamProfileMap.get(target.getTeamId()); if (Objects.isNull(member) || Objects.isNull(teamProfile) @@ -264,14 +346,16 @@ public class TeamManualSalaryPaymentClientServiceImpl implements policyCache); TeamBillMemberTarget billTarget = calculateBillTarget(target, policyManager); - TeamSalaryPayment payment = teamSalaryPaymentService.getOneById(paymentId(target.getUserId(), - billBelong)); - List policyPayments = listUnpaidPolicyPayments(policyManager, billTarget, - payment, target.getUserId(), region); - BigDecimal payableSalary = policyPayments.stream() - .map(PolicyPayment::getSalary) - .reduce(BigDecimal.ZERO, BigDecimal::add) + List cyclePayments = listCyclePayments(salaryPaymentMap, + target.getUserId(), billBelong); + PaymentSummary paymentSummary = summarizePayments(cyclePayments); + BigDecimal expectedSalary = expectedMemberSalary(billTarget); + BigDecimal payableSalary = expectedSalary.subtract(paymentSummary.getTotalIssuedSalary()) .setScale(2, RoundingMode.DOWN); + boolean currentLevelSettled = isCurrentLevelSettled(cyclePayments, target.getUserId(), region, + billTarget.getLevel(), payableSalary); + List policyPayments = buildSettlementPayments(policyManager, billTarget, + payableSalary, currentLevelSettled); TeamManualSalaryPaymentDTO dto = new TeamManualSalaryPaymentDTO() .setId(target.getId()) @@ -282,15 +366,16 @@ public class TeamManualSalaryPaymentClientServiceImpl implements .setTeamOwnId(teamProfile.getOwnUserId()) .setUserId(target.getUserId()) .setBillBelong(billBelong) - .setLastSettlementTarget(getLastSettlementTarget(payment)) + .setLastSettlementTarget(getLastSettlementTarget(cyclePayments)) .setCurrentTarget(target.sumAcceptGiftValue()) .setPolicyLevel(billTarget.getLevel()) .setSatisfyLevel(billTarget.getSatisfyLevel()) + .setExpectedSalary(expectedSalary) .setPayableSalary(payableSalary) - .setTotalIssuedSalary(getTotalIssuedSalary(payment)) + .setTotalIssuedSalary(paymentSummary.getTotalIssuedSalary()) + .setOverIssuedSalary(paymentSummary.getOverIssuedSalary()) .setCurrentSalaryBalance(getAccurateBalance(target.getUserId())) - .setPaid(Objects.nonNull(billTarget.getLevel()) - && BigDecimal.ZERO.compareTo(payableSalary) == 0); + .setPaid(currentLevelSettled); return new ManualSalaryRow(dto, target, teamProfile, policyManager, billTarget, policyPayments); } @@ -345,51 +430,55 @@ public class TeamManualSalaryPaymentClientServiceImpl implements .setSatisfyLevel(Boolean.FALSE); } - private List listUnpaidPolicyPayments(TeamPolicyManager policyManager, - TeamBillMemberTarget billTarget, TeamSalaryPayment payment, Long userId, String region) { - if (Objects.isNull(policyManager) || CollectionUtils.isEmpty(policyManager.getPolicy()) + private BigDecimal expectedMemberSalary(TeamBillMemberTarget billTarget) { + return Optional.ofNullable(billTarget) + .map(TeamBillMemberTarget::getMemberSalary) + .orElse(BigDecimal.ZERO) + .setScale(2, RoundingMode.DOWN); + } + + private List buildSettlementPayments(TeamPolicyManager policyManager, + TeamBillMemberTarget billTarget, BigDecimal payableSalary, boolean currentLevelSettled) { + if (currentLevelSettled || Objects.isNull(billTarget) || Objects.isNull(billTarget.getLevel())) { return List.of(); } - List completePolicies = policyManager.getPolicy().stream() - .filter(policy -> Objects.nonNull(policy.getLevel())) - .filter(policy -> policy.getLevel() <= billTarget.getLevel()) - .sorted(Comparator.comparing(TeamPolicy::getLevel)) - .collect(Collectors.toList()); - if (CollectionUtils.isEmpty(completePolicies)) { - return List.of(); - } - - List payments = new ArrayList<>(); - for (int i = 0; i < completePolicies.size(); i++) { - TeamPolicy policy = completePolicies.get(i); - if (Objects.nonNull(payment) && Boolean.TRUE.equals( - teamSalaryPaymentService.checkRepeatPayment(payment.getId(), userId, region, - policy.getLevel(), Boolean.TRUE))) { - continue; - } - - BigDecimal salary = Optional.ofNullable(policy.getMemberSalary()).orElse(BigDecimal.ZERO); - if (i > 0) { - salary = salary.subtract(Optional.ofNullable(completePolicies.get(i - 1).getMemberSalary()) - .orElse(BigDecimal.ZERO)); - } - salary = salary.setScale(2, RoundingMode.DOWN); - if (salary.compareTo(BigDecimal.ZERO) <= 0) { - continue; - } - - payments.add(new PolicyPayment(policy, policy.getLevel(), salary)); - } - return payments; + BigDecimal issueSalary = payableSalary.compareTo(BigDecimal.ZERO) > 0 + ? payableSalary.setScale(2, RoundingMode.DOWN) + : BigDecimal.ZERO.setScale(2, RoundingMode.DOWN); + return List.of(new PolicyPayment(findPolicy(policyManager, billTarget.getLevel()), + billTarget.getLevel(), issueSalary)); } - private Long getLastSettlementTarget(TeamSalaryPayment payment) { - return Optional.ofNullable(payment) - .map(TeamSalaryPayment::getSalarySendDetails) - .stream() - .flatMap(List::stream) + private TeamPolicy findPolicy(TeamPolicyManager policyManager, Integer level) { + if (Objects.isNull(policyManager) || CollectionUtils.isEmpty(policyManager.getPolicy()) + || Objects.isNull(level)) { + return null; + } + return policyManager.getPolicy().stream() + .filter(policy -> Objects.equals(policy.getLevel(), level)) + .findFirst() + .orElse(null); + } + + private boolean isCurrentLevelSettled(List cyclePayments, Long userId, + String region, Integer policyLevel, BigDecimal payableSalary) { + return Objects.nonNull(policyLevel) + && payableSalary.compareTo(BigDecimal.ZERO) <= 0 + && hasPaidPolicy(cyclePayments, userId, region, policyLevel); + } + + private boolean hasPaidPolicy(List cyclePayments, Long userId, String region, + Integer policyLevel) { + return listValidAnchorDetails(cyclePayments).stream() + .anyMatch(details -> Objects.equals(details.getTeamMemberId(), userId) + && Objects.equals(details.getTeamRegionId(), region) + && Objects.equals(details.getPolicyLevel(), policyLevel)); + } + + private Long getLastSettlementTarget(List cyclePayments) { + return listValidAnchorDetails(cyclePayments).stream() .filter(this::validAnchorDetails) .max(Comparator.comparing(details -> Optional.ofNullable(details.getCreateTime()) .orElse(new Timestamp(0)))) @@ -397,15 +486,54 @@ public class TeamManualSalaryPaymentClientServiceImpl implements .orElse(0L); } - private BigDecimal getTotalIssuedSalary(TeamSalaryPayment payment) { - return Optional.ofNullable(payment) - .map(TeamSalaryPayment::getSalarySendDetails) - .stream() - .flatMap(List::stream) - .filter(this::validAnchorDetails) - .map(details -> Optional.ofNullable(details.getSalary()).orElse(BigDecimal.ZERO)) + private PaymentSummary summarizePayments(List cyclePayments) { + List details = listValidAnchorDetails(cyclePayments); + BigDecimal totalIssuedSalary = details.stream() + .map(detail -> Optional.ofNullable(detail.getSalary()).orElse(BigDecimal.ZERO)) .reduce(BigDecimal.ZERO, BigDecimal::add) .setScale(2, RoundingMode.DOWN); + return new PaymentSummary(totalIssuedSalary, getOverIssuedSalary(details)); + } + + private BigDecimal getOverIssuedSalary(List details) { + if (CollectionUtils.isEmpty(details)) { + return BigDecimal.ZERO.setScale(2, RoundingMode.DOWN); + } + Set issuedKeys = new HashSet<>(); + return details.stream() + .sorted(Comparator.comparing(detail -> Optional.ofNullable(detail.getCreateTime()) + .orElse(new Timestamp(0)))) + .filter(detail -> !issuedKeys.add(paymentDetailsPolicyKey(detail))) + .map(detail -> Optional.ofNullable(detail.getSalary()).orElse(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add) + .setScale(2, RoundingMode.DOWN); + } + + private String paymentDetailsPolicyKey(TeamSalaryPaymentDetails details) { + if (Objects.isNull(details) + || Objects.isNull(details.getTeamMemberId()) + || StringUtils.isBlank(details.getTeamRegionId()) + || Objects.isNull(details.getPolicyLevel()) + || Objects.isNull(details.getAnchor())) { + return Optional.ofNullable(details).map(TeamSalaryPaymentDetails::getId) + .orElse(String.valueOf(System.identityHashCode(details))); + } + return details.getTeamMemberId() + ":" + details.getTeamRegionId() + ":" + + details.getPolicyLevel() + ":" + details.getAnchor(); + } + + private List listValidAnchorDetails( + List cyclePayments) { + if (CollectionUtils.isEmpty(cyclePayments)) { + return List.of(); + } + return cyclePayments.stream() + .filter(Objects::nonNull) + .flatMap(payment -> Optional.ofNullable(payment.getSalarySendDetails()) + .stream() + .flatMap(List::stream)) + .filter(this::validAnchorDetails) + .collect(Collectors.toList()); } private boolean validAnchorDetails(TeamSalaryPaymentDetails details) { @@ -471,6 +599,25 @@ public class TeamManualSalaryPaymentClientServiceImpl implements return userId + "_" + billBelong; } + private static class PaymentSummary { + + private final BigDecimal totalIssuedSalary; + private final BigDecimal overIssuedSalary; + + private PaymentSummary(BigDecimal totalIssuedSalary, BigDecimal overIssuedSalary) { + this.totalIssuedSalary = totalIssuedSalary; + this.overIssuedSalary = overIssuedSalary; + } + + private BigDecimal getTotalIssuedSalary() { + return totalIssuedSalary; + } + + private BigDecimal getOverIssuedSalary() { + return overIssuedSalary; + } + } + private static class ManualSalaryRow { private final TeamManualSalaryPaymentDTO dto; @@ -527,6 +674,18 @@ public class TeamManualSalaryPaymentClientServiceImpl implements return dto.getCurrentTarget(); } + private BigDecimal getPayableSalary() { + return dto.getPayableSalary(); + } + + private BigDecimal getTotalIssuedSalary() { + return dto.getTotalIssuedSalary(); + } + + private BigDecimal getCurrentSalaryBalance() { + return dto.getCurrentSalaryBalance(); + } + private TeamPolicyManager getPolicyManager() { return policyManager; }