package logic import ( "context" "strings" "github.com/gogf/gf/v2/frame/g" v1 "tool-api/api/admin/v1" "tool-api/internal/consts" "tool-api/internal/model/entity" ) // ============================================================================ // 年会域:查座审计列表(A19,架构 §5.2 遗漏项补录) // // 数据源 event_seat_queries(架构 §3.2.9),与公开查座(event_query.go)共用同一张表: // - phone_masked 落库时已脱敏(writeSeatAudit → maskPhone),此处原样返回,不再二次脱敏; // - openid 不做落库脱敏(限流需要原始值),输出时二次脱敏(前 6 位 + ***); // - event_title 通过一次 IN 查询批量补齐,避免逐条查(N+1)。 // ============================================================================ // AdminEventQueryLog 查座审计列表。 // // EventId = 0 表示全部活动; // Result = -1 表示全部结果,0 失败 / 1 成功(其余取值同样视为全部,避免前端漏传时误过滤)。 func AdminEventQueryLog(ctx context.Context, req *v1.EventQueryLogReq) (*v1.EventQueryLogRes, error) { model := g.Model(consts.TableEventSeatQueries) if req.EventId > 0 { model = model.Where("event_id", req.EventId) } if req.Result == consts.SeatResultFail || req.Result == consts.SeatResultSuccess { model = model.Where("result", req.Result) } total, err := model.Count() if err != nil { return nil, err } page, size := normalizeAdminPage(req.Page, req.PageSize) records, err := model.OrderDesc("id").Page(page, size).All() if err != nil { return nil, err } // 解析实体 + 收集去重后的活动 id rows := make([]*entity.EventSeatQueries, 0, len(records)) eventIds := make([]int64, 0) seen := make(map[int64]bool) for _, r := range records { item := &entity.EventSeatQueries{} if err = r.Struct(item); err != nil { continue } rows = append(rows, item) if item.EventId > 0 && !seen[item.EventId] { seen[item.EventId] = true eventIds = append(eventIds, item.EventId) } } // 批量补齐活动标题(一次 IN 查询,避免 N+1) titleMap, err := loadEventTitles(ctx, eventIds) if err != nil { return nil, err } list := make([]v1.QueryLogItem, 0, len(rows)) for _, item := range rows { list = append(list, v1.QueryLogItem{ Id: item.Id, EventId: item.EventId, EventTitle: titleMap[item.EventId], Ip: item.Ip, PhoneMasked: item.PhoneMasked, OpenidMasked: maskOpenid(item.Openid), Result: item.Result, CreatedAt: formatGTime(item.CreatedAt), }) } return &v1.EventQueryLogRes{List: list, Total: total}, nil } // loadEventTitles 批量读取活动标题:id -> title(一次 IN 查询,避免 N+1)。 func loadEventTitles(ctx context.Context, eventIds []int64) (map[int64]string, error) { out := make(map[int64]string, len(eventIds)) if len(eventIds) == 0 { return out, nil } records, err := g.Model(consts.TableAnnualEvents).Fields("id", "title").WhereIn("id", eventIds).All() if err != nil { return nil, err } for _, r := range records { out[r["id"].Int64()] = r["title"].String() } return out, nil } // maskOpenid openid 脱敏:保留前 6 位 + "***";空串返回空串。 // 说明:openid 落库未脱敏(限流需原始值比对),仅在审计列表输出时脱敏。 func maskOpenid(openid string) string { openid = strings.TrimSpace(openid) if openid == "" { return "" } if len(openid) <= 6 { return openid + "***" } return openid[:6] + "***" }