31 changed files with 1122 additions and 90 deletions
@ -0,0 +1,204 @@ |
|||||
|
package cc.hiver.mall.controller; |
||||
|
|
||||
|
import cc.hiver.core.common.utils.ResultUtil; |
||||
|
import cc.hiver.core.common.vo.Result; |
||||
|
import cc.hiver.mall.entity.MallSettlementRecord; |
||||
|
import cc.hiver.mall.entity.Shop; |
||||
|
import cc.hiver.mall.service.ShopService; |
||||
|
import cc.hiver.mall.service.mybatis.MallSettlementRecordService; |
||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
import io.swagger.annotations.Api; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import io.swagger.annotations.ApiOperation; |
||||
|
import lombok.Data; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
@Slf4j |
||||
|
@RestController |
||||
|
@Api(tags = "后台管理系统结算记录接口") |
||||
|
@RequestMapping("/hiver/mall/admin/settlement") |
||||
|
public class AdminSettlementController { |
||||
|
|
||||
|
@Autowired |
||||
|
private MallSettlementRecordService mallSettlementRecordService; |
||||
|
|
||||
|
@Autowired |
||||
|
private ShopService shopService; |
||||
|
|
||||
|
@Data |
||||
|
public static class SettlementQuery { |
||||
|
private String shopId; |
||||
|
private Integer status; |
||||
|
private Integer type; |
||||
|
private int pageNumber = 1; |
||||
|
private int pageSize = 10; |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/page") |
||||
|
@ApiOperation(value = "分页查询结算记录") |
||||
|
public Result<Page<MallSettlementRecord>> page(@RequestBody SettlementQuery query) { |
||||
|
LambdaQueryWrapper<MallSettlementRecord> qw = new LambdaQueryWrapper<>(); |
||||
|
if (StringUtils.isNotBlank(query.getShopId())) { |
||||
|
qw.eq(MallSettlementRecord::getShopId, query.getShopId()); |
||||
|
} |
||||
|
if (query.getStatus() != null) { |
||||
|
qw.eq(MallSettlementRecord::getStatus, query.getStatus()); |
||||
|
} |
||||
|
if (query.getType() != null) { |
||||
|
qw.eq(MallSettlementRecord::getType, query.getType()); |
||||
|
} |
||||
|
qw.orderByDesc(MallSettlementRecord::getCreateTime); |
||||
|
|
||||
|
Page<MallSettlementRecord> page = new Page<>(query.getPageNumber(), query.getPageSize()); |
||||
|
Page<MallSettlementRecord> result = mallSettlementRecordService.page(page, qw); |
||||
|
return new ResultUtil<Page<MallSettlementRecord>>().setData(result); |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
public static class ConfirmReq { |
||||
|
private List<String> ids; |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/confirm") |
||||
|
@ApiOperation(value = "每日结算确认(支持批量)") |
||||
|
public Result confirm(@RequestBody ConfirmReq req) { |
||||
|
if (req.getIds() == null || req.getIds().isEmpty()) { |
||||
|
return ResultUtil.error("请选择要确认的结算记录"); |
||||
|
} |
||||
|
try { |
||||
|
mallSettlementRecordService.confirmSettlements(req.getIds()); |
||||
|
return ResultUtil.success("结算确认成功"); |
||||
|
} catch (Exception e) { |
||||
|
log.error("结算确认失败: {}", e.getMessage(), e); |
||||
|
return ResultUtil.error("结算确认失败: " + e.getMessage()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
public static class SummaryQuery { |
||||
|
@ApiModelProperty(value = "区域ID") |
||||
|
private String regionId; |
||||
|
@ApiModelProperty(value = "结算日期,格式yyyy-MM-dd") |
||||
|
private String settlementDate; |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
public static class SettlementSummaryVo { |
||||
|
@ApiModelProperty(value = "商家ID") |
||||
|
private String shopId; |
||||
|
@ApiModelProperty(value = "商家名称") |
||||
|
private String shopName; |
||||
|
@ApiModelProperty(value = "扣除售后商家所得总额基数") |
||||
|
private BigDecimal totalBaseAmount; |
||||
|
@ApiModelProperty(value = "服务费总额") |
||||
|
private BigDecimal totalCommissionAmount; |
||||
|
@ApiModelProperty(value = "实际结算给商家的钱") |
||||
|
private BigDecimal totalSettlementAmount; |
||||
|
@ApiModelProperty(value = "记录数") |
||||
|
private Integer recordCount; |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/summaryList") |
||||
|
@ApiOperation(value = "按区域和日期获取商家结算汇总") |
||||
|
public Result<List<SettlementSummaryVo>> summaryList(@RequestBody SummaryQuery query) { |
||||
|
if (StringUtils.isBlank(query.getRegionId()) || StringUtils.isBlank(query.getSettlementDate())) { |
||||
|
return ResultUtil.error("区域ID或日期不能为空"); |
||||
|
} |
||||
|
|
||||
|
LambdaQueryWrapper<MallSettlementRecord> qw = new LambdaQueryWrapper<>(); |
||||
|
qw.eq(MallSettlementRecord::getRegionId, query.getRegionId()); |
||||
|
qw.eq(MallSettlementRecord::getStatus, 0); |
||||
|
qw.apply("DATE(create_time) = {0}", query.getSettlementDate()); |
||||
|
|
||||
|
List<MallSettlementRecord> list = mallSettlementRecordService.list(qw); |
||||
|
|
||||
|
// Group by shopId
|
||||
|
Map<String, List<MallSettlementRecord>> map = list.stream().collect(Collectors.groupingBy(MallSettlementRecord::getShopId)); |
||||
|
|
||||
|
List<SettlementSummaryVo> result = new ArrayList<>(); |
||||
|
for (Map.Entry<String, List<MallSettlementRecord>> entry : map.entrySet()) { |
||||
|
String shopId = entry.getKey(); |
||||
|
List<MallSettlementRecord> shopRecords = entry.getValue(); |
||||
|
|
||||
|
SettlementSummaryVo vo = new SettlementSummaryVo(); |
||||
|
vo.setShopId(shopId); |
||||
|
|
||||
|
Shop shop = shopService.findById(shopId); |
||||
|
if (shop != null) { |
||||
|
vo.setShopName(shop.getShopName()); |
||||
|
} |
||||
|
|
||||
|
BigDecimal totalBase = BigDecimal.ZERO; |
||||
|
BigDecimal totalComm = BigDecimal.ZERO; |
||||
|
BigDecimal totalSettlement = BigDecimal.ZERO; |
||||
|
|
||||
|
for (MallSettlementRecord r : shopRecords) { |
||||
|
if (r.getBaseAmount() != null) totalBase = totalBase.add(r.getBaseAmount()); |
||||
|
if (r.getCommissionAmount() != null) totalComm = totalComm.add(r.getCommissionAmount()); |
||||
|
if (r.getSettlementAmount() != null) totalSettlement = totalSettlement.add(r.getSettlementAmount()); |
||||
|
} |
||||
|
vo.setTotalBaseAmount(totalBase); |
||||
|
vo.setTotalCommissionAmount(totalComm); |
||||
|
vo.setTotalSettlementAmount(totalSettlement); |
||||
|
vo.setRecordCount(shopRecords.size()); |
||||
|
|
||||
|
result.add(vo); |
||||
|
} |
||||
|
return new ResultUtil<List<SettlementSummaryVo>>().setData(result); |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
public static class ConfirmShopReq { |
||||
|
@ApiModelProperty(value = "选中的商家ID列表") |
||||
|
private List<String> shopIds; |
||||
|
@ApiModelProperty(value = "区域ID") |
||||
|
private String regionId; |
||||
|
@ApiModelProperty(value = "结算日期,格式yyyy-MM-dd") |
||||
|
private String settlementDate; |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/confirmByShop") |
||||
|
@ApiOperation(value = "每日结算确认(按商家)") |
||||
|
public Result confirmByShop(@RequestBody ConfirmShopReq req) { |
||||
|
if (req.getShopIds() == null || req.getShopIds().isEmpty()) { |
||||
|
return ResultUtil.error("请选择要确认的商家"); |
||||
|
} |
||||
|
try { |
||||
|
LambdaQueryWrapper<MallSettlementRecord> qw = new LambdaQueryWrapper<>(); |
||||
|
if (StringUtils.isNotBlank(req.getRegionId())) { |
||||
|
qw.eq(MallSettlementRecord::getRegionId, req.getRegionId()); |
||||
|
} |
||||
|
if (StringUtils.isNotBlank(req.getSettlementDate())) { |
||||
|
qw.apply("DATE(create_time) = {0}", req.getSettlementDate()); |
||||
|
} |
||||
|
qw.in(MallSettlementRecord::getShopId, req.getShopIds()); |
||||
|
qw.eq(MallSettlementRecord::getStatus, 0); |
||||
|
|
||||
|
List<MallSettlementRecord> list = mallSettlementRecordService.list(qw); |
||||
|
if (list == null || list.isEmpty()) { |
||||
|
return ResultUtil.success("没有需要确认的结算记录"); |
||||
|
} |
||||
|
List<String> recordIds = list.stream().map(MallSettlementRecord::getId).collect(Collectors.toList()); |
||||
|
|
||||
|
mallSettlementRecordService.confirmSettlements(recordIds); |
||||
|
|
||||
|
return ResultUtil.success("结算确认成功"); |
||||
|
} catch (Exception e) { |
||||
|
log.error("结算确认失败: {}", e.getMessage(), e); |
||||
|
return ResultUtil.error("结算确认失败: " + e.getMessage()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
package cc.hiver.mall.controller; |
||||
|
|
||||
|
import cc.hiver.core.common.utils.ResultUtil; |
||||
|
import cc.hiver.core.common.vo.Result; |
||||
|
import cc.hiver.mall.entity.MallSettlementRecord; |
||||
|
import cc.hiver.mall.service.mybatis.MallSettlementRecordService; |
||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
import io.swagger.annotations.Api; |
||||
|
import io.swagger.annotations.ApiOperation; |
||||
|
import lombok.Data; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
|
||||
|
@Slf4j |
||||
|
@RestController |
||||
|
@Api(tags = "商家端结算记录查询接口") |
||||
|
@RequestMapping("/hiver/mall/shop/settlement") |
||||
|
public class ShopSettlementController { |
||||
|
|
||||
|
@Autowired |
||||
|
private MallSettlementRecordService mallSettlementRecordService; |
||||
|
|
||||
|
@Data |
||||
|
public static class ShopSettlementQuery { |
||||
|
// Required for safety
|
||||
|
private String shopId; |
||||
|
private Integer status; |
||||
|
private Integer type; |
||||
|
private int pageNumber = 1; |
||||
|
private int pageSize = 10; |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/page") |
||||
|
@ApiOperation(value = "商家分页查询结算记录") |
||||
|
public Result<Page<MallSettlementRecord>> page(@RequestBody ShopSettlementQuery query) { |
||||
|
if (StringUtils.isBlank(query.getShopId())) { |
||||
|
return ResultUtil.error("缺少商家店铺ID参数"); |
||||
|
} |
||||
|
|
||||
|
LambdaQueryWrapper<MallSettlementRecord> qw = new LambdaQueryWrapper<>(); |
||||
|
qw.eq(MallSettlementRecord::getShopId, query.getShopId()); |
||||
|
|
||||
|
if (query.getStatus() != null) { |
||||
|
qw.eq(MallSettlementRecord::getStatus, query.getStatus()); |
||||
|
} |
||||
|
if (query.getType() != null) { |
||||
|
qw.eq(MallSettlementRecord::getType, query.getType()); |
||||
|
} |
||||
|
qw.orderByDesc(MallSettlementRecord::getCreateTime); |
||||
|
|
||||
|
Page<MallSettlementRecord> page = new Page<>(query.getPageNumber(), query.getPageSize()); |
||||
|
Page<MallSettlementRecord> result = mallSettlementRecordService.page(page, qw); |
||||
|
return new ResultUtil<Page<MallSettlementRecord>>().setData(result); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
package cc.hiver.mall.dao.mapper; |
||||
|
|
||||
|
import cc.hiver.mall.entity.MallSettlementRecord; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import org.springframework.stereotype.Repository; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* Mapper 接口 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author hiver |
||||
|
*/ |
||||
|
@Repository |
||||
|
public interface MallSettlementRecordMapper extends BaseMapper<MallSettlementRecord> { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
package cc.hiver.mall.entity; |
||||
|
|
||||
|
import cc.hiver.core.common.utils.SnowFlakeUtil; |
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
import javax.persistence.Entity; |
||||
|
import javax.persistence.Id; |
||||
|
import javax.persistence.Table; |
||||
|
import javax.persistence.Transient; |
||||
|
import java.io.Serializable; |
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
@Data |
||||
|
@Entity |
||||
|
@Table(name = "mall_settlement_record") |
||||
|
@TableName("mall_settlement_record") |
||||
|
@ApiModel(value = "商家账单结算记录") |
||||
|
public class MallSettlementRecord implements Serializable { |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@Id |
||||
|
@TableId |
||||
|
@ApiModelProperty(value = "主键") |
||||
|
private String id = SnowFlakeUtil.nextId().toString(); |
||||
|
|
||||
|
@ApiModelProperty(value = "关联订单ID") |
||||
|
private String orderId; |
||||
|
|
||||
|
@ApiModelProperty(value = "关联店铺ID") |
||||
|
private String shopId; |
||||
|
|
||||
|
@ApiModelProperty(value = "关联学校区域ID") |
||||
|
private String regionId; |
||||
|
|
||||
|
@ApiModelProperty(value = "关联售后记录ID (负数计算使用)") |
||||
|
private String refundId; |
||||
|
|
||||
|
@ApiModelProperty(value = "结算类型 1:正数结算(正常) 2:负数结算(售后)") |
||||
|
private Integer type; |
||||
|
|
||||
|
@ApiModelProperty(value = "基数金额(退款会扣除售后商品金额)") |
||||
|
private BigDecimal baseAmount; |
||||
|
|
||||
|
@ApiModelProperty(value = "抽佣比例") |
||||
|
private BigDecimal commissionRate; |
||||
|
|
||||
|
@ApiModelProperty(value = "平台抽成佣金") |
||||
|
private BigDecimal commissionAmount; |
||||
|
|
||||
|
@ApiModelProperty(value = "商家实际结算金额(可提现金额)") |
||||
|
private BigDecimal settlementAmount; |
||||
|
|
||||
|
@ApiModelProperty(value = "状态 0:待确认 1:已确认结算") |
||||
|
private Integer status; |
||||
|
|
||||
|
@ApiModelProperty(value = "创建时间") |
||||
|
private Date createTime; |
||||
|
|
||||
|
@ApiModelProperty(value = "确认时间") |
||||
|
private Date confirmTime; |
||||
|
|
||||
|
@Transient |
||||
|
@TableField(exist = false) |
||||
|
@ApiModelProperty(value = "订单编号(快照)") |
||||
|
private String orderNumber; |
||||
|
|
||||
|
@Transient |
||||
|
@TableField(exist = false) |
||||
|
@ApiModelProperty(value = "店铺名称(快照)") |
||||
|
private String shopName; |
||||
|
} |
||||
@ -0,0 +1,216 @@ |
|||||
|
package cc.hiver.mall.quartz; |
||||
|
|
||||
|
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 com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.scheduling.annotation.Scheduled; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.Date; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class MerchantSettlementTask { |
||||
|
@Autowired |
||||
|
private MallOrderService mallOrderService; |
||||
|
@Autowired |
||||
|
private MallRefundRecordService mallRefundRecordService; |
||||
|
@Autowired |
||||
|
private MallReturnOrderGoodsMapper mallReturnOrderGoodsMapper; |
||||
|
@Autowired |
||||
|
private MallSettlementRecordService mallSettlementRecordService; |
||||
|
@Autowired |
||||
|
private ShopTakeawayService shopTakeawayService; |
||||
|
@Autowired |
||||
|
private ShopService shopService; |
||||
|
|
||||
|
@Scheduled(cron = "0 0 2 * * ?") |
||||
|
public void executeSettlement() { |
||||
|
log.info("[MerchantSettlementTask] Starting daily settlement calculation..."); |
||||
|
|
||||
|
// 1. Process Normal Unsettled Orders
|
||||
|
try { |
||||
|
LambdaQueryWrapper<MallOrder> qw = new LambdaQueryWrapper<>(); |
||||
|
qw.in(MallOrder::getStatus, 5, 12); |
||||
|
qw.eq(MallOrder::getSettlementStatus, 0); |
||||
|
List<MallOrder> orders = mallOrderService.list(qw); |
||||
|
|
||||
|
for (MallOrder order : orders) { |
||||
|
try { |
||||
|
processUnsettledOrder(order); |
||||
|
} catch (Exception e) { |
||||
|
log.error("[MerchantSettlementTask] Failed to process unsettled order {}", order.getId(), e); |
||||
|
} |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
log.error("[MerchantSettlementTask] Error processing unsettled orders", e); |
||||
|
} |
||||
|
|
||||
|
// 2. Process Already Settled Orders with New Refunds
|
||||
|
try { |
||||
|
LambdaQueryWrapper<MallRefundRecord> refundQw = new LambdaQueryWrapper<>(); |
||||
|
refundQw.eq(MallRefundRecord::getStatus, 4); |
||||
|
List<MallRefundRecord> refundRecords = mallRefundRecordService.list(refundQw); |
||||
|
|
||||
|
for (MallRefundRecord refund : refundRecords) { |
||||
|
try { |
||||
|
processSettledRefund(refund); |
||||
|
} catch (Exception e) { |
||||
|
log.error("[MerchantSettlementTask] Failed to process settled refund {}", refund.getId(), e); |
||||
|
} |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
log.error("[MerchantSettlementTask] Error processing settled refunds", e); |
||||
|
} |
||||
|
|
||||
|
log.info("[MerchantSettlementTask] Daily settlement calculation finished."); |
||||
|
} |
||||
|
|
||||
|
private void processUnsettledOrder(MallOrder order) { |
||||
|
LambdaQueryWrapper<MallSettlementRecord> existsQw = new LambdaQueryWrapper<>(); |
||||
|
existsQw.eq(MallSettlementRecord::getOrderId, order.getId()); |
||||
|
existsQw.eq(MallSettlementRecord::getType, 1); |
||||
|
long count = mallSettlementRecordService.count(existsQw); |
||||
|
if (count > 0) return; // Skip if already pending confirmation
|
||||
|
|
||||
|
LambdaQueryWrapper<ShopTakeaway> shopQw = new LambdaQueryWrapper<>(); |
||||
|
shopQw.eq(ShopTakeaway::getShopId, order.getShopId()); |
||||
|
ShopTakeaway takeaway = shopTakeawayService.getOne(shopQw); |
||||
|
|
||||
|
BigDecimal commissionRate = BigDecimal.ZERO; |
||||
|
if (takeaway != null) { |
||||
|
if (order.getOrderType() != null && order.getOrderType() == 1) { |
||||
|
commissionRate = takeaway.getCommissionRateOne(); |
||||
|
} else if (order.getOrderType() != null && (order.getOrderType() == 2 || order.getOrderType() == 3)) { |
||||
|
commissionRate = takeaway.getCommissionRateMore(); |
||||
|
} |
||||
|
} |
||||
|
if (commissionRate == null) { |
||||
|
commissionRate = BigDecimal.ZERO; |
||||
|
} |
||||
|
|
||||
|
LambdaQueryWrapper<MallRefundRecord> refQw = new LambdaQueryWrapper<>(); |
||||
|
refQw.eq(MallRefundRecord::getOrderId, order.getId()); |
||||
|
refQw.eq(MallRefundRecord::getStatus, 4); |
||||
|
List<MallRefundRecord> refunds = mallRefundRecordService.list(refQw); |
||||
|
|
||||
|
BigDecimal refundAmount = BigDecimal.ZERO; |
||||
|
for (MallRefundRecord r : refunds) { |
||||
|
String linkId = r.getLinkId(); |
||||
|
// 只有商家负责的售后(linkId不为W开头)才需要从商家的结算基准中扣除
|
||||
|
if (linkId == null || !linkId.toUpperCase().startsWith("W")) { |
||||
|
if (r.getRefundAmount() != null) { |
||||
|
refundAmount = refundAmount.add(r.getRefundAmount()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
BigDecimal goodsAmount = order.getGoodsAmount() != null ? order.getGoodsAmount() : BigDecimal.ZERO; |
||||
|
BigDecimal packageFee = order.getPackageFee() != null ? order.getPackageFee() : BigDecimal.ZERO; |
||||
|
|
||||
|
BigDecimal baseAmount = goodsAmount.add(packageFee).subtract(refundAmount); |
||||
|
|
||||
|
BigDecimal actualCommission = BigDecimal.ZERO; |
||||
|
if (baseAmount.compareTo(BigDecimal.ZERO) > 0) { |
||||
|
actualCommission = baseAmount.multiply(commissionRate); |
||||
|
} else { |
||||
|
baseAmount = BigDecimal.ZERO; |
||||
|
} |
||||
|
|
||||
|
BigDecimal settlementAmount = baseAmount.subtract(actualCommission); |
||||
|
|
||||
|
MallSettlementRecord record = new MallSettlementRecord(); |
||||
|
record.setOrderId(order.getId()); |
||||
|
record.setShopId(order.getShopId()); |
||||
|
record.setType(1); |
||||
|
record.setBaseAmount(baseAmount); |
||||
|
record.setCommissionRate(commissionRate); |
||||
|
record.setCommissionAmount(actualCommission); |
||||
|
record.setRegionId(order.getRegionId()); |
||||
|
record.setSettlementAmount(settlementAmount); |
||||
|
record.setStatus(0); |
||||
|
record.setCreateTime(new Date()); |
||||
|
|
||||
|
mallSettlementRecordService.save(record); |
||||
|
} |
||||
|
|
||||
|
private void processSettledRefund(MallRefundRecord refund) { |
||||
|
String linkId = refund.getLinkId(); |
||||
|
// 配送员负责的售后(linkId为W开头),不扣商家的款
|
||||
|
if (linkId != null && linkId.toUpperCase().startsWith("W")) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
MallOrder order = mallOrderService.getById(refund.getOrderId()); |
||||
|
if (order == null || order.getSettlementStatus() == null || order.getSettlementStatus() == 0) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
LambdaQueryWrapper<MallSettlementRecord> existsQw = new LambdaQueryWrapper<>(); |
||||
|
existsQw.eq(MallSettlementRecord::getRefundId, refund.getId()); |
||||
|
existsQw.eq(MallSettlementRecord::getType, 2); |
||||
|
long count = mallSettlementRecordService.count(existsQw); |
||||
|
if (count > 0) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
LambdaQueryWrapper<ShopTakeaway> shopQw = new LambdaQueryWrapper<>(); |
||||
|
shopQw.eq(ShopTakeaway::getShopId, order.getShopId()); |
||||
|
ShopTakeaway takeaway = shopTakeawayService.getOne(shopQw); |
||||
|
|
||||
|
BigDecimal commissionRate = BigDecimal.ZERO; |
||||
|
if (takeaway != null) { |
||||
|
if (order.getOrderType() != null && order.getOrderType() == 1) { |
||||
|
commissionRate = takeaway.getCommissionRateOne(); |
||||
|
} else if (order.getOrderType() != null && (order.getOrderType() == 2 || order.getOrderType() == 3)) { |
||||
|
commissionRate = takeaway.getCommissionRateMore(); |
||||
|
} |
||||
|
} |
||||
|
if (commissionRate == null) { |
||||
|
commissionRate = BigDecimal.ZERO; |
||||
|
} |
||||
|
|
||||
|
LambdaQueryWrapper<MallReturnOrderGoods> goodsQw = new LambdaQueryWrapper<>(); |
||||
|
goodsQw.eq(MallReturnOrderGoods::getOrderId, refund.getId()); |
||||
|
List<MallReturnOrderGoods> returnGoods = mallReturnOrderGoodsMapper.selectList(goodsQw); |
||||
|
|
||||
|
BigDecimal totalReturnPice = BigDecimal.ZERO; |
||||
|
if (returnGoods != null) { |
||||
|
for (MallReturnOrderGoods g : returnGoods) { |
||||
|
BigDecimal price = g.getPrice() != null ? g.getPrice() : BigDecimal.ZERO; |
||||
|
BigDecimal quantity = g.getQuantity() != null ? new BigDecimal(g.getQuantity()) : BigDecimal.ZERO; |
||||
|
totalReturnPice = totalReturnPice.add(price.multiply(quantity)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (totalReturnPice.compareTo(BigDecimal.ZERO) == 0) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
BigDecimal deductionAmount = totalReturnPice.multiply(BigDecimal.ONE.subtract(commissionRate)); |
||||
|
|
||||
|
MallSettlementRecord record = new MallSettlementRecord(); |
||||
|
record.setOrderId(order.getId()); |
||||
|
record.setShopId(order.getShopId()); |
||||
|
record.setRefundId(refund.getId()); |
||||
|
record.setType(2); |
||||
|
record.setRegionId(order.getRegionId()); |
||||
|
record.setBaseAmount(totalReturnPice); |
||||
|
record.setCommissionRate(commissionRate); |
||||
|
record.setCommissionAmount(totalReturnPice.multiply(commissionRate)); |
||||
|
record.setSettlementAmount(deductionAmount.negate()); |
||||
|
record.setStatus(0); |
||||
|
record.setCreateTime(new Date()); |
||||
|
|
||||
|
mallSettlementRecordService.save(record); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package cc.hiver.mall.service.mybatis; |
||||
|
|
||||
|
import cc.hiver.mall.entity.MallRefundRecord; |
||||
|
import cc.hiver.mall.entity.MallSettlementRecord; |
||||
|
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 服务类 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author hiver |
||||
|
*/ |
||||
|
public interface MallSettlementRecordService extends IService<MallSettlementRecord> { |
||||
|
|
||||
|
void confirmSettlements(List<String> ids); |
||||
|
|
||||
|
void processSettledRefund(MallRefundRecord refund); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,138 @@ |
|||||
|
package cc.hiver.mall.serviceimpl.mybatis; |
||||
|
|
||||
|
import cc.hiver.mall.dao.mapper.MallSettlementRecordMapper; |
||||
|
import cc.hiver.mall.entity.MallOrder; |
||||
|
import cc.hiver.mall.entity.MallSettlementRecord; |
||||
|
import cc.hiver.mall.entity.Shop; |
||||
|
import cc.hiver.mall.service.ShopService; |
||||
|
import cc.hiver.mall.service.mybatis.MallOrderService; |
||||
|
import cc.hiver.mall.service.mybatis.MallSettlementRecordService; |
||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.Date; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 服务实现类 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author hiver |
||||
|
*/ |
||||
|
@Service |
||||
|
public class MallSettlementRecordServiceImpl extends ServiceImpl<MallSettlementRecordMapper, MallSettlementRecord> implements MallSettlementRecordService { |
||||
|
|
||||
|
@Autowired |
||||
|
private MallOrderService mallOrderService; |
||||
|
|
||||
|
@Autowired |
||||
|
private ShopService shopService; |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void confirmSettlements(List<String> ids) { |
||||
|
if (ids == null || ids.isEmpty()) return; |
||||
|
List<MallSettlementRecord> records = this.listByIds(ids); |
||||
|
for (MallSettlementRecord record : records) { |
||||
|
if (record.getStatus() != null && record.getStatus() == 1) { |
||||
|
continue; // Already confirmed
|
||||
|
} |
||||
|
|
||||
|
// Confirm it
|
||||
|
record.setStatus(1); |
||||
|
record.setConfirmTime(new Date()); |
||||
|
this.updateById(record); |
||||
|
|
||||
|
// Update MallOrder if it's a normal settlement
|
||||
|
if (record.getType() != null && record.getType() == 1) { |
||||
|
MallOrder order = mallOrderService.getById(record.getOrderId()); |
||||
|
if (order != null) { |
||||
|
order.setSettlementStatus(1); |
||||
|
mallOrderService.updateById(order); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Update Shop balance
|
||||
|
Shop shop = shopService.findById(record.getShopId()); |
||||
|
if (shop != null && record.getSettlementAmount() != null) { |
||||
|
BigDecimal balance = shop.getBalance() != null ? shop.getBalance() : BigDecimal.ZERO; |
||||
|
shop.setBalance(balance.add(record.getSettlementAmount())); |
||||
|
shopService.update(shop); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Autowired |
||||
|
private cc.hiver.mall.service.ShopTakeawayService shopTakeawayService; |
||||
|
@Autowired |
||||
|
private cc.hiver.mall.dao.mapper.MallReturnOrderGoodsMapper mallReturnOrderGoodsMapper; |
||||
|
|
||||
|
@Override |
||||
|
public void processSettledRefund(cc.hiver.mall.entity.MallRefundRecord refund) { |
||||
|
MallOrder order = mallOrderService.getById(refund.getOrderId()); |
||||
|
if (order == null || order.getSettlementStatus() == null || order.getSettlementStatus() == 0) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MallSettlementRecord> existsQw = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>(); |
||||
|
existsQw.eq(MallSettlementRecord::getRefundId, refund.getId()); |
||||
|
existsQw.eq(MallSettlementRecord::getType, 2); |
||||
|
long count = this.count(existsQw); |
||||
|
if (count > 0) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<cc.hiver.mall.entity.ShopTakeaway> shopQw = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>(); |
||||
|
shopQw.eq(cc.hiver.mall.entity.ShopTakeaway::getShopId, order.getShopId()); |
||||
|
cc.hiver.mall.entity.ShopTakeaway takeaway = shopTakeawayService.getOne(shopQw); |
||||
|
|
||||
|
BigDecimal commissionRate = BigDecimal.ZERO; |
||||
|
if (takeaway != null) { |
||||
|
if (order.getOrderType() != null && order.getOrderType() == 1) { |
||||
|
commissionRate = takeaway.getCommissionRateOne(); |
||||
|
} else if (order.getOrderType() != null && (order.getOrderType() == 2 || order.getOrderType() == 3)) { |
||||
|
commissionRate = takeaway.getCommissionRateMore(); |
||||
|
} |
||||
|
} |
||||
|
if (commissionRate == null) { |
||||
|
commissionRate = BigDecimal.ZERO; |
||||
|
} |
||||
|
|
||||
|
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<cc.hiver.mall.entity.MallReturnOrderGoods> goodsQw = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>(); |
||||
|
goodsQw.eq(cc.hiver.mall.entity.MallReturnOrderGoods::getOrderId, refund.getId()); |
||||
|
List<cc.hiver.mall.entity.MallReturnOrderGoods> returnGoods = mallReturnOrderGoodsMapper.selectList(goodsQw); |
||||
|
|
||||
|
BigDecimal totalReturnPice = BigDecimal.ZERO; |
||||
|
if (returnGoods != null) { |
||||
|
for (cc.hiver.mall.entity.MallReturnOrderGoods g : returnGoods) { |
||||
|
BigDecimal price = g.getPrice() != null ? g.getPrice() : BigDecimal.ZERO; |
||||
|
BigDecimal quantity = g.getQuantity() != null ? new BigDecimal(g.getQuantity()) : BigDecimal.ZERO; |
||||
|
totalReturnPice = totalReturnPice.add(price.multiply(quantity)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (totalReturnPice.compareTo(BigDecimal.ZERO) == 0) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
BigDecimal deductionAmount = totalReturnPice.multiply(BigDecimal.ONE.subtract(commissionRate)); |
||||
|
|
||||
|
MallSettlementRecord record = new MallSettlementRecord(); |
||||
|
record.setOrderId(order.getId()); |
||||
|
record.setShopId(order.getShopId()); |
||||
|
record.setRefundId(refund.getId()); |
||||
|
record.setType(2); |
||||
|
record.setBaseAmount(totalReturnPice); |
||||
|
record.setCommissionRate(commissionRate); |
||||
|
record.setCommissionAmount(totalReturnPice.multiply(commissionRate)); |
||||
|
record.setSettlementAmount(deductionAmount.negate()); |
||||
|
record.setStatus(0); |
||||
|
record.setCreateTime(new Date()); |
||||
|
|
||||
|
this.save(record); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue