Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions server/api/v1/system/sys_dictionary_detail.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package system

import (
"strconv"

"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
Expand Down Expand Up @@ -146,3 +148,120 @@ func (s *DictionaryDetailApi) GetSysDictionaryDetailList(c *gin.Context) {
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}

// GetDictionaryTreeList
// @Tags SysDictionaryDetail
// @Summary 获取字典详情树形结构
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param sysDictionaryID query int true "字典ID"
// @Success 200 {object} response.Response{data=[]system.SysDictionaryDetail,msg=string} "获取字典详情树形结构"
// @Router /sysDictionaryDetail/getDictionaryTreeList [get]
func (s *DictionaryDetailApi) GetDictionaryTreeList(c *gin.Context) {
sysDictionaryID := c.Query("sysDictionaryID")
if sysDictionaryID == "" {
response.FailWithMessage("字典ID不能为空", c)
return
}

var id uint
if idUint64, err := strconv.ParseUint(sysDictionaryID, 10, 32); err != nil {
response.FailWithMessage("字典ID格式错误", c)
return
} else {
id = uint(idUint64)
}

list, err := dictionaryDetailService.GetDictionaryTreeList(id)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(gin.H{"list": list}, "获取成功", c)
}

// GetDictionaryTreeListByType
// @Tags SysDictionaryDetail
// @Summary 根据字典类型获取字典详情树形结构
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param type query string true "字典类型"
// @Success 200 {object} response.Response{data=[]system.SysDictionaryDetail,msg=string} "获取字典详情树形结构"
// @Router /sysDictionaryDetail/getDictionaryTreeListByType [get]
func (s *DictionaryDetailApi) GetDictionaryTreeListByType(c *gin.Context) {
dictType := c.Query("type")
if dictType == "" {
response.FailWithMessage("字典类型不能为空", c)
return
}

list, err := dictionaryDetailService.GetDictionaryTreeListByType(dictType)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(gin.H{"list": list}, "获取成功", c)
}

// GetDictionaryDetailsByParent
// @Tags SysDictionaryDetail
// @Summary 根据父级ID获取字典详情
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query request.GetDictionaryDetailsByParentRequest true "查询参数"
// @Success 200 {object} response.Response{data=[]system.SysDictionaryDetail,msg=string} "获取字典详情列表"
// @Router /sysDictionaryDetail/getDictionaryDetailsByParent [get]
func (s *DictionaryDetailApi) GetDictionaryDetailsByParent(c *gin.Context) {
var req request.GetDictionaryDetailsByParentRequest
err := c.ShouldBindQuery(&req)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}

list, err := dictionaryDetailService.GetDictionaryDetailsByParent(req)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(gin.H{"list": list}, "获取成功", c)
}

// GetDictionaryPath
// @Tags SysDictionaryDetail
// @Summary 获取字典详情的完整路径
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param id query uint true "字典详情ID"
// @Success 200 {object} response.Response{data=[]system.SysDictionaryDetail,msg=string} "获取字典详情路径"
// @Router /sysDictionaryDetail/getDictionaryPath [get]
func (s *DictionaryDetailApi) GetDictionaryPath(c *gin.Context) {
idStr := c.Query("id")
if idStr == "" {
response.FailWithMessage("字典详情ID不能为空", c)
return
}

var id uint
if idUint64, err := strconv.ParseUint(idStr, 10, 32); err != nil {
response.FailWithMessage("字典详情ID格式错误", c)
return
} else {
id = uint(idUint64)
}

path, err := dictionaryDetailService.GetDictionaryPath(id)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(gin.H{"path": path}, "获取成功", c)
}
5 changes: 2 additions & 3 deletions server/middleware/casbin_rbac.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package middleware

import (
"strconv"
"strings"

"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"strconv"
"strings"
)

