feat: 合并Gin_Vue
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
tokenConst "ems.agt/src/framework/constants/token"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
@@ -43,7 +44,7 @@ func GetParamConfigFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
restHostPort := fmt.Sprintf("http://127.0.0.1:%d", config.GetYamlConfig().Rest[0].Port)
|
restHostPort := fmt.Sprintf("http://127.0.0.1:%d", config.GetYamlConfig().Rest[0].Port)
|
||||||
getNeInfoPattern := fmt.Sprintf(config.UriPrefix+"/databaseManagement/v1/%s/ne_info", config.GetYamlConfig().Database.Name)
|
getNeInfoPattern := fmt.Sprintf(config.DefaultUriPrefix+"/databaseManagement/v1/%s/ne_info", config.GetYamlConfig().Database.Name)
|
||||||
getNeInfoURI := restHostPort + getNeInfoPattern
|
getNeInfoURI := restHostPort + getNeInfoPattern
|
||||||
neId := services.GetUriParamString(r, "ne_id", ",", true, false)
|
neId := services.GetUriParamString(r, "ne_id", ",", true, false)
|
||||||
if neId == "" {
|
if neId == "" {
|
||||||
@@ -56,6 +57,7 @@ func GetParamConfigFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
client := resty.New()
|
client := resty.New()
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
|
||||||
SetHeaders(map[string]string{"accessToken": token}).
|
SetHeaders(map[string]string{"accessToken": token}).
|
||||||
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ func (s *FirewallApi) Rule(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body model.RuleQuerys
|
var body model.RuleQuerys
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.Type == "" {
|
if err != nil || body.Type == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data, err := s.firewallService.RulePage(body)
|
data, err := s.firewallService.RulePage(body)
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ import (
|
|||||||
"ems.agt/lib/services"
|
"ems.agt/lib/services"
|
||||||
"ems.agt/restagent/config"
|
"ems.agt/restagent/config"
|
||||||
|
|
||||||
|
tokenConst "ems.agt/src/framework/constants/token"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -153,7 +152,7 @@ func PostMMLToNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
for _, mml := range mmlRequest.MML {
|
for _, mml := range mmlRequest.MML {
|
||||||
mmlCommand := fmt.Sprintf("%s\n", mml)
|
mmlCommand := fmt.Sprintf("%s\n", mml)
|
||||||
log.Debug("mml command:", mmlCommand)
|
log.Debug("mml command:", mmlCommand)
|
||||||
n, err = conn.Write([]byte(mmlCommand))
|
_, err = conn.Write([]byte(mmlCommand))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error: %s", err.Error())
|
log.Errorf("Error: %s", err.Error())
|
||||||
return
|
return
|
||||||
@@ -211,14 +210,15 @@ func PostMMLToOMC(w http.ResponseWriter, r *http.Request) {
|
|||||||
hostUri := fmt.Sprintf("http://%s:%s", neInfo.Ip, neInfo.Port)
|
hostUri := fmt.Sprintf("http://%s:%s", neInfo.Ip, neInfo.Port)
|
||||||
|
|
||||||
omcMmlVar := &mmlp.MmlVar{
|
omcMmlVar := &mmlp.MmlVar{
|
||||||
Version: "16.1.1",
|
Version: "16.1.1",
|
||||||
Output: mmlp.DefaultFormatType,
|
Output: mmlp.DefaultFormatType,
|
||||||
MmlHome: config.GetYamlConfig().MML.MmlHome,
|
MmlHome: config.GetYamlConfig().MML.MmlHome,
|
||||||
Limit: 50,
|
Limit: 50,
|
||||||
User: "",
|
User: "",
|
||||||
SessionToken: token,
|
SessionToken: token, // 旧token
|
||||||
HttpUri: hostUri,
|
Authorization: r.Header.Get(tokenConst.HEADER_KEY), // 请求Token
|
||||||
UserAgent: config.GetDefaultUserAgent(),
|
HttpUri: hostUri,
|
||||||
|
UserAgent: config.GetDefaultUserAgent(),
|
||||||
}
|
}
|
||||||
mmlRequest := new(MMLRequest)
|
mmlRequest := new(MMLRequest)
|
||||||
_ = json.Unmarshal(body, mmlRequest)
|
_ = json.Unmarshal(body, mmlRequest)
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
sysConfigService "ems.agt/features/sys_config/service"
|
sysConfigService "ems.agt/features/sys_config/service"
|
||||||
"ems.agt/lib/core/account"
|
"ems.agt/lib/core/account"
|
||||||
"ems.agt/lib/core/cache"
|
"ems.agt/lib/core/cache"
|
||||||
"ems.agt/lib/core/conf"
|
|
||||||
"ems.agt/lib/core/constants/cachekey"
|
"ems.agt/lib/core/constants/cachekey"
|
||||||
"ems.agt/lib/core/utils/ctx"
|
"ems.agt/lib/core/utils/ctx"
|
||||||
"ems.agt/lib/core/vo/result"
|
"ems.agt/lib/core/vo/result"
|
||||||
@@ -24,7 +23,8 @@ import (
|
|||||||
"ems.agt/lib/oauth"
|
"ems.agt/lib/oauth"
|
||||||
"ems.agt/lib/services"
|
"ems.agt/lib/services"
|
||||||
"ems.agt/restagent/config"
|
"ems.agt/restagent/config"
|
||||||
"github.com/go-admin-team/go-admin-core/logger"
|
srcConfig "ems.agt/src/framework/config"
|
||||||
|
"ems.agt/src/framework/redis"
|
||||||
"github.com/mojocn/base64Captcha"
|
"github.com/mojocn/base64Captcha"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -136,9 +136,10 @@ func LoginFromOMC(w http.ResponseWriter, r *http.Request) {
|
|||||||
if user != nil {
|
if user != nil {
|
||||||
// 缓存用户信息
|
// 缓存用户信息
|
||||||
account.CacheLoginUser(user)
|
account.CacheLoginUser(user)
|
||||||
|
redis.SetByExpire("", "session_token", token, time.Second*1800)
|
||||||
// 角色权限集合,管理员拥有所有权限
|
// 角色权限集合,管理员拥有所有权限
|
||||||
userId := fmt.Sprint(user.Id)
|
userId := fmt.Sprint(user.Id)
|
||||||
isAdmin := conf.IsAdmin(userId)
|
isAdmin := srcConfig.IsAdmin(userId)
|
||||||
roles, perms := service.NewServiceAccount.RoleAndMenuPerms(userId, isAdmin)
|
roles, perms := service.NewServiceAccount.RoleAndMenuPerms(userId, isAdmin)
|
||||||
services.ResponseStatusOK200LoginWhitRP(w, token, user, roles, perms)
|
services.ResponseStatusOK200LoginWhitRP(w, token, user, roles, perms)
|
||||||
return
|
return
|
||||||
@@ -237,13 +238,13 @@ func LoginOMC(w http.ResponseWriter, r *http.Request) {
|
|||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Invalid Json Format")
|
log.Error("Invalid Json Format")
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// response 400-5
|
// response 400-5
|
||||||
if body.Username == "" || body.Password == "" {
|
if body.Username == "" || body.Password == "" {
|
||||||
log.Error("Wrong parameter value")
|
log.Error("Wrong parameter value")
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,20 +258,20 @@ func LoginOMC(w http.ResponseWriter, r *http.Request) {
|
|||||||
if captchaEnabled {
|
if captchaEnabled {
|
||||||
if body.Code == "" || body.UUID == "" {
|
if body.Code == "" || body.UUID == "" {
|
||||||
log.Error("Authentication failed, mismatch captcha")
|
log.Error("Authentication failed, mismatch captcha")
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "验证码信息错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "Verification code information error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
verifyKey := cachekey.CAPTCHA_CODE_KEY + body.UUID
|
verifyKey := cachekey.CAPTCHA_CODE_KEY + body.UUID
|
||||||
captcha, ok := cache.GetLocalTTL(verifyKey)
|
captcha, ok := cache.GetLocalTTL(verifyKey)
|
||||||
if captcha == nil || !ok {
|
if captcha == nil || !ok {
|
||||||
log.Error("Authentication failed, captcha emtry")
|
log.Error("Authentication failed, captcha emtry")
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "验证码已失效"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "The verification code has expired"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cache.DeleteLocalTTL(verifyKey)
|
cache.DeleteLocalTTL(verifyKey)
|
||||||
if captcha.(string) != body.Code {
|
if captcha.(string) != body.Code {
|
||||||
log.Error("Authentication failed, not match captcha")
|
log.Error("Authentication failed, not match captcha")
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "验证码错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "Verification code error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,7 +364,7 @@ func CaptchaImage(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 验证码表达式解析输出
|
// 验证码表达式解析输出
|
||||||
item, err := driverCaptcha.DrawCaptcha(question)
|
item, err := driverCaptcha.DrawCaptcha(question)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Infof("Generate Id Question Answer %s : %v", question, err)
|
log.Infof("Generate Id Question Answer %s : %v", question, err)
|
||||||
} else {
|
} else {
|
||||||
data["uuid"] = id
|
data["uuid"] = id
|
||||||
data["img"] = item.EncodeB64string()
|
data["img"] = item.EncodeB64string()
|
||||||
@@ -388,7 +389,7 @@ func UserInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
// 角色权限集合,管理员拥有所有权限
|
// 角色权限集合,管理员拥有所有权限
|
||||||
userId := fmt.Sprint(loginUser.UserID)
|
userId := fmt.Sprint(loginUser.UserID)
|
||||||
isAdmin := conf.IsAdmin(userId)
|
isAdmin := srcConfig.IsAdmin(userId)
|
||||||
roles, perms := service.NewServiceAccount.RoleAndMenuPerms(userId, isAdmin)
|
roles, perms := service.NewServiceAccount.RoleAndMenuPerms(userId, isAdmin)
|
||||||
|
|
||||||
ctx.JSON(w, 200, result.OkData(map[string]any{
|
ctx.JSON(w, 200, result.OkData(map[string]any{
|
||||||
@@ -403,7 +404,7 @@ func Routers(w http.ResponseWriter, r *http.Request) {
|
|||||||
userID := ctx.LoginUserToUserID(r)
|
userID := ctx.LoginUserToUserID(r)
|
||||||
|
|
||||||
// 前端路由,管理员拥有所有
|
// 前端路由,管理员拥有所有
|
||||||
isAdmin := conf.IsAdmin(userID)
|
isAdmin := srcConfig.IsAdmin(userID)
|
||||||
buildMenus := service.NewServiceAccount.RouteMenus(userID, isAdmin)
|
buildMenus := service.NewServiceAccount.RouteMenus(userID, isAdmin)
|
||||||
ctx.JSON(w, 200, result.OkData(buildMenus))
|
ctx.JSON(w, 200, result.OkData(buildMenus))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,51 @@ package sm
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"ems.agt/lib/log"
|
"ems.agt/lib/log"
|
||||||
|
"ems.agt/lib/services"
|
||||||
"ems.agt/restagent/config"
|
"ems.agt/restagent/config"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Get OMC local time
|
||||||
|
UriOMCLocalTime = config.DefaultUriPrefix + "/systemManagement/{apiVersion}/elementType/OMC/objectType/time"
|
||||||
|
|
||||||
|
CustomUriOMCLocalTime = config.UriPrefix + "/systemManagement/{apiVersion}/elementType/OMC/objectType/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OMCLocalTime struct {
|
||||||
|
Timestamp int64 `json:"timestamp"` // 时间戳 (单位:毫秒)
|
||||||
|
TimeZone int `json:"timeZone"` // 本地时区偏移(单位:秒)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOMCLocalTime(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Debug("GetOMCLocalTime processing... ")
|
||||||
|
|
||||||
|
_, err := services.CheckFrontValidRequest(w, r)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Http request error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := time.Now()
|
||||||
|
_, offset := t.Zone()
|
||||||
|
|
||||||
|
localTime := OMCLocalTime{
|
||||||
|
Timestamp: t.UnixMilli(),
|
||||||
|
TimeZone: offset,
|
||||||
|
}
|
||||||
|
response := services.DataResponse{
|
||||||
|
Data: localTime,
|
||||||
|
}
|
||||||
|
services.ResponseWithJson(w, http.StatusOK, response)
|
||||||
|
}
|
||||||
|
|
||||||
var dbConfig = config.GetYamlConfig().Database
|
var dbConfig = config.GetYamlConfig().Database
|
||||||
|
|
||||||
func DatabaseWhoreBackup() {
|
func DatabaseWhoreBackup() {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"ems.agt/lib/log"
|
"ems.agt/lib/log"
|
||||||
"ems.agt/lib/services"
|
"ems.agt/lib/services"
|
||||||
"ems.agt/restagent/config"
|
"ems.agt/restagent/config"
|
||||||
|
tokenConst "ems.agt/src/framework/constants/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CpuUsage struct {
|
type CpuUsage struct {
|
||||||
@@ -238,6 +239,7 @@ func GetOneLicenseInfoFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
|
||||||
SetHeaders(map[string]string{"accessToken": token}).
|
SetHeaders(map[string]string{"accessToken": token}).
|
||||||
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -342,6 +344,7 @@ func GetAllLicenseInfoFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
|
||||||
SetHeaders(map[string]string{"accessToken": token}).
|
SetHeaders(map[string]string{"accessToken": token}).
|
||||||
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -461,6 +464,7 @@ func GetOneSysinfoFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
|
||||||
SetHeaders(map[string]string{"accessToken": token}).
|
SetHeaders(map[string]string{"accessToken": token}).
|
||||||
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -594,6 +598,7 @@ func GetAllSysinfoFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
|
||||||
SetHeaders(map[string]string{"accessToken": token}).
|
SetHeaders(map[string]string{"accessToken": token}).
|
||||||
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -704,13 +709,14 @@ func GetStateFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
// query all NFs
|
// query all NFs
|
||||||
// create rest client
|
// create rest client
|
||||||
restHostPort := fmt.Sprintf("http://127.0.0.1:%d", config.GetYamlConfig().Rest[0].Port)
|
restHostPort := fmt.Sprintf("http://127.0.0.1:%d", config.GetYamlConfig().Rest[0].Port)
|
||||||
getNeInfoPattern := fmt.Sprintf(config.UriPrefix+"/databaseManagement/v1/elementType/%s/objectType/ne_info",
|
getNeInfoPattern := fmt.Sprintf(config.DefaultUriPrefix+"/databaseManagement/v1/elementType/%s/objectType/ne_info",
|
||||||
config.GetYamlConfig().Database.Name)
|
config.GetYamlConfig().Database.Name)
|
||||||
getNeInfoURI := restHostPort + getNeInfoPattern + "?WHERE=status='0'"
|
getNeInfoURI := restHostPort + getNeInfoPattern + "?WHERE=status='0'"
|
||||||
log.Debug("getNeInfoPattern:", getNeInfoPattern)
|
log.Debug("getNeInfoPattern:", getNeInfoPattern)
|
||||||
|
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
|
||||||
SetHeaders(map[string]string{"AccessToken": token}).
|
SetHeaders(map[string]string{"AccessToken": token}).
|
||||||
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -724,7 +730,7 @@ func GetStateFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
neList, _ = dborm.XormParseResult(resp.Body())
|
neList, _ = dborm.XormParseResult(resp.Body())
|
||||||
default:
|
default:
|
||||||
restHostPort := fmt.Sprintf("http://127.0.0.1:%d", config.GetYamlConfig().Rest[0].Port)
|
restHostPort := fmt.Sprintf("http://127.0.0.1:%d", config.GetYamlConfig().Rest[0].Port)
|
||||||
getNeInfoPattern := fmt.Sprintf(config.UriPrefix+"/databaseManagement/v1/elementType/%s/objectType/ne_info",
|
getNeInfoPattern := fmt.Sprintf(config.DefaultUriPrefix+"/databaseManagement/v1/elementType/%s/objectType/ne_info",
|
||||||
config.GetYamlConfig().Database.Name)
|
config.GetYamlConfig().Database.Name)
|
||||||
getNeInfoURI := restHostPort + getNeInfoPattern
|
getNeInfoURI := restHostPort + getNeInfoPattern
|
||||||
neId := services.GetUriParamString(r, "ne_id", ",", true, false)
|
neId := services.GetUriParamString(r, "ne_id", ",", true, false)
|
||||||
@@ -737,6 +743,7 @@ func GetStateFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
|
||||||
SetHeaders(map[string]string{"accessToken": token}).
|
SetHeaders(map[string]string{"accessToken": token}).
|
||||||
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -767,6 +774,7 @@ func GetStateFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
result["ipAddress"] = ne.Ip
|
result["ipAddress"] = ne.Ip
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
|
||||||
SetHeaders(map[string]string{"accessToken": token}).
|
SetHeaders(map[string]string{"accessToken": token}).
|
||||||
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ func (s *SysConfigApi) List(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysConfigApi) Info(w http.ResponseWriter, r *http.Request) {
|
func (s *SysConfigApi) Info(w http.ResponseWriter, r *http.Request) {
|
||||||
configId := ctx.Param(r, "configId")
|
configId := ctx.Param(r, "configId")
|
||||||
if configId == "" {
|
if configId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := s.sysConfigService.SelectConfigById(configId)
|
data := s.sysConfigService.SelectConfigById(configId)
|
||||||
@@ -123,14 +123,14 @@ func (s *SysConfigApi) Add(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body model.SysConfig
|
var body model.SysConfig
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.ConfigID != "" {
|
if err != nil || body.ConfigID != "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查属性值唯一
|
// 检查属性值唯一
|
||||||
uniqueConfigKey := s.sysConfigService.CheckUniqueConfigKey(body.ConfigKey, "")
|
uniqueConfigKey := s.sysConfigService.CheckUniqueConfigKey(body.ConfigKey, "")
|
||||||
if !uniqueConfigKey {
|
if !uniqueConfigKey {
|
||||||
msg := fmt.Sprintf("参数配置新增【%s】失败,参数键名已存在", body.ConfigKey)
|
msg := fmt.Sprintf("[%s] Parameter key name already exists", body.ConfigKey)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -151,14 +151,14 @@ func (s *SysConfigApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body model.SysConfig
|
var body model.SysConfig
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.ConfigID == "" {
|
if err != nil || body.ConfigID == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查属性值唯一
|
// 检查属性值唯一
|
||||||
uniqueConfigKey := s.sysConfigService.CheckUniqueConfigKey(body.ConfigKey, body.ConfigID)
|
uniqueConfigKey := s.sysConfigService.CheckUniqueConfigKey(body.ConfigKey, body.ConfigID)
|
||||||
if !uniqueConfigKey {
|
if !uniqueConfigKey {
|
||||||
msg := fmt.Sprintf("参数配置修改【%s】失败,参数键名已存在", body.ConfigKey)
|
msg := fmt.Sprintf("[%s] Parameter key name already exists", body.ConfigKey)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,7 @@ func (s *SysConfigApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 检查是否存在
|
// 检查是否存在
|
||||||
config := s.sysConfigService.SelectConfigById(body.ConfigID)
|
config := s.sysConfigService.SelectConfigById(body.ConfigID)
|
||||||
if config.ConfigID != body.ConfigID {
|
if config.ConfigID != body.ConfigID {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问参数配置数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access parameter configuration data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ func (s *SysConfigApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysConfigApi) Remove(w http.ResponseWriter, r *http.Request) {
|
func (s *SysConfigApi) Remove(w http.ResponseWriter, r *http.Request) {
|
||||||
configIds := ctx.Param(r, "configIds")
|
configIds := ctx.Param(r, "configIds")
|
||||||
if configIds == "" {
|
if configIds == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 处理字符转id数组后去重
|
// 处理字符转id数组后去重
|
||||||
@@ -218,7 +218,7 @@ func (s *SysConfigApi) RefreshCache(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysConfigApi) ConfigKey(w http.ResponseWriter, r *http.Request) {
|
func (s *SysConfigApi) ConfigKey(w http.ResponseWriter, r *http.Request) {
|
||||||
configKey := ctx.Param(r, "configKey")
|
configKey := ctx.Param(r, "configKey")
|
||||||
if configKey == "" {
|
if configKey == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
key := s.sysConfigService.SelectConfigValueByKey(configKey)
|
key := s.sysConfigService.SelectConfigValueByKey(configKey)
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ func (s *SysDictDataApi) List(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysDictDataApi) Info(w http.ResponseWriter, r *http.Request) {
|
func (s *SysDictDataApi) Info(w http.ResponseWriter, r *http.Request) {
|
||||||
dictCode := ctx.Param(r, "dictCode")
|
dictCode := ctx.Param(r, "dictCode")
|
||||||
if dictCode == "" {
|
if dictCode == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := s.sysDictDataService.SelectDictDataByCode(dictCode)
|
data := s.sysDictDataService.SelectDictDataByCode(dictCode)
|
||||||
@@ -121,21 +121,21 @@ func (s *SysDictDataApi) Add(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body model.SysDictData
|
var body model.SysDictData
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.DictCode != "" {
|
if err != nil || body.DictCode != "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查字典类型是否存在
|
// 检查字典类型是否存在
|
||||||
sysDictType := s.sysDictTypeService.SelectDictTypeByType(body.DictType)
|
sysDictType := s.sysDictTypeService.SelectDictTypeByType(body.DictType)
|
||||||
if sysDictType.DictType != body.DictType {
|
if sysDictType.DictType != body.DictType {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问字典类型数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access dictionary type data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查字典标签唯一
|
// 检查字典标签唯一
|
||||||
uniqueDictLabel := s.sysDictDataService.CheckUniqueDictLabel(body.DictType, body.DictLabel, "")
|
uniqueDictLabel := s.sysDictDataService.CheckUniqueDictLabel(body.DictType, body.DictLabel, "")
|
||||||
if !uniqueDictLabel {
|
if !uniqueDictLabel {
|
||||||
msg := fmt.Sprintf("数据新增【%s】失败,该字典类型下标签名已存在", body.DictLabel)
|
msg := fmt.Sprintf("[%s] The subscript signature of this dictionary type already exists", body.DictLabel)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -143,7 +143,7 @@ func (s *SysDictDataApi) Add(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 检查字典键值唯一
|
// 检查字典键值唯一
|
||||||
uniqueDictValue := s.sysDictDataService.CheckUniqueDictValue(body.DictType, body.DictValue, "")
|
uniqueDictValue := s.sysDictDataService.CheckUniqueDictValue(body.DictType, body.DictValue, "")
|
||||||
if !uniqueDictValue {
|
if !uniqueDictValue {
|
||||||
msg := fmt.Sprintf("数据新增【%s】失败,该字典类型下标签值已存在", body.DictValue)
|
msg := fmt.Sprintf("[%s] The label value under this dictionary type already exists", body.DictValue)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -164,28 +164,28 @@ func (s *SysDictDataApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body model.SysDictData
|
var body model.SysDictData
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.DictCode == "" {
|
if err != nil || body.DictCode == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查字典类型是否存在
|
// 检查字典类型是否存在
|
||||||
sysDictType := s.sysDictTypeService.SelectDictTypeByType(body.DictType)
|
sysDictType := s.sysDictTypeService.SelectDictTypeByType(body.DictType)
|
||||||
if sysDictType.DictType != body.DictType {
|
if sysDictType.DictType != body.DictType {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问字典类型数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access dictionary type data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查字典编码是否存在
|
// 检查字典编码是否存在
|
||||||
SysDictDataApi := s.sysDictDataService.SelectDictDataByCode(body.DictCode)
|
SysDictDataApi := s.sysDictDataService.SelectDictDataByCode(body.DictCode)
|
||||||
if SysDictDataApi.DictCode != body.DictCode {
|
if SysDictDataApi.DictCode != body.DictCode {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问字典编码数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access dictionary encoding data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查字典标签唯一
|
// 检查字典标签唯一
|
||||||
uniqueDictLabel := s.sysDictDataService.CheckUniqueDictLabel(body.DictType, body.DictLabel, body.DictCode)
|
uniqueDictLabel := s.sysDictDataService.CheckUniqueDictLabel(body.DictType, body.DictLabel, body.DictCode)
|
||||||
if !uniqueDictLabel {
|
if !uniqueDictLabel {
|
||||||
msg := fmt.Sprintf("数据修改【%s】失败,该字典类型下标签名已存在", body.DictLabel)
|
msg := fmt.Sprintf("Data modification failed for [%s], the dictionary type subscript signature already exists", body.DictLabel)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -193,7 +193,7 @@ func (s *SysDictDataApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 检查字典键值唯一
|
// 检查字典键值唯一
|
||||||
uniqueDictValue := s.sysDictDataService.CheckUniqueDictValue(body.DictType, body.DictValue, body.DictCode)
|
uniqueDictValue := s.sysDictDataService.CheckUniqueDictValue(body.DictType, body.DictValue, body.DictCode)
|
||||||
if !uniqueDictValue {
|
if !uniqueDictValue {
|
||||||
msg := fmt.Sprintf("数据修改【%s】失败,该字典类型下标签值已存在", body.DictValue)
|
msg := fmt.Sprintf("Data modification failed for [%s], label value already exists under this dictionary type", body.DictValue)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -213,7 +213,7 @@ func (s *SysDictDataApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysDictDataApi) Remove(w http.ResponseWriter, r *http.Request) {
|
func (s *SysDictDataApi) Remove(w http.ResponseWriter, r *http.Request) {
|
||||||
dictCodes := ctx.Param(r, "dictCodes")
|
dictCodes := ctx.Param(r, "dictCodes")
|
||||||
if dictCodes == "" {
|
if dictCodes == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 处理字符转id数组后去重
|
// 处理字符转id数组后去重
|
||||||
@@ -228,7 +228,7 @@ func (s *SysDictDataApi) Remove(w http.ResponseWriter, r *http.Request) {
|
|||||||
ctx.JSON(w, 200, result.ErrMsg(err.Error()))
|
ctx.JSON(w, 200, result.ErrMsg(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
msg := fmt.Sprintf("删除成功:%d", rows)
|
msg := fmt.Sprintf("Successfully deleted: %d", rows)
|
||||||
ctx.JSON(w, 200, result.OkMsg(msg))
|
ctx.JSON(w, 200, result.OkMsg(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +238,7 @@ func (s *SysDictDataApi) Remove(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysDictDataApi) DictType(w http.ResponseWriter, r *http.Request) {
|
func (s *SysDictDataApi) DictType(w http.ResponseWriter, r *http.Request) {
|
||||||
dictType := ctx.Param(r, "dictType")
|
dictType := ctx.Param(r, "dictType")
|
||||||
if dictType == "" {
|
if dictType == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ func (s *SysDictTypeApi) List(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysDictTypeApi) Info(w http.ResponseWriter, r *http.Request) {
|
func (s *SysDictTypeApi) Info(w http.ResponseWriter, r *http.Request) {
|
||||||
dictId := ctx.Param(r, "dictId")
|
dictId := ctx.Param(r, "dictId")
|
||||||
if dictId == "" {
|
if dictId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := s.sysDictTypeService.SelectDictTypeByID(dictId)
|
data := s.sysDictTypeService.SelectDictTypeByID(dictId)
|
||||||
@@ -123,14 +123,14 @@ func (s *SysDictTypeApi) Add(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body model.SysDictType
|
var body model.SysDictType
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.DictID != "" {
|
if err != nil || body.DictID != "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查字典名称唯一
|
// 检查字典名称唯一
|
||||||
uniqueDictName := s.sysDictTypeService.CheckUniqueDictName(body.DictName, "")
|
uniqueDictName := s.sysDictTypeService.CheckUniqueDictName(body.DictName, "")
|
||||||
if !uniqueDictName {
|
if !uniqueDictName {
|
||||||
msg := fmt.Sprintf("字典新增【%s】失败,字典名称已存在", body.DictName)
|
msg := fmt.Sprintf("Failed to add dictionary '%s', dictionary name already exists", body.DictName)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,7 @@ func (s *SysDictTypeApi) Add(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 检查字典类型唯一
|
// 检查字典类型唯一
|
||||||
uniqueDictType := s.sysDictTypeService.CheckUniqueDictType(body.DictType, "")
|
uniqueDictType := s.sysDictTypeService.CheckUniqueDictType(body.DictType, "")
|
||||||
if !uniqueDictType {
|
if !uniqueDictType {
|
||||||
msg := fmt.Sprintf("字典新增【%s】失败,字典类型已存在", body.DictType)
|
msg := fmt.Sprintf("Failed to add dictionary '%s', dictionary type already exists", body.DictType)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -159,21 +159,21 @@ func (s *SysDictTypeApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body model.SysDictType
|
var body model.SysDictType
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.DictID == "" {
|
if err != nil || body.DictID == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查数据是否存在
|
// 检查数据是否存在
|
||||||
dictInfo := s.sysDictTypeService.SelectDictTypeByID(body.DictID)
|
dictInfo := s.sysDictTypeService.SelectDictTypeByID(body.DictID)
|
||||||
if dictInfo.DictID != body.DictID {
|
if dictInfo.DictID != body.DictID {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问字典类型数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access dictionary type data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查字典名称唯一
|
// 检查字典名称唯一
|
||||||
uniqueDictName := s.sysDictTypeService.CheckUniqueDictName(body.DictName, body.DictID)
|
uniqueDictName := s.sysDictTypeService.CheckUniqueDictName(body.DictName, body.DictID)
|
||||||
if !uniqueDictName {
|
if !uniqueDictName {
|
||||||
msg := fmt.Sprintf("字典修改【%s】失败,字典名称已存在", body.DictName)
|
msg := fmt.Sprintf("Dictionary modification failed for [%s], dictionary name already exists", body.DictName)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -181,7 +181,7 @@ func (s *SysDictTypeApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 检查字典类型唯一
|
// 检查字典类型唯一
|
||||||
uniqueDictType := s.sysDictTypeService.CheckUniqueDictType(body.DictType, body.DictID)
|
uniqueDictType := s.sysDictTypeService.CheckUniqueDictType(body.DictType, body.DictID)
|
||||||
if !uniqueDictType {
|
if !uniqueDictType {
|
||||||
msg := fmt.Sprintf("字典修改【%s】失败,字典类型已存在", body.DictType)
|
msg := fmt.Sprintf("Dictionary modification failed for [%s], dictionary type already exists", body.DictType)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -201,7 +201,7 @@ func (s *SysDictTypeApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysDictTypeApi) Remove(w http.ResponseWriter, r *http.Request) {
|
func (s *SysDictTypeApi) Remove(w http.ResponseWriter, r *http.Request) {
|
||||||
dictIds := ctx.Param(r, "dictIds")
|
dictIds := ctx.Param(r, "dictIds")
|
||||||
if dictIds == "" {
|
if dictIds == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 处理字符转id数组后去重
|
// 处理字符转id数组后去重
|
||||||
@@ -216,7 +216,7 @@ func (s *SysDictTypeApi) Remove(w http.ResponseWriter, r *http.Request) {
|
|||||||
ctx.JSON(w, 200, result.ErrMsg(err.Error()))
|
ctx.JSON(w, 200, result.ErrMsg(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
msg := fmt.Sprintf("删除成功:%d", rows)
|
msg := fmt.Sprintf("Successfully deleted: %d", rows)
|
||||||
ctx.JSON(w, 200, result.OkMsg(msg))
|
ctx.JSON(w, 200, result.OkMsg(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import (
|
|||||||
"ems.agt/features/sys_menu/consts"
|
"ems.agt/features/sys_menu/consts"
|
||||||
"ems.agt/features/sys_menu/model"
|
"ems.agt/features/sys_menu/model"
|
||||||
"ems.agt/features/sys_menu/service"
|
"ems.agt/features/sys_menu/service"
|
||||||
"ems.agt/lib/core/conf"
|
|
||||||
"ems.agt/lib/core/utils/ctx"
|
"ems.agt/lib/core/utils/ctx"
|
||||||
"ems.agt/lib/core/utils/regular"
|
"ems.agt/lib/core/utils/regular"
|
||||||
"ems.agt/lib/core/vo/result"
|
"ems.agt/lib/core/vo/result"
|
||||||
"ems.agt/lib/midware"
|
"ems.agt/lib/midware"
|
||||||
"ems.agt/lib/services"
|
"ems.agt/lib/services"
|
||||||
"ems.agt/restagent/config"
|
"ems.agt/restagent/config"
|
||||||
|
srcConfig "ems.agt/src/framework/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 菜单接口添加到路由
|
// 菜单接口添加到路由
|
||||||
@@ -109,7 +109,7 @@ func (s *SysMenuApi) List(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userId := ctx.LoginUserToUserID(r)
|
userId := ctx.LoginUserToUserID(r)
|
||||||
if conf.IsAdmin(userId) {
|
if srcConfig.IsAdmin(userId) {
|
||||||
userId = "*"
|
userId = "*"
|
||||||
}
|
}
|
||||||
data := s.sysMenuService.SelectMenuList(query, userId)
|
data := s.sysMenuService.SelectMenuList(query, userId)
|
||||||
@@ -122,7 +122,7 @@ func (s *SysMenuApi) List(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysMenuApi) Info(w http.ResponseWriter, r *http.Request) {
|
func (s *SysMenuApi) Info(w http.ResponseWriter, r *http.Request) {
|
||||||
menuId := ctx.Param(r, "menuId")
|
menuId := ctx.Param(r, "menuId")
|
||||||
if menuId == "" {
|
if menuId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := s.sysMenuService.SelectMenuById(menuId)
|
data := s.sysMenuService.SelectMenuById(menuId)
|
||||||
@@ -140,7 +140,7 @@ func (s *SysMenuApi) Add(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body model.SysMenu
|
var body model.SysMenu
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.MenuID != "" {
|
if err != nil || body.MenuID != "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,13 +185,13 @@ func (s *SysMenuApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body model.SysMenu
|
var body model.SysMenu
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.MenuID == "" {
|
if err != nil || body.MenuID == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上级菜单不能选自己
|
// 上级菜单不能选自己
|
||||||
if body.MenuID == body.ParentID {
|
if body.MenuID == body.ParentID {
|
||||||
msg := fmt.Sprintf("菜单修改【%s】失败,上级菜单不能选择自己", body.MenuName)
|
msg := fmt.Sprintf("Menu modification failed for [%s], parent menu cannot select itself", body.MenuName)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -199,19 +199,19 @@ func (s *SysMenuApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 检查数据是否存在
|
// 检查数据是否存在
|
||||||
menuInfo := s.sysMenuService.SelectMenuById(body.MenuID)
|
menuInfo := s.sysMenuService.SelectMenuById(body.MenuID)
|
||||||
if menuInfo.MenuID != body.MenuID {
|
if menuInfo.MenuID != body.MenuID {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问菜单数据"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access menu data"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 父级ID不为0是要检查
|
// 父级ID不为0是要检查
|
||||||
if body.ParentID != "0" {
|
if body.ParentID != "0" {
|
||||||
menuParent := s.sysMenuService.SelectMenuById(body.ParentID)
|
menuParent := s.sysMenuService.SelectMenuById(body.ParentID)
|
||||||
if menuParent.MenuID != body.ParentID {
|
if menuParent.MenuID != body.ParentID {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问菜单数据"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access menu data"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 禁用菜单时检查父菜单是否使用
|
// 禁用菜单时检查父菜单是否使用
|
||||||
if body.Status == "1" && menuParent.Status == "0" {
|
if body.Status == "1" && menuParent.Status == "0" {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("父菜单未启用!"))
|
ctx.JSON(w, 200, result.ErrMsg("Parent menu not enabled!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,21 +266,21 @@ func (s *SysMenuApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysMenuApi) Remove(w http.ResponseWriter, r *http.Request) {
|
func (s *SysMenuApi) Remove(w http.ResponseWriter, r *http.Request) {
|
||||||
menuId := ctx.Param(r, "menuId")
|
menuId := ctx.Param(r, "menuId")
|
||||||
if menuId == "" {
|
if menuId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查数据是否存在
|
// 检查数据是否存在
|
||||||
menu := s.sysMenuService.SelectMenuById(menuId)
|
menu := s.sysMenuService.SelectMenuById(menuId)
|
||||||
if menu.MenuID != menuId {
|
if menu.MenuID != menuId {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问菜单数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access menu data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否存在子菜单
|
// 检查是否存在子菜单
|
||||||
hasChild := s.sysMenuService.HasChildByMenuIdAndStatus(menuId, "")
|
hasChild := s.sysMenuService.HasChildByMenuIdAndStatus(menuId, "")
|
||||||
if hasChild > 0 {
|
if hasChild > 0 {
|
||||||
msg := fmt.Sprintf("不允许删除,存在子菜单数:%d", hasChild)
|
msg := fmt.Sprintf("Deletion not allowed, there are sub orders: %d", hasChild)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -288,14 +288,14 @@ func (s *SysMenuApi) Remove(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 检查是否分配给角色
|
// 检查是否分配给角色
|
||||||
existRole := s.sysMenuService.CheckMenuExistRole(menuId)
|
existRole := s.sysMenuService.CheckMenuExistRole(menuId)
|
||||||
if existRole > 0 {
|
if existRole > 0 {
|
||||||
msg := fmt.Sprintf("不允许删除,菜单已分配给角色数:%d", existRole)
|
msg := fmt.Sprintf("Deletion not allowed, menu already assigned to roles: %d", existRole)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rows := s.sysMenuService.DeleteMenuById(menuId)
|
rows := s.sysMenuService.DeleteMenuById(menuId)
|
||||||
if rows > 0 {
|
if rows > 0 {
|
||||||
msg := fmt.Sprintf("删除成功:%d", rows)
|
msg := fmt.Sprintf("Successfully deleted: %d", rows)
|
||||||
ctx.JSON(w, 200, result.OkMsg(msg))
|
ctx.JSON(w, 200, result.OkMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ func (s *SysMenuApi) TreeSelect(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userId := ctx.LoginUserToUserID(r)
|
userId := ctx.LoginUserToUserID(r)
|
||||||
if conf.IsAdmin(userId) {
|
if srcConfig.IsAdmin(userId) {
|
||||||
userId = "*"
|
userId = "*"
|
||||||
}
|
}
|
||||||
data := s.sysMenuService.SelectMenuTreeSelectByUserId(query, userId)
|
data := s.sysMenuService.SelectMenuTreeSelectByUserId(query, userId)
|
||||||
@@ -329,7 +329,7 @@ func (s *SysMenuApi) TreeSelect(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysMenuApi) RoleMenuTreeSelect(w http.ResponseWriter, r *http.Request) {
|
func (s *SysMenuApi) RoleMenuTreeSelect(w http.ResponseWriter, r *http.Request) {
|
||||||
roleId := ctx.Param(r, "roleId")
|
roleId := ctx.Param(r, "roleId")
|
||||||
if roleId == "" {
|
if roleId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +342,7 @@ func (s *SysMenuApi) RoleMenuTreeSelect(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
userId := ctx.LoginUserToUserID(r)
|
userId := ctx.LoginUserToUserID(r)
|
||||||
if conf.IsAdmin(userId) {
|
if srcConfig.IsAdmin(userId) {
|
||||||
userId = "*"
|
userId = "*"
|
||||||
}
|
}
|
||||||
menuTreeSelect := s.sysMenuService.SelectMenuTreeSelectByUserId(query, userId)
|
menuTreeSelect := s.sysMenuService.SelectMenuTreeSelectByUserId(query, userId)
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ func (s *SysRoleApi) List(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysRoleApi) Info(w http.ResponseWriter, r *http.Request) {
|
func (s *SysRoleApi) Info(w http.ResponseWriter, r *http.Request) {
|
||||||
roleId := ctx.Param(r, "roleId")
|
roleId := ctx.Param(r, "roleId")
|
||||||
if roleId == "" {
|
if roleId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := s.sysRoleService.SelectRoleById(roleId)
|
data := s.sysRoleService.SelectRoleById(roleId)
|
||||||
@@ -139,14 +139,14 @@ func (s *SysRoleApi) Add(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body model.SysRole
|
var body model.SysRole
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.RoleID != "" {
|
if err != nil || body.RoleID != "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断角色名称是否唯一
|
// 判断角色名称是否唯一
|
||||||
uniqueRoleName := s.sysRoleService.CheckUniqueRoleName(body.RoleName, "")
|
uniqueRoleName := s.sysRoleService.CheckUniqueRoleName(body.RoleName, "")
|
||||||
if !uniqueRoleName {
|
if !uniqueRoleName {
|
||||||
msg := fmt.Sprintf("角色新增【%s】失败,角色名称已存在", body.RoleName)
|
msg := fmt.Sprintf("[%s] Role name already exists", body.RoleName)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ func (s *SysRoleApi) Add(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 判断角色键值是否唯一
|
// 判断角色键值是否唯一
|
||||||
uniqueRoleKey := s.sysRoleService.CheckUniqueRoleKey(body.RoleKey, "")
|
uniqueRoleKey := s.sysRoleService.CheckUniqueRoleKey(body.RoleKey, "")
|
||||||
if !uniqueRoleKey {
|
if !uniqueRoleKey {
|
||||||
msg := fmt.Sprintf("角色新增【%s】失败,角色键值已存在", body.RoleName)
|
msg := fmt.Sprintf("[%s] The role key value already exists", body.RoleName)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -175,27 +175,27 @@ func (s *SysRoleApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body model.SysRole
|
var body model.SysRole
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.RoleID == "" {
|
if err != nil || body.RoleID == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否管理员角色
|
// 检查是否管理员角色
|
||||||
if body.RoleID == "1" {
|
if body.RoleID == "1" {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("不允许操作管理员角色"))
|
ctx.JSON(w, 200, result.ErrMsg("Operation of administrator role is not allowed"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否存在
|
// 检查是否存在
|
||||||
role := s.sysRoleService.SelectRoleById(body.RoleID)
|
role := s.sysRoleService.SelectRoleById(body.RoleID)
|
||||||
if role.RoleID != body.RoleID {
|
if role.RoleID != body.RoleID {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问角色数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access role data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断角色名称是否唯一
|
// 判断角色名称是否唯一
|
||||||
uniqueRoleName := s.sysRoleService.CheckUniqueRoleName(body.RoleName, body.RoleID)
|
uniqueRoleName := s.sysRoleService.CheckUniqueRoleName(body.RoleName, body.RoleID)
|
||||||
if !uniqueRoleName {
|
if !uniqueRoleName {
|
||||||
msg := fmt.Sprintf("角色修改【%s】失败,角色名称已存在", body.RoleName)
|
msg := fmt.Sprintf("[%s] Role name already exists", body.RoleName)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ func (s *SysRoleApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 判断角色键值是否唯一
|
// 判断角色键值是否唯一
|
||||||
uniqueRoleKey := s.sysRoleService.CheckUniqueRoleKey(body.RoleKey, body.RoleID)
|
uniqueRoleKey := s.sysRoleService.CheckUniqueRoleKey(body.RoleKey, body.RoleID)
|
||||||
if !uniqueRoleKey {
|
if !uniqueRoleKey {
|
||||||
msg := fmt.Sprintf("角色修改【%s】失败,角色键值已存在", body.RoleName)
|
msg := fmt.Sprintf("[%s] The role key value already exists", body.RoleName)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ func (s *SysRoleApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysRoleApi) Remove(w http.ResponseWriter, r *http.Request) {
|
func (s *SysRoleApi) Remove(w http.ResponseWriter, r *http.Request) {
|
||||||
roleIds := ctx.Param(r, "roleIds")
|
roleIds := ctx.Param(r, "roleIds")
|
||||||
if roleIds == "" {
|
if roleIds == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 处理字符转id数组后去重
|
// 处理字符转id数组后去重
|
||||||
@@ -236,7 +236,7 @@ func (s *SysRoleApi) Remove(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 检查是否管理员角色
|
// 检查是否管理员角色
|
||||||
for _, id := range uniqueIDs {
|
for _, id := range uniqueIDs {
|
||||||
if id == "1" {
|
if id == "1" {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("不允许操作管理员角色"))
|
ctx.JSON(w, 200, result.ErrMsg("Operation of administrator role is not allowed"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,7 +245,7 @@ func (s *SysRoleApi) Remove(w http.ResponseWriter, r *http.Request) {
|
|||||||
ctx.JSON(w, 200, result.ErrMsg(err.Error()))
|
ctx.JSON(w, 200, result.ErrMsg(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
msg := fmt.Sprintf("删除成功:%d", rows)
|
msg := fmt.Sprintf("Successfully deleted: %d", rows)
|
||||||
ctx.JSON(w, 200, result.OkMsg(msg))
|
ctx.JSON(w, 200, result.OkMsg(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,26 +261,26 @@ func (s *SysRoleApi) Status(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否管理员角色
|
// 检查是否管理员角色
|
||||||
if body.RoleID == "1" {
|
if body.RoleID == "1" {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("不允许操作管理员角色"))
|
ctx.JSON(w, 200, result.ErrMsg("Operation of administrator role is not allowed"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否存在
|
// 检查是否存在
|
||||||
role := s.sysRoleService.SelectRoleById(body.RoleID)
|
role := s.sysRoleService.SelectRoleById(body.RoleID)
|
||||||
if role.RoleID != body.RoleID {
|
if role.RoleID != body.RoleID {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问角色数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access role data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 与旧值相等不变更
|
// 与旧值相等不变更
|
||||||
if role.Status == body.Status {
|
if role.Status == body.Status {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("变更状态与旧值相等!"))
|
ctx.JSON(w, 200, result.ErrMsg("Change status equals old value!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,14 +306,14 @@ func (s *SysRoleApi) AuthUserAllocatedList(w http.ResponseWriter, r *http.Reques
|
|||||||
querys := ctx.QueryMap(r)
|
querys := ctx.QueryMap(r)
|
||||||
roleId, ok := querys["roleId"]
|
roleId, ok := querys["roleId"]
|
||||||
if !ok || roleId == "" {
|
if !ok || roleId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否存在
|
// 检查是否存在
|
||||||
role := s.sysRoleService.SelectRoleById(roleId.(string))
|
role := s.sysRoleService.SelectRoleById(roleId.(string))
|
||||||
if role.RoleID != roleId {
|
if role.RoleID != roleId {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问角色数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access role data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,7 +335,7 @@ func (s *SysRoleApi) AuthUserChecked(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +350,7 @@ func (s *SysRoleApi) AuthUserChecked(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 检查是否存在
|
// 检查是否存在
|
||||||
role := s.sysRoleService.SelectRoleById(body.RoleID)
|
role := s.sysRoleService.SelectRoleById(body.RoleID)
|
||||||
if role.RoleID != body.RoleID {
|
if role.RoleID != body.RoleID {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问角色数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access role data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import (
|
|||||||
sysRoleService "ems.agt/features/sys_role/service"
|
sysRoleService "ems.agt/features/sys_role/service"
|
||||||
sysUserModel "ems.agt/features/sys_user/model"
|
sysUserModel "ems.agt/features/sys_user/model"
|
||||||
"ems.agt/features/sys_user/service"
|
"ems.agt/features/sys_user/service"
|
||||||
"ems.agt/lib/core/conf"
|
|
||||||
"ems.agt/lib/core/utils/ctx"
|
"ems.agt/lib/core/utils/ctx"
|
||||||
"ems.agt/lib/core/utils/parse"
|
"ems.agt/lib/core/utils/parse"
|
||||||
"ems.agt/lib/core/vo/result"
|
"ems.agt/lib/core/vo/result"
|
||||||
"ems.agt/lib/midware"
|
"ems.agt/lib/midware"
|
||||||
"ems.agt/lib/services"
|
"ems.agt/lib/services"
|
||||||
"ems.agt/restagent/config"
|
"ems.agt/restagent/config"
|
||||||
|
srcConfig "ems.agt/src/framework/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 用户接口添加到路由
|
// 用户接口添加到路由
|
||||||
@@ -107,14 +107,14 @@ func (s *SysUserApi) List(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysUserApi) Info(w http.ResponseWriter, r *http.Request) {
|
func (s *SysUserApi) Info(w http.ResponseWriter, r *http.Request) {
|
||||||
userId := ctx.Param(r, "userId")
|
userId := ctx.Param(r, "userId")
|
||||||
if userId == "" {
|
if userId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 查询系统角色列表
|
// 查询系统角色列表
|
||||||
roles := s.sysRoleService.SelectRoleList(sysRoleModel.SysRole{})
|
roles := s.sysRoleService.SelectRoleList(sysRoleModel.SysRole{})
|
||||||
|
|
||||||
// 不是系统指定管理员需要排除其角色
|
// 不是系统指定管理员需要排除其角色
|
||||||
if !conf.IsAdmin(userId) {
|
if !srcConfig.IsAdmin(userId) {
|
||||||
rolesFilter := make([]sysRoleModel.SysRole, 0)
|
rolesFilter := make([]sysRoleModel.SysRole, 0)
|
||||||
for _, r := range roles {
|
for _, r := range roles {
|
||||||
if r.RoleID != "1" {
|
if r.RoleID != "1" {
|
||||||
@@ -137,7 +137,7 @@ func (s *SysUserApi) Info(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 检查用户是否存在
|
// 检查用户是否存在
|
||||||
user := s.sysUserService.SelectUserById(userId)
|
user := s.sysUserService.SelectUserById(userId)
|
||||||
if user.Id != userId {
|
if user.Id != userId {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问用户数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access user data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,14 +161,14 @@ func (s *SysUserApi) Add(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body sysUserModel.SysUser
|
var body sysUserModel.SysUser
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.Id != "" {
|
if err != nil || body.Id != "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查用户登录账号是否唯一
|
// 检查用户登录账号是否唯一
|
||||||
uniqueUserName := s.sysUserService.CheckUniqueUserName(body.AccountId, "")
|
uniqueUserName := s.sysUserService.CheckUniqueUserName(body.AccountId, "")
|
||||||
if !uniqueUserName {
|
if !uniqueUserName {
|
||||||
msg := fmt.Sprintf("新增用户【%s】失败,登录账号已存在", body.AccountId)
|
msg := fmt.Sprintf("[%s] Login account already exists", body.AccountId)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -188,26 +188,26 @@ func (s *SysUserApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
var body sysUserModel.SysUser
|
var body sysUserModel.SysUser
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.Id == "" {
|
if err != nil || body.Id == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否管理员用户
|
// 检查是否管理员用户
|
||||||
// if conf.IsAdmin(body.Id) {
|
// if srcConfig.IsAdmin(body.Id) {
|
||||||
// ctx.JSON(w, 200, result.ErrMsg("不允许操作管理员用户"))
|
// ctx.JSON(w, 200, result.ErrMsg("不允许操作管理员用户"))
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
user := s.sysUserService.SelectUserById(body.Id)
|
user := s.sysUserService.SelectUserById(body.Id)
|
||||||
if user.Id != body.Id {
|
if user.Id != body.Id {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问用户数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access user data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查用户登录账号是否唯一
|
// 检查用户登录账号是否唯一
|
||||||
uniqueUserName := s.sysUserService.CheckUniqueUserName(body.AccountId, body.Id)
|
uniqueUserName := s.sysUserService.CheckUniqueUserName(body.AccountId, body.Id)
|
||||||
if !uniqueUserName {
|
if !uniqueUserName {
|
||||||
msg := fmt.Sprintf("修改用户【%s】失败,登录账号已存在", body.AccountId)
|
msg := fmt.Sprintf("[%s] Login account already exists", body.AccountId)
|
||||||
ctx.JSON(w, 200, result.ErrMsg(msg))
|
ctx.JSON(w, 200, result.ErrMsg(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,7 @@ func (s *SysUserApi) Edit(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *SysUserApi) Remove(w http.ResponseWriter, r *http.Request) {
|
func (s *SysUserApi) Remove(w http.ResponseWriter, r *http.Request) {
|
||||||
userIds := ctx.Param(r, "userIds")
|
userIds := ctx.Param(r, "userIds")
|
||||||
if userIds == "" {
|
if userIds == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 处理字符转id数组后去重
|
// 处理字符转id数组后去重
|
||||||
@@ -243,7 +243,7 @@ func (s *SysUserApi) Remove(w http.ResponseWriter, r *http.Request) {
|
|||||||
ctx.JSON(w, 200, result.ErrMsg(err.Error()))
|
ctx.JSON(w, 200, result.ErrMsg(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
msg := fmt.Sprintf("删除成功:%d", rows)
|
msg := fmt.Sprintf("Successfully deleted: %d", rows)
|
||||||
ctx.JSON(w, 200, result.OkMsg(msg))
|
ctx.JSON(w, 200, result.OkMsg(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,19 +256,19 @@ func (s *SysUserApi) ResetPwd(w http.ResponseWriter, r *http.Request) {
|
|||||||
Password string `json:"password" binding:"required"`
|
Password string `json:"password" binding:"required"`
|
||||||
}
|
}
|
||||||
if err := ctx.ShouldBindJSON(r, &body); err != nil {
|
if err := ctx.ShouldBindJSON(r, &body); err != nil {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否管理员用户
|
// 检查是否管理员用户
|
||||||
if conf.IsAdmin(body.UserID) {
|
if srcConfig.IsAdmin(body.UserID) {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("不允许操作管理员用户"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access user data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user := s.sysUserService.SelectUserById(body.UserID)
|
user := s.sysUserService.SelectUserById(body.UserID)
|
||||||
if user.Id != body.UserID {
|
if user.Id != body.UserID {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问用户数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access user data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,20 +293,20 @@ func (s *SysUserApi) Status(w http.ResponseWriter, r *http.Request) {
|
|||||||
Status string `json:"status" binding:"required"`
|
Status string `json:"status" binding:"required"`
|
||||||
}
|
}
|
||||||
if err := ctx.ShouldBindJSON(r, &body); err != nil {
|
if err := ctx.ShouldBindJSON(r, &body); err != nil {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否存在
|
// 检查是否存在
|
||||||
user := s.sysUserService.SelectUserById(body.UserID)
|
user := s.sysUserService.SelectUserById(body.UserID)
|
||||||
if user.Id != body.UserID {
|
if user.Id != body.UserID {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("没有权限访问用户数据!"))
|
ctx.JSON(w, 200, result.ErrMsg("No permission to access user data!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 与旧值相等不变更
|
// 与旧值相等不变更
|
||||||
if user.Status == body.Status {
|
if user.Status == body.Status {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("变更状态与旧值相等!"))
|
ctx.JSON(w, 200, result.ErrMsg("Change status equals old value!"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import (
|
|||||||
sysRoleModel "ems.agt/features/sys_role/model"
|
sysRoleModel "ems.agt/features/sys_role/model"
|
||||||
sysUserModel "ems.agt/features/sys_user/model"
|
sysUserModel "ems.agt/features/sys_user/model"
|
||||||
"ems.agt/lib/core/datasource"
|
"ems.agt/lib/core/datasource"
|
||||||
"ems.agt/lib/core/utils/crypto"
|
|
||||||
"ems.agt/lib/core/utils/date"
|
"ems.agt/lib/core/utils/date"
|
||||||
"ems.agt/lib/core/utils/parse"
|
"ems.agt/lib/core/utils/parse"
|
||||||
"ems.agt/lib/log"
|
"ems.agt/lib/log"
|
||||||
|
"ems.agt/src/framework/utils/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 实例化数据层 RepoSysUser 结构体
|
// 实例化数据层 RepoSysUser 结构体
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func TcpdumpNeTask(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.NeType == "" || body.NeId == "" || body.Timeout < 5 || body.Cmd == "" {
|
if err != nil || body.NeType == "" || body.NeId == "" || body.Timeout < 5 || body.Cmd == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ func TcpdumpPcapDownload(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.NeType == "" || body.NeId == "" || body.FileName == "" {
|
if err != nil || body.NeType == "" || body.NeId == "" || body.FileName == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ func TcpdumpNeUPFTask(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.NeType != "UPF" || body.NeId == "" || body.Cmd == "" {
|
if err != nil || body.NeType != "UPF" || body.NeId == "" || body.Cmd == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ func (s *UdmUserApi) UdmAuthUserList(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *UdmUserApi) UdmAuthUserSave(w http.ResponseWriter, r *http.Request) {
|
func (s *UdmUserApi) UdmAuthUserSave(w http.ResponseWriter, r *http.Request) {
|
||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
if neId == "" {
|
if neId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +244,7 @@ func (s *UdmUserApi) UdmAuthUserInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
imsi := ctx.Param(r, "imsi")
|
imsi := ctx.Param(r, "imsi")
|
||||||
if neId == "" || imsi == "" {
|
if neId == "" || imsi == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,14 +290,14 @@ func (s *UdmUserApi) UdmAuthUserInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *UdmUserApi) UdmAuthUserAdd(w http.ResponseWriter, r *http.Request) {
|
func (s *UdmUserApi) UdmAuthUserAdd(w http.ResponseWriter, r *http.Request) {
|
||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
if neId == "" {
|
if neId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var body model.UdmAuthUser
|
var body model.UdmAuthUser
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.Imsi == "" {
|
if err != nil || body.Imsi == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,14 +331,14 @@ func (s *UdmUserApi) UdmAuthUserAdds(w http.ResponseWriter, r *http.Request) {
|
|||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
num := ctx.Param(r, "num")
|
num := ctx.Param(r, "num")
|
||||||
if neId == "" || num == "" {
|
if neId == "" || num == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var body model.UdmAuthUser
|
var body model.UdmAuthUser
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.Imsi == "" {
|
if err != nil || body.Imsi == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,14 +371,14 @@ func (s *UdmUserApi) UdmAuthUserAdds(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *UdmUserApi) UdmAuthUserEdit(w http.ResponseWriter, r *http.Request) {
|
func (s *UdmUserApi) UdmAuthUserEdit(w http.ResponseWriter, r *http.Request) {
|
||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
if neId == "" {
|
if neId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var body model.UdmAuthUser
|
var body model.UdmAuthUser
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.Imsi == "" {
|
if err != nil || body.Imsi == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,7 +425,7 @@ func (s *UdmUserApi) UdmAuthUserRemove(w http.ResponseWriter, r *http.Request) {
|
|||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
imsi := ctx.Param(r, "imsi")
|
imsi := ctx.Param(r, "imsi")
|
||||||
if neId == "" || imsi == "" {
|
if neId == "" || imsi == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,7 +460,7 @@ func (s *UdmUserApi) UdmAuthUserRemoves(w http.ResponseWriter, r *http.Request)
|
|||||||
imsi := ctx.Param(r, "imsi")
|
imsi := ctx.Param(r, "imsi")
|
||||||
num := ctx.Param(r, "num")
|
num := ctx.Param(r, "num")
|
||||||
if neId == "" || imsi == "" || num == "" {
|
if neId == "" || imsi == "" || num == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,16 +497,16 @@ func (s *UdmUserApi) UdmAuthUserExport(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.NeId == "" || body.Type == "" {
|
if err != nil || body.NeId == "" || body.Type == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(body.Type == "csv" || body.Type == "txt") {
|
if !(body.Type == "csv" || body.Type == "txt") {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("导出文件类型支持csv、txt"))
|
ctx.JSON(w, 200, result.ErrMsg("Export file types support CSV and txt"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
neId := "-"
|
neId := ""
|
||||||
list := s.authUser.List(model.UdmAuthUser{NeID: neId})
|
list := s.authUser.List(model.UdmAuthUser{NeID: neId})
|
||||||
// 文件名
|
// 文件名
|
||||||
fileName := fmt.Sprintf("OMC_AUTH_USER_EXPORT_%s_%d.%s", neId, time.Now().UnixMilli(), body.Type)
|
fileName := fmt.Sprintf("OMC_AUTH_USER_EXPORT_%s_%d.%s", neId, time.Now().UnixMilli(), body.Type)
|
||||||
@@ -554,7 +554,7 @@ func (s *UdmUserApi) UdmAuthUserExport(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *UdmUserApi) UdmAuthUserImport(w http.ResponseWriter, r *http.Request) {
|
func (s *UdmUserApi) UdmAuthUserImport(w http.ResponseWriter, r *http.Request) {
|
||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
if neId == "" {
|
if neId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,7 +565,7 @@ func (s *UdmUserApi) UdmAuthUserImport(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !(strings.HasSuffix(fileHeader.Filename, ".csv") || strings.HasSuffix(fileHeader.Filename, ".txt")) {
|
if !(strings.HasSuffix(fileHeader.Filename, ".csv") || strings.HasSuffix(fileHeader.Filename, ".txt")) {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("请上传.csv、.txt格式文件,内容字段imsi,ki,algo,amf,opc"))
|
ctx.JSON(w, 200, result.ErrMsg("Please upload files in. csv and. txt formats with content fields: imsi,ki,algo,amf,opc"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,7 +634,7 @@ func (s *UdmUserApi) UdmSubUserList(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *UdmUserApi) UdmSubUserSave(w http.ResponseWriter, r *http.Request) {
|
func (s *UdmUserApi) UdmSubUserSave(w http.ResponseWriter, r *http.Request) {
|
||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
if neId == "" {
|
if neId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,7 +650,7 @@ func (s *UdmUserApi) UdmSubUserInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
imsi := ctx.Param(r, "imsi")
|
imsi := ctx.Param(r, "imsi")
|
||||||
if neId == "" || imsi == "" {
|
if neId == "" || imsi == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,14 +714,14 @@ func (s *UdmUserApi) UdmSubUserInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *UdmUserApi) UdmSubUserAdd(w http.ResponseWriter, r *http.Request) {
|
func (s *UdmUserApi) UdmSubUserAdd(w http.ResponseWriter, r *http.Request) {
|
||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
if neId == "" {
|
if neId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var body model.UdmSubUser
|
var body model.UdmSubUser
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.Imsi == "" {
|
if err != nil || body.Imsi == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -760,14 +760,14 @@ func (s *UdmUserApi) UdmSubUserAdds(w http.ResponseWriter, r *http.Request) {
|
|||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
num := ctx.Param(r, "num")
|
num := ctx.Param(r, "num")
|
||||||
if neId == "" || num == "" {
|
if neId == "" || num == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var body model.UdmSubUser
|
var body model.UdmSubUser
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.Imsi == "" {
|
if err != nil || body.Imsi == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -805,14 +805,14 @@ func (s *UdmUserApi) UdmSubUserAdds(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *UdmUserApi) UdmSubUserAdd4G(w http.ResponseWriter, r *http.Request) {
|
func (s *UdmUserApi) UdmSubUserAdd4G(w http.ResponseWriter, r *http.Request) {
|
||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
if neId == "" {
|
if neId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var body model.UdmSubUser
|
var body model.UdmSubUser
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.Imsi == "" || body.SubNum == "" {
|
if err != nil || body.Imsi == "" || body.SubNum == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -845,14 +845,14 @@ func (s *UdmUserApi) UdmSubUserAdd4G(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *UdmUserApi) UdmSubUserEdit(w http.ResponseWriter, r *http.Request) {
|
func (s *UdmUserApi) UdmSubUserEdit(w http.ResponseWriter, r *http.Request) {
|
||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
if neId == "" {
|
if neId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var body model.UdmSubUser
|
var body model.UdmSubUser
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.Imsi == "" {
|
if err != nil || body.Imsi == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -940,14 +940,14 @@ func (s *UdmUserApi) UdmSubUserEdit(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *UdmUserApi) UdmSubUser4GIP(w http.ResponseWriter, r *http.Request) {
|
func (s *UdmUserApi) UdmSubUser4GIP(w http.ResponseWriter, r *http.Request) {
|
||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
if neId == "" {
|
if neId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var body model.UdmSubUser
|
var body model.UdmSubUser
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.Imsi == "" || body.SubNum == "" || body.StaticIp == "" {
|
if err != nil || body.Imsi == "" || body.SubNum == "" || body.StaticIp == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -979,14 +979,14 @@ func (s *UdmUserApi) UdmSubUser4GIP(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *UdmUserApi) UdmSubUserSmData(w http.ResponseWriter, r *http.Request) {
|
func (s *UdmUserApi) UdmSubUserSmData(w http.ResponseWriter, r *http.Request) {
|
||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
if neId == "" {
|
if neId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var body model.UdmSubUser
|
var body model.UdmSubUser
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.Imsi == "" || body.SubNum == "" || body.SmData == "" {
|
if err != nil || body.Imsi == "" || body.SubNum == "" || body.SmData == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1020,7 +1020,7 @@ func (s *UdmUserApi) UdmSubUserRemove(w http.ResponseWriter, r *http.Request) {
|
|||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
imsi := ctx.Param(r, "imsi")
|
imsi := ctx.Param(r, "imsi")
|
||||||
if neId == "" || imsi == "" {
|
if neId == "" || imsi == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1055,7 +1055,7 @@ func (s *UdmUserApi) UdmSubUserRemoves(w http.ResponseWriter, r *http.Request) {
|
|||||||
imsi := ctx.Param(r, "imsi")
|
imsi := ctx.Param(r, "imsi")
|
||||||
num := ctx.Param(r, "num")
|
num := ctx.Param(r, "num")
|
||||||
if neId == "" || imsi == "" || num == "" {
|
if neId == "" || imsi == "" || num == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1092,16 +1092,16 @@ func (s *UdmUserApi) UdmSubUserExport(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
err := ctx.ShouldBindJSON(r, &body)
|
err := ctx.ShouldBindJSON(r, &body)
|
||||||
if err != nil || body.NeId == "" || body.Type == "" {
|
if err != nil || body.NeId == "" || body.Type == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(body.Type == "csv" || body.Type == "txt") {
|
if !(body.Type == "csv" || body.Type == "txt") {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("导出文件类型支持csv、txt"))
|
ctx.JSON(w, 200, result.ErrMsg("Export file type support csv、txt"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
neId := "-"
|
neId := ""
|
||||||
list := s.subUser.List(model.UdmSubUser{NeID: neId})
|
list := s.subUser.List(model.UdmSubUser{NeID: neId})
|
||||||
// 文件名
|
// 文件名
|
||||||
fileName := fmt.Sprintf("OMC_AUTH_USER_EXPORT_%s_%d.%s", neId, time.Now().UnixMilli(), body.Type)
|
fileName := fmt.Sprintf("OMC_AUTH_USER_EXPORT_%s_%d.%s", neId, time.Now().UnixMilli(), body.Type)
|
||||||
@@ -1145,7 +1145,7 @@ func (s *UdmUserApi) UdmSubUserExport(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (s *UdmUserApi) UdmSubUserImport(w http.ResponseWriter, r *http.Request) {
|
func (s *UdmUserApi) UdmSubUserImport(w http.ResponseWriter, r *http.Request) {
|
||||||
neId := ctx.Param(r, "neId")
|
neId := ctx.Param(r, "neId")
|
||||||
if neId == "" {
|
if neId == "" {
|
||||||
ctx.JSON(w, 400, result.CodeMsg(400, "参数错误"))
|
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1156,7 +1156,7 @@ func (s *UdmUserApi) UdmSubUserImport(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !(strings.HasSuffix(fileHeader.Filename, ".csv") || strings.HasSuffix(fileHeader.Filename, ".txt")) {
|
if !(strings.HasSuffix(fileHeader.Filename, ".csv") || strings.HasSuffix(fileHeader.Filename, ".txt")) {
|
||||||
ctx.JSON(w, 200, result.ErrMsg("请上传.csv、.txt格式文件,内容字段imsi,msisdn,ambr,nssai,arfb,sar,rat,cn,smf_sel,sm_dat,eps_dat"))
|
ctx.JSON(w, 200, result.ErrMsg("Please upload files in. csv and. txt formats with content fields: imsi,msisdn,ambr,nssai,arfb,sar,rat,cn,smf_sel,sm_dat,eps_dat"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"ems.agt/features/udm_user/model"
|
"ems.agt/features/udm_user/model"
|
||||||
"ems.agt/lib/core/redis"
|
"ems.agt/src/framework/redis"
|
||||||
)
|
)
|
||||||
|
|
||||||
// phoneImsiList 获取所有imsi
|
// phoneImsiList 获取所有imsi
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"ems.agt/lib/log"
|
"ems.agt/lib/log"
|
||||||
"ems.agt/lib/services"
|
"ems.agt/lib/services"
|
||||||
"ems.agt/restagent/config"
|
"ems.agt/restagent/config"
|
||||||
|
tokenConst "ems.agt/src/framework/constants/token"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
@@ -129,6 +130,7 @@ func GetUEInfoFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
|
||||||
SetHeaders(map[string]string{"accessToken": token}).
|
SetHeaders(map[string]string{"accessToken": token}).
|
||||||
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -192,6 +194,7 @@ func GetUENumFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
|
||||||
SetHeaders(map[string]string{"accessToken": token}).
|
SetHeaders(map[string]string{"accessToken": token}).
|
||||||
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -255,6 +258,7 @@ func GetNBInfoFromNF(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
|
||||||
SetHeaders(map[string]string{"accessToken": token}).
|
SetHeaders(map[string]string{"accessToken": token}).
|
||||||
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
|
|||||||
39
go.mod
39
go.mod
@@ -4,13 +4,18 @@ go 1.20
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/go-admin-team/go-admin-core v1.3.12-0.20221121065133-27b7dbe27a8f
|
github.com/dlclark/regexp2 v1.10.0
|
||||||
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/go-resty/resty/v2 v2.7.0
|
github.com/go-resty/resty/v2 v2.7.0
|
||||||
github.com/go-sql-driver/mysql v1.7.1
|
github.com/go-sql-driver/mysql v1.7.1
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.0.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/gosnmp/gosnmp v1.35.0
|
github.com/gosnmp/gosnmp v1.35.0
|
||||||
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f
|
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f
|
||||||
|
github.com/matoous/go-nanoid/v2 v2.0.0
|
||||||
|
github.com/mojocn/base64Captcha v1.3.5
|
||||||
|
github.com/mssola/user_agent v0.6.0
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/redis/go-redis/v9 v9.1.0
|
github.com/redis/go-redis/v9 v9.1.0
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
@@ -18,30 +23,38 @@ require (
|
|||||||
github.com/shirou/gopsutil/v3 v3.23.7
|
github.com/shirou/gopsutil/v3 v3.23.7
|
||||||
github.com/spf13/afero v1.9.5
|
github.com/spf13/afero v1.9.5
|
||||||
github.com/spf13/viper v1.16.0
|
github.com/spf13/viper v1.16.0
|
||||||
|
github.com/xuri/excelize/v2 v2.7.1
|
||||||
golang.org/x/crypto v0.12.0
|
golang.org/x/crypto v0.12.0
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
gorm.io/driver/mysql v1.5.1
|
||||||
|
gorm.io/gorm v1.25.2
|
||||||
xorm.io/xorm v1.3.2
|
xorm.io/xorm v1.3.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/mattn/go-sqlite3 v1.14.15 // indirect
|
github.com/bytedance/sonic v1.9.1 // indirect
|
||||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
|
||||||
github.com/onsi/gomega v1.21.1 // indirect
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
|
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
|
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||||
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect
|
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect
|
||||||
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
|
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
@@ -50,24 +63,32 @@ require (
|
|||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/mojocn/base64Captcha v1.3.5
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
|
github.com/richardlehane/mscfb v1.0.4 // indirect
|
||||||
|
github.com/richardlehane/msoleps v1.0.3 // indirect
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
github.com/spf13/cast v1.5.1 // indirect
|
github.com/spf13/cast v1.5.1 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/subosito/gotenv v1.4.2 // indirect
|
github.com/subosito/gotenv v1.4.2 // indirect
|
||||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||||
github.com/tebeka/strftime v0.1.5 // indirect
|
github.com/tebeka/strftime v0.1.5 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
|
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect
|
||||||
|
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||||
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
golang.org/x/image v0.5.0 // indirect
|
golang.org/x/image v0.5.0 // indirect
|
||||||
golang.org/x/net v0.10.0 // indirect
|
golang.org/x/net v0.10.0 // indirect
|
||||||
golang.org/x/sys v0.11.0 // indirect
|
golang.org/x/sys v0.11.0 // indirect
|
||||||
golang.org/x/text v0.12.0 // indirect
|
golang.org/x/text v0.12.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
|||||||
114
go.sum
114
go.sum
@@ -66,12 +66,18 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
|||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0=
|
github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0=
|
||||||
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
|
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
|
||||||
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
|
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||||
|
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
@@ -98,6 +104,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
|
|||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
|
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
|
||||||
|
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||||
@@ -118,12 +126,15 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB
|
|||||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
|
||||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-admin-team/go-admin-core v1.3.12-0.20221121065133-27b7dbe27a8f h1:2xHpluWqY/ZlYoUpOU8VwDponYSnukRDhkOr7rk3ffU=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/go-admin-team/go-admin-core v1.3.12-0.20221121065133-27b7dbe27a8f/go.mod h1:a9/XW1rCChPLVJ3bST13hB6R8YfVjYeF0GYjb8If6Yg=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||||
|
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
@@ -135,14 +146,21 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
|
|||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||||
|
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
||||||
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
|
||||||
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
@@ -152,6 +170,8 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG
|
|||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
@@ -181,6 +201,7 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
|||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
@@ -197,6 +218,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
@@ -258,6 +280,7 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
|||||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
@@ -313,6 +336,10 @@ github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
|
|||||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
|
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
|
||||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
|
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
@@ -329,6 +356,9 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
|
|||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
@@ -339,6 +369,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||||
|
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||||
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 h1:0iQektZGS248WXmGIYOwRXSQhD4qn3icjMpuxwO7qlo=
|
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 h1:0iQektZGS248WXmGIYOwRXSQhD4qn3icjMpuxwO7qlo=
|
||||||
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE=
|
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE=
|
||||||
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f h1:sgUSP4zdTUZYZgAGGtN5Lxk92rK+JUFOwf+FT99EEI4=
|
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f h1:sgUSP4zdTUZYZgAGGtN5Lxk92rK+JUFOwf+FT99EEI4=
|
||||||
@@ -357,6 +389,9 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2
|
|||||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
|
github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=
|
||||||
|
github.com/matoous/go-nanoid/v2 v2.0.0 h1:d19kur2QuLeHmJBkvYkFdhFBzLoo1XVm2GgTpL+9Tj0=
|
||||||
|
github.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
@@ -371,9 +406,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
|||||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
|
||||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
@@ -392,8 +426,12 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
|
github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
|
||||||
github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
|
github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
|
||||||
|
github.com/mssola/user_agent v0.6.0 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4=
|
||||||
|
github.com/mssola/user_agent v0.6.0/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||||
@@ -402,22 +440,14 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
|
|||||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
|
||||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
|
||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
|
||||||
github.com/onsi/gomega v1.21.1 h1:OB/euWYIExnPBohllTicTHmGTrMaqJ67nIu80j0/uEM=
|
|
||||||
github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
|
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||||
@@ -469,6 +499,11 @@ github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0Niuqvtf
|
|||||||
github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
|
github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
|
||||||
|
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
|
||||||
|
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||||
|
github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
|
||||||
|
github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
@@ -525,9 +560,12 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
@@ -542,9 +580,19 @@ github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7Am
|
|||||||
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
||||||
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
|
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c=
|
||||||
|
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
||||||
|
github.com/xuri/excelize/v2 v2.7.1 h1:gm8q0UCAyaTt3MEF5wWMjVdmthm2EHAWesGSKS9tdVI=
|
||||||
|
github.com/xuri/excelize/v2 v2.7.1/go.mod h1:qc0+2j4TvAUrBw36ATtcTeC1VCM0fFdAXZOmcF4nTpY=
|
||||||
|
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Qsdt4+M5+ltca05dA5bG2M=
|
||||||
|
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@@ -575,6 +623,9 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
|
|||||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||||
|
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
@@ -594,6 +645,7 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
|
|||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@@ -634,6 +686,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -664,7 +717,6 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
|
|||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
@@ -677,6 +729,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@@ -699,6 +753,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -720,12 +775,9 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -750,7 +802,6 @@ golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -759,16 +810,21 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -778,6 +834,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@@ -837,12 +894,12 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f
|
|||||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -945,6 +1002,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||||
|
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||||
@@ -954,6 +1014,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
|
|||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||||
@@ -967,12 +1028,16 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
|
|||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
|
||||||
|
gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
|
||||||
|
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
|
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
|
||||||
|
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
@@ -1091,6 +1156,7 @@ modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
|
|||||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
|
modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import (
|
|||||||
sysMenuService "ems.agt/features/sys_menu/service"
|
sysMenuService "ems.agt/features/sys_menu/service"
|
||||||
sysRoleService "ems.agt/features/sys_role/service"
|
sysRoleService "ems.agt/features/sys_role/service"
|
||||||
"ems.agt/lib/core/cache"
|
"ems.agt/lib/core/cache"
|
||||||
"ems.agt/lib/core/conf"
|
|
||||||
"ems.agt/lib/core/vo"
|
"ems.agt/lib/core/vo"
|
||||||
"ems.agt/lib/dborm"
|
"ems.agt/lib/dborm"
|
||||||
|
srcConfig "ems.agt/src/framework/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 登录缓存用户信息
|
// 登录缓存用户信息
|
||||||
@@ -35,7 +35,7 @@ func CacheLoginUser(user *dborm.User) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 是否管理员
|
// 是否管理员
|
||||||
if conf.IsAdmin(loginUser.UserID) {
|
if srcConfig.IsAdmin(loginUser.UserID) {
|
||||||
loginUser.Permissions = []string{"*:*:*"}
|
loginUser.Permissions = []string{"*:*:*"}
|
||||||
} else {
|
} else {
|
||||||
// 获取权限标识
|
// 获取权限标识
|
||||||
|
|||||||
@@ -2,51 +2,35 @@ package conf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var v *viper.Viper
|
||||||
|
|
||||||
// 配置文件读取
|
// 配置文件读取
|
||||||
func InitConfig(configFile string) {
|
func InitConfig(configFile string) {
|
||||||
|
v = viper.New()
|
||||||
|
|
||||||
// 设置配置文件路径
|
// 设置配置文件路径
|
||||||
viper.SetConfigFile(configFile)
|
v.SetConfigFile(configFile)
|
||||||
|
|
||||||
// 读取配置文件
|
// 读取配置文件
|
||||||
err := viper.ReadInConfig()
|
err := v.ReadInConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("读取配置文件失败: %v \n", err)
|
fmt.Printf("读取配置文件失败: %v \n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记录程序开始运行的时间点
|
|
||||||
viper.Set("runTime", time.Now())
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunTime 程序开始运行的时间
|
|
||||||
func RunTime() time.Time {
|
|
||||||
return viper.GetTime("runTime")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get 获取配置信息
|
// Get 获取配置信息
|
||||||
//
|
//
|
||||||
// Get("framework.name")
|
// Get("framework.name")
|
||||||
func Get(key string) any {
|
func Get(key string) any {
|
||||||
return viper.Get(key)
|
return v.Get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAdmin 用户是否为管理员
|
// AllSettings 全部配置信息
|
||||||
func IsAdmin(userID string) bool {
|
func AllSettings() map[string]interface{} {
|
||||||
if userID == "" {
|
return v.AllSettings()
|
||||||
return false
|
|
||||||
}
|
|
||||||
// 从本地配置获取user信息
|
|
||||||
// admins := Get("user.adminList").([]any)
|
|
||||||
admins := []string{"1", "2", "3"}
|
|
||||||
for _, s := range admins {
|
|
||||||
if s == userID {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,11 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"ems.agt/lib/core/vo"
|
"ems.agt/lib/core/vo"
|
||||||
|
commonConstants "ems.agt/src/framework/constants/common"
|
||||||
|
tokenConst "ems.agt/src/framework/constants/token"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -101,13 +104,27 @@ func SaveUploadedFile(r *http.Request, dst string) error {
|
|||||||
|
|
||||||
/// ==== 登录用户信息, 通过中间件后预置入
|
/// ==== 登录用户信息, 通过中间件后预置入
|
||||||
|
|
||||||
|
// Authorization 解析请求头
|
||||||
|
func Authorization(r *http.Request) string {
|
||||||
|
authHeader := r.Header.Get(tokenConst.HEADER_KEY)
|
||||||
|
if authHeader == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// 拆分 Authorization 请求头,提取 JWT 令牌部分
|
||||||
|
arr := strings.Split(authHeader, tokenConst.HEADER_PREFIX)
|
||||||
|
if len(arr) == 2 && arr[1] == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return arr[1]
|
||||||
|
}
|
||||||
|
|
||||||
// 定义自定义类型作为键
|
// 定义自定义类型作为键
|
||||||
type ContextKey string
|
type ContextKey string
|
||||||
|
|
||||||
// LoginUser 登录用户信息需要Authorize中间件
|
// LoginUser 登录用户信息需要Authorize中间件
|
||||||
func LoginUser(r *http.Request) (vo.LoginUser, error) {
|
func LoginUser(r *http.Request) (vo.LoginUser, error) {
|
||||||
// 上下文
|
// 上下文
|
||||||
v := r.Context().Value(ContextKey("LoginUser"))
|
v := r.Context().Value(ContextKey(commonConstants.CTX_LOGIN_USER))
|
||||||
if v != nil {
|
if v != nil {
|
||||||
return v.(vo.LoginUser), nil
|
return v.(vo.LoginUser), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Version string
|
Version string = "-"
|
||||||
BuildTime string
|
BuildTime string = "-"
|
||||||
GoVer string
|
GoVer string = "-"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"ems.agt/lib/core/vo"
|
"ems.agt/lib/core/vo"
|
||||||
"ems.agt/lib/core/vo/result"
|
"ems.agt/lib/core/vo/result"
|
||||||
"ems.agt/lib/dborm"
|
"ems.agt/lib/dborm"
|
||||||
|
commonConstants "ems.agt/src/framework/constants/common"
|
||||||
|
tokenUtils "ems.agt/src/framework/utils/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Authorize 用户身份授权认证校验
|
// Authorize 用户身份授权认证校验
|
||||||
@@ -25,30 +27,74 @@ func Authorize(options map[string][]string) func(http.Handler) http.Handler {
|
|||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// 获取请求头标识信息
|
// 获取请求头标识信息
|
||||||
|
tokenStr := ctx.Authorization(r)
|
||||||
|
// 获取请求头标识信息-旧头
|
||||||
accessToken := r.Header.Get("AccessToken")
|
accessToken := r.Header.Get("AccessToken")
|
||||||
if accessToken == "" {
|
if tokenStr == "" && accessToken != "" {
|
||||||
ctx.JSON(w, 401, result.CodeMsg(401, "token error 无效身份授权"))
|
// 验证令牌 == 这里直接查数据库session
|
||||||
|
if !dborm.XormExistValidToken(accessToken, 0) {
|
||||||
|
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization valid error"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
se, err := dborm.XormUpdateSessionShakeTime(accessToken)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization shake error"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取缓存的用户信息
|
||||||
|
data, ok := cache.GetLocalTTL(se.AccountId)
|
||||||
|
if data == nil || !ok {
|
||||||
|
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization info error"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loginUser := data.(vo.LoginUser)
|
||||||
|
|
||||||
|
// 登录用户角色权限校验
|
||||||
|
if options != nil {
|
||||||
|
var roles []string
|
||||||
|
for _, item := range loginUser.User.Roles {
|
||||||
|
roles = append(roles, item.RoleKey)
|
||||||
|
}
|
||||||
|
perms := loginUser.Permissions
|
||||||
|
verifyOk := verifyRolePermission(roles, perms, options)
|
||||||
|
if !verifyOk {
|
||||||
|
msg := fmt.Sprintf("Unauthorized access %s %s", r.Method, r.RequestURI)
|
||||||
|
ctx.JSON(w, 403, result.CodeMsg(403, msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在请求的 Context 中存储数据
|
||||||
|
rContext := r.Context()
|
||||||
|
rContext = context.WithValue(rContext, ctx.ContextKey(commonConstants.CTX_LOGIN_USER), loginUser)
|
||||||
|
// 继续处理请求
|
||||||
|
next.ServeHTTP(w, r.WithContext(rContext))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证令牌 == 这里直接查数据库session
|
// 获取请求头标识信息
|
||||||
if !dborm.XormExistValidToken(accessToken, 0) {
|
if tokenStr == "" {
|
||||||
ctx.JSON(w, 401, result.CodeMsg(401, "valid error 无效身份授权"))
|
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization token error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
se, err := dborm.XormUpdateSessionShakeTime(accessToken)
|
|
||||||
|
// 验证令牌
|
||||||
|
claims, err := tokenUtils.Verify(tokenStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(w, 401, result.CodeMsg(401, "shake error 无效身份授权"))
|
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization valid error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取缓存的用户信息
|
// 获取缓存的用户信息
|
||||||
data, ok := cache.GetLocalTTL(se.AccountId)
|
loginUser := tokenUtils.LoginUser(claims)
|
||||||
if data == nil || !ok {
|
if loginUser.UserID == "" {
|
||||||
ctx.JSON(w, 401, result.CodeMsg(401, "info error 无效身份授权"))
|
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization shake error"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
loginUser := data.(vo.LoginUser)
|
|
||||||
|
// 检查刷新有效期后存入上下文
|
||||||
|
tokenUtils.RefreshIn(&loginUser)
|
||||||
|
|
||||||
// 登录用户角色权限校验
|
// 登录用户角色权限校验
|
||||||
if options != nil {
|
if options != nil {
|
||||||
@@ -59,7 +105,7 @@ func Authorize(options map[string][]string) func(http.Handler) http.Handler {
|
|||||||
perms := loginUser.Permissions
|
perms := loginUser.Permissions
|
||||||
verifyOk := verifyRolePermission(roles, perms, options)
|
verifyOk := verifyRolePermission(roles, perms, options)
|
||||||
if !verifyOk {
|
if !verifyOk {
|
||||||
msg := fmt.Sprintf("无权访问 %s %s", r.Method, r.RequestURI)
|
msg := fmt.Sprintf("Unauthorized access %s %s", r.Method, r.RequestURI)
|
||||||
ctx.JSON(w, 403, result.CodeMsg(403, msg))
|
ctx.JSON(w, 403, result.CodeMsg(403, msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -67,7 +113,7 @@ func Authorize(options map[string][]string) func(http.Handler) http.Handler {
|
|||||||
|
|
||||||
// 在请求的 Context 中存储数据
|
// 在请求的 Context 中存储数据
|
||||||
rContext := r.Context()
|
rContext := r.Context()
|
||||||
rContext = context.WithValue(rContext, ctx.ContextKey("LoginUser"), loginUser)
|
rContext = context.WithValue(rContext, ctx.ContextKey(commonConstants.CTX_LOGIN_USER), loginUser)
|
||||||
// 继续处理请求
|
// 继续处理请求
|
||||||
next.ServeHTTP(w, r.WithContext(rContext))
|
next.ServeHTTP(w, r.WithContext(rContext))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"ems.agt/lib/log"
|
"ems.agt/lib/log"
|
||||||
"ems.agt/lib/services"
|
"ems.agt/lib/services"
|
||||||
|
tokenConst "ems.agt/src/framework/constants/token"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ func LoggerTrace(next http.Handler) http.Handler {
|
|||||||
log.Trace(" User-Agent:", r.Header.Get("User-Agent"))
|
log.Trace(" User-Agent:", r.Header.Get("User-Agent"))
|
||||||
log.Trace(" Content-Type:", r.Header.Get("Content-Type"))
|
log.Trace(" Content-Type:", r.Header.Get("Content-Type"))
|
||||||
log.Trace(" AccessToken:", r.Header.Get("AccessToken"))
|
log.Trace(" AccessToken:", r.Header.Get("AccessToken"))
|
||||||
|
log.Trace(" Authorization:", r.Header.Get(tokenConst.HEADER_KEY))
|
||||||
log.Trace("Trace End=====")
|
log.Trace("Trace End=====")
|
||||||
//body, _ := io.ReadAll(io.LimitReader(r.Body, global.RequestBodyMaxLen))
|
//body, _ := io.ReadAll(io.LimitReader(r.Body, global.RequestBodyMaxLen))
|
||||||
// nop-close to ready r.Body !!!
|
// nop-close to ready r.Body !!!
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"ems.agt/lib/global"
|
"ems.agt/lib/global"
|
||||||
"ems.agt/lib/log"
|
"ems.agt/lib/log"
|
||||||
"ems.agt/lib/run"
|
"ems.agt/lib/run"
|
||||||
|
tokenConst "ems.agt/src/framework/constants/token"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,14 +37,15 @@ type MmlCommand struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MmlVar struct {
|
type MmlVar struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Output string `json:"output"`
|
Output string `json:"output"`
|
||||||
MmlHome string `json:"mmlHome"`
|
MmlHome string `json:"mmlHome"`
|
||||||
Limit int `json:"limit"`
|
Limit int `json:"limit"`
|
||||||
User string `json:"user"`
|
User string `json:"user"`
|
||||||
SessionToken string `josn:"sessionToken"`
|
SessionToken string `josn:"sessionToken"`
|
||||||
HttpUri string `json:"httpUri"`
|
Authorization string `josn:"authorization"`
|
||||||
UserAgent string `json:"userAgent"`
|
HttpUri string `json:"httpUri"`
|
||||||
|
UserAgent string `json:"userAgent"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// func init() {
|
// func init() {
|
||||||
@@ -504,6 +506,7 @@ func TransMml2HttpReq(omcMmlVar *MmlVar, mml *MmlCommand) (*[]byte, error) {
|
|||||||
log.Debugf("method: Get requestURI: %s", requestURI)
|
log.Debugf("method: Get requestURI: %s", requestURI)
|
||||||
response, err := client.R().
|
response, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: omcMmlVar.Authorization}).
|
||||||
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
|
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
|
||||||
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
|
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -520,6 +523,7 @@ func TransMml2HttpReq(omcMmlVar *MmlVar, mml *MmlCommand) (*[]byte, error) {
|
|||||||
log.Debugf("method: Post requestURI: %s", requestURI)
|
log.Debugf("method: Post requestURI: %s", requestURI)
|
||||||
response, err := client.R().
|
response, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: omcMmlVar.Authorization}).
|
||||||
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
|
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
|
||||||
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
|
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -553,6 +557,7 @@ func TransMml2HttpReq(omcMmlVar *MmlVar, mml *MmlCommand) (*[]byte, error) {
|
|||||||
body := ParseInputBody(inputJson, mml)
|
body := ParseInputBody(inputJson, mml)
|
||||||
response, err := client.R().
|
response, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: omcMmlVar.Authorization}).
|
||||||
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
|
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
|
||||||
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
|
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -569,6 +574,7 @@ func TransMml2HttpReq(omcMmlVar *MmlVar, mml *MmlCommand) (*[]byte, error) {
|
|||||||
log.Debugf("method: Delete requestURI: %s", requestURI)
|
log.Debugf("method: Delete requestURI: %s", requestURI)
|
||||||
response, err := client.R().
|
response, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: omcMmlVar.Authorization}).
|
||||||
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
|
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
|
||||||
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
|
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -584,6 +590,7 @@ func TransMml2HttpReq(omcMmlVar *MmlVar, mml *MmlCommand) (*[]byte, error) {
|
|||||||
log.Debugf("method: patch requestURI: %s", requestURI)
|
log.Debugf("method: patch requestURI: %s", requestURI)
|
||||||
response, err := client.R().
|
response, err := client.R().
|
||||||
EnableTrace().
|
EnableTrace().
|
||||||
|
SetHeaders(map[string]string{tokenConst.HEADER_KEY: omcMmlVar.Authorization}).
|
||||||
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
|
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
|
||||||
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
|
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
|
||||||
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
|
||||||
@@ -764,12 +771,19 @@ func ParseOutputResponse(omcMmlVar *MmlVar, outputJson *dborm.MmlOutput, respons
|
|||||||
output = *ParseErrorOutput(string(response.Body()))
|
output = *ParseErrorOutput(string(response.Body()))
|
||||||
} else {
|
} else {
|
||||||
log.Trace("mapResults:", mapResults)
|
log.Trace("mapResults:", mapResults)
|
||||||
errResult := mapResults["error"]
|
if v, ok := mapResults["error"]; ok {
|
||||||
log.Trace("errResult:", errResult)
|
vMap := v.(map[string]interface{})
|
||||||
if len(errResult.(map[string]interface{})) > 0 {
|
if len(vMap) > 0 {
|
||||||
errCode, _ := strconv.Atoi(fmt.Sprintf("%v", errResult.(map[string]interface{})["errorCode"]))
|
errCode, _ := strconv.Atoi(fmt.Sprintf("%v", vMap["errorCode"]))
|
||||||
errorInfo := errResult.(map[string]interface{})["errorInfo"]
|
errorInfo := vMap["errorInfo"]
|
||||||
|
output = []byte(fmt.Sprintf(outputJson.ErrMsg, errCode, errorInfo))
|
||||||
|
}
|
||||||
|
} else if v, ok := mapResults["code"]; ok {
|
||||||
|
errCode, _ := strconv.Atoi(fmt.Sprintf("%v", v))
|
||||||
|
errorInfo := mapResults["msg"]
|
||||||
output = []byte(fmt.Sprintf(outputJson.ErrMsg, errCode, errorInfo))
|
output = []byte(fmt.Sprintf(outputJson.ErrMsg, errCode, errorInfo))
|
||||||
|
} else {
|
||||||
|
output = []byte(fmt.Sprintf("%v", mapResults))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"ems.agt/features/nbi"
|
"ems.agt/features/nbi"
|
||||||
"ems.agt/features/pm"
|
"ems.agt/features/pm"
|
||||||
"ems.agt/features/security"
|
"ems.agt/features/security"
|
||||||
|
"ems.agt/features/sm"
|
||||||
"ems.agt/features/state"
|
"ems.agt/features/state"
|
||||||
sysconfig "ems.agt/features/sys_config"
|
sysconfig "ems.agt/features/sys_config"
|
||||||
sysdictdata "ems.agt/features/sys_dict_data"
|
sysdictdata "ems.agt/features/sys_dict_data"
|
||||||
@@ -66,6 +67,9 @@ func init() {
|
|||||||
Register("GET", state.CustomUriLicenseInfoAll, state.GetAllLicenseInfoFromNF, nil)
|
Register("GET", state.CustomUriLicenseInfoAll, state.GetAllLicenseInfoFromNF, nil)
|
||||||
Register("GET", state.CustomUriLicenseInfoOne, state.GetOneLicenseInfoFromNF, nil)
|
Register("GET", state.CustomUriLicenseInfoOne, state.GetOneLicenseInfoFromNF, nil)
|
||||||
|
|
||||||
|
Register("GET", sm.UriOMCLocalTime, sm.GetOMCLocalTime, nil)
|
||||||
|
Register("GET", sm.CustomUriOMCLocalTime, sm.GetOMCLocalTime, nil)
|
||||||
|
|
||||||
// 数据库直连操作权限
|
// 数据库直连操作权限
|
||||||
selectPermission := midware.Authorize(map[string][]string{
|
selectPermission := midware.Authorize(map[string][]string{
|
||||||
"hasRoles": {"dba"},
|
"hasRoles": {"dba"},
|
||||||
@@ -364,12 +368,12 @@ func NewRouter() *mux.Router {
|
|||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
|
|
||||||
// set custom handle for status 404/405
|
// set custom handle for status 404/405
|
||||||
r.NotFoundHandler = services.CustomResponseNotFound404Handler()
|
// r.NotFoundHandler = services.CustomResponseNotFound404Handler()
|
||||||
r.MethodNotAllowedHandler = services.CustomResponseMethodNotAllowed405Handler()
|
// r.MethodNotAllowedHandler = services.CustomResponseMethodNotAllowed405Handler()
|
||||||
|
|
||||||
r.Use(midware.LoggerTrace)
|
r.Use(midware.LoggerTrace)
|
||||||
r.Use(midware.Cors)
|
// r.Use(midware.Cors)
|
||||||
//r.Use(midware.OptionProcess)
|
// r.Use(midware.OptionProcess)
|
||||||
// r.Use(midware.ArrowIPAddr)
|
// r.Use(midware.ArrowIPAddr)
|
||||||
|
|
||||||
for _, router := range routers {
|
for _, router := range routers {
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"ems.agt/lib/core/conf"
|
|
||||||
"ems.agt/lib/global"
|
"ems.agt/lib/global"
|
||||||
"ems.agt/lib/log"
|
"ems.agt/lib/log"
|
||||||
|
|
||||||
@@ -288,7 +286,7 @@ func GetLogLevel() log.LogLevel {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultUriPrefix string = "/api/rest"
|
DefaultUriPrefix string = "/api/rest"
|
||||||
UriPrefix string = "/api/rest"
|
UriPrefix string = "/omc/rest"
|
||||||
//TestDataUDM []map[string]interface{}
|
//TestDataUDM []map[string]interface{}
|
||||||
TDatas map[string]NeTestData
|
TDatas map[string]NeTestData
|
||||||
)
|
)
|
||||||
@@ -312,33 +310,33 @@ func GetDefaultUserAgent() string {
|
|||||||
return "OMC-restagent/" + global.Version
|
return "OMC-restagent/" + global.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultConfigFile = "./etc/restconf.yaml"
|
// const defaultConfigFile = "./etc/restconf.yaml"
|
||||||
|
|
||||||
func init() {
|
// func init() {
|
||||||
cfile := flag.String("c", defaultConfigFile, "config file")
|
// cfile := flag.String("c", defaultConfigFile, "config file")
|
||||||
pv := flag.Bool("version", false, "print version")
|
// pv := flag.Bool("version", false, "print version")
|
||||||
ph := flag.Bool("help", false, "print help")
|
// ph := flag.Bool("help", false, "print help")
|
||||||
|
|
||||||
global.BuildTime = "Wed May 31 18:24:04 CST 2023"
|
// //global.BuildTime = "Wed May 31 18:24:04 CST 2023"
|
||||||
global.GoVer = "go version go1.15.7 linux/arm64"
|
// //global.GoVer = "go version go1.15.7 linux/arm64"
|
||||||
flag.Parse()
|
// flag.Parse()
|
||||||
if *pv {
|
// if *pv {
|
||||||
fmt.Printf("OMC restagent version: %s\n%s\n%s\n\n", global.Version, global.BuildTime, global.GoVer)
|
// fmt.Printf("OMC restagent version: %s\n%s\n%s\n\n", global.Version, global.BuildTime, global.GoVer)
|
||||||
os.Exit(0)
|
// os.Exit(0)
|
||||||
}
|
// }
|
||||||
if *ph {
|
// if *ph {
|
||||||
flag.Usage()
|
// flag.Usage()
|
||||||
os.Exit(0)
|
// os.Exit(0)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 使用viper读取配置
|
// // 使用viper读取配置
|
||||||
conf.InitConfig(*cfile)
|
// conf.InitConfig(*cfile)
|
||||||
|
|
||||||
ReadConfig(*cfile)
|
// ReadConfig(*cfile)
|
||||||
if GetYamlConfig().OMC.UriPrefix != "" {
|
// if GetYamlConfig().OMC.UriPrefix != "" {
|
||||||
UriPrefix = GetYamlConfig().OMC.UriPrefix
|
// UriPrefix = GetYamlConfig().OMC.UriPrefix
|
||||||
}
|
// }
|
||||||
if GetYamlConfig().TestConfig.Enabled {
|
// if GetYamlConfig().TestConfig.Enabled {
|
||||||
ReadTestConfigYaml(GetYamlConfig().TestConfig.File)
|
// ReadTestConfigYaml(GetYamlConfig().TestConfig.File)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ ne:
|
|||||||
|
|
||||||
# chk2ne: true/false, if put OmcNeConfig parameters to NE
|
# chk2ne: true/false, if put OmcNeConfig parameters to NE
|
||||||
omc:
|
omc:
|
||||||
uriPrefix: /api/rest/oam
|
uriPrefix: "/api/rest/oam"
|
||||||
neType: OMC
|
neType: OMC
|
||||||
neId: 001
|
neId: 001
|
||||||
rmUID: 4400HX101
|
rmUID: 4400HX101
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ ne:
|
|||||||
|
|
||||||
# chk2ne: true/false, if put OmcNeConfig parameters to NE
|
# chk2ne: true/false, if put OmcNeConfig parameters to NE
|
||||||
omc:
|
omc:
|
||||||
uriPrefix: /api/rest
|
uriPrefix: /api/rest/oam
|
||||||
neType: OMC
|
neType: OMC
|
||||||
neId: 001
|
neId: 001
|
||||||
rmUID: 4400HX101
|
rmUID: 4400HX101
|
||||||
|
|||||||
BIN
restagent/license/upf/upf_1350015_system.ini
Normal file
BIN
restagent/license/upf/upf_1350015_system.ini
Normal file
Binary file not shown.
@@ -4,23 +4,23 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"ems.agt/lib/core/redis"
|
|
||||||
"ems.agt/lib/dborm"
|
|
||||||
"ems.agt/lib/global"
|
|
||||||
"ems.agt/lib/log"
|
|
||||||
"ems.agt/lib/routes"
|
|
||||||
|
|
||||||
"ems.agt/features/dbrest"
|
"ems.agt/features/dbrest"
|
||||||
"ems.agt/features/fm"
|
"ems.agt/features/fm"
|
||||||
"ems.agt/features/lm"
|
"ems.agt/features/lm"
|
||||||
"ems.agt/features/pm"
|
"ems.agt/features/pm"
|
||||||
|
"ems.agt/lib/dborm"
|
||||||
|
"ems.agt/lib/global"
|
||||||
|
"ems.agt/lib/log"
|
||||||
|
"ems.agt/lib/routes"
|
||||||
"ems.agt/restagent/config"
|
"ems.agt/restagent/config"
|
||||||
|
"ems.agt/src"
|
||||||
|
libSession "ems.agt/src/lib_features/session"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// const defaultConfigFile = "./etc/restconf.yaml"
|
// const defaultConfigFile = "./etc/restconf.yaml"
|
||||||
@@ -45,25 +45,25 @@ import (
|
|||||||
// //fmt.Println(config.UriPrefix)
|
// //fmt.Println(config.UriPrefix)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func listenIPv6(ipv6 string, port int) {
|
// func listenIPv6(ipv6 string, port int) {
|
||||||
//
|
// //
|
||||||
addr := &net.TCPAddr{
|
// addr := &net.TCPAddr{
|
||||||
IP: net.ParseIP(ipv6),
|
// IP: net.ParseIP(ipv6),
|
||||||
Port: port,
|
// Port: port,
|
||||||
}
|
// }
|
||||||
|
|
||||||
listener, err := net.ListenTCP("tcp6", addr)
|
// listener, err := net.ListenTCP("tcp6", addr)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
fmt.Println("Failed to listen:", err)
|
// fmt.Println("Failed to listen:", err)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
server := &http.Server{}
|
// server := &http.Server{}
|
||||||
err = server.Serve(listener)
|
// err = server.Serve(listener)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
fmt.Println("Failed to serve:", err)
|
// fmt.Println("Failed to serve:", err)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func HttpListen(addr string, router http.Handler) {
|
func HttpListen(addr string, router http.Handler) {
|
||||||
err := http.ListenAndServe(addr, router)
|
err := http.ListenAndServe(addr, router)
|
||||||
@@ -127,6 +127,10 @@ func HttpListenWebServer(addr string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// src 配置中心初始加载
|
||||||
|
src.ConfigurationInit()
|
||||||
|
app := src.AppEngine()
|
||||||
|
|
||||||
conf := config.GetYamlConfig()
|
conf := config.GetYamlConfig()
|
||||||
|
|
||||||
log.InitLogger(conf.Logger.File, conf.Logger.Duration, conf.Logger.Count, "omc:restagent", config.GetLogLevel())
|
log.InitLogger(conf.Logger.File, conf.Logger.Duration, conf.Logger.Count, "omc:restagent", config.GetLogLevel())
|
||||||
@@ -165,10 +169,16 @@ func main() {
|
|||||||
os.Exit(4)
|
os.Exit(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 连接redis
|
// 将 mux.Router 注册到 gin.Engine
|
||||||
redis.Connect()
|
|
||||||
|
|
||||||
router := routes.NewRouter()
|
// 默认路由组
|
||||||
|
defaultUriGroup := app.Group(config.DefaultUriPrefix)
|
||||||
|
defaultUriGroup.Use(libSession.SessionHeader())
|
||||||
|
defaultUriGroup.Any("/*any", gin.WrapH(routes.NewRouter()))
|
||||||
|
// 可配置前缀路由组
|
||||||
|
uriGroup := app.Group(config.UriPrefix)
|
||||||
|
uriGroup.Use(libSession.SessionHeader())
|
||||||
|
uriGroup.Any("/*any", gin.WrapH(routes.NewRouter()))
|
||||||
|
|
||||||
// 开启监控采集
|
// 开启监控采集
|
||||||
// monitor.StartMonitor(false, "")
|
// monitor.StartMonitor(false, "")
|
||||||
@@ -178,9 +188,9 @@ func main() {
|
|||||||
if rest.IPv4 != "" {
|
if rest.IPv4 != "" {
|
||||||
listen := rest.IPv4 + ":" + strconv.Itoa(int(rest.Port))
|
listen := rest.IPv4 + ":" + strconv.Itoa(int(rest.Port))
|
||||||
if strings.ToLower(rest.Scheme) == "https" {
|
if strings.ToLower(rest.Scheme) == "https" {
|
||||||
go HttpListenTLS(listen, rest.CertFile, rest.KeyFile, router)
|
go HttpListenTLS(listen, rest.CertFile, rest.KeyFile, app)
|
||||||
} else {
|
} else {
|
||||||
go HttpListen(listen, router)
|
go HttpListen(listen, app)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -188,9 +198,9 @@ func main() {
|
|||||||
if rest.IPv6 != "" {
|
if rest.IPv6 != "" {
|
||||||
listenv6 := "[" + rest.IPv6 + "]" + ":" + strconv.Itoa(int(rest.Port))
|
listenv6 := "[" + rest.IPv6 + "]" + ":" + strconv.Itoa(int(rest.Port))
|
||||||
if strings.ToLower(rest.Scheme) == "https" {
|
if strings.ToLower(rest.Scheme) == "https" {
|
||||||
go HttpListenTLS(listenv6, rest.CertFile, rest.KeyFile, router)
|
go HttpListenTLS(listenv6, rest.CertFile, rest.KeyFile, app)
|
||||||
} else {
|
} else {
|
||||||
go HttpListen(listenv6, router)
|
go HttpListen(listenv6, app)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
119
src/app.go
Normal file
119
src/app.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
"ems.agt/src/framework/errorcatch"
|
||||||
|
"ems.agt/src/framework/middleware"
|
||||||
|
"ems.agt/src/framework/middleware/security"
|
||||||
|
"ems.agt/src/modules/common"
|
||||||
|
"ems.agt/src/modules/monitor"
|
||||||
|
"ems.agt/src/modules/system"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 路由函数句柄,交给由 http.ListenAndServe(addr, router)
|
||||||
|
func AppEngine() *gin.Engine {
|
||||||
|
app := initAppEngine()
|
||||||
|
|
||||||
|
// 初始全局默认
|
||||||
|
initDefeat(app)
|
||||||
|
|
||||||
|
// 初始模块路由
|
||||||
|
initModulesRoute(app)
|
||||||
|
|
||||||
|
// 读取服务配置
|
||||||
|
app.ForwardedByClientIP = config.Get("server.proxy").(bool)
|
||||||
|
addr := fmt.Sprintf(":%d", config.Get("server.port").(int))
|
||||||
|
|
||||||
|
// 启动服务
|
||||||
|
fmt.Printf("\nopen http://localhost%s \n\n", addr)
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
// 运行服务程序 main.go
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// src.ConfigurationInit()
|
||||||
|
// if err := src.RunServer(); err != nil {
|
||||||
|
// src.ConfigurationClose()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
func RunServer() error {
|
||||||
|
app := initAppEngine()
|
||||||
|
|
||||||
|
// 初始全局默认
|
||||||
|
initDefeat(app)
|
||||||
|
|
||||||
|
// 初始模块路由
|
||||||
|
initModulesRoute(app)
|
||||||
|
|
||||||
|
// 读取服务配置
|
||||||
|
app.ForwardedByClientIP = config.Get("server.proxy").(bool)
|
||||||
|
addr := fmt.Sprintf(":%d", config.Get("server.port").(int))
|
||||||
|
|
||||||
|
// 启动服务
|
||||||
|
fmt.Printf("\nopen http://localhost%s \n\n", addr)
|
||||||
|
return app.Run(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始应用引擎
|
||||||
|
func initAppEngine() *gin.Engine {
|
||||||
|
var app *gin.Engine
|
||||||
|
|
||||||
|
// 禁止控制台日志输出的颜色
|
||||||
|
gin.DisableConsoleColor()
|
||||||
|
|
||||||
|
// 根据运行环境注册引擎
|
||||||
|
if config.Env() == "prod" {
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
app = gin.New()
|
||||||
|
app.Use(gin.Recovery())
|
||||||
|
} else {
|
||||||
|
app = gin.Default()
|
||||||
|
}
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始全局默认
|
||||||
|
func initDefeat(app *gin.Engine) {
|
||||||
|
// 全局中间件
|
||||||
|
app.Use(errorcatch.ErrorCatch(), middleware.Report(), middleware.Cors(), security.Security())
|
||||||
|
|
||||||
|
// 静态目录-静态资源
|
||||||
|
if v := config.Get("staticFile.default"); v != nil {
|
||||||
|
fsMap := v.(map[string]any)
|
||||||
|
prefix, dir := fsMap["prefix"], fsMap["dir"]
|
||||||
|
if prefix != nil && dir != nil {
|
||||||
|
app.StaticFS(prefix.(string), gin.Dir(dir.(string), true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 静态目录-上传资源
|
||||||
|
if v := config.Get("staticFile.upload"); v != nil {
|
||||||
|
fsMap := v.(map[string]any)
|
||||||
|
prefix, dir := fsMap["prefix"], fsMap["dir"]
|
||||||
|
if prefix != nil && dir != nil {
|
||||||
|
app.StaticFS(prefix.(string), gin.Dir(dir.(string), true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 路由未找到时
|
||||||
|
app.NoRoute(func(c *gin.Context) {
|
||||||
|
c.JSON(404, gin.H{
|
||||||
|
"code": 404,
|
||||||
|
"msg": fmt.Sprintf("%s Not Found", c.Request.RequestURI),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始模块路由
|
||||||
|
func initModulesRoute(app *gin.Engine) {
|
||||||
|
|
||||||
|
common.Setup(app)
|
||||||
|
monitor.Setup(app)
|
||||||
|
system.Setup(app)
|
||||||
|
}
|
||||||
35
src/configuration.go
Normal file
35
src/configuration.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
"ems.agt/src/framework/cron"
|
||||||
|
"ems.agt/src/framework/datasource"
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
"ems.agt/src/framework/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 配置中心初始加载
|
||||||
|
func ConfigurationInit() {
|
||||||
|
// 初始配置参数
|
||||||
|
config.InitConfig()
|
||||||
|
// 初始程序日志
|
||||||
|
logger.InitLogger()
|
||||||
|
// 连接数据库实例
|
||||||
|
datasource.Connect()
|
||||||
|
// 连接Redis实例
|
||||||
|
redis.Connect()
|
||||||
|
// 启动调度任务实例
|
||||||
|
cron.StartCron()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置中心相关配置关闭连接
|
||||||
|
func ConfigurationClose() {
|
||||||
|
// 停止调度任务实例
|
||||||
|
cron.StopCron()
|
||||||
|
// 关闭Redis实例
|
||||||
|
redis.Close()
|
||||||
|
// 关闭数据库实例
|
||||||
|
datasource.Close()
|
||||||
|
// 关闭程序日志
|
||||||
|
logger.Close()
|
||||||
|
}
|
||||||
163
src/framework/config/config.go
Normal file
163
src/framework/config/config.go
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
libConfig "ems.agt/src/lib_features/config"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed config/*.yaml
|
||||||
|
var configFiles embed.FS
|
||||||
|
|
||||||
|
// 初始化程序配置
|
||||||
|
func InitConfig() {
|
||||||
|
initFlag()
|
||||||
|
initViper()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 指定参数绑定
|
||||||
|
func initFlag() {
|
||||||
|
// --env prod
|
||||||
|
pflag.String("env", "prod", "Specify Run Environment Configuration local or prod")
|
||||||
|
// --c /etc/restconf.yaml
|
||||||
|
// -c /etc/restconf.yaml
|
||||||
|
pConfig := pflag.StringP("config", "c", "./etc/restconf.yaml", "Specify Configuration File")
|
||||||
|
// --version
|
||||||
|
// -V
|
||||||
|
pVersion := pflag.BoolP("version", "V", false, "Output program version")
|
||||||
|
// --help
|
||||||
|
pHelp := pflag.Bool("help", false, "Viewing Help Commands")
|
||||||
|
|
||||||
|
pflag.Parse()
|
||||||
|
|
||||||
|
// 参数固定输出
|
||||||
|
if *pVersion {
|
||||||
|
buildInfo := libConfig.BuildInfo()
|
||||||
|
fmt.Println(buildInfo)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if *pHelp {
|
||||||
|
pflag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 外层lib和features使用的配置
|
||||||
|
libConfig.ConfigRead(*pConfig)
|
||||||
|
|
||||||
|
viper.BindPFlags(pflag.CommandLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置文件读取
|
||||||
|
func initViper() {
|
||||||
|
// 在当前工作目录中寻找配置
|
||||||
|
// viper.AddConfigPath("config")
|
||||||
|
// viper.AddConfigPath("src/config")
|
||||||
|
// 如果配置文件名中没有扩展名,则需要设置Type
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
|
||||||
|
// 从 embed.FS 中读取默认配置文件内容
|
||||||
|
configDefault, err := configFiles.ReadFile("config/config.default.yaml")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ReadFile config default file: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 设置默认配置文件内容到 viper
|
||||||
|
err = viper.ReadConfig(bytes.NewReader(configDefault))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("NewReader config default file: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 配置文件的名称(无扩展名)
|
||||||
|
// viper.SetConfigName("config.default")
|
||||||
|
// // 读取默认配置文件
|
||||||
|
// if err := viper.ReadInConfig(); err != nil {
|
||||||
|
// log.Fatalf("fatal error config default file: %s", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
env := viper.GetString("env")
|
||||||
|
if env != "local" && env != "prod" {
|
||||||
|
log.Fatalf("fatal error config env for local or prod : %s", env)
|
||||||
|
}
|
||||||
|
log.Printf("Current service environment operation configuration => %s \n", env)
|
||||||
|
|
||||||
|
// 加载运行配置文件合并相同配置
|
||||||
|
if env == "prod" {
|
||||||
|
// viper.SetConfigName("config.prod")
|
||||||
|
// 从 embed.FS 中读取默认配置文件内容
|
||||||
|
configProd, err := configFiles.ReadFile("config/config.prod.yaml")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ReadFile config prod file: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 设置默认配置文件内容到 viper
|
||||||
|
err = viper.MergeConfig(bytes.NewReader(configProd))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("NewReader config prod file: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// viper.SetConfigName("config.local")
|
||||||
|
// 从 embed.FS 中读取默认配置文件内容
|
||||||
|
configLocal, err := configFiles.ReadFile("config/config.local.yaml")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ReadFile config local file: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 设置默认配置文件内容到 viper
|
||||||
|
err = viper.MergeConfig(bytes.NewReader(configLocal))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("NewReader config local file: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if err := viper.MergeInConfig(); err != nil {
|
||||||
|
// log.Fatalf("fatal error config MergeInConfig: %s", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 合并外层lib和features使用配置
|
||||||
|
libConfig.ConfigInMerge()
|
||||||
|
|
||||||
|
// 记录程序开始运行的时间点
|
||||||
|
viper.Set("runTime", time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Env 获取运行服务环境
|
||||||
|
// local prod
|
||||||
|
func Env() string {
|
||||||
|
return viper.GetString("env")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunTime 程序开始运行的时间
|
||||||
|
func RunTime() time.Time {
|
||||||
|
return viper.GetTime("runTime")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 获取配置信息
|
||||||
|
//
|
||||||
|
// Get("framework.name")
|
||||||
|
func Get(key string) any {
|
||||||
|
return viper.Get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAdmin 用户是否为管理员
|
||||||
|
func IsAdmin(userID string) bool {
|
||||||
|
if userID == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 从本地配置获取user信息
|
||||||
|
admins := Get("user.adminList").([]any)
|
||||||
|
for _, s := range admins {
|
||||||
|
if s.(string) == userID {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
208
src/framework/config/config/config.default.yaml
Normal file
208
src/framework/config/config/config.default.yaml
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
# 项目信息
|
||||||
|
framework:
|
||||||
|
name: "ems_agt"
|
||||||
|
version: "1.6.2"
|
||||||
|
|
||||||
|
# 应用服务配置
|
||||||
|
server:
|
||||||
|
# 服务端口
|
||||||
|
port: 3040
|
||||||
|
# 是否开启代理
|
||||||
|
proxy: false
|
||||||
|
|
||||||
|
# 日志
|
||||||
|
logger:
|
||||||
|
fileDir: "/usr/local/omc/logs"
|
||||||
|
fileName: "ems_agt.log"
|
||||||
|
level: 2 # 日志记录的等级 0:silent<1:info<2:warn<3:error
|
||||||
|
maxDay: 30 # 日志会保留 30 天
|
||||||
|
maxSize: 10 # 调整按 10MB 大小的切割
|
||||||
|
|
||||||
|
# 静态文件配置, 相对项目根路径或填绝对路径
|
||||||
|
staticFile:
|
||||||
|
# 默认资源,dir目录需要预先创建
|
||||||
|
default:
|
||||||
|
prefix: "/static"
|
||||||
|
dir: "/usr/local/omc/static"
|
||||||
|
# 文件上传资源目录映射,与项目目录同级
|
||||||
|
upload:
|
||||||
|
prefix: "/upload"
|
||||||
|
dir: "/usr/local/omc/upload"
|
||||||
|
|
||||||
|
# 文件上传
|
||||||
|
upload:
|
||||||
|
# 最大上传文件大小,默认为 10mb
|
||||||
|
fileSize: 10
|
||||||
|
# 文件扩展名白名单
|
||||||
|
whitelist:
|
||||||
|
# 图片
|
||||||
|
- ".bmp"
|
||||||
|
- ".webp"
|
||||||
|
- ".gif"
|
||||||
|
- ".jpg"
|
||||||
|
- ".jpeg"
|
||||||
|
- ".png"
|
||||||
|
# word excel powerpoint
|
||||||
|
- ".doc"
|
||||||
|
- ".docx"
|
||||||
|
- ".xls"
|
||||||
|
- ".xlsx"
|
||||||
|
- ".ppt"
|
||||||
|
- ".pptx"
|
||||||
|
# 文本文件
|
||||||
|
- ".html"
|
||||||
|
- ".htm"
|
||||||
|
- ".txt"
|
||||||
|
# pdf
|
||||||
|
- ".pdf"
|
||||||
|
# 压缩文件
|
||||||
|
- ".zip"
|
||||||
|
- ".gz"
|
||||||
|
- ".tgz"
|
||||||
|
- ".gzip"
|
||||||
|
# 音视频格式
|
||||||
|
- ".mp3"
|
||||||
|
- ".mp4"
|
||||||
|
- ".avi"
|
||||||
|
- ".rmvb"
|
||||||
|
|
||||||
|
# cors 跨域
|
||||||
|
cors:
|
||||||
|
# 设置 Access-Control-Allow-Origin 的值,【默认值】会获取请求头上的 origin
|
||||||
|
# 例如:http://mask-api.org
|
||||||
|
# 如果请求设置了 credentials,则 origin 不能设置为 *
|
||||||
|
origin: "*"
|
||||||
|
# 设置 Access-Control-Allow-Credentials,【默认值】false
|
||||||
|
credentials: true
|
||||||
|
# 设置 Access-Control-Max-Age
|
||||||
|
maxAge: 31536000
|
||||||
|
# 允许跨域的方法,【默认值】为 GET,HEAD,PUT,POST,DELETE,PATCH
|
||||||
|
allowMethods:
|
||||||
|
- "OPTIONS"
|
||||||
|
- "HEAD"
|
||||||
|
- "GET"
|
||||||
|
- "POST"
|
||||||
|
- "PUT"
|
||||||
|
- "DELETE"
|
||||||
|
- "PATCH"
|
||||||
|
# 设置 Access-Control-Allow-Headers 的值,【默认值】会获取请求头上的 Access-Control-Request-Headers
|
||||||
|
allowHeaders:
|
||||||
|
- "X-App-Code"
|
||||||
|
- "X-App-Version"
|
||||||
|
- "Authorization"
|
||||||
|
- "Origin"
|
||||||
|
- "X-Requested-With"
|
||||||
|
- "Content-Type"
|
||||||
|
- "Content-Language"
|
||||||
|
- "Accept"
|
||||||
|
- "Range"
|
||||||
|
- "Accesstoken"
|
||||||
|
- "Operationtype"
|
||||||
|
# 设置 Access-Control-Expose-Headers 的值
|
||||||
|
exposeHeaders:
|
||||||
|
- "X-RepeatSubmit-Rest"
|
||||||
|
|
||||||
|
# security 安全
|
||||||
|
security:
|
||||||
|
csrf:
|
||||||
|
enable: false
|
||||||
|
type: "referer"
|
||||||
|
# 允许调用的域名地址的,例如:http://<Referer地址>/mask-api
|
||||||
|
refererWhiteList:
|
||||||
|
- "127.0.0.1:3030"
|
||||||
|
xframe:
|
||||||
|
enable: true
|
||||||
|
value: "SAMEORIGIN"
|
||||||
|
csp:
|
||||||
|
enable: true
|
||||||
|
hsts:
|
||||||
|
enable: false
|
||||||
|
maxAge: 31536000
|
||||||
|
includeSubdomains: false
|
||||||
|
noopen:
|
||||||
|
enable: false
|
||||||
|
nosniff:
|
||||||
|
enable: false
|
||||||
|
xssProtection:
|
||||||
|
enable: true
|
||||||
|
value: "1; mode=block"
|
||||||
|
|
||||||
|
# JWT 令牌配置
|
||||||
|
jwt:
|
||||||
|
# 令牌算法 HS256 HS384 HS512
|
||||||
|
algorithm: "HS512"
|
||||||
|
# 令牌密钥
|
||||||
|
secret: "217a0481c7f9cfe1cb547d32ee012b0f"
|
||||||
|
# 令牌有效期(默认120分钟)
|
||||||
|
expiresIn: 120
|
||||||
|
# 验证令牌有效期,相差不足xx分钟,自动刷新缓存
|
||||||
|
refreshIn: 20
|
||||||
|
|
||||||
|
# GORM 数据源
|
||||||
|
gorm:
|
||||||
|
dataSource:
|
||||||
|
# 默认数据库实例
|
||||||
|
default:
|
||||||
|
type: "mysql"
|
||||||
|
host: "127.0.0.1"
|
||||||
|
port: 3306
|
||||||
|
username: "<用户名>"
|
||||||
|
password: "<密码>"
|
||||||
|
database: "<数据库>"
|
||||||
|
logging: false
|
||||||
|
# 多个数据源时可以用这个指定默认的数据源
|
||||||
|
defaultDataSourceName: "default"
|
||||||
|
|
||||||
|
# Redis 缓存数据
|
||||||
|
redis:
|
||||||
|
dataSource:
|
||||||
|
default:
|
||||||
|
port: 6379 # Redis port
|
||||||
|
host: "127.0.0.1" # Redis host
|
||||||
|
password: "<密码>"
|
||||||
|
db: 0 # Redis db_num
|
||||||
|
# 多个数据源时可以用这个指定默认的数据源
|
||||||
|
defaultDataSourceName: "default"
|
||||||
|
|
||||||
|
# 用户配置
|
||||||
|
user:
|
||||||
|
# 密码
|
||||||
|
password:
|
||||||
|
# 密码最大错误次数
|
||||||
|
maxRetryCount: 5
|
||||||
|
# 密码锁定时间,单位分钟(默认10分钟)
|
||||||
|
lockTime: 10
|
||||||
|
# 管理员列表
|
||||||
|
adminList:
|
||||||
|
- "1"
|
||||||
|
- "2"
|
||||||
|
|
||||||
|
# char 字符验证码配置
|
||||||
|
charCaptcha:
|
||||||
|
# 宽度
|
||||||
|
width: 120
|
||||||
|
# 高度
|
||||||
|
height: 40
|
||||||
|
# 干扰线条的数量
|
||||||
|
noise: 4
|
||||||
|
# 验证码的字符是否有颜色,默认没有,如果设定了背景,则默认有
|
||||||
|
color: true
|
||||||
|
# 验证码图片背景颜色
|
||||||
|
background: "#fafafa"
|
||||||
|
# 验证码长度
|
||||||
|
size: 4
|
||||||
|
# 验证码字符
|
||||||
|
chars: "023456789abcdefghjkmnprstuvwxyz"
|
||||||
|
|
||||||
|
# math 数值计算码配置
|
||||||
|
mathCaptcha:
|
||||||
|
# 宽度
|
||||||
|
width: 120
|
||||||
|
# 高度
|
||||||
|
height: 40
|
||||||
|
# 干扰线条的数量
|
||||||
|
noise: 4
|
||||||
|
# 验证码的字符是否有颜色,默认没有,如果设定了背景,则默认有
|
||||||
|
color: true
|
||||||
|
# 验证码图片背景颜色
|
||||||
|
background: "#fafafa"
|
||||||
53
src/framework/config/config/config.local.yaml
Normal file
53
src/framework/config/config/config.local.yaml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# 应用服务配置
|
||||||
|
server:
|
||||||
|
port: 3040
|
||||||
|
|
||||||
|
# 日志
|
||||||
|
logger:
|
||||||
|
fileDir: "C:/usr/local/omc/logs"
|
||||||
|
level: 0 # 输出最低等级
|
||||||
|
|
||||||
|
# 静态文件配置, 相对项目根路径或填绝对路径
|
||||||
|
staticFile:
|
||||||
|
default:
|
||||||
|
dir: "C:/usr/local/omc/static"
|
||||||
|
# 文件上传资源目录映射,与项目目录同级
|
||||||
|
upload:
|
||||||
|
dir: "C:/usr/local/omc/upload"
|
||||||
|
|
||||||
|
# security 安全
|
||||||
|
security:
|
||||||
|
csrf:
|
||||||
|
refererWhiteList:
|
||||||
|
- "localhost:3131"
|
||||||
|
- "127.0.0.1:3131"
|
||||||
|
|
||||||
|
# GORM 数据源
|
||||||
|
gorm:
|
||||||
|
dataSource:
|
||||||
|
default:
|
||||||
|
type: "mysql"
|
||||||
|
host: "192.168.0.229"
|
||||||
|
port: 33066
|
||||||
|
username: "root"
|
||||||
|
password: "1000omc@kp!"
|
||||||
|
database: "omc_db_dev"
|
||||||
|
logging: true
|
||||||
|
|
||||||
|
# Redis 缓存数据,数据源声明全小写
|
||||||
|
redis:
|
||||||
|
dataSource:
|
||||||
|
# OMC系统使用库
|
||||||
|
default:
|
||||||
|
port: 6379 # Redis port
|
||||||
|
host: "192.168.0.229" # Redis host
|
||||||
|
password: ""
|
||||||
|
db: 10 # Redis db_num
|
||||||
|
# UDM网元用户库
|
||||||
|
udmuser:
|
||||||
|
port: 6379 # Redis port
|
||||||
|
host: "192.168.0.229"
|
||||||
|
password: ""
|
||||||
|
db: 0 # Redis db_num
|
||||||
|
# 多个数据源时可以用这个指定默认的数据源
|
||||||
|
defaultDataSourceName: "default"
|
||||||
32
src/framework/config/config/config.prod.yaml
Normal file
32
src/framework/config/config/config.prod.yaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# 应用服务配置
|
||||||
|
server:
|
||||||
|
port: 3040
|
||||||
|
proxy: true
|
||||||
|
|
||||||
|
# security 安全
|
||||||
|
security:
|
||||||
|
csrf:
|
||||||
|
# 允许调用的域名地址的,例如:http://<Referer地址>/
|
||||||
|
refererWhiteList:
|
||||||
|
- "127.0.0.1"
|
||||||
|
- "<Referer地址>"
|
||||||
|
|
||||||
|
# GORM 数据源
|
||||||
|
gorm:
|
||||||
|
dataSource:
|
||||||
|
default:
|
||||||
|
type: "mysql"
|
||||||
|
host: "<mysql地址>"
|
||||||
|
port: 3306
|
||||||
|
username: "<用户名>"
|
||||||
|
password: "<密码>"
|
||||||
|
database: "<数据库>"
|
||||||
|
|
||||||
|
# Redis 缓存数据
|
||||||
|
redis:
|
||||||
|
dataSource:
|
||||||
|
default:
|
||||||
|
port: 6379 # Redis port
|
||||||
|
host: "<redis地址>"
|
||||||
|
password: "<密码>"
|
||||||
|
db: 0 # Redis db_num
|
||||||
12
src/framework/constants/admin/admin.go
Normal file
12
src/framework/constants/admin/admin.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
// 管理员常量信息
|
||||||
|
|
||||||
|
// 管理员-系统指定角色ID
|
||||||
|
const ROLE_ID = "1"
|
||||||
|
|
||||||
|
// 管理员-系统指定角色KEY
|
||||||
|
const ROLE_KEY = "admin"
|
||||||
|
|
||||||
|
// 管理员-系统指定权限
|
||||||
|
const PERMISSION = "*:*:*"
|
||||||
24
src/framework/constants/cachekey/cachekey.go
Normal file
24
src/framework/constants/cachekey/cachekey.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package cachekey
|
||||||
|
|
||||||
|
// 缓存的key常量
|
||||||
|
|
||||||
|
// 登录用户
|
||||||
|
const LOGIN_TOKEN_KEY = "login_tokens:"
|
||||||
|
|
||||||
|
// 验证码
|
||||||
|
const CAPTCHA_CODE_KEY = "captcha_codes:"
|
||||||
|
|
||||||
|
// 参数管理
|
||||||
|
const SYS_CONFIG_KEY = "sys_config:"
|
||||||
|
|
||||||
|
// 字典管理
|
||||||
|
const SYS_DICT_KEY = "sys_dict:"
|
||||||
|
|
||||||
|
// 防重提交
|
||||||
|
const REPEAT_SUBMIT_KEY = "repeat_submit:"
|
||||||
|
|
||||||
|
// 限流
|
||||||
|
const RATE_LIMIT_KEY = "rate_limit:"
|
||||||
|
|
||||||
|
// 登录账户密码错误次数
|
||||||
|
const PWD_ERR_CNT_KEY = "pwd_err_cnt:"
|
||||||
12
src/framework/constants/captcha/captcha.go
Normal file
12
src/framework/constants/captcha/captcha.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package captcha
|
||||||
|
|
||||||
|
// 验证码常量信息
|
||||||
|
|
||||||
|
// 验证码有效期,单位秒
|
||||||
|
const EXPIRATION = 2 * 60
|
||||||
|
|
||||||
|
// 验证码类型-数值计算
|
||||||
|
const TYPE_CHAR = "char"
|
||||||
|
|
||||||
|
// 验证码类型-字符验证
|
||||||
|
const TYPE_MATH = "math"
|
||||||
21
src/framework/constants/common/common.go
Normal file
21
src/framework/constants/common/common.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
// 通用常量信息
|
||||||
|
|
||||||
|
// www主域
|
||||||
|
const WWW = "www."
|
||||||
|
|
||||||
|
// http请求
|
||||||
|
const HTTP = "http://"
|
||||||
|
|
||||||
|
// https请求
|
||||||
|
const HTTPS = "https://"
|
||||||
|
|
||||||
|
// 通用状态标识-正常/成功/是
|
||||||
|
const STATUS_YES = "1"
|
||||||
|
|
||||||
|
// 通用状态标识-停用/失败/否
|
||||||
|
const STATUS_NO = "0"
|
||||||
|
|
||||||
|
// 上下文信息-登录用户
|
||||||
|
const CTX_LOGIN_USER = "loginuser"
|
||||||
24
src/framework/constants/menu/menu.go
Normal file
24
src/framework/constants/menu/menu.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package menu
|
||||||
|
|
||||||
|
// 系统菜单常量信息
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 组件布局类型-基础布局组件标识
|
||||||
|
COMPONENT_LAYOUT_BASIC = "BasicLayout"
|
||||||
|
// 组件布局类型-空白布局组件标识
|
||||||
|
COMPONENT_LAYOUT_BLANK = "BlankLayout"
|
||||||
|
// 组件布局类型-内链接布局组件标识
|
||||||
|
COMPONENT_LAYOUT_LINK = "LinkLayout"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 菜单类型-目录
|
||||||
|
TYPE_DIR = "D"
|
||||||
|
// 菜单类型-菜单
|
||||||
|
TYPE_MENU = "M"
|
||||||
|
// 菜单类型-按钮
|
||||||
|
TYPE_BUTTON = "B"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 菜单内嵌地址标识-带/前缀
|
||||||
|
const PATH_INLINE = "/inline"
|
||||||
15
src/framework/constants/result/result.go
Normal file
15
src/framework/constants/result/result.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package result
|
||||||
|
|
||||||
|
// 响应结果常量信息
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 响应-code错误失败
|
||||||
|
CODE_ERROR = 0
|
||||||
|
// 响应-msg错误失败
|
||||||
|
MSG_ERROR = "error"
|
||||||
|
|
||||||
|
// 响应-msg正常成功
|
||||||
|
CODE_SUCCESS = 1
|
||||||
|
// 响应-code正常成功
|
||||||
|
MSG_SUCCESS = "success"
|
||||||
|
)
|
||||||
29
src/framework/constants/roledatascope/roledatascope.go
Normal file
29
src/framework/constants/roledatascope/roledatascope.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package roledatascope
|
||||||
|
|
||||||
|
// 系统角色数据范围常量
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 全部数据权限
|
||||||
|
ALL = "1"
|
||||||
|
|
||||||
|
// 自定数据权限
|
||||||
|
CUSTOM = "2"
|
||||||
|
|
||||||
|
// 部门数据权限
|
||||||
|
DEPT = "3"
|
||||||
|
|
||||||
|
// 部门及以下数据权限
|
||||||
|
DEPT_AND_CHILD = "4"
|
||||||
|
|
||||||
|
// 仅本人数据权限
|
||||||
|
SELF = "5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 系统角色数据范围映射
|
||||||
|
var RoleDataScope = map[string]string{
|
||||||
|
ALL: "全部数据权限",
|
||||||
|
CUSTOM: "自定数据权限",
|
||||||
|
DEPT: "部门数据权限",
|
||||||
|
DEPT_AND_CHILD: "部门及以下数据权限",
|
||||||
|
SELF: "仅本人数据权限",
|
||||||
|
}
|
||||||
21
src/framework/constants/token/token.go
Normal file
21
src/framework/constants/token/token.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package token
|
||||||
|
|
||||||
|
// 令牌常量信息
|
||||||
|
|
||||||
|
// 令牌-数据响应字段
|
||||||
|
const RESPONSE_FIELD = "access_token"
|
||||||
|
|
||||||
|
// 令牌-请求头标识前缀
|
||||||
|
const HEADER_PREFIX = "Bearer "
|
||||||
|
|
||||||
|
// 令牌-请求头标识
|
||||||
|
const HEADER_KEY = "Authorization"
|
||||||
|
|
||||||
|
// 令牌-JWT唯一标识字段
|
||||||
|
const JWT_UUID = "login_key"
|
||||||
|
|
||||||
|
// 令牌-JWT标识用户主键字段
|
||||||
|
const JWT_KEY = "user_id"
|
||||||
|
|
||||||
|
// 令牌-JWT标识用户登录账号字段
|
||||||
|
const JWT_NAME = "user_name"
|
||||||
37
src/framework/constants/uploadsubpath/uploadsubpath.go
Normal file
37
src/framework/constants/uploadsubpath/uploadsubpath.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package uploadsubpath
|
||||||
|
|
||||||
|
// 文件上传-子路径类型常量
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 默认
|
||||||
|
DEFAULT = "default"
|
||||||
|
|
||||||
|
// 头像
|
||||||
|
AVATART = "avatar"
|
||||||
|
|
||||||
|
// 导入
|
||||||
|
IMPORT = "import"
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
EXPORT = "export"
|
||||||
|
|
||||||
|
// 通用上传
|
||||||
|
COMMON = "common"
|
||||||
|
|
||||||
|
// 下载
|
||||||
|
DOWNLOAD = "download"
|
||||||
|
|
||||||
|
// 切片
|
||||||
|
CHUNK = "chunk"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 子路径类型映射
|
||||||
|
var UploadSubpath = map[string]string{
|
||||||
|
DEFAULT: "默认",
|
||||||
|
AVATART: "头像",
|
||||||
|
IMPORT: "导入",
|
||||||
|
EXPORT: "导出",
|
||||||
|
COMMON: "通用上传",
|
||||||
|
DOWNLOAD: "下载",
|
||||||
|
CHUNK: "切片",
|
||||||
|
}
|
||||||
202
src/framework/cron/cron.go
Normal file
202
src/framework/cron/cron.go
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
package cron
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/robfig/cron/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 定义内部调度任务实例
|
||||||
|
var c *cron.Cron
|
||||||
|
|
||||||
|
// 任务队列
|
||||||
|
var queueMap map[string]Queue
|
||||||
|
|
||||||
|
// StartCron 启动调度任务实例
|
||||||
|
func StartCron() {
|
||||||
|
queueMap = make(map[string]Queue)
|
||||||
|
c = cron.New(cron.WithSeconds())
|
||||||
|
c.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopCron 停止调度任务实例
|
||||||
|
func StopCron() {
|
||||||
|
c.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateQueue 创建队列注册处理器
|
||||||
|
func CreateQueue(name string, processor QueueProcessor) Queue {
|
||||||
|
queue := Queue{
|
||||||
|
Name: name,
|
||||||
|
Processor: processor,
|
||||||
|
Job: &[]*QueueJob{},
|
||||||
|
}
|
||||||
|
queueMap[name] = queue
|
||||||
|
return queue
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQueue 通过名称获取队列实例
|
||||||
|
func GetQueue(name string) Queue {
|
||||||
|
if v, ok := queueMap[name]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return Queue{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueueNames 获取注册的队列名称
|
||||||
|
func QueueNames() []string {
|
||||||
|
keys := make([]string, 0, len(queueMap))
|
||||||
|
for k := range queueMap {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue 任务队列
|
||||||
|
type Queue struct {
|
||||||
|
Name string // 队列名
|
||||||
|
Processor QueueProcessor
|
||||||
|
Job *[]*QueueJob
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueueProcessor 队列处理函数接口
|
||||||
|
type QueueProcessor interface {
|
||||||
|
// Execute 实际执行函数
|
||||||
|
Execute(data any) any
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunJob 运行任务,data是传入的数据
|
||||||
|
func (q *Queue) RunJob(data any, opts JobOptions) int {
|
||||||
|
job := &QueueJob{
|
||||||
|
Status: Waiting,
|
||||||
|
Data: data,
|
||||||
|
Opts: opts,
|
||||||
|
queueName: q.Name,
|
||||||
|
queueProcessor: &q.Processor,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非重复任务立即执行
|
||||||
|
if opts.Cron == "" {
|
||||||
|
// 获取执行的任务
|
||||||
|
currentJob := job.GetJob(false)
|
||||||
|
if currentJob.Status == Active {
|
||||||
|
return Active
|
||||||
|
}
|
||||||
|
// 从切片 jobs 中删除指定索引位置的元素
|
||||||
|
for i, v := range *q.Job {
|
||||||
|
if v.cid == 0 {
|
||||||
|
jobs := *q.Job
|
||||||
|
jobs = append(jobs[:i], jobs[i+1:]...)
|
||||||
|
*q.Job = jobs
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go job.Run()
|
||||||
|
} else {
|
||||||
|
// 移除已存的任务ID
|
||||||
|
q.RemoveJob(opts.JobId)
|
||||||
|
// 添加新任务
|
||||||
|
cid, err := c.AddJob(opts.Cron, job)
|
||||||
|
if err != nil {
|
||||||
|
newLog.Error(err, "err")
|
||||||
|
job.Status = Failed
|
||||||
|
}
|
||||||
|
job.cid = cid
|
||||||
|
}
|
||||||
|
|
||||||
|
*q.Job = append(*q.Job, job)
|
||||||
|
newLog.Info("RunJob", job.cid, opts.JobId, job.Status)
|
||||||
|
return job.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveJob 移除任务
|
||||||
|
func (q *Queue) RemoveJob(jobId string) bool {
|
||||||
|
for i, v := range *q.Job {
|
||||||
|
if jobId == v.Opts.JobId {
|
||||||
|
newLog.Info("RemoveJob", v.cid, jobId, v.Status)
|
||||||
|
c.Remove(v.cid)
|
||||||
|
// 从切片 jobs 中删除指定索引位置的元素
|
||||||
|
jobs := *q.Job
|
||||||
|
jobs = append(jobs[:i], jobs[i+1:]...)
|
||||||
|
*q.Job = jobs
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status 任务执行状态
|
||||||
|
const (
|
||||||
|
Waiting = iota
|
||||||
|
Active
|
||||||
|
Completed
|
||||||
|
Failed
|
||||||
|
)
|
||||||
|
|
||||||
|
// JobOptions 任务参数信息
|
||||||
|
type JobOptions struct {
|
||||||
|
JobId string // 执行任务编号
|
||||||
|
Cron string // 重复任务cron表达式
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueueJob 队列内部执行任务
|
||||||
|
type QueueJob struct {
|
||||||
|
Status int // 任务执行状态
|
||||||
|
Timestamp int64 // 执行时间
|
||||||
|
Data any // 执行任务时传入的参数
|
||||||
|
Opts JobOptions
|
||||||
|
|
||||||
|
cid cron.EntryID // 执行ID
|
||||||
|
|
||||||
|
queueName string //队列名
|
||||||
|
queueProcessor *QueueProcessor
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetJob 获取当前执行任务
|
||||||
|
func (job *QueueJob) GetJob(repeat bool) *QueueJob {
|
||||||
|
q := GetQueue(job.queueName)
|
||||||
|
for _, v := range *q.Job {
|
||||||
|
if repeat && v.Opts.JobId == job.Opts.JobId {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
if !repeat && v.cid == 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return job
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run 实现的接口函数
|
||||||
|
func (s QueueJob) Run() {
|
||||||
|
// 检查当前任务
|
||||||
|
job := s.GetJob(s.cid != 0)
|
||||||
|
|
||||||
|
// Active 状态不执行
|
||||||
|
if job.Status == Active {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// panics 异常收集
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err, ok := r.(error)
|
||||||
|
if !ok {
|
||||||
|
err = fmt.Errorf("%v", r)
|
||||||
|
}
|
||||||
|
job.Status = Failed
|
||||||
|
newLog.Error(err, "failed", job)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 开始执行
|
||||||
|
job.Status = Active
|
||||||
|
job.Timestamp = time.Now().UnixMilli()
|
||||||
|
newLog.Info("run", job.cid, job.Opts.JobId)
|
||||||
|
|
||||||
|
// 获取队列处理器接口实现
|
||||||
|
processor := *job.queueProcessor
|
||||||
|
result := processor.Execute(job.Data)
|
||||||
|
job.Status = Completed
|
||||||
|
newLog.Completed(result, "completed", job)
|
||||||
|
}
|
||||||
179
src/framework/cron/cron_test.go
Normal file
179
src/framework/cron/cron_test.go
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
package cron
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 参考文章:
|
||||||
|
// https://blog.csdn.net/zjbyough/article/details/113853582
|
||||||
|
// https://mp.weixin.qq.com/s/Ak7RBv1NuS-VBeDNo8_fww
|
||||||
|
func init() {
|
||||||
|
StartCron()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简单示例 队列任务处理
|
||||||
|
var NewSimple = &Simple{}
|
||||||
|
|
||||||
|
type Simple struct{}
|
||||||
|
|
||||||
|
func (s *Simple) Execute(data any) any {
|
||||||
|
logger.Infof("执行=> %+v ", data)
|
||||||
|
// 实现任务处理逻辑
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSimple(t *testing.T) {
|
||||||
|
|
||||||
|
simple := CreateQueue("simple", NewSimple)
|
||||||
|
simple.RunJob(map[string]string{
|
||||||
|
"ok": "ok",
|
||||||
|
"data": "data",
|
||||||
|
}, JobOptions{
|
||||||
|
JobId: "101",
|
||||||
|
})
|
||||||
|
|
||||||
|
simpleC := CreateQueue("simple", NewSimple)
|
||||||
|
simpleC.RunJob(map[string]string{
|
||||||
|
"corn": "*/5 * * * * *",
|
||||||
|
"id": "102",
|
||||||
|
}, JobOptions{
|
||||||
|
JobId: "102",
|
||||||
|
Cron: "*/5 * * * * *",
|
||||||
|
})
|
||||||
|
|
||||||
|
// simpleC.RunJob(map[string]string{
|
||||||
|
// "corn": "*/15 * * * * *",
|
||||||
|
// "id": "103",
|
||||||
|
// }, JobOptions{
|
||||||
|
// JobId: "103",
|
||||||
|
// Cron: "*/15 * * * * *",
|
||||||
|
// })
|
||||||
|
|
||||||
|
// simpleC.RemoveJob("102")
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foo 队列任务处理
|
||||||
|
var NewFooProcessor = &FooProcessor{
|
||||||
|
progress: 0,
|
||||||
|
count: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
type FooProcessor struct {
|
||||||
|
progress int
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FooProcessor) Execute(data any) any {
|
||||||
|
logger.Infof("执行 %d %d => %+v ", s.count, s.progress, data)
|
||||||
|
s.count++
|
||||||
|
|
||||||
|
// 实现任务处理逻辑
|
||||||
|
i := 0
|
||||||
|
s.progress = i
|
||||||
|
for i < 10 {
|
||||||
|
// 获取任务进度
|
||||||
|
progress := s.progress
|
||||||
|
logger.Infof("data: %v => 任务进度:%d", data, progress)
|
||||||
|
// 延迟响应
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
i++
|
||||||
|
// 改变任务进度
|
||||||
|
s.progress = i
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFoo(t *testing.T) {
|
||||||
|
|
||||||
|
foo := CreateQueue("foo", NewFooProcessor)
|
||||||
|
foo.RunJob(map[string]string{
|
||||||
|
"data": "2",
|
||||||
|
}, JobOptions{
|
||||||
|
JobId: "2",
|
||||||
|
})
|
||||||
|
|
||||||
|
fooC := CreateQueue("foo", NewFooProcessor)
|
||||||
|
fooC.RunJob(map[string]string{
|
||||||
|
"corn": "*/5 * * * * *",
|
||||||
|
}, JobOptions{
|
||||||
|
JobId: "3",
|
||||||
|
Cron: "*/5 * * * * *",
|
||||||
|
})
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bar 队列任务处理
|
||||||
|
var NewBarProcessor = &BarProcessor{
|
||||||
|
progress: 0,
|
||||||
|
count: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
type BarProcessor struct {
|
||||||
|
progress int
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BarProcessor) Execute(data any) any {
|
||||||
|
logger.Infof("执行 %d %d => %+v ", s.count, s.progress, data)
|
||||||
|
s.count++
|
||||||
|
|
||||||
|
// 实现任务处理逻辑
|
||||||
|
i := 0
|
||||||
|
s.progress = i
|
||||||
|
for i < 5 {
|
||||||
|
// 获取任务进度
|
||||||
|
progress := s.progress
|
||||||
|
logger.Infof("data: %v => 任务进度:%d", data, progress)
|
||||||
|
// 延迟响应
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
// 程序中途执行错误
|
||||||
|
if i == 3 {
|
||||||
|
// arr := [1]int{1}
|
||||||
|
// arr[i] = 3
|
||||||
|
// fmt.Println(arr)
|
||||||
|
// return "i = 3"
|
||||||
|
panic("程序中途执行错误")
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
// 改变任务进度
|
||||||
|
s.progress = i
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBar(t *testing.T) {
|
||||||
|
|
||||||
|
bar := CreateQueue("bar", NewBarProcessor)
|
||||||
|
bar.RunJob(map[string]string{
|
||||||
|
"data": "wdf",
|
||||||
|
}, JobOptions{
|
||||||
|
JobId: "81923",
|
||||||
|
})
|
||||||
|
|
||||||
|
barC := CreateQueue("bar", NewBarProcessor)
|
||||||
|
barC.RunJob(map[string]string{
|
||||||
|
"corn": "*/5 * * * * *",
|
||||||
|
}, JobOptions{
|
||||||
|
JobId: "789",
|
||||||
|
Cron: "*/5 * * * * *",
|
||||||
|
})
|
||||||
|
|
||||||
|
// barDB := CreateQueue("barDB", NewBarProcessor)
|
||||||
|
// barDB.RunJob(JobData{
|
||||||
|
// SysJob: model.SysJob{
|
||||||
|
// JobID: "9123",
|
||||||
|
// JobName: "测试任务",
|
||||||
|
// },
|
||||||
|
// }, JobOptions{
|
||||||
|
// JobId: "9123",
|
||||||
|
// })
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
||||||
112
src/framework/cron/log.go
Normal file
112
src/framework/cron/log.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package cron
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/constants/common"
|
||||||
|
"ems.agt/src/modules/monitor/model"
|
||||||
|
"ems.agt/src/modules/monitor/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 实例任务执行日志收集
|
||||||
|
var newLog = cronlog{}
|
||||||
|
|
||||||
|
// cronlog 任务执行日志收集
|
||||||
|
type cronlog struct{}
|
||||||
|
|
||||||
|
// Info 任务普通信息收集
|
||||||
|
func (s cronlog) Info(msg string, keysAndValues ...any) {
|
||||||
|
// logger.Infof("Info msg: %v ====> kv: %v", msg, keysAndValues)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error 任务异常错误收集
|
||||||
|
func (s cronlog) Error(err error, msg string, keysAndValues ...any) {
|
||||||
|
// logger.Errorf("Error: %v -> msg: %v ====> kv: %v", err, msg, keysAndValues)
|
||||||
|
// logger.Errorf("k0: %v", keysAndValues[0].(*QueueJob))
|
||||||
|
|
||||||
|
// 指定的错误收集
|
||||||
|
if msg == "failed" {
|
||||||
|
// 任务对象
|
||||||
|
job := keysAndValues[0].(*QueueJob)
|
||||||
|
|
||||||
|
// 结果信息序列化字符串
|
||||||
|
jsonByte, _ := json.Marshal(map[string]any{
|
||||||
|
"name": "failed",
|
||||||
|
"message": err.Error(),
|
||||||
|
})
|
||||||
|
jobMsg := string(jsonByte)
|
||||||
|
if len(jobMsg) > 500 {
|
||||||
|
jobMsg = jobMsg[:500]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取任务信息创建日志对象
|
||||||
|
if data, ok := job.Data.(JobData); ok {
|
||||||
|
duration := time.Since(time.UnixMilli(job.Timestamp))
|
||||||
|
sysJob := data.SysJob
|
||||||
|
if sysJob.JobID == job.Opts.JobId {
|
||||||
|
sysJobLog := model.SysJobLog{
|
||||||
|
JobName: sysJob.JobName,
|
||||||
|
JobGroup: sysJob.JobGroup,
|
||||||
|
InvokeTarget: sysJob.InvokeTarget,
|
||||||
|
TargetParams: sysJob.TargetParams,
|
||||||
|
Status: common.STATUS_NO,
|
||||||
|
JobMsg: jobMsg,
|
||||||
|
CostTime: duration.Milliseconds(),
|
||||||
|
}
|
||||||
|
// 插入数据
|
||||||
|
repository.NewSysJobLogImpl.InsertJobLog(sysJobLog)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Completed 任务完成return的结果收集
|
||||||
|
func (s cronlog) Completed(result any, msg string, keysAndValues ...any) {
|
||||||
|
// logger.Infof("Completed: %v -> msg: %v ====> kv: %v", result, msg, keysAndValues)
|
||||||
|
// logger.Infof("k0: %v", keysAndValues[0].(*QueueJob))
|
||||||
|
|
||||||
|
// 指定的完成收集
|
||||||
|
if msg == "completed" {
|
||||||
|
// 任务对象
|
||||||
|
job := keysAndValues[0].(*QueueJob)
|
||||||
|
|
||||||
|
// 结果信息序列化字符串
|
||||||
|
jsonByte, _ := json.Marshal(map[string]any{
|
||||||
|
"name": "completed",
|
||||||
|
"message": result,
|
||||||
|
})
|
||||||
|
jobMsg := string(jsonByte)
|
||||||
|
if len(jobMsg) > 500 {
|
||||||
|
jobMsg = jobMsg[:500]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取任务信息创建日志对象
|
||||||
|
if data, ok := job.Data.(JobData); ok {
|
||||||
|
duration := time.Since(time.UnixMilli(job.Timestamp))
|
||||||
|
sysJob := data.SysJob
|
||||||
|
if sysJob.JobID == job.Opts.JobId {
|
||||||
|
sysJobLog := model.SysJobLog{
|
||||||
|
JobName: sysJob.JobName,
|
||||||
|
JobGroup: sysJob.JobGroup,
|
||||||
|
InvokeTarget: sysJob.InvokeTarget,
|
||||||
|
TargetParams: sysJob.TargetParams,
|
||||||
|
Status: common.STATUS_YES,
|
||||||
|
JobMsg: jobMsg,
|
||||||
|
CostTime: duration.Milliseconds(),
|
||||||
|
}
|
||||||
|
// 插入数据
|
||||||
|
repository.NewSysJobLogImpl.InsertJobLog(sysJobLog)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JobData 调度任务日志收集结构体,执行任务时传入的接收参数
|
||||||
|
type JobData struct {
|
||||||
|
// 触发执行cron重复多次
|
||||||
|
Repeat bool
|
||||||
|
// 定时任务调度表记录信息
|
||||||
|
SysJob model.SysJob
|
||||||
|
}
|
||||||
161
src/framework/datasource/datasource.go
Normal file
161
src/framework/datasource/datasource.go
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
package datasource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
gormLog "gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 数据库连接实例
|
||||||
|
var dbMap = make(map[string]*gorm.DB)
|
||||||
|
|
||||||
|
type dialectInfo struct {
|
||||||
|
dialector gorm.Dialector
|
||||||
|
logging bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 载入数据库连接
|
||||||
|
func loadDialect() map[string]dialectInfo {
|
||||||
|
dialects := make(map[string]dialectInfo, 0)
|
||||||
|
|
||||||
|
// 读取数据源配置
|
||||||
|
datasource := config.Get("gorm.datasource").(map[string]any)
|
||||||
|
for key, value := range datasource {
|
||||||
|
item := value.(map[string]any)
|
||||||
|
// 数据库类型对应的数据库连接
|
||||||
|
switch item["type"] {
|
||||||
|
case "mysql":
|
||||||
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||||
|
item["username"],
|
||||||
|
item["password"],
|
||||||
|
item["host"],
|
||||||
|
item["port"],
|
||||||
|
item["database"],
|
||||||
|
)
|
||||||
|
dialects[key] = dialectInfo{
|
||||||
|
dialector: mysql.Open(dsn),
|
||||||
|
logging: item["logging"].(bool),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
logger.Fatalf("%s: %v\n Not Load DB Config Type", key, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialects
|
||||||
|
}
|
||||||
|
|
||||||
|
// 载入连接日志配置
|
||||||
|
func loadLogger() gormLog.Interface {
|
||||||
|
newLogger := gormLog.New(
|
||||||
|
log.New(os.Stdout, "[GORM] ", log.LstdFlags), // 将日志输出到控制台
|
||||||
|
gormLog.Config{
|
||||||
|
SlowThreshold: time.Second, // Slow SQL 阈值
|
||||||
|
LogLevel: gormLog.Info, // 日志级别 Silent不输出任何日志
|
||||||
|
ParameterizedQueries: false, // 参数化查询SQL 用实际值带入?的执行语句
|
||||||
|
Colorful: false, // 彩色日志输出
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return newLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
// 连接数据库实例
|
||||||
|
func Connect() {
|
||||||
|
// 遍历进行连接数据库实例
|
||||||
|
for key, info := range loadDialect() {
|
||||||
|
opts := &gorm.Config{}
|
||||||
|
// 是否需要日志输出
|
||||||
|
if info.logging {
|
||||||
|
opts.Logger = loadLogger()
|
||||||
|
}
|
||||||
|
// 创建连接
|
||||||
|
db, err := gorm.Open(info.dialector, opts)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("failed error db connect: %s", err)
|
||||||
|
}
|
||||||
|
// 获取底层 SQL 数据库连接
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("failed error underlying SQL database: %v", err)
|
||||||
|
}
|
||||||
|
// 测试数据库连接
|
||||||
|
err = sqlDB.Ping()
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("failed error ping database: %v", err)
|
||||||
|
}
|
||||||
|
logger.Infof("database %s connection is successful.", key)
|
||||||
|
dbMap[key] = db
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭数据库实例
|
||||||
|
func Close() {
|
||||||
|
for _, db := range dbMap {
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := sqlDB.Close(); err != nil {
|
||||||
|
logger.Errorf("fatal error db close: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取默认数据源
|
||||||
|
func DefaultDB() *gorm.DB {
|
||||||
|
source := config.Get("gorm.defaultDataSourceName").(string)
|
||||||
|
return dbMap[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取数据源
|
||||||
|
func DB(source string) *gorm.DB {
|
||||||
|
return dbMap[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawDB 原生查询语句
|
||||||
|
func RawDB(source string, sql string, parameters []any) ([]map[string]any, error) {
|
||||||
|
// 数据源
|
||||||
|
db := DefaultDB()
|
||||||
|
if source != "" {
|
||||||
|
db = DB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用正则表达式替换连续的空白字符为单个空格
|
||||||
|
fmtSql := regexp.MustCompile(`\s+`).ReplaceAllString(sql, " ")
|
||||||
|
|
||||||
|
// logger.Infof("sql=> %v", fmtSql)
|
||||||
|
// logger.Infof("parameters=> %v", parameters)
|
||||||
|
|
||||||
|
// 查询结果
|
||||||
|
var rows []map[string]any
|
||||||
|
res := db.Raw(fmtSql, parameters...).Scan(&rows)
|
||||||
|
if res.Error != nil {
|
||||||
|
return nil, res.Error
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecDB 原生执行语句
|
||||||
|
func ExecDB(source string, sql string, parameters []any) (int64, error) {
|
||||||
|
// 数据源
|
||||||
|
db := DefaultDB()
|
||||||
|
if source != "" {
|
||||||
|
db = DB(source)
|
||||||
|
}
|
||||||
|
// 使用正则表达式替换连续的空白字符为单个空格
|
||||||
|
fmtSql := regexp.MustCompile(`\s+`).ReplaceAllString(sql, " ")
|
||||||
|
// 执行结果
|
||||||
|
res := db.Exec(fmtSql, parameters...)
|
||||||
|
if res.Error != nil {
|
||||||
|
return 0, res.Error
|
||||||
|
}
|
||||||
|
return res.RowsAffected, nil
|
||||||
|
}
|
||||||
40
src/framework/errorcatch/errorcatch.go
Normal file
40
src/framework/errorcatch/errorcatch.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package errorcatch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
"ems.agt/src/framework/vo/result"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrorCatch 全局异常捕获
|
||||||
|
func ErrorCatch() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
defer func() {
|
||||||
|
// 在这里处理 Panic 异常,例如记录日志或返回错误信息给客户端
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
logger.Errorf("发生了 Panic 异常: %v", err)
|
||||||
|
|
||||||
|
// 返回错误响应给客户端
|
||||||
|
if config.Env() == "prod" {
|
||||||
|
c.JSON(500, result.ErrMsg("服务器内部错误"))
|
||||||
|
} else {
|
||||||
|
// 通过实现 error 接口的 Error() 方法自定义错误类型进行捕获
|
||||||
|
switch v := err.(type) {
|
||||||
|
case error:
|
||||||
|
c.JSON(500, result.ErrMsg(v.Error()))
|
||||||
|
default:
|
||||||
|
c.JSON(500, result.ErrMsg(fmt.Sprint(err)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Abort() // 停止执行后续的处理函数
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/framework/logger/logger.go
Normal file
49
src/framework/logger/logger.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logWriter *Logger
|
||||||
|
|
||||||
|
// 初始程序日志
|
||||||
|
func InitLogger() {
|
||||||
|
env := viper.GetString("env")
|
||||||
|
conf := viper.GetStringMap("logger")
|
||||||
|
fileDir := conf["filedir"].(string)
|
||||||
|
fileName := conf["filename"].(string)
|
||||||
|
level := conf["level"].(int)
|
||||||
|
maxDay := conf["maxday"].(int)
|
||||||
|
maxSize := conf["maxsize"].(int)
|
||||||
|
|
||||||
|
newLog, err := NewLogger(env, fileDir, fileName, level, maxDay, maxSize)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to initialize logger: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logWriter = newLog
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭程序日志写入
|
||||||
|
func Close() {
|
||||||
|
logWriter.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Infof(format string, v ...any) {
|
||||||
|
logWriter.Infof(format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warnf(format string, v ...any) {
|
||||||
|
logWriter.Warnf(format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Errorf(format string, v ...any) {
|
||||||
|
logWriter.Errorf(format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf 抛出错误并退出程序
|
||||||
|
func Fatalf(format string, v ...any) {
|
||||||
|
log.Fatalf(format, v...)
|
||||||
|
}
|
||||||
190
src/framework/logger/writer.go
Normal file
190
src/framework/logger/writer.go
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 日志器对象
|
||||||
|
type Logger struct {
|
||||||
|
env string // 运行环境
|
||||||
|
filePath string // 文件路径
|
||||||
|
fileName string // 文件名
|
||||||
|
level int // 日志等级标识
|
||||||
|
maxDay int // 保留最长天数
|
||||||
|
maxSize int64 // 文件最大空间
|
||||||
|
fileHandle *os.File // 文件实例
|
||||||
|
logger *log.Logger // 日志实例
|
||||||
|
logLevelMap map[int]string // 日志等级标识名
|
||||||
|
logDay int // 日志当前日
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
LOG_LEVEL_SILENT = iota
|
||||||
|
LOG_LEVEL_INFO
|
||||||
|
LOG_LEVEL_WARN
|
||||||
|
LOG_LEVEL_ERROR
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewLogger 实例日志器对象
|
||||||
|
func NewLogger(env, fileDir, fileName string, level, maxDay, maxSize int) (*Logger, error) {
|
||||||
|
logFilePath := filepath.Join(fileDir, fileName)
|
||||||
|
if err := os.MkdirAll(filepath.Dir(logFilePath), 0750); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to mkdir logger dir: %v", err)
|
||||||
|
}
|
||||||
|
fileHandle, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open log file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
writer := io.Writer(fileHandle)
|
||||||
|
if env == "local" {
|
||||||
|
writer = io.MultiWriter(fileHandle, os.Stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := log.New(writer, "", log.LstdFlags|log.Lshortfile)
|
||||||
|
|
||||||
|
logLevelMap := map[int]string{
|
||||||
|
LOG_LEVEL_INFO: "INFO",
|
||||||
|
LOG_LEVEL_WARN: "WARN",
|
||||||
|
LOG_LEVEL_ERROR: "ERROR",
|
||||||
|
}
|
||||||
|
|
||||||
|
stdLogger := &Logger{
|
||||||
|
env: env,
|
||||||
|
filePath: fileDir,
|
||||||
|
fileName: fileName,
|
||||||
|
level: level,
|
||||||
|
maxDay: maxDay,
|
||||||
|
maxSize: int64(maxSize * 1024 * 1024),
|
||||||
|
fileHandle: fileHandle,
|
||||||
|
logger: logger,
|
||||||
|
logLevelMap: logLevelMap,
|
||||||
|
logDay: time.Now().Day(),
|
||||||
|
}
|
||||||
|
|
||||||
|
go stdLogger.checkFile()
|
||||||
|
|
||||||
|
return stdLogger, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkFile 检查文件分割,自定时调用
|
||||||
|
func (l *Logger) checkFile() {
|
||||||
|
fileInfo, err := l.fileHandle.Stat()
|
||||||
|
if err != nil {
|
||||||
|
l.logger.Printf("Failed to get log file info: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currTime := time.Now()
|
||||||
|
if l.logDay != currTime.Day() {
|
||||||
|
l.logDay = currTime.Day()
|
||||||
|
l.rotateFile(currTime.AddDate(0, 0, -1).Format("2006_01_02"))
|
||||||
|
// 移除超过保存最长天数的文件
|
||||||
|
l.removeOldFile(currTime.AddDate(0, 0, -l.maxDay))
|
||||||
|
} else if fileInfo.Size() >= l.maxSize {
|
||||||
|
l.rotateFile(currTime.Format("2006_01_02_150405"))
|
||||||
|
} else if time.Since(fileInfo.ModTime()).Hours() > 24 {
|
||||||
|
l.rotateFile(fileInfo.ModTime().Format("2006_01_02"))
|
||||||
|
}
|
||||||
|
|
||||||
|
time.AfterFunc(1*time.Minute, l.checkFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rotateFile 检查文件大小进行分割
|
||||||
|
func (l *Logger) rotateFile(timeFormat string) {
|
||||||
|
l.fileHandle.Close()
|
||||||
|
|
||||||
|
newFileName := fmt.Sprintf("%s.%s", l.fileName, timeFormat)
|
||||||
|
newFilePath := filepath.Join(l.filePath, newFileName)
|
||||||
|
oldfilePath := filepath.Join(l.filePath, l.fileName)
|
||||||
|
|
||||||
|
// 重命名
|
||||||
|
os.Rename(oldfilePath, newFilePath)
|
||||||
|
|
||||||
|
// 新文件句柄
|
||||||
|
fileHandle, err := os.OpenFile(oldfilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
l.logger.Printf("Failed to open log file: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.fileHandle = fileHandle
|
||||||
|
|
||||||
|
// 重新设置 logger 的 writer
|
||||||
|
writer := io.Writer(l.fileHandle)
|
||||||
|
if l.env == "local" {
|
||||||
|
writer = io.MultiWriter(l.fileHandle, os.Stderr)
|
||||||
|
}
|
||||||
|
l.logger.SetOutput(writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveOldFile 删除旧文件
|
||||||
|
func (l *Logger) removeOldFile(oldFileDate time.Time) {
|
||||||
|
// 遍历目标文件夹中的文件
|
||||||
|
files, err := os.ReadDir(l.filePath)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorf("logger RemoveOldFile ReadDir err: %v", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
idx := strings.LastIndex(file.Name(), ".log.")
|
||||||
|
if idx == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dateStr := file.Name()[idx+5 : idx+15]
|
||||||
|
|
||||||
|
// 解析日期字符串
|
||||||
|
fileDate, err := time.Parse("2006_01_02", dateStr)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorf("logger RemoveOldFile Parse err: %v", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断文件日期是否在给定日期之前
|
||||||
|
if fileDate.Before(oldFileDate) {
|
||||||
|
// 删除旧文件
|
||||||
|
err := os.Remove(filepath.Join(l.filePath, file.Name()))
|
||||||
|
if err != nil {
|
||||||
|
l.Errorf("logger RemoveOldFile Remove err: %v", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeLog 写入chan
|
||||||
|
func (l *Logger) writeLog(level int, format string, args ...interface{}) {
|
||||||
|
if level < l.level {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logMsg := fmt.Sprintf("[%s] %s\n", l.logLevelMap[level], fmt.Sprintf(format, args...))
|
||||||
|
l.logger.Output(4, logMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Infof(format string, args ...interface{}) {
|
||||||
|
l.writeLog(LOG_LEVEL_INFO, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Warnf(format string, args ...interface{}) {
|
||||||
|
l.writeLog(LOG_LEVEL_WARN, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Errorf(format string, args ...interface{}) {
|
||||||
|
l.writeLog(LOG_LEVEL_ERROR, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close 日志关闭
|
||||||
|
func (l *Logger) Close() {
|
||||||
|
err := l.fileHandle.Close()
|
||||||
|
if err != nil {
|
||||||
|
l.logger.Printf("Failed to close log file: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
182
src/framework/middleware/collectlogs/operate_log.go
Normal file
182
src/framework/middleware/collectlogs/operate_log.go
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
package collectlogs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/constants/common"
|
||||||
|
"ems.agt/src/framework/utils/ctx"
|
||||||
|
"ems.agt/src/framework/utils/parse"
|
||||||
|
"ems.agt/src/framework/vo/result"
|
||||||
|
"ems.agt/src/modules/system/model"
|
||||||
|
"ems.agt/src/modules/system/service"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 业务操作类型-其它
|
||||||
|
BUSINESS_TYPE_OTHER = "0"
|
||||||
|
|
||||||
|
// 业务操作类型-新增
|
||||||
|
BUSINESS_TYPE_INSERT = "1"
|
||||||
|
|
||||||
|
// 业务操作类型-修改
|
||||||
|
BUSINESS_TYPE_UPDATE = "2"
|
||||||
|
|
||||||
|
// 业务操作类型-删除
|
||||||
|
BUSINESS_TYPE_DELETE = "3"
|
||||||
|
|
||||||
|
// 业务操作类型-授权
|
||||||
|
BUSINESS_TYPE_GRANT = "4"
|
||||||
|
|
||||||
|
// 业务操作类型-导出
|
||||||
|
BUSINESS_TYPE_EXPORT = "5"
|
||||||
|
|
||||||
|
// 业务操作类型-导入
|
||||||
|
BUSINESS_TYPE_IMPORT = "6"
|
||||||
|
|
||||||
|
// 业务操作类型-强退
|
||||||
|
BUSINESS_TYPE_FORCE = "7"
|
||||||
|
|
||||||
|
// 业务操作类型-清空数据
|
||||||
|
BUSINESS_TYPE_CLEAN = "8"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 操作人类别-其它
|
||||||
|
OPERATOR_TYPE_OTHER = "0"
|
||||||
|
|
||||||
|
// 操作人类别-后台用户
|
||||||
|
OPERATOR_TYPE_MANAGE = "1"
|
||||||
|
|
||||||
|
// 操作人类别-手机端用户
|
||||||
|
OPERATOR_TYPE_MOBILE = "2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Option 操作日志参数
|
||||||
|
type Options struct {
|
||||||
|
Title string `json:"title"` // 标题
|
||||||
|
BusinessType string `json:"businessType"` // 类型,默认常量 BUSINESS_TYPE_OTHER
|
||||||
|
OperatorType string `json:"operatorType"` // 操作人类别,默认常量 OPERATOR_TYPE_OTHER
|
||||||
|
IsSaveRequestData bool `json:"isSaveRequestData"` // 是否保存请求的参数
|
||||||
|
IsSaveResponseData bool `json:"isSaveResponseData"` // 是否保存响应的参数
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionNew 操作日志参数默认值
|
||||||
|
//
|
||||||
|
// 标题 "title":"--"
|
||||||
|
//
|
||||||
|
// 类型 "businessType": BUSINESS_TYPE_OTHER
|
||||||
|
//
|
||||||
|
// 注意之后JSON反序列使用:c.ShouldBindBodyWith(¶ms, binding.JSON)
|
||||||
|
func OptionNew(title, businessType string) Options {
|
||||||
|
return Options{
|
||||||
|
Title: title,
|
||||||
|
BusinessType: businessType,
|
||||||
|
OperatorType: OPERATOR_TYPE_OTHER,
|
||||||
|
IsSaveRequestData: true,
|
||||||
|
IsSaveResponseData: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 敏感属性字段进行掩码
|
||||||
|
var maskProperties []string = []string{
|
||||||
|
"password",
|
||||||
|
"oldPassword",
|
||||||
|
"newPassword",
|
||||||
|
"confirmPassword",
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperateLog 访问操作日志记录
|
||||||
|
//
|
||||||
|
// 请在用户身份授权认证校验后使用以便获取登录用户信息
|
||||||
|
func OperateLog(options Options) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
c.Set("startTime", time.Now())
|
||||||
|
|
||||||
|
// 函数名
|
||||||
|
funcName := c.HandlerName()
|
||||||
|
lastDotIndex := strings.LastIndex(funcName, "/")
|
||||||
|
funcName = funcName[lastDotIndex+1:]
|
||||||
|
|
||||||
|
// 解析ip地址
|
||||||
|
ipaddr, location := ctx.IPAddrLocation(c)
|
||||||
|
|
||||||
|
// 获取登录用户信息
|
||||||
|
loginUser, err := ctx.LoginUser(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(401, result.CodeMsg(401, "无效身份授权"))
|
||||||
|
c.Abort() // 停止执行后续的处理函数
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 操作日志记录
|
||||||
|
operLog := model.SysLogOperate{
|
||||||
|
Title: options.Title,
|
||||||
|
BusinessType: options.BusinessType,
|
||||||
|
OperatorType: options.OperatorType,
|
||||||
|
Method: funcName,
|
||||||
|
OperURL: c.Request.RequestURI,
|
||||||
|
RequestMethod: c.Request.Method,
|
||||||
|
OperIP: ipaddr,
|
||||||
|
OperLocation: location,
|
||||||
|
OperName: loginUser.User.UserName,
|
||||||
|
DeptName: loginUser.User.Dept.DeptName,
|
||||||
|
}
|
||||||
|
|
||||||
|
if loginUser.User.UserType == "sys" {
|
||||||
|
operLog.OperatorType = OPERATOR_TYPE_MANAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否需要保存request,参数和值
|
||||||
|
if options.IsSaveRequestData {
|
||||||
|
params := ctx.RequestParamsMap(c)
|
||||||
|
for k, v := range params {
|
||||||
|
// 敏感属性字段进行掩码
|
||||||
|
for _, s := range maskProperties {
|
||||||
|
if s == k {
|
||||||
|
params[k] = parse.SafeContent(v.(string))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jsonStr, _ := json.Marshal(params)
|
||||||
|
paramsStr := string(jsonStr)
|
||||||
|
if len(paramsStr) > 2000 {
|
||||||
|
paramsStr = paramsStr[:2000]
|
||||||
|
}
|
||||||
|
operLog.OperParam = paramsStr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用下一个处理程序
|
||||||
|
c.Next()
|
||||||
|
|
||||||
|
// 响应状态
|
||||||
|
status := c.Writer.Status()
|
||||||
|
if status == 200 {
|
||||||
|
operLog.Status = common.STATUS_YES
|
||||||
|
} else {
|
||||||
|
operLog.Status = common.STATUS_NO
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否需要保存response,参数和值
|
||||||
|
if options.IsSaveResponseData {
|
||||||
|
contentDisposition := c.Writer.Header().Get("Content-Disposition")
|
||||||
|
contentType := c.Writer.Header().Get("Content-Type")
|
||||||
|
content := contentType + contentDisposition
|
||||||
|
msg := fmt.Sprintf(`{"status":"%d","size":"%d","content-type":"%s"}`, status, c.Writer.Size(), content)
|
||||||
|
operLog.OperMsg = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日志记录时间
|
||||||
|
duration := time.Since(c.GetTime("startTime"))
|
||||||
|
operLog.CostTime = duration.Milliseconds()
|
||||||
|
operLog.OperTime = time.Now().UnixMilli()
|
||||||
|
|
||||||
|
// 保存操作记录到数据库
|
||||||
|
service.NewSysLogOperateImpl.InsertSysLogOperate(operLog)
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/framework/middleware/cors.go
Normal file
83
src/framework/middleware/cors.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cors 跨域
|
||||||
|
func Cors() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// 设置Vary头部
|
||||||
|
c.Header("Vary", "Origin")
|
||||||
|
c.Header("Keep-Alive", "timeout=5")
|
||||||
|
|
||||||
|
requestOrigin := c.GetHeader("Origin")
|
||||||
|
if requestOrigin == "" {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
origin := requestOrigin
|
||||||
|
if v := config.Get("cors.origin"); v != nil {
|
||||||
|
origin = v.(string)
|
||||||
|
}
|
||||||
|
c.Header("Access-Control-Allow-Origin", origin)
|
||||||
|
|
||||||
|
if v := config.Get("cors.credentials"); v != nil && v.(bool) {
|
||||||
|
c.Header("Access-Control-Allow-Credentials", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OPTIONS
|
||||||
|
if method := c.Request.Method; method == "OPTIONS" {
|
||||||
|
requestMethod := c.GetHeader("Access-Control-Request-Method")
|
||||||
|
if requestMethod == "" {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应最大时间值
|
||||||
|
if v := config.Get("cors.maxAge"); v != nil && v.(int) > 10000 {
|
||||||
|
c.Header("Access-Control-Max-Age", fmt.Sprint(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 允许方法
|
||||||
|
if v := config.Get("cors.allowMethods"); v != nil {
|
||||||
|
var allowMethods = make([]string, 0)
|
||||||
|
for _, s := range v.([]any) {
|
||||||
|
allowMethods = append(allowMethods, s.(string))
|
||||||
|
}
|
||||||
|
c.Header("Access-Control-Allow-Methods", strings.Join(allowMethods, ","))
|
||||||
|
} else {
|
||||||
|
c.Header("Access-Control-Allow-Methods", "GET,HEAD,PUT,POST,DELETE,PATCH")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 允许请求头
|
||||||
|
if v := config.Get("cors.allowHeaders"); v != nil {
|
||||||
|
var allowHeaders = make([]string, 0)
|
||||||
|
for _, s := range v.([]any) {
|
||||||
|
allowHeaders = append(allowHeaders, s.(string))
|
||||||
|
}
|
||||||
|
c.Header("Access-Control-Allow-Headers", strings.Join(allowHeaders, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AbortWithStatus(204)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暴露请求头
|
||||||
|
if v := config.Get("cors.exposeHeaders"); v != nil {
|
||||||
|
var exposeHeaders = make([]string, 0)
|
||||||
|
for _, s := range v.([]any) {
|
||||||
|
exposeHeaders = append(exposeHeaders, s.(string))
|
||||||
|
}
|
||||||
|
c.Header("Access-Control-Expose-Headers", strings.Join(exposeHeaders, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
180
src/framework/middleware/pre_authorize.go
Normal file
180
src/framework/middleware/pre_authorize.go
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
AdminConstants "ems.agt/src/framework/constants/admin"
|
||||||
|
commonConstants "ems.agt/src/framework/constants/common"
|
||||||
|
ctxUtils "ems.agt/src/framework/utils/ctx"
|
||||||
|
tokenUtils "ems.agt/src/framework/utils/token"
|
||||||
|
"ems.agt/src/framework/vo/result"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PreAuthorize 用户身份授权认证校验
|
||||||
|
//
|
||||||
|
// 只需含有其中角色 "hasRoles": {"xxx"},
|
||||||
|
//
|
||||||
|
// 只需含有其中权限 "hasPerms": {"xxx"},
|
||||||
|
//
|
||||||
|
// 同时匹配其中角色 "matchRoles": {"xxx"},
|
||||||
|
//
|
||||||
|
// 同时匹配其中权限 "matchPerms": {"xxx"},
|
||||||
|
func PreAuthorize(options map[string][]string) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// 获取请求头标识信息
|
||||||
|
tokenStr := ctxUtils.Authorization(c)
|
||||||
|
if tokenStr == "" {
|
||||||
|
c.JSON(401, result.CodeMsg(401, "无效身份授权"))
|
||||||
|
c.Abort() // 停止执行后续的处理函数
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证令牌
|
||||||
|
claims, err := tokenUtils.Verify(tokenStr)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(401, result.CodeMsg(401, err.Error()))
|
||||||
|
c.Abort() // 停止执行后续的处理函数
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取缓存的用户信息
|
||||||
|
loginUser := tokenUtils.LoginUser(claims)
|
||||||
|
if loginUser.UserID == "" {
|
||||||
|
c.JSON(401, result.CodeMsg(401, "无效身份授权"))
|
||||||
|
c.Abort() // 停止执行后续的处理函数
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查刷新有效期后存入上下文
|
||||||
|
tokenUtils.RefreshIn(&loginUser)
|
||||||
|
c.Set(commonConstants.CTX_LOGIN_USER, loginUser)
|
||||||
|
|
||||||
|
// 登录用户角色权限校验
|
||||||
|
if options != nil {
|
||||||
|
var roles []string
|
||||||
|
for _, item := range loginUser.User.Roles {
|
||||||
|
roles = append(roles, item.RoleKey)
|
||||||
|
}
|
||||||
|
perms := loginUser.Permissions
|
||||||
|
verifyOk := verifyRolePermission(roles, perms, options)
|
||||||
|
if !verifyOk {
|
||||||
|
msg := fmt.Sprintf("无权访问 %s %s", c.Request.Method, c.Request.RequestURI)
|
||||||
|
c.JSON(403, result.CodeMsg(403, msg))
|
||||||
|
c.Abort() // 停止执行后续的处理函数
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用下一个处理程序
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyRolePermission 校验角色权限是否满足
|
||||||
|
//
|
||||||
|
// roles 角色字符数组
|
||||||
|
//
|
||||||
|
// perms 权限字符数组
|
||||||
|
//
|
||||||
|
// options 参数
|
||||||
|
func verifyRolePermission(roles, perms []string, options map[string][]string) bool {
|
||||||
|
// 直接放行 管理员角色或任意权限
|
||||||
|
if contains(roles, AdminConstants.ROLE_KEY) || contains(perms, AdminConstants.PERMISSION) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
opts := make([]bool, 4)
|
||||||
|
|
||||||
|
// 只需含有其中角色
|
||||||
|
hasRole := false
|
||||||
|
if arr, ok := options["hasRoles"]; ok && len(arr) > 0 {
|
||||||
|
hasRole = some(roles, arr)
|
||||||
|
opts[0] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只需含有其中权限
|
||||||
|
hasPerms := false
|
||||||
|
if arr, ok := options["hasPerms"]; ok && len(arr) > 0 {
|
||||||
|
hasPerms = some(perms, arr)
|
||||||
|
opts[1] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同时匹配其中角色
|
||||||
|
matchRoles := false
|
||||||
|
if arr, ok := options["matchRoles"]; ok && len(arr) > 0 {
|
||||||
|
matchRoles = every(roles, arr)
|
||||||
|
opts[2] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同时匹配其中权限
|
||||||
|
matchPerms := false
|
||||||
|
if arr, ok := options["matchPerms"]; ok && len(arr) > 0 {
|
||||||
|
matchPerms = every(perms, arr)
|
||||||
|
opts[3] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同时判断 含有其中
|
||||||
|
if opts[0] && opts[1] {
|
||||||
|
return hasRole || hasPerms
|
||||||
|
}
|
||||||
|
// 同时判断 匹配其中
|
||||||
|
if opts[2] && opts[3] {
|
||||||
|
return matchRoles && matchPerms
|
||||||
|
}
|
||||||
|
// 同时判断 含有其中且匹配其中
|
||||||
|
if opts[0] && opts[3] {
|
||||||
|
return hasRole && matchPerms
|
||||||
|
}
|
||||||
|
if opts[1] && opts[2] {
|
||||||
|
return hasPerms && matchRoles
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasRole || hasPerms || matchRoles || matchPerms
|
||||||
|
}
|
||||||
|
|
||||||
|
// contains 检查字符串数组中是否包含指定的字符串
|
||||||
|
func contains(arr []string, target string) bool {
|
||||||
|
for _, str := range arr {
|
||||||
|
if str == target {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// some 检查字符串数组中含有其中一项
|
||||||
|
func some(origin []string, target []string) bool {
|
||||||
|
has := false
|
||||||
|
for _, t := range target {
|
||||||
|
for _, o := range origin {
|
||||||
|
if t == o {
|
||||||
|
has = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if has {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return has
|
||||||
|
}
|
||||||
|
|
||||||
|
// every 检查字符串数组中同时包含所有项
|
||||||
|
func every(origin []string, target []string) bool {
|
||||||
|
match := true
|
||||||
|
for _, t := range target {
|
||||||
|
found := false
|
||||||
|
for _, o := range origin {
|
||||||
|
if t == o {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
match = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
}
|
||||||
101
src/framework/middleware/rate_limit.go
Normal file
101
src/framework/middleware/rate_limit.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/constants/cachekey"
|
||||||
|
"ems.agt/src/framework/redis"
|
||||||
|
"ems.agt/src/framework/utils/ctx"
|
||||||
|
"ems.agt/src/framework/utils/ip2region"
|
||||||
|
"ems.agt/src/framework/vo/result"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 默认策略全局限流
|
||||||
|
LIMIT_GLOBAL = 1
|
||||||
|
|
||||||
|
// 根据请求者IP进行限流
|
||||||
|
LIMIT_IP = 2
|
||||||
|
|
||||||
|
// 根据用户ID进行限流
|
||||||
|
LIMIT_USER = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// LimitOption 请求限流参数
|
||||||
|
type LimitOption struct {
|
||||||
|
Time int64 `json:"time"` // 限流时间,单位秒
|
||||||
|
Count int64 `json:"count"` // 限流次数
|
||||||
|
Type int64 `json:"type"` // 限流条件类型,默认LIMIT_GLOBAL
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimit 请求限流
|
||||||
|
//
|
||||||
|
// 示例参数:middleware.LimitOption{ Time: 5, Count: 10, Type: middleware.LIMIT_IP }
|
||||||
|
//
|
||||||
|
// 参数表示:5秒内,最多请求10次,限制类型为 IP
|
||||||
|
//
|
||||||
|
// 使用 USER 时,请在用户身份授权认证校验后使用
|
||||||
|
// 以便获取登录用户信息,无用户信息时默认为 GLOBAL
|
||||||
|
func RateLimit(option LimitOption) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// 初始可选参数数据
|
||||||
|
if option.Time < 5 {
|
||||||
|
option.Time = 5
|
||||||
|
}
|
||||||
|
if option.Count < 10 {
|
||||||
|
option.Count = 10
|
||||||
|
}
|
||||||
|
if option.Type == 0 {
|
||||||
|
option.Type = LIMIT_GLOBAL
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取执行函数名称
|
||||||
|
funcName := c.HandlerName()
|
||||||
|
lastDotIndex := strings.LastIndex(funcName, "/")
|
||||||
|
funcName = funcName[lastDotIndex+1:]
|
||||||
|
// 生成限流key
|
||||||
|
var limitKey string = cachekey.RATE_LIMIT_KEY + funcName
|
||||||
|
|
||||||
|
// 用户
|
||||||
|
if option.Type == LIMIT_USER {
|
||||||
|
loginUser, err := ctx.LoginUser(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(401, result.Err(map[string]any{
|
||||||
|
"code": 401,
|
||||||
|
"msg": err.Error(),
|
||||||
|
}))
|
||||||
|
c.Abort() // 停止执行后续的处理函数
|
||||||
|
return
|
||||||
|
}
|
||||||
|
limitKey = cachekey.RATE_LIMIT_KEY + loginUser.UserID + ":" + funcName
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP
|
||||||
|
if option.Type == LIMIT_IP {
|
||||||
|
clientIP := ip2region.ClientIP(c.ClientIP())
|
||||||
|
limitKey = cachekey.RATE_LIMIT_KEY + clientIP + ":" + funcName
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在Redis查询并记录请求次数
|
||||||
|
rateCount, _ := redis.RateLimit("", limitKey, option.Time, option.Count)
|
||||||
|
rateTime, _ := redis.GetExpire("", limitKey)
|
||||||
|
|
||||||
|
// 设置响应头中的限流声明字段
|
||||||
|
c.Header("X-RateLimit-Limit", fmt.Sprintf("%d", option.Count)) // 总请求数限制
|
||||||
|
c.Header("X-RateLimit-Remaining", fmt.Sprintf("%d", option.Count-rateCount)) // 剩余可用请求数
|
||||||
|
c.Header("X-RateLimit-Reset", fmt.Sprintf("%d", time.Now().Unix()+int64(rateTime))) // 重置时间戳
|
||||||
|
|
||||||
|
if rateCount >= option.Count {
|
||||||
|
c.JSON(200, result.ErrMsg("访问过于频繁,请稍候再试"))
|
||||||
|
c.Abort() // 停止执行后续的处理函数
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用下一个处理程序
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/framework/middleware/repeat/repeat.go
Normal file
84
src/framework/middleware/repeat/repeat.go
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package repeat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/constants/cachekey"
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
"ems.agt/src/framework/redis"
|
||||||
|
"ems.agt/src/framework/utils/ctx"
|
||||||
|
"ems.agt/src/framework/utils/ip2region"
|
||||||
|
"ems.agt/src/framework/vo/result"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// repeatParam 重复提交参数的类型定义
|
||||||
|
type repeatParam struct {
|
||||||
|
Time int64 `json:"time"`
|
||||||
|
Params string `json:"params"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepeatSubmit 防止表单重复提交,小于间隔时间视为重复提交
|
||||||
|
//
|
||||||
|
// 间隔时间(单位秒) 默认:5
|
||||||
|
//
|
||||||
|
// 注意之后JSON反序列使用:c.ShouldBindBodyWith(¶ms, binding.JSON)
|
||||||
|
func RepeatSubmit(interval int64) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
if interval < 5 {
|
||||||
|
interval = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交参数
|
||||||
|
params := ctx.RequestParamsMap(c)
|
||||||
|
paramsJSONByte, err := json.Marshal(params)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("RepeatSubmit params json marshal err: %v", err)
|
||||||
|
}
|
||||||
|
paramsJSONStr := string(paramsJSONByte)
|
||||||
|
|
||||||
|
// 唯一标识(指定key + 客户端IP + 请求地址)
|
||||||
|
clientIP := ip2region.ClientIP(c.ClientIP())
|
||||||
|
repeatKey := cachekey.REPEAT_SUBMIT_KEY + clientIP + ":" + c.Request.RequestURI
|
||||||
|
|
||||||
|
// 在Redis查询并记录请求次数
|
||||||
|
repeatStr, _ := redis.Get("", repeatKey)
|
||||||
|
if repeatStr != "" {
|
||||||
|
var rp repeatParam
|
||||||
|
err := json.Unmarshal([]byte(repeatStr), &rp)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("RepeatSubmit repeatStr json unmarshal err: %v", err)
|
||||||
|
}
|
||||||
|
compareTime := time.Now().Unix() - rp.Time
|
||||||
|
compareParams := rp.Params == paramsJSONStr
|
||||||
|
|
||||||
|
// 设置重复提交声明响应头(毫秒)
|
||||||
|
c.Header("X-RepeatSubmit-Rest", strconv.FormatInt(time.Now().Add(time.Duration(compareTime)*time.Second).UnixNano()/int64(time.Millisecond), 10))
|
||||||
|
|
||||||
|
// 小于间隔时间且参数内容一致
|
||||||
|
if compareTime < interval && compareParams {
|
||||||
|
c.JSON(200, result.ErrMsg("不允许重复提交,请稍候再试"))
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前请求参数
|
||||||
|
rp := repeatParam{
|
||||||
|
Time: time.Now().Unix(),
|
||||||
|
Params: paramsJSONStr,
|
||||||
|
}
|
||||||
|
rpJSON, err := json.Marshal(rp)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("RepeatSubmit rp json marshal err: %v", err)
|
||||||
|
}
|
||||||
|
// 保存请求时间和参数
|
||||||
|
redis.SetByExpire("", repeatKey, string(rpJSON), time.Duration(interval)*time.Second)
|
||||||
|
|
||||||
|
// 调用下一个处理程序
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/framework/middleware/report.go
Normal file
23
src/framework/middleware/report.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Report 请求响应日志
|
||||||
|
func Report() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
// 调用下一个处理程序
|
||||||
|
c.Next()
|
||||||
|
|
||||||
|
// 计算请求处理时间,并打印日志
|
||||||
|
duration := time.Since(start)
|
||||||
|
logger.Infof("%s %s report end=> %v", c.Request.Method, c.Request.RequestURI, duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/framework/middleware/security/csp.go
Normal file
22
src/framework/middleware/security/csp.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
"ems.agt/src/framework/utils/generate"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// csp 这将帮助防止跨站脚本攻击(XSS)。
|
||||||
|
// HTTP 响应头 Content-Security-Policy 允许站点管理者控制指定的页面加载哪些资源。
|
||||||
|
func csp(c *gin.Context) {
|
||||||
|
enable := false
|
||||||
|
if v := config.Get("security.csp.enable"); v != nil {
|
||||||
|
enable = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enable {
|
||||||
|
c.Header("x-csp-nonce", generate.Code(8))
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/framework/middleware/security/hsts.go
Normal file
37
src/framework/middleware/security/hsts.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// hsts 是一个安全功能 HTTP Strict Transport Security(通常简称为 HSTS )
|
||||||
|
// 它告诉浏览器只能通过 HTTPS 访问当前资源,而不是 HTTP。
|
||||||
|
func hsts(c *gin.Context) {
|
||||||
|
enable := false
|
||||||
|
if v := config.Get("security.hsts.enable"); v != nil {
|
||||||
|
enable = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
maxAge := 365 * 24 * 3600
|
||||||
|
if v := config.Get("security.hsts.maxAge"); v != nil {
|
||||||
|
maxAge = v.(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
includeSubdomains := false
|
||||||
|
if v := config.Get("security.hsts.includeSubdomains"); v != nil {
|
||||||
|
includeSubdomains = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
str := fmt.Sprintf("max-age=%d", maxAge)
|
||||||
|
if includeSubdomains {
|
||||||
|
str += "; includeSubdomains"
|
||||||
|
}
|
||||||
|
|
||||||
|
if enable {
|
||||||
|
c.Header("strict-transport-security", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/framework/middleware/security/noopen.go
Normal file
20
src/framework/middleware/security/noopen.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// noopen 用于指定 IE 8 以上版本的用户不打开文件而直接保存文件。
|
||||||
|
// 在下载对话框中不显式“打开”选项。
|
||||||
|
func noopen(c *gin.Context) {
|
||||||
|
enable := false
|
||||||
|
if v := config.Get("security.noopen.enable"); v != nil {
|
||||||
|
enable = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enable {
|
||||||
|
c.Header("x-download-options", "noopen")
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/framework/middleware/security/nosniff.go
Normal file
26
src/framework/middleware/security/nosniff.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// nosniff 用于防止 XSS 等跨站脚本攻击
|
||||||
|
// 如果从 script 或 stylesheet 读入的文件的 MIME 类型与指定 MIME 类型不匹配,不允许读取该文件。
|
||||||
|
func nosniff(c *gin.Context) {
|
||||||
|
// 排除状态码范围
|
||||||
|
status := c.Writer.Status()
|
||||||
|
if status >= 300 && status <= 308 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
enable := false
|
||||||
|
if v := config.Get("security.nosniff.enable"); v != nil {
|
||||||
|
enable = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enable {
|
||||||
|
c.Header("x-content-type-options", "nosniff")
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/framework/middleware/security/referer.go
Normal file
74
src/framework/middleware/security/referer.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
"ems.agt/src/framework/vo/result"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// referer 配置 referer 的 host 部分
|
||||||
|
func referer(c *gin.Context) {
|
||||||
|
enable := false
|
||||||
|
if v := config.Get("security.csrf.enable"); v != nil {
|
||||||
|
enable = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// csrf 校验类型
|
||||||
|
okType := false
|
||||||
|
if v := config.Get("security.csrf.type"); v != nil {
|
||||||
|
vType := v.(string)
|
||||||
|
if vType == "all" || vType == "any" || vType == "referer" {
|
||||||
|
okType = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !okType {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 忽略请求方法
|
||||||
|
method := c.Request.Method
|
||||||
|
ignoreMethods := []string{"GET", "HEAD", "OPTIONS", "TRACE"}
|
||||||
|
for _, ignore := range ignoreMethods {
|
||||||
|
if ignore == method {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
referer := c.GetHeader("Referer")
|
||||||
|
if referer == "" {
|
||||||
|
c.AbortWithStatusJSON(200, result.ErrMsg("无效 Referer 未知"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取host
|
||||||
|
u, err := url.Parse(referer)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatusJSON(200, result.ErrMsg("无效 Referer 未知"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
host := u.Host
|
||||||
|
|
||||||
|
// 允许的来源白名单
|
||||||
|
refererWhiteList := make([]string, 0)
|
||||||
|
if v := config.Get("security.csrf.refererWhiteList"); v != nil {
|
||||||
|
for _, s := range v.([]any) {
|
||||||
|
refererWhiteList = append(refererWhiteList, s.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if enable && okType {
|
||||||
|
ok := false
|
||||||
|
for _, domain := range refererWhiteList {
|
||||||
|
if domain == host {
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
c.AbortWithStatusJSON(200, result.ErrMsg("无效 Referer "+host))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/framework/middleware/security/security.go
Normal file
23
src/framework/middleware/security/security.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Security 安全
|
||||||
|
func Security() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// 拦截,判断是否有效Referer
|
||||||
|
referer(c)
|
||||||
|
|
||||||
|
// 无拦截,仅仅设置响应头
|
||||||
|
xframe(c)
|
||||||
|
csp(c)
|
||||||
|
hsts(c)
|
||||||
|
noopen(c)
|
||||||
|
nosniff(c)
|
||||||
|
xssProtection(c)
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/framework/middleware/security/xframe.go
Normal file
26
src/framework/middleware/security/xframe.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// xframe 用来配置 X-Frame-Options 响应头
|
||||||
|
// 用来给浏览器指示允许一个页面可否在 frame, iframe, embed 或者 object 中展现的标记。
|
||||||
|
// 站点可以通过确保网站没有被嵌入到别人的站点里面,从而避免 clickjacking 攻击。
|
||||||
|
func xframe(c *gin.Context) {
|
||||||
|
enable := false
|
||||||
|
if v := config.Get("security.xframe.enable"); v != nil {
|
||||||
|
enable = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
value := "sameorigin"
|
||||||
|
if v := config.Get("security.xframe.value"); v != nil {
|
||||||
|
value = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enable {
|
||||||
|
c.Header("x-frame-options", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/framework/middleware/security/xss_protection.go
Normal file
24
src/framework/middleware/security/xss_protection.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// xssProtection 用于启用浏览器的XSS过滤功能,以防止 XSS 跨站脚本攻击。
|
||||||
|
func xssProtection(c *gin.Context) {
|
||||||
|
enable := false
|
||||||
|
if v := config.Get("security.xssProtection.enable"); v != nil {
|
||||||
|
enable = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
value := "1; mode=block"
|
||||||
|
if v := config.Get("security.xssProtection.value"); v != nil {
|
||||||
|
value = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enable {
|
||||||
|
c.Header("x-xss-protection", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
358
src/framework/redis/redis.go
Normal file
358
src/framework/redis/redis.go
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
package redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Redis连接实例
|
||||||
|
var rdbMap = make(map[string]*redis.Client)
|
||||||
|
|
||||||
|
// 声明定义限流脚本命令
|
||||||
|
var rateLimitCommand = redis.NewScript(`
|
||||||
|
local key = KEYS[1]
|
||||||
|
local time = tonumber(ARGV[1])
|
||||||
|
local count = tonumber(ARGV[2])
|
||||||
|
local current = redis.call('get', key);
|
||||||
|
if current and tonumber(current) >= count then
|
||||||
|
return tonumber(current);
|
||||||
|
end
|
||||||
|
current = redis.call('incr', key)
|
||||||
|
if tonumber(current) == 1 then
|
||||||
|
redis.call('expire', key, time)
|
||||||
|
end
|
||||||
|
return tonumber(current);`)
|
||||||
|
|
||||||
|
// 连接Redis实例
|
||||||
|
func Connect() {
|
||||||
|
ctx := context.Background()
|
||||||
|
// 读取数据源配置
|
||||||
|
datasource := config.Get("redis.dataSource").(map[string]any)
|
||||||
|
for k, v := range datasource {
|
||||||
|
client := v.(map[string]any)
|
||||||
|
// 创建连接
|
||||||
|
address := fmt.Sprintf("%s:%d", client["host"], client["port"])
|
||||||
|
rdb := redis.NewClient(&redis.Options{
|
||||||
|
Addr: address,
|
||||||
|
Password: client["password"].(string),
|
||||||
|
DB: client["db"].(int),
|
||||||
|
})
|
||||||
|
// 测试数据库连接
|
||||||
|
pong, err := rdb.Ping(ctx).Result()
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("Ping redis %s is %v", k, err)
|
||||||
|
}
|
||||||
|
logger.Infof("redis %s %s %d connection is successful.", k, pong, client["db"].(int))
|
||||||
|
rdbMap[k] = rdb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭Redis实例
|
||||||
|
func Close() {
|
||||||
|
for _, rdb := range rdbMap {
|
||||||
|
if err := rdb.Close(); err != nil {
|
||||||
|
logger.Errorf("fatal error db close: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取默认实例
|
||||||
|
func DefaultRDB() *redis.Client {
|
||||||
|
source := config.Get("redis.defaultDataSourceName").(string)
|
||||||
|
return rdbMap[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取实例
|
||||||
|
func RDB(source string) *redis.Client {
|
||||||
|
return rdbMap[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info 获取redis服务信息
|
||||||
|
func Info(source string) map[string]map[string]string {
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
info, err := rdb.Info(ctx).Result()
|
||||||
|
if err != nil {
|
||||||
|
return map[string]map[string]string{}
|
||||||
|
}
|
||||||
|
infoObj := make(map[string]map[string]string)
|
||||||
|
lines := strings.Split(info, "\r\n")
|
||||||
|
label := ""
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.Contains(line, "#") {
|
||||||
|
label = strings.Fields(line)[len(strings.Fields(line))-1]
|
||||||
|
label = strings.ToLower(label)
|
||||||
|
infoObj[label] = make(map[string]string)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
kvArr := strings.Split(line, ":")
|
||||||
|
if len(kvArr) >= 2 {
|
||||||
|
key := strings.TrimSpace(kvArr[0])
|
||||||
|
value := strings.TrimSpace(kvArr[len(kvArr)-1])
|
||||||
|
infoObj[label][key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return infoObj
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeySize 获取redis当前连接可用键Key总数信息
|
||||||
|
func KeySize(source string) int64 {
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
size, err := rdb.DBSize(ctx).Result()
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandStats 获取redis命令状态信息
|
||||||
|
func CommandStats(source string) []map[string]string {
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
commandstats, err := rdb.Info(ctx, "commandstats").Result()
|
||||||
|
if err != nil {
|
||||||
|
return []map[string]string{}
|
||||||
|
}
|
||||||
|
statsObjArr := make([]map[string]string, 0)
|
||||||
|
lines := strings.Split(commandstats, "\r\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if !strings.HasPrefix(line, "cmdstat_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
kvArr := strings.Split(line, ":")
|
||||||
|
key := kvArr[0]
|
||||||
|
valueStr := kvArr[len(kvArr)-1]
|
||||||
|
statsObj := make(map[string]string)
|
||||||
|
statsObj["name"] = key[8:]
|
||||||
|
statsObj["value"] = valueStr[6:strings.Index(valueStr, ",usec=")]
|
||||||
|
statsObjArr = append(statsObjArr, statsObj)
|
||||||
|
}
|
||||||
|
return statsObjArr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取键的剩余有效时间(秒)
|
||||||
|
func GetExpire(source string, key string) (float64, error) {
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
ttl, err := rdb.TTL(ctx, key).Result()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return ttl.Seconds(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得缓存数据的key列表
|
||||||
|
func GetKeys(source string, pattern string) ([]string, error) {
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化变量
|
||||||
|
var keys []string
|
||||||
|
var cursor uint64 = 0
|
||||||
|
ctx := context.Background()
|
||||||
|
// 循环遍历获取匹配的键
|
||||||
|
for {
|
||||||
|
// 使用 SCAN 命令获取匹配的键
|
||||||
|
batchKeys, nextCursor, err := rdb.Scan(ctx, cursor, pattern, 100).Result()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Failed to scan keys: %v", err)
|
||||||
|
return keys, err
|
||||||
|
}
|
||||||
|
cursor = nextCursor
|
||||||
|
keys = append(keys, batchKeys...)
|
||||||
|
// 当 cursor 为 0,表示遍历完成
|
||||||
|
if cursor == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量获得缓存数据
|
||||||
|
func GetBatch(source string, keys []string) ([]any, error) {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return []any{}, fmt.Errorf("not keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取缓存数据
|
||||||
|
result, err := rdb.MGet(context.Background(), keys...).Result()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Failed to get batch data: %v", err)
|
||||||
|
return []any{}, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得缓存数据
|
||||||
|
func Get(source, key string) (string, error) {
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
value, err := rdb.Get(ctx, key).Result()
|
||||||
|
if err == redis.Nil || err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得缓存数据Hash
|
||||||
|
func GetHash(source, key string) (map[string]string, error) {
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
value, err := rdb.HGetAll(ctx, key).Result()
|
||||||
|
if err == redis.Nil || err != nil {
|
||||||
|
return map[string]string{}, err
|
||||||
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否存在
|
||||||
|
func Has(source string, keys ...string) (bool, error) {
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
exists, err := rdb.Exists(ctx, keys...).Result()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return exists >= 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置缓存数据
|
||||||
|
func Set(source, key string, value any) (bool, error) {
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
err := rdb.Set(ctx, key, value, 0).Err()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("redis Set err %v", err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置缓存数据与过期时间
|
||||||
|
func SetByExpire(source, key string, value any, expiration time.Duration) (bool, error) {
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
err := rdb.Set(ctx, key, value, expiration).Err()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("redis SetByExpire err %v", err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除单个
|
||||||
|
func Del(source string, key string) (bool, error) {
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
err := rdb.Del(ctx, key).Err()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("redis Del err %v", err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除多个
|
||||||
|
func DelKeys(source string, keys []string) (bool, error) {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return false, fmt.Errorf("no keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
err := rdb.Del(ctx, keys...).Err()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("redis DelKeys err %v", err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限流查询并记录
|
||||||
|
func RateLimit(source, limitKey string, time, count int64) (int64, error) {
|
||||||
|
// 数据源
|
||||||
|
rdb := DefaultRDB()
|
||||||
|
if source != "" {
|
||||||
|
rdb = RDB(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
result, err := rateLimitCommand.Run(ctx, rdb, []string{limitKey}, time, count).Result()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("redis RateLimit err %v", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return result.(int64), err
|
||||||
|
}
|
||||||
20
src/framework/utils/crypto/crypto.go
Normal file
20
src/framework/utils/crypto/crypto.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BcryptHash Bcrypt密码加密
|
||||||
|
func BcryptHash(originStr string) string {
|
||||||
|
hash, err := bcrypt.GenerateFromPassword([]byte(originStr), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BcryptCompare Bcrypt密码匹配检查
|
||||||
|
func BcryptCompare(originStr, hashStr string) bool {
|
||||||
|
err := bcrypt.CompareHashAndPassword([]byte(hashStr), []byte(originStr))
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
199
src/framework/utils/ctx/ctx.go
Normal file
199
src/framework/utils/ctx/ctx.go
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
package ctx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
"ems.agt/src/framework/constants/common"
|
||||||
|
"ems.agt/src/framework/constants/roledatascope"
|
||||||
|
"ems.agt/src/framework/constants/token"
|
||||||
|
"ems.agt/src/framework/utils/ip2region"
|
||||||
|
"ems.agt/src/framework/utils/ua"
|
||||||
|
"ems.agt/src/framework/vo"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
)
|
||||||
|
|
||||||
|
// QueryMap 查询参数转换Map
|
||||||
|
func QueryMap(c *gin.Context) map[string]any {
|
||||||
|
queryValues := c.Request.URL.Query()
|
||||||
|
queryParams := make(map[string]any)
|
||||||
|
for key, values := range queryValues {
|
||||||
|
queryParams[key] = values[0]
|
||||||
|
}
|
||||||
|
return queryParams
|
||||||
|
}
|
||||||
|
|
||||||
|
// BodyJSONMap JSON参数转换Map
|
||||||
|
func BodyJSONMap(c *gin.Context) map[string]any {
|
||||||
|
params := make(map[string]any)
|
||||||
|
c.ShouldBindBodyWith(¶ms, binding.JSON)
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestParamsMap 请求参数转换Map
|
||||||
|
func RequestParamsMap(c *gin.Context) map[string]any {
|
||||||
|
params := make(map[string]any)
|
||||||
|
// json
|
||||||
|
if strings.HasPrefix(c.ContentType(), "application/json") {
|
||||||
|
c.ShouldBindBodyWith(¶ms, binding.JSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单
|
||||||
|
bodyParams := c.Request.PostForm
|
||||||
|
for key, value := range bodyParams {
|
||||||
|
params[key] = value[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
queryParams := c.Request.URL.Query()
|
||||||
|
for key, value := range queryParams {
|
||||||
|
params[key] = value[0]
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAddrLocation 解析ip地址
|
||||||
|
func IPAddrLocation(c *gin.Context) (string, string) {
|
||||||
|
ip := ip2region.ClientIP(c.ClientIP())
|
||||||
|
location := ip2region.RealAddressByIp(ip)
|
||||||
|
return ip, location
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorization 解析请求头
|
||||||
|
func Authorization(c *gin.Context) string {
|
||||||
|
authHeader := c.GetHeader(token.HEADER_KEY)
|
||||||
|
if authHeader == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// 拆分 Authorization 请求头,提取 JWT 令牌部分
|
||||||
|
arr := strings.Split(authHeader, token.HEADER_PREFIX)
|
||||||
|
if len(arr) == 2 && arr[1] == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return arr[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// UaOsBrowser 解析请求用户代理信息
|
||||||
|
func UaOsBrowser(c *gin.Context) (string, string) {
|
||||||
|
userAgent := c.GetHeader("user-agent")
|
||||||
|
uaInfo := ua.Info(userAgent)
|
||||||
|
|
||||||
|
browser := "未知 未知"
|
||||||
|
bName, bVersion := uaInfo.Browser()
|
||||||
|
if bName != "" && bVersion != "" {
|
||||||
|
browser = bName + " " + bVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
os := "未知 未知"
|
||||||
|
bos := uaInfo.OS()
|
||||||
|
if bos != "" {
|
||||||
|
os = bos
|
||||||
|
}
|
||||||
|
return os, browser
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginUser 登录用户信息
|
||||||
|
func LoginUser(c *gin.Context) (vo.LoginUser, error) {
|
||||||
|
value, exists := c.Get(common.CTX_LOGIN_USER)
|
||||||
|
if exists {
|
||||||
|
return value.(vo.LoginUser), nil
|
||||||
|
}
|
||||||
|
return vo.LoginUser{}, fmt.Errorf("无效登录用户信息")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginUserToUserID 登录用户信息-用户ID
|
||||||
|
func LoginUserToUserID(c *gin.Context) string {
|
||||||
|
value, exists := c.Get(common.CTX_LOGIN_USER)
|
||||||
|
if exists {
|
||||||
|
loginUser := value.(vo.LoginUser)
|
||||||
|
return loginUser.UserID
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginUserToUserName 登录用户信息-用户名称
|
||||||
|
func LoginUserToUserName(c *gin.Context) string {
|
||||||
|
value, exists := c.Get(common.CTX_LOGIN_USER)
|
||||||
|
if exists {
|
||||||
|
loginUser := value.(vo.LoginUser)
|
||||||
|
return loginUser.User.UserName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginUserToDataScopeSQL 登录用户信息-角色数据范围过滤SQL字符串
|
||||||
|
func LoginUserToDataScopeSQL(c *gin.Context, deptAlias string, userAlias string) string {
|
||||||
|
dataScopeSQL := ""
|
||||||
|
// 登录用户信息
|
||||||
|
loginUser, err := LoginUser(c)
|
||||||
|
if err != nil {
|
||||||
|
return dataScopeSQL
|
||||||
|
}
|
||||||
|
userInfo := loginUser.User
|
||||||
|
|
||||||
|
// 如果是管理员,则不过滤数据
|
||||||
|
if config.IsAdmin(userInfo.UserID) {
|
||||||
|
return dataScopeSQL
|
||||||
|
}
|
||||||
|
// 无用户角色
|
||||||
|
if len(userInfo.Roles) <= 0 {
|
||||||
|
return dataScopeSQL
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录角色权限范围定义添加过, 非自定数据权限不需要重复拼接SQL
|
||||||
|
var scopeKeys []string
|
||||||
|
var conditions []string
|
||||||
|
for _, role := range userInfo.Roles {
|
||||||
|
dataScope := role.DataScope
|
||||||
|
|
||||||
|
if roledatascope.ALL == dataScope {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if roledatascope.CUSTOM != dataScope {
|
||||||
|
hasKey := false
|
||||||
|
for _, key := range scopeKeys {
|
||||||
|
if key == dataScope {
|
||||||
|
hasKey = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hasKey {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if roledatascope.CUSTOM == dataScope {
|
||||||
|
sql := fmt.Sprintf(`%s.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = '%s' )`, deptAlias, role.RoleID)
|
||||||
|
conditions = append(conditions, sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
if roledatascope.DEPT_AND_CHILD == dataScope {
|
||||||
|
sql := fmt.Sprintf(`%s.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = '%s' or find_in_set('%s' , ancestors ) )`, deptAlias, userInfo.DeptID, userInfo.DeptID)
|
||||||
|
conditions = append(conditions, sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
if roledatascope.SELF == dataScope {
|
||||||
|
// 数据权限为仅本人且没有userAlias别名不查询任何数据
|
||||||
|
if userAlias == "" {
|
||||||
|
sql := fmt.Sprintf(`%s.dept_id = '0'`, deptAlias)
|
||||||
|
conditions = append(conditions, sql)
|
||||||
|
} else {
|
||||||
|
sql := fmt.Sprintf(`%s.user_id = '%s'`, userAlias, userInfo.UserID)
|
||||||
|
conditions = append(conditions, sql)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录角色范围
|
||||||
|
scopeKeys = append(scopeKeys, dataScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建查询条件语句
|
||||||
|
if len(conditions) > 0 {
|
||||||
|
dataScopeSQL = fmt.Sprintf(" AND ( %s ) ", strings.Join(conditions, " OR "))
|
||||||
|
}
|
||||||
|
return dataScopeSQL
|
||||||
|
}
|
||||||
69
src/framework/utils/date/date.go
Normal file
69
src/framework/utils/date/date.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package date
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 年 列如:2022
|
||||||
|
YYYY = "2006"
|
||||||
|
// 年-月 列如:2022-12
|
||||||
|
YYYY_MM = "2006-01"
|
||||||
|
// 年-月-日 列如:2022-12-30
|
||||||
|
YYYY_MM_DD = "2006-01-02"
|
||||||
|
// 年月日时分秒 列如:20221230010159
|
||||||
|
YYYYMMDDHHMMSS = "20060102150405"
|
||||||
|
// 年-月-日 时:分:秒 列如:2022-12-30 01:01:59
|
||||||
|
YYYY_MM_DD_HH_MM_SS = "2006-01-02 15:04:05"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 格式时间字符串
|
||||||
|
//
|
||||||
|
// dateStr 时间字符串
|
||||||
|
//
|
||||||
|
// formatStr 时间格式 默认YYYY-MM-DD HH:mm:ss
|
||||||
|
func ParseStrToDate(dateStr, formatStr string) time.Time {
|
||||||
|
t, err := time.Parse(formatStr, dateStr)
|
||||||
|
if err != nil {
|
||||||
|
logger.Infof("utils ParseStrToDate err %v", err)
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式时间
|
||||||
|
//
|
||||||
|
// date 可转的Date对象
|
||||||
|
//
|
||||||
|
// formatStr 时间格式 默认YYYY-MM-DD HH:mm:ss
|
||||||
|
func ParseDateToStr(date any, formatStr string) string {
|
||||||
|
t, ok := date.(time.Time)
|
||||||
|
if !ok {
|
||||||
|
switch v := date.(type) {
|
||||||
|
case int64:
|
||||||
|
if v == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
t = time.UnixMilli(v)
|
||||||
|
case string:
|
||||||
|
parsedTime, err := time.Parse(formatStr, v)
|
||||||
|
if err != nil {
|
||||||
|
logger.Infof("utils ParseDateToStr err %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
t = parsedTime
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t.Format(formatStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式时间成日期路径
|
||||||
|
//
|
||||||
|
// 年/月 列如:2022/12
|
||||||
|
func ParseDatePath(date time.Time) string {
|
||||||
|
return date.Format("2006/01")
|
||||||
|
}
|
||||||
154
src/framework/utils/file/excel.go
Normal file
154
src/framework/utils/file/excel.go
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"mime/multipart"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/constants/uploadsubpath"
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
"ems.agt/src/framework/utils/date"
|
||||||
|
|
||||||
|
"github.com/xuri/excelize/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TransferExeclUploadFile 表格文件上传保存
|
||||||
|
//
|
||||||
|
// file 上传文件对象
|
||||||
|
func TransferExeclUploadFile(file *multipart.FileHeader) (string, error) {
|
||||||
|
// 上传文件检查
|
||||||
|
err := isAllowWrite(file.Filename, []string{".xls", ".xlsx"}, file.Size)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// 上传资源路径
|
||||||
|
_, dir := resourceUpload()
|
||||||
|
// 新文件名称并组装文件地址
|
||||||
|
filePath := filepath.Join(uploadsubpath.IMPORT, date.ParseDatePath(time.Now()))
|
||||||
|
fileName := generateFileName(file.Filename)
|
||||||
|
writePathFile := filepath.Join(dir, filePath, fileName)
|
||||||
|
// 存入新文件路径
|
||||||
|
err = transferToNewFile(file, writePathFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.ToSlash(writePathFile), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格读取数据
|
||||||
|
//
|
||||||
|
// filePath 文件路径地址
|
||||||
|
//
|
||||||
|
// sheetName 工作簿名称, 空字符默认Sheet1
|
||||||
|
func ReadSheet(filePath, sheetName string) ([]map[string]string, error) {
|
||||||
|
data := make([]map[string]string, 0)
|
||||||
|
// 打开 Excel 文件
|
||||||
|
f, err := excelize.OpenFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
logger.Errorf("工作表文件关闭失败 : %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 检查工作簿是否存在
|
||||||
|
if sheetName == "" {
|
||||||
|
sheetName = "Sheet1"
|
||||||
|
}
|
||||||
|
if visible, _ := f.GetSheetVisible(sheetName); !visible {
|
||||||
|
return data, fmt.Errorf("读取工作簿 %s 失败", sheetName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取工作簿记录
|
||||||
|
rows, err := f.GetRows(sheetName)
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, row := range rows {
|
||||||
|
// 跳过第一行
|
||||||
|
if i == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 遍历每个单元格
|
||||||
|
rowData := map[string]string{}
|
||||||
|
for columnIndex, cellValue := range row {
|
||||||
|
columnName, _ := excelize.ColumnNumberToName(columnIndex + 1)
|
||||||
|
rowData[columnName] = cellValue
|
||||||
|
}
|
||||||
|
|
||||||
|
data = append(data, rowData)
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格写入数据
|
||||||
|
//
|
||||||
|
// headerCells 第一行表头标题 "A1":"?"
|
||||||
|
//
|
||||||
|
// dataCells 从第二行开始的数据 "A2":"?"
|
||||||
|
//
|
||||||
|
// fileName 文件名称
|
||||||
|
//
|
||||||
|
// sheetName 工作簿名称, 空字符默认Sheet1
|
||||||
|
func WriteSheet(headerCells map[string]string, dataCells []map[string]any, fileName, sheetName string) (string, error) {
|
||||||
|
f := excelize.NewFile()
|
||||||
|
defer func() {
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
logger.Errorf("工作表文件关闭失败 : %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 创建一个工作表
|
||||||
|
if sheetName == "" {
|
||||||
|
sheetName = "Sheet1"
|
||||||
|
}
|
||||||
|
index, err := f.NewSheet(sheetName)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("创建工作表失败 %v", err)
|
||||||
|
}
|
||||||
|
// 设置工作簿的默认工作表
|
||||||
|
f.SetActiveSheet(index)
|
||||||
|
|
||||||
|
// 首个和最后key名称
|
||||||
|
firstKey := "A"
|
||||||
|
lastKey := "B"
|
||||||
|
|
||||||
|
// 第一行表头标题
|
||||||
|
for key, title := range headerCells {
|
||||||
|
f.SetCellValue(sheetName, key, title)
|
||||||
|
if key[:1] > lastKey {
|
||||||
|
lastKey = key[:1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置工作表上宽度为 20
|
||||||
|
f.SetColWidth(sheetName, firstKey, lastKey, 20)
|
||||||
|
|
||||||
|
// 从第二行开始的数据
|
||||||
|
for _, cell := range dataCells {
|
||||||
|
for key, value := range cell {
|
||||||
|
f.SetCellValue(sheetName, key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传资源路径
|
||||||
|
_, dir := resourceUpload()
|
||||||
|
filePath := filepath.Join(uploadsubpath.EXPORT, date.ParseDatePath(time.Now()))
|
||||||
|
saveFilePath := filepath.Join(dir, filePath, fileName)
|
||||||
|
|
||||||
|
// 创建文件目录
|
||||||
|
if err := os.MkdirAll(filepath.Dir(saveFilePath), 0750); err != nil {
|
||||||
|
return "", fmt.Errorf("创建保存文件失败 %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据指定路径保存文件
|
||||||
|
if err := f.SaveAs(saveFilePath); err != nil {
|
||||||
|
return "", fmt.Errorf("保存工作表失败 %v", err)
|
||||||
|
}
|
||||||
|
return saveFilePath, nil
|
||||||
|
}
|
||||||
297
src/framework/utils/file/file.go
Normal file
297
src/framework/utils/file/file.go
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"mime/multipart"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
"ems.agt/src/framework/constants/uploadsubpath"
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
"ems.agt/src/framework/utils/date"
|
||||||
|
"ems.agt/src/framework/utils/generate"
|
||||||
|
"ems.agt/src/framework/utils/parse"
|
||||||
|
"ems.agt/src/framework/utils/regular"
|
||||||
|
)
|
||||||
|
|
||||||
|
/**最大文件名长度 */
|
||||||
|
const DEFAULT_FILE_NAME_LENGTH = 100
|
||||||
|
|
||||||
|
// 文件上传路径 prefix, dir
|
||||||
|
func resourceUpload() (string, string) {
|
||||||
|
upload := config.Get("staticFile.upload").(map[string]any)
|
||||||
|
dir, err := filepath.Abs(upload["dir"].(string))
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("file resourceUpload err %v", err)
|
||||||
|
}
|
||||||
|
return upload["prefix"].(string), dir
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最大上传文件大小
|
||||||
|
func uploadFileSize() int64 {
|
||||||
|
fileSize := 1 * 1024 * 1024
|
||||||
|
size := config.Get("upload.fileSize").(int)
|
||||||
|
if size > 1 {
|
||||||
|
fileSize = size * 1024 * 1024
|
||||||
|
}
|
||||||
|
return int64(fileSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件上传扩展名白名单
|
||||||
|
func uploadWhiteList() []string {
|
||||||
|
arr := config.Get("upload.whitelist").([]any)
|
||||||
|
strings := make([]string, len(arr))
|
||||||
|
for i, val := range arr {
|
||||||
|
if str, ok := val.(string); ok {
|
||||||
|
strings[i] = str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成文件名称 fileName_随机值.extName
|
||||||
|
//
|
||||||
|
// fileName 原始文件名称含后缀,如:logo.png
|
||||||
|
func generateFileName(fileName string) string {
|
||||||
|
fileExt := filepath.Ext(fileName)
|
||||||
|
// 替换掉后缀和特殊字符保留文件名
|
||||||
|
newFileName := regular.Replace(fileName, fileExt, "")
|
||||||
|
newFileName = regular.Replace(newFileName, `[<>:"\\|?*]+`, "")
|
||||||
|
newFileName = strings.TrimSpace(newFileName)
|
||||||
|
return fmt.Sprintf("%s_%s%s", newFileName, generate.Code(6), fileExt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件允许写入本地
|
||||||
|
//
|
||||||
|
// fileName 原始文件名称含后缀,如:midway1_logo_iipc68.png
|
||||||
|
//
|
||||||
|
// allowExts 允许上传拓展类型,['.png']
|
||||||
|
func isAllowWrite(fileName string, allowExts []string, fileSize int64) error {
|
||||||
|
// 判断上传文件名称长度
|
||||||
|
if len(fileName) > DEFAULT_FILE_NAME_LENGTH {
|
||||||
|
return fmt.Errorf("上传文件名称长度限制最长为 %d", DEFAULT_FILE_NAME_LENGTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最大上传文件大小
|
||||||
|
maxFileSize := uploadFileSize()
|
||||||
|
if fileSize > maxFileSize {
|
||||||
|
return fmt.Errorf("最大上传文件大小 %s", parse.Bit(float64(maxFileSize)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断文件拓展是否为允许的拓展类型
|
||||||
|
fileExt := filepath.Ext(fileName)
|
||||||
|
hasExt := false
|
||||||
|
if len(allowExts) == 0 {
|
||||||
|
allowExts = uploadWhiteList()
|
||||||
|
}
|
||||||
|
for _, ext := range allowExts {
|
||||||
|
if ext == fileExt {
|
||||||
|
hasExt = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasExt {
|
||||||
|
return fmt.Errorf("上传文件类型不支持,仅支持以下类型:%s", strings.Join(allowExts, "、"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件允许本地读取
|
||||||
|
//
|
||||||
|
// filePath 文件存放资源路径,URL相对地址
|
||||||
|
func isAllowRead(filePath string) error {
|
||||||
|
// 禁止目录上跳级别
|
||||||
|
if strings.Contains(filePath, "..") {
|
||||||
|
return fmt.Errorf("禁止目录上跳级别")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查允许下载的文件规则
|
||||||
|
fileExt := filepath.Ext(filePath)
|
||||||
|
hasExt := false
|
||||||
|
for _, ext := range uploadWhiteList() {
|
||||||
|
if ext == fileExt {
|
||||||
|
hasExt = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasExt {
|
||||||
|
return fmt.Errorf("非法下载的文件规则:%s", fileExt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransferUploadFile 上传资源文件转存
|
||||||
|
//
|
||||||
|
// subPath 子路径,默认 UploadSubPath.DEFAULT
|
||||||
|
//
|
||||||
|
// allowExts 允许上传拓展类型(含“.”),如 ['.png','.jpg']
|
||||||
|
func TransferUploadFile(file *multipart.FileHeader, subPath string, allowExts []string) (string, error) {
|
||||||
|
// 上传文件检查
|
||||||
|
err := isAllowWrite(file.Filename, allowExts, file.Size)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// 上传资源路径
|
||||||
|
prefix, dir := resourceUpload()
|
||||||
|
// 新文件名称并组装文件地址
|
||||||
|
fileName := generateFileName(file.Filename)
|
||||||
|
filePath := filepath.Join(subPath, date.ParseDatePath(time.Now()))
|
||||||
|
writePathFile := filepath.Join(dir, filePath, fileName)
|
||||||
|
// 存入新文件路径
|
||||||
|
err = transferToNewFile(file, writePathFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
urlPath := filepath.Join(prefix, filePath, fileName)
|
||||||
|
return filepath.ToSlash(urlPath), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadUploadFileStream 上传资源文件读取
|
||||||
|
//
|
||||||
|
// filePath 文件存放资源路径,URL相对地址 如:/upload/common/2023/06/xxx.png
|
||||||
|
//
|
||||||
|
// headerRange 断点续传范围区间,bytes=0-12131
|
||||||
|
func ReadUploadFileStream(filePath, headerRange string) (map[string]any, error) {
|
||||||
|
// 读取文件检查
|
||||||
|
err := isAllowRead(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{}, err
|
||||||
|
}
|
||||||
|
// 上传资源路径
|
||||||
|
prefix, dir := resourceUpload()
|
||||||
|
fileAsbPath := strings.Replace(filePath, prefix, dir, 1)
|
||||||
|
|
||||||
|
// 响应结果
|
||||||
|
result := map[string]any{
|
||||||
|
"range": "",
|
||||||
|
"chunkSize": 0,
|
||||||
|
"fileSize": 0,
|
||||||
|
"data": nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件大小
|
||||||
|
fileSize := getFileSize(fileAsbPath)
|
||||||
|
if fileSize <= 0 {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
result["fileSize"] = fileSize
|
||||||
|
|
||||||
|
if headerRange != "" {
|
||||||
|
partsStr := strings.Replace(headerRange, "bytes=", "", 1)
|
||||||
|
parts := strings.Split(partsStr, "-")
|
||||||
|
start, err := strconv.ParseInt(parts[0], 10, 64)
|
||||||
|
if err != nil || start > fileSize {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
end, err := strconv.ParseInt(parts[1], 10, 64)
|
||||||
|
if err != nil || end > fileSize {
|
||||||
|
end = fileSize - 1
|
||||||
|
}
|
||||||
|
if start > end {
|
||||||
|
start = end
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分片结果
|
||||||
|
result["range"] = fmt.Sprintf("bytes %d-%d/%d", start, end, fileSize)
|
||||||
|
result["chunkSize"] = end - start + 1
|
||||||
|
byteArr, err := getFileStream(fileAsbPath, start, end)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{}, errors.New("读取文件失败")
|
||||||
|
}
|
||||||
|
result["data"] = byteArr
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
byteArr, err := getFileStream(fileAsbPath, 0, fileSize)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{}, errors.New("读取文件失败")
|
||||||
|
}
|
||||||
|
result["data"] = byteArr
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransferChunkUploadFile 上传资源切片文件转存
|
||||||
|
//
|
||||||
|
// file 上传文件对象
|
||||||
|
//
|
||||||
|
// index 切片文件序号
|
||||||
|
//
|
||||||
|
// identifier 切片文件目录标识符
|
||||||
|
func TransferChunkUploadFile(file *multipart.FileHeader, index, identifier string) (string, error) {
|
||||||
|
// 上传文件检查
|
||||||
|
err := isAllowWrite(file.Filename, []string{}, file.Size)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// 上传资源路径
|
||||||
|
prefix, dir := resourceUpload()
|
||||||
|
// 新文件名称并组装文件地址
|
||||||
|
filePath := filepath.Join(uploadsubpath.CHUNK, date.ParseDatePath(time.Now()), identifier)
|
||||||
|
writePathFile := filepath.Join(dir, filePath, index)
|
||||||
|
// 存入新文件路径
|
||||||
|
err = transferToNewFile(file, writePathFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
urlPath := filepath.Join(prefix, filePath, index)
|
||||||
|
return filepath.ToSlash(urlPath), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传资源切片文件检查
|
||||||
|
//
|
||||||
|
// identifier 切片文件目录标识符
|
||||||
|
//
|
||||||
|
// originalFileName 原始文件名称,如logo.png
|
||||||
|
func ChunkCheckFile(identifier, originalFileName string) ([]string, error) {
|
||||||
|
// 读取文件检查
|
||||||
|
err := isAllowWrite(originalFileName, []string{}, 0)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, err
|
||||||
|
}
|
||||||
|
// 上传资源路径
|
||||||
|
_, dir := resourceUpload()
|
||||||
|
dirPath := path.Join(uploadsubpath.CHUNK, date.ParseDatePath(time.Now()), identifier)
|
||||||
|
readPath := path.Join(dir, dirPath)
|
||||||
|
fileList, err := getDirFileNameList(readPath)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, errors.New("读取文件失败")
|
||||||
|
}
|
||||||
|
return fileList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传资源切片文件检查
|
||||||
|
//
|
||||||
|
// identifier 切片文件目录标识符
|
||||||
|
//
|
||||||
|
// originalFileName 原始文件名称,如logo.png
|
||||||
|
//
|
||||||
|
// subPath 子路径,默认 DEFAULT
|
||||||
|
func ChunkMergeFile(identifier, originalFileName, subPath string) (string, error) {
|
||||||
|
// 读取文件检查
|
||||||
|
err := isAllowWrite(originalFileName, []string{}, 0)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// 上传资源路径
|
||||||
|
prefix, dir := resourceUpload()
|
||||||
|
// 切片存放目录
|
||||||
|
dirPath := path.Join(uploadsubpath.CHUNK, date.ParseDatePath(time.Now()), identifier)
|
||||||
|
readPath := path.Join(dir, dirPath)
|
||||||
|
// 组合存放文件路径
|
||||||
|
fileName := generateFileName(originalFileName)
|
||||||
|
filePath := path.Join(subPath, date.ParseDatePath(time.Now()))
|
||||||
|
writePath := path.Join(dir, filePath)
|
||||||
|
err = mergeToNewFile(readPath, writePath, fileName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
urlPath := filepath.Join(prefix, filePath, fileName)
|
||||||
|
return filepath.ToSlash(urlPath), nil
|
||||||
|
}
|
||||||
185
src/framework/utils/file/utils.go
Normal file
185
src/framework/utils/file/utils.go
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// transferToNewFile 读取目标文件转移到新路径下
|
||||||
|
//
|
||||||
|
// readFilePath 读取目标文件
|
||||||
|
//
|
||||||
|
// writePath 写入路径
|
||||||
|
//
|
||||||
|
// fileName 文件名称
|
||||||
|
func transferToNewFile(file *multipart.FileHeader, dst string) error {
|
||||||
|
src, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
|
||||||
|
if err = os.MkdirAll(filepath.Dir(dst), 0750); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(out, src)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeToNewFile 将多个文件合并成一个文件并删除合并前的切片目录文件
|
||||||
|
//
|
||||||
|
// dirPath 读取要合并文件的目录
|
||||||
|
//
|
||||||
|
// writePath 写入路径
|
||||||
|
//
|
||||||
|
// fileName 文件名称
|
||||||
|
func mergeToNewFile(dirPath string, writePath string, fileName string) error {
|
||||||
|
// 读取目录下所有文件并排序,注意文件名称是否数值
|
||||||
|
fileNameList, err := getDirFileNameList(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("读取合并目标文件失败: %v", err)
|
||||||
|
}
|
||||||
|
if len(fileNameList) <= 0 {
|
||||||
|
return fmt.Errorf("读取合并目标文件失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序
|
||||||
|
sort.Slice(fileNameList, func(i, j int) bool {
|
||||||
|
numI, _ := strconv.Atoi(fileNameList[i])
|
||||||
|
numJ, _ := strconv.Atoi(fileNameList[j])
|
||||||
|
return numI < numJ
|
||||||
|
})
|
||||||
|
|
||||||
|
// 写入到新路径文件
|
||||||
|
newFilePath := filepath.Join(writePath, fileName)
|
||||||
|
if err = os.MkdirAll(filepath.Dir(newFilePath), 0750); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转移完成后删除切片目录
|
||||||
|
defer os.Remove(dirPath)
|
||||||
|
|
||||||
|
// 打开新路径文件
|
||||||
|
outputFile, err := os.Create(newFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create file: %v", err)
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
|
||||||
|
// 逐个读取文件后进行流拷贝数据块
|
||||||
|
for _, fileName := range fileNameList {
|
||||||
|
chunkPath := filepath.Join(dirPath, fileName)
|
||||||
|
// 拷贝结束后删除切片
|
||||||
|
defer os.Remove(chunkPath)
|
||||||
|
// 打开切片文件
|
||||||
|
inputFile, err := os.Open(chunkPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open file: %v", err)
|
||||||
|
}
|
||||||
|
defer inputFile.Close()
|
||||||
|
// 拷贝文件流
|
||||||
|
_, err = io.Copy(outputFile, inputFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to copy file data: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFileSize 读取文件大小
|
||||||
|
func getFileSize(filePath string) int64 {
|
||||||
|
// 获取文件信息
|
||||||
|
fileInfo, err := os.Stat(filePath)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Failed stat %s: %v", filePath, err)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// 获取文件大小(字节数)
|
||||||
|
return fileInfo.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件流用于返回下载
|
||||||
|
//
|
||||||
|
// filePath 文件路径
|
||||||
|
// startOffset, endOffset 分片块读取区间,根据文件切片的块范围
|
||||||
|
func getFileStream(filePath string, startOffset, endOffset int64) ([]byte, error) {
|
||||||
|
// 打开文件
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// 获取文件的大小
|
||||||
|
fileInfo, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fileSize := fileInfo.Size()
|
||||||
|
|
||||||
|
// 确保起始和结束偏移量在文件范围内
|
||||||
|
if startOffset > fileSize {
|
||||||
|
startOffset = 0
|
||||||
|
}
|
||||||
|
if endOffset >= fileSize {
|
||||||
|
endOffset = fileSize - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算切片的大小
|
||||||
|
chunkSize := endOffset - startOffset + 1
|
||||||
|
|
||||||
|
// 创建 SectionReader
|
||||||
|
reader := io.NewSectionReader(file, startOffset, chunkSize)
|
||||||
|
|
||||||
|
// 创建一个缓冲区来存储读取的数据
|
||||||
|
buffer := make([]byte, chunkSize)
|
||||||
|
|
||||||
|
// 读取数据到缓冲区
|
||||||
|
_, err = reader.Read(buffer)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件目录下所有文件名称,不含目录名称
|
||||||
|
//
|
||||||
|
// filePath 文件路径
|
||||||
|
func getDirFileNameList(dirPath string) ([]string, error) {
|
||||||
|
fileNames := []string{}
|
||||||
|
|
||||||
|
dir, err := os.Open(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return fileNames, nil
|
||||||
|
}
|
||||||
|
defer dir.Close()
|
||||||
|
|
||||||
|
fileInfos, err := dir.Readdir(-1)
|
||||||
|
if err != nil {
|
||||||
|
return fileNames, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fileInfo := range fileInfos {
|
||||||
|
if fileInfo.Mode().IsRegular() {
|
||||||
|
fileNames = append(fileNames, fileInfo.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileNames, nil
|
||||||
|
}
|
||||||
43
src/framework/utils/generate/generate.go
Normal file
43
src/framework/utils/generate/generate.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package generate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
|
||||||
|
gonanoid "github.com/matoous/go-nanoid/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 生成随机Code
|
||||||
|
// 包含数字、小写字母
|
||||||
|
// 不保证长度满足
|
||||||
|
func Code(size int) string {
|
||||||
|
str, err := gonanoid.Generate("0123456789abcdefghijklmnopqrstuvwxyz", size)
|
||||||
|
if err != nil {
|
||||||
|
logger.Infof("%d : %v", size, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成随机字符串
|
||||||
|
// 包含数字、大小写字母、下划线、横杠
|
||||||
|
// 不保证长度满足
|
||||||
|
func String(size int) string {
|
||||||
|
str, err := gonanoid.New(size)
|
||||||
|
if err != nil {
|
||||||
|
logger.Infof("%d : %v", size, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// 随机数 纯数字0-9
|
||||||
|
func Number(size int) int {
|
||||||
|
source := rand.NewSource(time.Now().UnixNano())
|
||||||
|
random := rand.New(source)
|
||||||
|
min := int64(0)
|
||||||
|
max := int64(9 * int(size))
|
||||||
|
return int(random.Int63n(max-min+1) + min)
|
||||||
|
}
|
||||||
238
src/framework/utils/ip2region/binding.go
Normal file
238
src/framework/utils/ip2region/binding.go
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
package ip2region
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
HeaderInfoLength = 256
|
||||||
|
VectorIndexRows = 256
|
||||||
|
VectorIndexCols = 256
|
||||||
|
VectorIndexSize = 8
|
||||||
|
SegmentIndexBlockSize = 14
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- Index policy define
|
||||||
|
|
||||||
|
type IndexPolicy int
|
||||||
|
|
||||||
|
const (
|
||||||
|
VectorIndexPolicy IndexPolicy = 1
|
||||||
|
BTreeIndexPolicy IndexPolicy = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i IndexPolicy) String() string {
|
||||||
|
switch i {
|
||||||
|
case VectorIndexPolicy:
|
||||||
|
return "VectorIndex"
|
||||||
|
case BTreeIndexPolicy:
|
||||||
|
return "BtreeIndex"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Header define
|
||||||
|
|
||||||
|
type Header struct {
|
||||||
|
// data []byte
|
||||||
|
Version uint16
|
||||||
|
IndexPolicy IndexPolicy
|
||||||
|
CreatedAt uint32
|
||||||
|
StartIndexPtr uint32
|
||||||
|
EndIndexPtr uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHeader(input []byte) (*Header, error) {
|
||||||
|
if len(input) < 16 {
|
||||||
|
return nil, fmt.Errorf("invalid input buffer")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Header{
|
||||||
|
Version: binary.LittleEndian.Uint16(input),
|
||||||
|
IndexPolicy: IndexPolicy(binary.LittleEndian.Uint16(input[2:])),
|
||||||
|
CreatedAt: binary.LittleEndian.Uint32(input[4:]),
|
||||||
|
StartIndexPtr: binary.LittleEndian.Uint32(input[8:]),
|
||||||
|
EndIndexPtr: binary.LittleEndian.Uint32(input[12:]),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- searcher implementation
|
||||||
|
|
||||||
|
type Searcher struct {
|
||||||
|
handle *os.File
|
||||||
|
|
||||||
|
ioCount int
|
||||||
|
|
||||||
|
// use it only when this feature enabled.
|
||||||
|
// Preload the vector index will reduce the number of IO operations
|
||||||
|
// thus speedup the search process
|
||||||
|
vectorIndex []byte
|
||||||
|
|
||||||
|
// content buffer.
|
||||||
|
// running with the whole xdb file cached
|
||||||
|
contentBuff []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func baseNew(dbFile string, vIndex []byte, cBuff []byte) (*Searcher, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// content buff first
|
||||||
|
if cBuff != nil {
|
||||||
|
return &Searcher{
|
||||||
|
vectorIndex: nil,
|
||||||
|
contentBuff: cBuff,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the xdb binary file
|
||||||
|
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Searcher{
|
||||||
|
handle: handle,
|
||||||
|
vectorIndex: vIndex,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWithFileOnly(dbFile string) (*Searcher, error) {
|
||||||
|
return baseNew(dbFile, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWithVectorIndex(dbFile string, vIndex []byte) (*Searcher, error) {
|
||||||
|
return baseNew(dbFile, vIndex, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWithBuffer(cBuff []byte) (*Searcher, error) {
|
||||||
|
return baseNew("", nil, cBuff)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Searcher) Close() {
|
||||||
|
if s.handle != nil {
|
||||||
|
err := s.handle.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIOCount return the global io count for the last search
|
||||||
|
func (s *Searcher) GetIOCount() int {
|
||||||
|
return s.ioCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchByStr find the region for the specified ip string
|
||||||
|
func (s *Searcher) SearchByStr(str string) (string, error) {
|
||||||
|
ip, err := CheckIP(str)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Search(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search find the region for the specified long ip
|
||||||
|
func (s *Searcher) Search(ip uint32) (string, error) {
|
||||||
|
// reset the global ioCount
|
||||||
|
s.ioCount = 0
|
||||||
|
|
||||||
|
// locate the segment index block based on the vector index
|
||||||
|
var il0 = (ip >> 24) & 0xFF
|
||||||
|
var il1 = (ip >> 16) & 0xFF
|
||||||
|
var idx = il0*VectorIndexCols*VectorIndexSize + il1*VectorIndexSize
|
||||||
|
var sPtr, ePtr = uint32(0), uint32(0)
|
||||||
|
if s.vectorIndex != nil {
|
||||||
|
sPtr = binary.LittleEndian.Uint32(s.vectorIndex[idx:])
|
||||||
|
ePtr = binary.LittleEndian.Uint32(s.vectorIndex[idx+4:])
|
||||||
|
} else if s.contentBuff != nil {
|
||||||
|
sPtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx:])
|
||||||
|
ePtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx+4:])
|
||||||
|
} else {
|
||||||
|
// read the vector index block
|
||||||
|
var buff = make([]byte, VectorIndexSize)
|
||||||
|
err := s.read(int64(HeaderInfoLength+idx), buff)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("read vector index block at %d: %w", HeaderInfoLength+idx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sPtr = binary.LittleEndian.Uint32(buff)
|
||||||
|
ePtr = binary.LittleEndian.Uint32(buff[4:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("sPtr=%d, ePtr=%d", sPtr, ePtr)
|
||||||
|
|
||||||
|
// binary search the segment index to get the region
|
||||||
|
var dataLen, dataPtr = 0, uint32(0)
|
||||||
|
var buff = make([]byte, SegmentIndexBlockSize)
|
||||||
|
var l, h = 0, int((ePtr - sPtr) / SegmentIndexBlockSize)
|
||||||
|
for l <= h {
|
||||||
|
m := (l + h) >> 1
|
||||||
|
p := sPtr + uint32(m*SegmentIndexBlockSize)
|
||||||
|
err := s.read(int64(p), buff)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("read segment index at %d: %w", p, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode the data step by step to reduce the unnecessary operations
|
||||||
|
sip := binary.LittleEndian.Uint32(buff)
|
||||||
|
if ip < sip {
|
||||||
|
h = m - 1
|
||||||
|
} else {
|
||||||
|
eip := binary.LittleEndian.Uint32(buff[4:])
|
||||||
|
if ip > eip {
|
||||||
|
l = m + 1
|
||||||
|
} else {
|
||||||
|
dataLen = int(binary.LittleEndian.Uint16(buff[8:]))
|
||||||
|
dataPtr = binary.LittleEndian.Uint32(buff[10:])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Printf("dataLen: %d, dataPtr: %d", dataLen, dataPtr)
|
||||||
|
if dataLen == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// load and return the region data
|
||||||
|
var regionBuff = make([]byte, dataLen)
|
||||||
|
err := s.read(int64(dataPtr), regionBuff)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("read region at %d: %w", dataPtr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(regionBuff), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the data read operation based on the setting.
|
||||||
|
// content buffer first or will read from the file.
|
||||||
|
// this operation will invoke the Seek for file based read.
|
||||||
|
func (s *Searcher) read(offset int64, buff []byte) error {
|
||||||
|
if s.contentBuff != nil {
|
||||||
|
cLen := copy(buff, s.contentBuff[offset:])
|
||||||
|
if cLen != len(buff) {
|
||||||
|
return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, err := s.handle.Seek(offset, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("seek to %d: %w", offset, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ioCount++
|
||||||
|
rLen, err := s.handle.Read(buff)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("handle read: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rLen != len(buff) {
|
||||||
|
return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
89
src/framework/utils/ip2region/ip2region.go
Normal file
89
src/framework/utils/ip2region/ip2region.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package ip2region
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 网络地址(内网)
|
||||||
|
const LOCAT_HOST = "127.0.0.1"
|
||||||
|
|
||||||
|
// 全局查询对象
|
||||||
|
var searcher *Searcher
|
||||||
|
|
||||||
|
//go:embed ip2region.xdb
|
||||||
|
var ip2regionDB embed.FS
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// 从 dbPath 加载整个 xdb 到内存
|
||||||
|
buf, err := ip2regionDB.ReadFile("ip2region.xdb")
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("failed error load xdb from : %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用全局的 cBuff 创建完全基于内存的查询对象。
|
||||||
|
base, err := NewWithBuffer(buf)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("failed error create searcher with content: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 赋值到全局查询对象
|
||||||
|
searcher = base
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegionSearchByIp 查询IP所在地
|
||||||
|
//
|
||||||
|
// 国家|区域|省份|城市|ISP
|
||||||
|
func RegionSearchByIp(ip string) (string, int, int64) {
|
||||||
|
ip = ClientIP(ip)
|
||||||
|
if ip == LOCAT_HOST {
|
||||||
|
return "0|0|0|内网IP|内网IP", 0, 0
|
||||||
|
}
|
||||||
|
tStart := time.Now()
|
||||||
|
region, err := searcher.SearchByStr(ip)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("failed to SearchIP(%s): %s\n", ip, err)
|
||||||
|
return "0|0|0|0|0", 0, 0
|
||||||
|
}
|
||||||
|
return region, 0, time.Since(tStart).Milliseconds()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RealAddressByIp 地址IP所在地
|
||||||
|
//
|
||||||
|
// 218.4.167.70 江苏省 苏州市
|
||||||
|
func RealAddressByIp(ip string) string {
|
||||||
|
ip = ClientIP(ip)
|
||||||
|
if ip == LOCAT_HOST {
|
||||||
|
return "内网IP"
|
||||||
|
}
|
||||||
|
region, err := searcher.SearchByStr(ip)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("failed to SearchIP(%s): %s\n", ip, err)
|
||||||
|
return "未知"
|
||||||
|
}
|
||||||
|
parts := strings.Split(region, "|")
|
||||||
|
province := parts[2]
|
||||||
|
city := parts[3]
|
||||||
|
if province == "0" && city != "0" {
|
||||||
|
return city
|
||||||
|
}
|
||||||
|
return province + " " + city
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientIP 处理客户端IP地址显示iPv4
|
||||||
|
//
|
||||||
|
// 转换 ip2region.ClientIP(c.ClientIP())
|
||||||
|
func ClientIP(ip string) string {
|
||||||
|
if strings.HasPrefix(ip, "::ffff:") {
|
||||||
|
ip = strings.Replace(ip, "::ffff:", "", 1)
|
||||||
|
}
|
||||||
|
if ip == LOCAT_HOST || ip == "::1" {
|
||||||
|
return LOCAT_HOST
|
||||||
|
}
|
||||||
|
return ip
|
||||||
|
}
|
||||||
BIN
src/framework/utils/ip2region/ip2region.xdb
Normal file
BIN
src/framework/utils/ip2region/ip2region.xdb
Normal file
Binary file not shown.
175
src/framework/utils/ip2region/util.go
Normal file
175
src/framework/utils/ip2region/util.go
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
package ip2region
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var shiftIndex = []int{24, 16, 8, 0}
|
||||||
|
|
||||||
|
func CheckIP(ip string) (uint32, error) {
|
||||||
|
var ps = strings.Split(strings.TrimSpace(ip), ".")
|
||||||
|
if len(ps) != 4 {
|
||||||
|
return 0, fmt.Errorf("invalid ip address `%s`", ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
var val = uint32(0)
|
||||||
|
for i, s := range ps {
|
||||||
|
d, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("the %dth part `%s` is not an integer", i, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
if d < 0 || d > 255 {
|
||||||
|
return 0, fmt.Errorf("the %dth part `%s` should be an integer bettween 0 and 255", i, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
val |= uint32(d) << shiftIndex[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the ip to integer
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Long2IP(ip uint32) string {
|
||||||
|
return fmt.Sprintf("%d.%d.%d.%d", (ip>>24)&0xFF, (ip>>16)&0xFF, (ip>>8)&0xFF, ip&0xFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MidIP(sip uint32, eip uint32) uint32 {
|
||||||
|
return uint32((uint64(sip) + uint64(eip)) >> 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadHeader load the header info from the specified handle
|
||||||
|
func LoadHeader(handle *os.File) (*Header, error) {
|
||||||
|
_, err := handle.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("seek to the header: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buff = make([]byte, HeaderInfoLength)
|
||||||
|
rLen, err := handle.Read(buff)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rLen != len(buff) {
|
||||||
|
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHeader(buff)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadHeaderFromFile load header info from the specified db file path
|
||||||
|
func LoadHeaderFromFile(dbFile string) (*Header, error) {
|
||||||
|
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func(handle *os.File) {
|
||||||
|
_ = handle.Close()
|
||||||
|
}(handle)
|
||||||
|
|
||||||
|
header, err := LoadHeader(handle)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return header, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadHeaderFromBuff wrap the header info from the content buffer
|
||||||
|
func LoadHeaderFromBuff(cBuff []byte) (*Header, error) {
|
||||||
|
return NewHeader(cBuff[0:256])
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadVectorIndex util function to load the vector index from the specified file handle
|
||||||
|
func LoadVectorIndex(handle *os.File) ([]byte, error) {
|
||||||
|
// load all the vector index block
|
||||||
|
_, err := handle.Seek(HeaderInfoLength, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("seek to vector index: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buff = make([]byte, VectorIndexRows*VectorIndexCols*VectorIndexSize)
|
||||||
|
rLen, err := handle.Read(buff)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rLen != len(buff) {
|
||||||
|
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadVectorIndexFromFile load vector index from a specified file path
|
||||||
|
func LoadVectorIndexFromFile(dbFile string) ([]byte, error) {
|
||||||
|
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = handle.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
vIndex, err := LoadVectorIndex(handle)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return vIndex, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadContent load the whole xdb content from the specified file handle
|
||||||
|
func LoadContent(handle *os.File) ([]byte, error) {
|
||||||
|
// get file size
|
||||||
|
fi, err := handle.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("stat: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
size := fi.Size()
|
||||||
|
|
||||||
|
// seek to the head of the file
|
||||||
|
_, err = handle.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("seek to get xdb file length: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buff = make([]byte, size)
|
||||||
|
rLen, err := handle.Read(buff)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rLen != len(buff) {
|
||||||
|
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadContentFromFile load the whole xdb content from the specified db file path
|
||||||
|
func LoadContentFromFile(dbFile string) ([]byte, error) {
|
||||||
|
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = handle.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
cBuff, err := LoadContent(handle)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cBuff, nil
|
||||||
|
}
|
||||||
|
|
||||||
167
src/framework/utils/parse/parse.go
Normal file
167
src/framework/utils/parse/parse.go
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
package parse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image/color"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/robfig/cron/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Number 解析数值型
|
||||||
|
func Number(str any) int64 {
|
||||||
|
switch str := str.(type) {
|
||||||
|
case string:
|
||||||
|
if str == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
num, err := strconv.ParseInt(str, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return num
|
||||||
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||||
|
return reflect.ValueOf(str).Int()
|
||||||
|
case float32, float64:
|
||||||
|
return int64(reflect.ValueOf(str).Float())
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boolean 解析布尔型
|
||||||
|
func Boolean(str any) bool {
|
||||||
|
switch str := str.(type) {
|
||||||
|
case string:
|
||||||
|
if str == "" || str == "false" || str == "0" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 尝试将字符串解析为数字
|
||||||
|
if num, err := strconv.ParseFloat(str, 64); err == nil {
|
||||||
|
return num != 0
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||||
|
num := reflect.ValueOf(str).Int()
|
||||||
|
return num != 0
|
||||||
|
case float32, float64:
|
||||||
|
num := reflect.ValueOf(str).Float()
|
||||||
|
return num != 0
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertToCamelCase 字符串转换驼峰形式
|
||||||
|
//
|
||||||
|
// 字符串 dict/inline/data/:dictId 结果 DictInlineDataDictId
|
||||||
|
func ConvertToCamelCase(str string) string {
|
||||||
|
if len(str) == 0 {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
reg := regexp.MustCompile(`[-_:/]\w`)
|
||||||
|
result := reg.ReplaceAllStringFunc(str, func(match string) string {
|
||||||
|
return strings.ToUpper(string(match[1]))
|
||||||
|
})
|
||||||
|
|
||||||
|
words := strings.Fields(result)
|
||||||
|
for i, word := range words {
|
||||||
|
str := word[1:]
|
||||||
|
str = strings.ReplaceAll(str, "/", "")
|
||||||
|
words[i] = strings.ToUpper(word[:1]) + str
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(words, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bit 比特位为单位
|
||||||
|
func Bit(bit float64) string {
|
||||||
|
var GB, MB, KB string
|
||||||
|
|
||||||
|
if bit > float64(1<<30) {
|
||||||
|
GB = fmt.Sprintf("%0.2f", bit/(1<<30))
|
||||||
|
}
|
||||||
|
|
||||||
|
if bit > float64(1<<20) && bit < (1<<30) {
|
||||||
|
MB = fmt.Sprintf("%.2f", bit/(1<<20))
|
||||||
|
}
|
||||||
|
|
||||||
|
if bit > float64(1<<10) && bit < (1<<20) {
|
||||||
|
KB = fmt.Sprintf("%.2f", bit/(1<<10))
|
||||||
|
}
|
||||||
|
|
||||||
|
if GB != "" {
|
||||||
|
return GB + "GB"
|
||||||
|
} else if MB != "" {
|
||||||
|
return MB + "MB"
|
||||||
|
} else if KB != "" {
|
||||||
|
return KB + "KB"
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%vB", bit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CronExpression 解析 Cron 表达式,返回下一次执行的时间戳(毫秒)
|
||||||
|
//
|
||||||
|
// 【*/5 * * * * ?】 6个参数
|
||||||
|
func CronExpression(expression string) int64 {
|
||||||
|
specParser := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
|
||||||
|
schedule, err := specParser.Parse(expression)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return schedule.Next(time.Now()).UnixMilli()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SafeContent 内容值进行安全掩码
|
||||||
|
func SafeContent(value string) string {
|
||||||
|
if len(value) < 3 {
|
||||||
|
return strings.Repeat("*", len(value))
|
||||||
|
} else if len(value) < 6 {
|
||||||
|
return string(value[0]) + strings.Repeat("*", len(value)-1)
|
||||||
|
} else if len(value) < 10 {
|
||||||
|
return string(value[0]) + strings.Repeat("*", len(value)-2) + string(value[len(value)-1])
|
||||||
|
} else if len(value) < 15 {
|
||||||
|
return value[:2] + strings.Repeat("*", len(value)-4) + value[len(value)-2:]
|
||||||
|
} else {
|
||||||
|
return value[:3] + strings.Repeat("*", len(value)-6) + value[len(value)-3:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveDuplicates 数组内字符串去重
|
||||||
|
func RemoveDuplicates(ids []string) []string {
|
||||||
|
uniqueIDs := make(map[string]bool)
|
||||||
|
uniqueIDSlice := make([]string, 0)
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
_, ok := uniqueIDs[id]
|
||||||
|
if !ok && id != "" {
|
||||||
|
uniqueIDs[id] = true
|
||||||
|
uniqueIDSlice = append(uniqueIDSlice, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniqueIDSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color 解析颜色 #fafafa
|
||||||
|
func Color(colorStr string) *color.RGBA {
|
||||||
|
// 去除 # 号
|
||||||
|
colorStr = colorStr[1:]
|
||||||
|
|
||||||
|
// 将颜色字符串拆分为 R、G、B 分量
|
||||||
|
r, _ := strconv.ParseInt(colorStr[0:2], 16, 0)
|
||||||
|
g, _ := strconv.ParseInt(colorStr[2:4], 16, 0)
|
||||||
|
b, _ := strconv.ParseInt(colorStr[4:6], 16, 0)
|
||||||
|
|
||||||
|
return &color.RGBA{
|
||||||
|
R: uint8(r),
|
||||||
|
G: uint8(g),
|
||||||
|
B: uint8(b),
|
||||||
|
A: 255, // 不透明
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/framework/utils/regular/regular.go
Normal file
86
src/framework/utils/regular/regular.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package regular
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/dlclark/regexp2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Replace 正则替换
|
||||||
|
func Replace(originStr, pattern, repStr string) string {
|
||||||
|
regex := regexp.MustCompile(pattern)
|
||||||
|
return regex.ReplaceAllString(originStr, repStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为有效用户名格式
|
||||||
|
//
|
||||||
|
// 用户名不能以数字开头,可包含大写小写字母,数字,且不少于5位
|
||||||
|
func ValidUsername(username string) bool {
|
||||||
|
if username == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
pattern := `^[a-zA-Z][a-z0-9A-Z]{5,}`
|
||||||
|
match, err := regexp.MatchString(pattern, username)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为有效密码格式
|
||||||
|
//
|
||||||
|
// 密码至少包含大小写字母、数字、特殊符号,且不少于6位
|
||||||
|
func ValidPassword(password string) bool {
|
||||||
|
if password == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
pattern := `^(?![A-Za-z0-9]+$)(?![a-z0-9\W]+$)(?![A-Za-z\W]+$)(?![A-Z0-9\W]+$)[a-zA-Z0-9\W]{6,}$`
|
||||||
|
re := regexp2.MustCompile(pattern, 0)
|
||||||
|
match, err := re.MatchString(password)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为有效手机号格式,1开头的11位手机号
|
||||||
|
func ValidMobile(mobile string) bool {
|
||||||
|
if mobile == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
pattern := `^1[3|4|5|6|7|8|9][0-9]\d{8}$`
|
||||||
|
match, err := regexp.MatchString(pattern, mobile)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为有效邮箱格式
|
||||||
|
func ValidEmail(email string) bool {
|
||||||
|
if email == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
pattern := `^(([^<>()\\.,;:\s@"]+(\.[^<>()\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+\.)+[a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{2,}))$`
|
||||||
|
re := regexp2.MustCompile(pattern, 0)
|
||||||
|
match, err := re.MatchString(email)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为http(s)://开头
|
||||||
|
//
|
||||||
|
// link 网络链接
|
||||||
|
func ValidHttp(link string) bool {
|
||||||
|
if link == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
pattern := `^http(s)?:\/\/+`
|
||||||
|
match, err := regexp.MatchString(pattern, link)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
}
|
||||||
132
src/framework/utils/repo/repo.go
Normal file
132
src/framework/utils/repo/repo.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/utils/parse"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PageNumSize 分页页码记录数
|
||||||
|
func PageNumSize(pageNum, pageSize any) (int64, int64) {
|
||||||
|
// 记录起始索引
|
||||||
|
num := parse.Number(pageNum)
|
||||||
|
if num > 5000 {
|
||||||
|
num = 5000
|
||||||
|
}
|
||||||
|
if num < 1 {
|
||||||
|
num = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示记录数
|
||||||
|
size := parse.Number(pageSize)
|
||||||
|
if size > 50000 {
|
||||||
|
size = 50000
|
||||||
|
}
|
||||||
|
if size < 0 {
|
||||||
|
size = 10
|
||||||
|
}
|
||||||
|
return num - 1, size
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFieldValue 判断结构体内是否存在指定字段并设置值
|
||||||
|
func SetFieldValue(obj any, fieldName string, value any) {
|
||||||
|
// 获取结构体的反射值
|
||||||
|
userValue := reflect.ValueOf(obj)
|
||||||
|
|
||||||
|
// 获取字段的反射值
|
||||||
|
fieldValue := userValue.Elem().FieldByName(fieldName)
|
||||||
|
|
||||||
|
// 检查字段是否存在
|
||||||
|
if fieldValue.IsValid() && fieldValue.CanSet() {
|
||||||
|
// 获取字段的类型
|
||||||
|
fieldType := fieldValue.Type()
|
||||||
|
|
||||||
|
// 转换传入的值类型为字段类型
|
||||||
|
switch fieldType.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
if value == nil {
|
||||||
|
fieldValue.SetString("")
|
||||||
|
} else {
|
||||||
|
fieldValue.SetString(fmt.Sprintf("%v", value))
|
||||||
|
}
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
intValue, err := strconv.ParseInt(fmt.Sprintf("%v", value), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
intValue = 0
|
||||||
|
}
|
||||||
|
fieldValue.SetInt(intValue)
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
uintValue, err := strconv.ParseUint(fmt.Sprintf("%v", value), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
uintValue = 0
|
||||||
|
}
|
||||||
|
fieldValue.SetUint(uintValue)
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
floatValue, err := strconv.ParseFloat(fmt.Sprintf("%v", value), 64)
|
||||||
|
if err != nil {
|
||||||
|
floatValue = 0
|
||||||
|
}
|
||||||
|
fieldValue.SetFloat(floatValue)
|
||||||
|
default:
|
||||||
|
// 设置字段的值
|
||||||
|
fieldValue.Set(reflect.ValueOf(value).Convert(fieldValue.Type()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertIdsSlice 将 []string 转换为 []any
|
||||||
|
func ConvertIdsSlice(ids []string) []any {
|
||||||
|
// 将 []string 转换为 []any
|
||||||
|
arr := make([]any, len(ids))
|
||||||
|
for i, v := range ids {
|
||||||
|
arr[i] = v
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询-参数值的占位符
|
||||||
|
func KeyPlaceholderByQuery(sum int) string {
|
||||||
|
placeholders := make([]string, sum)
|
||||||
|
for i := 0; i < sum; i++ {
|
||||||
|
placeholders[i] = "?"
|
||||||
|
}
|
||||||
|
return strings.Join(placeholders, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插入-参数映射键值占位符 keys, placeholder, values
|
||||||
|
func KeyPlaceholderValueByInsert(params map[string]any) ([]string, string, []any) {
|
||||||
|
// 参数映射的键
|
||||||
|
keys := make([]string, len(params))
|
||||||
|
// 参数映射的值
|
||||||
|
values := make([]any, len(params))
|
||||||
|
sum := 0
|
||||||
|
for k, v := range params {
|
||||||
|
keys[sum] = k
|
||||||
|
values[sum] = v
|
||||||
|
sum++
|
||||||
|
}
|
||||||
|
// 参数值的占位符
|
||||||
|
placeholders := make([]string, sum)
|
||||||
|
for i := 0; i < sum; i++ {
|
||||||
|
placeholders[i] = "?"
|
||||||
|
}
|
||||||
|
return keys, strings.Join(placeholders, ","), values
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新-参数映射键值占位符 keys, values
|
||||||
|
func KeyValueByUpdate(params map[string]any) ([]string, []any) {
|
||||||
|
// 参数映射的键
|
||||||
|
keys := make([]string, len(params))
|
||||||
|
// 参数映射的值
|
||||||
|
values := make([]any, len(params))
|
||||||
|
sum := 0
|
||||||
|
for k, v := range params {
|
||||||
|
keys[sum] = k + "=?"
|
||||||
|
values[sum] = v
|
||||||
|
sum++
|
||||||
|
}
|
||||||
|
return keys, values
|
||||||
|
}
|
||||||
151
src/framework/utils/token/token.go
Normal file
151
src/framework/utils/token/token.go
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
package token
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/src/framework/config"
|
||||||
|
cachekeyConstants "ems.agt/src/framework/constants/cachekey"
|
||||||
|
tokenConstants "ems.agt/src/framework/constants/token"
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
redisCahe "ems.agt/src/framework/redis"
|
||||||
|
"ems.agt/src/framework/utils/generate"
|
||||||
|
"ems.agt/src/framework/vo"
|
||||||
|
|
||||||
|
jwt "github.com/golang-jwt/jwt/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Remove 清除登录用户信息UUID
|
||||||
|
func Remove(tokenStr string) string {
|
||||||
|
claims, err := Verify(tokenStr)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("token verify err %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// 清除缓存KEY
|
||||||
|
uuid := claims[tokenConstants.JWT_UUID].(string)
|
||||||
|
tokenKey := cachekeyConstants.LOGIN_TOKEN_KEY + uuid
|
||||||
|
hasKey, _ := redisCahe.Has("", tokenKey)
|
||||||
|
if hasKey {
|
||||||
|
redisCahe.Del("", tokenKey)
|
||||||
|
}
|
||||||
|
return claims[tokenConstants.JWT_NAME].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 令牌生成
|
||||||
|
func Create(loginUser *vo.LoginUser, ilobArgs ...string) string {
|
||||||
|
// 生成用户唯一tokne32位
|
||||||
|
loginUser.UUID = generate.Code(32)
|
||||||
|
|
||||||
|
// 设置请求用户登录客户端
|
||||||
|
loginUser.IPAddr = ilobArgs[0]
|
||||||
|
loginUser.LoginLocation = ilobArgs[1]
|
||||||
|
loginUser.OS = ilobArgs[2]
|
||||||
|
loginUser.Browser = ilobArgs[3]
|
||||||
|
|
||||||
|
// 设置用户令牌有效期并存入缓存
|
||||||
|
Cache(loginUser)
|
||||||
|
|
||||||
|
// 令牌算法 HS256 HS384 HS512
|
||||||
|
algorithm := config.Get("jwt.algorithm").(string)
|
||||||
|
var method *jwt.SigningMethodHMAC
|
||||||
|
switch algorithm {
|
||||||
|
case "HS512":
|
||||||
|
method = jwt.SigningMethodHS512
|
||||||
|
case "HS384":
|
||||||
|
method = jwt.SigningMethodHS384
|
||||||
|
case "HS256":
|
||||||
|
default:
|
||||||
|
method = jwt.SigningMethodHS256
|
||||||
|
}
|
||||||
|
// 生成令牌负荷绑定uuid标识
|
||||||
|
jwtToken := jwt.NewWithClaims(method, jwt.MapClaims{
|
||||||
|
tokenConstants.JWT_UUID: loginUser.UUID,
|
||||||
|
tokenConstants.JWT_KEY: loginUser.UserID,
|
||||||
|
tokenConstants.JWT_NAME: loginUser.User.UserName,
|
||||||
|
"exp": loginUser.ExpireTime,
|
||||||
|
"ait": loginUser.LoginTime,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 生成令牌设置密钥
|
||||||
|
secret := config.Get("jwt.secret").(string)
|
||||||
|
tokenStr, err := jwtToken.SignedString([]byte(secret))
|
||||||
|
if err != nil {
|
||||||
|
logger.Infof("jwt sign err : %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return tokenStr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache 缓存登录用户信息
|
||||||
|
func Cache(loginUser *vo.LoginUser) {
|
||||||
|
// 计算配置的有效期
|
||||||
|
expTime := config.Get("jwt.expiresIn").(int)
|
||||||
|
expTimestamp := time.Duration(expTime) * time.Minute
|
||||||
|
iatTimestamp := time.Now().UnixMilli()
|
||||||
|
loginUser.LoginTime = iatTimestamp
|
||||||
|
loginUser.ExpireTime = iatTimestamp + expTimestamp.Milliseconds()
|
||||||
|
// 根据登录标识将loginUser缓存
|
||||||
|
tokenKey := cachekeyConstants.LOGIN_TOKEN_KEY + loginUser.UUID
|
||||||
|
jsonBytes, err := json.Marshal(loginUser)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
redisCahe.SetByExpire("", tokenKey, string(jsonBytes), expTimestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefreshIn 验证令牌有效期,相差不足xx分钟,自动刷新缓存
|
||||||
|
func RefreshIn(loginUser *vo.LoginUser) {
|
||||||
|
// 相差不足xx分钟,自动刷新缓存
|
||||||
|
refreshTime := config.Get("jwt.refreshIn").(int)
|
||||||
|
refreshTimestamp := time.Duration(refreshTime) * time.Minute
|
||||||
|
// 过期时间
|
||||||
|
expireTimestamp := loginUser.ExpireTime
|
||||||
|
currentTimestamp := time.Now().UnixMilli()
|
||||||
|
if expireTimestamp-currentTimestamp <= refreshTimestamp.Milliseconds() {
|
||||||
|
Cache(loginUser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify 校验令牌是否有效
|
||||||
|
func Verify(tokenString string) (jwt.MapClaims, error) {
|
||||||
|
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
|
||||||
|
// 判断加密算法是预期的加密算法
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok {
|
||||||
|
secret := config.Get("jwt.secret").(string)
|
||||||
|
return []byte(secret), nil
|
||||||
|
}
|
||||||
|
return nil, jwt.ErrSignatureInvalid
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("token String Verify : %v", err)
|
||||||
|
return nil, errors.New("无效身份授权")
|
||||||
|
}
|
||||||
|
// 如果解析负荷成功并通过签名校验
|
||||||
|
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("token valid error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginUser 缓存的登录用户信息
|
||||||
|
func LoginUser(claims jwt.MapClaims) vo.LoginUser {
|
||||||
|
uuid := claims[tokenConstants.JWT_UUID].(string)
|
||||||
|
tokenKey := cachekeyConstants.LOGIN_TOKEN_KEY + uuid
|
||||||
|
hasKey, _ := redisCahe.Has("", tokenKey)
|
||||||
|
var loginUser vo.LoginUser
|
||||||
|
if hasKey {
|
||||||
|
loginUserStr, _ := redisCahe.Get("", tokenKey)
|
||||||
|
if loginUserStr == "" {
|
||||||
|
return loginUser
|
||||||
|
}
|
||||||
|
err := json.Unmarshal([]byte(loginUserStr), &loginUser)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("loginuser info json err : %v", err)
|
||||||
|
return loginUser
|
||||||
|
}
|
||||||
|
return loginUser
|
||||||
|
}
|
||||||
|
return loginUser
|
||||||
|
}
|
||||||
8
src/framework/utils/ua/ua.go
Normal file
8
src/framework/utils/ua/ua.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package ua
|
||||||
|
|
||||||
|
import "github.com/mssola/user_agent"
|
||||||
|
|
||||||
|
// 获取user-agent信息
|
||||||
|
func Info(userAgent string) *user_agent.UserAgent {
|
||||||
|
return user_agent.New(userAgent)
|
||||||
|
}
|
||||||
39
src/framework/vo/loginuser.go
Normal file
39
src/framework/vo/loginuser.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package vo
|
||||||
|
|
||||||
|
import systemModel "ems.agt/src/modules/system/model"
|
||||||
|
|
||||||
|
// LoginUser 登录用户身份权限信息对象
|
||||||
|
type LoginUser struct {
|
||||||
|
// UserID 用户ID
|
||||||
|
UserID string `json:"userId"`
|
||||||
|
|
||||||
|
// DeptID 部门ID
|
||||||
|
DeptID string `json:"deptId"`
|
||||||
|
|
||||||
|
// UUID 用户唯一标识
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
|
||||||
|
// LoginTime 登录时间时间戳
|
||||||
|
LoginTime int64 `json:"loginTime"`
|
||||||
|
|
||||||
|
// ExpireTime 过期时间时间戳
|
||||||
|
ExpireTime int64 `json:"expireTime"`
|
||||||
|
|
||||||
|
// IPAddr 登录IP地址 x.x.x.x
|
||||||
|
IPAddr string `json:"ipaddr"`
|
||||||
|
|
||||||
|
// LoginLocation 登录地点 xx xx
|
||||||
|
LoginLocation string `json:"loginLocation"`
|
||||||
|
|
||||||
|
// Browser 浏览器类型
|
||||||
|
Browser string `json:"browser"`
|
||||||
|
|
||||||
|
// OS 操作系统
|
||||||
|
OS string `json:"os"`
|
||||||
|
|
||||||
|
// Permissions 权限列表
|
||||||
|
Permissions []string `json:"permissions"`
|
||||||
|
|
||||||
|
// User 用户信息
|
||||||
|
User systemModel.SysUser `json:"user"`
|
||||||
|
}
|
||||||
71
src/framework/vo/result/result.go
Normal file
71
src/framework/vo/result/result.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package result
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ems.agt/src/framework/constants/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CodeMsg 响应结果
|
||||||
|
func CodeMsg(code int, msg string) map[string]any {
|
||||||
|
args := make(map[string]any)
|
||||||
|
args["code"] = code
|
||||||
|
args["msg"] = msg
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应成功结果 map[string]any{}
|
||||||
|
func Ok(v map[string]any) map[string]any {
|
||||||
|
args := make(map[string]any)
|
||||||
|
args["code"] = result.CODE_SUCCESS
|
||||||
|
args["msg"] = result.MSG_SUCCESS
|
||||||
|
// v合并到args
|
||||||
|
for key, value := range v {
|
||||||
|
args[key] = value
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应成功结果信息
|
||||||
|
func OkMsg(msg string) map[string]any {
|
||||||
|
args := make(map[string]any)
|
||||||
|
args["code"] = result.CODE_SUCCESS
|
||||||
|
args["msg"] = msg
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应成功结果数据
|
||||||
|
func OkData(data any) map[string]any {
|
||||||
|
args := make(map[string]any)
|
||||||
|
args["code"] = result.CODE_SUCCESS
|
||||||
|
args["msg"] = result.MSG_SUCCESS
|
||||||
|
args["data"] = data
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应失败结果 map[string]any{}
|
||||||
|
func Err(v map[string]any) map[string]any {
|
||||||
|
args := make(map[string]any)
|
||||||
|
args["code"] = result.CODE_ERROR
|
||||||
|
args["msg"] = result.MSG_ERROR
|
||||||
|
// v合并到args
|
||||||
|
for key, value := range v {
|
||||||
|
args[key] = value
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应失败结果信息
|
||||||
|
func ErrMsg(msg string) map[string]any {
|
||||||
|
args := make(map[string]any)
|
||||||
|
args["code"] = result.CODE_ERROR
|
||||||
|
args["msg"] = msg
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应失败结果数据
|
||||||
|
func ErrData(data any) map[string]any {
|
||||||
|
args := make(map[string]any)
|
||||||
|
args["code"] = result.CODE_ERROR
|
||||||
|
args["msg"] = result.MSG_ERROR
|
||||||
|
args["data"] = data
|
||||||
|
return args
|
||||||
|
}
|
||||||
17
src/framework/vo/router.go
Normal file
17
src/framework/vo/router.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package vo
|
||||||
|
|
||||||
|
// Router 路由信息对象
|
||||||
|
type Router struct {
|
||||||
|
// 路由名字 英文首字母大写
|
||||||
|
Name string `json:"name"`
|
||||||
|
// 路由地址
|
||||||
|
Path string `json:"path"`
|
||||||
|
// 其他元素
|
||||||
|
Meta RouterMeta `json:"meta"`
|
||||||
|
// 组件地址
|
||||||
|
Component string `json:"component"`
|
||||||
|
// 重定向地址
|
||||||
|
Redirect string `json:"redirect"`
|
||||||
|
// 子路由
|
||||||
|
Children []Router `json:"children,omitempty"`
|
||||||
|
}
|
||||||
17
src/framework/vo/router_meta.go
Normal file
17
src/framework/vo/router_meta.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package vo
|
||||||
|
|
||||||
|
// RouterMeta 路由元信息对象
|
||||||
|
type RouterMeta struct {
|
||||||
|
// 设置该菜单在侧边栏和面包屑中展示的名字
|
||||||
|
Title string `json:"title"`
|
||||||
|
// 设置该菜单的图标
|
||||||
|
Icon string `json:"icon"`
|
||||||
|
// 设置为true,则不会被 <keep-alive>缓存
|
||||||
|
Cache bool `json:"cache"`
|
||||||
|
// 内链地址(http(s)://开头), 打开目标位置 '_blank' | '_self' | ''
|
||||||
|
Target string `json:"target"`
|
||||||
|
// 在菜单中隐藏子节点
|
||||||
|
HideChildInMenu bool `json:"hideChildInMenu"`
|
||||||
|
// 在菜单中隐藏自己和子节点
|
||||||
|
HideInMenu bool `json:"hideInMenu"`
|
||||||
|
}
|
||||||
51
src/framework/vo/treeselect.go
Normal file
51
src/framework/vo/treeselect.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package vo
|
||||||
|
|
||||||
|
import systemModel "ems.agt/src/modules/system/model"
|
||||||
|
|
||||||
|
// TreeSelect 树结构实体类
|
||||||
|
type TreeSelect struct {
|
||||||
|
// ID 节点ID
|
||||||
|
ID string `json:"id"`
|
||||||
|
|
||||||
|
// Label 节点名称
|
||||||
|
Label string `json:"label"`
|
||||||
|
|
||||||
|
// Children 子节点
|
||||||
|
Children []TreeSelect `json:"children"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SysMenuTreeSelect 使用给定的 SysMenu 对象解析为 TreeSelect 对象
|
||||||
|
func SysMenuTreeSelect(sysMenu systemModel.SysMenu) TreeSelect {
|
||||||
|
t := TreeSelect{}
|
||||||
|
t.ID = sysMenu.MenuID
|
||||||
|
t.Label = sysMenu.MenuName
|
||||||
|
|
||||||
|
if len(sysMenu.Children) > 0 {
|
||||||
|
for _, menu := range sysMenu.Children {
|
||||||
|
child := SysMenuTreeSelect(menu)
|
||||||
|
t.Children = append(t.Children, child)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Children = []TreeSelect{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// SysDeptTreeSelect 使用给定的 SysDept 对象解析为 TreeSelect 对象
|
||||||
|
func SysDeptTreeSelect(sysDept systemModel.SysDept) TreeSelect {
|
||||||
|
t := TreeSelect{}
|
||||||
|
t.ID = sysDept.DeptID
|
||||||
|
t.Label = sysDept.DeptName
|
||||||
|
|
||||||
|
if len(sysDept.Children) > 0 {
|
||||||
|
for _, dept := range sysDept.Children {
|
||||||
|
child := SysDeptTreeSelect(dept)
|
||||||
|
t.Children = append(t.Children, child)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Children = []TreeSelect{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
32
src/lib_features/account/account.go
Normal file
32
src/lib_features/account/account.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package libfeatures
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ems.agt/lib/dborm"
|
||||||
|
"ems.agt/lib/oauth"
|
||||||
|
libConfig "ems.agt/restagent/config"
|
||||||
|
"ems.agt/src/framework/logger"
|
||||||
|
"ems.agt/src/framework/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SessionToken 设置登录会话-兼容旧登录方式
|
||||||
|
func SessionToken(username, sourceAddr string) bool {
|
||||||
|
token, _ := redis.Get("", "session_token")
|
||||||
|
if token != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
token = oauth.GenRandToken("omc") // Generate new token to session ID
|
||||||
|
affected, err := dborm.XormInsertSession(username, sourceAddr, token, libConfig.GetExpiresFromConfig(), libConfig.GetYamlConfig().Auth.Session)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("SessionToken XormInsertSession err %v", err)
|
||||||
|
}
|
||||||
|
if affected == 1 {
|
||||||
|
// 过期时间单位秒 配置1800是半小时
|
||||||
|
expireTime := time.Duration(int64(libConfig.GetExpiresFromConfig())) * time.Second
|
||||||
|
redis.SetByExpire("", "session_token", token, expireTime)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
53
src/lib_features/config/config.go
Normal file
53
src/lib_features/config/config.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package libfeatures
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
libConf "ems.agt/lib/core/conf"
|
||||||
|
libGlobal "ems.agt/lib/global"
|
||||||
|
libConfig "ems.agt/restagent/config"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildInfo 程序-V查看编译版本号信息
|
||||||
|
func BuildInfo() string {
|
||||||
|
return fmt.Sprintf("OMC restagent version: %s\n%s\n%s\n\n", libGlobal.Version, libGlobal.BuildTime, libGlobal.GoVer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigRead 指定配置文件读取
|
||||||
|
func ConfigRead(configFile string) {
|
||||||
|
// 外层lib和features使用的配置
|
||||||
|
libConfig.ReadConfig(configFile)
|
||||||
|
uriPrefix := libConfig.GetYamlConfig().OMC.UriPrefix
|
||||||
|
if uriPrefix != "" {
|
||||||
|
libConfig.UriPrefix = uriPrefix
|
||||||
|
}
|
||||||
|
if libConfig.GetYamlConfig().TestConfig.Enabled {
|
||||||
|
libConfig.ReadTestConfigYaml(libConfig.GetYamlConfig().TestConfig.File)
|
||||||
|
}
|
||||||
|
// 外层lib和features使用配置
|
||||||
|
libConf.InitConfig(configFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置文件读取进行内部参数合并
|
||||||
|
func ConfigInMerge() {
|
||||||
|
// 合并外层lib和features使用配置
|
||||||
|
for key, value := range libConf.AllSettings() {
|
||||||
|
// 跳过配置
|
||||||
|
if key == "testconfig" || key == "rest" || key == "logger" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 数据库配置
|
||||||
|
if key == "database" {
|
||||||
|
item := value.(map[string]any)
|
||||||
|
defaultItem := viper.GetStringMap("gorm.datasource.default")
|
||||||
|
defaultItem["host"] = item["host"]
|
||||||
|
defaultItem["port"] = item["port"]
|
||||||
|
defaultItem["username"] = item["user"]
|
||||||
|
defaultItem["password"] = item["password"]
|
||||||
|
defaultItem["database"] = item["name"]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
viper.Set(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/lib_features/readme.md
Normal file
5
src/lib_features/readme.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# 外层 lib 和 features 粘合层
|
||||||
|
|
||||||
|
- config.go 配置合并: restagent.yaml 文件内容,主要是数据库配置
|
||||||
|
- account.go 登录会话生成 token
|
||||||
|
- session.go 中间件方式设置请求头 token 值
|
||||||
23
src/lib_features/session/session.go
Normal file
23
src/lib_features/session/session.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package session
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ems.agt/src/framework/redis"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SessionHeader 旧登录方式token头
|
||||||
|
func SessionHeader() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// 读取登录生成的会话token
|
||||||
|
token, err := redis.Get("", "session_token")
|
||||||
|
if err == nil {
|
||||||
|
c.Request.Header.Set("Accesstoken", token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accesstoken: omc-ce4d0a86-8515-ad51-3249-4913c95f8e34
|
||||||
|
// 调用下一个处理程序
|
||||||
|
c.Next()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
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)))
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user