From 28e90587a7e1a4c32db409b7126e1c0a5bd0bc31 Mon Sep 17 00:00:00 2001 From: chengqian Date: Thu, 28 May 2026 21:16:54 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E7=A7=9F?= =?UTF-8?q?=E6=88=B7=E7=99=BB=E8=AE=B0=E4=BF=AE=E6=94=B9=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/reference/goframe-conventions.md | 32 +++++++++++++++++++++++++++ internal/logic/admin/tenant_level.go | 25 ++++++++++++--------- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/docs/reference/goframe-conventions.md b/docs/reference/goframe-conventions.md index f936289..5f24c73 100644 --- a/docs/reference/goframe-conventions.md +++ b/docs/reference/goframe-conventions.md @@ -285,3 +285,35 @@ r := g.RequestFromCtx(ctx) grep -r "CodeNotImplemented" internal/controller/ ``` 如果有匹配,手动将桩代码替换为 `return service.Admin().MethodName(ctx, req)`。或者先删除对应控制器文件再重新执行 `gf gen ctrl`。 + +### 2026-05-28:Data(g.Map{}) 做部分更新导致更新无效 + +**问题**:租户等级配置的 `UpdateTenantLevelConfig` 使用 `g.Map{}` 收集待更新字段,再调用 `Data(data).Update()`。接口返回成功但数据库数据不变。 + +**原因**:GoFrame 规范要求数据库操作必须使用 DO 对象,禁止使用 `g.Map`。DO 结构体字段类型为 `interface{}`(`any`),未赋值的字段保持 `nil`,ORM 内置 OmitNil 行为会自动跳过;而已赋值的字段(包括零值 `0`、`""`)会被正常写入。使用 `g.Map` 则可能遇到:键名与列名不匹配被静默忽略、框架行为不一致等问题。 + +**修复**:将 `g.Map{}` 替换为 `do.TntTenantLevelConfigs{}`,通过指针 nil 检查决定是否赋值 DO 字段,用 `hasUpdate` 标记是否有更新。 + +**正确做法**: +```go +// 正确 — 使用 DO 对象做部分更新 +data := do.XxxTable{} +hasUpdate := false +if req.Name != nil { + data.Name = *req.Name + hasUpdate = true +} +if req.Count != nil { + data.Count = *req.Count // 即使 *req.Count == 0 也会写入 + hasUpdate = true +} +if !hasUpdate { + return res, nil +} +dao.XxxTable.Ctx(ctx).Where("id", req.Id).Data(data).Update() + +// 错误 — 使用 g.Map +data := g.Map{} +if req.Name != nil { data["name"] = *req.Name } +dao.XxxTable.Ctx(ctx).Where("id", req.Id).Data(data).Update() +``` diff --git a/internal/logic/admin/tenant_level.go b/internal/logic/admin/tenant_level.go index 5323589..20f88fa 100644 --- a/internal/logic/admin/tenant_level.go +++ b/internal/logic/admin/tenant_level.go @@ -3,8 +3,6 @@ package admin import ( "context" - "github.com/gogf/gf/v2/frame/g" - "github.com/qianfree/team-api/api/admin/v1" "github.com/qianfree/team-api/internal/dao" "github.com/qianfree/team-api/internal/logic/common" @@ -65,27 +63,34 @@ func (s *sAdmin) CreateTenantLevelConfig(ctx context.Context, req *v1.TenantLeve } func (s *sAdmin) UpdateTenantLevelConfig(ctx context.Context, req *v1.TenantLevelConfigUpdateReq) (*v1.TenantLevelConfigUpdateRes, error) { - data := g.Map{} + data := do.TntTenantLevelConfigs{} + hasUpdate := false if req.Name != nil { - data["name"] = *req.Name + data.Name = *req.Name + hasUpdate = true } if req.CumulativeRechargeThreshold != nil { - data["cumulative_recharge_threshold"] = *req.CumulativeRechargeThreshold + data.CumulativeRechargeThreshold = *req.CumulativeRechargeThreshold + hasUpdate = true } if req.MaxMembers != nil { - data["max_members"] = *req.MaxMembers + data.MaxMembers = *req.MaxMembers + hasUpdate = true } if req.MaxConcurrency != nil { - data["max_concurrency"] = *req.MaxConcurrency + data.MaxConcurrency = *req.MaxConcurrency + hasUpdate = true } if req.PriceMultiplier != nil { - data["price_multiplier"] = *req.PriceMultiplier + data.PriceMultiplier = *req.PriceMultiplier + hasUpdate = true } if req.SortOrder != nil { - data["sort_order"] = *req.SortOrder + data.SortOrder = *req.SortOrder + hasUpdate = true } - if len(data) == 0 { + if !hasUpdate { return &v1.TenantLevelConfigUpdateRes{}, nil }