Files
olons_admin/server/internal/logic/sys/biz_audit.go
T
2026-10-10 20:56:17 +08:00

150 lines
5.0 KiB
Go

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
}