ShopSystem Day 13 完成路线图第一阶段「基础设施补全」末项 地址管理完整闭环:补齐地址簿 API 缺失的 PUT/PATCH(原本只有 GET/POST/DELETE,编辑功能得绕过整条新建再删除),解决”删默认地址后用户无默认”的兜底逻辑(自动提 id 最大即最新一条为新默认),把地址簿从账户页 Tab 抽成独立路由 /account/addresses,让结算页 /checkout 真正可选地址簿条目下单、并把 orders.address_id 写入数据库形成 orders → addresses 外键追溯能力。整套改动 5 个文件 495 行新增 / 119 行修改,无新表(schema 早已就绪),0 个真 Bug(Qclaw 浏览器用户视角 12 项测试全过)。

结算页地址选择视图:已登录用户进 /checkout,服务端 getOptionalUser() 拿当前用户,查 addresses 表返回 savedAddresses 数组传给客户端。前端用 radio 卡渲染所有地址,默认项 auto-checked(右上角带「默认」徽章),用户切到任一地址,下方收货人姓名/手机号/详细地址字段实时同步成这条地址的值,不需要重新填一遍。底部「+ 使用新地址」按钮按需展开新表单——勾选「同时保存到我的地址簿」可在下单时一并入库,取消则当作临时一次性地址只走 address_snapshot JSON 快照(对历史匿名订单兼容)。

用户中心地址簿视图: /account/addresses 独立路由,AccountShell 统一侧栏 + 顶栏布局,复用 AddressesTab 组件。列表展示所有地址(默认项置顶),每张卡含「编辑」「设为默认」「删除」三按钮 + 「+ 新增收货地址」入口。编辑走 PUT /api/addresses/[id](完整覆盖)或 PATCH /api/addresses/[id](部分字段),支持字段白名单(recipient/phone/province/city/district/detail/zip) + 手机号格式正则校验;isDefault 互斥由后端事务保证——同一用户任意时刻最多一条 is_default=1。删除走 DELETE /api/addresses/[id],若删的是默认且还有其他地址,后端 SELECT MAX(id) 自动提拔最新一条为新默认,前端无需任何额外操作。

订单详情视图:下单时若选了地址簿某条,后端校验 ownership(防止 A 用户用 B 的 address_id) → 写 orders.address_id + 仍兼容 orders.address_snapshot JSON 双轨;订单详情页 /orders/[orderNo] 渲染时优先用 JOIN addresses 拿最新地址,若地址已被用户删除则降级用 address_snapshot 历史快照——这样既保证订单能看到当前地址的最新状态(地址改了订单也跟新),又保证地址删了订单不丢收货信息。已登录用户用地址簿下单、未登录用户走一次性匿名表单、已登录但地址簿为空仍能下单三种场景全覆盖。Qclaw 用 xb 浏览器 + curl 混合跑了 12 项测试:列表/编辑/设默认互斥/新增/删除兜底/手机号校验/checkout 三状态/订单写入/匿名下单,全过 0 bug。
API 层关键设计:PUT/PATCH 走动态 SET 防止部分编辑时把已填字段覆盖成 NULL(如只改 phone 不重传 recipient);isDefault 互斥用单事务 UPDATE addresses SET is_default = (id=?) WHERE user_id=? 一条 SQL 完成切换,避免 SELECT-then-UPDATE 的竞态;DELETE 兜底逻辑用 IF/ELSE 在 Node 端判断行数,既删地址又自动提升新默认,事务保证二者原子。踩坑方面:getOptionalUser import 路径要从 @/lib/auth(不是 @/lib/session,老代码惯性地引错);PUT/PATCH handleUpdate 显式收 (req, params) 对象避免 Next.js 14 App Router 的 TS 误判;动态 SQL 的 SET 子句不能漏 comma,这是 SQL 拼接的经典坑。Day 13 累计完成 5/5 文件改动 + 0 bug,代码已推送到 GitHub main 分支。按路线图原计划 Day 14(2026-08-27)进入物流跟踪(模拟快递100 API),属于第一阶段最后一环,补完就完成 4 天基础设施 → 进入第二阶段用户体验(Day 15 退款/售后)。
OpenClaw—AI研究