文档目录
订单控制器

简介

本文件面向 DouPHP 的订单控制器及相关服务,系统性说明从购物车到下单、支付、状态流转、售后等完整生命周期。重点覆盖:

  • 购物车管理(加购、改数量、删除)
  • 结算页数据准备、运费重算、优惠券试算
  • 下单事务(库存检查、价格防篡改、地址快照、优惠券核销、分销奖励)
  • 收银台(余额+网关混合支付、线下凭证上传、货到付款)
  • 订单状态机与支付回调联动
  • 可扩展点:新增支付方式、物流服务、订单分析与自动化任务

项目结构

订单相关的前端入口集中在 front/controller/order 下,按职责拆分为:

  • OrderController:路由薄入口,默认跳转到购物车
  • CartController:购物车页面与操作
  • CheckoutController:结算页、提交订单、运费与优惠券交互
  • CashierController:收银台、发起支付、线下凭证、货到付款

业务逻辑下沉至 front/service/order 下的 CartService、CheckoutService、CashierService;通用能力由 core/service/order/OrderService 提供门面编排。

graph TB
A["OrderController<br/>路由入口"] --> B["CartController<br/>购物车"]
B --> C["CartService<br/>加购/改数量/删除"]
B --> D["OrderService<br/>购物车视图/清空"]
C --> E["PricingService<br/>定价"]
C --> F["OrderStockGuard<br/>库存校验"]
G["CheckoutController<br/>结算/下单"] --> H["CheckoutService<br/>结算数据/下单事务"]
H --> I["OrderService<br/>订单号/状态/列表"]
H --> J["WalletService<br/>积分扣减"]
H --> K["CouponEligibility<br/>券资格/抵扣"]
L["CashierController<br/>收银台/支付"] --> M["CashierService<br/>余额+网关/离线/货到付款"]
M --> N["PaymentService<br/>支付台账/回调处理"]
M --> O["OrderStatusTransition<br/>状态推进"]

核心组件

  • 购物车控制器与服务:负责用户加购、规格属性校验、库存检查、数量更新、删除条目,并驱动"立即购买"或"进入结算"的跳转策略。
  • 结算控制器与服务:负责结算页数据装配(购物车、配送方式、优惠券)、运费重算、优惠券试算、下单事务(库存二次校验、价格防篡改、地址快照、优惠券核销、分销奖励)。
  • 收银台控制器与服务:负责收银台展示、发起支付(含余额+网关混合支付)、线下付款凭证上传、货到付款流程。
  • 订单核心服务:统一对外暴露购物车、状态机、库存、定时任务、结算选项等能力,屏蔽内部子服务细节。

架构总览

订单链路采用"控制器薄 + 服务厚"的分层设计:

  • 控制器仅做参数收集、鉴权、响应格式化
  • 服务封装领域规则(库存、价格、优惠券、钱包、状态机)
  • 核心服务作为门面聚合各子服务,保证对外接口稳定
sequenceDiagram
participant U as "用户"
participant CC as "CartController"
participant CS as "CartService"
participant OS as "OrderService"
participant PS as "PricingService"
participant SG as "OrderStockGuard"
U->>CC : 加入购物车
CC->>CS : addToCart(userId,module,item_id,number,mode,action,atts)
CS->>SG : checkStock(module,item_id,number)
SG-->>CS : 库存结果
CS->>PS : salePrice(...)
PS-->>CS : 售价信息
CS->>OS : clearCart()/getCart()
CS-->>CC : 结果(mode/module/action)
CC-->>U : 跳转(购物车/结算)

详细组件分析

购物车模块(CartController + CartService)

  • 登录校验:未登录时 AJAX 返回 401,普通请求跳转登录
  • 加购流程:
    • 解析 module/itemid/number/mode/action/att* 参数
    • 跨模块或积分兑换时清空购物车
    • 非商品或立即购买/一键购买时清空购物车
    • 规格属性校验(attribute_value 归属校验)
    • 库存检查(不可超卖)
    • 写入 order_cart(合并数量或新增)
    • 根据 mode/module/action 决定跳转(积分→结算;商品→购物车或结算)
  • 更新数量:
    • 仅商品且非一键购买时允许
    • 实时库存回退
    • 重新计算小计与合计
  • 删除条目:按 user_id 安全删除
flowchart TD
Start(["开始"]) --> Login["登录校验"]
Login --> |未登录| Redirect["跳转登录或返回401"]
Login --> |已登录| Parse["解析参数<br/>module/item_id/number/mode/action/atts"]
Parse --> ClearRule{"是否跨模块/积分/立即购买?"}
ClearRule --> |是| ClearCart["清空购物车"]
ClearRule --> |否| Next1["继续"]
ClearCart --> Next1
Next1 --> Attr["规格属性校验"]
Attr --> Stock["库存检查"]
Stock --> |失败| Err["返回错误"]
Stock --> |通过| Upsert["写入/合并购物车行"]
Upsert --> Jump{"跳转策略"}
Jump --> |积分| ToCheckout["跳转结算"]
Jump --> |商品| ToCartOrCheckout["跳转购物车或结算"]
Jump --> |其他| ToCheckout
ToCheckout --> End(["结束"])
ToCartOrCheckout --> End
Err --> End

