150 lines
5.0 KiB
Go
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
|
|
}
|