示例代码均为匿名化改写,用于说明模式;实际状态和操作应按服务端合同映射。
状态码最终会变成操作按钮
订单状态会影响列表页和详情页展示哪些操作。若每个视图分别维护自己的条件分支,产品规则很容易在页面迭代后分叉。
可以由一层纯逻辑把外部状态映射为语义化动作,例如付款、取消、联系业务对方、申请退款、完成流程、查看评价或再次创建。视图只负责排列按钮和导航,规则层负责决定当前允许的动作。
enum OrderState: Sendable {
case awaitingPayment, inProgress, completed, cancelled, unknown
}
enum OrderAction: Hashable, Sendable {
case pay, cancel, contact, requestRefund, review, createAgain
}
enum OrderSurface {
case list, detail
}
func actions(for state: OrderState, on surface: OrderSurface) -> Set<OrderAction> {
switch (state, surface) {
case (.awaitingPayment, _):
return [.pay, .cancel]
case (.inProgress, .detail):
return [.contact, .requestRefund]
case (.inProgress, .list):
return [.contact]
case (.completed, _):
return [.review, .createAgain]
case (.cancelled, _):
return [.createAgain]
case (.unknown, _):
return []
}
}
多个页面的矩阵可以不同
列表、详情、状态卡片和条目卡片的空间与用户目标不同,它们不必呈现完全相同的操作集合。某个操作只出现在详情页可能是有意的入口设计;是否应统一需要产品合同,而不是只凭代码重复程度判断。
测试应逐状态覆盖各页面可见动作,并额外检查服务端返回的凭证是否允许显示核验入口。完成流程或附加服务的参数,也适合由纯函数生成并独立验证。
列表和详情页的操作可以有意不同;列表优先减少误触,取消等高影响操作可以放在详情确认流程中。入口差异应写进状态矩阵与测试,不要仅为消除重复而强行统一。
让状态码更容易阅读
用原始整数散落在多个分支里,会迫使维护者反复查找后端定义。可以在解码边界转换为明确的状态类型,同时保留未知状态兜底。不要在没有接口合同的情况下猜测数字含义。
解码层还应保留未知状态兜底:遇到服务端新状态时映射到安全的未知值,并返回空操作集,避免旧客户端崩溃或显示不适用的操作。
实践建议
- 将状态到动作的映射集中在一个纯逻辑层。
- 对列表与详情分别描述预期矩阵,保留有意差异。
- 对危险操作使用明确的二次确认。
- 服务端仍需重新校验客户端提交的状态变更。
