## 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_app(APP) - 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 | 是否VIP(1是 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 | | 平台管理员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 | | ----------- | --------- | -------- | ------- | ------- | | 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 | 参与检索 | | 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 | | 业务表ID:sales_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`)