增加字典
This commit is contained in:
@@ -0,0 +1,511 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"server/models"
|
||||
"server/services"
|
||||
"strconv"
|
||||
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// DictController 字典管理控制器
|
||||
type DictController struct {
|
||||
web.Controller
|
||||
}
|
||||
|
||||
// GetDictTypes 获取字典类型列表
|
||||
func (c *DictController) GetDictTypes() {
|
||||
// 获取租户ID
|
||||
tenantIdData := c.Ctx.Input.GetData("tenantId")
|
||||
tenantId := 0
|
||||
if tenantIdData != nil {
|
||||
if tid, ok := tenantIdData.(int); ok {
|
||||
tenantId = tid
|
||||
}
|
||||
}
|
||||
|
||||
parentId, _ := c.GetInt("parent_id", -1)
|
||||
statusStr := c.GetString("status")
|
||||
|
||||
var status *int8
|
||||
if statusStr != "" {
|
||||
s, _ := strconv.ParseInt(statusStr, 10, 8)
|
||||
statusVal := int8(s)
|
||||
status = &statusVal
|
||||
}
|
||||
|
||||
dictTypes, err := services.GetDictTypes(tenantId, parentId, status)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "查询字典类型失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"data": dictTypes,
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetDictTypeById 根据ID获取字典类型
|
||||
func (c *DictController) GetDictTypeById() {
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil || id <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数错误",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
dictType, err := services.GetDictTypeById(id)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "字典类型不存在",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"data": dictType,
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddDictType 添加字典类型
|
||||
func (c *DictController) AddDictType() {
|
||||
var dictType models.DictType
|
||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &dictType); err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数解析失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 获取租户ID和用户名
|
||||
tenantIdData := c.Ctx.Input.GetData("tenantId")
|
||||
tenantId := 0
|
||||
if tenantIdData != nil {
|
||||
if tid, ok := tenantIdData.(int); ok {
|
||||
tenantId = tid
|
||||
}
|
||||
}
|
||||
usernameData := c.Ctx.Input.GetData("username")
|
||||
username := ""
|
||||
if usernameData != nil {
|
||||
if u, ok := usernameData.(string); ok {
|
||||
username = u
|
||||
}
|
||||
}
|
||||
|
||||
// 设置租户ID和创建人
|
||||
dictType.TenantId = tenantId
|
||||
dictType.CreateBy = username
|
||||
if dictType.Status == 0 {
|
||||
dictType.Status = 1 // 默认启用
|
||||
}
|
||||
|
||||
id, err := services.AddDictType(&dictType)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "添加成功",
|
||||
"data": map[string]interface{}{"id": id},
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateDictType 更新字典类型
|
||||
func (c *DictController) UpdateDictType() {
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil || id <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数错误",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
var dictType models.DictType
|
||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &dictType); err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数解析失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 获取用户名
|
||||
usernameData := c.Ctx.Input.GetData("username")
|
||||
username := ""
|
||||
if usernameData != nil {
|
||||
if u, ok := usernameData.(string); ok {
|
||||
username = u
|
||||
}
|
||||
}
|
||||
|
||||
dictType.Id = id
|
||||
dictType.UpdateBy = username
|
||||
|
||||
err = services.UpdateDictType(&dictType)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "更新成功",
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteDictType 删除字典类型
|
||||
func (c *DictController) DeleteDictType() {
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil || id <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数错误",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 获取租户ID
|
||||
tenantIdData := c.Ctx.Input.GetData("tenantId")
|
||||
tenantId := 0
|
||||
if tenantIdData != nil {
|
||||
if tid, ok := tenantIdData.(int); ok {
|
||||
tenantId = tid
|
||||
}
|
||||
}
|
||||
|
||||
err = services.DeleteDictType(id, tenantId)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "删除成功",
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetDictItems 获取字典项列表
|
||||
func (c *DictController) GetDictItems() {
|
||||
dictTypeId, _ := c.GetInt("dict_type_id", 0)
|
||||
if dictTypeId <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数错误:dict_type_id 必填",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
parentIdStr := c.GetString("parent_id")
|
||||
statusStr := c.GetString("status")
|
||||
|
||||
var parentId *int
|
||||
if parentIdStr != "" {
|
||||
pid, _ := strconv.Atoi(parentIdStr)
|
||||
parentId = &pid
|
||||
}
|
||||
|
||||
var status *int8
|
||||
if statusStr != "" {
|
||||
s, _ := strconv.ParseInt(statusStr, 10, 8)
|
||||
statusVal := int8(s)
|
||||
status = &statusVal
|
||||
}
|
||||
|
||||
dictItems, err := services.GetDictItems(dictTypeId, parentId, status)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "查询字典项失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"data": dictItems,
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetDictItemById 根据ID获取字典项
|
||||
func (c *DictController) GetDictItemById() {
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil || id <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数错误",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
dictItem, err := services.GetDictItemById(id)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "字典项不存在",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"data": dictItem,
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddDictItem 添加字典项
|
||||
func (c *DictController) AddDictItem() {
|
||||
var dictItem models.DictItem
|
||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &dictItem); err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数解析失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
if dictItem.DictTypeId <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数错误:dict_type_id 必填",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 获取用户名
|
||||
usernameData := c.Ctx.Input.GetData("username")
|
||||
username := ""
|
||||
if usernameData != nil {
|
||||
if u, ok := usernameData.(string); ok {
|
||||
username = u
|
||||
}
|
||||
}
|
||||
|
||||
// 设置创建人
|
||||
dictItem.CreateBy = username
|
||||
if dictItem.Status == 0 {
|
||||
dictItem.Status = 1 // 默认启用
|
||||
}
|
||||
|
||||
id, err := services.AddDictItem(&dictItem)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "添加成功",
|
||||
"data": map[string]interface{}{"id": id},
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateDictItem 更新字典项
|
||||
func (c *DictController) UpdateDictItem() {
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil || id <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数错误",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
var dictItem models.DictItem
|
||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &dictItem); err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数解析失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 获取用户名
|
||||
usernameData := c.Ctx.Input.GetData("username")
|
||||
username := ""
|
||||
if usernameData != nil {
|
||||
if u, ok := usernameData.(string); ok {
|
||||
username = u
|
||||
}
|
||||
}
|
||||
|
||||
dictItem.Id = id
|
||||
dictItem.UpdateBy = username
|
||||
|
||||
err = services.UpdateDictItem(&dictItem)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "更新成功",
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteDictItem 删除字典项
|
||||
func (c *DictController) DeleteDictItem() {
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil || id <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数错误",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
err = services.DeleteDictItem(id)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "删除成功",
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetDictItemsByCode 根据字典编码获取字典项(用于业务查询)
|
||||
func (c *DictController) GetDictItemsByCode() {
|
||||
dictCode := c.Ctx.Input.Param(":code")
|
||||
if dictCode == "" {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数错误:code 必填",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 获取租户ID
|
||||
tenantIdData := c.Ctx.Input.GetData("tenantId")
|
||||
tenantId := 0
|
||||
if tenantIdData != nil {
|
||||
if tid, ok := tenantIdData.(int); ok {
|
||||
tenantId = tid
|
||||
}
|
||||
}
|
||||
|
||||
includeDisabled := c.GetString("include_disabled") == "1"
|
||||
|
||||
dictItems, err := services.GetDictItemsByCode(dictCode, tenantId, includeDisabled)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "查询字典项失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"data": dictItems,
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// BatchUpdateDictItemSort 批量更新字典项排序
|
||||
func (c *DictController) BatchUpdateDictItemSort() {
|
||||
var items []struct {
|
||||
Id int `json:"id"`
|
||||
Sort int `json:"sort"`
|
||||
}
|
||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &items); err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数解析失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
err := services.BatchUpdateDictItemSort(items)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "更新成功",
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
)
|
||||
|
||||
// DictType 字典类型模型
|
||||
type DictType struct {
|
||||
Id int `orm:"auto" json:"id"`
|
||||
TenantId int `orm:"column(tenant_id);default(0)" json:"tenant_id"` // 0表示平台字典,>0表示租户字典
|
||||
DictCode string `orm:"column(dict_code);size(50)" json:"dict_code"` // 字典编码(唯一)
|
||||
DictName string `orm:"column(dict_name);size(100)" json:"dict_name"` // 字典名称
|
||||
ParentId int `orm:"column(parent_id);default(0)" json:"parent_id"` // 父级字典ID(支持多级)
|
||||
Status int8 `orm:"column(status);default(1)" json:"status"` // 0-禁用,1-启用
|
||||
Sort int `orm:"column(sort);default(0)" json:"sort"` // 排序号
|
||||
Remark string `orm:"column(remark);size(500);null" json:"remark"` // 备注
|
||||
CreateBy string `orm:"column(create_by);size(50);null" json:"create_by"`
|
||||
CreateTime time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
|
||||
UpdateBy string `orm:"column(update_by);size(50);null" json:"update_by"`
|
||||
UpdateTime time.Time `orm:"column(update_time);type(datetime);auto_now" json:"update_time"`
|
||||
IsDeleted int8 `orm:"column(is_deleted);default(0)" json:"is_deleted"` // 0-未删,1-已删
|
||||
}
|
||||
|
||||
// TableName 设置表名
|
||||
func (d *DictType) TableName() string {
|
||||
return "sys_dict_type"
|
||||
}
|
||||
|
||||
// DictItem 字典项模型
|
||||
type DictItem struct {
|
||||
Id int `orm:"auto" json:"id"`
|
||||
DictTypeId int `orm:"column(dict_type_id)" json:"dict_type_id"` // 关联字典类型ID
|
||||
DictLabel string `orm:"column(dict_label);size(100)" json:"dict_label"` // 字典标签(显示值)
|
||||
DictValue string `orm:"column(dict_value);size(100)" json:"dict_value"` // 字典值(存储值)
|
||||
ParentId int `orm:"column(parent_id);default(0)" json:"parent_id"` // 父级字典项ID(支持多级)
|
||||
Status int8 `orm:"column(status);default(1)" json:"status"` // 0-禁用,1-启用
|
||||
Sort int `orm:"column(sort);default(0)" json:"sort"` // 排序号
|
||||
Color string `orm:"column(color);size(20);null" json:"color"` // 颜色标记
|
||||
Icon string `orm:"column(icon);size(50);null" json:"icon"` // 图标
|
||||
Remark string `orm:"column(remark);size(500);null" json:"remark"` // 备注
|
||||
CreateBy string `orm:"column(create_by);size(50);null" json:"create_by"`
|
||||
CreateTime time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
|
||||
UpdateBy string `orm:"column(update_by);size(50);null" json:"update_by"`
|
||||
UpdateTime time.Time `orm:"column(update_time);type(datetime);auto_now" json:"update_time"`
|
||||
IsDeleted int8 `orm:"column(is_deleted);default(0)" json:"is_deleted"` // 0-未删,1-已删
|
||||
}
|
||||
|
||||
// TableName 设置表名
|
||||
func (d *DictItem) TableName() string {
|
||||
return "sys_dict_item"
|
||||
}
|
||||
|
||||
func init() {
|
||||
orm.RegisterModel(new(DictType))
|
||||
orm.RegisterModel(new(DictItem))
|
||||
}
|
||||
@@ -324,7 +324,6 @@ func GetAllMenuPermissionsForUser(userId int, userType string, roleId int) ([]*M
|
||||
|
||||
// 分配角色权限,更新role菜单ID
|
||||
func AssignRolePermissions(roleId int, menuIds []int, createBy string) error {
|
||||
o := orm.NewOrm()
|
||||
var jsonData []byte
|
||||
var err error
|
||||
if len(menuIds) == 0 {
|
||||
@@ -335,11 +334,54 @@ func AssignRolePermissions(roleId int, menuIds []int, createBy string) error {
|
||||
return fmt.Errorf("序列化菜单ID失败: %v", err)
|
||||
}
|
||||
}
|
||||
_, 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)
|
||||
|
||||
// 使用重试机制处理连接失效问题
|
||||
maxRetries := 3
|
||||
var lastErr error
|
||||
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 在执行前检查连接有效性
|
||||
db, dbErr := orm.GetDB("default")
|
||||
if dbErr == nil {
|
||||
// 尝试 Ping 检查连接
|
||||
if pingErr := db.Ping(); pingErr != nil {
|
||||
// 连接失效,等待后重试
|
||||
if i < maxRetries-1 {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
lastErr = fmt.Errorf("数据库连接失效: %v", pingErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试执行更新
|
||||
_, 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 nil
|
||||
}
|
||||
|
||||
// 检查是否是连接错误
|
||||
errStr := err.Error()
|
||||
if strings.Contains(errStr, "invalid connection") ||
|
||||
strings.Contains(errStr, "driver: bad connection") ||
|
||||
strings.Contains(errStr, "connection reset") {
|
||||
lastErr = err
|
||||
// 如果是连接错误且不是最后一次重试,等待后继续
|
||||
if i < maxRetries-1 {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// 非连接错误,直接返回
|
||||
return fmt.Errorf("更新角色权限失败: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
// 所有重试都失败了
|
||||
return fmt.Errorf("更新角色权限失败(已重试%d次): %v", maxRetries, lastErr)
|
||||
}
|
||||
|
||||
// 获取用户所有权限(从所属角色)
|
||||
|
||||
@@ -44,6 +44,8 @@ func Init(version string) {
|
||||
orm.RegisterModel(new(Knowledge))
|
||||
orm.RegisterModel(new(KnowledgeCategory))
|
||||
orm.RegisterModel(new(KnowledgeTag))
|
||||
orm.RegisterModel(new(DictType))
|
||||
orm.RegisterModel(new(DictItem))
|
||||
|
||||
ormConfig, err := beego.AppConfig.String("orm")
|
||||
if err != nil {
|
||||
@@ -60,6 +62,7 @@ func Init(version string) {
|
||||
}
|
||||
|
||||
// 构建连接字符串,添加连接池和性能优化参数
|
||||
// Go MySQL驱动会自动通过连接池管理连接,不需要额外的reconnect参数
|
||||
dsn := user + ":" + pass + "@tcp(" + urls + ")/" + dbName + "?charset=utf8mb4&parseTime=True&loc=Local&timeout=10s&readTimeout=30s&writeTimeout=30s"
|
||||
fmt.Println("数据库连接字符串:", dsn)
|
||||
|
||||
@@ -76,9 +79,12 @@ func Init(version string) {
|
||||
}
|
||||
|
||||
// 设置连接池参数
|
||||
dbConn.SetMaxIdleConns(10) // 设置空闲连接池中连接的最大数量
|
||||
dbConn.SetMaxOpenConns(100) // 设置打开数据库连接的最大数量
|
||||
dbConn.SetConnMaxLifetime(time.Hour) // 设置连接可复用的最大时间
|
||||
dbConn.SetMaxIdleConns(10) // 设置空闲连接池中连接的最大数量
|
||||
dbConn.SetMaxOpenConns(100) // 设置打开数据库连接的最大数量
|
||||
dbConn.SetConnMaxLifetime(30 * time.Minute) // 设置连接可复用的最大时间(30分钟,避免连接过期)
|
||||
// 注意:SetConnMaxIdleTime 在 Go 1.15+ 和 database/sql 1.4+ 中可用
|
||||
// 如果版本不支持,可以移除这一行
|
||||
// dbConn.SetConnMaxIdleTime(10 * time.Minute) // 设置空闲连接的最大空闲时间
|
||||
|
||||
fmt.Println("数据库连接成功!")
|
||||
fmt.Printf("当前项目版本: %s\n", version)
|
||||
|
||||
@@ -315,6 +315,14 @@ func init() {
|
||||
beego.Router("/api/dashboard/platform-stats", &controllers.DashboardController{}, "get:GetPlatformStats")
|
||||
beego.Router("/api/dashboard/tenant-stats", &controllers.DashboardController{}, "get:GetTenantStats")
|
||||
|
||||
// 字典管理路由
|
||||
beego.Router("/api/dict/types", &controllers.DictController{}, "get:GetDictTypes;post:AddDictType")
|
||||
beego.Router("/api/dict/types/:id", &controllers.DictController{}, "get:GetDictTypeById;put:UpdateDictType;delete:DeleteDictType")
|
||||
beego.Router("/api/dict/items", &controllers.DictController{}, "get:GetDictItems;post:AddDictItem")
|
||||
beego.Router("/api/dict/items/:id", &controllers.DictController{}, "get:GetDictItemById;put:UpdateDictItem;delete:DeleteDictItem")
|
||||
beego.Router("/api/dict/items/code/:code", &controllers.DictController{}, "get:GetDictItemsByCode")
|
||||
beego.Router("/api/dict/items/sort", &controllers.DictController{}, "put:BatchUpdateDictItemSort")
|
||||
|
||||
// 手动配置特殊路由(无法通过自动路由处理的)
|
||||
beego.Router("/api/allmenu", &controllers.MenuController{}, "get:GetAllMenus")
|
||||
beego.Router("/api/program-categories/public", &controllers.ProgramCategoryController{}, "get:GetProgramCategoriesPublic")
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"server/models"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
)
|
||||
|
||||
// ========== 字典类型相关服务 ==========
|
||||
|
||||
// AddDictType 添加字典类型
|
||||
func AddDictType(dictType *models.DictType) (int64, error) {
|
||||
// 校验字典编码格式(只能包含字母、数字、下划线)
|
||||
matched, err := regexp.MatchString(`^[A-Za-z0-9_]+$`, dictType.DictCode)
|
||||
if err != nil || !matched {
|
||||
return 0, fmt.Errorf("字典编码格式错误,只能包含字母、数字、下划线")
|
||||
}
|
||||
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 检查字典编码是否已存在(同一租户下唯一)
|
||||
existType := &models.DictType{}
|
||||
err = o.Raw("SELECT id FROM sys_dict_type WHERE dict_code = ? AND tenant_id = ? AND is_deleted = 0",
|
||||
dictType.DictCode, dictType.TenantId).QueryRow(existType)
|
||||
if err == nil {
|
||||
return 0, fmt.Errorf("字典编码 %s 已存在", dictType.DictCode)
|
||||
}
|
||||
|
||||
// 插入新字典类型
|
||||
id, err := o.Insert(dictType)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("添加字典类型失败: %v", err)
|
||||
}
|
||||
|
||||
// 清除相关缓存
|
||||
clearDictCache(dictType.TenantId, dictType.DictCode)
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// UpdateDictType 更新字典类型
|
||||
func UpdateDictType(dictType *models.DictType) error {
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 获取原字典类型信息
|
||||
oldType := &models.DictType{Id: dictType.Id}
|
||||
err := o.Read(oldType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("字典类型不存在: %v", err)
|
||||
}
|
||||
|
||||
// 不允许修改 dict_code(避免业务引用失效)
|
||||
if oldType.DictCode != dictType.DictCode {
|
||||
return fmt.Errorf("不允许修改字典编码")
|
||||
}
|
||||
|
||||
// 更新字段(排除 dict_code)
|
||||
_, err = o.Update(dictType, "dict_name", "parent_id", "status", "sort", "remark", "update_by", "update_time")
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新字典类型失败: %v", err)
|
||||
}
|
||||
|
||||
// 清除相关缓存
|
||||
clearDictCache(dictType.TenantId, dictType.DictCode)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteDictType 删除字典类型(逻辑删除)
|
||||
func DeleteDictType(id int, tenantId int) error {
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 检查是否存在
|
||||
dictType := &models.DictType{Id: id}
|
||||
err := o.Read(dictType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("字典类型不存在: %v", err)
|
||||
}
|
||||
|
||||
// 检查租户权限
|
||||
if dictType.TenantId != tenantId {
|
||||
return fmt.Errorf("无权操作该字典类型")
|
||||
}
|
||||
|
||||
// 检查是否关联字典项
|
||||
var count int
|
||||
err = o.Raw("SELECT COUNT(*) FROM sys_dict_item WHERE dict_type_id = ? AND is_deleted = 0", id).QueryRow(&count)
|
||||
if err == nil && count > 0 {
|
||||
return fmt.Errorf("该字典类型下存在字典项,请先删除字典项")
|
||||
}
|
||||
|
||||
// 逻辑删除
|
||||
dictType.IsDeleted = 1
|
||||
dictType.UpdateTime = time.Now()
|
||||
_, err = o.Update(dictType, "is_deleted", "update_time")
|
||||
if err != nil {
|
||||
return fmt.Errorf("删除字典类型失败: %v", err)
|
||||
}
|
||||
|
||||
// 清除相关缓存
|
||||
clearDictCache(dictType.TenantId, dictType.DictCode)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDictTypeById 根据ID获取字典类型
|
||||
func GetDictTypeById(id int) (*models.DictType, error) {
|
||||
o := orm.NewOrm()
|
||||
dictType := &models.DictType{Id: id}
|
||||
err := o.Read(dictType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dictType.IsDeleted == 1 {
|
||||
return nil, fmt.Errorf("字典类型已删除")
|
||||
}
|
||||
return dictType, nil
|
||||
}
|
||||
|
||||
// GetDictTypes 获取字典类型列表
|
||||
func GetDictTypes(tenantId int, parentId int, status *int8) ([]*models.DictType, error) {
|
||||
o := orm.NewOrm()
|
||||
qs := o.QueryTable("sys_dict_type").Filter("is_deleted", 0)
|
||||
|
||||
// 租户过滤:0表示平台字典,>0表示租户字典
|
||||
if tenantId > 0 {
|
||||
// 租户用户只能看到自己的字典和平台字典
|
||||
qs = qs.Filter("tenant_id__in", 0, tenantId)
|
||||
} else {
|
||||
// 平台用户只能看到平台字典
|
||||
qs = qs.Filter("tenant_id", 0)
|
||||
}
|
||||
|
||||
// 父级过滤
|
||||
if parentId >= 0 {
|
||||
qs = qs.Filter("parent_id", parentId)
|
||||
}
|
||||
|
||||
// 状态过滤
|
||||
if status != nil {
|
||||
qs = qs.Filter("status", *status)
|
||||
}
|
||||
|
||||
var dictTypes []*models.DictType
|
||||
_, err := qs.OrderBy("sort", "create_time").All(&dictTypes)
|
||||
return dictTypes, err
|
||||
}
|
||||
|
||||
// ========== 字典项相关服务 ==========
|
||||
|
||||
// AddDictItem 添加字典项
|
||||
func AddDictItem(dictItem *models.DictItem) (int64, error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 检查字典类型是否存在
|
||||
dictType := &models.DictType{Id: dictItem.DictTypeId}
|
||||
err := o.Read(dictType)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("字典类型不存在: %v", err)
|
||||
}
|
||||
if dictType.IsDeleted == 1 || dictType.Status == 0 {
|
||||
return 0, fmt.Errorf("字典类型已删除或已禁用")
|
||||
}
|
||||
|
||||
// 检查同一字典类型下 dict_value 是否已存在
|
||||
existItem := &models.DictItem{}
|
||||
err = o.Raw("SELECT id FROM sys_dict_item WHERE dict_type_id = ? AND dict_value = ? AND is_deleted = 0",
|
||||
dictItem.DictTypeId, dictItem.DictValue).QueryRow(existItem)
|
||||
if err == nil {
|
||||
return 0, fmt.Errorf("字典值 %s 已存在", dictItem.DictValue)
|
||||
}
|
||||
|
||||
// 插入新字典项
|
||||
id, err := o.Insert(dictItem)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("添加字典项失败: %v", err)
|
||||
}
|
||||
|
||||
// 清除相关缓存
|
||||
clearDictCacheByTypeId(dictItem.DictTypeId)
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// UpdateDictItem 更新字典项
|
||||
func UpdateDictItem(dictItem *models.DictItem) error {
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 获取原字典项信息
|
||||
oldItem := &models.DictItem{Id: dictItem.Id}
|
||||
err := o.Read(oldItem)
|
||||
if err != nil {
|
||||
return fmt.Errorf("字典项不存在: %v", err)
|
||||
}
|
||||
|
||||
// 不允许修改 dict_value(避免业务存储值与字典不匹配)
|
||||
if oldItem.DictValue != dictItem.DictValue {
|
||||
return fmt.Errorf("不允许修改字典值")
|
||||
}
|
||||
|
||||
// 如果修改了 dict_type_id,需要检查新类型是否存在
|
||||
if oldItem.DictTypeId != dictItem.DictTypeId {
|
||||
dictType := &models.DictType{Id: dictItem.DictTypeId}
|
||||
err = o.Read(dictType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("字典类型不存在: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新字段(排除 dict_value)
|
||||
_, err = o.Update(dictItem, "dict_label", "dict_type_id", "parent_id", "status", "sort", "color", "icon", "remark", "update_by", "update_time")
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新字典项失败: %v", err)
|
||||
}
|
||||
|
||||
// 清除相关缓存
|
||||
clearDictCacheByTypeId(oldItem.DictTypeId)
|
||||
if oldItem.DictTypeId != dictItem.DictTypeId {
|
||||
clearDictCacheByTypeId(dictItem.DictTypeId)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteDictItem 删除字典项(逻辑删除)
|
||||
func DeleteDictItem(id int) error {
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 检查是否存在
|
||||
dictItem := &models.DictItem{Id: id}
|
||||
err := o.Read(dictItem)
|
||||
if err != nil {
|
||||
return fmt.Errorf("字典项不存在: %v", err)
|
||||
}
|
||||
|
||||
// 检查是否有子项
|
||||
var count int
|
||||
err = o.Raw("SELECT COUNT(*) FROM sys_dict_item WHERE parent_id = ? AND is_deleted = 0", id).QueryRow(&count)
|
||||
if err == nil && count > 0 {
|
||||
return fmt.Errorf("该字典项下存在子项,请先删除子项")
|
||||
}
|
||||
|
||||
// 逻辑删除
|
||||
dictItem.IsDeleted = 1
|
||||
dictItem.UpdateTime = time.Now()
|
||||
_, err = o.Update(dictItem, "is_deleted", "update_time")
|
||||
if err != nil {
|
||||
return fmt.Errorf("删除字典项失败: %v", err)
|
||||
}
|
||||
|
||||
// 清除相关缓存
|
||||
clearDictCacheByTypeId(dictItem.DictTypeId)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDictItemById 根据ID获取字典项
|
||||
func GetDictItemById(id int) (*models.DictItem, error) {
|
||||
o := orm.NewOrm()
|
||||
dictItem := &models.DictItem{Id: id}
|
||||
err := o.Read(dictItem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dictItem.IsDeleted == 1 {
|
||||
return nil, fmt.Errorf("字典项已删除")
|
||||
}
|
||||
return dictItem, nil
|
||||
}
|
||||
|
||||
// GetDictItems 获取字典项列表
|
||||
func GetDictItems(dictTypeId int, parentId *int, status *int8) ([]*models.DictItem, error) {
|
||||
o := orm.NewOrm()
|
||||
qs := o.QueryTable("sys_dict_item").Filter("is_deleted", 0).Filter("dict_type_id", dictTypeId)
|
||||
|
||||
// 父级过滤
|
||||
if parentId != nil {
|
||||
qs = qs.Filter("parent_id", *parentId)
|
||||
}
|
||||
|
||||
// 状态过滤
|
||||
if status != nil {
|
||||
qs = qs.Filter("status", *status)
|
||||
}
|
||||
|
||||
var dictItems []*models.DictItem
|
||||
_, err := qs.OrderBy("sort", "create_time").All(&dictItems)
|
||||
return dictItems, err
|
||||
}
|
||||
|
||||
// GetDictItemsByCode 根据字典编码获取字典项列表(支持缓存)
|
||||
func GetDictItemsByCode(dictCode string, tenantId int, includeDisabled bool) ([]*models.DictItem, error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 先查询字典类型
|
||||
dictType := &models.DictType{}
|
||||
err := o.Raw("SELECT * FROM sys_dict_type WHERE dict_code = ? AND tenant_id = ? AND is_deleted = 0 AND status = 1",
|
||||
dictCode, tenantId).QueryRow(dictType)
|
||||
if err != nil {
|
||||
// 如果租户字典不存在,尝试查询平台字典
|
||||
if tenantId > 0 {
|
||||
err = o.Raw("SELECT * FROM sys_dict_type WHERE dict_code = ? AND tenant_id = 0 AND is_deleted = 0 AND status = 1",
|
||||
dictCode).QueryRow(dictType)
|
||||
if err != nil {
|
||||
return []*models.DictItem{}, nil // 返回空列表而不是错误
|
||||
}
|
||||
} else {
|
||||
return []*models.DictItem{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 查询字典项
|
||||
qs := o.QueryTable("sys_dict_item").Filter("dict_type_id", dictType.Id).Filter("is_deleted", 0)
|
||||
if !includeDisabled {
|
||||
qs = qs.Filter("status", 1)
|
||||
}
|
||||
|
||||
var dictItems []*models.DictItem
|
||||
_, err = qs.OrderBy("sort", "create_time").All(&dictItems)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dictItems, nil
|
||||
}
|
||||
|
||||
// ========== 缓存相关(简化版,实际应使用Redis) ==========
|
||||
|
||||
// 清除字典缓存(简化实现,实际应使用Redis)
|
||||
func clearDictCache(tenantId int, dictCode string) {
|
||||
// TODO: 实现Redis缓存清除
|
||||
// 格式:SYS_DICT:ITEM:{tenantId}:{dictCode}
|
||||
}
|
||||
|
||||
// 根据字典类型ID清除缓存
|
||||
func clearDictCacheByTypeId(dictTypeId int) {
|
||||
o := orm.NewOrm()
|
||||
dictType := &models.DictType{Id: dictTypeId}
|
||||
err := o.Read(dictType)
|
||||
if err == nil {
|
||||
clearDictCache(dictType.TenantId, dictType.DictCode)
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 批量操作 ==========
|
||||
|
||||
// BatchUpdateDictItemSort 批量更新字典项排序
|
||||
func BatchUpdateDictItemSort(items []struct {
|
||||
Id int `json:"id"`
|
||||
Sort int `json:"sort"`
|
||||
}) error {
|
||||
o := orm.NewOrm()
|
||||
|
||||
for _, item := range items {
|
||||
dictItem := &models.DictItem{Id: item.Id}
|
||||
err := o.Read(dictItem)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
dictItem.Sort = item.Sort
|
||||
dictItem.UpdateTime = time.Now()
|
||||
_, err = o.Update(dictItem, "sort", "update_time")
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新字典项排序失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
-- 字典类型表
|
||||
CREATE TABLE IF NOT EXISTS `sys_dict_type` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`tenant_id` int(11) NOT NULL DEFAULT '0' COMMENT '租户ID(0表示平台字典,>0表示租户字典)',
|
||||
`dict_code` varchar(50) NOT NULL COMMENT '字典编码(唯一,如 USER_STATUS)',
|
||||
`dict_name` varchar(100) NOT NULL COMMENT '字典名称(如 用户状态)',
|
||||
`parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '父级字典ID(0表示一级字典)',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0-禁用,1-启用)',
|
||||
`sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序号',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` varchar(50) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` varchar(50) DEFAULT NULL COMMENT '更新人',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除(0-未删,1-已删)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_dict_code_tenant` (`dict_code`, `is_deleted`),
|
||||
KEY `idx_parent_id` (`parent_id`, `is_deleted`),
|
||||
KEY `idx_status` (`status`, `is_deleted`),
|
||||
KEY `idx_tenant_id` (`tenant_id`, `is_deleted`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字典类型表';
|
||||
|
||||
-- 字典项表
|
||||
CREATE TABLE IF NOT EXISTS `sys_dict_item` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`dict_type_id` bigint(20) NOT NULL COMMENT '字典类型ID',
|
||||
`dict_label` varchar(100) NOT NULL COMMENT '字典标签(显示值,如 正常)',
|
||||
`dict_value` varchar(100) NOT NULL COMMENT '字典值(存储值,如 1)',
|
||||
`parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '父级字典项ID(0表示一级项)',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0-禁用,1-启用)',
|
||||
`sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序号',
|
||||
`color` varchar(20) DEFAULT NULL COMMENT '颜色标记(如 #1890ff)',
|
||||
`icon` varchar(50) DEFAULT NULL COMMENT '图标(如 el-icon-success)',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` varchar(50) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` varchar(50) DEFAULT NULL COMMENT '更新人',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除(0-未删,1-已删)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_dict_type_parent_status` (`dict_type_id`, `parent_id`, `status`, `is_deleted`),
|
||||
UNIQUE KEY `uk_dict_type_value` (`dict_type_id`, `dict_value`, `is_deleted`),
|
||||
KEY `idx_parent_id` (`parent_id`, `status`, `is_deleted`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字典项表';
|
||||
|
||||
Reference in New Issue
Block a user