Files
qitongxue-api/internal/logic/event_query_log.go
2026-09-29 10:57:01 +08:00

112 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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] + "***"
}