准备上传

This commit is contained in:
2025-10-08 19:15:20 +08:00
parent 0aa7d55a23
commit 0e14a5fa1c
193 changed files with 14697 additions and 2461 deletions

View File

@@ -1,28 +0,0 @@
## 管理端开发说明(当前实现)
### 1. 模块概览
- **VIP 系统**`/vip/system` 管理统一售价与充值记录,`/vip/list` 维护店铺会员状态。价格管理页面引用 `/api/admin/vip/system/price`,充值列表读取 `/api/admin/vip/system/recharges`
- **公告管理**`/notice/list` 支持公告检索、创建、编辑、发布与下线,对应接口 `/api/admin/notices` 及其子路径。
- **咨询回复**`/consult` 基于 `/api/admin/consults` 完成列表、单次回复、标记已解决。
- **用户/配件/供应商**`/users``/parts``/supplier` 依次调用 `/api/admin/users``/api/admin/parts``/api/suppliers` 进行检索与维护。
- **附件与图片**:图片上传统一走 `/api/attachments`,列表页展示时需通过 `withBaseUrl()` 处理相对路径。
### 2. 认证与上下文
- 后端默认启用 `AdminAuthInterceptor`,优先校验 Bearer Token未登录情况下回退到请求头 `X-Admin-Id``X-User-Id`
- 管理端前端目前仍使用本地存储写入 `USER_ID`/`ADMIN_ID`(详见 `admin/src/api/http.ts`);接入登录页时需改为在登录成功后写入 Token 并移除默认 ID。
- 所有请求必须附带 `X-Shop-Id`(默认 1可在本地存储或环境变量覆盖以匹配租户范围。
### 3. 接口要点
- **VIP 列表**`GET /api/admin/vips` 已落地;`POST /api/admin/vips` 需要传入 `shopId``userId` 才能成功创建。
- **VIP 价格**`GET/PUT /api/admin/vip/system/price` 会清空 `vip_price` 再写入单条记录,不允许并行修改;可考虑后续改为 `UPDATE` 语句。
- **VIP 充值**`GET /api/admin/vip/system/recharges` 支持关键字(姓名/手机号)过滤,默认按创建时间倒序。
- **公告管理**:创建与更新均支持标签、置顶、时间窗,未填写时间默认为即时生效/长期有效。
- **附件上传**:上传成功返回 `url` 与元信息,若需要落库请在业务表维持引用;多次上传同一文件会复用记录(按 hash 去重)。
### 4. 条码识别接入
- 管理端不提供扫码入口,仅用户端调用 `/api/barcode/scan`
- 后端代理服务会将图片转发至 Python TXMFastAPI并返回首个匹配条码需保持 Java 与 Python 两侧的 `PY_BARCODE_MAX_UPLOAD_MB` 一致。
- 部署时应通过环境变量配置:
- `PY_BARCODE_HOST`/`PY_BARCODE_PORT`Python 服务地址,默认 `127.0.0.1:8000`
- `PY_BARCODE_MAX_UPLOAD_MB`:上传大小限制,默认 8MB。
- 小程序端调用扫码接口需将后端域名加入“request 合法域名”并启用 HTTPS。

View File

@@ -1,26 +0,0 @@
# 管理端需求文档
## 1. 页面结构
- **VIP 系统**:含价格配置、充值记录与会员列表三块;价格修改需立即生效并同步到前端 `VIP_PRICE` 显示;列表支持手机号模糊检索。
- **公告管理**:支持公告的新增、编辑、发布、下线、置顶,字段包括标题、内容、标签、有效期、生效状态。
- **咨询回复**:列出店铺咨询,管理员可单次回复并标记已解决。
- **用户管理**:展示用户基本信息,支持编辑、启停(黑名单)。
- **用户配件管理**:适配用户提交的配件数据,允许管理员编辑品牌/型号/规格与图片链接。
- **供应商管理**:列表检索、创建、编辑供应商信息,含欠款字段展示。
- **主数据字典**:主单位、主类别维护入口,仅平台管理员可用。
## 2. 认证约束
- 所有接口通过 `AdminAuthInterceptor` 鉴权:优先 Bearer Token其次 `X-Admin-Id`/`X-User-Id`
- 请求必须携带 `X-Shop-Id`,缺省取 1多租户数据严格隔离。
- 登录功能正在开发中,当前临时通过本地存储写入管理员 ID。
## 3. 功能约束
- 禁止硬编码配置值,价格、库存等需从后端接口读取。
- 上传图片统一调用 `/api/attachments`,返回的 `url` 需落库或直接引用。
- 所有列表接口支持分页(默认 `page=1&size=20`);前端需预留分页扩展点。
- 删除功能未启用,均以启停或逻辑状态位代替。
## 4. 未完成功能
- 管理端登录页与权限粒度控制尚未上线。
- 配件审核流仅完成基础编辑,驳回/通过流程待接入。
- 公告板目前缺少多语言与富文本支持。

File diff suppressed because it is too large Load Diff

View File

@@ -2869,7 +2869,6 @@ components:
model: { type: string }
spec: { type: string }
categoryId: { type: integer, format: int64, nullable: true }
unitId: { type: integer, format: int64 }
safeMin: { type: number, nullable: true }
safeMax: { type: number, nullable: true }
purchasePrice: { type: number }
@@ -2891,7 +2890,6 @@ components:
model: { type: string, nullable: true }
spec: { type: string, nullable: true }
categoryId: { type: integer, format: int64, nullable: true }
unitId: { type: integer, format: int64 }
safeMin: { type: number, nullable: true }
safeMax: { type: number, nullable: true }
prices:

View File

@@ -1,187 +0,0 @@
# 货品功能扩展需求文档(草案)
## 1. 当前实现概览
### 1.1 用户端uni-app
- `pages/product/list.vue`:支持关键字与按类别筛选、下拉分页、查看详情并跳转编辑;展示“平台推荐/我的提交”标签。
- `pages/product/form.vue`:可创建/编辑货品,字段涵盖名称、条码、品牌、型号、规格、产地、单位、类别、安全库存、四列售价、初始库存、图片与备注;提供图片识码能力;图片上传走 `/api/attachments`
- **`pages/product/submit.vue`**:新增配件提交入口,支持型号唯一校验、多图上传、参数 JSON、备注、安全库存等字段提交成功跳转“我的提交”。
- **`pages/product/submissions.vue``pages/product/submission-detail.vue`**:新增提交列表与详情页,支持状态筛选、滚动分页、驳回重新提交、图片/参数展示。
- 字典:调用 `/api/product-units``/api/product-categories` 读取全局单位/类别,并缓存到本地。
### 1.2 管理端Vue3 + Element Plus
- **`admin/src/views/parts/Submissions.vue`**:已上线配件审核模块,提供筛选、详情、编辑、通过/驳回、Excel 导出能力。
- 其它模块VIP 系统、公告管理、咨询回复、用户管理、配件管理、供应商管理、主数据字典。
- 待迭代:登录页、角色权限细分、操作日志可视化。
### 1.3 后端Spring Boot
- `ProductSubmissionController` + `ProductSubmissionService`:完成用户提交、用户查看、管理员审核/驳回、导出全链路;审批通过时同步 `products``product_images``source_submission_id` 等字段。
- OpenAPI 已新增 `/api/products/submissions*``/api/admin/parts/submissions*` 路径,并标记为 ✅ Fully Implemented。
- 数据模型:`products``product_prices``inventories``product_images` 已投产;`part_submissions` 已扩展 JSON、状态、审核信息等字段。
- 图片存储:统一走 `attachments`,提交与商品共享资源。
## 2. 差距分析
| 需求点 | 现状 | 差距 |
| --- | --- | --- |
| 用户提交配件待审核 | 用户端新增提交/列表/详情页面,与后台接口打通 | 持续跟踪型号唯一范围(店铺或全局);补充并发校验测试 |
| 审核管理 | 管理端已上线审核列表、详情编辑、通过/驳回、导出 | 后续可扩展批量操作、导出更大数据量的异步方案 |
| 数据查询增强 | 后端支持多条件、返回 total前端列表分页同步 | 可继续优化查询性能(索引/缓存)与前端展示字段 |
| 图片管理 | 提交端与审核端均支持多图显示、预览、排序 | 可根据需求补充图片备注、AI 识别等增强能力 |
| 上架逻辑 | 审核通过自动写入 `products` 并关联 `source_submission_id``global_sku_id` | 待确认产品图片、参数同步策略是否满足后续拓展(如平台库) |
| 导出 | 管理端支持按筛选条件导出 Excel2000 条以内同步导出) | 后续可评估异步导出/下载中心、导出模板自定义 |
## 3. 设计方案
> 项目采用方案 A基于 `part_submissions` + `products` 双轨流程),方案 B 已放弃,不再纳入考虑。
### 方案 A基于现有 `part_submissions` + `products` 双轨流程(已确认)
1. **用户提交**
- 新增用户端页面 `pages/product/submit.vue`字段型号model唯一必填、配件名称name、配件参数parameters自由文本/JSON 字段)、图片(多图,最多 9 张)、备注。
- 调用 `POST /api/products/submissions`,写入 `part_submissions``shop_id``user_id``model_unique``status=pending``images JSON``extra_attrs``created_at`
- 验证规则:
- `model_unique` 同一店铺 + 全局唯一(需确认范围)。
- 图片通过 `attachments` 保存,限制尺寸/数量。
- 上传成功后展示提交状态(待审核)。
2. **管理员审核**
- 新增管理端视图 `admin/src/views/parts/Submissions.vue`
- 列表字段:型号、名称、提交人、提交时间、当前状态、备注、图片缩略图。
- 过滤项:状态(待审/已审/驳回)、关键字(支持型号/名称/提交人)、提交时间区间、店铺。
- 操作:
- 查看详情(弹窗):可编辑名称、型号、参数、图片(增删排序)、备注。
- 审核按钮:批准/驳回,必须填写备注(驳回)。
- 批量导出:选中或按筛选条件导出 Excel。
- 审核通过逻辑:
- 检测是否存在相同型号产品:
- 若没有:创建 `products`source_submission_id 引用)、初始化库存 0、价格默认 0关联图片。
- 若已有:补充图片、参数(可覆盖或合并),并记录 submission→product 对应关系。
- 更新 `part_submissions.status=approved``reviewer_id``reviewed_at``product_id`
- 将产品显示给提交用户(若产品属于平台库,可关联 `global_sku_id`)。
- 审核驳回:状态=reject记录 `remark``reviewer_id``reviewed_at`
3. **数据查询**
- `GET /api/products` 增强:支持 `brand``model``status`(是否审核通过)、`createdStart/End` 等参数;返回 `{ list, total }`
- `GET /api/admin/parts/submissions` 支持分页+多条件,默认按创建时间倒序。
- Excel 导出:`GET /api/admin/parts/submissions/export`,返回文件下载,支持当前过滤条件。
4. **图片管理**
- 提交阶段:图片存储 `attachments`ownerType=submission路径按 hash 去重。
- 审核详情弹窗内使用 `el-image` + preview 功能查看大图;允许删除/新增(调用 `/api/attachments` 上传)。
- 审核通过时同步图片至 `product_images`(可重用 attachment URL无需复制物理文件根据排序保存 `sort_order`
5. **上架逻辑**
- 平台维护 `global_skus`(可选):
- 审核通过界面提供“关联平台配件”下拉,或创建新平台配件,同时写入 `global_skus`
- `products.global_sku_id` 记录来源,供其他店铺引用。
- 用户提交的产品通过审核后自动加入其店铺的 `products`,并可在商品列表中展示“平台推荐/自有”区分。
### 4.1 数据模型变更细节
#### part_submissions新增/调整)
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `name` | VARCHAR(120) | 配件名称(用户填写,可用于审核) |
| `parameters` | JSON | 结构化参数,如规格、尺寸、材质等 |
| `images` | JSON | 图片 URL 列表,提交阶段存储 |
| `status` | ENUM('pending','approved','rejected') | 当前审核状态 |
| `remark` | VARCHAR(255) | 审核备注(驳回原因等) |
| `reviewer_id` | BIGINT | 审核人 ID |
| `reviewed_at` | DATETIME | 审核时间 |
| `product_id` | BIGINT | 审核通过后关联的产品 ID |
| `global_sku_id` | BIGINT | 可选,关联平台配件 ID |
| `source_shop_id` | BIGINT | 原提交店铺(若与 shop_id 区分需求明确可保留) |
> 若原表已有旧字段(如 `compatible`、`size` 等),可迁移到 `parameters` JSON 中或保留以兼容老数据。
#### products新增字段
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `source_submission_id` | BIGINT | 来源提交记录 ID |
| `global_sku_id` | BIGINT | 平台配件 ID可选 |
| `platform_status` | ENUM('platform','custom') | 产品来源标识(可选) |
需要为 `model_unique``shop_id` 添加唯一索引,保障型号不可重复提交(规则确定后实施)。
### 4.2 接口契约摘要
| 接口 | 方法 | 说明 | 核心入参 | 返回 |
| --- | --- | --- | --- | --- |
| `/api/products/submissions` | POST | 用户提交配件 | `{ name, model, parameters?, images[], remark? }` | `{ id, status }` |
| `/api/products/submissions` | GET | 用户查看提交记录 | `status?, page, size` | `{ list, total }` |
| `/api/admin/parts/submissions` | GET | 管理端列表 | `status?, kw?, shopId?, reviewerId?, startAt?, endAt?, page, size` | `{ list, total }` |
| `/api/admin/parts/submissions/{id}` | GET | 查看详情 | - | 详细信息(含图片) |
| `/api/admin/parts/submissions/{id}` | PUT | 编辑字段(名称/参数/图片) | `{ name?, parameters?, images?, remark? }` | `{ ok: true }` |
| `/api/admin/parts/submissions/{id}/approve` | POST | 审核通过 | `{ remark?, assignGlobalSkuId?, createGlobalSku? }` | `{ ok: true, productId }` |
| `/api/admin/parts/submissions/{id}/reject` | POST | 审核驳回 | `{ remark }` | `{ ok: true }` |
| `/api/admin/parts/submissions/export` | GET | Excel 导出 | 同列表查询参数 | 二进制文件 |
| `/api/products` | GET | 增强查询 | `kw, brand, model, status, categoryId, startAt, endAt, page, size` | `{ list, total }` |
OpenAPI 需同步更新:新增路径、请求体/响应体、状态标注。
### 4.3 前端/后端任务拆分
**后端**
1. 数据库迁移脚本(新增字段、索引、默认值)。
2. 提交接口实现:存储提交记录、处理图片、型号唯一校验。
3. 审核接口实现:
- 获取详情、编辑提交记录。
- 批准流程(创建/更新产品、同步图片、记录审计)。
- 驳回流程。
4. 列表查询与导出(注意权限:仅管理员)。
5. 产品查询增强(支持多条件和 total
6. 单元测试/集成测试:审核通过/驳回、重复型号、导出大数据量。
**管理端前端**
1. 新建 `Submissions.vue` 页面:
- 列表 + 搜索表单 + 分页。
- Excel 导出按钮。
2. 审核详情弹窗组件:
- 多图预览/排序Element Plus `el-image` + `el-upload` 或复用现有 `ImageUploader` 组件)。
- 字段编辑与保存。
- 审核通过/驳回按钮及确认弹窗。
3. 上架结果联动:审批通过后刷新列表,提示成功。
**用户端前端**
1. 新增 `pages/product/submit.vue`
- 表单输入 + 多图上传 + 校验(型号唯一、本地格式校验)。
- 提交成功提示与跳转。
2. 新增“我的提交”页面或在现有列表中添加状态指示。
3. 货品列表/详情展示新字段(如“平台配件”标签、审核状态)。
## 5. 开发计划建议
1. **第一阶段:后端改造**
- 数据库变更(通过 MysqlMCP更新 `part_submissions``products``global_skus` 相关字段与索引。
- 新增接口实现(提交、审核、导出、多条件查询)。
- 扩展 `ProductService` 支持 total、更多筛选。
- 调整 OpenAPI标注实现状态。
2. **第二阶段:管理端前端**
- 新建审核模块页面,集成组合查询 + 分页 + 导出。
- 审核详情对话框支持多图预览/编辑、字段修改、审核按钮。
3. **第三阶段:用户端前端**
- 新增“提交配件”入口(从货品列表或我的模块进入)。
- 提交表单 + 多图上传(复用 ImageUploader
- 提交记录列表(待审/已通过/驳回)与状态提示。
- 审核通过后自动同步到货品列表(带标签)。
4. **第四阶段:测试与文档**
- 定义审批流程测试用例:提交->审批->列表刷新->导出。
- 验证图片上传、重复型号、并发审批场景。
- 更新需求文档、开发文档、用户操作指引。
## 6. 验收标准
- 用户端可提交配件:型号唯一校验、上传多图成功、提交后状态为“待审核”。
- 管理端待审核列表能根据筛选条件展示结果,并支持详情查看(含大图预览、编辑字段)。
- 管理端可对单条记录审核通过/驳回:
- 通过:在 `products` 中生成或更新商品;提交用户在商品列表中可见;状态变更为“已通过”。
- 驳回:状态变更为“已驳回”,记录审核备注。
- 导出功能可按筛选条件导出 Excel至少包含 2000 条数据测试无超时;生成文件名避免冲突(时间戳+操作人)。
- 数据查询支持组合条件,返回总数;前后端分页一致。
- 图片管理:审核详情中可预览大图、删除、重新上传;重复文件自动识别 hash避免冲突。
- 上线后数据库结构、OpenAPI、开发文档同步更新无遗漏。
## 7. 风险与注意事项
- 型号唯一性需统一标准化(去空格、大小写)并考虑跨店铺冲突策略。
- 审核通过时同步到 `products`,需明确库存与价格的初始值(建议默认 0提醒用户自行编辑
- Excel 导出需加限流或异步处理,防止长时间请求阻塞。
- 图片引用避免重复占用存储;可直接引用同一 URL而非复制文件。
- 若需消息通知(如审核结束提醒),需要后续补充通知机制。
- 需要回顾 `global_skus``products` 的关系,明确平台配件库与用户私有货品的边界,以免数据混乱。
---
> 本文档结合现状与新增需求整理。请确认方案后,可进一步拆解任务并排期执行。

View File

@@ -1,176 +0,0 @@
* ### **配件查询App需求规格说明书**
#### 1.0 项目概述
本项目旨在开发一款面向小微商户的移动端进销存管理应用,命名为“配件查询”。该应用核心功能是帮助用户高效管理商品、库存、销售、采购、客户、供应商及财务收支,并通过数据报表提供经营状况分析,助力商户实现数字化经营。
参考的小程序“智慧记进销存”,但是多了一个配件查询功能,以下所罗列的内容大多也参考至该小程序,如有歧义可优先参照这个小程序,拿不准优先问。
当前交付内容移动端已实现基础的登录、VIP 开通、用户/客户/供应商管理、下单与报表入口;管理端完成会员、公告、咨询、配件、字典维护模块。
#### 2.0 功能模块需求
**2.1 首页 (Dashboard)**
* **2.1.1 核心数据概览:** 首页需直观展示当日、当月的核心经营数据。
> 已实现基础销量/库存统计,利润汇总需依赖真实成本数据完善。
* 今日销售额
* 本月销售额
* 本月利润
* 库存商品数量
**2.1.2 广告位:** 在首页区域提供一个展示广告的区域。
> 待设计,当前保留静态占位图。
* **2.1.3 快捷功能入口:** 提供一个快捷功能区域,方便用户快速访问常用功能。
* 默认应包含客户管理、销售开单、账户管理、供应商管理、进货开单、其他支出、VIP会员、报表统计等。
* 现状:移动端 Tab 首页已集成常用入口,后续需根据配置扩展。
* **2.1.4 在线客服:** 提供一个悬浮的“咨询”或“在线客服”入口,方便用户随时获取帮助。
* 当前通过“我的-咨询”入口触达,悬浮入口尚未实现。
**2.2 货品管理模块**
* **2.2.1 货品列表与库存:**
* 展示所有货品的列表,包含名称、库存等基本信息。
* 支持按“全部类别”或指定类别筛选货品。
* 提供搜索功能,支持通过货品名称或条形码进行模糊查找。
* 列表底部显示总货品种类数量。
* 当库存为空时,应有明显的空状态提示,并引导用户“点击右上角‘+’录入货品信息”。
* **2.2.2 新增/编辑货品:**
* 支持添加商品图片。
* App端支持录入或扫描商品条形码小程序端仅支持手动录入不支持扫码。
* **货品名称**为必填项。
* 可为货品选择**类别**和**主单位**。
* 支持自定义扩展货品属性(如品牌、型号、产地、保质期等)。
* 货品图片支持多图上传,支持拖拽排序,支持图片放大预览。
* 可录入**当前库存**、**安全库存**(一个数值区间,用于库存预警)。
* 需分别录入四种价格,**进货价**、**批发价**、**大单报价**和**零售价**。
* 提供**备注**字段,用于记录额外信息。
* 保存后,可选择“继续新增”或返回列表。
* **2.2.3 货品设置:**
* 支持自定义**货品类别**管理。
* 支持自定义**货品单位**管理。
* 提供开关选项,允许用户选择是否“隐藏零库存商品”和“隐藏进货价”。
**2.3 开单(交易)模块**
* **2.3.1 核心功能:** 该模块是应用的核心操作区,整合了销售、进货和财务记账功能。
* **2.3.2 销售开单:**
* **出货单:**
* 自动记录开单**时间**,并支持手动修改。
* 可选择**客户**,默认为“零售客户”。
* 通过“+”号从货品列表中选择商品,自动计算**合计金额**。
* 支持在订单中对单个商品进行操作(如修改数量、价格等)。
* **退货单:** 用于处理客户退货业务。
* **收款单:** 用于处理销售单的后续收款或直接创建收款记录。
* **2.3.3 进货开单:** 用于记录从供应商处采购商品的业务流程。
* **2.3.4 其他收入/支出:**
* **其他收入:**
* 支持对收入进行分类,如“销售收入”、“经营所得”、“利息收入”等。
* 可选择**往来单位**和**结算账户**(如现金、银行等)。
* 可添加备注并选择日期。
* **其他支出:**
* 支持对支出进行分类,如“经营支出”、“办公用品”、“房租”等。
* 同样支持选择**往来单位**和**结算账户**。
**2.4 明细查询模块**
* **2.4.1 维度筛选:**
* 提供按时间维度(自定义、本周、今日、本月、本年)快速筛选单据。
* 提供按业务类型(销售、进货、收银、资金、盘点)进行分类查看。
* **2.4.2 单据列表:**
* 在选定维度下,以列表形式展示所有相关单据。
* 提供搜索功能,支持通过单据号、客户/供应商名、品名、备注等关键字查询。
* 显示当前筛选条件下的总金额。
* 当无数据时,提供清晰的空状态提示。
* 提供“+”号,支持在当前分类下快速新建单据。
**2.5 报表统计模块**
* **2.5.1 资金报表:**
* **利润统计:** 分析指定时间范围内的收入、支出和利润。
* **营业员统计:** 按销售人员维度统计销售业绩。
* **经营业绩:** 提供综合性的经营状况分析。
* **导入导出模块:** 提供导入导出功能方便用户切换手机或账号后仍能将旧数据导入。
* **2.5.2 进销存报表:**
* **销售统计:** 按商品、客户、时间等维度分析销售数据。
* **进货统计:** 按商品、供应商、时间等维度分析采购数据。
* **库存统计:** 提供当前库存成本、数量及分布情况的报告。
* **应收/应付对账单:** 生成与客户和供应商的对账单据。
**2.6 “我的”(用户中心)模块**
* **2.6.1 用户信息:** 显示用户头像、店铺名称、注册手机号及“老板”身份标识。
> 个人信息读取 `/api/user/me` 已上线。
* **2.6.2 会员与订单:**
* 提供**VIP会员**入口,展示会员特权。
> `/pages/my/vip` 已接入 `/api/vip/status`,开通调用 `/api/vip/pay`。
* 提供**我的订单**入口,可能用于查看应用内服务订单。
> 入口预留,服务订单接口尚未上线。
* **2.6.3 基础管理:**
* **供应商管理**
* **客户管理**
* **客户报价**
* **店铺管理**
* **2.6.4 设置中心:**
* **账号与安全:**
* 修改个人信息(头像、姓名)。
* 修改登录密码。
* **商品设置:**
* **系统参数:**
* 提供多种业务逻辑开关,如:“销售价低于进货价时提示”、“销售单默认全部收款”、“启用单行折扣”、“启用客户/供应商双身份”。
* **关于与协议:** 包含“关于我们”、“隐私协议”、“个人信息安全投诉”等静态页面。
* **账号操作:** 提供“账号注销”和“退出登录”功能。
#### 3.0 全局性需求
* **3.1 导航:** 采用底部Tab栏导航包含“首页”、“货品”、“开单”、“明细”、“我的”五个主要模块。
> uni-app 端已实现,管理端采用侧栏导航。
* **3.2 统一的UI/UX:** 应用整体风格简洁、清晰,操作流程符合移动端使用习惯。
* **3.3 空状态页面:** 在列表、报表等数据为空的页面,需提供友好的空状态提示图和引导性文字。
* **3.4 数据同步:** 应用数据应在云端同步,保证用户更换设备或多设备使用时数据一致性。
* **3.5 多租户数据隔离:** 所有业务数据按店铺(租户)隔离,用户不可访问他人数据。
* 所有业务表需包含`user_id`并在读取/写入中强制按`user_id`过滤。
* 支持租户内角色与权限控制;导出仅限本租户数据。
* **3.6 公共SKU全局商品库众包与审核:** 全体用户共同补充、纠错SKU经审核发布为全局可选SKU。
* 用户可提交“新增SKU/编辑建议”,进入审核流(草稿/待审/驳回/发布/下架)。
* 全局SKU字段名称、品牌、规格、条码、主单位、图片、别名、分类标签等。
* 各用户通过“本地商品”引用全局SKU并保留本地私有字段价格、库存、分类、单位换算、条码别名等
* **3.7 商品模糊查询(增强):** 在货品列表、开单选品、对账等场景支持多字段模糊匹配。
* 支持名称/条码/别名/拼音/品牌/规格模糊匹配,并高亮命中片段。
* 支持全局SKU与本地商品联合检索优先展示本地商品结果可分页。
* 需满足大规模SKU下的性能目标可通过系统参数配置匹配策略。
* **3.8 客户端平台:** 提供移动App与小程序小程序不支持商品条形码扫描功能。
> HBuilderX 打包的 App 版本与微信小程序版本已具备扫描差异处理;条码识别仅用户端调用。
* **3.9 多列销售价格:** 销售价格分四列,即同一种商品有四个销售价格
### 配件查询
1. **数据查询功能**
- 多参数组合查询(分类、尺寸、型号等)
- 模糊匹配关键字
- 分页展示查询结果
- 一键导出Excel数据
2. **数据提交系统**
- 用户提交新配件数据
- 型号为唯一必填项
- 支持图片上传
- 提交后等待管理员审核
3. **审核管理系统**
- 管理员查看待审核列表
- 可编辑所有字段
- 支持图片更新和删除
- 一键批准或拒绝提交
4. **图片管理系统**
- 每条数据可关联多张图片
- 点击图片可放大查看
- 管理员可管理所有图片
- 自动处理文件名冲突
## 全局说明(必看)
由于这个文档写的还不是很完善,目前有存疑的部分先行参考小程序小程序“智慧记进销存”(功能和按钮可以参考,界面样式除外),管理端文档目前待定。
客户要求的是做双端应用app端+小程序端),需要考虑兼容性相关问题。
本程序和“智慧记进销存”大多一致,主要的区别在于客户有配件查询要求,即在产品页面中要额外加一个配件查询按钮或入口,且要求一个产品要有四个销售价格(先按零售价 分销价 批发价 大客户价),且要求能自定义添加各种规格(尺寸,孔径等)。
有疑惑的部分一定要及时沟通(如未提及的页面和功能需要确认的时候)

View File

@@ -1,331 +0,0 @@
### VIP 功能开发文档
#### 1. 范围与目标
- 目标为“配件查询”应用提供面向店铺租户的会员体系VIP支持会员开通、审核、生效与到期管理并为后续增值能力如数据永久存储、高级报表、优先审核等提供统一的权限判定入口。
- 范围:后端服务与管理端优先落地;移动端展示会员状态与开通入口,后续再接入支付与自动续期。
#### 2. 现状与差距
- 前端 App`frontend/pages/my/vip.vue` 当前为静态演示页,读取 `VIP_PRICE_PER_MONTH` 展示,未接入后端状态与支付。
- 管理端:已存在页面
- `admin/src/views/vip/VipList.vue`:查询、编辑、启停。
- `admin/src/views/vip/VipReview.vue`:列出待启用(审核)并提供通过/驳回。
- 后端:`AdminVipController` 提供基础接口:列表、创建、更新、审核通过/驳回(部分实现)。依赖表 `vip_users`
- OpenAPI`/doc/openapi.yaml` 已定义管理端 VIP 接口,标注为「❌ Partially Implemented」。
- 数据库:本地 `backend/db/db.sql` 尚未创建 `vip_users` 表;而远程数据库文档 `/doc/database_documentation.md` 已包含该表结构。这是“脚本未对齐线上库”的差距,需要弥合(见实施计划)。
#### 3. 业务与权限
- 归属维度VIP 为“用户级”。每个用户仅归属一个店铺(无店员概念)。
- 状态定义:
- `isVip`是否为会员标记1/0
- `status`启用状态1=生效0=未启用/停用)。
- `expireAt`:到期时间(为空代表永久或未设置)。
#### 4. 数据模型设计(多方案)
- 方案A单表 `vip_users`
- 字段:`id, shop_id, user_id, is_vip, status, expire_at, remark, reviewer_id, reviewed_at, created_at, updated_at`
- 优点:简单直观,满足当前开通/续期/停用/审核的核心需求。
- 局限后续若需要等级gold/platinum、付费订单、优惠券、权益包等需要再扩展新表。
- 方案B三表拆分推荐用于中长期
- `vip_users`(会员资格)、`vip_levels`(等级/权益配置)、`vip_orders`(开通/续费订单记录)。
- 优点:扩展性好,可平滑引入支付渠道、对账、活动定价。
- 初期实施成本更高。可采用“增量演进”先落地方案A同步预留 `vip_orders` 的接口草案与库表蓝图,待支付接入时启用。
当前建议首期采用方案A满足“开通-审核-生效-到期”的MVP同时在配置中预留价格与时长便于后续衔接 `vip_orders`
#### 5. 配置与价格策略(严禁硬编码)
- 前端价格:已通过 `frontend/common/config.js``VIP_PRICE_PER_MONTH` 从环境变量/本地存储读取,默认 15 元/月。
- 后端价格与时长建议:通过 `system_parameters` 表维护,键建议:
- `vip.price`(单位:元/月number
- `vip.durationDays`(开通/续费时长,默认 30
- 可选:`vip.trialDays`(试用天数,默认 0
- 后端读取优先级:系统参数 > 环境变量(如 `VIP_PRICE`,`VIP_DURATION_DAYS`> 安全默认值;并通过管理端配置面板维护参数(后续迭代)。
管理员端价格设置(新增):
- 平台统一价,存储于表 `vip_price`(仅一条记录)。
- 在管理端“VIP管理”新增“价格设置”入口读取/修改 `vip_price.price`
- 修改保存后立即生效;前端价格展示以后端返回为准(避免前后端不一致)。
#### 6. 接口契约(与 openapi.yaml 对齐)
- 管理端:
- GET `/api/admin/vips`(列表)
- POST `/api/admin/vips`(新增)
- PUT `/api/admin/vips/{id}`(更新 isVip/status/expireAt/remark
- 审核流程已取消approve/reject 已移除);统一由用户端一键开通或管理员直接设置。
- App建议新增未在 openapi.yaml 中登记,待一方开工后再登记):
- GET `/api/vip/status`:返回当前用户 VIP 状态与到期时间。
- POST `/api/vip/pay`:一键支付并立即开通(临时方案:点击即视为支付成功,直接写入/更新 `vip_users`,置 `status=1,isVip=1,expireAt=now+durationDays`;后续接入真实支付将替换该接口实现)。
- 说明:上述 App 接口仅为“建议方案”,在前端或后端开始实现后再将其写入 `/doc/openapi.yaml` 并标注实现状态,避免“仅设计未实现”的文档污染。
示例返回(状态查询):
```json
{
"isVip": true,
"status": 1,
"expireAt": "2025-12-31T23:59:59Z"
}
```
#### 7. 前后端对接要点
- Header管理端接口需要 `X-User-Id`(用于写入审核人);多租户场景下优先根据用户解析 `shop_id`,或显式传递 `X-Shop-Id`
- 列表过滤:`phone` 模糊匹配 `users.phone``status` 精确匹配 `vip_users.status`
- 审核流:通过/驳回接口必须记录 `reviewer_id``reviewed_at`,便于审计。
- App 端状态展示:进入“我的-会员”页时调用 `GET /api/vip/status`,根据返回值展示“已激活/有效期至/立即开通”。
##### 7.1 数据可见性与留存策略(关键新增)
- 规则:
- VIP 用户:可查看“所有历史数据”(不做时间裁剪)。
- 普通用户:仅显示“最近两个月内”的数据,早于两个月的数据不显示。
- 配置项(严禁硬编码):
- `vip.dataRetentionDaysForNonVip`(默认 60单位写入 `system_parameters`;也可通过环境变量 `VIP_NONVIP_RETENTION_DAYS` 覆盖。
- 当该值小于等于 0 时视为“非VIP也不做裁剪”便于灰度/应急)。
- 后端落地建议:
- 在列表/查询型接口(如 `/api/orders`, `/api/purchase-orders`, `/api/payments`, `/api/inventories/logs`, `/api/other-transactions`统一增加“非VIP数据时间窗”约束
- 通过当前用户的 VIP 状态与 `expireAt` 判断是否 VIP非VIP则在 SQL WHERE 中增加 `tx_time/order_time >= NOW() - INTERVAL retentionDays DAY` 等条件。
- 采用服务层统一拦截/拼接过滤,避免各控制器重复实现。
- 在导出/报表接口同样应用该约束,确保口径一致。
- 前端落地建议:
- 当非VIP进行跨越两个月以上的日期筛选时给出轻提示“普通用户仅显示近两个月数据开通VIP可查看全部历史”。
- 页面不做本地硬裁剪,一切以后端约束为准,前端仅用于用户沟通与引导开通。
#### 8. 业务流程Graphviz
```dot
digraph G {
rankdir=LR;
node [shape=box, style=rounded];
start [label="用户进入VIP页"];
pay [label="一键支付开通\n(POST /api/vip/pay)"];
active [label="生效: status=1, isVip=1\n设置expireAt=now+durationDays"];
expire [label="到期: 定时任务或登录判定\n自动置 isVip=0 / 提示续期"];
start -> pay -> active -> expire;
}
```
#### 9. 实施计划MVP
1) 数据库脚本对齐(仅通过 MysqlMCP 执行)
- 若目标库缺失 `vip_users`:按 `/doc/database_documentation.md` 定义创建,并补充必要索引与外键。
- 备注:执行后同步更新 `/doc/database_documentation.md` 中的更新时间与差异说明(保持与线上一致)。
2) 后端接口完备
- 补齐 `AdminVipController` 的参数校验、店铺隔离、分页 total 统计(可选)。
- 新增 App 侧接口:`GET /api/vip/status``POST /api/vip/pay`(点击即开通临时方案)。当开始实现后,将路径登记至 `/doc/openapi.yaml` 并标注状态。
- 配置读取:接入 `system_parameters``vip.price`/`vip.durationDays`
3) 管理端接入
- `VipList.vue``VipReview.vue` 接口字段对齐;在创建/编辑时透传 `shopId`(或后端根据用户解析)。
- 在“VIP管理”中新增“价格设置”对话框读取/修改 `vip.price`(走系统参数接口或新增专用接口)。
4) App 接入
- `pages/my/vip.vue` 调用 `GET /api/vip/status`;“立即开通”改为调用 `POST /api/vip/pay`(临时:点击即开通)。
5) 定时/登录时到期检查
- 每日定时任务或用户登录时,对已到期记录置 `isVip=0` 并提示续期(可由查询接口实时判断并回写)。
#### 10. 测试用例(核心)
- 列表:无过滤/按手机号/按状态返回正确集合;分页稳定。
- 新增:缺少 `shopId`/`userId` 报错;完整入参成功写入,`status=0` 待启用。
- 更新:仅更新传入字段,`updated_at` 变更。
- 审核:通过后 `status=1` 且记录 `reviewer_id/reviewed_at`;驳回后 `status=0`
- 到期:`expireAt < now()` 时,状态查询返回 `isVip=false`(或提示续期),符合设计。
- 数据可见性:
- 非VIP订单/支付/库存流水/其他收支等查询接口,返回记录的最早时间不早于“当前时间-60天”默认值当调整 `vip.dataRetentionDaysForNonVip` 后行为随之变化。
- VIP在同样条件下可查询到两个月前的历史记录。
- 前端在选择超窗的日期范围时出现轻提示,不影响接口正常返回。
- 一键支付开通:
- 点击开通后接口返回成功,`vip_users` 写入/更新,`status=1,isVip=1,expireAt=now+durationDays`
- 再次点击在有效期内应提示“已是VIP/延长有效期”,并将 `expireAt` 顺延 `durationDays`(若设计为续期)。
- 管理端价格设置:
- 读取当前价格成功显示;修改后保存成功,重新读取为新值;前端展示同步更新(以接口返回为准)。
#### 11. 非功能性要求
- 审计:记录审核人与审核时间。
- 性能:常用过滤列建索引(`shop_id,status`)。
- 安全:所有接口在租户维度强制鉴权与隔离;仅店主/平台管理员可审核。
#### 12. 验收标准
- 管理端可增改查 VIP支持启停与审核通过/驳回。
- App 可正确显示会员状态与有效期,支持提交开通申请。
- 配置可通过参数调整价格与时长,无任何硬编码常量。
- OpenAPI 中 VIP 路径状态标注准确,接口字段与实现一致。
- 数据可见性非VIP仅显示近两个月数据VIP显示全部历史可通过系统参数动态调整非VIP留存天数。
- 一键支付点击立即开通可直接成为VIP并设置到期时间有效期内再次开通实现续期如有设计
- 价格设置管理员可在“VIP管理”中读取/修改VIP价格保存后前端价格展示与后端一致。
#### 13. 普通管理员Admin-Lite与申请机制新增需求
- 背景:在现有平台管理员(`admins`之外新增“普通管理员Normal Admin供用户端的 VIP 用户申请成为普通管理员后登录一个精简的普通管理端admin-lite
- 目标:
- 普通管理端仅包含“配件审核”与“我的”两个功能。
- 普通管理员使用“用户端账号(邮箱 + 密码)”登录普通管理端。
- 申请资格:仅限 VIP且建议要求处于有效期内
##### 13.1 业务规则
- 申请前置:用户必须为 VIP 用户(建议校验 `vip_users.status=1``expire_at>now()`)。
- 审批策略(多方案):
- 方案A推荐平台审核提交申请→平台管理员审核→通过后赋予“普通管理员”权限。
- 方案B 自动通过:成为 VIP 后点击“申请”即自动获取权限(适合早期冷启动)。
- 方案C 店主审核:若后续引入“店主/员工”角色体系,可由店主审批本店普通管理员。
- 有效性约束(推荐):普通管理员权限与 VIP 状态绑定;当 VIP 过期则自动失去普通管理员权限(或进入“受限”状态,仅保留查看)。
- 范围隔离:普通管理员仅可访问其所属店铺 `shop_id` 范围内的资源(含配件提交与审核)。
##### 13.2 数据模型(多方案)
- 方案A复用 `users.role`,并新增审计表(快速落地)
- `users.role` 可写入 `normal_admin` 作为标识;新增 `normal_admin_audits` 记录申请、审批与撤销流水。
- 优点:改动小,落地快。
- 风险:`role` 单值扩展性差,后续多角色并存不便。
- 方案B新增 `user_roles`(推荐中长期)
- 结构:`id, user_id, role, status, created_at, updated_at`,可并存多个角色;配套 `user_role_audits` 留痕。
- 优点:扩展性强,便于细粒度权限。
- 成本:需要通用角色判定与拦截器改造。
- 方案C新增 `normal_admins`
- 结构:`id, shop_id, user_id, status, expire_at(可选), remark, reviewer_id, reviewed_at, created_at, updated_at`
- 优点:语义清晰;可直接与 VIP 绑定。
- 成本:专用表+专用逻辑,通用性略弱。
当前建议MVP 采用方案A或C二选一优先保证上线速度后续演进到方案B以支撑更多角色与权限。
配置项(严禁硬编码,写入 `system_parameters` 或全局参数表):
- `normalAdmin.autoApprove`:是否自动通过申请(默认 false
- `normalAdmin.requiredVipActive`:是否要求 VIP 有效期内(默认 true
##### 13.3 接口契约(开始实现任一侧后同步登记至 `/doc/openapi.yaml` 并标注状态)
- 用户端App/小程序):
- POST `/api/normal-admin/apply`:提交申请。需要登录且满足 VIP 条件;根据 `normalAdmin.autoApprove` 决定是否即时生效。
- GET `/api/normal-admin/application/status`:查询本人申请状态与生效情况。
- 平台管理端:
- GET `/api/admin/normal-admin/applications`:申请列表(支持状态/关键词/时间范围)。
- POST `/api/admin/normal-admin/applications/{id}/approve`:审批通过。
- POST `/api/admin/normal-admin/applications/{id}/reject`:审批驳回(需备注)。
- 普通管理端鉴权(见 13.4
- 登录沿用用户侧认证:`POST /api/auth/email/login` 或现有登录接口,后端签发 Token 时加入 `scope`/`role` 标识(如 `normal_admin`)。
示例返回(申请状态):
```json
{ "status": "approved", "isVip": true, "vipExpireAt": "2025-12-31T23:59:59Z" }
```
##### 13.4 鉴权与拦截Admin-Lite
- 新增拦截器 `NormalAdminAuthInterceptor`(或等效过滤器):
- 校验登录 Token 为用户侧 Token读取 `role/scope``normal_admin`
- 若启用 `normalAdmin.requiredVipActive`:同时校验 `vip_users` 有效。
- 强制多租户隔离:将 `shop_id` 固化为当前用户所属店铺,杜绝越权访问。
- 路由命名建议:`/api/normal-admin/**` 与平台 `/api/admin/**` 区隔;服务层可复用同一业务逻辑。
- 兼容方案:若短期沿用现有 `/api/admin/parts/submissions*`,需在后端根据 Token 类型与 `shop_id` 自动收敛到本店数据(不建议长期共用)。
##### 13.5 普通管理端admin-lite前端
- 技术栈Vue3 + Vite + Element Plus与现管理端一致新建 `normal-admin/` 工程或在现有 `admin/` 下以独立入口构建。
- 模块:
- 配件审核:复制 `admin/src/views/parts/Submissions.vue`仅保留必要字段与操作HTTP 基址指向 `/api/normal-admin/...`(或复用后端别名);列表限定为本店数据。
- 我的展示账户信息、VIP 状态、普通管理员状态、退出登录;若未获批且为 VIP显示“申请成为普通管理员”按钮调用 `/api/normal-admin/apply`)。
- 路由示例:
- `/` → 重定向 `/parts/submissions`
- `/parts/submissions` → 审核页
- `/my` → 我的
- 配置(环境变量,不得硬编码):
- `VITE_APP_TITLE=配件审核(普通管理端)`
- `VITE_API_BASE=/api`(与部署网关一致)
- `VITE_ENABLE_EXPORT=true/false`(是否开启导出)
##### 13.6 流程图Graphviz
```dot
digraph G {
rankdir=LR;
node [shape=box, style=rounded];
a[label="成为VIP\n(GET /api/vip/status)"];
b[label="申请普通管理员\n(POST /api/normal-admin/apply)"];
c[label="平台审批/自动通过\n(根据 normalAdmin.autoApprove)"];
d[label="获得 normal_admin 权限\n(Token 含 scope)"];
e[label="登录普通管理端\n(邮箱+密码)"];
f[label="配件审核\n(/api/normal-admin/parts/submissions*)"];
a -> b -> c -> d -> e -> f;
}
```
##### 13.7 验收标准Admin-Lite
- 登录:使用用户邮箱+密码成功登录普通管理端;非普通管理员登录被拒绝并提示申请。
- 资格:非 VIP 点击“申请”提示需先成为 VIPVIP 点击“申请”按配置自动通过或进入待审;审批通过后刷新权限可访问。
- 范围:仅能查看与操作本店 `shop_id` 范围内的配件提交;越权访问被拦截。
- 审核:通过/驳回、备注、图片管理、参数编辑等操作与现管理端一致;操作成功刷新列表。
- 导出(可选):按配置开放导出功能,文件名与权限口径与现实现一致。
- 退化:当 VIP 失效时,若配置为强绑定,应在下一次鉴权时移除 `normal_admin` 访问权限(或降级为只读)。
##### 13.8 实施计划(补充)
1) 后端:
- 新增 `NormalAdminAuthInterceptor``/api/normal-admin/**` 路由;实现申请接口与(可选)平台审批接口;复用配件审核服务,默认按当前用户 `shop_id` 限定范围。
- Token 扩展:在用户侧登录成功时,根据数据库角色写入 `role/scope=normal_admin`
- 配置读取:接入 `normalAdmin.autoApprove``normalAdmin.requiredVipActive`(来自 `system_parameters` 或全局参数表)。
2) 前端admin-lite
- 建立最小可用工程,复制并精简 `Submissions.vue`;新增 `My.vue`;完善登录页与路由守卫(未授权跳转登录)。
- “我的”页集成申请按钮与状态显示;成功后刷新权限并跳转审核页。
3) 文档与 OpenAPI
- 任一侧开工后,将上述接口登记至 `/doc/openapi.yaml` 并标注实现状态(❌/✅)。
##### 13.9 风险与注意事项
- 权限收敛:严禁普通管理员访问跨店铺数据;所有查询/导出在服务层统一追加 `shop_id=当前用户.shop_id` 条件。
- 身份冲突:若同一用户既是平台管理员又是普通管理员,需优先以平台后台入口登录对应端;两个端的 Token/域名/路由需区分。
- 续期与降级:明确 VIP 失效后的权限处理策略与用户提示文案,避免误用。
- 审计留痕:申请/审批/撤销必须入审计表;审核操作记录操作人与时间。
##### 13.10 选型落地方案A
- 选型结论:
- 审批策略采用「方案A平台审核提交申请→平台管理员审核→通过后赋予“普通管理员”权限。
- 数据模型采用「方案A复用 `users.role` + 审计表)」:当前生效权限以 `users.role` 为准,申请/审批/撤销全量留痕于审计表。
- 审计表 DDL提交后通过 MysqlMCP 执行;此处为权威定义,禁止硬编码):
```sql
CREATE TABLE IF NOT EXISTS normal_admin_audits (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
shop_id BIGINT UNSIGNED NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
action ENUM('apply','approve','reject','revoke','expire') NOT NULL,
remark VARCHAR(255) NULL,
operator_admin_id BIGINT UNSIGNED NULL COMMENT '平台管理员IDapply时可空',
previous_role VARCHAR(32) NULL,
new_role VARCHAR(32) NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_naudit_shop_time (shop_id, created_at),
KEY idx_naudit_user_time (user_id, created_at),
CONSTRAINT fk_naudit_shop FOREIGN KEY (shop_id) REFERENCES shops(id),
CONSTRAINT fk_naudit_user FOREIGN KEY (user_id) REFERENCES users(id),
CONSTRAINT fk_naudit_admin FOREIGN KEY (operator_admin_id) REFERENCES admins(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='普通管理员申请/审批审计日志';
```
- 状态判定与待办列表(基于审计流,不单独建申请表):
- 当前状态 = 按 `user_id` 取最后一条审计记录的 `action`
- 待审批 = 最后一条为 `apply` 且其后没有 `approve/reject`
- 失效处理 = 定时或登录时检测 VIP 过期,若配置 `normalAdmin.requiredVipActive=true`,写入 `expire` 审计并触发降级(见下)。
- 接口约定细化(开始实现任一侧后再登记至 `/doc/openapi.yaml` 并标注状态):
- 用户申请POST `/api/normal-admin/apply`
- 校验:已登录 + VIP 有效(当 `requiredVipActive=true`)。
- 行为:写入 `normal_admin_audits(action='apply')`;若 `autoApprove=true` 则转调用审批通过流程。
- 审批列表GET `/api/admin/normal-admin/applications`
- 规则:按照审计记录推导“当前为 apply 且未被approve/reject”的记录支持关键词与时间过滤返回 `{ list, total }`
- 审批通过POST `/api/admin/normal-admin/applications/{userId}/approve`
- 行为:读取当前 `users.role` 记为 `previous_role`;更新 `users.role='normal_admin'`;写入审计 `approve`(含 `previous_role/new_role`)。
- 审批驳回POST `/api/admin/normal-admin/applications/{userId}/reject`
- 行为:写入审计 `reject`(需 remark
- 撤销权限POST `/api/admin/normal-admin/users/{userId}/revoke`
- 行为:将 `users.role` 回滚到最近一次 `approve``previous_role`(若不存在则设为 `'staff'` 或根据 `is_owner` 退回 `'owner'`);写入审计 `revoke`
- 鉴权与TokenAdmin-Lite
- 登录沿用用户端登录接口(邮箱/密码),签发 Token 时附带 `role``shopId`Admin-Lite 入口要求 `role='normal_admin'`
- 在拦截器 `NormalAdminAuthInterceptor` 中:
- 校验 `role='normal_admin'`;若 `requiredVipActive=true`,同时校验当前用户 VIP 有效。
- 将请求上下文中的 `shop_id` 固化为当前用户 `shop_id`,所有查询自动拼接该条件。
- 失效与回滚策略:
- 当 VIP 过期(检测点:每日任务/登录/接口访问拦截),若启用强绑定则触发降级:
-`users.role``normal_admin` 回滚至上次 `approve``previous_role`(若无记录:`is_owner=1` 回滚到 `'owner'`,否则 `'staff'`)。
- 写入 `expire` 审计remark 记录触发缘由)。
- 风险与缓解方案A局限
- `users.role` 为单值字段,无法并存多角色;短期仅用于识别 Admin-Lite 访问权限,不影响用户侧功能(用户侧权限建议依赖 `is_owner` 与接口授权)。
- 后续若需多角色并存,按 13.2 的方案B 迁移至 `user_roles`,并在迁移期间保留写入 `users.role` 的兼容逻辑。
- 测试要点补充:
- 申请→审批通过:审计有 `apply``approve` 两条,`users.role` 变为 `normal_admin`Admin-Lite 可登录访问。
- 申请→驳回:审计有 `apply``reject``users.role` 不变Admin-Lite 拒绝访问。
- 撤销:`users.role` 回滚为 `previous_role`,新增 `revoke` 审计;再次访问被拒绝。
- VIP 过期:若强绑定开启,触发降级并写入 `expire`Admin-Lite 访问被拒绝;关闭强绑定时仅告警不降级。
—— 本文档为当前功能实现说明与落地细节,不含历史变更记录;如有与数据模型不匹配之处,以 `/doc/database_documentation.md` 为准并及时校准。

