Files
iotlcm/admin-svr/server/internal/logic/agent/agent_dept.go
T

1051 lines
33 KiB
Go

package agent
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"time"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/event"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rolepolicy"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/do"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
type sAgentDept struct {
*sAgentMenu
}
func NewAgentDept() *sAgentDept {
return &sAgentDept{
sAgentMenu: &sAgentMenu{
agentBase: newAgentBase(),
},
}
}
func init() {
service.RegisterAgentDept(NewAgentDept())
}
func (s *sAgentDept) DeptList(ctx context.Context, in *model.AgentDeptListParam) (list []*model.AgentEnterpriseCustomerModel, total int, err error) {
if in == nil {
in = &model.AgentDeptListParam{}
}
in.NormalizePagination()
list = make([]*model.AgentEnterpriseCustomerModel, 0)
currentDeptId := contexts.GetDeptId(ctx)
if currentDeptId == 0 {
return nil, 0, gerror.New("当前用户未绑定组织")
}
m := s.deptModel(ctx).
Where(dao.SysDept.Columns().TenantId, contexts.GetTenantId(ctx)).
Where(dao.SysDept.Columns().ParentId, currentDeptId).
Where(dao.SysDept.Columns().DeptType, 1)
if in.DeptName != "" {
m = m.WhereLike(dao.SysDept.Columns().DeptName, "%"+in.DeptName+"%")
}
if in.Status != "" {
m = m.Where(dao.SysDept.Columns().Status, in.Status)
}
err = m.Order(dao.SysDept.Columns().DeptId, "ASC").
Page(in.Page, in.PageSize).
ScanAndCount(&list, &total, false)
if err != nil {
return nil, 0, err
}
if err = s.fillEnterpriseDefaultUsers(ctx, list); err != nil {
return nil, 0, err
}
return list, total, nil
}
func (s *sAgentDept) DeptView(ctx context.Context, deptId int64) (res *model.AgentEnterpriseCustomerModel, err error) {
if err = s.ensureEnterpriseDept(ctx, deptId); err != nil {
return nil, err
}
err = s.deptModel(ctx).Where(dao.SysDept.Columns().DeptId, deptId).Scan(&res)
if err != nil {
return nil, err
}
if res == nil || res.DeptId == 0 {
return nil, gerror.New("企业客户不存在")
}
if err = s.fillEnterpriseDefaultUsers(ctx, []*model.AgentEnterpriseCustomerModel{res}); err != nil {
return nil, err
}
return res, nil
}
func (s *sAgentDept) DeptSave(ctx context.Context, in *model.AgentDeptSaveParam) (deptId int64, err error) {
user := contexts.GetUser(ctx)
if user == nil {
return 0, gerror.New("登录已过期")
}
if user.DeptId == 0 {
return 0, gerror.New("当前用户未绑定组织")
}
if err = s.validateEnterpriseSave(ctx, user.TenantId, in); err != nil {
return 0, err
}
var roleId, userId int64
isAdd := in.DeptId == 0
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
if isAdd {
deptId, roleId, userId, err = s.addEnterpriseDept(ctx, tx, user, in)
return err
}
deptId = in.DeptId
roleId, userId, err = s.editEnterpriseDept(ctx, tx, user, in)
return err
})
if err != nil {
return 0, err
}
if err = service.SysDept().RefreshDeptAncestors(ctx); err != nil {
return 0, err
}
if isAdd {
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysDeptCreate, deptId)
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysRoleCreate, roleId)
event.EventsInstance().Emit(ctx, consts.EventKeyUserCreate, userId)
} else {
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysDeptUpdate, deptId)
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysRoleUpdate, roleId)
event.EventsInstance().Emit(ctx, consts.EventKeyUserUpdate, userId)
}
return deptId, nil
}
func (s *sAgentDept) EnsureCurrentDeptDefaultRole(ctx context.Context) error {
if contexts.GetUser(ctx) == nil {
return gerror.New("登录已过期")
}
deptID := contexts.GetDeptId(ctx)
if deptID <= 0 {
return gerror.New("当前用户未绑定组织")
}
var dept *entity.SysDept
if err := s.deptModel(ctx).Where(dao.SysDept.Columns().DeptId, deptID).Scan(&dept); err != nil {
return err
}
if dept == nil {
return gerror.New("组织不存在")
}
if dept.DeptType == 1 && dept.DeptRole <= 0 {
return gerror.Newf("机构%d未关联默认角色,请联系平台管理员核对机构角色绑定", deptID)
}
return nil
}
func (s *sAgentDept) validateEnterpriseSave(ctx context.Context, tenantId string, in *model.AgentDeptSaveParam) error {
in.DeptName = strings.TrimSpace(in.DeptName)
in.UserName = strings.TrimSpace(in.UserName)
in.Password = strings.TrimSpace(in.Password)
in.Phone = strings.TrimSpace(in.Phone)
in.Email = strings.TrimSpace(in.Email)
in.AreaCode = strings.TrimSpace(in.AreaCode)
in.Address = strings.TrimSpace(in.Address)
in.CreditCode = strings.TrimSpace(in.CreditCode)
if in.DeptName == "" {
return gerror.New("企业名称不能为空")
}
if in.UserName == "" {
return gerror.New("登录账号不能为空")
}
if in.DeptId == 0 && in.Password == "" {
return gerror.New("密码不能为空")
}
var excludeUserId int64
if in.DeptId > 0 {
if err := s.ensureEnterpriseDept(ctx, in.DeptId); err != nil {
return err
}
defaultUser, err := s.defaultUserByDept(ctx, in.DeptId, 0)
if err != nil {
return err
}
if defaultUser != nil {
excludeUserId = defaultUser.UserId
}
}
return s.ensureUserNameUnique(ctx, tenantId, in.UserName, excludeUserId)
}
func (s *sAgentDept) addEnterpriseDept(ctx context.Context, tx gdb.TX, user *model.Identity, in *model.AgentDeptSaveParam) (deptId, roleId, userId int64, err error) {
now := gtime.Now()
deptKey, err := s.nextCustomerDeptKey(ctx)
if err != nil {
return 0, 0, 0, err
}
ancestors, err := s.childDeptAncestors(ctx, user.DeptId)
if err != nil {
return 0, 0, 0, err
}
deptId, err = tx.Ctx(ctx).Model(dao.SysDept.Table()).Data(do.SysDept{
TenantId: user.TenantId,
ParentId: user.DeptId,
Ancestors: ancestors,
DeptKey: deptKey,
DeptName: in.DeptName,
DeptType: 1,
DeptCategory: in.DeptCategory,
OrderNum: in.OrderNum,
Phone: in.Phone,
Email: in.Email,
Status: consts.SysDeptStatusNormal,
CreditCode: in.CreditCode,
AreaCode: in.AreaCode,
Address: in.Address,
CreatedDept: user.DeptId,
CreatedBy: user.ID,
CreatedAt: now,
UpdatedBy: user.ID,
UpdatedAt: now,
}).OmitNil().InsertAndGetId()
if err != nil {
return 0, 0, 0, err
}
passRole, menuIds, err := s.currentDeptPassRole(ctx)
if err != nil {
return 0, 0, 0, err
}
roleId, err = s.createEnterpriseRole(ctx, tx, user, deptId, passRole, menuIds)
if err != nil {
return 0, 0, 0, err
}
if err = s.updateDeptRole(ctx, tx, deptId, roleId, user.ID); err != nil {
return 0, 0, 0, err
}
userId, err = s.createEnterpriseUser(ctx, tx, user, deptId, roleId, in)
if err != nil {
return 0, 0, 0, err
}
if err = s.updateDeptLeader(ctx, tx, deptId, userId, user.ID); err != nil {
return 0, 0, 0, err
}
return deptId, roleId, userId, nil
}
func (s *sAgentDept) editEnterpriseDept(ctx context.Context, tx gdb.TX, user *model.Identity, in *model.AgentDeptSaveParam) (roleId, userId int64, err error) {
var dept *entity.SysDept
if err = s.deptModel(ctx).Where(dao.SysDept.Columns().DeptId, in.DeptId).Scan(&dept); err != nil {
return 0, 0, err
}
if dept == nil || dept.DeptId == 0 {
return 0, 0, gerror.New("企业客户不存在")
}
ancestors, err := s.childDeptAncestors(ctx, user.DeptId)
if err != nil {
return 0, 0, err
}
_, err = tx.Ctx(ctx).Model(dao.SysDept.Table()).
Where(dao.SysDept.Columns().DeptId, in.DeptId).
Data(do.SysDept{
DeptName: in.DeptName,
DeptType: 1,
ParentId: user.DeptId,
Ancestors: ancestors,
Phone: in.Phone,
Email: in.Email,
Status: consts.SysDeptStatusNormal,
CreditCode: in.CreditCode,
AreaCode: in.AreaCode,
Address: in.Address,
UpdatedBy: user.ID,
UpdatedAt: gtime.Now(),
}).OmitNil().Update()
if err != nil {
return 0, 0, err
}
roleId = dept.DeptRole
if roleId == 0 {
passRole, menuIds, err := s.currentDeptPassRole(ctx)
if err != nil {
return 0, 0, err
}
roleId, err = s.createEnterpriseRole(ctx, tx, user, in.DeptId, passRole, menuIds)
if err != nil {
return 0, 0, err
}
if err = s.updateDeptRole(ctx, tx, in.DeptId, roleId, user.ID); err != nil {
return 0, 0, err
}
} else if err = s.updateEnterpriseRole(ctx, tx, user, in.DeptId, roleId); err != nil {
return 0, 0, err
}
defaultUser, err := s.defaultUserByDept(ctx, in.DeptId, roleId)
if err != nil {
return 0, 0, err
}
if defaultUser == nil {
userId, err = s.createEnterpriseUser(ctx, tx, user, in.DeptId, roleId, in)
if err != nil {
return 0, 0, err
}
} else {
userId = defaultUser.UserId
if err = s.updateEnterpriseUser(ctx, tx, user, userId, in.DeptId, roleId, in); err != nil {
return 0, 0, err
}
}
if err = s.updateDeptLeader(ctx, tx, in.DeptId, userId, user.ID); err != nil {
return 0, 0, err
}
return roleId, userId, nil
}
func (s *sAgentDept) createEnterpriseRole(ctx context.Context, tx gdb.TX, user *model.Identity, deptId int64, sourceRole *entity.SysRole, roleMenus []*entity.SysRoleMenu) (roleId int64, err error) {
now := gtime.Now()
roleId, err = tx.Ctx(ctx).Model(dao.SysRole.Table()).Data(do.SysRole{
TenantId: user.TenantId,
DeptId: deptId,
RoleName: strings.TrimSpace(sourceRole.RoleName),
RoleKey: sourceRole.RoleKey,
RoleSort: 0,
DataScope: consts.SysRoleDataScopeDeptAndBelow,
MenuCheckStrictly: 1,
DeptCheckStrictly: 1,
IsPass: 0,
Status: "0",
CreatedDept: user.DeptId,
CreatedBy: user.ID,
CreatedAt: now,
UpdatedBy: user.ID,
UpdatedAt: now,
}).OmitNil().InsertAndGetId()
if err != nil {
return 0, err
}
if err = s.copyRoleMenus(ctx, tx, roleId, roleMenus); err != nil {
return 0, err
}
if err = s.replaceRoleDepts(ctx, tx, roleId, deptId); err != nil {
return 0, err
}
return roleId, nil
}
func inheritedRoleName(name string) string {
// Inherited roles keep the source role name; source fields identify the inheritance.
return strings.TrimSpace(name)
}
func (s *sAgentDept) updateEnterpriseRole(ctx context.Context, tx gdb.TX, user *model.Identity, deptId, roleId int64) error {
_, err := tx.Ctx(ctx).Model(dao.SysRole.Table()).
Where(dao.SysRole.Columns().RoleId, roleId).
Where(dao.SysRole.Columns().DeptId, deptId).
Data(do.SysRole{
DataScope: consts.SysRoleDataScopeDeptAndBelow,
MenuCheckStrictly: 1,
DeptCheckStrictly: 1,
Status: "0",
UpdatedBy: user.ID,
UpdatedAt: gtime.Now(),
}).OmitNil().Update()
if err != nil {
return err
}
return s.replaceRoleDepts(ctx, tx, roleId, deptId)
}
func (s *sAgentDept) createEnterpriseUser(ctx context.Context, tx gdb.TX, user *model.Identity, deptId, roleId int64, in *model.AgentDeptSaveParam) (userId int64, err error) {
now := gtime.Now()
salt := utility.RandomString(5)
password := utility.PasswordEncrypt(in.Password, salt)
userId, err = tx.Ctx(ctx).Model(dao.SysUser.Table()).Data(do.SysUser{
TenantId: user.TenantId,
DeptId: deptId,
UserName: in.UserName,
NickName: in.DeptName,
UserType: consts.SysUserTypeSys,
Email: in.Email,
Phonenumber: in.Phone,
Sex: consts.SysUserSexUnknown,
Salt: salt,
Password: password,
IsInitPassword: 1, // 管理员设置的初始密码,登录后引导修改(与用户管理处新增保持一致)
Status: consts.SysUserStatusNormal,
CreatedDept: user.DeptId,
CreatedBy: user.ID,
CreatedAt: now,
UpdatedBy: user.ID,
UpdatedAt: now,
}).OmitNil().InsertAndGetId()
if err != nil {
return 0, err
}
return userId, s.replaceUserRole(ctx, tx, userId, roleId)
}
func (s *sAgentDept) updateEnterpriseUser(ctx context.Context, tx gdb.TX, user *model.Identity, userId, deptId, roleId int64, in *model.AgentDeptSaveParam) error {
userData := do.SysUser{
DeptId: deptId,
UserName: in.UserName,
UserType: consts.SysUserTypeSys,
Email: in.Email,
Phonenumber: in.Phone,
Sex: consts.SysUserSexUnknown,
Status: consts.SysUserStatusNormal,
UpdatedBy: user.ID,
UpdatedAt: gtime.Now(),
}
if nickName := strings.TrimSpace(in.NickName); nickName != "" {
userData.NickName = nickName
}
_, err := tx.Ctx(ctx).Model(dao.SysUser.Table()).
Where(dao.SysUser.Columns().UserId, userId).
Data(userData).OmitNil().Update()
if err != nil {
return err
}
return s.replaceUserRole(ctx, tx, userId, roleId)
}
func (s *sAgentDept) updateDeptRole(ctx context.Context, tx gdb.TX, deptId, roleId, userId int64) error {
_, err := tx.Ctx(ctx).Model(dao.SysDept.Table()).
Where(dao.SysDept.Columns().DeptId, deptId).
Data(do.SysDept{DeptRole: roleId, UpdatedBy: userId, UpdatedAt: gtime.Now()}).
OmitNil().Update()
return err
}
func (s *sAgentDept) updateDeptLeader(ctx context.Context, tx gdb.TX, deptId, leader, userId int64) error {
_, err := tx.Ctx(ctx).Model(dao.SysDept.Table()).
Where(dao.SysDept.Columns().DeptId, deptId).
Data(do.SysDept{Leader: leader, UpdatedBy: userId, UpdatedAt: gtime.Now()}).
OmitNil().Update()
return err
}
// copyRoleMenus copies the already-authorized menu set of the creator's effective role.
func (s *sAgentDept) copyRoleMenus(ctx context.Context, tx gdb.TX, roleId int64, roleMenus []*entity.SysRoleMenu) error {
if _, err := tx.Ctx(ctx).Model(dao.SysRoleMenu.Table()).
Where(dao.SysRoleMenu.Columns().RoleId, roleId).Delete(); err != nil {
return err
}
if len(roleMenus) == 0 {
return nil
}
data := make([]do.SysRoleMenu, 0, len(roleMenus))
for _, roleMenu := range roleMenus {
data = append(data, do.SysRoleMenu{
TenantId: contexts.GetTenantId(ctx),
RoleId: roleId,
MenuId: roleMenu.MenuId,
DataScope: roleMenu.DataScope,
})
}
_, err := tx.Ctx(ctx).Model(dao.SysRoleMenu.Table()).Data(data).OmitNil().Insert()
return err
}
func (s *sAgentDept) currentDeptPassRole(ctx context.Context) (*entity.SysRole, []*entity.SysRoleMenu, error) {
return s.passRoleByDept(ctx, contexts.GetDeptId(ctx))
}
func (s *sAgentDept) passRoleByDept(ctx context.Context, deptId int64) (*entity.SysRole, []*entity.SysRoleMenu, error) {
template, err := rolepolicy.Template(ctx)
if err != nil {
return nil, nil, err
}
if template != nil {
if contexts.GetTenantId(ctx) != template.TenantId {
return nil, nil, gerror.New("当前租户不属于平台代理模板授权范围")
}
// 配置了平台模板后,下发来源不再依赖 is_pass=1:
// 一级代理由平台模板推出,其默认角色是复制来的 is_pass=0 角色;
// 各级代理再创建下级时,必须能从本机构默认角色(sys_dept.dept_role)继续传递,
// 否则二级及以下代理永远建不了下级。
source := template
if deptId != template.DeptId {
var dept *entity.SysDept
if err := s.deptModel(ctx).Where(dao.SysDept.Columns().DeptId, deptId).
Where(dao.SysDept.Columns().Status, consts.SysDeptStatusNormal).
Scan(&dept); err != nil {
return nil, nil, err
}
if dept == nil || dept.DeptId == 0 {
return nil, nil, gerror.New("当前机构不存在或已停用")
}
if dept.DeptRole <= 0 {
return nil, nil, gerror.Newf("机构%d未关联默认角色,请联系平台管理员核对机构角色绑定", deptId)
}
source, err = s.defaultRoleForPass(ctx, deptId, dept.DeptRole)
if err != nil {
return nil, nil, err
}
}
var menus []*entity.SysRoleMenu
if err := dao.SysRoleMenu.Ctx(ctx).Master().Where("tenant_id", template.TenantId).Where("role_id", source.RoleId).Scan(&menus); err != nil {
return nil, nil, err
}
ids := make([]int64, 0, len(menus))
for _, menu := range menus {
ids = append(ids, menu.MenuId)
}
allowed, err := rolepolicy.RestrictMenus(ctx, template.TenantId, deptId, ids)
if err != nil {
return nil, nil, err
}
selected := make(map[int64]bool)
for _, id := range allowed {
selected[id] = true
}
result := make([]*entity.SysRoleMenu, 0, len(menus))
for _, menu := range menus {
if selected[menu.MenuId] {
copy := *menu
copy.DataScope = consts.SysRoleMenuDataScopeRole
result = append(result, &copy)
}
}
return source, result, nil
}
var roles []*entity.SysRole
if err := s.roleModel(ctx).
Where(dao.SysRole.Columns().DeptId, deptId).
Where(dao.SysRole.Columns().Status, "0").
Where("is_pass", 1).
Scan(&roles); err != nil {
return nil, nil, err
}
if len(roles) != 1 {
return nil, nil, gerror.New("请先在角色管理配置唯一的下级管理员角色")
}
var roleMenus []*entity.SysRoleMenu
if err := dao.SysRoleMenu.Ctx(ctx).
Where(dao.SysRoleMenu.Columns().RoleId, roles[0].RoleId).
OrderAsc(dao.SysRoleMenu.Columns().MenuId).
Scan(&roleMenus); err != nil {
return nil, nil, err
}
return roles[0], roleMenus, nil
}
// defaultRoleForPass resolves the org's bound default role (sys_dept.dept_role) as
// the distribution source. Configured platform template owners keep using the
// template role itself; everyone else must have a live default role bound.
func (s *sAgentDept) defaultRoleForPass(ctx context.Context, deptId, deptRoleId int64) (*entity.SysRole, error) {
var role *entity.SysRole
if err := s.roleModel(ctx).
Where(dao.SysRole.Columns().RoleId, deptRoleId).
Where(dao.SysRole.Columns().DeptId, deptId).
Where(dao.SysRole.Columns().Status, "0").
Scan(&role); err != nil {
return nil, err
}
if role == nil || role.RoleId == 0 {
return nil, gerror.Newf("机构%d默认角色不存在、已停用或归属不匹配,请联系平台管理员核对", deptId)
}
return role, nil
}
// currentUserEffectiveRoleMenuIds returns the menu set of the role that authorized
// the current request. The child administrator receives a copy, never the parent role.
func (s *sAgentDept) currentUserEffectiveRoleMenuIds(ctx context.Context, user *model.Identity) ([]int64, error) {
roleId, err := s.currentUserEffectiveRoleId(ctx, user)
if err != nil {
return nil, err
}
var roleMenus []*entity.SysRoleMenu
if err = dao.SysRoleMenu.Ctx(ctx).
Where(dao.SysRoleMenu.Columns().RoleId, roleId).
OrderAsc(dao.SysRoleMenu.Columns().MenuId).
Scan(&roleMenus); err != nil {
return nil, err
}
menuIds := make([]int64, 0, len(roleMenus))
for _, roleMenu := range roleMenus {
menuIds = append(menuIds, roleMenu.MenuId)
}
return menuIds, nil
}
func (s *sAgentDept) currentUserEffectiveRoleId(ctx context.Context, user *model.Identity) (int64, error) {
if user == nil {
return 0, gerror.New("登录已过期")
}
roleId := int64(0)
if currentRoleId := contexts.GetDataValue(ctx, "currentRoleId"); currentRoleId != nil {
roleIdStr, ok := currentRoleId.(string)
if !ok {
return 0, gerror.New("当前生效角色异常")
}
parsedRoleId, err := strconv.ParseInt(roleIdStr, 10, 64)
if err != nil || parsedRoleId <= 0 {
return 0, gerror.New("当前生效角色异常")
}
roleId = parsedRoleId
} else {
defaultRoleId, err := s.currentDeptDefaultRoleId(ctx)
if err != nil {
return 0, err
}
for _, role := range user.Roles {
if role.RoleId == defaultRoleId {
roleId = role.RoleId
break
}
}
if roleId == 0 && len(user.Roles) == 1 {
roleId = user.Roles[0].RoleId
}
}
if roleId <= 0 {
return 0, gerror.New("无法确定当前用户的有效角色")
}
count, err := dao.SysUserRole.Ctx(ctx).
Where(dao.SysUserRole.Columns().UserId, user.ID).
Where(dao.SysUserRole.Columns().RoleId, roleId).
Count()
if err != nil {
return 0, err
}
if count == 0 {
return 0, gerror.New("当前生效角色未绑定登录用户")
}
return roleId, nil
}
func (s *sAgentDept) replaceRoleDepts(ctx context.Context, tx gdb.TX, roleId, deptId int64) error {
if _, err := tx.Ctx(ctx).Model(dao.SysRoleDept.Table()).
Where(dao.SysRoleDept.Columns().RoleId, roleId).Delete(); err != nil {
return err
}
_, err := tx.Ctx(ctx).Model(dao.SysRoleDept.Table()).
Data(do.SysRoleDept{RoleId: roleId, DeptId: deptId}).OmitNil().Insert()
return err
}
func (s *sAgentDept) replaceUserRole(ctx context.Context, tx gdb.TX, userId, roleId int64) error {
if _, err := tx.Ctx(ctx).Model(dao.SysUserRole.Table()).
Where(dao.SysUserRole.Columns().UserId, userId).Delete(); err != nil {
return err
}
_, err := tx.Ctx(ctx).Model(dao.SysUserRole.Table()).
Data(do.SysUserRole{UserId: userId, RoleId: roleId}).OmitNil().Insert()
return err
}
func (s *sAgentDept) childDeptAncestors(ctx context.Context, parentId int64) (string, error) {
var parent *entity.SysDept
if err := s.deptModel(ctx).
Where(dao.SysDept.Columns().DeptId, parentId).
Scan(&parent); err != nil {
return "", err
}
if parent == nil || parent.DeptId == 0 {
return "", gerror.New("父级组织不存在")
}
ancestors := strings.Trim(parent.Ancestors, ",")
if ancestors == "" {
ancestors = "0"
}
// ancestors 必须与平台约定格式一致:以本机构结尾并带尾逗号(如 "0,1,172,")。
// 早期实现返回 "0,1,172"(缺尾逗号),而 GetDeptAndSub 按 '%,id,%' 展开
// 「本机构及以下」数据权限,缺尾逗号时匹配不到,表现为上级机构看不到下级
// (例:一级代理在卡片划拨里选不到下级客户)。
return fmt.Sprintf("%s,%d,", ancestors, parent.DeptId), nil
}
func (s *sAgentDept) nextCustomerDeptKey(ctx context.Context) (string, error) {
prefix := "CUS-" + time.Now().Format("200601")
seq := 1
var latest *entity.SysDept
err := dao.SysDept.Ctx(ctx).
WhereLike(dao.SysDept.Columns().DeptKey, prefix+"-%").
OrderDesc(dao.SysDept.Columns().DeptKey).
Limit(1).
Scan(&latest)
if err != nil {
return "", err
}
if latest != nil && latest.DeptKey != "" {
parts := strings.Split(latest.DeptKey, "-")
if len(parts) == 3 {
if latestSeq, parseErr := strconv.Atoi(parts[2]); parseErr == nil {
seq = latestSeq + 1
}
}
}
for {
deptKey := fmt.Sprintf("%s-%04d", prefix, seq)
count, err := dao.SysDept.Ctx(ctx).Where(dao.SysDept.Columns().DeptKey, deptKey).Count()
if err != nil {
return "", err
}
if count == 0 {
return deptKey, nil
}
seq++
}
}
func (s *sAgentDept) ensureUserNameUnique(ctx context.Context, tenantId, userName string, excludeUserId int64) error {
m := dao.SysUser.Ctx(ctx).
Where(dao.SysUser.Columns().TenantId, tenantId).
Where(dao.SysUser.Columns().UserName, userName)
if excludeUserId > 0 {
m = m.WhereNot(dao.SysUser.Columns().UserId, excludeUserId)
}
count, err := m.Count()
if err != nil {
return err
}
if count > 0 {
return gerror.New("登录账号已存在")
}
return nil
}
func (s *sAgentDept) fillEnterpriseDefaultUsers(ctx context.Context, list []*model.AgentEnterpriseCustomerModel) error {
if len(list) == 0 {
return nil
}
var (
deptIDs = make([]int64, 0, len(list))
roleIDs = make([]int64, 0, len(list))
bindings []struct {
RoleId int64
UserId int64
}
fallbacks []struct {
DeptId int64
UserId int64
}
)
for _, dept := range list {
deptIDs = append(deptIDs, dept.DeptId)
if dept.DeptRole > 0 {
roleIDs = append(roleIDs, dept.DeptRole)
}
}
tenantID := contexts.GetTenantId(ctx)
if len(roleIDs) > 0 {
if err := dao.SysUserRole.Ctx(ctx).Where("tenant_id", tenantID).
WhereIn("role_id", roleIDs).Fields("role_id", "MIN(user_id) AS user_id").Group("role_id").Scan(&bindings); err != nil {
return err
}
}
if err := s.userModel(ctx).Where("tenant_id", tenantID).WhereIn("dept_id", deptIDs).
Fields("dept_id", "MIN(user_id) AS user_id").Group("dept_id").Scan(&fallbacks); err != nil {
return err
}
var (
roleUsers = make(map[int64]int64, len(bindings))
deptUsers = make(map[int64]int64, len(fallbacks))
userIDs = make([]int64, 0, len(bindings)+len(fallbacks))
users []*entity.SysUser
)
for _, binding := range bindings {
roleUsers[binding.RoleId] = binding.UserId
userIDs = append(userIDs, binding.UserId)
}
for _, fallback := range fallbacks {
deptUsers[fallback.DeptId] = fallback.UserId
userIDs = append(userIDs, fallback.UserId)
}
if len(userIDs) == 0 {
return nil
}
if err := s.userModel(ctx).Where("tenant_id", tenantID).WhereIn("dept_id", deptIDs).
WhereIn("user_id", userIDs).Fields("user_id", "dept_id", "user_name", "nick_name").Scan(&users); err != nil {
return err
}
usersByID := make(map[int64]*entity.SysUser, len(users))
for _, user := range users {
usersByID[user.UserId] = user
}
for _, dept := range list {
user := usersByID[roleUsers[dept.DeptRole]]
if user == nil || user.DeptId != dept.DeptId {
user = usersByID[deptUsers[dept.DeptId]]
}
if user == nil {
continue
}
dept.DefaultUserId = user.UserId
dept.UserName = user.UserName
dept.NickName = user.NickName
}
return nil
}
func (s *sAgentDept) defaultUserByDept(ctx context.Context, deptId, roleId int64) (*entity.SysUser, error) {
if roleId == 0 {
var dept *entity.SysDept
if err := s.deptModel(ctx).Where(dao.SysDept.Columns().DeptId, deptId).Scan(&dept); err != nil {
return nil, err
}
if dept != nil {
roleId = dept.DeptRole
}
}
if roleId > 0 {
var userRole *entity.SysUserRole
if err := dao.SysUserRole.Ctx(ctx).
Where(dao.SysUserRole.Columns().RoleId, roleId).
OrderAsc(dao.SysUserRole.Columns().UserId).
Scan(&userRole); err != nil {
return nil, err
}
if userRole != nil {
var user *entity.SysUser
if err := s.userModel(ctx).
Where(dao.SysUser.Columns().UserId, userRole.UserId).
Where(dao.SysUser.Columns().DeptId, deptId).
Scan(&user); err != nil {
return nil, err
}
if user != nil && user.UserId > 0 {
return user, nil
}
}
}
var user *entity.SysUser
err := s.userModel(ctx).
Where(dao.SysUser.Columns().DeptId, deptId).
OrderAsc(dao.SysUser.Columns().UserId).
Scan(&user)
return user, err
}
func (s *sAgentDept) ensureEnterpriseDept(ctx context.Context, deptId int64) error {
currentDeptId := contexts.GetDeptId(ctx)
if currentDeptId == 0 {
return gerror.New("当前用户未绑定组织")
}
count, err := s.deptModel(ctx).
Where(dao.SysDept.Columns().DeptId, deptId).
Where(dao.SysDept.Columns().ParentId, currentDeptId).
Where(dao.SysDept.Columns().DeptType, 1).
Count()
if err != nil {
return err
}
if count == 0 {
return gerror.New("只能操作下一级企业客户")
}
return nil
}
// DeptDelete 删除企业客户
func (s *sAgentDept) DeptDelete(ctx context.Context, deptId int64) (err error) {
if err = s.ensureEnterpriseDept(ctx, deptId); err != nil {
return err
}
user := contexts.GetUser(ctx)
if user == nil {
return gerror.New("登录已过期")
}
var dept *entity.SysDept
if err = s.deptModel(ctx).Where(dao.SysDept.Columns().DeptId, deptId).Scan(&dept); err != nil {
return err
}
if dept == nil || dept.DeptId == 0 {
return gerror.New("企业客户不存在")
}
children, err := s.deptModel(ctx).Where(dao.SysDept.Columns().ParentId, deptId).Count()
if err != nil {
return err
}
if children > 0 {
return gerror.New("该企业客户下有子组织,不能删除")
}
defaultUser, err := s.defaultUserByDept(ctx, deptId, dept.DeptRole)
if err != nil {
return err
}
users, err := s.userModel(ctx).Where(dao.SysUser.Columns().DeptId, deptId).Count()
if err != nil {
return err
}
if defaultUser == nil && users > 0 {
return gerror.New("该企业客户下有用户,不能删除")
}
if defaultUser != nil && users > 1 {
return gerror.New("该企业客户下有其他用户,不能删除")
}
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
if _, err := tx.Ctx(ctx).Model(dao.TAgentEnterprise.Table()).
Where(dao.TAgentEnterprise.Columns().DeptId, deptId).Unscoped().Delete(); err != nil {
return gerror.Wrap(err, "删除企业客户单位信息失败,请稍后重试")
}
if defaultUser != nil {
if _, err := tx.Ctx(ctx).Model(dao.SysUserRole.Table()).
Where(dao.SysUserRole.Columns().UserId, defaultUser.UserId).Delete(); err != nil {
return err
}
if _, err := tx.Ctx(ctx).Model(dao.SysUser.Table()).
Where(dao.SysUser.Columns().UserId, defaultUser.UserId).
Data(do.SysUser{DeletedBy: user.ID, DeletedAt: gtime.Now()}).
OmitNil().Update(); err != nil {
return err
}
}
if dept.DeptRole > 0 {
if _, err := tx.Ctx(ctx).Model(dao.SysRoleMenu.Table()).
Where(dao.SysRoleMenu.Columns().RoleId, dept.DeptRole).Delete(); err != nil {
return err
}
if _, err := tx.Ctx(ctx).Model(dao.SysRoleDept.Table()).
Where(dao.SysRoleDept.Columns().RoleId, dept.DeptRole).Delete(); err != nil {
return err
}
if _, err := tx.Ctx(ctx).Model(dao.SysRole.Table()).
Where(dao.SysRole.Columns().RoleId, dept.DeptRole).
Data(do.SysRole{DeletedBy: user.ID, DeletedAt: gtime.Now()}).
OmitNil().Update(); err != nil {
return err
}
}
_, err := tx.Ctx(ctx).Model(dao.SysDept.Table()).
Where(dao.SysDept.Columns().DeptId, deptId).
Data(do.SysDept{DeletedBy: user.ID, DeletedAt: gtime.Now()}).
OmitNil().Update()
return err
})
if err != nil {
return err
}
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysDeptDelete, deptId)
if dept.DeptRole > 0 {
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysRoleDelete, []int64{dept.DeptRole})
}
if defaultUser != nil {
event.EventsInstance().Emit(ctx, consts.EventKeyUserDelete, []int64{defaultUser.UserId})
}
return nil
}
func (s *sAgentDept) DeptResetPassword(ctx context.Context, deptId int64, password string) (err error) {
if strings.TrimSpace(password) == "" {
return gerror.New("密码不能为空")
}
if err = s.ensureEnterpriseDept(ctx, deptId); err != nil {
return err
}
user, err := s.defaultUserByDept(ctx, deptId, 0)
if err != nil {
return err
}
if user == nil || user.UserId == 0 {
return gerror.New("企业客户默认用户不存在")
}
return service.SysUser().ResetPassword(ctx, user.UserId, password)
}
// GetDeptApiInfo 获取当前代理商组织的 API 集成信息
func (s *sAgentDept) GetDeptApiInfo(ctx context.Context) (res *model.AgentDeptApiModel, err error) {
var api model.AgentDeptApiModel
mod := service.SysDept().Model(ctx)
deptId := contexts.GetDeptId(ctx)
sysDept := dao.SysDept.Table()
tApi := dao.TApi.Table()
mod = mod.Where(sysDept+"."+dao.SysDept.Columns().DeptId, deptId)
mod = mod.LeftJoin(tApi, tApi+"."+dao.TApi.Columns().DeptId+"="+sysDept+"."+dao.SysDept.Columns().DeptId)
mod = mod.Fields(sysDept + "." + dao.SysDept.Columns().DeptKey + " as deptKey")
mod = mod.Fields(sysDept + "." + dao.SysDept.Columns().DeptName + " as deptName")
mod = mod.Fields(tApi + "." + dao.TApi.Columns().AppKey + " as appKey")
mod = mod.Fields(tApi + "." + dao.TApi.Columns().AppSecret + " as appSecret")
err = mod.Scan(&api)
if err != nil {
return nil, err
}
if api.AppKey == "" || api.AppSecret == "" {
return nil, errors.New("您未开通api功能,请联系管理员开通")
}
return &api, nil
}
// SubCompany 获取当前代理商组织的子组织列表
func (s *sAgentDept) SubCompany(ctx context.Context) (list []*model.SysDeptTreeModel, err error) {
deptId := contexts.GetDeptId(ctx)
currentDept, err := service.SysDept().GetDeptById(ctx, deptId)
if err != nil {
return nil, err
}
if currentDept == nil {
return []*model.SysDeptTreeModel{}, nil
}
root := &model.SysDeptTreeModel{
DeptId: currentDept.DeptId,
ParentId: currentDept.ParentId,
Key: fmt.Sprintf("%d", currentDept.DeptId),
DeptName: currentDept.DeptName,
Children: make([]*model.SysDeptTreeModel, 0),
Ancestors: currentDept.Ancestors,
DeptType: currentDept.DeptType,
}
deptType := 1
depts, _, err := service.SysDept().GetDeptList(ctx, model.SysDeptListParam{
DeptType: &deptType,
})
if err != nil {
return nil, err
}
for _, dept := range depts {
if dept.ParentId == deptId {
root.Children = append(root.Children, &model.SysDeptTreeModel{
DeptId: dept.DeptId,
ParentId: dept.ParentId,
Key: fmt.Sprintf("%d", dept.DeptId),
DeptName: dept.DeptName,
Children: make([]*model.SysDeptTreeModel, 0),
Ancestors: dept.Ancestors,
DeptType: dept.DeptType,
})
}
}
return []*model.SysDeptTreeModel{root}, nil
}
// IsTopDept 判断当前组织是否为顶级组织
func (s *sAgentDept) IsTopDept(ctx context.Context) (isTop bool, dept *entity.SysDept, err error) {
currentDeptId := contexts.GetDeptId(ctx)
if currentDeptId == 0 {
return false, nil, gerror.New("当前用户未绑定组织")
}
err = s.deptModel(ctx).
Where(dao.SysDept.Columns().DeptId, currentDeptId).
Scan(&dept)
if err != nil {
return false, nil, err
}
if dept == nil || dept.DeptId == 0 {
return false, nil, gerror.New("组织不存在")
}
isTop = dept.ParentId == 0 || strings.Trim(dept.Ancestors, ",") == "0"
return isTop, dept, nil
}