feat: 订单

pull/1121/head
梁宇奇 2025-04-22 20:59:43 +08:00
parent 111f5afe3a
commit 7ee4abd483
13 changed files with 285 additions and 31 deletions

View File

@ -5,6 +5,7 @@ import com.github.pagehelper.Page;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.XktBaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.SimpleEntity;
import com.ruoyi.common.core.page.PageVO;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.exception.ServiceException;
@ -205,13 +206,15 @@ public class StoreOrderController extends XktBaseController {
//TODO 权限
StoreOrderRefundConfirmDTO dto = BeanUtil.toBean(vo, StoreOrderRefundConfirmDTO.class);
dto.setOperatorId(SecurityUtils.getUserId());
//售后状态->售后完成,支付状态->支付中
//售后状态->售后完成,支付状态->支付中,创建收款单
StoreOrderRefund storeOrderRefund = storeOrderService.prepareRefundOrder(dto);
//三方退款
PaymentManager paymentManager = getPaymentManager(EPayChannel.of(storeOrderRefund.getRefundOrder().getPayChannel()));
paymentManager.refundStoreOrder(storeOrderRefund);
//支付状态->已支付,创建收款单
storeOrderService.refundSuccess(storeOrderRefund);
//支付状态->已支付,收款单到账
storeOrderService.refundSuccess(storeOrderRefund.getRefundOrder().getId(),
storeOrderRefund.getRefundOrderDetails().stream().map(SimpleEntity::getId).collect(Collectors.toList()),
SecurityUtils.getUserId());
return success();
}

View File