View File

@@ -1,139 +0,0 @@
https://icons8.com/
### 后端使用文档(简版)
本文件用于指导在新电脑上启动 Spring Boot 后端,并直接连接远程 MySQL 数据库。
### 环境要求
- **操作系统**: Windows 10/11PowerShell
- **JDK**: 17 及以上(`java -version` 应显示 17+
- **网络**: 可访问 `mysql.tonaspace.com:3306`
- **构建工具**: 无需单独安装 Maven项目已提供 `mvnw.cmd`
### 快速启动(默认连接远程库)
1) 打开 PowerShell进入项目后端目录
```powershell
cd backend
```
2) 启动后端(使用默认远程数据库配置):
```powershell
.\mvnw.cmd spring-boot:run -DskipTests
```
> 说明:`application.properties` 已内置远程库默认值(`DB_URL/DB_USER/DB_PASSWORD`)。除非你的终端已设置了同名环境变量并想覆盖,否则无需再配置。
### 启动 Python 条码识别服务(可选)
本项目提供 Python 条码识别服务FastAPI目录 `backend/txm`),可与 Java 同时启动或独立启动。
- 独立启动(推荐先验证):
```powershell
cd .\backend\txm; python -m pip install -r requirements.txt; python -m app.server.main
```
- 随 Java 一并启动(通过环境变量启用;默认关闭):
```powershell
$env:PY_BARCODE_ENABLED="true"; $env:PY_BARCODE_WORKDIR=".\txm"; $env:PY_BARCODE_PYTHON="python"; .\mvnw.cmd spring-boot:run -DskipTests
```
可覆盖的相关配置键(同名环境变量可覆盖,括号为默认值):
```
python.barcode.enabled=${PY_BARCODE_ENABLED:false}
python.barcode.working-dir=${PY_BARCODE_WORKDIR:./txm}
python.barcode.python=${PY_BARCODE_PYTHON:python}
python.barcode.app-module=${PY_BARCODE_APP_MODULE:app.server.main}
python.barcode.use-module-main=${PY_BARCODE_USE_MODULE:true}
python.barcode.host=${PY_BARCODE_HOST:127.0.0.1}
python.barcode.port=${PY_BARCODE_PORT:8000}
python.barcode.health-path=${PY_BARCODE_HEALTH:/openapi.json}
python.barcode.startup-timeout-sec=${PY_BARCODE_TIMEOUT:20}
python.barcode.log-file=${PY_BARCODE_LOG:}
```
Java 侧代理接口:`POST /api/barcode/scan`(表单字段名:`file`)。
返回:
```
{
success: true,
barcodeType: 'EAN13' | 'CODE128' | 'QRCODE' | ...,
barcode: '字符串',
others: [{ type, code }, ...] // 可能为空
}
```
说明:优先 EAN-13否则返回任意码制的第一个结果并同时返回 others。
### 可选:显式指定远程数据库(避免被旧环境变量覆盖)
如需显式声明一次连接信息(建议在怀疑本机已有旧变量时使用):
```powershell
$env:DB_URL="jdbc:mysql://mysql.tonaspace.com:3306/partsinquiry?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8mb4&connectionCollation=utf8mb4_0900_ai_ci"; $env:DB_USER="root"; $env:DB_PASSWORD="TONA1234"
.\mvnw.cmd spring-boot:run -DskipTests
```
### 启动验证
- 浏览器访问:`http://localhost:8080/api/dashboard/overview`
- 返回概览数据即表示服务与数据库连接正常
### 按用户ID登录用户端快速登录通道
> 仅在调试或特定场景启用。默认关闭。
1) 启用开关(临时):
```powershell
$env:AUTH_ID_LOGIN_ENABLED="true"; .\mvnw.cmd spring-boot:run -DskipTests
```
2) 请求示例:
```http
POST http://localhost:8080/api/auth/login-by-id
Content-Type: application/json
{ "userId": 2 }
```
3) 成功返回:`{ token, expiresIn, user:{ userId, shopId, phone? } }`
4) 之后在调用业务接口时携带:`Authorization: Bearer <token>`
### 常见问题
- **端口被占用**:更换启动端口
```powershell
.\mvnw.cmd spring-boot:run -DskipTests -Dserver.port=8081
```
- **远程库连不通**:检查网络是否放行 `mysql.tonaspace.com:3306`;如在公司网络,确认代理/防火墙策略已放通。
- **Java 未安装或版本不符**:安装 JDK 17并在新开终端内确认 `java -version`
### 可选设置
- 占位图(非必需):若需启用 `/api/attachments/placeholder`
```powershell
$env:ATTACHMENTS_PLACEHOLDER_IMAGE="C:\Users\Public\Pictures\placeholder.png"
```
- 前端联调 CORS按需
```powershell
$env:CORS_ALLOWED_ORIGINS="http://localhost:5173"
```
### 可选:打包为可执行 JAR
```powershell
cd backend; .\mvnw.cmd clean package -DskipTests; java -jar .\target\demo-0.0.1-SNAPSHOT.jar
```
### SMTP 邮件配置Windows PowerShell
请在启动后端前设置以下环境变量QQ 邮箱):
```powershell
$env:MAIL_HOST="smtp.qq.com"; $env:MAIL_PORT="465"; $env:MAIL_PROTOCOL="smtps"; $env:MAIL_USERNAME="sdssds@163.com"; $env:MAIL_PASSWORD="NQLihrab8vGiAjiE"; $env:MAIL_FROM="sdssds@163.com"; $env:MAIL_SUBJECT_PREFIX="[配件查询]"
```
说明:
- MAIL_USERNAME/MAIL_FROM发件邮箱地址
- MAIL_PASSWORDSMTP 授权码
- 采用 465 + SMTPS + SSL若使用 587请改为 `MAIL_PORT=587` 并设置 `spring.mail.properties.mail.smtp.starttls.enable=true`
### 邮箱验证码接口
- POST `/api/auth/email/send`:请求体 `{ email, scene? }`,成功返回 `{ ok, cooldownSec }`
- POST `/api/auth/email/login`:请求体 `{ email, code }`,成功返回 `{ token, expiresIn, user }`
返回的 JWT 通过 `Authorization: Bearer <token>` 使用,解析后包含 `userId/shopId/email/provider` 等声明。
以上即为在新电脑上启动后端并连接远程数据库的最小步骤。

