优化系统

This commit is contained in:
2025-11-06 23:10:17 +08:00
parent f4244c09b9
commit 0cd0b9c705
23 changed files with 1086 additions and 129 deletions
+15 -11
View File
@@ -93,7 +93,7 @@ func (c *AuthController) Login() {
"nickname": user.Nickname,
"tenant_id": user.TenantId,
"role": user.Role, // 角色ID
"type": "user", // 标识是用户登录
"type": "user", // 标识是用户登录
}
} else if employee != nil {
// 员工登录
@@ -101,16 +101,16 @@ func (c *AuthController) Login() {
usernameForToken = employee.EmployeeNo
tenantId = employee.TenantId
userInfo = map[string]interface{}{
"id": employee.Id,
"username": employee.EmployeeNo,
"name": employee.Name,
"email": employee.Email,
"phone": employee.Phone,
"tenant_id": employee.TenantId,
"id": employee.Id,
"username": employee.EmployeeNo,
"name": employee.Name,
"email": employee.Email,
"phone": employee.Phone,
"tenant_id": employee.TenantId,
"department_id": employee.DepartmentId,
"position_id": employee.PositionId,
"role": employee.Role, // 角色ID
"type": "employee", // 标识是员工登录
"position_id": employee.PositionId,
"role": employee.Role, // 角色ID
"type": "employee", // 标识是员工登录
}
} else {
c.Data["json"] = map[string]interface{}{
@@ -122,7 +122,11 @@ func (c *AuthController) Login() {
}
// 使用models包中的GenerateToken函数生成token
tokenString, err = models.GenerateToken(userId, usernameForToken, tenantId)
userType := "user"
if employee != nil {
userType = "employee"
}
tokenString, err = models.GenerateToken(userId, usernameForToken, tenantId, userType)
if err != nil {
c.Data["json"] = map[string]interface{}{
+108
View File
@@ -0,0 +1,108 @@
package controllers
import (
"server/models"
"github.com/beego/beego/v2/client/orm"
beego "github.com/beego/beego/v2/server/web"
)
// DashboardController 仪表盘控制器
type DashboardController struct {
beego.Controller
}
// GetPlatformStats 获取平台统计数据(平台用户使用)
// @router /api/dashboard/platform-stats [get]
func (c *DashboardController) GetPlatformStats() {
// 获取租户总数
tenants, err := models.GetTenantList()
tenantCount := int64(0)
if err == nil {
tenantCount = int64(len(tenants))
}
// 获取知识库总数及增长率(所有租户)
knowledgeCount, currentMonthKnowledge, lastMonthKnowledge, knowledgeGrowthRate, _ := models.GetKnowledgeCountWithGrowth(0)
// 获取用户总数(所有租户的用户,使用简单查询)
o := orm.NewOrm()
var userCount int64
o.Raw("SELECT COUNT(*) FROM yz_users WHERE delete_time IS NULL").QueryRow(&userCount)
// 获取员工总数(所有租户的员工)
var employeeCount int64
o.Raw("SELECT COUNT(*) FROM yz_tenant_employees WHERE delete_time IS NULL").QueryRow(&employeeCount)
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "success",
"data": map[string]interface{}{
"tenantCount": tenantCount,
"userCount": userCount,
"knowledgeCount": map[string]interface{}{
"total": knowledgeCount,
"currentMonth": currentMonthKnowledge,
"lastMonth": lastMonthKnowledge,
"growthRate": knowledgeGrowthRate,
},
"employeeCount": employeeCount,
},
}
c.ServeJSON()
}
// GetTenantStats 获取租户统计数据(租户员工使用)
// @router /api/dashboard/tenant-stats [get]
func (c *DashboardController) GetTenantStats() {
// 获取租户ID(从JWT token中获取)
tenantId := 0
if tenantIdVal, ok := c.Ctx.Input.GetData("tenantId").(int); ok && tenantIdVal > 0 {
if userType, ok := c.Ctx.Input.GetData("userType").(string); ok && userType == "employee" {
tenantId = tenantIdVal
}
}
if tenantId <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "无法获取租户信息",
"data": nil,
}
c.ServeJSON()
return
}
// 获取知识库数量及增长率
knowledgeCount, currentMonthKnowledge, lastMonthKnowledge, knowledgeGrowthRate, _ := models.GetKnowledgeCountWithGrowth(tenantId)
// 获取员工数量(简单查询)
o := orm.NewOrm()
var employeeCount int64
o.Raw("SELECT COUNT(*) FROM yz_tenant_employees WHERE tenant_id = ? AND delete_time IS NULL", tenantId).QueryRow(&employeeCount)
// 获取部门数量
var departmentCount int64
o.Raw("SELECT COUNT(*) FROM yz_tenant_departments WHERE tenant_id = ? AND delete_time IS NULL", tenantId).QueryRow(&departmentCount)
// 获取职位数量
var positionCount int64
o.Raw("SELECT COUNT(*) FROM yz_tenant_positions WHERE tenant_id = ? AND delete_time IS NULL", tenantId).QueryRow(&positionCount)
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "success",
"data": map[string]interface{}{
"knowledgeCount": map[string]interface{}{
"total": knowledgeCount,
"currentMonth": currentMonthKnowledge,
"lastMonth": lastMonthKnowledge,
"growthRate": knowledgeGrowthRate,
},
"employeeCount": employeeCount,
"departmentCount": departmentCount,
"positionCount": positionCount,
},
}
c.ServeJSON()
}
+45 -9
View File
@@ -23,7 +23,7 @@ func (c *KnowledgeController) List() {
categoryId, _ := c.GetInt("categoryId", 0)
share, _ := c.GetInt8("share", -1) // Default -1 to query all
keyword := c.GetString("keyword", "")
// 获取租户ID(如果是员工登录,从JWT token中获取)
tenantId := 0
if tenantIdVal, ok := c.Ctx.Input.GetData("tenantId").(int); ok && tenantIdVal > 0 {
@@ -58,6 +58,42 @@ func (c *KnowledgeController) List() {
c.ServeJSON()
}
// GetCount 获取知识库数量及增长率
// @router /api/knowledge/count [get]
func (c *KnowledgeController) GetCount() {
// 获取租户ID(如果是员工登录,从JWT token中获取)
tenantId := 0
if tenantIdVal, ok := c.Ctx.Input.GetData("tenantId").(int); ok && tenantIdVal > 0 {
// 检查是否是员工登录(type === "employee"
if userType, ok := c.Ctx.Input.GetData("userType").(string); ok && userType == "employee" {
tenantId = tenantIdVal
}
}
totalCount, currentMonthCount, lastMonthCount, growthRate, err := models.GetKnowledgeCountWithGrowth(tenantId)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "获取知识库数量失败: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "success",
"data": map[string]interface{}{
"count": totalCount,
"currentMonthCount": currentMonthCount,
"lastMonthCount": lastMonthCount,
"growthRate": growthRate,
},
}
c.ServeJSON()
}
// Detail 获取知识详情
// @router /api/knowledge/detail [get]
func (c *KnowledgeController) Detail() {
@@ -71,7 +107,7 @@ func (c *KnowledgeController) Detail() {
c.ServeJSON()
return
}
// 获取租户ID(如果是员工登录,从JWT token中获取)
tenantId := 0
if tenantIdVal, ok := c.Ctx.Input.GetData("tenantId").(int); ok && tenantIdVal > 0 {
@@ -130,7 +166,7 @@ func (c *KnowledgeController) Create() {
// Add share parsing (default 0 for personal)
share, _ := c.GetInt8("share", 0)
knowledge.Share = share
// 获取租户ID(如果是员工登录,从JWT token中获取并设置)
if tenantIdVal, ok := c.Ctx.Input.GetData("tenantId").(int); ok && tenantIdVal > 0 {
// 检查是否是员工登录(type === "employee"
@@ -184,7 +220,7 @@ func (c *KnowledgeController) Update() {
c.ServeJSON()
return
}
// 获取租户ID(如果是员工登录,从JWT token中获取)
tenantId := 0
if tenantIdVal, ok := c.Ctx.Input.GetData("tenantId").(int); ok && tenantIdVal > 0 {
@@ -247,7 +283,7 @@ func (c *KnowledgeController) Delete() {
if deleteBy == "" {
deleteBy = "system" // 默认值
}
// 获取租户ID(如果是员工登录,从JWT token中获取)
tenantId := 0
if tenantIdVal, ok := c.Ctx.Input.GetData("tenantId").(int); ok && tenantIdVal > 0 {
@@ -287,7 +323,7 @@ func (c *KnowledgeController) GetCategories() {
tenantId = tenantIdVal
}
}
categories, err := models.GetAllCategories(tenantId)
if err != nil {
c.Data["json"] = map[string]interface{}{
@@ -332,7 +368,7 @@ func (c *KnowledgeController) GetTags() {
tenantId = tenantIdVal
}
}
tags, err := models.GetAllTags(tenantId)
if err != nil {
c.Data["json"] = map[string]interface{}{
@@ -380,7 +416,7 @@ func (c *KnowledgeController) AddCategory() {
c.ServeJSON()
return
}
// 获取租户ID(如果是员工登录,从JWT token中获取并设置)
if tenantIdVal, ok := c.Ctx.Input.GetData("tenantId").(int); ok && tenantIdVal > 0 {
// 检查是否是员工登录(type === "employee"
@@ -423,7 +459,7 @@ func (c *KnowledgeController) AddTag() {
c.ServeJSON()
return
}
// 获取租户ID(如果是员工登录,从JWT token中获取并设置)
if tenantIdVal, ok := c.Ctx.Input.GetData("tenantId").(int); ok && tenantIdVal > 0 {
// 检查是否是员工登录(type === "employee"
+63 -23
View File
@@ -6,8 +6,8 @@ import (
"server/models"
"strconv"
beego "github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/core/logs"
beego "github.com/beego/beego/v2/server/web"
)
// PermissionController 权限管理控制器
@@ -16,9 +16,50 @@ type PermissionController struct {
}
// GetAllMenuPermissions 获取所有菜单权限列表(用于分配权限)
// 根据当前登录用户的权限和选中角色的default值过滤菜单
// 如果提供了roleId参数,根据该角色的default值过滤菜单:
// - role.default=1(平台用户角色):只能分配default=1或default=0的菜单
// - role.default=2(租户用户角色):只能分配default=2或default=0的菜单
func (c *PermissionController) GetAllMenuPermissions() {
menus, err := models.GetAllMenuPermissions()
// 从JWT中获取用户ID和用户类型
userIdData := c.Ctx.Input.GetData("userId")
userTypeData := c.Ctx.Input.GetData("userType")
if userIdData == nil {
c.Data["json"] = map[string]interface{}{
"success": false,
"message": "未获取到用户信息",
}
c.ServeJSON()
return
}
userId, ok := userIdData.(int)
if !ok {
c.Data["json"] = map[string]interface{}{
"success": false,
"message": "用户ID格式错误",
}
c.ServeJSON()
return
}
userType := "user" // 默认为平台用户
if userTypeData != nil {
if ut, ok := userTypeData.(string); ok {
userType = ut
}
}
// 获取可选的roleId参数(用于根据角色的default值过滤菜单)
var roleId int
if roleIdParam, err := c.GetInt("roleId"); err == nil && roleIdParam > 0 {
roleId = roleIdParam
}
// 根据用户类型、权限和角色default值获取菜单列表
menus, err := models.GetAllMenuPermissionsForUser(userId, userType, roleId)
if err != nil {
c.Data["json"] = map[string]interface{}{
"success": false,
@@ -32,7 +73,7 @@ func (c *PermissionController) GetAllMenuPermissions() {
"data": menus,
}
}
c.ServeJSON()
}
@@ -47,7 +88,7 @@ func (c *PermissionController) GetRolePermissions() {
c.ServeJSON()
return
}
permissions, err := models.GetRolePermissions(roleId)
if err != nil {
c.Data["json"] = map[string]interface{}{
@@ -62,7 +103,7 @@ func (c *PermissionController) GetRolePermissions() {
"data": permissions,
}
}
c.ServeJSON()
}
@@ -77,12 +118,12 @@ func (c *PermissionController) AssignRolePermissions() {
c.ServeJSON()
return
}
// 解析请求体
var requestData struct {
MenuIds []int `json:"menu_ids"`
}
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &requestData); err != nil {
c.Data["json"] = map[string]interface{}{
"success": false,
@@ -92,7 +133,7 @@ func (c *PermissionController) AssignRolePermissions() {
c.ServeJSON()
return
}
// 获取当前用户ID(从JWT中获取)
userIdData := c.Ctx.Input.GetData("userId")
var createBy string
@@ -102,10 +143,10 @@ func (c *PermissionController) AssignRolePermissions() {
createBy = strconv.Itoa(userId)
}
}
// 记录日志(用于调试)
logs.Info(fmt.Sprintf("开始为角色 %d 分配权限,共 %d 个菜单", roleId, len(requestData.MenuIds)))
// 分配权限
err = models.AssignRolePermissions(roleId, requestData.MenuIds, createBy)
if err != nil {
@@ -122,7 +163,7 @@ func (c *PermissionController) AssignRolePermissions() {
"message": "分配权限成功",
}
}
c.ServeJSON()
}
@@ -138,7 +179,7 @@ func (c *PermissionController) GetUserPermissions() {
c.ServeJSON()
return
}
userId, ok := userIdData.(int)
if !ok {
c.Data["json"] = map[string]interface{}{
@@ -148,7 +189,7 @@ func (c *PermissionController) GetUserPermissions() {
c.ServeJSON()
return
}
permissions, err := models.GetUserPermissions(userId)
if err != nil {
c.Data["json"] = map[string]interface{}{
@@ -163,7 +204,7 @@ func (c *PermissionController) GetUserPermissions() {
"data": permissions,
}
}
c.ServeJSON()
}
@@ -179,7 +220,7 @@ func (c *PermissionController) GetUserMenuTree() {
c.ServeJSON()
return
}
userId, ok := userIdData.(int)
if !ok {
c.Data["json"] = map[string]interface{}{
@@ -189,7 +230,7 @@ func (c *PermissionController) GetUserMenuTree() {
c.ServeJSON()
return
}
menuTree, err := models.GetUserMenuTree(userId)
if err != nil {
c.Data["json"] = map[string]interface{}{
@@ -204,7 +245,7 @@ func (c *PermissionController) GetUserMenuTree() {
"data": menuTree,
}
}
c.ServeJSON()
}
@@ -220,7 +261,7 @@ func (c *PermissionController) CheckPermission() {
c.ServeJSON()
return
}
userId, ok := userIdData.(int)
if !ok {
c.Data["json"] = map[string]interface{}{
@@ -230,7 +271,7 @@ func (c *PermissionController) CheckPermission() {
c.ServeJSON()
return
}
// 获取权限标识
permission := c.GetString("permission")
if permission == "" {
@@ -241,7 +282,7 @@ func (c *PermissionController) CheckPermission() {
c.ServeJSON()
return
}
hasPermission, err := models.CheckUserPermission(userId, permission)
if err != nil {
c.Data["json"] = map[string]interface{}{
@@ -258,7 +299,6 @@ func (c *PermissionController) CheckPermission() {
},
}
}
c.ServeJSON()
}
+58 -5
View File
@@ -12,9 +12,33 @@ type RoleController struct {
}
// GetAllRoles 获取所有角色
// 根据当前登录用户的 tenant_id 和 userType 过滤角色
// @router /api/roles [get]
func (c *RoleController) GetAllRoles() {
roles, err := models.GetAllRoles()
// 从JWT中获取租户ID和用户类型
tenantIdData := c.Ctx.Input.GetData("tenantId")
userTypeData := c.Ctx.Input.GetData("userType")
tenantId := 0
if tenantIdData != nil {
if tid, ok := tenantIdData.(int); ok {
tenantId = tid
}
}
// 如果请求参数中有 tenant_id,优先使用请求参数
if requestTenantId, err := c.GetInt("tenant_id"); err == nil && requestTenantId > 0 {
tenantId = requestTenantId
}
userType := "user" // 默认为平台用户
if userTypeData != nil {
if ut, ok := userTypeData.(string); ok {
userType = ut
}
}
roles, err := models.GetAllRoles(tenantId, userType)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
@@ -126,11 +150,11 @@ func (c *RoleController) GetRoleByTenantId() {
c.ServeJSON()
return
}
// 获取当前登录用户类型(从JWT中间件获取)
userType, _ := c.Ctx.Input.GetData("userType").(string)
isEmployee := userType == "employee"
// 转换为前端需要的格式,确保包含 tenantId 和 default 字段
// 如果是租户登录(employee),过滤掉 default=1 的角色
roleList := make([]map[string]interface{}, 0)
@@ -139,7 +163,7 @@ func (c *RoleController) GetRoleByTenantId() {
if isEmployee && role.Default == 1 {
continue
}
roleList = append(roleList, map[string]interface{}{
"roleId": role.RoleId,
"tenantId": role.TenantId,
@@ -155,7 +179,7 @@ func (c *RoleController) GetRoleByTenantId() {
"updateBy": role.UpdateBy,
})
}
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "获取角色列表成功",
@@ -199,6 +223,21 @@ func (c *RoleController) CreateRole() {
return
}
// 如果请求中没有 tenant_id,从JWT中获取
if role.TenantId == 0 {
if tenantIdData := c.Ctx.Input.GetData("tenantId"); tenantIdData != nil {
if tid, ok := tenantIdData.(int); ok && tid > 0 {
role.TenantId = tid
}
}
}
// 获取当前用户名(用于记录创建操作)
if username, ok := c.Ctx.Input.GetData("username").(string); ok && username != "" {
role.CreateBy = username
role.UpdateBy = username
}
// 检查角色代码是否已存在
existingRole, err := models.GetRoleByCode(role.RoleCode)
if err == nil && existingRole != nil {
@@ -291,6 +330,20 @@ func (c *RoleController) UpdateRole() {
// 设置角色ID
role.RoleId = roleId
// 如果请求中没有 tenant_id,从JWT中获取
if role.TenantId == 0 {
if tenantIdData := c.Ctx.Input.GetData("tenantId"); tenantIdData != nil {
if tid, ok := tenantIdData.(int); ok && tid > 0 {
role.TenantId = tid
}
}
}
// 获取当前用户名(用于记录更新操作)
if username, ok := c.Ctx.Input.GetData("username").(string); ok && username != "" {
role.UpdateBy = username
}
err = models.UpdateRole(&role)
if err != nil {
c.Data["json"] = map[string]interface{}{
+11 -13
View File
@@ -4,7 +4,6 @@ import (
"strings"
"server/models"
"server/services"
"github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/server/web/context"
@@ -61,17 +60,16 @@ func JWTAuthMiddleware() web.FilterFunc {
return
}
// 将用户信息存储在上下文
ctx.Input.SetData("userId", claims.UserID)
ctx.Input.SetData("username", claims.Username)
ctx.Input.SetData("tenantId", claims.TenantId)
// 判断用户类型:检查userId是否在员工表中
// 如果userId在yz_tenant_employees表中存在,则为员工登录;否则为用户登录
userType := "user"
if services.IsEmployee(claims.UserID) {
userType = "employee"
// 将用户信息存储在上下文
ctx.Input.SetData("userId", claims.UserID)
ctx.Input.SetData("username", claims.Username)
ctx.Input.SetData("tenantId", claims.TenantId)
// 从token中获取用户类型(如果token中没有,则默认为"user"
userType := claims.UserType
if userType == "" {
userType = "user"
}
ctx.Input.SetData("userType", userType)
}
ctx.Input.SetData("userType", userType)
}
}
+3 -1
View File
@@ -15,11 +15,12 @@ type Claims struct {
UserID int `json:"user_id"`
Username string `json:"username"`
TenantId int `json:"tenant_id"` // 租户ID
UserType string `json:"user_type"` // 用户类型:"user" 或 "employee"
jwt.RegisteredClaims
}
// GenerateToken 生成JWT token
func GenerateToken(userID int, username string, tenantId int) (string, error) {
func GenerateToken(userID int, username string, tenantId int, userType string) (string, error) {
// 设置token过期时间
expirationTime := time.Now().Add(24 * time.Hour) // 24小时后过期
@@ -28,6 +29,7 @@ func GenerateToken(userID int, username string, tenantId int) (string, error) {
UserID: userID,
Username: username,
TenantId: tenantId,
UserType: userType,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime),
IssuedAt: jwt.NewNumericDate(time.Now()),
+113 -8
View File
@@ -90,7 +90,7 @@ func GetKnowledgeById(id int, tenantId int) (*Knowledge, error) {
WHERE k.knowledge_id = ? AND k.delete_time IS NULL
`
params := []interface{}{id}
// 如果tenantId > 0,添加租户过滤
if tenantId > 0 {
querySQL += " AND k.tenant_id = ?"
@@ -180,7 +180,7 @@ func GetAllKnowledge(page, pageSize int, status int8, categoryId int, share int8
whereSQL := "delete_time IS NULL"
params := []interface{}{}
// 如果tenantId > 0,添加租户过滤
if tenantId > 0 {
whereSQL += " AND k.tenant_id = ?"
@@ -232,6 +232,111 @@ func GetAllKnowledge(page, pageSize int, status int8, categoryId int, share int8
return knowledges, total, nil
}
// GetKnowledgeCount 获取知识库数量(按租户)
func GetKnowledgeCount(tenantId int) (int64, error) {
o := orm.NewOrm()
whereSQL := "delete_time IS NULL"
params := []interface{}{}
// 如果tenantId > 0,添加租户过滤
if tenantId > 0 {
whereSQL += " AND tenant_id = ?"
params = append(params, tenantId)
}
var count int64
err := o.Raw("SELECT COUNT(*) FROM yz_knowledge WHERE "+whereSQL, params...).QueryRow(&count)
return count, err
}
// GetKnowledgeCountWithGrowth 获取知识库总数量及增长率(按租户)
// 返回:总数量、本月新增数量、上个月新增数量、增长率
func GetKnowledgeCountWithGrowth(tenantId int) (int64, int64, int64, float64, error) {
o := orm.NewOrm()
// 获取当前月份的开始和结束时间
now := time.Now()
currentYear := now.Year()
currentMonth := int(now.Month())
// 当前月的开始时间
currentMonthStart := time.Date(currentYear, time.Month(currentMonth), 1, 0, 0, 0, 0, time.Local)
// 当前月的结束时间(下个月的第一天)
nextMonth := currentMonth + 1
nextYear := currentYear
if nextMonth > 12 {
nextMonth = 1
nextYear++
}
currentMonthEnd := time.Date(nextYear, time.Month(nextMonth), 1, 0, 0, 0, 0, time.Local)
// 上个月的时间范围
lastMonth := currentMonth - 1
lastYear := currentYear
if lastMonth < 1 {
lastMonth = 12
lastYear--
}
lastMonthStart := time.Date(lastYear, time.Month(lastMonth), 1, 0, 0, 0, 0, time.Local)
lastMonthEnd := currentMonthStart
// 构建查询条件
baseWhere := "delete_time IS NULL"
tenantFilter := ""
params := []interface{}{}
if tenantId > 0 {
tenantFilter = " AND tenant_id = ?"
params = append(params, tenantId)
}
// 查询总数量(所有未删除的知识库)
totalWhere := baseWhere + tenantFilter
var totalCount int64
totalParams := params
if len(totalParams) == 0 {
err := o.Raw("SELECT COUNT(*) FROM yz_knowledge WHERE " + totalWhere).QueryRow(&totalCount)
if err != nil {
return 0, 0, 0, 0, err
}
} else {
err := o.Raw("SELECT COUNT(*) FROM yz_knowledge WHERE "+totalWhere, totalParams...).QueryRow(&totalCount)
if err != nil {
return 0, 0, 0, 0, err
}
}
// 查询本月新增数量(创建时间在当月范围内的)
currentWhere := baseWhere + " AND create_time >= ? AND create_time < ?" + tenantFilter
currentParams := append([]interface{}{currentMonthStart, currentMonthEnd}, params...)
var currentMonthCount int64
err := o.Raw("SELECT COUNT(*) FROM yz_knowledge WHERE "+currentWhere, currentParams...).QueryRow(&currentMonthCount)
if err != nil {
return 0, 0, 0, 0, err
}
// 查询上个月新增数量
lastWhere := baseWhere + " AND create_time >= ? AND create_time < ?" + tenantFilter
lastParams := append([]interface{}{lastMonthStart, lastMonthEnd}, params...)
var lastMonthCount int64
err = o.Raw("SELECT COUNT(*) FROM yz_knowledge WHERE "+lastWhere, lastParams...).QueryRow(&lastMonthCount)
if err != nil {
return 0, 0, 0, 0, err
}
// 计算增长率(本月新增相比上个月新增的增长率)
var growthRate float64
if lastMonthCount > 0 {
growthRate = float64(currentMonthCount-lastMonthCount) / float64(lastMonthCount) * 100
} else if currentMonthCount > 0 {
growthRate = 100.0 // 上个月为0,这个月有数据,增长100%
}
return totalCount, currentMonthCount, lastMonthCount, growthRate, nil
}
// UpdateKnowledge 更新知识
func UpdateKnowledge(id int, k *Knowledge, tenantId int) error {
o := orm.NewOrm()
@@ -240,7 +345,7 @@ func UpdateKnowledge(id int, k *Knowledge, tenantId int) error {
if err != nil {
return err
}
// 如果tenantId > 0,验证租户ID是否匹配
if tenantId > 0 && knowledge.TenantId != tenantId {
return orm.ErrNoRows // 返回无记录错误,表示该知识不属于当前租户
@@ -272,7 +377,7 @@ func DeleteKnowledge(id int, deleteBy string, tenantId int) error {
if err != nil {
return err
}
// 如果tenantId > 0,验证租户ID是否匹配
if tenantId > 0 && knowledge.TenantId != tenantId {
return orm.ErrNoRows // 返回无记录错误,表示该知识不属于当前租户
@@ -287,12 +392,12 @@ func DeleteKnowledge(id int, deleteBy string, tenantId int) error {
func GetAllCategories(tenantId int) ([]*KnowledgeCategory, error) {
o := orm.NewOrm()
qs := o.QueryTable("yz_knowledge_category")
// 如果tenantId > 0,添加租户过滤
if tenantId > 0 {
qs = qs.Filter("tenant_id", tenantId)
}
var categories []*KnowledgeCategory
_, err := qs.OrderBy("sort_order").All(&categories)
return categories, err
@@ -310,12 +415,12 @@ func GetCategoryById(id int) (*KnowledgeCategory, error) {
func GetAllTags(tenantId int) ([]*KnowledgeTag, error) {
o := orm.NewOrm()
qs := o.QueryTable("yz_knowledge_tags")
// 如果tenantId > 0,添加租户过滤
if tenantId > 0 {
qs = qs.Filter("tenant_id", tenantId)
}
var tags []*KnowledgeTag
_, err := qs.All(&tags)
return tags, err
+221 -4
View File
@@ -40,6 +40,7 @@ type MenuPermission struct {
MenuType int `json:"menu_type"` // 1: 页面菜单, 2: API接口
Permission string `json:"permission"` // 权限标识
ParentId int `json:"parent_id"`
Default int8 `json:"default"` // 默认可见性:0-全局,1-平台用户,2-租户用户
}
func init() {
@@ -64,7 +65,7 @@ func GetRoleMenus(roleId int) ([]int, error) {
} else {
fmt.Printf("GetRoleMenus: 方法1结果为空,尝试方法2\n")
}
// 重置变量
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)
@@ -77,7 +78,7 @@ func GetRoleMenus(roleId int) ([]int, error) {
fmt.Printf("GetRoleMenus: 方法2也失败,角色 %d 的 menu_ids 读取失败: %v\n", roleId, err2)
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))])
@@ -193,16 +194,232 @@ func GetRolePermissions(roleId int) (*RolePermission, error) {
// 获取所有菜单权限列表(用于分配权限时展示,未删除的)
func GetAllMenuPermissions() ([]*MenuPermission, error) {
o := orm.NewOrm()
var menus []*MenuPermission
_, 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(&menus)
// 查询菜单(菜单表没有default字段,直接使用0作为默认值)
var resultsWithoutDefault []struct {
MenuId int
MenuName string
Path string
MenuType int
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)
if err != nil {
return nil, fmt.Errorf("获取菜单列表失败: %v", err)
}
// 转换为MenuPermission结构,default字段设为0(全局可见)
menus := make([]*MenuPermission, 0, len(resultsWithoutDefault))
for _, r := range resultsWithoutDefault {
menu := &MenuPermission{
MenuId: r.MenuId,
MenuName: r.MenuName,
Path: r.Path,
MenuType: r.MenuType,
ParentId: r.ParentId,
Default: 0, // 默认值为0(全局可见),因为菜单表没有default字段
}
if r.Permission.Valid {
menu.Permission = r.Permission.String
} else {
menu.Permission = ""
}
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表示全局,不进行过滤
if roleId > 0 {
role, err := GetRoleById(roleId)
if err == nil && role != nil {
roleDefault = role.Default
}
}
// 如果是平台用户,返回所有菜单(根据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中处理了)
// 为了性能优化,一次性查询所有菜单的父子关系
type menuParent struct {
Id int
ParentId int
}
var allMenuParents []menuParent
_, err = o.Raw("SELECT id, parent_id FROM yz_menus WHERE delete_time IS NULL").QueryRows(&allMenuParents)
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) {
if pid == 0 || parentIds[pid] {
return
}
parentIds[pid] = true
if parentId, exists := menuParentMap[pid]; exists && parentId > 0 {
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
}
for pid := range parentIds {
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 {
placeholders[i] = "?"
args[i] = id
}
// 查询菜单(包括父菜单)
type menuResult struct {
MenuId int
MenuName string
Path string
MenuType int
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)
if err != nil {
return nil, fmt.Errorf("获取菜单列表失败: %v", err)
}
// 转换为MenuPermission结构
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字段
}
// 处理permission字段
if r.Permission.Valid {
menu.Permission = r.Permission.String
} else {
menu.Permission = ""
}
menus = append(menus, menu)
}
// 如果roleDefault>0,根据角色的default值进一步过滤菜单
// 但由于菜单表没有default字段,所有菜单都是default=0,所以这里实际上不会过滤
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字段
func AssignRolePermissions(roleId int, menuIds []int, createBy string) error {
o := orm.NewOrm()
+30 -2
View File
@@ -173,7 +173,9 @@ func GetRoleById(roleId int) (*Role, error) {
}
// GetAllRoles 获取所有角色(未删除的)
func GetAllRoles() ([]*Role, error) {
// tenantId: 租户ID0表示所有租户
// userType: 用户类型,"user"表示平台用户,"employee"表示租户员工
func GetAllRoles(tenantId int, userType string) ([]*Role, error) {
o := orm.NewOrm()
var roles []*Role
@@ -195,7 +197,33 @@ func GetAllRoles() ([]*Role, error) {
UpdateBy string
}
_, err := o.Raw("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").QueryRows(&results)
// 构建查询条件
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{}{}
}
}
_, err := o.Raw(query, args...).QueryRows(&results)
if err != nil {
return nil, err
}
+5
View File
@@ -273,6 +273,7 @@ func init() {
// 知识库路由
beego.Router("/api/knowledge/list", &controllers.KnowledgeController{}, "get:List")
beego.Router("/api/knowledge/count", &controllers.KnowledgeController{}, "get:GetCount")
beego.Router("/api/knowledge/detail", &controllers.KnowledgeController{}, "get:Detail")
beego.Router("/api/knowledge/create", &controllers.KnowledgeController{}, "post:Create")
beego.Router("/api/knowledge/update", &controllers.KnowledgeController{}, "post:Update")
@@ -309,6 +310,10 @@ func init() {
beego.Router("/api/permissions/user/menus", &controllers.PermissionController{}, "get:GetUserMenuTree")
beego.Router("/api/permissions/check", &controllers.PermissionController{}, "get:CheckPermission")
// 仪表盘路由
beego.Router("/api/dashboard/platform-stats", &controllers.DashboardController{}, "get:GetPlatformStats")
beego.Router("/api/dashboard/tenant-stats", &controllers.DashboardController{}, "get:GetTenantStats")
// 手动配置特殊路由(无法通过自动路由处理的)
beego.Router("/api/allmenu", &controllers.MenuController{}, "get:GetAllMenus")
beego.Router("/api/program-categories/public", &controllers.ProgramCategoryController{}, "get:GetProgramCategoriesPublic")
BIN
View File
Binary file not shown.