更新oa工作台

This commit is contained in:
2025-11-07 15:12:09 +08:00
parent d2806311bb
commit e2874cf9f0
7 changed files with 2452 additions and 769 deletions
+33 -160
View File
@@ -10,7 +10,7 @@ import (
"github.com/beego/beego/v2/client/orm"
)
// RoleMenu 角色-菜单关联表模型
// 角色-菜单关联表
type RoleMenu struct {
Id int `orm:"auto" json:"id"`
RoleId int `orm:"column(role_id)" json:"role_id"`
@@ -19,115 +19,79 @@ type RoleMenu struct {
CreateBy string `orm:"column(create_by);size(50);null" json:"create_by"`
}
// TableName 指定表名
func (r *RoleMenu) TableName() string {
return "yz_role_menus"
}
// RolePermission 角色权限响应结构(包含菜单信息)
// 角色权限响应结构
type RolePermission struct {
RoleId int `json:"role_id"`
RoleName string `json:"role_name"`
MenuIds []int `json:"menu_ids"`
Permissions []string `json:"permissions"` // 权限标识列表
Permissions []string `json:"permissions"`
}
// MenuPermission 菜单权限信息
// 菜单权限信息结构
type MenuPermission struct {
MenuId int `json:"menu_id"`
MenuName string `json:"menu_name"`
Path string `json:"path"`
MenuType int `json:"menu_type"` // 1: 页面菜单, 2: API接口
MenuType int `json:"menu_type"` // 1页面菜单, 2API
Permission string `json:"permission"` // 权限标识
ParentId int `json:"parent_id"`
Default int8 `json:"default"` // 默认可见性:0-全局,1-平台用户2-租户用户
Default int8 `json:"default"` // 0全局, 1平台用户, 2租户用户
}
func init() {
orm.RegisterModel(new(RoleMenu))
}
// GetRoleMenus 获取指定角色的所有菜单权限(从JSON字段读取)
// 获取指定角色的菜单ID列表
func GetRoleMenus(roleId int) ([]int, error) {
o := orm.NewOrm()
var menuIdsJson sql.NullString
var menuIdsStr string
// 方法1: 尝试使用 JSON_UNQUOTE 读取 JSON 字段
// 优先尝试解析JSON字段
err := o.Raw("SELECT IFNULL(JSON_UNQUOTE(JSON_EXTRACT(menu_ids, '$')), '[]') FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&menuIdsStr)
if err == nil && menuIdsStr != "" && menuIdsStr != "[]" {
menuIdsJson = sql.NullString{String: menuIdsStr, Valid: true}
fmt.Printf("GetRoleMenus: 方法1成功,角色 %d 的 menu_ids: %s\n", roleId, menuIdsStr[:min(100, len(menuIdsStr))])
} else {
// 方法1失败,尝试方法2: 直接 CAST
if err != nil {
fmt.Printf("GetRoleMenus: 方法1失败 (%v),尝试方法2\n", err)
} else {
fmt.Printf("GetRoleMenus: 方法1结果为空,尝试方法2\n")
}
// 重置变量
// 若失败用CAST再次尝试,依然失败直接返回空
menuIdsStr = ""
err2 := o.Raw("SELECT CAST(IFNULL(menu_ids, '[]') AS CHAR) FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&menuIdsStr)
if err2 != nil {
// 如果角色不存在,返回空数组而不是错误(兼容性处理)
if err2 == orm.ErrNoRows {
fmt.Printf("GetRoleMenus: 角色 %d 不存在\n", roleId)
return []int{}, nil
}
fmt.Printf("GetRoleMenus: 方法2也失败,角色 %d 的 menu_ids 读取失败: %v\n", roleId, err2)
return []int{}, nil // 返回空数组而不是错误,保持兼容性
return []int{}, nil
}
if menuIdsStr != "" && menuIdsStr != "[]" && menuIdsStr != "null" {
menuIdsJson = sql.NullString{String: menuIdsStr, Valid: true}
fmt.Printf("GetRoleMenus: 方法2成功,角色 %d 的 menu_ids: %s\n", roleId, menuIdsStr[:min(100, len(menuIdsStr))])
} else {
fmt.Printf("GetRoleMenus: 方法2结果也为空,角色 %d 的 menu_ids 为空或 null\n", roleId)
return []int{}, nil
}
}
// 如果 menuIdsJson 无效或为空,返回空数组
if !menuIdsJson.Valid || menuIdsJson.String == "" {
fmt.Printf("GetRoleMenus: 角色 %d 的 menu_ids 最终为空或无效\n", roleId)
return []int{}, nil
}
// 清理可能的空白字符和换行符
jsonStr := strings.TrimSpace(menuIdsJson.String)
jsonStr = strings.ReplaceAll(jsonStr, "\n", "")
jsonStr = strings.ReplaceAll(jsonStr, "\r", "")
jsonStr = strings.ReplaceAll(jsonStr, " ", "") // 移除所有空格
// 调试:输出原始 JSON 字符串
fmt.Printf("角色 %d 的 menu_ids 原始值: %s (长度: %d)\n", roleId, jsonStr, len(jsonStr))
jsonStr = strings.ReplaceAll(jsonStr, " ", "")
if jsonStr == "" || jsonStr == "[]" || jsonStr == "null" || jsonStr == "NULL" {
fmt.Printf("角色 %d 的 menu_ids 为空数组或 null\n", roleId)
return []int{}, nil
}
var menuIds []int
err = json.Unmarshal([]byte(jsonStr), &menuIds)
if err != nil {
// 如果解析失败,记录详细错误信息用于调试
fmt.Printf("错误:解析角色 %d 的菜单ID失败: %v\n", roleId, err)
fmt.Printf("原始值: %s\n", jsonStr)
fmt.Printf("原始值长度: %d\n", len(jsonStr))
// 尝试打印前200个字符用于调试
if len(jsonStr) > 200 {
fmt.Printf("原始值前200字符: %s\n", jsonStr[:200])
}
return []int{}, nil
}
// 调试输出:成功解析的菜单ID数量
fmt.Printf("成功解析角色 %d 的菜单ID,共 %d 个\n", roleId, len(menuIds))
if len(menuIds) > 0 {
fmt.Printf("前10个菜单ID: %v\n", menuIds[:min(10, len(menuIds))])
}
return menuIds, nil
}
@@ -139,45 +103,32 @@ func min(a, b int) int {
return b
}
// GetRolePermissions 获取角色的详细权限信息(包括菜单和API权限)
// 主要基于 yz_roles.menu_ids 字段来获取权限
// 获取角色的详细权限菜单和API权限)
func GetRolePermissions(roleId int) (*RolePermission, error) {
o := orm.NewOrm()
// 直接使用 GetRoleById 获取角色信息,因为它已经正确实现了 JSON 字段的读取
role, err := GetRoleById(roleId)
if err != nil {
return nil, fmt.Errorf("角色不存在: %v", err)
}
// 从角色对象中获取菜单ID列表(已经从 menu_ids JSON字段解析)
menuIds := role.MenuIds
if menuIds == nil {
menuIds = []int{}
}
// 调试输出
fmt.Printf("GetRolePermissions: 角色 %d (%s) 的 menu_ids: %v (共 %d 个)\n", roleId, role.RoleName, menuIds, len(menuIds))
fmt.Printf("GetRolePermissions: role.MenuIdsJson.Valid=%v, role.MenuIdsJson.String=%s\n", role.MenuIdsJson.Valid, role.MenuIdsJson.String)
// 3. 根据菜单ID列表获取权限标识列表(从菜单的 permission 字段获取)
// 权限标识来源于 yz_menus 表的 permission 字段
permissions := []string{} // 初始化为空数组,避免返回 null
permissions := []string{}
if len(menuIds) > 0 {
// 构建IN查询的占位符和参数
placeholders := make([]string, len(menuIds))
args := make([]interface{}, len(menuIds))
for i, id := range menuIds {
placeholders[i] = "?"
args[i] = id
}
// 查询所有菜单的权限标识(包括页面菜单和API接口,且未删除的)
query := fmt.Sprintf("SELECT DISTINCT permission FROM yz_menus WHERE id IN (%s) AND delete_time IS NULL AND permission IS NOT NULL AND permission != ''", strings.Join(placeholders, ","))
_, err = o.Raw(query, args...).QueryRows(&permissions)
if err != nil {
return nil, fmt.Errorf("获取权限标识失败: %v", err)
}
// 确保 permissions 不为 nil
if permissions == nil {
permissions = []string{}
}
@@ -186,17 +137,15 @@ func GetRolePermissions(roleId int) (*RolePermission, error) {
return &RolePermission{
RoleId: role.RoleId,
RoleName: role.RoleName,
MenuIds: menuIds, // 来自 yz_roles.menu_ids
Permissions: permissions, // 来自 yz_menus.permission(基于 menu_ids
MenuIds: menuIds,
Permissions: permissions,
}, nil
}
// 获取所有菜单权限列表(用于分配权限时展示,未删除
// 获取全部菜单权限信息(未删除)
func GetAllMenuPermissions() ([]*MenuPermission, error) {
o := orm.NewOrm()
// 查询菜单(菜单表没有default字段,直接使用0作为默认值)
var resultsWithoutDefault []struct {
var results []struct {
MenuId int
MenuName string
Path string
@@ -204,21 +153,20 @@ func GetAllMenuPermissions() ([]*MenuPermission, error) {
Permission sql.NullString
ParentId int
}
_, err := o.Raw("SELECT id as menu_id, name as menu_name, path, menu_type, permission, parent_id FROM yz_menus WHERE delete_time IS NULL ORDER BY parent_id, `order`").QueryRows(&resultsWithoutDefault)
_, err := o.Raw("SELECT id as menu_id, name as menu_name, path, menu_type, permission, parent_id FROM yz_menus WHERE delete_time IS NULL ORDER BY parent_id, `order`").QueryRows(&results)
if err != nil {
return nil, fmt.Errorf("获取菜单列表失败: %v", err)
}
// 转换为MenuPermission结构,default字段设为0(全局可见)
menus := make([]*MenuPermission, 0, len(resultsWithoutDefault))
for _, r := range resultsWithoutDefault {
menus := make([]*MenuPermission, 0, len(results))
for _, r := range results {
menu := &MenuPermission{
MenuId: r.MenuId,
MenuName: r.MenuName,
Path: r.Path,
MenuType: r.MenuType,
ParentId: r.ParentId,
Default: 0, // 默认值为0(全局可见),因为菜单表没有default字段
Default: 0,
}
if r.Permission.Valid {
menu.Permission = r.Permission.String
@@ -227,22 +175,14 @@ func GetAllMenuPermissions() ([]*MenuPermission, error) {
}
menus = append(menus, menu)
}
return menus, nil
}
// GetAllMenuPermissionsForUser 根据当前登录用户的权限获取可分配菜单列表
// userType: "user" 表示平台用户(可以看到所有菜单),"employee" 表示租户员工
// roleId: 可选的角色ID,如果提供则根据该角色的default值过滤菜单
// 设计说明:
// - 平台用户:可以看到所有菜单,可以给任何角色分配任何菜单
// - 租户员工:在权限分配界面(提供roleId时)只能看到平台管理员已经分配给自己的菜单(包括父菜单)
// - 租户员工:在菜单显示时(不提供roleId时)只看到自己有权限的菜单
// 获取可分配菜单列表,支持平台/租户员工
func GetAllMenuPermissionsForUser(userId int, userType string, roleId int) ([]*MenuPermission, error) {
o := orm.NewOrm()
// 如果提供了roleId,获取角色的default值用于过滤菜单
var roleDefault int8 = 0 // 0表示全局,不进行过滤
var roleDefault int8 = 0
if roleId > 0 {
role, err := GetRoleById(roleId)
if err == nil && role != nil {
@@ -250,57 +190,41 @@ func GetAllMenuPermissionsForUser(userId int, userType string, roleId int) ([]*M
}
}
// 如果是平台用户,返回所有菜单(根据roleDefault过滤)
if userType == "user" {
allMenus, err := GetAllMenuPermissions()
if err != nil {
return nil, err
}
// 如果roleDefault>0,根据角色的default值过滤菜单
if roleDefault > 0 {
filteredMenus := make([]*MenuPermission, 0)
for _, menu := range allMenus {
// 角色default=1(平台用户角色):只能分配default=1或default=0的菜单
// 角色default=2(租户用户角色):只能分配default=2或default=0的菜单
if menu.Default == 0 || menu.Default == roleDefault {
filteredMenus = append(filteredMenus, menu)
}
}
return filteredMenus, nil
}
return allMenus, nil
}
// 如果是租户员工
if userType == "employee" {
// 获取员工信息
var employee Employee
err := o.Raw("SELECT * FROM yz_tenant_employees WHERE id = ? AND delete_time IS NULL", userId).QueryRow(&employee)
if err != nil {
return nil, fmt.Errorf("员工不存在: %v", err)
}
// 如果员工没有角色,返回空列表
if employee.Role == 0 {
return []*MenuPermission{}, nil
}
// 获取员工角色的菜单ID列表(这是平台管理员分配给该员工的菜单)
menuIds, err := GetRoleMenus(employee.Role)
if err != nil {
return nil, fmt.Errorf("获取角色菜单失败: %v", err)
}
// 如果没有权限,返回空列表
if len(menuIds) == 0 {
return []*MenuPermission{}, nil
}
// 如果提供了roleId(权限分配界面),需要包含父菜单
// 如果没有提供roleId(菜单显示),也需要包含父菜单(但这里已经在GetTenantMenus中处理了)
// 为了性能优化,一次性查询所有菜单的父子关系
// 获取全部菜单父子关系,内存递归收集所有父菜单ID
type menuParent struct {
Id int
ParentId int
@@ -310,14 +234,10 @@ func GetAllMenuPermissionsForUser(userId int, userType string, roleId int) ([]*M
if err != nil {
return nil, fmt.Errorf("获取菜单父子关系失败: %v", err)
}
// 构建菜单ID到父菜单ID的映射
menuParentMap := make(map[int]int)
for _, mp := range allMenuParents {
menuParentMap[mp.Id] = mp.ParentId
}
// 递归查找所有父菜单ID(使用内存中的映射,避免数据库查询)
parentIds := make(map[int]bool)
var findParents func(pid int)
findParents = func(pid int) {
@@ -329,15 +249,11 @@ func GetAllMenuPermissionsForUser(userId int, userType string, roleId int) ([]*M
findParents(parentId)
}
}
// 为每个菜单查找其父菜单
for _, menuId := range menuIds {
if parentId, exists := menuParentMap[menuId]; exists && parentId > 0 {
findParents(parentId)
}
}
// 合并原始菜单ID和父菜单ID
allMenuIds := make(map[int]bool)
for _, id := range menuIds {
allMenuIds[id] = true
@@ -346,12 +262,10 @@ func GetAllMenuPermissionsForUser(userId int, userType string, roleId int) ([]*M
allMenuIds[pid] = true
}
// 构建IN查询的占位符和参数
finalMenuIds := make([]int, 0, len(allMenuIds))
for id := range allMenuIds {
finalMenuIds = append(finalMenuIds, id)
}
placeholders := make([]string, len(finalMenuIds))
args := make([]interface{}, len(finalMenuIds))
for i, id := range finalMenuIds {
@@ -359,7 +273,6 @@ func GetAllMenuPermissionsForUser(userId int, userType string, roleId int) ([]*M
args[i] = id
}
// 查询菜单(包括父菜单)
type menuResult struct {
MenuId int
MenuName string
@@ -368,7 +281,6 @@ func GetAllMenuPermissionsForUser(userId int, userType string, roleId int) ([]*M
Permission sql.NullString
ParentId int
}
var results []menuResult
query := fmt.Sprintf("SELECT id as menu_id, name as menu_name, path, menu_type, permission, parent_id FROM yz_menus WHERE id IN (%s) AND delete_time IS NULL ORDER BY parent_id, `order`", strings.Join(placeholders, ","))
_, err = o.Raw(query, args...).QueryRows(&results)
@@ -376,7 +288,6 @@ func GetAllMenuPermissionsForUser(userId int, userType string, roleId int) ([]*M
return nil, fmt.Errorf("获取菜单列表失败: %v", err)
}
// 转换为MenuPermission结构
menus := make([]*MenuPermission, 0, len(results))
for _, r := range results {
menu := &MenuPermission{
@@ -385,46 +296,35 @@ func GetAllMenuPermissionsForUser(userId int, userType string, roleId int) ([]*M
Path: r.Path,
MenuType: r.MenuType,
ParentId: r.ParentId,
Default: 0, // 默认值为0(全局可见),因为菜单表没有default字段
Default: 0,
}
// 处理permission字段
if r.Permission.Valid {
menu.Permission = r.Permission.String
} else {
menu.Permission = ""
}
menus = append(menus, menu)
}
// 如果roleDefault>0,根据角色的default值进一步过滤菜单
// 但由于菜单表没有default字段,所有菜单都是default=0,所以这里实际上不会过滤
// 菜单表没有default字段,下面逻辑实际上不会筛选掉任何菜单,仅保留
if roleDefault > 0 {
filteredMenus := make([]*MenuPermission, 0)
for _, menu := range menus {
// 角色default=1(平台用户角色):只能分配default=1或default=0的菜单
// 角色default=2(租户用户角色):只能分配default=2或default=0的菜单
// 由于菜单表没有default字段,所有菜单都是default=0,所以所有菜单都可以分配
if menu.Default == 0 || menu.Default == roleDefault {
filteredMenus = append(filteredMenus, menu)
}
}
return filteredMenus, nil
}
return menus, nil
}
// 未知的用户类型,返回空列表
return []*MenuPermission{}, nil
}
// 为角色分配权限(菜单)- 更新JSON字段
// 分配角色权限,更新role菜单ID
func AssignRolePermissions(roleId int, menuIds []int, createBy string) error {
o := orm.NewOrm()
// 将菜单ID数组序列化为JSON
var jsonData []byte
var err error
if len(menuIds) == 0 {
@@ -435,28 +335,21 @@ func AssignRolePermissions(roleId int, menuIds []int, createBy string) error {
return fmt.Errorf("序列化菜单ID失败: %v", err)
}
}
// 更新角色表的menu_ids字段
_, err = o.Raw("UPDATE yz_roles SET menu_ids = ?, update_by = ?, update_time = NOW() WHERE role_id = ?", string(jsonData), createBy, roleId).Exec()
if err != nil {
return fmt.Errorf("更新角色权限失败: %v", err)
}
return nil
}
// GetUserPermissions 获取用户所有权限(通过用户角色)
// 获取用户所有权限(从所属角色)
func GetUserPermissions(userId int) (*RolePermission, error) {
o := orm.NewOrm()
// 获取用户信息
var user User
err := o.Raw("SELECT * FROM yz_users WHERE id = ? AND delete_time IS NULL", userId).QueryRow(&user)
if err != nil {
return nil, fmt.Errorf("用户不存在: %v", err)
}
// 如果用户没有角色,返回空权限
if user.Role == 0 {
return &RolePermission{
RoleId: 0,
@@ -465,33 +358,27 @@ func GetUserPermissions(userId int) (*RolePermission, error) {
Permissions: []string{},
}, nil
}
// 获取角色权限
return GetRolePermissions(user.Role)
}
// CheckUserPermission 检查用户是否拥有指定权限
// 检查用户是否拥有指定权限
func CheckUserPermission(userId int, permission string) (bool, error) {
if permission == "" {
return true, nil // 空权限标识表示不需要权限控制
return true, nil
}
userPerms, err := GetUserPermissions(userId)
if err != nil {
return false, err
}
// 检查权限列表中是否包含指定权限
for _, perm := range userPerms.Permissions {
if perm == permission {
return true, nil
}
}
return false, nil
}
// MenuTreeNode 菜单树节点(包含子节点)
// 菜单树节点
type MenuTreeNode struct {
Id int `json:"id"`
Name string `json:"name"`
@@ -508,34 +395,26 @@ type MenuTreeNode struct {
Children []*MenuTreeNode `json:"children"`
}
// GetUserMenuTree 获取用户有权限访问的菜单树(仅页面菜单)
// 获取用户菜单树(仅页面菜单)
func GetUserMenuTree(userId int) ([]*MenuTreeNode, error) {
o := orm.NewOrm()
// 获取用户角色
var user User
err := o.Raw("SELECT * FROM yz_users WHERE id = ? AND delete_time IS NULL", userId).QueryRow(&user)
if err != nil {
return nil, fmt.Errorf("用户不存在: %v", err)
}
if user.Role == 0 {
return []*MenuTreeNode{}, nil
}
// 获取角色的菜单ID列表
menuIds, err := GetRoleMenus(user.Role)
if err != nil {
return nil, err
}
if len(menuIds) == 0 {
return []*MenuTreeNode{}, nil
}
// 获取菜单信息(仅页面菜单)
var menus []*Menu
// 构建IN查询的占位符和参数
placeholders := make([]string, len(menuIds))
args := make([]interface{}, len(menuIds))
for i, id := range menuIds {
@@ -547,8 +426,6 @@ func GetUserMenuTree(userId int) ([]*MenuTreeNode, error) {
if err != nil {
return nil, fmt.Errorf("获取菜单列表失败: %v", err)
}
// 转换为MenuTreeNode
var nodes []*MenuTreeNode
for _, m := range menus {
nodes = append(nodes, &MenuTreeNode{
@@ -567,21 +444,17 @@ func GetUserMenuTree(userId int) ([]*MenuTreeNode, error) {
Children: []*MenuTreeNode{},
})
}
// 构建菜单树
return buildMenuTree(nodes, 0), nil
}
// buildMenuTree 构建菜单树
// 构建菜单树
func buildMenuTree(menus []*MenuTreeNode, parentId int) []*MenuTreeNode {
var tree []*MenuTreeNode
for _, menu := range menus {
if menu.ParentId == parentId {
menu.Children = buildMenuTree(menus, menu.Id)
tree = append(tree, menu)
}
}
return tree
}
+18 -73
View File
@@ -3,24 +3,23 @@ package models
import (
"database/sql"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/beego/beego/v2/client/orm"
)
// Role 角色模型
// Role 角色
type Role struct {
RoleId int `orm:"pk;auto;column(role_id)" json:"roleId"`
TenantId int `orm:"column(tenant_id)" json:"tenantId"`
Default int8 `orm:"column(default);default(2)" json:"default"` // 角色默认分配:1-只给租户12-所有租户可用,3-租户专属
Default int8 `orm:"column(default);default(2)" json:"default"` // 1-租户1, 2-所有租户, 3-租户专属
RoleCode string `orm:"size(50);unique" json:"roleCode"`
RoleName string `orm:"size(100)" json:"roleName"`
Description string `orm:"type(text);null" json:"description"`
MenuIds []int `orm:"-" json:"menuIds"` // 前端使用的菜单ID数组(不存储在数据库)
MenuIdsJson sql.NullString `orm:"column(menu_ids);type(json);null" json:"-"` // 数据库存储的JSON字段
Status int8 `orm:"default(1)" json:"status"` // 1:启用 0:禁用
MenuIds []int `orm:"-" json:"menuIds"` // 前端菜单ID
MenuIdsJson sql.NullString `orm:"column(menu_ids);type(json);null" json:"-"` // JSON字段
Status int8 `orm:"default(1)" json:"status"` // 1启用 0禁用
SortOrder int `orm:"default(0)" json:"sortOrder"` // 排序
CreateTime time.Time `orm:"auto_now_add;type(datetime)" json:"createTime"`
UpdateTime time.Time `orm:"auto_now;type(datetime)" json:"updateTime"`
@@ -29,32 +28,22 @@ type Role struct {
UpdateBy string `orm:"size(50);null" json:"updateBy"`
}
// AfterRead 读取数据后解析JSON字段
// AfterRead 解析MenuIdsJson
func (r *Role) AfterRead() {
// 调试输出
fmt.Printf("AfterRead: MenuIdsJson.Valid=%v, MenuIdsJson.String=%s\n", r.MenuIdsJson.Valid, r.MenuIdsJson.String)
if r.MenuIdsJson.Valid && r.MenuIdsJson.String != "" && r.MenuIdsJson.String != "[]" {
// 清理可能的空白字符
jsonStr := strings.TrimSpace(r.MenuIdsJson.String)
jsonStr = strings.ReplaceAll(jsonStr, "\n", "")
jsonStr = strings.ReplaceAll(jsonStr, "\r", "")
err := json.Unmarshal([]byte(jsonStr), &r.MenuIds)
if err != nil {
// 如果解析失败,记录错误但使用空数组
fmt.Printf("AfterRead: JSON解析失败: %v, 原始值: %s\n", err, jsonStr)
r.MenuIds = []int{}
} else {
fmt.Printf("AfterRead: 成功解析 %d 个菜单ID\n", len(r.MenuIds))
}
} else {
fmt.Printf("AfterRead: MenuIdsJson 无效或为空\n")
r.MenuIds = []int{}
}
}
// BeforeInsert 插入前序列化JSON字段
// BeforeInsert 序列化MenuIds到MenuIdsJson
func (r *Role) BeforeInsert() {
if len(r.MenuIds) > 0 {
jsonData, _ := json.Marshal(r.MenuIds)
@@ -64,7 +53,7 @@ func (r *Role) BeforeInsert() {
}
}
// BeforeUpdate 更新前序列化JSON字段
// BeforeUpdate 序列化MenuIds到MenuIdsJson
func (r *Role) BeforeUpdate() {
if len(r.MenuIds) > 0 {
jsonData, _ := json.Marshal(r.MenuIds)
@@ -82,12 +71,10 @@ func init() {
orm.RegisterModel(new(Role))
}
// GetRoleById 根据ID获取角色
// GetRoleById ID获取角色
func GetRoleById(roleId int) (*Role, error) {
o := orm.NewOrm()
// 使用Raw查询以正确读取JSON字段
// 定义一个临时结构体来接收查询结果
type roleResult struct {
RoleId int
TenantId int
@@ -106,7 +93,6 @@ func GetRoleById(roleId int) (*Role, error) {
}
var result roleResult
// 先读取其他字段(不包括 menu_ids),因为 Beego ORM 可能无法直接读取 JSON 类型
err := o.Raw("SELECT role_id, tenant_id, `default`, role_code, role_name, description, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(
&result.RoleId, &result.TenantId, &result.Default, &result.RoleCode, &result.RoleName, &result.Description,
&result.Status, &result.SortOrder, &result.CreateTime, &result.UpdateTime,
@@ -116,39 +102,24 @@ func GetRoleById(roleId int) (*Role, error) {
return nil, err
}
// 单独读取 menu_ids JSON 字段,使用 JSON_UNQUOTE 确保正确读取
var menuIdsStr string
err2 := o.Raw("SELECT IFNULL(JSON_UNQUOTE(JSON_EXTRACT(menu_ids, '$')), '[]') FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&menuIdsStr)
if err2 != nil {
fmt.Printf("GetRoleById: JSON_UNQUOTE 读取失败: %v,尝试 CAST\n", err2)
// 如果 JSON_UNQUOTE 失败,尝试直接 CAST
err3 := o.Raw("SELECT CAST(IFNULL(menu_ids, '[]') AS CHAR) FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&menuIdsStr)
if err3 != nil {
fmt.Printf("GetRoleById: CAST 也失败: %v,使用空数组\n", err3)
menuIdsStr = "[]"
}
}
// 设置 MenuIdsJson
if menuIdsStr != "" && menuIdsStr != "[]" && menuIdsStr != "null" {
result.MenuIdsJson = sql.NullString{String: menuIdsStr, Valid: true}
// 只打印前100个字符,避免日志过长
preview := menuIdsStr
if len(preview) > 100 {
preview = preview[:100] + "..."
}
fmt.Printf("GetRoleById: 角色 %d 的 menu_ids 读取成功: %s (总长度: %d)\n", roleId, preview, len(menuIdsStr))
} else {
result.MenuIdsJson = sql.NullString{String: "[]", Valid: true}
fmt.Printf("GetRoleById: 角色 %d 的 menu_ids 为空,使用空数组\n", roleId)
}
// 检查是否已删除(虽然SQL已经过滤了,但为了安全还是检查一下)
if result.DeleteTime != nil {
return nil, orm.ErrNoRows
}
// 构建Role对象
role := &Role{
RoleId: result.RoleId,
TenantId: result.TenantId,
@@ -165,21 +136,15 @@ func GetRoleById(roleId int) (*Role, error) {
CreateBy: result.CreateBy,
UpdateBy: result.UpdateBy,
}
// 解析JSON字段
role.AfterRead()
return role, nil
}
// GetAllRoles 获取所有角色(未删除的)
// tenantId: 租户ID0表示所有租户
// userType: 用户类型,"user"表示平台用户,"employee"表示租户员工
// GetAllRoles 获取所有角色
func GetAllRoles(tenantId int, userType string) ([]*Role, error) {
o := orm.NewOrm()
var roles []*Role
// 使用Raw查询以正确读取JSON字段
var results []struct {
RoleId int
TenantId int
@@ -197,27 +162,17 @@ func GetAllRoles(tenantId int, userType string) ([]*Role, error) {
UpdateBy string
}
// 构建查询条件
var query string
var args []interface{}
// 如果是平台用户(user),可以看到所有角色
if userType == "user" {
query = "SELECT role_id, tenant_id, `default`, role_code, role_name, description, CAST(IFNULL(menu_ids, '[]') AS CHAR) as menu_ids, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE delete_time IS NULL ORDER BY sort_order ASC, role_id ASC"
args = []interface{}{}
} else {
// 如果是租户员工(employee),根据 tenant_id 和 default 过滤
// 规则:
// 1. default=0: 全局角色,所有租户可见
// 2. default=1: 平台用户角色,租户员工不可见
// 3. default=2: 租户用户角色,只有对应租户可见
// 4. tenant_id=0: 全局角色
// 5. tenant_id=当前租户ID: 当前租户的角色
if tenantId > 0 {
query = "SELECT role_id, tenant_id, `default`, role_code, role_name, description, CAST(IFNULL(menu_ids, '[]') AS CHAR) as menu_ids, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE delete_time IS NULL AND ((`default` = 0) OR (`default` = 2 AND (tenant_id = ? OR tenant_id = 0))) ORDER BY sort_order ASC, role_id ASC"
args = []interface{}{tenantId}
} else {
// tenantId=0,只返回全局角色(default=0
query = "SELECT role_id, tenant_id, `default`, role_code, role_name, description, CAST(IFNULL(menu_ids, '[]') AS CHAR) as menu_ids, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE delete_time IS NULL AND `default` = 0 ORDER BY sort_order ASC, role_id ASC"
args = []interface{}{}
}
@@ -252,12 +207,11 @@ func GetAllRoles(tenantId int, userType string) ([]*Role, error) {
return roles, nil
}
// GetRoleByTenantId 根据租户ID获取角色列表(未删除的)
// GetRoleByTenantId 租户ID获取角色
func GetRoleByTenantId(tenantId int) ([]*Role, error) {
o := orm.NewOrm()
var roles []*Role
// 使用Raw查询以正确读取JSON字段
var results []struct {
RoleId int
TenantId int
@@ -304,11 +258,10 @@ func GetRoleByTenantId(tenantId int) ([]*Role, error) {
return roles, nil
}
// GetRoleByCode 根据角色代码获取角色
// GetRoleByCode 角色代码获取角色
func GetRoleByCode(roleCode string) (*Role, error) {
o := orm.NewOrm()
// 使用Raw查询以正确读取所有字段,包括 default 和 menu_ids
type roleResult struct {
RoleId int
TenantId int
@@ -336,7 +289,6 @@ func GetRoleByCode(roleCode string) (*Role, error) {
return nil, err
}
// 手动读取 menu_ids JSON 字段
var menuIdsStr string
err2 := o.Raw("SELECT IFNULL(JSON_UNQUOTE(JSON_EXTRACT(menu_ids, '$')), '[]') FROM yz_roles WHERE role_code = ? AND delete_time IS NULL", roleCode).QueryRow(&menuIdsStr)
if err2 == nil && menuIdsStr != "" && menuIdsStr != "[]" {
@@ -359,7 +311,6 @@ func GetRoleByCode(roleCode string) (*Role, error) {
CreateBy: result.CreateBy,
UpdateBy: result.UpdateBy,
}
role.AfterRead()
return role, nil
}
@@ -369,14 +320,13 @@ func CreateRole(role *Role) error {
o := orm.NewOrm()
role.BeforeInsert()
// 使用Raw插入以正确处理JSON字段,并获取插入后的ID
// 如果没有设置 default 值,根据 tenant_id 自动设置:tenant_id=0 时 default=2,否则 default=3
// 自动处理default
defaultValue := role.Default
if defaultValue == 0 {
if role.TenantId == 0 {
defaultValue = 2 // 所有租户可用
defaultValue = 2
} else {
defaultValue = 3 // 租户专属
defaultValue = 3
}
}
res, err := o.Raw("INSERT INTO yz_roles (tenant_id, `default`, role_code, role_name, description, menu_ids, status, sort_order, create_time, update_time, create_by, update_by) VALUES (?, ?, ?, ?, ?, CAST(? AS JSON), ?, ?, NOW(), NOW(), ?, ?)",
@@ -385,18 +335,14 @@ func CreateRole(role *Role) error {
return err
}
// 获取插入后的ID
lastInsertId, err := res.LastInsertId()
if err != nil {
// 如果无法获取 LastInsertId,尝试通过角色代码查询
createdRole, queryErr := GetRoleByCode(role.RoleCode)
if queryErr == nil && createdRole != nil {
role.RoleId = createdRole.RoleId
}
return nil
}
// 设置插入后的ID
role.RoleId = int(lastInsertId)
return nil
}
@@ -406,14 +352,13 @@ func UpdateRole(role *Role) error {
o := orm.NewOrm()
role.BeforeUpdate()
// 使用Raw更新以正确处理JSON字段
// 如果没有设置 default 值,根据 tenant_id 自动设置:tenant_id=0 时 default=2,否则 default=3
// 自动处理default
defaultValue := role.Default
if defaultValue == 0 {
if role.TenantId == 0 {
defaultValue = 2 // 所有租户可用
defaultValue = 2
} else {
defaultValue = 3 // 租户专属
defaultValue = 3
}
}
_, err := o.Raw("UPDATE yz_roles SET tenant_id = ?, `default` = ?, role_code = ?, role_name = ?, description = ?, menu_ids = CAST(? AS JSON), status = ?, sort_order = ?, update_time = NOW(), update_by = ? WHERE role_id = ? AND delete_time IS NULL",