fix(model):修复模型结构体未设置导致查询出错
This commit is contained in:
1 parent
b267119f12
commit
80b4754208
14 files changed
+159
-127
No files matched your search
@@ -927,3 +927,32 @@ s.Start()
|
||||
**修复方式**:显式指定列 `dao.SysOptions.Ctx(ctx).Where("key", key).Fields("value").Value()`,与代码库其余 5 处 `Fields("xxx").Value()` 的既有写法保持一致。
|
||||
|
||||
**正确做法(通用规则)**:`Value()` / `Array()` 这类取单列的快捷方法**必须**显式 `Fields("列名")`,否则取到的是 `id`。评审信号:`grep -rn '\.Value()'` 命中且前面没有 `.Fields(` 的调用。
|
||||
|
||||
### 2026-09-05:Scan 进无 orm 标签的匿名结构体——TableFields 元数据查询失败时帕斯卡列名裸进 SQL
|
||||
|
||||
**问题**:管理后台租户列表接口 `/api/admin/tenants` 偶发报 `pq: column "Id" does not exist`,SQL 里出现 `"Id","Name","LogoURL"` 等帕斯卡命名列。PostgreSQL 引号标识符大小写敏感,真实列是 `id`/`logo_url`,查询必然失败。
|
||||
|
||||
**原因**:`internal/logic/admin/tenant.go` 的 `ListTenants` 把查询结果 `Scan` 进一个**只有 json 标签、没有 orm 标签的匿名结构体**。gf 的 `Model.Scan`(`doStructs`)有个内置行为 *"Auto selecting fields by struct attributes"*:模型未显式 `Fields()` 时,会用**扫描结构体的字段名**自动生成 SELECT 字段列表。字段解析链路(`gdb_func.go` 的 `getFieldsFromStructOrMap`):有 orm 标签取标签值,**没有则取 Go 字段名原样**;随后 `mappingAndFilterToTableFields` 尝试用真实表结构把 `Id→id`、`LogoURL→logo_url` 纠偏,但它对元数据查询失败的容忍方式是(`gdb_model_utility.go`):
|
||||
|
||||
```go
|
||||
fieldsMap, _ := m.TableFields(fieldsTable) // 错误被静默吞掉
|
||||
if len(fieldsMap) == 0 {
|
||||
return fields // 元数据拿不到就原样透传 Go 字段名
|
||||
}
|
||||
```
|
||||
|
||||
pgsql 驱动的 `TableFields` 要实时查 `pg_attribute` 系统表(`'表名'::regclass`)。远程数据库瞬时抖动一次、这条目录查询失败,帕斯卡字段名就直接进了 SQL。**平时不炸的原因**:TableFields 成功结果进程内永久缓存(`gcache.DurationNoExpire`),启动后第一次查成功就一直复用;失败不缓存,所以重试通常即恢复。同请求内先执行的 `Count()` 走数据链路成功,进一步印证只有目录查询那一步挂了。
|
||||
|
||||
**修复方式**:给匿名结构体补上 orm 标签(`json:"id" orm:"id"` 等 13 个字段)。orm 标签值直接成为 SELECT 字段名——即使 TableFields 失败、字段名原样透传,透传出去的也是正确的小写蛇形列名,SQL 依然成立,彻底消除对运行时元数据的依赖。随后用 go/AST 脚本全仓排查同款写法,批量修复 24 处(幂等中间件、admin/tenant/open 列表与导出、成员模型范围、预算巡检等)。
|
||||
|
||||
**不受影响的两种同貌写法(排查时排除)**:
|
||||
|
||||
- **模型链上有显式 `.Fields(...)`**:`doStructs` 的自动投影有前置条件 `len(fields)==0 && len(fieldsEx)==0`,显式 Fields 时不触发,SELECT 列已固定。
|
||||
- **`Raw(sql).Scan(&x)` 与 `result[0].Struct(&x)`**:Raw 路径在 `getFormattedSqlAndArgs` 里直接透传 rawSql、不拼接 Fields;`Record.Struct` 是内存行→结构体转换,根本不生成 SQL。
|
||||
|
||||
**正确做法(通用规则)**:
|
||||
|
||||
- **凡 `Scan`/`Struct`/`Structs` 进非 `model/entity` 生成的结构体(匿名结构体、手写 DTO),每个字段必须带 `orm` 标签**,列名写死不依赖运行时映射;`entity.Xxx` 自带 orm 标签天然安全。
|
||||
- 判断风险的方法:`grep -rn 'var .* \[\]struct {' internal/` 找到匿名结构体,再看它是否被传给 `dao.Xxx.Ctx(ctx)...Scan(&x)` 链路且链上无 `Fields(...)`/`Raw(...)`——是则全部依赖这条元数据映射,远程 DB 抖动哪个请求撞上哪个接口炸,且报错完全看不出是网络问题。
|
||||
|
||||
排查信号:`pq: column "Xxx" does not exist` 且 SQL 列名是帕斯卡命名;偶发、重试即好、与网络抖动时间点吻合。
|
||||
@@ -29,16 +29,16 @@ func (s *sAdmin) ListUsers(ctx context.Context, req *v1.AdminUserListReq) (*v1.A
|
||||
m := buildUserFilters(dao.SysAdminUsers.Ctx(ctx), req.Keyword, req.Role, req.Status)
|
||||
|
||||
var users []struct {
|
||||
Id int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Role string `json:"role"`
|
||||
Status string `json:"status"`
|
||||
LastLoginAt *gtime.Time `json:"last_login_at"`
|
||||
LastLoginIp string `json:"last_login_ip"`
|
||||
LockedUntil *gtime.Time `json:"locked_until"`
|
||||
CreatedAt *gtime.Time `json:"created_at"`
|
||||
Id int64 `json:"id" orm:"id"`
|
||||
Username string `json:"username" orm:"username"`
|
||||
Email string `json:"email" orm:"email"`
|
||||
DisplayName string `json:"display_name" orm:"display_name"`
|
||||
Role string `json:"role" orm:"role"`
|
||||
Status string `json:"status" orm:"status"`
|
||||
LastLoginAt *gtime.Time `json:"last_login_at" orm:"last_login_at"`
|
||||
LastLoginIp string `json:"last_login_ip" orm:"last_login_ip"`
|
||||
LockedUntil *gtime.Time `json:"locked_until" orm:"locked_until"`
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at"`
|
||||
}
|
||||
err = m.OrderDesc("id").
|
||||
Page(page, pageSize).
|
||||
@@ -517,15 +517,15 @@ func (s *sAdmin) ExportUsers(ctx context.Context, req *v1.AdminUserExportReq) (*
|
||||
for {
|
||||
m := buildUserFilters(dao.SysAdminUsers.Ctx(ctx), req.Keyword, req.Role, req.Status)
|
||||
var batch []struct {
|
||||
Id int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Role string `json:"role"`
|
||||
Status string `json:"status"`
|
||||
LastLoginAt *gtime.Time `json:"last_login_at"`
|
||||
LastLoginIp string `json:"last_login_ip"`
|
||||
CreatedAt *gtime.Time `json:"created_at"`
|
||||
Id int64 `json:"id" orm:"id"`
|
||||
Username string `json:"username" orm:"username"`
|
||||
Email string `json:"email" orm:"email"`
|
||||
DisplayName string `json:"display_name" orm:"display_name"`
|
||||
Role string `json:"role" orm:"role"`
|
||||
Status string `json:"status" orm:"status"`
|
||||
LastLoginAt *gtime.Time `json:"last_login_at" orm:"last_login_at"`
|
||||
LastLoginIp string `json:"last_login_ip" orm:"last_login_ip"`
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at"`
|
||||
}
|
||||
if err := m.OrderDesc("id").Limit(1000).Offset(offset).Scan(&batch); err != nil {
|
||||
return
|
||||
|
||||
@@ -719,13 +719,13 @@ func (s *sAdmin) GetChannelKeys(ctx context.Context, req *v1.ChannelKeyListReq)
|
||||
// GetChannelAbilities 获取渠道模型能力列表
|
||||
func (s *sAdmin) GetChannelAbilities(ctx context.Context, req *v1.ChannelAbilitiesGetReq) (*v1.ChannelAbilitiesGetRes, error) {
|
||||
var abilities []struct {
|
||||
ID int64 `json:"id"`
|
||||
ModelName string `json:"model_name"`
|
||||
UpstreamModel string `json:"upstream_model"`
|
||||
Enabled bool `json:"enabled"`
|
||||
CostRatio float64 `json:"cost_ratio"`
|
||||
SupportsResponses bool `json:"supports_responses"`
|
||||
ChatViaResponses bool `json:"chat_via_responses"`
|
||||
ID int64 `json:"id" orm:"id"`
|
||||
ModelName string `json:"model_name" orm:"model_name"`
|
||||
UpstreamModel string `json:"upstream_model" orm:"upstream_model"`
|
||||
Enabled bool `json:"enabled" orm:"enabled"`
|
||||
CostRatio float64 `json:"cost_ratio" orm:"cost_ratio"`
|
||||
SupportsResponses bool `json:"supports_responses" orm:"supports_responses"`
|
||||
ChatViaResponses bool `json:"chat_via_responses" orm:"chat_via_responses"`
|
||||
}
|
||||
|
||||
err := dao.ChnAbilities.Ctx(ctx).
|
||||
|
||||
@@ -443,18 +443,18 @@ func parseCapabilities(raw string) map[string]bool {
|
||||
// GetModelPricing 获取模型定价
|
||||
func (s *sAdmin) GetModelPricing(ctx context.Context, req *v1.PricingGetReq) (*v1.PricingGetRes, error) {
|
||||
var rows []struct {
|
||||
BillingMode string `json:"billing_mode"`
|
||||
MinTokens int64 `json:"min_tokens"`
|
||||
MaxTokens *int64 `json:"max_tokens"`
|
||||
InputPrice float64 `json:"input_price"`
|
||||
OutputPrice float64 `json:"output_price"`
|
||||
PerRequestPrice *float64 `json:"per_request_price"`
|
||||
CacheReadPrice float64 `json:"cache_read_price"`
|
||||
CacheCreationPrice float64 `json:"cache_creation_price"`
|
||||
TimeSegments string `json:"time_segments"`
|
||||
PriceNote *string `json:"price_note"`
|
||||
DiscountLabel *string `json:"discount_label"`
|
||||
PriceChangeNote *string `json:"price_change_note"`
|
||||
BillingMode string `json:"billing_mode" orm:"billing_mode"`
|
||||
MinTokens int64 `json:"min_tokens" orm:"min_tokens"`
|
||||
MaxTokens *int64 `json:"max_tokens" orm:"max_tokens"`
|
||||
InputPrice float64 `json:"input_price" orm:"input_price"`
|
||||
OutputPrice float64 `json:"output_price" orm:"output_price"`
|
||||
PerRequestPrice *float64 `json:"per_request_price" orm:"per_request_price"`
|
||||
CacheReadPrice float64 `json:"cache_read_price" orm:"cache_read_price"`
|
||||
CacheCreationPrice float64 `json:"cache_creation_price" orm:"cache_creation_price"`
|
||||
TimeSegments string `json:"time_segments" orm:"time_segments"`
|
||||
PriceNote *string `json:"price_note" orm:"price_note"`
|
||||
DiscountLabel *string `json:"discount_label" orm:"discount_label"`
|
||||
PriceChangeNote *string `json:"price_change_note" orm:"price_change_note"`
|
||||
}
|
||||
|
||||
err := dao.MdlPricing.Ctx(ctx).
|
||||
|
||||
@@ -163,7 +163,7 @@ func PermissionGroups() []v1.PermissionGroup {
|
||||
func (s *sAdmin) GetUserPermissions(ctx context.Context, req *v1.AdminPermissionListReq) (*v1.AdminPermissionListRes, error) {
|
||||
// Get permission points
|
||||
var perms []struct {
|
||||
PermissionPoint string `json:"permission_point"`
|
||||
PermissionPoint string `json:"permission_point" orm:"permission_point"`
|
||||
}
|
||||
err := dao.SysAdminRolePerms.Ctx(ctx).
|
||||
Where("admin_user_id", req.Id).
|
||||
@@ -179,9 +179,9 @@ func (s *sAdmin) GetUserPermissions(ctx context.Context, req *v1.AdminPermission
|
||||
|
||||
// Get data scopes
|
||||
var scopes []struct {
|
||||
ID int64 `json:"id"`
|
||||
ScopeType string `json:"scope_type"`
|
||||
ScopeValue string `json:"scope_value"`
|
||||
ID int64 `json:"id" orm:"id"`
|
||||
ScopeType string `json:"scope_type" orm:"scope_type"`
|
||||
ScopeValue string `json:"scope_value" orm:"scope_value"`
|
||||
}
|
||||
err = dao.SysAdminDataScopes.Ctx(ctx).
|
||||
Where("admin_user_id", req.Id).
|
||||
@@ -348,9 +348,9 @@ func GetDataScopes(ctx context.Context, userID int64, role string) ([]v1.DataSco
|
||||
}
|
||||
|
||||
var scopes []struct {
|
||||
ID int64 `json:"id"`
|
||||
ScopeType string `json:"scope_type"`
|
||||
ScopeValue string `json:"scope_value"`
|
||||
ID int64 `json:"id" orm:"id"`
|
||||
ScopeType string `json:"scope_type" orm:"scope_type"`
|
||||
ScopeValue string `json:"scope_value" orm:"scope_value"`
|
||||
}
|
||||
err := dao.SysAdminDataScopes.Ctx(ctx).
|
||||
Where("admin_user_id", userID).
|
||||
|
||||
@@ -101,14 +101,14 @@ func (s *sAdmin) ListRoles(ctx context.Context, _ *v1.AdminRoleListReq) (*v1.Adm
|
||||
return nil, err
|
||||
}
|
||||
var roles []struct {
|
||||
ID int64 `json:"id"`
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
IsBuiltin bool `json:"is_builtin"`
|
||||
IsEnabled bool `json:"is_enabled"`
|
||||
Sort int `json:"sort"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
ID int64 `json:"id" orm:"id"`
|
||||
Code string `json:"code" orm:"code"`
|
||||
Name string `json:"name" orm:"name"`
|
||||
Description string `json:"description" orm:"description"`
|
||||
IsBuiltin bool `json:"is_builtin" orm:"is_builtin"`
|
||||
IsEnabled bool `json:"is_enabled" orm:"is_enabled"`
|
||||
Sort int `json:"sort" orm:"sort"`
|
||||
CreatedAt string `json:"created_at" orm:"created_at"`
|
||||
}
|
||||
err := dao.SysAdminRoles.Ctx(ctx).OrderAsc("sort").OrderAsc("id").Scan(&roles)
|
||||
if err = lcommon.IgnoreScanNoRows(err); err != nil {
|
||||
|
||||
@@ -290,20 +290,23 @@ func (s *sAdmin) ListTenants(ctx context.Context, req *v1.TenantListReq) (*v1.Te
|
||||
m = m.Where("status", req.Status)
|
||||
}
|
||||
|
||||
// 扫描结构体必须带 orm 标签:gf 的 Scan 会用「结构体字段名」自动生成 SELECT 字段列表,
|
||||
// 无 orm 标签时靠运行时查表结构(TableFields)做 Id→id 映射;该元数据查询失败会被框架静默吞掉,
|
||||
// 帕斯卡字段名将原样进 SQL,PostgreSQL 引号列名大小写敏感,报 column "Id" does not exist。
|
||||
var tenants []struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
LogoURL string `json:"logo_url"`
|
||||
OwnerUserID int64 `json:"owner_user_id"`
|
||||
Status string `json:"status"`
|
||||
MaxMembers *int `json:"max_members"`
|
||||
MaxConcurrency *int `json:"max_concurrency"`
|
||||
DefaultChannelScope string `json:"default_channel_scope"`
|
||||
Settings string `json:"settings"`
|
||||
Level int `json:"level"`
|
||||
CreatedAt *gtime.Time `json:"created_at"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at"`
|
||||
Id int64 `json:"id" orm:"id"`
|
||||
Name string `json:"name" orm:"name"`
|
||||
Code string `json:"code" orm:"code"`
|
||||
LogoURL string `json:"logo_url" orm:"logo_url"`
|
||||
OwnerUserID int64 `json:"owner_user_id" orm:"owner_user_id"`
|
||||
Status string `json:"status" orm:"status"`
|
||||
MaxMembers *int `json:"max_members" orm:"max_members"`
|
||||
MaxConcurrency *int `json:"max_concurrency" orm:"max_concurrency"`
|
||||
DefaultChannelScope string `json:"default_channel_scope" orm:"default_channel_scope"`
|
||||
Settings string `json:"settings" orm:"settings"`
|
||||
Level int `json:"level" orm:"level"`
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at"`
|
||||
}
|
||||
err = m.OrderDesc("id").
|
||||
Page(page, pageSize).
|
||||
|
||||
@@ -82,14 +82,14 @@ func (s *sAdmin) UsageLogCleanupList(ctx context.Context, req *v1.UsageLogCleanu
|
||||
}
|
||||
|
||||
var tasks []struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
ErrorMessage string `json:"error_message"`
|
||||
Result string `json:"result"`
|
||||
StartedAt *time.Time `json:"started_at"`
|
||||
FinishedAt *time.Time `json:"finished_at"`
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
Id int64 `json:"id" orm:"id"`
|
||||
Name string `json:"name" orm:"name"`
|
||||
Status string `json:"status" orm:"status"`
|
||||
ErrorMessage string `json:"error_message" orm:"error_message"`
|
||||
Result string `json:"result" orm:"result"`
|
||||
StartedAt *time.Time `json:"started_at" orm:"started_at"`
|
||||
FinishedAt *time.Time `json:"finished_at" orm:"finished_at"`
|
||||
CreatedAt *time.Time `json:"created_at" orm:"created_at"`
|
||||
}
|
||||
err = m.Page(page, pageSize).Scan(&tasks)
|
||||
if err != nil {
|
||||
|
||||
@@ -101,12 +101,12 @@ func (s *sOpen) OpenMemberList(ctx context.Context, req *v1.OpenMemberListReq) (
|
||||
}
|
||||
|
||||
var users []struct {
|
||||
Id int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Role string `json:"role"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Id int64 `json:"id" orm:"id"`
|
||||
Username string `json:"username" orm:"username"`
|
||||
Email string `json:"email" orm:"email"`
|
||||
Role string `json:"role" orm:"role"`
|
||||
Status string `json:"status" orm:"status"`
|
||||
CreatedAt string `json:"created_at" orm:"created_at"`
|
||||
}
|
||||
err = m.OrderDesc("id").Page(page, pageSize).Scan(&users)
|
||||
if err != nil {
|
||||
@@ -354,7 +354,7 @@ func (s *sOpen) OpenMemberModels(ctx context.Context, req *v1.OpenMemberModelsRe
|
||||
}
|
||||
|
||||
var scopes []struct {
|
||||
ModelID string `json:"model_id"`
|
||||
ModelID string `json:"model_id" orm:"model_id"`
|
||||
}
|
||||
err := dao.TntMemberModelScopes.Ctx(ctx).
|
||||
Where("tenant_id", tenantID).
|
||||
@@ -460,11 +460,11 @@ func (s *sOpen) OpenKeyList(ctx context.Context, req *v1.OpenKeyListReq) (*v1.Op
|
||||
}
|
||||
|
||||
var keys []struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
KeyPrefix string `json:"key_prefix"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt *gtime.Time `json:"created_at"`
|
||||
Id int64 `json:"id" orm:"id"`
|
||||
Name string `json:"name" orm:"name"`
|
||||
KeyPrefix string `json:"key_prefix" orm:"key_prefix"`
|
||||
Status string `json:"status" orm:"status"`
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at"`
|
||||
}
|
||||
err = m.OrderDesc("id").Page(page, pageSize).Scan(&keys)
|
||||
if err != nil {
|
||||
|
||||
@@ -65,13 +65,13 @@ func (s *sSettings) PublicAnnouncements(ctx context.Context, req *v1.PublicAnnou
|
||||
}
|
||||
|
||||
var items []struct {
|
||||
Id int64 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Type string `json:"type"`
|
||||
Content string `json:"content"`
|
||||
IsPinned int `json:"is_pinned"`
|
||||
DisplayPosition string `json:"display_position"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Id int64 `json:"id" orm:"id"`
|
||||
Title string `json:"title" orm:"title"`
|
||||
Type string `json:"type" orm:"type"`
|
||||
Content string `json:"content" orm:"content"`
|
||||
IsPinned int `json:"is_pinned" orm:"is_pinned"`
|
||||
DisplayPosition string `json:"display_position" orm:"display_position"`
|
||||
CreatedAt string `json:"created_at" orm:"created_at"`
|
||||
}
|
||||
err := m.OrderDesc("is_pinned").OrderDesc("created_at").
|
||||
Limit(20).
|
||||
|
||||
@@ -49,18 +49,18 @@ func (s *sTenant) ListMembers(ctx context.Context, req *v1.TenantMemberListReq)
|
||||
}
|
||||
|
||||
var users []struct {
|
||||
Id int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Role string `json:"role"`
|
||||
Status string `json:"status"`
|
||||
LockedUntil string `json:"locked_until"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
QuotaType string `json:"quota_type"`
|
||||
QuotaLimit float64 `json:"quota_limit"`
|
||||
QuotaPeriod string `json:"quota_period"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
Id int64 `json:"id" orm:"id"`
|
||||
Username string `json:"username" orm:"username"`
|
||||
Email string `json:"email" orm:"email"`
|
||||
DisplayName string `json:"display_name" orm:"display_name"`
|
||||
Role string `json:"role" orm:"role"`
|
||||
Status string `json:"status" orm:"status"`
|
||||
LockedUntil string `json:"locked_until" orm:"locked_until"`
|
||||
CreatedAt string `json:"created_at" orm:"created_at"`
|
||||
QuotaType string `json:"quota_type" orm:"quota_type"`
|
||||
QuotaLimit float64 `json:"quota_limit" orm:"quota_limit"`
|
||||
QuotaPeriod string `json:"quota_period" orm:"quota_period"`
|
||||
UpdatedAt string `json:"updated_at" orm:"updated_at"`
|
||||
}
|
||||
err = model.OrderDesc("id").
|
||||
Page(page, pageSize).
|
||||
@@ -81,8 +81,8 @@ func (s *sTenant) ListMembers(ctx context.Context, req *v1.TenantMemberListReq)
|
||||
hasScopeMap := make(map[int64]bool)
|
||||
if len(userIds) > 0 {
|
||||
var scopeRows []struct {
|
||||
UserId int64 `json:"user_id"`
|
||||
ModelId int64 `json:"model_id"`
|
||||
UserId int64 `json:"user_id" orm:"user_id"`
|
||||
ModelId int64 `json:"model_id" orm:"model_id"`
|
||||
}
|
||||
if err = dao.TntMemberModelScopes.Ctx(ctx).
|
||||
Where("tenant_id", tenantID).
|
||||
@@ -1176,17 +1176,17 @@ func (s *sTenant) ExportMembers(ctx context.Context, req *v1.TenantMemberExportR
|
||||
}
|
||||
|
||||
var users []struct {
|
||||
Id int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Role string `json:"role"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
QuotaType string `json:"quota_type"`
|
||||
QuotaLimit float64 `json:"quota_limit"`
|
||||
QuotaPeriod string `json:"quota_period"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
Id int64 `json:"id" orm:"id"`
|
||||
Username string `json:"username" orm:"username"`
|
||||
Email string `json:"email" orm:"email"`
|
||||
DisplayName string `json:"display_name" orm:"display_name"`
|
||||
Role string `json:"role" orm:"role"`
|
||||
Status string `json:"status" orm:"status"`
|
||||
CreatedAt string `json:"created_at" orm:"created_at"`
|
||||
QuotaType string `json:"quota_type" orm:"quota_type"`
|
||||
QuotaLimit float64 `json:"quota_limit" orm:"quota_limit"`
|
||||
QuotaPeriod string `json:"quota_period" orm:"quota_period"`
|
||||
UpdatedAt string `json:"updated_at" orm:"updated_at"`
|
||||
}
|
||||
err := model.OrderDesc("id").Limit(1000).Offset(offset).Scan(&users)
|
||||
if err = common.IgnoreScanNoRows(err); err != nil {
|
||||
@@ -1203,8 +1203,8 @@ func (s *sTenant) ExportMembers(ctx context.Context, req *v1.TenantMemberExportR
|
||||
monthCostMap := make(map[int64]float64)
|
||||
if len(userIds) > 0 {
|
||||
var scopeRows []struct {
|
||||
UserId int64 `json:"user_id"`
|
||||
ModelId int64 `json:"model_id"`
|
||||
UserId int64 `json:"user_id" orm:"user_id"`
|
||||
ModelId int64 `json:"model_id" orm:"model_id"`
|
||||
}
|
||||
if err = dao.TntMemberModelScopes.Ctx(ctx).
|
||||
Where("tenant_id", tenantID).
|
||||
|
||||
@@ -34,7 +34,7 @@ func (s *sTenant) MemberModelScopes(ctx context.Context, req *v1.TenantMemberMod
|
||||
}
|
||||
|
||||
var rows []struct {
|
||||
ModelID int64 `json:"model_id"`
|
||||
ModelID int64 `json:"model_id" orm:"model_id"`
|
||||
}
|
||||
err = dao.TntMemberModelScopes.Ctx(ctx).
|
||||
Where("tenant_id", tenantID).
|
||||
|
||||
@@ -399,9 +399,9 @@ func (s *sTenant) ProjectUnarchive(ctx context.Context, req *v1.TenantProjectUna
|
||||
func CheckBudgetExhausted(ctx context.Context) error {
|
||||
// Find active projects with budget set
|
||||
var projects []struct {
|
||||
ID int64 `json:"id"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
Budget float64 `json:"budget"`
|
||||
ID int64 `json:"id" orm:"id"`
|
||||
TenantID int64 `json:"tenant_id" orm:"tenant_id"`
|
||||
Budget float64 `json:"budget" orm:"budget"`
|
||||
}
|
||||
err := dao.TntProjects.Ctx(ctx).
|
||||
Where("status", "active").
|
||||
|
||||
@@ -58,9 +58,9 @@ func Idempotency(r *ghttp.Request) {
|
||||
|
||||
// Check if this key has been processed before
|
||||
var record struct {
|
||||
Id int64 `json:"id"`
|
||||
Status string `json:"status"`
|
||||
ResponseBody string `json:"response_body"`
|
||||
Id int64 `json:"id" orm:"id"`
|
||||
Status string `json:"status" orm:"status"`
|
||||
ResponseBody string `json:"response_body" orm:"response_body"`
|
||||
}
|
||||
|
||||
err := dao.SysIdempotencyRecords.Ctx(ctx).
|
||||
|
||||
Reference in new issue
Block a user