05203a9b5b
Design Source Checks / design-source (push) Failing after 14m23s
- git mv 全目录(历史保留);.gitignore 收敛为整个 .superpowers/ 忽略 - 全仓 16 处引用同步(CI checks.yml / l1-sync / screens.mjs / hooks / CLAUDE.md / CONTRACT / SSR 模板注释 / docs / web 注释) - 修 pre-commit 路径正则残留;5180 评审服务已切新路径 - 验证:check-ds 12 道 / l1-sync 6 道 / fidelity 抽查全过 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
376 lines
20 KiB
Markdown
376 lines
20 KiB
Markdown
# 酒库管理系统 — 项目规则与 Orchestrator
|
||
|
||
## 必读
|
||
|
||
**开始任何任务前,先读 `docs/context/project.md`** 了解项目全貌。
|
||
|
||
**文档地图**(入口 `docs/index.html`):
|
||
- 用户使用手册:`docs/manual/user-manual.html`(官网帮助页精简版源=`web/content/docs.md`,两者同步改)
|
||
- 开发手册(架构/后端/前端/数据库/API/运维/发版):`docs/manual/dev-manual.html`
|
||
- 数据库列级文档:`docs/db-schema.html`(schema 变更时同步)
|
||
- 设计契约与像素闸:`design/CONTRACT.md` + `tools/screens.mjs`
|
||
|
||
---
|
||
|
||
## Orchestrator:如何自动选择 Agent
|
||
|
||
根据用户请求的关键词和意图,按以下规则判断调用哪些 Agent:
|
||
|
||
### 新功能开发(完整流水线)
|
||
|
||
触发词:「新增」「开发」「实现」「做一个」+ 功能描述
|
||
|
||
```
|
||
串行:requirements-analyst → architect
|
||
并行:api-designer + db-designer(同时进行,都依赖架构但互不依赖)
|
||
|
||
→ ui-designer 判断复杂度:
|
||
简单变更:直接输出规范 → 继续下一步
|
||
复杂设计:输出 Figma 需求文档 → 暂停等用户确认设计稿
|
||
|
||
并行:backend-coder + flutter-coder(都依赖 API 设计,互不依赖)
|
||
串行:test-engineer → linter → code-reviewer → security-auditor
|
||
```
|
||
|
||
### 仅后端修改
|
||
|
||
触发词:「修复后端」「后端接口」「API」+ 具体描述
|
||
|
||
```
|
||
backend-coder → test-engineer → linter → code-reviewer
|
||
```
|
||
|
||
### 仅前端修改
|
||
|
||
触发词:「UI」「界面」「Flutter」「页面」
|
||
|
||
```
|
||
flutter-coder → linter
|
||
```
|
||
|
||
### Bug 修复
|
||
|
||
触发词:「bug」「报错」「修复」「问题」
|
||
|
||
```
|
||
1. 先判断 bug 来源(后端/前端/数据库)
|
||
2. 调用对应的 coder agent 修复
|
||
3. test-engineer 验证修复
|
||
```
|
||
|
||
### 安全/代码质量检查
|
||
|
||
触发词:「安全」「漏洞」「review」「审查」「代码质量」
|
||
|
||
```
|
||
并行:security-auditor + code-reviewer + linter
|
||
```
|
||
|
||
### 线上问题
|
||
|
||
触发词:「线上」「故障」「宕机」「慢」「报错」
|
||
|
||
```
|
||
sre(优先级最高,立即响应)
|
||
→ 根据 sre 诊断结果,再决定是否调用 backend-coder 修复
|
||
```
|
||
|
||
### 文档需求
|
||
|
||
触发词:「文档」「说明」「手册」「README」
|
||
|
||
```
|
||
doc-writer
|
||
```
|
||
|
||
---
|
||
|
||
## Agent 边界规则(强制执行)
|
||
|
||
| Agent | 可以写 | 不可以写 |
|
||
|-------|--------|----------|
|
||
| requirements-analyst | docs/requirements/ | 任何代码 |
|
||
| architect | docs/architecture/ | 任何代码 |
|
||
| api-designer | docs/api/ | 任何代码 |
|
||
| db-designer | backend/schema/ | 业务代码、测试代码 |
|
||
| backend-coder | backend/internal/, backend/main.go | *_test.go, schema/ |
|
||
| ui-designer | docs/design/ | 任何代码 |
|
||
| flutter-coder | client/ | backend/ |
|
||
| test-engineer | *_test.go, docs/review/*-bugs.md | 业务代码 |
|
||
| linter | 格式自动修复(gofmt/dart format),docs/review/lint-report.md | 逻辑代码 |
|
||
| code-reviewer | docs/review/*-review.md | 任何代码 |
|
||
| security-auditor | docs/security/ | 任何代码 |
|
||
| devops | deploy/, .gitea/workflows/, scripts/ci/ | 业务代码 |
|
||
| sre | docs/runbooks/ | 任何代码 |
|
||
| doc-writer | docs/(除 context/ 外) | 任何代码 |
|
||
|
||
---
|
||
|
||
## Agent 间通信协议
|
||
|
||
**文件传递**(主要方式):
|
||
- 每个 Agent 完成后将结果写入约定文件
|
||
- 下游 Agent 开始前读取上游产出文件
|
||
- 文件格式:混合模式(中文叙述 + YAML/代码块)
|
||
|
||
**短链式调用**(反馈循环):
|
||
- test-engineer 发现 bug → 直接将错误信息和 `docs/review/{功能}-bugs.md` 路径传给 backend-coder
|
||
- security-auditor 发现 Critical 问题 → 直接告知 backend-coder 修复,不等下次迭代
|
||
|
||
---
|
||
|
||
## 并行 Agent 调度规则
|
||
|
||
当多个 Agent 可以并行时,在同一条消息中发起多个 Agent 调用:
|
||
|
||
```
|
||
# 可以并行的情况(互不依赖):
|
||
- api-designer + db-designer
|
||
- backend-coder + flutter-coder
|
||
- security-auditor + code-reviewer + linter
|
||
|
||
# 必须串行的情况(有依赖):
|
||
- requirements-analyst 完成后 → architect 才能开始
|
||
- architect 完成后 → api-designer 和 db-designer 才能并行开始
|
||
- backend-coder 完成后 → test-engineer 才能开始
|
||
```
|
||
|
||
---
|
||
|
||
## Git 提交规范
|
||
|
||
每个 Agent 完成工作后提交,commit message 格式:
|
||
|
||
```
|
||
{类型}({模块}): {简短说明}
|
||
|
||
类型:feat | fix | test | docs | chore | security | refactor
|
||
模块:backend | client | db | deploy | docs
|
||
|
||
示例:
|
||
feat(backend): 新增财务报表接口
|
||
test(backend): 财务报表模块测试用例
|
||
docs(api): 财务报表 API 接口文档
|
||
security: 修复入库单多租户隔离漏洞
|
||
```
|
||
|
||
---
|
||
|
||
## 开发完成标准(Definition of Done)
|
||
|
||
**任何功能或修复,必须满足以下全部条件才算完成:**
|
||
|
||
### 后端变更
|
||
|
||
```bash
|
||
# 1. 编译通过
|
||
export PATH="/opt/homebrew/bin:$PATH"
|
||
cd backend && go build ./...
|
||
|
||
# 2. 所有测试通过(不得有 FAIL)
|
||
cd backend && go test ./...
|
||
|
||
# 3. 无编译警告(vet 检查)
|
||
cd backend && go vet ./...
|
||
```
|
||
|
||
### 前端变更
|
||
|
||
```bash
|
||
# 1. 静态分析无 error(warning/info 允许存在)
|
||
cd client && flutter analyze --no-fatal-infos --no-fatal-warnings
|
||
|
||
# 2. 所有测试通过
|
||
cd client && flutter test
|
||
```
|
||
|
||
### 通用要求
|
||
|
||
- [ ] 代码已按 Git 提交规范提交(feat/fix/test/...)
|
||
- [ ] 涉及新接口:已在对应测试文件中覆盖核心路径
|
||
- [ ] 涉及数据库变更:schema.sql 和 model 同步更新
|
||
- [ ] 多租户隔离未被破坏(所有查询含 shop_id 条件)
|
||
|
||
**测试未通过前,禁止提交代码、禁止打 tag 发版。**
|
||
|
||
---
|
||
|
||
## 代码质量门禁
|
||
|
||
以下情况**不得提交**:
|
||
- `go build ./...` 失败
|
||
- `go test ./...` 有失败用例
|
||
- 存在 security-auditor 标记的 Critical 级别问题
|
||
- 存在 code-reviewer 标记的 blocking 级别问题
|
||
|
||
---
|
||
|
||
## 项目强制规则
|
||
|
||
### 多租户隔离
|
||
- `shop_id` **永远**从 JWT token 中提取,使用 `middleware.GetShopID(c)`
|
||
- **绝不**从请求参数、URL 或请求体中读取 `shop_id`
|
||
- 所有数据库查询必须带 `WHERE shop_id = ?` 条件
|
||
|
||
### 数据模型:product = 特有产品/序列号(核心铁律)
|
||
- `products` 每行 = 一个**特有产品/序列号**(`code`=商品编号,同店唯一 `uk_shop_code`),**不是 SKU**;语义:`name`=品牌、`series`=型号、`spec`=版本。product 是商品信息的**单一来源**(序列号/品牌型号版本/生产日期 `production_date`/批次 `batch_no`/进价/图片/`public_id`)。
|
||
- **入库每录一行 = 新建一个独立 product**(`createIndependentProduct` + `nextProductCode` max+1),**绝不**按名称/系列/规格复用(`findOrCreate` 复用已废弃)。同理任何"导入/建库存"逻辑:有商品编号时按编号匹配/建 product,**禁止**按名称合并(历史 bug 根因)。
|
||
- 基础数据字典(品牌/型号/版本 + 产地/保质期/储存/介绍)在「基础数据」页管理,入库选文本后据此建 product。
|
||
- 库存(`inventories`)/出入库明细 = 指向 product 的引用 + **快照列**(`product_code/name/series/spec/...`,导入/审核时拷贝,作历史保真+显示兜底)。显示优先 product、回退快照(`COALESCE(p.*, 快照)` / 前端 `lineOrProduct`)。**快照列保留,勿擅自删**。
|
||
|
||
### 定价字段口径(2026-07 消歧后,唯一口径)
|
||
- 明细两侧价格分列:`cost_price`(成本/进价单价)+ `cost_amount`(成本小计);出库另有 `sale_price`(售价)+ `sale_amount`(售价小计,待定价=0)。**旧列 unit_price/total_price/total_amount 已弃用**(值已回填新列,观察一版后 DROP),新代码禁止读写旧列。
|
||
- 单据合计:入库 `cost_total`(应付=Σ总进价);出库 `sale_total`(应收=Σ售价小计)+ `profit_total`(总利润=Σ(售价>0?(售价-成本)×数量:0),建单落库,确认售价/确认进价联动重算,重算函数 `recalcStockOutProfit`)。
|
||
- **成本与利润仅管理员可见**:出库 List/Get 对 operator/readonly 服务端抹零(`stripStockOutCost`),前端同时隐藏对应列——新增出库相关展示时两侧都要遵守。**库存同口径**(2026-07-06):库存 List 抹空 unit_price、Summary 抹零 stock_value/last_month_value(`stripInventoryCost`),前端隐藏成本价/总价列、卡片单价、货值 KPI(`isAdminProvider`)、商品抽屉成本行。
|
||
- 财务口径不变:应收/应付、退单冲账均按上述合计列;出库退单展示与冲账同为售价口径。
|
||
|
||
### 库存变更
|
||
- 入库/出库审核通过时,必须在同一事务中同时完成:
|
||
1. 更新 `inventories` 表数量
|
||
2. 写入 `inventory_logs` 流水记录
|
||
- 出库前必须校验库存充足,不足时返回错误并回滚
|
||
- **库存三态**(2026-07-07 落库 `inventories.status`):`stock` 库存(默认,不公开)⇄ `on_sale` 在售(公开酒单展示,抽屉 switch 手动挂售,接口 `PUT /inventory/:id/status` 仅收这两值)→ `sold` 已售(出库审核扣光自动置入,**留痕不再软删**;退单恢复置回 stock)。列表默认排除已售(显式 `status=sold` 才返回);Summary 的 SKU/数量/货值排除已售、另出 `sold_count`;公开店铺页只列 on_sale。盘亏归零仍软删(灭失≠售出)。预警/缺货退出状态维度,由数量 vs 安全库存染色承担
|
||
- 审核中(pending)单据支持**撤回**(`withdraw`)回 draft:管理员/超管任意单、操作员限本人单(`OperatorID==本人`,handler 内判权,非 AdminOnly 中间件)
|
||
|
||
### Schema 管理
|
||
- 表结构变更:修改 `backend/schema/schema.sql` + `backend/internal/model/` 对应 model
|
||
- 启动时自动 AutoMigrate,无单独迁移文件
|
||
- **禁止**为每个新业务字段修改表结构,使用 `custom_fields JSON` 动态扩展
|
||
|
||
### 种子数据
|
||
- 测试数据以 SQL 文件形式维护:`backend/seeds/<shop_code>.sql`
|
||
- 通过 `sh scripts/dev.sh seed <shop_code>` 执行(内部用 `docker exec` 注入 MySQL 容器)
|
||
- 每个门店一个文件,SQL 文件顶部包含 TRUNCATE,每次执行完整重建
|
||
|
||
### Go 命令
|
||
- 所有 Go 命令前须确保 PATH:`export PATH="/opt/homebrew/bin:$PATH"`
|
||
- 或直接使用 `sh scripts/dev.sh` 脚本(已内置 PATH)
|
||
|
||
### 前端 URL 配置
|
||
- 所有后端 URL 必须通过 `AppConfig` 读取(`client/lib/core/config/app_config.dart`)
|
||
- **禁止**在任何 provider / repository / widget 中硬编码 `localhost:8080` 或任何 IP/域名
|
||
- 使用:`AppConfig.apiBaseUrl`、`AppConfig.healthUrl`、`AppConfig.versionUrl`
|
||
|
||
### 前端平台判断
|
||
- 使用 `dart:io` 的 `Platform` 类前,必须先检查 `kIsWeb`(`import 'package:flutter/foundation.dart'`)
|
||
- Web 平台不支持 `dart:io`,直接使用会在 Web 构建时崩溃
|
||
|
||
### 设计真相源分层(原型漂移治理,2026-07-04 拍板)
|
||
|
||
原型(`design/prototype/`)与真实页面**不追求全量永久同步**,按层分治:
|
||
|
||
- **L1 设计系统层**(`tokens.css` / `atoms.css` / `mobile-atoms.css` / `icons.js` + `index.html` 登记):原型是**单一真源**。新增颜色/组件/图标必须**先登记原型再同步**代码与官网(如 `web/_includes/icons.njk`)。闸:pre-commit `check-ds.mjs` 12 道(本地钩子,新机器先跑 `sh scripts/hooks/install.sh`)+ 代码侧 `client/tool/check_ds_code.mjs`。
|
||
- **L2 屏级三态**(台账 = `design/CONTRACT.md` 块 4「真相源」列):
|
||
- `同步`:注册于 `tools/screens.mjs`,fidelity 像素闸生效。整屏改版须原型先行;小迭代改后跑 fidelity 确认阈内,超阈则同步原型或降级快照。
|
||
- `快照`:原型退役为历史参考,**golden + CONTRACT 文字规格为准**;小迭代只更 golden,**不回填原型**。
|
||
- `代码先行`:无原型屏,golden 为唯一像素基准。
|
||
- **L3 新屏/整屏改版**:design-first 不变(原型 → CONTRACT → 实现 → fidelity 验收),建成后入 `同步` 态。
|
||
- **漂移体检**:不逐提交强制同步;大版本/整屏改版前跑 `node tools/fidelity.mjs`,按报告逐屏决定「重新对齐原型」或「降级快照」,结果回填 CONTRACT。
|
||
- **CI 闸**(`.gitea/workflows/checks.yml`,PR + main push 自动跑):① `check-ds.mjs` 原型 12 道;② `tools/check-l1-sync.mjs` L1 同源四道(tokens 快照逐字节 / icons 两端同集同内容 / 官网 token 值对齐原型主题 A / web 硬编码 hex,白名单 #fff、#1677ff,`ds-allow` 可豁免);③ `client/tool/check_ds_code.mjs` Flutter 颜色单源;④ codegen 新鲜度(regen→`dart format`→与入库产物零 diff)。本地可直接跑同名命令。
|
||
|
||
### 表格与筛选/排序(ds 真相源)
|
||
- 列表屏统一 `DsTable`(`widgets/ds/ds_table.dart`:toolbar+表格+pager 连成一卡),列定义 `DsColumn`,可隐藏列走列设置菜单
|
||
- **筛选统一放工具栏 `DsChip`**(2026-07-07 用户口径,形式参照出库管理:单选/多选 chip + 选中值/N 项摘要 + × 单清);列头**只放排序** `DsSortHeader`(点击循环 无→升→降→无,服务端全局排序)。列头漏斗 `DsFilterHeader` 已退役删除
|
||
- 旧 `FilterableColumnHeader`/`multi_select_dropdown.dart` 仅存量引用,勿在新屏使用
|
||
|
||
### 响应式 / 移动端适配
|
||
- 断点判定统一用 `context.isMobile`(`client/lib/core/responsive/responsive.dart`,宽度 < 600 为窄屏/手机),**禁止**在各处散落 `MediaQuery.width < 600` 这类魔法数
|
||
- **列表屏**:统一用 `DsTable`(`widgets/ds/ds_table.dart`,真相源表格:toolbar+列+pager);窄屏卡片流用 `MCard`(`widgets/ds/m_card.dart`,镜像原型 mobile-atoms)。旧 `MobileListCard` 为 legacy 仅存量引用(迁移在 todo),旧 `DataTableCard` 已删除(2026-07-03),勿再引用
|
||
- **弹窗固定宽度**:一律用 `context.dialogWidth(X)`(≤ 屏宽 92%),**禁止**裸写 `width: <固定值>` 导致窄屏溢出
|
||
- **导航**:窄屏(手机)= 底部 5 tab(库存/入库/出库/财务/我的)+「我的」hub 聚合二级屏(`app_shell.dart` + `m_tab_bar.dart`,2026-07-04 起,Drawer 已退役);宽屏保持侧边栏;一切弹层窄屏统一走 `showMSheet` 底部 sheet
|
||
- 平台判断仍遵守上面「前端平台判断」规则(`kIsWeb` 先于 `dart:io`)
|
||
|
||
### 权限中间件(已全局挂载)
|
||
- `middleware.ReadOnly()`:已挂载到主 `api` 路由组,只读用户(role=readonly)所有写操作自动返回 403
|
||
- `middleware.AdminOnly()`:挂载在 `/users` 路由组,仅管理员可管理用户
|
||
- **新增写操作路由**时,默认受 `ReadOnly()` 保护;若仅管理员可用,需加入 `AdminOnly()` 子组
|
||
|
||
### 并发安全(数据库操作)
|
||
- 在同一事务内读后写的操作,必须用 `FOR UPDATE` 锁行:
|
||
```go
|
||
tx.Set("gorm:query_option", "FOR UPDATE").Where(...).First(&model)
|
||
```
|
||
- 适用场景:单号生成(`number_rules`)、库存扣减(`inventories`)、任何 check-then-act 模式
|
||
|
||
### 发版流程
|
||
|
||
**发版拆成三条互不影响的流水线**,各有 tag 前缀、独立版本序列、独立 CHANGELOG:
|
||
|
||
| part | 范围 | tag 前缀 | CHANGELOG | workflow |
|
||
|------|------|---------|-----------|----------|
|
||
| **client** | `client/` Flutter 全平台(Web→`/app`、macOS、Windows、Android、iOS)+ 应用自更新清单 `version.yaml` | `client-v*` | `CHANGELOG-client.md` | `deploy-client.yml` |
|
||
| **site** | `web/` Eleventy 营销宣传站(→`/opt/jiu/marketing`,nginx `/`)。**不含** Web 版 app | `site-v*` | `CHANGELOG-site.md` | `deploy-site.yml` |
|
||
| **server** | `backend/` Go 服务(`jiu-server`)+ 共享基建 nginx-jiu.conf / jiu.service | `server-v*` | `CHANGELOG-server.md` | `deploy-server.yml` |
|
||
|
||
使用 `/release <part> [version]` slash command:
|
||
|
||
```
|
||
/release client 1.0.55 # 指定版本
|
||
/release server # 省略则自增 server-v* 最新 tag 的 patch
|
||
```
|
||
|
||
执行顺序:本地 build → test(按 part)→ 更新对应 CHANGELOG → git commit → tag `<part>-v<ver>` → push main+tag。
|
||
CI/CD(Forgejo)按 tag 前缀触发对应 workflow,自动:编译 → 测试 → 创建 Release → 部署 ali(jiu.51yanmei.com,443)→ Telegram 通知。
|
||
|
||
**归属与解耦**(关键):
|
||
- `version.yaml` 归 **client**(写 version/build_number/release_notes/下载链接/changelog);nginx/systemd 归 **server**。
|
||
- 后端 `/version` 与 `/api/v1/public/release` **每请求实时读** `version.yaml`,无缓存——client 部署 version.yaml 后立即生效,**不重启后端、不触发 server 流水线**。
|
||
- 官网下载页运行时 `fetch('/api/v1/public/release')`:版本徽章、各平台下载链接、**更新日志时间线**全部动态刷新——client 发版**无需重建官网**。接口不可达时回退到构建时静态内容(`web/_data/changelog.js` 读 `CHANGELOG-client.md`)。
|
||
|
||
**CI 脚本**(`scripts/ci/`):`lib-forgejo.sh`(公共函数)+ `compile-{client-web,site,backend,macos,android,ios,windows}.sh` + `release-{client,site,server}.sh` + `deploy-{client,site,server}.sh`。`compile-{macos,android,ios,windows}.sh` 去 `client-v` 前缀取版本。
|
||
|
||
**client 多平台构建矩阵**:mac runner(容量 1)串行链 `build-client-web → build-macos → build-android → build-ios`;windows runner 并行 `build-windows`(Inno Setup);`release-deploy-client` 收齐后发布。Android 需 `ANDROID_*`、iOS 走 TestFlight 需 `IOS_*`/`APPSTORE_*` secrets,未配置时该 job 优雅跳过(exit 0,不阻塞)。
|
||
|
||
**手动回滚**:`manual.yml` 输入带前缀 tag(如 `client-v1.0.54`),按前缀路由到对应 `deploy-<part>.sh`,从 Forgejo Release 下载产物。
|
||
|
||
**CHANGELOG 格式**(Keep a Changelog,三个文件同构):
|
||
```markdown
|
||
## [1.0.2] - YYYY-MM-DD
|
||
|
||
### 新功能
|
||
- ...
|
||
|
||
### 改进
|
||
- ...
|
||
|
||
### 修复
|
||
- ...
|
||
```
|
||
只保留有内容的分类。`/release` 会检查对应 CHANGELOG 是否已有该版本节,没有则从该 part 上一个 tag 的 git log 自动生成。
|
||
|
||
### 异常上报
|
||
|
||
客户端异常已在两处统一捕获,**无需**在每个业务层重复处理:
|
||
- `main.dart`:`FlutterError.onError` + `runZonedGuarded` 捕获所有未处理异常
|
||
- `api_client.dart`:Dio 拦截器自动上报所有 HTTP 5xx 错误
|
||
|
||
需要**手动调用** `reportError(e, st)` 的场景:
|
||
- 技术性异常(JSON 解析失败、第三方 SDK 崩溃等)
|
||
- **不需要**上报:`AppException` 及其子类(已知业务错误)
|
||
|
||
```dart
|
||
} catch (e, st) {
|
||
reportError(e, st); // 一行,fire-and-forget
|
||
rethrow;
|
||
}
|
||
```
|
||
|
||
### 项目 TODO 管理
|
||
|
||
**统一使用全局 todo 工具**(`~/.claude/skills/todo/`,遵循全局 `~/.claude/CLAUDE.md` 的规则)。本项目**不再维护自己的 `/todo` slash command**(已删除),一律调用全局 todo skill。
|
||
|
||
- **数据落地**:全局 skill 把待办存到**当前项目**的 `todo/` 目录(`todo.json` + 看板 `todo/todo.html`,首次使用自动创建);命令须在项目根目录运行,例如 `node ~/.claude/skills/todo/todo.mjs list`。
|
||
- **禁止**使用 TaskCreate / TodoWrite 等任何内置 todo 工具,一律走全局 todo skill。
|
||
- **改动等级把控**:接口/schema/跨 2+ 模块的「大改」先进 plan 模式经用户批准,完成后同步更新设计文档;小 bugfix 直接做。
|
||
|
||
---
|
||
|
||
### 搜索实现(拼音)
|
||
|
||
商品名搜索同时支持汉字、全拼、首字母:
|
||
- `products` 表有 `name_pinyin`(全拼)和 `name_initials`(首字母)两列
|
||
- Create / Update / FindOrCreate 写入时自动调用 `util.ToPinyin()` 生成,**无需手动维护**
|
||
- 启动时对 `name_pinyin = ''` 的存量数据自动回填(`backfillPinyin`)
|
||
- 库存搜索 SQL 同时 LIKE 匹配 name、code、name_pinyin、name_initials
|
||
- 新增其他需要拼音搜索的实体时,复用 `backend/internal/util/pinyin.go` 的 `ToPinyin()`
|