From 278488c5d55b98ecac8dde37bcde9238f68bd381 Mon Sep 17 00:00:00 2001 From: czl231 <3286836406@qq.com> Date: Thu, 13 Aug 2026 19:43:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=99=BA=E8=83=BD=E6=B0=94?= =?UTF-8?q?=E9=98=80=E7=94=9F=E4=BA=A7=E5=95=86=E5=85=B3=E8=81=94=E5=B1=95?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 智能气阀列表改为显示生产商名称,并提供生产商唯一标识复制入口;无关联数据时显示‘未填写’。修复未来 Mock 数据生成时缺少生产商关联的问题,但不执行 Mock、迁移或 SQL,不修改当前数据库及历史数据。补充种子测试、操作日志和项目文档。 --- backend/api/internal/seed/mock.go | 60 ++++++++++++------- backend/api/internal/seed/mock_test.go | 16 +++++ ...作日志_智能气阀生产商关联展示_20260813.md | 43 +++++++++++++ docs/项目文档_智能气阀生产商关联展示_v1.0.md | 37 ++++++++++++ frontend/platform_admin/src/api/resources.ts | 2 +- 5 files changed, 135 insertions(+), 23 deletions(-) create mode 100644 docs/操作日志_智能气阀生产商关联展示_20260813.md create mode 100644 docs/项目文档_智能气阀生产商关联展示_v1.0.md diff --git a/backend/api/internal/seed/mock.go b/backend/api/internal/seed/mock.go index c596509..3838348 100644 --- a/backend/api/internal/seed/mock.go +++ b/backend/api/internal/seed/mock.go @@ -120,6 +120,17 @@ func MockData(database *gorm.DB) error { return err } + producer := models.ProducerAccount{ + Entity: entity(49, common.StatusEnable), ProducerCode: "MOCK-PRODUCER-001", + Name: "和气示例生产企业", CreditCode: "91310000MOCKPROD01", + Principal: "周厂长", Phone: "13600000001", Address: "上海市示例工业路 6 号", + Username: "mock_producer_admin", DisplayName: "示例生产管理员", + PasswordHash: string(passwordHash), RoleCode: "admin", Remark: "仅用于开发联调", + } + if err := put(tx, &producer); err != nil { + return err + } + enabledAt := yesterday productInfo := models.ProductInfo{ Entity: entity(12, common.StatusEnable), Code: "MOCK-CYLINDER-001", Name: "示例液化气钢瓶", @@ -127,6 +138,9 @@ func MockData(database *gorm.DB) error { ProductTypeID: productType.ID, Params: `{"weight":"15kg","medium":"LPG"}`, WarehouseID: warehouse.ID, ProducedAt: now.AddDate(-1, 0, 0), EnabledAt: &enabledAt, } + if err := linkMockProductProducer(&productInfo, producer); err != nil { + return err + } if err := put(tx, &productInfo); err != nil { return err } @@ -446,17 +460,6 @@ func MockData(database *gorm.DB) error { return err } - producer := models.ProducerAccount{ - Entity: entity(49, common.StatusEnable), ProducerCode: "MOCK-PRODUCER-001", - Name: "和气示例生产企业", CreditCode: "91310000MOCKPROD01", - Principal: "周厂长", Phone: "13600000001", Address: "上海市示例工业路 6 号", - Username: "mock_producer_admin", DisplayName: "示例生产管理员", - PasswordHash: string(passwordHash), RoleCode: "admin", Remark: "仅用于开发联调", - } - if err := put(tx, &producer); err != nil { - return err - } - recharge := models.WalletRechargeOrder{ Entity: entity(50, common.StatusEnable), RechargeStatus: common.StatusCompleted, WalletBasicID: wallet.ID, RechargeNo: "MOCK-RECHARGE-001", @@ -622,12 +625,27 @@ func seedAdditionalCoreScenarios(database *gorm.DB, passwordHash string, now tim return err } + producer := models.ProducerAccount{ + Entity: entity(sequence+20, common.StatusEnable), ProducerCode: "MOCK-PRODUCER-" + suffix, + Name: fmt.Sprintf("示例生产企业 %d", scenario), CreditCode: "91310000MOCKPROD" + suffix, + Principal: fmt.Sprintf("示例厂长%d", scenario), Phone: "136" + phoneSuffix, + Address: fmt.Sprintf("上海市工业路 %d 号", scenario), Username: "mock_producer_" + suffix, + DisplayName: fmt.Sprintf("示例生产管理员%d", scenario), PasswordHash: passwordHash, + RoleCode: "admin", Remark: "仅用于开发联调", + } + if err := put(database, &producer); err != nil { + return err + } + product := models.ProductInfo{ Entity: entity(sequence+9, common.StatusEnable), Code: "MOCK-CYLINDER-" + suffix, Name: fmt.Sprintf("示例智能瓶阀 %d", scenario), ProductStatus: common.StatusInStock, ProductTypeID: productType.ID, Params: `{"source":"mock-data"}`, WarehouseID: warehouse.ID, ProducedAt: now.AddDate(-1, 0, scenario), } + if err := linkMockProductProducer(&product, producer); err != nil { + return err + } if err := put(database, &product); err != nil { return err } @@ -734,21 +752,19 @@ func seedAdditionalCoreScenarios(database *gorm.DB, passwordHash string, now tim return err } - producer := models.ProducerAccount{ - Entity: entity(sequence+20, common.StatusEnable), ProducerCode: "MOCK-PRODUCER-" + suffix, - Name: fmt.Sprintf("示例生产企业 %d", scenario), CreditCode: "91310000MOCKPROD" + suffix, - Principal: fmt.Sprintf("示例厂长%d", scenario), Phone: "136" + phoneSuffix, - Address: fmt.Sprintf("上海市工业路 %d 号", scenario), Username: "mock_producer_" + suffix, - DisplayName: fmt.Sprintf("示例生产管理员%d", scenario), PasswordHash: passwordHash, - RoleCode: "admin", Remark: "仅用于开发联调", - } - if err := put(database, &producer); err != nil { - return err - } } return nil } +// linkMockProductProducer 为未来生成的 Mock 智能气阀建立真实生产商关联。 +func linkMockProductProducer(product *models.ProductInfo, producer models.ProducerAccount) error { + if product == nil || producer.ID == 0 { + return errors.New("mock product producer is missing") + } + product.ProducerAccountID = producer.ID + return nil +} + // ensureMockRoot 复用已有 root 账号,仅在账号不存在时写入默认 root 用户。 func ensureMockRoot(database *gorm.DB, passwordHash string) error { role := models.PlatformRole{ diff --git a/backend/api/internal/seed/mock_test.go b/backend/api/internal/seed/mock_test.go index 255992f..8521c3e 100644 --- a/backend/api/internal/seed/mock_test.go +++ b/backend/api/internal/seed/mock_test.go @@ -5,6 +5,7 @@ import ( "testing" "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" + "git.apinb.com/heqiapp/platforms/backend/api/internal/models" ) func TestMockEntityIdentitiesAreStableAndUnique(t *testing.T) { @@ -27,6 +28,21 @@ func TestMockEntityIdentitiesAreStableAndUnique(t *testing.T) { } } +// TestLinkMockProductProducer 验证未来 Mock 智能气阀始终关联已落库生产商。 +func TestLinkMockProductProducer(t *testing.T) { + product := models.ProductInfo{} + producer := models.ProducerAccount{Entity: models.Entity{ID: 9}} + if err := linkMockProductProducer(&product, producer); err != nil { + t.Fatalf("关联 Mock 生产商失败:%v", err) + } + if product.ProducerAccountID != producer.ID { + t.Fatalf("生产商关联错误:got %d, want %d", product.ProducerAccountID, producer.ID) + } + if err := linkMockProductProducer(&models.ProductInfo{}, models.ProducerAccount{}); err == nil { + t.Fatal("未落库生产商不应建立 Mock 关联") + } +} + func TestIdentityOfEmbeddedEntity(t *testing.T) { record := struct { Entity struct { diff --git a/docs/操作日志_智能气阀生产商关联展示_20260813.md b/docs/操作日志_智能气阀生产商关联展示_20260813.md new file mode 100644 index 0000000..f9af024 --- /dev/null +++ b/docs/操作日志_智能气阀生产商关联展示_20260813.md @@ -0,0 +1,43 @@ +# 智能气阀生产商关联展示操作日志 + +操作时间:2026-08-13 +操作类型:修改 +影响模块:平台总后台智能气阀管理、开发 Mock 数据 + +## 操作前状态 + +智能气阀列表直接显示 `producer_account_identity`,列标题为“生产商唯一标识”。无关联时显示 `-`;有关联时只显示唯一标识,无法快速识别生产商名称。未来 Mock 数据生成时也没有为智能气阀设置生产商关联。 + +## 具体操作 + +1. 列标题改为“生产商”。 +2. 有关联时显示生产商名称,并提供复制生产商唯一标识的按钮。 +3. 无关联时显示“未填写”。 +4. 新建、编辑继续要求选择有效生产商。 +5. 调整未来 Mock 数据生成顺序,先创建生产商,再关联智能气阀。 +6. 不回填、不修改当前数据库中的历史记录。 + +## 操作后状态 + +- 智能气阀列表以业务名称识别生产商,并可复制稳定 identity。 +- 历史空关联记录明确显示“未填写”。 +- 未来新生成的 Mock 智能气阀具有有效生产商关联。 +- 当前数据库结构和数据保持不变。 + +## 代码变更 + +- `frontend/platform_admin/src/api/resources.ts`:配置生产商关联名称、复制和空值展示。 +- `backend/api/internal/seed/mock.go`:未来 Mock 智能气阀关联生产商。 +- `backend/api/internal/seed/mock_test.go`:新增 Mock 生产商关联测试。 + +## 验证结果 + +- `go test ./internal/seed ./internal/logic/platform/product`:通过。 +- `npm.cmd run resource-pages:check`:通过,详情 46 类、新建 25 类、编辑 23 类。 +- `npm.cmd run build`:通过,TypeScript 检查与 Vite 生产构建成功。 +- `git diff --check`:通过。 +- 未执行 Mock、迁移或 SQL 命令,当前数据库数据未修改。 + +## 风险评估 + +当前历史空关联数据不会被自动补写,仍需在业务需要时通过正常编辑流程选择生产商。Mock 调整仅影响未来显式执行的数据生成。 diff --git a/docs/项目文档_智能气阀生产商关联展示_v1.0.md b/docs/项目文档_智能气阀生产商关联展示_v1.0.md new file mode 100644 index 0000000..dbee10d --- /dev/null +++ b/docs/项目文档_智能气阀生产商关联展示_v1.0.md @@ -0,0 +1,37 @@ +# 项目文档:智能气阀生产商关联展示 v1.0 + +## 1. 项目概述 + +本次修改将智能气阀列表中的生产商关联从内部唯一标识展示升级为“生产商名称 + 唯一标识复制”,并为历史空关联提供“未填写”提示。数据库不做任何修改。 + +## 2. 目录结构说明 + +```text +platforms/ +├── backend/api/internal/seed/ +│ ├── mock.go # 未来 Mock 智能气阀生产商关联 +│ └── mock_test.go # Mock 关联规则测试 +├── frontend/platform_admin/src/api/ +│ └── resources.ts # 生产商关联展示配置 +└── docs/ + ├── 项目文档_智能气阀生产商关联展示_v1.0.md + └── 操作日志_智能气阀生产商关联展示_20260813.md +``` + +## 3. 核心文件说明 + +- `resources.ts`:生产商字段使用现有关联名称组件,列表显示名称并复制 identity;空值显示“未填写”。 +- `mock.go`:生产商先落库,再将其数据库 ID 写入未来生成的 `ProductInfo.ProducerAccountID`。 +- `mock_test.go`:验证有效生产商可建立关联,未落库生产商会被拒绝。 + +## 4. 变更记录 + +- 列标题改为“生产商”。 +- 有关联时显示名称及复制入口。 +- 无关联时显示“未填写”。 +- 修复未来 Mock 数据缺少生产商关联的问题。 +- 不执行数据库迁移或历史数据回填。 + +## 5. 维护指南 + +生产商关系必须通过 `producer_account_identity` 输入,并由后端解析为数据库内部 ID。前端不得直接提交内部 ID。未来新增关联名称展示时应复用 `RelationNameText`,保持名称识别与 identity 复制能力一致。 diff --git a/frontend/platform_admin/src/api/resources.ts b/frontend/platform_admin/src/api/resources.ts index 309df60..f744271 100644 --- a/frontend/platform_admin/src/api/resources.ts +++ b/frontend/platform_admin/src/api/resources.ts @@ -447,7 +447,7 @@ export const resources: ResourceUiDefinition[] = [ define('producer_account', '生产商管理', 'writable', [f('producer_code', { required: true }), f('name', { required: true }), f('credit_code'), f('principal'), f('phone'), f('address', { emptyText: '未填写', placeholder: '请输入地址' }), f('username', { required: true }), f('password', { required: true }), f('display_name'), producerAdminRole(), f('remark')]), define('product_type', '智能气阀类型', 'editable', [f('code', { required: true }), f('name', { required: true })]), define('product_warehouse', '智能气阀库房', 'editable', [f('code', { required: true }), f('name', { required: true }), f('address', { emptyText: '未填写', placeholder: '请输入地址' }), f('manager'), f('phone')]), - define('product_info', '智能气阀', 'editable', [f('code', { required: true }), f('name', { required: true }), relation('producer_account_identity', '/producer_account', true), relation('product_type_identity', '/product_type', true), f('params', { required: true }), relation('warehouse_identity', '/product_warehouse'), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('user_account_identity', '/user_account'), f('produced_at', { required: true })], 'list', [ + define('product_info', '智能气阀', 'editable', [f('code', { required: true }), f('name', { required: true }), relation('producer_account_identity', '/producer_account', true, { label: '生产商', listLabel: '生产商', listRelationNameOnly: true, displayRelationLabel: true, showIdentityCopy: true, emptyText: '未填写', placeholder: '请选择生产商' }), relation('product_type_identity', '/product_type', true), f('params', { required: true }), relation('warehouse_identity', '/product_warehouse'), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('user_account_identity', '/user_account'), f('produced_at', { required: true })], 'list', [ { name: '修改智能气阀状态', resource: '/product_info/:identity/lifecycle', method: 'PATCH', fields: [f('product_status', { required: true, type: 'select', options: [{ label: '待处理', value: 10 }, { label: '在库', value: 28 }, { label: '运输中', value: 29 }, { label: '使用中', value: 30 }, { label: '维修中', value: 31 }, { label: '已报废', value: 27 }] })] }, { name: '变更智能气阀归属', resource: '/product_info/:identity', method: 'PUT', fields: [relation('warehouse_identity', '/product_warehouse'), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('user_account_identity', '/user_account'), f('action', { required: true, type: 'select', options: [{ label: '入库', value: 'warehouse' }, { label: '分配', value: 'assigned' }, { label: '归还', value: 'returned' }, { label: '人工调整', value: 'manual' }] }), f('reason', { required: true }), f('remark')] }, ]),