商城系统最容易写乱的,常常不是支付,而是状态文案和跨页面一致性

11 阅读7分钟

很多人一提到商城系统,第一反应都是支付、订单和库存,我最近这几个月在 Flutter 项目里做道具商城、我的背包、装扮和购买流程,最大的感受反而是:最容易把系统做乱的,往往不是支付,而是同一件商品在不同页面里到底该怎么解释、怎么预览、怎么刷新状态。

以前我对商城系统的理解也比较直接。

总觉得最难的地方应该是:

  • 下单
  • 支付
  • 订单状态

这些当然重要。
但我这几个月在 Flutter 项目里把道具商城、我的背包、装扮列表、购买弹窗、购买成功页一路做下来以后,越来越强烈地感觉到:

真正最容易把商城系统做乱的,常常不是支付,而是状态文案和跨页面一致性。

因为支付这件事,大家天然会小心。

反而是这些看起来“像小事”的地方,最容易一点点漂掉:

  • 到底算不算拥有
  • 永久拥有和已拥有怎么区分
  • 已过期什么时候提醒
  • 预览状态和真实拥有状态是不是一回事
  • 买完之后,哪些页面应该马上刷新

这些东西只要一不一致,用户很快就会觉得系统“不靠谱”。

1. 商城系统一旦串起多页面,真正难的不是“能不能买”,而是“同一件东西到底是不是在说同一种话?”

如果一个商城只有商品列表和支付页,问题还没那么明显。

但项目一旦发展到同时有:

  • 商城列表
  • 我的背包
  • 装扮选择
  • 用户详情预览
  • 购买弹窗
  • 购买成功页

系统的难点就会慢慢变掉。

因为这时候同一个道具,不再只出现在一个地方。

它会在很多页面里以不同状态出现。

比如:

  • 商城里你看到的是“还没买”
  • 购买弹窗里你看到的是“是否永久拥有”
  • 背包里你看到的是“是否过期”
  • 预览时又可能是“虽然没买,但正在试戴”

如果这些页面说的不是同一种语言,用户感受到的就不是“功能很多”,而是:

怎么每个地方都像在说不同的话。

2. 这次项目里,最让我警惕的不是支付链,而是状态文字开始在各处漂

这类问题一开始都很细。

比如有的地方写:

  • 暂未拥有

有的地方写:

  • 已拥有

有的地方又要判断:

  • 永久拥有
  • 几天后过期
  • 几小时后过期
  • 已过期

项目里购买弹窗的状态判断,已经不是一句简单文案,而是专门做了一层规则:

if (widget.isUnlock == false) {
  return _StatusInfo(text: '暂未拥有', isWarning: false);
}

if (expireStr == null || expireStr.isEmpty) {
  return _StatusInfo(text: '已拥有', isWarning: false);
}

if (difference.isNegative) {
  return _StatusInfo(text: '已过期', isWarning: true);
}

而且后面还会继续往下细分:

  • 7 天以上怎么显示
  • 1 天以内怎么显示
  • 是否要变红

这类代码看起来很小。

但它其实在说明一件很重要的事:

商城系统真正容易乱的,常常不是接口,而是“状态解释”。

因为一旦解释不统一,用户会直接怀疑:

  • 我到底有没有这个东西
  • 我现在能不能用
  • 这个状态算正常还是异常

3. 最难的地方不是一个页面怎么写,而是预览、拥有和过期这三件事经常不是同一个概念

这是我这次做装扮和背包相关页面时,感受特别深的一层。

很多人做商品页时,会默认把这些状态揉在一起:

  • 正在预览
  • 已经拥有
  • 当前穿戴中
  • 已经过期

可真实项目里,这些根本不是同一个概念。

比如用户详情页选择头像框时,就会有这种逻辑:

  • 预览时优先展示临时选中的效果
  • 即使这个头像框还没拥有,也照样允许先看
  • 保存成功后再回到真实状态

而背包页里又要处理另一层:

  • 已拥有但未过期
  • 已拥有但已过期
  • 未拥有但正在试戴

只要这几种状态混在一起写,后面一定越来越乱。

所以我后面越来越觉得,商城系统里最重要的一层,不是 UI 画得多好,而是:

你有没有把“预览”“拥有”“穿戴”“过期”这些状态分清楚。

分不清,页面迟早互相打架。

4. 购买成功以后真正麻烦的,也不是弹个提示,而是状态要不要同步回所有相关页面

这也是商城类系统特别容易被低估的一点。

很多人会觉得,买成功以后:

  • 弹个成功页
  • 提示一下

基本就结束了。

但真实项目里,购买成功只是另一轮一致性问题的开始。

因为用户买完以后,至少会关心这些地方是不是一起更新了:

  • 商城列表
  • 当前购买弹窗
  • 我的背包
  • 装扮列表
  • 用户详情里的预览效果

项目里的购买逻辑,除了支付本身,还要处理:

  • 已拥有数量
  • 购买成功状态
  • 购买成功回调
  • 跳转到背包时的定位信息

像这里:

final RxInt ownedCount = 0.obs;
final RxBool isPurchaseSuccess = false.obs;
final Future<void> Function()? onPurchaseSuccess;
final int? assetId;
final int? tag;

这说明购买动作本身,已经不是单点页面行为了。

它在影响:

  • 购买弹窗怎么关
  • 成功页怎么展示
  • 背包页跳过去定位到哪里
  • 上一层列表是否需要刷新

如果这些动作没有提前设计好,后面就很容易出现一种非常伤体验的情况:

明明买成功了,但不同页面还在各说各的。

5. 这类系统越往后做,越不能只靠“哪儿不对就补哪儿”

我觉得,商城系统和很多普通页面最大的区别是:

它特别不适合靠“哪儿不对就补哪儿”的方式往下做。

因为支付、背包、预览、装扮、昵称颜色、头像框这些东西一串起来以后,系统真正需要的已经不是局部修补,而是:

  • 哪些状态在所有页面都要统一
  • 哪些状态只是当前页面临时展示
  • 哪些状态改变后,必须把相关页面一起带动

如果没有这层意识,后面你会越来越频繁地遇到这种提交:

  • 修商城右下角文案
  • 修购买页文案
  • 修背包状态显示
  • 修购买成功后不刷新
  • 修某个页面和另一个页面说法不一样

这些提交单独看都合理。
但它们反复出现,本身就说明一件事:

系统还没把“状态一致性”收成公共规则。

6. 我现在确定,商城系统真正值钱的不是“把购买流程跑通”,而是把状态解释做稳

支付当然重要,而且必须稳定。

但如果现在让我回头总结这条线里最值钱的经验,我不会先讲支付。

我反而会先讲:

  • 状态文案怎么统一
  • 过期规则怎么表达
  • 预览和真实拥有怎么拆开
  • 购买成功后哪些页面必须同步

因为用户最后记住的,不是你内部有几层控制器,也不是订单接口调得多漂亮。

用户记住的是:

  • 我看到的状态是不是清楚
  • 不同页面是不是在说同一件事
  • 我买完之后系统是不是马上变得可信

这也是为什么我现在会觉得:

商城系统最容易写乱的,常常不是支付,而是状态文案和跨页面一致性。

支付出了问题,大家会第一时间紧张。
反而是这些看起来不大的状态问题,最容易在一个复杂项目里一点点把体验拉散。