feat: 合并Gin_Vue

This commit is contained in:
TsMask
2023-10-16 17:10:38 +08:00
parent 5289818fd4
commit 40a32cb67f
203 changed files with 19719 additions and 178 deletions

View 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("退出成功"))
}

View 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))
}

View 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")
}

View 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))
}

View 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)))
}

View 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(&registerBody); 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))
}