结算模块(CheckoutController + CheckoutService)

  • 结算页数据:
    • 获取购物车视图
    • 加载配送插件配置,计算运费(免邮阈值/积分模式/非商品)
    • 初始化会话中的运费与优惠券金额
    • 加载会员默认收货地址快照
    • 加载可用优惠券列表
  • 运费重算:根据选择的配送方式 slug 动态计算运费与订单总额
  • 优惠券试算:
    • 单券模式,校验归属与可用性
    • 计算抵扣金额(百分比封顶、生效范围限制)
    • 写回会话供前端展示
  • 下单事务(createOrder):
    • 再次库存校验
    • 价格防篡改:基于定价服务与属性加价重算,与购物车缓存对比
    • 生成订单号
    • 事务内:
      • 积分兑换:先扣积分
      • 优惠券核销:标记 coupon_log 为已用,写入 order_coupon
      • 运费计算与减免
      • 写入 order、order_item、order_address 快照
      • 可选同步更新会员默认收货地址
    • 0元订单直接推进到 PAID
    • 清理购物车
    • 异常回滚并记录日志

更新 输入验证逻辑重构,引入中间变量提升安全性和可维护性

sequenceDiagram
participant UC as "用户"
participant CO as "CheckoutController"
participant COS as "CheckoutService"
participant OS as "OrderService"
participant WS as "WalletService"
participant CE as "CouponEligibility"
UC->>CO : 提交订单(contact_id,shipping_id,mode,coupon_id,...)
CO->>COS : createOrder(userId,form)
COS->>OS : getCart()/checkStock()
COS->>WS : createPoint(-order_point) // 积分模式
COS->>CE : canUse(coupon, lines)
CE-->>COS : 可用/不可用
COS->>COS : 计算抵扣/写coupon_log/order_coupon
COS->>COS : 计算运费/订单金额
COS->>OS : createOrderSn()
COS->>COS : DB事务(写入order/order_item/order_address)
alt 0元或积分
COS->>OS : changeStatus(PAID)
end
COS->>OS : clearCart()
COS-->>CO : {ok, order_sn, order_amount, mode}
CO-->>UC : 跳转(订单详情/收银台/我的订单)

收银台模块(CashierController + CashierService)

  • 收银台展示:
    • 校验待付款订单归属
    • 计算余额+网关混合支付预览(可抵扣金额、网关剩余)
    • 渲染支付方式列表
  • 发起支付(charge):
    • 校验 pay_id 合法性与订单归属
    • 若勾选使用余额:调用混合支付,可能直接收尾或转入网关支付
    • 创建 dou_order_payment(status=pending),设置过期时间
    • 写订单 pay_id
    • 调起支付插件 provider.start(),返回第三方 HTML 给浏览器
  • 线下凭证上传:
    • 校验订单归属
    • 存储附件并关联 payment_sn
    • 将订单状态推进到 awaiting_confirmation
  • 货到付款:
    • 立即标记支付成功(内部会推进状态)
    • 写 pay_id/paid_at 审计字段
sequenceDiagram
participant U as "用户"
participant CA as "CashierController"
participant CAS as "CashierService"
participant PS as "PaymentService"
participant PR as "PluginRegistry"
participant ST as "OrderStatusTransition"
U->>CA : POST charge(order_sn,pay_id,use_wallet?)
CA->>CAS : getPayOrder()/previewWalletSplit()
alt 使用余额
CA->>CAS : payWithWalletAndGateway()
CAS->>PS : markSucceededByWallet(order_sn,wallet)
alt 余额已付满
CAS->>ST : changeStatus(PAID/COMPLETED)
CAS-->>CA : finalized=true
CA-->>U : 跳转订单详情
else 仍需网关
CAS-->>CA : gateway_amount
CA->>PS : createForOrder(...,gateway_amount)
CA->>PR : provider.start()
CA-->>U : 返回第三方支付HTML
end
else 纯网关
CA->>PS : createForOrder(...,order_amount)
CA->>PR : provider.start()
CA-->>U : 返回第三方支付HTML
end

订单状态流转与支付回调

  • 下单后初始状态:PENDING
  • 0元订单(积分兑换或优惠券打到0):直接推进到 PAID
  • 支付成功后:
    • 在线支付:由支付回调触发 PaymentService 标记成功,内部调用状态机推进
    • 货到付款:CashierService.submitCod 标记成功并推进状态
    • 线下凭证:保存凭证后推进到 AWAITING_CONFIRMATION,等待后台确认
  • 完成态:无物流插件或非商品模块时,PAID 顺势推进到 COMPLETED
stateDiagram-v2
[*] --> PENDING : "下单成功"
PENDING --> PAID : "支付成功/0元订单"
PENDING --> AWAITING_CONFIRMATION : "线下凭证上传"
PAID --> COMPLETED : "无物流/非商品"
AWAITING_CONFIRMATION --> PAID : "后台确认"
PAID --> COMPLETED : "发货完成"

