批量修复问题

This commit is contained in:
2026-04-02 00:05:03 +08:00
parent 4d6210ccf8
commit d62872cc6c
10 changed files with 495 additions and 39 deletions
+66 -16
View File
@@ -24,23 +24,72 @@ type menuPayload struct {
Sort *int64 `json:"sort"`
Status *int8 `json:"status"`
IsVisible *int8 `json:"is_visible"`
IsPlatform *int8 `json:"is_platform"`
Views []int `json:"views"`
Type *int8 `json:"type"`
Permission *string `json:"permission"`
}
func parseViews(raw *string) []int {
if raw == nil {
return nil
}
s := strings.TrimSpace(*raw)
if s == "" {
return nil
}
var arr []int
if err := json.Unmarshal([]byte(s), &arr); err != nil {
return nil
}
return arr
}
func hasView(arr []int, v int) bool {
for _, n := range arr {
if n == v {
return true
}
}
return false
}
func viewsJSON(views []int) string {
// 默认:平台端显示
if len(views) == 0 {
views = []int{1}
}
b, _ := json.Marshal(views)
return string(b)
}
func filterMenusByView(menus []models.SystemMenu, v int) []models.SystemMenu {
out := make([]models.SystemMenu, 0, len(menus))
for _, m := range menus {
views := parseViews(m.Views)
// 兼容旧数据:views 为空时,平台端菜单默认可见(保持旧 is_platform=1 的常见默认体验)
// 租户端不做默认放行,避免把未迁移数据误暴露到租户端。
if v == 1 && len(views) == 0 {
out = append(out, m)
continue
}
if hasView(views, v) {
out = append(out, m)
}
}
return out
}
// GetMenu 获取指定用户可见的菜单列表(简化版:当前先忽略用户权限,返回全部启用且平台端菜单)
// 路由示例:GET /platform/menu/1
func (c *AdminMenuController) GetMenu() {
// 从路由参数中解析用户 ID,占位保留,方便后续按用户权限过滤
_ = c.Ctx.Input.Param(":id")
// 查询所有启用且标记为平台端的菜单
// 查询所有启用菜单,再按 views 过滤平台端可见
var menus []models.SystemMenu
qs := models.Orm.
QueryTable(new(models.SystemMenu)).
Filter("status", 1).
Filter("is_platform", 1)
Filter("status", 1)
_, err := qs.All(&menus)
if err != nil {
c.Data["json"] = map[string]interface{}{
@@ -51,6 +100,7 @@ func (c *AdminMenuController) GetMenu() {
_ = c.ServeJSON()
return
}
menus = filterMenusByView(menus, 1)
// 将平铺的菜单列表构建为树形结构
menuTree := buildMenuTree(menus, 0)
@@ -72,8 +122,7 @@ func (c *AdminMenuController) GetBackendMenu() {
var menus []models.SystemMenu
qs := models.Orm.
QueryTable(new(models.SystemMenu)).
Filter("status", 1).
Filter("is_platform", 0)
Filter("status", 1)
_, err := qs.All(&menus)
if err != nil {
c.Data["json"] = map[string]interface{}{
@@ -84,6 +133,7 @@ func (c *AdminMenuController) GetBackendMenu() {
_ = c.ServeJSON()
return
}
menus = filterMenusByView(menus, 2)
menuTree := buildMenuTree(menus, 0)
c.Data["json"] = map[string]interface{}{
@@ -103,11 +153,6 @@ func (c *AdminMenuController) GetAllMenus() {
qs := models.Orm.QueryTable(new(models.SystemMenu))
// 菜单管理默认返回全量菜单;仅在明确传 cid 时按分类筛选
// cid: 1平台角色 -> 平台菜单;2租户角色 -> 租户菜单
if cid == 1 {
qs = qs.Filter("is_platform", 1)
} else if cid == 2 {
qs = qs.Filter("is_platform", 0)
}
_, err := qs.All(&menus)
if err != nil {
c.Data["json"] = map[string]interface{}{
@@ -118,6 +163,11 @@ func (c *AdminMenuController) GetAllMenus() {
_ = c.ServeJSON()
return
}
if cid == 1 {
menus = filterMenusByView(menus, 1)
} else if cid == 2 {
menus = filterMenusByView(menus, 2)
}
tree := buildMenuTree(menus, 0)
@@ -134,7 +184,6 @@ func (c *AdminMenuController) GetAllMenus() {
func (c *AdminMenuController) GetAllBackendMenus() {
var menus []models.SystemMenu
_, err := models.Orm.QueryTable(new(models.SystemMenu)).
Filter("is_platform", 0).
All(&menus)
if err != nil {
c.Data["json"] = map[string]interface{}{
@@ -145,6 +194,7 @@ func (c *AdminMenuController) GetAllBackendMenus() {
_ = c.ServeJSON()
return
}
menus = filterMenusByView(menus, 2)
tree := buildMenuTree(menus, 0)
c.Data["json"] = map[string]interface{}{
"code": 200,
@@ -165,7 +215,7 @@ type menuNode struct {
Sort int64 `json:"sort"`
Status int8 `json:"status"`
IsVisible *int8 `json:"is_visible,omitempty"`
IsPlatform *int8 `json:"is_platform,omitempty"`
Views []int `json:"views,omitempty"`
Type int8 `json:"type"`
Permission string `json:"permission,omitempty"`
Children []*menuNode `json:"children,omitempty"`
@@ -183,7 +233,7 @@ func buildMenuTree(menus []models.SystemMenu, pid int64) []*menuNode {
Sort: m.Sort,
Status: m.Status,
IsVisible: m.IsVisible,
IsPlatform: m.IsPlatform,
Views: parseViews(m.Views),
Type: m.Type,
}
if m.Path != nil {
@@ -257,7 +307,7 @@ func (c *AdminMenuController) CreateMenu() {
Sort: valueInt64(payload.Sort, 0),
Status: valueInt8(payload.Status, 1),
IsVisible: ptrInt8(valueInt8(payload.IsVisible, 1)),
IsPlatform: ptrInt8(valueInt8(payload.IsPlatform, 1)),
Views: ptrString(viewsJSON(payload.Views)),
Type: valueInt8(payload.Type, 1),
}
@@ -305,7 +355,7 @@ func (c *AdminMenuController) UpdateMenu() {
"sort": valueInt64(payload.Sort, 0),
"status": valueInt8(payload.Status, 1),
"is_visible": valueInt8(payload.IsVisible, 1),
"is_platform": valueInt8(payload.IsPlatform, 1),
"views": viewsJSON(payload.Views),
"type": valueInt8(payload.Type, 1),
"permission": valueString(payload.Permission, ""),
}
+105 -10
View File
@@ -5,6 +5,7 @@ import (
"io"
"strings"
"server/models"
"server/pkg/jwtutil"
"server/services"
@@ -14,12 +15,14 @@ import (
type platformLoginRequest struct {
Account string `json:"account"`
Password string `json:"password"`
Code string `json:"code"`
}
type backendLoginRequest struct {
TenantName string `json:"tenant_name"`
Account string `json:"account"`
Password string `json:"password"`
Code string `json:"code"`
}
// PlatformAuthController 平台端认证控制器
@@ -59,6 +62,21 @@ func (c *PlatformAuthController) LoginPlatform() {
_ = c.ServeJSON()
return
}
cfg, _ := models.GetPlatformLoginVerify()
if cfg.OpenVerifyEnabled == 1 {
if cfg.VerifyType == "sms" || cfg.VerifyType == "email" {
if strings.TrimSpace(req.Code) == "" {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "请输入验证码"}
_ = c.ServeJSON()
return
}
if err := services.VerifyPlatformLoginCode(req.Account, cfg.VerifyType, req.Code); err != nil {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": err.Error()}
_ = c.ServeJSON()
return
}
}
}
// 控制器只做 HTTP 解析与响应编排,业务逻辑放 services 层
token, loginUser, err := services.PlatformAdminLogin(req.Account, req.Password)
@@ -80,7 +98,6 @@ func (c *PlatformAuthController) LoginPlatform() {
"id": loginUser.ID,
"account": loginUser.Account,
"name": loginUser.Name,
"tid": loginUser.Tid,
"rid": loginUser.Rid,
"avatar": loginUser.Avatar,
"role_name": loginUser.RoleName,
@@ -110,6 +127,21 @@ func (c *PlatformAuthController) LoginBackend() {
_ = c.ServeJSON()
return
}
cfg, _ := models.GetPlatformLoginVerify()
if cfg.OpenVerifyEnabled == 1 {
if cfg.VerifyType == "sms" || cfg.VerifyType == "email" {
if strings.TrimSpace(req.Code) == "" {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "请输入验证码"}
_ = c.ServeJSON()
return
}
if err := services.VerifyBackendLoginCode(req.TenantName, req.Account, cfg.VerifyType, req.Code); err != nil {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": err.Error()}
_ = c.ServeJSON()
return
}
}
}
token, loginUser, err := services.BackendLogin(req.TenantName, req.Account, req.Password)
if err != nil {
@@ -185,10 +217,45 @@ func (c *PlatformAuthController) GetCurrentUser() {
// SendLoginCode 发送登录验证码(占位实现)
func (c *PlatformAuthController) SendLoginCode() {
c.Data["json"] = map[string]interface{}{
"code": 501,
"msg": "发送登录验证码暂未实现",
var req struct {
Account string `json:"account"`
TenantName string `json:"tenant_name"`
Channel string `json:"channel"`
}
body, _ := io.ReadAll(c.Ctx.Request.Body)
if err := json.Unmarshal(body, &req); err != nil {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "参数错误"}
_ = c.ServeJSON()
return
}
cfg, _ := models.GetPlatformLoginVerify()
if cfg.OpenVerifyEnabled != 1 {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "当前未开启验证"}
_ = c.ServeJSON()
return
}
channel := strings.TrimSpace(req.Channel)
if channel == "" {
channel = cfg.VerifyType
}
if channel != "sms" && channel != "email" {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "仅支持短信/邮箱验证码"}
_ = c.ServeJSON()
return
}
path := strings.ToLower(c.Ctx.Request.URL.Path)
var sendErr error
if strings.HasPrefix(path, "/backend/") {
sendErr = services.SendBackendLoginCode(req.TenantName, req.Account, channel)
} else {
sendErr = services.SendPlatformLoginCode(req.Account, channel)
}
if sendErr != nil {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": sendErr.Error()}
_ = c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "验证码已发送"}
_ = c.ServeJSON()
}
@@ -212,32 +279,60 @@ func (c *PlatformAuthController) Logout() {
// GetGeetest3Infos 获取极验3.0配置(占位实现)
func (c *PlatformAuthController) GetGeetest3Infos() {
cfg, _ := models.GetPlatformLoginVerify()
if cfg.Geetest3ID == nil || cfg.Geetest3Key == nil {
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "未配置极验3参数"}
_ = c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{
"code": 501,
"msg": "极验3.0暂未实现",
"code": 200,
"msg": "success",
"data": map[string]interface{}{
"captcha_id": *cfg.Geetest3ID,
"captcha_key": *cfg.Geetest3Key,
},
}
_ = c.ServeJSON()
}
// GetGeetest4Infos 获取极验4.0配置(占位实现)
func (c *PlatformAuthController) GetGeetest4Infos() {
cfg, _ := models.GetPlatformLoginVerify()
if cfg.Geetest4ID == nil || cfg.Geetest4Key == nil {
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "未配置极验4参数"}
_ = c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{
"code": 501,
"msg": "极验4.0暂未实现",
"code": 200,
"msg": "success",
"data": map[string]interface{}{
"captcha_id": *cfg.Geetest4ID,
"captcha_key": *cfg.Geetest4Key,
},
}
_ = c.ServeJSON()
}
// GetOpenVerify 判断是否开启登录验证(占位实现)
func (c *PlatformAuthController) GetOpenVerify() {
cfg, _ := models.GetPlatformLoginVerify()
openVerify := "0"
if cfg.OpenVerifyEnabled == 1 {
openVerify = "1"
}
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "ok",
// data 为配置项数组,这里固定关闭验证:openVerify=0
"data": []map[string]string{
{
"label": "openVerify",
"value": "0",
"value": openVerify,
},
{
"label": "verifyType",
"value": cfg.VerifyType,
},
},
}
+124
View File
@@ -0,0 +1,124 @@
package controllers
import (
"encoding/json"
"io"
"strings"
"server/models"
beego "github.com/beego/beego/v2/server/web"
)
type PlatformLoginVerifyController struct {
beego.Controller
}
type loginVerifyPayload struct {
OpenVerifyEnabled *int8 `json:"openVerify_enabled"`
VerifyType string `json:"use_geetest"`
Geetest3ID *string `json:"geetest3_id"`
Geetest3Key *string `json:"geetest3_key"`
Geetest4ID *string `json:"geetest4_id"`
Geetest4Key *string `json:"geetest4_key"`
}
func normalizeVerifyType(v string) string {
switch strings.TrimSpace(v) {
case "sms", "geetest", "email", "captcha":
return strings.TrimSpace(v)
default:
return "captcha"
}
}
// GetLoginVerifyInfos 获取登录验证配置
// GET /platform/loginVerifyInfos
func (c *PlatformLoginVerifyController) GetLoginVerifyInfos() {
cfg, err := models.GetPlatformLoginVerify()
if err != nil {
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "获取配置失败"}
_ = c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "success",
"data": map[string]interface{}{
"openVerify_enabled": cfg.OpenVerifyEnabled,
"use_geetest": cfg.VerifyType,
"geetest3_id": cfg.Geetest3ID,
"geetest3_key": cfg.Geetest3Key,
"geetest4_id": cfg.Geetest4ID,
"geetest4_key": cfg.Geetest4Key,
},
}
_ = c.ServeJSON()
}
// SaveLoginVerifyInfos 保存登录验证配置
// POST /platform/saveloginVerifyInfos
func (c *PlatformLoginVerifyController) SaveLoginVerifyInfos() {
var p loginVerifyPayload
raw, _ := io.ReadAll(c.Ctx.Request.Body)
if err := json.Unmarshal(raw, &p); err != nil {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "参数错误"}
_ = c.ServeJSON()
return
}
verifyType := normalizeVerifyType(p.VerifyType)
openVerifyEnabled := int8(1)
if p.OpenVerifyEnabled != nil {
openVerifyEnabled = *p.OpenVerifyEnabled
}
if verifyType == "geetest" {
if p.Geetest4ID == nil || strings.TrimSpace(*p.Geetest4ID) == "" {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "geetest4_id 不能为空"}
_ = c.ServeJSON()
return
}
if p.Geetest4Key == nil || strings.TrimSpace(*p.Geetest4Key) == "" {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "geetest4_key 不能为空"}
_ = c.ServeJSON()
return
}
}
var existed models.PlatformLoginVerify
err := models.Orm.QueryTable(new(models.PlatformLoginVerify)).OrderBy("-id").One(&existed)
if err == nil {
update := map[string]interface{}{
"open_verify_enabled": openVerifyEnabled,
"verify_type": verifyType,
"geetest3_id": p.Geetest3ID,
"geetest3_key": p.Geetest3Key,
"geetest4_id": p.Geetest4ID,
"geetest4_key": p.Geetest4Key,
}
_, err = models.Orm.QueryTable(new(models.PlatformLoginVerify)).Filter("id", existed.ID).Update(update)
if err != nil {
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "保存失败"}
_ = c.ServeJSON()
return
}
} else {
row := &models.PlatformLoginVerify{
OpenVerifyEnabled: openVerifyEnabled,
VerifyType: verifyType,
Geetest3ID: p.Geetest3ID,
Geetest3Key: p.Geetest3Key,
Geetest4ID: p.Geetest4ID,
Geetest4Key: p.Geetest4Key,
}
if _, err := models.Orm.Insert(row); err != nil {
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "保存失败"}
_ = c.ServeJSON()
return
}
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
_ = c.ServeJSON()
}
+23 -12
View File
@@ -19,7 +19,7 @@ type PlatformModulesController struct {
beego.Controller
}
func (c *PlatformModulesController) platformClaims() (*jwtutil.Claims, error) {
func (c *PlatformModulesController) modulesClaims() (*jwtutil.Claims, error) {
auth := c.Ctx.Request.Header.Get("Authorization")
if auth == "" {
return nil, fmt.Errorf("未登录")
@@ -32,8 +32,19 @@ func (c *PlatformModulesController) platformClaims() (*jwtutil.Claims, error) {
if err != nil {
return nil, fmt.Errorf("无效的token")
}
if claims.UserType != "platform" {
return nil, fmt.Errorf("无权访问")
// 语义更正:
// - /platform/* 只能 platform 访问
// - /backend/* 只能 backend 访问
// 兼容:历史 token 可能缺少 user_type(按 user 处理),此时都拒绝访问以避免越权。
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
}
@@ -46,7 +57,7 @@ func (c *PlatformModulesController) jsonErr(httpStatus, bizCode int, msg string)
// GetList GET /platform/modules/list
func (c *PlatformModulesController) GetList() {
if _, err := c.platformClaims(); err != nil {
if _, err := c.modulesClaims(); err != nil {
c.jsonErr(401, 401, err.Error())
return
}
@@ -73,7 +84,7 @@ func (c *PlatformModulesController) GetList() {
// GetTenantList GET /platform/modules/getTenantList
// 兼容旧接口命名:返回当前账号可见的模块。当前实现:返回 status=1 且 is_show=1 的全部模块。
func (c *PlatformModulesController) GetTenantList() {
if _, err := c.platformClaims(); err != nil {
if _, err := c.modulesClaims(); err != nil {
c.jsonErr(401, 401, err.Error())
return
}
@@ -101,7 +112,7 @@ func (c *PlatformModulesController) GetTenantList() {
// GetDetail GET /platform/modules/:id
func (c *PlatformModulesController) GetDetail() {
if _, err := c.platformClaims(); err != nil {
if _, err := c.modulesClaims(); err != nil {
c.jsonErr(401, 401, err.Error())
return
}
@@ -139,7 +150,7 @@ type modulePayload struct {
// Add POST /platform/modules
func (c *PlatformModulesController) Add() {
if _, err := c.platformClaims(); err != nil {
if _, err := c.modulesClaims(); err != nil {
c.jsonErr(401, 401, err.Error())
return
}
@@ -194,7 +205,7 @@ func (c *PlatformModulesController) Add() {
// Edit PUT /platform/modules/:id
func (c *PlatformModulesController) Edit() {
if _, err := c.platformClaims(); err != nil {
if _, err := c.modulesClaims(); err != nil {
c.jsonErr(401, 401, err.Error())
return
}
@@ -262,7 +273,7 @@ func (c *PlatformModulesController) Edit() {
// Delete DELETE /platform/modules/:id(软删)
func (c *PlatformModulesController) Delete() {
if _, err := c.platformClaims(); err != nil {
if _, err := c.modulesClaims(); err != nil {
c.jsonErr(401, 401, err.Error())
return
}
@@ -291,7 +302,7 @@ func (c *PlatformModulesController) Delete() {
// BatchDelete POST /platform/modules/batchDelete body:{ids:[]}
func (c *PlatformModulesController) BatchDelete() {
if _, err := c.platformClaims(); err != nil {
if _, err := c.modulesClaims(); err != nil {
c.jsonErr(401, 401, err.Error())
return
}
@@ -323,7 +334,7 @@ func (c *PlatformModulesController) BatchDelete() {
// ChangeStatus POST /platform/modules/status body:{id,status}
// 兼容前端:这里的 status 实际用于切换 is_show(显示开关)。
func (c *PlatformModulesController) ChangeStatus() {
if _, err := c.platformClaims(); err != nil {
if _, err := c.modulesClaims(); err != nil {
c.jsonErr(401, 401, err.Error())
return
}
@@ -362,7 +373,7 @@ func (c *PlatformModulesController) ChangeStatus() {
// GetSelectList GET /platform/modules/select/list
func (c *PlatformModulesController) GetSelectList() {
if _, err := c.platformClaims(); err != nil {
if _, err := c.modulesClaims(); err != nil {
c.jsonErr(401, 401, err.Error())
return
}