|
|
|
@ -1,29 +1,38 @@ |
|
|
|
package cc.hiver.mall.quartz; |
|
|
|
|
|
|
|
import cc.hiver.core.common.constant.CommonConstant; |
|
|
|
import cc.hiver.core.common.constant.DealingsRecordConstant; |
|
|
|
import cc.hiver.core.entity.User; |
|
|
|
import cc.hiver.core.entity.Worker; |
|
|
|
import cc.hiver.core.service.UserService; |
|
|
|
import cc.hiver.core.serviceimpl.WorkerServiceImpl; |
|
|
|
import cc.hiver.mall.dao.ShopAreaDao; |
|
|
|
import cc.hiver.mall.dao.mapper.MallReturnOrderGoodsMapper; |
|
|
|
import cc.hiver.mall.entity.*; |
|
|
|
import cc.hiver.mall.service.ShopService; |
|
|
|
import cc.hiver.mall.service.ShopTakeawayService; |
|
|
|
import cc.hiver.mall.service.mybatis.MallOrderService; |
|
|
|
import cc.hiver.mall.service.mybatis.MallRefundRecordService; |
|
|
|
import cc.hiver.mall.service.mybatis.MallSettlementRecordService; |
|
|
|
import cc.hiver.mall.service.mybatis.WorkerDailyBalanceRecordService; |
|
|
|
import cc.hiver.mall.service.mybatis.*; |
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.apache.commons.lang3.StringUtils; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.scheduling.annotation.Scheduled; |
|
|
|
import org.springframework.stereotype.Component; |
|
|
|
|
|
|
|
import java.math.BigDecimal; |
|
|
|
import java.time.LocalDate; |
|
|
|
import java.time.LocalDateTime; |
|
|
|
import java.time.LocalTime; |
|
|
|
import java.time.ZoneId; |
|
|
|
import java.util.Date; |
|
|
|
import java.util.List; |
|
|
|
|
|
|
|
@Slf4j |
|
|
|
@Component |
|
|
|
public class MerchantSettlementTask { |
|
|
|
private static final BigDecimal ONE_PERCENT = new BigDecimal("0.01"); |
|
|
|
private static final String SECOND_AGENT_REBATE_LINK_PREFIX = "SECOND_AGENT_REBATE:"; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private MallOrderService mallOrderService; |
|
|
|
@Autowired |
|
|
|
@ -40,6 +49,12 @@ public class MerchantSettlementTask { |
|
|
|
private ShopService shopService; |
|
|
|
@Autowired |
|
|
|
private WorkerDailyBalanceRecordService workerDailyBalanceRecordService; |
|
|
|
@Autowired |
|
|
|
private ShopAreaDao shopAreaDao; |
|
|
|
@Autowired |
|
|
|
private UserService userService; |
|
|
|
@Autowired |
|
|
|
private DealingsRecordService dealingsRecordService; |
|
|
|
|
|
|
|
@Scheduled(cron = "0 0 2 * * ?") |
|
|
|
public void executeSettlement() { |
|
|
|
@ -109,6 +124,26 @@ public class MerchantSettlementTask { |
|
|
|
log.info("[MerchantSettlementTask] Daily settlement calculation finished."); |
|
|
|
} |
|
|
|
|
|
|
|
@Scheduled(cron = "0 0 3 * * ?") |
|
|
|
public void executeSecondAgentRebate() { |
|
|
|
LocalDate settlementDate = LocalDate.now().minusDays(1); |
|
|
|
log.info("[MerchantSettlementTask] Starting second agent rebate, date={}", settlementDate); |
|
|
|
try { |
|
|
|
List<ShopArea> areas = shopAreaDao.findByParentIdOrderBySortOrder(CommonConstant.PARENT_ID); |
|
|
|
for (ShopArea area : areas) { |
|
|
|
try { |
|
|
|
processSecondAgentRebate(area, settlementDate); |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("[MerchantSettlementTask] Failed to process second agent rebate, regionId={}", |
|
|
|
area == null ? null : area.getId(), e); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("[MerchantSettlementTask] Error processing second agent rebates", e); |
|
|
|
} |
|
|
|
log.info("[MerchantSettlementTask] Second agent rebate finished, date={}", settlementDate); |
|
|
|
} |
|
|
|
|
|
|
|
private void saveWorkerDailyBalanceRecord(Worker worker, LocalDate balanceDate) { |
|
|
|
if (worker == null |
|
|
|
|| worker.getWorkerStatus() == null |
|
|
|
@ -137,6 +172,106 @@ public class MerchantSettlementTask { |
|
|
|
workerDailyBalanceRecordService.saveOrUpdate(record); |
|
|
|
} |
|
|
|
|
|
|
|
private void processSecondAgentRebate(ShopArea area, LocalDate settlementDate) { |
|
|
|
if (area == null |
|
|
|
|| StringUtils.isBlank(area.getId()) |
|
|
|
|| StringUtils.isBlank(area.getParentAgentId()) |
|
|
|
|| area.getCommissionRatio() == null |
|
|
|
|| area.getCommissionRatio() <= 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
if (area.getCommissionRatio() > 100) { |
|
|
|
log.warn("[MerchantSettlementTask] Skip second agent rebate, invalid commissionRatio={}, regionId={}", |
|
|
|
area.getCommissionRatio(), area.getId()); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
String linkId = SECOND_AGENT_REBATE_LINK_PREFIX + area.getId() + ":" + settlementDate; |
|
|
|
String dealingsWay = safeText(area.getTitle()) + DealingsRecordConstant.TYPE_REMARK[13]; |
|
|
|
if (existsSecondAgentRebate(linkId)) { |
|
|
|
log.info("[MerchantSettlementTask] Second agent rebate already exists, regionId={}, date={}", |
|
|
|
area.getId(), settlementDate); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
BigDecimal headquartersDeduction = calculateHeadquartersDeduction(area.getId(), settlementDate); |
|
|
|
if (headquartersDeduction.compareTo(BigDecimal.ZERO) <= 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
BigDecimal rebateAmount = money(headquartersDeduction |
|
|
|
.multiply(rateDecimal(BigDecimal.valueOf(area.getCommissionRatio())))); |
|
|
|
if (rebateAmount.compareTo(BigDecimal.ZERO) <= 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
User agent = userService.findById(area.getParentAgentId()); |
|
|
|
if (agent == null) { |
|
|
|
log.warn("[MerchantSettlementTask] Skip second agent rebate, agent not found, regionId={}, agentId={}", |
|
|
|
area.getId(), area.getParentAgentId()); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
agent.setDepoBal(safeAmount(agent.getDepoBal()).add(rebateAmount)); |
|
|
|
userService.update(agent); |
|
|
|
|
|
|
|
DealingsRecord dealingsRecord = new DealingsRecord(); |
|
|
|
dealingsRecord.setDealingsWay(dealingsWay); |
|
|
|
dealingsRecord.setAmount(rebateAmount); |
|
|
|
dealingsRecord.setRegionId(area.getId()); |
|
|
|
dealingsRecord.setLinkUserId(agent.getId()); |
|
|
|
dealingsRecord.setLinkId(linkId); |
|
|
|
dealingsRecord.setDealingsTime(new Date()); |
|
|
|
dealingsRecord.setLinkType(DealingsRecordConstant.TYPE[0]); |
|
|
|
dealingsRecord.setDealingsType(DealingsRecordConstant.DEALINGS_TYPE[0]); |
|
|
|
dealingsRecord.setCreateBy(agent.getId()); |
|
|
|
dealingsRecord.setCreateByName(agent.getNickname()); |
|
|
|
dealingsRecord.setRemark("结算日期:" + settlementDate |
|
|
|
+ ",返佣比例:" + area.getCommissionRatio() + "%"); |
|
|
|
dealingsRecordService.save(dealingsRecord); |
|
|
|
|
|
|
|
log.info("[MerchantSettlementTask] Second agent rebate success, regionId={}, agentId={}, amount={}, date={}", |
|
|
|
area.getId(), agent.getId(), rebateAmount, settlementDate); |
|
|
|
} |
|
|
|
|
|
|
|
private boolean existsSecondAgentRebate(String linkId) { |
|
|
|
LambdaQueryWrapper<DealingsRecord> qw = new LambdaQueryWrapper<>(); |
|
|
|
qw.eq(DealingsRecord::getLinkId, linkId); |
|
|
|
return dealingsRecordService.count(qw) > 0; |
|
|
|
} |
|
|
|
|
|
|
|
private BigDecimal calculateHeadquartersDeduction(String regionId, LocalDate settlementDate) { |
|
|
|
Date start = startOfDay(settlementDate); |
|
|
|
Date end = endOfDay(settlementDate); |
|
|
|
|
|
|
|
List<MallOrder> orders = mallOrderService.list(new LambdaQueryWrapper<MallOrder>() |
|
|
|
.eq(MallOrder::getRegionId, regionId) |
|
|
|
.ge(MallOrder::getPayTime, start) |
|
|
|
.le(MallOrder::getPayTime, end) |
|
|
|
.notIn(MallOrder::getStatus, 0, 6)); |
|
|
|
|
|
|
|
List<MallRefundRecord> refunds = mallRefundRecordService.list(new LambdaQueryWrapper<MallRefundRecord>() |
|
|
|
.eq(MallRefundRecord::getRegionId, regionId) |
|
|
|
.ge(MallRefundRecord::getSuccessTime, start) |
|
|
|
.le(MallRefundRecord::getSuccessTime, end) |
|
|
|
.in(MallRefundRecord::getStatus, 1, 4)); |
|
|
|
|
|
|
|
List<WorkerDailyBalanceRecord> workerBalanceRecords = workerDailyBalanceRecordService.list( |
|
|
|
new LambdaQueryWrapper<WorkerDailyBalanceRecord>() |
|
|
|
.eq(WorkerDailyBalanceRecord::getRegionId, regionId) |
|
|
|
.eq(WorkerDailyBalanceRecord::getBalanceDate, settlementDate)); |
|
|
|
|
|
|
|
BigDecimal grossFlow = orders.stream() |
|
|
|
.map(MallOrder::getTotalAmount) |
|
|
|
.map(this::safeAmount) |
|
|
|
.reduce(BigDecimal.ZERO, BigDecimal::add); |
|
|
|
BigDecimal refundAmount = sumRefundAmount(refunds, 1); |
|
|
|
BigDecimal afterSaleAmount = sumRefundAmount(refunds, 4); |
|
|
|
BigDecimal effectiveFlow = grossFlow.subtract(refundAmount).subtract(afterSaleAmount); |
|
|
|
BigDecimal deliveryFeeAmount = sumPositiveWorkerBalance(workerBalanceRecords); |
|
|
|
return effectiveFlow.subtract(deliveryFeeAmount).multiply(ONE_PERCENT); |
|
|
|
} |
|
|
|
|
|
|
|
private void processUnsettledOrder(MallOrder order) { |
|
|
|
if (!isMerchantSettlementOrder(order)) { |
|
|
|
return; |
|
|
|
@ -278,6 +413,42 @@ public class MerchantSettlementTask { |
|
|
|
return amount == null ? BigDecimal.ZERO : amount; |
|
|
|
} |
|
|
|
|
|
|
|
private BigDecimal money(BigDecimal amount) { |
|
|
|
return safeAmount(amount).setScale(2, BigDecimal.ROUND_HALF_UP); |
|
|
|
} |
|
|
|
|
|
|
|
private BigDecimal sumRefundAmount(List<MallRefundRecord> refunds, int status) { |
|
|
|
return refunds.stream() |
|
|
|
.filter(item -> item.getStatus() != null && item.getStatus() == status) |
|
|
|
.map(MallRefundRecord::getRefundAmount) |
|
|
|
.map(this::safeAmount) |
|
|
|
.reduce(BigDecimal.ZERO, BigDecimal::add); |
|
|
|
} |
|
|
|
|
|
|
|
private BigDecimal sumPositiveWorkerBalance(List<WorkerDailyBalanceRecord> records) { |
|
|
|
return records.stream() |
|
|
|
.map(WorkerDailyBalanceRecord::getDailyBalance) |
|
|
|
.map(this::safeAmount) |
|
|
|
.filter(amount -> amount.compareTo(BigDecimal.ZERO) > 0) |
|
|
|
.reduce(BigDecimal.ZERO, BigDecimal::add); |
|
|
|
} |
|
|
|
|
|
|
|
private Date startOfDay(LocalDate date) { |
|
|
|
return Date.from(LocalDateTime.of(date, LocalTime.MIN) |
|
|
|
.atZone(ZoneId.systemDefault()) |
|
|
|
.toInstant()); |
|
|
|
} |
|
|
|
|
|
|
|
private Date endOfDay(LocalDate date) { |
|
|
|
return Date.from(LocalDateTime.of(date, LocalTime.MAX) |
|
|
|
.atZone(ZoneId.systemDefault()) |
|
|
|
.toInstant()); |
|
|
|
} |
|
|
|
|
|
|
|
private String safeText(String value) { |
|
|
|
return value == null ? "" : value; |
|
|
|
} |
|
|
|
|
|
|
|
private BigDecimal goodsPackageAmount(MallOrder order) { |
|
|
|
if (order == null) { |
|
|
|
return BigDecimal.ZERO; |
|
|
|
|