2
This commit is contained in:
@@ -1,59 +1,28 @@
|
||||
# 管理端开发文档
|
||||
## 管理端开发说明(当前实现)
|
||||
|
||||
本文档用于同步当前管理端(admin/)前端与数据库的开发状态与下一步计划。
|
||||
### 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()` 处理相对路径。
|
||||
|
||||
## 一、前端(admin/)
|
||||
- 技术栈:Vue3 + Vite + Element Plus
|
||||
- 主题:深色尊贵风格,数据展示面板/卡片/表格采用深蓝背景;Tag 颜色整体降亮。
|
||||
- 路由与页面:
|
||||
- `/vip-review`:VIP审核(默认页,卡片式,一卡一申请)
|
||||
- `/vip`:VIP管理(是否VIP、到期时间、启用/停用)
|
||||
- `/users`:用户管理(拉黑/恢复,状态文案“正常/黑名单”)
|
||||
- `/parts`:用户配件管理(拉黑/恢复)
|
||||
- `/consult`:咨询回复(回复/标记解决,标记后行内即时显示“已解决”)
|
||||
- Mock 数据:
|
||||
- 开启:`VITE_USE_MOCK=true`;使用 `public/mock/*.json`
|
||||
- 覆盖:`admin_vips.json`、`admin_vips_reviews.json`、`admin_users.json`、`admin_parts.json`、`admin_consults.json`
|
||||
- 状态:
|
||||
- 页面与交互:已完成
|
||||
- 主题色与降亮:已完成
|
||||
- Mock:已完成
|
||||
### 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,可在本地存储或环境变量覆盖),以匹配租户范围。
|
||||
|
||||
## 二、数据库(DB)
|
||||
- 已有与复用
|
||||
- `users.status`:1=正常、0=黑名单(供用户管理拉黑/恢复)
|
||||
- `global_skus`:公共SKU库(供未来配件审核通过后发布使用)
|
||||
- 新增表
|
||||
- `vip_users`:管理VIP(is_vip/status/expire_at/reviewer_id/reviewed_at/remark)
|
||||
- `consults`、`consult_replies`:咨询与回复
|
||||
- 字段与索引改动
|
||||
- `products.is_blacklisted TINYINT(1) NOT NULL DEFAULT 0`(黑名单标记)
|
||||
- 索引:`idx_products_shop_blacklist (shop_id, is_blacklisted)`
|
||||
- 文档:`doc/database_documentation.md` 已同步
|
||||
### 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 去重)。
|
||||
|
||||
## 三、API 规范(OpenAPI)
|
||||
- 已补充
|
||||
- 管理端VIP:`/api/admin/vips` 列表/创建/更新/审核通过/驳回(标注“❌ Partially Implemented”)
|
||||
- 待补充
|
||||
- 管理端用户:列表(kw)、更新(status 拉黑/恢复)
|
||||
- 管理端用户配件:列表(kw/status)、黑名单/恢复(更新 `products.is_blacklisted`)
|
||||
- 管理端咨询:列表(status/kw)、回复、标记解决
|
||||
|
||||
## 四、落地与联调建议
|
||||
- 后端接口:按上述待补充接口实现,完成后将 OpenAPI 的相应条目更新为“✅ Fully Implemented”。
|
||||
- 前台/小程序:默认过滤 `products.is_blacklisted=0`,防止黑名单配件外显。
|
||||
- 安全收口:后端增加 JWT 校验,从 Token 注入 `shopId/userId`,避免完全信任请求头。
|
||||
|
||||
## 五、环境与运行
|
||||
- 开发:
|
||||
```powershell
|
||||
cd admin; npm i; $env:VITE_APP_API_BASE_URL="http://127.0.0.1:8080"; $env:VITE_APP_SHOP_ID="1"; npm run dev
|
||||
```
|
||||
- Mock:
|
||||
```powershell
|
||||
cd admin; npm i; $env:VITE_USE_MOCK="true"; npm run dev
|
||||
```
|
||||
- 构建/预览:
|
||||
```powershell
|
||||
npm run build; npm run preview
|
||||
```
|
||||
### 4. 条码识别接入
|
||||
- 管理端不提供扫码入口,仅用户端调用 `/api/barcode/scan`。
|
||||
- 后端代理服务会将图片转发至 Python TXM(FastAPI)并返回首个匹配条码,需保持 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。
|
||||
|
||||
@@ -1,59 +1,26 @@
|
||||
# 管理端需求说明(配件管家)
|
||||
# 管理端需求文档
|
||||
|
||||
本文件描述管理端前端与后端的需求要点,遵循“默认同意,管理端可拉黑/恢复”的运营策略;所有可配置值放置在配置或数据库,不允许硬编码。
|
||||
## 1. 页面结构
|
||||
- **VIP 系统**:含价格配置、充值记录与会员列表三块;价格修改需立即生效并同步到前端 `VIP_PRICE` 显示;列表支持手机号模糊检索。
|
||||
- **公告管理**:支持公告的新增、编辑、发布、下线、置顶,字段包括标题、内容、标签、有效期、生效状态。
|
||||
- **咨询回复**:列出店铺咨询,管理员可单次回复并标记已解决。
|
||||
- **用户管理**:展示用户基本信息,支持编辑、启停(黑名单)。
|
||||
- **用户配件管理**:适配用户提交的配件数据,允许管理员编辑品牌/型号/规格与图片链接。
|
||||
- **供应商管理**:列表检索、创建、编辑供应商信息,含欠款字段展示。
|
||||
- **主数据字典**:主单位、主类别维护入口,仅平台管理员可用。
|
||||
|
||||
## 1. 运营策略
|
||||
- 用户与用户配件(投稿/商品)默认允许展示与使用。
|
||||
- 管理端具备拉黑与恢复能力:
|
||||
- 拉黑后:对应对象在前台/小程序/客户侧不可见或被限制。
|
||||
- 恢复后:恢复正常可用状态。
|
||||
## 2. 认证约束
|
||||
- 所有接口通过 `AdminAuthInterceptor` 鉴权:优先 Bearer Token,其次 `X-Admin-Id`/`X-User-Id`。
|
||||
- 请求必须携带 `X-Shop-Id`,缺省取 1;多租户数据严格隔离。
|
||||
- 登录功能正在开发中,当前临时通过本地存储写入管理员 ID。
|
||||
|
||||
## 2. 数据模型
|
||||
### 2.1 用户黑名单
|
||||
- 复用 `users.status` 字段:`1=正常`,`0=黑名单`。
|
||||
- 管理端动作:
|
||||
- 拉黑用户 → `PUT /api/admin/users/{id}` body `{ status: 0 }`
|
||||
- 恢复用户 → `PUT /api/admin/users/{id}` body `{ status: 1 }`
|
||||
## 3. 功能约束
|
||||
- 禁止硬编码配置值,价格、库存等需从后端接口读取。
|
||||
- 上传图片统一调用 `/api/attachments`,返回的 `url` 需落库或直接引用。
|
||||
- 所有列表接口支持分页(默认 `page=1&size=20`);前端需预留分页扩展点。
|
||||
- 删除功能未启用,均以启停或逻辑状态位代替。
|
||||
|
||||
### 2.2 用户配件黑名单
|
||||
- 建议在承载表中增加状态位供管理端控制:
|
||||
- 若使用 `products` 承载:新增 `products.is_blacklisted TINYINT(1) NOT NULL DEFAULT 0`(黑名单标记)。
|
||||
- 若使用专表(如未来 `part_submissions`/`user_parts`):同样增加 `is_blacklisted`。
|
||||
- 索引建议:`KEY idx_products_shop_blacklist (shop_id, is_blacklisted)`,便于后台筛选。
|
||||
- 管理端动作:
|
||||
- 拉黑配件 → `PUT /api/admin/parts/{id}/blacklist`
|
||||
- 恢复配件 → `PUT /api/admin/parts/{id}/restore`
|
||||
|
||||
## 3. 前端(admin/)
|
||||
- 路由:
|
||||
- 用户管理:列表提供“拉黑/恢复”按钮,状态显示“正常/黑名单”。
|
||||
- 用户配件管理:列表提供“拉黑/恢复”按钮,状态显示“正常/黑名单”。
|
||||
- Mock:
|
||||
- 开启 `VITE_USE_MOCK=true` 使用 `public/mock/*.json`。
|
||||
|
||||
## 4. 后端接口(OpenAPI 标注)
|
||||
- 用户:
|
||||
- `GET /api/admin/users?kw=` → 列表(分页)
|
||||
- `PUT /api/admin/users/{id}` → 修改 `status`/`name`/`phone`/`role`
|
||||
- 用户配件:
|
||||
- `GET /api/admin/parts?kw=&status=` → 列表(分页,支持按 `is_blacklisted` 过滤)
|
||||
- `PUT /api/admin/parts/{id}/blacklist` → 标记黑名单
|
||||
- `PUT /api/admin/parts/{id}/restore` → 取消黑名单
|
||||
- 咨询服务:
|
||||
- `GET /api/admin/consults?status=&kw=` → 列表
|
||||
- `POST /api/admin/consults/{id}/reply` → 回复
|
||||
- `PUT /api/admin/consults/{id}/resolve` → 标记解决(前端需即时将该行状态改为 resolved)
|
||||
|
||||
说明:OpenAPI 需更新到 `doc/openapi.yaml` 并在 summary 中标注“❌ Partially Implemented”。
|
||||
|
||||
## 5. 前台/小程序侧行为
|
||||
- 用户被拉黑:登录受限或仅保留最低权限(由业务规则定义);其数据不可见或只读。
|
||||
- 配件被拉黑:前台与小程序配件列表/搜索/选择均需基于 `is_blacklisted=0` 过滤。
|
||||
|
||||
## 6. 审计与可观测性
|
||||
- 建议记录操作日志(操作者、对象、操作、时间),便于追责与回溯。
|
||||
- 建议在管理端提供简单的筛选/导出功能。
|
||||
|
||||
## 7. 迁移计划
|
||||
- 若使用 `products`:新增字段 `is_blacklisted` 并补齐索引;前台/小程序查询添加过滤条件。
|
||||
- 若采用专表方案:保持同名字段与相同行为,编写同步脚本将历史数据初始化为非黑名单。
|
||||
## 4. 未完成功能
|
||||
- 管理端登录页与权限粒度控制尚未上线。
|
||||
- 配件审核流仅完成基础编辑,驳回/通过流程待接入。
|
||||
- 公告板目前缺少多语言与富文本支持。
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
## partsinquiry 数据库文档
|
||||
|
||||
更新日期:2025-09-23(已插入演示数据;新增演示店B/演示店C与其默认账户、两名用户)
|
||||
更新日期:2025-09-27(对齐远程线上库结构;提醒:`backend/db/db.sql` 尚未覆盖 VIP 相关表,请勿直接依赖本地脚本)
|
||||
|
||||
说明:本文件根据远程库 mysql.tonaspace.com 中 `partsinquiry` 的实际结构生成,字段/索引/外键信息以线上为准。
|
||||
说明:本文件根据远程库 mysql.tonaspace.com 中 `partsinquiry` 的实际结构生成,字段/索引/外键信息以线上为准。如需执行结构变更,请通过 MysqlMCP,并在成功后更新此文档和 `backend/db/db.sql`。差异概览:
|
||||
- ✅ 线上已存在 `vip_users`、`vip_price`、`vip_recharges`、`attachments` 表;`backend/db/db.sql` 仍缺少对应建表语句。
|
||||
- ✅ 线上 `admins` 表存储平台管理员,管理端接口通过 `AdminAuthInterceptor` 校验。
|
||||
- ❌ `global_skus` 及配件审核体系仅部分表有数据,审批流程仍在试运行阶段。
|
||||
- ✅ 新增“模板化配件参数”相关结构:`part_templates`、`part_template_params`;为 `products` 与 `part_submissions` 增加 `template_id` 与 `dedupe_key`,并建立唯一与辅助索引。
|
||||
|
||||
### shops
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
@@ -27,23 +31,39 @@
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | 用户ID |
|
||||
| shop_id | BIGINT UNSIGNED | NOT NULL | | 所属店铺 |
|
||||
| phone | VARCHAR(32) | YES | | 手机号 |
|
||||
| email | VARCHAR(128) | YES | | 邮箱 |
|
||||
| name | VARCHAR(64) | NOT NULL | | 姓名 |
|
||||
| role | VARCHAR(32) | NOT NULL | staff | 角色:owner/staff/finance/... |
|
||||
| password_hash | VARCHAR(255) | YES | | 密码哈希(若采用短信登录可为空) |
|
||||
| status | TINYINT UNSIGNED | NOT NULL | 1 | 状态:1启用 0停用 |
|
||||
| is_owner | TINYINT(1) | NOT NULL | 0 | 是否店主 |
|
||||
| is_platform_admin | TINYINT(1) | NOT NULL | 0 | 是否平台管理员 |
|
||||
(字段已调整:移除 `is_platform_admin`,平台管理员改为独立表 `admins`)
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| deleted_at | DATETIME | YES | | |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_users_shop` (`shop_id`) - UNIQUE: `uk_users_phone` (`phone`) - UNIQUE: `ux_users_shop_phone` (`shop_id`,`phone`)
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_users_shop` (`shop_id`) - UNIQUE: `uk_users_phone` (`phone`) - UNIQUE: `ux_users_shop_phone` (`shop_id`,`phone`) - UNIQUE: `ux_users_email` (`email`)
|
||||
|
||||
字段说明:
|
||||
- shop_id: 归属店铺
|
||||
- role: 角色标识字符串
|
||||
- is_owner: 是否店主标记
|
||||
- is_platform_admin: 是否平台管理员(平台级权限)
|
||||
- 平台管理员:请参见 `admins` 表
|
||||
### admins
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | 管理员ID |
|
||||
| username | VARCHAR(64) | NOT NULL | | 登录名/展示名 |
|
||||
| phone | VARCHAR(32) | YES | | 手机号 |
|
||||
| password_hash | VARCHAR(255) | YES | | 密码哈希 |
|
||||
| status | TINYINT UNSIGNED | NOT NULL | 1 | 状态:1启用 0停用 |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| deleted_at | DATETIME | YES | | |
|
||||
|
||||
说明:平台管理员不隶属店铺,不具备店铺资源(店铺/配件)归属,仅用于管理端功能。
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - UNIQUE: `ux_admins_username` (`username`) - UNIQUE: `ux_admins_phone` (`phone`)
|
||||
- 其余同名含义
|
||||
|
||||
### user_identities
|
||||
@@ -109,6 +129,38 @@
|
||||
|
||||
说明:该表为全局配置表,仅包含一条记录用于表示当前 VIP 单月价格。
|
||||
|
||||
### vip_recharges
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | 充值记录ID |
|
||||
| shop_id | BIGINT UNSIGNED | NOT NULL | | 店铺ID |
|
||||
| user_id | BIGINT UNSIGNED | NOT NULL | | 用户ID |
|
||||
| price | DECIMAL(10,2) | NOT NULL | | 本次充值价格(元) |
|
||||
| duration_days | INT | NOT NULL | | 本次续期天数 |
|
||||
| expire_from | DATETIME | YES | | 生效前到期时间(可空) |
|
||||
| expire_to | DATETIME | NOT NULL | | 生效后到期时间 |
|
||||
| channel | VARCHAR(32) | NOT NULL | oneclick | 渠道(oneclick/…) |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | 创建时间 |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_vr_shop` (`shop_id`) - KEY: `idx_vr_user` (`user_id`)
|
||||
**Foreign Keys**: - `fk_vr_shop`: `shop_id` → `shops(id)` - `fk_vr_user`: `user_id` → `users(id)`
|
||||
### normal_admin_audits(普通管理员申请/审批审计日志)
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| 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) | YES | | 备注 |
|
||||
| operator_admin_id | BIGINT UNSIGNED | YES | | 平台管理员ID(apply时可空) |
|
||||
| previous_role | VARCHAR(32) | YES | | 变更前角色 |
|
||||
| new_role | VARCHAR(32) | YES | | 变更后角色 |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | 创建时间 |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_naudit_shop_time` (`shop_id`,`created_at`) - KEY: `idx_naudit_user_time` (`user_id`,`created_at`)
|
||||
**Foreign Keys**: - `fk_naudit_shop`: `shop_id` → `shops(id)` - `fk_naudit_user`: `user_id` → `users(id)` - `fk_naudit_admin`: `operator_admin_id` → `admins(id)`
|
||||
|
||||
|
||||
### wechat_sessions
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
@@ -216,11 +268,13 @@
|
||||
| name | VARCHAR(120) | NOT NULL | | 供全文检索 |
|
||||
| category_id | BIGINT UNSIGNED | YES | | |
|
||||
| unit_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| template_id | BIGINT UNSIGNED | YES | | 关联的模板 |
|
||||
| brand | VARCHAR(64) | YES | | |
|
||||
| model | VARCHAR(64) | YES | | |
|
||||
| spec | VARCHAR(128) | YES | | |
|
||||
| origin | VARCHAR(64) | YES | | |
|
||||
| barcode | VARCHAR(32) | YES | | |
|
||||
| dedupe_key | VARCHAR(512) | YES | | 去重键(规范化后计算) |
|
||||
| alias | VARCHAR(120) | YES | | |
|
||||
| is_blacklisted | TINYINT(1) | NOT NULL | 0 | 黑名单标记(管理端可控) |
|
||||
| description | TEXT | YES | | |
|
||||
@@ -237,8 +291,43 @@
|
||||
- safe_min/safe_max: 安全库存上下限
|
||||
- search_text: 聚合检索字段(触发器维护)
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_products_shop` (`shop_id`) - KEY: `idx_products_category` (`category_id`) - KEY: `idx_products_unit` (`unit_id`) - 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`)
|
||||
**Foreign Keys**: - `fk_products_shop`: `shop_id` → `shops(id)` - `fk_products_user`: `user_id` → `users(id)` - `fk_products_category`: `category_id` → `product_categories(id)` - `fk_products_unit`: `unit_id` → `product_units(id)` - `fk_products_globalsku`: `global_sku_id` → `global_skus(id)`
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_products_shop` (`shop_id`) - KEY: `idx_products_category` (`category_id`) - KEY: `idx_products_unit` (`unit_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`)
|
||||
**Foreign Keys**: - `fk_products_shop`: `shop_id` → `shops(id)` - `fk_products_user`: `user_id` → `users(id)` - `fk_products_category`: `category_id` → `product_categories(id)` - `fk_products_unit`: `unit_id` → `product_units(id)` - `fk_products_template`: `template_id` → `part_templates(id)` - `fk_products_globalsku`: `global_skus(id)`
|
||||
|
||||
### part_submissions(配件提交与审核)
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | 提交ID |
|
||||
| shop_id | BIGINT UNSIGNED | NOT NULL | | 店铺 |
|
||||
| user_id | BIGINT UNSIGNED | NOT NULL | | 提交用户 |
|
||||
| name | VARCHAR(255) | YES | | 配件名称 |
|
||||
| external_code | VARCHAR(255) | YES | | 外部编码 |
|
||||
| model_unique | VARCHAR(255) | NOT NULL | | 规范化型号(唯一校验) |
|
||||
| brand | VARCHAR(64) | YES | | 品牌 |
|
||||
| spec | VARCHAR(128) | YES | | 规格 |
|
||||
| unit_id | BIGINT UNSIGNED | YES | | 单位 |
|
||||
| category_id | BIGINT UNSIGNED | YES | | 分类 |
|
||||
| template_id | BIGINT UNSIGNED | YES | | 模板 |
|
||||
| attributes | JSON | YES | | 参数JSON |
|
||||
| images | JSON | YES | | 图片URL数组JSON |
|
||||
| size | VARCHAR(64) | YES | | 兼容历史字段 |
|
||||
| aperture | VARCHAR(64) | YES | | 兼容历史字段 |
|
||||
| compatible | VARCHAR(255) | YES | | 兼容机型文本 |
|
||||
| barcode | VARCHAR(32) | YES | | 条码 |
|
||||
| dedupe_key | VARCHAR(512) | YES | | 去重键 |
|
||||
| remark | TEXT | YES | | 备注 |
|
||||
| status | ENUM('pending','approved','rejected') | NOT NULL | | 审核状态 |
|
||||
| reviewer_id | BIGINT UNSIGNED | YES | | 审核人 |
|
||||
| product_id | BIGINT UNSIGNED | YES | | 关联生成的商品ID |
|
||||
| global_sku_id | BIGINT UNSIGNED | YES | | 关联全局SKU |
|
||||
| reviewed_at | DATETIME | YES | | 审核时间 |
|
||||
| review_remark | VARCHAR(255) | YES | | 审核备注 |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| deleted_at | DATETIME | YES | | |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_ps_template` (`template_id`) - KEY: `idx_ps_dedupe` (`dedupe_key`) - UNIQUE: `ux_ps_template_name_model` (`template_id`,`name`,`model_unique`)
|
||||
**Foreign Keys**: - `fk_ps_template`: `template_id` → `part_templates(id)`
|
||||
|
||||
### product_aliases
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
@@ -277,6 +366,41 @@
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_product_images_product` (`product_id`) - UNIQUE: `ux_product_image_hash` (`product_id`,`hash`)
|
||||
**Foreign Keys**: - `fk_pimg_shop`: `shop_id` → `shops(id)` - `fk_pimg_user`: `user_id` → `users(id)` - `fk_pimg_product`: `product_id` → `products(id)` ON DELETE CASCADE
|
||||
|
||||
### part_templates(配件模板)
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | 模板ID |
|
||||
| category_id | BIGINT UNSIGNED | NOT NULL | | 绑定分类 |
|
||||
| name | VARCHAR(120) | NOT NULL | | 配件名 |
|
||||
| model_rule | VARCHAR(255) | YES | | 型号规则(说明/正则,可空) |
|
||||
| status | TINYINT UNSIGNED | NOT NULL | 1 | 1启用 0停用 |
|
||||
| created_by_admin_id | BIGINT UNSIGNED | YES | | 创建管理员 |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_pt_category` (`category_id`) - KEY: `idx_pt_status` (`status`) - KEY: `idx_pt_admin` (`created_by_admin_id`)
|
||||
**Foreign Keys**: - `fk_pt_category`: `category_id` → `product_categories(id)` - `fk_pt_admin`: `created_by_admin_id` → `admins(id)`
|
||||
|
||||
### part_template_params(模板参数字段)
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| template_id | BIGINT UNSIGNED | NOT NULL | | 所属模板 |
|
||||
| field_key | VARCHAR(64) | NOT NULL | | 参数键(字母/下划线) |
|
||||
| field_label | VARCHAR(120) | NOT NULL | | 参数名(展示) |
|
||||
| type | ENUM('string','number','boolean','enum','date') | NOT NULL | | 参数类型 |
|
||||
| required | TINYINT(1) | NOT NULL | 0 | 是否必填 |
|
||||
| unit | VARCHAR(32) | YES | | 单位(文本) |
|
||||
| enum_options | JSON | YES | | 枚举项(type=enum) |
|
||||
| searchable | TINYINT(1) | NOT NULL | 0 | 参与检索 |
|
||||
| dedupe_participate | TINYINT(1) | NOT NULL | 0 | 参与去重键 |
|
||||
| sort_order | INT | NOT NULL | 0 | |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
|
||||
**Indexes**: - 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`)
|
||||
**Foreign Keys**: - `fk_ptp_template`: `template_id` → `part_templates(id)` ON DELETE CASCADE
|
||||
|
||||
### product_prices
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
@@ -321,7 +445,6 @@
|
||||
| phone | VARCHAR(32) | YES | | 座机 |
|
||||
| address | VARCHAR(255) | YES | | 送货地址 |
|
||||
| mobile | VARCHAR(32) | YES | | 手机 |
|
||||
| level | VARCHAR(32) | YES | | 客户等级标签 |
|
||||
| contact_name | VARCHAR(64) | YES | | 联系人 |
|
||||
| price_level | ENUM('零售价','批发价','大单报价') | NOT NULL | 零售价 | 默认售价列(中文存储) |
|
||||
| status | TINYINT UNSIGNED | NOT NULL | 1 | |
|
||||
@@ -332,7 +455,6 @@
|
||||
| deleted_at | DATETIME | YES | | |
|
||||
|
||||
字段说明(customers):
|
||||
- level: 等级标签
|
||||
- price_level: 默认售价列(中文存储:零售价/批发价/大单报价)
|
||||
- ar_opening: 期初应收
|
||||
|
||||
@@ -420,10 +542,13 @@
|
||||
| quantity | DECIMAL(18,3) | NOT NULL | | |
|
||||
| unit_price | DECIMAL(18,2) | NOT NULL | | |
|
||||
| discount_rate | DECIMAL(5,2) | NOT NULL | 0.00 | 折扣百分比0-100 |
|
||||
| cost_price | DECIMAL(18,2) | NOT NULL | 0.00 | 记录生成单据时的成本单价 |
|
||||
| cost_amount | DECIMAL(18,2) | NOT NULL | 0.00 | 成本金额 = 数量×成本单价 |
|
||||
| amount | DECIMAL(18,2) | NOT NULL | | |
|
||||
|
||||
字段说明(sales_order_items):
|
||||
- quantity/unit_price/discount_rate/amount: 数量/单价/折扣%/行金额
|
||||
- cost_price/cost_amount: 记录生成单据时的成本(用于利润分析)
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_soi_order` (`order_id`) - KEY: `idx_soi_product` (`product_id`)
|
||||
**Foreign Keys**: - `fk_soi_order`: `order_id` → `sales_orders(id)` ON DELETE CASCADE - `fk_soi_product`: `product_id` → `products(id)`
|
||||
@@ -463,7 +588,7 @@
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_poi_order` (`order_id`) - KEY: `idx_poi_product` (`product_id`)
|
||||
**Foreign Keys**: - `fk_poi_order`: `order_id` → `purchase_orders(id)` ON DELETE CASCADE - `fk_poi_product`: `product_id` → `products(id)`
|
||||
image.png
|
||||
|
||||
### payments
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
@@ -477,16 +602,107 @@ image.png
|
||||
| amount | DECIMAL(18,2) | NOT NULL | | |
|
||||
| pay_time | DATETIME | NOT NULL | | |
|
||||
| remark | VARCHAR(255) | YES | | |
|
||||
| category | VARCHAR(64) | YES | | 分类 key(主要用于其他收支) |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
|
||||
字段说明(payments):
|
||||
- biz_type/biz_id: 业务来源及关联主键
|
||||
- direction: in 收款 / out 付款
|
||||
- account_id: 使用的结算账户
|
||||
- category: 分类 key(主要用于其他收支;用于台账明细展示与统计)
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_payments_shop_time` (`shop_id`,`pay_time`) - KEY: `idx_payments_biz` (`biz_type`,`biz_id`)
|
||||
**Foreign Keys**: - `fk_payments_shop`: `shop_id` → `shops(id)` - `fk_payments_user`: `user_id` → `users(id)` - `fk_payments_account`: `account_id` → `accounts(id)`
|
||||
|
||||
### inventory_movements
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| shop_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| user_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| product_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| source_type | VARCHAR(32) | NOT NULL | | 来源:sale/purchase/sale_return/purchase_return/adjust/audit |
|
||||
| source_id | BIGINT UNSIGNED | YES | | 来源表ID(可空) |
|
||||
| qty_delta | DECIMAL(18,3) | NOT NULL | | 数量增减,出库为负,入库为正 |
|
||||
| amount_delta | DECIMAL(18,2) | YES | | 金额增减(可空) |
|
||||
| cost_price | DECIMAL(18,2) | YES | | 业务发生时的成本单价(可空) |
|
||||
| cost_amount | DECIMAL(18,2) | YES | | 成本金额(可空) |
|
||||
| reason | VARCHAR(64) | YES | | 原因/类别 |
|
||||
| tx_time | DATETIME | NOT NULL | | 业务时间 |
|
||||
| remark | VARCHAR(255) | YES | | |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_im_shop_time` (`shop_id`,`tx_time`) - KEY: `idx_im_product` (`product_id`)
|
||||
|
||||
### sales_return_orders
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| shop_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| user_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| customer_id | BIGINT UNSIGNED | YES | | |
|
||||
| order_no | VARCHAR(32) | NOT NULL | | |
|
||||
| order_time | DATETIME | NOT NULL | | |
|
||||
| status | ENUM('approved','void') | NOT NULL | approved | |
|
||||
| amount | DECIMAL(18,2) | NOT NULL | 0.00 | 退货金额合计 |
|
||||
| paid_amount | DECIMAL(18,2) | NOT NULL | 0.00 | 已退/已收合计 |
|
||||
| remark | VARCHAR(255) | YES | | |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| deleted_at | DATETIME | YES | | |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - UNIQUE: `ux_sr_order_no` (`shop_id`,`order_no`) - KEY: `idx_sr_shop_time` (`shop_id`,`order_time`)
|
||||
**Foreign Keys**: - `fk_sr_customer`: `customer_id` → `customers(id)` - `fk_sr_user`: `user_id` → `users(id)`
|
||||
|
||||
### sales_return_order_items
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| order_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| product_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| quantity | DECIMAL(18,3) | NOT NULL | | |
|
||||
| unit_price | DECIMAL(18,2) | NOT NULL | | |
|
||||
| discount_rate | DECIMAL(5,2) | NOT NULL | 0.00 | 折扣百分比0-100 |
|
||||
| cost_price | DECIMAL(18,2) | NOT NULL | 0.00 | 退货时对应的成本单价 |
|
||||
| cost_amount | DECIMAL(18,2) | NOT NULL | 0.00 | 成本金额 = 数量×成本单价 |
|
||||
| amount | DECIMAL(18,2) | NOT NULL | | 行金额 |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_sroi_order` (`order_id`) - KEY: `idx_sroi_product` (`product_id`)
|
||||
**Foreign Keys**: - `fk_sroi_order`: `order_id` → `sales_return_orders(id)` - `fk_sroi_product`: `product_id` → `products(id)`
|
||||
|
||||
### purchase_return_orders
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| shop_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| user_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| supplier_id | BIGINT UNSIGNED | YES | | |
|
||||
| order_no | VARCHAR(32) | NOT NULL | | |
|
||||
| order_time | DATETIME | NOT NULL | | |
|
||||
| status | ENUM('approved','void') | NOT NULL | approved | |
|
||||
| amount | DECIMAL(18,2) | NOT NULL | 0.00 | 退货金额合计 |
|
||||
| paid_amount | DECIMAL(18,2) | NOT NULL | 0.00 | 已付合计(退款/扣减) |
|
||||
| remark | VARCHAR(255) | YES | | |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| deleted_at | DATETIME | YES | | |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - UNIQUE: `ux_pr_order_no` (`shop_id`,`order_no`) - KEY: `idx_pr_shop_time` (`shop_id`,`order_time`)
|
||||
**Foreign Keys**: - `fk_pr_supplier`: `supplier_id` → `suppliers(id)` - `fk_pr_user`: `user_id` → `users(id)`
|
||||
|
||||
### purchase_return_order_items
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| order_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| product_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| quantity | DECIMAL(18,3) | NOT NULL | | |
|
||||
| unit_price | DECIMAL(18,2) | NOT NULL | | |
|
||||
| amount | DECIMAL(18,2) | NOT NULL | | 行金额 |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_proi_order` (`order_id`) - KEY: `idx_proi_product` (`product_id`)
|
||||
**Foreign Keys**: - `fk_proi_order`: `order_id` → `purchase_return_orders(id)` - `fk_proi_product`: `product_id` → `products(id)`
|
||||
|
||||
### other_transactions
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
@@ -509,4 +725,74 @@ image.png
|
||||
- type/category: 收入/支出与分类
|
||||
- counterparty_type/id: 往来单位(可空)
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_ot_shop_time`
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_ot_shop_time` (`shop_id`,`tx_time`)
|
||||
|
||||
### consults
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | 咨询ID |
|
||||
| shop_id | BIGINT UNSIGNED | NOT NULL | | 所属店铺 |
|
||||
| user_id | BIGINT UNSIGNED | NOT NULL | | 提问用户 |
|
||||
| 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 | 更新时间 |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_consult_shop_status` (`shop_id`,`status`) - KEY: `fk_consult_user` (`user_id`)
|
||||
**Foreign Keys**: - `fk_consult_shop`: `shop_id` → `shops(id)` - `fk_consult_user`: `user_id` → `users(id)`
|
||||
|
||||
### consult_replies
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | 回复ID |
|
||||
| consult_id | BIGINT UNSIGNED | NOT NULL | | 所属咨询 |
|
||||
| user_id | BIGINT UNSIGNED | NOT NULL | | 回复人(管理员) |
|
||||
| content | TEXT | NOT NULL | | 回复内容 |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | 回复时间 |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_cr_consult` (`consult_id`) - KEY: `fk_cr_user` (`user_id`)
|
||||
**Foreign Keys**: - `fk_cr_consult`: `consult_id` → `consults(id)` - `fk_cr_user`: `user_id` → `users(id)`
|
||||
|
||||
```
|
||||
**触发器**:
|
||||
- `trg_consult_replies_ai`: AFTER INSERT ON `consult_replies` → 更新 `consults.status='resolved'` 且 `updated_at=NOW()`
|
||||
```
|
||||
|
||||
### notices
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| title | VARCHAR(120) | NOT NULL | | 标题 |
|
||||
| content | VARCHAR(500) | NOT NULL | | 内容(跑马灯/简讯) |
|
||||
| tag | VARCHAR(32) | YES | | 标签(如“活动”) |
|
||||
| is_pinned | TINYINT(1) | NOT NULL | 0 | 是否置顶 |
|
||||
| starts_at | DATETIME | YES | | 生效开始时间(为空立即生效) |
|
||||
| ends_at | DATETIME | YES | | 生效结束时间(为空长期) |
|
||||
| status | ENUM('draft','published','offline') | NOT NULL | published | 状态 |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| deleted_at | DATETIME | YES | | 逻辑删除 |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_notices_time` (`starts_at`,`ends_at`)
|
||||
|
||||
字段说明:
|
||||
- 生效窗口:仅当 `status='published'` 且当前时间处于 `[starts_at, ends_at]` 区间(空表示不限制)时,前台 `/api/notices` 会返回;排序 `is_pinned DESC, created_at DESC`。
|
||||
- 数据范围:平台全局公告(与租户无关)。如需“店铺公告”,需在该表增加 `shop_id` 并调整接口逻辑。
|
||||
|
||||
### email_codes
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| email | VARCHAR(128) | NOT NULL | | 邮箱 |
|
||||
| scene | VARCHAR(32) | NOT NULL | | 场景(login/register/…) |
|
||||
| code_hash | VARCHAR(64) | NOT NULL | | 验证码哈希(SHA-256) |
|
||||
| salt | VARCHAR(64) | NOT NULL | | 加盐字符串 |
|
||||
| expire_at | DATETIME | NOT NULL | | 过期时间 |
|
||||
| status | TINYINT UNSIGNED | NOT NULL | 0 | 0=unused,1=used,2=expired |
|
||||
| fail_count | INT | NOT NULL | 0 | 错误次数 |
|
||||
| ip | VARCHAR(64) | YES | | 发送IP |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_email_scene_created` (`email`,`scene`,`created_at`) - KEY: `idx_email_expire` (`expire_at`)
|
||||
|
||||
1513
doc/openapi.yaml
1513
doc/openapi.yaml
File diff suppressed because it is too large
Load Diff
187
doc/product_enhancement_requirements.md
Normal file
187
doc/product_enhancement_requirements.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# 货品功能扩展需求文档(草案)
|
||||
|
||||
## 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` | 待确认产品图片、参数同步策略是否满足后续拓展(如平台库) |
|
||||
| 导出 | 管理端支持按筛选条件导出 Excel(2000 条以内同步导出) | 后续可评估异步导出/下载中心、导出模板自定义 |
|
||||
|
||||
## 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` 的关系,明确平台配件库与用户私有货品的边界,以免数据混乱。
|
||||
|
||||
---
|
||||
> 本文档结合现状与新增需求整理。请确认方案后,可进一步拆解任务并排期执行。
|
||||
@@ -3,26 +3,30 @@
|
||||
#### 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 货品列表与库存:**
|
||||
@@ -92,9 +96,12 @@
|
||||
|
||||
**2.6 “我的”(用户中心)模块**
|
||||
* **2.6.1 用户信息:** 显示用户头像、店铺名称、注册手机号及“老板”身份标识。
|
||||
> 个人信息读取 `/api/user/me` 已上线。
|
||||
* **2.6.2 会员与订单:**
|
||||
* 提供**VIP会员**入口,展示会员特权。
|
||||
> `/pages/my/vip` 已接入 `/api/vip/status`,开通调用 `/api/vip/pay`。
|
||||
* 提供**我的订单**入口,可能用于查看应用内服务订单。
|
||||
> 入口预留,服务订单接口尚未上线。
|
||||
* **2.6.3 基础管理:**
|
||||
* **供应商管理**
|
||||
* **客户管理**
|
||||
@@ -113,6 +120,7 @@
|
||||
#### 3.0 全局性需求
|
||||
|
||||
* **3.1 导航:** 采用底部Tab栏导航,包含“首页”、“货品”、“开单”、“明细”、“我的”五个主要模块。
|
||||
> uni-app 端已实现,管理端采用侧栏导航。
|
||||
* **3.2 统一的UI/UX:** 应用整体风格简洁、清晰,操作流程符合移动端使用习惯。
|
||||
* **3.3 空状态页面:** 在列表、报表等数据为空的页面,需提供友好的空状态提示图和引导性文字。
|
||||
* **3.4 数据同步:** 应用数据应在云端同步,保证用户更换设备或多设备使用时数据一致性。
|
||||
@@ -129,11 +137,12 @@
|
||||
* 需满足大规模SKU下的性能目标;可通过系统参数配置匹配策略。
|
||||
|
||||
* **3.8 客户端平台:** 提供移动App与小程序;小程序不支持商品条形码扫描功能。
|
||||
|
||||
> HBuilderX 打包的 App 版本与微信小程序版本已具备扫描差异处理;条码识别仅用户端调用。
|
||||
|
||||
* **3.9 多列销售价格:** 销售价格分四列,即同一种商品有四个销售价格
|
||||
|
||||
|
||||
### 配件查询
|
||||
|
||||
|
||||
1. **数据查询功能**
|
||||
- 多参数组合查询(分类、尺寸、型号等)
|
||||
- 模糊匹配关键字
|
||||
@@ -154,14 +163,14 @@
|
||||
- 点击图片可放大查看
|
||||
- 管理员可管理所有图片
|
||||
- 自动处理文件名冲突
|
||||
|
||||
|
||||
## 全局说明(必看)
|
||||
|
||||
|
||||
由于这个文档写的还不是很完善,目前有存疑的部分先行参考小程序小程序“智慧记进销存”(功能和按钮可以参考,界面样式除外),管理端文档目前待定。
|
||||
|
||||
|
||||
客户要求的是做双端应用(app端+小程序端),需要考虑兼容性相关问题。
|
||||
|
||||
|
||||
本程序和“智慧记进销存”大多一致,主要的区别在于客户有配件查询要求,即在产品页面中要额外加一个配件查询按钮或入口,且要求一个产品要有四个销售价格(先按零售价 分销价 批发价 大客户价),且要求能自定义添加各种规格(尺寸,孔径等)。
|
||||
|
||||
|
||||
有疑惑的部分一定要及时沟通(如未提及的页面和功能需要确认的时候)
|
||||
|
||||
|
||||
331
doc/vip_development.md
Normal file
331
doc/vip_development.md
Normal file
@@ -0,0 +1,331 @@
|
||||
### 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 点击“申请”提示需先成为 VIP;VIP 点击“申请”按配置自动通过或进入待审;审批通过后刷新权限可访问。
|
||||
- 范围:仅能查看与操作本店 `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 '平台管理员ID(apply时可空)',
|
||||
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`。
|
||||
|
||||
- 鉴权与Token(Admin-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` 为准并及时校准。
|
||||
@@ -21,6 +21,49 @@ cd backend
|
||||
```
|
||||
> 说明:`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
|
||||
@@ -72,6 +115,25 @@ $env:CORS_ALLOWED_ORIGINS="http://localhost:5173"
|
||||
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_PASSWORD:SMTP 授权码
|
||||
- 采用 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` 等声明。
|
||||
|
||||
以上即为在新电脑上启动后端并连接远程数据库的最小步骤。
|
||||
|
||||
|
||||
|
||||
35
doc/开发进度与问题.md
Normal file
35
doc/开发进度与问题.md
Normal file
@@ -0,0 +1,35 @@
|
||||
## 当前开发进度概览(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. **配件提交流程验收**:需补充测试用例(提交→审核→导出),并确认型号唯一策略是否支持多店共享或全局唯一。
|
||||
Reference in New Issue
Block a user