开发时间:2026-08-17
功能模块:酒店顾客端支付流程(Mock 模式)
实现了酒店顾客端完整的支付流程,支持先支付后接单的业务模式。当前采用 Mock 支付模式,后续可无缝对接微信支付、支付宝小程序支付。
文件:forge-server/db/migration/V1.0.105__add_hotel_order_payment_fields.sql
MySQL 8.0 兼容:使用
information_schema+PREPARE/EXECUTE模式替代 MariaDB 专有的IF NOT EXISTS扩展语法。
pay_status TINYINT -- 支付状态:0=待支付, 1=已支付, 2=已退款, 3=支付超时已取消
pay_expire_time DATETIME -- 支付截止时间
pay_time DATETIME -- 实际支付时间
pay_trade_no VARCHAR(128) -- 第三方支付交易号
paid_amount DECIMAL(10,2) -- 实付金额
pay_source VARCHAR(32) -- 支付来源:WECHAT/ALIPAY/H5/MOCK
pay_method:V1.0.104 创建但实体已移除,迁移脚本自动 DROP COLUMNidx_pay_expire:加速支付超时订单扫描(pay_status, pay_expire_time)uk_order_no:已在 V1.0.104 中创建,不重复记录每笔支付的完整流水,包括:
新增字典类型:
hotel_pay_status:支付状态(0=待支付, 1=已支付, 2=已退款, 3=支付超时已取消)hotel_pay_source:支付来源(WECHAT/ALIPAY/H5/MOCK 等)hotel_order_status:补充 0=待支付、10=超时取消文件:HotelOrderConstants.java
// 订单状态
STATUS_PENDING_PAY = 0 // 待支付(新增)
STATUS_PLACED = 1 // 待接单(已支付)
STATUS_PAY_TIMEOUT = 10 // 超时取消(新增)
// 支付状态
PAY_STATUS_PENDING = 0 // 待支付
PAY_STATUS_SUCCESS = 1 // 已支付
PAY_STATUS_TIMEOUT = 2 // 支付超时
// 支付来源
PAY_SOURCE_WECHAT = "WECHAT"
PAY_SOURCE_ALIPAY = "ALIPAY"
PAY_SOURCE_H5 = "H5"
PAY_SOURCE_MOCK = "MOCK"
// 超时配置
PAY_TIMEOUT_MINUTES = 15 // 支付超时时间(分钟)
新增字段:
payStatus(Integer):支付状态payExpireTime(LocalDateTime):支付截止时间payTime(LocalDateTime):实际支付时间payTradeNo(String):支付交易号paidAmount(BigDecimal):实付金额paySource(String):支付来源移除字段:
payMethod(已废弃,改用 paySource)支付流水实体,包含完整的支付记录字段。
新增方法:
List<Long> selectExpiredUnpaidOrderIds()
查询所有超时未支付的订单 ID。
resultMap 和 OrderColumns:添加支付相关字段映射selectDashboard、countTodayOrders、sumTodayRevenue:改为只统计已支付订单(pay_status = 1)selectOrderPage:筛选条件从 payMethod 改为 payStatus、paySourceselectExpiredUnpaidOrderIds:查询超时订单新增方法:
int incrementSales(@Param("dishId") Long dishId, @Param("quantity") Integer quantity)
原子更新菜品销量。
新增 SQL:
<update id="incrementSales">
UPDATE hotel_dish
SET sales = IFNULL(sales, 0) + #{quantity},
update_time = NOW()
WHERE id = #{dishId} AND del_flag = 0
</update>
HotelPayLog selectByOutTradeNo(@Param("outTradeNo") String outTradeNo)
根据商户订单号查询支付流水。
orderCreate() 方法重构:
STATUS_PENDING_PAY(待支付)payStatus = PAY_STATUS_PENDINGpayExpireTime = now + 15 分钟payMethod 字段赋值generateOrderNo() 并发修复:
selectCount)incrementDishSales() 并发修复:
dishMapper.incrementSales() 直接执行 SET sales = IFNULL(sales, 0) + #{quantity}customerRefund() 新增逻辑:
STATUS_PAY_TIMEOUT支付服务接口,定义核心方法:
Map<String, Object> createPay(Long orderId, String paySource)
PayResultVO mockPaySuccess(Long orderId)
void handlePayCallback(String outTradeNo, String tradeNo, boolean success, String rawContent)
PayResultVO queryPayStatus(Long orderId)
void cancelTimeoutOrder(Long orderId)
createPay():
PAY + 时间戳 + 订单ID后6位)hotel_pay_log)mockPaySuccess():
handlePayCallback():
cancelTimeoutOrder():
STATUS_PAY_TIMEOUTPAY_STATUS_TIMEOUTqueryPayStatus():
路径:/hotel/open/pay/**(免登录,已在 SaToken 白名单中)
接口列表:
POST /create 发起支付(创建支付流水)
POST /mockSuccess 模拟支付成功(开发阶段)
GET /status 查询支付状态(前端轮询)
POST /notify 支付回调通知(微信/支付宝异步通知入口,Mock 占位)
所有接口都需要传入 tenantId 参数,通过 TenantContextHolder.executeWithTenant() 在指定租户上下文中执行。
@Scheduled(fixedDelay = 120000, initialDelay = 30000)
public void cancelExpiredOrders()
TenantContextHolder.executeIgnore() 跨租户扫描payService.cancelTimeoutOrder() 取消文件:forge-h5-ui/src/api/index.js
新增支付相关 API:
hotelPayCreate: (data) => request.post('/hotel/open/pay/create', data)
hotelPayMockSuccess: (data) => request.post('/hotel/open/pay/mockSuccess', data)
hotelPayStatus: (params) => request.get('/hotel/open/pay/status', { params })
文件:forge-h5-ui/src/pages/hotel/customer/pay.vue
核心功能:
wx.miniProgram.getPhoneNumber()my.tradePay()hotelPayCreate 创建支付流水hotelPayMockSuccess)UI 元素:
文件:forge-h5-ui/src/pages/hotel/customer/order-status.vue
新增状态处理:
isFinished() 返回 true(订单结束)文件:forge-h5-ui/src/pages/hotel/customer/orders.vue
状态标签更新:
statusLabel(status) {
const map = {
0: '待支付', 1: '待接单', 2: '已接单', 3: '备餐中',
4: '已出餐', 5: '配送中', 6: '已完成',
7: '已拒单', 8: '退单审核中', 9: '已退单',
10: '超时取消',
}
return map[status] || '未知'
}
状态颜色:
.o-st--0:黄色背景(待支付).o-st--10:灰色背景(超时取消)待支付订单特殊处理:
文件:forge-h5-ui/src/pages/hotel/customer/order-confirm.vue
更新跳转逻辑:
orderId、tenantId、amount问题:多个订单同时生成可能产生重复订单号
解决方案:
order_no 添加唯一索引generateOrderNo() 添加 3 次重试selectCount 验证唯一性问题:先查后写导致并发时销量丢失
解决方案:
SET sales = IFNULL(sales, 0) + #{quantity}顾客 H5前端 后端API 数据库
| | | |
|--下单----------->| | |
| |--创建订单-------->| |
| | |--插入订单(status=0)|
| | |<-------------------|
| |<--返回orderId-----| |
| | | |
|--跳转支付页----->| | |
| |--创建支付-------->| |
| | |--插入pay_log |
| | |<-------------------|
| |<--返回支付参数----| |
| | | |
|--点击支付------->| | |
| |--模拟支付成功---->| |
| | |--更新pay_log |
| | |--更新订单(status=1)|
| | |<-------------------|
| |<--返回支付结果----| |
| | | |
|--跳转订单状态--->| | |
| |--轮询支付状态---->| |
| | |--查询订单 |
| | |<-------------------|
| |<--返回已支付------| |
| | | |
HotelPayServiceImpl.createPay() 中调用微信统一下单接口pay.vue 中调用 wx.requestPayment()/hotel/open/pay/notifyHotelPayServiceImpl.createPay() 中调用支付宝订单创建接口pay.vue 中调用 my.tradePay()/hotel/open/pay/notify需要在 application.yml 中添加:
forge:
pay:
wechat:
appId: xxx
mchId: xxx
apiKey: xxx
alipay:
appId: xxx
privateKey: xxx
alipayPublicKey: xxx
forge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/pay/domain/HotelPayLog.javaforge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/pay/mapper/HotelPayLogMapper.javaforge-server/forge-business/forge-hotel/src/main/resources/mapper/business/hotel/pay/HotelPayLogMapper.xmlforge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/pay/service/HotelPayService.javaforge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/pay/service/impl/HotelPayServiceImpl.javaforge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/pay/vo/PayResultVO.javaforge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/controller/open/HotelPayController.javaforge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/pay/task/PayTimeoutTask.javaforge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/order/constant/HotelOrderConstants.javaforge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/order/domain/HotelOrder.javaforge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/order/mapper/HotelOrderMapper.javaforge-server/forge-business/forge-hotel/src/main/resources/mapper/business/hotel/order/HotelOrderMapper.xmlforge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/mapper/HotelDishMapper.javaforge-server/forge-business/forge-hotel/src/main/resources/mapper/business/hotel/HotelDishMapper.xmlforge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/order/service/impl/HotelOrderServiceImpl.javaforge-server/forge-business/forge-hotel/src/main/java/com/mdframe/forge/business/core/hotel/order/vo/HotelOrderVO.javaforge-server/db/migration/V1.0.105__add_hotel_order_payment_fields.sql无
forge-h5-ui/src/api/index.jsforge-h5-ui/src/pages/hotel/customer/pay.vueforge-h5-ui/src/pages/hotel/customer/order-status.vueforge-h5-ui/src/pages/hotel/customer/orders.vueforge-h5-ui/src/pages/hotel/customer/order-confirm.vue正常支付流程:
超时取消:
主动取消:
重复支付防护:
订单号唯一性:
菜品销量准确性:
支付超时边界:
库存不足:
Mock 模式安全性:
定时任务精度:
支付回调重试:
支付对账:
支付统计:
退款流程:
本次开发完成了酒店订单支付系统的核心功能,包括:
当前系统已支持完整的 Mock 支付流程,后续可无缝对接微信支付、支付宝小程序支付。所有代码已通过编译验证,无语法错误。
文档版本:v1.0
最后更新:2026-08-17
维护人员:AI Assistant