diff --git a/backend/api/internal/logic/delivery/order.go b/backend/api/internal/logic/delivery/order.go index 7f4ad82..73efd43 100644 --- a/backend/api/internal/logic/delivery/order.go +++ b/backend/api/internal/logic/delivery/order.go @@ -1,5 +1,5 @@ // 功能描述:实现配送点范围内的合同、合同气瓶和配送订单接口。 -// 版本:v1.2.0。 +// 版本:v1.3.0。 package delivery import ( @@ -364,10 +364,30 @@ func scopedOrder(ctx *gin.Context, identity string, pointID uint64) (models.Gaso func ListOrder(ctx *gin.Context) { point, _, ok := currentScope(ctx) - if ok { - listScoped(ctx, &models.GasorderBasic{}, common.ActiveRecords(db().Model(&models.GasorderBasic{})). - Where("delivery_basic_id = ?", point.ID), "gasorder_basic.created_at desc") + if !ok { + return } + page, size := common.PageSize(ctx) + query := common.ApplyKeywordFilter(ctx, + common.ActiveRecords(db().Model(&models.GasorderBasic{})).Where("delivery_basic_id = ?", point.ID), + &models.GasorderBasic{}, + ) + var total int64 + if err := query.Count(&total).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + var orders []models.GasorderBasic + if err := query.Order("gasorder_basic.created_at desc").Offset((page - 1) * size).Limit(size).Find(&orders).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + response, err := platformgasorder.BuildGasorderListResponse(ctx, orders) + if err != nil { + infra.Response.Error(ctx, err) + return + } + infra.Response.Success(ctx, gin.H{"total": total, "list": response}) } func GetOrder(ctx *gin.Context) { 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 1b100e8..192c2aa 100644 --- a/backend/api/internal/logic/platform/gasorder/order_list_display.go +++ b/backend/api/internal/logic/platform/gasorder/order_list_display.go @@ -1,5 +1,5 @@ // 功能描述:为平台配送订单列表与详情补充关联名称、地址和合同气瓶摘要。 -// 版本:v1.1.0 +// 版本:v1.2.0 package gasorder import ( @@ -62,18 +62,27 @@ func ListGasorderBasic(ctx *gin.Context) { infra.Response.Error(ctx, err) return } - displays, err := buildGasorderListDisplays(orders) + response, err := BuildGasorderListResponse(ctx, orders) if err != nil { infra.Response.Error(ctx, err) return } + infra.Response.Success(ctx, gin.H{"total": total, "list": response}) +} + +// BuildGasorderListResponse 为已经完成数据范围筛选的订单构造安全、可读的管理端列表响应。 +// 调用方必须先限定自身可见订单;本函数只补充派生名称、气瓶摘要并恢复订单地址快照。 +func BuildGasorderListResponse(ctx *gin.Context, orders []models.GasorderBasic) (any, error) { + displays, err := buildGasorderListDisplays(orders) + if err != nil { + return nil, err + } response, err := common.PublicResourceResponse(displays) if err != nil { - infra.Response.Error(ctx, err) - return + return nil, err } protected := common.ProtectPreciseLocation(ctx, &models.GasorderBasic{}, response) - infra.Response.Success(ctx, gin.H{"total": total, "list": restoreGasorderListAddresses(protected, orders)}) + return restoreGasorderListAddresses(protected, orders), nil } // buildGasorderListDisplays 批量读取创建方与有效订单气瓶,避免列表逐行查询。 diff --git a/docs/操作日志_配送点订单列表可读信息_20260825.md b/docs/操作日志_配送点订单列表可读信息_20260825.md new file mode 100644 index 0000000..66b0251 --- /dev/null +++ b/docs/操作日志_配送点订单列表可读信息_20260825.md @@ -0,0 +1,40 @@ +# 操作日志:配送点订单列表可读信息 + +操作时间:2026-08-25 +操作类型:扩展、修改 +影响模块:5176 配送订单列表、配送订单列表接口 + +## 操作前状态 + +- 收货地址快照经过通用位置保护后,列表显示为 `-`。 +- 合同气瓶属于订单明细集合,通用列表响应没有可读摘要。 +- 5173 已有受控地址恢复和气瓶摘要,但 5176 仍使用通用列表函数。 + +## 具体操作 + +1. 把 5173 已有订单列表展示构造过程封装为共享函数。 +2. 配送端先限定当前配送点订单,再调用共享函数生成安全响应。 +3. 前端字段声明读取 `address` 和 `contract_products_summary` 派生字段。 +4. 合同气瓶摘要增加订单详情入口,空值统一显示“未填写”。 +5. 增加静态契约检查,防止以后退回裸关联字段或横线展示。 + +## 行为变化 + +- 变更前:收货地址和合同气瓶均显示 `-`。 +- 变更后:地址显示订单快照;气瓶显示可读摘要并可进入订单详情。 +- 兼容性:不修改数据库、创建订单请求和既有路由,只扩展已鉴权列表响应。 + +## 验证结果 + +- `go test ./api/internal/logic/delivery ./api/internal/logic/platform/gasorder`:通过。 +- `npm.cmd run type:check`:通过。 +- `npm.cmd run resource-pages:check`:通过,详情 17、新建 9、编辑 5。 +- `npm.cmd run contract:check`:通过,18 个资源。 +- `npm.cmd run build`:通过,2648 个模块完成生产构建。 +- 后端已重新构建并恢复监听 `12426`,旧程序已备份为 `platform-api.before-order-list-20260825-232713.exe`。 + +## 风险评估 + +- 地址属于敏感数据:仅对当前配送点已授权订单恢复快照,不影响公共接口。 +- 气瓶当前名称可能随设备档案改名变化;类型和编码仍使用订单快照。 +- 完整气瓶信息继续以订单详情为准,列表只提供紧凑摘要。 diff --git a/docs/项目文档_配送点订单列表可读信息_v1.0.md b/docs/项目文档_配送点订单列表可读信息_v1.0.md new file mode 100644 index 0000000..d537977 --- /dev/null +++ b/docs/项目文档_配送点订单列表可读信息_v1.0.md @@ -0,0 +1,43 @@ +# 项目文档:配送点订单列表可读信息 v1.0 + +## 1. 项目概述 + +5176 配送点管理端订单列表复用 5173 的受控展示规则,在当前配送点数据范围内显示订单收货地址快照和合同气瓶摘要。数据库、订单创建协议及通用敏感字段保护规则保持不变。 + +## 2. 核心实现 + +```text +backend/api/internal/logic/ +├── delivery/order.go # 先限定当前配送点,再构造订单列表响应 +└── platform/gasorder/order_list_display.go # 统一生成地址和气瓶可读摘要 +frontend/delivery_admin/src/ +├── api/resources.ts # 声明列表派生展示字段 +└── views/shared/ResourceListPage.vue # 渲染只读摘要和详情入口 +``` + +## 3. 展示规则 + +- 收货地址显示订单创建时保存的地址快照;空快照显示“未填写”。 +- 合同气瓶显示第一只气瓶的当前智能气阀名称和下单时类型快照;多只气瓶追加“等 N 个”。 +- 合同气瓶摘要可进入订单详情查看完整明细。 +- 地址和气瓶摘要均为只读派生字段,不参与新建或业务动作提交。 + +## 4. 安全边界 + +- 配送端必须先按当前登录账号所属配送点筛选订单,再调用共享展示构造器。 +- 地址恢复只作用于已经鉴权、已经完成配送点范围过滤的订单分页结果。 +- 气瓶摘要只按当前页订单主键批量查询,不接受前端提供任意订单主键。 +- 通用资源响应的地址、经纬度保护规则不变。 + +## 5. 验证 + +```text +go test ./api/internal/logic/delivery ./api/internal/logic/platform/gasorder +npm.cmd run type:check +npm.cmd run resource-pages:check +npm.cmd run build +``` + +## 6. 变更记录 + +- v1.0(2026-08-25):修复配送点订单列表收货地址和合同气瓶显示为横线的问题。 diff --git a/frontend/delivery_admin/scripts/check-resource-pages.mjs b/frontend/delivery_admin/scripts/check-resource-pages.mjs index 6ad5a39..efd9591 100644 --- a/frontend/delivery_admin/scripts/check-resource-pages.mjs +++ b/frontend/delivery_admin/scripts/check-resource-pages.mjs @@ -138,5 +138,9 @@ assert(recordPage.includes('if (saving.value) return'), '订单保存缺少显 assert(orderCreation.includes('productAmount + deliveryFee - discountAmount'), '订单金额预估公式不正确'); assert(orderCreation.includes("优惠金额必须小于商品金额与配送费之和"), '优惠金额缺少应付金额正数校验'); assert(orderPreview.includes('金额仅供预览'), '订单金额预估未声明服务端最终计算'); +assert(definitions.includes("listDisplayKey: 'address'"), '订单列表收货地址未使用受控地址快照'); +assert(definitions.includes("listDisplayKey: 'contract_products_summary'"), '订单列表合同气瓶未使用可读摘要'); +assert(listPage.includes('function displayListField'), '资源列表未支持后端派生展示字段'); +assert(listPage.includes('field.listDetailLink'), '订单气瓶摘要缺少详情入口'); console.log(`独立资源页面契约通过:详情 ${listResources.length},新建 ${creatable.size},编辑 ${editable.size}`); diff --git a/frontend/delivery_admin/src/api/resources.ts b/frontend/delivery_admin/src/api/resources.ts index 096decd..23c72ef 100644 --- a/frontend/delivery_admin/src/api/resources.ts +++ b/frontend/delivery_admin/src/api/resources.ts @@ -49,6 +49,10 @@ export type ResourceField = { listCopyable?: boolean; /** 关联选项优先展示的业务字段。 */ relationOptionLabelKey?: string; + /** 列表使用后端派生的只读展示字段,不改变提交字段。 */ + listDisplayKey?: string; + /** 列表摘要可直接进入当前记录详情。 */ + listDetailLink?: boolean; /** 合同气瓶等关系的组合展示方式。 */ relationOptionDisplay?: 'product-name-type'; /** 为默认候选追加中文标识,并在加载完成后自动选择。 */ @@ -564,6 +568,9 @@ const deliveryOverrides: ResourceUiDefinition[] = [ }), relation('user_address_identity', '/user_address', true, { label: '收货地址', + listDisplayKey: 'address', + listDetailLink: false, + emptyText: '未填写', placeholder: '请先选择配送合同,再选择该合同用户的收货地址', relationOptionLabelKey: 'address', relationOptionDefaultKey: 'is_default', @@ -581,6 +588,9 @@ const deliveryOverrides: ResourceUiDefinition[] = [ required: true, type: 'identity-list', relation: '/gasorder_contract_product', + listDisplayKey: 'contract_products_summary', + listDetailLink: true, + emptyText: '未填写', placeholder: '请输入智能气阀名称、气瓶类型或气瓶编码搜索', relationOptionDisplay: 'product-name-type', relationEmptyText: '该合同暂无可用气瓶,请先为合同绑定气瓶', diff --git a/frontend/delivery_admin/src/views/shared/ResourceListPage.vue b/frontend/delivery_admin/src/views/shared/ResourceListPage.vue index c860db1..cb4be5c 100644 --- a/frontend/delivery_admin/src/views/shared/ResourceListPage.vue +++ b/frontend/delivery_admin/src/views/shared/ResourceListPage.vue @@ -1,4 +1,4 @@ - +