更新oa代码
This commit is contained in:
+2
-221
@@ -1,14 +1,9 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
// Employee 员工模型
|
||||
@@ -24,8 +19,8 @@ type Employee struct {
|
||||
Role int `orm:"column(role);null;default(0)" json:"role"` // 角色ID
|
||||
BankName string `orm:"column(bank_name);size(100);null" json:"bank_name"`
|
||||
BankAccount string `orm:"column(bank_account);size(50);null" json:"bank_account"`
|
||||
Password string `orm:"size(255);null" json:"-"` // 不返回给前端
|
||||
Salt string `orm:"size(100);null" json:"-"` // 不返回给前端
|
||||
Password string `orm:"size(255);null" json:"-"` // 不返回给前端
|
||||
Salt string `orm:"size(100);null" json:"-"` // 不返回给前端
|
||||
LastLoginTime *time.Time `orm:"column(last_login_time);null;type(datetime)" json:"last_login_time,omitempty"`
|
||||
LastLoginIp string `orm:"column(last_login_ip);null;size(50)" json:"last_login_ip,omitempty"`
|
||||
Status int8 `orm:"column(status);default(1)" json:"status"` // 1-在职,0-离职
|
||||
@@ -42,217 +37,3 @@ func (e *Employee) TableName() string {
|
||||
func init() {
|
||||
orm.RegisterModel(new(Employee))
|
||||
}
|
||||
|
||||
// GetTenantEmployees 获取租户下的所有员工
|
||||
func GetTenantEmployees(tenantId int) ([]*Employee, error) {
|
||||
o := orm.NewOrm()
|
||||
var employees []*Employee
|
||||
_, err := o.QueryTable("yz_tenant_employees").
|
||||
Filter("tenant_id", tenantId).
|
||||
Filter("delete_time__isnull", true).
|
||||
OrderBy("-create_time").
|
||||
All(&employees)
|
||||
return employees, err
|
||||
}
|
||||
|
||||
// GetEmployeeById 根据ID获取员工信息
|
||||
func GetEmployeeById(id int) (*Employee, error) {
|
||||
o := orm.NewOrm()
|
||||
employee := &Employee{Id: id}
|
||||
err := o.Read(employee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 检查是否已删除
|
||||
if employee.DeleteTime != nil {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
return employee, nil
|
||||
}
|
||||
|
||||
// generateSalt 生成随机盐值
|
||||
func generateEmployeeSalt() (string, error) {
|
||||
salt := make([]byte, 16)
|
||||
_, err := rand.Read(salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(salt), nil
|
||||
}
|
||||
|
||||
// hashEmployeePassword 使用scrypt算法对密码进行加密
|
||||
func hashEmployeePassword(password, salt string) (string, error) {
|
||||
saltBytes, err := base64.URLEncoding.DecodeString(salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
const (
|
||||
N = 16384
|
||||
r = 8
|
||||
p = 1
|
||||
)
|
||||
hashBytes, err := scrypt.Key([]byte(password), saltBytes, N, r, p, 32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(hashBytes), nil
|
||||
}
|
||||
|
||||
// AddEmployee 添加员工(自动设置默认密码)
|
||||
func AddEmployee(employee *Employee, defaultPassword string) (int64, error) {
|
||||
// 生成盐值
|
||||
salt, err := generateEmployeeSalt()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("生成盐值失败: %v", err)
|
||||
}
|
||||
employee.Salt = salt
|
||||
|
||||
// 加密默认密码
|
||||
hashedPassword, err := hashEmployeePassword(defaultPassword, salt)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("密码加密失败: %v", err)
|
||||
}
|
||||
employee.Password = hashedPassword
|
||||
|
||||
o := orm.NewOrm()
|
||||
id, err := o.Insert(employee)
|
||||
return id, err
|
||||
}
|
||||
|
||||
// UpdateEmployee 更新员工信息
|
||||
func UpdateEmployee(employee *Employee) error {
|
||||
o := orm.NewOrm()
|
||||
_, err := o.Update(employee, "employee_no", "name", "phone", "email", "department_id", "position_id", "role", "bank_name", "bank_account", "status", "update_time")
|
||||
return err
|
||||
}
|
||||
|
||||
// ResetEmployeePassword 重置员工密码为默认密码
|
||||
func ResetEmployeePassword(employeeId int, defaultPassword string) error {
|
||||
o := orm.NewOrm()
|
||||
employee := &Employee{Id: employeeId}
|
||||
if err := o.Read(employee); err != nil {
|
||||
return fmt.Errorf("员工不存在: %v", err)
|
||||
}
|
||||
|
||||
// 生成新盐值
|
||||
salt, err := generateEmployeeSalt()
|
||||
if err != nil {
|
||||
return fmt.Errorf("生成盐值失败: %v", err)
|
||||
}
|
||||
employee.Salt = salt
|
||||
|
||||
// 加密默认密码
|
||||
hashedPassword, err := hashEmployeePassword(defaultPassword, salt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("密码加密失败: %v", err)
|
||||
}
|
||||
employee.Password = hashedPassword
|
||||
|
||||
_, err = o.Update(employee, "Password", "Salt")
|
||||
return err
|
||||
}
|
||||
|
||||
// ChangeEmployeePassword 修改员工密码
|
||||
func ChangeEmployeePassword(employeeId int, oldPassword, newPassword string) error {
|
||||
o := orm.NewOrm()
|
||||
employee := &Employee{Id: employeeId}
|
||||
if err := o.Read(employee); err != nil {
|
||||
return fmt.Errorf("员工不存在: %v", err)
|
||||
}
|
||||
|
||||
// 验证旧密码
|
||||
if !verifyEmployeePassword(oldPassword, employee.Salt, employee.Password) {
|
||||
return errors.New("旧密码不正确")
|
||||
}
|
||||
|
||||
// 加密新密码
|
||||
hashedPassword, err := hashEmployeePassword(newPassword, employee.Salt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("密码加密失败: %v", err)
|
||||
}
|
||||
employee.Password = hashedPassword
|
||||
|
||||
_, err = o.Update(employee, "Password")
|
||||
return err
|
||||
}
|
||||
|
||||
// verifyEmployeePassword 验证密码是否正确
|
||||
func verifyEmployeePassword(password, salt, storedHash string) bool {
|
||||
hash, err := hashEmployeePassword(password, salt)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return hash == storedHash
|
||||
}
|
||||
|
||||
// ValidateEmployee 验证员工登录信息(使用工号作为登录账号)
|
||||
func ValidateEmployee(employeeNo, password string, tenantId int) (*Employee, error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 1. 根据工号和租户ID查询员工(排除已删除的)
|
||||
var employee Employee
|
||||
err := o.QueryTable("yz_tenant_employees").
|
||||
Filter("employee_no", employeeNo).
|
||||
Filter("tenant_id", tenantId).
|
||||
Filter("delete_time__isnull", true).
|
||||
Filter("status", 1). // 只允许在职员工登录
|
||||
One(&employee)
|
||||
|
||||
if err == orm.ErrNoRows {
|
||||
return nil, errors.New("员工不存在或已离职")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询员工失败: %v", err)
|
||||
}
|
||||
|
||||
// 2. 检查密码和盐是否存在
|
||||
if employee.Password == "" || employee.Salt == "" {
|
||||
return nil, errors.New("员工密码未设置,请联系管理员")
|
||||
}
|
||||
|
||||
// 3. 验证密码
|
||||
if verifyEmployeePassword(password, employee.Salt, employee.Password) {
|
||||
return &employee, nil
|
||||
}
|
||||
return nil, errors.New("密码不正确")
|
||||
}
|
||||
|
||||
// DeleteEmployee 软删除员工
|
||||
func DeleteEmployee(id int) error {
|
||||
o := orm.NewOrm()
|
||||
employee := &Employee{Id: id}
|
||||
if err := o.Read(employee); err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now()
|
||||
employee.DeleteTime = &now
|
||||
_, err := o.Update(employee, "delete_time")
|
||||
return err
|
||||
}
|
||||
|
||||
// GetAllEmployees 获取所有员工(排除已删除的)
|
||||
func GetAllEmployees() ([]*Employee, error) {
|
||||
o := orm.NewOrm()
|
||||
var employees []*Employee
|
||||
_, err := o.QueryTable("yz_tenant_employees").
|
||||
Filter("delete_time__isnull", true).
|
||||
OrderBy("-create_time").
|
||||
All(&employees)
|
||||
return employees, err
|
||||
}
|
||||
|
||||
// IsEmployee 检查指定的ID是否是员工(用于判断登录类型)
|
||||
func IsEmployee(id int) bool {
|
||||
o := orm.NewOrm()
|
||||
employee := &Employee{Id: id}
|
||||
err := o.Read(employee)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// 检查是否已删除
|
||||
if employee.DeleteTime != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -107,9 +107,8 @@ func GetTenantMenus(roleId int) ([]map[string]interface{}, error) {
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
// 1. 从yz_role_menus表获取该角色的所有菜单ID
|
||||
var menuIds []int
|
||||
_, err := o.Raw("SELECT DISTINCT menu_id FROM yz_role_menus WHERE role_id = ?", roleId).QueryRows(&menuIds)
|
||||
// 1. 从yz_roles表的menu_ids JSON字段获取该角色的所有菜单ID
|
||||
menuIds, err := GetRoleMenus(roleId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+136
-84
@@ -1,6 +1,8 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -35,8 +37,8 @@ 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接口
|
||||
Permission string `json:"permission"` // 权限标识
|
||||
MenuType int `json:"menu_type"` // 1: 页面菜单, 2: API接口
|
||||
Permission string `json:"permission"` // 权限标识
|
||||
ParentId int `json:"parent_id"`
|
||||
}
|
||||
|
||||
@@ -44,38 +46,109 @@ func init() {
|
||||
orm.RegisterModel(new(RoleMenu))
|
||||
}
|
||||
|
||||
// GetRoleMenus 获取指定角色的所有菜单权限
|
||||
// GetRoleMenus 获取指定角色的所有菜单权限(从JSON字段读取)
|
||||
func GetRoleMenus(roleId int) ([]int, error) {
|
||||
o := orm.NewOrm()
|
||||
var menuIds []int
|
||||
|
||||
_, err := o.Raw("SELECT menu_id FROM yz_role_menus WHERE role_id = ?", roleId).QueryRows(&menuIds)
|
||||
var menuIdsJson sql.NullString
|
||||
|
||||
// 方法1: 尝试使用 JSON_UNQUOTE 读取 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(&menuIdsJson)
|
||||
|
||||
// 如果方法1失败或结果为空,尝试方法2: 直接 CAST
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取角色菜单失败: %v", err)
|
||||
fmt.Printf("方法1失败,尝试方法2: %v\n", err)
|
||||
err = nil // 重置错误,尝试方法2
|
||||
}
|
||||
|
||||
|
||||
if err != nil || !menuIdsJson.Valid || menuIdsJson.String == "" || menuIdsJson.String == "[]" {
|
||||
fmt.Printf("方法1结果无效,尝试方法2\n")
|
||||
err2 := o.Raw("SELECT CAST(IFNULL(menu_ids, '[]') AS CHAR) FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&menuIdsJson)
|
||||
if err2 != nil {
|
||||
// 如果角色不存在,返回空数组而不是错误(兼容性处理)
|
||||
if err2 == orm.ErrNoRows {
|
||||
fmt.Printf("角色 %d 不存在\n", roleId)
|
||||
return []int{}, nil
|
||||
}
|
||||
fmt.Printf("读取角色 %d 的 menu_ids 失败: %v\n", roleId, err2)
|
||||
return nil, fmt.Errorf("获取角色菜单失败: %v", err2)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果 menuIdsJson 无效或为空,返回空数组
|
||||
if !menuIdsJson.Valid || menuIdsJson.String == "" {
|
||||
fmt.Printf("角色 %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))
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// min 辅助函数
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// GetRolePermissions 获取角色的详细权限信息(包括菜单和API权限)
|
||||
// 主要基于 yz_roles.menu_ids 字段来获取权限
|
||||
func GetRolePermissions(roleId int) (*RolePermission, error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 获取角色信息
|
||||
var role Role
|
||||
err := o.Raw("SELECT * FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&role)
|
||||
|
||||
// 直接使用 GetRoleById 获取角色信息,因为它已经正确实现了 JSON 字段的读取
|
||||
role, err := GetRoleById(roleId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("角色不存在: %v", err)
|
||||
}
|
||||
|
||||
// 获取角色关联的所有菜单ID
|
||||
menuIds, err := GetRoleMenus(roleId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
// 从角色对象中获取菜单ID列表(已经从 menu_ids JSON字段解析)
|
||||
menuIds := role.MenuIds
|
||||
if menuIds == nil {
|
||||
menuIds = []int{}
|
||||
}
|
||||
|
||||
// 获取权限标识列表
|
||||
var permissions []string
|
||||
|
||||
// 调试输出
|
||||
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
|
||||
if len(menuIds) > 0 {
|
||||
// 构建IN查询的占位符和参数
|
||||
placeholders := make([]string, len(menuIds))
|
||||
@@ -84,95 +157,75 @@ func GetRolePermissions(roleId int) (*RolePermission, error) {
|
||||
placeholders[i] = "?"
|
||||
args[i] = id
|
||||
}
|
||||
query := fmt.Sprintf("SELECT DISTINCT permission FROM yz_menus WHERE id IN (%s) AND permission IS NOT NULL AND permission != ''", strings.Join(placeholders, ","))
|
||||
// 查询所有菜单的权限标识(包括页面菜单和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{}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return &RolePermission{
|
||||
RoleId: role.RoleId,
|
||||
RoleName: role.RoleName,
|
||||
MenuIds: menuIds,
|
||||
Permissions: permissions,
|
||||
MenuIds: menuIds, // 来自 yz_roles.menu_ids
|
||||
Permissions: permissions, // 来自 yz_menus.permission(基于 menu_ids)
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAllMenuPermissions 获取所有菜单权限列表(用于分配权限时展示,未删除的)
|
||||
// 获取所有菜单权限列表(用于分配权限时展示,未删除的)
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取菜单列表失败: %v", err)
|
||||
}
|
||||
|
||||
|
||||
return menus, nil
|
||||
}
|
||||
|
||||
// AssignRolePermissions 为角色分配权限(菜单)
|
||||
// 为角色分配权限(菜单)- 更新JSON字段
|
||||
func AssignRolePermissions(roleId int, menuIds []int, createBy string) error {
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 先删除该角色的所有权限(使用更快的方式)
|
||||
_, err := o.Raw("DELETE FROM yz_role_menus WHERE role_id = ?", roleId).Exec()
|
||||
if err != nil {
|
||||
return fmt.Errorf("删除旧权限失败: %v", err)
|
||||
}
|
||||
|
||||
// 如果没有新权限,直接返回
|
||||
|
||||
// 将菜单ID数组序列化为JSON
|
||||
var jsonData []byte
|
||||
var err error
|
||||
if len(menuIds) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 使用更高效的批量插入方式
|
||||
// 如果数据量太大,分批插入以避免超时
|
||||
batchSize := 500 // 每批500条,MySQL可以高效处理
|
||||
total := len(menuIds)
|
||||
|
||||
for i := 0; i < total; i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
|
||||
batch := menuIds[i:end]
|
||||
|
||||
// 构建批量INSERT语句
|
||||
query := "INSERT INTO yz_role_menus (role_id, menu_id, create_by) VALUES "
|
||||
values := make([]interface{}, 0, len(batch)*3)
|
||||
|
||||
placeholders := make([]string, 0, len(batch))
|
||||
for _, menuId := range batch {
|
||||
placeholders = append(placeholders, "(?, ?, ?)")
|
||||
values = append(values, roleId, menuId, createBy)
|
||||
}
|
||||
|
||||
query += strings.Join(placeholders, ", ")
|
||||
|
||||
// 执行批量插入
|
||||
_, err = o.Raw(query, values...).Exec()
|
||||
jsonData = []byte("[]")
|
||||
} else {
|
||||
jsonData, err = json.Marshal(menuIds)
|
||||
if err != nil {
|
||||
return fmt.Errorf("插入新权限失败(批次 %d/%d): %v", i/batchSize+1, (total+batchSize-1)/batchSize, err)
|
||||
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{
|
||||
@@ -182,7 +235,7 @@ func GetUserPermissions(userId int) (*RolePermission, error) {
|
||||
Permissions: []string{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
// 获取角色权限
|
||||
return GetRolePermissions(user.Role)
|
||||
}
|
||||
@@ -192,19 +245,19 @@ func CheckUserPermission(userId int, permission string) (bool, error) {
|
||||
if permission == "" {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -228,28 +281,28 @@ type MenuTreeNode struct {
|
||||
// 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查询的占位符和参数
|
||||
@@ -264,7 +317,7 @@ func GetUserMenuTree(userId int) ([]*MenuTreeNode, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取菜单列表失败: %v", err)
|
||||
}
|
||||
|
||||
|
||||
// 转换为MenuTreeNode
|
||||
var nodes []*MenuTreeNode
|
||||
for _, m := range menus {
|
||||
@@ -284,7 +337,7 @@ func GetUserMenuTree(userId int) ([]*MenuTreeNode, error) {
|
||||
Children: []*MenuTreeNode{},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 构建菜单树
|
||||
return buildMenuTree(nodes, 0), nil
|
||||
}
|
||||
@@ -292,14 +345,13 @@ func GetUserMenuTree(userId int) ([]*MenuTreeNode, error) {
|
||||
// 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
|
||||
}
|
||||
|
||||
|
||||
+293
-69
@@ -1,6 +1,10 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
@@ -8,21 +12,67 @@ import (
|
||||
|
||||
// Role 角色模型
|
||||
type Role struct {
|
||||
RoleId int `orm:"pk;auto;column(role_id)" json:"roleId"`
|
||||
TenantId int `orm:"column(tenant_id)" json:"tenantId"`
|
||||
RoleCode string `orm:"size(50);unique" json:"roleCode"`
|
||||
RoleName string `orm:"size(50)" json:"roleName"`
|
||||
Description string `orm:"type(text);null" json:"description"`
|
||||
Status int8 `orm:"default(1)" json:"status"`
|
||||
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"`
|
||||
DeleteTime *time.Time `orm:"null;type(datetime)" json:"deleteTime"`
|
||||
CreateBy string `orm:"size(50);null" json:"createBy"`
|
||||
UpdateBy string `orm:"size(50);null" json:"updateBy"`
|
||||
RoleId int `orm:"pk;auto;column(role_id)" json:"roleId"`
|
||||
TenantId int `orm:"column(tenant_id)" json:"tenantId"`
|
||||
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:禁用
|
||||
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"`
|
||||
DeleteTime *time.Time `orm:"null;type(datetime)" json:"deleteTime"`
|
||||
CreateBy string `orm:"size(50);null" json:"createBy"`
|
||||
UpdateBy string `orm:"size(50);null" json:"updateBy"`
|
||||
}
|
||||
|
||||
// AfterRead 读取数据后解析JSON字段
|
||||
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字段
|
||||
func (r *Role) BeforeInsert() {
|
||||
if len(r.MenuIds) > 0 {
|
||||
jsonData, _ := json.Marshal(r.MenuIds)
|
||||
r.MenuIdsJson = sql.NullString{String: string(jsonData), Valid: true}
|
||||
} else {
|
||||
r.MenuIdsJson = sql.NullString{String: "[]", Valid: true}
|
||||
}
|
||||
}
|
||||
|
||||
// BeforeUpdate 更新前序列化JSON字段
|
||||
func (r *Role) BeforeUpdate() {
|
||||
if len(r.MenuIds) > 0 {
|
||||
jsonData, _ := json.Marshal(r.MenuIds)
|
||||
r.MenuIdsJson = sql.NullString{String: string(jsonData), Valid: true}
|
||||
} else {
|
||||
r.MenuIdsJson = sql.NullString{String: "[]", Valid: true}
|
||||
}
|
||||
}
|
||||
|
||||
// TableName 设置表名
|
||||
func (r *Role) TableName() string {
|
||||
return "yz_roles"
|
||||
}
|
||||
@@ -31,83 +81,257 @@ func init() {
|
||||
orm.RegisterModel(new(Role))
|
||||
}
|
||||
|
||||
// GetAllRoles 获取所有角色(排除已删除的)
|
||||
func GetAllRoles() ([]Role, error) {
|
||||
o := orm.NewOrm()
|
||||
var roles []Role
|
||||
_, err := o.QueryTable("yz_roles").Filter("DeleteTime__isnull", true).Filter("Status", 1).OrderBy("SortOrder").All(&roles)
|
||||
return roles, err
|
||||
}
|
||||
|
||||
// GetRoleById 根据ID获取角色
|
||||
func GetRoleById(roleId int) (*Role, error) {
|
||||
o := orm.NewOrm()
|
||||
role := &Role{RoleId: roleId}
|
||||
err := o.Read(role)
|
||||
return role, err
|
||||
}
|
||||
|
||||
// GetRoleByTenantId 根据租户ID获取角色列表
|
||||
func GetRoleByTenantId(tenantId int) ([]Role, error) {
|
||||
o := orm.NewOrm()
|
||||
var roles []Role
|
||||
qs := o.QueryTable("yz_roles").Filter("DeleteTime__isnull", true)
|
||||
if tenantId > 0 {
|
||||
// 显示指定租户和公共(tenant_id=0)的角色
|
||||
qs = qs.Filter("TenantId__in", []int{0, tenantId})
|
||||
} else if tenantId == 0 {
|
||||
// 仅显示公共(tenant_id=0)的角色
|
||||
qs = qs.Filter("TenantId", 0)
|
||||
// 使用Raw查询以正确读取JSON字段
|
||||
// 定义一个临时结构体来接收查询结果
|
||||
type roleResult struct {
|
||||
RoleId int
|
||||
TenantId int
|
||||
RoleCode string
|
||||
RoleName string
|
||||
Description string
|
||||
MenuIdsJson sql.NullString
|
||||
Status int8
|
||||
SortOrder int
|
||||
CreateTime time.Time
|
||||
UpdateTime time.Time
|
||||
DeleteTime *time.Time
|
||||
CreateBy string
|
||||
UpdateBy string
|
||||
}
|
||||
_, err := qs.OrderBy("SortOrder").All(&roles)
|
||||
return roles, err
|
||||
|
||||
var result roleResult
|
||||
// 先读取其他字段(不包括 menu_ids),因为 Beego ORM 可能无法直接读取 JSON 类型
|
||||
err := o.Raw("SELECT role_id, tenant_id, 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.RoleCode, &result.RoleName, &result.Description,
|
||||
&result.Status, &result.SortOrder, &result.CreateTime, &result.UpdateTime,
|
||||
&result.DeleteTime, &result.CreateBy, &result.UpdateBy,
|
||||
)
|
||||
if err != nil {
|
||||
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,
|
||||
RoleCode: result.RoleCode,
|
||||
RoleName: result.RoleName,
|
||||
Description: result.Description,
|
||||
MenuIdsJson: result.MenuIdsJson,
|
||||
Status: result.Status,
|
||||
SortOrder: result.SortOrder,
|
||||
CreateTime: result.CreateTime,
|
||||
UpdateTime: result.UpdateTime,
|
||||
DeleteTime: result.DeleteTime,
|
||||
CreateBy: result.CreateBy,
|
||||
UpdateBy: result.UpdateBy,
|
||||
}
|
||||
|
||||
// 解析JSON字段
|
||||
role.AfterRead()
|
||||
|
||||
return role, nil
|
||||
}
|
||||
|
||||
// GetRoleByCode 根据代码获取角色(排除已删除的)
|
||||
// GetAllRoles 获取所有角色(未删除的)
|
||||
func GetAllRoles() ([]*Role, error) {
|
||||
o := orm.NewOrm()
|
||||
var roles []*Role
|
||||
|
||||
// 使用Raw查询以正确读取JSON字段
|
||||
var results []struct {
|
||||
RoleId int
|
||||
TenantId int
|
||||
RoleCode string
|
||||
RoleName string
|
||||
Description string
|
||||
MenuIdsJson sql.NullString
|
||||
Status int8
|
||||
SortOrder int
|
||||
CreateTime time.Time
|
||||
UpdateTime time.Time
|
||||
DeleteTime *time.Time
|
||||
CreateBy string
|
||||
UpdateBy string
|
||||
}
|
||||
|
||||
_, err := o.Raw("SELECT role_id, tenant_id, 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, r := range results {
|
||||
role := &Role{
|
||||
RoleId: r.RoleId,
|
||||
TenantId: r.TenantId,
|
||||
RoleCode: r.RoleCode,
|
||||
RoleName: r.RoleName,
|
||||
Description: r.Description,
|
||||
MenuIdsJson: r.MenuIdsJson,
|
||||
Status: r.Status,
|
||||
SortOrder: r.SortOrder,
|
||||
CreateTime: r.CreateTime,
|
||||
UpdateTime: r.UpdateTime,
|
||||
DeleteTime: r.DeleteTime,
|
||||
CreateBy: r.CreateBy,
|
||||
UpdateBy: r.UpdateBy,
|
||||
}
|
||||
role.AfterRead()
|
||||
roles = append(roles, role)
|
||||
}
|
||||
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
// GetRoleByTenantId 根据租户ID获取角色列表(未删除的)
|
||||
func GetRoleByTenantId(tenantId int) ([]*Role, error) {
|
||||
o := orm.NewOrm()
|
||||
var roles []*Role
|
||||
|
||||
// 使用Raw查询以正确读取JSON字段
|
||||
var results []struct {
|
||||
RoleId int
|
||||
TenantId int
|
||||
RoleCode string
|
||||
RoleName string
|
||||
Description string
|
||||
MenuIdsJson sql.NullString
|
||||
Status int8
|
||||
SortOrder int
|
||||
CreateTime time.Time
|
||||
UpdateTime time.Time
|
||||
DeleteTime *time.Time
|
||||
CreateBy string
|
||||
UpdateBy string
|
||||
}
|
||||
|
||||
_, err := o.Raw("SELECT role_id, tenant_id, 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 (tenant_id = ? OR tenant_id = 0) AND delete_time IS NULL ORDER BY sort_order ASC, role_id ASC", tenantId).QueryRows(&results)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, r := range results {
|
||||
role := &Role{
|
||||
RoleId: r.RoleId,
|
||||
TenantId: r.TenantId,
|
||||
RoleCode: r.RoleCode,
|
||||
RoleName: r.RoleName,
|
||||
Description: r.Description,
|
||||
MenuIdsJson: r.MenuIdsJson,
|
||||
Status: r.Status,
|
||||
SortOrder: r.SortOrder,
|
||||
CreateTime: r.CreateTime,
|
||||
UpdateTime: r.UpdateTime,
|
||||
DeleteTime: r.DeleteTime,
|
||||
CreateBy: r.CreateBy,
|
||||
UpdateBy: r.UpdateBy,
|
||||
}
|
||||
role.AfterRead()
|
||||
roles = append(roles, role)
|
||||
}
|
||||
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
// GetRoleByCode 根据角色代码获取角色
|
||||
func GetRoleByCode(roleCode string) (*Role, error) {
|
||||
o := orm.NewOrm()
|
||||
role := &Role{}
|
||||
err := o.QueryTable("yz_roles").Filter("RoleCode", roleCode).Filter("DeleteTime__isnull", true).One(role)
|
||||
return role, err
|
||||
var role Role
|
||||
|
||||
err := o.QueryTable("yz_roles").Filter("role_code", roleCode).Filter("delete_time__isnull", true).One(&role)
|
||||
if err != nil {
|
||||
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 != "[]" {
|
||||
role.MenuIdsJson = sql.NullString{String: menuIdsStr, Valid: true}
|
||||
}
|
||||
|
||||
role.AfterRead()
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
// CreateRole 创建角色
|
||||
func CreateRole(role *Role) (int64, error) {
|
||||
func CreateRole(role *Role) error {
|
||||
o := orm.NewOrm()
|
||||
id, err := o.Insert(role)
|
||||
return id, err
|
||||
role.BeforeInsert()
|
||||
|
||||
// 使用Raw插入以正确处理JSON字段,并获取插入后的ID
|
||||
res, err := o.Raw("INSERT INTO yz_roles (tenant_id, role_code, role_name, description, menu_ids, status, sort_order, create_time, update_time, create_by, update_by) VALUES (?, ?, ?, ?, CAST(? AS JSON), ?, ?, NOW(), NOW(), ?, ?)",
|
||||
role.TenantId, role.RoleCode, role.RoleName, role.Description, role.MenuIdsJson.String, role.Status, role.SortOrder, role.CreateBy, role.UpdateBy).Exec()
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// UpdateRole 更新角色
|
||||
func UpdateRole(role *Role) error {
|
||||
o := orm.NewOrm()
|
||||
_, err := o.Update(role)
|
||||
role.BeforeUpdate()
|
||||
|
||||
// 使用Raw更新以正确处理JSON字段
|
||||
_, err := o.Raw("UPDATE yz_roles SET tenant_id = ?, 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",
|
||||
role.TenantId, role.RoleCode, role.RoleName, role.Description, role.MenuIdsJson.String, role.Status, role.SortOrder, role.UpdateBy, role.RoleId).Exec()
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteRole 删除角色(软删除,设置删除时间)
|
||||
func DeleteRole(roleId int) error {
|
||||
// DeleteRole 软删除角色
|
||||
func DeleteRole(roleId int, updateBy string) error {
|
||||
o := orm.NewOrm()
|
||||
role := &Role{RoleId: roleId}
|
||||
err := o.Read(role)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now()
|
||||
role.DeleteTime = &now
|
||||
_, err = o.Update(role, "DeleteTime")
|
||||
return err
|
||||
}
|
||||
|
||||
// 修改角色状态
|
||||
func ChangeStatus(roleId int, status int) error {
|
||||
o := orm.NewOrm()
|
||||
role := &Role{RoleId: roleId}
|
||||
err := o.Read(role)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
role.Status = int8(status)
|
||||
_, err = o.Update(role, "Status")
|
||||
_, err := o.Raw("UPDATE yz_roles SET delete_time = NOW(), update_by = ? WHERE role_id = ? AND delete_time IS NULL", updateBy, roleId).Exec()
|
||||
return err
|
||||
}
|
||||
|
||||
+15
-363
@@ -1,14 +1,10 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
@@ -38,360 +34,7 @@ func (u *User) TableName() string {
|
||||
return "yz_users"
|
||||
}
|
||||
|
||||
// generateSalt 生成随机盐值
|
||||
func generateSalt() (string, error) {
|
||||
salt := make([]byte, 16)
|
||||
_, err := rand.Read(salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(salt), nil
|
||||
}
|
||||
|
||||
// hashPassword 使用scrypt算法对密码进行加密
|
||||
func hashPassword(password, salt string) (string, error) {
|
||||
saltBytes, err := base64.URLEncoding.DecodeString(salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
const (
|
||||
N = 16384
|
||||
r = 8
|
||||
p = 1
|
||||
)
|
||||
hashBytes, err := scrypt.Key([]byte(password), saltBytes, N, r, p, 32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(hashBytes), nil
|
||||
}
|
||||
|
||||
// verifyPassword 验证密码是否正确
|
||||
func verifyPassword(password, salt, storedHash string) bool {
|
||||
hash, err := hashPassword(password, salt)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return hash == storedHash
|
||||
}
|
||||
|
||||
// ResetPassword 重置用户密码
|
||||
func ResetPassword(username, superPassword string, tenantId int) error {
|
||||
if superPassword != "Lzq920103" {
|
||||
return fmt.Errorf("超级密码错误")
|
||||
}
|
||||
|
||||
o := orm.NewOrm()
|
||||
user, err := GetUserInfo(0, username, tenantId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("用户不存在: %v", err)
|
||||
}
|
||||
|
||||
// 总是生成新的盐值,确保密码重置的完整性
|
||||
salt, err := generateSalt()
|
||||
if err != nil {
|
||||
return fmt.Errorf("生成盐值失败: %v", err)
|
||||
}
|
||||
user.Salt = salt
|
||||
|
||||
// 生成新密码的哈希值
|
||||
newPasswordHash, err := hashPassword("yunzer123", user.Salt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("密码加密失败: %v", err)
|
||||
}
|
||||
|
||||
user.Password = newPasswordHash
|
||||
_, err = o.Update(user, "Password", "Salt")
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新密码失败: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("用户 %s 密码重置成功,新密码: yunzer123\n", username)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangePassword 修改用户密码
|
||||
func ChangePassword(username, oldPassword, newPassword string, tenantId int) error {
|
||||
user, err := GetUserInfo(0, username, tenantId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !verifyPassword(oldPassword, user.Salt, user.Password) {
|
||||
return errors.New("旧密码不正确")
|
||||
}
|
||||
newPasswordHash, err := hashPassword(newPassword, user.Salt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user.Password = newPasswordHash
|
||||
o := orm.NewOrm()
|
||||
_, err = o.Update(user, "Password")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// GetAllUsers 获取所有用户
|
||||
func GetAllUsers(tenantId int) []*User {
|
||||
o := orm.NewOrm()
|
||||
var users []*User
|
||||
if tenantId > 0 {
|
||||
// 按租户ID查询
|
||||
_, err := o.Raw("SELECT * FROM yz_users WHERE tenant_id = ?", tenantId).QueryRows(&users)
|
||||
if err != nil {
|
||||
return []*User{}
|
||||
}
|
||||
} else {
|
||||
// 查询所有用户
|
||||
_, err := o.QueryTable("yz_users").All(&users)
|
||||
if err != nil {
|
||||
return []*User{}
|
||||
}
|
||||
}
|
||||
return users
|
||||
}
|
||||
|
||||
// GetTenantUsers 获取指定租户下的所有用户(排除已删除的用户)
|
||||
func GetTenantUsers(tenantId int) ([]*User, error) {
|
||||
o := orm.NewOrm()
|
||||
var users []*User
|
||||
|
||||
// 查询指定租户下未删除的用户
|
||||
_, err := o.Raw("SELECT * FROM yz_users WHERE tenant_id = ? AND delete_time IS NULL ORDER BY id DESC", tenantId).QueryRows(&users)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询租户用户失败: %v", err)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// GetUserInfo 根据用户ID或用户名获取用户
|
||||
func GetUserInfo(userId int, username string, tenantId int) (*User, error) {
|
||||
o := orm.NewOrm()
|
||||
user := &User{}
|
||||
var err error
|
||||
|
||||
if userId > 0 {
|
||||
// 按ID查询
|
||||
user.Id = userId
|
||||
err = o.Read(user)
|
||||
if err == orm.ErrNoRows {
|
||||
return nil, errors.New("用户不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// 按用户名和租户ID查询
|
||||
err = o.Raw("SELECT * FROM yz_users WHERE username = ? AND tenant_id = ?", username, tenantId).QueryRow(user)
|
||||
if err == orm.ErrNoRows {
|
||||
return nil, errors.New("用户不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// ValidateUser 验证用户登录信息(先检查用户表,找不到再检查员工表)
|
||||
func ValidateUser(username, password string, tenantName string) (*User, *Employee, error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 1. 根据租户名称查询租户(只查询未删除的)
|
||||
var tenant struct {
|
||||
Id int
|
||||
Status string
|
||||
DeleteTime interface{} // 使用 interface{} 来处理 NULL 值
|
||||
}
|
||||
err := o.Raw("SELECT id, status, delete_time FROM yz_tenants WHERE name = ? AND delete_time IS NULL", tenantName).QueryRow(&tenant)
|
||||
if err == orm.ErrNoRows {
|
||||
// 租户不存在(数据库中根本没有这个名称)
|
||||
return nil, nil, errors.New("租户不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("查询租户失败: %v", err)
|
||||
}
|
||||
|
||||
// 检查租户状态
|
||||
if tenant.Status == "disabled" {
|
||||
return nil, nil, errors.New("租户已被禁用")
|
||||
}
|
||||
|
||||
if tenant.Status != "enabled" {
|
||||
return nil, nil, fmt.Errorf("租户状态异常: %s", tenant.Status)
|
||||
}
|
||||
|
||||
tenantId := tenant.Id
|
||||
|
||||
// 2. 先尝试从用户表获取
|
||||
user, err := GetUserInfo(0, username, tenantId)
|
||||
if err == nil && user != nil {
|
||||
// 用户存在,验证密码
|
||||
if verifyPassword(password, user.Salt, user.Password) {
|
||||
return user, nil, nil
|
||||
}
|
||||
return nil, nil, errors.New("密码不正确")
|
||||
}
|
||||
|
||||
// 3. 用户表中没有找到,尝试从员工表获取
|
||||
employee, err := ValidateEmployee(username, password, tenantId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 员工验证成功,返回员工信息(user为nil表示是员工登录)
|
||||
return nil, employee, nil
|
||||
}
|
||||
|
||||
// AddUser 向数据库添加新用户
|
||||
func AddUser(username, password, email, nickname, avatar string, tenantId, role, departmentId, positionId int) (*User, error) {
|
||||
// 1. 验证租户是否存在且有效
|
||||
o := orm.NewOrm()
|
||||
var tenantExists bool
|
||||
err := o.Raw("SELECT EXISTS(SELECT 1 FROM yz_tenants WHERE id = ? AND delete_time IS NULL AND status = 'enabled')", tenantId).QueryRow(&tenantExists)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("验证租户失败: %v", err)
|
||||
}
|
||||
if !tenantExists {
|
||||
return nil, fmt.Errorf("租户不存在或已被禁用")
|
||||
}
|
||||
|
||||
// 2. 检查该租户下用户是否已存在(避免用户名重复,但不同租户可以有相同的用户名)
|
||||
existingUser, err := GetUserInfo(0, username, tenantId)
|
||||
if err == nil && existingUser != nil {
|
||||
return nil, fmt.Errorf("该租户下用户名已存在")
|
||||
}
|
||||
if err != nil && err.Error() != "用户不存在" { // 排除"用户不存在"的正常错误
|
||||
return nil, fmt.Errorf("查询用户失败: %v", err)
|
||||
}
|
||||
|
||||
// 2. 生成盐值(每个用户唯一)
|
||||
salt, err := generateSalt()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("生成盐值失败: %v", err)
|
||||
}
|
||||
|
||||
// 3. 加密密码(结合盐值)
|
||||
hashedPassword, err := hashPassword(password, salt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("密码加密失败: %v", err)
|
||||
}
|
||||
|
||||
// 4. 构建用户对象
|
||||
user := &User{
|
||||
TenantId: tenantId,
|
||||
Username: username,
|
||||
Password: hashedPassword,
|
||||
Salt: salt,
|
||||
Email: email,
|
||||
Nickname: nickname,
|
||||
Avatar: avatar,
|
||||
Role: role,
|
||||
DepartmentId: departmentId,
|
||||
PositionId: positionId,
|
||||
Status: 1,
|
||||
}
|
||||
|
||||
// 5. 插入数据库(使用之前定义的 o)
|
||||
_, err = o.Insert(user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("数据库插入失败: %v", err)
|
||||
}
|
||||
|
||||
// 6. 返回新创建的用户对象
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// EditUser 更新用户信息
|
||||
func EditUser(id int, username, email, nickname, avatar, status string, roleId, departmentId, positionId int) (*User, error) {
|
||||
// 根据ID查询用户
|
||||
o := orm.NewOrm()
|
||||
user := &User{}
|
||||
err := o.Raw("SELECT * FROM yz_users WHERE id = ?", id).QueryRow(user)
|
||||
if err == orm.ErrNoRows {
|
||||
return nil, fmt.Errorf("用户不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询用户失败: %v", err)
|
||||
}
|
||||
|
||||
// 仅更新非空字段(避免覆盖原有值)
|
||||
if username != "" {
|
||||
// 若更新用户名,需检查同一租户下新用户名是否已被占用
|
||||
existingUser, _ := GetUserInfo(0, username, user.TenantId)
|
||||
if existingUser != nil && existingUser.Id != id {
|
||||
return nil, fmt.Errorf("该租户下用户名已被占用")
|
||||
}
|
||||
user.Username = username
|
||||
}
|
||||
if email != "" {
|
||||
user.Email = email
|
||||
}
|
||||
if nickname != "" {
|
||||
user.Nickname = nickname
|
||||
}
|
||||
if avatar != "" {
|
||||
user.Avatar = avatar
|
||||
}
|
||||
|
||||
// 更新状态(将字符串转换为数字)
|
||||
if status != "" {
|
||||
if status == "active" {
|
||||
user.Status = 1
|
||||
} else if status == "inactive" {
|
||||
user.Status = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 更新角色ID
|
||||
if roleId > 0 {
|
||||
user.Role = roleId
|
||||
}
|
||||
|
||||
// 更新部门ID
|
||||
if departmentId >= 0 {
|
||||
user.DepartmentId = departmentId
|
||||
}
|
||||
|
||||
// 更新职位ID
|
||||
if positionId >= 0 {
|
||||
user.PositionId = positionId
|
||||
}
|
||||
|
||||
// 执行数据库更新
|
||||
_, err = o.Update(user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("数据库更新失败: %v", err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// DeleteUser 根据ID进行软删除
|
||||
func DeleteUser(id int) error {
|
||||
o := orm.NewOrm()
|
||||
user := &User{}
|
||||
err := o.Raw("SELECT * FROM yz_users WHERE id = ?", id).QueryRow(user)
|
||||
if err == orm.ErrNoRows {
|
||||
return fmt.Errorf("用户不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("查询用户失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置删除时间为当前时间(软删除)
|
||||
now := time.Now()
|
||||
user.DeleteTime = &now
|
||||
_, err = o.Update(user, "DeleteTime")
|
||||
if err != nil {
|
||||
return fmt.Errorf("设置删除时间失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Init 初始化数据库
|
||||
// Init 初始化数据库(注册所有模型和连接数据库)
|
||||
func Init(version string) {
|
||||
orm.RegisterModel(new(User))
|
||||
orm.RegisterModel(new(Menu))
|
||||
@@ -411,13 +54,13 @@ func Init(version string) {
|
||||
user, err1 := beego.AppConfig.String("mysqluser")
|
||||
pass, err2 := beego.AppConfig.String("mysqlpass")
|
||||
urls, err3 := beego.AppConfig.String("mysqlurls")
|
||||
db, err4 := beego.AppConfig.String("mysqldb")
|
||||
dbName, err4 := beego.AppConfig.String("mysqldb")
|
||||
if err1 != nil || err2 != nil || err3 != nil || err4 != nil {
|
||||
panic("数据库配置错误")
|
||||
}
|
||||
|
||||
// 构建连接字符串
|
||||
dsn := user + ":" + pass + "@tcp(" + urls + ")/" + db + "?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
// 构建连接字符串,添加连接池和性能优化参数
|
||||
dsn := user + ":" + pass + "@tcp(" + urls + ")/" + dbName + "?charset=utf8mb4&parseTime=True&loc=Local&timeout=10s&readTimeout=30s&writeTimeout=30s"
|
||||
fmt.Println("数据库连接字符串:", dsn)
|
||||
|
||||
// 注册数据库
|
||||
@@ -426,10 +69,19 @@ func Init(version string) {
|
||||
panic("数据库连接失败: " + err.Error())
|
||||
}
|
||||
|
||||
// 测试连接
|
||||
// 注意:Beego v2 中不需要显式调用 Using,默认使用 "default"
|
||||
// 配置数据库连接池(关键性能优化)
|
||||
dbConn, err := orm.GetDB("default")
|
||||
if err != nil {
|
||||
panic("获取数据库连接失败: " + err.Error())
|
||||
}
|
||||
|
||||
// 设置连接池参数
|
||||
dbConn.SetMaxIdleConns(10) // 设置空闲连接池中连接的最大数量
|
||||
dbConn.SetMaxOpenConns(100) // 设置打开数据库连接的最大数量
|
||||
dbConn.SetConnMaxLifetime(time.Hour) // 设置连接可复用的最大时间
|
||||
|
||||
fmt.Println("数据库连接成功!")
|
||||
fmt.Printf("当前项目版本: %s\n", version)
|
||||
fmt.Println("数据库连接池配置: MaxIdleConns=10, MaxOpenConns=100, ConnMaxLifetime=1h")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user