|
|
@@ -3,6 +3,7 @@ package com.etotem.cfc.service;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.etotem.cfc.common.Result;
|
|
|
import com.etotem.cfc.entity.ProductOrder;
|
|
|
import com.etotem.cfc.entity.SupplySettlement;
|
|
|
import com.etotem.cfc.entity.SupplySettlementDetail;
|
|
|
@@ -11,16 +12,26 @@ import com.etotem.cfc.mapper.ProductOrderMapper;
|
|
|
import com.etotem.cfc.mapper.SupplySettlementDetailMapper;
|
|
|
import com.etotem.cfc.mapper.SupplySettlementMapper;
|
|
|
import com.etotem.cfc.mapper.SupplySystemMapper;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
@Service
|
|
|
public class SupplySettlementService extends ServiceImpl<SupplySettlementMapper, SupplySettlement> {
|
|
|
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(SupplySettlementService.class);
|
|
|
+
|
|
|
@Resource
|
|
|
private SupplySettlementDetailMapper supplySettlementDetailMapper;
|
|
|
|
|
|
@@ -30,62 +41,160 @@ public class SupplySettlementService extends ServiceImpl<SupplySettlementMapper,
|
|
|
@Resource
|
|
|
private ProductOrderMapper productOrderMapper;
|
|
|
|
|
|
- public Page<SupplySettlement> list(Long systemId, String status, int page, int size) {
|
|
|
- Page<SupplySettlement> pageParam = new Page<>(page, size);
|
|
|
- LambdaQueryWrapper<SupplySettlement> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ /**
|
|
|
+ * 分页查询结算记录列表
|
|
|
+ */
|
|
|
+ public Result<Map<String, Object>> list(Map<String, Object> params) {
|
|
|
+ Integer pageNum = params.get("page") != null ? ((Number) params.get("page")).intValue() : 1;
|
|
|
+ Integer pageSize = params.get("size") != null ? ((Number) params.get("size")).intValue() : 20;
|
|
|
+ Long systemId = params.get("systemId") != null ? ((Number) params.get("systemId")).longValue() : null;
|
|
|
+ String status = (String) params.get("status");
|
|
|
+
|
|
|
+ Page<SupplySettlement> pageParam = new Page<>(pageNum, pageSize);
|
|
|
+ LambdaQueryWrapper<SupplySettlement> wrapper = new LambdaQueryWrapper<SupplySettlement>()
|
|
|
+ .orderByDesc(SupplySettlement::getCreatedAt);
|
|
|
+
|
|
|
if (systemId != null) {
|
|
|
wrapper.eq(SupplySettlement::getSystemId, systemId);
|
|
|
}
|
|
|
if (status != null && !status.isEmpty()) {
|
|
|
wrapper.eq(SupplySettlement::getStatus, status);
|
|
|
}
|
|
|
- wrapper.orderByDesc(SupplySettlement::getId);
|
|
|
- return this.page(pageParam, wrapper);
|
|
|
+
|
|
|
+ Page<SupplySettlement> result = this.page(pageParam, wrapper);
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("records", result.getRecords());
|
|
|
+ data.put("total", result.getTotal());
|
|
|
+ data.put("page", result.getCurrent());
|
|
|
+ data.put("size", result.getSize());
|
|
|
+ return Result.success(data);
|
|
|
}
|
|
|
|
|
|
- public SupplySettlement detail(Long id) {
|
|
|
- return this.getById(id);
|
|
|
+ /**
|
|
|
+ * 按 ID 查询结算详情
|
|
|
+ */
|
|
|
+ public Result<SupplySettlement> detail(Long id) {
|
|
|
+ SupplySettlement settlement = this.getById(id);
|
|
|
+ if (settlement == null) {
|
|
|
+ return Result.error("结算记录不存在");
|
|
|
+ }
|
|
|
+ return Result.success(settlement);
|
|
|
}
|
|
|
|
|
|
- public List<SupplySettlementDetail> detailItems(Long settlementId) {
|
|
|
+ /**
|
|
|
+ * 查询结算明细列表
|
|
|
+ */
|
|
|
+ public Result<List<SupplySettlementDetail>> detailItems(Long settlementId) {
|
|
|
LambdaQueryWrapper<SupplySettlementDetail> wrapper = new LambdaQueryWrapper<>();
|
|
|
wrapper.eq(SupplySettlementDetail::getSettlementId, settlementId);
|
|
|
- return supplySettlementDetailMapper.selectList(wrapper);
|
|
|
+ List<SupplySettlementDetail> items = supplySettlementDetailMapper.selectList(wrapper);
|
|
|
+ return Result.success(items);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 手动生成结算单
|
|
|
+ *
|
|
|
+ * 汇总指定周期内该体系下所有已完成的订单,按公式计算平台留利和供应商实付:
|
|
|
+ * platform_profit = total_sales × platform_profit_rate / 100
|
|
|
+ * supplier_payout = total_sales - platform_profit
|
|
|
+ */
|
|
|
@Transactional
|
|
|
- public SupplySettlement createSettlement(Long systemId, Date periodStart, Date periodEnd, String remark) {
|
|
|
+ public Result<SupplySettlement> createSettlement(Map<String, Object> params) {
|
|
|
+ Long systemId = params.get("systemId") != null ? ((Number) params.get("systemId")).longValue() : null;
|
|
|
+ String periodStartStr = (String) params.get("periodStart");
|
|
|
+ String periodEndStr = (String) params.get("periodEnd");
|
|
|
+
|
|
|
+ if (systemId == null) {
|
|
|
+ return Result.error("systemId不能为空");
|
|
|
+ }
|
|
|
+ if (periodStartStr == null || periodEndStr == null) {
|
|
|
+ return Result.error("结算周期不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
SupplySystem system = supplySystemMapper.selectById(systemId);
|
|
|
- if (system == null) return null;
|
|
|
+ if (system == null) {
|
|
|
+ return Result.error("供应商体系不存在");
|
|
|
+ }
|
|
|
|
|
|
- // Query all completed orders in the period for this system
|
|
|
+ Date periodStart;
|
|
|
+ Date periodEnd;
|
|
|
+ try {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ periodStart = sdf.parse(periodStartStr);
|
|
|
+ periodEnd = sdf.parse(periodEndStr);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return Result.error("日期格式错误,应为 yyyy-MM-dd");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询该体系下已完成且未被结算的订单
|
|
|
LambdaQueryWrapper<ProductOrder> orderWrapper = new LambdaQueryWrapper<>();
|
|
|
orderWrapper.eq(ProductOrder::getSupplySystemId, systemId)
|
|
|
- .ge(ProductOrder::getPaidAt, periodStart)
|
|
|
- .le(ProductOrder::getPaidAt, periodEnd)
|
|
|
- .ne(ProductOrder::getStatus, "CANCELLED");
|
|
|
+ .eq(ProductOrder::getStatus, "completed")
|
|
|
+ .ge(ProductOrder::getCreatedAt, periodStart)
|
|
|
+ .le(ProductOrder::getCreatedAt, periodEnd);
|
|
|
+
|
|
|
List<ProductOrder> orders = productOrderMapper.selectList(orderWrapper);
|
|
|
+ if (orders.isEmpty()) {
|
|
|
+ return Result.error("该周期内没有已完成的订单");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 排除已存在于结算明细中的订单
|
|
|
+ List<Long> orderIds = orders.stream().map(ProductOrder::getId).collect(Collectors.toList());
|
|
|
+ LambdaQueryWrapper<SupplySettlementDetail> existingWrapper = new LambdaQueryWrapper<>();
|
|
|
+ existingWrapper.in(SupplySettlementDetail::getOrderId, orderIds);
|
|
|
+ List<SupplySettlementDetail> existingDetails = supplySettlementDetailMapper.selectList(existingWrapper);
|
|
|
+ Set<Long> existingOrderIds = existingDetails.stream()
|
|
|
+ .map(SupplySettlementDetail::getOrderId)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ List<ProductOrder> settleableOrders = orders.stream()
|
|
|
+ .filter(o -> !existingOrderIds.contains(o.getId()))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ if (settleableOrders.isEmpty()) {
|
|
|
+ return Result.error("该周期内的订单均已结算");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算销售总额(单位:分)
|
|
|
+ int totalSales = settleableOrders.stream()
|
|
|
+ .mapToInt(o -> o.getTotalAmount() != null ? o.getTotalAmount() : 0)
|
|
|
+ .sum();
|
|
|
|
|
|
- if (orders.isEmpty()) return null;
|
|
|
+ // 获取平台留利比例
|
|
|
+ BigDecimal rate = system.getPlatformProfitRate() != null
|
|
|
+ ? system.getPlatformProfitRate()
|
|
|
+ : BigDecimal.ZERO;
|
|
|
|
|
|
- int totalSales = 0;
|
|
|
- int platformProfit = 0;
|
|
|
+ // 计算公式(金额单位:分)
|
|
|
+ // platform_profit = total_sales × platform_profit_rate / 100
|
|
|
+ BigDecimal totalSalesBD = BigDecimal.valueOf(totalSales);
|
|
|
+ BigDecimal platformProfitBD = totalSalesBD.multiply(rate)
|
|
|
+ .divide(BigDecimal.valueOf(100), 0, BigDecimal.ROUND_HALF_UP);
|
|
|
+ BigDecimal supplierPayoutBD = totalSalesBD.subtract(platformProfitBD);
|
|
|
|
|
|
+ int platformProfit = platformProfitBD.intValue();
|
|
|
+ int supplierPayout = supplierPayoutBD.intValue();
|
|
|
+
|
|
|
+ // 创建结算记录
|
|
|
SupplySettlement settlement = new SupplySettlement();
|
|
|
settlement.setSystemId(systemId);
|
|
|
settlement.setPeriodStart(periodStart);
|
|
|
settlement.setPeriodEnd(periodEnd);
|
|
|
+ settlement.setTotalSales(totalSales);
|
|
|
+ settlement.setPlatformProfit(platformProfit);
|
|
|
+ settlement.setSupplierPayout(supplierPayout);
|
|
|
settlement.setStatus("pending");
|
|
|
- settlement.setRemark(remark);
|
|
|
settlement.setCreatedAt(new Date());
|
|
|
+ this.save(settlement);
|
|
|
|
|
|
- for (ProductOrder order : orders) {
|
|
|
+ // 创建结算明细
|
|
|
+ for (ProductOrder order : settleableOrders) {
|
|
|
int orderAmount = order.getTotalAmount() != null ? order.getTotalAmount() : 0;
|
|
|
- int profitRate = system.getPlatformProfitRate() != null ? system.getPlatformProfitRate().multiply(java.math.BigDecimal.valueOf(10)).intValue() : 0;
|
|
|
- int orderProfit = (orderAmount * profitRate) / 1000;
|
|
|
-
|
|
|
- totalSales += orderAmount;
|
|
|
- platformProfit += orderProfit;
|
|
|
+ BigDecimal orderAmountBD = BigDecimal.valueOf(orderAmount);
|
|
|
+ int orderPlatformFee = orderAmountBD.multiply(rate)
|
|
|
+ .divide(BigDecimal.valueOf(100), 0, BigDecimal.ROUND_HALF_UP)
|
|
|
+ .intValue();
|
|
|
+ int orderPayout = orderAmount - orderPlatformFee;
|
|
|
|
|
|
SupplySettlementDetail detail = new SupplySettlementDetail();
|
|
|
detail.setSettlementId(settlement.getId());
|
|
|
@@ -93,26 +202,36 @@ public class SupplySettlementService extends ServiceImpl<SupplySettlementMapper,
|
|
|
detail.setOrderNo(order.getOrderNo());
|
|
|
detail.setProductName(order.getProductName());
|
|
|
detail.setAmount(orderAmount);
|
|
|
- detail.setPlatformFee(orderProfit);
|
|
|
+ detail.setPlatformFee(orderPlatformFee);
|
|
|
detail.setSupplierId(order.getSupplierId());
|
|
|
- detail.setPayout(orderAmount - orderProfit);
|
|
|
+ detail.setPayout(orderPayout);
|
|
|
+ detail.setCreatedAt(new Date());
|
|
|
supplySettlementDetailMapper.insert(detail);
|
|
|
}
|
|
|
|
|
|
- settlement.setTotalSales(totalSales);
|
|
|
- settlement.setPlatformProfit(platformProfit);
|
|
|
- settlement.setSupplierPayout(totalSales - platformProfit);
|
|
|
- this.save(settlement);
|
|
|
+ log.info("已创建结算单 ID={}, systemId={}, 周期={}~{}, 总额={}, 留利={}, 实付={}",
|
|
|
+ settlement.getId(), systemId, periodStartStr, periodEndStr,
|
|
|
+ totalSales, platformProfit, supplierPayout);
|
|
|
|
|
|
- return settlement;
|
|
|
+ return Result.success(settlement);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 确认结算(pending → settled)
|
|
|
+ */
|
|
|
@Transactional
|
|
|
- public boolean confirm(Long id) {
|
|
|
+ public Result<Void> confirm(Long id) {
|
|
|
SupplySettlement settlement = this.getById(id);
|
|
|
- if (settlement == null) return false;
|
|
|
+ if (settlement == null) {
|
|
|
+ return Result.error("结算记录不存在");
|
|
|
+ }
|
|
|
+ if (!"pending".equals(settlement.getStatus())) {
|
|
|
+ return Result.error("只有待结算状态的记录才能确认");
|
|
|
+ }
|
|
|
settlement.setStatus("settled");
|
|
|
settlement.setSettledAt(new Date());
|
|
|
- return this.updateById(settlement);
|
|
|
+ this.updateById(settlement);
|
|
|
+ log.info("已确认结算单 ID={}", id);
|
|
|
+ return Result.success(null);
|
|
|
}
|
|
|
}
|