gengxin
This commit is contained in:
parent
31fc86e878
commit
e6b84aad80
@ -32,7 +32,7 @@ var validModules = map[string]bool{
|
|||||||
"krio": true,
|
"krio": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiGetCardController) cardErr(httpStatus, code int, msg string) {
|
func (c *ApiGetCardController) cardErr(_ int, _ int, msg string) {
|
||||||
c.Ctx.Output.SetStatus(200)
|
c.Ctx.Output.SetStatus(200)
|
||||||
c.Ctx.Output.Header("Content-Type", "text/plain; charset=utf-8")
|
c.Ctx.Output.Header("Content-Type", "text/plain; charset=utf-8")
|
||||||
_ = c.Ctx.Output.Body([]byte("error:" + msg))
|
_ = c.Ctx.Output.Body([]byte("error:" + msg))
|
||||||
@ -91,33 +91,42 @@ func (c *ApiGetCardController) GetCard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiGetCardController) extractCursor(platform, dataType string, now time.Time) {
|
func (c *ApiGetCardController) extractCursor(platform, dataType string, now time.Time) {
|
||||||
var row models.PlatformAccountPoolCursor
|
for {
|
||||||
qs := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).
|
var row models.PlatformAccountPoolCursor
|
||||||
Filter("is_extracted", 0).
|
qs := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).
|
||||||
Filter("delete_time__isnull", true)
|
Filter("is_extracted", 0).
|
||||||
if dataType != "" {
|
Filter("delete_time__isnull", true)
|
||||||
qs = qs.Filter("data_type", dataType)
|
if dataType != "" {
|
||||||
}
|
qs = qs.Filter("data_type", dataType)
|
||||||
if err := qs.OrderBy("id").One(&row); err != nil {
|
}
|
||||||
if err == orm.ErrNoRows {
|
if err := qs.OrderBy("id").One(&row); err != nil {
|
||||||
c.cardErr(404, 404, "暂无可用卡密")
|
if err == orm.ErrNoRows {
|
||||||
} else {
|
c.cardErr(404, 404, "暂无可用卡密")
|
||||||
c.cardErr(500, 500, "查询失败")
|
} else {
|
||||||
|
c.cardErr(500, 500, "查询失败")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).
|
||||||
|
Filter("id", row.ID).
|
||||||
|
Update(map[string]interface{}{
|
||||||
|
"is_extracted": 1,
|
||||||
|
"extracted_time": now,
|
||||||
|
"extracted_platform": platform,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.cardErr(500, 500, "提取失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cursor 号池需要先判断可用状态:is_used=1 才发送给前端;
|
||||||
|
// is_used=0(已用完/不可用)或 NULL(未探测)则继续提取下一条。
|
||||||
|
if row.IsUsed != nil && *row.IsUsed == 1 {
|
||||||
|
c.cardOK(buildCardResult(&row.Account, &row.Password, row.Token, row.DataType))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
_, err := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).
|
|
||||||
Filter("id", row.ID).
|
|
||||||
Update(map[string]interface{}{
|
|
||||||
"is_extracted": 1,
|
|
||||||
"extracted_time": now,
|
|
||||||
"extracted_platform": platform,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
c.cardErr(500, 500, "提取失败")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.cardOK(buildCardResult(&row.Account, &row.Password, row.Token, row.DataType))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiGetCardController) extractWindsurf(platform, dataType string, now time.Time) {
|
func (c *ApiGetCardController) extractWindsurf(platform, dataType string, now time.Time) {
|
||||||
|
|||||||
322
controllers/backend_auth.go
Normal file
322
controllers/backend_auth.go
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"server/models"
|
||||||
|
"server/pkg/jwtutil"
|
||||||
|
"server/services"
|
||||||
|
|
||||||
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
type backendAuthLoginRequest struct {
|
||||||
|
TenantName string `json:"tenant_name"`
|
||||||
|
Account string `json:"account"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
// 极验4验证参数
|
||||||
|
CaptchaID string `json:"captcha_id"`
|
||||||
|
LotNumber string `json:"lot_number"`
|
||||||
|
PassToken string `json:"pass_token"`
|
||||||
|
GenTime string `json:"gen_time"`
|
||||||
|
CaptchaOutput string `json:"captcha_output"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackendAuthController backend 端认证控制器
|
||||||
|
type BackendAuthController struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendAuthController) serveJSON(data map[string]interface{}) {
|
||||||
|
c.Data["json"] = data
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginBackend backend 登录(需要租户)
|
||||||
|
func (c *BackendAuthController) LoginBackend() {
|
||||||
|
var req backendAuthLoginRequest
|
||||||
|
|
||||||
|
body := c.Ctx.Input.RequestBody
|
||||||
|
if len(body) == 0 {
|
||||||
|
var err error
|
||||||
|
body, err = io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(body) == 0 {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &req); err != nil {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.TenantName = strings.TrimSpace(req.TenantName)
|
||||||
|
req.Account = strings.TrimSpace(req.Account)
|
||||||
|
req.Password = strings.TrimSpace(req.Password)
|
||||||
|
if req.TenantName == "" || req.Account == "" || req.Password == "" {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": "租户名称、用户名或密码不能为空"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, _ := models.GetPlatformLoginVerify()
|
||||||
|
if cfg.OpenVerifyEnabled == 1 {
|
||||||
|
if cfg.VerifyType == "geetest4" {
|
||||||
|
if req.LotNumber == "" || req.PassToken == "" || req.GenTime == "" || req.CaptchaOutput == "" {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": "请完成人机验证"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: 集成极验4服务端 SDK 后在这里进行二次校验
|
||||||
|
} else if cfg.VerifyType == "geetest3" {
|
||||||
|
if req.CaptchaOutput == "" {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": "请完成人机验证"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: 集成极验3服务端 SDK 后在这里进行二次校验
|
||||||
|
} else if cfg.VerifyType == "sms" || cfg.VerifyType == "email" {
|
||||||
|
if strings.TrimSpace(req.Code) == "" {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": "请输入验证码"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := services.VerifyBackendLoginCode(req.TenantName, req.Account, cfg.VerifyType, req.Code); err != nil {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token, loginUser, err := services.BackendLogin(req.TenantName, req.Account, req.Password)
|
||||||
|
if err != nil {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 401, "msg": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.serveJSON(map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "登录成功",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"token": token,
|
||||||
|
"user": map[string]interface{}{
|
||||||
|
"id": loginUser.ID,
|
||||||
|
"account": loginUser.Account,
|
||||||
|
"name": loginUser.Name,
|
||||||
|
"tid": loginUser.Tid,
|
||||||
|
"rid": loginUser.Rid,
|
||||||
|
"avatar": loginUser.Avatar,
|
||||||
|
"role_name": loginUser.RoleName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrentUser 当前登录 backend 用户信息,需 Bearer Token
|
||||||
|
func (c *BackendAuthController) GetCurrentUser() {
|
||||||
|
authHeader := c.Ctx.Request.Header.Get("Authorization")
|
||||||
|
if authHeader == "" {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 401, "msg": "未登录"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
authParts := strings.SplitN(authHeader, " ", 2)
|
||||||
|
if len(authParts) != 2 || authParts[0] != "Bearer" {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 401, "msg": "认证信息格式错误"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims, err := jwtutil.ParseToken(authParts[1])
|
||||||
|
if err != nil {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 401, "msg": "无效的token"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if claims.UserType != "backend" {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 403, "msg": "无权访问"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var tenantUser models.SystemTenantUser
|
||||||
|
err = models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||||
|
Filter("uid", claims.UserID).
|
||||||
|
Filter("tid", claims.TenantId).
|
||||||
|
One(&tenantUser)
|
||||||
|
if err != nil {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 401, "msg": "用户不存在"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tenantUser.Status == 0 {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 401, "msg": "账号已禁用"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
account := ""
|
||||||
|
if tenantUser.Account != nil {
|
||||||
|
account = strings.TrimSpace(*tenantUser.Account)
|
||||||
|
}
|
||||||
|
name := ""
|
||||||
|
if tenantUser.Name != nil {
|
||||||
|
name = strings.TrimSpace(*tenantUser.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.serveJSON(map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"id": tenantUser.Uid,
|
||||||
|
"account": account,
|
||||||
|
"name": name,
|
||||||
|
"tid": tenantUser.Tid,
|
||||||
|
"rid": 0,
|
||||||
|
"avatar": "",
|
||||||
|
"role_name": "",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendLoginCode 发送 backend 登录验证码
|
||||||
|
func (c *BackendAuthController) SendLoginCode() {
|
||||||
|
var req struct {
|
||||||
|
Account string `json:"account"`
|
||||||
|
TenantName string `json:"tenant_name"`
|
||||||
|
Channel string `json:"channel"`
|
||||||
|
}
|
||||||
|
body := c.Ctx.Input.RequestBody
|
||||||
|
if len(body) == 0 {
|
||||||
|
var err error
|
||||||
|
body, err = io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &req); err != nil {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, _ := models.GetPlatformLoginVerify()
|
||||||
|
if cfg.OpenVerifyEnabled != 1 {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": "当前未开启验证"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channel := strings.TrimSpace(req.Channel)
|
||||||
|
if channel == "" {
|
||||||
|
channel = cfg.VerifyType
|
||||||
|
}
|
||||||
|
if channel != "sms" && channel != "email" {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": "仅支持短信/邮箱验证码"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := services.SendBackendLoginCode(req.TenantName, req.Account, channel); err != nil {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 400, "msg": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 200, "msg": "验证码已发送"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginBySms 手机号验证码登录(占位实现)
|
||||||
|
func (c *BackendAuthController) LoginBySms() {
|
||||||
|
c.serveJSON(map[string]interface{}{
|
||||||
|
"code": 501,
|
||||||
|
"msg": "手机号验证码登录暂未实现",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logout backend 退出登录(当前为无状态直接返回成功)
|
||||||
|
func (c *BackendAuthController) Logout() {
|
||||||
|
c.serveJSON(map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "退出成功",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGeetest3Infos 获取 backend 极验3.0配置
|
||||||
|
func (c *BackendAuthController) GetGeetest3Infos() {
|
||||||
|
cfg, _ := models.GetPlatformLoginVerify()
|
||||||
|
if cfg.Geetest3ID == nil || cfg.Geetest3Key == nil {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 404, "msg": "未配置极验3参数"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.serveJSON(map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"captcha_id": *cfg.Geetest3ID,
|
||||||
|
"captcha_key": *cfg.Geetest3Key,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGeetest4Infos 获取 backend 极验4.0配置
|
||||||
|
func (c *BackendAuthController) GetGeetest4Infos() {
|
||||||
|
cfg, _ := models.GetPlatformLoginVerify()
|
||||||
|
if cfg.Geetest4ID == nil || cfg.Geetest4Key == nil {
|
||||||
|
c.serveJSON(map[string]interface{}{"code": 404, "msg": "未配置极验4参数"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.serveJSON(map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"captcha_id": *cfg.Geetest4ID,
|
||||||
|
"captcha_key": *cfg.Geetest4Key,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOpenVerify 判断是否开启 backend 登录验证
|
||||||
|
func (c *BackendAuthController) GetOpenVerify() {
|
||||||
|
cfg, _ := models.GetPlatformLoginVerify()
|
||||||
|
openVerify := "0"
|
||||||
|
if cfg.OpenVerifyEnabled == 1 {
|
||||||
|
openVerify = "1"
|
||||||
|
}
|
||||||
|
c.serveJSON(map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "ok",
|
||||||
|
"data": []map[string]string{
|
||||||
|
{
|
||||||
|
"label": "openVerify",
|
||||||
|
"value": openVerify,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "verifyType",
|
||||||
|
"value": cfg.VerifyType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register 注册(占位实现)
|
||||||
|
func (c *BackendAuthController) Register() {
|
||||||
|
c.serveJSON(map[string]interface{}{
|
||||||
|
"code": 501,
|
||||||
|
"msg": "注册暂未实现",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendRegisterCode 发送注册验证码(占位实现)
|
||||||
|
func (c *BackendAuthController) SendRegisterCode() {
|
||||||
|
c.serveJSON(map[string]interface{}{
|
||||||
|
"code": 501,
|
||||||
|
"msg": "发送注册验证码暂未实现",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetPassword 忘记密码重置(占位实现)
|
||||||
|
func (c *BackendAuthController) ResetPassword() {
|
||||||
|
c.serveJSON(map[string]interface{}{
|
||||||
|
"code": 501,
|
||||||
|
"msg": "重置密码暂未实现",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendResetCode 发送找回密码验证码(占位实现)
|
||||||
|
func (c *BackendAuthController) SendResetCode() {
|
||||||
|
c.serveJSON(map[string]interface{}{
|
||||||
|
"code": 501,
|
||||||
|
"msg": "发送找回密码验证码暂未实现",
|
||||||
|
})
|
||||||
|
}
|
||||||
907
controllers/backend_file.go
Normal file
907
controllers/backend_file.go
Normal file
@ -0,0 +1,907 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"server/models"
|
||||||
|
"server/pkg/jwtutil"
|
||||||
|
"server/services"
|
||||||
|
|
||||||
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackendFileController 平台端文件管理(yz_system_files / yz_system_files_category)
|
||||||
|
type BackendFileController struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileUploadMaxMB = 2048 // 2GB,适用于大型软件安装包
|
||||||
|
const fileUploadMaxBytes = fileUploadMaxMB * 1024 * 1024
|
||||||
|
|
||||||
|
var fileTypeByCategory = map[string]uint8{
|
||||||
|
"image": 1,
|
||||||
|
"document": 2,
|
||||||
|
"video": 3,
|
||||||
|
"audio": 4,
|
||||||
|
"appsupgrade": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
var allowedExtByCategory = map[string][]string{
|
||||||
|
"image": {"jpg", "jpeg", "png", "gif", "bmp", "webp"},
|
||||||
|
"document": {"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt"},
|
||||||
|
"video": {"mp4", "webm", "mov"},
|
||||||
|
"audio": {"mp3", "wav", "ogg"},
|
||||||
|
// 安装包 / 软件升级(上传时 cate 选 appsupgrade 分类即可,扩展名在此放行)
|
||||||
|
"appsupgrade": {"zip", "exe", "dmg", "msi", "msix", "apk", "deb", "rpm", "7z", "tar", "gz", "pkg"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendFileController) backendClaims() (*jwtutil.Claims, error) {
|
||||||
|
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||||
|
if auth == "" {
|
||||||
|
return nil, fmt.Errorf("未登录")
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(auth, " ", 2)
|
||||||
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||||
|
return nil, fmt.Errorf("认证信息格式错误")
|
||||||
|
}
|
||||||
|
claims, err := jwtutil.ParseToken(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("无效的token")
|
||||||
|
}
|
||||||
|
if claims.UserType != "backend" {
|
||||||
|
return nil, fmt.Errorf("无权访问")
|
||||||
|
}
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendFileController) effectiveTid(claims *jwtutil.Claims) uint64 {
|
||||||
|
_ = c.ParseForm(1 << 20)
|
||||||
|
if tid, err := c.GetUint64("tid"); err == nil && tid > 0 {
|
||||||
|
return tid
|
||||||
|
}
|
||||||
|
if h := strings.TrimSpace(c.Ctx.Request.Header.Get("X-Tenant-Id")); h != "" {
|
||||||
|
if v, e := strconv.ParseUint(h, 10, 64); e == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if claims != nil && claims.TenantId > 0 {
|
||||||
|
return uint64(claims.TenantId)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendFileController) jsonErr(httpStatus, bizCode int, msg string) {
|
||||||
|
c.Ctx.Output.SetStatus(httpStatus)
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendFileController) jsonOK(data interface{}) {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectFileType(ext string) uint8 {
|
||||||
|
ext = strings.ToLower(strings.TrimPrefix(ext, "."))
|
||||||
|
for cat, exts := range allowedExtByCategory {
|
||||||
|
for _, e := range exts {
|
||||||
|
if e == ext {
|
||||||
|
if t, ok := fileTypeByCategory[cat]; ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileExt(name string) string {
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
if i := strings.LastIndex(name, "."); i >= 0 && i < len(name)-1 {
|
||||||
|
return strings.ToLower(name[i+1:])
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileToMap(f *models.SystemFile) map[string]interface{} {
|
||||||
|
ct := f.CreateTime.Format("2006-01-02 15:04:05")
|
||||||
|
m := map[string]interface{}{
|
||||||
|
"id": f.ID,
|
||||||
|
"tid": f.Tid,
|
||||||
|
"name": f.Name,
|
||||||
|
"type": f.Type,
|
||||||
|
"cate": f.Cate,
|
||||||
|
"size": f.Size,
|
||||||
|
"src": f.Src,
|
||||||
|
"uploader": f.Uploader,
|
||||||
|
"md5": f.Md5,
|
||||||
|
"create_time": ct,
|
||||||
|
"createTime": ct,
|
||||||
|
"groupId": f.Cate,
|
||||||
|
"url": f.Src,
|
||||||
|
}
|
||||||
|
if f.Uid != nil {
|
||||||
|
m["uid"] = *f.Uid
|
||||||
|
}
|
||||||
|
if f.Tuid != nil {
|
||||||
|
m["tuid"] = *f.Tuid
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func removePhysicalBySrc(webSrc string) {
|
||||||
|
webSrc = strings.TrimSpace(webSrc)
|
||||||
|
if webSrc == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
webSrc = strings.TrimPrefix(webSrc, "/")
|
||||||
|
_ = os.Remove(webSrc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllFiles GET /backend/allfiles
|
||||||
|
func (c *BackendFileController) GetAllFiles() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
page, _ := c.GetInt("page", 1)
|
||||||
|
pageSize, _ := c.GetInt("pageSize", 10)
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if pageSize < 1 {
|
||||||
|
pageSize = 10
|
||||||
|
}
|
||||||
|
cate, _ := c.GetUint64("cate")
|
||||||
|
keyword := strings.TrimSpace(c.GetString("keyword"))
|
||||||
|
|
||||||
|
qs := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true)
|
||||||
|
if cate > 0 {
|
||||||
|
qs = qs.Filter("cate", cate)
|
||||||
|
}
|
||||||
|
if keyword != "" {
|
||||||
|
qs = qs.Filter("name__icontains", keyword)
|
||||||
|
}
|
||||||
|
total, err := qs.Count()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取文件列表失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var rows []models.SystemFile
|
||||||
|
_, err = qs.OrderBy("-create_time").Limit(pageSize, (page-1)*pageSize).All(&rows)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取文件列表失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list := make([]map[string]interface{}, 0, len(rows))
|
||||||
|
for i := range rows {
|
||||||
|
list = append(list, fileToMap(&rows[i]))
|
||||||
|
}
|
||||||
|
c.jsonOK(map[string]interface{}{
|
||||||
|
"list": list,
|
||||||
|
"total": total,
|
||||||
|
"page": page,
|
||||||
|
"pageSize": pageSize,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserCate GET /backend/usercate
|
||||||
|
func (c *BackendFileController) GetUserCate() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
|
||||||
|
var cates []models.SystemFilesCategory
|
||||||
|
_, err = models.Orm.QueryTable(new(models.SystemFilesCategory)).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
OrderBy("id").
|
||||||
|
All(&cates)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取用户分类失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out := make([]map[string]interface{}, 0, len(cates))
|
||||||
|
for i := range cates {
|
||||||
|
cnt, _ := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("cate", cates[i].ID).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Count()
|
||||||
|
out = append(out, map[string]interface{}{
|
||||||
|
"id": cates[i].ID,
|
||||||
|
"name": cates[i].Name,
|
||||||
|
"total": cnt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
type createCateBody struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Tuid *uint64 `json:"tuid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFileCate POST /backend/createfilecate
|
||||||
|
func (c *BackendFileController) CreateFileCate() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var body createCateBody
|
||||||
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name := strings.TrimSpace(body.Name)
|
||||||
|
if name == "" {
|
||||||
|
c.jsonErr(400, 400, "分组名称不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid := uint64(claims.UserID)
|
||||||
|
row := &models.SystemFilesCategory{
|
||||||
|
Tid: tid,
|
||||||
|
Name: name,
|
||||||
|
Uid: &uid,
|
||||||
|
Tuid: body.Tuid,
|
||||||
|
}
|
||||||
|
id, err := models.Orm.Insert(row)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "新建文件分组失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "新建文件分组成功",
|
||||||
|
"data": map[string]interface{}{"id": uint64(id)},
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
type renameCateBody struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenameFileCate POST /backend/renamefilecate/:id
|
||||||
|
func (c *BackendFileController) RenameFileCate() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil || id == 0 {
|
||||||
|
c.jsonErr(400, 400, "无效的分组ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var body renameCateBody
|
||||||
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name := strings.TrimSpace(body.Name)
|
||||||
|
if name == "" {
|
||||||
|
c.jsonErr(400, 400, "分组名称不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n, err := models.Orm.QueryTable(new(models.SystemFilesCategory)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Update(map[string]interface{}{"name": name})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "重命名文件分组失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
c.jsonErr(404, 404, "分组不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "重命名文件分组成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFileCate DELETE /backend/deletefilecate/:id
|
||||||
|
func (c *BackendFileController) DeleteFileCate() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil || id == 0 {
|
||||||
|
c.jsonErr(400, 400, "无效的分组ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cnt, err := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("cate", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Count()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "删除文件分组失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cnt > 0 {
|
||||||
|
c.jsonErr(400, 400, fmt.Sprintf("该分组下还有 %d 个文件,请先删除分组内文件!", cnt))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
n, err := models.Orm.QueryTable(new(models.SystemFilesCategory)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Update(map[string]interface{}{"delete_time": now})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "删除文件分组失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
c.jsonErr(404, 404, "分组不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "删除文件分组成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCateFiles GET /backend/catefiles/:id
|
||||||
|
func (c *BackendFileController) GetCateFiles() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
cateID, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "无效的分类ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
page, _ := c.GetInt("page", 1)
|
||||||
|
pageSize, _ := c.GetInt("pageSize", 24)
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if pageSize < 1 {
|
||||||
|
pageSize = 24
|
||||||
|
}
|
||||||
|
keyword := strings.TrimSpace(c.GetString("keyword"))
|
||||||
|
|
||||||
|
qs := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("cate", cateID).
|
||||||
|
Filter("delete_time__isnull", true)
|
||||||
|
if keyword != "" {
|
||||||
|
qs = qs.Filter("name__icontains", keyword)
|
||||||
|
}
|
||||||
|
total, err := qs.Count()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取分类文件失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var rows []models.SystemFile
|
||||||
|
_, err = qs.OrderBy("-create_time").Limit(pageSize, (page-1)*pageSize).All(&rows)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取分类文件失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list := make([]map[string]interface{}, 0, len(rows))
|
||||||
|
for i := range rows {
|
||||||
|
list = append(list, fileToMap(&rows[i]))
|
||||||
|
}
|
||||||
|
c.jsonOK(map[string]interface{}{
|
||||||
|
"list": list,
|
||||||
|
"total": total,
|
||||||
|
"page": page,
|
||||||
|
"pageSize": pageSize,
|
||||||
|
"categoryId": cateID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileByID GET /backend/file/:id
|
||||||
|
func (c *BackendFileController) GetFileByID() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil || id == 0 {
|
||||||
|
c.jsonErr(400, 400, "无效的文件ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var f models.SystemFile
|
||||||
|
err = models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
One(&f)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(404, 404, "文件不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.jsonOK(fileToMap(&f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadFile POST /backend/uploadfile
|
||||||
|
func (c *BackendFileController) UploadFile() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
if err := c.Ctx.Request.ParseMultipartForm(fileUploadMaxBytes); err != nil {
|
||||||
|
c.jsonErr(400, 400, "解析上传失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fh, header, err := c.GetFile("file")
|
||||||
|
if err != nil || fh == nil {
|
||||||
|
c.jsonErr(400, 400, "请选择要上传的文件")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fh.Close()
|
||||||
|
|
||||||
|
if header != nil && header.Size > fileUploadMaxBytes {
|
||||||
|
c.jsonErr(400, 400, fmt.Sprintf("文件大小不能超过%dMB", fileUploadMaxMB))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := fileExt(header.Filename)
|
||||||
|
if ext == "" {
|
||||||
|
c.jsonErr(400, 400, "无法识别文件扩展名")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取存储服务
|
||||||
|
storageService, err := services.GetStorageService()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取存储服务失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
result, err := storageService.Upload(fh, header)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "上传文件失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件是否已存在(通过MD5)
|
||||||
|
var exist models.SystemFile
|
||||||
|
err = models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("md5", result.MD5).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
One(&exist)
|
||||||
|
if err == nil {
|
||||||
|
// 文件已存在,返回已有记录
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 201,
|
||||||
|
"msg": "文件已存在",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"url": exist.Src,
|
||||||
|
"id": exist.ID,
|
||||||
|
"name": exist.Name,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取分类
|
||||||
|
cateStr := c.GetString("cate")
|
||||||
|
var cate uint64
|
||||||
|
if cateStr != "" {
|
||||||
|
cate, _ = strconv.ParseUint(cateStr, 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
adminID := uint64(claims.UserID)
|
||||||
|
var tuidPtr *uint64
|
||||||
|
if ts := strings.TrimSpace(c.GetString("tuid")); ts != "" {
|
||||||
|
if v, e := strconv.ParseUint(ts, 10, 64); e == nil {
|
||||||
|
tuidPtr = &v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存文件记录到数据库
|
||||||
|
row := &models.SystemFile{
|
||||||
|
Tid: tid,
|
||||||
|
Uid: &adminID,
|
||||||
|
Tuid: tuidPtr,
|
||||||
|
Name: header.Filename,
|
||||||
|
Type: detectFileType(ext),
|
||||||
|
Cate: cate,
|
||||||
|
Size: uint64(result.Size),
|
||||||
|
Src: result.URL,
|
||||||
|
Uploader: adminID,
|
||||||
|
Md5: result.MD5,
|
||||||
|
}
|
||||||
|
id, err := models.Orm.Insert(row)
|
||||||
|
if err != nil {
|
||||||
|
// 数据库插入失败,尝试删除已上传的文件
|
||||||
|
_ = storageService.Delete(result.Key)
|
||||||
|
c.jsonErr(500, 500, "上传失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "上传成功",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"url": result.URL,
|
||||||
|
"id": uint64(id),
|
||||||
|
"name": header.Filename,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
func md5HashFile(path string) (string, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
h := md5.New()
|
||||||
|
if _, err := io.Copy(h, f); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(h.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type updateFileBody struct {
|
||||||
|
Name *string `json:"name"`
|
||||||
|
Cate *uint64 `json:"cate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFile POST /backend/updatefile/:id
|
||||||
|
func (c *BackendFileController) UpdateFile() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil || id == 0 {
|
||||||
|
c.jsonErr(400, 400, "无效的文件ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var body updateFileBody
|
||||||
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
up := map[string]interface{}{}
|
||||||
|
if body.Name != nil {
|
||||||
|
up["name"] = strings.TrimSpace(*body.Name)
|
||||||
|
}
|
||||||
|
if body.Cate != nil {
|
||||||
|
up["cate"] = *body.Cate
|
||||||
|
}
|
||||||
|
if len(up) == 0 {
|
||||||
|
c.jsonErr(400, 400, "无更新数据")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
up["update_time"] = now
|
||||||
|
n, err := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Update(up)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "更新失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
c.jsonErr(404, 404, "文件不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "更新成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFile DELETE /backend/deletefile/:id
|
||||||
|
func (c *BackendFileController) DeleteFile() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil || id == 0 {
|
||||||
|
c.jsonErr(400, 400, "无效的文件ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
n, err := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Update(map[string]interface{}{"delete_time": now})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "删除失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
c.jsonErr(404, 404, "文件不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "删除成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFilePermanently DELETE /backend/deletefilepermanently/:id
|
||||||
|
func (c *BackendFileController) DeleteFilePermanently() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil || id == 0 {
|
||||||
|
c.jsonErr(400, 400, "无效的文件ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var f models.SystemFile
|
||||||
|
err = models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
One(&f)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(404, 404, "文件不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
removePhysicalBySrc(f.Src)
|
||||||
|
_, err = models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Delete()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "永久删除失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "永久删除成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveFile GET /backend/movefile/:id
|
||||||
|
func (c *BackendFileController) MoveFile() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil || id == 0 {
|
||||||
|
c.jsonErr(400, 400, "无效的文件ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cate, _ := c.GetUint64("cate")
|
||||||
|
now := time.Now()
|
||||||
|
n, err := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Update(map[string]interface{}{"cate": cate, "update_time": now})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "移动失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
c.jsonErr(404, 404, "文件不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "移动成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
type idsBody struct {
|
||||||
|
IDs []uint64 `json:"ids"`
|
||||||
|
Cate *uint64 `json:"cate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchDeleteFiles POST /backend/batchdeletefiles
|
||||||
|
func (c *BackendFileController) BatchDeleteFiles() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var body idsBody
|
||||||
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(body.IDs) == 0 {
|
||||||
|
c.jsonErr(400, 400, "请选择要删除的文件")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
for _, id := range body.IDs {
|
||||||
|
var f models.SystemFile
|
||||||
|
e := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
One(&f)
|
||||||
|
if e == nil && f.Src != "" {
|
||||||
|
removePhysicalBySrc(f.Src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n, err := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("id__in", body.IDs).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Update(map[string]interface{}{"delete_time": now})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "批量删除失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
c.jsonErr(404, 404, "文件不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "批量删除成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchDeleteFilesPermanently POST /backend/batchDeleteFilesPermanently
|
||||||
|
func (c *BackendFileController) BatchDeleteFilesPermanently() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var body idsBody
|
||||||
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(body.IDs) == 0 {
|
||||||
|
c.jsonErr(400, 400, "请选择要彻底删除的文件")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var rows []models.SystemFile
|
||||||
|
_, err = models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("id__in", body.IDs).
|
||||||
|
Filter("tid", tid).
|
||||||
|
All(&rows)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "批量彻底删除失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := range rows {
|
||||||
|
removePhysicalBySrc(rows[i].Src)
|
||||||
|
}
|
||||||
|
n, err := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("id__in", body.IDs).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Delete()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "批量彻底删除失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
c.jsonErr(404, 404, "文件不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "批量彻底删除成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadAvatar POST /backend/uploadavatar(占位)
|
||||||
|
func (c *BackendFileController) UploadAvatar() {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 501, "msg": "上传头像暂未实现"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAvatar POST /backend/uploadavatar/:id(占位)
|
||||||
|
func (c *BackendFileController) UpdateAvatar() {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 501, "msg": "更新头像暂未实现"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchMoveFiles POST /backend/batchMoveFiles
|
||||||
|
func (c *BackendFileController) BatchMoveFiles() {
|
||||||
|
claims, err := c.backendClaims()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tid := c.effectiveTid(claims)
|
||||||
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var body idsBody
|
||||||
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(body.IDs) == 0 {
|
||||||
|
c.jsonErr(400, 400, "请选择要移动的文件")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if body.Cate == nil {
|
||||||
|
c.jsonErr(400, 400, "缺少目标分类")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
n, err := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
|
Filter("id__in", body.IDs).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Update(map[string]interface{}{"cate": *body.Cate, "update_time": now})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "批量移动失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
c.jsonErr(404, 404, "文件不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "批量移动成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
249
controllers/backend_login_verify.go
Normal file
249
controllers/backend_login_verify.go
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"server/models"
|
||||||
|
"server/pkg/jwtutil"
|
||||||
|
|
||||||
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackendLoginVerifyController 后台登录验证配置
|
||||||
|
// 对应前端 backend/src/api/sitesettings.js:
|
||||||
|
// - GET /backend/loginVerifyInfos
|
||||||
|
// - POST /backend/saveloginVerifyInfos
|
||||||
|
type BackendLoginVerifyController struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendLoginVerifyController) backendLoginVerifyClaims() (*jwtutil.Claims, error) {
|
||||||
|
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||||
|
if auth == "" {
|
||||||
|
return nil, errBackendLoginVerify("未登录")
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(auth, " ", 2)
|
||||||
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||||
|
return nil, errBackendLoginVerify("认证信息格式错误")
|
||||||
|
}
|
||||||
|
claims, err := jwtutil.ParseToken(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, errBackendLoginVerify("无效的token")
|
||||||
|
}
|
||||||
|
if claims.UserType != "backend" {
|
||||||
|
return nil, errBackendLoginVerify("无权访问")
|
||||||
|
}
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type backendLoginVerifyError string
|
||||||
|
|
||||||
|
func (e backendLoginVerifyError) Error() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func errBackendLoginVerify(msg string) error {
|
||||||
|
return backendLoginVerifyError(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendLoginVerifyController) jsonErr(httpStatus, bizCode int, msg string) {
|
||||||
|
c.Ctx.Output.SetStatus(httpStatus)
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
type backendLoginVerifyPayload struct {
|
||||||
|
OpenVerify *bool `json:"openVerify"`
|
||||||
|
OpenVerifyInt *int8 `json:"openVerify_enabled"`
|
||||||
|
VerifyModel string `json:"verifyModel"`
|
||||||
|
UseGeetest string `json:"use_geetest"`
|
||||||
|
Geetest3ID *string `json:"geetest3ID"`
|
||||||
|
Geetest3IDSnake *string `json:"geetest3_id"`
|
||||||
|
Geetest3Key *string `json:"geetest3KEY"`
|
||||||
|
Geetest3KeySnake *string `json:"geetest3_key"`
|
||||||
|
Geetest4ID *string `json:"geetest4ID"`
|
||||||
|
Geetest4IDSnake *string `json:"geetest4_id"`
|
||||||
|
Geetest4Key *string `json:"geetest4KEY"`
|
||||||
|
Geetest4KeySnake *string `json:"geetest4_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func backendVerifyTypeToModel(v string) string {
|
||||||
|
switch strings.TrimSpace(v) {
|
||||||
|
case "captcha":
|
||||||
|
return "1"
|
||||||
|
case "sms":
|
||||||
|
return "2"
|
||||||
|
case "email":
|
||||||
|
return "3"
|
||||||
|
case "geetest3":
|
||||||
|
return "4"
|
||||||
|
case "geetest", "geetest4":
|
||||||
|
return "5"
|
||||||
|
default:
|
||||||
|
return "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func backendVerifyModelToType(v string) string {
|
||||||
|
switch strings.TrimSpace(v) {
|
||||||
|
case "1":
|
||||||
|
return "captcha"
|
||||||
|
case "2":
|
||||||
|
return "sms"
|
||||||
|
case "3":
|
||||||
|
return "email"
|
||||||
|
case "4":
|
||||||
|
return "geetest3"
|
||||||
|
case "5":
|
||||||
|
return "geetest4"
|
||||||
|
default:
|
||||||
|
switch strings.TrimSpace(v) {
|
||||||
|
case "captcha", "sms", "email", "geetest", "geetest3", "geetest4":
|
||||||
|
return strings.TrimSpace(v)
|
||||||
|
default:
|
||||||
|
return "captcha"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func backendStringPtrValue(primary, fallback *string) string {
|
||||||
|
if primary != nil {
|
||||||
|
return *primary
|
||||||
|
}
|
||||||
|
if fallback != nil {
|
||||||
|
return *fallback
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func backendStringPtrOrNil(primary, fallback *string) *string {
|
||||||
|
value := strings.TrimSpace(backendStringPtrValue(primary, fallback))
|
||||||
|
if value == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &value
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLoginVerifyInfos GET /backend/loginVerifyInfos
|
||||||
|
func (c *BackendLoginVerifyController) GetLoginVerifyInfos() {
|
||||||
|
if _, err := c.backendLoginVerifyClaims(); err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := models.GetPlatformLoginVerify()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取配置失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
openVerify := "0"
|
||||||
|
if cfg.OpenVerifyEnabled == 1 {
|
||||||
|
openVerify = "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []map[string]string{
|
||||||
|
{"label": "openVerify", "value": openVerify},
|
||||||
|
{"label": "verifyModel", "value": backendVerifyTypeToModel(cfg.VerifyType)},
|
||||||
|
{"label": "geetest3ID", "value": backendStringPtrValue(cfg.Geetest3ID, nil)},
|
||||||
|
{"label": "geetest3KEY", "value": backendStringPtrValue(cfg.Geetest3Key, nil)},
|
||||||
|
{"label": "geetest4ID", "value": backendStringPtrValue(cfg.Geetest4ID, nil)},
|
||||||
|
{"label": "geetest4KEY", "value": backendStringPtrValue(cfg.Geetest4Key, nil)},
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveLoginVerifyInfos POST /backend/saveloginVerifyInfos
|
||||||
|
func (c *BackendLoginVerifyController) SaveLoginVerifyInfos() {
|
||||||
|
if _, err := c.backendLoginVerifyClaims(); err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var p backendLoginVerifyPayload
|
||||||
|
if err := json.Unmarshal(raw, &p); err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
openVerifyEnabled := int8(0)
|
||||||
|
if p.OpenVerify != nil && *p.OpenVerify {
|
||||||
|
openVerifyEnabled = 1
|
||||||
|
}
|
||||||
|
if p.OpenVerifyInt != nil {
|
||||||
|
openVerifyEnabled = *p.OpenVerifyInt
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyModel := p.VerifyModel
|
||||||
|
if strings.TrimSpace(verifyModel) == "" {
|
||||||
|
verifyModel = p.UseGeetest
|
||||||
|
}
|
||||||
|
verifyType := backendVerifyModelToType(verifyModel)
|
||||||
|
|
||||||
|
geetest3ID := backendStringPtrOrNil(p.Geetest3ID, p.Geetest3IDSnake)
|
||||||
|
geetest3Key := backendStringPtrOrNil(p.Geetest3Key, p.Geetest3KeySnake)
|
||||||
|
geetest4ID := backendStringPtrOrNil(p.Geetest4ID, p.Geetest4IDSnake)
|
||||||
|
geetest4Key := backendStringPtrOrNil(p.Geetest4Key, p.Geetest4KeySnake)
|
||||||
|
|
||||||
|
if verifyType == "geetest3" {
|
||||||
|
if geetest3ID == nil || geetest3Key == nil {
|
||||||
|
c.jsonErr(400, 400, "极验3.0 ID和KEY不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if verifyType == "geetest4" || verifyType == "geetest" {
|
||||||
|
if geetest4ID == nil || geetest4Key == nil {
|
||||||
|
c.jsonErr(400, 400, "极验4.0 ID和KEY不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
var existed models.PlatformLoginVerify
|
||||||
|
err = models.Orm.QueryTable(new(models.PlatformLoginVerify)).OrderBy("-id").One(&existed)
|
||||||
|
if err == nil {
|
||||||
|
_, err = models.Orm.QueryTable(new(models.PlatformLoginVerify)).
|
||||||
|
Filter("id", existed.ID).
|
||||||
|
Update(map[string]interface{}{
|
||||||
|
"open_verify_enabled": openVerifyEnabled,
|
||||||
|
"verify_type": verifyType,
|
||||||
|
"geetest3_id": geetest3ID,
|
||||||
|
"geetest3_key": geetest3Key,
|
||||||
|
"geetest4_id": geetest4ID,
|
||||||
|
"geetest4_key": geetest4Key,
|
||||||
|
"update_time": now,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "保存失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
row := &models.PlatformLoginVerify{
|
||||||
|
OpenVerifyEnabled: openVerifyEnabled,
|
||||||
|
VerifyType: verifyType,
|
||||||
|
Geetest3ID: geetest3ID,
|
||||||
|
Geetest3Key: geetest3Key,
|
||||||
|
Geetest4ID: geetest4ID,
|
||||||
|
Geetest4Key: geetest4Key,
|
||||||
|
UpdateTime: &now,
|
||||||
|
}
|
||||||
|
if _, err := models.Orm.Insert(row); err != nil {
|
||||||
|
c.jsonErr(500, 500, "保存失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
70
controllers/backend_modules.go
Normal file
70
controllers/backend_modules.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"server/models"
|
||||||
|
"server/pkg/jwtutil"
|
||||||
|
|
||||||
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackendModulesController backend 模块接口(yz_system_modules)
|
||||||
|
type BackendModulesController struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendModulesController) backendModulesClaims() (*jwtutil.Claims, error) {
|
||||||
|
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||||
|
if auth == "" {
|
||||||
|
return nil, fmt.Errorf("未登录")
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(auth, " ", 2)
|
||||||
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||||
|
return nil, fmt.Errorf("认证信息格式错误")
|
||||||
|
}
|
||||||
|
claims, err := jwtutil.ParseToken(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("无效的token")
|
||||||
|
}
|
||||||
|
if claims.UserType != "backend" {
|
||||||
|
return nil, fmt.Errorf("无权访问")
|
||||||
|
}
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendModulesController) jsonErr(httpStatus, bizCode int, msg string) {
|
||||||
|
c.Ctx.Output.SetStatus(httpStatus)
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTenantList GET /backend/modules/getTenantList
|
||||||
|
// 返回当前 backend 账号可见的模块。当前实现:返回 status=1 且 is_show=1 的全部模块。
|
||||||
|
func (c *BackendModulesController) GetTenantList() {
|
||||||
|
if _, err := c.backendModulesClaims(); err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var rows []models.SystemModules
|
||||||
|
_, err := models.Orm.QueryTable(new(models.SystemModules)).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Filter("status", 1).
|
||||||
|
Filter("is_show", 1).
|
||||||
|
OrderBy("sort", "id").
|
||||||
|
All(&rows)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取失败:"+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "获取成功",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"list": rows,
|
||||||
|
"total": len(rows),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
332
controllers/backend_operation_log.go
Normal file
332
controllers/backend_operation_log.go
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"server/models"
|
||||||
|
"server/pkg/jwtutil"
|
||||||
|
|
||||||
|
"github.com/beego/beego/v2/client/orm"
|
||||||
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackendOperationLogController 操作日志(yz_system_operation_log)
|
||||||
|
type BackendOperationLogController struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendOperationLogController) backendClaims() (*jwtutil.Claims, error) {
|
||||||
|
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||||
|
if auth == "" {
|
||||||
|
return nil, fmt.Errorf("未登录")
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(auth, " ", 2)
|
||||||
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||||
|
return nil, fmt.Errorf("认证信息格式错误")
|
||||||
|
}
|
||||||
|
claims, err := jwtutil.ParseToken(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("无效的token")
|
||||||
|
}
|
||||||
|
if claims.UserType != "backend" {
|
||||||
|
return nil, fmt.Errorf("无权访问")
|
||||||
|
}
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendOperationLogController) jsonErr(httpStatus, bizCode int, msg string) {
|
||||||
|
c.Ctx.Output.SetStatus(httpStatus)
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// List GET /backend/operationLogs?page=1&pageSize=20&keyword=&module=&action=&status=&startTime=&endTime=
|
||||||
|
func (c *BackendOperationLogController) List() {
|
||||||
|
if _, err := c.backendClaims(); err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
page, _ := c.GetInt("page", 1)
|
||||||
|
pageSize, _ := c.GetInt("pageSize", 20)
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if pageSize < 1 {
|
||||||
|
pageSize = 20
|
||||||
|
}
|
||||||
|
if pageSize > 200 {
|
||||||
|
pageSize = 200
|
||||||
|
}
|
||||||
|
|
||||||
|
keyword := strings.TrimSpace(c.GetString("keyword"))
|
||||||
|
module := strings.TrimSpace(c.GetString("module"))
|
||||||
|
action := strings.TrimSpace(c.GetString("action"))
|
||||||
|
statusStr := strings.TrimSpace(c.GetString("status"))
|
||||||
|
startTimeStr := strings.TrimSpace(c.GetString("startTime"))
|
||||||
|
endTimeStr := strings.TrimSpace(c.GetString("endTime"))
|
||||||
|
|
||||||
|
qs := models.Orm.QueryTable(new(models.SystemOperationLog)).Filter("delete_time__isnull", true)
|
||||||
|
|
||||||
|
// 条件拼装
|
||||||
|
cond := orm.NewCondition()
|
||||||
|
needCond := false
|
||||||
|
|
||||||
|
if module != "" {
|
||||||
|
cond = cond.And("module", module)
|
||||||
|
needCond = true
|
||||||
|
}
|
||||||
|
if action != "" {
|
||||||
|
cond = cond.And("action", action)
|
||||||
|
needCond = true
|
||||||
|
}
|
||||||
|
if statusStr != "" {
|
||||||
|
if st, err := strconv.Atoi(statusStr); err == nil {
|
||||||
|
cond = cond.And("status", st)
|
||||||
|
needCond = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if keyword != "" {
|
||||||
|
kw := orm.NewCondition().
|
||||||
|
Or("module__icontains", keyword).
|
||||||
|
Or("action__icontains", keyword).
|
||||||
|
Or("method__icontains", keyword).
|
||||||
|
Or("url__icontains", keyword).
|
||||||
|
Or("ip__icontains", keyword).
|
||||||
|
Or("user_agent__icontains", keyword)
|
||||||
|
if uid, err := strconv.ParseUint(keyword, 10, 64); err == nil && uid > 0 {
|
||||||
|
kw = kw.Or("user_id", uid)
|
||||||
|
}
|
||||||
|
cond = cond.AndCond(kw)
|
||||||
|
needCond = true
|
||||||
|
}
|
||||||
|
if t, err := parseTimeFlexible(startTimeStr); err == nil && !t.IsZero() {
|
||||||
|
cond = cond.And("create_time__gte", t)
|
||||||
|
needCond = true
|
||||||
|
}
|
||||||
|
if t, err := parseTimeFlexible(endTimeStr); err == nil && !t.IsZero() {
|
||||||
|
cond = cond.And("create_time__lte", t)
|
||||||
|
needCond = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if needCond {
|
||||||
|
qs = qs.SetCond(cond)
|
||||||
|
}
|
||||||
|
|
||||||
|
total, err := qs.Count()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取操作日志失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var rows []models.SystemOperationLog
|
||||||
|
_, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&rows)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取操作日志失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list := make([]map[string]interface{}, 0, len(rows))
|
||||||
|
for i := range rows {
|
||||||
|
item := map[string]interface{}{
|
||||||
|
"id": rows[i].ID,
|
||||||
|
"tid": rows[i].Tid,
|
||||||
|
"user_id": rows[i].UserID,
|
||||||
|
"module": rows[i].Module,
|
||||||
|
"action": rows[i].Action,
|
||||||
|
"method": rows[i].Method,
|
||||||
|
"url": rows[i].URL,
|
||||||
|
"ip": rows[i].IP,
|
||||||
|
"user_agent": rows[i].UserAgent,
|
||||||
|
"request_data": rows[i].RequestData,
|
||||||
|
"response_data": rows[i].ResponseData,
|
||||||
|
"status": rows[i].Status,
|
||||||
|
"error_message": rows[i].ErrorMessage,
|
||||||
|
"execution_time": rows[i].ExecutionTime,
|
||||||
|
"create_time": rows[i].CreateTime.Format("2006-01-02 15:04:05"),
|
||||||
|
"update_time": "",
|
||||||
|
}
|
||||||
|
if rows[i].UpdateTime != nil {
|
||||||
|
item["update_time"] = rows[i].UpdateTime.Format("2006-01-02 15:04:05")
|
||||||
|
}
|
||||||
|
list = append(list, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"list": list,
|
||||||
|
"total": total,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail GET /backend/operationLogs/:id
|
||||||
|
func (c *BackendOperationLogController) Detail() {
|
||||||
|
if _, err := c.backendClaims(); err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil || id == 0 {
|
||||||
|
c.jsonErr(400, 400, "无效ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var row models.SystemOperationLog
|
||||||
|
err = models.Orm.QueryTable(new(models.SystemOperationLog)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
One(&row)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(404, 404, "记录不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out := map[string]interface{}{
|
||||||
|
"id": row.ID,
|
||||||
|
"tid": row.Tid,
|
||||||
|
"user_id": row.UserID,
|
||||||
|
"module": row.Module,
|
||||||
|
"action": row.Action,
|
||||||
|
"method": row.Method,
|
||||||
|
"url": row.URL,
|
||||||
|
"ip": row.IP,
|
||||||
|
"user_agent": row.UserAgent,
|
||||||
|
"request_data": row.RequestData,
|
||||||
|
"response_data": row.ResponseData,
|
||||||
|
"status": row.Status,
|
||||||
|
"error_message": row.ErrorMessage,
|
||||||
|
"execution_time": row.ExecutionTime,
|
||||||
|
"create_time": row.CreateTime.Format("2006-01-02 15:04:05"),
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete DELETE /backend/operationLogs/:id
|
||||||
|
func (c *BackendOperationLogController) Delete() {
|
||||||
|
if _, err := c.backendClaims(); err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil || id == 0 {
|
||||||
|
c.jsonErr(400, 400, "无效ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
n, err := models.Orm.QueryTable(new(models.SystemOperationLog)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Update(map[string]interface{}{"delete_time": now})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "删除失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
c.jsonErr(404, 404, "记录不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "删除成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
type backendBatchDeletePayload struct {
|
||||||
|
IDs []uint64 `json:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchDelete POST /backend/operationLogs/batchDelete
|
||||||
|
func (c *BackendOperationLogController) BatchDelete() {
|
||||||
|
if _, err := c.backendClaims(); err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var p backendBatchDeletePayload
|
||||||
|
if err := json.Unmarshal(raw, &p); err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(p.IDs) == 0 {
|
||||||
|
c.jsonErr(400, 400, "请选择要删除的日志")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
_, err = models.Orm.QueryTable(new(models.SystemOperationLog)).
|
||||||
|
Filter("id__in", p.IDs).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Update(map[string]interface{}{"delete_time": now})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "批量删除失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "批量删除成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statistics GET /backend/operationLogs/statistics
|
||||||
|
// 供前端筛选项:modules/actions
|
||||||
|
func (c *BackendOperationLogController) Statistics() {
|
||||||
|
if _, err := c.backendClaims(); err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var moduleRows []models.SystemOperationLog
|
||||||
|
_, _ = models.Orm.QueryTable(new(models.SystemOperationLog)).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Filter("module__isnull", false).
|
||||||
|
Limit(1000).
|
||||||
|
All(&moduleRows, "Module")
|
||||||
|
modSet := map[string]struct{}{}
|
||||||
|
for i := range moduleRows {
|
||||||
|
m := strings.TrimSpace(moduleRows[i].Module)
|
||||||
|
if m != "" {
|
||||||
|
modSet[m] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modules := make([]string, 0, len(modSet))
|
||||||
|
for k := range modSet {
|
||||||
|
modules = append(modules, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
var actionRows []models.SystemOperationLog
|
||||||
|
_, _ = models.Orm.QueryTable(new(models.SystemOperationLog)).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Filter("action__isnull", false).
|
||||||
|
Limit(1000).
|
||||||
|
All(&actionRows, "Action")
|
||||||
|
actSet := map[string]struct{}{}
|
||||||
|
for i := range actionRows {
|
||||||
|
a := strings.TrimSpace(actionRows[i].Action)
|
||||||
|
if a != "" {
|
||||||
|
actSet[a] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actions := make([]string, 0, len(actSet))
|
||||||
|
for k := range actSet {
|
||||||
|
actions = append(actions, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"modules": modules,
|
||||||
|
"actions": actions,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
607
controllers/backend_site_settings.go
Normal file
607
controllers/backend_site_settings.go
Normal file
@ -0,0 +1,607 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"server/models"
|
||||||
|
"server/pkg/jwtutil"
|
||||||
|
|
||||||
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackendSiteSettingsController 租户站点设置(站点基本信息)
|
||||||
|
// 对应前端 normalSettings.vue 的:
|
||||||
|
// - GET /backend/normalInfos
|
||||||
|
// - POST /backend/saveNormalInfos
|
||||||
|
// - GET /platform/normalInfos
|
||||||
|
// - POST /platform/saveNormalInfos
|
||||||
|
type BackendSiteSettingsController struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendSiteSettingsController) jsonErr(httpStatus, bizCode int, msg string) {
|
||||||
|
c.Ctx.Output.SetStatus(httpStatus)
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendSiteSettingsController) claimsByPath() (*jwtutil.Claims, error) {
|
||||||
|
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||||
|
if auth == "" {
|
||||||
|
return nil, fmt.Errorf("未登录")
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(auth, " ", 2)
|
||||||
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||||
|
return nil, fmt.Errorf("认证信息格式错误")
|
||||||
|
}
|
||||||
|
claims, err := jwtutil.ParseToken(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("无效的token")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := strings.ToLower(c.Ctx.Request.URL.Path)
|
||||||
|
if strings.HasPrefix(path, "/platform/") {
|
||||||
|
if claims.UserType != "platform" {
|
||||||
|
return nil, fmt.Errorf("无权访问")
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(path, "/backend/") {
|
||||||
|
if claims.UserType != "backend" {
|
||||||
|
return nil, fmt.Errorf("无权访问")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBackendUint64Flexible(v interface{}) uint64 {
|
||||||
|
if v == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
switch x := v.(type) {
|
||||||
|
case float64:
|
||||||
|
if x <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return uint64(x)
|
||||||
|
case string:
|
||||||
|
s := strings.TrimSpace(x)
|
||||||
|
if s == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
n, err := strconv.ParseUint(s, 10, 64)
|
||||||
|
if err != nil || n == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type backendNormalInfosOutput struct {
|
||||||
|
Sitename string `json:"sitename"`
|
||||||
|
Companyintroduction string `json:"companyintroduction"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Copyright string `json:"copyright"`
|
||||||
|
Companyname string `json:"companyname"`
|
||||||
|
Icp string `json:"icp"`
|
||||||
|
Logo string `json:"logo"`
|
||||||
|
Logow string `json:"logow"`
|
||||||
|
Ico string `json:"ico"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNormalInfos GET /backend/normalInfos 或 /platform/normalInfos
|
||||||
|
func (c *BackendSiteSettingsController) GetNormalInfos() {
|
||||||
|
claims, err := c.claimsByPath()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优先使用 token 中的租户 id;若为 0,则允许前端通过查询参数传入(兼容历史/平台端)。
|
||||||
|
tid := uint64(claims.TenantId)
|
||||||
|
if tid == 0 {
|
||||||
|
tidStr := strings.TrimSpace(c.GetString("tid"))
|
||||||
|
if tidStr != "" {
|
||||||
|
if n, err := strconv.ParseUint(tidStr, 10, 64); err == nil {
|
||||||
|
tid = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out := backendNormalInfosOutput{
|
||||||
|
Sitename: "",
|
||||||
|
Companyintroduction: "",
|
||||||
|
Description: "",
|
||||||
|
Copyright: "",
|
||||||
|
Companyname: "",
|
||||||
|
Icp: "",
|
||||||
|
Logo: "",
|
||||||
|
Logow: "",
|
||||||
|
Ico: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
// tid 缺失时不报错,直接返回空对象给前端渲染(避免 UI 直接崩)。
|
||||||
|
if tid == 0 {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var rows []models.TenantSiteSetting
|
||||||
|
_, err = models.Orm.QueryTable(new(models.TenantSiteSetting)).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Limit(1).
|
||||||
|
All(&rows)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(rows) > 0 {
|
||||||
|
r := rows[0]
|
||||||
|
out.Sitename = r.Sitename
|
||||||
|
out.Companyintroduction = r.Companyintroduction
|
||||||
|
out.Logo = r.Logo
|
||||||
|
out.Logow = r.Logow
|
||||||
|
out.Ico = r.Ico
|
||||||
|
out.Description = r.Description
|
||||||
|
out.Copyright = r.Copyright
|
||||||
|
out.Companyname = r.Companyname
|
||||||
|
out.Icp = r.Icp
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
type backendNormalInfosPayload struct {
|
||||||
|
// 前端会传 tid(但我们仍优先使用 token 的 tenant_id)
|
||||||
|
Tid interface{} `json:"tid"`
|
||||||
|
|
||||||
|
Sitename string `json:"sitename"`
|
||||||
|
Companyintroduction string `json:"companyintroduction"`
|
||||||
|
Logo string `json:"logo"`
|
||||||
|
Logow string `json:"logow"`
|
||||||
|
Ico string `json:"ico"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Copyright string `json:"copyright"`
|
||||||
|
Companyname string `json:"companyname"`
|
||||||
|
Icp string `json:"icp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveNormalInfos POST /backend/saveNormalInfos 或 /platform/saveNormalInfos
|
||||||
|
func (c *BackendSiteSettingsController) SaveNormalInfos() {
|
||||||
|
claims, err := c.claimsByPath()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var p backendNormalInfosPayload
|
||||||
|
if uerr := json.Unmarshal(raw, &p); uerr != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tid := uint64(claims.TenantId)
|
||||||
|
if tid == 0 {
|
||||||
|
tid = parseBackendUint64Flexible(p.Tid)
|
||||||
|
}
|
||||||
|
if tid == 0 {
|
||||||
|
c.jsonErr(400, 400, "tid不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sitename := strings.TrimSpace(p.Sitename)
|
||||||
|
if sitename == "" {
|
||||||
|
c.jsonErr(400, 400, "站点名称不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
up := map[string]interface{}{
|
||||||
|
"tid": tid,
|
||||||
|
"sitename": sitename,
|
||||||
|
"companyintroduction": strings.TrimSpace(p.Companyintroduction),
|
||||||
|
"logo": strings.TrimSpace(p.Logo),
|
||||||
|
"logow": strings.TrimSpace(p.Logow),
|
||||||
|
"ico": strings.TrimSpace(p.Ico),
|
||||||
|
"description": strings.TrimSpace(p.Description),
|
||||||
|
"copyright": strings.TrimSpace(p.Copyright),
|
||||||
|
"companyname": strings.TrimSpace(p.Companyname),
|
||||||
|
"icp": strings.TrimSpace(p.Icp),
|
||||||
|
"update_time": now,
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt, err := models.Orm.QueryTable(new(models.TenantSiteSetting)).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Count()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "保存失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnt == 0 {
|
||||||
|
row := &models.TenantSiteSetting{
|
||||||
|
Tid: tid,
|
||||||
|
Sitename: sitename,
|
||||||
|
Companyintroduction: strings.TrimSpace(p.Companyintroduction),
|
||||||
|
Logo: strings.TrimSpace(p.Logo),
|
||||||
|
Logow: strings.TrimSpace(p.Logow),
|
||||||
|
Ico: strings.TrimSpace(p.Ico),
|
||||||
|
Description: strings.TrimSpace(p.Description),
|
||||||
|
Copyright: strings.TrimSpace(p.Copyright),
|
||||||
|
Companyname: strings.TrimSpace(p.Companyname),
|
||||||
|
Icp: strings.TrimSpace(p.Icp),
|
||||||
|
CreateTime: now,
|
||||||
|
UpdateTime: &now,
|
||||||
|
}
|
||||||
|
_, err = models.Orm.Insert(row)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "保存失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, err = models.Orm.QueryTable(new(models.TenantSiteSetting)).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Update(up)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "保存失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendSiteSettingsController) resolveBackendTenantID(claims *jwtutil.Claims, payloadTid interface{}) uint64 {
|
||||||
|
tid := uint64(claims.TenantId)
|
||||||
|
if tid == 0 {
|
||||||
|
tid = parseBackendUint64Flexible(payloadTid)
|
||||||
|
}
|
||||||
|
if tid == 0 {
|
||||||
|
tidStr := strings.TrimSpace(c.GetString("tid"))
|
||||||
|
if tidStr != "" {
|
||||||
|
if n, err := strconv.ParseUint(tidStr, 10, 64); err == nil {
|
||||||
|
tid = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendSiteSettingsController) ensureBackendSettingItemsTable() error {
|
||||||
|
_, err := models.Orm.Raw(`
|
||||||
|
CREATE TABLE IF NOT EXISTS yz_system_tenant_setting_items (
|
||||||
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
tid BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
||||||
|
setting_key VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
|
setting_value LONGTEXT NULL,
|
||||||
|
create_time DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
update_time DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
delete_time DATETIME NULL DEFAULT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE KEY uk_tid_key (tid, setting_key),
|
||||||
|
KEY idx_tid (tid),
|
||||||
|
KEY idx_delete_time (delete_time)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户站点扩展设置';
|
||||||
|
`).Exec()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendSiteSettingsController) getBackendSettingItems(tid uint64, keys []string) (map[string]string, error) {
|
||||||
|
out := make(map[string]string, len(keys))
|
||||||
|
for _, key := range keys {
|
||||||
|
out[key] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.ensureBackendSettingItemsTable(); err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type rowItem struct {
|
||||||
|
SettingKey string
|
||||||
|
SettingValue string
|
||||||
|
}
|
||||||
|
var rows []rowItem
|
||||||
|
_, err := models.Orm.Raw(
|
||||||
|
"SELECT setting_key, IFNULL(setting_value, '') AS setting_value FROM yz_system_tenant_setting_items WHERE tid = ? AND setting_key IN ('"+strings.Join(keys, "','")+"') AND delete_time IS NULL",
|
||||||
|
tid,
|
||||||
|
).QueryRows(&rows)
|
||||||
|
if err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
out[row.SettingKey] = row.SettingValue
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BackendSiteSettingsController) saveBackendSettingItems(tid uint64, values map[string]string) error {
|
||||||
|
if err := c.ensureBackendSettingItemsTable(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for key, value := range values {
|
||||||
|
_, err := models.Orm.Raw(`
|
||||||
|
INSERT INTO yz_system_tenant_setting_items (tid, setting_key, setting_value, create_time, update_time)
|
||||||
|
VALUES (?, ?, ?, NOW(), NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), update_time = NOW(), delete_time = NULL
|
||||||
|
`, tid, key, value).Exec()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLegalInfos GET /backend/legalInfos
|
||||||
|
func (c *BackendSiteSettingsController) GetLegalInfos() {
|
||||||
|
claims, err := c.claimsByPath()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tid := c.resolveBackendTenantID(claims, nil)
|
||||||
|
if tid == 0 {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": []map[string]string{
|
||||||
|
{"label": "legalNotice", "value": ""},
|
||||||
|
{"label": "privacyTerms", "value": ""},
|
||||||
|
}}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
values, err := c.getBackendSettingItems(tid, []string{"legalNotice", "privacyTerms"})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": []map[string]string{
|
||||||
|
{"label": "legalNotice", "value": values["legalNotice"]},
|
||||||
|
{"label": "privacyTerms", "value": values["privacyTerms"]},
|
||||||
|
}}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
type backendLegalInfosPayload struct {
|
||||||
|
Tid interface{} `json:"tid"`
|
||||||
|
LegalNotice string `json:"legalNotice"`
|
||||||
|
PrivacyTerms string `json:"privacyTerms"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveLegalInfos POST /backend/saveLegalInfos
|
||||||
|
func (c *BackendSiteSettingsController) SaveLegalInfos() {
|
||||||
|
claims, err := c.claimsByPath()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var p backendLegalInfosPayload
|
||||||
|
if err := json.Unmarshal(raw, &p); err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tid := c.resolveBackendTenantID(claims, p.Tid)
|
||||||
|
if tid == 0 {
|
||||||
|
c.jsonErr(400, 400, "tid不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.saveBackendSettingItems(tid, map[string]string{
|
||||||
|
"legalNotice": strings.TrimSpace(p.LegalNotice),
|
||||||
|
"privacyTerms": strings.TrimSpace(p.PrivacyTerms),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "保存失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanyInfos GET /backend/companyInfos
|
||||||
|
func (c *BackendSiteSettingsController) GetCompanyInfos() {
|
||||||
|
claims, err := c.claimsByPath()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tid := c.resolveBackendTenantID(claims, nil)
|
||||||
|
out := map[string]interface{}{
|
||||||
|
"contact_phone": "",
|
||||||
|
"contact_email": "",
|
||||||
|
"address": "",
|
||||||
|
"worktime": "",
|
||||||
|
}
|
||||||
|
if tid == 0 {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var row models.SystemTenant
|
||||||
|
err = models.Orm.QueryTable(new(models.SystemTenant)).
|
||||||
|
Filter("id", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
One(&row)
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if row.ContactPhone != nil {
|
||||||
|
out["contact_phone"] = *row.ContactPhone
|
||||||
|
}
|
||||||
|
if row.ContactEmail != nil {
|
||||||
|
out["contact_email"] = *row.ContactEmail
|
||||||
|
}
|
||||||
|
if row.Address != nil {
|
||||||
|
out["address"] = *row.Address
|
||||||
|
}
|
||||||
|
if row.Worktime != nil {
|
||||||
|
out["worktime"] = *row.Worktime
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
type backendCompanyInfosPayload struct {
|
||||||
|
Tid interface{} `json:"tid"`
|
||||||
|
ContactPhone string `json:"contact_phone"`
|
||||||
|
ContactEmail string `json:"contact_email"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Worktime string `json:"worktime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveCompanyInfos POST /backend/saveCompanyInfos
|
||||||
|
func (c *BackendSiteSettingsController) SaveCompanyInfos() {
|
||||||
|
claims, err := c.claimsByPath()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var p backendCompanyInfosPayload
|
||||||
|
if err := json.Unmarshal(raw, &p); err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tid := c.resolveBackendTenantID(claims, p.Tid)
|
||||||
|
if tid == 0 {
|
||||||
|
c.jsonErr(400, 400, "tid不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = models.Orm.QueryTable(new(models.SystemTenant)).
|
||||||
|
Filter("id", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Update(map[string]interface{}{
|
||||||
|
"contact_phone": strings.TrimSpace(p.ContactPhone),
|
||||||
|
"contact_email": strings.TrimSpace(p.ContactEmail),
|
||||||
|
"address": strings.TrimSpace(p.Address),
|
||||||
|
"worktime": strings.TrimSpace(p.Worktime),
|
||||||
|
"update_time": time.Now(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "保存失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanySeo GET /backend/companySeo
|
||||||
|
func (c *BackendSiteSettingsController) GetCompanySeo() {
|
||||||
|
claims, err := c.claimsByPath()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tid := c.resolveBackendTenantID(claims, nil)
|
||||||
|
out := map[string]string{
|
||||||
|
"seoTitle": "",
|
||||||
|
"seoKeywords": "",
|
||||||
|
"seoDescription": "",
|
||||||
|
}
|
||||||
|
if tid == 0 {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
values, err := c.getBackendSettingItems(tid, []string{"seoTitle", "seoKeywords", "seoDescription"})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "获取失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out["seoTitle"] = values["seoTitle"]
|
||||||
|
out["seoKeywords"] = values["seoKeywords"]
|
||||||
|
out["seoDescription"] = values["seoDescription"]
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
type backendCompanySeoPayload struct {
|
||||||
|
Tid interface{} `json:"tid"`
|
||||||
|
SeoTitle string `json:"seoTitle"`
|
||||||
|
SeoKeywords string `json:"seoKeywords"`
|
||||||
|
SeoDescription string `json:"seoDescription"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveCompanySeo POST /backend/saveCompanySeo
|
||||||
|
func (c *BackendSiteSettingsController) SaveCompanySeo() {
|
||||||
|
claims, err := c.claimsByPath()
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(401, 401, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var p backendCompanySeoPayload
|
||||||
|
if err := json.Unmarshal(raw, &p); err != nil {
|
||||||
|
c.jsonErr(400, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tid := c.resolveBackendTenantID(claims, p.Tid)
|
||||||
|
if tid == 0 {
|
||||||
|
c.jsonErr(400, 400, "tid不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.saveBackendSettingItems(tid, map[string]string{
|
||||||
|
"seoTitle": strings.TrimSpace(p.SeoTitle),
|
||||||
|
"seoKeywords": strings.TrimSpace(p.SeoKeywords),
|
||||||
|
"seoDescription": strings.TrimSpace(p.SeoDescription),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.jsonErr(500, 500, "保存失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
@ -23,10 +23,10 @@ type PlatformFileController struct {
|
|||||||
beego.Controller
|
beego.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileUploadMaxMB = 2048 // 2GB,适用于大型软件安装包
|
const platformFileUploadMaxMB = 2048 // 2GB,适用于大型软件安装包
|
||||||
const fileUploadMaxBytes = fileUploadMaxMB * 1024 * 1024
|
const platformFileUploadMaxBytes = platformFileUploadMaxMB * 1024 * 1024
|
||||||
|
|
||||||
var fileTypeByCategory = map[string]uint8{
|
var platformFileTypeByCategory = map[string]uint8{
|
||||||
"image": 1,
|
"image": 1,
|
||||||
"document": 2,
|
"document": 2,
|
||||||
"video": 3,
|
"video": 3,
|
||||||
@ -34,7 +34,7 @@ var fileTypeByCategory = map[string]uint8{
|
|||||||
"appsupgrade": 2,
|
"appsupgrade": 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
var allowedExtByCategory = map[string][]string{
|
var platformAllowedExtByCategory = map[string][]string{
|
||||||
"image": {"jpg", "jpeg", "png", "gif", "bmp", "webp"},
|
"image": {"jpg", "jpeg", "png", "gif", "bmp", "webp"},
|
||||||
"document": {"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt"},
|
"document": {"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt"},
|
||||||
"video": {"mp4", "webm", "mov"},
|
"video": {"mp4", "webm", "mov"},
|
||||||
@ -89,12 +89,12 @@ func (c *PlatformFileController) jsonOK(data interface{}) {
|
|||||||
_ = c.ServeJSON()
|
_ = c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
func detectFileType(ext string) uint8 {
|
func platformDetectFileType(ext string) uint8 {
|
||||||
ext = strings.ToLower(strings.TrimPrefix(ext, "."))
|
ext = strings.ToLower(strings.TrimPrefix(ext, "."))
|
||||||
for cat, exts := range allowedExtByCategory {
|
for cat, exts := range platformAllowedExtByCategory {
|
||||||
for _, e := range exts {
|
for _, e := range exts {
|
||||||
if e == ext {
|
if e == ext {
|
||||||
if t, ok := fileTypeByCategory[cat]; ok {
|
if t, ok := platformFileTypeByCategory[cat]; ok {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
return 2
|
return 2
|
||||||
@ -104,7 +104,7 @@ func detectFileType(ext string) uint8 {
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileExt(name string) string {
|
func platformFileExt(name string) string {
|
||||||
name = strings.TrimSpace(name)
|
name = strings.TrimSpace(name)
|
||||||
if i := strings.LastIndex(name, "."); i >= 0 && i < len(name)-1 {
|
if i := strings.LastIndex(name, "."); i >= 0 && i < len(name)-1 {
|
||||||
return strings.ToLower(name[i+1:])
|
return strings.ToLower(name[i+1:])
|
||||||
@ -112,7 +112,7 @@ func fileExt(name string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileToMap(f *models.SystemFile) map[string]interface{} {
|
func platformFileToMap(f *models.SystemFile) map[string]interface{} {
|
||||||
ct := f.CreateTime.Format("2006-01-02 15:04:05")
|
ct := f.CreateTime.Format("2006-01-02 15:04:05")
|
||||||
m := map[string]interface{}{
|
m := map[string]interface{}{
|
||||||
"id": f.ID,
|
"id": f.ID,
|
||||||
@ -138,7 +138,7 @@ func fileToMap(f *models.SystemFile) map[string]interface{} {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func removePhysicalBySrc(webSrc string) {
|
func platformRemovePhysicalBySrc(webSrc string) {
|
||||||
webSrc = strings.TrimSpace(webSrc)
|
webSrc = strings.TrimSpace(webSrc)
|
||||||
if webSrc == "" {
|
if webSrc == "" {
|
||||||
return
|
return
|
||||||
@ -188,7 +188,7 @@ func (c *PlatformFileController) GetAllFiles() {
|
|||||||
}
|
}
|
||||||
list := make([]map[string]interface{}, 0, len(rows))
|
list := make([]map[string]interface{}, 0, len(rows))
|
||||||
for i := range rows {
|
for i := range rows {
|
||||||
list = append(list, fileToMap(&rows[i]))
|
list = append(list, platformFileToMap(&rows[i]))
|
||||||
}
|
}
|
||||||
c.jsonOK(map[string]interface{}{
|
c.jsonOK(map[string]interface{}{
|
||||||
"list": list,
|
"list": list,
|
||||||
@ -234,7 +234,7 @@ func (c *PlatformFileController) GetUserCate() {
|
|||||||
_ = c.ServeJSON()
|
_ = c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
type createCateBody struct {
|
type platformCreateCateBody struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Tuid *uint64 `json:"tuid"`
|
Tuid *uint64 `json:"tuid"`
|
||||||
}
|
}
|
||||||
@ -252,7 +252,7 @@ func (c *PlatformFileController) CreateFileCate() {
|
|||||||
c.jsonErr(400, 400, "参数错误")
|
c.jsonErr(400, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var body createCateBody
|
var body platformCreateCateBody
|
||||||
if err := json.Unmarshal(raw, &body); err != nil {
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
c.jsonErr(400, 400, "参数错误")
|
c.jsonErr(400, 400, "参数错误")
|
||||||
return
|
return
|
||||||
@ -282,7 +282,7 @@ func (c *PlatformFileController) CreateFileCate() {
|
|||||||
_ = c.ServeJSON()
|
_ = c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
type renameCateBody struct {
|
type platformRenameCateBody struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +305,7 @@ func (c *PlatformFileController) RenameFileCate() {
|
|||||||
c.jsonErr(400, 400, "参数错误")
|
c.jsonErr(400, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var body renameCateBody
|
var body platformRenameCateBody
|
||||||
if err := json.Unmarshal(raw, &body); err != nil {
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
c.jsonErr(400, 400, "参数错误")
|
c.jsonErr(400, 400, "参数错误")
|
||||||
return
|
return
|
||||||
@ -421,7 +421,7 @@ func (c *PlatformFileController) GetCateFiles() {
|
|||||||
}
|
}
|
||||||
list := make([]map[string]interface{}, 0, len(rows))
|
list := make([]map[string]interface{}, 0, len(rows))
|
||||||
for i := range rows {
|
for i := range rows {
|
||||||
list = append(list, fileToMap(&rows[i]))
|
list = append(list, platformFileToMap(&rows[i]))
|
||||||
}
|
}
|
||||||
c.jsonOK(map[string]interface{}{
|
c.jsonOK(map[string]interface{}{
|
||||||
"list": list,
|
"list": list,
|
||||||
@ -456,7 +456,7 @@ func (c *PlatformFileController) GetFileByID() {
|
|||||||
c.jsonErr(404, 404, "文件不存在")
|
c.jsonErr(404, 404, "文件不存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.jsonOK(fileToMap(&f))
|
c.jsonOK(platformFileToMap(&f))
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadFile POST /platform/uploadfile
|
// UploadFile POST /platform/uploadfile
|
||||||
@ -467,7 +467,7 @@ func (c *PlatformFileController) UploadFile() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
tid := c.effectiveTid(claims)
|
tid := c.effectiveTid(claims)
|
||||||
if err := c.Ctx.Request.ParseMultipartForm(fileUploadMaxBytes); err != nil {
|
if err := c.Ctx.Request.ParseMultipartForm(platformFileUploadMaxBytes); err != nil {
|
||||||
c.jsonErr(400, 400, "解析上传失败: "+err.Error())
|
c.jsonErr(400, 400, "解析上传失败: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -478,12 +478,12 @@ func (c *PlatformFileController) UploadFile() {
|
|||||||
}
|
}
|
||||||
defer fh.Close()
|
defer fh.Close()
|
||||||
|
|
||||||
if header != nil && header.Size > fileUploadMaxBytes {
|
if header != nil && header.Size > platformFileUploadMaxBytes {
|
||||||
c.jsonErr(400, 400, fmt.Sprintf("文件大小不能超过%dMB", fileUploadMaxMB))
|
c.jsonErr(400, 400, fmt.Sprintf("文件大小不能超过%dMB", platformFileUploadMaxMB))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ext := fileExt(header.Filename)
|
ext := platformFileExt(header.Filename)
|
||||||
if ext == "" {
|
if ext == "" {
|
||||||
c.jsonErr(400, 400, "无法识别文件扩展名")
|
c.jsonErr(400, 400, "无法识别文件扩展名")
|
||||||
return
|
return
|
||||||
@ -546,7 +546,7 @@ func (c *PlatformFileController) UploadFile() {
|
|||||||
Uid: &adminID,
|
Uid: &adminID,
|
||||||
Tuid: tuidPtr,
|
Tuid: tuidPtr,
|
||||||
Name: header.Filename,
|
Name: header.Filename,
|
||||||
Type: detectFileType(ext),
|
Type: platformDetectFileType(ext),
|
||||||
Cate: cate,
|
Cate: cate,
|
||||||
Size: uint64(result.Size),
|
Size: uint64(result.Size),
|
||||||
Src: result.URL,
|
Src: result.URL,
|
||||||
@ -573,7 +573,7 @@ func (c *PlatformFileController) UploadFile() {
|
|||||||
_ = c.ServeJSON()
|
_ = c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
func md5HashFile(path string) (string, error) {
|
func platformMd5HashFile(path string) (string, error) {
|
||||||
f, err := os.Open(path)
|
f, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -586,7 +586,7 @@ func md5HashFile(path string) (string, error) {
|
|||||||
return hex.EncodeToString(h.Sum(nil)), nil
|
return hex.EncodeToString(h.Sum(nil)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type updateFileBody struct {
|
type platformUpdateFileBody struct {
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
Cate *uint64 `json:"cate"`
|
Cate *uint64 `json:"cate"`
|
||||||
}
|
}
|
||||||
@ -610,7 +610,7 @@ func (c *PlatformFileController) UpdateFile() {
|
|||||||
c.jsonErr(400, 400, "参数错误")
|
c.jsonErr(400, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var body updateFileBody
|
var body platformUpdateFileBody
|
||||||
if err := json.Unmarshal(raw, &body); err != nil {
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
c.jsonErr(400, 400, "参数错误")
|
c.jsonErr(400, 400, "参数错误")
|
||||||
return
|
return
|
||||||
@ -700,7 +700,7 @@ func (c *PlatformFileController) DeleteFilePermanently() {
|
|||||||
c.jsonErr(404, 404, "文件不存在")
|
c.jsonErr(404, 404, "文件不存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
removePhysicalBySrc(f.Src)
|
platformRemovePhysicalBySrc(f.Src)
|
||||||
_, err = models.Orm.QueryTable(new(models.SystemFile)).
|
_, err = models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
Filter("id", id).
|
Filter("id", id).
|
||||||
Filter("tid", tid).
|
Filter("tid", tid).
|
||||||
@ -746,7 +746,7 @@ func (c *PlatformFileController) MoveFile() {
|
|||||||
_ = c.ServeJSON()
|
_ = c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
type idsBody struct {
|
type platformIdsBody struct {
|
||||||
IDs []uint64 `json:"ids"`
|
IDs []uint64 `json:"ids"`
|
||||||
Cate *uint64 `json:"cate"`
|
Cate *uint64 `json:"cate"`
|
||||||
}
|
}
|
||||||
@ -764,7 +764,7 @@ func (c *PlatformFileController) BatchDeleteFiles() {
|
|||||||
c.jsonErr(400, 400, "参数错误")
|
c.jsonErr(400, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var body idsBody
|
var body platformIdsBody
|
||||||
if err := json.Unmarshal(raw, &body); err != nil {
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
c.jsonErr(400, 400, "参数错误")
|
c.jsonErr(400, 400, "参数错误")
|
||||||
return
|
return
|
||||||
@ -781,7 +781,7 @@ func (c *PlatformFileController) BatchDeleteFiles() {
|
|||||||
Filter("tid", tid).
|
Filter("tid", tid).
|
||||||
One(&f)
|
One(&f)
|
||||||
if e == nil && f.Src != "" {
|
if e == nil && f.Src != "" {
|
||||||
removePhysicalBySrc(f.Src)
|
platformRemovePhysicalBySrc(f.Src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n, err := models.Orm.QueryTable(new(models.SystemFile)).
|
n, err := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
@ -813,7 +813,7 @@ func (c *PlatformFileController) BatchDeleteFilesPermanently() {
|
|||||||
c.jsonErr(400, 400, "参数错误")
|
c.jsonErr(400, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var body idsBody
|
var body platformIdsBody
|
||||||
if err := json.Unmarshal(raw, &body); err != nil {
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
c.jsonErr(400, 400, "参数错误")
|
c.jsonErr(400, 400, "参数错误")
|
||||||
return
|
return
|
||||||
@ -832,7 +832,7 @@ func (c *PlatformFileController) BatchDeleteFilesPermanently() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
for i := range rows {
|
for i := range rows {
|
||||||
removePhysicalBySrc(rows[i].Src)
|
platformRemovePhysicalBySrc(rows[i].Src)
|
||||||
}
|
}
|
||||||
n, err := models.Orm.QueryTable(new(models.SystemFile)).
|
n, err := models.Orm.QueryTable(new(models.SystemFile)).
|
||||||
Filter("id__in", body.IDs).
|
Filter("id__in", body.IDs).
|
||||||
@ -875,7 +875,7 @@ func (c *PlatformFileController) BatchMoveFiles() {
|
|||||||
c.jsonErr(400, 400, "参数错误")
|
c.jsonErr(400, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var body idsBody
|
var body platformIdsBody
|
||||||
if err := json.Unmarshal(raw, &body); err != nil {
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
c.jsonErr(400, 400, "参数错误")
|
c.jsonErr(400, 400, "参数错误")
|
||||||
return
|
return
|
||||||
|
|||||||
@ -14,23 +14,23 @@ import (
|
|||||||
beego "github.com/beego/beego/v2/server/web"
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SiteSettingsController 租户站点设置(站点基本信息)
|
// PlatformSiteSettingsController 租户站点设置(站点基本信息)
|
||||||
// 对应前端 normalSettings.vue 的:
|
// 对应前端 normalSettings.vue 的:
|
||||||
// - GET /backend/normalInfos
|
// - GET /backend/normalInfos
|
||||||
// - POST /backend/saveNormalInfos
|
// - POST /backend/saveNormalInfos
|
||||||
// - GET /platform/normalInfos
|
// - GET /platform/normalInfos
|
||||||
// - POST /platform/saveNormalInfos
|
// - POST /platform/saveNormalInfos
|
||||||
type SiteSettingsController struct {
|
type PlatformSiteSettingsController struct {
|
||||||
beego.Controller
|
beego.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SiteSettingsController) jsonErr(httpStatus, bizCode int, msg string) {
|
func (c *PlatformSiteSettingsController) jsonErr(httpStatus, bizCode int, msg string) {
|
||||||
c.Ctx.Output.SetStatus(httpStatus)
|
c.Ctx.Output.SetStatus(httpStatus)
|
||||||
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||||
_ = c.ServeJSON()
|
_ = c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SiteSettingsController) claimsByPath() (*jwtutil.Claims, error) {
|
func (c *PlatformSiteSettingsController) claimsByPath() (*jwtutil.Claims, error) {
|
||||||
auth := c.Ctx.Request.Header.Get("Authorization")
|
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||||
if auth == "" {
|
if auth == "" {
|
||||||
return nil, fmt.Errorf("未登录")
|
return nil, fmt.Errorf("未登录")
|
||||||
@ -84,19 +84,19 @@ func parseUint64Flexible(v interface{}) uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type normalInfosOutput struct {
|
type normalInfosOutput struct {
|
||||||
Sitename string `json:"sitename"`
|
Sitename string `json:"sitename"`
|
||||||
Companyintroduction string `json:"companyintroduction"`
|
Companyintroduction string `json:"companyintroduction"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Copyright string `json:"copyright"`
|
Copyright string `json:"copyright"`
|
||||||
Companyname string `json:"companyname"`
|
Companyname string `json:"companyname"`
|
||||||
Icp string `json:"icp"`
|
Icp string `json:"icp"`
|
||||||
Logo string `json:"logo"`
|
Logo string `json:"logo"`
|
||||||
Logow string `json:"logow"`
|
Logow string `json:"logow"`
|
||||||
Ico string `json:"ico"`
|
Ico string `json:"ico"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNormalInfos GET /backend/normalInfos 或 /platform/normalInfos
|
// GetNormalInfos GET /backend/normalInfos 或 /platform/normalInfos
|
||||||
func (c *SiteSettingsController) GetNormalInfos() {
|
func (c *PlatformSiteSettingsController) GetNormalInfos() {
|
||||||
claims, err := c.claimsByPath()
|
claims, err := c.claimsByPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.jsonErr(401, 401, err.Error())
|
c.jsonErr(401, 401, err.Error())
|
||||||
@ -115,15 +115,15 @@ func (c *SiteSettingsController) GetNormalInfos() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
out := normalInfosOutput{
|
out := normalInfosOutput{
|
||||||
Sitename: "",
|
Sitename: "",
|
||||||
Companyintroduction: "",
|
Companyintroduction: "",
|
||||||
Description: "",
|
Description: "",
|
||||||
Copyright: "",
|
Copyright: "",
|
||||||
Companyname: "",
|
Companyname: "",
|
||||||
Icp: "",
|
Icp: "",
|
||||||
Logo: "",
|
Logo: "",
|
||||||
Logow: "",
|
Logow: "",
|
||||||
Ico: "",
|
Ico: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
// tid 缺失时不报错,直接返回空对象给前端渲染(避免 UI 直接崩)。
|
// tid 缺失时不报错,直接返回空对象给前端渲染(避免 UI 直接崩)。
|
||||||
@ -164,19 +164,19 @@ type normalInfosPayload struct {
|
|||||||
// 前端会传 tid(但我们仍优先使用 token 的 tenant_id)
|
// 前端会传 tid(但我们仍优先使用 token 的 tenant_id)
|
||||||
Tid interface{} `json:"tid"`
|
Tid interface{} `json:"tid"`
|
||||||
|
|
||||||
Sitename string `json:"sitename"`
|
Sitename string `json:"sitename"`
|
||||||
Companyintroduction string `json:"companyintroduction"`
|
Companyintroduction string `json:"companyintroduction"`
|
||||||
Logo string `json:"logo"`
|
Logo string `json:"logo"`
|
||||||
Logow string `json:"logow"`
|
Logow string `json:"logow"`
|
||||||
Ico string `json:"ico"`
|
Ico string `json:"ico"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Copyright string `json:"copyright"`
|
Copyright string `json:"copyright"`
|
||||||
Companyname string `json:"companyname"`
|
Companyname string `json:"companyname"`
|
||||||
Icp string `json:"icp"`
|
Icp string `json:"icp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveNormalInfos POST /backend/saveNormalInfos 或 /platform/saveNormalInfos
|
// SaveNormalInfos POST /backend/saveNormalInfos 或 /platform/saveNormalInfos
|
||||||
func (c *SiteSettingsController) SaveNormalInfos() {
|
func (c *PlatformSiteSettingsController) SaveNormalInfos() {
|
||||||
claims, err := c.claimsByPath()
|
claims, err := c.claimsByPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.jsonErr(401, 401, err.Error())
|
c.jsonErr(401, 401, err.Error())
|
||||||
@ -213,17 +213,17 @@ func (c *SiteSettingsController) SaveNormalInfos() {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
up := map[string]interface{}{
|
up := map[string]interface{}{
|
||||||
"tid": tid,
|
"tid": tid,
|
||||||
"sitename": sitename,
|
"sitename": sitename,
|
||||||
"companyintroduction": strings.TrimSpace(p.Companyintroduction),
|
"companyintroduction": strings.TrimSpace(p.Companyintroduction),
|
||||||
"logo": strings.TrimSpace(p.Logo),
|
"logo": strings.TrimSpace(p.Logo),
|
||||||
"logow": strings.TrimSpace(p.Logow),
|
"logow": strings.TrimSpace(p.Logow),
|
||||||
"ico": strings.TrimSpace(p.Ico),
|
"ico": strings.TrimSpace(p.Ico),
|
||||||
"description": strings.TrimSpace(p.Description),
|
"description": strings.TrimSpace(p.Description),
|
||||||
"copyright": strings.TrimSpace(p.Copyright),
|
"copyright": strings.TrimSpace(p.Copyright),
|
||||||
"companyname": strings.TrimSpace(p.Companyname),
|
"companyname": strings.TrimSpace(p.Companyname),
|
||||||
"icp": strings.TrimSpace(p.Icp),
|
"icp": strings.TrimSpace(p.Icp),
|
||||||
"update_time": now,
|
"update_time": now,
|
||||||
}
|
}
|
||||||
|
|
||||||
cnt, err := models.Orm.QueryTable(new(models.TenantSiteSetting)).
|
cnt, err := models.Orm.QueryTable(new(models.TenantSiteSetting)).
|
||||||
@ -237,18 +237,18 @@ func (c *SiteSettingsController) SaveNormalInfos() {
|
|||||||
|
|
||||||
if cnt == 0 {
|
if cnt == 0 {
|
||||||
row := &models.TenantSiteSetting{
|
row := &models.TenantSiteSetting{
|
||||||
Tid: tid,
|
Tid: tid,
|
||||||
Sitename: sitename,
|
Sitename: sitename,
|
||||||
Companyintroduction: strings.TrimSpace(p.Companyintroduction),
|
Companyintroduction: strings.TrimSpace(p.Companyintroduction),
|
||||||
Logo: strings.TrimSpace(p.Logo),
|
Logo: strings.TrimSpace(p.Logo),
|
||||||
Logow: strings.TrimSpace(p.Logow),
|
Logow: strings.TrimSpace(p.Logow),
|
||||||
Ico: strings.TrimSpace(p.Ico),
|
Ico: strings.TrimSpace(p.Ico),
|
||||||
Description: strings.TrimSpace(p.Description),
|
Description: strings.TrimSpace(p.Description),
|
||||||
Copyright: strings.TrimSpace(p.Copyright),
|
Copyright: strings.TrimSpace(p.Copyright),
|
||||||
Companyname: strings.TrimSpace(p.Companyname),
|
Companyname: strings.TrimSpace(p.Companyname),
|
||||||
Icp: strings.TrimSpace(p.Icp),
|
Icp: strings.TrimSpace(p.Icp),
|
||||||
CreateTime: now,
|
CreateTime: now,
|
||||||
UpdateTime: &now,
|
UpdateTime: &now,
|
||||||
}
|
}
|
||||||
_, err = models.Orm.Insert(row)
|
_, err = models.Orm.Insert(row)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -269,4 +269,3 @@ func (c *SiteSettingsController) SaveNormalInfos() {
|
|||||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
|
||||||
_ = c.ServeJSON()
|
_ = c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ type TenantSiteSetting struct {
|
|||||||
Description string `orm:"column(description);size(255);null" json:"description"`
|
Description string `orm:"column(description);size(255);null" json:"description"`
|
||||||
Copyright string `orm:"column(copyright);size(255);null" json:"copyright"`
|
Copyright string `orm:"column(copyright);size(255);null" json:"copyright"`
|
||||||
Companyname string `orm:"column(companyname);size(255);null" json:"companyname"`
|
Companyname string `orm:"column(companyname);size(255);null" json:"companyname"`
|
||||||
Icp string `orm:"column(icp);size(255);null" json:"icp"`
|
Icp string `orm:"column(icp);size(255);null" json:"icp"`
|
||||||
|
|
||||||
CreateTime time.Time `orm:"column(create_time);type(datetime);auto_now_add;null" json:"create_time"`
|
CreateTime time.Time `orm:"column(create_time);type(datetime);auto_now_add;null" json:"create_time"`
|
||||||
UpdateTime *time.Time `orm:"column(update_time);type(datetime);auto_now;null" json:"update_time"`
|
UpdateTime *time.Time `orm:"column(update_time);type(datetime);auto_now;null" json:"update_time"`
|
||||||
@ -26,6 +26,5 @@ type TenantSiteSetting struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *TenantSiteSetting) TableName() string {
|
func (m *TenantSiteSetting) TableName() string {
|
||||||
return "yz_tenant_site_setting"
|
return "yz_system_tenant_site_setting"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,37 +15,63 @@ func Register() {
|
|||||||
// RegisterAuthRoutes 注册 backend 认证相关路由。
|
// RegisterAuthRoutes 注册 backend 认证相关路由。
|
||||||
func RegisterAuthRoutes() {
|
func RegisterAuthRoutes() {
|
||||||
// backend 登录相关(统一走 /backend/*)
|
// backend 登录相关(统一走 /backend/*)
|
||||||
beego.Router("/backend/login", &controllers.PlatformAuthController{}, "post:LoginBackend")
|
beego.Router("/backend/login", &controllers.BackendAuthController{}, "post:LoginBackend")
|
||||||
beego.Router("/backend/sendLoginCode", &controllers.PlatformAuthController{}, "post:SendLoginCode")
|
beego.Router("/backend/sendLoginCode", &controllers.BackendAuthController{}, "post:SendLoginCode")
|
||||||
beego.Router("/backend/loginBySms", &controllers.PlatformAuthController{}, "post:LoginBySms")
|
beego.Router("/backend/loginBySms", &controllers.BackendAuthController{}, "post:LoginBySms")
|
||||||
beego.Router("/backend/logout", &controllers.PlatformAuthController{}, "post:Logout")
|
beego.Router("/backend/logout", &controllers.BackendAuthController{}, "post:Logout")
|
||||||
|
|
||||||
// 极验与登录验证配置
|
// 极验与登录验证配置
|
||||||
beego.Router("/backend/login/getGeetest3Infos", &controllers.PlatformAuthController{}, "get:GetGeetest3Infos")
|
beego.Router("/backend/login/getGeetest3Infos", &controllers.BackendAuthController{}, "get:GetGeetest3Infos")
|
||||||
beego.Router("/backend/login/getGeetest4Infos", &controllers.PlatformAuthController{}, "get:GetGeetest4Infos")
|
beego.Router("/backend/login/getGeetest4Infos", &controllers.BackendAuthController{}, "get:GetGeetest4Infos")
|
||||||
beego.Router("/backend/login/getOpenVerify", &controllers.PlatformAuthController{}, "get:GetOpenVerify")
|
beego.Router("/backend/login/getOpenVerify", &controllers.BackendAuthController{}, "get:GetOpenVerify")
|
||||||
|
|
||||||
// 登录相关接口
|
// 登录相关接口
|
||||||
beego.Router("/backend/login/getGeetest3Infos", &controllers.PlatformAuthController{}, "get:GetGeetest3Infos")
|
beego.Router("/backend/login/getGeetest3Infos", &controllers.BackendAuthController{}, "get:GetGeetest3Infos")
|
||||||
beego.Router("/backend/login/getGeetest4Infos", &controllers.PlatformAuthController{}, "get:GetGeetest4Infos")
|
beego.Router("/backend/login/getGeetest4Infos", &controllers.BackendAuthController{}, "get:GetGeetest4Infos")
|
||||||
beego.Router("/backend/login/getOpenVerify", &controllers.PlatformAuthController{}, "get:GetOpenVerify")
|
beego.Router("/backend/login/getOpenVerify", &controllers.BackendAuthController{}, "get:GetOpenVerify")
|
||||||
|
|
||||||
// 注册与找回密码
|
// 注册与找回密码
|
||||||
beego.Router("/backend/register", &controllers.PlatformAuthController{}, "post:Register")
|
beego.Router("/backend/register", &controllers.BackendAuthController{}, "post:Register")
|
||||||
beego.Router("/backend/sendRegisterCode", &controllers.PlatformAuthController{}, "post:SendRegisterCode")
|
beego.Router("/backend/sendRegisterCode", &controllers.BackendAuthController{}, "post:SendRegisterCode")
|
||||||
beego.Router("/backend/resetPassword", &controllers.PlatformAuthController{}, "post:ResetPassword")
|
beego.Router("/backend/resetPassword", &controllers.BackendAuthController{}, "post:ResetPassword")
|
||||||
beego.Router("/backend/sendResetCode", &controllers.PlatformAuthController{}, "post:SendResetCode")
|
beego.Router("/backend/sendResetCode", &controllers.BackendAuthController{}, "post:SendResetCode")
|
||||||
|
|
||||||
// 租户站点设置
|
// 租户站点设置
|
||||||
beego.Router("/backend/normalInfos", &controllers.SiteSettingsController{}, "get:GetNormalInfos")
|
beego.Router("/backend/normalInfos", &controllers.BackendSiteSettingsController{}, "get:GetNormalInfos")
|
||||||
beego.Router("/backend/saveNormalInfos", &controllers.SiteSettingsController{}, "post:SaveNormalInfos")
|
beego.Router("/backend/saveNormalInfos", &controllers.BackendSiteSettingsController{}, "post:SaveNormalInfos")
|
||||||
|
|
||||||
// 菜单接口
|
// 菜单接口
|
||||||
beego.Router("/backend/menu/:id", &controllers.BackendMenuController{}, "get:GetBackendMenu")
|
beego.Router("/backend/menu/:id", &controllers.BackendMenuController{}, "get:GetBackendMenu")
|
||||||
beego.Router("/backend/allmenu", &controllers.BackendMenuController{}, "get:GetAllBackendMenus")
|
beego.Router("/backend/allmenu", &controllers.BackendMenuController{}, "get:GetAllBackendMenus")
|
||||||
|
|
||||||
|
// 操作日志(yz_system_operation_log)
|
||||||
|
beego.Router("/backend/operationLogs", &controllers.BackendOperationLogController{}, "get:List")
|
||||||
|
beego.Router("/backend/operationLogs/statistics", &controllers.BackendOperationLogController{}, "get:Statistics")
|
||||||
|
beego.Router("/backend/operationLogs/:id", &controllers.BackendOperationLogController{}, "get:Detail;delete:Delete")
|
||||||
|
beego.Router("/backend/operationLogs/batchDelete", &controllers.BackendOperationLogController{}, "post:BatchDelete")
|
||||||
|
|
||||||
|
// 文件管理(yz_system_files / yz_system_files_category)
|
||||||
|
beego.Router("/backend/usercate", &controllers.BackendFileController{}, "get:GetUserCate")
|
||||||
|
beego.Router("/backend/allfiles", &controllers.BackendFileController{}, "get:GetAllFiles")
|
||||||
|
beego.Router("/backend/catefiles/:id", &controllers.BackendFileController{}, "get:GetCateFiles")
|
||||||
|
beego.Router("/backend/file/:id", &controllers.BackendFileController{}, "get:GetFileByID")
|
||||||
|
beego.Router("/backend/deletefilepermanently/:id", &controllers.BackendFileController{}, "delete:DeleteFilePermanently")
|
||||||
|
beego.Router("/backend/uploadfile", &controllers.BackendFileController{}, "post:UploadFile")
|
||||||
|
beego.Router("/backend/uploadfiles", &controllers.BackendFileController{}, "post:UploadFile")
|
||||||
|
beego.Router("/backend/updatefile/:id", &controllers.BackendFileController{}, "post:UpdateFile")
|
||||||
|
beego.Router("/backend/deletefile/:id", &controllers.BackendFileController{}, "delete:DeleteFile")
|
||||||
|
beego.Router("/backend/movefile/:id", &controllers.BackendFileController{}, "get:MoveFile")
|
||||||
|
beego.Router("/backend/createfilecate", &controllers.BackendFileController{}, "post:CreateFileCate")
|
||||||
|
beego.Router("/backend/renamefilecate/:id", &controllers.BackendFileController{}, "post:RenameFileCate")
|
||||||
|
beego.Router("/backend/deletefilecate/:id", &controllers.BackendFileController{}, "delete:DeleteFileCate")
|
||||||
|
beego.Router("/backend/uploadavatar", &controllers.BackendFileController{}, "post:UploadAvatar")
|
||||||
|
beego.Router("/backend/uploadavatar/:id", &controllers.BackendFileController{}, "post:UpdateAvatar")
|
||||||
|
beego.Router("/backend/batchdeletefiles", &controllers.BackendFileController{}, "post:BatchDeleteFiles")
|
||||||
|
beego.Router("/backend/batchDeleteFilesPermanently", &controllers.BackendFileController{}, "post:BatchDeleteFilesPermanently")
|
||||||
|
beego.Router("/backend/batchMoveFiles", &controllers.BackendFileController{}, "post:BatchMoveFiles")
|
||||||
|
|
||||||
// 模块接口
|
// 模块接口
|
||||||
beego.Router("/backend/modules/getTenantList", &controllers.PlatformModulesController{}, "get:GetTenantList")
|
beego.Router("/backend/modules/getTenantList", &controllers.BackendModulesController{}, "get:GetTenantList")
|
||||||
|
|
||||||
// 用户接口
|
// 用户接口
|
||||||
beego.Router("/backend/getTenantUsers/:tid", &controllers.BackendAdminUserController{}, "get:GetTenantUsers")
|
beego.Router("/backend/getTenantUsers/:tid", &controllers.BackendAdminUserController{}, "get:GetTenantUsers")
|
||||||
|
|||||||
@ -118,8 +118,8 @@ func Register() {
|
|||||||
beego.Router("/platform/softwareupgrade/:id", &controllers.PlatformSoftwareUpgradeController{}, "get:Detail;post:Update;delete:Delete")
|
beego.Router("/platform/softwareupgrade/:id", &controllers.PlatformSoftwareUpgradeController{}, "get:Detail;post:Update;delete:Delete")
|
||||||
|
|
||||||
// 租户站点设置(yz_tenant_site_setting)
|
// 租户站点设置(yz_tenant_site_setting)
|
||||||
beego.Router("/platform/normalInfos", &controllers.SiteSettingsController{}, "get:GetNormalInfos")
|
beego.Router("/platform/normalInfos", &controllers.PlatformSiteSettingsController{}, "get:GetNormalInfos")
|
||||||
beego.Router("/platform/saveNormalInfos", &controllers.SiteSettingsController{}, "post:SaveNormalInfos")
|
beego.Router("/platform/saveNormalInfos", &controllers.PlatformSiteSettingsController{}, "post:SaveNormalInfos")
|
||||||
|
|
||||||
// 系统邮箱配置(yz_system_email)
|
// 系统邮箱配置(yz_system_email)
|
||||||
beego.Router("/platform/email/info", &controllers.PlatformEmailController{}, "get:GetInfo")
|
beego.Router("/platform/email/info", &controllers.PlatformEmailController{}, "get:GetInfo")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user