View File

@@ -1,35 +0,0 @@
## 当前开发进度概览2025-09-27
### 一、后端Spring Boot
- **会员体系**:已实现 `vip_users``vip_price``vip_recharges` CRUD 及接口 `/api/vip/*``/api/admin/vip/*`
- **公告管理**`/api/admin/notices` 完整可用,支持创建/编辑/发布/下线。
- **附件系统**`/api/attachments` 支持本地存储、hash 去重与 URL 校验;用户与管理端均在使用。
- **条码代理**`/api/barcode/scan` 代理 Python TXM 服务,需人工启动 Python 端。
- **鉴权机制**:管理员接口仍依赖 `X-Admin-Id` 兼容头JWT 登录接口已实现但尚未前端集成。
### 二、管理端Vue 3 + Element Plus
- 已上线模块VIP 系统、VIP 列表、公告管理、咨询回复、用户管理、配件管理、主数据字典、**配件审核(新增 Submissions 页面,支持筛选/详情/通过/驳回/导出)**。
- 尚缺模块:登录页、角色权限细分、操作日志可视化。
- 交互问题:表格分页统一使用后端分页参数,个别页面(如 VIP 列表)暂未展示 total需要补齐。
### 三、移动端uni-app
- **主流程**:商品、订单、客户、供应商、报表等基础功能可用。
- **登录/注册**`pages/auth/login.vue` 新增邮箱密码登录、验证码注册、忘记密码三合一页面,调用 `/api/auth/password/login``/api/auth/email/register``/api/auth/email/reset-password`;验证码发送支持 register/reset 场景。
- **配件提交(新增)**`pages/product/submit.vue``pages/product/submissions.vue``pages/product/submission-detail.vue` 已接入配件提交、列表、详情;支持多图上传、参数 JSON、驳回重新提交与后端 `/api/products/submissions*` 系列接口对齐。
- **VIP 页面**`pages/my/vip.vue` 已接入状态查询与一键开通,待接续续费提示与权益引导。
- **咨询入口**:请求与管理端对接正常,尚缺悬浮入口。
- **扫码**:仅 App 端调用条码识别,小程序端提示不支持扫码。
### 四、数据库
- 远程库包含 VIP、附件等新增表本地脚本 `backend/db/db.sql` 尚未同步;执行结构变更需通过 MysqlMCP 并手动更新脚本。
- 需追加的建表脚本:`vip_users``vip_price``vip_recharges``attachments` 等。
### 五、待解决问题
1. **管理员登录上线**:前端需接入 `/api/admin/auth/login` 并替换本地 ADMIN_ID 写死逻辑。
2. **VIP 续期逻辑**`/api/vip/pay` 当前覆盖式设置到期时间,需确认是否改为顺延有效期并记录 `expire_from`
3. **公告富文本**:现为纯文本,若需富文本需评估安全策略。
4. **前端分页补充**:管理端表格统一展示 total 并接入分页控件。
5. **数据库脚本同步**:更新 `backend/db/db.sql` 与 doc 文档保持一致,避免新环境缺表。
6. **条码服务部署**:需编写部署说明,确定 Python TXM 服务是否随 Java 进程自动启动。
7. **登录方式文档同步**OpenAPI 已新增 `/api/auth/email/reset-password`,前端登录页三流程已实现,需对外文档同步展示入口与约束。
8. **配件提交流程验收**:需补充测试用例(提交→审核→导出),并确认型号唯一策略是否支持多店共享或全局唯一。

