diff --git a/scripts/init.sql b/scripts/init.sql index 975c69b8..6e451d96 100644 --- a/scripts/init.sql +++ b/scripts/init.sql @@ -16,7 +16,7 @@ CREATE TABLE revoked_tokens (jti VARCHAR(64) PRIMARY KEY, user_id BIGINT NOT NUL CREATE TABLE material_types (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(100) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_material_types_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE material_categories (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(100) NOT NULL, description VARCHAR(1000) NOT NULL DEFAULT '', parent_id BIGINT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_material_categories_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT, FOREIGN KEY(parent_id) REFERENCES material_categories(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE units (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(100) NOT NULL, precision_scale TINYINT NOT NULL DEFAULT 0, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_units_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -CREATE TABLE materials (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_code VARCHAR(50) NOT NULL, name VARCHAR(200) NOT NULL, material_image VARCHAR(500), spec VARCHAR(200), manufacturer_model VARCHAR(200), source VARCHAR(20) NULL COMMENT '物料来源:外购或外协', remark VARCHAR(500) NULL COMMENT '物料备注,最多500字', supplier_id BIGINT NULL COMMENT 'Default supplier organization_partner ID', manufacturer_id BIGINT NULL COMMENT 'Manufacturer organization_partner ID', material_type_id BIGINT, category_id BIGINT NOT NULL, base_unit_id BIGINT, unit VARCHAR(20), status VARCHAR(20) NOT NULL DEFAULT 'active', version INT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_materials_org_code(organization_id,material_code), KEY idx_materials_category(category_id), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT, FOREIGN KEY(material_type_id) REFERENCES material_types(id) ON DELETE RESTRICT, FOREIGN KEY(category_id) REFERENCES material_categories(id) ON DELETE RESTRICT, FOREIGN KEY(base_unit_id) REFERENCES units(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE materials (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_code VARCHAR(50) NOT NULL, name VARCHAR(200) NOT NULL, material_image VARCHAR(500), spec VARCHAR(200), source VARCHAR(20) NULL COMMENT '物料来源:外购或外协', remark VARCHAR(500) NULL COMMENT '物料备注,最多500字', material_type_id BIGINT, category_id BIGINT NOT NULL, base_unit_id BIGINT, unit VARCHAR(20), status VARCHAR(20) NOT NULL DEFAULT 'active', version INT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_materials_org_code(organization_id,material_code), KEY idx_materials_category(category_id), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT, FOREIGN KEY(material_type_id) REFERENCES material_types(id) ON DELETE RESTRICT, FOREIGN KEY(category_id) REFERENCES material_categories(id) ON DELETE RESTRICT, FOREIGN KEY(base_unit_id) REFERENCES units(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE code_rules (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, category_id BIGINT NOT NULL, prefix VARCHAR(30) NOT NULL, sequence_length TINYINT NOT NULL, next_sequence BIGINT NOT NULL DEFAULT 1, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_code_rules_org_category(organization_id,category_id), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(category_id) REFERENCES material_categories(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE material_import_logs (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, operator VARCHAR(100) NOT NULL, file_name VARCHAR(255) NOT NULL, total_rows INT NOT NULL DEFAULT 0, success_rows INT NOT NULL DEFAULT 0, failed_rows INT NOT NULL DEFAULT 0, status VARCHAR(20) NOT NULL, failure_reason VARCHAR(1000) NOT NULL DEFAULT '', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_material_import_logs_org_created(organization_id,created_at), FOREIGN KEY(organization_id) REFERENCES organizations(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE material_unit_conversions (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_id BIGINT NOT NULL, from_unit_id BIGINT NOT NULL, to_unit_id BIGINT NOT NULL, conversion_rate DECIMAL(20,6) NOT NULL, effective_from DATE NOT NULL, effective_to DATE NULL, KEY idx_material_unit_conversions_material(organization_id,material_id), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(material_id) REFERENCES materials(id), FOREIGN KEY(from_unit_id) REFERENCES units(id), FOREIGN KEY(to_unit_id) REFERENCES units(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; @@ -24,14 +24,12 @@ CREATE TABLE tags (id BIGINT AUTO_INCREMENT PRIMARY KEY, material_id BIGINT NOT CREATE TABLE business_partners (id BIGINT AUTO_INCREMENT PRIMARY KEY, legal_name VARCHAR(200) NOT NULL, unified_credit_code VARCHAR(50) UNIQUE, contact_name VARCHAR(100), contact_phone VARCHAR(50), address VARCHAR(500), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- Partner categories support selecting material and finished_product together; -- an empty set means unclassified. Both fields are created with the table. -CREATE TABLE organization_partners (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, business_partner_id BIGINT NOT NULL, partner_code VARCHAR(50) NOT NULL, is_supplier BOOLEAN NOT NULL DEFAULT FALSE, is_manufacturer BOOLEAN NOT NULL DEFAULT FALSE, is_customer BOOLEAN NOT NULL DEFAULT FALSE, supplier_status VARCHAR(20), supplier_categories SET('material','finished_product') NOT NULL DEFAULT '', customer_status VARCHAR(20), customer_categories SET('material','finished_product') NOT NULL DEFAULT '', settlement_currency CHAR(3), payment_terms VARCHAR(200), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_organization_partners_org_id(organization_id,id), UNIQUE KEY uk_organization_partners_org_partner(organization_id,business_partner_id), UNIQUE KEY uk_organization_partners_org_code(organization_id,partner_code), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(business_partner_id) REFERENCES business_partners(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; --- Partner references must belong to the same organization as the material. -ALTER TABLE materials - ADD KEY idx_materials_org_supplier(organization_id,supplier_id), - ADD KEY idx_materials_org_manufacturer(organization_id,manufacturer_id), - ADD CONSTRAINT fk_materials_supplier FOREIGN KEY(organization_id,supplier_id) REFERENCES organization_partners(organization_id,id) ON DELETE RESTRICT ON UPDATE RESTRICT, - ADD CONSTRAINT fk_materials_manufacturer FOREIGN KEY(organization_id,manufacturer_id) REFERENCES organization_partners(organization_id,id) ON DELETE RESTRICT ON UPDATE RESTRICT; -CREATE TABLE material_suppliers (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_id BIGINT NOT NULL, organization_partner_id BIGINT NOT NULL, price DECIMAL(20,6) NOT NULL, currency CHAR(3) NOT NULL, minimum_order_quantity DECIMAL(20,6) NOT NULL DEFAULT 0, lead_time_days INT NOT NULL DEFAULT 0, is_preferred BOOLEAN NOT NULL DEFAULT FALSE, effective_from DATE NOT NULL, effective_to DATE, status VARCHAR(20) NOT NULL DEFAULT 'active', KEY idx_material_suppliers_material(organization_id,material_id), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(material_id) REFERENCES materials(id), FOREIGN KEY(organization_partner_id) REFERENCES organization_partners(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE organization_partners (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, business_partner_id BIGINT NOT NULL, partner_code VARCHAR(50) NOT NULL, is_supplier BOOLEAN NOT NULL DEFAULT FALSE, is_manufacturer BOOLEAN NOT NULL DEFAULT FALSE, is_customer BOOLEAN NOT NULL DEFAULT FALSE, supplier_status VARCHAR(20), supplier_categories SET('material','finished_product') NOT NULL DEFAULT '', customer_status VARCHAR(20), customer_categories SET('material','finished_product') NOT NULL DEFAULT '', settlement_currency CHAR(3), payment_terms VARCHAR(200), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_organization_partners_org_partner(organization_id,business_partner_id), UNIQUE KEY uk_organization_partners_org_code(organization_id,partner_code), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(business_partner_id) REFERENCES business_partners(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +-- 物料与制造商关系表:某物料有哪些可用制造商,以及各自的厂家型号 +-- 本表 manufacturer_id 引用 organization_partners,故建在其后。 +CREATE TABLE material_manufacturers (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_id BIGINT NOT NULL, manufacturer_id BIGINT NOT NULL COMMENT 'organization_partners ID', model VARCHAR(200) NULL COMMENT '厂家型号', status VARCHAR(20) NOT NULL DEFAULT 'active', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_material_manufacturers_dim(organization_id,material_id,manufacturer_id), FOREIGN KEY(material_id) REFERENCES materials(id), FOREIGN KEY(manufacturer_id) REFERENCES organization_partners(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +-- 物料与供应商关系表:每条记录一个货源(某供应商供某物料、供的是哪个制造商条目)及其采购条件 +CREATE TABLE material_suppliers (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_id BIGINT NOT NULL, supplier_id BIGINT NULL COMMENT 'organization_partners ID', material_manufacturer_id BIGINT NULL COMMENT 'material_manufacturers ID', price DECIMAL(20,6) NULL, currency CHAR(3) NULL, minimum_order_quantity DECIMAL(20,6) NOT NULL DEFAULT 0, lead_time_days INT NOT NULL DEFAULT 0, is_preferred BOOLEAN NOT NULL DEFAULT FALSE, status VARCHAR(20) NOT NULL DEFAULT 'active', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, supplier_key BIGINT GENERATED ALWAYS AS (COALESCE(supplier_id, 0)) VIRTUAL, manufacturer_entry_key BIGINT GENERATED ALWAYS AS (COALESCE(material_manufacturer_id, 0)) VIRTUAL, preferred_row BIGINT GENERATED ALWAYS AS (CASE WHEN is_preferred AND status = 'active' THEN 1 ELSE NULL END) VIRTUAL, UNIQUE KEY uk_material_suppliers_dim(organization_id,material_id,supplier_key,manufacturer_entry_key), UNIQUE KEY uk_material_suppliers_preferred(organization_id,material_id,preferred_row), CONSTRAINT chk_material_suppliers_dimension CHECK (supplier_id IS NOT NULL OR material_manufacturer_id IS NOT NULL), KEY idx_material_suppliers_supplier(organization_id,supplier_key), KEY idx_material_suppliers_manufacturer(organization_id,manufacturer_entry_key), FOREIGN KEY(material_id) REFERENCES materials(id), FOREIGN KEY(supplier_id) REFERENCES organization_partners(id), FOREIGN KEY(material_manufacturer_id) REFERENCES material_manufacturers(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE warehouses (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(150) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_warehouses_org_code(organization_id,code), UNIQUE KEY uk_warehouses_organization(organization_id), FOREIGN KEY(organization_id) REFERENCES organizations(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE warehouse_locations (id BIGINT AUTO_INCREMENT PRIMARY KEY, warehouse_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(150) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_warehouse_locations_warehouse_code(warehouse_id,code), UNIQUE KEY uk_warehouse_locations_warehouse(warehouse_id), FOREIGN KEY(warehouse_id) REFERENCES warehouses(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE user_warehouses (user_id BIGINT NOT NULL, warehouse_id BIGINT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(user_id,warehouse_id), FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(warehouse_id) REFERENCES warehouses(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; @@ -657,8 +655,20 @@ SET @p_wh_034 = (SELECT id FROM organization_partners WHERE organization_id=@org -- 源里没有 drawing_no 的落点(目标表无此列),两库该字段本来就全空。 -- spec 与 unit 为空时写空串而不是 NULL:后端把这两列直接 Scan 进 string -- (不是 sql.NullString),写 NULL 会让物料列表/详情接口 500。 + +-- ---------- 物料:经临时表中转,分流到 materials 与货源表 ---------- +-- 源字面量含 supplier_id / manufacturer_id 两列,而 materials 已删掉它们, +-- 因此原样进临时表,再分别流向 materials 与货源表。见设计文档 7.2。 +DROP TEMPORARY TABLE IF EXISTS tmp_materials; +CREATE TEMPORARY TABLE tmp_materials ( + organization_id BIGINT, material_code VARCHAR(50), name VARCHAR(200), + spec VARCHAR(200), source VARCHAR(20), remark VARCHAR(500), + supplier_id BIGINT, manufacturer_id BIGINT, category_id BIGINT, + base_unit_id BIGINT, unit VARCHAR(20), status VARCHAR(20), + version INT, created_at DATETIME, updated_at DATETIME +) DEFAULT CHARSET=utf8mb4; -- ===== 北京:16 条 ===== -INSERT INTO materials +INSERT INTO tmp_materials (organization_id, material_code, name, spec, source, remark, supplier_id, manufacturer_id, category_id, base_unit_id, unit, status, version, created_at, updated_at) @@ -726,16 +736,10 @@ VALUES (@org_bj, 'A040211', 'LJJ镊子从端-006', '', '外协', NULL, NULL, NULL, @c_bj_A04, @u_bj_pcs, 'Pcs', - 'active', 1, '2026-10-10 07:54:29', '2026-10-10 07:54:57') -AS new -ON DUPLICATE KEY UPDATE - name = new.name, spec = new.spec, source = new.source, remark = new.remark, - supplier_id = new.supplier_id, manufacturer_id = new.manufacturer_id, - category_id = new.category_id, base_unit_id = new.base_unit_id, unit = new.unit, - status = new.status, updated_at = new.updated_at; + 'active', 1, '2026-10-10 07:54:29', '2026-10-10 07:54:57'); -- ===== 武汉:183 条 ===== -INSERT INTO materials +INSERT INTO tmp_materials (organization_id, material_code, name, spec, source, remark, supplier_id, manufacturer_id, category_id, base_unit_id, unit, status, version, created_at, updated_at) @@ -1490,13 +1494,43 @@ LF-3070A', NULL, '单台数量:1; 物料等级:B; 存储条件:存放于室内 (@org_wh, 'F010003', '按键模组', '', NULL, NULL, NULL, NULL, @c_wh_F01, @u_wh_pcs, '件', - 'active', 1, '2026-10-13 07:27:22', '2026-10-13 07:27:22') -AS new + 'active', 1, '2026-10-13 07:27:22', '2026-10-13 07:27:22'); + +-- 分流 1:物料本体 +INSERT INTO materials + (organization_id, material_code, name, spec, source, remark, + category_id, base_unit_id, unit, status, version, created_at, updated_at) +SELECT t.organization_id, t.material_code, t.name, t.spec, t.source, t.remark, + t.category_id, t.base_unit_id, t.unit, t.status, t.version, t.created_at, t.updated_at + FROM tmp_materials t ON DUPLICATE KEY UPDATE - name = new.name, spec = new.spec, source = new.source, remark = new.remark, - supplier_id = new.supplier_id, manufacturer_id = new.manufacturer_id, - category_id = new.category_id, base_unit_id = new.base_unit_id, unit = new.unit, - status = new.status, updated_at = new.updated_at; + name = t.name, spec = t.spec, source = t.source, remark = t.remark, + category_id = t.category_id, base_unit_id = t.base_unit_id, unit = t.unit, + status = t.status, updated_at = t.updated_at; + +-- 分流 2:制造商条目(有制造商的物料) +INSERT INTO material_manufacturers (organization_id, material_id, manufacturer_id, status) +SELECT t.organization_id, m.id, t.manufacturer_id, 'active' + FROM tmp_materials t + JOIN materials m ON m.organization_id = t.organization_id + AND m.material_code = t.material_code + WHERE t.manufacturer_id IS NOT NULL +ON DUPLICATE KEY UPDATE status = material_manufacturers.status; + +-- 分流 3:货源(有供应商或有制造商的物料) +INSERT INTO material_suppliers + (organization_id, material_id, supplier_id, material_manufacturer_id, is_preferred, status) +SELECT t.organization_id, m.id, t.supplier_id, mm.id, TRUE, 'active' + FROM tmp_materials t + JOIN materials m ON m.organization_id = t.organization_id + AND m.material_code = t.material_code + LEFT JOIN material_manufacturers mm ON mm.organization_id = t.organization_id + AND mm.material_id = m.id + AND mm.manufacturer_id = t.manufacturer_id + WHERE t.supplier_id IS NOT NULL OR t.manufacturer_id IS NOT NULL +ON DUPLICATE KEY UPDATE is_preferred = material_suppliers.is_preferred; + +DROP TEMPORARY TABLE tmp_materials; -- ---------- 变更日志 → material_logs ---------- -- 源表没有操作类型,统一记作 update;修改原因进 description。