This commit is contained in:
邱贞招 2025-03-27 17:32:49 +08:00
parent 5d91db94b1
commit 2cc3bb7948
12 changed files with 256 additions and 143 deletions

View File

@ -2,6 +2,8 @@ package com.ruoyi.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.ss.cardOrder.service.ICardOrderAssembler;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@ -36,6 +38,9 @@ public class CardOrderController extends BaseController
@Autowired
private ICardOrderService cardOrderService;
@Autowired
private ICardOrderAssembler cardOrderAssembler;
/**
* 查询卡券订单列表
*/
@ -46,6 +51,7 @@ public class CardOrderController extends BaseController
startPage();
startOrderBy();
List<CardOrderVO> list = cardOrderService.selectCardOrderList(query);
cardOrderAssembler.assembleAvailableRoom(list);
return getDataTable(list);
}

View File

@ -1370,5 +1370,19 @@ public class ServiceConstants {
public static final String VALIDITY_UNIT_YEAR = "year";
/**----------------------------有效期单位end----------------------------*/
/**---------------------------- 卡券类型 ----------------------------*/
/**
* 卡券类型1-平台卡
*/
public static final String CARD_TYPE_PLATFORM = "1";
/**
* 卡券类型2-商户卡
*/
public static final String CARD_TYPE_MERCHANT = "2";
/**---------------------------- 卡券类型 ----------------------------*/
}

View File

