Prevent oversell by conditioning stock updates and locking SKUs in primary-key order, and record the same concurrent-counter rule in the API spec and agent guide. Co-authored-by: Cursor <cursoragent@cursor.com>
4.2 KiB
4.2 KiB
AGENTS.md — 给编码代理的指引
本仓库是 VMall B2B2C 商城 MVP(pnpm + Cargo 双 workspace)。改动前先读本文件与 README.md;功能变更须同步 OpenSpec。
布局与所有权
apps/api— Rust 后端(cratevmall-api)。迁移在apps/api/migrations/(sqlx,启动时自动执行,只增不改)。分层约定见英文:docs/adr/、docs/tech-specs/rust-api.md、openspec/specs/api-architecture/spec.md。代码按限界上下文放在src/modules/<ctx>/(handler → service → repo);HTTP 抽取器在src/http/。新功能写进对应模块,不要把 SQL 堆回 handler。简单 CRUD 允许 handler 直接调 repo。Service 返回ApiResult<Dto>,不依赖Json/StatusCode。Repo 接受&mut PgConnection/&mut Transaction以便组合事务。不引入泛型 Repository trait。apps/mall/apps/shop-admin/apps/admin— 三个 Nuxt 3 应用。端口固定 3000/3001/3002。packages/shared—@vmall/shared:唯一 API 契约(src/types.ts+src/api.ts)、en/zh 语言包、共享样式ui.css。前端禁止自建 API 封装;契约变更只在这里改,且三个前端都要过构建。openspec/— 规范。specs/是已归档能力规范(auth, rbac, catalog, currency, cart, order, shipment, invoice, api-architecture, frontend-*)。scripts/seed-demo.mjs— 幂等演示数据。
硬性约定
- 金额:
i64/number最小单位 + 币种码;换算走后端/api/currencies/convert或共享formatMoney(minor, code, exponent, locale)。禁止浮点金额运算,禁止在前端硬编码 exponent=2(JPY 是 0)——用币种表里的 exponent。 - i18n 内容:凡是面向用户的内容字段都是
{"en","zh"}JSONB;UI 文案一律$t(),缺键加到本应用的locales-extra.ts,不要从应用代理改packages/shared语言包以外的文件(共享包改动属契约变更,需谨慎并跑齐三个构建)。 - TS 纪律:禁止
any/as any/@ts-ignore;外部数据用类型守卫或 schema 校验;客户端方法类型在@vmall/shared命名导出。 - Rust 纪律:错误统一
ApiError({"error":{"code","message"}});SQL 用sqlx::query*+ 显式 bind;状态迁移必须校验前置状态(参考 orders/shipments 的UPDATE ... WHERE status = ...模式)。并发计数扣减(目前只有skus.stock):多行FOR UPDATE必须先ORDER BY主键;扣减必须SET col = col - $qty WHERE id = $1 AND col >= $qty,rows_affected = 0→Conflict。禁止无条件SET col = col - n。回补用col = col + n,不要用读出的绝对值写回。商家覆盖赋值(SET stock = $n)不是扣减,不走此模式。 - RBAC:受保护路由声明角色(
auth.require(&[...]));店铺资源必须过auth.own_shop()作用域,跨店返回 404。
开发流程(OpenSpec)
- 新能力/行为变更:在
openspec/changes/<name>/建proposal.md+tasks.md+specs/<capability>/spec.md(## ADDED Requirements/### Requirement:/#### Scenario:格式),openspec change validate <name> --strict必须通过。 - 按 tasks 实现;完成后勾选并
openspec archive <name> --yes;归档后openspec validate --all --strict保持全绿。 - 只改文档/样式/重构可不建 change。
验证(交付前必跑)
cargo test -p vmall-api # 集成测试连 docker 的 vmall_test + Redis,须全绿且可重复运行(测试夹具 slug 已唯一化,勿改回固定 slug)
pnpm --filter @vmall/<改动过的前端> build
- 修 bug:先复现,修完确认复现不再触发;有价值的场景补成集成测试(放
apps/api/tests/,复用tests/common/mod.rs夹具)。 - UI 变更:起 dev server 用浏览器实际点一遍改动路径,视觉确认才算完成。
- 不要跑与改动无关的全仓命令(
pnpm -r build、lint 全仓等)。
环境
- Postgres:
docker exec pg18 psql -U postgres(postgres/postgres;库vmall/vmall_test)。Redis:容器rdb8。 - 后端环境变量见 README;测试库连接串写死在
apps/api/tests/common/mod.rs。