@ -15,7 +15,7 @@ import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FinanceBillInfo {
public class FinanceBillExt {
private FinanceBill financeBill;

View File

@ -0,0 +1,30 @@
package com.ruoyi.xkt.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author liangyq
* @date 2025-04-22 13:42
*/
@Getter
@AllArgsConstructor
public enum EAccountOwnerType {
PLATFORM(1, "平台"),
STORE(2, "档口"),
USER(3, "用户"),
;
private final Integer value;
private final String label;
public static EAccountOwnerType of(Integer value) {
for (EAccountOwnerType e : EAccountOwnerType.values()) {
if (e.getValue().equals(value)) {
return e;
}
}
return null;
}
}

View File

@ -13,7 +13,9 @@ public enum EFinBillSrcType {
STORE_ORDER_PAID(1, "代发订单支付"),
STORE_ORDER_COMPLETED(2, "代发订单完成"),
WITHDRAW(3, "提现");
WITHDRAW(3, "提现"),
STORE_ORDER_REFUND(4, "代发订单退款"),
;
private final Integer value;
private final String label;

View File

@ -1,6 +1,6 @@
package com.ruoyi.xkt.service;
import com.ruoyi.xkt.dto.finance.FinanceBillInfo;
import com.ruoyi.xkt.dto.finance.FinanceBillExt;
import com.ruoyi.xkt.dto.order.StoreOrderExt;
import com.ruoyi.xkt.enums.EPayChannel;
@ -10,12 +10,37 @@ import com.ruoyi.xkt.enums.EPayChannel;
*/
public interface IFinanceBillService {
/**
*
*
*
* @param orderExt
* @param payId
* @param payChannel
* @return
*/
FinanceBillInfo createCollectionBillAfterOrderPaid(StoreOrderExt orderExt, Long payId, EPayChannel payChannel);
FinanceBillExt createOrderPaidCollectionBill(StoreOrderExt orderExt, Long payId, EPayChannel payChannel);
/**
*
*
* @param orderExt
* @return
*/
FinanceBillExt createOrderCompletedTransferBill(StoreOrderExt orderExt);
/**
*
*
* @param orderExt
* @return
*/
FinanceBillExt createRefundOrderPaymentBill(StoreOrderExt orderExt);
/**
*
*
* @param storeOrderId
*/
void entryRefundOrderPaymentBill(Long storeOrderId);
}

View File

@ -2,6 +2,7 @@ package com.ruoyi.xkt.service;
import com.ruoyi.xkt.domain.InternalAccount;
import com.ruoyi.xkt.dto.finance.TransInfo;
import com.ruoyi.xkt.enums.EAccountOwnerType;
import com.ruoyi.xkt.enums.EEntryStatus;
import com.ruoyi.xkt.enums.EFinBillType;
import com.ruoyi.xkt.enums.ELoanDirection;
@ -19,6 +20,15 @@ public interface IInternalAccountService {
*/
InternalAccount getById(Long id);
/**
*
*
* @param ownerId
* @param ownerType
* @return
*/
InternalAccount getInternalAccount(Long ownerId, EAccountOwnerType ownerType);
/**
*
*

View File

@ -154,9 +154,11 @@ public interface IStoreOrderService {
/**
* 退
*
* @param storeOrderRefund
* @param storeOrderId
* @param storeOrderDetailIds
* @param operatorId
*/
void refundSuccess(StoreOrderRefund storeOrderRefund);
void refundSuccess(Long storeOrderId, List<Long> storeOrderDetailIds, Long operatorId);
}

View File

@ -37,14 +37,14 @@ public class AlipayCallbackServiceImpl implements IAlipayCallbackService {
.eq(AlipayCallback::getNotifyId, notifyId));
}
@Transactional
@Transactional(rollbackFor = Exception.class)
@Override
public int insertAlipayCallback(AlipayCallback alipayCallback) {
Assert.notNull(alipayCallback);
return alipayCallbackMapper.insert(alipayCallback);
}
@Transactional
@Transactional(rollbackFor = Exception.class)
@Override
public void processOrderPaid(AlipayCallback info) {
//更新回调状态
@ -56,6 +56,6 @@ public class AlipayCallbackServiceImpl implements IAlipayCallbackService {
StoreOrderExt orderExt = storeOrderService.paySuccess(order.getId(), info.getTradeNo(), info.getTotalAmount(),
info.getReceiptAmount());
//创建收款单
financeBillService.createCollectionBillAfterOrderPaid(orderExt, info.getId(), EPayChannel.ALI_PAY);
financeBillService.createOrderPaidCollectionBill(orderExt, info.getId(), EPayChannel.ALI_PAY);
}
}

View File

@ -47,7 +47,7 @@ public class ExternalAccountServiceImpl implements IExternalAccountService {
return externalAccountMapper.selectById(id);
}
@Transactional
@Transactional(rollbackFor = Exception.class)
@Override
public int addTransDetail(Long externalAccountId, TransInfo transInfo, ELoanDirection loanDirection,
EEntryStatus entryStatus) {
@ -72,7 +72,7 @@ public class ExternalAccountServiceImpl implements IExternalAccountService {
return externalAccountTransDetailMapper.insert(transDetail);
}
@Transactional
@Transactional(rollbackFor = Exception.class)
@Override
public void entryTransDetail(Long srcBillId, EFinBillType srcBillType) {
Assert.notNull(srcBillId);

View File

@ -1,11 +1,16 @@
package com.ruoyi.xkt.service.impl;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.bean.BeanValidators;
import com.ruoyi.xkt.domain.FinanceBill;
import com.ruoyi.xkt.domain.FinanceBillDetail;
import com.ruoyi.xkt.domain.StoreOrderDetail;
import com.ruoyi.xkt.dto.finance.FinanceBillInfo;
import com.ruoyi.xkt.dto.finance.FinanceBillExt;
import com.ruoyi.xkt.dto.finance.TransInfo;
import com.ruoyi.xkt.dto.order.StoreOrderExt;
import com.ruoyi.xkt.enums.*;
@ -19,6 +24,7 @@ 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.ArrayList;
import java.util.Date;
import java.util.List;
@ -39,14 +45,13 @@ public class FinanceBillServiceImpl implements IFinanceBillService {
@Autowired
private IExternalAccountService externalAccountService;
@Transactional
@Transactional(rollbackFor = Exception.class)
@Override
public FinanceBillInfo createCollectionBillAfterOrderPaid(StoreOrderExt orderExt, Long payId,
EPayChannel payChannel) {
public FinanceBillExt createOrderPaidCollectionBill(StoreOrderExt orderExt, Long payId,
EPayChannel payChannel) {
Assert.notNull(orderExt);
Assert.notNull(payId);
Assert.notNull(payChannel);
//获取平台内部账户并加锁
FinanceBill bill = new FinanceBill();
bill.setBillNo(generateBillNo(EFinBillType.COLLECTION));
bill.setBillType(EFinBillType.COLLECTION.getValue());
@ -92,7 +97,130 @@ public class FinanceBillServiceImpl implements IFinanceBillService {
//外部账户
externalAccountService.addTransDetail(payChannel.getPlatformExternalAccountId(), transInfo,
ELoanDirection.DEBIT, EEntryStatus.FINISH);
return new FinanceBillInfo(bill, billDetails);
return new FinanceBillExt(bill, billDetails);
}
@Transactional(rollbackFor = Exception.class)
@Override
public FinanceBillExt createOrderCompletedTransferBill(StoreOrderExt orderExt) {
Assert.notNull(orderExt);
FinanceBill bill = new FinanceBill();
bill.setBillNo(generateBillNo(EFinBillType.TRANSFER));
bill.setBillType(EFinBillType.TRANSFER.getValue());
//直接标记成功
bill.setBillStatus(EFinBillStatus.SUCCESS.getValue());
bill.setSrcType(EFinBillSrcType.STORE_ORDER_COMPLETED.getValue());
bill.setSrcId(orderExt.getOrder().getId());
bill.setRelType(EFinBillRelType.STORE_ORDER.getValue());
bill.setRelId(orderExt.getOrder().getId());
//业务唯一键
String businessUniqueKey = "STORE_ORDER_COMPLETED_" + orderExt.getOrder().getId();
bill.setBusinessUniqueKey(businessUniqueKey);
Long inputInternalAccountId = internalAccountService.getInternalAccount(orderExt.getOrder().getStoreId(),
EAccountOwnerType.STORE).getId();
bill.setInputInternalAccountId(inputInternalAccountId);
bill.setOutputInternalAccountId(Constants.PLATFORM_INTERNAL_ACCOUNT_ID);
bill.setBusinessAmount(orderExt.getOrder().getTotalAmount());
bill.setTransAmount(orderExt.getOrder().getRealTotalAmount());
bill.setVersion(0L);
bill.setDelFlag(Constants.UNDELETED);
financeBillMapper.insert(bill);
List<FinanceBillDetail> billDetails = new ArrayList<>(orderExt.getOrderDetails().size());
for (StoreOrderDetail orderDetail : orderExt.getOrderDetails()) {
FinanceBillDetail billDetail = new FinanceBillDetail();
billDetail.setFinanceBillId(bill.getId());
billDetail.setRelType(EFinBillDetailRelType.STORE_ORDER_DETAIL.getValue());
billDetail.setRelId(orderDetail.getId());
billDetail.setBusinessAmount(orderDetail.getTotalAmount());
billDetail.setTransAmount(orderDetail.getRealTotalAmount());
billDetail.setDelFlag(Constants.UNDELETED);
financeBillDetailMapper.insert(billDetail);
billDetails.add(billDetail);
}
TransInfo transInfo = TransInfo.builder()
.srcBillId(bill.getId())
.srcBillType(bill.getBillType())
.transAmount(bill.getTransAmount())
.transTime(new Date())
.handlerId(null)
.remark("订单完成")
.build();
//内部账户
internalAccountService.addTransDetail(Constants.PLATFORM_INTERNAL_ACCOUNT_ID, transInfo,
ELoanDirection.CREDIT, EEntryStatus.FINISH);
internalAccountService.addTransDetail(inputInternalAccountId, transInfo,
ELoanDirection.DEBIT, EEntryStatus.FINISH);
return new FinanceBillExt(bill, billDetails);
}
@Transactional(rollbackFor = Exception.class)
@Override
public FinanceBillExt createRefundOrderPaymentBill(StoreOrderExt orderExt) {
Assert.notNull(orderExt);
FinanceBill bill = new FinanceBill();
bill.setBillNo(generateBillNo(EFinBillType.PAYMENT));
bill.setBillType(EFinBillType.PAYMENT.getValue());
//执行中
bill.setBillStatus(EFinBillStatus.PROCESSING.getValue());
bill.setSrcType(EFinBillSrcType.STORE_ORDER_REFUND.getValue());
bill.setSrcId(orderExt.getOrder().getId());
bill.setRelType(EFinBillRelType.STORE_ORDER.getValue());
bill.setRelId(orderExt.getOrder().getId());
//业务唯一键
String businessUniqueKey = "STORE_ORDER_REFUND_" + orderExt.getOrder().getId();
bill.setBusinessUniqueKey(businessUniqueKey);
bill.setOutputInternalAccountId(Constants.PLATFORM_INTERNAL_ACCOUNT_ID);
//售后订单金额以明细为准
BigDecimal businessAmount = BigDecimal.ZERO;
BigDecimal transferAmount = BigDecimal.ZERO;
for (StoreOrderDetail orderDetail : orderExt.getOrderDetails()) {
businessAmount = NumberUtil.add(businessAmount, orderDetail.getTotalAmount());
transferAmount = NumberUtil.add(transferAmount, orderDetail.getRealTotalAmount());
}
bill.setBusinessAmount(businessAmount);
bill.setTransAmount(transferAmount);
bill.setVersion(0L);
bill.setDelFlag(Constants.UNDELETED);
financeBillMapper.insert(bill);
List<FinanceBillDetail> billDetails = new ArrayList<>(orderExt.getOrderDetails().size());
for (StoreOrderDetail orderDetail : orderExt.getOrderDetails()) {
FinanceBillDetail billDetail = new FinanceBillDetail();
billDetail.setFinanceBillId(bill.getId());
billDetail.setRelType(EFinBillDetailRelType.STORE_ORDER_DETAIL.getValue());
billDetail.setRelId(orderDetail.getId());
billDetail.setBusinessAmount(orderDetail.getTotalAmount());
billDetail.setTransAmount(orderDetail.getRealTotalAmount());
billDetail.setDelFlag(Constants.UNDELETED);
financeBillDetailMapper.insert(billDetail);
billDetails.add(billDetail);
}
TransInfo transInfo = TransInfo.builder()
.srcBillId(bill.getId())
.srcBillType(bill.getBillType())
.transAmount(bill.getTransAmount())
.transTime(new Date())
.handlerId(null)
.remark("订单售后退款")
.build();
//内部账户
internalAccountService.addTransDetail(Constants.PLATFORM_INTERNAL_ACCOUNT_ID, transInfo,
ELoanDirection.CREDIT, EEntryStatus.WAITING);
return new FinanceBillExt(bill, billDetails);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void entryRefundOrderPaymentBill(Long storeOrderId) {
Assert.notNull(storeOrderId);
//业务唯一键
String businessUniqueKey = "STORE_ORDER_REFUND_" + storeOrderId;
FinanceBill financeBill = financeBillMapper.selectOne(Wrappers.lambdaQuery(FinanceBill.class)
.eq(FinanceBill::getBusinessUniqueKey, businessUniqueKey));
if (!BeanValidators.exists(financeBill)) {
throw new ServiceException(CharSequenceUtil.format("售后订单[{}]对应付款单不存在", storeOrderId));
}
//内部账户入账
internalAccountService.entryTransDetail(financeBill.getId(), EFinBillType.of(financeBill.getBillType()));
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.xkt.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.constant.Constants;
@ -45,7 +46,21 @@ public class InternalAccountServiceImpl implements IInternalAccountService {
return internalAccountMapper.selectById(id);
}
@Transactional
@Override
public InternalAccount getInternalAccount(Long ownerId, EAccountOwnerType ownerType) {
Assert.notNull(ownerId);
Assert.notNull(ownerType);
InternalAccount account = internalAccountMapper.selectOne(Wrappers.lambdaQuery(InternalAccount.class)
.eq(InternalAccount::getOwnerId, ownerId)
.eq(InternalAccount::getOwnerType, ownerType.getValue()));
if (!BeanValidators.exists(account)) {
throw new ServiceException(CharSequenceUtil.format("{}[{}]内部账户不存在", ownerType.getLabel(),
ownerId));
}
return account;
}
@Transactional(rollbackFor = Exception.class)
@Override
public int addTransDetail(Long internalAccountId, TransInfo transInfo, ELoanDirection loanDirection,
EEntryStatus entryStatus) {
@ -122,7 +137,7 @@ public class InternalAccountServiceImpl implements IInternalAccountService {
return internalAccountTransDetailMapper.insert(transDetail);
}
@Transactional
@Transactional(rollbackFor = Exception.class)
@Override
public void entryTransDetail(Long srcBillId, EFinBillType srcBillType) {
Assert.notNull(srcBillId);

View File

@ -22,7 +22,7 @@ public class OperationRecordServiceImpl implements IOperationRecordService {
@Autowired
private StoreOrderOperationRecordMapper storeOrderOperationRecordMapper;
@Transactional
@Transactional(rollbackFor = Exception.class)
@Override
public void addOrderOperationRecords(List<StoreOrderOperationRecordAddDTO> recordList) {
if (CollUtil.isEmpty(recordList)) {

View File

@ -27,10 +27,7 @@ import com.ruoyi.xkt.enums.*;
import com.ruoyi.xkt.manager.ExpressManager;
import com.ruoyi.xkt.manager.PaymentManager;
import com.ruoyi.xkt.mapper.*;
import com.ruoyi.xkt.service.IExpressService;
import com.ruoyi.xkt.service.IOperationRecordService;
import com.ruoyi.xkt.service.IStoreOrderService;
import com.ruoyi.xkt.service.IVoucherSequenceService;
import com.ruoyi.xkt.service.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -76,6 +73,8 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
@Autowired
private IVoucherSequenceService voucherSequenceService;
@Autowired
private IFinanceBillService financeBillService;
@Autowired
private List<PaymentManager> paymentManagers;
@Autowired
private List<ExpressManager> expressManagers;
@ -813,7 +812,10 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
//操作记录
addOperationRecords(order.getId(), EOrderAction.COMPLETE, orderDetailIdList, EOrderAction.COMPLETE,
"确认收货", operatorId, new Date());
return new StoreOrderExt(order, orderDetails);
StoreOrderExt orderExt = new StoreOrderExt(order, orderDetails);
//创建转移单
financeBillService.createOrderCompletedTransferBill(orderExt);
return orderExt;
}
@Transactional(rollbackFor = Exception.class)
@ -1028,13 +1030,50 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
EOrderAction.CONFIRM_AFTER_SALE,
refundConfirmDTO.getOperatorId(),
new Date());
//创建付款单
financeBillService.createRefundOrderPaymentBill(new StoreOrderExt(order, refundOrderDetails));
return new StoreOrderRefund(order, refundOrderDetails, getAndBaseCheck(order.getRefundOrderId()));
}
@Transactional(rollbackFor = Exception.class)
@Override
public void refundSuccess(StoreOrderRefund storeOrderRefund) {
//TODO
public void refundSuccess(Long storeOrderId, List<Long> storeOrderDetailIds, Long operatorId) {
StoreOrder order = getAndBaseCheck(storeOrderId);
Map<Long, StoreOrderDetail> orderDetailMap = storeOrderDetailMapper.selectList(Wrappers
.lambdaQuery(StoreOrderDetail.class)
.eq(StoreOrderDetail::getStoreOrderId, order.getId())
.eq(SimpleEntity::getDelFlag, Constants.UNDELETED))
.stream()
.collect(Collectors.toMap(SimpleEntity::getId, Function.identity()));
for (Long storeOrderDetailId : storeOrderDetailIds) {
StoreOrderDetail orderDetail = orderDetailMap.get(storeOrderDetailId);
if (!BeanValidators.exists(orderDetail)) {
throw new ServiceException(CharSequenceUtil.format("订单明细[{}]不存在或与订单[{}]不匹配",
storeOrderDetailId, storeOrderId));
}
if (!EOrderStatus.COMPLETED.getValue().equals(orderDetail.getDetailStatus())
|| !EPayStatus.PAYING.getValue().equals(orderDetail.getPayStatus())) {
throw new ServiceException(CharSequenceUtil.format("订单明细[{}]状态异常", storeOrderDetailId));
}
orderDetail.setPayStatus(EPayStatus.PAID.getValue());
int orderDetailSuccess = storeOrderDetailMapper.updateById(orderDetail);
if (orderDetailSuccess == 0) {
throw new ServiceException(Constants.VERSION_LOCK_ERROR_COMMON_MSG);
}
}
int orderSuccess = storeOrderMapper.updateById(order);
if (orderSuccess == 0) {
throw new ServiceException(Constants.VERSION_LOCK_ERROR_COMMON_MSG);
}
//操作记录
addOperationRecords(order.getId(),
EOrderAction.REFUND,
storeOrderDetailIds,
EOrderAction.REFUND,
operatorId,
new Date());
//付款单到账
financeBillService.entryRefundOrderPaymentBill(order.getId());
}
/**