kra-oa/internal/data/repository/department.go

271 lines
8.6 KiB
Go

package system
import (
"context"
"errors"
"strconv"
"strings"
"time"
"kra/internal/biz"
"gorm.io/gorm"
)
type departmentRepo struct{ data Provider }
func NewDepartmentRepo(data Provider) biz.DepartmentRepo { return &departmentRepo{data: data} }
type departmentPO struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string `gorm:"index"`
ParentID uint
Ancestors string
Sort int
LeaderID uint
Status *bool `gorm:"default:true"`
}
func (departmentPO) TableName() string { return "sys_departments" }
type userDepartmentPO struct {
// Match the reference join model: no composite primary key, so repeated IDs in a
// request are persisted in the same order/shape as the reference service.
UserID uint `gorm:"column:sys_user_id"`
DepartmentID uint `gorm:"column:sys_department_id"`
}
func (userDepartmentPO) TableName() string { return "sys_user_departments" }
type authorityDepartmentPO struct {
AuthorityID uint `gorm:"column:sys_authority_authority_id;index"`
DepartmentID uint `gorm:"column:sys_department_id"`
}
func (authorityDepartmentPO) TableName() string { return "sys_authority_departments" }
func deptFromPO(po departmentPO) *biz.Department {
return &biz.Department{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, ParentID: po.ParentID, Ancestors: po.Ancestors, Sort: po.Sort, LeaderID: po.LeaderID, Status: po.Status}
}
func (r *departmentRepo) attachDepartmentLeaders(ctx context.Context, departments []*biz.Department) error {
leaderIDs := make([]uint, 0, len(departments))
for _, department := range departments {
if department.LeaderID != 0 {
leaderIDs = append(leaderIDs, department.LeaderID)
}
}
if len(leaderIDs) == 0 {
return nil
}
var leaders []userPO
if err := r.data.DB().WithContext(ctx).Where("id IN ?", leaderIDs).Find(&leaders).Error; err != nil {
return err
}
leaderByID := make(map[uint]*biz.User, len(leaders))
for i := range leaders {
leaderByID[leaders[i].ID] = baseBizUser(&leaders[i])
}
for _, department := range departments {
department.Leader = leaderByID[department.LeaderID]
}
return nil
}
func (r *departmentRepo) CreateDepartment(ctx context.Context, v *biz.Department) error {
v.Ancestors = "0"
if v.ParentID != 0 {
var parent departmentPO
if err := r.data.DB().WithContext(ctx).First(&parent, v.ParentID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("父部门不存在")
}
return err
}
v.Ancestors = strings.Trim(parent.Ancestors+","+strconv.FormatUint(uint64(parent.ID), 10), ",")
}
return r.data.DB().WithContext(ctx).Create(&departmentPO{Name: v.Name, ParentID: v.ParentID, Ancestors: v.Ancestors, Sort: v.Sort, LeaderID: v.LeaderID, Status: v.Status}).Error
}
func (r *departmentRepo) UpdateDepartment(ctx context.Context, v *biz.Department) error {
if v.ParentID == v.ID {
return errors.New("父部门不能是自己")
}
v.Ancestors = "0"
if v.ParentID != 0 {
var parent departmentPO
if err := r.data.DB().WithContext(ctx).First(&parent, v.ParentID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("父部门不存在")
}
return err
}
v.Ancestors = strings.Trim(parent.Ancestors+","+strconv.FormatUint(uint64(parent.ID), 10), ",")
}
return r.data.DB().WithContext(ctx).Model(&departmentPO{}).Where("id = ?", v.ID).Updates(map[string]any{"name": v.Name, "parent_id": v.ParentID, "ancestors": v.Ancestors, "sort": v.Sort, "leader_id": v.LeaderID, "status": v.Status}).Error
}
func (r *departmentRepo) DeleteDepartment(ctx context.Context, id uint) error {
if id == 0 {
return errors.New("部门ID不能为空")
}
var count int64
if err := r.data.DB().WithContext(ctx).Model(&departmentPO{}).Where("parent_id = ?", id).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("存在子部门,不允许删除")
}
if err := r.data.DB().WithContext(ctx).Model(&userPO{}).Where("dept_id = ?", id).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("该部门下存在用户,不允许删除")
}
if err := r.data.DB().WithContext(ctx).Model(&userDepartmentPO{}).Where("sys_department_id = ?", id).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("该部门下存在用户,不允许删除")
}
return r.data.DB().WithContext(ctx).Delete(&departmentPO{}, id).Error
}
func (r *departmentRepo) FindDepartment(ctx context.Context, id uint) (*biz.Department, error) {
var po departmentPO
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
return nil, err
}
department := deptFromPO(po)
if err := r.attachDepartmentLeaders(ctx, []*biz.Department{department}); err != nil {
return nil, err
}
return department, nil
}
func (r *departmentRepo) ListDepartments(ctx context.Context, name string) ([]*biz.Department, error) {
var pos []departmentPO
db := r.data.DB().WithContext(ctx).Order("sort")
if name != "" {
db = db.Where("name LIKE ?", "%"+name+"%")
}
if err := db.Find(&pos).Error; err != nil {
return nil, err
}
nodes := map[uint]*biz.Department{}
items := make([]*biz.Department, 0, len(pos))
for _, po := range pos {
item := deptFromPO(po)
if name == "" {
// Recursive GORM child queries initialize leaf collections to an empty
// slice. Preserve that tree response shape while search and single-item
// queries keep the model's nil children value.
item.Children = []*biz.Department{}
}
nodes[po.ID] = item
items = append(items, item)
}
if err := r.attachDepartmentLeaders(ctx, items); err != nil {
return nil, err
}
if name != "" {
return items, nil
}
roots := make([]*biz.Department, 0)
for _, po := range pos {
n := nodes[po.ID]
if p := nodes[po.ParentID]; p != nil {
p.Children = append(p.Children, n)
} else if po.ParentID == 0 {
roots = append(roots, n)
}
}
return roots, nil
}
func (r *departmentRepo) DepartmentUserIDs(ctx context.Context, id uint) ([]uint, error) {
var ids []uint
err := r.data.DB().WithContext(ctx).Model(&userDepartmentPO{}).Where("sys_department_id = ?", id).Pluck("sys_user_id", &ids).Error
return ids, err
}
func (r *departmentRepo) SetDepartmentUsers(ctx context.Context, id uint, ids []uint) error {
return r.replaceUserDepartments(ctx, id, ids)
}
func (r *departmentRepo) replaceUserDepartments(ctx context.Context, id uint, ids []uint) error {
if id == 0 {
return errors.New("部门ID不能为空")
}
return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var current []uint
if err := tx.Model(&userDepartmentPO{}).Where("sys_department_id = ?", id).Pluck("sys_user_id", &current).Error; err != nil {
return err
}
if err := tx.Where("sys_department_id = ?", id).Delete(&userDepartmentPO{}).Error; err != nil {
return err
}
links := make([]userDepartmentPO, 0, len(ids))
for _, uid := range ids {
links = append(links, userDepartmentPO{UserID: uid, DepartmentID: id})
}
if len(links) > 0 {
if err := tx.Create(&links).Error; err != nil {
return err
}
}
target := make(map[uint]struct{}, len(ids))
for _, uid := range ids {
target[uid] = struct{}{}
}
removed := make([]uint, 0)
for _, uid := range current {
if _, ok := target[uid]; !ok {
removed = append(removed, uid)
}
}
if len(removed) > 0 {
if err := tx.Model(&userPO{}).Where("dept_id = ? AND id IN ?", id, removed).Update("dept_id", 0).Error; err != nil {
return err
}
}
if len(ids) > 0 {
if err := tx.Model(&userPO{}).Where("dept_id = ? AND id IN ?", 0, ids).Update("dept_id", id).Error; err != nil {
return err
}
}
return nil
})
}
func (r *departmentRepo) SetUserDepartments(ctx context.Context, uid uint, ids []uint, primary uint) error {
return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var user userPO
if err := tx.First(&user, uid).Error; err != nil {
return errors.New("查询用户数据失败")
}
if err := tx.Where("sys_user_id = ?", uid).Delete(&userDepartmentPO{}).Error; err != nil {
return err
}
links := make([]userDepartmentPO, 0, len(ids))
for _, id := range ids {
links = append(links, userDepartmentPO{UserID: uid, DepartmentID: id})
}
if len(links) > 0 {
if err := tx.Create(&links).Error; err != nil {
return err
}
}
if primary == 0 && len(ids) > 0 {
primary = ids[0]
}
if primary != 0 {
inSet := false
for _, id := range ids {
if id == primary {
inSet = true
break
}
}
if !inSet {
return errors.New("主部门必须在归属部门范围内")
}
}
return tx.Model(&userPO{}).Where("id = ?", uid).Update("dept_id", primary).Error
})
}