refactor(order): 重构订单模块并添加退款功能

- 重构了订单相关实体类和VO类,优化了字段设计
- 新增了AftersaleStatus枚举类,用于表示售后状态- 在OrderService中实现了退款功能
- 优化了订单查询相关SQL和Mapper方法
-调整了分账相关逻辑,增加了回退状态
This commit is contained in:
huk 2025-09-12 13:22:26 +08:00
parent 76342416ad
commit cb05c45909
29 changed files with 574 additions and 259 deletions

View File

@ -150,11 +150,5 @@ public class OrderController extends BaseController {
}
}
@Tag(name = "订单退款")
@GetMapping("/refund")
public R refund(@RequestParam Long orderId, @RequestParam(required = false) String reason) {
log.info("订单退款开始订单ID: {}", orderId);
return orderService.refund(orderId,reason);
}
}

View File

@ -38,11 +38,13 @@ public class OrderBo extends BaseBO<Order> {
@Schema(description ="支付方式0->未支付1->支付宝2->微信 精确匹配")
private Integer payType;
@Schema(description ="订单状态0->待付款1->待核销2->已完成3->已关闭4->退款中5->已退款 6->已取消 9->已分账10->无效订单 精确匹配")
/**
* 订单状态枚举类型见{@link com.wzj.soopin.order.emum.OrderStatusEnum}
*/
private Integer status;
@Schema(description ="退款状态枚举值1无售后或售后关闭2售后处理中3退款中4 退款成功 精确匹配")
private Integer aftersaleStatus;
@Schema(description="是否处于退款状态")
private Boolean aftersale;
@Schema(description ="省份/直辖市 精确匹配")
@ -72,10 +74,6 @@ public class OrderBo extends BaseBO<Order> {
@Schema(description ="商家备注 精确匹配")
private String MEMBER_ID;
@Schema(description ="删除状态0->未删除1->已删除 精确匹配")
private Integer deleteStatus;
@Schema(description ="支付时间 精确匹配")
private LocalDateTime paymentTime;
@ -102,8 +100,7 @@ public class OrderBo extends BaseBO<Order> {
.eq(tenantId != null, Order::getTenantId, tenantId)
.eq(payType != null, Order::getPayType, payType)
.eq(status != null, Order::getStatus, status)
.eq(aftersaleStatus != null, Order::getAftersaleStatus, aftersaleStatus)
.eq(deleteStatus != null, Order::getDeleteStatus, deleteStatus)
.eq(aftersale != null, Order::getAftersale, aftersale)
.between(startTime != null && endTime != null, Order::getCreateTime, startTime, endTime)
.ge(startTime != null && endTime == null, Order::getCreateTime, startTime)
.le(endTime != null && startTime == null, Order::getCreateTime, endTime);

View File

@ -0,0 +1,79 @@
package com.wzj.soopin.order.domain.bo;
import com.wzj.soopin.order.domain.entity.Order;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.common.excel.annotation.Excel;
import java.math.BigDecimal;
import java.util.List;
/**
* 退款请求
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class RefundBO {
/**
* 订单ID
*/
@NotNull(message = "订单ID不能为空")
private Long orderId;
/**
* 退款项
*/
@NotNull(message = "退款项不能为空")
@Valid
private List<RefundItemBO> refundItemBoList;
/**
* 退款原因
*/
private String refundReason;
/**
* 订单信息
*/
@Schema(description = "订单信息", hidden = true)
private Order order;
/**
* 退款总金额
*/
@Schema(description = "退款总金额", hidden = true)
private BigDecimal totalRefundAmount;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static class RefundItemBO {
/**
* 订单子项ID
*/
@NotNull(message = "订单子项ID不能为空")
private Long orderItemId;
/**
* 退款金额单位
*/
@NotNull(message = "退款金额不能为空")
private BigDecimal refundAmount;
/**
* 退货数量
*/
@NotNull(message = "退货数量不能为空")
private Integer quantity;
}
}

View File

@ -2,10 +2,14 @@ package com.wzj.soopin.order.domain.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.domain.model.BaseAudit;
import org.dromara.common.excel.annotation.Excel;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
@ -13,10 +17,13 @@ import java.math.BigDecimal;
*
* @author zcc
*/
@EqualsAndHashCode(callSuper = true)
@Schema(description = "订单售后对象")
@Data
@TableName("oms_aftersale_item")
public class AftersaleItem extends BaseAudit {
@Builder
public class AftersaleItem extends BaseAudit implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "ID")

View File

@ -2,13 +2,12 @@ package com.wzj.soopin.order.domain.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.*;
import org.dromara.common.core.domain.model.BaseAudit;
import org.dromara.common.excel.annotation.Excel;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@ -17,14 +16,16 @@ import java.time.LocalDateTime;
*
* @author zcc
*/
@EqualsAndHashCode(callSuper = true)
@Schema(description = "订单表对象")
@Data
@TableName("oms_order")
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order extends BaseAudit {
public class Order extends BaseAudit implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "订单id")
@ -76,9 +77,11 @@ public class Order extends BaseAudit {
*/
private Integer status;
@Schema(description = "退款状态枚举值1无售后或售后关闭2售后处理中3退款中4 退款成功")
@Excel(name = "退款状态枚举值1无售后或售后关闭2售后处理中3退款中4 退款成功")
private Integer aftersaleStatus;
/**
* 是否处于退款状态
*/
@Schema(description="是否处于退款状态")
private Boolean aftersale;
@ -118,10 +121,6 @@ public class Order extends BaseAudit {
@Excel(name = "订单备注")
private String note;
@Schema(description = "删除状态0->未删除1->已删除")
@Excel(name = "删除状态0->未删除1->已删除")
private Integer deleteStatus;
@Schema(description = "支付时间")
@Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime paymentTime;

View File

@ -35,11 +35,13 @@ public class OrderQuery {
@Schema(description = "支付方式0->未支付1->支付宝2->微信 精确匹配")
private Integer payType;
@Schema(description = "订单状态0->待付款1->待发货2->已发货3->已完成4->已关闭5->无效订单 精确匹配")
/**
* 订单状态枚举类型见{@link com.wzj.soopin.order.emum.OrderStatusEnum}
*/
private Integer status;
@Schema(description = "退款状态枚举值1无售后或售后关闭2售后处理中3退款中4 退款成功 精确匹配")
private Integer aftersaleStatus;
@Schema(description="是否处于退款状态")
private Boolean aftersale;
@Schema(description = "物流公司 精确匹配")
private String deliveryCompany;
@ -86,9 +88,6 @@ public class OrderQuery {
@Schema(description = "确认收货状态0->未确认1->已确认 精确匹配")
private Integer confirmStatus;
@Schema(description = "删除状态0->未删除1->已删除 精确匹配")
private Integer deleteStatus;
@Schema(description = "支付时间 精确匹配")
private LocalDateTime paymentTime;

View File

@ -38,8 +38,8 @@ public class ManagerOrderVO {
@Schema(name = "status", description = "订单状态0->待付款1->待发货2->已发货3->已完成4->已关闭5->无效订单", required = true, implementation = Integer.class)
private Integer status;
@Schema(description = "退款状态枚举值1无售后或售后关闭2售后处理中3退款中4 退款成功")
private Integer aftersaleStatus;
@Schema(description="是否处于退款状态")
private Boolean aftersale;
@Schema(name = "pic", description = "商品图片", required = true, implementation = String.class)
private String pic;
@ -119,8 +119,8 @@ public class ManagerOrderVO {
if (status != null) {
queryWrapper.eq(Order::getStatus, status);
}
if (aftersaleStatus != null) {
queryWrapper.eq(Order::getAftersaleStatus, aftersaleStatus);
if (aftersale != null) {
queryWrapper.eq(Order::getAftersale, aftersale);
}
if (totalAmount != null) {
queryWrapper.eq(Order::getTotalAmount, totalAmount);

View File

@ -61,13 +61,13 @@ public class OrderVO extends BaseAudit {
@Excel(name = "支付方式0->未支付1->支付宝2->微信")
private Integer payType;
@Schema(description ="订单状态0->待付款1->待发货2->已发货3->已完成4->已关闭5->无效订单")
@Excel(name = "订单状态0->待付款1->待发货2->已发货3->已完成4->已关闭5->无效订单")
/**
* 订单状态枚举类型见{@link com.wzj.soopin.order.emum.OrderStatusEnum}
*/
private Integer status;
@Schema(description ="退款状态枚举值1无售后或售后关闭2售后处理中3退款中4 退款成功")
@Excel(name = "退款状态枚举值1无售后或售后关闭2售后处理中3退款中4 退款成功")
private Integer aftersaleStatus;
@Schema(description="是否处于退款状态")
private Boolean aftersale;
@Schema(description ="省份/直辖市")
@ -102,11 +102,6 @@ public class OrderVO extends BaseAudit {
@Excel(name = "订单备注")
private String note;
@Schema(description ="发删除状态0->未删除1->已删除")
@Excel(name = "删除状态0->未删除1->已删除")
private Integer deleteStatus;
@Schema(description ="支付时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")

View File

@ -0,0 +1,22 @@
package com.wzj.soopin.order.emum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 售后状态枚举
* 0->待处理1->退款中2->已退款3->已拒绝
*/
@Getter
@AllArgsConstructor
public enum AftersaleStatus {
UNPAID(0, "待处理"),
VERIFY(1, "退款中"),
PAID(2, "已退款"),
CLOSED(3, "已拒绝");
private final Integer code;
private final String desc;
}

View File

@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 订单状态0->待付款1->待核销2->完成3->已关闭4->退款中5->已退款 6->已取消 9->已分账10->无效订单 精确匹配
* 订单状态0->待付款1->待核销2->支付3->已关闭4->退款中5->已退款 6->已取消 9->已分账10->无效订单 精确匹配
*/
@Getter
@AllArgsConstructor

View File

@ -39,7 +39,7 @@ public interface OrderMapper extends BaseMapper<Order> {
@Select("SELECT " +
"COUNT(*) AS count1, " +
"SUM(CASE WHEN aftersale_status = 2 THEN 1 ELSE 0 END) AS count2 " +
"SUM(aftersale = 1) AS count2 " +
"FROM oms_order")
Map<String, Object> countOrder();

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.wzj.soopin.order.domain.bo.OrderBo;
import com.wzj.soopin.order.domain.bo.RefundBO;
import com.wzj.soopin.order.domain.bo.SaveOrderBO;
import com.wzj.soopin.order.domain.entity.Order;
import com.wzj.soopin.order.domain.vo.ManagerOrderDetailVO;
@ -32,7 +33,5 @@ public interface OrderService extends IService<Order> {
void cancel(Long orderId);
R refund(Long orderId, String reason);
void refund(RefundBO refundBO);
}

View File

@ -12,6 +12,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzj.soopin.goods.domain.entity.Product;
@ -22,6 +23,7 @@ import com.wzj.soopin.member.domain.po.Member;
import com.wzj.soopin.member.mapper.MemberMapper;
import com.wzj.soopin.member.mapper.MemberWechatMapper;
import com.wzj.soopin.order.domain.bo.OrderBo;
import com.wzj.soopin.order.domain.bo.RefundBO;
import com.wzj.soopin.order.domain.bo.SaveOrderBO;
import com.wzj.soopin.order.domain.bo.SaveOrderSkuItemBO;
import com.wzj.soopin.order.domain.entity.*;
@ -29,6 +31,7 @@ import com.wzj.soopin.order.domain.form.DeliverProductForm;
import com.wzj.soopin.order.domain.form.ManagerOrderQueryForm;
import com.wzj.soopin.order.domain.query.OrderH5Query;
import com.wzj.soopin.order.domain.vo.*;
import com.wzj.soopin.order.emum.AftersaleStatus;
import com.wzj.soopin.order.emum.OrderStatusEnum;
import com.wzj.soopin.order.mapper.*;
import com.wzj.soopin.order.service.OrderService;
@ -48,6 +51,7 @@ import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@ -58,7 +62,6 @@ import java.util.stream.Collectors;
/**
* 订单表Service业务层处理
*
*
* @author zcc
*/
@Service
@ -66,21 +69,30 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
private final OrderMapper orderMapper;
private final OrderItemMapper orderItemMapper;
private final SkuMapper skuMapper;
private final ProductMapper productMapper;
private final OrderOperateHistoryMapper orderOperateHistoryMapper;
private final MemberMapper memberMapper;
private final OrderDeliveryHistoryMapper orderDeliveryHistoryMapper;
private final SysTenantMapper sysTenantMapper;
private final AftersaleMapper aftersaleMapper;
private final AftersaleItemMapper aftersaleItemMapper;
/**
* 订单前缀
*/
private final static String ORDER_SN_PREFIX = "WZJ";
private final ProductMapper productMapper;
/**
* 查询订单表
@ -95,7 +107,6 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
Assert.notNull(order, () -> new ServiceException("无该订单信息"));
ManagerOrderDetailVO managerOrderDetailVO = new ManagerOrderDetailVO();
//封装订单信息
managerOrderDetailVO.setOrderId(id);
@ -157,7 +168,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
* 查询订单表列表
*
* @param query 查询条件
* @param page 分页条件
* @param page 分页条件
* @return 订单表
*/
public PageImpl<ManagerOrderVO> selectList(ManagerOrderQueryForm query, Pageable page) {
@ -168,16 +179,16 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
//// query.setUserPhone(AesCryptoUtils.encrypt(aesKey, query.getUserPhone()));
// }
List<ManagerOrderVO> managerOrderVOList = orderMapper.selectManagerOrderPage(query);
if (CollectionUtil.isEmpty(managerOrderVOList)){
if (CollectionUtil.isEmpty(managerOrderVOList)) {
return new PageImpl<>(managerOrderVOList, page, 0);
}
long total = managerOrderVOList.size();
Map<Long, ManagerOrderVO> orderMap = managerOrderVOList.stream().collect(Collectors.toMap(ManagerOrderVO::getId, it -> it, (v1,v2) -> v2, LinkedHashMap::new));
long total = managerOrderVOList.size();
Map<Long, ManagerOrderVO> orderMap = managerOrderVOList.stream().collect(Collectors.toMap(ManagerOrderVO::getId, it -> it, (v1, v2) -> v2, LinkedHashMap::new));
//查orderItem
QueryWrapper<OrderItem> qw = new QueryWrapper<>();
qw.in("order_id", orderMap.keySet());
Map<Long, List<OrderItem>> groupedOrderItemMap = orderItemMapper.selectList(qw)
.stream().collect(Collectors.groupingBy(OrderItem::getOrderId));
.stream().collect(Collectors.groupingBy(OrderItem::getOrderId));
groupedOrderItemMap.keySet().forEach(key -> {
ManagerOrderVO managerOrderVO = orderMap.get(key);
managerOrderVO.setBuyNum(0);
@ -206,6 +217,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public OrderVO saveOrder(SaveOrderBO saveOrderBO) {
LoginUser loginUser = LoginHelper.getLoginUser();
List<SaveOrderSkuItemBO> skuItemBOList = saveOrderBO.getSkuItemBOList();
@ -217,6 +229,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
.stream().collect(Collectors.toMap(Product::getId, it -> it, (v1, v2) -> v2, LinkedHashMap::new));
for (Sku sku : skus) {
Integer quantity = map.get(sku.getId());
Assert.isTrue(quantity < sku.getStock(), () -> new ServiceException("下单商品库存不足"));
skuMapper.updateStockById(sku.getId(), LocalDateTime.now(), quantity);
BigDecimal multiply = sku.getPrice().multiply(new BigDecimal(quantity));
totalAmount = totalAmount.add(multiply);
}
@ -240,7 +254,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
.skuId(sku.getId())
.productName(product != null ? product.getName() : "")
.pic(sku.getPic())
.salePrice(product != null ? product.getPrice() : null)
.salePrice(sku.getPrice())
.quantity(map.get(sku.getId()))
.build();
}).toList();
@ -257,6 +271,52 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
orderMapper.updateById(Order.builder().id(orderId).status(OrderStatusEnum.CLOSED.getValue()).build());
}
@Override
public void refund(RefundBO refundBO) {
Long orderId = refundBO.getOrderId();
String refundReason = refundBO.getRefundReason();
List<RefundBO.RefundItemBO> refundItemBoList = refundBO.getRefundItemBoList();
Order order = refundBO.getOrder();
// 修改订单状态
orderMapper.updateById(Order.builder()
.id(orderId)
.aftersale(false)
.payAmount(order.getPayAmount().subtract(refundBO.getTotalRefundAmount()))
.status(OrderStatusEnum.REFUNDED_SUCCESS.getValue())
.build());
// 更新售后记录
Aftersale aftersale = aftersaleMapper.selectOne(Wrappers.lambdaQuery(Aftersale.class)
.eq(Aftersale::getOrderId, orderId).last("limit 1"));
if(aftersale == null){
aftersale = new Aftersale();
aftersale.setMemberId(order.getMemberId());
aftersale.setOrderId(orderId);
aftersale.setReturnAmount(refundBO.getTotalRefundAmount());
aftersale.setQuantity(refundItemBoList.size());
aftersale.setType(1);
aftersale.setStatus(AftersaleStatus.PAID.getCode());
aftersale.setReason(refundReason);
aftersale.setAuthFlag(2);
aftersaleMapper.insert(aftersale);
}else{
aftersale.setReturnAmount(refundBO.getTotalRefundAmount());
aftersale.setStatus(AftersaleStatus.PAID.getCode());
aftersale.setQuantity(refundItemBoList.size());
aftersaleMapper.updateById(aftersale);
}
Long aftersaleId = aftersale.getId();
List<AftersaleItem> aftersaleItems = refundItemBoList.stream().map(item -> AftersaleItem.builder()
.aftersaleId(aftersaleId)
.memberId(order.getMemberId())
.orderId(orderId)
.orderItemId(item.getOrderItemId())
.returnAmount(item.getRefundAmount())
.quantity(item.getQuantity())
.build()).toList();
aftersaleItemMapper.insert(aftersaleItems);
}
/**
* 生成订单sn
*/
@ -275,17 +335,15 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
* @return 结果
*/
public R<Order> update(Order order) {
int update = orderMapper.updateById(order);
if (update>1){
int update = orderMapper.updateById(order);
if (update > 1) {
return R.fail("订单修改失败");
}else {
} else {
return R.ok(order);
}
}
@Override
public R saveMerchantNote(Order order) {
// 1. 查询现有订单
@ -301,7 +359,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
int affectedRows = orderMapper.update(null, qw);
if (affectedRows > 0) {
orderMapper.selectById(order.getId());
orderMapper.selectById(order.getId());
return R.ok();
}
return R.fail("更新失败");
@ -357,7 +415,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
public IPage<OrderVO> getlist(Page<Order> page, OrderBo query) {
Long tenantId = Long.valueOf(LoginHelper.getTenantId());
IPage<OrderVO> resultPage = orderMapper.getlist(page,query,tenantId);
IPage<OrderVO> resultPage = orderMapper.getlist(page, query, tenantId);
List<OrderVO> orderVOList = resultPage.getRecords();
if (orderVOList.isEmpty()) {
return resultPage;
@ -415,7 +473,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
@Override
public Order getByNo(String orderNo) {
return baseMapper.selectOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn,orderNo));
return baseMapper.selectOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, orderNo));
}
// public void sendMessage(Order order) {
@ -440,49 +498,5 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
// }
// }
/**
* 处理退款逻辑
* @param orderId 订单ID
* @return 操作结果
*/
@Override
public R refund(Long orderId, String reason) {
if (orderId == null) {
return R.fail("订单ID不能为空");
}
try {
// 根据订单ID获取订单信息
Order order = orderMapper.selectById(orderId);
if (order == null) {
return R.fail("未找到对应订单");
}
if (order.getAftersaleStatus() !=1){
return R.fail("订单未处于待售后状态");
}
// // 设置售后状态
order.setAftersaleStatus(2);
orderMapper.updateById(order);
// 创建售后记录
Aftersale aftersale = new Aftersale();
aftersale.setMemberId(order.getMemberId());
aftersale.setOrderId(orderId);
aftersale.setReturnAmount(order.getPayAmount());
aftersale.setQuantity(1);
aftersale.setType(1);
aftersale.setStatus(0);
aftersale.setReason(reason);
aftersale.setAuthFlag(0);
aftersaleMapper.insert(aftersale);
return R.ok("退款申请已提交",aftersale);
} catch (Exception e) {
log.error("处理退款时出错订单ID: " + orderId, e);
return R.fail("退款处理失败: " + e.getMessage());
}
}
}

View File

@ -13,7 +13,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="payAmount" column="pay_amount"/>
<result property="payType" column="pay_type"/>
<result property="status" column="status"/>
<result property="aftersaleStatus" column="aftersale_status"/>
<result property="aftersale" column="aftersale"/>
<result property="deliveryCompany" column="delivery_company"/>
<result property="deliverySn" column="delivery_sn"/>
<result property="autoConfirmDay" column="auto_confirm_day"/>
@ -29,7 +29,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="receiverDetailAddress" column="receiver_detail_address"/>
<result property="note" column="note"/>
<result property="confirmStatus" column="confirm_status"/>
<result property="deleteStatus" column="delete_status"/>
<result property="paymentTime" column="payment_time"/>
<result property="deliveryTime" column="delivery_time"/>
<result property="receiveTime" column="receive_time"/>
@ -44,7 +43,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectOrderVo">
select id, member_id, member_username, total_amount, purchase_price, pay_amount, freight_amount, pay_type, status, aftersale_status, delivery_company, delivery_sn, auto_confirm_day, receiver_name, receiver_phone, receiver_post_code, receiver_province, receiver_city, receiver_district, receiver_province_id, receiver_city_id, receiver_district_id, receiver_detail_address, note, confirm_status, delete_status, payment_time, delivery_time, receive_time, create_by, create_time, update_by, update_time from oms_order
select id, member_id, member_username, total_amount, purchase_price, pay_amount, freight_amount, pay_type, status, aftersale, delivery_company, delivery_sn, auto_confirm_day, receiver_name, receiver_phone, receiver_post_code, receiver_province, receiver_city, receiver_district, receiver_province_id, receiver_city_id, receiver_district_id, receiver_detail_address, note, confirm_status, payment_time, delivery_time, receive_time, create_by, create_time, update_by, update_time from oms_order
</sql>
<update id="cancelBatch">
<foreach collection="list" item="item" index="index" open="" close="" separator=";">
@ -94,7 +93,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select
a.id,
a.order_sn orderSn,
a.aftersale_status aftersaleStatus,
a.aftersale aftersale,
a.status,
a.member_username userName,
a.total_amount totalAmount,
@ -125,7 +124,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
from oms_order a
-- left join oms_order_item b on a.id = b.order_id
left join ums_member c on a.member_id = c.id
where a.aftersale_status = 1
where a.aftersale = 1
<if test="orderSn != null and orderSn != ''">
and a.order_sn = #{orderSn}
</if>
@ -165,7 +164,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
total_amount,
pay_amount,
status,
aftersale_status,
aftersale,
note,
delivery_sn,
coupon_amount,
@ -186,10 +185,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</if>
<if test="status>-1 and status&lt;3">
and status=#{status}
and aftersale_status=1
</if>
<if test="status==-2">
and aftersale_status in (2, 3)
</if>
order by create_time desc
</select>
@ -203,7 +198,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
pay_amount,
coupon_amount,
status,
aftersale_status,
aftersale,
note,
delivery_sn,
create_time,
@ -216,15 +211,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
receiver_detail_address
from oms_order
where
delete_status=0
and id=#{orderId}
id=#{orderId}
</select>
<select id="countByStatusAndMemberId" resultType="com.wzj.soopin.order.domain.vo.CountOrderVO">
select
IFNULL(sum(case when status=0 and aftersale_status=1 then 1 else 0 end),0) unpaid,
IFNULL(sum(case when status=1 and aftersale_status=1 then 1 else 0 end),0) nosend,
IFNULL(sum(case when status=2 and aftersale_status=1 then 1 else 0 end),0) noget,
IFNULL(sum(case when aftersale_status in (2,3) then 1 else 0 end),0) aftersale
IFNULL(sum(case when status=0 and aftersale = 1 then 1 else 0 end),0) unpaid,
IFNULL(sum(case when status=1 and aftersale=1 then 1 else 0 end),0) nosend,
IFNULL(sum(case when status=2 and aftersale=1 then 1 else 0 end),0) noget,
IFNULL(sum(case when aftersale in (2,3) then 1 else 0 end),0) aftersale
from oms_order
where member_id=#{memberId} and delete_status=0
</select>
@ -233,7 +227,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
IFNULL(count(id), 0) orderCount,
IFNULL(sum(pay_amount), 0) orderAmount
from oms_order
where status in (1,2,3) and aftersale_status=1 and member_id=#{memberId}
where status in (1,2,3) and aftersale = 1 and member_id=#{memberId}
</select>
<select id="statWaitDelivered" resultType="java.lang.Integer">
select IFNULL(count(id), 0) from oms_order where status=1 and aftersale_status=1
@ -311,8 +305,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.withdrawStatus != null and query.withdrawStatus != ''">
AND o.withdraw_status LIKE CONCAT('%', #{query.withdrawStatus}, '%')
</if>
<if test="query.aftersaleStatus != null and query.aftersaleStatus != ''">
AND o.aftersale_status LIKE CONCAT('%', #{query.aftersaleStatus}, '%')
<if test="query.aftersale != null and query.aftersale != ''">
AND o.aftersale LIKE CONCAT('%', #{query.aftersale}, '%')
</if>
<if test="query.startTime != null">
AND o.create_time >= #{query.startTime, jdbcType=TIMESTAMP}

View File

@ -277,7 +277,7 @@ public class SysTenantServiceImpl implements ISysTenantService {
SysUser user = new SysUser();
user.setTenantId(tenantId);
user.setUserName(bo.getUsername());
user.setNickName(bo.getUsername());
user.setNickName(bo.getContactUserName());
user.setPassword(BCrypt.hashpw(bo.getPassword()));
user.setDeptId(deptId);
user.setPhonenumber(bo.getContactPhone());

View File

@ -1,11 +1,11 @@
package com.wzj.soopin.transaction.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.annotation.SaMode;
import com.wzj.soopin.transaction.domain.bo.MerchantAddBO;
import com.wzj.soopin.transaction.domain.bo.PaymentBO;
import com.wzj.soopin.order.domain.bo.RefundBO;
import com.wzj.soopin.transaction.domain.bo.easypay.EasyPayRequest;
import com.wzj.soopin.transaction.domain.vo.EasypayTransResultVO;
import com.wzj.soopin.transaction.domain.vo.EasypayPrePayVO;
@ -80,13 +80,13 @@ public class TransEasypayController {
/**
* 实时退款
*
* @param orderId 订单id
* @param refundBO 退款信息
*/
@Log(title = "易生支付-实时退款", businessType = BusinessType.OTHER)
@PostMapping("/refund/{orderId}")
@SaCheckPermission(value = "trans:easypay:refund",mode = SaMode.OR, orRole = TenantConstants.TENANT_ADMIN_ROLE_KEY)
public R refund(@PathVariable("orderId") Long orderId) throws ServerException {
easypayService.refund(orderId);
public R refund(@RequestBody RefundBO refundBO) throws ServerException {
easypayService.refund(refundBO);
return R.ok();
}

View File

@ -8,6 +8,7 @@ import lombok.Data;
import org.dromara.common.core.domain.BaseBO;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 分账规则明细
@ -66,22 +67,28 @@ public class DivideDetailBO extends BaseBO<DivideDetail> {
*/
@Schema(description = "金额分配比例")
@ExcelProperty(value = "金额分配比例", order = 5)
private BigDecimal moneyPercent;
/**
* 手续费分配比例
*/
@Schema(description = "手续费分配比例")
@ExcelProperty(value = "手续费分配比例", order = 5)
private BigDecimal feePercent;
/**
* 账户类型 1 平台 2 商户 3 代理人
* */
/**
* 账户类型
* */
private Integer type;
/**
* 分账完成时间
*/
@Schema(description = "分账完成时间")
@ExcelProperty(value = "分账完成时间", order = 5)
private LocalDateTime separateTime;
@Schema(description = "账户id")
@ExcelProperty(value = "账户id", order = 6)
private Long accountId;

View File

@ -0,0 +1,42 @@
package com.wzj.soopin.transaction.domain.bo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 分账回退参数
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SeparateRefundBO {
/**
* 支付方式
*/
private String payType;
/**
* 商户订单号商户生成请求易生的流水号
*/
private String orgTrace;
/**
* 原分账总单流水
*/
private String oriSeparateBatchTrace;
/**
* 原分账子单流水号
*/
private String oriSeparateTrade;
/**
* 原业务日期
*/
private String oriTransDate;
/**
* 回退金额
*/
private long refundAmount;
}

View File

@ -27,8 +27,7 @@ public class PayInfo {
*/
private String bizType;
/**
* 支付方式
* @See com.wzj.soopin.transaction.enums.easypay.PayType
* 支付方式 {@link com.wzj.soopin.transaction.enums.easypay.PayType}
*/
private String payType;
/**

View File

@ -0,0 +1,57 @@
package com.wzj.soopin.transaction.domain.bo.easypay.separate.refund.req;
import com.wzj.soopin.transaction.domain.bo.easypay.PayInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.ReqInfo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 分账回退请求参数
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SeparateRefundReqBody {
/**
* 支付信息
*/
private PayInfo payInfo;
/**
* 请求方信息
*/
private ReqInfo reqInfo;
/**
* 基础订单信息
*/
private ReqOrderInfo reqOrderInfo;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static class ReqOrderInfo {
/**
* 商户订单号商户生成请求易生的流水号
*/
private String orgTrace;
/**
* 原分账总单流水
*/
private String oriSeparateBatchTrace;
/**
* 原分账子单流水号
*/
private String oriSeparateTrade;
/**
* 原业务日期
*/
private String oriTransDate;
/**
* 回退金额
*/
private long refundAmount;
}
}

View File

@ -0,0 +1,42 @@
package com.wzj.soopin.transaction.domain.bo.easypay.separate.refund.resp;
import com.wzj.soopin.transaction.domain.bo.easypay.RespStateInfo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 分账回退响应参数
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SeparateRefundRespBody {
/**
* 基础订单信息
*/
private RespOrderInfo respOrderInfo;
/**
* 返回码信息
*/
private RespStateInfo respStateInfo;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static class RespOrderInfo {
/**
* 商户订单号
*/
private String orgTrace;
/**
* 原商户订单号
*/
private String oriOrgTrace;
}
}

View File

@ -1,57 +0,0 @@
package com.wzj.soopin.transaction.domain.bo.easypay.trade.notice.req;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.req.Promo;
import com.wzj.soopin.transaction.domain.bo.easypay.RespStateInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.resp.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 交易结果通知请求体
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TradeNoticeReqBody {
/**
* ali业务参数
*/
private AliQueryRespParamInfoVO aliRespParamInfo;
/**
* toC营销信息
*/
private List<MarketingParamInfo> marketingRespParamInfoList;
/**
* toB营销信息
*/
private List<Promo> promos;
/**
* 银联业务参数
*/
private QrRespParamInfoVO qrRespParamInfo;
/**
* 基础订单信息
*/
private TradeQueryRespOrderInfo respOrderInfo;
/**
* 返回码信息规则详见F.1
*/
private RespStateInfo respStateInfo;
/**
* 风险控制信息
*/
private RiskInfo riskParamInfo;
/**
* 清算信息
*/
private SettleRespParamInfo settleRespParamInfo;
/**
* wx业务参数
*/
private WxQueryRespParamInfoVO wxRespParamInfo;
}

View File

@ -9,9 +9,9 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.ibatis.type.JdbcType;
import org.dromara.common.core.domain.model.BaseAudit;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 分账规则明细
@ -95,6 +95,13 @@ public class DivideDetail extends BaseAudit {
@ExcelProperty(value = "分账子单流水号", order = 5)
private String separateTrade;
/**
* 分账完成时间
*/
@Schema(description = "分账完成时间")
@ExcelProperty(value = "分账完成时间", order = 5)
private LocalDateTime separateTime;
@Schema(description = "账户code")
@ExcelProperty(value = "账户code", order = 6)
private String accountCode;

View File

@ -8,6 +8,7 @@ import org.dromara.common.core.domain.model.BaseAudit;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 分账规则明细
@ -53,6 +54,22 @@ public class DivideDetailVO extends BaseEntity {
@Schema(description = "手续费")
@ExcelProperty(value = "手续费", order = 5)
private BigDecimal fee;
/**
* 分账完成时间
*/
@Schema(description = "分账完成时间")
@ExcelProperty(value = "分账完成时间", order = 5)
private LocalDateTime separateTime;
/**
* 分账子单流水号
*/
@Schema(description = "分账子单流水号")
@ExcelProperty(value = "分账子单流水号", order = 5)
private String separateTrade;
/**
* 金额分配比例
*/
@ -65,12 +82,12 @@ public class DivideDetailVO extends BaseEntity {
*/
@Schema(description = "手续费分配比例")
@ExcelProperty(value = "手续费分配比例", order = 5)
private BigDecimal feePercent;
/**
* 账户类型 1 平台 2 商户 3 代理人
* */
/**
* 账户类型
*
* */
private Integer type;

View File

@ -9,7 +9,7 @@ public enum DivideStatus {
PROCESSING(1, "分账中"),
SUCCESS(2, "分账成功"),
FAIL(3, "分账失败"),
REFUNDED(4, "退");
REFUNDED(4, "退");
private final int code;

View File

@ -18,8 +18,5 @@ import java.util.List;
@Mapper
public interface DivideDetailMapper extends BaseMapperPlus<DivideDetail, DivideDetailVO> {
@Select("select * from trans_divide_detail where divide_id = #{divideId} and del_flag = '0' ")
List<DivideDetail> selectByDivideId(@Param("divideId") Long divideId);
BigDecimal getMoney(@Param("accountId") Long accountId, @Param("status") Integer status);
}

View File

@ -3,7 +3,9 @@ package com.wzj.soopin.transaction.service;
import com.wzj.soopin.transaction.domain.bo.MerchantAddBO;
import com.wzj.soopin.transaction.domain.bo.PaymentBO;
import com.wzj.soopin.order.domain.bo.RefundBO;
import com.wzj.soopin.transaction.domain.bo.SeparateApplyBO;
import com.wzj.soopin.transaction.domain.bo.SeparateRefundBO;
import com.wzj.soopin.transaction.domain.bo.easypay.EasyPayRequest;
import com.wzj.soopin.transaction.domain.vo.EasypayAccountVO;
import com.wzj.soopin.transaction.domain.vo.EasypayTransResultVO;
@ -38,9 +40,9 @@ public interface IEasypayService {
/**
* 易生退款
* @param orderId
* @param refundBO
*/
void refund(Long orderId);
void refund(RefundBO refundBO);
/**
* 退款查询
@ -64,6 +66,8 @@ public interface IEasypayService {
void merchantBinding(String mchtCode);
boolean separateRefund(SeparateRefundBO separateRefundBO);
/**
* 获取易生账户
* @param memberId

View File

@ -153,8 +153,7 @@ public class DivideServiceImpl extends ServiceImpl<DivideMapper, Divide> impleme
if (divide == null) {
return null;
}
divideDetailMapper.selectByDivideId(divideId);
List<DivideDetail> detailList = divideDetailMapper.selectByDivideId(divideId);
List<DivideDetail> detailList = divideDetailMapper.selectList(Wrappers.lambdaQuery(DivideDetail.class).eq(DivideDetail::getDivideId, divideId));
divide.setDetails(detailConvert.toVO(detailList));
return divide;
}
@ -282,7 +281,7 @@ public class DivideServiceImpl extends ServiceImpl<DivideMapper, Divide> impleme
.money(sellerAmount)
.moneyPercent(divideRate)
.feePercent(ruleDetailVO.getFeePercent())
.type(DivideRuleDetailType.SELLER.getValue())
.type(TenantType.MERCHANT.getType())
.status(DivideStatus.PENDING.getCode())
.build();
divideDetailMapper.insert(divideDetail);
@ -323,7 +322,7 @@ public class DivideServiceImpl extends ServiceImpl<DivideMapper, Divide> impleme
.money(proxyAmount)
.moneyPercent(rule.getMoneyPercent())
.feePercent(rule.getFeePercent())
.type(DivideRuleDetailType.PROXY.getValue())
.type(TenantType.AGENT.getType())
.status(DivideStatus.PENDING.getCode())
.build();
divideDetailMapper.insert(divideDetail);
@ -364,7 +363,7 @@ public class DivideServiceImpl extends ServiceImpl<DivideMapper, Divide> impleme
.money(referencAmount)
.moneyPercent(rule.getMoneyPercent())
.feePercent(rule.getFeePercent())
.type(DivideRuleDetailType.REFERENCE.getValue())
.type(TenantType.REFERENCE.getType())
.status(DivideStatus.PENDING.getCode())
.build();
divideDetailMapper.insert(divideDetail);
@ -405,7 +404,7 @@ public class DivideServiceImpl extends ServiceImpl<DivideMapper, Divide> impleme
.money(platformAmount)
.moneyPercent(rule.getMoneyPercent())
.feePercent(rule.getFeePercent())
.type(DivideRuleDetailType.PLATFORM.getValue())
.type(TenantType.PLATFORM.getType())
.status(DivideStatus.PENDING.getCode())
.build();
divideDetailMapper.insert(divideDetail);

View File

@ -8,7 +8,6 @@ import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.Sign;
@ -17,15 +16,17 @@ import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wzj.soopin.order.domain.bo.RefundBO;
import com.wzj.soopin.order.domain.entity.Order;
import com.wzj.soopin.order.domain.entity.OrderItem;
import com.wzj.soopin.order.emum.OrderStatusEnum;
import com.wzj.soopin.order.mapper.OrderMapper;
import com.wzj.soopin.order.service.OrderItemService;
import com.wzj.soopin.order.service.OrderService;
import com.wzj.soopin.transaction.config.EasypayConfig;
import com.wzj.soopin.transaction.config.WechatMiniProgramConfig;
import com.wzj.soopin.transaction.domain.bo.MerchantAddBO;
import com.wzj.soopin.transaction.domain.bo.PaymentBO;
import com.wzj.soopin.transaction.domain.bo.SeparateApplyBO;
import com.wzj.soopin.transaction.domain.bo.SeparateItemBO;
import com.wzj.soopin.transaction.domain.bo.*;
import com.wzj.soopin.transaction.domain.bo.easypay.*;
import com.wzj.soopin.transaction.domain.bo.easypay.merchant.add.*;
import com.wzj.soopin.transaction.domain.bo.easypay.merchant.binding.MerchantBindingReqBody;
@ -39,6 +40,7 @@ import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.req.SeparateR
import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.resp.SeparateApplyRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.resp.SeparateRespInfoList;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.resp.SeparateRespOrderInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.refund.resp.SeparateRefundRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.req.*;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsApiRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsapiRespOrderInfo;
@ -46,30 +48,27 @@ import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.WxJsApiResp
import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.req.TradeQueryReqBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.resp.TradeQueryRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.resp.TradeQueryRespOrderInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.refund.req.SeparateRefundReqBody;
import com.wzj.soopin.transaction.domain.entity.WxAuthResponse;
import com.wzj.soopin.transaction.domain.po.Divide;
import com.wzj.soopin.transaction.domain.po.DivideDetail;
import com.wzj.soopin.transaction.domain.po.DivideRule;
import com.wzj.soopin.transaction.domain.po.PayOrder;
import com.wzj.soopin.transaction.domain.vo.*;
import com.wzj.soopin.transaction.enums.DivideRuleStatus;
import com.wzj.soopin.transaction.enums.DivideStatus;
import com.wzj.soopin.transaction.enums.TransState;
import com.wzj.soopin.transaction.enums.easypay.*;
import com.wzj.soopin.transaction.mapper.DivideDetailMapper;
import com.wzj.soopin.transaction.mapper.DivideMapper;
import com.wzj.soopin.transaction.mapper.DivideRuleMapper;
import com.wzj.soopin.transaction.mapper.PayOrderMapper;
import com.wzj.soopin.transaction.service.IEasypayService;
import com.wzj.soopin.transaction.util.SnowFlake;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.enums.FormatsType;
import org.dromara.common.core.enums.TenantType;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.ConfigService;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@ -99,7 +98,9 @@ public class EasypayServiceImpl implements IEasypayService {
private final WxAuthService wxAuthService;
private final OrderMapper orderMapper;
private final OrderService orderService;
private final OrderItemService orderItemService;
private final PayOrderMapper payOrderMapper;
@ -196,7 +197,6 @@ public class EasypayServiceImpl implements IEasypayService {
* 发起支付
*
* @param paymentBO
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
@ -248,9 +248,10 @@ public class EasypayServiceImpl implements IEasypayService {
payOrder.setTransState(TransState.PAYMENT.getCode());
payOrderMapper.updateById(payOrder);
//关联订单的最新支付单
orderMapper.updateById(Order.builder()
orderService.updateById(Order.builder()
.id(payOrder.getOrderId())
.payId(payOrder.getId())
.payAmount(BigDecimal.valueOf(payOrder.getTransAmount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP))
.status(OrderStatusEnum.VERIFY.getValue())
.build());
// 生成返回前端的预支付信息
@ -270,7 +271,6 @@ public class EasypayServiceImpl implements IEasypayService {
}
/**
* 根据支付类型校验请求参数是否完整
*
@ -321,7 +321,7 @@ public class EasypayServiceImpl implements IEasypayService {
* @param apiReqBody
*/
private PayOrder setReqOrderInfo(JsApiReqBody apiReqBody, PaymentBO paymentBO) {
Order order = orderMapper.selectById(paymentBO.getOrderId());
Order order = orderService.getById(paymentBO.getOrderId());
Assert.notNull(order, () -> new ServiceException("订单不存在"));
Assert.isTrue(Objects.equals(order.getStatus(), OrderStatusEnum.UNPAID.getValue()), () -> new ServiceException("订单已支付"));
// 订单中的金额单位为元需转换为分
@ -392,7 +392,7 @@ public class EasypayServiceImpl implements IEasypayService {
@Override
@Transactional(rollbackFor = Exception.class)
public EasypayTransResultVO paymentQuery(Long orderId) throws ServerException {
Order order = orderMapper.selectById(orderId);
Order order = orderService.getById(orderId);
Assert.notNull(order, () -> new ServiceException("订单不存在"));
EasypayTransResultVO paymentResultVO = EasypayTransResultVO.builder()
.orderId(order.getId())
@ -401,7 +401,7 @@ public class EasypayServiceImpl implements IEasypayService {
.transState(TransState.PENDING.getCode())
.build();
PayOrder payOrder = payOrderMapper.selectById(order.getPayId());
if(payOrder == null){
if (payOrder == null) {
return paymentResultVO;
}
paymentResultVO.setTransState(TransState.PAYMENT.getCode());
@ -448,9 +448,9 @@ public class EasypayServiceImpl implements IEasypayService {
.transState(TransState.PAID.getCode())
.build();
payOrderMapper.updateById(payOrderUpdate);
orderMapper.updateById(Order.builder()
orderService.updateById(Order.builder()
.id(order.getId())
.status(TransState.PAID.getCode())
.status(OrderStatusEnum.PAID.getValue())
.build());
paymentResultVO.setTransState(TransState.PAID.getCode());
}
@ -476,7 +476,7 @@ public class EasypayServiceImpl implements IEasypayService {
TradeQueryRespOrderInfo respOrderInfo = tradeQueryRespBody.getRespOrderInfo();
PayOrder payOrder = payOrderMapper.selectById(String.valueOf(respOrderInfo.getOrgTrace()));
if (payOrder != null) {
Order order = orderMapper.selectById(payOrder.getOrderId());
Order order = orderService.getById(payOrder.getOrderId());
if (order != null) {
// 支付完成 进入终态
if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PARTIALLY_OK)) {
@ -490,7 +490,7 @@ public class EasypayServiceImpl implements IEasypayService {
.transState(TransState.PAID.getCode())
.build());
// 更新订单信息
orderMapper.updateById(Order.builder()
orderService.updateById(Order.builder()
.id(order.getId())
.status(OrderStatusEnum.PAID.getValue())
.paymentTime(LocalDateTimeUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()).toString(), "yyyyMMddHHmmss"))
@ -514,17 +514,70 @@ public class EasypayServiceImpl implements IEasypayService {
@Override
@Transactional(rollbackFor = Exception.class)
public void refund(Long orderId) {
Order order = orderMapper.selectById(orderId);
public void refund(RefundBO refundBO) {
Long orderId = refundBO.getOrderId();
Order order = orderService.getById(orderId);
Assert.notNull(order, () -> new ServiceException("订单不存在"));
PayOrder payOrder = payOrderMapper.selectById(order.getPayId());
Assert.notNull(payOrder, () -> new ServiceException("订单不存在"));
Assert.isTrue(payOrder.getTransState() == TransState.PAID.getCode(), () -> new ServiceException("订单未支付,不可发起退款"));
// 更新支付订单状态为退款中
payOrderMapper.updateById(PayOrder.builder()
.id(payOrder.getId())
.transState(TransState.REFUND_PENDING.getCode())
.build());
// 订单已支付或已分账状态才可发起退款
boolean orderDivided = Objects.equals(order.getStatus(), OrderStatusEnum.PAID.getValue()) || Objects.equals(order.getStatus(), OrderStatusEnum.DIVIDED.getValue());
// 更新商品订单和支付订单状态为退款中
orderService.updateById(Order.builder().id(orderId).status(OrderStatusEnum.REFUNDED.getValue()).build());
payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.REFUND_PENDING.getCode()).build());
refundBO.setOrder(order);
List<OrderItem> orderItems = orderItemService.findByOrderId(orderId);
BigDecimal refundAmount = BigDecimal.ZERO;
List<RefundBO.RefundItemBO> refundItemBoList = refundBO.getRefundItemBoList();
for (RefundBO.RefundItemBO refundItemBO : refundItemBoList) {
Long orderItemId = refundItemBO.getOrderItemId();
OrderItem orderItem = orderItems.stream().filter(item -> item.getId().equals(orderItemId)).findFirst().orElse(null);
Assert.notNull(orderItem, () -> new ServiceException("订单项不存在"));
Assert.isTrue(refundItemBO.getRefundAmount().compareTo(orderItem.getSalePrice()) <= 0, () -> new ServiceException("订单项退款金额超出支付金额"));
refundAmount = refundAmount.add(refundItemBO.getRefundAmount());
// 已分账的订单需要先回退订单子项的分账在退款
if (orderDivided) {
Divide divide = divideMapper.selectOne(Wrappers.lambdaQuery(Divide.class)
.eq(Divide::getOrderId, orderId)
.eq(Divide::getOrderItemId, orderItemId)
.eq(Divide::getStatus, DivideStatus.SUCCESS.getCode())
.last("limit 1"));
Assert.notNull(divide, () -> new ServiceException("订单项未分账"));
List<DivideDetail> detailList = divideDetailMapper.selectList(Wrappers.lambdaQuery(DivideDetail.class).eq(DivideDetail::getDivideId, divide.getId()));
Assert.notEmpty(detailList, () -> new ServiceException("未发现订单项分账明细"));
//如果退款金额小于实际订单子项金额回退商家的分账即可
if (refundItemBO.getRefundAmount().compareTo(divide.getOrderItemMoney()) < 0) {
DivideDetail merchantDivideDetail = detailList.stream().filter(detail -> detail.getType() == TenantType.MERCHANT.getType())
.findFirst().orElseThrow(() -> new ServiceException("商家分账信息丢失"));
SeparateRefundBO separateRefundBO = SeparateRefundBO.builder()
.payType(payOrder.getPayType())
.orgTrace(StrBuilder.create(TRACE_PREFIX).append(SnowFlake.getIdStr()).toString())
.oriSeparateBatchTrace(divide.getSeparateBatchTrace())
.oriSeparateTrade(merchantDivideDetail.getSeparateTrade())
.oriTransDate(LocalDateTimeUtil.format(merchantDivideDetail.getSeparateTime(), "yyyyMMdd"))
.refundAmount(refundItemBO.getRefundAmount().multiply(BigDecimal.valueOf(100)).longValue())
.build();
separateRefund(separateRefundBO);
} else {
// 退款金额等于实际订单子项金额则回退全部的分账
detailList.forEach(detail -> {
// 分账金额() = 订单子项实际销售金额 * 100 * 分账比例百分比值 / 100 = 订单子项实际销售金额 * 分账比例百分比值
long amount = orderItem.getSalePrice().multiply(BigDecimal.valueOf(detail.getMoneyPercent())).longValue();
SeparateRefundBO separateRefundBO = SeparateRefundBO.builder()
.payType(payOrder.getPayType())
.orgTrace(StrBuilder.create(TRACE_PREFIX).append(SnowFlake.getIdStr()).toString())
.oriSeparateBatchTrace(divide.getSeparateBatchTrace())
.oriSeparateTrade(detail.getSeparateTrade())
.oriTransDate(LocalDateTimeUtil.format(detail.getSeparateTime(), "yyyyMMdd"))
.refundAmount(amount)
.build();
separateRefund(separateRefundBO);
});
}
}
}
refundBO.setTotalRefundAmount(refundAmount);
EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
RefundApplyReqBody refundApplyReqBody = RefundApplyReqBody.builder()
.reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build())
@ -533,7 +586,7 @@ public class EasypayServiceImpl implements IEasypayService {
.orgTrace(StrBuilder.create(TRACE_PREFIX).append(SnowFlake.getIdStr()).toString())
.oriOrgTrace(String.valueOf(order.getPayId()))
.oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, payOrder.getEndTransDate()))
.refundAmount(payOrder.getTransAmount())
.refundAmount(refundAmount.multiply(BigDecimal.valueOf(100)).longValue())
.build())
.build();
String reqSign = getSignStr(reqHeader, refundApplyReqBody);
@ -559,7 +612,7 @@ public class EasypayServiceImpl implements IEasypayService {
if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK)) {
// 更新支付单退款状态
payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.REFUNDED.getCode()).refundDate(new Date()).build());
orderMapper.updateStatusById(order.getId(), OrderStatusEnum.REFUNDED.getValue());
orderService.refund(refundBO);
}
} else {
log.error("易生退款失败:{}", respStateInfo.getRespDesc());
@ -575,7 +628,7 @@ public class EasypayServiceImpl implements IEasypayService {
@Override
public EasypayTransResultVO refundQuery(Long orderId) {
Order order = orderMapper.selectById(orderId);
Order order = orderService.getById(orderId);
Assert.notNull(order, () -> new ServiceException("订单不存在"));
PayOrder payOrder = payOrderMapper.selectById(order.getPayId());
Assert.notNull(payOrder, () -> new ServiceException("订单不存在"));
@ -697,7 +750,6 @@ public class EasypayServiceImpl implements IEasypayService {
* @param respOrderInfo
*/
private void handleSeparateInfo(SeparateApplyBO separateApplyBO, SeparateRespOrderInfo respOrderInfo) {
Long orderId = separateApplyBO.getOrderId();
Map<String, SeparateItemBO> separateItemBOMap = separateApplyBO.getSeparateItemBOList().stream().collect(Collectors.toMap(SeparateItemBO::getSeparateTrade, o -> o));
List<DivideDetail> updateDetailList = new ArrayList<>();
List<SeparateRespInfoList> separateRespInfoList = respOrderInfo.getSeparateRespInfoList();
@ -731,15 +783,17 @@ public class EasypayServiceImpl implements IEasypayService {
.status(DivideStatus.PROCESSING.getCode()).build();
if (itemOkCount == separateItemBOMap.size()) {
divideUpdate.setStatus(DivideStatus.SUCCESS.getCode());
orderMapper.updateStatusById(orderId, OrderStatusEnum.DIVIDED.getValue());
orderService.updateById(Order.builder()
.id(separateApplyBO.getOrderId())
.status(OrderStatusEnum.DIVIDED.getValue())
.build());
}
divideMapper.updateById(divideUpdate);
}
/**
* 上传图片至易生
* 上传图片至易生
*/
@Override
public String merchantPicUpload(MultipartFile pic) throws IOException {
@ -782,7 +836,7 @@ public class EasypayServiceImpl implements IEasypayService {
}
/**
* 添加分账商户
* 添加分账商户
*/
@Override
public void merchantAdd(MerchantAddBO merchantAddBO) {
@ -828,6 +882,7 @@ public class EasypayServiceImpl implements IEasypayService {
/**
* 绑定分账商户
*
* @param mchtCode
*/
@Override
@ -857,8 +912,7 @@ public class EasypayServiceImpl implements IEasypayService {
verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
MerchantAddApplyRespBody merchantAddApplyRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), MerchantAddApplyRespBody.class);
if (StrUtil.equals(RSP_BODY_RESP_OK, merchantAddApplyRespBody.getRetCode())) {
// TODO 更新商户绑定信息
// updateMerchantBinding(mchtCode);
log.info("绑定商户[{}]成功:{}", mchtCode, merchantAddApplyRespBody.getRetMsg());
} else {
log.error("绑定商户失败:{}", merchantAddApplyRespBody.getRetMsg());
throw new ServiceException("绑定商户失败:" + merchantAddApplyRespBody.getRetMsg());
@ -869,6 +923,54 @@ public class EasypayServiceImpl implements IEasypayService {
}
}
/**
* 分账回退
*
* @param separateRefundBO
*/
@Override
public boolean separateRefund(SeparateRefundBO separateRefundBO) {
boolean flag = false;
EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
SeparateRefundReqBody separateApplyReqBody = SeparateRefundReqBody.builder()
.reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build())
.payInfo(PayInfo.builder().transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())).payType(separateRefundBO.getPayType()).build())
.reqOrderInfo(BeanUtil.copyProperties(separateRefundBO, SeparateRefundReqBody.ReqOrderInfo.class))
.build();
String reqSign = getSignStr(reqHeader, separateApplyReqBody);
EasyPayRequest easyRequest = EasyPayRequest.builder()
.reqHeader(reqHeader)
.reqBody(separateApplyReqBody)
.reqSign(reqSign)
.build();
log.debug("调用易生分账回退接口请求:{}", JSONObject.toJSONString(easyRequest));
String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/separate/cancel").toString();
String body = HttpRequest.post(url)
.timeout(3000)
.body(JSON.toJSONString(easyRequest))
.execute()
.body();
log.debug("调用易生分账回退接口响应:{}", body);
EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
SeparateRefundRespBody separateRefundRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), SeparateRefundRespBody.class);
RespStateInfo respStateInfo = separateRefundRespBody.getRespStateInfo();
if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK)) {
flag = true;
}
} else {
log.error("分账回退失败:{}", respStateInfo.getRespDesc());
throw new ServiceException("分账回退失败:" + respStateInfo.getRespDesc());
}
} else {
log.error("易生分账回退通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
throw new ServiceException("易生分账回退通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
}
return flag;
}
@Override
public EasypayAccountVO getEasypayAccount(Long memberId) {