feat(rental): 优化押金退还逻辑和订单相关功能

- 完善押金退还逻辑,增加对未归还物品的检查- 更新客户剩余押金金额
- 优化订单列表展示,增加剩余押金字段
- 修复订单号生成逻辑
- 新增根据订单号获取客户ID的功能
This commit is contained in:
furongxin 2024-12-20 15:34:20 +08:00
parent 24ab5dd06a
commit be9085e3ac
8 changed files with 85 additions and 14 deletions

View File

@ -280,6 +280,8 @@ public interface ErrorCodeConstants {
ErrorCode RENTAL_ITEMS_NUMBER_EXCESS = new ErrorCode(1_013_001_005, "归还数量不能大于剩余租借数量!");
ErrorCode RENTAL_RECEIVED_AMOUNT_EXCESS = new ErrorCode(1_013_001_006, "收款金额不能大于押金金额!");
ErrorCode RENTAL_REFUND_AMOUNT_EXCESS = new ErrorCode(1_013_001_007, "退款金额不能大于收款金额!");
ErrorCode RENTAL_ITEMS_NOT_REFUND = new ErrorCode(1_013_001_008, "物品还未全部退还,不能全额退款!");
ErrorCode RENTAL_ORDER_CUSTOMER_EXISTS = new ErrorCode(1_013_001_009, "该客户已存在租赁订单!");
// ========== 项目管理相关 1-014-001-001 ==========
ErrorCode PROJECT_NOT_EXISTS = new ErrorCode(1_014_001_001, "项目不存在!");

View File

@ -111,18 +111,22 @@ public class RentalOrderController {
.filter(item -> Objects.equals(item.getStatus(), RentalOrderDO.WAITING_FOR_REFUND))
.map(RentalOrderRespVO::getOrderNo)
.collect(Collectors.toList());
// 获取订单号对应的退款申请
Map<String, BpmOARefundDTO> refundMap = refundApi.getListByOrderNo(orderNos).getCheckedData();
if (CollectionUtil.isNotEmpty(refundMap)) {
// 设置对应的订单号中的 申请退款和扣款金额
pageResult.getList().forEach(item -> {
if (refundMap.get(item.getOrderNo()) != null) {
item.setApplyRefundAmount(refundMap.get(item.getOrderNo()).getRefundAmount());
item.setApplyChargebacksAmount(refundMap.get(item.getOrderNo()).getChargebacksAmount());
}
});
}
// 设置对应的订单号中的 申请退款和扣款金额
pageResult.getList().forEach(item -> {
// 设置剩余押金
item.setRemainingDeposit(item.getReceivedAmount().subtract(item.getRefundAmount().add(item.getChargebacksAmount())));
if (CollectionUtil.isNotEmpty(refundMap) && refundMap.get(item.getOrderNo()) != null) {
item.setApplyRefundAmount(refundMap.get(item.getOrderNo()).getRefundAmount());
item.setApplyChargebacksAmount(refundMap.get(item.getOrderNo()).getChargebacksAmount());
}
});
return success(pageResult);
}

View File

@ -34,6 +34,9 @@ public class RentalOrderRespVO {
@Schema(description = "押金金额")
private BigDecimal depositAmount;
@Schema(description = "剩余押金")
private BigDecimal remainingDeposit;
@Schema(description = "已收金额")
private BigDecimal receivedAmount;

View File

@ -27,8 +27,8 @@ public interface RentalOrderMapper extends BaseMapperX<RentalOrderDO> {
MPJLambdaWrapperX<RentalOrderDO> queryWrapper = new MPJLambdaWrapperX<>();
queryWrapper.selectAll(RentalOrderDO.class);
queryWrapper.selectAs(RentalCustomerDO::getName, RentalOrderRespVO::getCustomerName);
queryWrapper.selectAs("SUM( CASE WHEN deposit.type = 1 THEN deposit.amount END )", RentalOrderRespVO::getReceivedAmount);
queryWrapper.selectAs("SUM( CASE WHEN deposit.type = 2 THEN deposit.amount END )", RentalOrderRespVO::getRefundAmount);
queryWrapper.selectAs("COALESCE( SUM( CASE WHEN deposit.type = 1 THEN deposit.amount END ), 0 )", RentalOrderRespVO::getReceivedAmount);
queryWrapper.selectAs("COALESCE( SUM( CASE WHEN deposit.type = 2 THEN deposit.amount END ), 0 )", RentalOrderRespVO::getRefundAmount);
queryWrapper.selectAs(AdminUserDO::getNickname, RentalOrderRespVO::getCreatorName);
queryWrapper.leftJoin(RentalDepositRecordDO.class, "deposit", RentalDepositRecordDO::getOrderNo, RentalOrderDO::getOrderNo);
queryWrapper.leftJoin(AdminUserDO.class, AdminUserDO::getId, RentalOrderDO::getCreator);

View File

@ -5,6 +5,8 @@ import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.oa.BpmOARefundApi;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.system.controller.admin.rental.vo.customer.RentalCustomerSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.rental.vo.itemsrecord.RentalItemsCountReqVO;
import cn.iocoder.yudao.module.system.controller.admin.rental.vo.rentaldepositrecord.RentalDepositAmountReqVO;
import cn.iocoder.yudao.module.system.controller.admin.rental.vo.rentaldepositrecord.RentalDepositRecordPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.rental.vo.rentaldepositrecord.RentalDepositRecordSaveReqVO;
@ -46,6 +48,12 @@ public class RentalDepositRecordServiceImpl implements RentalDepositRecordServic
@Resource
private BpmOARefundApi refundApi;
@Resource
private RentalCustomerService customerService;
@Resource
private RentalItemsRecordService itemsRecordService;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createRentalDepositRecord(RentalDepositRecordSaveReqVO createReqVO) {
@ -58,6 +66,8 @@ public class RentalDepositRecordServiceImpl implements RentalDepositRecordServic
// 已收金额 - 退款金额
BigDecimal amount = reqVO.getReceivedAmount().subtract(reqVO.getRefundAmount());
// 客户剩余押金 金额
BigDecimal customerDepositAmount = amount;
switch (createReqVO.getType()) {
case 1:
// 押金收款
@ -65,6 +75,7 @@ public class RentalDepositRecordServiceImpl implements RentalDepositRecordServic
if (amount.add(createReqVO.getAmount()).compareTo(depositAmount) > 0) {
throw exception(RENTAL_RECEIVED_AMOUNT_EXCESS);
}
customerDepositAmount = amount.add(createReqVO.getAmount());
break;
case 2:
RentalOrderDO updateOrder = new RentalOrderDO();
@ -74,6 +85,13 @@ public class RentalDepositRecordServiceImpl implements RentalDepositRecordServic
if (amount.compareTo(refundAmount) < 0) {
throw exception(RENTAL_REFUND_AMOUNT_EXCESS);
} else if (amount.compareTo(refundAmount) == 0) { // 判断当前退款金额 是否等于 实际已收的金额 - 扣款金额时
// 获取租赁物品信息
List<RentalItemsCountReqVO> itemList = itemsRecordService.getRentalItemsCount(createReqVO.getOrderNo(), false);
// 判断租赁物品是否已经归还完毕
boolean isReturned = itemList.stream().allMatch(item -> item.getNumber() == 0);
if (!isReturned) {
throw exception(RENTAL_ITEMS_NOT_REFUND);
}
// 更新订单状态为 已退款
updateOrder.setOrderNo(createReqVO.getOrderNo());
updateOrder.setStatus(RentalOrderDO.REFUNDED);
@ -82,6 +100,7 @@ public class RentalDepositRecordServiceImpl implements RentalDepositRecordServic
updateOrder.setOrderNo(createReqVO.getOrderNo());
updateOrder.setStatus(RentalOrderDO.ON_LEASE);
}
customerDepositAmount = customerDepositAmount.subtract(refundAmount);
// 同步更新 退款申请流程中的 退款状态
refundApi.updateStatus(createReqVO.getOrderNo());
// 同步更新 租赁订单状态
@ -95,6 +114,16 @@ public class RentalDepositRecordServiceImpl implements RentalDepositRecordServic
// 更新交易凭证附件 业务编号
UpdateBusinessFile(rentalDepositRecord);
// 获取客户编号
Long customerId = rentalOrderService.getCustomerIdByOrderNo(createReqVO.getOrderNo());
if (customerId != null) {
// 同步更新客户剩余押金 金额
customerService.updateRentalCustomer(new RentalCustomerSaveReqVO()
.setId(customerId)
.setAmount(customerDepositAmount));
}
// 返回
return rentalDepositRecord.getId();
}

View File

@ -42,6 +42,11 @@ public class RentalItemsRecordServiceImpl implements RentalItemsRecordService{
List<RentalItemsCountReqVO> reqVOS = getRentalItemsCount(createReqVO.getOrderNo(), true);
Map<Integer, RentalItemsCountReqVO> countMap = convertMap(reqVOS, RentalItemsCountReqVO::getRentalItemsType);
// 如果数量为0 则跳过
if (createReqVO.getNumber() == 0) {
return 0L;
}
// 判断租借类型为归还时检验归还数量是否大于剩余数量
if (createReqVO.getType() == 2 && countMap.get(createReqVO.getRentalItemsType()).getNumber() < createReqVO.getNumber()) {
throw exception(RENTAL_ITEMS_NUMBER_EXCESS);
@ -61,6 +66,8 @@ public class RentalItemsRecordServiceImpl implements RentalItemsRecordService{
List<RentalItemsCountReqVO> reqVOS = getRentalItemsCount(createReqVO.get(0).getOrderNo(), true);
Map<Integer, RentalItemsCountReqVO> countMap = convertMap(reqVOS, RentalItemsCountReqVO::getRentalItemsType);
// 移除 数量为0的数据
createReqVO.removeIf(vo -> vo.getNumber() == 0);
for (RentalItemsRecordSaveReqVO vo : createReqVO) {
// 判断租借类型为归还时检验归还数量是否大于剩余数量

View File

@ -74,4 +74,11 @@ public interface RentalOrderService {
* @return 剩余押金金额
*/
BigDecimal getOrderAmount(String orderNo);
/**
* 根据订单号获取客户id
* @param orderNo 订单号
* @return 客户id
*/
Long getCustomerIdByOrderNo(String orderNo);
}

View File

@ -27,6 +27,7 @@ import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.RENTAL_ORDER_CUSTOMER_EXISTS;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.RENTAL_ORDER_NOT_EXISTS;
/**
@ -58,26 +59,35 @@ public class RentalOrderServiceImpl implements RentalOrderService {
@Override
public Long createRentalOrder(RentalOrderSaveReqVO createReqVO) {
// 检验当前客户是否已存在订单
Long count = rentalOrderMapper.selectCount(new LambdaQueryWrapperX<RentalOrderDO>()
.eq(RentalOrderDO::getCustomerId, createReqVO.getCustomerId())
.ne(RentalOrderDO::getStatus, 4));
if (count > 0L) {
throw exception(RENTAL_ORDER_CUSTOMER_EXISTS);
}
// 新增订单DO
RentalOrderDO rentalOrder = BeanUtils.toBean(createReqVO, RentalOrderDO.class);
// 获取当前日期
String now = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String key = "rental_order_no_" + now;
// 获取分布式锁
String LOCK_KEY = "lock:rental:order:create";
RLock lock = redissonClient.getLock(LOCK_KEY);
try {
lock.lock();
// redis 获取当天订单号
String no = stringRedisTemplate.opsForValue().get(now);
String no = stringRedisTemplate.opsForValue().get(key);
if (no != null) {
no = "ZL" + now + String.format("%03d", Integer.parseInt(no) + 1);
// redis 缓存订单号
stringRedisTemplate.opsForValue().increment(now, 1);
stringRedisTemplate.opsForValue().increment(key, 1);
}else {
no = "ZL" + now + String.format("%03d", 1);
// redis 缓存订单号
stringRedisTemplate.opsForValue().set(now, "1", 1, TimeUnit.DAYS);
stringRedisTemplate.opsForValue().set(key, "1", 1, TimeUnit.DAYS);
}
// 设置订单编号
rentalOrder.setOrderNo(no);
@ -164,4 +174,13 @@ public class RentalOrderServiceImpl implements RentalOrderService {
return rentalOrderMapper.selectOrderAmount(orderNo);
}
@Override
public Long getCustomerIdByOrderNo(String orderNo) {
RentalOrderDO orderDO = rentalOrderMapper.selectOne(RentalOrderDO::getOrderNo, orderNo);
if (orderDO != null) {
return orderDO.getCustomerId();
}
return null;
}
}