Browse Source

订单表字段修改

zhangchen 1 month ago
parent
commit
31b2286acd

+ 4 - 4
alien-entity/src/main/java/shop/alien/entity/store/UserReservationOrder.java

@@ -46,14 +46,14 @@ public class UserReservationOrder {
     @TableField("order_status")
     private Integer orderStatus;
 
+    @ApiModelProperty(value = "支付状态 0:未支付 1:已支付 2:已退款 3:部分退款")
+    @TableField("payment_status")
+    private Integer paymentStatus;
+
     @ApiModelProperty(value = "订单费用类型 0-免费,1-收费")
     @TableField("order_cost_type")
     private Integer orderCostType;
 
-    @ApiModelProperty(value = "订单/预订金额(元)")
-    @TableField("total_amount")
-    private Integer totalAmount;
-
     @ApiModelProperty(value = "订金金额(元),0表示免费预订")
     @TableField("deposit_amount")
     private BigDecimal depositAmount;

+ 38 - 0
alien-store/src/main/java/shop/alien/store/controller/ReservationOrderPageController.java

@@ -0,0 +1,38 @@
+package shop.alien.store.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.store.service.ReservationOrderPageService;
+import shop.alien.store.vo.ReservationOrderPageVo;
+
+/**
+ * 预订订单页面展示接口(按订单ID查询)
+ *
+ * @author system
+ */
+@Slf4j
+@Api(tags = {"预订订单页面"})
+@RestController
+@RequestMapping("/store/reservationOrder")
+@RequiredArgsConstructor
+public class ReservationOrderPageController {
+
+    private final ReservationOrderPageService reservationOrderPageService;
+
+    @ApiOperation("预订页面展示(按订单ID)")
+    @ApiImplicitParam(name = "orderId", value = "预订订单ID(user_reservation_order.id)", required = true, paramType = "query", dataType = "int")
+    @GetMapping("/page")
+    public R<ReservationOrderPageVo> getPage(@RequestParam Integer orderId) {
+        log.info("ReservationOrderPageController.getPage orderId={}", orderId);
+        ReservationOrderPageVo vo = reservationOrderPageService.getPageByOrderId(orderId);
+        if (vo == null) {
+            return R.fail("订单不存在");
+        }
+        return R.data(vo);
+    }
+}

+ 19 - 0
alien-store/src/main/java/shop/alien/store/service/ReservationOrderPageService.java

@@ -0,0 +1,19 @@
+package shop.alien.store.service;
+
+import shop.alien.store.vo.ReservationOrderPageVo;
+
+/**
+ * 预订订单页面展示服务(按订单ID查询)
+ *
+ * @author system
+ */
+public interface ReservationOrderPageService {
+
+    /**
+     * 按订单ID查询预订页面展示数据
+     *
+     * @param orderId 预订订单ID(user_reservation_order.id)
+     * @return 页面 VO,订单不存在返回 null
+     */
+    ReservationOrderPageVo getPageByOrderId(Integer orderId);
+}

+ 234 - 0
alien-store/src/main/java/shop/alien/store/service/impl/ReservationOrderPageServiceImpl.java

@@ -0,0 +1,234 @@
+package shop.alien.store.service.impl;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.*;
+import shop.alien.mapper.UserReservationTableMapper;
+import shop.alien.store.service.*;
+import shop.alien.store.vo.ReservationOrderPageVo;
+
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 预订订单页面展示服务实现
+ *
+ * @author system
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ReservationOrderPageServiceImpl implements ReservationOrderPageService {
+
+    private static final int PAYMENT_DEADLINE_MINUTES = 15;
+    private static final int ORDER_STATUS_UNPAID = 0;
+    private static final int CANCELLATION_FREE = 0;
+    private static final int CANCELLATION_NOT_FREE = 1;
+    private static final int CANCELLATION_CONDITIONAL = 2;
+
+    private final UserReservationOrderService userReservationOrderService;
+    private final StoreInfoService storeInfoService;
+    private final UserReservationService userReservationService;
+    private final UserReservationTableMapper userReservationTableMapper;
+    private final StoreBookingTableService storeBookingTableService;
+    private final StoreBookingCategoryService storeBookingCategoryService;
+
+    @Override
+    public ReservationOrderPageVo getPageByOrderId(Integer orderId) {
+        if (orderId == null) {
+            return null;
+        }
+        UserReservationOrder order = userReservationOrderService.getById(orderId);
+        if (order == null) {
+            return null;
+        }
+        return buildPageVo(order.getStoreId(), order);
+    }
+
+    private ReservationOrderPageVo buildPageVo(Integer storeId, UserReservationOrder order) {
+        ReservationOrderPageVo vo = new ReservationOrderPageVo();
+        vo.setOrderId(order.getId());
+        vo.setOrderSn(order.getOrderSn());
+        vo.setOrderStatus(order.getOrderStatus());
+        vo.setPaymentStatus(derivePaymentStatus(order.getOrderStatus()));
+        vo.setPayAmount(order.getDepositAmount() != null ? order.getDepositAmount() : BigDecimal.ZERO);
+        vo.setPaymentDeadlineMinutes(PAYMENT_DEADLINE_MINUTES);
+
+        // 支付倒计时
+        vo.setPaymentDeadline(order.getPaymentDeadline());
+        int secondsLeft = computePaymentSecondsLeft(order);
+        vo.setPaymentSecondsLeft(secondsLeft);
+        vo.setCanContinuePay(Boolean.valueOf(ORDER_STATUS_UNPAID == order.getOrderStatus() && secondsLeft > 0));
+
+        // 页面标题后缀
+        vo.setPageTitleSuffix(buildPageTitleSuffix(order.getCancellationPolicyType()));
+
+        // 门店
+        if (storeId != null) {
+            StoreInfo store = storeInfoService.getById(storeId);
+            if (store != null) {
+            vo.setStoreId(storeId);
+            vo.setStoreName(store.getStoreName());
+            vo.setStoreAddress(store.getStoreAddress());
+            vo.setStoreTel(store.getStoreTel());
+            vo.setStorePosition(store.getStorePosition());
+            }
+        }
+
+        // 预约信息
+        if (order.getReservationId() != null) {
+            UserReservation reservation = userReservationService.getById(order.getReservationId());
+            if (reservation != null) {
+                vo.setReservationDate(reservation.getReservationDate());
+                vo.setReservationDateText(formatDateWithToday(reservation.getReservationDate()));
+                vo.setGuestCount(reservation.getGuestCount());
+                vo.setDiningTimeSlotText(buildDiningTimeSlot(reservation.getStartTime(), reservation.getEndTime()));
+                vo.setDiningTimeText(buildDiningTimeSlot(reservation.getStartTime(), reservation.getEndTime()));
+
+                List<Integer> tableIds = listTableIdsByReservationId(reservation.getId());
+                List<StoreBookingTable> tables = new ArrayList<>();
+                String categoryName = null;
+                for (Integer tableId : tableIds) {
+                    StoreBookingTable t = storeBookingTableService.getById(tableId);
+                    if (t != null) {
+                        tables.add(t);
+                        if (categoryName == null && t.getCategoryId() != null) {
+                            StoreBookingCategory cat = storeBookingCategoryService.getById(t.getCategoryId());
+                            if (cat != null) {
+                                categoryName = cat.getCategoryName();
+                            }
+                        }
+                    }
+                }
+                String tableNumbersText = tables.stream().map(StoreBookingTable::getTableNumber).filter(Objects::nonNull).collect(Collectors.joining(","));
+                vo.setTableNumbersText(tableNumbersText.isEmpty() ? null : tableNumbersText);
+                vo.setGuestAndCategoryText(buildGuestAndCategoryText(categoryName, reservation.getGuestCount(), tables));
+                vo.setLocationTableText(buildLocationTableText(categoryName, tableNumbersText));
+            }
+        }
+
+        // 使用凭证
+        vo.setVerificationCode(order.getVerificationCode());
+        vo.setVerificationUrl(order.getVerificationUrl());
+
+        // 预订说明
+        vo.setLateArrivalGraceMinutes(order.getLateArrivalGraceMinutes() != null ? order.getLateArrivalGraceMinutes() : 0);
+        vo.setSeatRetentionText(order.getLateArrivalGraceMinutes() != null && order.getLateArrivalGraceMinutes() > 0
+                ? "未按时到店座位保留" + order.getLateArrivalGraceMinutes() + "分钟"
+                : null);
+        vo.setDepositAmount(order.getDepositAmount());
+        vo.setDepositText(order.getDepositAmount() != null && order.getDepositAmount().compareTo(BigDecimal.ZERO) > 0
+                ? "需支付¥" + order.getDepositAmount().stripTrailingZeros().toPlainString() + "订金"
+                : null);
+        vo.setDepositRefundRule(order.getDepositRefundRule());
+        vo.setCancellationPolicyType(order.getCancellationPolicyType());
+        vo.setFreeCancellationDeadline(order.getFreeCancellationDeadline());
+        vo.setFreeCancellationDeadlineText(formatFreeCancellationDeadline(order.getFreeCancellationDeadline()));
+        vo.setCancellationPolicyText(buildCancellationPolicyText(order));
+
+        // 订单信息
+        vo.setOrderCreatedTime(order.getCreatedTime());
+        vo.setOrderCreatedTimeText(order.getCreatedTime() != null ? formatDateWithToday(order.getCreatedTime()) : null);
+        vo.setContactName(null);
+        vo.setContactPhone(null);
+        vo.setContactText(null);
+
+        return vo;
+    }
+
+    private List<Integer> listTableIdsByReservationId(Integer reservationId) {
+        return userReservationTableMapper.selectList(
+                new LambdaQueryWrapper<UserReservationTable>()
+                        .eq(UserReservationTable::getReservationId, reservationId)
+                        .orderByAsc(UserReservationTable::getSort)
+                        .orderByAsc(UserReservationTable::getId)
+        ).stream().map(UserReservationTable::getTableId).collect(Collectors.toList());
+    }
+
+    private int computePaymentSecondsLeft(UserReservationOrder order) {
+        if (order.getOrderStatus() == null || order.getOrderStatus() != ORDER_STATUS_UNPAID || order.getPaymentDeadline() == null) {
+            return 0;
+        }
+        long now = System.currentTimeMillis();
+        long deadline = order.getPaymentDeadline().getTime();
+        long diff = (deadline - now) / 1000;
+        return diff > 0 ? (int) diff : 0;
+    }
+
+    private String buildPageTitleSuffix(Integer cancellationPolicyType) {
+        if (cancellationPolicyType == null) {
+            return "不可免责取消";
+        }
+        if (cancellationPolicyType == CANCELLATION_FREE) {
+            return "免费预订";
+        }
+        if (cancellationPolicyType == CANCELLATION_CONDITIONAL) {
+            return "分情况是否免责";
+        }
+        return "不可免责取消";
+    }
+
+    private String buildDiningTimeSlot(String startTime, String endTime) {
+        if (startTime == null && endTime == null) return null;
+        if (startTime != null && endTime != null) return startTime + "-" + endTime;
+        return startTime != null ? startTime : endTime;
+    }
+
+    private String buildGuestAndCategoryText(String categoryName, Integer guestCount, List<StoreBookingTable> tables) {
+        int capacity = tables.stream().mapToInt(t -> t.getSeatingCapacity() != null ? t.getSeatingCapacity() : 0).max().orElse(guestCount != null ? guestCount : 0);
+        if (capacity <= 0 && guestCount != null) capacity = guestCount;
+        String part = categoryName != null ? categoryName : "预订";
+        return part + (capacity > 0 ? capacity + "人" : "");
+    }
+
+    private String buildLocationTableText(String categoryName, String tableNumbersText) {
+        if (categoryName != null && tableNumbersText != null && !tableNumbersText.isEmpty()) {
+            return categoryName + "|" + tableNumbersText;
+        }
+        if (tableNumbersText != null && !tableNumbersText.isEmpty()) return tableNumbersText;
+        return categoryName;
+    }
+
+    private String formatDateWithToday(Date date) {
+        if (date == null) return null;
+        SimpleDateFormat f = new SimpleDateFormat("MM月dd日", Locale.CHINA);
+        String s = f.format(date);
+        Calendar cal = Calendar.getInstance();
+        Calendar d = Calendar.getInstance();
+        d.setTime(date);
+        if (cal.get(Calendar.YEAR) == d.get(Calendar.YEAR) && cal.get(Calendar.DAY_OF_YEAR) == d.get(Calendar.DAY_OF_YEAR)) {
+            s += " 今天";
+        }
+        return s;
+    }
+
+    private String formatFreeCancellationDeadline(Date deadline) {
+        if (deadline == null) return null;
+        SimpleDateFormat f = new SimpleDateFormat("MM月dd日 HH:mm", Locale.CHINA);
+        return f.format(deadline) + "前可免责取消";
+    }
+
+    private String buildCancellationPolicyText(UserReservationOrder order) {
+        Integer type = order.getCancellationPolicyType();
+        if (type == null) return "取消将收取订金";
+        if (type == CANCELLATION_FREE) return "免费取消";
+        if (type == CANCELLATION_NOT_FREE) return "不可免责取消,取消将收取订金";
+        if (type == CANCELLATION_CONDITIONAL) {
+            String line = formatFreeCancellationDeadline(order.getFreeCancellationDeadline());
+            return line != null ? line + ";在上述时间前取消订金可原路返回,之后取消将收取订金" : "分情况是否免责,取消将收取订金";
+        }
+        return "取消将收取订金";
+    }
+
+    /** 根据订单状态推导支付状态 0:未支付 1:已支付 2:已退款 */
+    private int derivePaymentStatus(Integer orderStatus) {
+        if (orderStatus == null) return 0;
+        if (orderStatus == 0) return 0;
+        if (orderStatus == 6 || orderStatus == 7) return 2;
+        return 1;
+    }
+}

+ 0 - 3
alien-store/src/main/java/shop/alien/store/strategy/merchantPayment/impl/MerchantAlipayPaymentStrategyImpl.java

@@ -299,9 +299,6 @@ public class MerchantAlipayPaymentStrategyImpl implements MerchantPaymentStrateg
             record.setTransactionId(tradeNo);
             record.setOutRefundNo(UniqueRandomNumGenerator.generateUniqueCode(19));
             record.setRefundStatus("SUCCESS");
-            if (order.getTotalAmount() != null) {
-                record.setTotalAmount(order.getTotalAmount().multiply(new BigDecimal(100)).longValue());
-            }
             record.setRefundAmount(refundAmountDecimal.multiply(new BigDecimal(100)).longValue());
             record.setRefundReason(refundReason);
             record.setOrderId(String.valueOf(order.getId()));

+ 1 - 4
alien-store/src/main/java/shop/alien/store/strategy/merchantPayment/impl/MerchantWechatPaymentStrategyImpl.java

@@ -286,7 +286,7 @@ public class MerchantWechatPaymentStrategyImpl implements MerchantPaymentStrateg
             request.notifyUrl = StringUtils.isNotBlank(refundNotifyUrl) ? refundNotifyUrl : "";
             request.amount = new WeChatPaymentStrategyImpl.AmountReq();
             request.amount.refund = new BigDecimal(refundAmount).multiply(new BigDecimal(100)).longValue();
-            request.amount.total = order.getTotalAmount() != null ? order.getTotalAmount().multiply(new BigDecimal(100)).longValue() : request.amount.refund;
+            request.amount.total = order.getDepositAmount().longValue();
             request.amount.currency = "CNY";
 
             WeChatPaymentStrategyImpl.Refund response = refundRun(config, privateKey, wechatPayPublicKey, request);
@@ -318,9 +318,6 @@ public class MerchantWechatPaymentStrategyImpl implements MerchantPaymentStrateg
             record.setOutRefundNo(response.outRefundNo != null ? response.outRefundNo : request.outRefundNo);
             record.setRefundId(response.refundId);
             record.setRefundStatus("SUCCESS");
-            if (order.getTotalAmount() != null) {
-                record.setTotalAmount(order.getTotalAmount().multiply(new BigDecimal(100)).longValue());
-            }
             record.setRefundAmount(refundAmountDecimal.multiply(new BigDecimal(100)).longValue());
             record.setRefundReason(refundReason);
             record.setOrderId(String.valueOf(order.getId()));

+ 148 - 0
alien-store/src/main/java/shop/alien/store/vo/ReservationOrderPageVo.java

@@ -0,0 +1,148 @@
+package shop.alien.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 预订订单页面展示 VO(待支付详情页:门店、预订信息、支付倒计时、使用凭证、预订说明、订单信息)
+ * 通过 store_id + order_id 或 order_sn 查询
+ *
+ * @author system
+ */
+@Data
+@ApiModel(value = "ReservationOrderPageVo", description = "预订订单页面展示")
+public class ReservationOrderPageVo {
+
+    @ApiModelProperty(value = "订单ID")
+    private Integer orderId;
+
+    @ApiModelProperty(value = "订单编号")
+    private String orderSn;
+
+    @ApiModelProperty(value = "订单状态 0:待支付 1:待使用 2:已完成 3:已过期 4:已取消 5:已关闭 6:退款中 7:已退款 8:商家预订")
+    private Integer orderStatus;
+
+    @ApiModelProperty(value = "支付状态 0:未支付 1:已支付 2:已退款")
+    private Integer paymentStatus;
+
+    @ApiModelProperty(value = "页面标题后缀:不可免责取消 / 分情况是否免责")
+    private String pageTitleSuffix;
+
+    // --------- 支付倒计时(待支付时有效) ---------
+    @ApiModelProperty(value = "支付截止时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date paymentDeadline;
+
+    @ApiModelProperty(value = "剩余支付秒数,用于前端倒计时「剩余 MM:SS」")
+    private Integer paymentSecondsLeft;
+
+    @ApiModelProperty(value = "待支付有效时长(分钟)")
+    private Integer paymentDeadlineMinutes;
+
+    // --------- 门店信息 ---------
+    @ApiModelProperty(value = "门店ID")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "门店名称")
+    private String storeName;
+
+    @ApiModelProperty(value = "门店地址")
+    private String storeAddress;
+
+    @ApiModelProperty(value = "门店电话")
+    private String storeTel;
+
+    @ApiModelProperty(value = "门店坐标(经度,纬度),用于导航")
+    private String storePosition;
+
+    // --------- 预订概要(顶部卡片) ---------
+    @ApiModelProperty(value = "就餐日期 yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date reservationDate;
+
+    @ApiModelProperty(value = "就餐日期展示 如 02月02日 今天")
+    private String reservationDateText;
+
+    @ApiModelProperty(value = "人数/桌型描述 如 包间6人")
+    private String guestAndCategoryText;
+
+    @ApiModelProperty(value = "桌号列表 如 A01,A02")
+    private String tableNumbersText;
+
+    @ApiModelProperty(value = "就餐时间段 如 10:00-12:03")
+    private String diningTimeSlotText;
+
+    // --------- 使用凭证 ---------
+    @ApiModelProperty(value = "核销码(用于展示与生成二维码)")
+    private String verificationCode;
+
+    @ApiModelProperty(value = "核销二维码内容或URL")
+    private String verificationUrl;
+
+    // --------- 预订说明 ---------
+    @ApiModelProperty(value = "未按时到店座位保留时长(分钟),0 表示不保留")
+    private Integer lateArrivalGraceMinutes;
+
+    @ApiModelProperty(value = "座位保留说明 如 未按时到店座位保留30分钟")
+    private String seatRetentionText;
+
+    @ApiModelProperty(value = "订金金额(元)")
+    private BigDecimal depositAmount;
+
+    @ApiModelProperty(value = "订金说明 如 需支付¥50订金")
+    private String depositText;
+
+    @ApiModelProperty(value = "订金退还规则描述")
+    private String depositRefundRule;
+
+    @ApiModelProperty(value = "取消政策类型 0:免费预订 1:不可免费取消 2:分情况免责")
+    private Integer cancellationPolicyType;
+
+    @ApiModelProperty(value = "免费取消截止时间(分情况免责时有值)")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date freeCancellationDeadline;
+
+    @ApiModelProperty(value = "免费取消截止展示 如 12月31日 09:00前可免责取消")
+    private String freeCancellationDeadlineText;
+
+    @ApiModelProperty(value = "取消政策说明文案")
+    private String cancellationPolicyText;
+
+    // --------- 订单信息 ---------
+    @ApiModelProperty(value = "预订/下单时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date orderCreatedTime;
+
+    @ApiModelProperty(value = "预订时间展示 如 01月01日 今天")
+    private String orderCreatedTimeText;
+
+    @ApiModelProperty(value = "预约人数")
+    private Integer guestCount;
+
+    @ApiModelProperty(value = "位置桌型 如 大厅|A01,A02")
+    private String locationTableText;
+
+    @ApiModelProperty(value = "就餐时间 如 10:00-12:00")
+    private String diningTimeText;
+
+    @ApiModelProperty(value = "联系人姓名")
+    private String contactName;
+
+    @ApiModelProperty(value = "联系人电话")
+    private String contactPhone;
+
+    @ApiModelProperty(value = "联系方式展示 如 张三 13847859088")
+    private String contactText;
+
+    // --------- 操作 ---------
+    @ApiModelProperty(value = "订金金额,用于「¥50 继续支付」按钮")
+    private BigDecimal payAmount;
+
+    @ApiModelProperty(value = "是否可继续支付(待支付且未超时)")
+    private Boolean canContinuePay;
+}