feat: 合并Gin_Vue
This commit is contained in:
85
src/modules/common/common.go
Normal file
85
src/modules/common/common.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"ems.agt/src/framework/logger"
|
||||
"ems.agt/src/framework/middleware"
|
||||
"ems.agt/src/modules/common/controller"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// 模块路由注册
|
||||
func Setup(router *gin.Engine) {
|
||||
logger.Infof("开始加载 ====> common 模块路由")
|
||||
|
||||
// 路由主页
|
||||
indexGroup := router.Group("/")
|
||||
indexGroup.GET("",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 300,
|
||||
Count: 10,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
controller.NewIndex.Handler,
|
||||
)
|
||||
|
||||
// 验证码操作处理
|
||||
indexGroup.GET("/captchaImage",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 300,
|
||||
Count: 60,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
controller.NewCaptcha.Image,
|
||||
)
|
||||
|
||||
// 账号身份操作处理
|
||||
{
|
||||
indexGroup.POST("/login",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 300,
|
||||
Count: 10,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
controller.NewAccount.Login,
|
||||
)
|
||||
indexGroup.GET("/getInfo", middleware.PreAuthorize(nil), controller.NewAccount.Info)
|
||||
indexGroup.GET("/getRouters", middleware.PreAuthorize(nil), controller.NewAccount.Router)
|
||||
indexGroup.POST("/logout",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 300,
|
||||
Count: 5,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
controller.NewAccount.Logout,
|
||||
)
|
||||
}
|
||||
|
||||
// 账号注册操作处理
|
||||
{
|
||||
indexGroup.POST("/register",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 300,
|
||||
Count: 10,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
controller.NewRegister.UserName,
|
||||
)
|
||||
}
|
||||
|
||||
// 通用请求
|
||||
commonGroup := router.Group("/common")
|
||||
{
|
||||
commonGroup.GET("/hash", middleware.PreAuthorize(nil), controller.NewCommont.Hash)
|
||||
}
|
||||
|
||||
// 文件操作处理
|
||||
fileGroup := router.Group("/file")
|
||||
{
|
||||
fileGroup.GET("/download/:filePath", middleware.PreAuthorize(nil), controller.NewFile.Download)
|
||||
fileGroup.POST("/upload", middleware.PreAuthorize(nil), controller.NewFile.Upload)
|
||||
fileGroup.POST("/chunkCheck", middleware.PreAuthorize(nil), controller.NewFile.ChunkCheck)
|
||||
fileGroup.POST("/chunkUpload", middleware.PreAuthorize(nil), controller.NewFile.ChunkUpload)
|
||||
fileGroup.POST("/chunkMerge", middleware.PreAuthorize(nil), controller.NewFile.ChunkMerge)
|
||||
}
|
||||
}
|
||||
144
src/modules/common/controller/account.go
Normal file
144
src/modules/common/controller/account.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"ems.agt/src/framework/config"
|
||||
commonConstants "ems.agt/src/framework/constants/common"
|
||||
tokenConstants "ems.agt/src/framework/constants/token"
|
||||
ctxUtils "ems.agt/src/framework/utils/ctx"
|
||||
tokenUtils "ems.agt/src/framework/utils/token"
|
||||
"ems.agt/src/framework/vo/result"
|
||||
libAccount "ems.agt/src/lib_features/account"
|
||||
commonModel "ems.agt/src/modules/common/model"
|
||||
commonService "ems.agt/src/modules/common/service"
|
||||
systemService "ems.agt/src/modules/system/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// 实例化控制层 AccountController 结构体
|
||||
var NewAccount = &AccountController{
|
||||
accountService: commonService.NewAccountImpl,
|
||||
sysLogLoginService: systemService.NewSysLogLoginImpl,
|
||||
}
|
||||
|
||||
// 账号身份操作处理
|
||||
//
|
||||
// PATH /
|
||||
type AccountController struct {
|
||||
// 账号身份操作服务
|
||||
accountService commonService.IAccount
|
||||
// 系统登录访问
|
||||
sysLogLoginService systemService.ISysLogLogin
|
||||
}
|
||||
|
||||
// 系统登录
|
||||
//
|
||||
// POST /login
|
||||
func (s *AccountController) Login(c *gin.Context) {
|
||||
var loginBody commonModel.LoginBody
|
||||
if err := c.ShouldBindJSON(&loginBody); err != nil {
|
||||
c.JSON(400, result.CodeMsg(400, "参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 当前请求信息
|
||||
ipaddr, location := ctxUtils.IPAddrLocation(c)
|
||||
os, browser := ctxUtils.UaOsBrowser(c)
|
||||
|
||||
// 校验验证码
|
||||
err := s.accountService.ValidateCaptcha(
|
||||
loginBody.Code,
|
||||
loginBody.UUID,
|
||||
)
|
||||
// 根据错误信息,创建系统访问记录
|
||||
if err != nil {
|
||||
msg := err.Error() + " " + loginBody.Code
|
||||
s.sysLogLoginService.NewSysLogLogin(
|
||||
loginBody.Username, commonConstants.STATUS_NO, msg,
|
||||
ipaddr, location, os, browser,
|
||||
)
|
||||
c.JSON(200, result.ErrMsg(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 登录用户信息
|
||||
loginUser, err := s.accountService.LoginByUsername(loginBody.Username, loginBody.Password)
|
||||
if err != nil {
|
||||
c.JSON(200, result.ErrMsg(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 生成令牌,创建系统访问记录
|
||||
tokenStr := tokenUtils.Create(&loginUser, ipaddr, location, os, browser)
|
||||
if tokenStr == "" {
|
||||
c.JSON(200, result.Err(nil))
|
||||
return
|
||||
} else {
|
||||
s.sysLogLoginService.NewSysLogLogin(
|
||||
loginBody.Username, commonConstants.STATUS_YES, "登录成功",
|
||||
ipaddr, location, os, browser,
|
||||
)
|
||||
}
|
||||
|
||||
// 设置登录会话-兼容旧登录方式
|
||||
libAccount.SessionToken(loginBody.Username, ipaddr)
|
||||
|
||||
c.JSON(200, result.OkData(map[string]any{
|
||||
tokenConstants.RESPONSE_FIELD: tokenStr,
|
||||
}))
|
||||
}
|
||||
|
||||
// 登录用户信息
|
||||
//
|
||||
// GET /getInfo
|
||||
func (s *AccountController) Info(c *gin.Context) {
|
||||
loginUser, err := ctxUtils.LoginUser(c)
|
||||
if err != nil {
|
||||
c.JSON(401, result.CodeMsg(401, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 角色权限集合,管理员拥有所有权限
|
||||
isAdmin := config.IsAdmin(loginUser.UserID)
|
||||
roles, perms := s.accountService.RoleAndMenuPerms(loginUser.UserID, isAdmin)
|
||||
|
||||
c.JSON(200, result.OkData(map[string]any{
|
||||
"user": loginUser.User,
|
||||
"roles": roles,
|
||||
"permissions": perms,
|
||||
}))
|
||||
}
|
||||
|
||||
// 登录用户路由信息
|
||||
//
|
||||
// GET /getRouters
|
||||
func (s *AccountController) Router(c *gin.Context) {
|
||||
userID := ctxUtils.LoginUserToUserID(c)
|
||||
|
||||
// 前端路由,管理员拥有所有
|
||||
isAdmin := config.IsAdmin(userID)
|
||||
buildMenus := s.accountService.RouteMenus(userID, isAdmin)
|
||||
c.JSON(200, result.OkData(buildMenus))
|
||||
}
|
||||
|
||||
// 系统登出
|
||||
//
|
||||
// POST /logout
|
||||
func (s *AccountController) Logout(c *gin.Context) {
|
||||
tokenStr := ctxUtils.Authorization(c)
|
||||
if tokenStr != "" {
|
||||
// 存在token时记录退出信息
|
||||
userName := tokenUtils.Remove(tokenStr)
|
||||
if userName != "" {
|
||||
// 当前请求信息
|
||||
ipaddr, location := ctxUtils.IPAddrLocation(c)
|
||||
os, browser := ctxUtils.UaOsBrowser(c)
|
||||
// 创建系统访问记录
|
||||
s.sysLogLoginService.NewSysLogLogin(
|
||||
userName, commonConstants.STATUS_NO, "退出成功",
|
||||
ipaddr, location, os, browser,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(200, result.OkMsg("退出成功"))
|
||||
}
|
||||
129
src/modules/common/controller/captcha.go
Normal file
129
src/modules/common/controller/captcha.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"ems.agt/src/framework/config"
|
||||
"ems.agt/src/framework/constants/cachekey"
|
||||
"ems.agt/src/framework/constants/captcha"
|
||||
"ems.agt/src/framework/logger"
|
||||
"ems.agt/src/framework/redis"
|
||||
"ems.agt/src/framework/utils/parse"
|
||||
"ems.agt/src/framework/vo/result"
|
||||
systemService "ems.agt/src/modules/system/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mojocn/base64Captcha"
|
||||
)
|
||||
|
||||
// 实例化控制层 CaptchaController 结构体
|
||||
var NewCaptcha = &CaptchaController{
|
||||
sysConfigService: systemService.NewSysConfigImpl,
|
||||
}
|
||||
|
||||
// 验证码操作处理
|
||||
//
|
||||
// PATH /
|
||||
type CaptchaController struct {
|
||||
// 参数配置服务
|
||||
sysConfigService systemService.ISysConfig
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
//
|
||||
// GET /captchaImage
|
||||
func (s *CaptchaController) Image(c *gin.Context) {
|
||||
// 从数据库配置获取验证码开关 true开启,false关闭
|
||||
captchaEnabledStr := s.sysConfigService.SelectConfigValueByKey("sys.account.captchaEnabled")
|
||||
captchaEnabled := parse.Boolean(captchaEnabledStr)
|
||||
if !captchaEnabled {
|
||||
c.JSON(200, result.Ok(map[string]any{
|
||||
"captchaEnabled": captchaEnabled,
|
||||
}))
|
||||
return
|
||||
}
|
||||
|
||||
// 生成唯一标识
|
||||
verifyKey := ""
|
||||
data := map[string]any{
|
||||
"captchaEnabled": captchaEnabled,
|
||||
"uuid": "",
|
||||
"img": "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
|
||||
}
|
||||
|
||||
// 从数据库配置获取验证码类型 math 数值计算 char 字符验证
|
||||
captchaType := s.sysConfigService.SelectConfigValueByKey("sys.account.captchaType")
|
||||
if captchaType == captcha.TYPE_MATH {
|
||||
math := config.Get("mathCaptcha").(map[string]any)
|
||||
driverCaptcha := &base64Captcha.DriverMath{
|
||||
//Height png height in pixel.
|
||||
Height: math["height"].(int),
|
||||
// Width Captcha png width in pixel.
|
||||
Width: math["width"].(int),
|
||||
//NoiseCount text noise count.
|
||||
NoiseCount: math["noise"].(int),
|
||||
//ShowLineOptions := OptionShowHollowLine | OptionShowSlimeLine | OptionShowSineLine .
|
||||
ShowLineOptions: base64Captcha.OptionShowHollowLine,
|
||||
}
|
||||
if math["color"].(bool) {
|
||||
//BgColor captcha image background color (optional)
|
||||
driverCaptcha.BgColor = parse.Color(math["background"].(string))
|
||||
}
|
||||
// 验证码生成
|
||||
id, question, answer := driverCaptcha.GenerateIdQuestionAnswer()
|
||||
// 验证码表达式解析输出
|
||||
item, err := driverCaptcha.DrawCaptcha(question)
|
||||
if err != nil {
|
||||
logger.Infof("Generate Id Question Answer %s %s : %v", captchaType, question, err)
|
||||
} else {
|
||||
data["uuid"] = id
|
||||
data["img"] = item.EncodeB64string()
|
||||
expiration := captcha.EXPIRATION * time.Second
|
||||
verifyKey = cachekey.CAPTCHA_CODE_KEY + id
|
||||
redis.SetByExpire("", verifyKey, answer, expiration)
|
||||
}
|
||||
}
|
||||
if captchaType == captcha.TYPE_CHAR {
|
||||
char := config.Get("charCaptcha").(map[string]any)
|
||||
driverCaptcha := &base64Captcha.DriverString{
|
||||
//Height png height in pixel.
|
||||
Height: char["height"].(int),
|
||||
// Width Captcha png width in pixel.
|
||||
Width: char["width"].(int),
|
||||
//NoiseCount text noise count.
|
||||
NoiseCount: char["noise"].(int),
|
||||
//Length random string length.
|
||||
Length: char["size"].(int),
|
||||
//Source is a unicode which is the rand string from.
|
||||
Source: char["chars"].(string),
|
||||
//ShowLineOptions := OptionShowHollowLine | OptionShowSlimeLine | OptionShowSineLine .
|
||||
ShowLineOptions: base64Captcha.OptionShowHollowLine,
|
||||
}
|
||||
if char["color"].(bool) {
|
||||
//BgColor captcha image background color (optional)
|
||||
driverCaptcha.BgColor = parse.Color(char["background"].(string))
|
||||
}
|
||||
// 验证码生成
|
||||
id, question, answer := driverCaptcha.GenerateIdQuestionAnswer()
|
||||
// 验证码表达式解析输出
|
||||
item, err := driverCaptcha.DrawCaptcha(question)
|
||||
if err != nil {
|
||||
logger.Infof("Generate Id Question Answer %s %s : %v", captchaType, question, err)
|
||||
} else {
|
||||
data["uuid"] = id
|
||||
data["img"] = item.EncodeB64string()
|
||||
expiration := captcha.EXPIRATION * time.Second
|
||||
verifyKey = cachekey.CAPTCHA_CODE_KEY + id
|
||||
redis.SetByExpire("", verifyKey, answer, expiration)
|
||||
}
|
||||
}
|
||||
|
||||
// 本地开发下返回验证码结果,方便接口调试
|
||||
if config.Env() == "local" {
|
||||
text, _ := redis.Get("", verifyKey)
|
||||
data["text"] = text
|
||||
c.JSON(200, result.Ok(data))
|
||||
return
|
||||
}
|
||||
c.JSON(200, result.Ok(data))
|
||||
}
|
||||
20
src/modules/common/controller/common.go
Normal file
20
src/modules/common/controller/common.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// 实例化控制层 CommontController 结构体
|
||||
var NewCommont = &CommontController{}
|
||||
|
||||
// 通用请求
|
||||
//
|
||||
// PATH /
|
||||
type CommontController struct{}
|
||||
|
||||
// 哈希加密
|
||||
//
|
||||
// GET /hash
|
||||
func (s *CommontController) Hash(c *gin.Context) {
|
||||
c.String(200, "commont Hash")
|
||||
}
|
||||
185
src/modules/common/controller/file.go
Normal file
185
src/modules/common/controller/file.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"ems.agt/src/framework/constants/uploadsubpath"
|
||||
"ems.agt/src/framework/utils/file"
|
||||
"ems.agt/src/framework/vo/result"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// 实例化控制层 FileController 结构体
|
||||
var NewFile = &FileController{}
|
||||
|
||||
// 文件操作处理
|
||||
//
|
||||
// PATH /
|
||||
type FileController struct{}
|
||||
|
||||
// 下载文件
|
||||
//
|
||||
// GET /download/:filePath
|
||||
func (s *FileController) Download(c *gin.Context) {
|
||||
filePath := c.Param("filePath")
|
||||
if len(filePath) < 8 {
|
||||
c.JSON(400, result.CodeMsg(400, "参数错误"))
|
||||
return
|
||||
}
|
||||
// base64解析出地址
|
||||
decodedBytes, err := base64.StdEncoding.DecodeString(filePath)
|
||||
if err != nil {
|
||||
c.JSON(400, result.CodeMsg(400, err.Error()))
|
||||
return
|
||||
}
|
||||
routerPath := string(decodedBytes)
|
||||
// 地址文件名截取
|
||||
fileName := routerPath[strings.LastIndex(routerPath, "/")+1:]
|
||||
|
||||
// 响应头
|
||||
c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+url.QueryEscape(fileName)+`"`)
|
||||
c.Writer.Header().Set("Accept-Ranges", "bytes")
|
||||
c.Writer.Header().Set("Content-Type", "application/octet-stream")
|
||||
|
||||
// 断点续传
|
||||
headerRange := c.GetHeader("Range")
|
||||
resultMap, err := file.ReadUploadFileStream(routerPath, headerRange)
|
||||
if err != nil {
|
||||
c.JSON(200, result.ErrMsg(err.Error()))
|
||||
return
|
||||
}
|
||||
if headerRange != "" {
|
||||
c.Writer.Header().Set("Content-Range", fmt.Sprint(resultMap["range"]))
|
||||
c.Writer.Header().Set("Content-Length", fmt.Sprint(resultMap["chunkSize"]))
|
||||
c.Status(206)
|
||||
} else {
|
||||
c.Writer.Header().Set("Content-Length", fmt.Sprint(resultMap["fileSize"]))
|
||||
c.Status(200)
|
||||
|
||||
}
|
||||
c.Writer.Write(resultMap["data"].([]byte))
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
//
|
||||
// POST /upload
|
||||
func (s *FileController) Upload(c *gin.Context) {
|
||||
// 上传的文件
|
||||
formFile, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(400, result.CodeMsg(400, "参数错误"))
|
||||
return
|
||||
}
|
||||
// 子路径
|
||||
subPath := c.PostForm("subPath")
|
||||
if _, ok := uploadsubpath.UploadSubpath[subPath]; !ok {
|
||||
c.JSON(400, result.CodeMsg(400, "参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 上传文件转存
|
||||
upFilePath, err := file.TransferUploadFile(formFile, subPath, nil)
|
||||
if err != nil {
|
||||
c.JSON(200, result.ErrMsg(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
newFileName := upFilePath[strings.LastIndex(upFilePath, "/")+1:]
|
||||
c.JSON(200, result.OkData(map[string]string{
|
||||
"url": "http://" + c.Request.Host + upFilePath,
|
||||
"fileName": upFilePath,
|
||||
"newFileName": newFileName,
|
||||
"originalFileName": formFile.Filename,
|
||||
}))
|
||||
}
|
||||
|
||||
// 切片文件检查
|
||||
//
|
||||
// POST /chunkCheck
|
||||
func (s *FileController) ChunkCheck(c *gin.Context) {
|
||||
var body struct {
|
||||
// 唯一标识
|
||||
Identifier string `json:"identifier" binding:"required"`
|
||||
// 文件名
|
||||
FileName string `json:"fileName" binding:"required"`
|
||||
}
|
||||
err := c.ShouldBindJSON(&body)
|
||||
if err != nil {
|
||||
c.JSON(400, result.CodeMsg(400, "参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 读取标识目录
|
||||
chunks, err := file.ChunkCheckFile(body.Identifier, body.FileName)
|
||||
if err != nil {
|
||||
c.JSON(200, result.ErrMsg(err.Error()))
|
||||
return
|
||||
}
|
||||
c.JSON(200, result.OkData(chunks))
|
||||
}
|
||||
|
||||
// 切片文件合并
|
||||
//
|
||||
// POST /chunkMerge
|
||||
func (s *FileController) ChunkMerge(c *gin.Context) {
|
||||
var body struct {
|
||||
// 唯一标识
|
||||
Identifier string `json:"identifier" binding:"required"`
|
||||
// 文件名
|
||||
FileName string `json:"fileName" binding:"required"`
|
||||
// 子路径类型
|
||||
SubPath string `json:"subPath" binding:"required"`
|
||||
}
|
||||
err := c.ShouldBindJSON(&body)
|
||||
if err != nil {
|
||||
c.JSON(400, result.CodeMsg(400, "参数错误"))
|
||||
return
|
||||
}
|
||||
if _, ok := uploadsubpath.UploadSubpath[body.SubPath]; !ok {
|
||||
c.JSON(400, result.CodeMsg(400, "参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 切片文件合并
|
||||
mergeFilePath, err := file.ChunkMergeFile(body.Identifier, body.FileName, body.SubPath)
|
||||
if err != nil {
|
||||
c.JSON(200, result.ErrMsg(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
newFileName := mergeFilePath[strings.LastIndex(mergeFilePath, "/")+1:]
|
||||
c.JSON(200, result.OkData(map[string]string{
|
||||
"url": "http://" + c.Request.Host + mergeFilePath,
|
||||
"fileName": mergeFilePath,
|
||||
"newFileName": newFileName,
|
||||
"originalFileName": body.FileName,
|
||||
}))
|
||||
}
|
||||
|
||||
// 切片文件上传
|
||||
//
|
||||
// POST /chunkUpload
|
||||
func (s *FileController) ChunkUpload(c *gin.Context) {
|
||||
// 切片编号
|
||||
index := c.PostForm("index")
|
||||
// 切片唯一标识
|
||||
identifier := c.PostForm("identifier")
|
||||
// 上传的文件
|
||||
formFile, err := c.FormFile("file")
|
||||
if index == "" || identifier == "" || err != nil {
|
||||
c.JSON(400, result.CodeMsg(400, "参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 上传文件转存
|
||||
chunkFilePath, err := file.TransferChunkUploadFile(formFile, index, identifier)
|
||||
if err != nil {
|
||||
c.JSON(200, result.ErrMsg(err.Error()))
|
||||
return
|
||||
}
|
||||
c.JSON(206, result.OkData(chunkFilePath))
|
||||
}
|
||||
28
src/modules/common/controller/index.go
Normal file
28
src/modules/common/controller/index.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ems.agt/src/framework/config"
|
||||
"ems.agt/src/framework/vo/result"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// 实例化控制层 IndexController 结构体
|
||||
var NewIndex = &IndexController{}
|
||||
|
||||
// 路由主页
|
||||
//
|
||||
// PATH /
|
||||
type IndexController struct{}
|
||||
|
||||
// 根路由
|
||||
//
|
||||
// GET /
|
||||
func (s *IndexController) Handler(c *gin.Context) {
|
||||
name := config.Get("framework.name").(string)
|
||||
version := config.Get("framework.version").(string)
|
||||
str := "欢迎使用%s后台管理框架,当前版本:%s,请通过前端管理地址访问。"
|
||||
c.JSON(200, result.OkMsg(fmt.Sprintf(str, name, version)))
|
||||
}
|
||||
88
src/modules/common/controller/register.go
Normal file
88
src/modules/common/controller/register.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
commonConstants "ems.agt/src/framework/constants/common"
|
||||
ctxUtils "ems.agt/src/framework/utils/ctx"
|
||||
"ems.agt/src/framework/utils/regular"
|
||||
"ems.agt/src/framework/vo/result"
|
||||
commonModel "ems.agt/src/modules/common/model"
|
||||
commonService "ems.agt/src/modules/common/service"
|
||||
systemService "ems.agt/src/modules/system/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// 实例化控制层 RegisterController 结构体
|
||||
var NewRegister = &RegisterController{
|
||||
registerService: commonService.NewRegisterImpl,
|
||||
sysLogLoginService: systemService.NewSysLogLoginImpl,
|
||||
}
|
||||
|
||||
// 账号注册操作处理
|
||||
//
|
||||
// PATH /
|
||||
type RegisterController struct {
|
||||
// 账号注册操作服务
|
||||
registerService commonService.IRegister
|
||||
// 系统登录访问
|
||||
sysLogLoginService systemService.ISysLogLogin
|
||||
}
|
||||
|
||||
// 账号注册
|
||||
//
|
||||
// GET /captchaImage
|
||||
func (s *RegisterController) UserName(c *gin.Context) {
|
||||
var registerBody commonModel.RegisterBody
|
||||
if err := c.ShouldBindJSON(®isterBody); err != nil {
|
||||
c.JSON(400, result.ErrMsg("参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 判断必传参数
|
||||
if !regular.ValidUsername(registerBody.Username) {
|
||||
c.JSON(200, result.ErrMsg("账号不能以数字开头,可包含大写小写字母,数字,且不少于5位"))
|
||||
return
|
||||
}
|
||||
if !regular.ValidPassword(registerBody.Password) {
|
||||
c.JSON(200, result.ErrMsg("登录密码至少包含大小写字母、数字、特殊符号,且不少于6位"))
|
||||
return
|
||||
}
|
||||
if registerBody.Password != registerBody.ConfirmPassword {
|
||||
c.JSON(200, result.ErrMsg("用户确认输入密码不一致"))
|
||||
return
|
||||
}
|
||||
|
||||
// 当前请求信息
|
||||
ipaddr, location := ctxUtils.IPAddrLocation(c)
|
||||
os, browser := ctxUtils.UaOsBrowser(c)
|
||||
|
||||
// 校验验证码
|
||||
err := s.registerService.ValidateCaptcha(
|
||||
registerBody.Code,
|
||||
registerBody.UUID,
|
||||
)
|
||||
// 根据错误信息,创建系统访问记录
|
||||
if err != nil {
|
||||
msg := err.Error() + " " + registerBody.Code
|
||||
s.sysLogLoginService.NewSysLogLogin(
|
||||
registerBody.Username, commonConstants.STATUS_NO, msg,
|
||||
ipaddr, location, os, browser,
|
||||
)
|
||||
c.JSON(200, result.ErrMsg(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
infoStr := s.registerService.ByUserName(registerBody.Username, registerBody.Password, registerBody.UserType)
|
||||
if !strings.HasPrefix(infoStr, "注册") {
|
||||
msg := registerBody.Username + " 注册成功 " + infoStr
|
||||
s.sysLogLoginService.NewSysLogLogin(
|
||||
registerBody.Username, commonConstants.STATUS_NO, msg,
|
||||
ipaddr, location, os, browser,
|
||||
)
|
||||
c.JSON(200, result.OkMsg("注册成功"))
|
||||
return
|
||||
}
|
||||
c.JSON(200, result.ErrMsg(infoStr))
|
||||
}
|
||||
16
src/modules/common/model/login_body.go
Normal file
16
src/modules/common/model/login_body.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package model
|
||||
|
||||
// LoginBody 用户登录对象
|
||||
type LoginBody struct {
|
||||
// Username 用户名
|
||||
Username string `json:"username" binding:"required"`
|
||||
|
||||
// Password 用户密码
|
||||
Password string `json:"password" binding:"required"`
|
||||
|
||||
// Code 验证码
|
||||
Code string `json:"code"`
|
||||
|
||||
// UUID 验证码唯一标识
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
22
src/modules/common/model/register_body.go
Normal file
22
src/modules/common/model/register_body.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package model
|
||||
|
||||
// RegisterBody 用户注册对象
|
||||
type RegisterBody struct {
|
||||
// Username 用户名
|
||||
Username string `json:"username" binding:"required"`
|
||||
|
||||
// Password 用户密码
|
||||
Password string `json:"password" binding:"required"`
|
||||
|
||||
// ConfirmPassword 用户确认密码
|
||||
ConfirmPassword string `json:"confirmPassword" binding:"required"`
|
||||
|
||||
// Code 验证码
|
||||
Code string `json:"code"`
|
||||
|
||||
// UUID 验证码唯一标识
|
||||
UUID string `json:"uuid"`
|
||||
|
||||
// UserType 标记用户类型
|
||||
UserType string `json:"userType"`
|
||||
}
|
||||
21
src/modules/common/service/account.go
Normal file
21
src/modules/common/service/account.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package service
|
||||
|
||||
import "ems.agt/src/framework/vo"
|
||||
|
||||
// 账号身份操作服务 服务层接口
|
||||
type IAccount interface {
|
||||
// ValidateCaptcha 校验验证码
|
||||
ValidateCaptcha(code, uuid string) error
|
||||
|
||||
// LoginByUsername 登录生成token
|
||||
LoginByUsername(username, password string) (vo.LoginUser, error)
|
||||
|
||||
// ClearLoginRecordCache 清除错误记录次数
|
||||
ClearLoginRecordCache(username string) bool
|
||||
|
||||
// RoleAndMenuPerms 角色和菜单数据权限
|
||||
RoleAndMenuPerms(userId string, isAdmin bool) ([]string, []string)
|
||||
|
||||
// RouteMenus 前端路由所需要的菜单
|
||||
RouteMenus(userId string, isAdmin bool) []vo.Router
|
||||
}
|
||||
166
src/modules/common/service/account.impl.go
Normal file
166
src/modules/common/service/account.impl.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"ems.agt/src/framework/config"
|
||||
adminConstants "ems.agt/src/framework/constants/admin"
|
||||
"ems.agt/src/framework/constants/cachekey"
|
||||
"ems.agt/src/framework/constants/common"
|
||||
"ems.agt/src/framework/redis"
|
||||
"ems.agt/src/framework/utils/crypto"
|
||||
"ems.agt/src/framework/utils/parse"
|
||||
"ems.agt/src/framework/vo"
|
||||
systemService "ems.agt/src/modules/system/service"
|
||||
)
|
||||
|
||||
// 实例化服务层 AccountImpl 结构体
|
||||
var NewAccountImpl = &AccountImpl{
|
||||
sysUserService: systemService.NewSysUserImpl,
|
||||
sysConfigService: systemService.NewSysConfigImpl,
|
||||
sysRoleService: systemService.NewSysRoleImpl,
|
||||
sysMenuService: systemService.NewSysMenuImpl,
|
||||
}
|
||||
|
||||
// 账号身份操作服务 服务层处理
|
||||
type AccountImpl struct {
|
||||
// 用户信息服务
|
||||
sysUserService systemService.ISysUser
|
||||
// 参数配置服务
|
||||
sysConfigService systemService.ISysConfig
|
||||
// 角色服务
|
||||
sysRoleService systemService.ISysRole
|
||||
// 菜单服务
|
||||
sysMenuService systemService.ISysMenu
|
||||
}
|
||||
|
||||
// ValidateCaptcha 校验验证码
|
||||
func (s *AccountImpl) ValidateCaptcha(code, uuid string) error {
|
||||
// 验证码检查,从数据库配置获取验证码开关 true开启,false关闭
|
||||
captchaEnabledStr := s.sysConfigService.SelectConfigValueByKey("sys.account.captchaEnabled")
|
||||
if !parse.Boolean(captchaEnabledStr) {
|
||||
return nil
|
||||
}
|
||||
if code == "" || uuid == "" {
|
||||
return errors.New("验证码信息错误")
|
||||
}
|
||||
verifyKey := cachekey.CAPTCHA_CODE_KEY + uuid
|
||||
captcha, _ := redis.Get("", verifyKey)
|
||||
if captcha == "" {
|
||||
return errors.New("验证码已失效")
|
||||
}
|
||||
redis.Del("", verifyKey)
|
||||
if captcha != code {
|
||||
return errors.New("验证码错误")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoginByUsername 登录创建用户信息
|
||||
func (s *AccountImpl) LoginByUsername(username, password string) (vo.LoginUser, error) {
|
||||
loginUser := vo.LoginUser{}
|
||||
|
||||
// 检查密码重试次数
|
||||
retrykey, retryCount, lockTime, err := s.passwordRetryCount(username)
|
||||
if err != nil {
|
||||
return loginUser, err
|
||||
}
|
||||
|
||||
// 查询用户登录账号
|
||||
sysUser := s.sysUserService.SelectUserByUserName(username)
|
||||
if sysUser.UserName != username {
|
||||
return loginUser, errors.New("用户不存在或密码错误")
|
||||
}
|
||||
if sysUser.DelFlag == common.STATUS_YES {
|
||||
return loginUser, errors.New("对不起,您的账号已被删除")
|
||||
}
|
||||
if sysUser.Status == common.STATUS_NO {
|
||||
return loginUser, errors.New("对不起,您的账号已禁用")
|
||||
}
|
||||
|
||||
// 检验用户密码
|
||||
compareBool := crypto.BcryptCompare(password, sysUser.Password)
|
||||
if !compareBool {
|
||||
redis.SetByExpire("", retrykey, retryCount+1, lockTime)
|
||||
return loginUser, errors.New("用户不存在或密码错误")
|
||||
} else {
|
||||
// 清除错误记录次数
|
||||
s.ClearLoginRecordCache(username)
|
||||
}
|
||||
|
||||
// 登录用户信息
|
||||
loginUser.UserID = sysUser.UserID
|
||||
loginUser.DeptID = sysUser.DeptID
|
||||
loginUser.User = sysUser
|
||||
// 用户权限组标识
|
||||
isAdmin := config.IsAdmin(sysUser.UserID)
|
||||
if isAdmin {
|
||||
loginUser.Permissions = []string{adminConstants.PERMISSION}
|
||||
} else {
|
||||
perms := s.sysMenuService.SelectMenuPermsByUserId(sysUser.UserID)
|
||||
loginUser.Permissions = parse.RemoveDuplicates(perms)
|
||||
}
|
||||
return loginUser, nil
|
||||
}
|
||||
|
||||
// ClearLoginRecordCache 清除错误记录次数
|
||||
func (s *AccountImpl) ClearLoginRecordCache(username string) bool {
|
||||
cacheKey := cachekey.PWD_ERR_CNT_KEY + username
|
||||
hasKey, _ := redis.Has("", cacheKey)
|
||||
if hasKey {
|
||||
delOk, _ := redis.Del("", cacheKey)
|
||||
return delOk
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// passwordRetryCount 密码重试次数
|
||||
func (s *AccountImpl) passwordRetryCount(username string) (string, int64, time.Duration, error) {
|
||||
// 验证登录次数和错误锁定时间
|
||||
maxRetryCount := config.Get("user.password.maxRetryCount").(int)
|
||||
lockTime := config.Get("user.password.lockTime").(int)
|
||||
// 验证缓存记录次数
|
||||
retrykey := cachekey.PWD_ERR_CNT_KEY + username
|
||||
retryCount, err := redis.Get("", retrykey)
|
||||
if retryCount == "" || err != nil {
|
||||
retryCount = "0"
|
||||
}
|
||||
// 是否超过错误值
|
||||
if parse.Number(retryCount) >= int64(maxRetryCount) {
|
||||
msg := fmt.Sprintf("密码输入错误 %d 次,帐户锁定 %d 分钟", maxRetryCount, lockTime)
|
||||
return retrykey, int64(maxRetryCount), time.Duration(lockTime) * time.Minute, errors.New(msg)
|
||||
}
|
||||
return retrykey, int64(maxRetryCount), time.Duration(lockTime) * time.Minute, nil
|
||||
}
|
||||
|
||||
// RoleAndMenuPerms 角色和菜单数据权限
|
||||
func (s *AccountImpl) RoleAndMenuPerms(userId string, isAdmin bool) ([]string, []string) {
|
||||
if isAdmin {
|
||||
return []string{adminConstants.ROLE_KEY}, []string{adminConstants.PERMISSION}
|
||||
} else {
|
||||
// 角色key
|
||||
roleGroup := []string{}
|
||||
roles := s.sysRoleService.SelectRoleListByUserId(userId)
|
||||
for _, role := range roles {
|
||||
roleGroup = append(roleGroup, role.RoleKey)
|
||||
}
|
||||
// 菜单权限key
|
||||
perms := s.sysMenuService.SelectMenuPermsByUserId(userId)
|
||||
return parse.RemoveDuplicates(roleGroup), parse.RemoveDuplicates(perms)
|
||||
}
|
||||
}
|
||||
|
||||
// RouteMenus 前端路由所需要的菜单
|
||||
func (s *AccountImpl) RouteMenus(userId string, isAdmin bool) []vo.Router {
|
||||
var buildMenus []vo.Router
|
||||
if isAdmin {
|
||||
menus := s.sysMenuService.SelectMenuTreeByUserId("*")
|
||||
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
|
||||
} else {
|
||||
menus := s.sysMenuService.SelectMenuTreeByUserId(userId)
|
||||
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
|
||||
}
|
||||
return buildMenus
|
||||
}
|
||||
10
src/modules/common/service/register.go
Normal file
10
src/modules/common/service/register.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package service
|
||||
|
||||
// 账号注册操作处理 服务层接口
|
||||
type IRegister interface {
|
||||
// ValidateCaptcha 校验验证码
|
||||
ValidateCaptcha(code, uuid string) error
|
||||
|
||||
// ByUserName 账号注册
|
||||
ByUserName(username, password, userType string) string
|
||||
}
|
||||
100
src/modules/common/service/register.impl.go
Normal file
100
src/modules/common/service/register.impl.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"ems.agt/src/framework/constants/cachekey"
|
||||
"ems.agt/src/framework/constants/common"
|
||||
"ems.agt/src/framework/redis"
|
||||
"ems.agt/src/framework/utils/parse"
|
||||
systemModel "ems.agt/src/modules/system/model"
|
||||
systemService "ems.agt/src/modules/system/service"
|
||||
)
|
||||
|
||||
// 实例化服务层 RegisterImpl 结构体
|
||||
var NewRegisterImpl = &RegisterImpl{
|
||||
sysUserService: systemService.NewSysUserImpl,
|
||||
sysConfigService: systemService.NewSysConfigImpl,
|
||||
sysRoleService: systemService.NewSysRoleImpl,
|
||||
}
|
||||
|
||||
// 账号注册操作处理 服务层处理
|
||||
type RegisterImpl struct {
|
||||
// 用户信息服务
|
||||
sysUserService systemService.ISysUser
|
||||
// 参数配置服务
|
||||
sysConfigService systemService.ISysConfig
|
||||
// 角色服务
|
||||
sysRoleService systemService.ISysRole
|
||||
}
|
||||
|
||||
// ValidateCaptcha 校验验证码
|
||||
func (s *RegisterImpl) ValidateCaptcha(code, uuid string) error {
|
||||
// 验证码检查,从数据库配置获取验证码开关 true开启,false关闭
|
||||
captchaEnabledStr := s.sysConfigService.SelectConfigValueByKey("sys.account.captchaEnabled")
|
||||
if !parse.Boolean(captchaEnabledStr) {
|
||||
return nil
|
||||
}
|
||||
if code == "" || uuid == "" {
|
||||
return errors.New("验证码信息错误")
|
||||
}
|
||||
verifyKey := cachekey.CAPTCHA_CODE_KEY + uuid
|
||||
captcha, err := redis.Get("", verifyKey)
|
||||
if captcha == "" || err != nil {
|
||||
return errors.New("验证码已失效")
|
||||
}
|
||||
redis.Del("", verifyKey)
|
||||
if captcha != code {
|
||||
return errors.New("验证码错误")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ByUserName 账号注册
|
||||
func (s *RegisterImpl) ByUserName(username, password, userType string) string {
|
||||
// 检查用户登录账号是否唯一
|
||||
uniqueUserName := s.sysUserService.CheckUniqueUserName(username, "")
|
||||
if !uniqueUserName {
|
||||
return fmt.Sprintf("注册用户【%s】失败,注册账号已存在", username)
|
||||
}
|
||||
|
||||
sysUser := systemModel.SysUser{
|
||||
UserName: username,
|
||||
NickName: username, // 昵称使用名称账号
|
||||
Password: password, // 原始密码
|
||||
Status: common.STATUS_YES, // 账号状态激活
|
||||
DeptID: "100", // 归属部门为根节点
|
||||
CreateBy: "注册", // 创建来源
|
||||
}
|
||||
// 标记用户类型
|
||||
if userType == "" {
|
||||
sysUser.UserType = "sys"
|
||||
}
|
||||
// 新增用户的角色管理
|
||||
sysUser.RoleIDs = s.registerRoleInit(userType)
|
||||
// 新增用户的岗位管理
|
||||
sysUser.PostIDs = s.registerPostInit(userType)
|
||||
|
||||
insertId := s.sysUserService.InsertUser(sysUser)
|
||||
if insertId != "" {
|
||||
return insertId
|
||||
}
|
||||
return "注册失败,请联系系统管理人员"
|
||||
}
|
||||
|
||||
// registerRoleInit 注册初始角色
|
||||
func (s *RegisterImpl) registerRoleInit(userType string) []string {
|
||||
if userType == "sys" {
|
||||
return []string{}
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// registerPostInit 注册初始岗位
|
||||
func (s *RegisterImpl) registerPostInit(userType string) []string {
|
||||
if userType == "sys" {
|
||||
return []string{}
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
Reference in New Issue
Block a user