358 lines
12 KiB
Go
358 lines
12 KiB
Go
package system
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"errors"
|
||
"strconv"
|
||
)
|
||
|
||
// Dictionary 字典实体
|
||
type Dictionary struct {
|
||
ID uint
|
||
Name string
|
||
Type string
|
||
Status *bool
|
||
Desc string
|
||
ParentID *uint
|
||
Children []*Dictionary
|
||
}
|
||
|
||
// DictionaryDetail 字典详情实体
|
||
type DictionaryDetail struct {
|
||
ID uint
|
||
Label string
|
||
Value string
|
||
Extend string
|
||
Status *bool
|
||
Sort int
|
||
SysDictionaryID uint
|
||
ParentID *uint
|
||
Level int
|
||
Path string
|
||
Disabled bool
|
||
Children []*DictionaryDetail
|
||
}
|
||
|
||
// DictionaryRepo 字典仓储接口
|
||
type DictionaryRepo interface {
|
||
Create(ctx context.Context, dict *Dictionary) error
|
||
Update(ctx context.Context, dict *Dictionary) error
|
||
Delete(ctx context.Context, id uint) error
|
||
FindByID(ctx context.Context, id uint) (*Dictionary, error)
|
||
FindByType(ctx context.Context, typ string) (*Dictionary, error)
|
||
GetByTypeOrID(ctx context.Context, typ string, id uint, status *bool) (*Dictionary, error)
|
||
List(ctx context.Context, name string) ([]*Dictionary, error)
|
||
CheckCircularReference(ctx context.Context, currentID, parentID uint) error
|
||
}
|
||
|
||
// DictionaryDetailRepo 字典详情仓储接口
|
||
type DictionaryDetailRepo interface {
|
||
Create(ctx context.Context, detail *DictionaryDetail) error
|
||
Update(ctx context.Context, detail *DictionaryDetail) error
|
||
Delete(ctx context.Context, id uint) error
|
||
FindByID(ctx context.Context, id uint) (*DictionaryDetail, error)
|
||
List(ctx context.Context, page, pageSize int, filters map[string]interface{}) ([]*DictionaryDetail, int64, error)
|
||
GetListByDictionaryID(ctx context.Context, dictionaryID uint) ([]*DictionaryDetail, error)
|
||
GetTreeList(ctx context.Context, dictionaryID uint) ([]*DictionaryDetail, error)
|
||
GetByParent(ctx context.Context, dictionaryID uint, parentID *uint, includeChildren bool) ([]*DictionaryDetail, error)
|
||
GetByDictionaryIDAndValue(ctx context.Context, dictionaryID uint, value string) (*DictionaryDetail, error)
|
||
GetByTypeAndValue(ctx context.Context, typ, value string) (*DictionaryDetail, error)
|
||
GetTreeListByType(ctx context.Context, typ string) ([]*DictionaryDetail, error)
|
||
GetListByType(ctx context.Context, typ string) ([]*DictionaryDetail, error)
|
||
HasChildren(ctx context.Context, id uint) (bool, error)
|
||
CheckCircularReference(ctx context.Context, id, parentID uint) bool
|
||
UpdateChildrenLevelAndPath(ctx context.Context, parentID uint) error
|
||
GetPath(ctx context.Context, id uint) ([]*DictionaryDetail, error)
|
||
DeleteByDictionaryID(ctx context.Context, dictionaryID uint) error
|
||
BatchCreate(ctx context.Context, details []*DictionaryDetail) error
|
||
BatchUpdateParentID(ctx context.Context, idMap map[uint]uint) error
|
||
}
|
||
|
||
// DictionaryUsecase 字典用例
|
||
type DictionaryUsecase struct {
|
||
repo DictionaryRepo
|
||
detailRepo DictionaryDetailRepo
|
||
}
|
||
|
||
// NewDictionaryUsecase 创建字典用例
|
||
func NewDictionaryUsecase(repo DictionaryRepo, detailRepo DictionaryDetailRepo) *DictionaryUsecase {
|
||
return &DictionaryUsecase{repo: repo, detailRepo: detailRepo}
|
||
}
|
||
|
||
// CreateDictionary 创建字典
|
||
func (uc *DictionaryUsecase) CreateDictionary(ctx context.Context, dict *Dictionary) error {
|
||
// 检查type是否重复
|
||
existing, err := uc.repo.FindByType(ctx, dict.Type)
|
||
if err == nil && existing != nil {
|
||
return errors.New("存在相同的type,不允许创建")
|
||
}
|
||
return uc.repo.Create(ctx, dict)
|
||
}
|
||
|
||
// UpdateDictionary 更新字典
|
||
func (uc *DictionaryUsecase) UpdateDictionary(ctx context.Context, dict *Dictionary) error {
|
||
old, err := uc.repo.FindByID(ctx, dict.ID)
|
||
if err != nil {
|
||
return errors.New("查询字典数据失败")
|
||
}
|
||
// 如果type变更,检查是否重复
|
||
if old.Type != dict.Type {
|
||
existing, err := uc.repo.FindByType(ctx, dict.Type)
|
||
if err == nil && existing != nil {
|
||
return errors.New("存在相同的type,不允许创建")
|
||
}
|
||
}
|
||
// 检查循环引用
|
||
if dict.ParentID != nil && *dict.ParentID != 0 {
|
||
if err := uc.repo.CheckCircularReference(ctx, dict.ID, *dict.ParentID); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return uc.repo.Update(ctx, dict)
|
||
}
|
||
|
||
// DeleteDictionary 删除字典
|
||
func (uc *DictionaryUsecase) DeleteDictionary(ctx context.Context, id uint) error {
|
||
dict, err := uc.repo.FindByID(ctx, id)
|
||
if err != nil {
|
||
return errors.New("请不要搞事")
|
||
}
|
||
if err := uc.repo.Delete(ctx, id); err != nil {
|
||
return err
|
||
}
|
||
// 删除关联的字典详情
|
||
return uc.detailRepo.DeleteByDictionaryID(ctx, dict.ID)
|
||
}
|
||
|
||
// GetDictionary 获取字典
|
||
func (uc *DictionaryUsecase) GetDictionary(ctx context.Context, typ string, id uint, status *bool) (*Dictionary, error) {
|
||
return uc.repo.GetByTypeOrID(ctx, typ, id, status)
|
||
}
|
||
|
||
// GetDictionaryList 获取字典列表
|
||
func (uc *DictionaryUsecase) GetDictionaryList(ctx context.Context, name string) ([]*Dictionary, error) {
|
||
return uc.repo.List(ctx, name)
|
||
}
|
||
|
||
// ExportDictionary 导出字典
|
||
func (uc *DictionaryUsecase) ExportDictionary(ctx context.Context, id uint) (map[string]interface{}, error) {
|
||
dict, err := uc.repo.FindByID(ctx, id)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
details, err := uc.detailRepo.GetListByDictionaryID(ctx, dict.ID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 清理详情数据
|
||
var cleanDetails []map[string]interface{}
|
||
for _, detail := range details {
|
||
cleanDetail := map[string]interface{}{
|
||
"label": detail.Label,
|
||
"value": detail.Value,
|
||
"extend": detail.Extend,
|
||
"status": detail.Status,
|
||
"sort": detail.Sort,
|
||
"level": detail.Level,
|
||
"path": detail.Path,
|
||
}
|
||
cleanDetails = append(cleanDetails, cleanDetail)
|
||
}
|
||
|
||
return map[string]interface{}{
|
||
"name": dict.Name,
|
||
"type": dict.Type,
|
||
"status": dict.Status,
|
||
"desc": dict.Desc,
|
||
"sysDictionaryDetails": cleanDetails,
|
||
}, nil
|
||
}
|
||
|
||
// ImportDictionary 导入字典
|
||
func (uc *DictionaryUsecase) ImportDictionary(ctx context.Context, jsonStr string) error {
|
||
var importData struct {
|
||
Name string `json:"name"`
|
||
Type string `json:"type"`
|
||
Status *bool `json:"status"`
|
||
Desc string `json:"desc"`
|
||
SysDictionaryDetails []*DictionaryDetail `json:"sysDictionaryDetails"`
|
||
}
|
||
if err := json.Unmarshal([]byte(jsonStr), &importData); err != nil {
|
||
return errors.New("JSON 格式错误: " + err.Error())
|
||
}
|
||
if importData.Name == "" {
|
||
return errors.New("字典名称不能为空")
|
||
}
|
||
if importData.Type == "" {
|
||
return errors.New("字典类型不能为空")
|
||
}
|
||
// 检查type是否重复
|
||
existing, err := uc.repo.FindByType(ctx, importData.Type)
|
||
if err == nil && existing != nil {
|
||
return errors.New("存在相同的type,不允许导入")
|
||
}
|
||
|
||
// 创建字典
|
||
dict := &Dictionary{
|
||
Name: importData.Name,
|
||
Type: importData.Type,
|
||
Status: importData.Status,
|
||
Desc: importData.Desc,
|
||
}
|
||
if err := uc.repo.Create(ctx, dict); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 处理字典详情
|
||
if len(importData.SysDictionaryDetails) > 0 {
|
||
idMap := make(map[uint]uint)
|
||
for _, detail := range importData.SysDictionaryDetails {
|
||
if detail.Label == "" || detail.Value == "" {
|
||
continue
|
||
}
|
||
oldID := detail.ID
|
||
newDetail := &DictionaryDetail{
|
||
Label: detail.Label,
|
||
Value: detail.Value,
|
||
Extend: detail.Extend,
|
||
Status: detail.Status,
|
||
Sort: detail.Sort,
|
||
Level: detail.Level,
|
||
Path: detail.Path,
|
||
SysDictionaryID: dict.ID,
|
||
}
|
||
if err := uc.detailRepo.Create(ctx, newDetail); err != nil {
|
||
return err
|
||
}
|
||
if oldID > 0 {
|
||
idMap[oldID] = newDetail.ID
|
||
}
|
||
}
|
||
// 更新parent_id关系
|
||
if len(idMap) > 0 {
|
||
if err := uc.detailRepo.BatchUpdateParentID(ctx, idMap); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// CreateDictionaryDetail 创建字典详情
|
||
func (uc *DictionaryUsecase) CreateDictionaryDetail(ctx context.Context, detail *DictionaryDetail) error {
|
||
// 计算层级和路径
|
||
if detail.ParentID != nil && *detail.ParentID != 0 {
|
||
parent, err := uc.detailRepo.FindByID(ctx, *detail.ParentID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
detail.Level = parent.Level + 1
|
||
if parent.Path == "" {
|
||
detail.Path = strconv.Itoa(int(parent.ID))
|
||
} else {
|
||
detail.Path = parent.Path + "," + strconv.Itoa(int(parent.ID))
|
||
}
|
||
} else {
|
||
detail.Level = 0
|
||
detail.Path = ""
|
||
}
|
||
return uc.detailRepo.Create(ctx, detail)
|
||
}
|
||
|
||
// UpdateDictionaryDetail 更新字典详情
|
||
func (uc *DictionaryUsecase) UpdateDictionaryDetail(ctx context.Context, detail *DictionaryDetail) error {
|
||
// 如果更新了父级ID,重新计算层级和路径
|
||
if detail.ParentID != nil && *detail.ParentID != 0 {
|
||
parent, err := uc.detailRepo.FindByID(ctx, *detail.ParentID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
// 检查循环引用
|
||
if uc.detailRepo.CheckCircularReference(ctx, detail.ID, *detail.ParentID) {
|
||
return errors.New("不能将字典详情设置为自己或其子项的父级")
|
||
}
|
||
detail.Level = parent.Level + 1
|
||
if parent.Path == "" {
|
||
detail.Path = strconv.Itoa(int(parent.ID))
|
||
} else {
|
||
detail.Path = parent.Path + "," + strconv.Itoa(int(parent.ID))
|
||
}
|
||
} else {
|
||
detail.Level = 0
|
||
detail.Path = ""
|
||
}
|
||
if err := uc.detailRepo.Update(ctx, detail); err != nil {
|
||
return err
|
||
}
|
||
// 更新所有子项的层级和路径
|
||
return uc.detailRepo.UpdateChildrenLevelAndPath(ctx, detail.ID)
|
||
}
|
||
|
||
// DeleteDictionaryDetail 删除字典详情
|
||
func (uc *DictionaryUsecase) DeleteDictionaryDetail(ctx context.Context, id uint) error {
|
||
hasChildren, err := uc.detailRepo.HasChildren(ctx, id)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if hasChildren {
|
||
return errors.New("该字典详情下还有子项,无法删除")
|
||
}
|
||
return uc.detailRepo.Delete(ctx, id)
|
||
}
|
||
|
||
// GetDictionaryDetail 获取字典详情
|
||
func (uc *DictionaryUsecase) GetDictionaryDetail(ctx context.Context, id uint) (*DictionaryDetail, error) {
|
||
return uc.detailRepo.FindByID(ctx, id)
|
||
}
|
||
|
||
// GetDictionaryDetailList 获取字典详情列表
|
||
func (uc *DictionaryUsecase) GetDictionaryDetailList(ctx context.Context, page, pageSize int, filters map[string]interface{}) ([]*DictionaryDetail, int64, error) {
|
||
return uc.detailRepo.List(ctx, page, pageSize, filters)
|
||
}
|
||
|
||
// GetDictionaryTreeList 获取字典树形列表
|
||
func (uc *DictionaryUsecase) GetDictionaryTreeList(ctx context.Context, dictionaryID uint) ([]*DictionaryDetail, error) {
|
||
return uc.detailRepo.GetTreeList(ctx, dictionaryID)
|
||
}
|
||
|
||
// GetDictionaryDetailsByParent 根据父级获取字典详情
|
||
func (uc *DictionaryUsecase) GetDictionaryDetailsByParent(ctx context.Context, dictionaryID uint, parentID *uint, includeChildren bool) ([]*DictionaryDetail, error) {
|
||
return uc.detailRepo.GetByParent(ctx, dictionaryID, parentID, includeChildren)
|
||
}
|
||
|
||
// GetDictionaryInfoByValue 根据字典ID和值获取详情
|
||
func (uc *DictionaryUsecase) GetDictionaryInfoByValue(ctx context.Context, dictionaryID uint, value string) (*DictionaryDetail, error) {
|
||
return uc.detailRepo.GetByDictionaryIDAndValue(ctx, dictionaryID, value)
|
||
}
|
||
|
||
// GetDictionaryInfoByTypeValue 根据字典类型和值获取详情
|
||
func (uc *DictionaryUsecase) GetDictionaryInfoByTypeValue(ctx context.Context, typ, value string) (*DictionaryDetail, error) {
|
||
return uc.detailRepo.GetByTypeAndValue(ctx, typ, value)
|
||
}
|
||
|
||
// GetDictionaryTreeListByType 根据类型获取树形列表
|
||
func (uc *DictionaryUsecase) GetDictionaryTreeListByType(ctx context.Context, typ string) ([]*DictionaryDetail, error) {
|
||
return uc.detailRepo.GetTreeListByType(ctx, typ)
|
||
}
|
||
|
||
// GetDictionaryListByType 根据类型获取列表
|
||
func (uc *DictionaryUsecase) GetDictionaryListByType(ctx context.Context, typ string) ([]*DictionaryDetail, error) {
|
||
return uc.detailRepo.GetListByType(ctx, typ)
|
||
}
|
||
|
||
// GetDictionaryPath 获取字典详情路径
|
||
func (uc *DictionaryUsecase) GetDictionaryPath(ctx context.Context, id uint) ([]*DictionaryDetail, error) {
|
||
return uc.detailRepo.GetPath(ctx, id)
|
||
}
|
||
|
||
// GetDictionaryPathByValue 根据值获取字典详情路径
|
||
func (uc *DictionaryUsecase) GetDictionaryPathByValue(ctx context.Context, dictionaryID uint, value string) ([]*DictionaryDetail, error) {
|
||
detail, err := uc.detailRepo.GetByDictionaryIDAndValue(ctx, dictionaryID, value)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return uc.detailRepo.GetPath(ctx, detail.ID)
|
||
}
|