diff --git a/DEPLOY.md b/DEPLOY.md index d1089efd..22543e2f 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -78,6 +78,10 @@ ipconfig | admin | admin123 | 管理员 | | user | user123 | 普通用户 | +这两个账号**不是 `scripts/init.sql` 建的**——该脚本只创建 `users` 表,不插入任何用户数据。账号由后端启动时调用 `repository.CreateDefaultUsers()`(见 `cmd/server/main.go`)幂等创建:`admin` 已存在就跳过,不存在才新建 `admin`/`user` 并绑定角色。 + +因此**重建数据库后,重启后端即可恢复默认账号**,不需要手工执行建用户的 SQL。本地 3306 验证库(用 `scripts/init.sql` 重建)走的也是同一条路径。反过来,如果改过 `admin` 密码或另建过账号,这些不会随重建回来。 + ## 常用命令 ```bash diff --git a/db-migration-original/material-apply-8090北京/material.db b/db-migration-original/material-apply-8090北京/material.db deleted file mode 100644 index c9cf4b4e..00000000 Binary files a/db-migration-original/material-apply-8090北京/material.db and /dev/null differ diff --git a/db-migration-original/material-apply-8090北京/material.db-shm b/db-migration-original/material-apply-8090北京/material.db-shm deleted file mode 100644 index c883ef4b..00000000 Binary files a/db-migration-original/material-apply-8090北京/material.db-shm and /dev/null differ diff --git a/db-migration-original/material-apply-8090北京/material.db-wal b/db-migration-original/material-apply-8090北京/material.db-wal deleted file mode 100644 index f93de1e7..00000000 Binary files a/db-migration-original/material-apply-8090北京/material.db-wal and /dev/null differ diff --git a/db-migration-original/material-apply武汉/material.db b/db-migration-original/material-apply武汉/material.db deleted file mode 100644 index c1308b72..00000000 Binary files a/db-migration-original/material-apply武汉/material.db and /dev/null differ diff --git a/db-migration-original/material-apply武汉/material.db-shm b/db-migration-original/material-apply武汉/material.db-shm deleted file mode 100644 index a05b847b..00000000 Binary files a/db-migration-original/material-apply武汉/material.db-shm and /dev/null differ diff --git a/db-migration-original/material-apply武汉/material.db-wal b/db-migration-original/material-apply武汉/material.db-wal deleted file mode 100644 index a7ec73f5..00000000 Binary files a/db-migration-original/material-apply武汉/material.db-wal and /dev/null differ diff --git a/db-migration-original/material-apply武汉/material.db.bak-20260911-071133 b/db-migration-original/material-apply武汉/material.db.bak-20260911-071133 deleted file mode 100644 index 2a75e6e3..00000000 Binary files a/db-migration-original/material-apply武汉/material.db.bak-20260911-071133 and /dev/null differ diff --git a/db-migration-original/material-apply武汉/material.db.before-pass2-20260911-170128 b/db-migration-original/material-apply武汉/material.db.before-pass2-20260911-170128 deleted file mode 100644 index d49c245a..00000000 Binary files a/db-migration-original/material-apply武汉/material.db.before-pass2-20260911-170128 and /dev/null differ diff --git a/db-migration-original/material-apply武汉/material.db.before-renumber-20260911-165522 b/db-migration-original/material-apply武汉/material.db.before-renumber-20260911-165522 deleted file mode 100644 index a2d38979..00000000 Binary files a/db-migration-original/material-apply武汉/material.db.before-renumber-20260911-165522 and /dev/null differ diff --git a/docs/api.md b/docs/api.md index 26195931..3efe9856 100644 --- a/docs/api.md +++ b/docs/api.md @@ -205,7 +205,7 @@ } ``` -`material_image`、`spec`、`manufacturer_model`、`source`、`remark`、`supplier_id`、`manufacturer_id`、`category_id`、`base_unit_id`、`unit` 为空时**不会出现在响应里**(后端 `omitempty`),前端取值时需要做空值兜底。`source` 可为空,非空时仅为「外购」或「外协」;`remark` 可为空且最多 200 字。`manufacturer_model` 是制造商为产品指定的型号或订货识别型号;`spec` 表示物料规格,`manufacturer_id` 表示制造商伙伴关联。`supplier_name` 和 `manufacturer_name` 是伙伴显示名称。 +`material_image`、`spec`、`manufacturer_model`、`source`、`remark`、`supplier_id`、`manufacturer_id`、`category_id`、`base_unit_id`、`unit` 为空时**不会出现在响应里**(后端 `omitempty`),前端取值时需要做空值兜底。`source` 可为空,非空时仅为「外购」或「外协」;`remark` 可为空且最多 500 字。`manufacturer_model` 是制造商为产品指定的型号或订货识别型号;`spec` 表示物料规格,`manufacturer_id` 表示制造商伙伴关联。`supplier_name` 和 `manufacturer_name` 是伙伴显示名称。 **状态码:** @@ -265,7 +265,7 @@ | spec | string | 否 | 规格 | | manufacturer_model| string | 否 | 制造商为产品指定的型号或订货识别型号 | | source | string | 否 | 来源,可留空;非空时填写「外购」或「外协」 | -| remark | string | 否 | 备注,可留空,最多 200 字 | +| remark | string | 否 | 备注,可留空,最多 500 字 | | base_unit_id | integer | 否 | 基本计量单位 ID | | unit | string | 否 | 单位编码(冗余展示字段) | | material_image | string | 否 | 图片地址 | @@ -329,7 +329,7 @@ | spec | string | 否 | 规格 | | manufacturer_model| string | 否 | 制造商为产品指定的型号或订货识别型号 | | source | string | 否 | 来源,可留空;传空字符串清空;非空时填写「外购」或「外协」 | -| remark | string | 否 | 备注,可留空;传空字符串清空;最多 200 字 | +| remark | string | 否 | 备注,可留空;传空字符串清空;最多 500 字 | | base_unit_id | integer | 否 | 基本计量单位 ID | | unit | string | 否 | 单位编码 | | material_image | string | 否 | 图片地址 | @@ -435,7 +435,7 @@ - 列顺序可以随意调整;表头允许带首尾空格和末尾的 `*` 必填标记;不认识的列忽略; - 必填列 `物料名称`、`大类`、`小类` 缺任何一列,判为模板不合法并返回「模板格式不正确,请重新下载最新模板」; -- 选填列 `规格`、`单位`、`供应商`、`制造商`、`来源`、`备注` 可以整列删掉,取值按空处理;来源只能为「外购」或「外协」,备注最多 200 字。 +- 选填列 `规格`、`单位`、`供应商`、`制造商`、`来源`、`备注` 可以整列删掉,取值按空处理;来源只能为「外购」或「外协」,备注最多 500 字。 分类**按名称**填写;名称之外也接受**编码**和界面上的「编码 名称」写法(界面一律显示成 `A 机械结构类`,用户整段照抄进模板是常事)。后端按「大类 + 小类」在本公司内组合匹配(小类名在一个公司内会重名,例如多个大类下都有「其他」,所以必须两级组合)。名称、编码、「编码 名称」三种写法同名或同码时以**名称**优先。单位按名称匹配,同时兼容按编码匹配(如 `kg` 与 `千克`)。 diff --git a/docs/数据库表结构与关系说明.md b/docs/数据库表结构与关系说明.md index 2563dcce..0cb36d2a 100644 --- a/docs/数据库表结构与关系说明.md +++ b/docs/数据库表结构与关系说明.md @@ -256,7 +256,7 @@ ## 13. 物料来源与备注(2026-10-14) -`materials.source` 为可空来源字段,允许值为「外购」或「外协」;`materials.remark` 为可空备注,最多 200 字。旧记录升级后两列均保持 `NULL`,新建和批量导入也允许留空。已有库使用 `scripts/upgrades/2026-10-14-material-source-remark.sql` 升级,空库使用 `scripts/init.sql`。 +`materials.source` 为可空来源字段,允许值为「外购」或「外协」;`materials.remark` 为可空备注,最多 500 字。旧记录升级后两列均保持 `NULL`,新建和批量导入也允许留空。已有库使用 `scripts/upgrades/2026-10-14-material-source-remark.sql` 升级,空库使用 `scripts/init.sql`。 ## 14. 物料日志操作人用户 ID(2026-10-14) diff --git a/docs/项目后端结构说明.md b/docs/项目后端结构说明.md index 3df80909..837f0fe4 100644 --- a/docs/项目后端结构说明.md +++ b/docs/项目后端结构说明.md @@ -13,6 +13,8 @@ | `scripts/` | `init.sql`、增量升级 SQL 与校验脚本 | | `docs/` | 项目文档 | +上表提到的「建默认用户」,指 `main.go` 启动时调用 `repository.CreateDefaultUsers()`:幂等创建 `admin`/`user`(`admin` 已存在就跳过),并绑定 `super_admin` / `warehouse_admin` 角色。这两个账号**不在 `scripts/init.sql` 里**——该脚本只建 `users` 表、不插入任何用户数据——因此数据库重建后**重启后端即可恢复默认账号**,不必手工执行建用户的 SQL;反过来,改过的密码或另建的账号不会随重建回来。 + ## 整体 `cmd/server/main.go` 是入口(注册路由、连库、校验 schema),`internal/` 只是内核。一次请求的链路是: diff --git a/frontend/src/views/Materials.vue b/frontend/src/views/Materials.vue index b015d026..123edcd4 100644 --- a/frontend/src/views/Materials.vue +++ b/frontend/src/views/Materials.vue @@ -125,7 +125,7 @@ - + @@ -595,8 +595,8 @@ async function handleSave() { ElMessage.warning('物料规格过长,请小于200字') return } - if (Array.from(form.remark || '').length > 200) { - ElMessage.warning('备注不能超过200字') + if (Array.from(form.remark || '').length > 500) { + ElMessage.warning('备注不能超过500字') return } saveLoading.value = true diff --git a/internal/service/material.go b/internal/service/material.go index 22203ee4..434e7c51 100644 --- a/internal/service/material.go +++ b/internal/service/material.go @@ -16,14 +16,14 @@ var ErrInvalidMaterialCategory = errors.New("请选择有效的小类类型") var ErrMaterialNameTooLong = errors.New("物料名称过长,请小于200字") var ErrMaterialSpecTooLong = errors.New("物料规格过长,请小于200字") var ErrInvalidMaterialSource = errors.New("物料来源只能填写外购或外协") -var ErrMaterialRemarkTooLong = errors.New("物料备注不能超过200字") +var ErrMaterialRemarkTooLong = errors.New("物料备注不能超过500字") var ErrInvalidMaterialSupplier = errors.New("请选择本公司的有效供应商") var ErrInvalidMaterialManufacturer = errors.New("请选择本公司的有效制造商") var ErrMaterialDuplicate = errors.New("发现名称、规格、单位、供应商和制造商均相同的物料,请先检查重复项") const maxMaterialNameLength = 200 const maxMaterialSpecLength = 200 -const maxMaterialRemarkLength = 200 +const maxMaterialRemarkLength = 500 func validMaterialSource(source string) bool { return source == "" || source == "外购" || source == "外协" diff --git a/internal/service/material_import.go b/internal/service/material_import.go index c49e32cf..4e303388 100644 --- a/internal/service/material_import.go +++ b/internal/service/material_import.go @@ -187,7 +187,7 @@ func (s *MaterialImportService) validateRows(organizationID int64, rows []model. reasons = append(reasons, "来源只能填写外购或外协") } if utf8.RuneCountInString(remark) > maxMaterialRemarkLength { - reasons = append(reasons, "备注超过 200 字") + reasons = append(reasons, "备注超过 500 字") } majorName := normalizeWhitespace(row.MajorName) diff --git a/internal/service/material_import_excel.go b/internal/service/material_import_excel.go index 87a5f52a..cbbcd243 100644 --- a/internal/service/material_import_excel.go +++ b/internal/service/material_import_excel.go @@ -54,7 +54,7 @@ func BuildMaterialImportTemplate() ([]byte, error) { {"供应商", "选填", "示例供应商或供应商编码", "填写当前组织已有供应商的名称或编码;留空表示不指定。"}, {"制造商", "选填", "示例制造商或制造商编码", "填写当前组织已有制造商的名称或编码;留空表示不指定。"}, {"来源", "选填", "外购 / 外协", "只能填写外购或外协;可留空。"}, - {"备注", "选填", "物料用途说明", "可留空,最多 200 字。"}, + {"备注", "选填", "物料用途说明", "可留空,最多 500 字。"}, {"大类", "必填", "机械结构类", "填写当前组织已启用的大类名称、编码或「编码 名称」。"}, {"小类", "必填", "标准件", "填写所选大类下的小类名称、编码或「编码 名称」;小类须配置启用的编码规则。"}, } @@ -70,7 +70,7 @@ func BuildMaterialImportTemplate() ([]byte, error) { 25: "重复检查:名称、规格、单位、供应商和制造商全部相同才判为重复;同一文件内也会检查。", 26: "编码与库存:物料编码由系统生成,无需填写;仅新增物料,不增加库存,也不更新已有物料。", 27: "默认单位:单位留空时使用「个」;当前组织未启用「个」时,请填写其他有效单位。", - 28: "来源和备注均可留空;来源仅支持外购或外协,备注最多 200 字。", + 28: "来源和备注均可留空;来源仅支持外购或外协,备注最多 500 字。", 34: "示例只展示写法,请替换为当前组织实际启用的数据;示例不在数据页,不会自动导入。", } for row, text := range sections { diff --git a/scripts/init.sql b/scripts/init.sql index ee8d3875..177d1528 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(200) NULL COMMENT '物料备注,最多200字', 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), 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 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; @@ -1324,7 +1324,7 @@ LF-3070A', NULL, '单台数量:1; 物料等级:B; 存储条件:存放于室内 NULL, NULL, @c_wh_B01, @u_wh_jian, '件', 'active', 1, '2026-09-11 07:44:59', '2026-09-11 07:44:59'), (@org_wh, 'B010059', '触摸屏8寸', - '8寸', '外购', 'https://item.taobao.com/item.htm?from=cart&id=749116190950&mi_id=000086DKOQjDf9p_N94ymKsE2dWs-QKiH_b2Om7xRMRWkkU&skuId=5940217905947&spm=a1z0d.6639537%2F202410.item.d749116190950.34747484e3AbaM&upStre', + '8寸', '外购', 'https://item.taobao.com/item.htm?from=cart&id=749116190950&mi_id=000086DKOQjDf9p_N94ymKsE2dWs-QKiH_b2Om7xRMRWkkU&skuId=5940217905947&spm=a1z0d.6639537%2F202410.item.d749116190950.34747484e3AbaM&upStreamPrice=28800&xxc=shop', NULL, NULL, @c_wh_B01, @u_wh_jian, '件', 'active', 1, '2026-09-11 07:44:59', '2026-09-28 08:43:20'), (@org_wh, 'F010049', '气路组件', @@ -1468,7 +1468,7 @@ LF-3070A', NULL, '单台数量:1; 物料等级:B; 存储条件:存放于室内 NULL, NULL, @c_wh_F01, NULL, '', 'active', 1, '2026-09-23 15:26:31', '2026-09-23 15:28:10'), (@org_wh, 'B010076', '触摸屏8寸', - '8寸', NULL, 'https://item.taobao.com/item.htm?from=cart&id=749116190950&mi_id=000086DKOQjDf9p_N94ymKsE2dWs-QKiH_b2Om7xRMRWkkU&skuId=5940217905947&spm=a1z0d.6639537%2F202410.item.d749116190950.34747484e3AbaM&upStre', + '8寸', NULL, 'https://item.taobao.com/item.htm?from=cart&id=749116190950&mi_id=000086DKOQjDf9p_N94ymKsE2dWs-QKiH_b2Om7xRMRWkkU&skuId=5940217905947&spm=a1z0d.6639537%2F202410.item.d749116190950.34747484e3AbaM&upStreamPrice=28800&xxc=shop', NULL, NULL, @c_wh_B01, NULL, '', 'obsolete', 1, '2026-09-28 06:17:01', '2026-09-28 06:47:57'), (@org_wh, 'B010077', '香橙派开发板Pi 5',