View File

@@ -0,0 +1,997 @@
# 配件查询系统 - 数据库设计文档(核心业务表)
**版本**: 1.0
**更新时间**: 2025-10-01
**数据库**: MySQL 8.0
**字符集**: utf8mb4 / utf8mb4_0900_ai_ci
---
## 一、租户与用户体系
### 1.1 shops店铺/租户)
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| name | VARCHAR(100) | NO | | 店铺名称 |
| status | TINYINT UNSIGNED | NO | 1 | 1=启用 0=停用 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_shops_status` (`status`)
**说明**多租户隔离的核心表所有业务数据必须关联shop_id。
---
### 1.2 users用户
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 所属店铺 |
| phone | VARCHAR(32) | YES | NULL | 手机号(全局唯一) |
| email | VARCHAR(128) | YES | NULL | 邮箱(全局唯一) |
| avatar_url | VARCHAR(512) | YES | NULL | 头像URL |
| name | VARCHAR(64) | NO | | 用户姓名 |
| role | VARCHAR(32) | NO | 'staff' | 角色owner/staff/normal_admin |
| password_hash | VARCHAR(255) | YES | NULL | 密码哈希 |
| status | TINYINT UNSIGNED | NO | 1 | 1=启用 0=停用 |
| is_owner | TINYINT(1) | NO | 0 | 是否店主 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_users_shop` (`shop_id`)
- UNIQUE: `uk_users_phone` (`phone`)
- UNIQUE: `ux_users_email` (`email`)
**外键**
- `fk_users_shop`: `shop_id``shops(id)`
**说明**支持手机号、邮箱双登录方式role字段支持普通管理员权限标识。
---
### 1.3 admins平台管理员
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| username | VARCHAR(64) | NO | | 登录名 |
| phone | VARCHAR(32) | YES | NULL | 手机号 |
| password_hash | VARCHAR(255) | YES | NULL | 密码哈希 |
| status | TINYINT UNSIGNED | NO | 1 | 1=启用 0=停用 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- UNIQUE: `ux_admins_username` (`username`)
- UNIQUE: `ux_admins_phone` (`phone`)
**说明**:平台管理员不归属任何店铺,可跨租户管理数据。
---
## 二、商品与库存体系
### 2.1 products商品
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 所属店铺 |
| user_id | BIGINT UNSIGNED | NO | | 创建人 |
| name | VARCHAR(120) | NO | | 商品名称 |
| category_id | BIGINT UNSIGNED | YES | NULL | 类别ID |
| template_id | BIGINT UNSIGNED | YES | NULL | 配件模板ID |
| brand | VARCHAR(64) | YES | NULL | 品牌 |
| model | VARCHAR(64) | YES | NULL | 型号 |
| spec | VARCHAR(128) | YES | NULL | 规格 |
| origin | VARCHAR(64) | YES | NULL | 产地 |
| barcode | VARCHAR(32) | YES | NULL | 条码 |
| dedupe_key | VARCHAR(512) | YES | NULL | 去重键 |
| alias | VARCHAR(120) | YES | NULL | 别名 |
| is_blacklisted | TINYINT(1) | NO | 0 | 黑名单标记 |
| description | TEXT | YES | NULL | 描述 |
| global_sku_id | BIGINT UNSIGNED | YES | NULL | 全局SKU ID |
| source_submission_id | BIGINT UNSIGNED | YES | NULL | 来源提交ID |
| attributes_json | JSON | YES | NULL | 扩展属性JSON |
| safe_min | DECIMAL(18,3) | YES | NULL | 安全库存下限 |
| safe_max | DECIMAL(18,3) | YES | NULL | 安全库存上限 |
| search_text | TEXT | YES | NULL | 全文检索聚合字段 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
| is_active | TINYINT | YES | | 计算字段deleted_at IS NULL |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_products_shop` (`shop_id`)
- KEY: `idx_products_category` (`category_id`)
- KEY: `idx_products_template` (`template_id`)
- KEY: `idx_products_dedupe` (`dedupe_key`)
- KEY: `idx_products_shop_blacklist` (`shop_id`, `is_blacklisted`)
- FULLTEXT: `ft_products_search` (`name`, `brand`, `model`, `spec`, `search_text`)
- UNIQUE: `ux_products_shop_barcode` (`shop_id`, `barcode`)
- UNIQUE: `ux_products_template_name_model` (`template_id`, `name`, `model`)
**外键**
- `fk_products_shop`: `shop_id``shops(id)`
- `fk_products_user`: `user_id``users(id)`
- `fk_products_category`: `category_id``product_categories(id)`
- `fk_products_template`: `template_id``part_templates(id)`
- `fk_products_globalsku`: `global_sku_id``global_skus(id)`
**说明**:核心商品表,支持模板化参数、全文检索、去重和黑名单管理。
---
### 2.2 product_prices商品价格
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| product_id | BIGINT UNSIGNED | NO | | 商品ID主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 最后修改人 |
| purchase_price | DECIMAL(18,2) | NO | 0.00 | 进货价 |
| retail_price | DECIMAL(18,2) | NO | 0.00 | 零售价 |
| wholesale_price | DECIMAL(18,2) | NO | 0.00 | 批发价 |
| big_client_price | DECIMAL(18,2) | NO | 0.00 | 大客户价 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `product_id`
- KEY: `idx_prices_shop` (`shop_id`)
**外键**
- `fk_prices_product`: `product_id``products(id)` ON DELETE CASCADE
- `fk_prices_shop`: `shop_id``shops(id)`
- `fk_prices_user`: `user_id``users(id)`
**约束**:所有价格字段 >= 0
**说明**:四列销售价格支持不同客户等级定价策略。
---
### 2.3 inventories库存
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| product_id | BIGINT UNSIGNED | NO | | 商品ID主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 最后修改人 |
| quantity | DECIMAL(18,3) | NO | 0.000 | 库存数量 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `product_id`
- KEY: `idx_inventories_shop` (`shop_id`)
**外键**
- `fk_inv_product`: `product_id``products(id)` ON DELETE CASCADE
- `fk_inv_shop`: `shop_id``shops(id)`
- `fk_inv_user`: `user_id``users(id)`
**约束**quantity >= 0
**说明**:支持小数精度(3位),适配多种计量单位。
---
### 2.4 product_categories商品类别
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID0=全局) |
| user_id | BIGINT UNSIGNED | NO | | 创建人 |
| name | VARCHAR(64) | NO | | 类别名称 |
| parent_id | BIGINT UNSIGNED | YES | NULL | 父类别ID |
| sort_order | INT | NO | 0 | 排序 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_categories_shop` (`shop_id`)
- KEY: `idx_categories_parent` (`parent_id`)
- UNIQUE: `ux_categories_shop_name` (`shop_id`, `name`)
**外键**
- `fk_categories_shop`: `shop_id``shops(id)`
- `fk_categories_user`: `user_id``users(id)`
- `fk_categories_parent`: `parent_id``product_categories(id)`
**说明**shop_id=0为全局字典所有租户共享。
---
### 2.5 product_units商品单位
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID0=全局) |
| user_id | BIGINT UNSIGNED | NO | | 创建人 |
| name | VARCHAR(16) | NO | | 单位名称 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_units_shop` (`shop_id`)
- UNIQUE: `ux_units_shop_name` (`shop_id`, `name`)
**外键**
- `fk_units_shop`: `shop_id``shops(id)`
- `fk_units_user`: `user_id``users(id)`
**说明**shop_id=0为全局字典所有租户共享。
---
### 2.6 product_images商品图片
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 上传人 |
| product_id | BIGINT UNSIGNED | NO | | 商品ID |
| url | VARCHAR(512) | NO | | 图片URL |
| hash | VARCHAR(64) | YES | NULL | 内容哈希(去重) |
| sort_order | INT | NO | 0 | 排序 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_product_images_product` (`product_id`)
- UNIQUE: `ux_product_image_hash` (`product_id`, `hash`)
**外键**
- `fk_pimg_shop`: `shop_id``shops(id)`
- `fk_pimg_user`: `user_id``users(id)`
- `fk_pimg_product`: `product_id``products(id)` ON DELETE CASCADE
**说明**通过hash实现图片去重。
---
### 2.7 product_aliases商品别名
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 创建人 |
| product_id | BIGINT UNSIGNED | NO | | 商品ID |
| alias | VARCHAR(120) | NO | | 别名 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_product_alias_product` (`product_id`)
- UNIQUE: `ux_product_alias` (`product_id`, `alias`)
**外键**
- `fk_alias_shop`: `shop_id``shops(id)`
- `fk_alias_user`: `user_id``users(id)`
- `fk_alias_product`: `product_id``products(id)`
**说明**支持商品多别名检索触发器自动同步到search_text。
---
## 三、配件审核与模板体系
### 3.1 part_submissions配件提交
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 提交店铺 |
| user_id | BIGINT UNSIGNED | NO | | 提交用户 |
| name | VARCHAR(120) | YES | NULL | 配件名称 |
| external_code | VARCHAR(64) | YES | NULL | 外部编码 |
| model_unique | VARCHAR(128) | NO | | 规范化型号(唯一) |
| brand | VARCHAR(64) | YES | NULL | 品牌 |
| spec | VARCHAR(128) | YES | NULL | 规格 |
| unit_id | BIGINT UNSIGNED | YES | NULL | 单位 |
| category_id | BIGINT UNSIGNED | YES | NULL | 类别 |
| template_id | BIGINT UNSIGNED | YES | NULL | 模板 |
| tags | JSON | YES | NULL | 标签 |
| attributes | JSON | YES | NULL | 参数JSON |
| images | JSON | YES | NULL | 图片URL数组 |
| size | VARCHAR(64) | YES | NULL | 尺寸(兼容) |
| aperture | VARCHAR(64) | YES | NULL | 孔径(兼容) |
| compatible | TEXT | YES | NULL | 适配信息 |
| barcode | VARCHAR(64) | YES | NULL | 条码 |
| dedupe_key | VARCHAR(512) | YES | NULL | 去重键 |
| remark | TEXT | YES | NULL | 备注 |
| status | ENUM('pending','approved','rejected') | NO | 'pending' | 审核状态 |
| reviewer_id | BIGINT UNSIGNED | YES | NULL | 审核人 |
| product_id | BIGINT UNSIGNED | YES | NULL | 关联商品ID |
| global_sku_id | BIGINT UNSIGNED | YES | NULL | 关联全局SKU |
| reviewed_at | DATETIME | YES | NULL | 审核时间 |
| review_remark | VARCHAR(255) | YES | NULL | 审核备注 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_ps_shop` (`shop_id`)
- KEY: `idx_ps_user` (`user_id`)
- KEY: `idx_ps_brand` (`brand`)
- KEY: `idx_ps_status` (`status`)
- KEY: `idx_ps_template` (`template_id`)
- KEY: `idx_ps_dedupe` (`dedupe_key`)
- KEY: `idx_ps_created_at` (`created_at`)
- UNIQUE: `ux_part_model_unique` (`model_unique`)
- UNIQUE: `ux_ps_template_name_model` (`template_id`, `name`, `model_unique`)
**外键**
- `fk_ps_shop`: `shop_id``shops(id)`
- `fk_ps_user`: `user_id``users(id)`
- `fk_ps_reviewer`: `reviewer_id``admins(id)`
- `fk_ps_product`: `product_id``products(id)`
- `fk_ps_global_sku`: `global_sku_id``global_skus(id)`
- `fk_ps_template`: `template_id``part_templates(id)`
**说明**用户提交配件数据审核通过后生成products记录。model_unique全局唯一。
---
### 3.2 part_templates配件模板
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| category_id | BIGINT UNSIGNED | NO | | 绑定类别 |
| name | VARCHAR(120) | NO | | 配件名 |
| model_rule | VARCHAR(255) | YES | NULL | 型号规则说明 |
| status | TINYINT UNSIGNED | NO | 1 | 1=启用 0=停用 |
| created_by_admin_id | BIGINT UNSIGNED | YES | NULL | 创建管理员 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_pt_category` (`category_id`)
- KEY: `idx_pt_status` (`status`)
- KEY: `idx_pt_admin` (`created_by_admin_id`)
- KEY: `idx_part_templates_deleted_at` (`deleted_at`)
**外键**
- `fk_pt_category`: `category_id``product_categories(id)`
- `fk_pt_admin`: `created_by_admin_id``admins(id)`
**说明**:配件模板定义,关联多个参数字段。
---
### 3.3 part_template_params模板参数
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| template_id | BIGINT UNSIGNED | NO | | 所属模板 |
| field_key | VARCHAR(64) | NO | | 参数键 |
| field_label | VARCHAR(120) | NO | | 参数名(展示) |
| type | ENUM('string','number','boolean','enum','date') | NO | | 参数类型 |
| required | TINYINT(1) | NO | 0 | 是否必填 |
| unit | VARCHAR(32) | YES | NULL | 单位 |
| enum_options | JSON | YES | NULL | 枚举选项 |
| searchable | TINYINT(1) | NO | 0 | 参与检索 |
| fuzzy_searchable | TINYINT(1) | NO | 0 | 可模糊查询(数值) |
| fuzzy_tolerance | DECIMAL(18,6) | YES | NULL | 容差NULL=平台默认) |
| dedupe_participate | TINYINT(1) | NO | 0 | 参与去重 |
| sort_order | INT | NO | 0 | 排序 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_ptp_template` (`template_id`)
- KEY: `idx_ptp_sort` (`template_id`, `sort_order`)
- UNIQUE: `ux_ptp_field_key` (`template_id`, `field_key`)
**外键**
- `fk_ptp_template`: `template_id``part_templates(id)` ON DELETE CASCADE
**说明**:定义模板的参数字段,支持类型化、验证和模糊搜索。
---
### 3.4 global_skus全局SKU
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| name | VARCHAR(120) | NO | | SKU名称 |
| brand | VARCHAR(64) | YES | NULL | 品牌 |
| model | VARCHAR(64) | YES | NULL | 型号 |
| spec | VARCHAR(128) | YES | NULL | 规格 |
| barcode | VARCHAR(32) | YES | NULL | 条码 |
| unit_id | BIGINT UNSIGNED | YES | NULL | 单位 |
| tags | JSON | YES | NULL | 标签 |
| status | ENUM('published','offline') | NO | 'published' | 状态 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_global_skus_brand_model` (`brand`, `model`)
- UNIQUE: `ux_global_skus_barcode` (`barcode`)
**外键**
- `fk_globalsku_unit`: `unit_id``product_units(id)`
**说明**:全局共享的商品库,供各租户引用。
---
## 四、客户与供应商
### 4.1 customers客户
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 创建人 |
| name | VARCHAR(120) | NO | | 客户名称 |
| contact_name | VARCHAR(64) | YES | NULL | 联系人 |
| mobile | VARCHAR(32) | YES | NULL | 手机 |
| phone | VARCHAR(32) | YES | NULL | 座机 |
| address | VARCHAR(255) | YES | NULL | 送货地址 |
| price_level | ENUM('零售价','批发价','大单报价') | NO | '零售价' | 默认价格等级 |
| status | TINYINT UNSIGNED | NO | 1 | 1=启用 0=停用 |
| ar_opening | DECIMAL(18,2) | NO | 0.00 | 期初应收 |
| remark | VARCHAR(255) | YES | NULL | 备注 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_customers_shop` (`shop_id`)
- KEY: `idx_customers_phone` (`phone`)
- KEY: `idx_customers_mobile` (`mobile`)
**外键**
- `fk_customers_shop`: `shop_id``shops(id)`
- `fk_customers_user`: `user_id``users(id)`
**说明**price_level关联product_prices的四列价格之一。
---
### 4.2 suppliers供应商
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 创建人 |
| name | VARCHAR(120) | NO | | 供应商名称 |
| contact_name | VARCHAR(64) | YES | NULL | 联系人 |
| mobile | VARCHAR(32) | YES | NULL | 手机 |
| phone | VARCHAR(32) | YES | NULL | 电话 |
| address | VARCHAR(255) | YES | NULL | 经营地址 |
| status | TINYINT UNSIGNED | NO | 1 | 1=启用 0=停用 |
| ap_opening | DECIMAL(18,2) | NO | 0.00 | 期初应付 |
| ap_payable | DECIMAL(18,2) | NO | 0.00 | 当前应付(实时) |
| remark | VARCHAR(255) | YES | NULL | 备注 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_suppliers_shop` (`shop_id`)
- KEY: `idx_suppliers_phone` (`phone`)
- KEY: `idx_suppliers_mobile` (`mobile`)
**外键**
- `fk_suppliers_shop`: `shop_id``shops(id)`
- `fk_suppliers_user`: `user_id``users(id)`
**说明**ap_payable由订单和付款记录联动维护。
---
### 4.3 accounts结算账户
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 创建人 |
| name | VARCHAR(64) | NO | | 账户名称 |
| type | ENUM('cash','bank','alipay','wechat','other') | NO | 'cash' | 账户类型 |
| bank_name | VARCHAR(64) | YES | NULL | 银行名称 |
| bank_account | VARCHAR(64) | YES | NULL | 银行账号 |
| balance | DECIMAL(18,2) | NO | 0.00 | 账户余额 |
| status | TINYINT UNSIGNED | NO | 1 | 1=启用 0=停用 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_accounts_shop` (`shop_id`)
- UNIQUE: `ux_accounts_shop_name` (`shop_id`, `name`)
**外键**
- `fk_accounts_shop`: `shop_id``shops(id)`
- `fk_accounts_user`: `user_id``users(id)`
**说明**:支持现金、银行、支付宝、微信等多种结算方式。
---
## 五、订单与财务
### 5.1 sales_orders销售单
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 创建人 |
| customer_id | BIGINT UNSIGNED | YES | NULL | 客户ID |
| order_no | VARCHAR(32) | NO | | 订单号 |
| order_time | DATETIME | NO | | 订单时间 |
| status | ENUM('draft','approved','returned','void') | NO | 'draft' | 单据状态 |
| amount | DECIMAL(18,2) | NO | 0.00 | 应收合计 |
| paid_amount | DECIMAL(18,2) | NO | 0.00 | 已收合计 |
| remark | VARCHAR(255) | YES | NULL | 备注 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_sales_shop_time` (`shop_id`, `order_time`)
- KEY: `idx_sales_customer` (`customer_id`)
- UNIQUE: `ux_sales_order_no` (`shop_id`, `order_no`)
**外键**
- `fk_sales_shop`: `shop_id``shops(id)`
- `fk_sales_user`: `user_id``users(id)`
- `fk_sales_customer`: `customer_id``customers(id)`
**说明**approved后自动扣减库存付款记录关联payments表。
---
### 5.2 sales_order_items销售单明细
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| order_id | BIGINT UNSIGNED | NO | | 订单ID |
| product_id | BIGINT UNSIGNED | NO | | 商品ID |
| quantity | DECIMAL(18,3) | NO | | 数量 |
| unit_price | DECIMAL(18,2) | NO | | 单价 |
| discount_rate | DECIMAL(5,2) | NO | 0.00 | 折扣率(0-100) |
| cost_price | DECIMAL(18,2) | NO | 0.00 | 成本单价 |
| cost_amount | DECIMAL(18,2) | NO | 0.00 | 成本金额 |
| amount | DECIMAL(18,2) | NO | | 行金额 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_soi_order` (`order_id`)
- KEY: `idx_soi_product` (`product_id`)
**外键**
- `fk_soi_order`: `order_id``sales_orders(id)` ON DELETE CASCADE
- `fk_soi_product`: `product_id``products(id)`
**约束**
- quantity > 0
- unit_price >= 0
- discount_rate >= 0 AND <= 100
**说明**:记录开单时的成本价,用于利润分析。
---
### 5.3 purchase_orders进货单
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 创建人 |
| supplier_id | BIGINT UNSIGNED | YES | NULL | 供应商ID |
| order_no | VARCHAR(32) | NO | | 订单号 |
| order_time | DATETIME | NO | | 订单时间 |
| status | ENUM('draft','approved','void','returned') | NO | 'draft' | 单据状态 |
| amount | DECIMAL(18,2) | NO | 0.00 | 应付合计 |
| paid_amount | DECIMAL(18,2) | NO | 0.00 | 已付合计 |
| remark | VARCHAR(255) | YES | NULL | 备注 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_purchase_shop_time` (`shop_id`, `order_time`)
- KEY: `idx_purchase_supplier` (`supplier_id`)
- UNIQUE: `ux_purchase_order_no` (`shop_id`, `order_no`)
**外键**
- `fk_purchase_shop`: `shop_id``shops(id)`
- `fk_purchase_user`: `user_id``users(id)`
- `fk_purchase_supplier`: `supplier_id``suppliers(id)`
**说明**approved后自动增加库存。
---
### 5.4 purchase_order_items进货单明细
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| order_id | BIGINT UNSIGNED | NO | | 订单ID |
| product_id | BIGINT UNSIGNED | NO | | 商品ID |
| quantity | DECIMAL(18,3) | NO | | 数量 |
| unit_price | DECIMAL(18,2) | NO | | 单价 |
| amount | DECIMAL(18,2) | NO | | 行金额 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_poi_order` (`order_id`)
- KEY: `idx_poi_product` (`product_id`)
**外键**
- `fk_poi_order`: `order_id``purchase_orders(id)` ON DELETE CASCADE
- `fk_poi_product`: `product_id``products(id)`
**约束**
- quantity > 0
- unit_price >= 0
---
### 5.5 payments收付款记录
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 操作人 |
| biz_type | ENUM('sale','purchase','other') | NO | | 业务类型 |
| biz_id | BIGINT UNSIGNED | YES | NULL | 业务单据ID |
| account_id | BIGINT UNSIGNED | NO | | 结算账户 |
| direction | ENUM('in','out') | NO | | 收款/付款 |
| amount | DECIMAL(18,2) | NO | | 金额 |
| pay_time | DATETIME | NO | | 付款时间 |
| category | VARCHAR(64) | YES | NULL | 分类key其他收支 |
| remark | VARCHAR(255) | YES | NULL | 备注 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_payments_shop_time` (`shop_id`, `pay_time`)
- KEY: `idx_payments_biz` (`biz_type`, `biz_id`)
**外键**
- `fk_payments_shop`: `shop_id``shops(id)`
- `fk_payments_user`: `user_id``users(id)`
- `fk_payments_account`: `account_id``accounts(id)`
**约束**amount > 0
**说明**:统一管理销售收款、进货付款和其他收支。
---
### 5.6 other_transactions其他收支
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 创建人 |
| type | ENUM('income','expense') | NO | | 收入/支出 |
| category | VARCHAR(64) | NO | | 分类key |
| counterparty_type | VARCHAR(32) | YES | NULL | customer/supplier/other |
| counterparty_id | BIGINT UNSIGNED | YES | NULL | 往来单位ID |
| account_id | BIGINT UNSIGNED | NO | | 结算账户 |
| amount | DECIMAL(18,2) | NO | | 金额 |
| tx_time | DATETIME | NO | | 交易时间 |
| remark | VARCHAR(255) | YES | NULL | 备注 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_ot_shop_time` (`shop_id`, `tx_time`)
- KEY: `idx_ot_account` (`account_id`)
**外键**
- `fk_ot_shop`: `shop_id``shops(id)`
- `fk_ot_user`: `user_id``users(id)`
- `fk_ot_account`: `account_id``accounts(id)`
**约束**amount > 0
**说明**:记录非进销业务的其他收入和支出。
---
### 5.7 inventory_movements库存流水
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 操作人 |
| product_id | BIGINT UNSIGNED | NO | | 商品ID |
| source_type | VARCHAR(32) | NO | | 来源sale/purchase/return/adjust/audit |
| source_id | BIGINT UNSIGNED | YES | NULL | 来源单据ID |
| qty_delta | DECIMAL(18,3) | NO | | 数量增减(+/- |
| amount_delta | DECIMAL(18,2) | YES | NULL | 金额增减 |
| cost_price | DECIMAL(18,2) | YES | NULL | 成本单价 |
| cost_amount | DECIMAL(18,2) | YES | NULL | 成本金额 |
| reason | VARCHAR(64) | YES | NULL | 原因/类别 |
| tx_time | DATETIME | NO | | 业务时间 |
| remark | VARCHAR(255) | YES | NULL | 备注 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_im_shop_time` (`shop_id`, `tx_time`)
- KEY: `idx_im_product` (`product_id`)
**说明**:所有库存变动的审计日志,出库为负,入库为正。
---
## 六、销售退货
### 6.1 sales_return_orders销售退货单
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 创建人 |
| customer_id | BIGINT UNSIGNED | YES | NULL | 客户ID |
| order_no | VARCHAR(32) | NO | | 订单号 |
| order_time | DATETIME | NO | | 订单时间 |
| status | ENUM('approved','void') | NO | 'approved' | 单据状态 |
| amount | DECIMAL(18,2) | NO | 0.00 | 退货金额合计 |
| paid_amount | DECIMAL(18,2) | NO | 0.00 | 已退/已收合计 |
| remark | VARCHAR(255) | YES | NULL | 备注 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_sr_shop_time` (`shop_id`, `order_time`)
- UNIQUE: `ux_sr_order_no` (`shop_id`, `order_no`)
**外键**
- `fk_sr_shop`: `shop_id``shops(id)`
- `fk_sr_user`: `user_id``users(id)`
- `fk_sr_customer`: `customer_id``customers(id)`
**说明**approved后增加库存。
---
### 6.2 sales_return_order_items销售退货明细
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| order_id | BIGINT UNSIGNED | NO | | 订单ID |
| product_id | BIGINT UNSIGNED | NO | | 商品ID |
| quantity | DECIMAL(18,3) | NO | | 数量 |
| unit_price | DECIMAL(18,2) | NO | | 单价 |
| discount_rate | DECIMAL(5,2) | NO | 0.00 | 折扣率 |
| cost_price | DECIMAL(18,2) | NO | 0.00 | 成本单价 |
| cost_amount | DECIMAL(18,2) | NO | 0.00 | 成本金额 |
| amount | DECIMAL(18,2) | NO | | 行金额 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_sroi_order` (`order_id`)
- KEY: `idx_sroi_product` (`product_id`)
**外键**
- `fk_sroi_order`: `order_id``sales_return_orders(id)` ON DELETE CASCADE
- `fk_sroi_product`: `product_id``products(id)`
---
### 6.3 purchase_return_orders进货退货单
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 创建人 |
| supplier_id | BIGINT UNSIGNED | YES | NULL | 供应商ID |
| order_no | VARCHAR(32) | NO | | 订单号 |
| order_time | DATETIME | NO | | 订单时间 |
| status | ENUM('approved','void') | NO | 'approved' | 单据状态 |
| amount | DECIMAL(18,2) | NO | 0.00 | 退货金额合计 |
| paid_amount | DECIMAL(18,2) | NO | 0.00 | 已付合计 |
| remark | VARCHAR(255) | YES | NULL | 备注 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_pr_shop_time` (`shop_id`, `order_time`)
- UNIQUE: `ux_pr_order_no` (`shop_id`, `order_no`)
**外键**
- `fk_pr_shop`: `shop_id``shops(id)`
- `fk_pr_user`: `user_id``users(id)`
- `fk_pr_supplier`: `supplier_id``suppliers(id)`
**说明**approved后减少库存。
---
### 6.4 purchase_return_order_items进货退货明细
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| order_id | BIGINT UNSIGNED | NO | | 订单ID |
| product_id | BIGINT UNSIGNED | NO | | 商品ID |
| quantity | DECIMAL(18,3) | NO | | 数量 |
| unit_price | DECIMAL(18,2) | NO | | 单价 |
| amount | DECIMAL(18,2) | NO | | 行金额 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_proi_order` (`order_id`)
- KEY: `idx_proi_product` (`product_id`)
**外键**
- `fk_proi_order`: `order_id``purchase_return_orders(id)` ON DELETE CASCADE
- `fk_proi_product`: `product_id``products(id)`
---
## 七、触发器
### 7.1 products.search_text 维护触发器
**trg_products_ai** (AFTER INSERT)
```sql
UPDATE products
SET search_text = CONCAT_WS(' ', NEW.name, NEW.brand, NEW.model, NEW.spec)
WHERE id = NEW.id;
```
**trg_products_au** (BEFORE UPDATE)
```sql
SET NEW.search_text = CONCAT_WS(' ', NEW.name, NEW.brand, NEW.model, NEW.spec);
```
### 7.2 product_aliases 同步触发器
**trg_palias_ai** (AFTER INSERT)
```sql
-- 聚合所有别名到products.search_text
UPDATE products p
JOIN (
SELECT pa.product_id, GROUP_CONCAT(pa.alias SEPARATOR ' ') AS aliases
FROM product_aliases pa
WHERE pa.product_id = NEW.product_id AND pa.deleted_at IS NULL
GROUP BY pa.product_id
) a ON a.product_id = p.id
SET p.search_text = CONCAT_WS(' ', p.name, p.brand, p.model, p.spec, a.aliases)
WHERE p.id = NEW.product_id;
```
**trg_palias_au** (AFTER UPDATE)、**trg_palias_ad** (AFTER DELETE) - 类似逻辑
### 7.3 consults 回复状态触发器
**trg_consult_replies_ai** (AFTER INSERT)
```sql
UPDATE consults
SET status = 'resolved', updated_at = NOW()
WHERE id = NEW.consult_id;
```
---
## 八、数据完整性约束
### 8.1 CHECK约束
- `products`: `safe_min <= safe_max`
- `product_prices`: 所有价格 >= 0
- `inventories`: `quantity >= 0`
- `sales_order_items`: `quantity > 0, unit_price >= 0, discount_rate BETWEEN 0 AND 100`
- `purchase_order_items`: `quantity > 0, unit_price >= 0`
- `payments`: `amount > 0`
- `other_transactions`: `amount > 0`
### 8.2 唯一性约束
- `shops`: 无业务层面唯一约束
- `users`: `phone` 全局唯一、`email` 全局唯一
- `products`: `(shop_id, barcode)` 唯一、`(template_id, name, model)` 唯一
- `part_submissions`: `model_unique` 全局唯一、`(template_id, name, model_unique)` 唯一
- `customers/suppliers`: 无强制唯一约束(允许同名)
- `accounts`: `(shop_id, name)` 唯一
- `sales_orders/purchase_orders`: `(shop_id, order_no)` 唯一
---
## 九、设计说明
### 9.1 多租户隔离
- 所有业务表必须包含 `shop_id`
- 全局字典表(单位、类别)使用 `shop_id=0` 表示平台共享
- 查询必须强制按 `shop_id` 过滤
- VIP数据可见性非VIP用户仅显示最近60天可配置数据
### 9.2 软删除策略
- 主要业务表使用 `deleted_at` 字段标记软删除
- 查询默认过滤 `deleted_at IS NULL`
- 部分关联表(如订单明细)采用级联删除
### 9.3 审计与追踪
- 所有表包含 `created_at`, `updated_at` 时间戳
- 审核表part_submissions、vip_users记录审核人和审核时间
- 库存变动通过 `inventory_movements` 完整留痕
- 普通管理员申请通过 `normal_admin_audits` 留痕
### 9.4 性能优化
- 高频查询字段建立复合索引(如 `shop_id + order_time`
- `products.search_text` 使用FULLTEXT索引支持全文检索
- 价格和库存表采用主键为业务主键product_id的设计
- 合理使用外键约束,但不过度使用以避免性能损失
### 9.5 扩展性设计
- 商品属性通过 `attributes_json` 支持动态扩展
- 配件模板系统支持类型化参数定义
- 财务分类通过配置表 `finance_categories` 动态管理
- 预留 `global_sku_id` 支持平台级商品库
---
**文档维护说明**
- 任何数据库结构变更必须同步更新本文档
- 执行DDL操作后需标注更新时间
- 新增表需补充完整的字段说明和索引信息
- 变更需同步更新 `backend/db/db.sql` 脚本

View File

@@ -0,0 +1,584 @@
# 配件查询系统 - 数据库设计文档(辅助配置表)
**版本**: 1.0
**更新时间**: 2025-10-01
**数据库**: MySQL 8.0
**字符集**: utf8mb4 / utf8mb4_0900_ai_ci
---
## 一、VIP会员体系
### 1.1 vip_users会员用户
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 用户ID |
| is_vip | TINYINT(1) | NO | 1 | 是否VIP1是 0否 |
| status | TINYINT UNSIGNED | NO | 0 | 启用状态1启用 0停用 |
| expire_at | DATETIME | YES | NULL | 到期时间 |
| remark | VARCHAR(255) | YES | NULL | 备注/审核说明 |
| reviewer_id | BIGINT UNSIGNED | YES | NULL | 审核人 |
| reviewed_at | DATETIME | YES | NULL | 审核时间 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_vu_shop_user` (`shop_id`, `user_id`)
- KEY: `idx_vu_shop_status` (`shop_id`, `status`)
**外键**
- `fk_vu_shop`: `shop_id``shops(id)`
- `fk_vu_user`: `user_id``users(id)`
- `fk_vu_reviewer`: `reviewer_id``users(id)`
**说明**
- VIP状态判定`status=1 AND is_vip=1 AND (expire_at IS NULL OR expire_at >= NOW())`
- 非VIP用户数据可见性最近60天可配置
- VIP用户查看全部历史数据
---
### 1.2 vip_priceVIP价格
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| price | DECIMAL(10,2) | NO | | 单月价格(元) |
**索引**:无
**说明**
- 全局配置表,仅一条记录
- 表示平台统一VIP单月价格
- 管理端可读取和修改
---
### 1.3 vip_rechargesVIP充值记录
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 用户ID |
| price | DECIMAL(10,2) | NO | | 本次充值价格(元) |
| duration_days | INT | NO | | 本次续期天数 |
| expire_from | DATETIME | YES | NULL | 生效前到期时间 |
| expire_to | DATETIME | NO | | 生效后到期时间 |
| channel | VARCHAR(32) | NO | 'oneclick' | 渠道oneclick/... |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_vr_shop` (`shop_id`)
- KEY: `idx_vr_user` (`user_id`)
**外键**
- `fk_vr_shop`: `shop_id``shops(id)`
- `fk_vr_user`: `user_id``users(id)`
**说明**记录每次VIP开通/续费的历史,支持对账和查询。
---
## 二、普通管理员体系
### 2.1 normal_admin_audits普通管理员审计
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 用户ID |
| action | ENUM('apply','approve','reject','revoke','expire') | NO | | 操作类型 |
| remark | VARCHAR(255) | YES | NULL | 备注 |
| operator_admin_id | BIGINT UNSIGNED | YES | NULL | 平台管理员ID |
| previous_role | VARCHAR(32) | YES | NULL | 变更前角色 |
| new_role | VARCHAR(32) | YES | NULL | 变更后角色 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_naudit_shop_time` (`shop_id`, `created_at`)
- KEY: `idx_naudit_user_time` (`user_id`, `created_at`)
**外键**
- `fk_naudit_shop`: `shop_id``shops(id)`
- `fk_naudit_user`: `user_id``users(id)`
- `fk_naudit_admin`: `operator_admin_id``admins(id)`
**说明**
- 普通管理员申请、审批、撤销的完整审计日志
- 当前状态 = 按user_id取最后一条记录的action
- VIP失效触发降级时写入expire审计
---
## 三、身份与认证
### 3.1 user_identities第三方身份映射
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 用户ID |
| provider | ENUM('wechat_mp','wechat_app') | NO | | 身份提供方 |
| openid | VARCHAR(64) | NO | | 微信openid |
| unionid | VARCHAR(64) | YES | NULL | 微信unionid |
| nickname | VARCHAR(64) | YES | NULL | 昵称 |
| avatar_url | VARCHAR(512) | YES | NULL | 头像URL |
| last_login_at | DATETIME | YES | NULL | 最后登录时间 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_identity_user` (`user_id`)
- KEY: `idx_identity_shop` (`shop_id`)
- UNIQUE: `ux_identity_provider_openid` (`provider`, `openid`)
- UNIQUE: `ux_identity_unionid` (`unionid`)
**外键**
- `fk_identity_shop`: `shop_id``shops(id)`
- `fk_identity_user`: `user_id``users(id)`
**说明**
- 支持微信小程序和APP登录
- 短信登录使用users.phone作为全局唯一身份不创建identity记录
---
### 3.2 wechat_sessions微信会话
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| provider | ENUM('wechat_mp','wechat_app') | NO | | 提供方 |
| openid | VARCHAR(64) | NO | | 微信openid |
| session_key | VARCHAR(128) | NO | | 会话密钥 |
| expires_at | DATETIME | NO | | 过期时间 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_wechat_session_expires` (`expires_at`)
- UNIQUE: `ux_wechat_session` (`provider`, `openid`)
**说明**:临时存储微信会话密钥。
---
### 3.3 sms_codes短信验证码
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| phone | VARCHAR(32) | NO | | 手机号 |
| scene | VARCHAR(32) | NO | 'login' | 场景login/register/... |
| code_hash | CHAR(64) | NO | | 验证码哈希SHA-256 |
| salt | CHAR(32) | NO | | 加盐字符串 |
| expire_at | DATETIME | NO | | 过期时间 |
| status | TINYINT UNSIGNED | NO | 0 | 0=active 1=used 2=expired 3=blocked |
| fail_count | TINYINT UNSIGNED | NO | 0 | 错误次数 |
| ip | VARCHAR(45) | YES | NULL | 发送IP |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_phone_created_at` (`phone`, `created_at`)
- KEY: `idx_phone_scene_status` (`phone`, `scene`, `status`)
- KEY: `idx_expire_at` (`expire_at`)
- KEY: `idx_ip_created_at` (`ip`, `created_at`)
**说明**
- 验证码采用哈希+盐存储
- 支持多场景(登录、注册、重置密码等)
- 记录失败次数防止暴力破解
---
### 3.4 email_codes邮箱验证码
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| email | VARCHAR(128) | NO | | 邮箱 |
| scene | VARCHAR(32) | NO | | 场景login/register/reset |
| code_hash | VARCHAR(64) | NO | | 验证码哈希SHA-256 |
| salt | VARCHAR(64) | NO | | 加盐字符串 |
| expire_at | DATETIME | NO | | 过期时间 |
| status | TINYINT UNSIGNED | NO | 0 | 0=unused 1=used 2=expired |
| fail_count | INT | NO | 0 | 错误次数 |
| ip | VARCHAR(64) | YES | NULL | 发送IP |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_email_scene_created` (`email`, `scene`, `created_at`)
- KEY: `idx_email_expire` (`expire_at`)
**说明**与sms_codes类似用于邮箱验证场景。
---
## 四、咨询与公告
### 4.1 consults咨询
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 所属店铺 |
| user_id | BIGINT UNSIGNED | NO | | 提问用户 |
| topic | VARCHAR(120) | NO | | 主题 |
| message | TEXT | NO | | 咨询内容 |
| status | ENUM('open','resolved','closed') | NO | 'open' | 状态 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_consult_shop_status` (`shop_id`, `status`)
- KEY: `fk_consult_user` (`user_id`)
**外键**
- `fk_consult_shop`: `shop_id``shops(id)`
- `fk_consult_user`: `user_id``users(id)`
**说明**
- open=未解决resolved=已解决closed=关闭
- 触发器有回复时自动标记为resolved
---
### 4.2 consult_replies咨询回复
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| consult_id | BIGINT UNSIGNED | NO | | 所属咨询 |
| user_id | BIGINT UNSIGNED | NO | | 回复人(管理员) |
| content | TEXT | NO | | 回复内容 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 回复时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_cr_consult` (`consult_id`)
- KEY: `fk_cr_user` (`user_id`)
**外键**
- `fk_cr_consult`: `consult_id``consults(id)`
- `fk_cr_user`: `user_id``users(id)`
**说明**:管理员对咨询的回复记录。
---
### 4.3 notices公告
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| title | VARCHAR(120) | NO | | 标题 |
| content | VARCHAR(500) | NO | | 内容(跑马灯) |
| tag | VARCHAR(32) | YES | NULL | 标签(如"活动" |
| is_pinned | TINYINT(1) | NO | 0 | 是否置顶 |
| starts_at | DATETIME | YES | NULL | 生效开始时间 |
| ends_at | DATETIME | YES | NULL | 生效结束时间 |
| status | ENUM('draft','published','offline') | NO | 'published' | 状态 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | DATETIME | YES | NULL | 软删除标记 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_notices_time` (`starts_at`, `ends_at`)
**说明**
- 平台全局公告,与租户无关
- 前台仅显示status='published'且在有效期内的公告
- 排序is_pinned DESC, created_at DESC
---
## 五、系统配置
### 5.1 system_parameters系统参数
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID |
| user_id | BIGINT UNSIGNED | NO | | 创建/修改人 |
| key | VARCHAR(64) | NO | | 参数键 |
| value | JSON | NO | | 参数值JSON |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_sysparams_shop` (`shop_id`)
- UNIQUE: `ux_sysparams_shop_key` (`shop_id`, `key`)
**外键**
- `fk_sysparams_shop`: `shop_id``shops(id)`
- `fk_sysparams_user`: `user_id``users(id)`
**说明**
- 租户级配置支持JSON格式存储复杂配置
- 常用配置键:
- `vip.dataRetentionDaysForNonVip`: 非VIP数据保留天数默认60
- `normalAdmin.autoApprove`: 普通管理员自动审批默认false
- `normalAdmin.requiredVipActive`: 要求VIP有效默认true
---
### 5.2 finance_categories财务分类
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | NO | | 店铺ID0=全局) |
| type | ENUM('income','expense') | NO | | 收入/支出 |
| key | VARCHAR(64) | NO | | 分类key |
| label | VARCHAR(120) | NO | | 分类名称 |
| sort_order | INT | NO | 0 | 排序 |
| status | TINYINT UNSIGNED | NO | 1 | 1=启用 0=停用 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_fc_shop_type` (`shop_id`, `type`)
- UNIQUE: `ux_fc_shop_key` (`shop_id`, `key`)
**说明**
- 管理其他收入/支出的分类
- shop_id=0为平台默认分类
- 读取优先级finance_categories表 → system_parameters → application.properties
---
## 六、附件管理
### 6.1 attachments通用附件
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| shop_id | BIGINT UNSIGNED | YES | NULL | 店铺ID全局资源可空 |
| user_id | BIGINT UNSIGNED | YES | NULL | 上传人 |
| owner_type | VARCHAR(32) | NO | | 归属类型product/part_submission/... |
| owner_id | BIGINT UNSIGNED | NO | | 归属ID |
| url | VARCHAR(512) | NO | | 文件URL |
| hash | VARCHAR(64) | YES | NULL | 内容哈希SHA-256 |
| meta | JSON | YES | NULL | 元数据 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_attachments_owner` (`owner_type`, `owner_id`)
- UNIQUE: `ux_attachments_hash` (`hash`)
**外键**
- `fk_att_shop`: `shop_id``shops(id)`
- `fk_att_user`: `user_id``users(id)`
**说明**
- 通过hash实现文件去重
- 支持多种归属类型商品、配件提交、全局SKU等
- meta字段存储文件元信息大小、类型等
---
## 七、配件参数字典(扩展)
### 7.1 part_attribute_dictionary参数字典
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| attribute_name | VARCHAR(64) | NO | | 参数名称 |
| attribute_unit | VARCHAR(16) | YES | NULL | 单位 |
| attribute_type | ENUM('numeric','text','enum') | NO | 'text' | 类型 |
| enum_values | JSON | YES | NULL | 枚举值 |
| is_searchable | TINYINT(1) | NO | 1 | 是否可搜索 |
| sort_order | INT | NO | 0 | 排序 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `id`
- UNIQUE: `ux_pad_name` (`attribute_name`)
**说明**:全局参数字典,定义可用的配件参数。
---
### 7.2 part_categories配件分类
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| name | VARCHAR(64) | NO | | 分类名称 |
| parent_id | BIGINT UNSIGNED | YES | NULL | 父分类 |
| sort_order | INT | NO | 0 | 排序 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_pc_parent` (`parent_id`)
- UNIQUE: `ux_pc_name` (`name`)
**说明**:配件专用分类,支持层级结构。
---
### 7.3 part_category_attributes分类参数关联
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| category_id | BIGINT UNSIGNED | NO | | 分类ID |
| attribute_id | BIGINT UNSIGNED | NO | | 参数ID |
| is_required | TINYINT(1) | NO | 0 | 是否必填 |
| sort_order | INT | NO | 0 | 排序 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_pca_category` (`category_id`)
- UNIQUE: `ux_pca_cat_attr` (`category_id`, `attribute_id`)
**外键**
- `fk_pca_category`: `category_id``part_categories(id)` ON DELETE CASCADE
- `fk_pca_attribute`: `attribute_id``part_attribute_dictionary(id)` ON DELETE CASCADE
**说明**:定义每个配件分类应包含哪些参数。
---
### 7.4 part_attribute_templates参数模板
| 字段 | 类型 | 空值 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | BIGINT UNSIGNED | NO | AUTO_INCREMENT | 主键 |
| template_name | VARCHAR(64) | NO | | 模板名称 |
| category_id | BIGINT UNSIGNED | YES | NULL | 关联分类 |
| attributes | JSON | NO | | 参数定义JSON |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新时间 |
**索引**
- PRIMARY KEY: `id`
- KEY: `idx_pat_category` (`category_id`)
- UNIQUE: `ux_pat_name` (`template_name`)
**说明**:预定义的参数模板,快速应用到配件。
---
## 八、配置参数说明
### 8.1 VIP相关配置
| 配置键 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| vip.dataRetentionDaysForNonVip | number | 60 | 非VIP数据保留天数 |
| vip.price | number | 15 | VIP单月价格 |
| vip.durationDays | number | 30 | 开通时长(天) |
### 8.2 普通管理员配置
| 配置键 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| normalAdmin.autoApprove | boolean | false | 是否自动通过申请 |
| normalAdmin.requiredVipActive | boolean | true | 是否要求VIP有效 |
### 8.3 财务分类配置
**收入分类**income_categories
- operation_income: 经营所得
- interest_income: 利息收入
- other_income: 其它收入
- deposit_ar_income: 收订金/欠款
- investment_income: 投资收入
- sale_income: 销售收入
- account_operation: 账户操作
- fund_transfer_in: 资金转账转入
**支出分类**expense_categories
- operation_expense: 经营支出
- office_supplies: 办公用品
- rent: 房租
- interest_expense: 利息支出
- other_expense: 其它支出
- account_operation: 账户操作
- fund_transfer_out: 资金转账转出
---
## 九、数据安全与性能
### 9.1 敏感数据加密
- 密码SHA-256哈希存储password_hash
- 验证码SHA-256哈希+盐code_hash + salt
- 第三方token加密存储wechat_sessions.session_key
### 9.2 索引策略
- 高频查询字段shop_id, user_id, status, created_at
- 唯一性约束email, phone, openid, hash
- 复合索引:(shop_id, status), (user_id, created_at)
- 全文索引仅用于products.search_text
### 9.3 数据清理
**定期清理**
- sms_codes/email_codes清理7天前expired记录
- wechat_sessions清理过期会话
- inventory_movements归档6个月前数据可选
**软删除**
- 业务表采用deleted_at标记
- 查询默认过滤deleted_at IS NULL
- 定期归档软删除数据
---
## 十、扩展性设计
### 10.1 JSON字段应用
- products.attributes_json: 扩展商品属性
- part_submissions.attributes/images/tags: 灵活配件数据
- attachments.meta: 文件元信息
- system_parameters.value: 动态配置
### 10.2 ENUM类型管理
- 新增枚举值需要ALTER TABLE
- 重要状态采用ENUM确保数据一致性
- 次要分类采用VARCHAR + 配置表
### 10.3 多语言支持预留
- 公告内容可扩展为JSON{zh: "中文", en: "English"}
- 财务分类label可支持多语言
- 当前版本仅支持简体中文
---
**文档维护说明**
- 辅助表主要用于系统配置、认证、审计和扩展功能
- 任何结构变更必须同步更新本文档和`backend/db/db.sql`
- 配置参数变更需同时更新application.properties默认值
- 新增表需评估是否属于核心业务表或辅助表,放入对应文档

View File

@@ -1,127 +0,0 @@
## 模板参数可模糊查询(±容差)功能需求文档
### 1. 背景与目标
当前用户端「按模板参数查询」要求参数值与数据库完全相同才能命中,实际使用中数值类参数(如内径、外径、长度等)存在测量/录入微小误差,严格等值导致命中率偏低。新增能力:在管理端创建模板时,为每个参数提供「可模糊查询」选项;开启后,用户搜索该参数时按数值区间匹配(±容差);未开启的参数继续精确等值。
### 2. 业务范围
- 场景:用户端/管理端的商品列表查询(含「按模板参数查询」模式)。
- 对象:模板参数定义(仅限数值型参数生效)。
- 不影响:名称/品牌/型号/规格关键字搜索逻辑;非数值类型参数的等值匹配逻辑。
### 3. 术语与约束
- 模板参数类型string/number/boolean/enum/date。
- 模糊查询仅对 type=number 生效;其他类型不展示该选项或忽略配置。
- 容差tolerance对搜索入参 v匹配区间为 \[v - tolerance, v + tolerance](闭区间)。默认容差为 1见配置项可在参数层级单独覆盖。
- 组合关系:多参数为 AND 关系;每个参数根据其「可模糊查询」与容差独立计算。
### 4. 交互与流程
- 管理端-模板配置:
- 新建/编辑模板参数时,新增选项:
- 可模糊查询(开关,仅当类型为 number 显示)
- 容差值number>0显示单位提示`unit` 字段;当开关开启时必填,否则置空)
- 校验:
- type≠number 时禁止开启;
- 容差必须为正数,支持小数;
- 可保存为“使用平台默认容差”,当字段留空时后端落默认(见配置)。
- 用户端/管理端-按模板参数查询:
- 入参与现状一致:仍以 `templateId` + 多个 `param_*` 传参;
- 行为变化:
- 对应参数若开启可模糊查询:按区间 \[v - tol, v + tol] 比较;
- 否则:仍为精确等值比较。
### 5. 数据模型变更(待实施)
- 表:`part_template_params`
- 新增列:
- `fuzzy_searchable` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否允许模糊查询(仅数值型)'
- `fuzzy_tolerance` DECIMAL(18,6) NULL COMMENT '容差NULL 表示使用平台默认容差'
- 说明:
- 仅当 `type='number' AND fuzzy_searchable=1` 时才使用容差;
- 初始迁移将全部历史记录置为 `fuzzy_searchable=0, fuzzy_tolerance=NULL`,保持现有行为不变。
### 6. 配置项(后端)
- `search.fuzzy.enabled`bool默认 true是否启用模糊查询全局开关
- `search.fuzzy.defaultTolerance`decimal默认 1.0):当参数未设置 `fuzzy_tolerance` 时使用;
- 读取途径Spring 配置application.properties/yaml或环境变量。禁止在代码中硬编码数字 1。
- 仅全局配置,不支持租户级(`system_parameters`)覆盖;无需设置小数精度上限/最大容差限制。
### 7. 接口协议与兼容性
- 查询接口:`GET /api/products`(已存在)
- 入参保持不变:`templateId``param_*`
- 语义扩展(无须 `templateId` 也启用模糊):后端将基于商品行的 `template_id` 与参数定义逐行判定某个 `param_*` 是否启用 ±容差;若该参数在对应模板中未开启模糊或非数值型,则对该条件执行等值匹配。
- 模板接口:`POST /api/admin/part-templates``PUT /api/admin/part-templates/{id}`(已存在)
- 参数定义对象新增字段:
- `fuzzySearchable`boolean
- `fuzzyTolerance`numbernullable
- 若前端暂未改造,后端默认按 `fuzzySearchable=false` 处理,兼容旧请求体。
(根据「接口规范生效条件」,待功能开发完成后更新 `doc/openapi.yaml` 中对应 schema 与描述,并在 summary/description 标注实现状态)
### 8. 后端实现要点(建议方案)
当前实现(精确匹配),示意:
```sql
-- 现状(等值):
AND JSON_UNQUOTE(JSON_EXTRACT(p.attributes_json, '$.内径')) = '10'
```
推荐实现:
- 行级判定方案(支持无 `templateId` 也启用模糊):
- 对每个传入的 `param_<key>=v`
-`EXISTS` 子查询或 `JOIN part_template_params ptp ON ptp.template_id=p.template_id AND ptp.field_key='<key>'` 获取参数定义;
-`ptp.type='number' AND ptp.fuzzy_searchable=1`:对 `v` 解析为数值,计算 `tol = COALESCE(ptp.fuzzy_tolerance, :defaultTolerance)`
- 下限截断:`lower = GREATEST(0, v - tol)`
- 条件:
```sql
CAST(JSON_UNQUOTE(JSON_EXTRACT(p.attributes_json, '$.<key>')) AS DECIMAL(18,6)) BETWEEN :lower AND :upper
```
- 否则:执行等值匹配:
```sql
JSON_UNQUOTE(JSON_EXTRACT(p.attributes_json, '$.<key>')) = :val
```
- 快路径(可选):当请求携带 `templateId` 时,可先一次性加载该模板参数定义映射到内存,按映射决定每个条件构造,以减少 `JOIN/EXISTS` 次数。
- 容差取值:优先 `ptp.fuzzy_tolerance`,否则全局 `search.fuzzy.defaultTolerance`。
性能建议:
- 初期:允许全表扫描 + JSON_EXTRACT观察真实 QPS 与延迟;
- 进阶(可选):对热点参数引入“生成列 + 索引”Generated Column例如
- 在 `products` 增加 `attr_<key> DECIMAL(18,6) GENERATED ALWAYS AS (CAST(JSON_UNQUOTE(JSON_EXTRACT(attributes_json, '$.<key>')) AS DECIMAL(18,6))) STORED` 并建索引,以支持范围查询;
- 仅对访问量高的少数参数启用,避免列爆炸。
### 9. 管理端实现要点UI/校验)
- `admin/src/views/parts/Templates.vue`
- 参数编辑行新增:
- 开关:可模糊查询(仅 type=number 显示)
- 数值输入:容差(显示单位,>0支持小数留空表示使用平台默认
- 保存/加载兼容:与后端新增字段映射,历史数据默认显示为关闭态。
- 校验:当参数开启模糊时,对应值在 UI 侧仅允许数字输入;单位提示与 `unit` 一致。
### 10. 验收标准Test Cases
- 单参数-模糊:模板字段 `内径`numberfuzzy=truetolerance=1商品 A/B/C 分别取值 9/10/11搜索 `param_内径=10` 命中 A/B/C。
- 单参数-精确:同上但 fuzzy=false搜索 `param_内径=10` 仅命中 B。
- 多参数组合:`内径`fuzzy=true, tol=0.5)、`长度`fuzzy=false搜索 `param_内径=10`、`param_长度=20` 仅命中满足区间与等值的交集。
- 无 templateId也启用模糊后端逐行按 `p.template_id` 与参数定义判定是否应用容差。
- 容差来源:当 `fuzzy_tolerance=NULL` 时,生效平台默认容差;覆盖值生效优先级高于默认。
- 非数值参数:即使请求携带 `param_颜色=黑`,也严格等值。
- 下限截断:当 `v - tol < 0` 时,以 `0` 作为下限;不支持负数参数匹配。
- 非法输入:当参数在模板中开启模糊但请求值非数字时,返回 400Bad Request
### 11. 兼容与回退
- 不改动现有请求入参与返回体,历史客户端无需升级亦可按原精确逻辑使用;
- 新能力由模板参数配置显式开启,可随时在模板中关闭;
- 如需全局关闭,可通过 `search.fuzzy.enabled=false` 临时禁用(后端配置)。
### 12. 风险与注意事项
- 数据质量:历史 `attributes_json` 中数值可能以字符串存储;需统一以 `CAST(JSON_UNQUOTE(...))` 解析。
- 单位与容差UI 需提示单位;容差与单位一一对应,避免“毫米 vs 厘米”误解。
- 性能:范围查询较等值更难走索引;必要时引入“生成列+索引”优化热点字段。
- 负数与边界:不支持负数参数;区间采用闭区间,且下限截断为 `0`。
### 13. 实施清单(参考)
1) 数据库:为 `part_template_params` 增列 `fuzzy_searchable`、`fuzzy_tolerance`;(变更需通过 MysqlMCP成功后同步更新 `doc/database_documentation.md` 与 `backend/db/db.sql`
2) 配置:新增 `search.fuzzy.*` 配置项并给出默认值(全局生效,无租户级覆盖);
3) 管理端:模板参数编辑 UI 新增开关与容差输入;
4) 后端:按 8 节改造查询 SQL 构建逻辑(无 `templateId` 也启用模糊,行级按模板判定);
5) 文档:在功能开发完成后更新 `doc/openapi.yaml` 中模板参数 schema 与 `GET /api/products` 的查询规则说明并标注实现状态
6) 发布前后端同步上线无需灰度与回滚开关
7) 验收 10 节用例覆盖单测/集成测试与手工回归

