package sys import ( "context" "crypto/sha256" "encoding/hex" "encoding/json" "hotgo/internal/dao" "hotgo/internal/library/contexts" "hotgo/internal/library/hgorm/handler" "hotgo/internal/library/hgorm/hook" "hotgo/internal/model/entity" "hotgo/internal/model/input/sysin" "hotgo/internal/service" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/grand" ) const bizAuditActorAdmin = 1 type sSysBizAudit struct{} func NewSysBizAudit() *sSysBizAudit { return &sSysBizAudit{} } func init() { service.RegisterSysBizAudit(NewSysBizAudit()) } func (s *sSysBizAudit) Model(ctx context.Context, option ...*handler.Option) *gdb.Model { if len(option) == 0 { option = append(option, &handler.Option{FilterTenant: true}) } return handler.Model(dao.BizAuditLog.Ctx(ctx), option...) } func (s *sSysBizAudit) Record(ctx context.Context, in *sysin.BizAuditRecordInp) error { if in.Module == "" || in.Action == "" || in.TargetType == "" { return gerror.New("审计模块、动作和目标类型不能为空") } var previous *entity.BizAuditLog if err := s.Model(ctx). Fields(dao.BizAuditLog.Columns().Id, dao.BizAuditLog.Columns().RowHash). OrderDesc(dao.BizAuditLog.Columns().Id). Limit(1).LockUpdate().Scan(&previous); err != nil { return gerror.Wrap(err, "读取审计链失败") } prevHash := "" if previous != nil { prevHash = previous.RowHash } beforeDigest, err := digestValue(in.Before) if err != nil { return gerror.Wrap(err, "生成变更前摘要失败") } afterDigest, err := digestValue(in.After) if err != nil { return gerror.Wrap(err, "生成变更后摘要失败") } auditNo := "AU" + gtime.Now().Format("YmdHis") + grand.S(6) createdAt := gtime.Now() hashSource := struct { AuditNo, Module, Action, TargetType, BeforeDigest, AfterDigest, ApprovalNo, TraceId, PrevHash string ActorId, TargetId uint64 CreatedAt int64 }{ AuditNo: auditNo, Module: in.Module, Action: in.Action, TargetType: in.TargetType, BeforeDigest: beforeDigest, AfterDigest: afterDigest, ApprovalNo: in.ApprovalNo, TraceId: gctx.CtxId(ctx), PrevHash: prevHash, ActorId: uint64(contexts.GetUserId(ctx)), TargetId: in.TargetId, CreatedAt: createdAt.TimestampMilli(), } rowHash, err := digestValue(hashSource) if err != nil { return gerror.Wrap(err, "生成审计行哈希失败") } columns := dao.BizAuditLog.Columns() _, err = s.Model(ctx, &handler.Option{}).Hook(hook.SaveTenant).Data(gdb.Map{ columns.AuditNo: auditNo, columns.ActorType: bizAuditActorAdmin, columns.ActorId: contexts.GetUserId(ctx), columns.Module: in.Module, columns.Action: in.Action, columns.TargetType: in.TargetType, columns.TargetId: in.TargetId, columns.BeforeDigest: beforeDigest, columns.AfterDigest: afterDigest, columns.ChangeJson: in.ChangeJson, columns.Reason: in.Reason, columns.ApprovalNo: in.ApprovalNo, columns.TraceId: gctx.CtxId(ctx), columns.PrevHash: prevHash, columns.RowHash: rowHash, columns.CreatedAt: createdAt, }).Insert() if err != nil { return gerror.Wrap(err, "写入业务审计日志失败") } return nil } func (s *sSysBizAudit) List(ctx context.Context, in *sysin.BizAuditListInp) (list []*sysin.BizAuditListModel, totalCount int, err error) { mod := s.Model(ctx).Fields(sysin.BizAuditListModel{}) columns := dao.BizAuditLog.Columns() if in.AuditNo != "" { mod = mod.WhereLike(columns.AuditNo, "%"+in.AuditNo+"%") } if in.Module != "" { mod = mod.Where(columns.Module, in.Module) } if in.Action != "" { mod = mod.Where(columns.Action, in.Action) } if in.TargetType != "" { mod = mod.Where(columns.TargetType, in.TargetType) } if in.TargetId > 0 { mod = mod.Where(columns.TargetId, in.TargetId) } if in.ApprovalNo != "" { mod = mod.WhereLike(columns.ApprovalNo, "%"+in.ApprovalNo+"%") } if in.Actor != "" { ids, queryErr := service.AdminMember().GetIdsByKeyword(ctx, in.Actor) if queryErr != nil { return nil, 0, queryErr } mod = mod.WhereIn(columns.ActorId, ids) } if len(in.CreatedAt) == 2 { mod = mod.WhereBetween(columns.CreatedAt, in.CreatedAt[0], in.CreatedAt[1]) } if err = mod.Page(in.Page, in.PerPage).OrderDesc(columns.Id).ScanAndCount(&list, &totalCount, false); err != nil { return nil, 0, gerror.Wrap(err, "获取业务审计日志失败") } return } func (s *sSysBizAudit) View(ctx context.Context, in *sysin.BizAuditViewInp) (res *sysin.BizAuditViewModel, err error) { if err = s.Model(ctx).WherePri(in.Id).Scan(&res); err != nil { return nil, gerror.Wrap(err, "获取业务审计详情失败") } if res == nil || res.Id == 0 { return nil, gerror.New("业务审计日志不存在或无权查看") } return } func digestValue(value any) (string, error) { if value == nil { return "", nil } data, err := json.Marshal(value) if err != nil { return "", err } sum := sha256.Sum256(data) return hex.EncodeToString(sum[:]), nil }