|
|
@@ -0,0 +1,668 @@
|
|
|
+package cn.iocoder.yudao.module.guide.service.order;
|
|
|
+
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.map.MapUtil;
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
+import cn.hutool.extra.spring.SpringUtil;
|
|
|
+import cn.iocoder.yudao.framework.common.core.KeyValue;
|
|
|
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
|
|
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
|
|
+import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
|
|
|
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
|
+import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
|
|
+import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
|
|
+import cn.iocoder.yudao.module.guide.controller.admin.order.vo.GuideTradeOrderRemarkReqVO;
|
|
|
+import cn.iocoder.yudao.module.guide.controller.admin.order.vo.GuideTradeOrderUpdatePriceReqVO;
|
|
|
+import cn.iocoder.yudao.module.guide.controller.app.order.vo.AppGuideTradeOrderCreateReqVO;
|
|
|
+import cn.iocoder.yudao.module.guide.controller.app.order.vo.AppGuideTradeOrderSettlementRespVO;
|
|
|
+import cn.iocoder.yudao.module.guide.controller.app.order.vo.OrderSettlementReqVO;
|
|
|
+import cn.iocoder.yudao.module.guide.controller.app.order.vo.item.AppGuideTradeOrderItemCommentCreateReqVO;
|
|
|
+import cn.iocoder.yudao.module.guide.dal.dataobject.order.GuideTradeOrderDO;
|
|
|
+import cn.iocoder.yudao.module.guide.dal.dataobject.order.GuideTradeOrderItemDO;
|
|
|
+import cn.iocoder.yudao.module.guide.dal.dataobject.schedule.ScheduleDO;
|
|
|
+import cn.iocoder.yudao.module.guide.dal.dataobject.trip.TripDO;
|
|
|
+import cn.iocoder.yudao.module.guide.dal.dataobject.users.UsersDO;
|
|
|
+
|
|
|
+import cn.iocoder.yudao.module.guide.dal.mysql.order.GuideTradeOrderItemMapper;
|
|
|
+import cn.iocoder.yudao.module.guide.dal.mysql.order.GuideTradeOrderMapper;
|
|
|
+import cn.iocoder.yudao.module.guide.dal.mysql.schedule.ScheduleMapper;
|
|
|
+import cn.iocoder.yudao.module.guide.dal.mysql.trip.TripI18nExtensionMapper;
|
|
|
+import cn.iocoder.yudao.module.guide.dal.mysql.trip.TripMapper;
|
|
|
+import cn.iocoder.yudao.module.guide.dal.mysql.users.UsersMapper;
|
|
|
+import cn.iocoder.yudao.module.guide.dal.redis.no.GuideTradeNoRedisDAO;
|
|
|
+
|
|
|
+import cn.iocoder.yudao.module.guide.enums.order.TradeOrderCancelTypeEnum;
|
|
|
+import cn.iocoder.yudao.module.guide.enums.order.TradeOrderOperateTypeEnum;
|
|
|
+import cn.iocoder.yudao.module.guide.enums.order.TradeOrderRefundStatusEnum;
|
|
|
+import cn.iocoder.yudao.module.guide.enums.order.TradeOrderStatusEnum;
|
|
|
+import cn.iocoder.yudao.module.guide.framework.order.config.GuideTradeOrderProperties;
|
|
|
+import cn.iocoder.yudao.module.guide.framework.order.core.annotations.GuideTradeOrderLog;
|
|
|
+import cn.iocoder.yudao.module.guide.framework.order.core.utils.GuideTradeOrderLogUtils;
|
|
|
+import cn.iocoder.yudao.module.guide.service.cart.GuideCartService;
|
|
|
+import cn.iocoder.yudao.module.guide.service.message.GuideTradeMessageService;
|
|
|
+
|
|
|
+import cn.iocoder.yudao.module.member.api.address.MemberAddressApi;
|
|
|
+import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
|
|
|
+
|
|
|
+
|
|
|
+import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
|
|
|
+import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
|
|
|
+import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
|
|
|
+import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import jakarta.annotation.Resource;
|
|
|
+import jakarta.validation.constraints.NotNull;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
|
|
|
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
|
|
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
|
|
|
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.minusTime;
|
|
|
+import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
|
|
|
+import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getTerminal;
|
|
|
+import static cn.iocoder.yudao.module.guide.enums.ErrorCodeConstants.*;
|
|
|
+import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 交易订单【写】Service 实现类
|
|
|
+ *
|
|
|
+ * @author LeeYan9
|
|
|
+ * @since 2022-08-26
|
|
|
+ */
|
|
|
+@Service
|
|
|
+@Slf4j
|
|
|
+public class GuideTradeOrderUpdateServiceImpl implements GuideTradeOrderUpdateService {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private GuideTradeOrderMapper guideTradeOrderMapper;
|
|
|
+ @Resource
|
|
|
+ private GuideTradeOrderItemMapper guideTradeOrderItemMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ScheduleMapper scheduleMapper;
|
|
|
+ @Resource
|
|
|
+ private GuideTradeNoRedisDAO guideTradeNoRedisDAO;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private GuideCartService guideCartService;
|
|
|
+ @Resource
|
|
|
+ private GuideTradeMessageService guideTradeMessageService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private PayOrderApi payOrderApi;
|
|
|
+ @Resource
|
|
|
+ private MemberAddressApi addressApi;
|
|
|
+
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private TripMapper tripMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private TripI18nExtensionMapper tripI18nExtensionMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private UsersMapper usersMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private GuideTradeOrderProperties guideTradeOrderProperties;
|
|
|
+
|
|
|
+ // =================== Order ===================
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public AppGuideTradeOrderSettlementRespVO settlementOrder(Long userId, List<OrderSettlementReqVO> settlementReqVOs) {
|
|
|
+ //检查OrderSettlementReqVO项目是否已经被被预约,是的话,报不能建立order的错误。orderschedule
|
|
|
+ checkOrderSettlementReqVO(userId,settlementReqVOs);
|
|
|
+ AppGuideTradeOrderSettlementRespVO returnValue=new AppGuideTradeOrderSettlementRespVO();
|
|
|
+ List<AppGuideTradeOrderSettlementRespVO.Item> responseItems=new ArrayList<AppGuideTradeOrderSettlementRespVO.Item>();
|
|
|
+ returnValue.setType(0);//普通订单
|
|
|
+
|
|
|
+ Integer price=0;
|
|
|
+
|
|
|
+ for (OrderSettlementReqVO requestItem : settlementReqVOs) {
|
|
|
+ AppGuideTradeOrderSettlementRespVO.Item resItem = new AppGuideTradeOrderSettlementRespVO.Item();
|
|
|
+ resItem.setBookingDate(requestItem.getBookingDate());
|
|
|
+ resItem.setCartId(requestItem.getCartId());
|
|
|
+ UsersDO user = usersMapper.selectById(requestItem.getGuideId());
|
|
|
+ resItem.setGuideInfo(user);
|
|
|
+ TripDO trip = tripMapper.selectById(requestItem.getTripId());
|
|
|
+ Integer itemPrice = trip!=null? trip.getPrice():user.getPrice();
|
|
|
+ resItem.setPrice(itemPrice);
|
|
|
+ price += itemPrice;
|
|
|
+ if (null != trip) {
|
|
|
+ trip.setTripI18nExtensionDOList(tripI18nExtensionMapper.selectList("trip_id", requestItem.getTripId()));
|
|
|
+ resItem.setTripInfo(trip);
|
|
|
+ }responseItems.add(resItem);
|
|
|
+ }
|
|
|
+ returnValue.setItems(responseItems);
|
|
|
+ returnValue.setPrice(price);
|
|
|
+ // 3. 拼接返回
|
|
|
+ return returnValue;//TradeOrderConvert.INSTANCE.convert(calculateRespBO, address);
|
|
|
+ }
|
|
|
+ private void checkOrderSettlementReqVO(Long userId, List<OrderSettlementReqVO> settlementReqVOs){
|
|
|
+
|
|
|
+ //TradeOrderItem中有相同数据存在的check 已经有相同的注文,二重注文防止,抛异常
|
|
|
+ for (OrderSettlementReqVO requestItem : settlementReqVOs) {
|
|
|
+ GuideTradeOrderItemDO orderItem = guideTradeOrderItemMapper.selectOne(GuideTradeOrderItemDO::getUserId,userId,
|
|
|
+ GuideTradeOrderItemDO::getGuideId,requestItem.getGuideId(),
|
|
|
+ GuideTradeOrderItemDO::getBookingDate,requestItem.getBookingDate());
|
|
|
+ if (orderItem !=null){
|
|
|
+ throw exception(ORDER_SETTLEMENT_FAIL_FOUND);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //ScheduleMapper 查看是否被其他人预约
|
|
|
+ for (OrderSettlementReqVO requestItem : settlementReqVOs) {
|
|
|
+ ScheduleDO scheduleItem = scheduleMapper.selectOne(ScheduleDO::getGuideId,requestItem.getGuideId(),
|
|
|
+ ScheduleDO::getBooked,0,
|
|
|
+ ScheduleDO::getBookingDate,requestItem.getBookingDate());
|
|
|
+ if (scheduleItem ==null){
|
|
|
+ throw exception(ORDER_SETTLEMENT_FAIL_SAMEDATE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @GuideTradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CREATE)
|
|
|
+ public GuideTradeOrderDO createOrder(Long userId, AppGuideTradeOrderCreateReqVO createReqVO) {
|
|
|
+ //1.1 检查相对应的订单是否已经存在,存在并且是未支付状态的话,将订单返回,不存在的话,建立订单。
|
|
|
+ GuideTradeOrderDO guideTradeOrderDO = checkOrder(userId,createReqVO);
|
|
|
+ if (guideTradeOrderDO !=null){
|
|
|
+ return guideTradeOrderDO;
|
|
|
+ }
|
|
|
+ // 1.2 构建订单
|
|
|
+ GuideTradeOrderDO order = buildTradeOrder(userId, createReqVO);
|
|
|
+ List<GuideTradeOrderItemDO> orderItems = buildTradeOrderItems(order, createReqVO);
|
|
|
+
|
|
|
+ // 2. 订单创建前的逻辑
|
|
|
+// tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(order, orderItems));
|
|
|
+
|
|
|
+ // 3. 保存订单
|
|
|
+ guideTradeOrderMapper.insert(order);
|
|
|
+ orderItems.forEach(orderItem -> orderItem.setOrderId(order.getId()));
|
|
|
+ guideTradeOrderItemMapper.insertBatch(orderItems);
|
|
|
+
|
|
|
+ // 4. 订单创建后的逻辑
|
|
|
+ afterCreateTradeOrder(order, orderItems, createReqVO);
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+ public GuideTradeOrderDO checkOrder(Long userId, AppGuideTradeOrderCreateReqVO createReqVO) {
|
|
|
+ //检查订单明细中的所有项目是否在订单明细表中存在,并且对应的orderID是唯一的时候,返回order,如果不存在(正常情况)返回null。如果交叉存在,则抛出异常
|
|
|
+ List<OrderSettlementReqVO> OrderSettlements = createReqVO.getOrderSettlementReqVOs();
|
|
|
+
|
|
|
+ Set<Long> uniqueOrderSet = new HashSet<>();
|
|
|
+ for (OrderSettlementReqVO orderSettlement : OrderSettlements) {
|
|
|
+ GuideTradeOrderItemDO orderItem = guideTradeOrderItemMapper.selectOne(GuideTradeOrderItemDO::getUserId,userId,
|
|
|
+ GuideTradeOrderItemDO::getGuideId,orderSettlement.getGuideId(),
|
|
|
+ GuideTradeOrderItemDO::getBookingDate,orderSettlement.getBookingDate(),
|
|
|
+ GuideTradeOrderItemDO::getStatus,TradeOrderStatusEnum.UNPAID.getStatus()
|
|
|
+ );
|
|
|
+ if (orderItem !=null){
|
|
|
+ uniqueOrderSet.add(orderItem.getOrderId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (uniqueOrderSet.isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }else if (uniqueOrderSet.size()==1){
|
|
|
+ Long orderId = uniqueOrderSet.toArray(new Long[0])[0];
|
|
|
+ return guideTradeOrderMapper.selectById(orderId);
|
|
|
+ }else{
|
|
|
+ throw exception(ORDER_ITEM_FAIL_EXISTS);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ private GuideTradeOrderDO buildTradeOrder(Long userId, AppGuideTradeOrderCreateReqVO createReqVO) {
|
|
|
+ GuideTradeOrderDO order = new GuideTradeOrderDO();
|
|
|
+ order.setType(0);
|
|
|
+
|
|
|
+ order.setUserId(userId);
|
|
|
+ order.setUserRemark(createReqVO.getRemark());
|
|
|
+ order.setNo(guideTradeNoRedisDAO.generate(GuideTradeNoRedisDAO.TRADE_ORDER_NO_PREFIX));
|
|
|
+ order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
|
|
|
+ order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
|
|
|
+ order.setProductCount(createReqVO.getOrderSettlementReqVOs().size());
|
|
|
+ order.setUserIp(getClientIP()).setTerminal(getTerminal());
|
|
|
+ // 支付 + 退款信息
|
|
|
+ order.setTotalPrice(createReqVO.getPrice()).setPayStatus(false);
|
|
|
+ order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0);
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<GuideTradeOrderItemDO> buildTradeOrderItems(GuideTradeOrderDO tradeOrderDO,
|
|
|
+ AppGuideTradeOrderCreateReqVO createReqVO) {
|
|
|
+ List<GuideTradeOrderItemDO> orderItems = new ArrayList<GuideTradeOrderItemDO>();
|
|
|
+ for (OrderSettlementReqVO item:createReqVO.getOrderSettlementReqVOs()) {
|
|
|
+ GuideTradeOrderItemDO orderItem = new GuideTradeOrderItemDO();
|
|
|
+ orderItem.setGuideId(item.getGuideId());
|
|
|
+ orderItem.setTripId(item.getTripId());
|
|
|
+ orderItem.setCartId(item.getCartId());
|
|
|
+ orderItem.setBookingDate(item.getBookingDate());
|
|
|
+ orderItem.setPicUrl(item.getPicUrl());
|
|
|
+ orderItem.setPrice(item.getPrice());
|
|
|
+ orderItem.setGuideName(item.getGuideName());
|
|
|
+ orderItem.setTripTitle(item.getTripTitle());
|
|
|
+ orderItem.setOrderId(tradeOrderDO.getId());
|
|
|
+ orderItem.setUserId(tradeOrderDO.getUserId());
|
|
|
+ orderItem.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
|
|
|
+ orderItem.setCommentStatus(false);
|
|
|
+ orderItems.add(orderItem);
|
|
|
+ }
|
|
|
+ return orderItems;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单创建后,执行后置逻辑
|
|
|
+ * <p>
|
|
|
+ * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等
|
|
|
+ *
|
|
|
+ * @param order 订单
|
|
|
+ * @param orderItems 订单项
|
|
|
+ * @param createReqVO 创建订单请求
|
|
|
+ */
|
|
|
+ private void afterCreateTradeOrder(GuideTradeOrderDO order, List<GuideTradeOrderItemDO> orderItems,
|
|
|
+ AppGuideTradeOrderCreateReqVO createReqVO) {
|
|
|
+ // 1. 执行订单创建后置处理器
|
|
|
+// tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(order, orderItems));
|
|
|
+ //1. guide schedule 设置成被预约的状态。
|
|
|
+ for (GuideTradeOrderItemDO orderItemDO :orderItems) {
|
|
|
+ ScheduleDO schedule = scheduleMapper.selectOne(ScheduleDO::getGuideId,orderItemDO.getGuideId()
|
|
|
+ ,ScheduleDO::getBookingDate,orderItemDO.getBookingDate());
|
|
|
+ schedule.setBooked(true);
|
|
|
+ scheduleMapper.updateById(schedule);
|
|
|
+ }
|
|
|
+ // 2. 删除购物车商品
|
|
|
+ Set<Long> cartIds = convertSet(createReqVO.getOrderSettlementReqVOs(), OrderSettlementReqVO::getCartId);
|
|
|
+ if (CollUtil.isNotEmpty(cartIds)) {
|
|
|
+ guideCartService.deleteCart(order.getUserId(), cartIds);
|
|
|
+ }
|
|
|
+ // 3. 生成预支付
|
|
|
+ createPayOrder(order, orderItems);
|
|
|
+
|
|
|
+ // 4. 插入订单日志
|
|
|
+ GuideTradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus());
|
|
|
+
|
|
|
+ // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
|
|
|
+ }
|
|
|
+
|
|
|
+ private void createPayOrder(GuideTradeOrderDO order, List<GuideTradeOrderItemDO> orderItems) {
|
|
|
+ // 创建支付单,用于后续的支付
|
|
|
+ PayOrderCreateReqDTO createReqDTO = new PayOrderCreateReqDTO()
|
|
|
+ .setAppId(guideTradeOrderProperties.getAppId())
|
|
|
+ .setUserIp(order.getUserIp());
|
|
|
+ // 商户相关字段
|
|
|
+ createReqDTO.setMerchantOrderId(String.valueOf(order.getId()));
|
|
|
+ String subject = "["+orderItems.get(0).getGuideName()+"]-["+orderItems.get(0).getBookingDate()+"]";
|
|
|
+ subject = StrUtils.maxLength(subject, PayOrderCreateReqDTO.SUBJECT_MAX_LENGTH); // 避免超过 32 位
|
|
|
+ createReqDTO.setSubject(subject);
|
|
|
+ createReqDTO.setBody(subject); // TODO 芋艿:临时写死
|
|
|
+ // 订单相关字段
|
|
|
+ createReqDTO.setPrice(order.getTotalPrice()).setExpireTime(addTime(guideTradeOrderProperties.getPayExpireTime()));
|
|
|
+ Long payOrderId = payOrderApi.createOrder(createReqDTO);
|
|
|
+
|
|
|
+ // 更新到交易单上
|
|
|
+ guideTradeOrderMapper.updateById(new GuideTradeOrderDO().setId(order.getId()).setPayOrderId(payOrderId));
|
|
|
+ order.setPayOrderId(payOrderId);
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @GuideTradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_PAY)
|
|
|
+ public void updateOrderPaid(Long id, Long payOrderId) {
|
|
|
+ // 1. 校验并获得交易订单(可支付)
|
|
|
+ KeyValue<GuideTradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPayable(id, payOrderId);
|
|
|
+ GuideTradeOrderDO order = orderResult.getKey();
|
|
|
+ PayOrderRespDTO payOrder = orderResult.getValue();
|
|
|
+
|
|
|
+ // 2. 更新 TradeOrderDO 状态为已支付,等待发货
|
|
|
+ int updateCount = guideTradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
|
|
|
+ new GuideTradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayStatus(true)
|
|
|
+ .setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode()));
|
|
|
+ if (updateCount == 0) {
|
|
|
+ throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 执行 TradeOrderHandler 的后置处理
|
|
|
+
|
|
|
+ guideTradeOrderItemMapper.updateStatusByOrderId(id,TradeOrderStatusEnum.UNDELIVERED.getStatus());
|
|
|
+// List<GuideTradeOrderItemDO> orderItems = guideTradeOrderItemMapper.selectListByOrderId(id);
|
|
|
+// tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order, orderItems));
|
|
|
+
|
|
|
+ // 4. 记录订单日志
|
|
|
+ GuideTradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus());
|
|
|
+// GuideTradeOrderLogUtils.setUserInfo(order.getUserId(), UserTypeEnum.MEMBER.getValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验交易订单满足被支付的条件
|
|
|
+ * <p>
|
|
|
+ * 1. 交易订单未支付
|
|
|
+ * 2. 支付单已支付
|
|
|
+ *
|
|
|
+ * @param id 交易订单编号
|
|
|
+ * @param payOrderId 支付订单编号
|
|
|
+ * @return 交易订单
|
|
|
+ */
|
|
|
+ private KeyValue<GuideTradeOrderDO, PayOrderRespDTO> validateOrderPayable(Long id, Long payOrderId) {
|
|
|
+ // 校验订单是否存在
|
|
|
+ GuideTradeOrderDO order = validateOrderExists(id);
|
|
|
+ // 校验订单未支付
|
|
|
+ if (!TradeOrderStatusEnum.isUnpaid(order.getStatus()) || order.getPayStatus()) {
|
|
|
+ log.error("[validateOrderPaid][order({}) 不处于待支付状态,请进行处理!order 数据是:{}]",
|
|
|
+ id, JsonUtils.toJsonString(order));
|
|
|
+ throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
|
|
|
+ }
|
|
|
+ // 校验支付订单匹配
|
|
|
+ if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
|
|
|
+ log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!order 数据是:{}]",
|
|
|
+ id, payOrderId, JsonUtils.toJsonString(order));
|
|
|
+ throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 校验支付单是否存在
|
|
|
+ PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
|
|
|
+ if (payOrder == null) {
|
|
|
+ log.error("[validateOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId);
|
|
|
+ throw exception(ORDER_NOT_FOUND);
|
|
|
+ }
|
|
|
+ // 校验支付单已支付
|
|
|
+ if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
|
|
|
+ log.error("[validateOrderPaid][order({}) payOrder({}) 未支付,请进行处理!payOrder 数据是:{}]",
|
|
|
+ id, payOrderId, JsonUtils.toJsonString(payOrder));
|
|
|
+ throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
|
|
|
+ }
|
|
|
+ // 校验支付金额一致
|
|
|
+ if (ObjectUtil.notEqual(payOrder.getPrice(), order.getTotalPrice())) {
|
|
|
+ log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配,请进行处理!order 数据是:{},payOrder 数据是:{}]",
|
|
|
+ id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
|
|
|
+ throw exception(ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
|
|
|
+ }
|
|
|
+ // 校验支付订单匹配(二次)
|
|
|
+ if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
|
|
|
+ log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!payOrder 数据是:{}]",
|
|
|
+ id, payOrderId, JsonUtils.toJsonString(payOrder));
|
|
|
+ throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
|
|
|
+ }
|
|
|
+ return new KeyValue<>(order, payOrder);
|
|
|
+ }
|
|
|
+ @NotNull
|
|
|
+ private GuideTradeOrderDO validateOrderExists(Long id) {
|
|
|
+ // 校验订单是否存在
|
|
|
+ GuideTradeOrderDO order = guideTradeOrderMapper.selectById(id);
|
|
|
+ if (order == null) {
|
|
|
+ throw exception(ORDER_NOT_FOUND);
|
|
|
+ }
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @GuideTradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL)
|
|
|
+ public void cancelOrderByMember(Long userId, Long id) {
|
|
|
+ // 1.1 校验存在
|
|
|
+ GuideTradeOrderDO order = guideTradeOrderMapper.selectOrderByIdAndUserId(id, userId);
|
|
|
+ if (order == null) {
|
|
|
+ throw exception(ORDER_NOT_FOUND);
|
|
|
+ }
|
|
|
+ // 1.2 校验状态
|
|
|
+ if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) {
|
|
|
+ throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 取消订单
|
|
|
+ cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 取消订单的核心实现
|
|
|
+ *
|
|
|
+ * @param order 订单
|
|
|
+ * @param cancelType 取消类型
|
|
|
+ */
|
|
|
+ private void cancelOrder0(GuideTradeOrderDO order, TradeOrderCancelTypeEnum cancelType) {
|
|
|
+ // 1. 更新 TradeOrderDO 状态为已取消
|
|
|
+ int updateCount = guideTradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
|
|
|
+ new GuideTradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
|
|
|
+ .setCancelType(cancelType.getType()).setCancelTime(LocalDateTime.now()));
|
|
|
+ if (updateCount == 0) {
|
|
|
+ throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 执行 TradeOrderHandler 的后置处理
|
|
|
+ guideTradeOrderItemMapper.updateStatusByOrderId(order.getId(),TradeOrderStatusEnum.CANCELED.getStatus());
|
|
|
+// List<GuideTradeOrderItemDO> orderItems = guideTradeOrderItemMapper.selectListByOrderId(order.getId());
|
|
|
+// tradeOrderHandlers.forEach(handler -> handler.afterCancelOrder(order, orderItems));
|
|
|
+ //2. (New)需要释放schedule(已经被预约变成没有被预约)
|
|
|
+ List<GuideTradeOrderItemDO> orderItems = guideTradeOrderItemMapper.selectListByOrderId(order.getId());
|
|
|
+ for (GuideTradeOrderItemDO orderItemDO :orderItems) {
|
|
|
+ ScheduleDO schedule = scheduleMapper.selectOne(ScheduleDO::getGuideId,orderItemDO.getGuideId()
|
|
|
+ ,ScheduleDO::getBookingDate,orderItemDO.getBookingDate());
|
|
|
+ if (ObjectUtil.isNotNull(schedule)){
|
|
|
+ schedule.setBooked(false);
|
|
|
+ scheduleMapper.updateById(schedule);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 3. 增加订单日志
|
|
|
+ GuideTradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus());
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ public int cancelOrderBySystem() {
|
|
|
+ // 1. 查询过期的待支付订单
|
|
|
+ LocalDateTime expireTime = minusTime(guideTradeOrderProperties.getPayExpireTime());
|
|
|
+ List<GuideTradeOrderDO> orders = guideTradeOrderMapper.selectListByStatusAndCreateTimeLt(
|
|
|
+ TradeOrderStatusEnum.UNPAID.getStatus(), expireTime);
|
|
|
+ if (CollUtil.isEmpty(orders)) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 遍历执行,逐个取消
|
|
|
+ int count = 0;
|
|
|
+ for (GuideTradeOrderDO order : orders) {
|
|
|
+ try {
|
|
|
+ getSelf().cancelOrderBySystem(order);
|
|
|
+ count++;
|
|
|
+ } catch (Throwable e) {
|
|
|
+ log.error("[cancelOrderBySystem][order({}) 过期订单异常]", order.getId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 自动取消单个订单
|
|
|
+ *
|
|
|
+ * @param order 订单
|
|
|
+ */
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @GuideTradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL)
|
|
|
+ public void cancelOrderBySystem(GuideTradeOrderDO order) {
|
|
|
+ cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @GuideTradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_DELETE)
|
|
|
+ public void deleteOrder(Long userId, Long id) {
|
|
|
+ // 1.1 校验存在
|
|
|
+ GuideTradeOrderDO order = guideTradeOrderMapper.selectOrderByIdAndUserId(id, userId);
|
|
|
+ if (order == null) {
|
|
|
+ throw exception(ORDER_NOT_FOUND);
|
|
|
+ }
|
|
|
+ // 1.2 校验状态
|
|
|
+ if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus())) {
|
|
|
+ throw exception(ORDER_DELETE_FAIL_STATUS_NOT_CANCEL);
|
|
|
+ }
|
|
|
+ // 2. 删除订单
|
|
|
+ guideTradeOrderMapper.deleteById(id);
|
|
|
+ // 删除guideTradeOrderItem
|
|
|
+ guideTradeOrderItemMapper.delete(new QueryWrapper<GuideTradeOrderItemDO>()
|
|
|
+ .lambda()
|
|
|
+ .eq(GuideTradeOrderItemDO::getOrderId, id));
|
|
|
+ //schedule更新
|
|
|
+ List<GuideTradeOrderItemDO> orderItems = guideTradeOrderItemMapper.selectListByOrderId(order.getId());
|
|
|
+ for (GuideTradeOrderItemDO orderItemDO :orderItems) {
|
|
|
+ ScheduleDO schedule = scheduleMapper.selectOne(ScheduleDO::getGuideId,orderItemDO.getGuideId()
|
|
|
+ ,ScheduleDO::getBookingDate,orderItemDO.getBookingDate());
|
|
|
+ if (ObjectUtil.isNotNull(schedule)) {
|
|
|
+ schedule.setBooked(false);
|
|
|
+ scheduleMapper.updateById(schedule);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 3. 记录日志
|
|
|
+ GuideTradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ public void updateOrderRemark(GuideTradeOrderRemarkReqVO reqVO) {
|
|
|
+ // 校验并获得交易订单
|
|
|
+ validateOrderExists(reqVO.getId());
|
|
|
+ // 更新
|
|
|
+ GuideTradeOrderDO order = BeanUtils.toBean(reqVO, GuideTradeOrderDO.class);
|
|
|
+// GuideTradeOrderDO order = TradeOrderConvert.INSTANCE.convert(reqVO);
|
|
|
+ guideTradeOrderMapper.updateById(order);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void updateOrderItemWhenAfterSaleCreate(Long id, Long afterSaleId) {
|
|
|
+ // 更新订单项
|
|
|
+ updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(),
|
|
|
+ TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), afterSaleId);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) {
|
|
|
+ updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
|
|
|
+ TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), null);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void updateOrderItemWhenAfterSaleCancel(Long id) {
|
|
|
+ // 更新订单项
|
|
|
+ updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
|
|
|
+ TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
|
|
|
+ }
|
|
|
+ private void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
|
|
|
+ Long afterSaleId) {
|
|
|
+ // 更新订单项
|
|
|
+ int updateCount = guideTradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, afterSaleId);
|
|
|
+ if (updateCount <= 0) {
|
|
|
+ throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @GuideTradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_COMMENT)
|
|
|
+ public Long createOrderItemCommentByMember(Long userId, AppGuideTradeOrderItemCommentCreateReqVO createReqVO) {
|
|
|
+ // 1.1 先通过订单项 ID,查询订单项是否存在
|
|
|
+ GuideTradeOrderItemDO orderItem = guideTradeOrderItemMapper.selectByIdAndUserId(createReqVO.getOrderItemId(), userId);
|
|
|
+ if (orderItem == null) {
|
|
|
+ throw exception(ORDER_ITEM_NOT_FOUND);
|
|
|
+ }
|
|
|
+ // 1.2 校验订单相关状态
|
|
|
+ GuideTradeOrderDO order = guideTradeOrderMapper.selectOrderByIdAndUserId(orderItem.getOrderId(), userId);
|
|
|
+ if (order == null) {
|
|
|
+ throw exception(ORDER_NOT_FOUND);
|
|
|
+ }
|
|
|
+ if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus())) {
|
|
|
+ throw exception(ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED);
|
|
|
+ }
|
|
|
+ if (ObjectUtil.notEqual(order.getCommentStatus(), Boolean.FALSE)) {
|
|
|
+ throw exception(ORDER_COMMENT_STATUS_NOT_FALSE);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 创建评价
|
|
|
+ Long commentId = createOrderItemComment0(orderItem, createReqVO);
|
|
|
+
|
|
|
+ // 3. 如果订单项都评论了,则更新订单评价状态
|
|
|
+ List<GuideTradeOrderItemDO> orderItems = guideTradeOrderItemMapper.selectListByOrderId(order.getId());
|
|
|
+ if (!anyMatch(orderItems, item -> Objects.equals(item.getCommentStatus(), Boolean.FALSE))) {
|
|
|
+ guideTradeOrderMapper.updateById(new GuideTradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE)
|
|
|
+ .setFinishTime(LocalDateTime.now()));
|
|
|
+ // 增加订单日志。注意:只有在所有订单项都评价后,才会增加
|
|
|
+ GuideTradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
|
|
|
+ }
|
|
|
+ return commentId;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建订单项的评论的核心实现
|
|
|
+ *
|
|
|
+ * @param orderItem 订单项
|
|
|
+ * @param createReqVO 评论内容
|
|
|
+ * @return 评论编号
|
|
|
+ */
|
|
|
+ private Long createOrderItemComment0(GuideTradeOrderItemDO orderItem, AppGuideTradeOrderItemCommentCreateReqVO createReqVO) {
|
|
|
+ // TODO must
|
|
|
+ // 1. 创建评价
|
|
|
+// ProductCommentCreateReqDTO productCommentCreateReqDTO = TradeOrderConvert.INSTANCE.convert04(createReqVO, orderItem);
|
|
|
+// Long commentId = productCommentApi.createComment(productCommentCreateReqDTO);
|
|
|
+
|
|
|
+ // 2. 更新订单项评价状态
|
|
|
+ guideTradeOrderItemMapper.updateById(new GuideTradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE));
|
|
|
+// return commentId;
|
|
|
+ return 0L;
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ public int createOrderItemCommentBySystem() {
|
|
|
+ // 1. 查询过期的待支付订单
|
|
|
+ LocalDateTime expireTime = minusTime(guideTradeOrderProperties.getCommentExpireTime());
|
|
|
+ List<GuideTradeOrderDO> orders = guideTradeOrderMapper.selectListByStatusAndReceiveTimeLt(
|
|
|
+ TradeOrderStatusEnum.COMPLETED.getStatus(), expireTime, false);
|
|
|
+ if (CollUtil.isEmpty(orders)) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 遍历执行,逐个取消
|
|
|
+ int count = 0;
|
|
|
+ for (GuideTradeOrderDO order : orders) {
|
|
|
+ try {
|
|
|
+ getSelf().createOrderItemCommentBySystemBySystem(order);
|
|
|
+ count++;
|
|
|
+ } catch (Throwable e) {
|
|
|
+ log.error("[createOrderItemCommentBySystem][order({}) 过期订单异常]", order.getId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @GuideTradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_COMMENT)
|
|
|
+ public void createOrderItemCommentBySystemBySystem(GuideTradeOrderDO order) {
|
|
|
+ // 1. 查询未评论的订单项
|
|
|
+ List<GuideTradeOrderItemDO> orderItems = guideTradeOrderItemMapper.selectListByOrderIdAndCommentStatus(
|
|
|
+ order.getId(), Boolean.FALSE);
|
|
|
+ if (CollUtil.isEmpty(orderItems)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 逐个评论
|
|
|
+ for (GuideTradeOrderItemDO orderItem : orderItems) {
|
|
|
+ // 2.1 创建评价
|
|
|
+ AppGuideTradeOrderItemCommentCreateReqVO commentCreateReqVO = new AppGuideTradeOrderItemCommentCreateReqVO()
|
|
|
+ .setOrderItemId(orderItem.getId()).setAnonymous(false).setContent("")
|
|
|
+ .setBenefitScores(5).setDescriptionScores(5);
|
|
|
+ createOrderItemComment0(orderItem, commentCreateReqVO);
|
|
|
+
|
|
|
+ // 2.2 更新订单项评价状态
|
|
|
+ guideTradeOrderItemMapper.updateById(new GuideTradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 所有订单项都评论了,则更新订单评价状态
|
|
|
+ guideTradeOrderMapper.updateById(new GuideTradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE)
|
|
|
+ .setFinishTime(LocalDateTime.now()));
|
|
|
+ // 增加订单日志。注意:只有在所有订单项都评价后,才会增加
|
|
|
+ GuideTradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void cancelPaidOrder(Long userId, Long orderId) {
|
|
|
+ // TODO 芋艿:这里实现要优化下;
|
|
|
+ GuideTradeOrderDO order = guideTradeOrderMapper.selectOrderByIdAndUserId(orderId, userId);
|
|
|
+ if (order == null) {
|
|
|
+ throw exception(ORDER_NOT_FOUND);
|
|
|
+ }
|
|
|
+ cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
|
|
|
+ }
|
|
|
+ private GuideTradeOrderUpdateServiceImpl getSelf() {
|
|
|
+ return SpringUtil.getBean(getClass());
|
|
|
+ }
|
|
|
+}
|