add exec history

This commit is contained in:
lkness 2025-05-05 10:30:33 +08:00
parent c65851b018
commit e2612f16b4
18 changed files with 246 additions and 19 deletions

View File

@ -12,8 +12,8 @@ func init() {
// Account 空表就是用来兼容资源增删改查公共操作的实际列举账号等都是走各个项目api拉取
type Account struct {
ProjectId int
Account string `name:"账号" json:"account"`
ServerConfId string `name:"区服id" json:"serveId"`
Account string `name:"账号" json:"account" where:"eq"`
ServerConfId string `name:"区服id" json:"serveId" where:"eq"`
RoleIds []string `gorm:"type:json;serializer:json" name:"角色id列表" json:"roleIds"`
LatestLoginTime time.Time `name:"最近登录时间" json:"latest_login_time"`
CreateTime time.Time `name:"创建时间" json:"create_time"`

View File

@ -18,7 +18,7 @@ type GlobalMail struct {
Content string `name:"邮件内容" required:"true"`
Attach []*MailAttachItem `gorm:"type:json;serializer:json" name:"邮件附件" type:"items" desc:"搜索道具并点击添加"`
CreatedAt time.Time `readonly:"true"`
CreatedAt time.Time `readonly:"true" where:"range"`
}
func (lm *GlobalMail) TableName() string {

View File

@ -12,10 +12,10 @@ func init() {
// Role 空表就是用来兼容资源增删改查公共操作的实际列举账号等都是走各个项目api拉取
type Role struct {
ProjectId int
RoleId string `name:"角色ID" json:"roleId"`
Account string `name:"账号" json:"account"`
ServerConfId string `name:"区服id" json:"serverId"`
Name string `name:"名称" json:"roleName"`
RoleId string `name:"角色ID" json:"roleId" where:"eq"`
Account string `name:"账号" json:"account" where:"eq"`
ServerConfId string `name:"区服id" json:"serverId" where:"eq"`
Name string `name:"名称" json:"roleName" where:"eq"`
Status string `name:"状态" desc:"离线|在线" json:"status"`
Level int `name:"等级" json:"roleLevel"`
Profession string `name:"职业" json:"profession"`

View File

@ -13,11 +13,11 @@ func init() {
type WhiteList struct {
ID int `gorm:"primarykey" readonly:"true"`
ProjectId int `gorm:"uniqueIndex:idx_whitelist"`
ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" choices:"GetChoiceServers"`
ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" choices:"GetChoiceServers" where:"eq"`
Account string `gorm:"type:varchar(128);uniqueIndex:idx_whitelist" name:"账户" required:"true"`
Desc string `name:"描述"`
CreatedAt time.Time `readonly:"true"`
CreatedAt time.Time `readonly:"true" where:"range"`
}
func (lm *WhiteList) TableName() string {

View File

@ -3,6 +3,7 @@ package server
import (
"admin/apps/game/model/dto"
"admin/internal/context"
stdContext "context"
)
func (ctl *controller) CommonList(ctx *context.WebContext, params *dto.CommonListReq, rsp *dto.CommonListRsp) error {
@ -17,7 +18,11 @@ func (ctl *controller) CommonList(ctx *context.WebContext, params *dto.CommonLis
func (ctl *controller) CommonPost(ctx *context.WebContext, params *dto.CommonPostReq, rsp *dto.CommonPostRsp) error {
projectId, resource := getCtxURIProjectIdAndResource(ctx)
newObj, err := ctl.svc.CommonPost(ctx, projectId, resource, *params.Dto)
ctx1 := stdContext.WithValue(ctx.Context, "user_id", ctx.Header.UserId)
ctx1 = stdContext.WithValue(ctx1, "user_name", ctx.Header.UserId)
newObj, err := ctl.svc.CommonPost(ctx1, projectId, resource, *params.Dto)
if err != nil {
return err
}

View File

@ -23,6 +23,8 @@ func (srv *Server) CheckToken(ctx *context.WebContext) {
return
}
ctx.Header.UserName = authRsp.User.NickName
ctx.GinCtx().Set("userInfo", authRsp)
return
}

View File

@ -6,6 +6,8 @@ import (
)
func (srv *Server) Route(engine *web.Engine) {
engine.Use(srv.CheckToken)
apiGroup := engine.Group("/api", "")
{

View File

@ -6,6 +6,7 @@ import (
"admin/apps/game/model/dto"
"admin/internal/consts"
"admin/internal/errcode"
"admin/internal/event"
"context"
"encoding/json"
"gorm.io/gorm"
@ -59,18 +60,61 @@ func (svc *Service) CommonPost(ctx context.Context, projectId int, resourceName
if resourceName != consts.ResourcesName_Project {
params["ProjectId"] = projectId
}
return svc.resourceSvc.Create(projectId, resourceName, params)
values, err := svc.resourceSvc.Create(projectId, resourceName, params)
userId := ctx.Value("user_id").(int)
userName := ctx.Value("user_name").(string)
event.GetMgrInstance().Publish(event.EventTopic_UserExecute, &event.EventPayload_UserExecute{
UserId: userId,
UserName: userName,
ProjectId: projectId,
Resource: resourceName,
Method: "新增",
NewData: params,
})
return values, err
}
func (svc *Service) CommonPut(ctx context.Context, projectId int, resourceName string, params dto.CommonDtoValues) error {
if resourceName != consts.ResourcesName_Project {
params["ProjectId"] = projectId
}
return svc.resourceSvc.Edit(projectId, resourceName, params)
err := svc.resourceSvc.Edit(projectId, resourceName, params)
userId := ctx.Value("user_id").(int)
userName := ctx.Value("user_name").(string)
event.GetMgrInstance().Publish(event.EventTopic_UserExecute, &event.EventPayload_UserExecute{
UserId: userId,
UserName: userName,
ProjectId: projectId,
Resource: resourceName,
Method: "编辑",
NewData: params,
})
return err
}
func (svc *Service) CommonDelete(ctx context.Context, projectId int, resourceName string, id int) error {
return svc.resourceSvc.Delete(projectId, resourceName, id)
err := svc.resourceSvc.Delete(projectId, resourceName, id)
userId := ctx.Value("user_id").(int)
userName := ctx.Value("user_name").(string)
event.GetMgrInstance().Publish(event.EventTopic_UserExecute, &event.EventPayload_UserExecute{
UserId: userId,
UserName: userName,
ProjectId: projectId,
Resource: resourceName,
Method: "删除",
NewData: id,
})
return err
}
func (svc *Service) GetSupportResourcesList(permissions []string) []*api.ResourceInitInfo {

View File

@ -24,6 +24,7 @@ type AuthReq struct {
type UserInfo struct {
UserId int `json:"user_id"`
UserName string `json:"user_name"`
NickName string `json:"nick_name"`
Icon string `json:"icon"`
Character string `json:"character"`

View File

@ -0,0 +1,35 @@
package model
import (
"admin/internal/db"
"admin/internal/global"
"time"
)
func init() {
db.RegisterTableModels(History{})
}
// History 用户执行历史
type History struct {
ID int `gorm:"primarykey" readonly:"true"`
UserId int
UserName string
ProjectId int
Resource string
Method string
Data string `gorm:"type:longtext"`
CreateTime time.Time
}
func (m *History) TableName() string {
return "history"
}
func (m *History) GetId() int {
return m.ID
}
func (m *History) Create() error {
return global.GLOB_DB.Create(m).Error
}

View File

@ -1,6 +1,12 @@
package server
import "admin/apps/user/service"
import (
"admin/apps/user/model"
"admin/apps/user/service"
"admin/internal/event"
"admin/lib/xlog"
"encoding/json"
)
type Server struct {
svc *service.Service
@ -13,3 +19,28 @@ func New(svc *service.Service) *Server {
ctl: newController(svc),
}
}
func (srv *Server) jobsSubscribe() {
event.GetMgrInstance().Subscribe("user", event.EventTopic_UserExecute, srv.subscriberHandlerUserExecute)
}
func (srv *Server) subscriberHandlerUserExecute(msg *event.Msg) {
po := new(model.History)
msgHistory := &event.EventPayload_UserExecute{}
err := json.Unmarshal(msg.Payload, msgHistory)
if err != nil {
xlog.Errorf("unmarshal msg(%+v) err:%v", string(msg.Payload), err)
}
po.UserId = msgHistory.UserId
po.UserName = msgHistory.UserName
po.ProjectId = msgHistory.ProjectId
po.Resource = msgHistory.Resource
po.Method = msgHistory.Method
po.Data = string(msg.Payload)
err = po.Create()
if err != nil {
xlog.Errorf("create user execute(%+v) err:%v", string(msg.Payload), err)
}
}

View File

@ -40,6 +40,7 @@ func (svc *Service) Auth(ctx context.Context, req *apiUser.AuthReq) (*apiUser.Au
}
rsp.User = &apiUser.UserInfo{
UserId: info.User.UserId,
UserName: info.User.NickName,
NickName: info.User.NickName,
Icon: info.User.Icon,
Character: info.User.Character,

View File

@ -9,9 +9,10 @@ import (
)
type WebHeader struct {
UserId int `json:"UserId"` // 用户id会与下面token解析出用户id做匹配校验
Token string `json:"Token"` // jwt token内置一些信息
Ip string `json:"IP"`
UserId int `json:"UserId"` // 用户id会与下面token解析出用户id做匹配校验
Token string `json:"Token"` // jwt token内置一些信息
Ip string `json:"IP"`
UserName string `json:"-"`
}
type WebContext struct {

View File

@ -0,0 +1,15 @@
package event
import "admin/lib/eventmgr"
type Msg = eventmgr.Msg
var manager *eventmgr.Mgr
func init() {
manager = eventmgr.NewMgr()
}
func GetMgrInstance() *eventmgr.Mgr {
return manager
}

View File

@ -0,0 +1,15 @@
package event
const (
EventTopic_UserExecute = "user.execute"
)
type EventPayload_UserExecute struct {
UserId int `json:"user_id"`
UserName string `json:"user_name"`
ProjectId int `json:"project_id"`
Resource string `json:"resource"`
Method string `json:"method"`
OldData any `json:"old_data"`
NewData any `json:"new_data"`
}

View File

@ -0,0 +1,66 @@
package eventmgr
import (
"admin/lib/xlog"
"encoding/json"
"runtime"
)
type Msg struct {
Event string
Payload []byte
}
type subscriber struct {
name string
topic string
handler func(*Msg)
}
type Mgr struct {
subscribers []*subscriber
broker chan *Msg
}
func NewMgr() *Mgr {
mgr := &Mgr{
subscribers: make([]*subscriber, 0),
broker: make(chan *Msg, 1024),
}
mgr.start()
return mgr
}
func (m *Mgr) start() {
for i := 0; i < runtime.NumCPU(); i++ {
go func() {
for {
select {
case msg := <-m.broker:
for _, sub := range m.subscribers {
if sub.topic == msg.Event {
sub.handler(msg)
}
}
}
}
}()
}
}
func (m *Mgr) Subscribe(name string, topic string, handler func(*Msg)) {
m.subscribers = append(m.subscribers, &subscriber{name: name, topic: topic, handler: handler})
}
func (m *Mgr) Publish(event string, payload any) {
payloadBin, err := json.Marshal(payload)
if err != nil {
panic(err)
}
msg := &Msg{Event: event, Payload: payloadBin}
select {
case m.broker <- msg:
default:
xlog.Errorf("publish event:%v broker is full", event)
}
}

Binary file not shown.

View File

@ -304,8 +304,17 @@ const handleQueryItem = (itemQueryStr) => {
</el-col>
</template>
<template v-else>
<el-input v-model="fieldDescInfo.value1"
:placeholder="(fieldDescInfo.name + fieldDescInfo.whereDesc)" style="width: 150px"></el-input>
<el-col :span="calcElColSpan">
<el-select v-model="fieldDescInfo.value1" :placeholder="(fieldDescInfo.multi_choice === true ? '--多选--' : '--单选--')"
style="width: 150px"
filterable v-if="(fieldDescInfo.choices.length > 0)">
<el-option v-for="choice in fieldDescInfo.choices" :key="choice.value" :label="choice.desc"
:value="choice.value"></el-option>
</el-select>
<el-input v-model="fieldDescInfo.value1"
:placeholder="(fieldDescInfo.name + fieldDescInfo.whereDesc)"
style="width: 150px" v-else></el-input>
</el-col>
</template>
</template>
<el-col :span="calcElColSpan">