Task 1 实现与评审暴露出文档和计划里的若干错误,据实修正: - 设计文档 6.2 / 6.4:preferred_row 生成列由 `THEN id` 改为 `THEN 1`。 原写法既被 MySQL 拒绝(生成列不能引用自增列,ERROR 3109),语义上也 失效——每行 id 各不相同,唯一键永不冲突,「每物料至多一条首选」形同 虚设。并补注它与 user_organizations 用 `THEN user_id` 的场合差异: 那张表没有自增列,用的是业务键。 - 设计文档 6.1 / 6.2、计划 Task 1:两张关系表补 `FOREIGN KEY(organization_id) REFERENCES organizations(id)`。改造前的 material_suppliers 旧定义本就有这条单列外键,重写时漏掉了,而项目里 所有按组织分表的表都有它。 - 计划 Global Constraints:删除「init.sql 必须可重复执行」这一错误约束。 脚本本就不幂等(普通建表没有 IF NOT EXISTS、schema_migrations 的 INSERT 也没有幂等,第二次执行在第 8 行即中止),改为只要求新增的分流 语句自身幂等。 - 计划 Task 1 Step 5:material_manufacturers 建表位置改为 organization_partners 之后(原位置触发前向外键 ERROR 1824)。 - 计划 Task 1 Step 7:临时表补 DEFAULT CHARSET=utf8mb4(不补会因排序 规则与持久表不一致报 ERROR 1267)。 - 计划 Task 1 Step 13:改为在 scratch 库重放三条分流语句验证幂等,不再 要求整脚本重跑。
24 KiB
物料多供应商与多制造商:数据库改造实施计划
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 把 materials 上的单值供应商/制造商改造为「物料 × 供应商 × 制造商」的三元货源模型,并在 init.sql 中完成 161 条货源数据的分流回填。
Architecture: 只改 scripts/init.sql 一个文件。新增 material_manufacturers 表,改造现有空表 material_suppliers,materials 删掉三个字段。为避免逐行修改 199 行数据字面量,用一张临时表中转:199 行原样进临时表,再分流到三张目标表。
Tech Stack: MySQL 8.0、单文件初始化脚本 scripts/init.sql(由 MySQL 官方镜像的 /docker-entrypoint-initdb.d/ 机制执行,整个文件在同一连接内,临时表可用)。
Spec: docs/superpowers/specs/2026-10-15-material-supplier-manufacturer-design.md
Global Constraints
-
MySQL 8.0,
CHECK约束依赖 8.0.16+。 -
字符集统一
utf8mb4/utf8mb4_unicode_ci。 -
所有外键均为单列,不引入任何复合外键(spec 的 D7)。公司隔离与物料一致性由应用层校验。
-
status取值固定为active/obsolete(D8),不用数字。 -
货源表表名沿用
material_suppliers,不改名(D5)。 -
init.sql本来就是一次性脚本,不保证可重复执行:普通建表没有IF NOT EXISTS,schema_migrations的 INSERT 也没有幂等处理,第二次执行会在第 8 行以Table 'users' already exists中止。本任务只要求新增的分流语句自身幂等,不要求整脚本幂等,也不要顺手去改既有的建表语句——那是另一个话题。 -
验证一律使用隔离库,绝不碰真实库。本机 MySQL 在 3306,账号
root/ 密码1234。 -
init.sql不能靠「指定库名」来隔离:它第 5-6 行硬编码了CREATE DATABASE IF NOT EXISTS wmms;与USE wmms;,脚本会自己切回wmms,把表建到真实库上去。必须先用sed把这两行的库名替换掉,再喂给 mysql:mysql -u root -p1234 -e "DROP DATABASE IF EXISTS wms_verify;" sed -e "s/CREATE DATABASE IF NOT EXISTS wmms/CREATE DATABASE IF NOT EXISTS wms_verify/" \ -e "s/^USE wmms;/USE wms_verify;/" scripts/init.sql | mysql -u root -p1234本计划下面每个验证步骤都已按这个写法给出。另外注意:mysql 在批处理模式下遇到 SQL 错误会中止,所以脚本跑挂时不用担心半途污染——但也意味着报错后必须从头重跑。
-
仓库里
scripts/upgrades/目录不存在,没有增量升级路径——改完只能删库用init.sql重建。
本计划的文件改动总览
| 文件 | 职责 |
|---|---|
scripts/init.sql |
唯一被改的文件。表结构定义 + 数据分流 + 货源回填全在这里 |
以下行号均以改造前的 scripts/init.sql 为准。
Task 1: 表结构改造 + 基础货源派生(129 条)
交付物: init.sql 在空库上完整跑通,materials 199 条、material_manufacturers 10 条、material_suppliers 129 条。
Files:
- Modify:
scripts/init.sql(CREATE TABLE materials约:19;ALTER TABLE materials:30-33;CREATE TABLE organization_partners约:27;CREATE TABLE material_suppliers:34;两处物料INSERT约:661与:738)
Interfaces:
-
Produces: 表
material_manufacturers(organization_id, material_id, manufacturer_id, model, status, ...);表material_suppliers(organization_id, material_id, supplier_id, material_manufacturer_id, price, currency, minimum_order_quantity, lead_time_days, is_preferred, status, ...)。Task 2 依赖这两张表的列名与唯一键。 -
Step 1: 建立隔离验证库
改造前先跑通一次基线,确认验证流程可用:
mysql -u root -p1234 -e "DROP DATABASE IF EXISTS wms_verify;"
sed -e "s/CREATE DATABASE IF NOT EXISTS wmms/CREATE DATABASE IF NOT EXISTS wms_verify/" -e "s/^USE wmms;/USE wms_verify;/" scripts/init.sql | mysql -u root -p1234
mysql -u root -p1234 wms_verify -e "SELECT COUNT(*) AS materials FROM materials;"
预期:输出 199。若这一步就失败,先解决环境问题,不要往下走。
- Step 2: 改
CREATE TABLE materials,删掉三个字段
在 scripts/init.sql:19 那一行的建表语句中,删除三段列定义:
manufacturer_model VARCHAR(200),
supplier_id BIGINT NULL COMMENT 'Default supplier organization_partner ID',
manufacturer_id BIGINT NULL COMMENT 'Manufacturer organization_partner ID',
删完该行应为(其余列保持原样,此处只显示关键区段):
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;
- Step 3: 删除
ALTER TABLE materials整段
删除 scripts/init.sql:29-33 整段(4 行 ALTER TABLE materials ADD ...:两个索引、两个复合外键)。这段是为旧的两列加的,列已删,整段作废。
- Step 4: 删除
uk_organization_partners_org_id
在 CREATE TABLE organization_partners(约 :27)中删掉:
UNIQUE KEY uk_organization_partners_org_id(organization_id,id),
注意只删这一个唯一键,另外两个(uk_organization_partners_org_partner、uk_organization_partners_org_code)保留。
- Step 5: 新建
material_manufacturers
插在 CREATE TABLE organization_partners 之后、CREATE TABLE material_suppliers 之前(首行注释不可省,它标明这是一张关系表):
不能紧接
CREATE TABLE materials:本表含FOREIGN KEY(manufacturer_id) REFERENCES organization_partners(id),而organization_partners创建在更后面;MySQL 不允许前向外键,实测ERROR 1824: Failed to open the referenced table 'organization_partners'。该脚本里也没有SET FOREIGN_KEY_CHECKS=0,所以只能靠调整建表顺序解决。
-- 物料与制造商关系表:某物料有哪些可用制造商,以及各自的厂家型号
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(organization_id) REFERENCES organizations(id), FOREIGN KEY(material_id) REFERENCES materials(id), FOREIGN KEY(manufacturer_id) REFERENCES organization_partners(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- Step 6: 用改造后的定义替换
CREATE TABLE material_suppliers
把 scripts/init.sql:34 整行替换为(首行注释不可省,它标明这是一张关系表):
-- 物料与供应商关系表:每条记录一个货源(某供应商供某物料、供的是哪个制造商条目)及其采购条件
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(organization_id) REFERENCES organizations(id), 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;
- Step 7: 在物料 INSERT 之前建临时表
在 -- ---------- 物料 ---------- 注释块(约 :654)之后、第一条物料 INSERT(约 :661)之前插入:
-- ---------- 物料:经临时表中转,分流到 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
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- Step 8: 把两处物料 INSERT 的目标表改成临时表
北京段(约 :661):把 INSERT INTO materials 改为 INSERT INTO tmp_materials,列清单与 16 行 VALUES 一字不动。
武汉段(约 :738):同样把 INSERT INTO materials 改为 INSERT INTO tmp_materials,183 行 VALUES 一字不动。
- Step 9: 删掉两处
AS new/ON DUPLICATE KEY UPDATE
两处 INSERT 的结尾(约 :730-735 与 :1494-1499)目前形如:
'active', 1, '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;
改为让 VALUES 直接以分号结束:
'active', 1, '2026-10-10 07:54:57');
临时表是空的,不需要幂等处理;幂等由下一步的分流语句承担。
- Step 10: 加分流语句(三张表)并删除临时表
紧接武汉段 INSERT 结束之后插入:
-- 分流 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 = 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;
三条顺序不可调换:分流 2 依赖分流 1 已写入 materials(要 JOIN 出 material_id),分流 3 依赖分流 2 已写入 material_manufacturers(要 JOIN 出 material_manufacturer_id)。
- Step 11: 隔离库重建并核对数量
mysql -u root -p1234 -e "DROP DATABASE IF EXISTS wms_verify;"
sed -e "s/CREATE DATABASE IF NOT EXISTS wmms/CREATE DATABASE IF NOT EXISTS wms_verify/" -e "s/^USE wmms;/USE wms_verify;/" scripts/init.sql | mysql -u root -p1234
mysql -u root -p1234 wms_verify -e "
SELECT (SELECT COUNT(*) FROM materials) AS materials,
(SELECT COUNT(*) FROM material_manufacturers) AS manufacturers,
(SELECT COUNT(*) FROM material_suppliers) AS sources;"
预期输出:
materials manufacturers sources
199 10 129
- Step 12: 抽查货源的配对是否正确
mysql -u root -p1234 wms_verify -e "
SELECT m.material_code, m.name,
sup.partner_code AS supplier, manu.partner_code AS manufacturer, ms.is_preferred
FROM material_suppliers ms
JOIN materials m ON m.id = ms.material_id
LEFT JOIN organization_partners sup ON sup.id = ms.supplier_id
LEFT JOIN material_manufacturers mm ON mm.id = ms.material_manufacturer_id
LEFT JOIN organization_partners manu ON manu.id = mm.manufacturer_id
WHERE m.material_code IN ('B010201','A020201','B010202');"
预期:B010201 与 A020201 各一行,供应商与制造商均为 BJ-P001 / BJ-P003(原厂直销,两者相同);B010202 一行,供应商为空、制造商为 BJ-P002。
- Step 13: 确认分流语句自身幂等
不要在同一个库上重跑整个脚本——init.sql 整体不幂等(见 Global Constraints),那样只会得到 ERROR 1050 at line 8: Table 'users' already exists 然后中止,证明不了任何事。
分流语句的幂等性由「唯一键 + ON DUPLICATE KEY UPDATE」的写法保证。若要实证,单独建一个 scratch 库、把 tmp_materials 建表 + 几行样例数据 + 三条分流语句抽成一个独立脚本,连跑两轮:
mysql -u root -p1234 -e "DROP DATABASE IF EXISTS wms_idem; CREATE DATABASE wms_idem CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql -u root -p1234 wms_idem < /tmp/idem_probe.sql # 第一轮
mysql -u root -p1234 wms_idem < /tmp/idem_probe.sql # 第二轮
mysql -u root -p1234 wms_idem -e "SELECT (SELECT COUNT(*) FROM materials) AS m, (SELECT COUNT(*) FROM material_manufacturers) AS mm, (SELECT COUNT(*) FROM material_suppliers) AS ms;"
预期:两轮之后计数完全相同(不翻倍)。验证完 DROP DATABASE wms_idem;。
- Step 14: 提交
git add scripts/init.sql
git commit -F /tmp/msg.txt # 中文提交信息写入文件,避免 shell 编码问题
Task 2: 补入 32 条被截断的多供应商(161 条)
交付物: 上述 32 条物料各多出一行货源,material_suppliers 从 129 条增至 161 条。
Files:
- Modify:
scripts/init.sql(在 Task 1 的分流 3 之后追加一段) - Create:
scripts/verify-multi-supplier.sql(可选,用于核对的查询)
Interfaces:
- Consumes: Task 1 建立的三张表与分流语句;源库
original/material-apply-wuhan/material.db。 - Produces: 32 条额外的
material_suppliers行。
背景: 源库用 7 种分隔符(\r\n、\n、\r、/、、、;、;)在 supplier 字段写多家供应商,迁移时只保留了第一家。实测 32 条,每条恰好 2 家,全在武汉。被丢弃的第二家其伙伴记录已经建好(migrate_to_mysql.py:323-326 对每个名字都建了伙伴),所以只需补货源行,不需要新建伙伴。
- Step 1: 确认源库的多供应商条数
源库在仓库外:C:\1111\12.project\PyCharm\sqlite-to-mysql-migration\original\material-apply-wuhan\material.db。用只读方式打开,按七种分隔符计数(用 char() 码点写中文分隔符,避免 shell 传中文被写坏):
DB="file:C:/1111/12.project/PyCharm/sqlite-to-mysql-migration/original/material-apply-wuhan/material.db?mode=ro"
sqlite3 "$DB" "SELECT COUNT(*) FROM material WHERE supplier LIKE '%/%' OR supplier LIKE '%'||char(10)||'%' OR supplier LIKE '%'||char(13)||'%' OR supplier LIKE '%'||char(12289)||'%' OR supplier LIKE '%'||char(65307)||'%' OR supplier LIKE '%;%';"
预期:32。若不等,停下来核对源库是不是换了版本,不要继续。
- Step 2: 用脚本直接生成全部 32 条补入 SQL
拆分规则与 migrate_to_mysql.py:98 的 PARTNER_SEPARATORS 完全一致,需 strip 与去重(见该文件 split_partners,:134-149)。脚本写在临时目录,不要放进仓库。
32 条物料总共只涉及 8 家供应商,其伙伴编码已从 init.sql:562-595 的 tmp_partner_wh 查出,写死在脚本里:
"""从源库生成 32 条多供应商的补入 SQL(第 2 家起)。只读源库。"""
import sqlite3
import sys
PARTNER_SEPARATORS = ("\r\n", "\n", "\r", "/", "、", ";", ";")
# 供应商名 → 组织伙伴编码(取自 init.sql:562-595 的 tmp_partner_wh)
NAME_TO_CODE = {
"东莞市纳百智造技术有限公司": "WH-P009",
"佛山戊实五金有限公司": "WH-P017",
"北京恒瑞博远科技有限公司": "WH-P016",
"北京致远合创电子科技有限公司": "WH-P007",
"昆山聚格电子材料有限公司": "WH-P029",
"欧时电子元件(上海)有限公司": "WH-P008",
"武汉市普创模型技术有限公司": "WH-P010",
"深圳市美星新材料科技有限公司": "WH-P028",
}
def split_partners(text):
if not text:
return []
parts = [text]
for sep in PARTNER_SEPARATORS:
nxt = []
for part in parts:
nxt.extend(part.split(sep))
parts = nxt
out = []
for part in parts:
name = part.strip()
if name and name not in out:
out.append(name)
return out
DB = "file:C:/1111/12.project/PyCharm/sqlite-to-mysql-migration/original/material-apply-wuhan/material.db?mode=ro"
con = sqlite3.connect(DB, uri=True)
STMT = """INSERT INTO material_suppliers
(organization_id, material_id, supplier_id, material_manufacturer_id, is_preferred, status)
SELECT @org_wh, m.id, {var}, NULL, FALSE, 'active'
FROM materials m WHERE m.organization_id = @org_wh AND m.material_code = '{code}'
ON DUPLICATE KEY UPDATE is_preferred = material_suppliers.is_preferred;"""
count = 0
for code, supplier in con.execute(
"SELECT material_code, supplier FROM material ORDER BY material_code"
):
names = split_partners(supplier)
if len(names) <= 1:
continue
for name in names[1:]: # 第 1 家已由 Task 1 写入
tag = NAME_TO_CODE.get(name)
if tag is None:
raise SystemExit(f"未映射的供应商名:{name}")
var = "@p_" + tag.lower().replace("-p", "_") # WH-P008 → @p_wh_008
print(STMT.format(var=var, code=code))
count += 1
print(f"-- 共生成 {count} 条", file=sys.stderr)
运行(输出重定向到文件,避免 Windows 控制台的中文乱码):
PYTHONIOENCODING=utf-8 python -I gen_multi_supplier.py > multi_supplier.sql
预期 stderr 打印 -- 共生成 32 条。
- Step 3: 把生成的 SQL 追加进
init.sql
把 multi_supplier.sql 里的 32 条语句,连同一行说明注释,追加到 Task 1 的「分流 3」之后:
-- ---------- 多供应商补入:源里用分隔符写了多家,迁移时只留了第一家 ----------
-- 见设计文档 7.4。32 条,每条 2 家,故每条补 1 行,全在武汉。
is_preferred = FALSE(第一家已在 Task 1 标为 TRUE),material_manufacturer_id 为 NULL(这 32 条的制造商字段本来就是空的)。
不需要新增伙伴变量。 @p_wh_001 ~ @p_wh_034 已在 init.sql:619-652 全部定义,本次用到的 @p_wh_007、@p_wh_008、@p_wh_010、@p_wh_017、@p_wh_029 都在其中。
- Step 4: 隔离库重建并核对总数
mysql -u root -p1234 -e "DROP DATABASE IF EXISTS wms_verify;"
sed -e "s/CREATE DATABASE IF NOT EXISTS wmms/CREATE DATABASE IF NOT EXISTS wms_verify/" -e "s/^USE wmms;/USE wms_verify;/" scripts/init.sql | mysql -u root -p1234
mysql -u root -p1234 wms_verify -e "SELECT COUNT(*) AS sources FROM material_suppliers;"
预期:161。
- Step 5: 抽查多供应商物料
mysql -u root -p1234 wms_verify -e "
SELECT m.material_code, op.partner_code, ms.is_preferred
FROM material_suppliers ms
JOIN materials m ON m.id = ms.material_id
JOIN organization_partners op ON op.id = ms.supplier_id
WHERE m.material_code = 'B010019'
ORDER BY ms.is_preferred DESC;"
预期:两行,WH-P0xx(原保留的第一家)is_preferred=1,WH-P008(欧时电子)is_preferred=0。
- Step 6: 全量核对 32 条都有 2 行
mysql -u root -p1234 wms_verify -e "
SELECT COUNT(*) AS materials_with_two FROM (
SELECT material_id FROM material_suppliers GROUP BY material_id HAVING COUNT(*) = 2
) x;"
预期:32(这 32 条各 2 行;其余物料若有供应商则各 1 行)。
- Step 7: 确认脚本仍可重复执行
sed -e "s/CREATE DATABASE IF NOT EXISTS wmms/CREATE DATABASE IF NOT EXISTS wms_verify/" -e "s/^USE wmms;/USE wms_verify;/" scripts/init.sql | mysql -u root -p1234
mysql -u root -p1234 wms_verify -e "SELECT COUNT(*) FROM material_suppliers;"
预期:不报错,仍为 161。
- Step 8: 提交
git add scripts/init.sql
git commit -F /tmp/msg.txt
完成后的状态
materials:199 条,已无supplier_id/manufacturer_id/manufacturer_model。material_manufacturers:10 条。material_suppliers:161 条(129 基础 + 32 补入)。init.sql仍是单文件、可重复执行、空库一次跑通。- 后端与前端尚未适配,此时启动应用会因查询旧列而失败——这是预期状态,应用层改造另行计划。