354 lines
12 KiB
Go
354 lines
12 KiB
Go
package data
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"kra/internal/biz"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type dictionaryRepo struct{ data *Data }
|
|
|
|
func NewDictionaryRepo(data *Data) biz.DictionaryRepo { return &dictionaryRepo{data: data} }
|
|
|
|
type dictionaryPO struct {
|
|
ID uint `gorm:"primaryKey"`
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
|
Name string
|
|
Type string
|
|
Status bool
|
|
Desc string
|
|
ParentID *uint
|
|
}
|
|
|
|
func (dictionaryPO) TableName() string { return "sys_dictionaries" }
|
|
|
|
type dictionaryDetailPO struct {
|
|
ID uint `gorm:"primaryKey"`
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
|
Label string
|
|
Value string
|
|
Extend string
|
|
Status bool
|
|
Sort int
|
|
DictionaryID uint `gorm:"column:sys_dictionary_id;index"`
|
|
ParentID *uint
|
|
Level int
|
|
Path string
|
|
}
|
|
|
|
func (dictionaryDetailPO) TableName() string { return "sys_dictionary_details" }
|
|
|
|
func dictionaryFromPO(po dictionaryPO) *biz.Dictionary {
|
|
return &biz.Dictionary{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, Type: po.Type, Status: po.Status, Desc: po.Desc, ParentID: po.ParentID}
|
|
}
|
|
func detailFromPO(po dictionaryDetailPO) *biz.DictionaryDetail {
|
|
return &biz.DictionaryDetail{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Label: po.Label, Value: po.Value, Extend: po.Extend, Status: po.Status, Sort: po.Sort, DictionaryID: po.DictionaryID, ParentID: po.ParentID, Level: po.Level, Path: po.Path}
|
|
}
|
|
func parameterFromPO(po parameterPO) *biz.SystemParameter {
|
|
return &biz.SystemParameter{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, Key: po.Key, Value: po.Value, Desc: po.Desc}
|
|
}
|
|
|
|
func (r *dictionaryRepo) CreateDictionary(ctx context.Context, v *biz.Dictionary) error {
|
|
po := dictionaryPO{Name: v.Name, Type: v.Type, Status: v.Status, Desc: v.Desc, ParentID: v.ParentID}
|
|
if err := r.data.gormDB.WithContext(ctx).Create(&po).Error; err != nil {
|
|
return err
|
|
}
|
|
v.ID = po.ID
|
|
return nil
|
|
}
|
|
|
|
func (r *dictionaryRepo) ImportDictionary(ctx context.Context, dictionary *biz.Dictionary, details []*biz.DictionaryDetail) error {
|
|
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
po := dictionaryPO{Name: dictionary.Name, Type: dictionary.Type, Status: dictionary.Status, Desc: dictionary.Desc, ParentID: dictionary.ParentID}
|
|
if err := tx.Create(&po).Error; err != nil {
|
|
return err
|
|
}
|
|
dictionary.ID = po.ID
|
|
idMap := make(map[uint]uint, len(details))
|
|
created := make(map[uint]uint, len(details))
|
|
for index, item := range details {
|
|
// Incomplete detail rows are ignored to preserve import behavior.
|
|
if item.Label == "" || item.Value == "" {
|
|
continue
|
|
}
|
|
oldID := item.ID
|
|
detail := dictionaryDetailPO{Label: item.Label, Value: item.Value, Extend: item.Extend, Status: item.Status, Sort: item.Sort, DictionaryID: po.ID, Level: item.Level, Path: item.Path}
|
|
if err := tx.Create(&detail).Error; err != nil {
|
|
return err
|
|
}
|
|
created[uint(index)] = detail.ID
|
|
if oldID > 0 {
|
|
idMap[oldID] = detail.ID
|
|
}
|
|
}
|
|
for index, item := range details {
|
|
if item.ID == 0 || item.ParentID == nil || *item.ParentID == 0 {
|
|
continue
|
|
}
|
|
newID, exists := created[uint(index)]
|
|
newParentID, parentExists := idMap[*item.ParentID]
|
|
if !exists || !parentExists {
|
|
continue
|
|
}
|
|
if err := tx.Model(&dictionaryDetailPO{}).Where("id = ?", newID).Update("parent_id", newParentID).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
func (r *dictionaryRepo) UpdateDictionary(ctx context.Context, v *biz.Dictionary) error {
|
|
return r.data.gormDB.WithContext(ctx).Model(&dictionaryPO{}).Where("id = ?", v.ID).Updates(map[string]any{"name": v.Name, "type": v.Type, "status": v.Status, "desc": v.Desc, "parent_id": v.ParentID}).Error
|
|
}
|
|
func (r *dictionaryRepo) DeleteDictionary(ctx context.Context, id uint) error {
|
|
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
var count int64
|
|
if err := tx.Model(&dictionaryPO{}).Where("parent_id = ?", id).Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count > 0 {
|
|
return errors.New("存在子字典不可删除")
|
|
}
|
|
if err := tx.Where("sys_dictionary_id = ?", id).Delete(&dictionaryDetailPO{}).Error; err != nil {
|
|
return err
|
|
}
|
|
return tx.Delete(&dictionaryPO{}, id).Error
|
|
})
|
|
}
|
|
func (r *dictionaryRepo) FindDictionary(ctx context.Context, id uint, typ string, status *bool, details bool) (*biz.Dictionary, error) {
|
|
var po dictionaryPO
|
|
db := r.data.gormDB.WithContext(ctx)
|
|
active := true
|
|
if status != nil {
|
|
active = *status
|
|
}
|
|
err := db.Where("(type = ? OR id = ?) AND status = ?", typ, id, active).First(&po).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := dictionaryFromPO(po)
|
|
if details {
|
|
out.Details, _, err = r.ListDictionaryDetails(ctx, 0, 0, biz.DictionaryDetailFilter{DictionaryID: out.ID})
|
|
if err == nil {
|
|
filtered := out.Details[:0]
|
|
for _, item := range out.Details {
|
|
if item.Status {
|
|
filtered = append(filtered, item)
|
|
}
|
|
}
|
|
out.Details = filtered
|
|
}
|
|
}
|
|
return out, err
|
|
}
|
|
|
|
func (r *dictionaryRepo) ExportDictionary(ctx context.Context, id uint) (*biz.Dictionary, error) {
|
|
var po dictionaryPO
|
|
if err := r.data.gormDB.WithContext(ctx).First(&po, id).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
value := dictionaryFromPO(po)
|
|
details, _, err := r.ListDictionaryDetails(ctx, 0, 0, biz.DictionaryDetailFilter{DictionaryID: id})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
value.Details = details
|
|
return value, nil
|
|
}
|
|
func (r *dictionaryRepo) ListDictionaries(ctx context.Context, page, size int, name, typ string, details bool) ([]*biz.Dictionary, int64, error) {
|
|
db := r.data.gormDB.WithContext(ctx).Model(&dictionaryPO{})
|
|
if name != "" {
|
|
like := "%" + name + "%"
|
|
db = db.Where("name LIKE ? OR type LIKE ?", like, like)
|
|
}
|
|
var total int64
|
|
if err := db.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
var pos []dictionaryPO
|
|
if err := applyPagination(db.Order("id"), page, size, 0).Find(&pos).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
parentIDs := make([]uint, 0, len(pos))
|
|
dictionaryIDs := make([]uint, 0, len(pos))
|
|
for _, po := range pos {
|
|
parentIDs = append(parentIDs, po.ID)
|
|
dictionaryIDs = append(dictionaryIDs, po.ID)
|
|
}
|
|
childrenByParent := make(map[uint][]dictionaryPO)
|
|
if len(parentIDs) > 0 {
|
|
var children []dictionaryPO
|
|
if err := r.data.gormDB.WithContext(ctx).Where("parent_id IN ?", parentIDs).Order("id").Find(&children).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
for _, child := range children {
|
|
if child.ParentID != nil {
|
|
childrenByParent[*child.ParentID] = append(childrenByParent[*child.ParentID], child)
|
|
}
|
|
}
|
|
}
|
|
detailsByDictionary := make(map[uint][]dictionaryDetailPO)
|
|
if details && len(dictionaryIDs) > 0 {
|
|
var values []dictionaryDetailPO
|
|
if err := r.data.gormDB.WithContext(ctx).Where("sys_dictionary_id IN ?", dictionaryIDs).Order("sort").Find(&values).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
for _, value := range values {
|
|
detailsByDictionary[value.DictionaryID] = append(detailsByDictionary[value.DictionaryID], value)
|
|
}
|
|
}
|
|
out := make([]*biz.Dictionary, 0, len(pos))
|
|
for _, po := range pos {
|
|
v := dictionaryFromPO(po)
|
|
for _, child := range childrenByParent[po.ID] {
|
|
v.Children = append(v.Children, dictionaryFromPO(child))
|
|
}
|
|
if details {
|
|
for _, detail := range detailsByDictionary[v.ID] {
|
|
v.Details = append(v.Details, detailFromPO(detail))
|
|
}
|
|
}
|
|
out = append(out, v)
|
|
}
|
|
return out, total, nil
|
|
}
|
|
|
|
func (r *dictionaryRepo) CreateDictionaryDetail(ctx context.Context, v *biz.DictionaryDetail) error {
|
|
po := dictionaryDetailPO{Label: v.Label, Value: v.Value, Extend: v.Extend, Status: v.Status, Sort: v.Sort, DictionaryID: v.DictionaryID, ParentID: v.ParentID}
|
|
po.Level = 0
|
|
po.Path = ""
|
|
if v.ParentID != nil && *v.ParentID != 0 {
|
|
var parent dictionaryDetailPO
|
|
if err := r.data.gormDB.WithContext(ctx).First(&parent, *v.ParentID).Error; err != nil {
|
|
return err
|
|
}
|
|
po.Level = parent.Level + 1
|
|
po.Path = strings.Trim(strings.Join([]string{parent.Path, fmt.Sprint(parent.ID)}, ","), ",")
|
|
}
|
|
if err := r.data.gormDB.WithContext(ctx).Create(&po).Error; err != nil {
|
|
return err
|
|
}
|
|
v.ID = po.ID
|
|
v.Level = po.Level
|
|
v.Path = po.Path
|
|
return nil
|
|
}
|
|
func (r *dictionaryRepo) UpdateDictionaryDetail(ctx context.Context, v *biz.DictionaryDetail) error {
|
|
var po dictionaryDetailPO
|
|
if err := r.data.gormDB.WithContext(ctx).First(&po, v.ID).Error; err != nil {
|
|
return err
|
|
}
|
|
level, path := 0, ""
|
|
if v.ParentID != nil && *v.ParentID != 0 {
|
|
if *v.ParentID == v.ID {
|
|
return errors.New("不能将自身设为父级")
|
|
}
|
|
var parent dictionaryDetailPO
|
|
if err := r.data.gormDB.WithContext(ctx).First(&parent, *v.ParentID).Error; err != nil {
|
|
return err
|
|
}
|
|
if strings.Contains(","+parent.Path+",", fmt.Sprintf(",%d,", v.ID)) {
|
|
return errors.New("不能移动到自己的子级")
|
|
}
|
|
level = parent.Level + 1
|
|
path = strings.Trim(strings.Join([]string{parent.Path, fmt.Sprint(parent.ID)}, ","), ",")
|
|
}
|
|
oldPrefix := strings.Trim(strings.Join([]string{po.Path, fmt.Sprint(po.ID)}, ","), ",")
|
|
newPrefix := strings.Trim(strings.Join([]string{path, fmt.Sprint(po.ID)}, ","), ",")
|
|
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
if err := tx.Model(&dictionaryDetailPO{}).Where("id = ?", v.ID).Updates(map[string]any{"label": v.Label, "value": v.Value, "extend": v.Extend, "status": v.Status, "sort": v.Sort, "sys_dictionary_id": v.DictionaryID, "parent_id": v.ParentID, "level": level, "path": path}).Error; err != nil {
|
|
return err
|
|
}
|
|
return tx.Model(&dictionaryDetailPO{}).Where("path = ? OR path LIKE ?", oldPrefix, oldPrefix+",%").Update("path", gorm.Expr("REPLACE(path, ?, ?)", oldPrefix, newPrefix)).Error
|
|
})
|
|
}
|
|
func (r *dictionaryRepo) DeleteDictionaryDetail(ctx context.Context, id uint) error {
|
|
var count int64
|
|
if err := r.data.gormDB.WithContext(ctx).Model(&dictionaryDetailPO{}).Where("parent_id = ?", id).Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count > 0 {
|
|
return errors.New("存在子级不可删除")
|
|
}
|
|
return r.data.gormDB.WithContext(ctx).Delete(&dictionaryDetailPO{}, id).Error
|
|
}
|
|
func (r *dictionaryRepo) FindDictionaryDetail(ctx context.Context, id uint) (*biz.DictionaryDetail, error) {
|
|
var po dictionaryDetailPO
|
|
if err := r.data.gormDB.WithContext(ctx).First(&po, id).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return detailFromPO(po), nil
|
|
}
|
|
func (r *dictionaryRepo) ListDictionaryDetails(ctx context.Context, page, size int, filter biz.DictionaryDetailFilter) ([]*biz.DictionaryDetail, int64, error) {
|
|
db := r.data.gormDB.WithContext(ctx).Model(&dictionaryDetailPO{})
|
|
if filter.DictionaryID != 0 {
|
|
db = db.Where("sys_dictionary_id = ?", filter.DictionaryID)
|
|
}
|
|
if filter.Label != "" {
|
|
db = db.Where("label LIKE ?", "%"+filter.Label+"%")
|
|
}
|
|
if filter.Value != "" {
|
|
db = db.Where("value = ?", filter.Value)
|
|
}
|
|
if filter.Status != nil {
|
|
db = db.Where("status = ?", *filter.Status)
|
|
}
|
|
if filter.ParentID != nil {
|
|
db = db.Where("parent_id = ?", *filter.ParentID)
|
|
}
|
|
if filter.Level != nil {
|
|
db = db.Where("level = ?", *filter.Level)
|
|
}
|
|
var total int64
|
|
if err := db.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
var pos []dictionaryDetailPO
|
|
if err := applyPagination(db.Order("sort,id"), page, size, 100).Find(&pos).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
out := make([]*biz.DictionaryDetail, 0, len(pos))
|
|
for _, po := range pos {
|
|
out = append(out, detailFromPO(po))
|
|
}
|
|
return out, total, nil
|
|
}
|
|
func (r *dictionaryRepo) DictionaryDetailTree(ctx context.Context, dictionaryID uint, typ string) ([]*biz.DictionaryDetail, error) {
|
|
if dictionaryID == 0 {
|
|
active := true
|
|
dictionary, err := r.FindDictionary(ctx, 0, typ, &active, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dictionaryID = dictionary.ID
|
|
}
|
|
items, _, err := r.ListDictionaryDetails(ctx, 0, 0, biz.DictionaryDetailFilter{DictionaryID: dictionaryID})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
byID := map[uint]*biz.DictionaryDetail{}
|
|
for _, item := range items {
|
|
item.Children = []*biz.DictionaryDetail{}
|
|
byID[item.ID] = item
|
|
}
|
|
roots := []*biz.DictionaryDetail{}
|
|
for _, item := range items {
|
|
if item.ParentID != nil && byID[*item.ParentID] != nil {
|
|
byID[*item.ParentID].Children = append(byID[*item.ParentID].Children, item)
|
|
} else {
|
|
roots = append(roots, item)
|
|
}
|
|
}
|
|
return roots, nil
|
|
}
|