Files
PartsInquiry/doc/database_documentation.md
2025-09-30 00:03:43 +08:00

802 lines
43 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## partsinquiry 数据库文档
更新日期2025-09-27对齐远程线上库结构提醒`backend/db/db.sql` 尚未覆盖 VIP 相关表,请勿直接依赖本地脚本)
说明:本文件根据远程库 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 |
| ----------- | --------- | -------- | ------- | ------- |
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | 店铺/租户ID |
| name | VARCHAR(100) | NOT NULL | | 店铺名称 |
| 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 | | |
字段说明:
- id: 主键,自增
- name: 店铺名称
- status: 店铺状态1启用/0停用
- created_at/updated_at: 创建/更新时间
- deleted_at: 逻辑删除时间
### users
| Column Name | Data Type | Nullable | Default | Comment |
| ----------- | --------- | -------- | ------- | ------- |
| 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`,平台管理员改为独立表 `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`) - UNIQUE: `ux_users_email` (`email`)
字段说明:
- shop_id: 归属店铺
- role: 角色标识字符串
- is_owner: 是否店主标记
- 平台管理员:请参见 `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
| 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 | | |
| provider | ENUM('wechat_mp','wechat_app') | NOT NULL | | 身份提供方:小程序/APP |
| openid | VARCHAR(64) | NOT NULL | | |
| unionid | VARCHAR(64) | YES | | |
| nickname | VARCHAR(64) | YES | | |
| avatar_url | VARCHAR(512) | YES | | |
| last_login_at | DATETIME | YES | | |
| 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 单月价格。
### 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 | | 平台管理员IDapply时可空 |
| 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 |
| ----------- | --------- | -------- | ------- | ------- |
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
| provider | ENUM('wechat_mp','wechat_app') | NOT NULL | | |
| openid | VARCHAR(64) | NOT NULL | | |
| session_key | VARCHAR(128) | NOT NULL | | |
| expires_at | DATETIME | NOT NULL | | |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
字段说明:
- session_key/expires_at: 会话密钥与过期时间
### system_parameters
| 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 | | 创建/最后修改人 |
| key | VARCHAR(64) | NOT NULL | | 参数键 |
| value | JSON | NOT NULL | | 参数值JSON |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
字段说明:
- key/value: 键/值JSON
### product_units含全局字典约定
| 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 | | |
| name | VARCHAR(16) | NOT NULL | | |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| deleted_at | DATETIME | YES | | |
字段说明product_units
- 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
| Column Name | Data Type | Nullable | Default | Comment |
| ----------- | --------- | -------- | ------- | ------- |
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
| name | VARCHAR(120) | NOT NULL | | SKU名称 |
| brand | VARCHAR(64) | YES | | |
| model | VARCHAR(64) | YES | | |
| spec | VARCHAR(128) | YES | | |
| barcode | VARCHAR(32) | YES | | |
| unit_id | BIGINT UNSIGNED | YES | | |
| tags | JSON | YES | | |
| status | ENUM('published','offline') | NOT NULL | published | |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| deleted_at | DATETIME | YES | | |
字段说明global_skus
- name/brand/model/spec/barcode: SKU 基本属性
- unit_id: 对应的计量单位
- tags: 结构化标签 JSON
- status: 上架状态published/offline
**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含全局字典约定
| 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 | | |
| name | VARCHAR(64) | NOT NULL | | |
| parent_id | BIGINT UNSIGNED | YES | | |
| sort_order | INT | NOT NULL | 0 | |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| deleted_at | DATETIME | YES | | |
字段说明product_categories
- parent_id: 父分类,可为空
- 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
| 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 | | |
| name | VARCHAR(120) | NOT NULL | | 供全文检索 |
| category_id | BIGINT UNSIGNED | YES | | |
| unit_id | (已移除) | | | |
| 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 | | |
| global_sku_id | BIGINT UNSIGNED | YES | | |
| safe_min | DECIMAL(18,3) | YES | | |
| safe_max | DECIMAL(18,3) | YES | | |
| search_text | TEXT | YES | | 供全文检索的聚合字段(名称/品牌/型号/规格/别名) |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| deleted_at | DATETIME | YES | | |
字段说明products
- category_id/unit_id/global_sku_id: 归属分类/单位/全局SKU
- 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_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_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 |
| ----------- | --------- | -------- | ------- | ------- |
| 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 | | |
| alias | VARCHAR(120) | NOT NULL | | |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| deleted_at | DATETIME | YES | | |
字段说明product_aliases
- alias: 商品别名(同义词)
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_product_alias_product` (`product_id`) - UNIQUE: `ux_product_alias` (`product_id`,`alias`)
**Foreign Keys**: - `fk_alias_shop`: `shop_id``shops(id)` - `fk_alias_user`: `user_id``users(id)` - `fk_alias_product`: `product_id``products(id)`
### product_images
| 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 | | |
| url | VARCHAR(512) | NOT NULL | | |
| hash | VARCHAR(64) | YES | | 内容哈希(去重) |
| sort_order | INT | NOT NULL | 0 | |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
字段说明product_images
- url/hash: 图片地址/内容哈希
- sort_order: 展示顺序
**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 | |
| deleted_at | DATETIME | YES | | 软删标记 |
**Indexes**: - 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`)
**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 | 参与检索 |
| fuzzy_searchable | TINYINT(1) | NOT NULL | 0 | 可模糊查询(仅数值型) |
| fuzzy_tolerance | DECIMAL(18,6) | YES | | 容差NULL 使用平台默认 |
| 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 |
| ----------- | --------- | -------- | ------- | ------- |
| product_id | BIGINT UNSIGNED | NOT NULL | | |
| shop_id | BIGINT UNSIGNED | NOT NULL | | |
| user_id | BIGINT UNSIGNED | NOT NULL | | |
| purchase_price | DECIMAL(18,2) | NOT NULL | 0.00 | |
| retail_price | DECIMAL(18,2) | NOT NULL | 0.00 | |
| wholesale_price | DECIMAL(18,2) | NOT NULL | 0.00 | |
| big_client_price | DECIMAL(18,2) | NOT NULL | 0.00 | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
字段说明product_prices
- purchase_price: 当前进价(用于近似毛利)
- retail/wholesale/big_client_price: 售价列
**Indexes**: - PRIMARY KEY: `product_id` - KEY: `idx_prices_shop` (`shop_id`)
**Foreign Keys**: - `fk_prices_product`: `product_id``products(id)` ON DELETE CASCADE - `fk_prices_shop`: `shop_id``shops(id)` - `fk_prices_user`: `user_id``users(id)`
### inventories
| Column Name | Data Type | Nullable | Default | Comment |
| ----------- | --------- | -------- | ------- | ------- |
| product_id | BIGINT UNSIGNED | NOT NULL | | |
| shop_id | BIGINT UNSIGNED | NOT NULL | | |
| user_id | BIGINT UNSIGNED | NOT NULL | | |
| quantity | DECIMAL(18,3) | NOT NULL | 0.000 | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
字段说明inventories
- quantity: 当前库存数量(按商品一行聚合)
**Indexes**: - PRIMARY KEY: `product_id` - KEY: `idx_inventories_shop` (`shop_id`)
**Foreign Keys**: - `fk_inv_product`: `product_id``products(id)` ON DELETE CASCADE - `fk_inv_shop`: `shop_id``shops(id)` - `fk_inv_user`: `user_id``users(id)`
### customers
| 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 | | |
| name | VARCHAR(120) | NOT NULL | | |
| phone | VARCHAR(32) | YES | | 座机 |
| address | VARCHAR(255) | YES | | 送货地址 |
| mobile | VARCHAR(32) | YES | | 手机 |
| contact_name | VARCHAR(64) | YES | | 联系人 |
| price_level | ENUM('零售价','批发价','大单报价') | NOT NULL | 零售价 | 默认售价列(中文存储) |
| status | TINYINT UNSIGNED | NOT NULL | 1 | |
| ar_opening | 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 | | |
字段说明customers
- price_level: 默认售价列(中文存储:零售价/批发价/大单报价)
- ar_opening: 期初应收
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_customers_shop` (`shop_id`) - KEY: `idx_customers_phone` (`phone`) - KEY: `idx_customers_mobile` (`mobile`)
**Foreign Keys**: - `fk_customers_shop`: `shop_id``shops(id)` - `fk_customers_user`: `user_id``users(id)`
### suppliers
| 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 | | |
| name | VARCHAR(120) | NOT NULL | | |
| contact_name | VARCHAR(64) | YES | | 联系人 |
| mobile | VARCHAR(32) | YES | | 手机 |
| phone | VARCHAR(32) | YES | | 电话 |
| address | VARCHAR(255) | YES | | 经营地址 |
| status | TINYINT UNSIGNED | NOT NULL | 1 | |
| ap_opening | DECIMAL(18,2) | NOT NULL | 0.00 | 期初应付 |
| ap_payable | 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 | | |
字段说明suppliers
- ap_opening/ap_payable: 期初应付/当前应付
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_suppliers_shop` (`shop_id`) - KEY: `idx_suppliers_phone` (`phone`) - KEY: `idx_suppliers_mobile` (`mobile`)
**Foreign Keys**: - `fk_suppliers_shop`: `shop_id``shops(id)` - `fk_suppliers_user`: `user_id``users(id)`
### accounts
| 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 | | |
| name | VARCHAR(64) | NOT NULL | | |
| type | ENUM('cash','bank','alipay','wechat','other') | NOT NULL | cash | |
| bank_name | VARCHAR(64) | YES | | 银行名称type=bank 可用) |
| bank_account | VARCHAR(64) | YES | | 银行账号type=bank 可用) |
| balance | DECIMAL(18,2) | NOT NULL | 0.00 | |
| status | TINYINT UNSIGNED | NOT NULL | 1 | |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| deleted_at | DATETIME | YES | | |
字段说明accounts
- type: 账户类型cash/bank/alipay/wechat/other
- bank_name/bank_account: 银行账户信息type=bank 时使用)
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_accounts_shop` (`shop_id`) - UNIQUE: `ux_accounts_shop_name` (`shop_id`,`name`)
**Foreign Keys**: - `fk_accounts_shop`: `shop_id``shops(id)` - `fk_accounts_user`: `user_id``users(id)`
### sales_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('draft','approved','returned','void') | NOT NULL | draft | |
| 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_orders
- status: 单据状态draft/approved/returned/void
- amount/paid_amount: 应收/已收合计
**Indexes**: - 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`)
**Foreign Keys**: - `fk_sales_shop`: `shop_id``shops(id)` - `fk_sales_user`: `user_id``users(id)` - `fk_sales_customer`: `customer_id``customers(id)`
### sales_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 | | |
字段说明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)`
### purchase_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('draft','approved','void','returned') | NOT NULL | draft | |
| 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_orders/purchase_order_items
- 与销售单结构类似,含应付与明细
**Indexes**: - 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`)
**Foreign Keys**: - `fk_purchase_shop`: `shop_id``shops(id)` - `fk_purchase_user`: `user_id``users(id)` - `fk_purchase_supplier`: `supplier_id``suppliers(id)`
### purchase_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_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)`
### payments
| 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 | | |
| biz_type | ENUM('sale','purchase','other') | NOT NULL | | |
| biz_id | BIGINT UNSIGNED | YES | | 业务表IDsales_orders/purchase_orders/other_transactions |
| account_id | BIGINT UNSIGNED | NOT NULL | | |
| direction | ENUM('in','out') | NOT NULL | | 收款/付款 |
| 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 |
| ----------- | --------- | -------- | ------- | ------- |
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
| shop_id | BIGINT UNSIGNED | NOT NULL | | |
| user_id | BIGINT UNSIGNED | NOT NULL | | |
| type | ENUM('income','expense') | NOT NULL | | |
| category | VARCHAR(64) | NOT NULL | | |
| counterparty_type | VARCHAR(32) | YES | | customer/supplier/other |
| counterparty_id | BIGINT UNSIGNED | YES | | |
| account_id | BIGINT UNSIGNED | NOT NULL | | |
| amount | DECIMAL(18,2) | NOT NULL | | |
| tx_time | DATETIME | NOT NULL | | |
| remark | VARCHAR(255) | YES | | |
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
| deleted_at | DATETIME | YES | | |
字段说明other_transactions
- type/category: 收入/支出与分类
- counterparty_type/id: 往来单位(可空)
**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`)