@ -262,20 +262,23 @@ public class CallbackServiceImpl implements ICallbackService {
4. 更新运营商账变
*/
Boolean execute = transactionTemplate.execute(e -> {
String cardType;
if(appId.equals(cardOrderVO.getAppId())){
//先判断是否是平台卡, 如果是平台卡则只有等到卡券被使用后已全部完成后才能进行结算
//先到待结算余额等用户卡券使用后再将待结算余额转为可用余额 待结算金额
// 更新商户待结算金额
int i = userService.updateUserUnsettledAmount(merchant.getUserId(), cardOrderVO.getPayFee());
ServiceUtil.assertion(i == 0, "【支付回调】卡券订单,更新商户待结算金额失败");
cardType = CARD_TYPE_PLATFORM;
}else{
/* 记录账变 */
int i = changeBalanceService.generateChangeBalance(cardOrderVO.getOrderNo(), payBillVO.getOrderNo(), FLOW_TYPE_INCOME,
ServiceConstants.COUPON_INCOME, cardOrderVO.getPayFee(), merchant.getUserId(), merchant.getUserName(),
merchant.getPhonenumber(),merchant.getUserType());
ServiceUtil.assertion(i == 0, "【支付回调】卡券订单,更新记录账变失败");
cardType = CARD_TYPE_MERCHANT;
}
saveUserCard(payTime, cardOrderVO);
saveUserCard(payTime, cardOrderVO, cardType);
updateCardOrder(cardOrderVO,payTime,payBillVO);
logger.info("【支付回调】----------更新卡券订单表成功----------");
@ -322,15 +325,18 @@ public class CallbackServiceImpl implements ICallbackService {
if(Boolean.FALSE.equals(execute))throw new ServiceException(" 0元订单处理失败");
}
private void saveUserCard(LocalDateTime payTime, CardOrderVO cardOrderVO) {
private void saveUserCard(LocalDateTime payTime, CardOrderVO cardOrderVO, String cardType) {
UserCard cardGetRecords = new UserCard();
cardGetRecords.setStatus(ServiceConstants.CARD_STATUS_UNUSED);
cardGetRecords.setUserId(cardOrderVO.getUserId());
cardGetRecords.setCouponId(cardOrderVO.getCardId());
cardGetRecords.setGetMethod(COUPON_GET_PLATFORM_PURCHASE);
cardGetRecords.setOrderNo(cardOrderVO.getOrderNo());
cardGetRecords.setStoredAmount(cardOrderVO.getStoredAmount());
cardGetRecords.setReceiveTime(payTime);
cardGetRecords.setStartTime(payTime);
cardGetRecords.setCardType(cardType);
cardGetRecords.setType(cardOrderVO.getType());
cardGetRecords.setEndTime(DateUtils.calculateEndTime(payTime, cardOrderVO.getValidityValue(), cardOrderVO.getValidityUnit()));
int cardInsertResult = userCardService.insertUserCard(cardGetRecords);

View File

@ -142,13 +142,13 @@ public class CardOrder extends BaseEntity{
@ApiModelProperty("可使用周: 1、2、3、4、5、6")
private List<String> availableWeek;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "可用时间结束", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "HH:mm:ss")
@Excel(name = "可用时间结束", width = 30, dateFormat = "HH:mm:ss")
@ApiModelProperty("可用时间结束")
private LocalTime availableTimeEnd;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "可用时间开始", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "HH:mm:ss")
@Excel(name = "可用时间开始", width = 30, dateFormat = "HH:mm:ss")
@ApiModelProperty("可用时间开始")
private LocalTime availableTimeStart;

View File

@ -1,7 +1,19 @@
package com.ruoyi.ss.cardOrder.domain;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
public class CardOrderVO extends CardOrder{
@ApiModelProperty("商户")
private String merchantName;
@Excel(name = "可用房间")
@ApiModelProperty("可用房间")
private List<String> availableRoomName;
}

View File

@ -4,47 +4,52 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.ss.cardOrder.mapper.CardOrderMapper">
<resultMap type="CardOrderVO" id="CardOrderResult" autoMapping="true" />
<resultMap type="CardOrderVO" id="CardOrderResult" autoMapping="true" >
<result property="availableRooms" column="available_rooms" typeHandler="com.ruoyi.system.mapper.typehandler.LongSplitListTypeHandler"/>
<result property="availableWeek" column="available_week" typeHandler="com.ruoyi.system.mapper.typehandler.StringSplitListTypeHandler"/>
</resultMap>
<sql id="selectCardOrderVo">
select
order_id,
order_no,
user_id,
phone,
user_name,
real_name,
pay_time,
pay_type,
paid,
pay_id,
type,
total_fee,
pay_fee,
mark,
status,
create_time,
pay_channel,
channel_name,
channel_cost,
merchant_id,
store_id,
store_name,
card_id,
card_name,
limit_num,
stored_amount,
validity_value,
validity_unit,
available_rooms,
available_week,
available_time_end,
available_time_start,
sku_id,
hours,
app_id,
card_type
from ss_card_order
co.order_id,
co.order_no,
co.user_id,
co.phone,
co.user_name,
co.real_name,
co.pay_time,
co.pay_type,
co.paid,
co.pay_id,
co.type,
co.total_fee,
co.pay_fee,
co.mark,
co.status,
co.create_time,
co.pay_channel,
co.channel_name,
co.channel_cost,
co.merchant_id,
m.user_name merchantName,
co.store_id,
co.store_name,
co.card_id,
co.card_name,
co.limit_num,
co.stored_amount,
co.validity_value,
co.validity_unit,
co.available_rooms,
co.available_week,
co.available_time_end,
co.available_time_start,
co.sku_id,
co.hours,
co.app_id,
co.card_type
from ss_card_order co
left join ss_user m on m.user_id = co.merchant_id
</sql>
<sql id="searchCondition">

View File

@ -0,0 +1,23 @@
package com.ruoyi.ss.cardOrder.service;
import com.ruoyi.ss.cardOrder.domain.CardOrderVO;
import java.util.List;
/**
* 卡券订单组装器
* @author qzz
* 2024/4/29
*/
public interface ICardOrderAssembler {
/**
* 查询卡券订单列表
*
* @param list 卡券订单
* @return 卡券订单集合
*/
void assembleAvailableRoom(List<CardOrderVO> list);
}

View File

@ -0,0 +1,66 @@
package com.ruoyi.ss.cardOrder.service.impl;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.ss.cardOrder.domain.CardOrderVO;
import com.ruoyi.ss.cardOrder.service.ICardOrderAssembler;
import com.ruoyi.ss.room.domain.RoomVO;
import com.ruoyi.ss.room.service.IRoomService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author qzz
* 2024/4/29
*/
@Service
public class CardOrderAssemblerImpl implements ICardOrderAssembler {
@Autowired
private IRoomService roomService;
/**
* 查询卡券订单列表
*
* @param list 卡券订单
* @return 卡券订单集合
*/
@Override
public void assembleAvailableRoom(List<CardOrderVO> list) {
if (CollectionUtils.isEmpty(list)) {
return;
}
// 1. 收集所有房间ID去重
Set<Long> roomIds = list.stream()
.filter(order -> CollectionUtils.isNotEmpty(order.getAvailableRooms()))
.flatMap(order -> order.getAvailableRooms().stream())
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(roomIds)) {
return;
}
// 2. 批量查询房间信息
Map<Long, String> roomIdNameMap = roomService.selectERoomByRoomIds(new ArrayList<>(roomIds))
.stream()
.collect(Collectors.toMap(RoomVO::getRoomId, RoomVO::getRoomName));
// 3. 组装房间名称列表
list.forEach(order -> {
if (CollectionUtils.isNotEmpty(order.getAvailableRooms())) {
order.setAvailableRoomName(
order.getAvailableRooms().stream()
.map(roomId -> roomIdNameMap.getOrDefault(roomId, "未知房间"))
.collect(Collectors.toList())
);
}
});
}
}

View File

@ -87,7 +87,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
o.refund_duration,
o.reminder_time,
o.is_reminded,
o.app_id
o.app_id,
o.user_card_id
FROM
ss_order o
left join ss_user u on o.user_id = u.user_id
@ -496,6 +497,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="productName != null">product_name,</if>
<if test="reminderTime != null">reminder_time,</if>
<if test="appId != null">app_id,</if>
<if test="userCardId != null">user_card_id,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="orderNo != null and orderNo != ''">#{orderNo},</if>
@ -560,6 +562,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="productName != null">#{productName},</if>
<if test="reminderTime != null">#{reminderTime},</if>
<if test="appId != null">#{appId},</if>
<if test="userCardId != null">#{userCardId},</if>
</trim>
</insert>
@ -636,6 +639,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="reminderTime != null">reminder_time = #{reminderTime},</if>
<if test="isReminded != null">is_reminded = #{isReminded},</if>
<if test="appId != null">app_id = #{appId},</if>
<if test="userCardId != null">user_card_id = #{userCardId}</if>
</trim>
where order_id = #{orderId}
</update>
@ -707,6 +711,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="reminderTime != null">reminder_time = #{reminderTime},</if>
<if test="isReminded != null">is_reminded = #{isReminded},</if>
<if test="appId != null">app_id = #{appId},</if>
<if test="userCardId != null">user_card_id = #{userCardId}</if>
</trim>
where order_no = #{orderNo}
</update>

View File

@ -195,6 +195,9 @@ public class OrderServiceImpl implements IOrderService
@Value("${aliyun.templateCode3}")
private String templateCode3;
@Value("${wx.appid}")
private String appId;
@Autowired
private IStoreService storeService;
@ -696,103 +699,6 @@ public class OrderServiceImpl implements IOrderService
throw new ServiceException("非法的订单类型");
}
// 优惠券处理逻辑
if (orderBO.getUserCardVO() != null) {
UserCardVO cardVO = orderBO.getUserCardVO();
SsCardVO ssCardVO = cardService.selectSsCardByCouponId(cardVO.getCouponId());
order.setUserCardId(cardVO.getRecordId());
// 基础校验
cardUsePreCheck(orderBO, ssCardVO);
// 创建更新对象
UserCard updatedUserCard = new UserCard();
updatedUserCard.setRecordId(cardVO.getRecordId());
// 根据卡券类型处理
CouponType type = CouponType.fromCode(ssCardVO.getType());
switch (type) {
case STORED_VALUE_CARD:
handleStoredValueCard(order, cardVO, updatedUserCard);
break;
case HOUR_CARD:
handleHourCard(order, cardVO, updatedUserCard);
break;
case TIMES_CARD:
handleTimesCard(order, cardVO, updatedUserCard);
break;
case GROUP_BUYING:
handleGroupBuyingCoupon(order, ssCardVO, updatedUserCard);
break;
}
// 更新卡券信息
int result = userCardService.updateUserCard(updatedUserCard);
ServiceUtil.assertion(result == 0, "更新卡券余额失败");
if(CARD_STATUS_USED.equals(updatedUserCard.getStatus())){// 如果卡券使用后则处理商户结算金额
// updatedUserCard.get
}
}
// if(orderBO.getUserCardVO() != null){
// UserCardVO cardVO = orderBO.getUserCardVO();
// SsCardVO ssCardVO = cardService.selectSsCardByCouponId(cardVO.getCouponId());
//
// // 使用前校验
// cardUsePreCheck(orderBO, ssCardVO);
//
// String type = ssCardVO.getType();
// UserCard updatedUserCard = new UserCard();
// updatedUserCard.setCouponId(cardVO.getCouponId());
//
// if(COUPON_ORDER_TYPE_STORED_VALUE_CARD.equals(type)){// 储值卡 剩余金额是否大于订单支付价格如果大于订单支付价格则可以使用如果不是则不可用
// ServiceUtil.assertion(cardVO.getStoredAmount().compareTo(order.getPayFee()) < 0, "储值卡余额不足");
// BigDecimal subtract = cardVO.getStoredAmount().subtract(order.getPayFee());
// order.setPayFee(BigDecimal.ZERO);
//
// updatedUserCard.setStoredAmount(subtract);
// if(subtract.compareTo(BigDecimal.ZERO) == 0){
// updatedUserCard.setStatus(CARD_STATUS_USED);
// }else{
// updatedUserCard.setStatus(CARD_STATUS_USING);
// }
//
// }else if(COUPON_ORDER_TYPE_HOUR_CARD.equals(type)){// 小时卡
// ServiceUtil.assertion(cardVO.getRemainingHours() - order.getDuration() < 0, "可使用小时不足");
// order.setPayFee(BigDecimal.ZERO);
// updatedUserCard.setRemainingHours(cardVO.getRemainingHours() - order.getDuration());
// updatedUserCard.setUsedHours(cardVO.getUsedHours() + order.getDuration());
// if(cardVO.getRemainingHours() - order.getDuration() == 0){
// updatedUserCard.setStatus(CARD_STATUS_USED);
// }else{
// updatedUserCard.setStatus(CARD_STATUS_USING);
// }
// }else if(COUPON_ORDER_TYPE_TIMES_CARD.equals(type)){// 次卡
// ServiceUtil.assertion(cardVO.getRemainingTimes() < 1, "可使用次数不足");
// order.setPayFee(BigDecimal.ZERO);
// updatedUserCard.setRemainingTimes(cardVO.getRemainingTimes() - 1);
// updatedUserCard.setUsedTimes(cardVO.getUsedTimes() + 1);
// if(cardVO.getRemainingTimes() == 0){
// updatedUserCard.setStatus(CARD_STATUS_USED);
// }else{
// updatedUserCard.setStatus(CARD_STATUS_USING);
// }
// }else if(COUPON_ORDER_TYPE_GROUP_BUYING_COUPON.equals(type)){// 团购券
// BigDecimal subtract = order.getPayFee().subtract(ssCardVO.getStoredAmount());
// if(subtract.compareTo(BigDecimal.ZERO) < 0){
// subtract = BigDecimal.ZERO;
// }
// order.setPayFee(subtract);
// updatedUserCard.setStoredAmount(BigDecimal.ZERO);
// updatedUserCard.setStatus(CARD_STATUS_USED);
// updatedUserCard.setUsedAmount(ssCardVO.getStoredAmount());
// }
//
// int i = userCardService.updateUserCard(updatedUserCard);
// ServiceUtil.assertion(i == 0, "更新卡券余额失败");
// }
//基本信息
order.setCreateTime(DateUtils.getNowDate());
order.setOrderNo(orderNo);
@ -831,9 +737,58 @@ public class OrderServiceImpl implements IOrderService
order.setAutoCancelTime(DateUtils.getTimeAfterXMinutes(order.getCreateTime(),Integer.parseInt(configByKey)));
// 校验
ServiceUtil.assertion(orderValidator.afterAddOrder(order));
// 优惠券处理逻辑
couponHandle(orderBO, order);
return order;
}
private void couponHandle(OrderBO orderBO, Order order) {
if (orderBO.getUserCardVO() != null) {
UserCardVO cardVO = orderBO.getUserCardVO();
SsCardVO ssCardVO = cardService.selectSsCardByCouponId(cardVO.getCouponId());
order.setUserCardId(cardVO.getRecordId());
// 基础校验
cardUsePreCheck(orderBO, ssCardVO);
// 创建更新对象
UserCard updatedUserCard = new UserCard();
updatedUserCard.setRecordId(cardVO.getRecordId());
// 根据卡券类型处理
CouponType type = CouponType.fromCode(ssCardVO.getType());
switch (type) {
case STORED_VALUE_CARD:
handleStoredValueCard(order, cardVO, updatedUserCard);
break;
case HOUR_CARD:
handleHourCard(order, cardVO, updatedUserCard);
break;
case TIMES_CARD:
handleTimesCard(order, cardVO, updatedUserCard);
break;
case GROUP_BUYING:
handleGroupBuyingCoupon(order, ssCardVO, updatedUserCard);
break;
}
// 更新卡券信息
int result = userCardService.updateUserCard(updatedUserCard);
ServiceUtil.assertion(result == 0, "更新卡券余额失败");
if(CARD_STATUS_USED.equals(updatedUserCard.getStatus())){// 如果卡券使用后则处理商户结算金额
UserVO merchant = orderBO.getMerchant();
BigDecimal retailPrice = ssCardVO.getRetailPrice();
merchant.setUnsettledAmount(merchant.getUnsettledAmount().subtract(retailPrice));
merchant.setBalance(merchant.getBalance().add(retailPrice));
merchant.setUserId(merchant.getUserId());
int i = userService.updateUser(merchant);
ServiceUtil.assertion(i == 0, "更新商户结算金额失败");
}
}
}
// 储值卡处理
private void handleStoredValueCard(Order order, UserCardVO cardVO, UserCard updatedUserCard) {
ServiceUtil.assertion(cardVO.getStoredAmount().compareTo(order.getPayFee()) < 0,"储值卡余额不足");
@ -935,6 +890,11 @@ public class OrderServiceImpl implements IOrderService
cardOrder.setPayFee(orderBO.getCard().getRetailPrice());
cardOrder.setStoredAmount(orderBO.getCard().getStoredAmount());
cardOrder.setAppId(orderBO.getApp().getConfig().getAppId());
cardOrder.setCardType(
appId.equals(orderBO.getApp().getConfig().getAppId())
? CARD_TYPE_PLATFORM
: CARD_TYPE_MERCHANT
);
//用户
cardOrder.setUserId(orderBO.getUser().getUserId());
@ -963,6 +923,7 @@ public class OrderServiceImpl implements IOrderService
cardOrder.setAvailableTimeEnd(orderBO.getCard().getAvailableTimeEnd());
cardOrder.setSkuId(orderBO.getCard().getSkuId());
cardOrder.setHours(orderBO.getCard().getHours());
return cardOrder;
}

View File

@ -39,6 +39,10 @@ public class UserCard extends BaseEntity{
@ApiModelProperty("获取方式")
private String getMethod;
@Excel(name = "订单号")
@ApiModelProperty("订单号")
private String orderNo;
@Excel(name = "状态")
@ApiModelProperty("状态")
private String status;
@ -75,4 +79,7 @@ public class UserCard extends BaseEntity{
@ApiModelProperty("卡券归属类型1-平台卡2-商户卡")
private String cardType;
@ApiModelProperty("类型1-储值卡;2-小时卡;3-团购券;4-次卡")
private String type;
}

View File

@ -23,7 +23,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
cr.remaining_hours,
cr.used_times,
cr.used_amount,
cr.used_hours
cr.used_hours,
cr.card_type,
cr.type
from ss_user_card cr
left join ss_user u on cr.user_id = u.user_id
left join ss_card c on cr.coupon_id = c.coupon_id
@ -67,6 +69,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="usedTimes != null">used_times,</if>
<if test="usedAmount != null">used_amount,</if>
<if test="usedHours != null">used_hours,</if>
<if test="cardType != null and cardType != ''">card_type,</if>
<if test="type != null and type != ''">type,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if>
@ -82,6 +86,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="usedTimes != null">#{usedTimes},</if>
<if test="usedAmount != null">#{usedAmount},</if>
<if test="usedHours != null">#{usedHours},</if>
<if test="cardType != null and cardType != ''">#{cardType},</if>
<if test="type != null and type != ''">#{type},</if>
</trim>
</insert>
@ -107,6 +113,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.usedTimes != null">used_times = #{data.usedTimes},</if>
<if test="data.usedAmount != null">used_amount = #{data.usedAmount},</if>
<if test="data.usedHours != null">used_hours = #{data.usedHours},</if>
<if test="data.cardType != null and data.cardType != ''">card_type = #{data.cardType},</if>
<if test="data.type != null and data.type != ''">type = #{data.type},</if>
</sql>
<delete id="deleteUserCardById" parameterType="Long">