112 lines
3.5 KiB
Go
112 lines
3.5 KiB
Go
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] + "***"
|
||
}
|