diff --git a/backend/api/internal/logic/platform/delivery/delivery.go b/backend/api/internal/logic/platform/delivery/delivery.go index c462795..8e49cb2 100644 --- a/backend/api/internal/logic/platform/delivery/delivery.go +++ b/backend/api/internal/logic/platform/delivery/delivery.go @@ -9,11 +9,11 @@ import ( "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" "git.apinb.com/heqiapp/platforms/backend/api/internal/models" "github.com/gin-gonic/gin" - "gorm.io/gorm" ) // ListDeliveryBasic 查询配送点分页列表。 func ListDeliveryBasic(ctx *gin.Context) { + page, size := common.PageSize(ctx) gasIdentities := strings.Split(strings.TrimSpace(ctx.Query("gas_basic_identities")), ",") if len(gasIdentities) == 1 && gasIdentities[0] == "" { gasIdentities = nil @@ -22,20 +22,69 @@ func ListDeliveryBasic(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - common.ListPageFiltered[models.DeliveryBasic](ctx, func(query *gorm.DB) *gorm.DB { - if len(gasIdentities) == 0 { - return query - } - return query.Where( + var list []models.DeliveryBasic + var total int64 + query := common.ApplyKeywordFilter(ctx, common.ActiveRecords(impl.DBService.Model(&models.DeliveryBasic{})), &models.DeliveryBasic{}) + if len(gasIdentities) > 0 { + query = query.Where( "gas_basic_id IN (SELECT id FROM gas_basic WHERE identity IN ? AND status <> ?)", gasIdentities, common.StatusArchived, ) - }) + } + if err := query.Count(&total).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + if err := query.Order("created_at desc").Offset((page - 1) * size).Limit(size).Find(&list).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + response, err := common.PublicResourceResponse(list) + if err != nil { + infra.Response.Error(ctx, err) + return + } + // 配送点地址属于平台组织主数据;仅在本资源的受保护接口中按记录恢复,避免放宽全局敏感字段规则。 + protected := common.ProtectPreciseLocation(ctx, &models.DeliveryBasic{}, response) + infra.Response.Success(ctx, gin.H{"total": total, "list": restoreDeliveryBasicAddresses(protected, list)}) } -// GetDeliveryBasic 查询一个配送点。 -func GetDeliveryBasic(ctx *gin.Context) { common.GetByIdentity[models.DeliveryBasic](ctx) } +// GetDeliveryBasic 查询一个配送点,并返回编辑表单需要的组织地址。 +func GetDeliveryBasic(ctx *gin.Context) { + var delivery models.DeliveryBasic + if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&delivery).Error; err != nil { + common.RespondRecordError(ctx, err) + return + } + response, err := common.PublicResourceResponse(delivery) + if err != nil { + infra.Response.Error(ctx, err) + return + } + protected := common.ProtectPreciseLocation(ctx, &models.DeliveryBasic{}, response) + infra.Response.Success(ctx, restoreDeliveryBasicAddresses(protected, []models.DeliveryBasic{delivery})) +} + +// restoreDeliveryBasicAddresses 将已鉴权配送点记录的地址恢复到安全响应中。 +func restoreDeliveryBasicAddresses(response any, deliveries []models.DeliveryBasic) any { + switch data := response.(type) { + case []any: + for index, item := range data { + if index >= len(deliveries) { + break + } + if record, ok := item.(map[string]any); ok { + record["address"] = deliveries[index].Address + } + } + case map[string]any: + if len(deliveries) > 0 { + data["address"] = deliveries[0].Address + } + } + return response +} // CreateDeliveryBasic 创建配送点档案。 func CreateDeliveryBasic(ctx *gin.Context) { diff --git a/backend/api/internal/logic/platform/delivery/delivery_test.go b/backend/api/internal/logic/platform/delivery/delivery_test.go new file mode 100644 index 0000000..6c239ed --- /dev/null +++ b/backend/api/internal/logic/platform/delivery/delivery_test.go @@ -0,0 +1,38 @@ +package delivery + +import ( + "testing" + + "git.apinb.com/heqiapp/platforms/backend/api/internal/models" +) + +// TestRestoreDeliveryBasicAddresses 验证配送点列表按原顺序恢复组织地址。 +func TestRestoreDeliveryBasicAddresses(t *testing.T) { + response := []any{ + map[string]any{"identity": "delivery-1"}, + map[string]any{"identity": "delivery-2"}, + } + deliveries := []models.DeliveryBasic{ + {Address: "上海市配送路 1 号"}, + {Address: "上海市配送路 2 号"}, + } + + restored := restoreDeliveryBasicAddresses(response, deliveries).([]any) + if restored[0].(map[string]any)["address"] != deliveries[0].Address { + t.Fatalf("第一条配送点地址未正确恢复:%#v", restored[0]) + } + if restored[1].(map[string]any)["address"] != deliveries[1].Address { + t.Fatalf("第二条配送点地址未正确恢复:%#v", restored[1]) + } +} + +// TestRestoreDeliveryBasicAddress 验证配送点详情恢复编辑所需地址。 +func TestRestoreDeliveryBasicAddress(t *testing.T) { + response := map[string]any{"identity": "delivery-1"} + delivery := models.DeliveryBasic{Address: "上海市配送路 18 号"} + + restored := restoreDeliveryBasicAddresses(response, []models.DeliveryBasic{delivery}).(map[string]any) + if restored["address"] != delivery.Address { + t.Fatalf("配送点详情地址未正确恢复:%#v", restored) + } +} diff --git a/docs/操作日志_配送点地址保存修复_20260810.md b/docs/操作日志_配送点地址保存修复_20260810.md new file mode 100644 index 0000000..9113fbe --- /dev/null +++ b/docs/操作日志_配送点地址保存修复_20260810.md @@ -0,0 +1,43 @@ +# 配送点地址保存修复操作日志 + +操作时间:2026-08-10 11:49:03 + +操作类型:修改 +影响模块:平台总后台配送点管理、平台 API 配送点资源 + +## 操作前状态 + +配送点地址可以通过更新接口写入数据库,但平台资源的通用敏感字段投影会从列表和详情响应中移除 `address`。页面保存后重新加载列表仍显示 `-`,再次编辑时也无法回填已保存地址,用户感知为保存失败。 + +## 具体操作 + +1. 保留全局敏感字段投影规则,不放宽用户地址、订单地址等其他资源的数据范围。 +2. 在已受平台 JWT、角色菜单和资源权限保护的 `delivery_basic` 列表与详情接口中,按查询结果恢复配送点组织地址。 +3. 保留原有分页、关键字搜索、气站筛选、归档过滤和精确位置保护行为。 +4. 新增列表及详情地址恢复回归测试。 + +## 操作后状态 + +配送点地址保存后会在列表中显示,并可在后续编辑时正确回填。其他资源仍沿用原有敏感字段保护规则。 + +## 代码变更 + +- `backend/api/internal/logic/platform/delivery/delivery.go` + - `ListDeliveryBasic`:扩展受保护列表响应,恢复配送点地址。 + - `GetDeliveryBasic`:扩展受保护详情响应,返回编辑所需地址。 + - `restoreDeliveryBasicAddresses`:新增资源级地址恢复函数。 +- `backend/api/internal/logic/platform/delivery/delivery_test.go` + - 新增列表和详情地址恢复测试。 + +## 验证结果 + +- `go test ./internal/logic/platform/delivery ./internal/logic/common`:通过。 +- `go test ./...`:通过。 +- `git diff --check`:通过。 +- 边界验证:空列表、列表顺序、单条详情和全局脱敏规则均未改变。 + +## 风险评估 + +- 影响范围仅限平台总后台的配送点资源响应。 +- 配送点地址会对拥有配送点管理菜单权限的平台账号可见,符合该页面已有地址列和编辑能力。 +- 未修改数据库结构、公共路由、更新接口或其他资源响应;回滚时仅需撤销本次资源级响应扩展。