// CasbinHandler 拦截器
Expand Down
32 changes: 32 additions & 0 deletions server/model/system/request/sys_dictionary_detail.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,36 @@ import (
type SysDictionaryDetailSearch struct {
system.SysDictionaryDetail
request.PageInfo
ParentID *uint `json:"parentID" form:"parentID"` // 父级字典详情ID,用于查询指定父级下的子项
Level *int `json:"level" form:"level"` // 层级深度,用于查询指定层级的数据
}

// CreateSysDictionaryDetailRequest 创建字典详情请求
type CreateSysDictionaryDetailRequest struct {
Label string `json:"label" form:"label" binding:"required"` // 展示值
Value string `json:"value" form:"value" binding:"required"` // 字典值
Extend string `json:"extend" form:"extend"` // 扩展值
Status *bool `json:"status" form:"status"` // 启用状态
Sort int `json:"sort" form:"sort"` // 排序标记
SysDictionaryID int `json:"sysDictionaryID" form:"sysDictionaryID" binding:"required"` // 关联标记
ParentID *uint `json:"parentID" form:"parentID"` // 父级字典详情ID
}

// UpdateSysDictionaryDetailRequest 更新字典详情请求
type UpdateSysDictionaryDetailRequest struct {
ID uint `json:"ID" form:"ID" binding:"required"` // 主键ID
Label string `json:"label" form:"label" binding:"required"` // 展示值
Value string `json:"value" form:"value" binding:"required"` // 字典值
Extend string `json:"extend" form:"extend"` // 扩展值
Status *bool `json:"status" form:"status"` // 启用状态
Sort int `json:"sort" form:"sort"` // 排序标记
SysDictionaryID int `json:"sysDictionaryID" form:"sysDictionaryID" binding:"required"` // 关联标记
ParentID *uint `json:"parentID" form:"parentID"` // 父级字典详情ID
}

// GetDictionaryDetailsByParentRequest 根据父级ID获取字典详情请求
type GetDictionaryDetailsByParentRequest struct {
SysDictionaryID int `json:"sysDictionaryID" form:"sysDictionaryID" binding:"required"` // 字典ID
ParentID *uint `json:"parentID" form:"parentID"` // 父级字典详情ID,为空时获取顶级
IncludeChildren bool `json:"includeChildren" form:"includeChildren"` // 是否包含子级数据
}
10 changes: 6 additions & 4 deletions server/model/system/sys_dictionary.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import (
// 如果含有time.Time 请自行import time包
type SysDictionary struct {
global.GVA_MODEL
Name string `json:"name" form:"name" gorm:"column:name;comment:字典名(中)"` // 字典名(中)
Type string `json:"type" form:"type" gorm:"column:type;comment:字典名(英)"` // 字典名(英)
Status *bool `json:"status" form:"status" gorm:"column:status;comment:状态"` // 状态
Desc string `json:"desc" form:"desc" gorm:"column:desc;comment:描述"` // 描述
Name string `json:"name" form:"name" gorm:"column:name;comment:字典名(中)"` // 字典名(中)
Type string `json:"type" form:"type" gorm:"column:type;comment:字典名(英)"` // 字典名(英)
Status *bool `json:"status" form:"status" gorm:"column:status;comment:状态"` // 状态
Desc string `json:"desc" form:"desc" gorm:"column:desc;comment:描述"` // 描述
ParentID *uint `json:"parentID" form:"parentID" gorm:"column:parent_id;comment:父级字典ID"` // 父级字典ID
Children []SysDictionary `json:"children" gorm:"foreignKey:ParentID"` // 子字典
SysDictionaryDetails []SysDictionaryDetail `json:"sysDictionaryDetails" form:"sysDictionaryDetails"`
}

Expand Down
16 changes: 10 additions & 6 deletions server/model/system/sys_dictionary_detail.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import (
// 如果含有time.Time 请自行import time包
type SysDictionaryDetail struct {
global.GVA_MODEL
Label string `json:"label" form:"label" gorm:"column:label;comment:展示值"` // 展示值
Value string `json:"value" form:"value" gorm:"column:value;comment:字典值"` // 字典值
Extend string `json:"extend" form:"extend" gorm:"column:extend;comment:扩展值"` // 扩展值
Status *bool `json:"status" form:"status" gorm:"column:status;comment:启用状态"` // 启用状态
Sort int `json:"sort" form:"sort" gorm:"column:sort;comment:排序标记"` // 排序标记
SysDictionaryID int `json:"sysDictionaryID" form:"sysDictionaryID" gorm:"column:sys_dictionary_id;comment:关联标记"` // 关联标记
Label string `json:"label" form:"label" gorm:"column:label;comment:展示值"` // 展示值
Value string `json:"value" form:"value" gorm:"column:value;comment:字典值"` // 字典值
Extend string `json:"extend" form:"extend" gorm:"column:extend;comment:扩展值"` // 扩展值
Status *bool `json:"status" form:"status" gorm:"column:status;comment:启用状态"` // 启用状态
Sort int `json:"sort" form:"sort" gorm:"column:sort;comment:排序标记"` // 排序标记
SysDictionaryID int `json:"sysDictionaryID" form:"sysDictionaryID" gorm:"column:sys_dictionary_id;comment:关联标记"` // 关联标记
ParentID *uint `json:"parentID" form:"parentID" gorm:"column:parent_id;comment:父级字典详情ID"` // 父级字典详情ID
Children []SysDictionaryDetail `json:"children" gorm:"foreignKey:ParentID"` // 子字典详情
Level int `json:"level" form:"level" gorm:"column:level;comment:层级深度"` // 层级深度,从0开始
Path string `json:"path" form:"path" gorm:"column:path;comment:层级路径"` // 层级路径,如 "1,2,3"
}

func (SysDictionaryDetail) TableName() string {
Expand Down
8 changes: 6 additions & 2 deletions server/router/system/sys_dictionary_detail.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ func (s *DictionaryDetailRouter) InitSysDictionaryDetailRouter(Router *gin.Route
dictionaryDetailRouter.PUT("updateSysDictionaryDetail", dictionaryDetailApi.UpdateSysDictionaryDetail) // 更新SysDictionaryDetail
}
{
dictionaryDetailRouterWithoutRecord.GET("findSysDictionaryDetail", dictionaryDetailApi.FindSysDictionaryDetail) // 根据ID获取SysDictionaryDetail
dictionaryDetailRouterWithoutRecord.GET("getSysDictionaryDetailList", dictionaryDetailApi.GetSysDictionaryDetailList) // 获取SysDictionaryDetail列表
dictionaryDetailRouterWithoutRecord.GET("findSysDictionaryDetail", dictionaryDetailApi.FindSysDictionaryDetail) // 根据ID获取SysDictionaryDetail
dictionaryDetailRouterWithoutRecord.GET("getSysDictionaryDetailList", dictionaryDetailApi.GetSysDictionaryDetailList) // 获取SysDictionaryDetail列表
dictionaryDetailRouterWithoutRecord.GET("getDictionaryTreeList", dictionaryDetailApi.GetDictionaryTreeList) // 获取字典详情树形结构
dictionaryDetailRouterWithoutRecord.GET("getDictionaryTreeListByType", dictionaryDetailApi.GetDictionaryTreeListByType) // 根据字典类型获取字典详情树形结构
dictionaryDetailRouterWithoutRecord.GET("getDictionaryDetailsByParent", dictionaryDetailApi.GetDictionaryDetailsByParent) // 根据父级ID获取字典详情
dictionaryDetailRouterWithoutRecord.GET("getDictionaryPath", dictionaryDetailApi.GetDictionaryPath) // 获取字典详情的完整路径
}
}
44 changes: 40 additions & 4 deletions server/service/system/sys_dictionary.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package system

import (
"errors"

"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/gin-gonic/gin"

Expand Down Expand Up @@ -62,10 +63,11 @@ func (dictionaryService *DictionaryService) DeleteSysDictionary(sysDictionary sy
func (dictionaryService *DictionaryService) UpdateSysDictionary(sysDictionary *system.SysDictionary) (err error) {
var dict system.SysDictionary
sysDictionaryMap := map[string]interface{}{
"Name": sysDictionary.Name,
"Type": sysDictionary.Type,
"Status": sysDictionary.Status,
"Desc": sysDictionary.Desc,
"Name": sysDictionary.Name,
"Type": sysDictionary.Type,
"Status": sysDictionary.Status,
"Desc": sysDictionary.Desc,
"ParentID": sysDictionary.ParentID,
}
err = global.GVA_DB.Where("id = ?", sysDictionary.ID).First(&dict).Error
if err != nil {
Expand All @@ -77,6 +79,14 @@ func (dictionaryService *DictionaryService) UpdateSysDictionary(sysDictionary *s
return errors.New("存在相同的type,不允许创建")
}
}

// 检查是否会形成循环引用
if sysDictionary.ParentID != nil && *sysDictionary.ParentID != 0 {
if err := dictionaryService.checkCircularReference(sysDictionary.ID, *sysDictionary.ParentID); err != nil {
return err
}
}

err = global.GVA_DB.Model(&dict).Updates(sysDictionaryMap).Error
return err
}
Expand Down Expand Up @@ -113,6 +123,32 @@ func (dictionaryService *DictionaryService) GetSysDictionaryInfoList(c *gin.Cont
if req.Name != "" {
query = query.Where("name LIKE ? OR type LIKE ?", "%"+req.Name+"%", "%"+req.Name+"%")
}
// 预加载子字典
query = query.Preload("Children")
err = query.Find(&sysDictionarys).Error
return sysDictionarys, err
}

// checkCircularReference 检查是否会形成循环引用
func (dictionaryService *DictionaryService) checkCircularReference(currentID uint, parentID uint) error {
if currentID == parentID {
return errors.New("不能将字典设置为自己的父级")
}

// 递归检查父级链条
var parent system.SysDictionary
err := global.GVA_DB.Where("id = ?", parentID).First(&parent).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil // 父级不存在,允许设置
}
return err
}

// 如果父级还有父级,继续检查
if parent.ParentID != nil && *parent.ParentID != 0 {
return dictionaryService.checkCircularReference(currentID, *parent.ParentID)
}

return nil
}
Loading