@@ -0,0 +1,566 @@
-- =====================================================================
-- 配件查询 App 数据库结构( MySQL 8.0)
-- 依据:/doc/requirements.md、/doc/functional_spec.md、/doc/architecture.md
-- 注意:不在此文件中创建数据库与用户,请在外部以环境配置完成
-- 字符集统一为 utf8mb4, 排序规则 utf8mb4_0900_ai_ci
-- =====================================================================
SET NAMES utf8mb4 ;
SET time_zone = ' +00:00 ' ;
SET sql_mode = ' STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION ' ;
SET FOREIGN_KEY_CHECKS = 0 ;
-- =====================================================================
-- 基础:租户与用户
-- =====================================================================
CREATE TABLE IF NOT EXISTS shops (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ' 店铺/租户ID ' ,
name VARCHAR ( 100 ) NOT NULL COMMENT ' 店铺名称 ' ,
status TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT ' 状态: 1启用 0停用 ' ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
KEY idx_shops_status ( status )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 店铺/租户 ' ;
CREATE TABLE IF NOT EXISTS users (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ' 用户ID ' ,
shop_id BIGINT UNSIGNED NOT NULL COMMENT ' 所属店铺 ' ,
phone VARCHAR ( 32 ) NULL COMMENT ' 手机号 ' ,
name VARCHAR ( 64 ) NOT NULL COMMENT ' 姓名 ' ,
role VARCHAR ( 32 ) NOT NULL DEFAULT ' staff ' COMMENT ' 角色: owner/staff/finance/... ' ,
password_hash VARCHAR ( 255 ) NULL COMMENT ' 密码哈希(若采用短信登录可为空) ' ,
status TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT ' 状态: 1启用 0停用 ' ,
is_owner TINYINT ( 1 ) NOT NULL DEFAULT 0 COMMENT ' 是否店主 ' ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_users_shop_phone ( shop_id , phone ) ,
KEY idx_users_shop ( shop_id ) ,
CONSTRAINT fk_users_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 用户 ' ;
-- 第三方身份映射(微信)
CREATE TABLE IF NOT EXISTS user_identities (
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 COMMENT ' 身份提供方:小程序/APP ' ,
openid VARCHAR ( 64 ) NOT NULL ,
unionid VARCHAR ( 64 ) NULL ,
nickname VARCHAR ( 64 ) NULL ,
avatar_url VARCHAR ( 512 ) NULL ,
last_login_at DATETIME NULL ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_identity_provider_openid ( provider , openid ) ,
UNIQUE KEY ux_identity_unionid ( unionid ) ,
KEY idx_identity_user ( user_id ) ,
KEY idx_identity_shop ( shop_id ) ,
CONSTRAINT fk_identity_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_identity_user FOREIGN KEY ( user_id ) REFERENCES users ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 第三方身份映射(微信) ' ;
-- 微信会话(小程序/APP 临时会话)
CREATE TABLE IF NOT EXISTS wechat_sessions (
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 DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_wechat_session ( provider , openid ) ,
KEY idx_wechat_session_expires ( expires_at )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 微信会话(临时) ' ;
CREATE TABLE IF NOT EXISTS system_parameters (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT ,
shop_id BIGINT UNSIGNED NOT NULL ,
user_id BIGINT UNSIGNED NOT NULL COMMENT ' 创建/最后修改人 ' ,
` key ` VARCHAR ( 64 ) NOT NULL COMMENT ' 参数键 ' ,
` value ` JSON NOT NULL COMMENT ' 参数值( JSON) ' ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_sysparams_shop_key ( shop_id , ` key ` ) ,
KEY idx_sysparams_shop ( shop_id ) ,
CONSTRAINT fk_sysparams_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_sysparams_user FOREIGN KEY ( user_id ) REFERENCES users ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 系统参数(租户级) ' ;
-- =====================================================================
-- 货品域(含价格/库存/图片/别名)
-- =====================================================================
CREATE TABLE IF NOT EXISTS product_categories (
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 NULL ,
sort_order INT NOT NULL DEFAULT 0 ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_categories_shop_name ( shop_id , name ) ,
KEY idx_categories_shop ( shop_id ) ,
KEY idx_categories_parent ( parent_id ) ,
CONSTRAINT fk_categories_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_categories_user FOREIGN KEY ( user_id ) REFERENCES users ( id ) ,
CONSTRAINT fk_categories_parent FOREIGN KEY ( parent_id ) REFERENCES product_categories ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 商品类别 ' ;
CREATE TABLE IF NOT EXISTS product_units (
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 DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_units_shop_name ( shop_id , name ) ,
KEY idx_units_shop ( shop_id ) ,
CONSTRAINT fk_units_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_units_user FOREIGN KEY ( user_id ) REFERENCES users ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 商品单位 ' ;
CREATE TABLE IF NOT EXISTS global_skus (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT ,
name VARCHAR ( 120 ) NOT NULL COMMENT ' SKU名称 ' ,
brand VARCHAR ( 64 ) NULL ,
model VARCHAR ( 64 ) NULL ,
spec VARCHAR ( 128 ) NULL ,
barcode VARCHAR ( 32 ) NULL ,
unit_id BIGINT UNSIGNED NULL ,
tags JSON NULL ,
status ENUM ( ' published ' , ' offline ' ) NOT NULL DEFAULT ' published ' ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_global_skus_barcode ( barcode ) ,
KEY idx_global_skus_brand_model ( brand , model ) ,
CONSTRAINT fk_globalsku_unit FOREIGN KEY ( unit_id ) REFERENCES product_units ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 全局SKU( 众包) ' ;
CREATE TABLE IF NOT EXISTS products (
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 NULL ,
unit_id BIGINT UNSIGNED NOT NULL ,
brand VARCHAR ( 64 ) NULL ,
model VARCHAR ( 64 ) NULL ,
spec VARCHAR ( 128 ) NULL ,
origin VARCHAR ( 64 ) NULL ,
barcode VARCHAR ( 32 ) NULL ,
alias VARCHAR ( 120 ) NULL ,
description TEXT NULL ,
global_sku_id BIGINT UNSIGNED NULL ,
safe_min DECIMAL ( 18 , 3 ) NULL ,
safe_max DECIMAL ( 18 , 3 ) NULL ,
search_text TEXT NULL COMMENT ' 供全文检索的聚合字段(名称/品牌/型号/规格/别名) ' ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_products_shop_barcode ( shop_id , barcode ) ,
KEY idx_products_shop ( shop_id ) ,
KEY idx_products_category ( category_id ) ,
KEY idx_products_unit ( unit_id ) ,
FULLTEXT KEY ft_products_search ( name , brand , model , spec , search_text ) ,
CONSTRAINT fk_products_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_products_user FOREIGN KEY ( user_id ) REFERENCES users ( id ) ,
CONSTRAINT fk_products_category FOREIGN KEY ( category_id ) REFERENCES product_categories ( id ) ,
CONSTRAINT fk_products_unit FOREIGN KEY ( unit_id ) REFERENCES product_units ( id ) ,
CONSTRAINT fk_products_globalsku FOREIGN KEY ( global_sku_id ) REFERENCES global_skus ( id ) ,
CONSTRAINT ck_products_safe_range CHECK ( safe_min IS NULL OR safe_max IS NULL OR safe_min < = safe_max )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 商品 ' ;
CREATE TABLE IF NOT EXISTS product_aliases (
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 DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_product_alias ( product_id , alias ) ,
KEY idx_product_alias_product ( product_id ) ,
CONSTRAINT fk_alias_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_alias_user FOREIGN KEY ( user_id ) REFERENCES users ( id ) ,
CONSTRAINT fk_alias_product FOREIGN KEY ( product_id ) REFERENCES products ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 商品别名 ' ;
CREATE TABLE IF NOT EXISTS product_prices (
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 DEFAULT 0 ,
retail_price DECIMAL ( 18 , 2 ) NOT NULL DEFAULT 0 ,
distribution_price DECIMAL ( 18 , 2 ) NOT NULL DEFAULT 0 ,
wholesale_price DECIMAL ( 18 , 2 ) NOT NULL DEFAULT 0 ,
big_client_price DECIMAL ( 18 , 2 ) NOT NULL DEFAULT 0 ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
PRIMARY KEY ( product_id ) ,
KEY idx_prices_shop ( shop_id ) ,
CONSTRAINT fk_prices_product FOREIGN KEY ( product_id ) REFERENCES products ( id ) ON DELETE CASCADE ,
CONSTRAINT fk_prices_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_prices_user FOREIGN KEY ( user_id ) REFERENCES users ( id ) ,
CONSTRAINT ck_prices_non_negative CHECK (
purchase_price > = 0 AND retail_price > = 0 AND distribution_price > = 0 AND wholesale_price > = 0 AND big_client_price > = 0
)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 商品价格(含四列销售价) ' ;
CREATE TABLE IF NOT EXISTS inventories (
product_id BIGINT UNSIGNED NOT NULL ,
shop_id BIGINT UNSIGNED NOT NULL ,
user_id BIGINT UNSIGNED NOT NULL ,
quantity DECIMAL ( 18 , 3 ) NOT NULL DEFAULT 0 ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
PRIMARY KEY ( product_id ) ,
KEY idx_inventories_shop ( shop_id ) ,
CONSTRAINT fk_inv_product FOREIGN KEY ( product_id ) REFERENCES products ( id ) ON DELETE CASCADE ,
CONSTRAINT fk_inv_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_inv_user FOREIGN KEY ( user_id ) REFERENCES users ( id ) ,
CONSTRAINT ck_inv_qty_non_negative CHECK ( quantity > = 0 )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 库存 ' ;
CREATE TABLE IF NOT EXISTS product_images (
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 ) NULL COMMENT ' 内容哈希(去重) ' ,
sort_order INT NOT NULL DEFAULT 0 ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_product_image_hash ( product_id , hash ) ,
KEY idx_product_images_product ( product_id ) ,
CONSTRAINT fk_pimg_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_pimg_user FOREIGN KEY ( user_id ) REFERENCES users ( id ) ,
CONSTRAINT fk_pimg_product FOREIGN KEY ( product_id ) REFERENCES products ( id ) ON DELETE CASCADE
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 商品图片 ' ;
-- =====================================================================
-- 往来单位与账户
-- =====================================================================
CREATE TABLE IF NOT EXISTS customers (
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 ) NULL ,
level VARCHAR ( 32 ) NULL COMMENT ' 客户等级标签 ' ,
price_level ENUM ( ' retail ' , ' distribution ' , ' wholesale ' , ' big_client ' ) NOT NULL DEFAULT ' retail ' COMMENT ' 默认售价列 ' ,
status TINYINT UNSIGNED NOT NULL DEFAULT 1 ,
remark VARCHAR ( 255 ) NULL ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
KEY idx_customers_shop ( shop_id ) ,
KEY idx_customers_phone ( phone ) ,
CONSTRAINT fk_customers_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_customers_user FOREIGN KEY ( user_id ) REFERENCES users ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 客户 ' ;
CREATE TABLE IF NOT EXISTS suppliers (
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 ) NULL ,
status TINYINT UNSIGNED NOT NULL DEFAULT 1 ,
remark VARCHAR ( 255 ) NULL ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
KEY idx_suppliers_shop ( shop_id ) ,
KEY idx_suppliers_phone ( phone ) ,
CONSTRAINT fk_suppliers_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_suppliers_user FOREIGN KEY ( user_id ) REFERENCES users ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 供应商 ' ;
CREATE TABLE IF NOT EXISTS accounts (
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 DEFAULT ' cash ' ,
balance DECIMAL ( 18 , 2 ) NOT NULL DEFAULT 0 ,
status TINYINT UNSIGNED NOT NULL DEFAULT 1 ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_accounts_shop_name ( shop_id , name ) ,
KEY idx_accounts_shop ( shop_id ) ,
CONSTRAINT fk_accounts_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_accounts_user FOREIGN KEY ( user_id ) REFERENCES users ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 结算账户 ' ;
-- =====================================================================
-- 单据域(销售/进货/其他收支/流水)
-- =====================================================================
CREATE TABLE IF NOT EXISTS sales_orders (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT ,
shop_id BIGINT UNSIGNED NOT NULL ,
user_id BIGINT UNSIGNED NOT NULL COMMENT ' 创建人 ' ,
customer_id BIGINT UNSIGNED NULL ,
order_no VARCHAR ( 32 ) NOT NULL ,
order_time DATETIME NOT NULL ,
status ENUM ( ' draft ' , ' approved ' , ' returned ' , ' void ' ) NOT NULL DEFAULT ' draft ' ,
amount DECIMAL ( 18 , 2 ) NOT NULL DEFAULT 0 COMMENT ' 应收合计 ' ,
paid_amount DECIMAL ( 18 , 2 ) NOT NULL DEFAULT 0 COMMENT ' 已收合计 ' ,
remark VARCHAR ( 255 ) NULL ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_sales_order_no ( shop_id , order_no ) ,
KEY idx_sales_shop_time ( shop_id , order_time ) ,
KEY idx_sales_customer ( customer_id ) ,
CONSTRAINT fk_sales_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_sales_user FOREIGN KEY ( user_id ) REFERENCES users ( id ) ,
CONSTRAINT fk_sales_customer FOREIGN KEY ( customer_id ) REFERENCES customers ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 销售单 ' ;
CREATE TABLE IF NOT EXISTS sales_order_items (
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 DEFAULT 0 COMMENT ' 折扣百分比0-100 ' ,
amount DECIMAL ( 18 , 2 ) NOT NULL ,
PRIMARY KEY ( id ) ,
KEY idx_soi_order ( order_id ) ,
KEY idx_soi_product ( product_id ) ,
CONSTRAINT fk_soi_order FOREIGN KEY ( order_id ) REFERENCES sales_orders ( id ) ON DELETE CASCADE ,
CONSTRAINT fk_soi_product FOREIGN KEY ( product_id ) REFERENCES products ( id ) ,
CONSTRAINT ck_soi_qty CHECK ( quantity > 0 ) ,
CONSTRAINT ck_soi_price CHECK ( unit_price > = 0 ) ,
CONSTRAINT ck_soi_discount CHECK ( discount_rate > = 0 AND discount_rate < = 100 )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 销售单明细 ' ;
CREATE TABLE IF NOT EXISTS purchase_orders (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT ,
shop_id BIGINT UNSIGNED NOT NULL ,
user_id BIGINT UNSIGNED NOT NULL ,
supplier_id BIGINT UNSIGNED NULL ,
order_no VARCHAR ( 32 ) NOT NULL ,
order_time DATETIME NOT NULL ,
status ENUM ( ' draft ' , ' approved ' , ' void ' ) NOT NULL DEFAULT ' draft ' ,
amount DECIMAL ( 18 , 2 ) NOT NULL DEFAULT 0 COMMENT ' 应付合计 ' ,
paid_amount DECIMAL ( 18 , 2 ) NOT NULL DEFAULT 0 COMMENT ' 已付合计 ' ,
remark VARCHAR ( 255 ) NULL ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_purchase_order_no ( shop_id , order_no ) ,
KEY idx_purchase_shop_time ( shop_id , order_time ) ,
KEY idx_purchase_supplier ( supplier_id ) ,
CONSTRAINT fk_purchase_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_purchase_user FOREIGN KEY ( user_id ) REFERENCES users ( id ) ,
CONSTRAINT fk_purchase_supplier FOREIGN KEY ( supplier_id ) REFERENCES suppliers ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 进货单 ' ;
CREATE TABLE IF NOT EXISTS purchase_order_items (
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 ,
PRIMARY KEY ( id ) ,
KEY idx_poi_order ( order_id ) ,
KEY idx_poi_product ( product_id ) ,
CONSTRAINT fk_poi_order FOREIGN KEY ( order_id ) REFERENCES purchase_orders ( id ) ON DELETE CASCADE ,
CONSTRAINT fk_poi_product FOREIGN KEY ( product_id ) REFERENCES products ( id ) ,
CONSTRAINT ck_poi_qty CHECK ( quantity > 0 ) ,
CONSTRAINT ck_poi_price CHECK ( unit_price > = 0 )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 进货单明细 ' ;
CREATE TABLE IF NOT EXISTS payments (
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 NULL COMMENT ' 业务表ID: sales_orders/purchase_orders/other_transactions ' ,
account_id BIGINT UNSIGNED NOT NULL ,
direction ENUM ( ' in ' , ' out ' ) NOT NULL COMMENT ' 收款/付款 ' ,
amount DECIMAL ( 18 , 2 ) NOT NULL ,
pay_time DATETIME NOT NULL ,
remark VARCHAR ( 255 ) NULL ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY ( id ) ,
KEY idx_payments_shop_time ( shop_id , pay_time ) ,
KEY idx_payments_biz ( biz_type , biz_id ) ,
CONSTRAINT fk_payments_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_payments_user FOREIGN KEY ( user_id ) REFERENCES users ( id ) ,
CONSTRAINT fk_payments_account FOREIGN KEY ( account_id ) REFERENCES accounts ( id ) ,
CONSTRAINT ck_payments_amount CHECK ( amount > 0 )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 收付款记录 ' ;
CREATE TABLE IF NOT EXISTS other_transactions (
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 ) NULL COMMENT ' customer/supplier/other ' ,
counterparty_id BIGINT UNSIGNED NULL ,
account_id BIGINT UNSIGNED NOT NULL ,
amount DECIMAL ( 18 , 2 ) NOT NULL ,
tx_time DATETIME NOT NULL ,
remark VARCHAR ( 255 ) NULL ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
KEY idx_ot_shop_time ( shop_id , tx_time ) ,
KEY idx_ot_account ( account_id ) ,
CONSTRAINT fk_ot_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_ot_user FOREIGN KEY ( user_id ) REFERENCES users ( id ) ,
CONSTRAINT fk_ot_account FOREIGN KEY ( account_id ) REFERENCES accounts ( id ) ,
CONSTRAINT ck_ot_amount CHECK ( amount > 0 )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 其他收入/支出 ' ;
-- =====================================================================
-- 配件查询与审核、附件
-- =====================================================================
CREATE TABLE IF NOT EXISTS part_submissions (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT ,
shop_id BIGINT UNSIGNED NOT NULL ,
user_id BIGINT UNSIGNED NOT NULL ,
model_unique VARCHAR ( 128 ) NOT NULL COMMENT ' 型号(唯一) ' ,
brand VARCHAR ( 64 ) NULL ,
spec VARCHAR ( 128 ) NULL ,
size VARCHAR ( 64 ) NULL ,
aperture VARCHAR ( 64 ) NULL ,
compatible TEXT NULL COMMENT ' 适配信息 ' ,
status ENUM ( ' draft ' , ' pending ' , ' rejected ' , ' published ' ) NOT NULL DEFAULT ' pending ' ,
reason VARCHAR ( 255 ) NULL COMMENT ' 驳回原因 ' ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
deleted_at DATETIME NULL ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_part_model_unique ( model_unique ) ,
KEY idx_part_submissions_shop ( shop_id ) ,
CONSTRAINT fk_part_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_part_user FOREIGN KEY ( user_id ) REFERENCES users ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 配件数据提交(审核) ' ;
CREATE TABLE IF NOT EXISTS attachments (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT ,
shop_id BIGINT UNSIGNED NULL COMMENT ' 全局资源可空,本地资源属于租户 ' ,
user_id BIGINT UNSIGNED NULL ,
owner_type VARCHAR ( 32 ) NOT NULL COMMENT ' 资源归属类型: product/part_submission/global_sku/... ' ,
owner_id BIGINT UNSIGNED NOT NULL ,
url VARCHAR ( 512 ) NOT NULL ,
hash VARCHAR ( 64 ) NULL ,
meta JSON NULL ,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY ( id ) ,
UNIQUE KEY ux_attachments_hash ( hash ) ,
KEY idx_attachments_owner ( owner_type , owner_id ) ,
CONSTRAINT fk_att_shop FOREIGN KEY ( shop_id ) REFERENCES shops ( id ) ,
CONSTRAINT fk_att_user FOREIGN KEY ( user_id ) REFERENCES users ( id )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = ' 通用附件(图片等) ' ;
-- =====================================================================
-- 触发器:维护 products.search_text 聚合字段
-- =====================================================================
DROP TRIGGER IF EXISTS trg_products_ai ;
DELIMITER $ $
CREATE TRIGGER trg_products_ai AFTER INSERT ON products
FOR EACH ROW
BEGIN
UPDATE products
SET search_text = CONCAT_WS ( ' ' , NEW . name , NEW . brand , NEW . model , NEW . spec )
WHERE id = NEW . id ;
END $ $
DELIMITER ;
DROP TRIGGER IF EXISTS trg_products_au ;
DELIMITER $ $
CREATE TRIGGER trg_products_au BEFORE UPDATE ON products
FOR EACH ROW
BEGIN
SET NEW . search_text = CONCAT_WS ( ' ' , NEW . name , NEW . brand , NEW . model , NEW . spec ) ;
END $ $
DELIMITER ;
-- 当别名变化时重建 search_text( 名称/品牌/型号/规格 + 所有别名)
DROP TRIGGER IF EXISTS trg_palias_ai ;
DELIMITER $ $
CREATE TRIGGER trg_palias_ai AFTER INSERT ON product_aliases
FOR EACH ROW
BEGIN
UPDATE products p
JOIN (
SELECT pa . product_id , GROUP_CONCAT ( pa . alias SEPARATOR ' ' ) AS aliases
FROM product_aliases pa
WHERE pa . product_id = NEW . product_id AND pa . deleted_at IS NULL
GROUP BY pa . product_id
) a ON a . product_id = p . id
SET p . search_text = CONCAT_WS ( ' ' , p . name , p . brand , p . model , p . spec , a . aliases )
WHERE p . id = NEW . product_id ;
END $ $
DELIMITER ;
DROP TRIGGER IF EXISTS trg_palias_au ;
DELIMITER $ $
CREATE TRIGGER trg_palias_au AFTER UPDATE ON product_aliases
FOR EACH ROW
BEGIN
UPDATE products p
JOIN (
SELECT pa . product_id , GROUP_CONCAT ( pa . alias SEPARATOR ' ' ) AS aliases
FROM product_aliases pa
WHERE pa . product_id = NEW . product_id AND pa . deleted_at IS NULL
GROUP BY pa . product_id
) a ON a . product_id = p . id
SET p . search_text = CONCAT_WS ( ' ' , p . name , p . brand , p . model , p . spec , a . aliases )
WHERE p . id = NEW . product_id ;
END $ $
DELIMITER ;
DROP TRIGGER IF EXISTS trg_palias_ad ;
DELIMITER $ $
CREATE TRIGGER trg_palias_ad AFTER DELETE ON product_aliases
FOR EACH ROW
BEGIN
UPDATE products p
LEFT JOIN (
SELECT pa . product_id , GROUP_CONCAT ( pa . alias SEPARATOR ' ' ) AS aliases
FROM product_aliases pa
WHERE pa . product_id = OLD . product_id AND pa . deleted_at IS NULL
GROUP BY pa . product_id
) a ON a . product_id = p . id
SET p . search_text = CONCAT_WS ( ' ' , p . name , p . brand , p . model , p . spec , COALESCE ( a . aliases , ' ' ) )
WHERE p . id = OLD . product_id ;
END $ $
DELIMITER ;
SET FOREIGN_KEY_CHECKS = 1 ;