View File

@@ -1,175 +0,0 @@
## 货品删除功能开发文档(软删方案)
### 1. 背景与目标
- 将“与货品相关”的删除行为统一为软删除,避免历史引用断裂,支持后续恢复与审计。
- 用户仅保留“拉黑/恢复”,订单维持“作废 void”不做删除。
### 2. 范围
- 货品主表:`products`
- 关联信息:`product_images``product_prices``inventories``product_aliases`
- 相关查询接口:商品搜索、详情、导出(如有)
### 2.1 父子级联关系(必须遵守)
- 分类(`product_categories` → 模板(`part_templates` → 商品(`products`
- 规则:
- 删除分类 ⇒ 级联软删该分类下所有模板;再级联软删由这些模板创建的所有商品;并同时软删所有 `category_id=该分类` 的商品(包括未通过模板创建的商品)。
- 删除模板 ⇒ 仅软删该模板下的商品,不影响同分类其它模板的商品。
- 订单不可删除仅允许作废void因此采用“软删”是必要前提避免历史订单断裂。
- 恢复:当前不提供任何恢复入口;如未来开放,恢复不做级联,需逐层独立恢复以避免误恢复。
### 3. 设计要点
- 软删标记:使用 `products.deleted_at DATETIME NULL`(已存在)。被软删即视为“不对外可见”。
- 恢复:当前不提供恢复入口。若未来开放,语义为将 `deleted_at=NULL`
- 查询默认过滤:所有列表/搜索默认附加 `deleted_at IS NULL`(当前搜索已实现)。
- 详情访问:若记录被软删,返回 404或通过 `includeDeleted=true` 显式读取)。
- 关联表处理:软删商品时不物理删除图片/价格/库存/别名(均按商品引用读取,详情被 404 屏蔽即可)。
- 模板软删标记统一:为 `part_templates` 引入 `deleted_at DATETIME NULL` 以统一软删标记;`status` 字段保留为启停用,不代表软删。所有查询需同时过滤 `deleted_at IS NULL AND status=1`(按需)。
- 字典与作用域:分类与单位属于 `shop_id=0` 的全局字典。删除分类会影响所有店铺下此分类的模板与商品;此操作需平台管理员权限并要求二次确认。
- 报表与搜索:默认排除软删记录;不提供“含回收站”开关。
- 数据保留与清理:支持配置项 `SOFT_DELETE_RETENTION_DAYS`(默认永久保留,仅清理无引用对象)。
- 单位删除校验:移除对已废弃 `products.unit_id` 的校验逻辑。
### 4. 数据库与索引
现状:`products` 存在唯一约束 `UNIQUE(shop_id, barcode)`。软删后可能需要“同店铺、同条码”重新建商品。
- 目标:唯一约束仅作用于“活动记录”(未软删)。
- 做法:增加生成列 `is_active` 并重建唯一索引MySQL 8
DDL上线脚本草案
```sql
-- 仅对生产环境执行一次;如已存在请跳过对应步骤
ALTER TABLE products
ADD COLUMN is_active TINYINT AS (CASE WHEN deleted_at IS NULL THEN 1 ELSE 0 END) STORED,
ADD INDEX idx_products_deleted_at (deleted_at);
-- 重建唯一索引,使其仅约束未软删记录
DROP INDEX ux_products_shop_barcode ON products; -- 若不存在请忽略
CREATE UNIQUE INDEX ux_products_shop_barcode_live ON products(shop_id, barcode, is_active);
```
风险与说明
- “条码为空”不会受唯一约束影响MySQL 对 NULL 不唯一);符合预期。
- 老数据不受影响;后续删除改为软删即可。
- 若未来需要“永久删除”,可新增仅限平台运维的强删脚本,先清理关联,再物理删除目标商品。
- 如未来开放“恢复”,当恢复商品与现存“活动记录”在 `(shop_id, barcode)` 上冲突时,恢复应返回 `409 Conflict` 并附带冲突商品信息。
模板表 DDL新增软删标记
```sql
ALTER TABLE part_templates
ADD COLUMN deleted_at DATETIME NULL,
ADD INDEX idx_part_templates_deleted_at (deleted_at);
```
### 5. 接口设计OpenAPI 约定)
说明:按规范,等后端开始开发即补充到 `/doc/openapi.yaml` 并标注实现状态;本方案不新增任何“恢复”接口。
1) 软删商品(行为不变,明确语义)
- Method/Path: `DELETE /api/products/{id}`
- 语义:软删,将 `deleted_at=NOW()`
- 返回:`200 {}`
- 鉴权:需要 `X-Shop-Id`/`X-User-Id` 或 Token且仅允许同店铺数据。
2) 商品详情(行为调整)
- Method/Path: `GET /api/products/{id}`
- 默认:若 `deleted_at IS NOT NULL` 返回 `404`
- 可选:`includeDeleted=true` 时允许读取已软删详情(仅管理端使用)。
3) 恢复接口
- 不同意新增以下恢复接口:`PUT /api/admin/dicts/categories/{id}/restore``PUT /api/admin/part-templates/{id}/restore``PUT /api/products/{id}/restore`
### 6. 后端实现说明
- Controller 改动(示意)
- `ProductController.delete(id, shopId)`:保持现有调用,内部执行软删。
- `GET /api/products/{id}`:调用 `productService.findDetail(id)` 前,先判断 `deleted_at`,若非空且未显式 `includeDeleted``404`
- Service 改动(核心)
- 移除/不提供任何恢复相关方法。
- `findDetail(id)`:若被软删且无 `includeDeleted` 参数 → 返回空 Optional。
- 模板表采用 `deleted_at` 表示软删,`status` 表示启停用;查询需同时过滤 `deleted_at IS NULL` 与必要的 `status` 条件。
#### 6.1 级联软删伪代码
```java
// 分类软删
void deleteCategorySoft(Long categoryId) {
// 1) 标记分类 deleted_at
UPDATE product_categories SET deleted_at=NOW() WHERE id=? AND deleted_at IS NULL;
// 2) 级联模板软删(统一使用 deleted_at
UPDATE part_templates SET deleted_at=NOW() WHERE category_id=? AND deleted_at IS NULL;
// 3) 级联商品软删:模板创建的商品 + 直接挂在分类下的商品
UPDATE products SET deleted_at=NOW() WHERE (
template_id IN (SELECT id FROM part_templates WHERE category_id=?)
OR category_id=?
) AND deleted_at IS NULL;
}
// 模板软删(不波及其它模板)
void deleteTemplateSoft(Long templateId) {
// 1) 模板标记为软删
UPDATE part_templates SET deleted_at=NOW() WHERE id=? AND deleted_at IS NULL;
// 2) 级联商品软删(仅该模板下)
UPDATE products SET deleted_at=NOW() WHERE template_id=? AND deleted_at IS NULL;
}
```
### 7. 前端改动
- 列表页:保持不显示软删项(现已过滤)。
- 详情页:若接口返回 404提示“已被删除或无权限”。
- 管理端:不提供“回收站/恢复”入口;删除按钮提示:该操作为软删除,对前台不可见,当前无恢复入口。
### 8. 权限与审计
- 鉴权:沿用现有用户/店铺头部识别;仅同店铺商品可操作。
- 权限边界:
- 普通用户:仅可删除本用户的货品;无权删除模板与分类;无恢复权限。
- 店铺管理员:仅有审核功能;无删除模板/分类与恢复权限。
- 平台管理员:可删除货品、模板、分类;删除全局分类需二次确认;无恢复权限。
- 审计:不记录操作日志(操作者、时间、来源 IP、对象 ID 与名称),以简化开发。
### 9. 测试用例
- 删除后搜索不可见;`GET /api/products/{id}` 返回 404。
- 条码唯一:软删后允许同店铺同条码新建。
- (如未来开放恢复)恢复时如与现有活动记录冲突,返回 409 并附带冲突商品信息。
### 10. 发布与回滚
- 发布顺序:
1) 执行数据库 DDL生成列与索引
2) 上线后端(调整 detail 行为,移除/不提供恢复逻辑)。
3) 上线前端(不提供回收站/恢复入口)。
- 回滚:
- 后端回滚到旧版本DDL 不需要回退(生成列与新索引向前兼容)。
### 11. FAQ / 风险
- 问:软删后图片与价格是否清理?
- 答:不清理,保持数据可恢复;若永久删除再统一清理关联。
- 问:库存与统计是否包含软删商品?
- 答:常规统计应排除软删;如需包含,增加显式参数。
- 问:条码冲突如何处理?
- 答:按“活动记录”唯一;如未来开放恢复,发现冲突则返回 409并指明冲突商品。
- 问:字典(分类/单位)是否为全局维度?删除是否影响所有店铺?
- 答:是,`shop_id=0` 全局字典;删除全局分类会影响所有店铺下该分类的模板与商品,需平台管理员二次确认。
- 问:是否保留“强删”入口?
- 答:保留仅限平台运维的强删入口(默认关闭)。分类/模板强删前需校验无订单关联商品后再执行。
- 问:为何不做物理删除?
- 答:订单/流水等历史记录必须可追溯;物理删除会破坏外键与统计。软删能满足“前台不可见、后台可恢复”的业务诉求。
### 12. 任务拆解(实施)
- 后端:
- [ ] `GET /api/products/{id}` 软删返回 404 / 支持 `includeDeleted`
- [ ] 分类删除级联扩展:同时软删 `category_id=该分类` 的商品(含未走模板创建)
- [ ] 模板表引入 `deleted_at`;查询同时过滤 `deleted_at IS NULL` 与必要的 `status`
- [ ] 移除“单位删除校验检查 products.unit_id”的逻辑
- 数据库:
- [ ]`products` 增加 `is_active` 与唯一索引(见 DDL
- [ ]`part_templates` 增加 `deleted_at` 与索引
- 前端管理端:
- [ ] 删除按钮文案更新(软删除,对前台不可见,当前无恢复入口)
- [ ] 不提供“回收站/恢复”入口
(本文件为技术方案与实施指引,变更上线后请同步 `/doc/openapi.yaml``/doc/database_documentation.md`

793
doc/项目开发文档.md Normal file
View File

@@ -0,0 +1,793 @@
# 配件查询系统 - 项目开发文档
**项目名称**配件查询App
**版本**v1.0
**更新时间**2025-10-01
**技术栈**Spring Boot 3 + MySQL 8.0 + Vue 3 + uni-app
---
## 一、项目概述
### 1.1 项目定位
面向小微商户的移动端进销存管理应用,核心功能包括:
- 商品管理(库存、价格、类别)
- 进销存业务(销售开单、进货开单、退货处理)
- 客户与供应商管理
- 财务管理(账户、收支、报表)
- **配件查询**:支持配件参数化查询、用户提交、审核上架
### 1.2 系统架构
```
前端层:
├── 移动端uni-app: 用户端App + 微信小程序
├── 管理端Vue3 + Element Plus: 平台管理后台
└── 普通管理端Vue3 + Element Plus: 精简审核后台
后端层:
├── Spring Boot 3.xRESTful API服务
├── MySQL 8.0:数据持久化
├── JWT用户认证
└── Python FastAPI可选条码识别服务
部署层:
├── 数据库mysql.tonaspace.com:3306
├── 后端Java应用服务器
└── 前端:静态资源 + CDN
```
### 1.3 多租户模型
- **租户隔离**所有业务数据必须关联shop_id严格按租户隔离
- **全局字典**类别、单位使用shop_id=0作为平台共享字典
- **权限体系**
- 平台管理员admins表跨租户管理
- 店主users.is_owner=1店铺所有权限
- 员工users.role=staff基础操作权限
- 普通管理员users.role=normal_admin配件审核权限
---
## 二、核心功能需求
### 2.1 用户端功能移动App + 小程序)
#### 2.1.1 首页Dashboard
-**数据概览**:今日/本月销售额、本月利润、库存数量
-**快捷入口**客户管理、销售开单、账户管理、供应商管理、进货开单、其他支出、VIP会员、报表统计
- ⚠️ **在线客服**:咨询入口已实现,悬浮按钮待添加
#### 2.1.2 货品管理
**列表功能**
- ✅ 按类别筛选、关键字搜索(名称/条码/别名)
- ✅ 显示库存数量、零库存提示
- ✅ 总货品种类统计
**新增/编辑**
- ✅ 商品图片上传(多图、排序、预览)
- ✅ 扫描条形码App支持小程序不支持
- ✅ 必填项:商品名称
- ✅ 可选项:类别、品牌、型号、规格、产地、条码、描述
- ✅ 价格:进货价、零售价、批发价、大客户价(四列)
- ✅ 库存:当前库存、安全库存上下限
- ✅ 支持模板化参数录入
**货品设置**
- ✅ 类别管理(读取全局字典)
- ✅ 单位管理(读取全局字典)
- ⚠️ 隐藏零库存商品、隐藏进货价:前端功能待实现
#### 2.1.3 配件查询与提交
-**配件搜索**:多参数组合查询、模糊匹配、分页展示
-**配件提交**
- 型号唯一校验
- 多图上传最多9张
- 参数JSON录入
- 备注、安全库存
- 提交后进入待审核状态
-**提交记录**查看pending/approved/rejected状态
-**提交详情**:查看审核结果、驳回原因
#### 2.1.4 开单模块
**销售单**
- ✅ 出货单:选择客户、添加商品、计算合计、收款
- ✅ 退货单:客户退货处理
- ✅ 收款单:后续收款记录
**进货单**
- ✅ 进货开单:选择供应商、添加商品、付款
- ✅ 进货退货:向供应商退货
**其他收支**
- ✅ 其他收入:分类、往来单位、结算账户、备注
- ✅ 其他支出:分类、往来单位、结算账户、备注
- ✅ 财务分类动态配置
#### 2.1.5 明细查询
- ✅ 时间维度筛选:自定义、本周、今日、本月、本年
- ✅ 业务类型筛选:销售、进货、收银、资金、盘点
- ✅ 关键字搜索:单据号、客户/供应商名、品名、备注
- ✅ 总金额统计
- ✅ 快速新建单据
#### 2.1.6 报表统计
**资金报表**
- ✅ 利润统计:按时间分析收入/支出/利润
- ✅ 销售报表:按客户或按货品维度聚合
- ⚠️ 营业员统计:待实现
- ⚠️ 导入导出:待实现
**进销存报表**
- ✅ 销售统计:按商品/客户/时间维度
- ✅ 进货统计:按商品/供应商/时间维度
- ✅ 库存统计:当前库存、成本、分布
- ⚠️ 应收应付对账单:待实现
#### 2.1.7 我的(用户中心)
**个人信息**
- ✅ 查看头像、姓名、手机号、邮箱
- ✅ 修改头像、姓名
- ✅ 修改密码
**VIP会员**
- ✅ VIP状态查询is_vip、expire_at、status
- ✅ 一键开通VIP点击即开通临时方案
- ✅ 充值记录查询
- ✅ 数据可见性:
- VIP用户查看全部历史数据
- 普通用户仅显示最近60天数据可配置
**基础管理**
- ✅ 客户管理:新增、编辑、查询、默认价格等级
- ✅ 供应商管理:新增、编辑、查询、欠款管理
**登录与注册**
- ✅ 手机号注册/登录
- ✅ 邮箱+密码登录
- ✅ 邮箱验证码注册
- ✅ 忘记密码(邮箱验证码重置)
- ⚠️ 短信验证码登录:后端已实现,前端待接入
- ⚠️ 微信登录:预留接口,待接入
**账号与安全**
- ✅ 修改个人信息
- ✅ 修改登录密码
- ⚠️ 账号注销、退出登录:待实现
---
### 2.2 管理端功能(平台管理后台)
#### 2.2.1 用户管理
- ✅ 用户列表按shop_id/关键字/分页查询
- ✅ 编辑用户:姓名、手机、角色、状态
- ✅ 拉黑/解除拉黑置用户status=0/1
#### 2.2.2 用户配件管理
- ✅ 配件列表按shop_id/关键字查询,显示模板信息
- ✅ 编辑配件:品牌、型号、规格、图片
- ✅ 配件恢复:取消软删除
#### 2.2.3 配件审核
- ✅ 提交列表:按状态/关键字/时间/店铺筛选
- ✅ 审核详情:查看完整信息、图片预览
- ✅ 编辑提交:修改名称、参数、图片、备注
- ✅ 审核通过生成products记录、关联图片、记录审核人
- ✅ 审核驳回:记录驳回原因、审核人、审核时间
- ✅ Excel导出按筛选条件导出限2000条
#### 2.2.4 配件模板管理
- ✅ 模板列表:查看所有模板
- ✅ 模板详情:查看参数定义
- ⚠️ 创建模板:后端接口已实现,前端待接入
- ⚠️ 更新模板:后端接口已实现,前端待接入
- ✅ 删除模板软删除status=0或强制删除
#### 2.2.5 VIP管理
- ✅ 会员列表:按手机号/分页查询
- ✅ 价格设置:读取/修改vip_price表
- ✅ 价格配置接口:`GET/PUT /api/admin/vip/price`
- ⚠️ 新增会员:后端接口已实现,前端待接入
- ⚠️ 更新会员:后端接口已实现,前端待接入
#### 2.2.6 公告管理
- ✅ 公告列表:按状态/关键字查询
- ✅ 创建公告:标题、内容、标签、有效期、状态
- ⚠️ 编辑公告:后端接口已实现,前端待接入
- ⚠️ 发布/下线:后端接口已实现,前端待接入
#### 2.2.7 咨询回复
- ✅ 咨询列表按shop_id/状态/关键字查询
- ✅ 回复咨询:单次回复并自动标记已解决
- ✅ 查看历史:查看用户历史咨询与回复
#### 2.2.8 主数据字典
- ✅ 主单位维护新增、编辑、删除shop_id=0
- ✅ 主类别维护新增、编辑、删除shop_id=0
#### 2.2.9 普通管理员审批
- ⚠️ 申请列表:查看待审核申请
- ⚠️ 审批通过赋予normal_admin权限
- ⚠️ 审批驳回:记录驳回原因
- ⚠️ 撤销权限移除normal_admin权限
#### 2.2.10 管理员登录
- ⚠️ 登录接口后端已实现JWT签发前端待接入
- 当前方案本地存储写入ADMIN_ID临时方案
---
### 2.3 普通管理端功能Admin-Lite
#### 2.3.1 定位
- 面向VIP用户申请成为普通管理员后使用
- 仅包含"配件审核"与"我的"两个功能
- 使用用户端账号(邮箱+密码)登录
#### 2.3.2 配件审核(复用平台管理端逻辑)
- ⚠️ 提交列表:限定为本店数据
- ⚠️ 审核详情:查看、编辑、图片管理
- ⚠️ 通过/驳回:操作权限限定本店
- ⚠️ 导出(可选):按配置开放
#### 2.3.3 我的
- ⚠️ 账户信息展示VIP状态、普通管理员状态
- ⚠️ 申请入口未获批VIP显示"申请成为普通管理员"按钮
- ⚠️ 退出登录
#### 2.3.4 权限规则
- 申请资格必须为VIP用户可配置是否要求有效期内
- 审批策略:
- 方案A当前采用平台审核
- 方案B自动通过通过配置normalAdmin.autoApprove控制
- 有效性约束普通管理员权限与VIP状态绑定可配置
- 范围隔离仅可访问所属shop_id的数据
---
## 三、技术实现
### 3.1 后端技术栈
**框架与依赖**
- Spring Boot 3.x
- Spring Data JPA
- Spring SecurityJWT
- MySQL Connector
- Apache POIExcel导出
- Java Mail邮件验证码
**项目结构**
```
src/main/java/com/example/demo/
├── account/ # 账户管理
├── admin/ # 管理端控制器
├── attachment/ # 附件服务
├── auth/ # 认证服务JWT、邮箱、短信、密码
├── barcode/ # 条码识别代理
├── common/ # 公共组件(拦截器、异常处理、配置)
├── consult/ # 咨询管理
├── customer/ # 客户管理
├── dashboard/ # 首页概览
├── notice/ # 公告管理
├── order/ # 订单管理
├── product/ # 商品管理(含配件提交、模板)
├── report/ # 报表服务
├── supplier/ # 供应商管理
├── user/ # 用户管理
└── vip/ # VIP管理
```
**配置管理**application.properties
- 数据库连接:通过环境变量`DB_URL/DB_USER/DB_PASSWORD`注入
- JWT配置`jwt.secret``jwt.expiresIn`
- 邮件SMTP`spring.mail.*`
- 附件存储:`attachments.upload.storage-dir`
- VIP配置`vip.dataRetentionDaysForNonVip`默认60天
- 普通管理员:`normalAdmin.autoApprove`默认false
- ⚠️ 禁止硬编码:所有配置值必须通过环境变量或配置文件注入
**认证与鉴权**
- JWT Token用户登录后签发包含userId、shopId、role
- AdminAuthInterceptor平台管理端鉴权支持Bearer Token和X-Admin-Id头
- NormalAdminAuthInterceptor普通管理端鉴权校验role=normal_admin且VIP有效
---
### 3.2 前端技术栈
#### 3.2.1 移动端uni-app
**技术选型**
- HBuilderX
- Vue 2
- uni-ui组件库
**目录结构**
```
frontend/
├── pages/ # 页面
│ ├── auth/ # 登录注册
│ ├── index/ # 首页
│ ├── product/ # 货品管理(含配件提交)
│ ├── order/ # 开单
│ ├── detail/ # 明细
│ ├── my/ # 我的
│ ├── customer/ # 客户
│ ├── supplier/ # 供应商
│ ├── account/ # 账户
│ └── report/ # 报表
├── components/ # 组件
│ ├── tabs/ # Tab组件
│ ├── layout/ # 布局组件
│ └── ImageUploader/ # 图片上传
└── common/ # 公共文件
├── config.js # 配置API_BASE_URL、VIP_PRICE等
├── http.js # HTTP封装
├── constants.js # 常量
└── navigation.js # 导航
```
**配置管理**common/config.js
- API_BASE_URL优先级环境变量 > 本地存储 > 默认值
- SHOP_ID店铺ID配置
- ENABLE_DEFAULT_USER开发模式开关
- VIP_PRICE_PER_MONTHVIP价格默认15元
- ⚠️ 所有配置禁止硬编码,必须支持环境变量覆盖
#### 3.2.2 管理端Vue 3 + Element Plus
**技术选型**
- Vue 3 + Vite
- Element Plus
- Axios
**目录结构**
```
admin/
├── src/
│ ├── views/ # 页面
│ │ ├── admin/ # 用户管理
│ │ ├── card/ # 卡片
│ │ ├── consult/ # 咨询
│ │ ├── dict/ # 字典
│ │ ├── normal-admin/ # 普通管理员审批
│ │ ├── notice/ # 公告
│ │ ├── parts/ # 配件管理含审核Submissions.vue
│ │ ├── supplier/ # 供应商
│ │ ├── users/ # 用户
│ │ └── vip/ # VIP管理
│ ├── api/ # API封装
│ ├── router/ # 路由
│ └── styles/ # 样式
└── vite.config.ts # Vite配置
```
**HTTP配置**src/api/http.ts
- 自动注入X-Shop-Id、X-Admin-Id、X-User-Id头
- 优先级localStorage > 环境变量 > 默认值
#### 3.2.3 普通管理端Vue 3 + Element Plus
**项目位置**`normal-admin/`
**功能**
- 复制admin项目结构精简为配件审核+我的两个模块
- 登录沿用用户端认证接口
- 鉴权要求role=normal_admin且VIP有效
⚠️ **当前状态**:项目框架已建立,核心功能待实现
---
### 3.3 数据库设计
**核心设计原则**
1. 多租户隔离所有业务表包含shop_id
2. 软删除使用deleted_at标记查询时过滤
3. 审计追踪:记录创建人、修改人、时间戳
4. 外键约束:合理使用,避免过度影响性能
5. 索引优化:高频查询字段建立复合索引
**表分类**
- 核心业务表30个见《数据库设计文档-核心业务表.md》
- 辅助配置表16个见《数据库设计文档-辅助配置表.md》
**重要约束**
- products.barcode(shop_id, barcode)唯一
- part_submissions.model_unique全局唯一
- users.phone/email全局唯一
- accounts.name(shop_id, name)唯一
- orders.order_no(shop_id, order_no)唯一
---
### 3.4 接口设计
**接口文档**`doc/openapi.yaml`
**实现状态标注**
- ✅ Fully Implemented前后端均已实现并联调通过
- ❌ Partially Implemented仅一方实现或未完全联调
**核心接口组**
1. 认证:`/api/auth/*`
2. 用户端:`/api/user/*``/api/products/*``/api/orders/*``/api/vip/*`
3. 平台管理端:`/api/admin/*`
4. 普通管理端:`/api/normal-admin/*`
5. 公共:`/api/notices``/api/attachments``/api/finance/categories`
**Header约定**
- X-Shop-Id店铺ID默认1
- X-User-Id用户ID用户端接口必传
- X-Admin-Id管理员ID管理端接口必传
- AuthorizationBearer TokenJWT方案逐步迁移
---
## 四、开发状态
### 4.1 已完成功能
**后端**
- ✅ 用户认证体系邮箱、密码、JWT
- ✅ 商品管理CRUD、价格、库存、图片
- ✅ 配件提交与审核(用户提交、管理员审核、导出)
- ✅ 配件模板管理CRUD、参数定义
- ✅ 客户与供应商管理
- ✅ 订单管理(销售、进货、退货、付款)
- ✅ 财务管理(账户、其他收支、分类配置)
- ✅ 库存流水记录
- ✅ VIP状态查询与一键开通
- ✅ VIP充值记录查询
- ✅ 公告管理CRUD、发布、下线
- ✅ 咨询与回复
- ✅ 主数据字典维护
- ✅ 附件上传与校验
- ✅ 首页概览统计
- ✅ 销售报表(按客户/货品维度)
- ✅ 条码识别代理
**移动端uni-app**
- ✅ 登录注册(邮箱密码、邮箱验证码)
- ✅ 首页概览
- ✅ 商品列表、详情、新增、编辑
- ✅ 配件提交、列表、详情
- ✅ 开单(销售、进货、退货、其他收支)
- ✅ 客户与供应商管理
- ✅ VIP状态查询与开通
- ✅ 用户信息查看与修改
**管理端Vue3**
- ✅ 用户管理(列表、编辑、拉黑)
- ✅ 用户配件管理(列表、编辑、恢复)
- ✅ 配件审核(列表、详情、通过、驳回、导出)
- ✅ 模板列表、详情、删除
- ✅ VIP列表、价格设置
- ✅ 公告列表、创建
- ✅ 咨询列表、回复、历史
- ✅ 主数据字典维护
---
### 4.2 待完成功能
**高优先级**
- ⚠️ 管理员登录页(前端)
- ⚠️ 普通管理员审批流程(前后端)
- ⚠️ 普通管理端核心功能(前端)
- ⚠️ 短信验证码登录(前端接入)
- ⚠️ 数据库脚本同步backend/db/db.sql缺少多个表
**中优先级**
- ⚠️ 公告编辑、发布、下线(前端)
- ⚠️ VIP新增、更新前端
- ⚠️ 配件模板创建、更新(前端)
- ⚠️ 账号注销、退出登录
- ⚠️ 货品设置(隐藏零库存、隐藏进货价)
- ⚠️ 悬浮客服入口
- ⚠️ 应收应付对账单
- ⚠️ 营业员统计
**低优先级**
- ⚠️ 微信登录(第三方认证)
- ⚠️ 数据导入导出
- ⚠️ 多语言支持
- ⚠️ 公告富文本编辑
- ⚠️ 操作日志可视化
---
### 4.3 已知问题
#### 4.3.1 严重问题
1. **数据库脚本不一致**
- backend/db/db.sql缺少admins、vip_users、vip_price、vip_recharges、sms_codes、email_codes、normal_admin_audits、consults、consult_replies、notices等表
- 新环境部署时无法初始化
- **建议**立即同步db.sql与线上数据库结构
2. **密码默认值泄露**
- application.properties第14行硬编码数据库密码默认值
- **建议**:移除默认值,强制要求环境变量
3. **unitId字段不一致**
- Product实体无unitId字段已移除
- ProductDtos仍保留unitId
- OpenAPI文档中ProductDetail包含unitId
- **建议**统一移除所有unitId引用
#### 4.3.2 中等问题
1. **身份验证混乱**
- 后端支持JWT和X-Admin-Id两种方式
- 前端仍使用X-Admin-Id
- **建议**统一迁移到JWT Bearer Token
2. **OpenAPI状态不准确**
- 多个已实现接口仍标记为"Partially Implemented"
- **建议**:逐一验证并更新状态
3. **配置硬编码**
- 前端存在多处硬编码fallback值http://127.0.0.1:8080
- **建议**:通过配置文件统一管理
---
## 五、部署与运维
### 5.1 环境要求
**生产环境**
- JDK 17+
- MySQL 8.0+
- Node.js 16+(前端构建)
- Python 3.8+(可选,条码识别服务)
**开发环境**
- IntelliJ IDEA / VS Code
- HBuilderXuni-app开发
- Maven 3.8+
- Git
---
### 5.2 环境变量配置
**后端必需**
```bash
# 数据库
DB_URL=jdbc:mysql://mysql.tonaspace.com:3306/partsinquiry
DB_USER=root
DB_PASSWORD=<实际密码>
# JWT
JWT_SECRET=<随机生成的密钥>
JWT_EXPIRES_IN=86400
# 邮件(如需邮箱验证码)
MAIL_HOST=smtp.qq.com
MAIL_PORT=465
MAIL_USERNAME=<邮箱>
MAIL_PASSWORD=<授权码>
MAIL_FROM=<发件邮箱>
# 附件存储
ATTACHMENTS_DIR=./data/attachments
ATTACHMENTS_PLACEHOLDER_IMAGE=<占位图路径>
# VIP配置
VIP_NONVIP_RETENTION_DAYS=60
```
**前端必需**
```bash
# 移动端uni-app
VITE_APP_API_BASE_URL=https://api.example.com
VITE_APP_SHOP_ID=1
VITE_APP_VIP_PRICE=15
# 管理端Vue3
VITE_APP_API_BASE_URL=https://api.example.com
VITE_APP_TITLE=配件查询管理端
```
---
### 5.3 部署流程
**后端部署**
```bash
# 1. 打包
mvn clean package -DskipTests
# 2. 上传jar包
scp target/demo-0.0.1-SNAPSHOT.jar user@server:/app/
# 3. 启动服务
java -jar demo-0.0.1-SNAPSHOT.jar \
--spring.profiles.active=prod \
--server.port=8080
```
**前端部署**
```bash
# 移动端uni-app
# HBuilderX: 发行 → 原生App-云打包 / 小程序-微信
# 管理端Vue3
cd admin
npm run build
# 将dist目录上传到Nginx/CDN
```
**数据库初始化**
```bash
# 首次部署执行
mysql -h mysql.tonaspace.com -u root -p partsinquiry < backend/db/db.sql
# ⚠️ 注意当前db.sql不完整需先同步结构
```
---
### 5.4 监控与日志
**日志配置**
- 路径:`./logs/application.log`
- 级别生产环境INFO开发环境DEBUG
- 轮转按日期分割保留30天
**监控指标**
- 应用健康检查:`/actuator/health`
- JVM内存通过Spring Boot Actuator暴露
- 数据库连接池Hikari监控
- 接口响应时间Logback日志记录
**告警规则**
- 数据库连接失败
- 磁盘空间<10%
- 内存使用>90%
- 接口5xx错误率>1%
---
## 六、开发规范
### 6.1 代码规范
**后端Java**
- 遵循阿里巴巴Java开发手册
- 统一使用Lombok简化代码
- 异常处理GlobalExceptionHandler统一捕获
- 日志使用SLF4J + Logback
- 事务:@Transactional注解只读操作标记readOnly=true
**前端JavaScript/TypeScript**
- ESLint规则推荐配置
- 组件命名PascalCase
- 变量命名camelCase
- 常量命名UPPER_SNAKE_CASE
---
### 6.2 Git工作流
**分支策略**
- main生产环境
- develop开发环境
- feature/*:功能分支
- hotfix/*:紧急修复
**提交规范**
```
feat: 新功能
fix: 修复bug
docs: 文档更新
style: 代码格式调整
refactor: 重构
test: 测试相关
chore: 构建/工具配置
```
**禁止操作**
- ❌ 直接push到main分支
- ❌ 提交时跳过hooks--no-verify
- ❌ force push到main/master
- ❌ 提交大文件(>10MB
- ❌ 提交敏感信息(密码、密钥)
---
### 6.3 数据库变更流程
1. **本地测试**在开发环境验证DDL
2. **更新文档**:同步修改`doc/数据库设计文档-*.md`
3. **更新脚本**:同步修改`backend/db/db.sql`
4. **执行变更**通过MysqlMCP执行线上变更
5. **验证结果**查询information_schema确认结构
6. **更新代码**修改Entity、DTO、Service
7. **更新接口**:修改`doc/openapi.yaml`
⚠️ **禁止直接在生产环境手动执行DDL**
---
## 七、测试策略
### 7.1 单元测试
**覆盖率要求**
- Service层>80%
- Controller层>60%
- Util工具类>90%
**测试框架**
- JUnit 5
- Mockito
- Spring Boot Test
### 7.2 集成测试
**关键场景**
- 配件提交 → 审核 → 生成商品
- 销售开单 → 库存扣减 → 收款
- VIP开通 → 数据可见性变化
- 普通管理员申请 → 审批 → 权限生效
### 7.3 性能测试
**压测指标**
- 商品列表查询:<200ms1000并发
- 订单创建<500ms100并发
- 配件搜索<300ms支持全文检索
**工具**
- JMeter
- Apache Bench
---
## 八、后续规划
### 8.1 短期1-2月
1. 完成普通管理员审批流程
2. 前端统一迁移到JWT认证
3. 同步数据库脚本db.sql
4. 补充缺失的前端功能公告编辑VIP管理等
5. 完善单元测试覆盖率
### 8.2 中期3-6月
1. 微信登录集成
2. 数据导入导出功能
3. 高级报表利润分析库龄分析
4. 消息通知系统审核结果VIP到期提醒
5. 多语言支持国际化
### 8.3 长期6月+
1. 移动端原生性能优化
2. 大数据量优化分库分表
3. 多角色权限体系RBAC
4. 第三方支付集成微信支付支付宝
5. 数据分析与BI看板
6. API开放平台OAuth2.0
---
## 九、联系方式
**技术支持**
- 项目地址C:\Users\21826\Desktop\wj\PartsInquiry
- 数据库mysql.tonaspace.com:3306
- 文档路径doc/
**文档维护**
- 数据库变更后必须更新数据库设计文档
- 接口变更后必须更新openapi.yaml
- 新功能上线后必须更新本开发文档
---
**文档修订历史**
- 2025-10-01初始版本整合现有需求与开发状态