This commit is contained in:
2025-09-24 20:35:15 +08:00
parent 39679f7330
commit 8a458ff0a4
12033 changed files with 1537546 additions and 13292 deletions

59
doc/admin_development.md Normal file
View File

@@ -0,0 +1,59 @@
# 管理端开发文档
本文档用于同步当前管理端admin/)前端与数据库的开发状态与下一步计划。
## 一、前端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已完成
## 二、数据库DB
- 已有与复用
- `users.status`1=正常、0=黑名单(供用户管理拉黑/恢复)
- `global_skus`公共SKU库供未来配件审核通过后发布使用
- 新增表
- `vip_users`管理VIPis_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` 已同步
## 三、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
```

59
doc/admin_requirements.md Normal file
View File

@@ -0,0 +1,59 @@
# 管理端需求说明(配件管家)
本文件描述管理端前端与后端的需求要点,遵循“默认同意,管理端可拉黑/恢复”的运营策略;所有可配置值放置在配置或数据库,不允许硬编码。
## 1. 运营策略
- 用户与用户配件(投稿/商品)默认允许展示与使用。
- 管理端具备拉黑与恢复能力:
- 拉黑后:对应对象在前台/小程序/客户侧不可见或被限制。
- 恢复后:恢复正常可用状态。
## 2. 数据模型
### 2.1 用户黑名单
- 复用 `users.status` 字段:`1=正常``0=黑名单`
- 管理端动作:
- 拉黑用户 → `PUT /api/admin/users/{id}` body `{ status: 0 }`
- 恢复用户 → `PUT /api/admin/users/{id}` body `{ status: 1 }`
### 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` 并补齐索引;前台/小程序查询添加过滤条件。
- 若采用专表方案:保持同名字段与相同行为,编写同步脚本将历史数据初始化为非黑名单。

View File

