From b219023a590de44dd67549a14ca3f5582fff9170 Mon Sep 17 00:00:00 2001 From: czl231 <3286836406@qq.com> Date: Fri, 14 Aug 2026 23:29:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=85=8D=E9=80=81=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=90=88=E5=90=8C=E6=A0=87=E9=A2=98=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../logic/platform/gasorder/gasorder.go | 11 ++++- .../platform/gasorder/order_list_display.go | 48 +++++++++++++++++++ .../gasorder/order_list_display_test.go | 20 ++++++++ ...作日志_配送订单列表可读信息优化_20260814.md | 3 +- .../scripts/check-gasorder-list-display.mjs | 4 +- .../src/api/resource-display.ts | 3 +- frontend/platform_admin/src/api/resources.ts | 4 +- 7 files changed, 88 insertions(+), 5 deletions(-) diff --git a/backend/api/internal/logic/platform/gasorder/gasorder.go b/backend/api/internal/logic/platform/gasorder/gasorder.go index 28b9033..5f427fd 100644 --- a/backend/api/internal/logic/platform/gasorder/gasorder.go +++ b/backend/api/internal/logic/platform/gasorder/gasorder.go @@ -158,8 +158,17 @@ func getGasorderBasic(ctx *gin.Context) { return } } + contractNames, err := loadGasorderContractNames([]models.GasorderBasic{order}) + if err != nil { + infra.Response.Error(ctx, err) + return + } + displayOrder := gasorderDetailDisplay{ + GasorderBasic: order, + ContractDisplayName: contractDisplayName(contractNames[order.GasorderContractID]), + } response, err := common.PublicResourceResponse(gin.H{ - "order": order, "items": items, "assignments": assignments, "statuses": statuses, + "order": displayOrder, "items": items, "assignments": assignments, "statuses": statuses, "tracks": tracks, "confirmations": confirmations, "payments": payments, }) if err != nil { diff --git a/backend/api/internal/logic/platform/gasorder/order_list_display.go b/backend/api/internal/logic/platform/gasorder/order_list_display.go index fb63662..462f1cf 100644 --- a/backend/api/internal/logic/platform/gasorder/order_list_display.go +++ b/backend/api/internal/logic/platform/gasorder/order_list_display.go @@ -17,9 +17,16 @@ import ( type gasorderListDisplay struct { models.GasorderBasic CreatorDisplayName string `json:"creator_display_name"` + ContractDisplayName string `json:"contract_display_name"` ContractProductsSummary string `json:"contract_products_summary"` } +// gasorderDetailDisplay 为订单详情补充合同当前标题和编号,不改变订单持久化快照。 +type gasorderDetailDisplay struct { + models.GasorderBasic + ContractDisplayName string `json:"contract_display_name"` +} + // ListGasorderBasic 返回平台订单分页列表,并补充只用于管理端展示的可读摘要。 func ListGasorderBasic(ctx *gin.Context) { page, size := common.PageSize(ctx) @@ -58,6 +65,10 @@ func buildGasorderListDisplays(orders []models.GasorderBasic) ([]gasorderListDis if err != nil { return nil, err } + contractNames, err := loadGasorderContractNames(orders) + if err != nil { + return nil, err + } displays := make([]gasorderListDisplay, 0, len(orders)) for _, order := range orders { creatorName := strings.TrimSpace(creatorNames[creatorLookupKey(order.CreatorType, order.CreatorID)]) @@ -67,12 +78,49 @@ func buildGasorderListDisplays(orders []models.GasorderBasic) ([]gasorderListDis displays = append(displays, gasorderListDisplay{ GasorderBasic: order, CreatorDisplayName: creatorName, + ContractDisplayName: contractDisplayName(contractNames[order.GasorderContractID]), ContractProductsSummary: productSummaries[order.ID], }) } return displays, nil } +// loadGasorderContractNames 批量读取订单关联合同的当前标题与唯一合同编号。 +func loadGasorderContractNames(orders []models.GasorderBasic) (map[uint64]models.GasorderContract, error) { + ids := make([]uint64, 0, len(orders)) + for _, order := range orders { + ids = append(ids, order.GasorderContractID) + } + contracts := map[uint64]models.GasorderContract{} + if len(ids) == 0 { + return contracts, nil + } + var records []models.GasorderContract + if err := common.ActiveRecords(impl.DBService).Where("id IN ? AND status <> ?", ids, common.StatusArchived).Find(&records).Error; err != nil { + return nil, err + } + for _, contract := range records { + contracts[contract.ID] = contract + } + return contracts, nil +} + +// contractDisplayName 组合可读标题和唯一合同编号,关联失效时返回明确文案。 +func contractDisplayName(contract models.GasorderContract) string { + title := strings.TrimSpace(contract.Title) + contractNo := strings.TrimSpace(contract.ContractNo) + if contract.ID == 0 { + return "记录已失效" + } + if title == "" { + return firstReadableName(contractNo, "未命名合同") + } + if contractNo == "" { + return title + } + return fmt.Sprintf("%s(%s)", title, contractNo) +} + func creatorLookupKey(creatorType string, creatorID uint64) string { return fmt.Sprintf("%s:%d", creatorType, creatorID) } diff --git a/backend/api/internal/logic/platform/gasorder/order_list_display_test.go b/backend/api/internal/logic/platform/gasorder/order_list_display_test.go index 50bb870..e982cd8 100644 --- a/backend/api/internal/logic/platform/gasorder/order_list_display_test.go +++ b/backend/api/internal/logic/platform/gasorder/order_list_display_test.go @@ -36,3 +36,23 @@ func TestRestoreGasorderListAddresses(t *testing.T) { t.Fatalf("空地址应保持为空,实际为 %v", got) } } + +// TestContractDisplayName 验证合同标题、编号和失效记录使用稳定可读文案。 +func TestContractDisplayName(t *testing.T) { + tests := []struct { + name string + contract models.GasorderContract + want string + }{ + {"标题和编号", models.GasorderContract{Entity: models.Entity{ID: 1}, Title: "居民供气合同", ContractNo: "HT-001"}, "居民供气合同(HT-001)"}, + {"仅编号", models.GasorderContract{Entity: models.Entity{ID: 2}, ContractNo: "HT-002"}, "HT-002"}, + {"失效记录", models.GasorderContract{}, "记录已失效"}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if got := contractDisplayName(test.contract); got != test.want { + t.Fatalf("合同展示名称 = %q,期望 %q", got, test.want) + } + }) + } +} diff --git a/docs/操作日志_配送订单列表可读信息优化_20260814.md b/docs/操作日志_配送订单列表可读信息优化_20260814.md index a0bea0e..6eae17c 100644 --- a/docs/操作日志_配送订单列表可读信息优化_20260814.md +++ b/docs/操作日志_配送订单列表可读信息优化_20260814.md @@ -24,6 +24,7 @@ - 收货地址显示订单创建时保存的地址快照,空值显示“未填写”,长文本沿用表格悬浮全文能力。 - 合同气瓶显示紧凑摘要,点击后可在详情页查看全部气瓶记录。 - 创建方可读名称的渲染优先于通用唯一标识分支,名称旁保留独立复制入口;失效记录显示“记录已失效”。 +- 配送合同在订单列表和详情中统一显示“标题(合同编号)”,并保留合同唯一标识复制入口。 ## 代码变更 @@ -37,7 +38,7 @@ - 后端平台配送订单专项测试通过。 - 平台总后台 TypeScript 类型检查与生产构建通过。 -- 配送订单列表创建方显示专项静态检查通过。 +- 配送订单列表与详情可读显示专项静态检查通过。 ## 风险评估 diff --git a/frontend/platform_admin/scripts/check-gasorder-list-display.mjs b/frontend/platform_admin/scripts/check-gasorder-list-display.mjs index 319358f..beb75d7 100644 --- a/frontend/platform_admin/scripts/check-gasorder-list-display.mjs +++ b/frontend/platform_admin/scripts/check-gasorder-list-display.mjs @@ -8,6 +8,8 @@ const listPage = fs.readFileSync(new URL('../src/views/shared/CrudListPage.vue', const assertions = [ [resources.includes("listDisplayKey: 'creator_display_name'"), '创建方未配置可读名称字段'], [resources.includes('listDisplayIdentityCopy: true'), '创建方未保留唯一标识复制入口'], + [resources.includes("detailDisplayKey: 'contract_display_name'"), '订单详情未配置合同标题展示字段'], + [resources.includes("listDisplayKey: 'contract_display_name'"), '订单列表未配置合同标题展示字段'], [listPage.includes('field.listDisplayIdentityCopy && identityFieldValue(field, record)'), '可读名称分支未优先于通用唯一标识分支'], [listPage.includes(':name="displayListField(field, record)"'), '创建方复制组件未使用列表可读名称'], ]; @@ -16,4 +18,4 @@ for (const [passed, message] of assertions) { if (!passed) throw new Error(message); } -console.log('配送订单列表创建方显示检查通过'); +console.log('配送订单列表与详情可读显示检查通过'); diff --git a/frontend/platform_admin/src/api/resource-display.ts b/frontend/platform_admin/src/api/resource-display.ts index b505602..356c999 100644 --- a/frontend/platform_admin/src/api/resource-display.ts +++ b/frontend/platform_admin/src/api/resource-display.ts @@ -277,7 +277,8 @@ export function displayResourceField( Array<{ label: string; value: string | number }> > = {}, ) { - const value = row[field.key] ?? row[`${field.key}_masked`]; + const displayValue = field.detailDisplayKey ? row[field.detailDisplayKey] : undefined; + const value = displayValue ?? row[field.key] ?? row[`${field.key}_masked`]; if (value == null || value === '') return field.emptyText ?? '-'; const hasOptionSource = Object.prototype.hasOwnProperty.call(fieldOptions, field.key) || diff --git a/frontend/platform_admin/src/api/resources.ts b/frontend/platform_admin/src/api/resources.ts index 8292299..0a425e7 100644 --- a/frontend/platform_admin/src/api/resources.ts +++ b/frontend/platform_admin/src/api/resources.ts @@ -80,6 +80,8 @@ export type ResourceField = { listCopyable?: boolean; /** 列表读取另一个只读响应字段展示,表单仍使用原业务字段。 */ listDisplayKey?: string; + /** 详情读取另一个只读响应字段展示,原关系唯一标识仍用于复制。 */ + detailDisplayKey?: string; /** 点击列表展示内容时进入当前记录详情。 */ listDetailLink?: boolean; /** 列表以可读展示字段为主文案,同时保留原关系唯一标识复制入口。 */ @@ -561,7 +563,7 @@ export const resources: ResourceUiDefinition[] = [ { name: '解绑气瓶', resource: '/gasorder_contract_product/:identity/unbind', danger: true, fields: reason }, ]), define('gasorder_contract_revision', '合同修订记录', 'readonly', []), - define('gasorder_basic', '气体配送订单', 'append_only', [f('request_no', { required: true }), relation('gasorder_contract_identity', '/gasorder_contract', true, { label: '配送合同', placeholder: '请选择当前可履约的生效合同', relationFilters: { candidate: 'order' }, relationStrictFilter: true, relationInvalidMessage: '所选配送合同已不可履约,已清空,请重新选择', relationEmptyText: '暂无可下单的生效合同,请先启用或续签配送合同' }), f('creator_type', { required: true, type: 'select', options: resourceSearchEnumOptions('gasorder_basic', 'creator_type') }), f('creator_identity', { label: '创建方', required: true, listDisplayKey: 'creator_display_name', listDisplayIdentityCopy: true, placeholder: '请先选择配送合同和创建方类型', readonlyRelationText: true, relationFilters: { status: '1' }, relationEmptyText: '当前合同下暂无可用的创建方', dynamicRelation: { + define('gasorder_basic', '气体配送订单', 'append_only', [f('request_no', { required: true }), relation('gasorder_contract_identity', '/gasorder_contract', true, { label: '配送合同', listDisplayKey: 'contract_display_name', detailDisplayKey: 'contract_display_name', listDisplayIdentityCopy: true, showIdentityCopy: true, placeholder: '请选择当前可履约的生效合同', relationFilters: { candidate: 'order' }, relationStrictFilter: true, relationInvalidMessage: '所选配送合同已不可履约,已清空,请重新选择', relationEmptyText: '暂无可下单的生效合同,请先启用或续签配送合同' }), f('creator_type', { required: true, type: 'select', options: resourceSearchEnumOptions('gasorder_basic', 'creator_type') }), f('creator_identity', { label: '创建方', required: true, listDisplayKey: 'creator_display_name', listDisplayIdentityCopy: true, placeholder: '请先选择配送合同和创建方类型', readonlyRelationText: true, relationFilters: { status: '1' }, relationEmptyText: '当前合同下暂无可用的创建方', dynamicRelation: { parentKey: 'creator_type', parentLabel: '创建方类型', contextParentKey: 'gasorder_contract_identity', resources: { user: '/user_account', staff: '/staff_account', delivery: '/delivery_basic', gas: '/gas_basic' }, fixedIdentityKeys: { user: 'user_account_identity', gas: 'gas_basic_identity', delivery: 'delivery_basic_identity' },