feat: 新增系统操作日志&其他优化

This commit is contained in:
meilin.huang
2022-07-14 11:39:12 +08:00
parent 1c18a01bf6
commit db554ebdc9
38 changed files with 6783 additions and 1388 deletions

5116
mayfly_go_web/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
window.globalConfig = {
"BaseApiUrl": "http://localhost:8888",
"BaseWsUrl": "ws://localhost:8888"
// 默认为空以访问根目录为api请求地址。若前后端分离部署可单独配置该后端api请求地址
"BaseApiUrl": "",
"BaseWsUrl": ""
}

View File

@@ -1,6 +1,6 @@
const config = {
baseApiUrl: `${(window as any).globalConfig.BaseApiUrl}/api`,
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl}/api`
baseApiUrl: `${(window as any).globalConfig.BaseApiUrl}/api`,
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${location.host}`}/api`
}
export default config

View File

@@ -10,6 +10,7 @@ export const imports = {
"ResourceList": () => import('@/views/system/resource'),
"RoleList": () => import('@/views/system/role'),
"AccountList": () => import('@/views/system/account'),
"SyslogList": () => import('@/views/system/syslog/SyslogList.vue'),
// project
"ProjectList": () => import('@/views/ops/project/ProjectList.vue'),
// db

View File

@@ -22,5 +22,5 @@ export const dbApi = {
getSqlNames: Api.create("/dbs/{id}/sql-names", 'get'),
deleteDbSql: Api.create("/dbs/{id}/sql", 'delete'),
// 获取数据库sql执行记录
getSqlExecs: Api.create("/dbs/{id}/sql-execs", 'get'),
getSqlExecs: Api.create("/dbs/{dbId}/sql-execs", 'get'),
}

View File

@@ -33,5 +33,5 @@ export const accountApi = {
}
export const logApi = {
list: Api.create("/sys/logs", "get")
list: Api.create("/syslogs", "get")
}

View File

@@ -10,15 +10,14 @@
<div style="float: right">
<el-input
placeholder="请输入角色名称"
placeholder="请输入角色名称"
class="mr2"
size="small"
style="width: 300px"
style="width: 200px"
v-model="query.name"
@clear="search"
clearable
></el-input>
<el-button @click="search" type="success" icon="search" size="small"></el-button>
<el-button @click="search" type="success" icon="search"></el-button>
</div>
<el-table :data="roles" @current-change="choose" ref="table" style="width: 100%">
<el-table-column label="选择" width="50px">

View File

@@ -0,0 +1,103 @@
<template>
<div class="role-list">
<el-card>
<div style="float: right">
<el-select
remote
:remote-method="getAccount"
v-model="query.creatorId"
filterable
placeholder="请输入并选择账号"
clearable
class="mr5"
>
<el-option v-for="item in accounts" :key="item.id" :label="item.username" :value="item.id"> </el-option>
</el-select>
<el-select v-model="query.type" filterable placeholder="请选择操作结果" clearable class="mr5">
<el-option label="成功" :value="1"> </el-option>
<el-option label="失败" :value="2"> </el-option>
</el-select>
<el-button @click="search" type="success" icon="search"></el-button>
</div>
<el-table :data="logs" style="width: 100%">
<el-table-column prop="creator" label="操作人" min-width="100" show-overflow-tooltip></el-table-column>
<el-table-column prop="createTime" label="操作时间" min-width="160">
<template #default="scope">
{{ $filters.dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column prop="type" label="结果" min-width="65">
<template #default="scope">
<el-tag v-if="scope.row.type == 1" type="success" size="small">成功</el-tag>
<el-tag v-if="scope.row.type == 2" type="danger" size="small">失败</el-tag>
</template>
</el-table-column>
<el-table-column prop="description" label="描述" min-width="160" show-overflow-tooltip></el-table-column>
<el-table-column prop="reqParam" label="请求信息" min-width="300" show-overflow-tooltip></el-table-column>
<el-table-column prop="resp" label="响应信息" min-width="200" show-overflow-tooltip></el-table-column>
</el-table>
<el-row style="margin-top: 20px" type="flex" justify="end">
<el-pagination
style="text-align: right"
@current-change="handlePageChange"
:total="total"
layout="prev, pager, next, total, jumper"
v-model:current-page="query.pageNum"
:page-size="query.pageSize"
></el-pagination>
</el-row>
</el-card>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
import { logApi, accountApi } from '../api';
export default defineComponent({
name: 'SyslogList',
components: {},
setup() {
const state = reactive({
query: {
pageNum: 1,
pageSize: 10,
name: null,
},
total: 0,
logs: [],
accounts: [],
});
onMounted(() => {
search();
});
const search = async () => {
let res = await logApi.list.request(state.query);
state.logs = res.list;
state.total = res.total;
};
const handlePageChange = (curPage: number) => {
state.query.pageNum = curPage;
search();
};
const getAccount = (username: any) => {
accountApi.list.request({ username }).then((res) => {
state.accounts = res.list;
});
};
return {
...toRefs(state),
search,
handlePageChange,
getAccount,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -30,7 +30,6 @@ const viteConfig: UserConfig = {
target: 'http://localhost:8888',
ws: true,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '/'),
},
},
},

File diff suppressed because it is too large Load Diff

View File

@@ -53,6 +53,7 @@ func InitRouter() *gin.Engine {
sys_router.InitResourceRouter(api)
sys_router.InitRoleRouter(api)
sys_router.InitSystemRouter(api)
sys_router.InitSyslogRouter(api)
devops_router.InitProjectRouter(api)
devops_router.InitDbRouter(api)

View File

@@ -0,0 +1,10 @@
package initialize
import (
sys_application "mayfly-go/internal/sys/application"
"mayfly-go/pkg/ctx"
)
func InitSaveLogFunc() ctx.SaveLogFunc {
return sys_application.SyslogApp.SaveFromReq
}

View File

@@ -45,10 +45,12 @@ func (d *Db) Save(rc *ctx.ReqCtx) {
form := &form.DbForm{}
ginx.BindJsonAndValid(rc.GinCtx, form)
rc.ReqParam = form
db := new(entity.Db)
utils.Copy(db, form)
// 密码脱敏记录日志
form.Password = "****"
rc.ReqParam = form
db.SetBaseInfo(rc.LoginAccount)
d.DbApp.Save(db)
}
@@ -136,6 +138,8 @@ func (d *Db) ExecSqlFile(rc *ctx.ReqCtx) {
filename := fileheader.Filename
dbId, db := GetIdAndDb(g)
rc.ReqParam = fmt.Sprintf("dbId: %d, db: %s, filename: %s", dbId, db, filename)
go func() {
db := d.DbApp.GetDbInstance(dbId, db)
@@ -192,7 +196,6 @@ func (d *Db) DumpSql(rc *ctx.ReqCtx) {
g.Header("Content-Type", "application/octet-stream")
g.Header("Content-Disposition", "attachment; filename="+filename)
rc.ReqParam = fmt.Sprintf("数据库id: %d -- %s", dbId, db)
writer := g.Writer
writer.WriteString("-- ----------------------------")
writer.WriteString("\n-- 导出平台: mayfly-go")
@@ -258,6 +261,8 @@ func (d *Db) DumpSql(rc *ctx.ReqCtx) {
writer.WriteString("COMMIT;\n")
}
rc.NoRes = true
rc.ReqParam = fmt.Sprintf("dbId: %d, db: %s, tables: %s, dumpType: %s", dbId, db, tablesStr, dumpType)
}
// @router /api/db/:dbId/t-metadata [get]

View File

@@ -57,6 +57,10 @@ func (m *Machine) SaveMachine(rc *ctx.ReqCtx) {
entity := new(entity.Machine)
utils.Copy(entity, machineForm)
// 密码脱敏记录日志
machineForm.Password = "****"
rc.ReqParam = machineForm
entity.SetBaseInfo(rc.LoginAccount)
m.MachineApp.Save(entity)
}

View File

@@ -43,7 +43,8 @@ func (p *Project) GetProjects(rc *ctx.ReqCtx) {
func (p *Project) SaveProject(rc *ctx.ReqCtx) {
project := &entity.Project{}
ginx.BindJsonAndValid(rc.GinCtx, project)
rc.ReqParam = project
rc.ReqParam = fmt.Sprintf("projectId: %d, projectName: %s, remark: %s", project.Id, project.Name, project.Remark)
project.SetBaseInfo(rc.LoginAccount)
p.ProjectApp.SaveProject(project)
@@ -81,7 +82,8 @@ func (p *Project) GetProjectMembers(rc *ctx.ReqCtx) {
func (p *Project) SaveProjectMember(rc *ctx.ReqCtx) {
projectMem := &entity.ProjectMember{}
ginx.BindJsonAndValid(rc.GinCtx, projectMem)
rc.ReqParam = projectMem
rc.ReqParam = fmt.Sprintf("projectId: %d, username: %s", projectMem.ProjectId, projectMem.Username)
// 校验账号并赋值username
account := &sys_entity.Account{}

View File

@@ -36,10 +36,12 @@ func (r *Redis) Save(rc *ctx.ReqCtx) {
form := &form.Redis{}
ginx.BindJsonAndValid(rc.GinCtx, form)
rc.ReqParam = form
redis := new(entity.Redis)
utils.Copy(redis, form)
// 密码脱敏记录日志
form.Password = "****"
rc.ReqParam = form
redis.SetBaseInfo(rc.LoginAccount)
r.RedisApp.Save(redis)
}

View File

@@ -24,14 +24,14 @@ func InitDbRouter(router *gin.RouterGroup) {
rc.Handle(d.Dbs)
})
saveDb := ctx.NewLogInfo("保存数据库信息")
saveDb := ctx.NewLogInfo("保存数据库信息").WithSave(true)
db.POST("", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).
WithLog(saveDb).
Handle(d.Save)
})
deleteDb := ctx.NewLogInfo("删除数据库信息")
deleteDb := ctx.NewLogInfo("删除数据库信息").WithSave(true)
db.DELETE(":dbId", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).
WithLog(deleteDb).
@@ -50,20 +50,23 @@ func InitDbRouter(router *gin.RouterGroup) {
ctx.NewReqCtxWithGin(c).Handle(d.GetCreateTableDdl)
})
// db.GET(":dbId/exec-sql", controllers.SelectData)
execSqlLog := ctx.NewLogInfo("执行Sql语句")
db.POST(":dbId/exec-sql", func(g *gin.Context) {
rc := ctx.NewReqCtxWithGin(g).WithLog(ctx.NewLogInfo("执行Sql语句"))
rc := ctx.NewReqCtxWithGin(g).WithLog(execSqlLog)
rc.Handle(d.ExecSql)
})
execSqlFileLog := ctx.NewLogInfo("执行Sql文件").WithSave(true)
db.POST(":dbId/exec-sql-file", func(g *gin.Context) {
rc := ctx.NewReqCtxWithGin(g).WithLog(ctx.NewLogInfo("执行Sql文件"))
rc.Handle(d.ExecSqlFile)
ctx.NewReqCtxWithGin(g).
WithLog(execSqlFileLog).
Handle(d.ExecSqlFile)
})
dumpLog := ctx.NewLogInfo("导出sql文件").WithSave(true)
db.GET(":dbId/dump", func(g *gin.Context) {
ctx.NewReqCtxWithGin(g).
WithLog(ctx.NewLogInfo("Sql文件dump")).
WithLog(dumpLog).
Handle(d.DumpSql)
})

View File

@@ -29,7 +29,7 @@ func InitMachineRouter(router *gin.RouterGroup) {
})
// 终止进程
killProcessL := ctx.NewLogInfo("终止进程")
killProcessL := ctx.NewLogInfo("终止进程").WithSave(true)
killProcessP := ctx.NewPermission("machine:killprocess")
machines.DELETE(":machineId/process", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).
@@ -38,28 +38,28 @@ func InitMachineRouter(router *gin.RouterGroup) {
Handle(m.KillProcess)
})
saveMachine := ctx.NewLogInfo("保存机器信息")
saveMachine := ctx.NewLogInfo("保存机器信息").WithSave(true)
machines.POST("", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).
WithLog(saveMachine).
Handle(m.SaveMachine)
})
changeStatus := ctx.NewLogInfo("调整机器状态")
changeStatus := ctx.NewLogInfo("调整机器状态").WithSave(true)
machines.PUT(":machineId/:status", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).
WithLog(changeStatus).
Handle(m.ChangeStatus)
})
delMachine := ctx.NewLogInfo("删除机器")
delMachine := ctx.NewLogInfo("删除机器").WithSave(true)
machines.DELETE(":machineId", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).
WithLog(delMachine).
Handle(m.DeleteMachine)
})
closeCli := ctx.NewLogInfo("关闭机器客户端")
closeCli := ctx.NewLogInfo("关闭机器客户端").WithSave(true)
machines.DELETE(":machineId/close-cli", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(closeCli).Handle(m.CloseCli)
})

View File

@@ -24,7 +24,7 @@ func InitMachineFileRouter(router *gin.RouterGroup) {
})
// 新增修改机器文件
addFileConf := ctx.NewLogInfo("新增机器文件配置")
addFileConf := ctx.NewLogInfo("新增机器文件配置").WithSave(true)
afcP := ctx.NewPermission("machine:file:add")
machineFile.POST(":machineId/files", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(addFileConf).
@@ -33,7 +33,7 @@ func InitMachineFileRouter(router *gin.RouterGroup) {
})
// 删除机器文件
delFileConf := ctx.NewLogInfo("删除机器文件配置")
delFileConf := ctx.NewLogInfo("删除机器文件配置").WithSave(true)
dfcP := ctx.NewPermission("machine:file:del")
machineFile.DELETE(":machineId/files/:fileId", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(delFileConf).
@@ -41,7 +41,7 @@ func InitMachineFileRouter(router *gin.RouterGroup) {
Handle(mf.DeleteFile)
})
getContent := ctx.NewLogInfo("读取机器文件内容")
getContent := ctx.NewLogInfo("读取机器文件内容").WithSave(true)
machineFile.GET(":machineId/files/:fileId/read", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(getContent).
Handle(mf.ReadFileContent)
@@ -53,7 +53,7 @@ func InitMachineFileRouter(router *gin.RouterGroup) {
Handle(mf.GetDirEntry)
})
writeFile := ctx.NewLogInfo("写入or下载文件内容")
writeFile := ctx.NewLogInfo("写入or下载文件内容").WithSave(true)
wfP := ctx.NewPermission("machine:file:write")
machineFile.POST(":machineId/files/:fileId/write", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(writeFile).
@@ -61,14 +61,14 @@ func InitMachineFileRouter(router *gin.RouterGroup) {
Handle(mf.WriteFileContent)
})
createFile := ctx.NewLogInfo("创建机器文件or目录")
createFile := ctx.NewLogInfo("创建机器文件or目录").WithSave(true)
machineFile.POST(":machineId/files/:fileId/create-file", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(createFile).
WithRequiredPermission(wfP).
Handle(mf.CreateFile)
})
uploadFile := ctx.NewLogInfo("文件上传")
uploadFile := ctx.NewLogInfo("文件上传").WithSave(true)
ufP := ctx.NewPermission("machine:file:upload")
machineFile.POST(":machineId/files/:fileId/upload", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(uploadFile).
@@ -76,7 +76,7 @@ func InitMachineFileRouter(router *gin.RouterGroup) {
Handle(mf.UploadFile)
})
removeFile := ctx.NewLogInfo("删除文件or文件夹")
removeFile := ctx.NewLogInfo("删除文件or文件夹").WithSave(true)
rfP := ctx.NewPermission("machine:file:rm")
machineFile.DELETE(":machineId/files/:fileId/remove", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(removeFile).

View File

@@ -22,7 +22,7 @@ func InitMachineScriptRouter(router *gin.RouterGroup) {
ctx.NewReqCtxWithGin(c).Handle(ms.MachineScripts)
})
saveMachienScriptLog := ctx.NewLogInfo("保存脚本")
saveMachienScriptLog := ctx.NewLogInfo("保存脚本").WithSave(true)
smsP := ctx.NewPermission("machine:script:save")
// 保存脚本
machines.POST(":machineId/scripts", func(c *gin.Context) {
@@ -31,7 +31,7 @@ func InitMachineScriptRouter(router *gin.RouterGroup) {
Handle(ms.SaveMachineScript)
})
deleteLog := ctx.NewLogInfo("删除脚本")
deleteLog := ctx.NewLogInfo("删除脚本").WithSave(true)
dP := ctx.NewPermission("machine:script:del")
// 保存脚本
machines.DELETE(":machineId/scripts/:scriptId", func(c *gin.Context) {
@@ -40,7 +40,7 @@ func InitMachineScriptRouter(router *gin.RouterGroup) {
Handle(ms.DeleteMachineScript)
})
runLog := ctx.NewLogInfo("执行机器脚本")
runLog := ctx.NewLogInfo("执行机器脚本").WithSave(true)
rP := ctx.NewPermission("machine:script:run")
// 运行脚本
machines.GET(":machineId/scripts/:scriptId/run", func(c *gin.Context) {

View File

@@ -25,7 +25,7 @@ func InitProjectRouter(router *gin.RouterGroup) {
ctx.NewReqCtxWithGin(c).Handle(m.GetProjects)
})
saveProjectLog := ctx.NewLogInfo("保存项目信息")
saveProjectLog := ctx.NewLogInfo("保存项目信息").WithSave(true)
savePP := ctx.NewPermission("project:save")
// 保存项目下的环境信息
project.POST("", func(c *gin.Context) {
@@ -34,7 +34,7 @@ func InitProjectRouter(router *gin.RouterGroup) {
Handle(m.SaveProject)
})
delProjectLog := ctx.NewLogInfo("删除项目信息")
delProjectLog := ctx.NewLogInfo("删除项目信息").WithSave(true)
delPP := ctx.NewPermission("project:del")
// 删除项目
project.DELETE("", func(c *gin.Context) {
@@ -48,7 +48,7 @@ func InitProjectRouter(router *gin.RouterGroup) {
ctx.NewReqCtxWithGin(c).Handle(m.GetProjectEnvs)
})
saveProjectEnvLog := ctx.NewLogInfo("新增项目环境信息")
saveProjectEnvLog := ctx.NewLogInfo("新增项目环境信息").WithSave(true)
savePeP := ctx.NewPermission("project:env:add")
// 保存项目下的环境信息
project.POST("/:projectId/envs", func(c *gin.Context) {
@@ -63,7 +63,7 @@ func InitProjectRouter(router *gin.RouterGroup) {
})
// 保存项目成员
saveProjectMemLog := ctx.NewLogInfo("新增项目成员")
saveProjectMemLog := ctx.NewLogInfo("新增项目成员").WithSave(true)
savePmP := ctx.NewPermission("project:member:add")
project.POST("/:projectId/members", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(saveProjectMemLog).
@@ -72,7 +72,7 @@ func InitProjectRouter(router *gin.RouterGroup) {
})
// 删除项目成员
delProjectMemLog := ctx.NewLogInfo("删除项目成员")
delProjectMemLog := ctx.NewLogInfo("删除项目成员").WithSave(true)
savePmdP := ctx.NewPermission("project:member:del")
project.DELETE("/:projectId/members/:accountId", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(delProjectMemLog).

View File

@@ -21,12 +21,12 @@ func InitRedisRouter(router *gin.RouterGroup) {
ctx.NewReqCtxWithGin(c).Handle(rs.RedisList)
})
save := ctx.NewLogInfo("保存redis信息")
save := ctx.NewLogInfo("保存redis信息").WithSave(true)
redis.POST("", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(save).Handle(rs.Save)
})
delRedis := ctx.NewLogInfo("删除redis信息")
delRedis := ctx.NewLogInfo("删除redis信息").WithSave(true)
redis.DELETE(":id", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(delRedis).Handle(rs.DeleteRedis)
})
@@ -45,8 +45,7 @@ func InitRedisRouter(router *gin.RouterGroup) {
})
// 删除key
deleteKeyL := ctx.NewLogInfo("redis删除key")
// deleteKey := ctx.NewPermission("project:save")
deleteKeyL := ctx.NewLogInfo("redis删除key").WithSave(true)
redis.DELETE(":id/key", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(deleteKeyL).Handle(rs.DeleteKey)
})

View File

@@ -10,6 +10,7 @@ import (
"mayfly-go/pkg/captcha"
"mayfly-go/pkg/ctx"
"mayfly-go/pkg/ginx"
"mayfly-go/pkg/model"
"mayfly-go/pkg/utils"
"strconv"
"strings"
@@ -29,7 +30,6 @@ type Account struct {
func (a *Account) Login(rc *ctx.ReqCtx) {
loginForm := &form.LoginForm{}
ginx.BindJsonAndValid(rc.GinCtx, loginForm)
rc.ReqParam = loginForm.Username
// 校验验证码
biz.IsTrue(captcha.Verify(loginForm.Cid, loginForm.Captcha), "验证码错误")
@@ -54,8 +54,13 @@ func (a *Account) Login(rc *ctx.ReqCtx) {
// 保存该账号的权限codes
ctx.SavePermissionCodes(account.Id, permissions)
clientIp := rc.GinCtx.ClientIP()
// 保存登录消息
go a.saveLogin(account, rc.GinCtx.ClientIP())
go a.saveLogin(account, clientIp)
rc.ReqParam = fmt.Sprintln("登录ip: ", clientIp)
// 赋值loginAccount 主要用于记录操作日志,因为操作日志保存请求上下文没有该信息不保存日志
rc.LoginAccount = &model.LoginAccount{Id: account.Id, Username: account.Username}
rc.ResData = map[string]interface{}{
"token": ctx.CreateToken(account.Id, account.Username),

View File

@@ -29,6 +29,7 @@ func (r *Role) SaveRole(rc *ctx.ReqCtx) {
g := rc.GinCtx
form := &form.RoleForm{}
ginx.BindJsonAndValid(g, form)
rc.ReqParam = form
role := new(entity.Role)
utils.Copy(role, form)

View File

@@ -0,0 +1,21 @@
package api
import (
"mayfly-go/internal/sys/application"
"mayfly-go/internal/sys/domain/entity"
"mayfly-go/pkg/ctx"
"mayfly-go/pkg/ginx"
)
type Syslog struct {
SyslogApp application.Syslog
}
func (r *Syslog) Syslogs(rc *ctx.ReqCtx) {
g := rc.GinCtx
condition := &entity.Syslog{
Type: int8(ginx.QueryInt(g, "type", 0)),
CreatorId: uint64(ginx.QueryInt(g, "creatorId", 0)),
}
rc.ResData = r.SyslogApp.GetPageList(condition, ginx.GetPageParam(g), new([]entity.Syslog), "create_time DESC")
}

View File

@@ -0,0 +1,79 @@
package application
import (
"encoding/json"
"fmt"
"mayfly-go/internal/sys/domain/entity"
"mayfly-go/internal/sys/domain/repository"
"mayfly-go/internal/sys/infrastructure/persistence"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/ctx"
"mayfly-go/pkg/model"
"mayfly-go/pkg/utils"
"reflect"
"time"
)
type Syslog interface {
GetPageList(condition *entity.Syslog, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
// 从请求上下文的参数保存系统日志
SaveFromReq(req *ctx.ReqCtx)
}
type syslogAppImpl struct {
syslogRepo repository.Syslog
}
// 实现类单例
var SyslogApp Syslog = &syslogAppImpl{
syslogRepo: persistence.SyslogDao,
}
func (m *syslogAppImpl) GetPageList(condition *entity.Syslog, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
return m.syslogRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
}
func (m *syslogAppImpl) SaveFromReq(req *ctx.ReqCtx) {
lg := req.LoginAccount
if lg == nil {
return
}
syslog := new(entity.Syslog)
syslog.CreateTime = time.Now()
syslog.Creator = lg.Username
syslog.CreatorId = lg.Id
syslog.Description = req.LogInfo.Description
if req.LogInfo.LogResp {
respB, _ := json.Marshal(req.ResData)
syslog.Resp = string(respB)
}
reqParam := req.ReqParam
if !utils.IsBlank(reflect.ValueOf(reqParam)) {
// 如果是字符串类型则不使用json序列化
if reqStr, ok := reqParam.(string); ok {
syslog.ReqParam = reqStr
} else {
reqB, _ := json.Marshal(reqParam)
syslog.ReqParam = string(reqB)
}
}
if err := req.Err; err != nil {
syslog.Type = entity.SyslogTypeError
var errMsg string
switch t := err.(type) {
case *biz.BizError:
errMsg = fmt.Sprintf("errCode: %d, errMsg: %s", t.Code(), t.Error())
case error:
errMsg = t.Error()
}
syslog.Resp = errMsg
} else {
syslog.Type = entity.SyslogTypeNorman
}
m.syslogRepo.Insert(syslog)
}

View File

@@ -0,0 +1,25 @@
package entity
import "time"
// 系统操作日志
type Syslog struct {
Id uint64 `json:"id"`
CreateTime time.Time `json:"createTime"`
CreatorId uint64 `json:"creatorId"`
Creator string `json:"creator"`
Type int8 `json:"type"`
Description string `json:"description"`
ReqParam string `json:"reqParam"` // 请求参数
Resp string `json:"resp"` // 响应结构
}
func (a *Syslog) TableName() string {
return "t_sys_log"
}
const (
SyslogTypeNorman int8 = 1 // 正常状态
SyslogTypeError int8 = 2 // 错误状态
)

View File

@@ -0,0 +1,12 @@
package repository
import (
"mayfly-go/internal/sys/domain/entity"
"mayfly-go/pkg/model"
)
type Syslog interface {
GetPageList(condition *entity.Syslog, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
Insert(log *entity.Syslog)
}

View File

@@ -0,0 +1,19 @@
package persistence
import (
"mayfly-go/internal/sys/domain/entity"
"mayfly-go/internal/sys/domain/repository"
"mayfly-go/pkg/model"
)
type syslogRepo struct{}
var SyslogDao repository.Syslog = &syslogRepo{}
func (m *syslogRepo) GetPageList(condition *entity.Syslog, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
return model.GetPage(pageParam, condition, toEntity, orderBy...)
}
func (m *syslogRepo) Insert(syslog *entity.Syslog) {
model.Insert(syslog)
}

View File

@@ -18,9 +18,12 @@ func InitAccountRouter(router *gin.RouterGroup) {
}
{
// 用户登录
loginLog := ctx.NewLogInfo("用户登录").WithSave(true)
account.POST("login", func(g *gin.Context) {
rc := ctx.NewReqCtxWithGin(g).WithNeedToken(false).WithLog(ctx.NewLogInfo("用户登录"))
rc.Handle(a.Login)
ctx.NewReqCtxWithGin(g).
WithNeedToken(false).
WithLog(loginLog).
Handle(a.Login)
})
// 获取个人账号信息
@@ -44,7 +47,7 @@ func InitAccountRouter(router *gin.RouterGroup) {
ctx.NewReqCtxWithGin(c).Handle(a.Accounts)
})
createAccount := ctx.NewLogInfo("创建账号")
createAccount := ctx.NewLogInfo("创建账号").WithSave(true)
addAccountPermission := ctx.NewPermission("account:add")
account.POST("", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).
@@ -53,14 +56,14 @@ func InitAccountRouter(router *gin.RouterGroup) {
Handle(a.CreateAccount)
})
changeStatus := ctx.NewLogInfo("修改账号状态")
changeStatus := ctx.NewLogInfo("修改账号状态").WithSave(true)
account.PUT("change-status/:id/:status", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).
WithLog(changeStatus).
Handle(a.ChangeStatus)
})
delAccount := ctx.NewLogInfo("删除账号")
delAccount := ctx.NewLogInfo("删除账号").WithSave(true)
delAccountPermission := ctx.NewPermission("account:del")
account.DELETE(":id", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).
@@ -75,7 +78,7 @@ func InitAccountRouter(router *gin.RouterGroup) {
})
// 保存用户角色
saveAccountRole := ctx.NewLogInfo("保存用户角色")
saveAccountRole := ctx.NewLogInfo("保存用户角色").WithSave(true)
sarPermission := ctx.NewPermission("account:saveRoles")
account.POST("/roles", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(saveAccountRole).

View File

@@ -12,10 +12,6 @@ func InitResourceRouter(router *gin.RouterGroup) {
r := &api.Resource{ResourceApp: application.ResourceApp}
db := router.Group("sys/resources")
{
// db.GET("/account", func(c *gin.Context) {
// ctx.NewReqCtxWithGin(c).Handle(r.ResourceTree)
// })
db.GET("", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).Handle(r.GetAllResourceTree)
})
@@ -24,7 +20,7 @@ func InitResourceRouter(router *gin.RouterGroup) {
ctx.NewReqCtxWithGin(c).Handle(r.GetById)
})
saveResource := ctx.NewLogInfo("保存资源")
saveResource := ctx.NewLogInfo("保存资源").WithSave(true)
srPermission := ctx.NewPermission("resource:add")
db.POST("", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).
@@ -33,7 +29,7 @@ func InitResourceRouter(router *gin.RouterGroup) {
Handle(r.SaveResource)
})
changeStatus := ctx.NewLogInfo("修改资源状态")
changeStatus := ctx.NewLogInfo("修改资源状态").WithSave(true)
csPermission := ctx.NewPermission("resource:changeStatus")
db.PUT(":id/:status", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).
@@ -42,7 +38,7 @@ func InitResourceRouter(router *gin.RouterGroup) {
Handle(r.ChangeStatus)
})
delResource := ctx.NewLogInfo("删除资源")
delResource := ctx.NewLogInfo("删除资源").WithSave(true)
dePermission := ctx.NewPermission("resource:delete")
db.DELETE(":id", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).

View File

@@ -20,7 +20,7 @@ func InitRoleRouter(router *gin.RouterGroup) {
ctx.NewReqCtxWithGin(c).Handle(r.Roles)
})
saveRole := ctx.NewLogInfo("保存角色")
saveRole := ctx.NewLogInfo("保存角色").WithSave(true)
sPermission := ctx.NewPermission("role:add")
db.POST("", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(saveRole).
@@ -28,7 +28,7 @@ func InitRoleRouter(router *gin.RouterGroup) {
Handle(r.SaveRole)
})
delRole := ctx.NewLogInfo("删除角色")
delRole := ctx.NewLogInfo("删除角色").WithSave(true)
drPermission := ctx.NewPermission("role:del")
db.DELETE(":id", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(delRole).
@@ -44,7 +44,7 @@ func InitRoleRouter(router *gin.RouterGroup) {
ctx.NewReqCtxWithGin(c).Handle(r.RoleResource)
})
saveResource := ctx.NewLogInfo("保存角色资源")
saveResource := ctx.NewLogInfo("保存角色资源").WithSave(true)
srPermission := ctx.NewPermission("role:saveResources")
db.POST(":id/resources", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).WithLog(saveResource).

View File

@@ -0,0 +1,21 @@
package router
import (
"mayfly-go/internal/sys/api"
"mayfly-go/internal/sys/application"
"mayfly-go/pkg/ctx"
"github.com/gin-gonic/gin"
)
func InitSyslogRouter(router *gin.RouterGroup) {
s := &api.Syslog{
SyslogApp: application.SyslogApp,
}
sys := router.Group("syslogs")
{
sys.GET("", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).Handle(s.Syslogs)
})
}
}

View File

@@ -363,12 +363,11 @@ CREATE TABLE `t_sys_resource` (
-- Records of t_sys_resource
-- ----------------------------
BEGIN;
INSERT INTO `t_sys_resource` VALUES (1, 0, 1, 1, '首页', '/home', 1, '{\"component\":\"Home\",\"icon\":\"Home\",\"isAffix\":true,\"isKeepAlive\":true,\"routeName\":\"Home\"}', 1, 'admin', 1, 'admin', '2021-05-25 16:44:41', '2021-05-27 09:12:56');
INSERT INTO `t_sys_resource` VALUES (1, 0, 1, 1, '首页', '/home', 1, '{\"component\":\"Home\",\"icon\":\"HomeFilled\",\"isAffix\":true,\"isKeepAlive\":true,\"routeName\":\"Home\"}', 1, 'admin', 1, 'admin', '2021-05-25 16:44:41', '2021-05-27 09:12:56');
INSERT INTO `t_sys_resource` VALUES (2, 0, 1, 1, '运维', '/ops', 3, '{\"icon\":\"Monitor\",\"isKeepAlive\":true,\"redirect\":\"machine/list\",\"routeName\":\"Ops\"}', 1, 'admin', 1, 'admin', '2021-05-25 16:48:16', '2021-06-08 14:20:24');
INSERT INTO `t_sys_resource` VALUES (3, 2, 1, 1, '机器列表', 'machines', 2, '{\"component\":\"MachineList\",\"icon\":\"Menu\",\"isKeepAlive\":true,\"routeName\":\"MachineList\"}', 2, 'admin', 1, 'admin', '2021-05-25 16:50:04', '2021-06-30 16:20:08');
INSERT INTO `t_sys_resource` VALUES (4, 0, 1, 1, '系统管理', '/sys', 5, '{\"icon\":\"Setting\",\"isKeepAlive\":true,\"redirect\":\"/sys/resources\",\"routeName\":\"sys\"}', 1, 'admin', 1, 'admin', '2021-05-26 15:20:20', '2021-07-29 18:03:06');
INSERT INTO `t_sys_resource` VALUES (5, 4, 1, 1, '资源管理', 'resources', 3, '{\"component\":\"ResourceList\",\"icon\":\"Menu\",\"isKeepAlive\":true,\"routeName\":\"ResourceList\"}', 1, 'admin', 1, 'admin', '2021-05-26 15:23:07', '2021-06-08 11:27:55');
INSERT INTO `t_sys_resource` VALUES (9, 0, 1, 1, 'iframes', '/iframes', 6, '{\"component\":\"RouterParent\",\"icon\":\"Menu\",\"isIframe\":true,\"isKeepAlive\":true,\"link\":\"https://www.baidu.com\",\"routeName\":\"Iframe\"}', 1, 'admin', 1, 'admin', '2021-05-27 09:58:37', '2021-07-29 18:03:13');
INSERT INTO `t_sys_resource` VALUES (11, 4, 1, 1, '角色管理', 'roles', 2, '{\"component\":\"RoleList\",\"icon\":\"Menu\",\"isKeepAlive\":true,\"routeName\":\"RoleList\"}', 1, 'admin', 1, 'admin', '2021-05-27 11:15:35', '2021-06-03 09:59:41');
INSERT INTO `t_sys_resource` VALUES (12, 3, 2, 1, '机器终端按钮', 'machine:terminal', 4, '', 1, 'admin', 1, 'admin', '2021-05-28 14:06:02', '2021-05-31 17:47:59');
INSERT INTO `t_sys_resource` VALUES (14, 4, 1, 1, '账号管理', 'accounts', 1, '{\"component\":\"AccountList\",\"icon\":\"Menu\",\"isKeepAlive\":true,\"routeName\":\"AccountList\"}', 1, 'admin', 1, 'admin', '2021-05-28 14:56:25', '2021-06-03 09:39:22');
@@ -383,7 +382,7 @@ INSERT INTO `t_sys_resource` VALUES (22, 11, 2, 1, '角色删除按钮', 'role:d
INSERT INTO `t_sys_resource` VALUES (23, 11, 2, 1, '角色新增按钮', 'role:add', 3, NULL, 1, 'admin', 1, 'admin', '2021-05-31 18:02:44', '2021-05-31 19:33:39');
INSERT INTO `t_sys_resource` VALUES (24, 11, 2, 1, '角色编辑按钮', 'role:update', 4, NULL, 1, 'admin', 1, 'admin', '2021-05-31 18:02:57', '2021-05-31 19:33:40');
INSERT INTO `t_sys_resource` VALUES (25, 5, 2, 1, '资源新增按钮', 'resource:add', 1, NULL, 1, 'admin', 1, 'admin', '2021-05-31 18:03:33', '2021-05-31 19:31:47');
INSERT INTO `t_sys_resource` VALUES (26, 5, 2, 1, '资源删除按钮', 'resource:del', 2, NULL, 1, 'admin', 1, 'admin', '2021-05-31 18:03:47', '2021-05-31 19:29:40');
INSERT INTO `t_sys_resource` VALUES (26, 5, 2, 1, '资源删除按钮', 'resource:delete', 2, NULL, 1, 'admin', 1, 'admin', '2021-05-31 18:03:47', '2021-05-31 19:29:40');
INSERT INTO `t_sys_resource` VALUES (27, 5, 2, 1, '资源编辑按钮', 'resource:update', 3, NULL, 1, 'admin', 1, 'admin', '2021-05-31 18:04:03', '2021-05-31 19:29:40');
INSERT INTO `t_sys_resource` VALUES (28, 5, 2, 1, '资源禁用启用按钮', 'resource:changeStatus', 4, NULL, 1, 'admin', 1, 'admin', '2021-05-31 18:04:33', '2021-05-31 18:04:33');
INSERT INTO `t_sys_resource` VALUES (29, 14, 2, 1, '账号添加按钮', 'account:add', 3, NULL, 1, 'admin', 1, 'admin', '2021-05-31 19:23:42', '2021-05-31 19:23:42');
@@ -429,6 +428,8 @@ INSERT INTO `t_sys_resource`(`id`, `pid`, `type`, `status`, `name`, `code`, `wei
INSERT INTO `t_sys_resource`(`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`) VALUES (81, 80, 2, 1, '基本权限', 'mongo:base', 1, 'null', 1, 'admin', 1, 'admin', '2022-05-13 14:04:16', '2022-05-13 14:04:16');
INSERT INTO `t_sys_resource`(`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`) VALUES (82, 79, 1, 1, 'Mongo管理', 'mongo-manage', 2, '{\"component\":\"MongoList\",\"icon\":\"Menu\",\"isKeepAlive\":true,\"routeName\":\"MongoList\"}', 1, 'admin', 1, 'admin', '2022-05-16 18:13:06', '2022-05-16 18:13:06');
INSERT INTO `t_sys_resource`(`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`) VALUES (83, 82, 2, 1, '基本权限', 'mongo:manage:base', 1, 'null', 1, 'admin', 1, 'admin', '2022-05-16 18:13:25', '2022-05-16 18:13:25');
INSERT INTO `t_sys_resource`(`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`) VALUES (84, 4, 1, 1, '操作日志', 'syslogs', 4, '{\"component\":\"SyslogList\",\"icon\":\"Tickets\",\"routeName\":\"SyslogList\"}', 1, 'admin', 1, 'admin', '2022-07-13 19:57:07', '2022-07-13 22:58:19');
INSERT INTO `t_sys_resource`(`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`) VALUES (85, 84, 2, 1, '操作日志基本权限', 'syslog', 1, 'null', 1, 'admin', 1, 'admin', '2022-07-13 19:57:55', '2022-07-13 19:57:55');
COMMIT;
-- ----------------------------
@@ -625,8 +626,34 @@ INSERT INTO `t_sys_role_resource` VALUES (496, 8, 61, 1, 'admin', '2021-11-05 15
INSERT INTO `t_sys_role_resource` VALUES (497, 8, 62, 1, 'admin', '2021-11-05 15:59:16');
INSERT INTO `t_sys_role_resource` VALUES (498, 8, 63, 1, 'admin', '2021-11-05 15:59:16');
INSERT INTO `t_sys_role_resource` VALUES (499, 8, 64, 1, 'admin', '2021-11-05 15:59:16');
INSERT INTO `t_sys_role_resource`(`id`, `role_id`, `resource_id`, `creator_id`, `creator`, `create_time`) VALUES (500, 1, 72, 1, 'admin', '2022-07-14 11:03:09');
INSERT INTO `t_sys_role_resource`(`id`, `role_id`, `resource_id`, `creator_id`, `creator`, `create_time`) VALUES (501, 1, 71, 1, 'admin', '2022-07-14 11:03:09');
INSERT INTO `t_sys_role_resource`(`id`, `role_id`, `resource_id`, `creator_id`, `creator`, `create_time`) VALUES (502, 1, 79, 1, 'admin', '2022-07-14 11:03:09');
INSERT INTO `t_sys_role_resource`(`id`, `role_id`, `resource_id`, `creator_id`, `creator`, `create_time`) VALUES (503, 1, 80, 1, 'admin', '2022-07-14 11:03:09');
INSERT INTO `t_sys_role_resource`(`id`, `role_id`, `resource_id`, `creator_id`, `creator`, `create_time`) VALUES (504, 1, 81, 1, 'admin', '2022-07-14 11:03:09');
INSERT INTO `t_sys_role_resource`(`id`, `role_id`, `resource_id`, `creator_id`, `creator`, `create_time`) VALUES (505, 1, 82, 1, 'admin', '2022-07-14 11:03:09');
INSERT INTO `t_sys_role_resource`(`id`, `role_id`, `resource_id`, `creator_id`, `creator`, `create_time`) VALUES (506, 1, 83, 1, 'admin', '2022-07-14 11:03:09');
INSERT INTO `t_sys_role_resource`(`id`, `role_id`, `resource_id`, `creator_id`, `creator`, `create_time`) VALUES (507, 1, 84, 1, 'admin', '2022-07-14 11:10:11');
INSERT INTO `t_sys_role_resource`(`id`, `role_id`, `resource_id`, `creator_id`, `creator`, `create_time`) VALUES (508, 1, 85, 1, 'admin', '2022-07-14 11:10:11');
COMMIT;
-- ----------------------------
-- 表结构: t_sys_log
-- ----------------------------
DROP TABLE IF EXISTS `t_sys_log`;
CREATE TABLE `t_sys_log` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`type` tinyint(4) NOT NULL COMMENT '类型',
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '描述',
`req_param` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '请求信息',
`resp` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '响应信息',
`creator` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '调用者',
`creator_id` bigint(20) NOT NULL COMMENT '调用者id',
`create_time` datetime NOT NULL COMMENT '操作时间',
PRIMARY KEY (`id`),
KEY `idx_creator_id` (`creator_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='系统操作日志';
-- ----------------------------
-- Table structure for t_mongo

View File

@@ -12,20 +12,38 @@ import (
"github.com/sirupsen/logrus"
)
type SaveLogFunc func(*ReqCtx)
var saveLog SaveLogFunc
// 设置保存日志处理函数
func SetSaveLogFunc(sl SaveLogFunc) {
saveLog = sl
}
type LogInfo struct {
LogResp bool // 是否记录返回结果
Description string // 请求描述
Save bool // 是否保存日志
}
// 新建日志信息
func NewLogInfo(description string) *LogInfo {
return &LogInfo{Description: description, LogResp: false}
}
// 是否记录返回结果
func (i *LogInfo) WithLogResp(logResp bool) *LogInfo {
i.LogResp = logResp
return i
}
// 是否保存日志
func (i *LogInfo) WithSave(saveLog bool) *LogInfo {
i.Save = saveLog
return i
}
func LogHandler(rc *ReqCtx) error {
li := rc.LogInfo
if li == nil {
@@ -41,6 +59,10 @@ func LogHandler(rc *ReqCtx) error {
req := rc.GinCtx.Request
lfs[req.Method] = req.URL.Path
// 如果需要保存日志,并且保存日志处理函数存在则执行保存日志函数
if li.Save && saveLog != nil {
go saveLog(rc)
}
if err := rc.Err; err != nil {
logger.Log.WithFields(lfs).Error(getErrMsg(rc, err))
return nil

View File

@@ -53,7 +53,7 @@ func (rc *ReqCtx) Handle(handler HandlerFunc) {
begin := time.Now()
handler(rc)
rc.timed = time.Now().Sub(begin).Milliseconds()
rc.timed = time.Since(begin).Milliseconds()
if !rc.NoRes {
ginx.SuccessRes(ginCtx, rc.ResData)
}

View File

@@ -13,6 +13,9 @@ func RunWebServer() {
ctx.UseBeforeHandlerInterceptor(ctx.PermissionHandler)
// 日志处理器
ctx.UseAfterHandlerInterceptor(ctx.LogHandler)
// 设置日志保存函数
ctx.SetSaveLogFunc(initialize.InitSaveLogFunc())
// 注册路由
web := initialize.InitRouter()

View File

@@ -1,7 +1,8 @@
后端配置:
服务端口mysql等信息在config.yml里配置即可。
前端配置:
static/config.js中的api地址配成启动后的后端服务的真实地址即可
相关配置文件:
后端:
config.yml: 服务端口mysql等信息在此配置即可。
前端:
static/config.js: 若前后端分开部署则将该文件中的api地址配成后端服务的真实地址即可否则无需修改。
服务启动:./startup.sh
服务关闭:./shutdown.sh