-- ===================================================================== -- 配件查询 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;