小程序一些功能更新

This commit is contained in:
李琦
2026-09-29 10:57:01 +08:00
parent 0cb70a7618
commit 3bb7fa8db3
96 changed files with 13890 additions and 213 deletions

View File

@@ -0,0 +1,142 @@
package logic
import (
"context"
"encoding/json"
"strconv"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
v1 "tool-api/api/admin/v1"
"tool-api/internal/consts"
)
// auditId 审计对象主键统一转字符串(避免与 note.go 的 itoa(int) 冲突)
func auditId(v int64) string { return strconv.FormatInt(v, 10) }
// ============================================================================
// 后台操作审计(T17)
//
// WriteAudit 只负责「落一条记录」,调用点负责提供前后值;写入失败仅告警,
// 绝不因审计异常影响主流程(审计是旁路,不是业务前置条件)。
// ============================================================================
// AuditEntry 一条待写入的操作审计
type AuditEntry struct {
Action string // 动作,如 user.set_level / event.status / admin.create
TargetType string // 对象类型:user/event/table/admin/export...
TargetId string // 对象主键(字符串,兼容复合键)
Before interface{} // 操作前值(可为 nil / map / struct)
After interface{} // 操作后值
Result int // consts.AuditResultSuccess / Fail
Remark string
}
// auditJSON 把前后值序列化成 JSON 字符串;nil 存 NULL。
func auditJSON(v interface{}) interface{} {
if v == nil {
return nil
}
b, err := json.Marshal(v)
if err != nil {
return nil
}
return string(b)
}
func auditClientIp(ctx context.Context) string {
req := g.RequestFromCtx(ctx)
if req == nil {
return ""
}
return req.GetClientIp()
}
// WriteAudit 写入一条操作审计(操作人取自上下文,由 AdminPerm 中间件注入)。
func WriteAudit(ctx context.Context, e AuditEntry) {
data := g.Map{
"admin_id": CtxAdminId(ctx),
"admin_account": CtxAdminAccount(ctx),
"admin_name": CtxAdminName(ctx),
"role_value": CtxAdminRole(ctx),
"action": e.Action,
"target_type": e.TargetType,
"target_id": e.TargetId,
"before_json": auditJSON(e.Before),
"after_json": auditJSON(e.After),
"result": e.Result,
"remark": e.Remark,
"ip": auditClientIp(ctx),
"created_at": gtime.Now(),
}
if _, err := g.Model(consts.TableAdminAuditLog).Data(data).Insert(); err != nil {
g.Log().Warningf(ctx, "[audit] 写入失败 action=%s err=%v", e.Action, err)
}
}
// AdminAuditList 审计列表(多条件筛选 + 分页)。
func AdminAuditList(ctx context.Context, req *v1.AuditListReq) (*v1.AuditListRes, error) {
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 20
}
m := g.Model(consts.TableAdminAuditLog)
if req.AdminKeyword != "" {
m = m.WhereLike("admin_account", "%"+req.AdminKeyword+"%")
}
if req.Action != "" {
m = m.WhereLike("action", "%"+req.Action+"%")
}
if req.Result != nil {
m = m.Where("result", *req.Result)
}
if req.DateFrom != "" {
m = m.WhereGTE("created_at", req.DateFrom+" 00:00:00")
}
if req.DateTo != "" {
m = m.WhereLTE("created_at", req.DateTo+" 23:59:59")
}
total, err := m.Count()
if err != nil {
return nil, err
}
records, err := m.Page(req.Page, req.PageSize).OrderDesc("id").All()
if err != nil {
return nil, err
}
list := make([]v1.AuditItem, 0, len(records))
for _, r := range records {
list = append(list, v1.AuditItem{
Id: r["id"].Int64(),
AdminId: r["admin_id"].Int64(),
AdminAccount: r["admin_account"].String(),
AdminName: r["admin_name"].String(),
RoleValue: r["role_value"].String(),
Action: r["action"].String(),
TargetType: r["target_type"].String(),
TargetId: r["target_id"].String(),
BeforeJson: r["before_json"].String(),
AfterJson: r["after_json"].String(),
Result: r["result"].Int(),
Remark: r["remark"].String(),
Ip: r["ip"].String(),
CreatedAt: r["created_at"].String(),
})
}
return &v1.AuditListRes{List: list, Total: total}, nil
}
// AdminAuditRecord 前端主动上报一条审计(如「导出」为纯前端动作,服务端无法感知)。
func AdminAuditRecord(ctx context.Context, req *v1.AuditRecordReq) (*v1.AuditRecordRes, error) {
WriteAudit(ctx, AuditEntry{
Action: req.Action,
TargetType: req.TargetType,
TargetId: req.TargetId,
Result: consts.AuditResultSuccess,
Remark: req.Remark,
})
return &v1.AuditRecordRes{}, nil
}