AI Coding · 统一码文件型券展示改造
产品需求写得很直白:出海订单中心,商品是"统一码",兑换方式是"上传文件"——券码信息区只展示券图,红框里的数字码模块(文案、长串码、复制按钮、"待使用"状态)全部藏起来。
技术约束同样直白:改动要小,只动这一个场景,别的券类型一行别碰;代码得能测、能维护,别把 VoucherDetail.tsx 再堆成意大利面。
听起来像"加个 if 的事儿"。我一开始也这么以为。
开局:我在代码里"猜"了一轮
没有接口文档的时候,人很容易用 UI 现象反推后端语义。我(以及 AI 助手)早期走的就是这条路:
orderType === ORDER_UNIFIED像是统一码 ✓displayType !== SHOW_TEXT像是"非纯数字码"→ 当成上传文件?✗voucherValue是个 jpeg URL → 有图就是文件型?✗
全仓搜 deliveryType、redemptionType、git 历史、Jest 缓存……有效信息密度低得可怜。后端仓库当时还是空的,没法交叉验证。
转折点来了:你扔过来两样东西——订单详情接口文档截图(vouchers 字段枚举),再加一条样例响应 JSON。局面才从"推断"变成"对照":
- 字段
vouchers[].orderType- 枚举别名
ORDER_UNIFIED
- 字段
vouchers[].displayType- 枚举别名
SHOW_FILE
vouchers[].orderTypeORDER_UNIFIEDvouchers[].displayTypeSHOW_FILEJSON 里 orderType: ORDER_UNIFIED、displayType: SHOW_FILE、voucherValue 指向 jpeg——和文档严丝合缝。这次改造最关键的质量保障,就在这一张截图 + 一条 JSON 上。
改对了什么
招式 1:展示规则和 UI 解耦(SRP 那种)
判定和取码逻辑抽到 voucherPolicy.ts 三个纯函数:
- resolveOrderType() — 处理字段层级差异(券级 → 子单级 → 订单级)
- isUnifiedFileVoucher() — 唯一的业务开关
- getVoucherDisplayCode() — 展示层取码
VoucherDetail.tsx 只负责一件事:红框模块渲不渲染。好处很实在:
- 11 个单测覆盖层级解析与边界(统一码+文件型 / 统一码+二维码 / 其他+文件型) - 以后改 UI 不用再把 orderType 判断抄第三遍 - 类组件里常见且稳妥:业务规则进无副作用模块,组件做编排
// 层级回退:?? 只对 null/undefined 生效,0 不会被误判成"缺失"
item?.orderType ?? subOrderType ?? orderData?.orderType招式 2:改动面卡得紧
没动 getStatus、退款参数 couponCode、图片轮播、displayType != SHOW_SPECIAL 过滤。非「统一码+文件型」仍走原 renderCodeSection 分支;统一码非文件型券仍用 code,其他场景仍用 couponCode(历史提交约定的取码规则保留)。
这就是"只改上传文件场景"该有的样子。
招式 3:主动避开同名字段的坑
排查过程中特意区分了几组容易看岔的字段:
- subOrders[0].bizType — 子单级业务类型,含义不同于券级展示类型
- product.redemptionType — 商品兑换配置,不是券展示类型
- createVoucherMethod / voucherCreationMode — 发码方式,文档没定义成"上传文件展示类型"
JSON 扁平嵌套下,同名不同义是集成前端最常见的 bug 来源。路径写不对,测再多也白搭。
还能更好的地方
前期探索成本偏高
根因很简单:需求里的"上传文件"是后端枚举,不能从前端 UI 反推;后端仓库又空的。类似任务,文档/抓包前置能砍掉大量无效搜索。推断不是不能用,得标明"可能误伤 统一码+二维码"这种风险。
测试覆盖还有洞
voucherPolicy.test.ts 把判定函数盖住了,但缺:
- 组件级测试:统一码+文件型下红框 DOM 是否真的消失
- 混合券列表:第一张非文件型、后面是文件型时的展示(renderVoucherBody 仍按第一张 displayType===SHOW_TEXT 决定图片区,这是历史隐患)
Review 还点出一个产品边界:统一码+文件型在 refunding/refunded 仍渲染"退款详情"按钮,和"严格只展示图片"可能不一致——待产品确认,目前算半开着的门。
文档可以再沉淀一行
displayType 枚举别名现在只散落在注释里。建议在 voucherPolicy.ts 或团队文档补一句"来源:订单详情接口文档",后人改代码时少踩一次坑。
原理,不废话版
1. 业务规则绑后端契约,不绑 UI 现象
voucherValue 有 URL → 有图,不能说明"兑换方式 = 上传文件"。displayType === SHOW_FILE → 文档定义"文件",才是契约。宽条件(比如 displayType !== SHOW_TEXT)的典型后果:统一码 + 二维码被误隐藏数字码。
2. 空字符串驱动 UI,结构分支防空壳
getVoucherDisplayCode 在统一码+文件型返回 '',renderCode / renderCopy 用 if (!couponCode) return null。数据层空值驱动不渲染,比在多处重复 if (isUnifiedFileVoucher) 更 DRY;renderCodeSection 仍对整个红框做分支,是为了避免只剩"券码"标题、下面啥也没有的空壳 DOM——数据驱动和结构分支的折中。
3. 单测金字塔
纯函数单测:快、稳,11 条已够判定逻辑回归。组件测试要 mock i18n / CopyToClipboard,成本高,但适合锁 UI 契约。E2E 用真实统一码+文件型订单走查。三层各干各的,别指望一层包打天下。
4. 遗留类组件上的最小 diff
VoucherDetail.tsx 还扛着轮播、Popup、CopyToClipboard、路由跳转。这次只动 renderCodeSection 分支,没重构 Hooks——交付风险可控。规则外置到 voucherPolicy.ts,已经为将来抽 useVoucherDisplayPolicy(orderData, item) 留了接口。
结语
核心场景(统一码+文件型 待使用)符合;退款态按钮待产品拍板
判定精确,统一码+二维码等场景未误伤
规则外置 + 单测较好;缺组件级与混合券回归
文档/抓包前置可显著缩短前期搜索
这次改造的价值,不在于又隐藏了一个红框,而在于用后端明确枚举替代前端推断,再用纯函数 + 精准分支实现"只改上传文件场景"。
下次接到"枚举型展示规则"的需求,我会先把文档或一条 orderDetail JSON 甩到聊天框里,再让 AI 写第一行代码。推断可以留着当草稿,但别把它当交付标准——不然你和我都会在前仓里多绕好几圈。
本文属于 AI 实用主义流派 的第 41 篇肉身实战。
发表评论
分享你的想法和反馈