package sys import ( "context" "encoding/json" "hotgo/internal/dao" "hotgo/internal/library/imadmin" "hotgo/internal/library/imtenant" "hotgo/internal/model/entity" "net/url" "strconv" "strings" "time" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/os/gtime" ) type imAdminQueryPage struct { Items []map[string]any `json:"items"` Total int `json:"total"` Page int `json:"page"` PerPage int `json:"per_page"` } func resolveImAdminTenant(ctx context.Context, selectedTenantId int64) (tenant *entity.ImTenant, err error) { scope, err := resolveImAdminScope(ctx, selectedTenantId) if err != nil { return nil, err } if err = imtenant.RegistryDB().Model(dao.ImTenant.Table()).Safe().Ctx(ctx).WherePri(scope.TenantId).Scan(&tenant); err != nil { return nil, gerror.Wrap(err, "获取IM租户失败") } if tenant == nil || tenant.Id <= 0 || tenant.Code == "" { return nil, gerror.New("IM租户不存在或编码为空") } return tenant, nil } func imAdminQueryList[T any](ctx context.Context, tenant *entity.ImTenant, path string, values url.Values, aliases map[string]string) (list []*T, totalCount int, err error) { if values == nil { values = url.Values{} } query := values.Encode() if query != "" { path += "?" + query } var page imAdminQueryPage if err = imadmin.DefaultClient(ctx).InternalGet(ctx, path, &page); err != nil { return nil, 0, err } normalizeImAdminRows(page.Items, aliases) if len(page.Items) == 0 { return []*T{}, page.Total, nil } raw, err := json.Marshal(page.Items) if err != nil { return nil, 0, gerror.Wrap(err, "转换IM内部查询列表失败") } if err = json.Unmarshal(raw, &list); err != nil { return nil, 0, gerror.Wrap(err, "解析IM内部查询列表失败") } fillImTenantFields(tenant, &list) return list, page.Total, nil } func imAdminQueryOne[T any](ctx context.Context, tenant *entity.ImTenant, path string, aliases map[string]string) (res *T, err error) { row := map[string]any{} if err = imadmin.DefaultClient(ctx).InternalGet(ctx, path, &row); err != nil { return nil, err } normalizeImAdminRow(row, aliases) raw, err := json.Marshal(row) if err != nil { return nil, gerror.Wrap(err, "转换IM内部查询详情失败") } if err = json.Unmarshal(raw, &res); err != nil { return nil, gerror.Wrap(err, "解析IM内部查询详情失败") } fillImTenantFields(tenant, res) return res, nil } func normalizeImAdminRows(rows []map[string]any, aliases map[string]string) { for _, row := range rows { normalizeImAdminRow(row, aliases) } } func normalizeImAdminRow(row map[string]any, aliases map[string]string) { for key, value := range row { target := snakeToLowerCamel(key) if aliases != nil { if alias := aliases[key]; alias != "" { target = alias } } value = normalizeImAdminValue(target, value) if target == key { row[key] = value continue } row[target] = value delete(row, key) } if _, ok := row["authNames"]; !ok { username, _ := row["username"].(string) phone, _ := row["phone"].(string) authNames := username if phone != "" && phone != username { if authNames != "" { authNames += ", " } authNames += phone } if authNames != "" { row["authNames"] = authNames } } } func normalizeImAdminValue(target string, value any) any { if target != "credentials" { return value } items, ok := value.([]any) if !ok { return value } parts := make([]string, 0, len(items)) for _, item := range items { m, ok := item.(map[string]any) if !ok { continue } method, _ := m["method"].(string) val, _ := m["value"].(string) if method == "" && val == "" { continue } text := method if val != "" { if text != "" { text += ":" } text += val } if done, ok := m["done"].(bool); ok && !done { text += "(未验证)" } parts = append(parts, text) } return strings.Join(parts, ", ") } func snakeToLowerCamel(s string) string { if !strings.Contains(s, "_") { return s } parts := strings.Split(s, "_") var b strings.Builder for i, part := range parts { if part == "" { continue } if i == 0 { b.WriteString(part) continue } b.WriteString(strings.ToUpper(part[:1])) if len(part) > 1 { b.WriteString(part[1:]) } } return b.String() } func fillImTenantFields(tenant *entity.ImTenant, target any) { raw, err := json.Marshal(target) if err != nil { return } var rows []map[string]any if err = json.Unmarshal(raw, &rows); err == nil { for _, row := range rows { row["tenantId"] = tenant.Id row["tenantName"] = tenant.Name } updated, _ := json.Marshal(rows) _ = json.Unmarshal(updated, target) return } var row map[string]any if err = json.Unmarshal(raw, &row); err == nil { row["tenantId"] = tenant.Id row["tenantName"] = tenant.Name updated, _ := json.Marshal(row) _ = json.Unmarshal(updated, target) } } func addPageQuery(values url.Values, page int, perPage int) url.Values { if values == nil { values = url.Values{} } if page <= 0 { page = 1 } if perPage <= 0 { perPage = 20 } values.Set("page", intToString(page)) values.Set("per_page", intToString(perPage)) return values } func addTimeRangeQuery(values url.Values, prefix string, times []*gtime.Time) { if len(times) != 2 { return } if times[0] != nil { values.Set(prefix+"_start", times[0].Time.UTC().Format(time.RFC3339Nano)) } if times[1] != nil { values.Set(prefix+"_end", times[1].Time.UTC().Format(time.RFC3339Nano)) } } func intToString(v int) string { return strconvFormatInt(int64(v)) } func int64ToString(v int64) string { return strconvFormatInt(v) } func strconvFormatInt(v int64) string { return strconv.FormatInt(v, 10) }