59eb34976a
iotlcm-admin-svr:管理后台服务。 - Go module:iotlcm → git.5iot.com/iotlcm-public/iotlcm-admin-svr(729 文件) - 凭据与地址脱敏:JWT signingKey(含代码里的 MustGet 默认值)、 MySQL DSN、Redis 口令、告警邮件发件人邮箱全部改为占位; 私网 IP → 文档地址段;库名 xiujie_admin → iotlcm - 真实手机号/邮箱 → 合法 11 位编造号 / example.com - 移除内部资产:CI 触发器、过时 DDL(sql/db_iotlcm.sql)、 私有依赖 go get 行、明文 registry 登录口令 - 启动 banner 统一 IOTLCM 方块字 + 社区惯例署名 - Dockerfile 改用公共基础镜像 - 拼错的 RADEME.md → README.md,README 重写 - 需要真实数据库的用例改为可用 IOTLCM_TEST_WITH_DB=1 显式启用 - 补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes 数据库 schema 不随本仓发布(生产 DDL 已过时),README 已说明。
1018 lines
31 KiB
Go
1018 lines
31 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) {
|
|
currentDeptId := contexts.GetDeptId(ctx)
|
|
if currentDeptId == 0 {
|
|
return nil, 0, gerror.New("当前用户未绑定组织")
|
|
}
|
|
m := s.deptModel(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").
|
|
Scan(&list)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if err = s.fillEnterpriseDefaultUsers(ctx, list); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return list, len(list), 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
|
|
}
|
|
|
|
// EnsureCurrentDeptDefaultRole upgrades legacy child enterprises that did not
|
|
// receive an inherited administrator role when they were created.
|
|
func (s *sAgentDept) EnsureCurrentDeptDefaultRole(ctx context.Context) error {
|
|
user := contexts.GetUser(ctx)
|
|
if user == 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 || dept.DeptId == 0 {
|
|
return gerror.New("组织不存在")
|
|
}
|
|
if dept.DeptType != 1 || dept.DeptRole != 0 {
|
|
return nil
|
|
}
|
|
|
|
sourceRole, menuIds, err := s.passRoleByDept(ctx, dept.ParentId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
var current *entity.SysDept
|
|
if err := tx.Ctx(ctx).Model(dao.SysDept.Table()).
|
|
Where(dao.SysDept.Columns().DeptId, deptId).
|
|
Scan(¤t); err != nil {
|
|
return err
|
|
}
|
|
if current == nil || current.DeptId == 0 {
|
|
return gerror.New("组织不存在")
|
|
}
|
|
if current.DeptRole != 0 {
|
|
return nil
|
|
}
|
|
|
|
roleId, err := s.createEnterpriseRole(ctx, tx, user, deptId, sourceRole, menuIds)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := s.updateDeptRole(ctx, tx, deptId, roleId, user.ID); err != nil {
|
|
return err
|
|
}
|
|
// Keep the legacy role for the active session. After re-login, this role
|
|
// becomes the department's selected default administrator role.
|
|
_, err = tx.Ctx(ctx).Model(dao.SysUserRole.Table()).
|
|
Data(do.SysUserRole{UserId: user.ID, RoleId: roleId}).
|
|
OmitNil().Insert()
|
|
return err
|
|
})
|
|
}
|
|
|
|
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 && contexts.GetTenantId(ctx) == template.TenantId {
|
|
// 配置了平台模板后,下发来源不再依赖 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).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, ©)
|
|
}
|
|
}
|
|
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"
|
|
}
|
|
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 {
|
|
for _, dept := range list {
|
|
user, err := s.defaultUserByDept(ctx, dept.DeptId, dept.DeptRole)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
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
|
|
}
|