From a66c0d9b906528ad25fdfc8927a64a88e2901ceb Mon Sep 17 00:00:00 2001 From: Rogee Date: Thu, 25 Dec 2025 14:08:09 +0800 Subject: [PATCH] chore: enforce gen transaction rule --- backend/app/services/tenant.go | 17 +++++++++++------ backend/llm.txt | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/backend/app/services/tenant.go b/backend/app/services/tenant.go index fe12ae4..14053dd 100644 --- a/backend/app/services/tenant.go +++ b/backend/app/services/tenant.go @@ -891,10 +891,9 @@ func (t *tenant) ApplyOwnedTenant(ctx context.Context, userID int64, code, name } // 事务边界:创建租户 + 写入 tenant_users(租户管理员角色)。 - // 使用全新 Session,避免复用带 Model/Statement 的 DB 句柄引发 GORM 反射 panic。 - db := _db.WithContext(ctx).Session(&gorm.Session{}) - err = db.Transaction(func(tx *gorm.DB) error { - if err := tx.Omit("Users").Create(tenant).Error; err != nil { + // MUST: 使用 Gen 事务包装,确保同一事务连接与一致的查询风格。 + err = models.Q.Transaction(func(tx *models.Query) error { + if err := tx.Tenant.WithContext(ctx).Create(tenant); err != nil { return err } @@ -904,11 +903,17 @@ func (t *tenant) ApplyOwnedTenant(ctx context.Context, userID int64, code, name Role: types.NewArray([]consts.TenantUserRole{consts.TenantUserRoleTenantAdmin}), Status: consts.UserStatusVerified, } - if err := tx.Create(tenantUser).Error; err != nil { + if err := tx.TenantUser.WithContext(ctx).Create(tenantUser); err != nil { return err } - return tx.First(tenant, tenant.ID).Error + // 回填 created_at/updated_at 等字段,保持与其他创建逻辑一致。 + fresh, err := tx.Tenant.WithContext(ctx).Where(tx.Tenant.ID.Eq(tenant.ID)).First() + if err != nil { + return err + } + *tenant = *fresh + return nil }) if err != nil { return nil, err diff --git a/backend/llm.txt b/backend/llm.txt index a87c55a..e32139a 100644 --- a/backend/llm.txt +++ b/backend/llm.txt @@ -11,6 +11,7 @@ This file condenses `backend/docs/dev/http_api.md` + `backend/docs/dev/model.md` - DO keep controller methods thin: parse/bind → call `services.*` → return result/error. - DO regenerate code after changes (routes/docs/models). - MUST: in `backend/app/services`, prefer the generated GORM-Gen DAO (`backend/database/models/*`) for DB access; treat raw `*gorm.DB` usage as a last resort. +- MUST: service-layer transactions MUST use `models.Q.Transaction(func(tx *models.Query) error { ... })`; DO NOT use raw `*_db.Transaction(...)` / `db.Transaction(...)` in services unless Gen cannot express the required operation. - MUST: after adding/removing/renaming any files under `backend/app/services/`, run `atomctl gen service --path ./app/services` to regenerate `backend/app/services/services.gen.go`; DO NOT edit `services.gen.go` manually. - DO add `// @provider` above every controller/service `struct` declaration. - DO keep HTTP middlewares in `backend/app/middlewares/` only. @@ -175,7 +176,7 @@ Reference: `backend/llm.gorm_gen.txt`. - MUST: use Gen transaction wrapper so all queries share the same tx connection: - `models.Q.Transaction(func(tx *models.Query) error { ... })` - Inside tx, use `tx..QueryContext(ctx)` / `tx.
.WithContext(ctx)` -- DO NOT: use `_db.WithContext(ctx).Transaction(...)` in services unless Gen cannot express a required operation. +- DO NOT: use `_db.WithContext(ctx).Transaction(...)` / `db.Transaction(...)` in services unless Gen cannot express a required operation. ### 3.3 Updates