依赖关系分析

  • 控制器依赖服务:
    • CartController → CartService、OrderService
    • CheckoutController → CheckoutService、OrderService
    • CashierController → CashierService、OrderService、PaymentService
  • 服务间协作:
    • CartService 依赖 PricingService、OrderStockGuard、OrderService
    • CheckoutService 依赖 WalletService、PricingService、CouponEligibility、OrderService
    • CashierService 依赖 PaymentService、OrderStatusTransition、UserStatsService、OrderService
  • 外部集成点:
    • 支付方式插件:通过 Module::make('plugin.registry_aggregator') 获取 provider
    • 配送方式插件:通过 plugin()->valueByGroup('shipping', 'config') 获取配置
    • 优惠券模块:可选注入 CouponEligibility,未安装时为 null
graph LR
CC["CartController"] --> CS["CartService"]
CC --> OS["OrderService"]
CS --> PS["PricingService"]
CS --> SG["OrderStockGuard"]
CO["CheckoutController"] --> COS["CheckoutService"]
CO --> OS
COS --> WS["WalletService"]
COS --> CE["CouponEligibility"]
CA["CashierController"] --> CAS["CashierService"]
CA --> OS
CA --> PM["PaymentService"]
CAS --> OST["OrderStatusTransition"]

性能与并发考虑

  • 库存检查:
    • 加购与下单均调用 OrderStockGuard.checkStock,避免超卖
    • 实时更新购物车数量时,若库存不足则回退到可售数量
  • 价格防篡改:
    • 下单前基于 PricingService 与属性加价重新计算最终价,与购物车缓存比对,差异过大拒绝下单
  • 事务保护:
    • 下单事务包含积分扣减、优惠券核销、订单/明细/地址写入,异常回滚并记录日志
  • 会话缓存:
    • 运费与优惠券金额暂存 Session,减少重复计算
  • 支付超时:
    • 支付记录设置过期时间,结合定时任务自动取消未付款订单

故障排查指南

  • 登录态丢失:
    • 控制器 assertLogin 对 AJAX 返回 401,普通请求跳转登录
  • 购物车为空:
    • 结算页 getCheckoutData 返回空时提示并返回首页
  • 库存不足:
    • addtocart/updateCartItem/checkStock 返回 stock_error,需调整数量或等待补货
  • 价格被篡改:
    • createOrder 中价格重算不一致返回 price_changed,需刷新页面重试
  • 优惠券无效:
    • applyCoupon 中资格判定不通过,返回 0 抵扣;检查 coupon_log 归属与状态
  • 支付失败:
    • charge 中 pay_id 非法或插件不可用,返回收银台;检查插件注册与配置
  • 线下凭证上传失败:
    • 附件存储抛出 DomainException,返回错误消息并停留在收银台
  • 下单异常:
    • 事务捕获异常并回滚,记录详细日志(通道、模块、负载、异常堆栈)

更新 输入验证改进后,非法的 shipping_id 和 slug 参数将被安全过滤,减少潜在的安全风险

结论

DouPHP 订单控制器以清晰的分层与职责划分,实现了从购物车到下单、支付、状态流转的完整闭环。通过服务化封装与核心门面编排,既保证了业务规则的集中治理,又提供了良好的扩展性。最近的输入验证重构进一步提升了系统的安全性和可维护性。建议在扩展新功能时遵循:

  • 控制器保持薄,业务规则下沉到服务
  • 关键路径使用事务与幂等设计
  • 严格进行库存与价格校验
  • 通过插件机制扩展支付与物流
  • 利用状态机推进订单生命周期
  • 采用安全的输入验证模式,如中间变量和正则表达式过滤

附录:扩展指引

  • 添加新的支付方式:
    • 实现支付插件 provider,并通过插件注册器暴露
    • 在收银台 charge 中通过 pay_id 调起 provider.start()
    • 确保回调能正确标记支付成功并推进状态
    • 参考:CashierController.charge:127-234
  • 集成物流服务:
    • 在配送插件中提供 config(fee/free)与查询接口
    • 结算页通过 plugin()->valueByGroup('shipping','config') 读取配置
    • 参考:CheckoutService.getCheckoutData:92-106
  • 实现订单分析:
    • 基于 order、order_item、order_address、order_coupon 等表统计
    • 结合 OrderItemQuery 与 OrderScheduledTasks 提供的能力
    • 参考:OrderService.getOrderItem:183-193
  • 扩展优惠券策略:
    • 自定义 CouponEligibility 实现,控制 canUse 与 eligibleAmount
    • 在 CheckoutService.applyCoupon 中复用
    • 参考:CheckoutService.applyCoupon:170-211

更新 输入验证最佳实践示例:

  • 使用中间变量存储原始值和验证后的值
  • 应用严格的正则表达式验证
  • 对非法输入提供安全的默认值
添加日期:2026-10-05