批量修复,增加组织架构

This commit is contained in:
2025-11-04 17:35:01 +08:00
parent 86c4060843
commit a7bde8bb72
29 changed files with 5200 additions and 88 deletions
+59 -12
View File
@@ -62,8 +62,8 @@ func (c *AuthController) Login() {
return
}
// 验证用户(先验证租户,再验证租户下的用户
user, err := models.ValidateUser(username, password, tenantName)
// 验证用户(先检查用户表,找不到再检查员工表
user, employee, err := models.ValidateUser(username, password, tenantName)
if err != nil {
// 登录失败
@@ -72,8 +72,54 @@ func (c *AuthController) Login() {
"message": err.Error(),
}
} else {
var tokenString string
var userId int
var usernameForToken string
var tenantId int
var userInfo map[string]interface{}
// 判断是用户登录还是员工登录
if user != nil {
// 用户登录
userId = user.Id
usernameForToken = user.Username
tenantId = user.TenantId
userInfo = map[string]interface{}{
"id": user.Id,
"username": user.Username,
"email": user.Email,
"avatar": user.Avatar,
"nickname": user.Nickname,
"tenant_id": user.TenantId,
"type": "user", // 标识是用户登录
}
} else if employee != nil {
// 员工登录
userId = employee.Id
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,
"department_id": employee.DepartmentId,
"position_id": employee.PositionId,
"type": "employee", // 标识是员工登录
}
} else {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "登录验证失败",
}
c.ServeJSON()
return
}
// 使用models包中的GenerateToken函数生成token
tokenString, err := models.GenerateToken(user.Id, user.Username, user.TenantId)
tokenString, err = models.GenerateToken(userId, usernameForToken, tenantId)
if err != nil {
c.Data["json"] = map[string]interface{}{
@@ -125,7 +171,15 @@ func (c *AuthController) Login() {
}
o := orm.NewOrm()
_, _ = o.Raw("UPDATE yz_users SET last_login_time = ?, last_login_ip = ?, login_count = IFNULL(login_count,0)+1 WHERE id = ?", loginTime, clientIP, user.Id).Exec()
// 更新登录信息
if user != nil {
// 更新用户表
_, _ = o.Raw("UPDATE yz_users SET last_login_time = ?, last_login_ip = ?, login_count = IFNULL(login_count,0)+1 WHERE id = ?", loginTime, clientIP, user.Id).Exec()
} else if employee != nil {
// 更新员工表(如果员工表有last_login_time和last_login_ip字段)
// 注意:如果员工表没有这些字段,需要先添加字段
_, _ = o.Raw("UPDATE yz_tenant_employees SET last_login_time = ?, last_login_ip = ? WHERE id = ?", loginTime, clientIP, employee.Id).Exec()
}
c.Data["json"] = map[string]interface{}{
"code": 0,
@@ -133,14 +187,7 @@ func (c *AuthController) Login() {
"data": map[string]interface{}{
"accessToken": tokenString,
"token": tokenString, // 兼容性
"user": map[string]interface{}{
"id": user.Id,
"username": user.Username,
"email": user.Email,
"avatar": user.Avatar,
"nickname": user.Nickname,
"tenant_id": user.TenantId,
},
"user": userInfo,
},
}
}
+270
View File
@@ -0,0 +1,270 @@
package controllers
import (
"encoding/json"
"server/models"
beego "github.com/beego/beego/v2/server/web"
)
// DepartmentController 部门控制器
type DepartmentController struct {
beego.Controller
}
// GetTenantDepartments 获取租户下的所有部门
// @router /departments/tenant/:tenantId [get]
func (c *DepartmentController) GetTenantDepartments() {
tenantId, err := c.GetInt(":tenantId")
if err != nil || tenantId <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "租户ID无效",
"data": nil,
}
c.ServeJSON()
return
}
departments, err := models.GetTenantDepartments(tenantId)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "获取部门列表失败: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
// 格式化返回数据
deptList := make([]map[string]interface{}, 0)
for _, dept := range departments {
deptList = append(deptList, map[string]interface{}{
"id": dept.Id,
"tenant_id": dept.TenantId,
"name": dept.Name,
"code": dept.Code,
"parent_id": dept.ParentId,
"description": dept.Description,
"manager_id": dept.ManagerId,
"sort_order": dept.SortOrder,
"status": dept.Status,
})
}
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "获取部门列表成功",
"data": deptList,
}
c.ServeJSON()
}
// GetDepartmentInfo 获取部门详情
// @router /departments/:id [get]
func (c *DepartmentController) GetDepartmentInfo() {
id, err := c.GetInt(":id")
if err != nil || id <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "部门ID无效",
"data": nil,
}
c.ServeJSON()
return
}
department, err := models.GetDepartmentById(id)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "部门不存在",
"data": nil,
}
c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "获取部门信息成功",
"data": map[string]interface{}{
"id": department.Id,
"tenant_id": department.TenantId,
"name": department.Name,
"code": department.Code,
"parent_id": department.ParentId,
"description": department.Description,
"manager_id": department.ManagerId,
"sort_order": department.SortOrder,
"status": department.Status,
},
}
c.ServeJSON()
}
// AddDepartment 添加部门
// @router /departments [post]
func (c *DepartmentController) AddDepartment() {
var deptData struct {
TenantId int `json:"tenant_id"`
Name string `json:"name"`
Code string `json:"code"`
ParentId int `json:"parent_id"`
Description string `json:"description"`
ManagerId int `json:"manager_id"`
SortOrder int `json:"sort_order"`
Status int8 `json:"status"`
}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &deptData)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "请求参数错误: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
department := &models.Department{
TenantId: deptData.TenantId,
Name: deptData.Name,
Code: deptData.Code,
ParentId: deptData.ParentId,
Description: deptData.Description,
ManagerId: deptData.ManagerId,
SortOrder: deptData.SortOrder,
Status: deptData.Status,
}
if department.TenantId == 0 {
department.TenantId = 1
}
id, err := models.AddDepartment(department)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "添加部门失败: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
department.Id = int(id)
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "添加部门成功",
"data": department,
}
c.ServeJSON()
}
// UpdateDepartment 更新部门信息
// @router /departments/:id [put]
func (c *DepartmentController) UpdateDepartment() {
id, err := c.GetInt(":id")
if err != nil || id <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "部门ID无效",
"data": nil,
}
c.ServeJSON()
return
}
var updateData struct {
Name string `json:"name"`
Code string `json:"code"`
ParentId int `json:"parent_id"`
Description string `json:"description"`
ManagerId int `json:"manager_id"`
SortOrder int `json:"sort_order"`
Status int8 `json:"status"`
}
err = json.Unmarshal(c.Ctx.Input.RequestBody, &updateData)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "请求参数错误: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
department, err := models.GetDepartmentById(id)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "部门不存在",
"data": nil,
}
c.ServeJSON()
return
}
department.Name = updateData.Name
department.Code = updateData.Code
department.ParentId = updateData.ParentId
department.Description = updateData.Description
department.ManagerId = updateData.ManagerId
department.SortOrder = updateData.SortOrder
department.Status = updateData.Status
if err := models.UpdateDepartment(department); 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": "更新部门信息成功",
"data": department,
}
c.ServeJSON()
}
// DeleteDepartment 删除部门
// @router /departments/:id [delete]
func (c *DepartmentController) DeleteDepartment() {
id, err := c.GetInt(":id")
if err != nil || id <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "部门ID无效",
"data": nil,
}
c.ServeJSON()
return
}
if err := models.DeleteDepartment(id); 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": "删除部门成功",
"data": nil,
}
c.ServeJSON()
}
+403
View File
@@ -0,0 +1,403 @@
package controllers
import (
"encoding/json"
"server/models"
beego "github.com/beego/beego/v2/server/web"
)
// EmployeeController 员工控制器
type EmployeeController struct {
beego.Controller
}
// GetAllEmployees 获取所有员工(可选,用于管理员查看所有员工)
// @router /employees [get]
func (c *EmployeeController) GetAllEmployees() {
employees, err := models.GetAllEmployees()
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "获取员工列表失败: " + err.Error(),
"data": nil,
}
} else {
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "获取员工列表成功",
"data": employees,
}
}
c.ServeJSON()
}
// GetTenantEmployees 获取租户下的所有员工
// @router /employees/tenant/:tenantId [get]
func (c *EmployeeController) GetTenantEmployees() {
tenantId, err := c.GetInt(":tenantId")
if err != nil || tenantId <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "租户ID无效",
"data": nil,
}
c.ServeJSON()
return
}
employees, err := models.GetTenantEmployees(tenantId)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "获取员工列表失败: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
// 格式化返回数据
employeeList := make([]map[string]interface{}, 0)
for _, emp := range employees {
employeeList = append(employeeList, map[string]interface{}{
"id": emp.Id,
"tenant_id": emp.TenantId,
"employee_no": emp.EmployeeNo,
"name": emp.Name,
"phone": emp.Phone,
"email": emp.Email,
"department_id": emp.DepartmentId,
"position_id": emp.PositionId,
"bank_name": emp.BankName,
"bank_account": emp.BankAccount,
"status": emp.Status,
"create_time": emp.CreateTime,
})
}
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "获取员工列表成功",
"data": employeeList,
}
c.ServeJSON()
}
// GetEmployeeInfo 获取员工详情
// @router /employees/:id [get]
func (c *EmployeeController) GetEmployeeInfo() {
id, err := c.GetInt(":id")
if err != nil || id <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "员工ID无效",
"data": nil,
}
c.ServeJSON()
return
}
employee, err := models.GetEmployeeById(id)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "员工不存在",
"data": nil,
}
c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "获取员工信息成功",
"data": map[string]interface{}{
"id": employee.Id,
"tenant_id": employee.TenantId,
"employee_no": employee.EmployeeNo,
"name": employee.Name,
"phone": employee.Phone,
"email": employee.Email,
"department_id": employee.DepartmentId,
"position_id": employee.PositionId,
"bank_name": employee.BankName,
"bank_account": employee.BankAccount,
"status": employee.Status,
"create_time": employee.CreateTime,
},
}
c.ServeJSON()
}
// AddEmployee 添加员工
// @router /employees [post]
func (c *EmployeeController) AddEmployee() {
var employeeData struct {
TenantId int `json:"tenant_id"`
EmployeeNo string `json:"employee_no"`
Name string `json:"name"`
Phone string `json:"phone"`
Email string `json:"email"`
DepartmentId int `json:"department_id"`
PositionId int `json:"position_id"`
BankName string `json:"bank_name"`
BankAccount string `json:"bank_account"`
Status int8 `json:"status"`
}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &employeeData)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "请求参数错误: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
employee := &models.Employee{
TenantId: employeeData.TenantId,
EmployeeNo: employeeData.EmployeeNo,
Name: employeeData.Name,
Phone: employeeData.Phone,
Email: employeeData.Email,
DepartmentId: employeeData.DepartmentId,
PositionId: employeeData.PositionId,
BankName: employeeData.BankName,
BankAccount: employeeData.BankAccount,
Status: employeeData.Status,
}
// 如果没有指定租户ID,从当前登录用户获取(需要JWT中间件支持)
// 这里暂时使用传入的tenant_id,如果为0则使用默认值
if employee.TenantId == 0 {
// 可以从JWT token中获取租户ID,这里暂时设为1作为默认值
employee.TenantId = 1
}
// 默认密码
defaultPassword := "yunzer123"
id, err := models.AddEmployee(employee, defaultPassword)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "添加员工失败: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
employee.Id = int(id)
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "添加员工成功",
"data": employee,
}
c.ServeJSON()
}
// UpdateEmployee 更新员工信息
// @router /employees/:id [put]
func (c *EmployeeController) UpdateEmployee() {
id, err := c.GetInt(":id")
if err != nil || id <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "员工ID无效",
"data": nil,
}
c.ServeJSON()
return
}
var updateData struct {
EmployeeNo string `json:"employee_no"`
Name string `json:"name"`
Phone string `json:"phone"`
Email string `json:"email"`
DepartmentId int `json:"department_id"`
PositionId int `json:"position_id"`
BankName string `json:"bank_name"`
BankAccount string `json:"bank_account"`
Status int8 `json:"status"`
}
err = json.Unmarshal(c.Ctx.Input.RequestBody, &updateData)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "请求参数错误: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
employee, err := models.GetEmployeeById(id)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "员工不存在",
"data": nil,
}
c.ServeJSON()
return
}
// 更新字段
employee.EmployeeNo = updateData.EmployeeNo
employee.Name = updateData.Name
employee.Phone = updateData.Phone
employee.Email = updateData.Email
employee.DepartmentId = updateData.DepartmentId
employee.PositionId = updateData.PositionId
employee.BankName = updateData.BankName
employee.BankAccount = updateData.BankAccount
employee.Status = updateData.Status
if err := models.UpdateEmployee(employee); 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": "更新员工信息成功",
"data": employee,
}
c.ServeJSON()
}
// DeleteEmployee 删除员工
// @router /employees/:id [delete]
func (c *EmployeeController) DeleteEmployee() {
id, err := c.GetInt(":id")
if err != nil || id <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "员工ID无效",
"data": nil,
}
c.ServeJSON()
return
}
if err := models.DeleteEmployee(id); 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": "删除员工成功",
"data": nil,
}
c.ServeJSON()
}
// ResetEmployeePassword 重置员工密码
// @router /employees/:id/reset-password [post]
func (c *EmployeeController) ResetEmployeePassword() {
id, err := c.GetInt(":id")
if err != nil || id <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "员工ID无效",
"data": nil,
}
c.ServeJSON()
return
}
// 默认密码
defaultPassword := "yunzer123"
if err := models.ResetEmployeePassword(id, defaultPassword); 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": "密码重置成功,新密码为: " + defaultPassword,
"data": nil,
}
c.ServeJSON()
}
// ChangeEmployeePassword 修改员工密码
// @router /employees/:id/change-password [post]
func (c *EmployeeController) ChangeEmployeePassword() {
id, err := c.GetInt(":id")
if err != nil || id <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "员工ID无效",
"data": nil,
}
c.ServeJSON()
return
}
var passwordData struct {
OldPassword string `json:"old_password"`
NewPassword string `json:"new_password"`
}
err = json.Unmarshal(c.Ctx.Input.RequestBody, &passwordData)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "请求参数错误: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
if passwordData.NewPassword == "" {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "新密码不能为空",
"data": nil,
}
c.ServeJSON()
return
}
if err := models.ChangeEmployeePassword(id, passwordData.OldPassword, passwordData.NewPassword); 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": "密码修改成功",
"data": nil,
}
c.ServeJSON()
}
+319
View File
@@ -0,0 +1,319 @@
package controllers
import (
"encoding/json"
"server/models"
beego "github.com/beego/beego/v2/server/web"
)
// PositionController 职位控制器
type PositionController struct {
beego.Controller
}
// GetTenantPositions 获取租户下的所有职位
// @router /positions/tenant/:tenantId [get]
func (c *PositionController) GetTenantPositions() {
tenantId, err := c.GetInt(":tenantId")
if err != nil || tenantId <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "租户ID无效",
"data": nil,
}
c.ServeJSON()
return
}
positions, err := models.GetTenantPositions(tenantId)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "获取职位列表失败: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
// 格式化返回数据
posList := make([]map[string]interface{}, 0)
for _, pos := range positions {
posList = append(posList, map[string]interface{}{
"id": pos.Id,
"tenant_id": pos.TenantId,
"name": pos.Name,
"code": pos.Code,
"department_id": pos.DepartmentId,
"level": pos.Level,
"description": pos.Description,
"sort_order": pos.SortOrder,
"status": pos.Status,
})
}
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "获取职位列表成功",
"data": posList,
}
c.ServeJSON()
}
// GetPositionsByDepartment 根据部门ID获取职位列表
// @router /positions/department/:departmentId [get]
func (c *PositionController) GetPositionsByDepartment() {
departmentId, err := c.GetInt(":departmentId")
if err != nil || departmentId <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "部门ID无效",
"data": nil,
}
c.ServeJSON()
return
}
positions, err := models.GetPositionsByDepartment(departmentId)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "获取职位列表失败: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
// 格式化返回数据
posList := make([]map[string]interface{}, 0)
for _, pos := range positions {
posList = append(posList, map[string]interface{}{
"id": pos.Id,
"tenant_id": pos.TenantId,
"name": pos.Name,
"code": pos.Code,
"department_id": pos.DepartmentId,
"level": pos.Level,
"description": pos.Description,
"sort_order": pos.SortOrder,
"status": pos.Status,
})
}
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "获取职位列表成功",
"data": posList,
}
c.ServeJSON()
}
// GetPositionInfo 获取职位详情
// @router /positions/:id [get]
func (c *PositionController) GetPositionInfo() {
id, err := c.GetInt(":id")
if err != nil || id <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "职位ID无效",
"data": nil,
}
c.ServeJSON()
return
}
position, err := models.GetPositionById(id)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "职位不存在",
"data": nil,
}
c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "获取职位信息成功",
"data": map[string]interface{}{
"id": position.Id,
"tenant_id": position.TenantId,
"name": position.Name,
"code": position.Code,
"department_id": position.DepartmentId,
"level": position.Level,
"description": position.Description,
"sort_order": position.SortOrder,
"status": position.Status,
},
}
c.ServeJSON()
}
// AddPosition 添加职位
// @router /positions [post]
func (c *PositionController) AddPosition() {
var posData struct {
TenantId int `json:"tenant_id"`
Name string `json:"name"`
Code string `json:"code"`
DepartmentId int `json:"department_id"`
Level int `json:"level"`
Description string `json:"description"`
SortOrder int `json:"sort_order"`
Status int8 `json:"status"`
}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &posData)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "请求参数错误: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
position := &models.Position{
TenantId: posData.TenantId,
Name: posData.Name,
Code: posData.Code,
DepartmentId: posData.DepartmentId,
Level: posData.Level,
Description: posData.Description,
SortOrder: posData.SortOrder,
Status: posData.Status,
}
if position.TenantId == 0 {
position.TenantId = 1
}
id, err := models.AddPosition(position)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "添加职位失败: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
position.Id = int(id)
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "添加职位成功",
"data": position,
}
c.ServeJSON()
}
// UpdatePosition 更新职位信息
// @router /positions/:id [put]
func (c *PositionController) UpdatePosition() {
id, err := c.GetInt(":id")
if err != nil || id <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "职位ID无效",
"data": nil,
}
c.ServeJSON()
return
}
var updateData struct {
Name string `json:"name"`
Code string `json:"code"`
DepartmentId int `json:"department_id"`
Level int `json:"level"`
Description string `json:"description"`
SortOrder int `json:"sort_order"`
Status int8 `json:"status"`
}
err = json.Unmarshal(c.Ctx.Input.RequestBody, &updateData)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "请求参数错误: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
position, err := models.GetPositionById(id)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "职位不存在",
"data": nil,
}
c.ServeJSON()
return
}
position.Name = updateData.Name
position.Code = updateData.Code
position.DepartmentId = updateData.DepartmentId
position.Level = updateData.Level
position.Description = updateData.Description
position.SortOrder = updateData.SortOrder
position.Status = updateData.Status
if err := models.UpdatePosition(position); 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": "更新职位信息成功",
"data": position,
}
c.ServeJSON()
}
// DeletePosition 删除职位
// @router /positions/:id [delete]
func (c *PositionController) DeletePosition() {
id, err := c.GetInt(":id")
if err != nil || id <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "职位ID无效",
"data": nil,
}
c.ServeJSON()
return
}
if err := models.DeletePosition(id); 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": "删除职位成功",
"data": nil,
}
c.ServeJSON()
}
+24 -16
View File
@@ -208,13 +208,15 @@ func (c *UserController) GetUserInfo() {
func (c *UserController) AddUser() {
// 定义接收用户数据的结构体
var userData struct {
Username string `json:"username"`
Password string `json:"password"`
Email string `json:"email"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
TenantId int `json:"tenant_id"`
Role int `json:"role"` // 角色ID
Username string `json:"username"`
Password string `json:"password"`
Email string `json:"email"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
TenantId int `json:"tenant_id"`
Role int `json:"role"` // 角色ID
DepartmentId int `json:"department_id"`
PositionId int `json:"position_id"`
}
// 解析请求体JSON数据
@@ -266,7 +268,9 @@ func (c *UserController) AddUser() {
userData.Nickname,
userData.Avatar,
userData.TenantId,
userData.Role, // 添加 role 参数
userData.Role,
userData.DepartmentId,
userData.PositionId,
)
if err != nil {
c.Data["json"] = map[string]interface{}{
@@ -295,13 +299,15 @@ func (c *UserController) AddUser() {
func (c *UserController) EditUser() {
// 定义接收更新数据的结构体
var updateData struct {
Id int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Status string `json:"status"`
Role int `json:"role"` // 改为 role,存储角色ID
Id int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Status string `json:"status"`
Role int `json:"role"` // 角色ID
DepartmentId int `json:"department_id"`
PositionId int `json:"position_id"`
}
// 解析请求体JSON
@@ -335,7 +341,9 @@ func (c *UserController) EditUser() {
updateData.Nickname,
updateData.Avatar,
updateData.Status,
updateData.Role, // 改为 Role
updateData.Role,
updateData.DepartmentId,
updateData.PositionId,
)
if err != nil {
c.Data["json"] = map[string]interface{}{
+108
View File
@@ -0,0 +1,108 @@
-- 为员工表添加工资卡信息和密码字段
-- 创建时间: 2025
SET @dbname = DATABASE();
SET @tablename = 'yz_tenant_employees';
-- 添加工资卡开户行字段
SET @columnname = 'bank_name';
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
'SELECT "Column bank_name already exists in yz_tenant_employees" AS "";',
'ALTER TABLE yz_tenant_employees ADD COLUMN bank_name VARCHAR(100) DEFAULT NULL COMMENT ''工资卡开户行'' AFTER position_id;'
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 添加工资卡卡号字段
SET @columnname = 'bank_account';
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
'SELECT "Column bank_account already exists in yz_tenant_employees" AS "";',
'ALTER TABLE yz_tenant_employees ADD COLUMN bank_account VARCHAR(50) DEFAULT NULL COMMENT ''工资卡卡号'' AFTER bank_name;'
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 添加登录密码字段
SET @columnname = 'password';
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
'SELECT "Column password already exists in yz_tenant_employees" AS "";',
'ALTER TABLE yz_tenant_employees ADD COLUMN password VARCHAR(255) DEFAULT NULL COMMENT ''登录密码(加密后)'' AFTER bank_account;'
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 添加盐值字段
SET @columnname = 'salt';
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
'SELECT "Column salt already exists in yz_tenant_employees" AS "";',
'ALTER TABLE yz_tenant_employees ADD COLUMN salt VARCHAR(100) DEFAULT NULL COMMENT ''密码盐值'' AFTER password;'
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 添加最后登录时间字段
SET @columnname = 'last_login_time';
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
'SELECT "Column last_login_time already exists in yz_tenant_employees" AS "";',
'ALTER TABLE yz_tenant_employees ADD COLUMN last_login_time DATETIME DEFAULT NULL COMMENT ''最后登录时间'' AFTER salt;'
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 添加最后登录IP字段
SET @columnname = 'last_login_ip';
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
'SELECT "Column last_login_ip already exists in yz_tenant_employees" AS "";',
'ALTER TABLE yz_tenant_employees ADD COLUMN last_login_ip VARCHAR(50) DEFAULT NULL COMMENT ''最后登录IP'' AFTER last_login_time;'
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
+38
View File
@@ -0,0 +1,38 @@
-- 为菜单表添加 delete_time 字段(软删除)
-- 如果字段已存在,则不会重复添加
SET @dbname = DATABASE();
SET @tablename = 'yz_menus';
SET @columnname = 'delete_time';
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
'SELECT "Column delete_time already exists in yz_menus" AS "";',
'ALTER TABLE yz_menus ADD COLUMN delete_time DATETIME DEFAULT NULL COMMENT ''删除时间(软删除)'' AFTER update_time;'
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 添加索引以优化查询性能
SET @indexname = 'idx_delete_time';
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (INDEX_NAME = @indexname)
) > 0,
'SELECT "Index idx_delete_time already exists in yz_menus" AS "";',
'ALTER TABLE yz_menus ADD INDEX idx_delete_time (delete_time);'
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
+15
View File
@@ -0,0 +1,15 @@
-- 添加组织架构管理菜单
-- 注意:请根据实际情况修改 parent_id(92 是 OA 模块的 ID,请确认)
INSERT INTO yz_menus (name, path, parent_id, icon, `order`, status, component_path, menu_type, description)
VALUES ('组织架构', '/apps/oa/organization', 92, 'fa-solid fa-sitemap', 1, 1, '@/views/apps/oa/organization/index.vue', 1, '组织架构管理(部门与职位)')
ON DUPLICATE KEY UPDATE
name = VALUES(name),
path = VALUES(path),
icon = VALUES(icon),
`order` = VALUES(`order`),
status = VALUES(status),
component_path = VALUES(component_path),
menu_type = VALUES(menu_type),
description = VALUES(description);
+163
View File
@@ -0,0 +1,163 @@
-- 创建OA模块相关表(租户应用)
-- 创建时间: 2025
-- 描述: 创建租户部门表、租户职位表和租户员工表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- =============================================
-- 1. 创建租户部门表
-- =============================================
CREATE TABLE IF NOT EXISTS yz_tenant_departments (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '部门ID',
tenant_id INT NOT NULL DEFAULT 0 COMMENT '租户ID',
name VARCHAR(100) NOT NULL COMMENT '部门名称',
code VARCHAR(50) DEFAULT NULL COMMENT '部门编码',
parent_id INT DEFAULT 0 COMMENT '父部门ID0表示顶级部门',
description TEXT DEFAULT NULL COMMENT '部门描述',
manager_id INT DEFAULT NULL COMMENT '部门经理ID',
sort_order INT DEFAULT 0 COMMENT '排序序号',
status TINYINT DEFAULT 1 COMMENT '状态:1-启用,0-禁用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
delete_time DATETIME DEFAULT NULL COMMENT '删除时间(软删除)',
-- 索引
INDEX idx_tenant_id (tenant_id),
INDEX idx_code (code),
INDEX idx_parent_id (parent_id),
INDEX idx_status (status),
INDEX idx_sort_order (sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户部门表';
-- =============================================
-- 2. 创建租户职位表
-- =============================================
CREATE TABLE IF NOT EXISTS yz_tenant_positions (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '职位ID',
tenant_id INT NOT NULL DEFAULT 0 COMMENT '租户ID',
name VARCHAR(100) NOT NULL COMMENT '职位名称',
code VARCHAR(50) DEFAULT NULL COMMENT '职位编码',
department_id INT DEFAULT NULL COMMENT '所属部门ID',
level INT DEFAULT 0 COMMENT '职位级别',
description TEXT DEFAULT NULL COMMENT '职位描述',
sort_order INT DEFAULT 0 COMMENT '排序序号',
status TINYINT DEFAULT 1 COMMENT '状态:1-启用,0-禁用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
delete_time DATETIME DEFAULT NULL COMMENT '删除时间(软删除)',
-- 索引
INDEX idx_tenant_id (tenant_id),
INDEX idx_code (code),
INDEX idx_department_id (department_id),
INDEX idx_status (status),
INDEX idx_sort_order (sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户职位表';
-- =============================================
-- 3. 创建租户员工表(如果不存在)
-- =============================================
CREATE TABLE IF NOT EXISTS yz_tenant_employees (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工ID',
tenant_id INT NOT NULL DEFAULT 0 COMMENT '租户ID',
employee_no VARCHAR(50) NOT NULL COMMENT '工号',
name VARCHAR(50) NOT NULL COMMENT '姓名',
phone VARCHAR(20) DEFAULT NULL COMMENT '手机号',
email VARCHAR(100) DEFAULT NULL COMMENT '邮箱',
department_id INT DEFAULT NULL COMMENT '部门ID',
position_id INT DEFAULT NULL COMMENT '职位ID',
status TINYINT DEFAULT 1 COMMENT '状态:1-在职,0-离职',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
delete_time DATETIME DEFAULT NULL COMMENT '删除时间(软删除)',
-- 索引
INDEX idx_tenant_id (tenant_id),
INDEX idx_employee_no (employee_no),
INDEX idx_name (name),
INDEX idx_department_id (department_id),
INDEX idx_position_id (position_id),
INDEX idx_status (status),
INDEX idx_create_time (create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户员工表';
-- =============================================
-- 4. 更新用户表,添加部门和职位字段(如果不存在)
-- =============================================
-- 检查并添加 department_id 字段
SET @dbname = DATABASE();
SET @tablename = 'yz_users';
SET @columnname = 'department_id';
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
'SELECT "Column department_id already exists in yz_users" AS "";',
'ALTER TABLE yz_users ADD COLUMN department_id INT DEFAULT NULL COMMENT ''部门ID'' AFTER role;'
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 检查并添加 position_id 字段
SET @columnname = 'position_id';
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
'SELECT "Column position_id already exists in yz_users" AS "";',
'ALTER TABLE yz_users ADD COLUMN position_id INT DEFAULT NULL COMMENT ''职位ID'' AFTER department_id;'
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- =============================================
-- 5. 更新租户员工表,添加部门和职位字段(如果不存在)
-- =============================================
-- 检查并添加 department_id 字段
SET @tablename = 'yz_tenant_employees';
SET @columnname = 'department_id';
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
'SELECT "Column department_id already exists in yz_employees" AS "";',
'ALTER TABLE yz_tenant_employees ADD COLUMN department_id INT DEFAULT NULL COMMENT ''部门ID'' AFTER email;'
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 检查并添加 position_id 字段
SET @columnname = 'position_id';
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
'SELECT "Column position_id already exists in yz_tenant_employees" AS "";',
'ALTER TABLE yz_tenant_employees ADD COLUMN position_id INT DEFAULT NULL COMMENT ''职位ID'' AFTER department_id;'
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
SET FOREIGN_KEY_CHECKS = 1;
+32
View File
@@ -0,0 +1,32 @@
-- 创建部门表
-- 创建时间: 2025
-- 描述: OA系统部门管理表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- 检查并创建租户部门表(如果不存在)
CREATE TABLE IF NOT EXISTS yz_tenant_departments (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '部门ID',
tenant_id INT NOT NULL DEFAULT 0 COMMENT '租户ID',
name VARCHAR(100) NOT NULL COMMENT '部门名称',
code VARCHAR(50) DEFAULT NULL COMMENT '部门编码',
parent_id INT DEFAULT 0 COMMENT '父部门ID0表示顶级部门',
description TEXT DEFAULT NULL COMMENT '部门描述',
manager_id INT DEFAULT NULL COMMENT '部门经理ID',
sort_order INT DEFAULT 0 COMMENT '排序序号',
status TINYINT DEFAULT 1 COMMENT '状态:1-启用,0-禁用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
delete_time DATETIME DEFAULT NULL COMMENT '删除时间(软删除)',
-- 索引
INDEX idx_tenant_id (tenant_id),
INDEX idx_code (code),
INDEX idx_parent_id (parent_id),
INDEX idx_status (status),
INDEX idx_sort_order (sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户部门表';
SET FOREIGN_KEY_CHECKS = 1;
+34
View File
@@ -0,0 +1,34 @@
-- 创建员工表
-- 创建时间: 2025
-- 描述: OA系统员工管理表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- 检查并创建租户员工表(如果不存在)
CREATE TABLE IF NOT EXISTS yz_tenant_employees (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工ID',
tenant_id INT NOT NULL DEFAULT 0 COMMENT '租户ID',
employee_no VARCHAR(50) NOT NULL COMMENT '工号',
name VARCHAR(50) NOT NULL COMMENT '姓名',
phone VARCHAR(20) DEFAULT NULL COMMENT '手机号',
email VARCHAR(100) DEFAULT NULL COMMENT '邮箱',
department_id INT DEFAULT NULL COMMENT '部门ID',
position_id INT DEFAULT NULL COMMENT '职位ID',
status TINYINT DEFAULT 1 COMMENT '状态:1-在职,0-离职',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
delete_time DATETIME DEFAULT NULL COMMENT '删除时间(软删除)',
-- 索引
INDEX idx_tenant_id (tenant_id),
INDEX idx_employee_no (employee_no),
INDEX idx_name (name),
INDEX idx_department_id (department_id),
INDEX idx_position_id (position_id),
INDEX idx_status (status),
INDEX idx_create_time (create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户员工表';
SET FOREIGN_KEY_CHECKS = 1;
+32
View File
@@ -0,0 +1,32 @@
-- 创建职位表
-- 创建时间: 2025
-- 描述: OA系统职位管理表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- 检查并创建租户职位表(如果不存在)
CREATE TABLE IF NOT EXISTS yz_tenant_positions (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '职位ID',
tenant_id INT NOT NULL DEFAULT 0 COMMENT '租户ID',
name VARCHAR(100) NOT NULL COMMENT '职位名称',
code VARCHAR(50) DEFAULT NULL COMMENT '职位编码',
department_id INT DEFAULT NULL COMMENT '所属部门ID',
level INT DEFAULT 0 COMMENT '职位级别',
description TEXT DEFAULT NULL COMMENT '职位描述',
sort_order INT DEFAULT 0 COMMENT '排序序号',
status TINYINT DEFAULT 1 COMMENT '状态:1-启用,0-禁用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
delete_time DATETIME DEFAULT NULL COMMENT '删除时间(软删除)',
-- 索引
INDEX idx_tenant_id (tenant_id),
INDEX idx_code (code),
INDEX idx_department_id (department_id),
INDEX idx_status (status),
INDEX idx_sort_order (sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户职位表';
SET FOREIGN_KEY_CHECKS = 1;
+87
View File
@@ -0,0 +1,87 @@
package models
import (
"time"
"github.com/beego/beego/v2/client/orm"
)
// Department 部门模型
type Department struct {
Id int `orm:"auto" json:"id"`
TenantId int `orm:"column(tenant_id);default(0)" json:"tenant_id"`
Name string `orm:"size(100)" json:"name"`
Code string `orm:"size(50);null" json:"code"`
ParentId int `orm:"column(parent_id);default(0)" json:"parent_id"`
Description string `orm:"type(text);null" json:"description"`
ManagerId int `orm:"column(manager_id);null" json:"manager_id"`
SortOrder int `orm:"column(sort_order);default(0)" json:"sort_order"`
Status int8 `orm:"default(1)" json:"status"` // 1-启用,0-禁用
CreateTime time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
UpdateTime time.Time `orm:"column(update_time);type(datetime);auto_now" json:"update_time"`
DeleteTime *time.Time `orm:"column(delete_time);null;type(datetime)" json:"delete_time,omitempty"`
}
// TableName 设置表名
func (d *Department) TableName() string {
return "yz_tenant_departments"
}
func init() {
orm.RegisterModel(new(Department))
}
// GetTenantDepartments 获取租户下的所有部门
func GetTenantDepartments(tenantId int) ([]*Department, error) {
o := orm.NewOrm()
var departments []*Department
_, err := o.QueryTable("yz_tenant_departments").
Filter("tenant_id", tenantId).
Filter("delete_time__isnull", true).
OrderBy("sort_order", "create_time").
All(&departments)
return departments, err
}
// GetDepartmentById 根据ID获取部门信息
func GetDepartmentById(id int) (*Department, error) {
o := orm.NewOrm()
department := &Department{Id: id}
err := o.Read(department)
if err != nil {
return nil, err
}
// 检查是否已删除
if department.DeleteTime != nil {
return nil, orm.ErrNoRows
}
return department, nil
}
// AddDepartment 添加部门
func AddDepartment(department *Department) (int64, error) {
o := orm.NewOrm()
id, err := o.Insert(department)
return id, err
}
// UpdateDepartment 更新部门信息
func UpdateDepartment(department *Department) error {
o := orm.NewOrm()
_, err := o.Update(department, "name", "code", "parent_id", "description", "manager_id", "sort_order", "status", "update_time")
return err
}
// DeleteDepartment 软删除部门
func DeleteDepartment(id int) error {
o := orm.NewOrm()
department := &Department{Id: id}
if err := o.Read(department); err != nil {
return err
}
now := time.Now()
department.DeleteTime = &now
_, err := o.Update(department, "delete_time")
return err
}
+242
View File
@@ -0,0 +1,242 @@
package models
import (
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"time"
"github.com/beego/beego/v2/client/orm"
"golang.org/x/crypto/scrypt"
)
// Employee 员工模型
type Employee struct {
Id int `orm:"auto" json:"id"`
TenantId int `orm:"column(tenant_id);default(0)" json:"tenant_id"`
EmployeeNo string `orm:"column(employee_no);size(50)" json:"employee_no"`
Name string `orm:"size(50)" json:"name"`
Phone string `orm:"size(20);null" json:"phone"`
Email string `orm:"size(100);null" json:"email"`
DepartmentId int `orm:"column(department_id);null;default(0)" json:"department_id"`
PositionId int `orm:"column(position_id);null;default(0)" json:"position_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:"-"` // 不返回给前端
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-离职
CreateTime time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
UpdateTime time.Time `orm:"column(update_time);type(datetime);auto_now" json:"update_time"`
DeleteTime *time.Time `orm:"column(delete_time);null;type(datetime)" json:"delete_time,omitempty"`
}
// TableName 设置表名
func (e *Employee) TableName() string {
return "yz_tenant_employees"
}
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", "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
}
+26 -18
View File
@@ -8,20 +8,21 @@ import (
// Menu 菜单模型
type Menu struct {
Id int `orm:"auto"`
Name string `orm:"size(100)"`
Path string `orm:"size(255)"`
ParentId int `orm:"default(0)"`
Icon string `orm:"size(100)"`
Order int `orm:"default(0)"`
Status int8 `orm:"default(1)"`
ComponentPath string `orm:"size(500);null"`
IsExternal int8 `orm:"default(0)"`
ExternalUrl string `orm:"size(1000);null"`
MenuType int8 `orm:"default(1)"`
Permission string `orm:"size(200);null"`
CreateTime time.Time `orm:"auto_now_add;type(datetime)"`
UpdateTime time.Time `orm:"auto_now;type(datetime)"`
Id int `orm:"auto"`
Name string `orm:"size(100)"`
Path string `orm:"size(255)"`
ParentId int `orm:"default(0)"`
Icon string `orm:"size(100)"`
Order int `orm:"default(0)"`
Status int8 `orm:"default(1)"`
ComponentPath string `orm:"size(500);null"`
IsExternal int8 `orm:"default(0)"`
ExternalUrl string `orm:"size(1000);null"`
MenuType int8 `orm:"default(1)"`
Permission string `orm:"size(200);null"`
CreateTime time.Time `orm:"auto_now_add;type(datetime)"`
UpdateTime time.Time `orm:"auto_now;type(datetime)"`
DeleteTime *time.Time `orm:"null;type(datetime)"`
}
// TableName 设置表名
@@ -29,11 +30,11 @@ func (m *Menu) TableName() string {
return "yz_menus"
}
// GetAllMenus 获取所有菜单
// GetAllMenus 获取所有菜单(未删除的)
func GetAllMenus() ([]map[string]interface{}, error) {
o := orm.NewOrm()
var menus []*Menu
_, err := o.QueryTable("yz_menus").Filter("Status", 1).All(&menus)
_, err := o.QueryTable("yz_menus").Filter("delete_time__isnull", true).All(&menus)
if err != nil {
return nil, err
}
@@ -46,6 +47,7 @@ func GetAllMenus() ([]map[string]interface{}, error) {
"parentId": m.ParentId,
"icon": m.Icon,
"order": m.Order,
"status": m.Status,
"componentPath": m.ComponentPath,
"isExternal": m.IsExternal,
"externalUrl": m.ExternalUrl,
@@ -82,9 +84,15 @@ func UpdateMenuStatus(id int, status int8) error {
return err
}
// DeleteMenu 删除菜单
// DeleteMenu 删除菜单(软删除)
func DeleteMenu(id int) error {
o := orm.NewOrm()
_, err := o.Delete(&Menu{Id: id})
menu := Menu{Id: id}
if err := o.Read(&menu); err != nil {
return err
}
now := time.Now()
menu.DeleteTime = &now
_, err := o.Update(&menu, "DeleteTime")
return err
}
+3 -3
View File
@@ -99,12 +99,12 @@ func GetRolePermissions(roleId int) (*RolePermission, error) {
}, nil
}
// GetAllMenuPermissions 获取所有菜单权限列表(用于分配权限时展示)
// 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 status = 1 ORDER BY parent_id, `order`").QueryRows(&menus)
_, 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)
}
@@ -259,7 +259,7 @@ func GetUserMenuTree(userId int) ([]*MenuTreeNode, error) {
placeholders[i] = "?"
args[i] = id
}
query := fmt.Sprintf("SELECT * FROM yz_menus WHERE id IN (%s) AND menu_type = 1 AND status = 1 ORDER BY parent_id, `order`", strings.Join(placeholders, ","))
query := fmt.Sprintf("SELECT * FROM yz_menus WHERE id IN (%s) AND menu_type = 1 AND delete_time IS NULL ORDER BY parent_id, `order`", strings.Join(placeholders, ","))
_, err = o.Raw(query, args...).QueryRows(&menus)
if err != nil {
return nil, fmt.Errorf("获取菜单列表失败: %v", err)
+100
View File
@@ -0,0 +1,100 @@
package models
import (
"time"
"github.com/beego/beego/v2/client/orm"
)
// Position 职位模型
type Position struct {
Id int `orm:"auto" json:"id"`
TenantId int `orm:"column(tenant_id);default(0)" json:"tenant_id"`
Name string `orm:"size(100)" json:"name"`
Code string `orm:"size(50);null" json:"code"`
DepartmentId int `orm:"column(department_id);null" json:"department_id"`
Level int `orm:"default(0)" json:"level"`
Description string `orm:"type(text);null" json:"description"`
SortOrder int `orm:"column(sort_order);default(0)" json:"sort_order"`
Status int8 `orm:"default(1)" json:"status"` // 1-启用,0-禁用
CreateTime time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
UpdateTime time.Time `orm:"column(update_time);type(datetime);auto_now" json:"update_time"`
DeleteTime *time.Time `orm:"column(delete_time);null;type(datetime)" json:"delete_time,omitempty"`
}
// TableName 设置表名
func (p *Position) TableName() string {
return "yz_tenant_positions"
}
func init() {
orm.RegisterModel(new(Position))
}
// GetTenantPositions 获取租户下的所有职位
func GetTenantPositions(tenantId int) ([]*Position, error) {
o := orm.NewOrm()
var positions []*Position
_, err := o.QueryTable("yz_tenant_positions").
Filter("tenant_id", tenantId).
Filter("delete_time__isnull", true).
OrderBy("sort_order", "create_time").
All(&positions)
return positions, err
}
// GetPositionsByDepartment 根据部门ID获取职位列表
func GetPositionsByDepartment(departmentId int) ([]*Position, error) {
o := orm.NewOrm()
var positions []*Position
_, err := o.QueryTable("yz_tenant_positions").
Filter("department_id", departmentId).
Filter("delete_time__isnull", true).
Filter("status", 1).
OrderBy("sort_order", "create_time").
All(&positions)
return positions, err
}
// GetPositionById 根据ID获取职位信息
func GetPositionById(id int) (*Position, error) {
o := orm.NewOrm()
position := &Position{Id: id}
err := o.Read(position)
if err != nil {
return nil, err
}
// 检查是否已删除
if position.DeleteTime != nil {
return nil, orm.ErrNoRows
}
return position, err
}
// AddPosition 添加职位
func AddPosition(position *Position) (int64, error) {
o := orm.NewOrm()
id, err := o.Insert(position)
return id, err
}
// UpdatePosition 更新职位信息
func UpdatePosition(position *Position) error {
o := orm.NewOrm()
_, err := o.Update(position, "name", "code", "department_id", "level", "description", "sort_order", "status", "update_time")
return err
}
// DeletePosition 软删除职位
func DeletePosition(id int) error {
o := orm.NewOrm()
position := &Position{Id: id}
if err := o.Read(position); err != nil {
return err
}
now := time.Now()
position.DeleteTime = &now
_, err := o.Update(position, "delete_time")
return err
}
+45 -24
View File
@@ -26,6 +26,8 @@ type User struct {
Nickname string
Status int `orm:"column(status);default(1)" json:"status"`
Role int `orm:"column(role);default(0)" json:"role"`
DepartmentId int `orm:"column(department_id);null;default(0)" json:"department_id"`
PositionId int `orm:"column(position_id);null;default(0)" json:"position_id"`
DeleteTime *time.Time `orm:"column(delete_time);null;type(datetime)" json:"delete_time"`
LastLoginTime *time.Time `orm:"column(last_login_time);null;type(datetime)" json:"last_login_time"`
LastLoginIp string `orm:"column(last_login_ip);null;size(50)" json:"last_login_ip"`
@@ -193,8 +195,8 @@ func GetUserInfo(userId int, username string, tenantId int) (*User, error) {
return user, nil
}
// ValidateUser 验证用户登录信息
func ValidateUser(username, password string, tenantName string) (*User, error) {
// ValidateUser 验证用户登录信息(先检查用户表,找不到再检查员工表)
func ValidateUser(username, password string, tenantName string) (*User, *Employee, error) {
o := orm.NewOrm()
// 1. 根据租户名称查询租户(只查询未删除的)
@@ -206,39 +208,45 @@ func ValidateUser(username, password string, tenantName string) (*User, error) {
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, errors.New("租户不存在")
return nil, nil, errors.New("租户不存在")
}
if err != nil {
return nil, fmt.Errorf("查询租户失败: %v", err)
return nil, nil, fmt.Errorf("查询租户失败: %v", err)
}
// 检查租户状态
if tenant.Status == "disabled" {
return nil, errors.New("租户已被禁用")
return nil, nil, errors.New("租户已被禁用")
}
if tenant.Status != "enabled" {
return nil, fmt.Errorf("租户状态异常: %s", tenant.Status)
return nil, nil, fmt.Errorf("租户状态异常: %s", tenant.Status)
}
tenantId := tenant.Id
// 2. 获取租户下的用户
// 2. 先尝试从用户表获取
user, err := GetUserInfo(0, username, tenantId)
if err != nil {
// 用户存在或查询失败
return nil, err
if err == nil && user != nil {
// 用户存在,验证密码
if verifyPassword(password, user.Salt, user.Password) {
return user, nil, nil
}
return nil, nil, errors.New("密码不正确")
}
// 3. 验证密码
if verifyPassword(password, user.Salt, user.Password) {
return user, nil
// 3. 用户表中没有找到,尝试从员工表获取
employee, err := ValidateEmployee(username, password, tenantId)
if err != nil {
return nil, nil, err
}
return nil, errors.New("密码不正确")
// 员工验证成功,返回员工信息(user为nil表示是员工登录)
return nil, employee, nil
}
// AddUser 向数据库添加新用户
func AddUser(username, password, email, nickname, avatar string, tenantId, role int) (*User, error) {
func AddUser(username, password, email, nickname, avatar string, tenantId, role, departmentId, positionId int) (*User, error) {
// 1. 验证租户是否存在且有效
o := orm.NewOrm()
var tenantExists bool
@@ -273,14 +281,17 @@ func AddUser(username, password, email, nickname, avatar string, tenantId, role
// 4. 构建用户对象
user := &User{
TenantId: tenantId,
Username: username,
Password: hashedPassword,
Salt: salt,
Email: email,
Nickname: nickname,
Avatar: avatar,
Role: role, // 设置角色ID
TenantId: tenantId,
Username: username,
Password: hashedPassword,
Salt: salt,
Email: email,
Nickname: nickname,
Avatar: avatar,
Role: role,
DepartmentId: departmentId,
PositionId: positionId,
Status: 1,
}
// 5. 插入数据库(使用之前定义的 o)
@@ -294,7 +305,7 @@ func AddUser(username, password, email, nickname, avatar string, tenantId, role
}
// EditUser 更新用户信息
func EditUser(id int, username, email, nickname, avatar, status string, roleId int) (*User, error) {
func EditUser(id int, username, email, nickname, avatar, status string, roleId, departmentId, positionId int) (*User, error) {
// 根据ID查询用户
o := orm.NewOrm()
user := &User{}
@@ -339,6 +350,16 @@ func EditUser(id int, username, email, nickname, avatar, status string, roleId i
user.Role = roleId
}
// 更新部门ID
if departmentId >= 0 {
user.DepartmentId = departmentId
}
// 更新职位ID
if positionId >= 0 {
user.PositionId = positionId
}
// 执行数据库更新
_, err = o.Update(user)
if err != nil {
+18
View File
@@ -71,6 +71,24 @@ func init() {
beego.Router("/api/reset-password", &controllers.UserController{}, "post:ResetPassword")
beego.Router("/api/tenantUsers/:tenantId", &controllers.UserController{}, "get:GetTenantUsers")
// 员工管理路由
beego.Router("/api/employees", &controllers.EmployeeController{}, "get:GetAllEmployees;post:AddEmployee")
beego.Router("/api/employees/:id", &controllers.EmployeeController{}, "get:GetEmployeeInfo;put:UpdateEmployee;delete:DeleteEmployee")
beego.Router("/api/employees/tenant/:tenantId", &controllers.EmployeeController{}, "get:GetTenantEmployees")
beego.Router("/api/employees/:id/reset-password", &controllers.EmployeeController{}, "post:ResetEmployeePassword")
beego.Router("/api/employees/:id/change-password", &controllers.EmployeeController{}, "post:ChangeEmployeePassword")
// 部门管理路由
beego.Router("/api/departments", &controllers.DepartmentController{}, "post:AddDepartment")
beego.Router("/api/departments/:id", &controllers.DepartmentController{}, "get:GetDepartmentInfo;put:UpdateDepartment;delete:DeleteDepartment")
beego.Router("/api/departments/tenant/:tenantId", &controllers.DepartmentController{}, "get:GetTenantDepartments")
// 职位管理路由
beego.Router("/api/positions", &controllers.PositionController{}, "post:AddPosition")
beego.Router("/api/positions/:id", &controllers.PositionController{}, "get:GetPositionInfo;put:UpdatePosition;delete:DeletePosition")
beego.Router("/api/positions/tenant/:tenantId", &controllers.PositionController{}, "get:GetTenantPositions")
beego.Router("/api/positions/department/:departmentId", &controllers.PositionController{}, "get:GetPositionsByDepartment")
// 认证路由
beego.Router("/api/login", &controllers.AuthController{}, "post:Login")
beego.Router("/api/logout", &controllers.AuthController{}, "post:Logout")