@@ -1,6 +1,6 @@
## partsinquiry 数据库文档
更新日期2025-09-16(已插入演示数据)
更新日期2025-09-23(已插入演示数据新增演示店B/演示店C与其默认账户、两名用户
说明:本文件根据远程库 mysql.tonaspace.com 中 `partsinquiry` 的实际结构生成,字段/索引/外键信息以线上为准。
@@ -32,14 +32,18 @@
| 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 | 是否平台管理员 |
| 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`)
字段说明:
- shop_id: 归属店铺
- role: 角色标识字符串
- is_owner: 是否店主标记
- is_platform_admin: 是否平台管理员(平台级权限)
- 其余同名含义
### user_identities
@@ -57,10 +61,54 @@
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
说明:当前 `user_identities` 仅支持微信身份;短信登录采用 `users.phone` 作为全局唯一身份,不新增 identity 记录。
### sms_codes
| Column Name | Data Type | Nullable | Default | Comment |
| ----------- | --------- | -------- | ------- | ------- |
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
| phone | VARCHAR(32) | NOT NULL | | 手机号 |
| scene | VARCHAR(32) | NOT NULL | login | 场景,默认为 login |
| code_hash | CHAR(64) | NOT NULL | | 验证码哈希SHA-256|
| salt | CHAR(32) | NOT NULL | | 加盐字符串 |
| expire_at | DATETIME | NOT NULL | | 过期时间 |
| status | TINYINT UNSIGNED | NOT NULL | 0 | 0=active,1=used,2=expired,3=blocked |
| fail_count | TINYINT UNSIGNED | NOT NULL | 0 | 错误次数 |
| ip | VARCHAR(45) | YES | | 发送IP |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
**Indexes**: - 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`)
字段说明:
- provider: wechat_mp小程序、wechat_appAPP
- openid/unionid: 微信身份标识
### vip_users
| 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 | | 用户 |
| is_vip | TINYINT(1) | NOT NULL | 1 | 是否VIP1是 0否 |
| status | TINYINT UNSIGNED | NOT NULL | 0 | 启用状态1启用 0停用审核通过后启用 |
| expire_at | DATETIME | YES | | 到期时间 |
| remark | VARCHAR(255) | YES | | 备注/审核说明 |
| reviewer_id | BIGINT UNSIGNED | YES | | 审核人 |
| reviewed_at | DATETIME | YES | | 审核时间 |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_vu_shop_user` (`shop_id`,`user_id`) - KEY: `idx_vu_shop_status` (`shop_id`,`status`)
**Foreign Keys**: - `fk_vu_shop`: `shop_id``shops(id)` - `fk_vu_user`: `user_id``users(id)` - `fk_vu_reviewer`: `reviewer_id``users(id)`
### vip_price
| Column Name | Data Type | Nullable | Default | Comment |
| ----------- | --------- | -------- | ------- | ------- |
| price | DECIMAL(10,2) | NOT NULL | | 全局价格(仅一条记录) |
说明:该表为全局配置表,仅包含一条记录用于表示当前 VIP 单月价格。
### wechat_sessions
| Column Name | Data Type | Nullable | Default | Comment |
| ----------- | --------- | -------- | ------- | ------- |
@@ -88,7 +136,7 @@
字段说明:
- key/value: 键/值JSON
### product_units
### product_units(含全局字典约定)
| Column Name | Data Type | Nullable | Default | Comment |
| ----------- | --------- | -------- | ------- | ------- |
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
@@ -103,6 +151,11 @@
- name: 单位名称,如 件/个/箱
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_units_shop` (`shop_id`) - UNIQUE: `ux_units_shop_name` (`shop_id`,`name`)
全局字典约定方案A
-`shop_id=0` 作为“全局字典库”的承载店铺(不对应真实租户)。
- 单位接口 `/api/product-units` 始终返回 `shop_id=0` 的记录;新建/修改/删除仅写入 `shop_id=0`
- 兼容历史:不强制迁移既有数据,各店已有单位保留。
**Foreign Keys**: - `fk_units_shop`: `shop_id``shops(id)` ON UPDATE NO ACTION ON DELETE NO ACTION - `fk_units_user`: `user_id``users(id)` ON UPDATE NO ACTION ON DELETE NO ACTION
### global_skus
@@ -130,7 +183,7 @@
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_global_skus_brand_model` (`brand`,`model`) - UNIQUE: `ux_global_skus_barcode` (`barcode`)
**Foreign Keys**: - `fk_globalsku_unit`: `unit_id``product_units(id)` ON UPDATE NO ACTION ON DELETE NO ACTION
### product_categories
### product_categories(含全局字典约定)
| Column Name | Data Type | Nullable | Default | Comment |
| ----------- | --------- | -------- | ------- | ------- |
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
@@ -148,6 +201,10 @@
- sort_order: 排序
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_categories_shop` (`shop_id`) - KEY: `idx_categories_parent` (`parent_id`) - UNIQUE: `ux_categories_shop_name` (`shop_id`,`name`)
全局字典约定方案A
- 类别接口 `/api/product-categories` 始终返回 `shop_id=0` 的记录;新建/修改/删除仅写入 `shop_id=0`
- 历史上各店铺已存在的基础项,后续逐步收敛至 `shop_id=0` 字典;为兼容引用,暂不强制删除。
**Foreign Keys**: - `fk_categories_shop`: `shop_id``shops(id)` ON UPDATE NO ACTION ON DELETE NO ACTION - `fk_categories_user`: `user_id``users(id)` ON UPDATE NO ACTION ON DELETE NO ACTION - `fk_categories_parent`: `parent_id``product_categories(id)` ON UPDATE NO ACTION ON DELETE NO ACTION
### products
@@ -165,6 +222,7 @@
| origin | VARCHAR(64) | YES | | |
| barcode | VARCHAR(32) | YES | | |
| alias | VARCHAR(120) | YES | | |
| is_blacklisted | TINYINT(1) | NOT NULL | 0 | 黑名单标记(管理端可控) |
| description | TEXT | YES | | |
| global_sku_id | BIGINT UNSIGNED | YES | | |
| safe_min | DECIMAL(18,3) | YES | | |
@@ -179,7 +237,7 @@
- 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`) - FULLTEXT: `ft_products_search` (`name`,`brand`,`model`,`spec`,`search_text`) - UNIQUE: `ux_products_shop_barcode` (`shop_id`,`barcode`)
**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)`
### product_aliases
@@ -405,7 +463,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 |
| ----------- | --------- | -------- | ------- | ------- |
@@ -451,161 +509,4 @@
- type/category: 收入/支出与分类
- counterparty_type/id: 往来单位(可空)
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_ot_shop_time` (`shop_id`,`tx_time`) - KEY: `idx_ot_account` (`account_id`)
**Foreign Keys**: - `fk_ot_shop`: `shop_id``shops(id)` - `fk_ot_user`: `user_id``users(id)` - `fk_ot_account`: `account_id``accounts(id)`
### finance_categories
| Column Name | Data Type | Nullable | Default | Comment |
| ----------- | --------- | -------- | ------- | ------- |
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
| shop_id | BIGINT UNSIGNED | NOT NULL | | |
| type | ENUM('income','expense') | NOT NULL | | 分类类型 |
| key | VARCHAR(64) | NOT NULL | | 分类键(稳定标识) |
| label | VARCHAR(64) | NOT NULL | | 分类名称(支持中文) |
| sort_order | INT | NOT NULL | 0 | 排序 |
| status | TINYINT UNSIGNED | NOT NULL | 1 | 1启用 0停用 |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
字段说明finance_categories
- key: 稳定标识(代码不随展示文案改变)
- label: 展示名称
**Indexes**: - PRIMARY KEY: `id` - UNIQUE: `ux_finance_cat` (`shop_id`,`type`,`key`) - KEY: `idx_finance_cat_shop_type` (`shop_id`,`type`)
**Foreign Keys**: - `fk_finance_cat_shop`: `shop_id``shops(id)`
### 触发器
- `trg_products_bi`: BEFORE INSERT ON `products` → 设置 `products.search_text`
- `trg_products_au`: BEFORE UPDATE ON `products` → 维护 `products.search_text`
- `trg_palias_ai`: AFTER INSERT ON `product_aliases` → 重建 `products.search_text`
- `trg_palias_au`: AFTER UPDATE ON `product_aliases` → 重建 `products.search_text`
- `trg_palias_ad`: AFTER DELETE ON `product_aliases` → 重建 `products.search_text`
### 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 | | |
字段说明notices
- is_pinned: 是否置顶
- starts_at/ends_at: 生效时间窗
- status: 草稿/发布/下线
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_notices_time` (`starts_at`,`ends_at`)
**Foreign Keys**: - 无
### 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 |
| source_id | BIGINT UNSIGNED | YES | | 关联单据ID |
| qty_delta | DECIMAL(18,3) | NOT NULL | | 数量增减(正加负减) |
| amount_delta | DECIMAL(18,2) | YES | | 金额变动(可空) |
| reason | VARCHAR(64) | YES | | |
| tx_time | DATETIME | NOT NULL | | |
| remark | VARCHAR(255) | YES | | |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
字段说明inventory_movements
- qty_delta: 数量变动(入库为正、出库为负)
- amount_delta: 金额变动(可选)
- source_type/source_id: 变动来源追溯
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_im_shop_time` (`shop_id`,`tx_time`) - KEY: `idx_im_product` (`product_id`)
**Foreign Keys**: - `fk_im_shop`: `shop_id``shops(id)` - `fk_im_user`: `user_id``users(id)` - `fk_im_product`: `product_id``products(id)`
### 附:演示种子数据(非完整,仅用于联调验证)
- 演示店铺演示店A用户 3全部店长 owner
- 商品域基础单位3条、类别2条、全局SKU2条、商品2条含别名/价格/库存/图片)
- 往来与账户客户2、供应商2、账户3
- 单据销售单1含明细2与进货单1含明细2、收付款各1、其他收支2
- 审核与公告part_submissions 1、attachments 1、notices 2、新增 wechat 身份与会话各1
### 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 | | |
字段说明sales_return_orders
- 与销售单结构一致,用于退货业务;状态为 approved/void
**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_shop`: `shop_id``shops(id)` - `fk_sr_user`: `user_id``users(id)` - `fk_sr_customer`: `customer_id``customers(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 | |
| 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)` ON DELETE CASCADE - `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 | | |
字段说明purchase_return_orders
- 与销售单结构一致,用于退货业务;状态为 approved/void
**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_shop`: `shop_id``shops(id)` - `fk_pr_user`: `user_id``users(id)` - `fk_pr_supplier`: `supplier_id``suppliers(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)` ON DELETE CASCADE - `fk_proi_product`: `product_id``products(id)`
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_ot_shop_time`

View File

@@ -361,6 +361,7 @@ paths:
/api/product-categories:
get:
summary: 类别列表(✅ Fully Implemented
description: 仅返回全局字典shop_id=0的类别列表。
responses:
'200':
description: 成功
@@ -412,6 +413,7 @@ paths:
/api/product-units:
get:
summary: 单位列表(✅ Fully Implemented
description: 仅返回全局字典shop_id=0的单位列表。
responses:
'200':
description: 成功
@@ -944,8 +946,234 @@ paths:
schema:
type: string
format: binary
/api/auth/register:
post:
summary: 手机号注册创建店铺与店主(✅ Fully Implemented
description: |
直接提交手机号进行注册若手机号尚不存在则创建店铺与店主用户owner`users.is_owner=1`),并初始化默认账户(现金/银行存款/微信)。
若手机号已存在,则直接签发 JWT 并返回用户/店铺信息。
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
phone: { type: string }
name: { type: string, nullable: true }
password: { type: string, nullable: true, description: '预留字段,当前不校验' }
required: [phone]
responses:
'200':
description: 成功
content:
application/json:
schema:
type: object
properties:
token: { type: string }
expiresIn: { type: integer }
user:
type: object
properties:
userId: { type: integer }
shopId: { type: integer }
phone: { type: string }
/api/admin/vips:
get:
summary: 管理端-会员列表(❌ Partially Implemented
description: 返回店铺会员(或待启用)列表,基于 `vip_users` 表。
parameters:
- in: query
name: phone
schema: { type: string }
- in: query
name: page
schema: { type: integer, default: 1 }
- in: query
name: size
schema: { type: integer, default: 20 }
responses:
'200':
description: 成功
content:
application/json:
schema:
type: object
properties:
list:
type: array
items:
$ref: '#/components/schemas/VipUser'
total: { type: integer }
post:
summary: 管理端-新增/申请会员(❌ Partially Implemented
description: 向 `vip_users` 插入一条记录,支持作为“申请”进入待启用状态。
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateVipUserRequest'
responses:
'200':
description: 成功
/api/admin/vips/{id}:
put:
summary: 管理端-更新会员(❌ Partially Implemented
parameters:
- in: path
name: id
required: true
schema: { type: integer, format: int64 }
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateVipUserRequest'
responses:
'200':
description: 成功
/api/admin/vips/{id}/approve:
post:
summary: 管理端-审核通过VIP❌ Partially Implemented
parameters:
- in: path
name: id
required: true
schema: { type: integer, format: int64 }
responses:
'200': { description: 成功 }
/api/admin/vips/{id}/reject:
post:
summary: 管理端-审核驳回VIP❌ Partially Implemented
parameters:
- in: path
name: id
required: true
schema: { type: integer, format: int64 }
responses:
'200': { description: 成功 }
/api/admin/dicts/units:
post:
summary: 管理端-新增主单位(✅ Fully Implemented
description: 仅平台管理员可用;写入 `shop_id=0` 全局字典。
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name: { type: string }
responses:
'200':
description: 成功
content:
application/json:
schema:
type: object
properties:
id: { type: integer, format: int64 }
/api/admin/dicts/units/{id}:
put:
summary: 管理端-更新主单位(✅ Fully Implemented
parameters:
- in: path
name: id
required: true
schema: { type: integer, format: int64 }
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name: { type: string }
responses: { '200': { description: 成功 } }
delete:
summary: 管理端-删除主单位(✅ Fully Implemented
parameters:
- in: path
name: id
required: true
schema: { type: integer, format: int64 }
responses: { '200': { description: 成功 } }
/api/admin/dicts/categories:
post:
summary: 管理端-新增主类别(✅ Fully Implemented
description: 仅平台管理员可用;写入 `shop_id=0` 全局字典。
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name: { type: string }
responses:
'200':
description: 成功
content:
application/json:
schema:
type: object
properties:
id: { type: integer, format: int64 }
/api/admin/dicts/categories/{id}:
put:
summary: 管理端-更新主类别(✅ Fully Implemented
parameters:
- in: path
name: id
required: true
schema: { type: integer, format: int64 }
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name: { type: string }
responses: { '200': { description: 成功 } }
delete:
summary: 管理端-删除主类别(✅ Fully Implemented
parameters:
- in: path
name: id
required: true
schema: { type: integer, format: int64 }
responses: { '200': { description: 成功 } }
components:
schemas:
VipUser:
type: object
properties:
id: { type: integer, format: int64 }
userId: { type: integer, format: int64 }
isVip: { type: integer, enum: [0,1] }
status: { type: integer, enum: [0,1] }
expireAt: { type: string, format: date-time, nullable: true }
remark: { type: string, nullable: true }
reviewerId: { type: integer, format: int64, nullable: true }
reviewedAt: { type: string, format: date-time, nullable: true }
CreateVipUserRequest:
type: object
properties:
userId: { type: integer, format: int64 }
isVip: { type: integer, enum: [0,1], default: 1 }
expireAt: { type: string, format: date-time, nullable: true }
remark: { type: string, nullable: true }
UpdateVipUserRequest:
type: object
properties:
isVip: { type: integer, enum: [0,1] }
status: { type: integer, enum: [0,1] }
expireAt: { type: string, format: date-time, nullable: true }
remark: { type: string, nullable: true }
DashboardOverview:
type: object
properties:

View File

@@ -32,6 +32,23 @@ $env:DB_URL="jdbc:mysql://mysql.tonaspace.com:3306/partsinquiry?useSSL=false&all
- 浏览器访问:`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