From f3604d983001fd79467c6daab5a135739e68c9ab Mon Sep 17 00:00:00 2001 From: JIAN Date: Tue, 10 Sep 2024 13:52:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(orders.manager):=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E6=9C=AA=E6=94=AF=E4=BB=98=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E7=9A=84=E5=8A=9F=E8=83=BD=20=201.=20=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E8=AE=BF=E9=97=AE=E5=8F=96=E6=B6=88=202.=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E8=BD=AE=E8=AF=A2=E5=8F=96=E6=B6=88=203.=20=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E6=97=B6=E8=87=AA=E5=8A=A8=E5=8F=96=E6=B6=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../base/model/domain/OrdersCanceled.java | 13 +- .../consumer/ConsumerOrdersController.java | 21 +++ .../inner/InnerOrdersController.java | 4 +- .../orders/manager/job/OrderCancelJob.java | 38 +++++ .../service/IOrdersManagerService.java | 41 +++-- .../impl/OrdersManagerServiceImpl.java | 152 +++++++++++++++--- 6 files changed, 221 insertions(+), 48 deletions(-) create mode 100644 jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/job/OrderCancelJob.java diff --git a/jzo2o-oreders/jzo2o-orders-base/src/main/java/com/jzo2o/orders/base/model/domain/OrdersCanceled.java b/jzo2o-oreders/jzo2o-orders-base/src/main/java/com/jzo2o/orders/base/model/domain/OrdersCanceled.java index bd7a5f4..484156a 100644 --- a/jzo2o-oreders/jzo2o-orders-base/src/main/java/com/jzo2o/orders/base/model/domain/OrdersCanceled.java +++ b/jzo2o-oreders/jzo2o-orders-base/src/main/java/com/jzo2o/orders/base/model/domain/OrdersCanceled.java @@ -1,23 +1,26 @@ package com.jzo2o.orders.base.model.domain; -import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.IdType; -import java.time.LocalDateTime; import com.baomidou.mybatisplus.annotation.TableId; -import java.io.Serializable; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; +import java.io.Serializable; +import java.time.LocalDateTime; + /** *

- * + * *

* * @author itcast * @since 2023-08-19 */ @Data +@Builder @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("orders_canceled") @@ -65,4 +68,4 @@ public class OrdersCanceled implements Serializable { * 更新时间 */ private LocalDateTime updateTime; -} +} \ No newline at end of file diff --git a/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/controller/consumer/ConsumerOrdersController.java b/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/controller/consumer/ConsumerOrdersController.java index b6a5345..e05dc99 100644 --- a/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/controller/consumer/ConsumerOrdersController.java +++ b/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/controller/consumer/ConsumerOrdersController.java @@ -1,9 +1,13 @@ package com.jzo2o.orders.manager.controller.consumer; +import com.jzo2o.api.orders.dto.request.OrderCancelReqDTO; import com.jzo2o.api.orders.dto.response.OrderResDTO; import com.jzo2o.api.orders.dto.response.OrderSimpleResDTO; +import com.jzo2o.common.expcetions.RequestUnauthorizedException; +import com.jzo2o.common.model.CurrentUserInfo; import com.jzo2o.common.utils.ObjectUtils; import com.jzo2o.mvc.utils.UserContext; +import com.jzo2o.orders.manager.model.dto.OrderCancelDTO; import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO; import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO; import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO; @@ -81,4 +85,21 @@ public class ConsumerOrdersController { public OrdersPayResDTO payResult(@PathVariable("id") Long id) { return ordersCreateService.getPayResult(id); } + + @PutMapping("/cancel") + @ApiOperation("取消订单") + public void cancel(@RequestBody OrderCancelReqDTO orderCancelReqDTO) { + CurrentUserInfo currentUser = UserContext.currentUser(); + if (ObjectUtils.isEmpty(currentUser) || ObjectUtils.isEmpty(currentUser.getId())) { + throw new RequestUnauthorizedException("无法获取当前用户信息"); + } + + ordersManagerService.cancel(OrderCancelDTO.builder() + .id(orderCancelReqDTO.getId()) + .cancelReason(orderCancelReqDTO.getCancelReason()) + .currentUserId(currentUser.getId()) + .currentUserName(currentUser.getName()) + .currentUserType(currentUser.getUserType()) + .build()); + } } \ No newline at end of file diff --git a/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/controller/inner/InnerOrdersController.java b/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/controller/inner/InnerOrdersController.java index b32f7ba..c14450b 100644 --- a/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/controller/inner/InnerOrdersController.java +++ b/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/controller/inner/InnerOrdersController.java @@ -33,7 +33,7 @@ public class InnerOrdersController implements OrdersApi { @ApiImplicitParam(name = "id", value = "订单id", required = true, dataTypeClass = Long.class), }) public OrderResDTO queryById(@PathVariable("id") Long id) { - Orders orders = ordersManagerService.queryById(id); + Orders orders = ordersManagerService.getById(id); return BeanUtil.toBean(orders, OrderResDTO.class); } @@ -57,4 +57,4 @@ public class InnerOrdersController implements OrdersApi { public void evaluate(@PathVariable("id") Long id) { ordersManagerService.evaluationOrder(id); } -} +} \ No newline at end of file diff --git a/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/job/OrderCancelJob.java b/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/job/OrderCancelJob.java new file mode 100644 index 0000000..faaa839 --- /dev/null +++ b/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/job/OrderCancelJob.java @@ -0,0 +1,38 @@ +package com.jzo2o.orders.manager.job; + +import com.jzo2o.common.utils.CollUtils; +import com.jzo2o.orders.base.model.domain.Orders; +import com.jzo2o.orders.manager.service.IOrdersManagerService; +import com.xxl.job.core.context.XxlJobHelper; +import com.xxl.job.core.handler.annotation.XxlJob; +import org.springframework.stereotype.Component; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 自动轮询取消订单 + * @author JIAN + */ +@Component +@SuppressWarnings("unused") +public class OrderCancelJob { + @Resource + private IOrdersManagerService ordersManagerService; + @Resource + private TransactionTemplate transactionTemplate; + + @XxlJob("cancelPayOverTimeOrder") + public void cancelPayOverTimeOrder() { + List orderList = ordersManagerService.getPayOverTimeOrder(100); + if (CollUtils.isEmpty(orderList)) { + XxlJobHelper.log("没有超时订单"); + return; + } + + // 取消所有超时未支付订单 + transactionTemplate.executeWithoutResult(status -> + orderList.forEach(order -> ordersManagerService.cancelPayOverTimeOrder(order.getId()))); + } +} \ No newline at end of file diff --git a/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/service/IOrdersManagerService.java b/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/service/IOrdersManagerService.java index ed1301c..ff70fd8 100644 --- a/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/service/IOrdersManagerService.java +++ b/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/service/IOrdersManagerService.java @@ -1,21 +1,10 @@ package com.jzo2o.orders.manager.service; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; -import com.jzo2o.api.market.dto.request.CouponUseBackReqDTO; -import com.jzo2o.api.market.dto.response.AvailableCouponsResDTO; import com.jzo2o.api.orders.dto.response.OrderResDTO; import com.jzo2o.api.orders.dto.response.OrderSimpleResDTO; -import com.jzo2o.common.model.PageResult; -import com.jzo2o.common.model.msg.TradeStatusMsg; import com.jzo2o.orders.base.model.domain.Orders; import com.jzo2o.orders.manager.model.dto.OrderCancelDTO; -import com.jzo2o.orders.manager.model.dto.request.OrderPageQueryReqDTO; -import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO; -import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO; -import com.jzo2o.orders.manager.model.dto.response.OperationOrdersDetailResDTO; -import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO; -import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO; import java.util.List; @@ -23,23 +12,17 @@ import java.util.List; *

* 订单表 服务类 *

- * * @author itcast * @since 2023-07-10 */ public interface IOrdersManagerService extends IService { - /** - * @param ids - * @return + * 查询对应订单id的所有订单信息 */ List batchQuery(List ids); - Orders queryById(Long id); - /** * 滚动分页查询 - * * @param currentUserId 当前用户id * @param ordersStatus 订单状态,0:待支付,100:派单中,200:待服务,300:服务中,400:待评价,500:订单完成,600:已取消,700:已关闭 * @param sortBy 排序字段 @@ -47,19 +30,33 @@ public interface IOrdersManagerService extends IService { */ List consumerQueryList(Long currentUserId, Integer ordersStatus, Long sortBy); - /** * 根据订单id查询 - * * @param id 订单id * @return 订单详情 */ OrderResDTO getDetail(Long id); + /** * 订单评价 - * * @param ordersId 订单id */ void evaluationOrder(Long ordersId); -} + /** + * 取消订单 + */ + void cancel(OrderCancelDTO orderCancelDTO); + + /** + * 系统取消超时订单(无前置判断) + *
仅内部使用!!! + */ + void cancelPayOverTimeOrder(Long id); + + /** + * 获取支付超时订单 + * @param count 订单数量 + */ + List getPayOverTimeOrder(Integer count); +} \ No newline at end of file diff --git a/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/service/impl/OrdersManagerServiceImpl.java b/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/service/impl/OrdersManagerServiceImpl.java index 129a217..9b81f40 100644 --- a/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/service/impl/OrdersManagerServiceImpl.java +++ b/jzo2o-oreders/jzo2o-orders-manager/src/main/java/com/jzo2o/orders/manager/service/impl/OrdersManagerServiceImpl.java @@ -1,7 +1,6 @@ package com.jzo2o.orders.manager.service.impl; import cn.hutool.core.bean.BeanUtil; -import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.core.toolkit.Wrappers; @@ -9,16 +8,30 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.jzo2o.api.orders.dto.response.OrderResDTO; import com.jzo2o.api.orders.dto.response.OrderSimpleResDTO; +import com.jzo2o.api.trade.TradingApi; +import com.jzo2o.api.trade.dto.response.TradingResDTO; +import com.jzo2o.api.trade.enums.TradingStateEnum; +import com.jzo2o.common.constants.UserType; import com.jzo2o.common.enums.EnableStatusEnum; +import com.jzo2o.common.expcetions.DBException; +import com.jzo2o.common.expcetions.ForbiddenOperationException; import com.jzo2o.common.utils.ObjectUtils; +import com.jzo2o.orders.base.enums.OrderStatusEnum; import com.jzo2o.orders.base.mapper.OrdersMapper; import com.jzo2o.orders.base.model.domain.Orders; -import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO; +import com.jzo2o.orders.base.model.domain.OrdersCanceled; +import com.jzo2o.orders.base.model.dto.OrderUpdateStatusDTO; +import com.jzo2o.orders.base.service.IOrdersCommonService; +import com.jzo2o.orders.manager.model.dto.OrderCancelDTO; +import com.jzo2o.orders.manager.service.IOrdersCanceledService; import com.jzo2o.orders.manager.service.IOrdersManagerService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionTemplate; +import javax.annotation.Resource; +import java.time.LocalDateTime; import java.util.List; import static com.jzo2o.orders.base.constants.FieldConstants.SORT_BY; @@ -27,28 +40,36 @@ import static com.jzo2o.orders.base.constants.FieldConstants.SORT_BY; *

* 订单表 服务实现类 *

- * * @author itcast * @since 2023-07-10 */ @Slf4j @Service public class OrdersManagerServiceImpl extends ServiceImpl implements IOrdersManagerService { + @Resource + private IOrdersCommonService ordersCommonService; + @Resource + private IOrdersCanceledService ordersCanceledService; + @Resource + private TransactionTemplate transactionTemplate; + @Resource + private TradingApi tradingApi; + + /** + * 订单超时时间 + */ + private final long PAY_OVERTIME_MINUTE = 15; @Override public List batchQuery(List ids) { - LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery().in(Orders::getId, ids).ge(Orders::getUserId, 0); + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery() + .in(Orders::getId, ids) + .ge(Orders::getUserId, 0); return baseMapper.selectList(queryWrapper); } - @Override - public Orders queryById(Long id) { - return baseMapper.selectById(id); - } - /** * 滚动分页查询 - * * @param currentUserId 当前用户id * @param ordersStatus 订单状态,0:待支付,100:派单中,200:待服务,300:服务中,400:待评价,500:订单完成,600:已取消,700:已关闭 * @param sortBy 排序字段 @@ -56,7 +77,7 @@ public class OrdersManagerServiceImpl extends ServiceImpl */ @Override public List consumerQueryList(Long currentUserId, Integer ordersStatus, Long sortBy) { - //1.构件查询条件 + // 1.构造查询条件 LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery() .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrdersStatus, ordersStatus) .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy) @@ -66,29 +87,46 @@ public class OrdersManagerServiceImpl extends ServiceImpl queryPage.addOrder(OrderItem.desc(SORT_BY)); queryPage.setSearchCount(false); - //2.查询订单列表 + // 2.查询订单列表 Page ordersPage = baseMapper.selectPage(queryPage, queryWrapper); List records = ordersPage.getRecords(); - List orderSimpleResDTOS = BeanUtil.copyToList(records, OrderSimpleResDTO.class); - return orderSimpleResDTOS; - + return BeanUtil.copyToList(records, OrderSimpleResDTO.class); } + /** * 根据订单id查询 - * * @param id 订单id * @return 订单详情 */ @Override public OrderResDTO getDetail(Long id) { - Orders orders = queryById(id); + Orders orders = baseMapper.selectById(id); + if (ObjectUtils.isEmpty(orders)) { + return new OrderResDTO(); + } + OrderResDTO orderResDTO = BeanUtil.toBean(orders, OrderResDTO.class); + // 订单超过15分钟未支付则自动取消 + if (OrderStatusEnum.NO_PAY.getStatus().equals(orders.getOrdersStatus()) + && orders.getCreateTime().isBefore(LocalDateTime.now().minusMinutes(PAY_OVERTIME_MINUTE))) { + cancelPayOverTimeOrder(id); + orderResDTO.setOrdersStatus(OrderStatusEnum.CANCELED.getStatus()); + } + + // 订单取消/关闭获取取消/关闭原因 + Integer ordersStatus = orderResDTO.getOrdersStatus(); + if (OrderStatusEnum.CANCELED.getStatus().equals(ordersStatus) + || OrderStatusEnum.CLOSED.getStatus().equals(ordersStatus)) { + OrdersCanceled canceledDetail = ordersCanceledService.getById(id); + orderResDTO.setCancelTime(canceledDetail.getCancelTime()); + orderResDTO.setCancelReason(canceledDetail.getCancelReason()); + } + return orderResDTO; } /** * 订单评价 - * * @param ordersId 订单id */ @Override @@ -106,4 +144,80 @@ public class OrdersManagerServiceImpl extends ServiceImpl // orderStateMachine.changeStatus(orders.getUserId(), orders.getId().toString(), OrderStatusChangeEventEnum.EVALUATE, orderSnapshotDTO); } -} + @Override + public void cancel(OrderCancelDTO orderCancelDTO) { + Long orderId = orderCancelDTO.getId(); + Orders orders = baseMapper.selectById(orderId); + if (ObjectUtils.isEmpty(orders)) { + throw new ForbiddenOperationException("订单不存在无法取消"); + } + // 补充前端无法确定的字段 + orderCancelDTO.setServeStartTime(orders.getServeStartTime()); + orderCancelDTO.setRealPayAmount(orders.getRealPayAmount()); + orderCancelDTO.setCityCode(orders.getCityCode()); + orderCancelDTO.setTradingOrderNo(orders.getTradingOrderNo()); + + Integer ordersStatus = orders.getOrdersStatus(); + if (OrderStatusEnum.NO_PAY.getStatus().equals(ordersStatus)) { + // 1. 未支付订单 -> 取消状态 + cancelNoPayOrder(orderCancelDTO); + } else if (OrderStatusEnum.DISPATCHING.getStatus().equals(ordersStatus)) { + // 2. 已支付订单 -> 关闭状态 + 退款 + cancelDispatchingOrder(orderCancelDTO); + } else { + throw new ForbiddenOperationException("订单无法取消"); + } + } + + @Override + public List getPayOverTimeOrder(Integer count) { + return lambdaQuery() + .eq(Orders::getOrdersStatus, OrderStatusEnum.NO_PAY.getStatus()) + .lt(Orders::getCreateTime, LocalDateTime.now().minusMinutes(PAY_OVERTIME_MINUTE)) + .last("LIMIT " + count) + .list(); + } + + @Override + public void cancelPayOverTimeOrder(Long id) { + // 二次确认防止在此期间支付 + TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(id); + if (ObjectUtils.isEmpty(tradingResDTO) || tradingResDTO.getTradingState() != TradingStateEnum.YJS) { + cancelNoPayOrder(OrderCancelDTO.builder() + .id(id) + .cancelReason("订单超时未支付自动取消") + .currentUserId(-1L) + .currentUserName("SYSTEM") + .currentUserType(UserType.SYSTEM) + .build()); + } + } + + private void cancelNoPayOrder(OrderCancelDTO orderCancelDTO) { + Long orderId = orderCancelDTO.getId(); + transactionTemplate.executeWithoutResult(status -> { + if (!ordersCanceledService.save(OrdersCanceled.builder() + .id(orderId) + .cancellerId(orderCancelDTO.getCurrentUserId()) + .cancelerName(orderCancelDTO.getCurrentUserName()) + .cancellerType(orderCancelDTO.getCurrentUserType()) + .cancelReason(orderCancelDTO.getCancelReason()) + .cancelTime(LocalDateTime.now()) + .build())) { + throw new DBException("订单取消表更新失败"); + } + + if (!ordersCommonService.updateStatus(OrderUpdateStatusDTO.builder() + .id(orderId) + .originStatus(OrderStatusEnum.NO_PAY.getStatus()) + .targetStatus(OrderStatusEnum.CANCELED.getStatus()) + .build())) { + throw new DBException("订单表订单状态更新失败"); + } + }); + } + + private void cancelDispatchingOrder(OrderCancelDTO orderCancelDTO) { + return; + } +} \ No newline at end of file