kra/internal/biz/system/dictionary.go

358 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}