add: multi-tenant

This commit is contained in:
2024-06-06 11:27:42 +08:00
parent 6ce288c3ef
commit 82420a8445
14 changed files with 1253 additions and 9 deletions

View File

@@ -208,7 +208,7 @@ func (r *RepoUdmSubUser) ClearAndInsert(neID string, subArr []model.UdmSubUser)
batch := subArr[i:end]
// multi-tenancy
r.SetTenantName(batch)
r.SetTenantName(&batch)
// 调用 InsertMulti 函数将批量数据插入数据库
results, err := datasource.DefaultDB().Table("u_sub_user").InsertMulti(batch)
@@ -253,7 +253,7 @@ func (r *RepoUdmSubUser) Inserts(subUser []model.UdmSubUser) int64 {
batch := subUser[i:end]
// multi-tenancy
r.SetTenantName(batch)
r.SetTenantName(&batch)
// 调用 InsertMulti 函数将批量数据插入数据库
results, err := datasource.DefaultDB().Table("u_sub_user").InsertMulti(batch)
@@ -285,7 +285,7 @@ func (r *RepoUdmSubUser) Insert4G(neID string, subUser model.UdmSubUser) int64 {
// multi-tenancy
subUserSlice := []model.UdmSubUser{subUser}
r.SetTenantName(subUserSlice)
r.SetTenantName(&subUserSlice)
results, err := datasource.DefaultDB().Table("u_sub_user").Insert(subUser)
if err == nil {
@@ -475,15 +475,18 @@ func (r *RepoUdmSubUser) Deletes(neID, imsi, num string) int64 {
}
// multi-tenancy solution, get tenant_name by imsi
func (r *RepoUdmSubUser) SetTenantName(subArr []model.UdmSubUser) {
for s := 0; s < len(subArr); s++ {
func (r *RepoUdmSubUser) SetTenantName(subArr *[]model.UdmSubUser) {
for s := 0; s < len(*subArr); s++ {
var tenantName string
err := datasource.DefaultDB().Table("sys_tenant_map").
Where("mapping_type='udm_sub' and mapping_key='?'", subArr[s].Imsi).Cols("tenant_name").Find(tenantName)
Where("mapping_type='udm_sub' and mapping_key='?'", (*subArr)[s].Imsi).Cols("tenant_name").Find(tenantName)
if err != nil {
log.Errorf("Find tenant_name err => %v", err)
continue
}
subArr[s].TenantName = tenantName
if tenantName != "" {
log.Infof("tenantName=%s imsi=%s", tenantName, (*subArr)[s].Imsi)
}
(*subArr)[s].TenantName = tenantName
}
}

View File

@@ -49,3 +49,21 @@ func SysDeptTreeSelect(sysDept systemModel.SysDept) TreeSelect {
return t
}
// SysDeptTreeSelect 使用给定的 SysDept 对象解析为 TreeSelect 对象
func SysTenantTreeSelect(sysTenant systemModel.SysTenant) TreeSelect {
t := TreeSelect{}
t.ID = sysTenant.TenantID
t.Label = sysTenant.TenantName
if len(sysTenant.Children) > 0 {
for _, tenant := range sysTenant.Children {
child := SysTenantTreeSelect(tenant)
t.Children = append(t.Children, child)
}
} else {
t.Children = []TreeSelect{}
}
return t
}

View File

@@ -1,6 +1,7 @@
package repository
import (
"fmt"
"strconv"
"strings"
@@ -40,7 +41,7 @@ var NewUDMSubImpl = &UDMSubImpl{
"context_id": "ContextId",
"apn_context": "ApnContext",
"static_ip": "StaticIp",
"tenant_name": "TenantName", // Tenant ID for multi-tenancy
"tenant_name": "TenantName", // Tenant name for multi-tenancy
},
}
@@ -97,8 +98,9 @@ func (r *UDMSubImpl) SelectPage(query map[string]any) map[string]any {
}
// for multi-tenancy solution
if v, ok := query["tenantName"]; ok && v != "" {
conditions = append(conditions, "tenant_name = '?'")
conditions = append(conditions, "tenant_name = ?")
params = append(params, v)
fmt.Printf("tenantName = %s", v)
}
// 构建查询条件语句

View File

@@ -0,0 +1,381 @@
package controller
import (
"strings"
"be.ems/src/framework/constants/common"
"be.ems/src/framework/i18n"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/vo"
"be.ems/src/framework/vo/result"
"be.ems/src/modules/system/model"
"be.ems/src/modules/system/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
// 实例化控制层 SysTenantController 结构体
var NewSysTenant = &SysTenantController{
sysTenantService: service.NewSysTenantImpl,
}
// 租户信息
//
// PATH /system/tenant
type SysTenantController struct {
// 租户服务
sysTenantService service.ISysTenant
}
// 租户列表
//
// GET /list
func (s *SysTenantController) List(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var querys struct {
// 租户ID
TenantID string `form:"tenantId"`
// 父租户ID
ParentID string `form:"parentId" `
// 租户名称
TenantName string `form:"tenantName" `
// 租户状态0正常 1停用
Status string `form:"status"`
}
err := c.ShouldBindQuery(&querys)
if err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 多语言值转key查询
if querys.TenantName != "" {
querys.TenantName = i18n.TFindKeyPrefix(language, "tenant", querys.TenantName)
}
SysTenantController := model.SysTenant{
TenantID: querys.TenantID,
ParentID: querys.ParentID,
TenantName: querys.TenantName,
Status: querys.Status,
}
dataScopeSQL := ctx.LoginUserToDataScopeSQL(c, "d", "")
data := s.sysTenantService.SelectTenantList(SysTenantController, dataScopeSQL)
// 闭包函数处理多语言
converI18n := func(language string, arr *[]model.SysTenant) {
for i := range *arr {
(*arr)[i].TenantName = i18n.TKey(language, (*arr)[i].TenantName)
}
}
converI18n(language, &data)
c.JSON(200, result.OkData(data))
}
// 租户信息
//
// GET /:tenantId
func (s *SysTenantController) Info(c *gin.Context) {
language := ctx.AcceptLanguage(c)
tenantId := c.Param("tenantId")
if tenantId == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
data := s.sysTenantService.SelectTenantById(tenantId)
if data.TenantID == tenantId {
// 处理多语言
data.TenantName = i18n.TKey(language, data.TenantName)
c.JSON(200, result.OkData(data))
return
}
c.JSON(200, result.Err(nil))
}
// 租户新增
//
// POST /
func (s *SysTenantController) Add(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var body model.SysTenant
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || body.TenantID != "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 父级ID不为0是要检查
if body.ParentID != "0" {
tenantParent := s.sysTenantService.SelectTenantById(body.ParentID)
if tenantParent.TenantID != body.ParentID {
// 没有可访问租户数据!
c.JSON(200, result.ErrMsg(i18n.TKey(language, "tenant.noData")))
return
}
if tenantParent.Status == common.STATUS_NO {
// 上级租户【%s】停用不允许新增
msg := i18n.TTemplate(language, "tenant.errParentStatus", map[string]any{"name": tenantParent.TenantName})
c.JSON(200, result.ErrMsg(msg))
return
}
if tenantParent.DelFlag == common.STATUS_YES {
// 上级租户【%s】已删除不允许新增
msg := i18n.TTemplate(language, "tenant.errParentDelFlag", map[string]any{"name": tenantParent.TenantName})
c.JSON(200, result.ErrMsg(msg))
return
}
body.Ancestors = tenantParent.Ancestors + "," + body.ParentID
} else {
body.Ancestors = "0"
}
// 检查同级下名称唯一
uniqueTenantName := s.sysTenantService.CheckUniqueTenantName(body.TenantName, body.ParentID, "")
if !uniqueTenantName {
// 租户新增【%s】失败租户名称已存在
msg := i18n.TTemplate(language, "tenant.errNameExists", map[string]any{"name": body.TenantName})
c.JSON(200, result.ErrMsg(msg))
return
}
body.CreateBy = ctx.LoginUserToUserName(c)
insertId := s.sysTenantService.InsertTenant(body)
if insertId != "" {
c.JSON(200, result.Ok(nil))
return
}
c.JSON(200, result.Err(nil))
}
// 租户修改
//
// PUT /
func (s *SysTenantController) Edit(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var body model.SysTenant
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || body.TenantID == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 上级租户不能选自己
if body.TenantID == body.ParentID {
// 租户修改【%s】失败上级租户不能是自己
msg := i18n.TTemplate(language, "tenant.errParentID", map[string]any{"name": body.TenantName})
c.JSON(200, result.ErrMsg(msg))
return
}
// 检查数据是否存在
tenantInfo := s.sysTenantService.SelectTenantById(body.TenantID)
if tenantInfo.TenantID != body.TenantID {
// 没有可访问租户数据!
c.JSON(200, result.ErrMsg(i18n.TKey(language, "tenant.noData")))
return
}
// 父级ID不为0是要检查
if body.ParentID != "0" {
tenantParent := s.sysTenantService.SelectTenantById(body.ParentID)
if tenantParent.TenantID != body.ParentID {
// 没有可访问租户数据!
c.JSON(200, result.ErrMsg(i18n.TKey(language, "tenant.noData")))
return
}
}
// 检查同级下名称唯一
uniqueTenantName := s.sysTenantService.CheckUniqueTenantName(body.TenantName, body.ParentID, body.TenantID)
if !uniqueTenantName {
// 租户修改【%s】失败租户名称已存在
msg := i18n.TTemplate(language, "tenant.errNameExists", map[string]any{"name": body.TenantName})
c.JSON(200, result.ErrMsg(msg))
return
}
// 上级停用需要检查下级是否有在使用
if body.Status == common.STATUS_NO {
hasChild := s.sysTenantService.HasChildByTenantId(body.TenantID)
if hasChild > 0 {
// 该租户包含未停用的子租户数量:%d
msg := i18n.TTemplate(language, "tenant.errHasChildUse", map[string]any{"num": hasChild})
c.JSON(200, result.ErrMsg(msg))
return
}
}
// 多语言非原始值
i18nValue := i18n.TKey(language, tenantInfo.TenantName)
if i18nValue != tenantInfo.TenantName {
i18n.UpdateKeyValue(language, tenantInfo.TenantName, body.TenantName)
body.TenantName = tenantInfo.TenantName
}
body.UpdateBy = ctx.LoginUserToUserName(c)
rows := s.sysTenantService.UpdateTenant(body)
if rows > 0 {
c.JSON(200, result.Ok(nil))
return
}
c.JSON(200, result.Err(nil))
}
// 租户删除
//
// DELETE /:tenantId
func (s *SysTenantController) Remove(c *gin.Context) {
language := ctx.AcceptLanguage(c)
tenantId := c.Param("tenantId")
if tenantId == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 检查数据是否存在
tenant := s.sysTenantService.SelectTenantById(tenantId)
if tenant.TenantID != tenantId {
// 没有可访问租户数据!
c.JSON(200, result.ErrMsg(i18n.TKey(language, "tenant.noData")))
return
}
// 检查是否存在子租户
hasChild := s.sysTenantService.HasChildByTenantId(tenantId)
if hasChild > 0 {
// 不允许删除,存在子租户数:%d
msg := i18n.TTemplate(language, "tenant.errHasChildUse", map[string]any{"num": hasChild})
c.JSON(200, result.ErrMsg(msg))
return
}
// 检查是否分配给用户
existUser := s.sysTenantService.CheckTenantExistUser(tenantId)
if existUser > 0 {
// 不允许删除,租户已分配给用户数:%d
msg := i18n.TTemplate(language, "tenant.errHasUserUse", map[string]any{"num": existUser})
c.JSON(200, result.ErrMsg(msg))
return
}
rows := s.sysTenantService.DeleteTenantById(tenantId)
if rows > 0 {
// 删除成功:%d
msg := i18n.TTemplate(language, "app.common.deleteSuccess", map[string]any{"num": rows})
c.JSON(200, result.OkMsg(msg))
return
}
c.JSON(200, result.Err(nil))
}
// 租户列表(排除节点)
//
// GET /list/exclude/:tenantId
func (s *SysTenantController) ExcludeChild(c *gin.Context) {
language := ctx.AcceptLanguage(c)
tenantId := c.Param("tenantId")
if tenantId == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
dataScopeSQL := ctx.LoginUserToDataScopeSQL(c, "d", "")
data := s.sysTenantService.SelectTenantList(model.SysTenant{}, dataScopeSQL)
// 过滤排除节点
filtered := make([]model.SysTenant, 0)
for _, tenant := range data {
hasAncestor := false
ancestorList := strings.Split(tenant.Ancestors, ",")
for _, ancestor := range ancestorList {
if ancestor == tenantId {
hasAncestor = true
break
}
}
if !(tenant.TenantID == tenantId || hasAncestor) {
tenant.TenantName = i18n.TKey(language, tenant.TenantName)
filtered = append(filtered, tenant)
}
}
c.JSON(200, result.OkData(filtered))
}
// 租户树结构列表
//
// GET /treeSelect
func (s *SysTenantController) TreeSelect(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var querys struct {
// 租户ID
TenantID string `json:"tenantId"`
// 父租户ID
ParentID string `json:"parentId" `
// 租户名称
TenantName string `json:"tenantName" `
// 租户状态0正常 1停用
Status string `json:"status"`
}
err := c.ShouldBindQuery(&querys)
if err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
SysTenantController := model.SysTenant{
TenantID: querys.TenantID,
ParentID: querys.ParentID,
TenantName: querys.TenantName,
Status: querys.Status,
}
dataScopeSQL := ctx.LoginUserToDataScopeSQL(c, "d", "")
tenantTreeSelect := s.sysTenantService.SelectTenantTreeSelect(SysTenantController, dataScopeSQL)
// 闭包函数处理多语言
var converI18n func(language string, arr *[]vo.TreeSelect)
converI18n = func(language string, arr *[]vo.TreeSelect) {
for i := range *arr {
(*arr)[i].Label = i18n.TKey(language, (*arr)[i].Label)
if len((*arr)[i].Children) > 0 {
converI18n(language, &(*arr)[i].Children)
}
}
}
converI18n(language, &tenantTreeSelect)
c.JSON(200, result.OkData(tenantTreeSelect))
}
// 租户树结构列表(指定角色)
//
// GET /roleTenantTreeSelect/:roleId
func (s *SysTenantController) RoleTenantTreeSelect(c *gin.Context) {
language := ctx.AcceptLanguage(c)
roleId := c.Param("roleId")
if roleId == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
dataScopeSQL := ctx.LoginUserToDataScopeSQL(c, "d", "")
tenantTreeSelect := s.sysTenantService.SelectTenantTreeSelect(model.SysTenant{}, dataScopeSQL)
checkedKeys := s.sysTenantService.SelectTenantListByRoleId(roleId)
// 闭包函数处理多语言
var converI18n func(language string, arr *[]vo.TreeSelect)
converI18n = func(language string, arr *[]vo.TreeSelect) {
for i := range *arr {
(*arr)[i].Label = i18n.TKey(language, (*arr)[i].Label)
if len((*arr)[i].Children) > 0 {
converI18n(language, &(*arr)[i].Children)
}
}
}
converI18n(language, &tenantTreeSelect)
c.JSON(200, result.OkData(map[string]any{
"tenants": tenantTreeSelect,
"checkedKeys": checkedKeys,
}))
}

View File

@@ -16,6 +16,8 @@ type SysRole struct {
MenuCheckStrictly string `json:"menuCheckStrictly"`
// 部门树选择项是否关联显示0父子不互相关联显示 1父子互相关联显示
DeptCheckStrictly string `json:"deptCheckStrictly"`
// 部门树选择项是否关联显示0父子不互相关联显示 1父子互相关联显示
TenantCheckStrictly string `json:"tenantCheckStrictly"`
// 角色状态0停用 1正常
Status string `json:"status"`
// 删除标志0代表存在 1代表删除
@@ -37,4 +39,6 @@ type SysRole struct {
MenuIds []string `json:"menuIds,omitempty"`
// 部门组(数据权限)
DeptIds []string `json:"deptIds,omitempty"`
// 租户权限
TenantIds []string `json:"tenantIds,omitempty"`
}

View File

@@ -0,0 +1,15 @@
package model
// SysRoleDept 角色和部门关联对象 sys_role_dept
type SysRoleTenant struct {
RoleID string `json:"roleId"` // 角色ID
TenantID string `json:"TenantID"` // 部门ID
}
// NewSysRoleDept 创建角色和部门关联对象的构造函数
func NewSysRoleTenant(roleID string, tenantID string) SysRoleTenant {
return SysRoleTenant{
RoleID: roleID,
TenantID: tenantID,
}
}

View File

@@ -0,0 +1,39 @@
package model
// SysTenant 部门对象 sys_Tenant
type SysTenant struct {
// 部门ID
TenantID string `json:"TenantId"`
// 父部门ID
ParentID string `json:"parentId" binding:"required"`
// 祖级列表
Ancestors string `json:"ancestors"`
// 部门名称
TenantName string `json:"TenantName" binding:"required"`
// 显示顺序
OrderNum int `json:"orderNum"`
// tenancy type: sd-sst, apn, imsi, msisdn
Type string `json:"type"`
// tenancy key: key of sd-sst, apn, imsi, msisdn
Key string `json:"key"`
// 部门状态0正常 1停用
Status string `json:"status"`
// 删除标志0代表存在 1代表删除
DelFlag string `json:"delFlag"`
// 创建者
CreateBy string `json:"createBy"`
// 创建时间
CreateTime int64 `json:"createTime"`
// 更新者
UpdateBy string `json:"updateBy"`
// 更新时间
UpdateTime int64 `json:"updateTime"`
// ====== 非数据库字段属性 ======
// 子部门列表
Children []SysTenant `json:"children,omitempty"`
// 父部门名称
ParentName string `json:"parentName,omitempty"`
}

View File

@@ -0,0 +1,15 @@
package repository
import "be.ems/src/modules/system/model"
// ISysRoleTenant 角色与部门关联表 数据层接口
type ISysRoleTenant interface {
// DeleteRoleTenant 批量删除角色部门关联信息
DeleteRoleTenant(roleIds []string) int64
// DeleteTenantRole 批量删除部门角色关联信息
DeleteTenantRole(tenantIds []string) int64
// BatchRoleTenant 批量新增角色部门信息
BatchRoleTenant(sysRoleTenants []model.SysRoleTenant) int64
}

View File

@@ -0,0 +1,58 @@
package repository
import (
"fmt"
"strings"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/repo"
"be.ems/src/modules/system/model"
)
// 实例化数据层 SysRoleTenantImpl 结构体
var NewSysRoleTenantImpl = &SysRoleTenantImpl{}
// SysRoleTenantImpl 角色与部门关联表 数据层处理
type SysRoleTenantImpl struct{}
// DeleteRoleTenant 批量删除角色部门关联信息
func (r *SysRoleTenantImpl) DeleteRoleTenant(roleIds []string) int64 {
placeholder := repo.KeyPlaceholderByQuery(len(roleIds))
sql := "delete from sys_role_tenant where role_id in (" + placeholder + ")"
parameters := repo.ConvertIdsSlice(roleIds)
results, err := datasource.ExecDB("", sql, parameters)
if err != nil {
logger.Errorf("delete err => %v", err)
return 0
}
return results
}
// DeleteTenantRole 批量删除部门角色关联信息
func (r *SysRoleTenantImpl) DeleteTenantRole(tenantIds []string) int64 {
placeholder := repo.KeyPlaceholderByQuery(len(tenantIds))
sql := "delete from sys_role_tenant where tenant_id in (" + placeholder + ")"
parameters := repo.ConvertIdsSlice(tenantIds)
results, err := datasource.ExecDB("", sql, parameters)
if err != nil {
logger.Errorf("delete err => %v", err)
return 0
}
return results
}
// BatchRoleTenant 批量新增角色部门信息
func (r *SysRoleTenantImpl) BatchRoleTenant(sysRoleTenants []model.SysRoleTenant) int64 {
keyValues := make([]string, 0)
for _, item := range sysRoleTenants {
keyValues = append(keyValues, fmt.Sprintf("(%s,%s)", item.RoleID, item.TenantID))
}
sql := "insert into sys_role_tenant(role_id, tenant_id) values " + strings.Join(keyValues, ",")
results, err := datasource.ExecDB("", sql, nil)
if err != nil {
logger.Errorf("delete err => %v", err)
return 0
}
return results
}

View File

@@ -0,0 +1,42 @@
package repository
import "be.ems/src/modules/system/model"
// ISysTenant 租户表 数据层接口
type ISysTenant interface {
// SelectTenantList 查询租户管理数据
SelectTenantList(sysTenant model.SysTenant, dataScopeSQL string) []model.SysTenant
// SelectTenantListByRoleId 根据角色ID查询租户树信息
SelectTenantListByRoleId(roleId string, tenantCheckStrictly bool) []string
// SelectTenantById 根据租户ID查询信息
SelectTenantById(tenantId string) model.SysTenant
// SelectChildrenTenantById 根据ID查询所有子租户
SelectChildrenTenantById(tenantId string) []model.SysTenant
// HasChildByTenantId 是否存在子节点
HasChildByTenantId(tenantId string) int64
// CheckTenantExistUser 查询租户是否存在用户
CheckTenantExistUser(tenantId string) int64
// CheckUniqueTenant 校验租户是否唯一
CheckUniqueTenant(sysTenant model.SysTenant) string
// InsertTenant 新增租户信息
InsertTenant(sysTenant model.SysTenant) string
// UpdateTenant 修改租户信息
UpdateTenant(sysTenant model.SysTenant) int64
// UpdateTenantStatusNormal 修改所在租户正常状态
UpdateTenantStatusNormal(tenantIds []string) int64
// UpdateTenantChildren 修改子元素关系
UpdateTenantChildren(sysTenants []model.SysTenant) int64
// DeleteTenantById 删除租户管理信息
DeleteTenantById(tenantId string) int64
}

View File

@@ -0,0 +1,386 @@
package repository
import (
"fmt"
"strings"
"time"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/repo"
"be.ems/src/modules/system/model"
)
// 实例化数据层 SysTenantImpl 结构体
var NewSysTenantImpl = &SysTenantImpl{
selectSql: `select
t.tenant_id, t.parent_id, t.ancestors, t.tenant_name, t.order_num, t.type, t.key, t.status, t.del_flag, t.create_by, t.create_time
from sys_tenant t`,
resultMap: map[string]string{
"tenant_id": "TenantID",
"parent_id": "ParentID",
"ancestors": "Ancestors",
"tenant_name": "TenantName",
"order_num": "OrderNum",
"type": "Type",
"key": "Key",
"status": "Status",
"del_flag": "DelFlag",
"create_by": "CreateBy",
"create_time": "CreateTime",
"update_by": "UpdateBy",
"update_time": "UpdateTime",
"parent_name": "ParentName",
},
}
// SysTenantImpl 部门表 数据层处理
type SysTenantImpl struct {
// 查询视图对象SQL
selectSql string
// 结果字段与实体映射
resultMap map[string]string
}
// convertResultRows 将结果记录转实体结果组
func (r *SysTenantImpl) convertResultRows(rows []map[string]any) []model.SysTenant {
arr := make([]model.SysTenant, 0)
for _, row := range rows {
sysTenant := model.SysTenant{}
for key, value := range row {
if keyMapper, ok := r.resultMap[key]; ok {
repo.SetFieldValue(&sysTenant, keyMapper, value)
}
}
arr = append(arr, sysTenant)
}
return arr
}
// SelectTenantList 查询部门管理数据
func (r *SysTenantImpl) SelectTenantList(sysTenant model.SysTenant, dataScopeSQL string) []model.SysTenant {
// 查询条件拼接
var conditions []string
var params []any
if sysTenant.TenantID != "" {
conditions = append(conditions, "tenant_id = ?")
params = append(params, sysTenant.TenantID)
}
if sysTenant.ParentID != "" {
conditions = append(conditions, "parent_id = ?")
params = append(params, sysTenant.ParentID)
}
if sysTenant.TenantName != "" {
conditions = append(conditions, "tenant_name like concat(?, '%')")
params = append(params, sysTenant.TenantName)
}
if sysTenant.Status != "" {
conditions = append(conditions, "status = ?")
params = append(params, sysTenant.Status)
}
// 构建查询条件语句
whereSql := " where t.del_flag = '0' "
if len(conditions) > 0 {
whereSql += " and " + strings.Join(conditions, " and ")
}
// 查询数据
orderSql := " order by t.parent_id, t.order_num asc "
querySql := r.selectSql + whereSql + dataScopeSQL + orderSql
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
return []model.SysTenant{}
}
// 转换实体
return r.convertResultRows(results)
}
// SelectTenantListByRoleId 根据角色ID查询部门树信息
func (r *SysTenantImpl) SelectTenantListByRoleId(roleId string, tenantCheckStrictly bool) []string {
querySql := `select t.tenant_id as 'str' from sys_tenant d
left join sys_role_tenant rd on t.tenant_id = rt.tenant_id
where rt.role_id = ? `
var params []any
params = append(params, roleId)
// 展开
if tenantCheckStrictly {
querySql += ` and t.tenant_id not in
(select t.parent_id from sys_tenant d
inner join sys_role_tenant rd on t.tenant_id = rt.tenant_id
and rt.role_id = ?) `
params = append(params, roleId)
}
querySql += "order by t.parent_id, t.order_num"
// 查询结果
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
return []string{}
}
if len(results) > 0 {
ids := make([]string, 0)
for _, v := range results {
ids = append(ids, fmt.Sprintf("%v", v["str"]))
}
return ids
}
return []string{}
}
// SelectTenantById 根据部门ID查询信息
func (r *SysTenantImpl) SelectTenantById(tenantId string) model.SysTenant {
querySql := `select t.tenant_id, t.parent_id, t.ancestors,
t.tenant_name, t.order_num, t.type, t.key, t.status,
(select tenant_name from sys_tenant where tenant_id = t.parent_id) parent_name
from sys_tenant t where t.tenant_id = ?`
results, err := datasource.RawDB("", querySql, []any{tenantId})
if err != nil {
logger.Errorf("query err => %v", err)
return model.SysTenant{}
}
// 转换实体
rows := r.convertResultRows(results)
if len(rows) > 0 {
return rows[0]
}
return model.SysTenant{}
}
// SelectChildrenTenantById 根据ID查询所有子部门
func (r *SysTenantImpl) SelectChildrenTenantById(tenantId string) []model.SysTenant {
querySql := r.selectSql + " where find_in_set(?, t.ancestors)"
results, err := datasource.RawDB("", querySql, []any{tenantId})
if err != nil {
logger.Errorf("query err => %v", err)
return []model.SysTenant{}
}
// 转换实体
return r.convertResultRows(results)
}
// HasChildByTenantId 是否存在子节点
func (r *SysTenantImpl) HasChildByTenantId(tenantId string) int64 {
querySql := "select count(1) as 'total' from sys_tenant where status = '1' and parent_id = ? limit 1"
results, err := datasource.RawDB("", querySql, []any{tenantId})
if err != nil {
logger.Errorf("query err => %v", err)
return 0
}
if len(results) > 0 {
return parse.Number(results[0]["total"])
}
return 0
}
// CheckTenantExistUser 查询部门是否存在用户
func (r *SysTenantImpl) CheckTenantExistUser(tenantId string) int64 {
querySql := "select count(1) as 'total' from sys_user where tenant_id = ? and del_flag = '0'"
results, err := datasource.RawDB("", querySql, []any{tenantId})
if err != nil {
logger.Errorf("query err => %v", err)
return 0
}
if len(results) > 0 {
return parse.Number(results[0]["total"])
}
return 0
}
// CheckUniqueTenant 校验部门是否唯一
func (r *SysTenantImpl) CheckUniqueTenant(sysTenant model.SysTenant) string {
// 查询条件拼接
var conditions []string
var params []any
if sysTenant.TenantName != "" {
conditions = append(conditions, "tenant_name = ?")
params = append(params, sysTenant.TenantName)
}
if sysTenant.ParentID != "" {
conditions = append(conditions, "parent_id = ?")
params = append(params, sysTenant.ParentID)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
if whereSql == "" {
return ""
}
// 查询数据
querySql := "select tenant_id as 'str' from sys_tenant " + whereSql + " and del_flag = '0' limit 1"
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err %v", err)
return ""
}
if len(results) > 0 {
return fmt.Sprint(results[0]["str"])
}
return ""
}
// InsertTenant 新增部门信息
func (r *SysTenantImpl) InsertTenant(sysTenant model.SysTenant) string {
// 参数拼接
params := make(map[string]any)
if sysTenant.TenantID != "" {
params["tenant_id"] = sysTenant.TenantID
}
if sysTenant.ParentID != "" {
params["parent_id"] = sysTenant.ParentID
}
if sysTenant.TenantName != "" {
params["tenant_name"] = sysTenant.TenantName
}
if sysTenant.Ancestors != "" {
params["ancestors"] = sysTenant.Ancestors
}
if sysTenant.OrderNum > 0 {
params["order_num"] = sysTenant.OrderNum
}
if sysTenant.Type != "" {
params["type"] = sysTenant.Type
}
if sysTenant.Key != "" {
params["key"] = sysTenant.Key
}
if sysTenant.Status != "" {
params["status"] = sysTenant.Status
}
if sysTenant.CreateBy != "" {
params["create_by"] = sysTenant.CreateBy
params["create_time"] = time.Now().UnixMilli()
}
// 构建执行语句
keys, placeholder, values := repo.KeyPlaceholderValueByInsert(params)
sql := "insert into sys_tenant (" + strings.Join(keys, ",") + ")values(" + placeholder + ")"
db := datasource.DefaultDB()
// 开启事务
tx := db.Begin()
// 执行插入
err := tx.Exec(sql, values...).Error
if err != nil {
logger.Errorf("insert row : %v", err.Error())
tx.Rollback()
return ""
}
// 获取生成的自增 ID
var insertedID string
err = tx.Raw("select last_insert_id()").Row().Scan(&insertedID)
if err != nil {
logger.Errorf("insert last id : %v", err.Error())
tx.Rollback()
return ""
}
// 提交事务
tx.Commit()
return insertedID
}
// UpdateTenant 修改部门信息
func (r *SysTenantImpl) UpdateTenant(sysTenant model.SysTenant) int64 {
// 参数拼接
params := make(map[string]any)
if sysTenant.ParentID != "" {
params["parent_id"] = sysTenant.ParentID
}
if sysTenant.TenantName != "" {
params["tenant_name"] = sysTenant.TenantName
}
if sysTenant.Ancestors != "" {
params["ancestors"] = sysTenant.Ancestors
}
if sysTenant.OrderNum > 0 {
params["order_num"] = sysTenant.OrderNum
}
if sysTenant.Type != "" {
params["type"] = sysTenant.Type
}
if sysTenant.Key != "" {
params["key"] = sysTenant.Key
}
if sysTenant.Status != "" {
params["status"] = sysTenant.Status
}
if sysTenant.UpdateBy != "" {
params["update_by"] = sysTenant.UpdateBy
params["update_time"] = time.Now().UnixMilli()
}
// 构建执行语句
keys, values := repo.KeyValueByUpdate(params)
sql := "update sys_tenant set " + strings.Join(keys, ",") + " where tenant_id = ?"
// 执行更新
values = append(values, sysTenant.TenantID)
rows, err := datasource.ExecDB("", sql, values)
if err != nil {
logger.Errorf("update row : %v", err.Error())
return 0
}
return rows
}
// UpdateTenantStatusNormal 修改所在部门正常状态
func (r *SysTenantImpl) UpdateTenantStatusNormal(tenantIds []string) int64 {
placeholder := repo.KeyPlaceholderByQuery(len(tenantIds))
sql := "update sys_tenant set status = '1' where tenant_id in (" + placeholder + ")"
parameters := repo.ConvertIdsSlice(tenantIds)
results, err := datasource.ExecDB("", sql, parameters)
if err != nil {
logger.Errorf("update err => %v", err)
return 0
}
return results
}
// UpdateTenantChildren 修改子元素关系
func (r *SysTenantImpl) UpdateTenantChildren(sysTenants []model.SysTenant) int64 {
// 无参数
if len(sysTenants) == 0 {
return 0
}
// 更新条件拼接
var conditions []string
var params []any
for _, tenant := range sysTenants {
caseSql := fmt.Sprintf("WHEN tenant_id = '%s' THEN '%s'", tenant.TenantID, tenant.Ancestors)
conditions = append(conditions, caseSql)
params = append(params, tenant.TenantID)
}
cases := strings.Join(conditions, " ")
placeholders := repo.KeyPlaceholderByQuery(len(params))
sql := "update sys_tenant set ancestors = CASE " + cases + " END where tenant_id in (" + placeholders + ")"
results, err := datasource.ExecDB("", sql, params)
if err != nil {
logger.Errorf("delete err => %v", err)
return 0
}
return results
}
// DeleteTenantById 删除部门管理信息
func (r *SysTenantImpl) DeleteTenantById(tenantId string) int64 {
sql := "update sys_tenant set status = '0', del_flag = '1' where tenant_id = ?"
results, err := datasource.ExecDB("", sql, []any{tenantId})
if err != nil {
logger.Errorf("delete err => %v", err)
return 0
}
return results
}

View File

@@ -0,0 +1,39 @@
package service
import (
"be.ems/src/framework/vo"
"be.ems/src/modules/system/model"
)
// ISysTenant 租户管理 服务层接口
type ISysTenant interface {
// SelectTenantList 查询租户管理数据
SelectTenantList(sysTenant model.SysTenant, dataScopeSQL string) []model.SysTenant
// SelectTenantListByRoleId 根据角色ID查询租户树信息
SelectTenantListByRoleId(roleId string) []string
// SelectTenantById 根据租户ID查询信息
SelectTenantById(tenantId string) model.SysTenant
// HasChildByTenantId 是否存在子节点
HasChildByTenantId(tenantId string) int64
// CheckTenantExistUser 查询租户是否存在用户
CheckTenantExistUser(tenantId string) int64
// CheckUniqueTenantName 校验同级租户名称是否唯一
CheckUniqueTenantName(tenantName, parentId, tenantId string) bool
// InsertTenant 新增租户信息
InsertTenant(sysTenant model.SysTenant) string
// UpdateTenant 修改租户信息
UpdateTenant(sysTenant model.SysTenant) int64
// DeleteTenantById 删除租户管理信息
DeleteTenantById(tenantId string) int64
// SelectTenantTreeSelect 查询租户树结构信息
SelectTenantTreeSelect(sysTenant model.SysTenant, dataScopeSQL string) []vo.TreeSelect
}

View File

@@ -0,0 +1,202 @@
package service
import (
"strings"
"be.ems/src/framework/constants/common"
"be.ems/src/framework/vo"
"be.ems/src/modules/system/model"
"be.ems/src/modules/system/repository"
)
// 实例化服务层 SysTenantImpl 结构体
var NewSysTenantImpl = &SysTenantImpl{
sysTenantRepository: repository.NewSysTenantImpl,
sysRoleRepository: repository.NewSysRoleImpl,
sysRoleTenantRepository: repository.NewSysRoleTenantImpl,
}
// SysTenantImpl 部门表 服务层处理
type SysTenantImpl struct {
// 部门服务
sysTenantRepository repository.ISysTenant
// 角色服务
sysRoleRepository repository.ISysRole
// 角色与部门关联服务
sysRoleTenantRepository repository.ISysRoleTenant
}
// SelectTenantList 查询部门管理数据
func (r *SysTenantImpl) SelectTenantList(sysTenant model.SysTenant, dataScopeSQL string) []model.SysTenant {
return r.sysTenantRepository.SelectTenantList(sysTenant, dataScopeSQL)
}
// SelectTenantListByRoleId 根据角色ID查询部门树信息 TODO
func (r *SysTenantImpl) SelectTenantListByRoleId(roleId string) []string {
roles := r.sysRoleRepository.SelectRoleByIds([]string{roleId})
if len(roles) > 0 {
role := roles[0]
if role.RoleID == roleId {
return r.sysTenantRepository.SelectTenantListByRoleId(
role.RoleID,
role.TenantCheckStrictly == "1",
)
}
}
return []string{}
}
// SelectTenantById 根据部门ID查询信息
func (r *SysTenantImpl) SelectTenantById(tenantId string) model.SysTenant {
return r.sysTenantRepository.SelectTenantById(tenantId)
}
// HasChildByTenantId 是否存在子节点
func (r *SysTenantImpl) HasChildByTenantId(tenantId string) int64 {
return r.sysTenantRepository.HasChildByTenantId(tenantId)
}
// CheckTenantExistUser 查询部门是否存在用户
func (r *SysTenantImpl) CheckTenantExistUser(tenantId string) int64 {
return r.sysTenantRepository.CheckTenantExistUser(tenantId)
}
// CheckUniqueTenantName 校验同级部门名称是否唯一
func (r *SysTenantImpl) CheckUniqueTenantName(tenantName, parentId, tenantId string) bool {
uniqueId := r.sysTenantRepository.CheckUniqueTenant(model.SysTenant{
TenantName: tenantName,
ParentID: parentId,
})
if uniqueId == tenantId {
return true
}
return uniqueId == ""
}
// InsertTenant 新增部门信息
func (r *SysTenantImpl) InsertTenant(sysTenant model.SysTenant) string {
return r.sysTenantRepository.InsertTenant(sysTenant)
}
// UpdateTenant 修改部门信息
func (r *SysTenantImpl) UpdateTenant(sysTenant model.SysTenant) int64 {
parentTenant := r.sysTenantRepository.SelectTenantById(sysTenant.ParentID)
tenant := r.sysTenantRepository.SelectTenantById(sysTenant.TenantID)
// 上级与当前部门祖级列表更新
if parentTenant.TenantID == sysTenant.ParentID && tenant.TenantID == sysTenant.TenantID {
newAncestors := parentTenant.Ancestors + "," + parentTenant.TenantID
oldAncestors := tenant.Ancestors
// 祖级列表不一致时更新
if newAncestors != oldAncestors {
sysTenant.Ancestors = newAncestors
r.updateTenantChildren(sysTenant.TenantID, newAncestors, oldAncestors)
}
}
// 如果该部门是启用状态,则启用该部门的所有上级部门
if sysTenant.Status == common.STATUS_YES && parentTenant.Status == common.STATUS_NO {
r.updateTenantStatusNormal(sysTenant.Ancestors)
}
return r.sysTenantRepository.UpdateTenant(sysTenant)
}
// updateTenantStatusNormal 修改所在部门正常状态
func (r *SysTenantImpl) updateTenantStatusNormal(ancestors string) int64 {
if ancestors == "" || ancestors == "0" {
return 0
}
tenantIds := strings.Split(ancestors, ",")
return r.sysTenantRepository.UpdateTenantStatusNormal(tenantIds)
}
// updateTenantChildren 修改子元素关系
func (r *SysTenantImpl) updateTenantChildren(tenantId, newAncestors, oldAncestors string) int64 {
childrens := r.sysTenantRepository.SelectChildrenTenantById(tenantId)
if len(childrens) == 0 {
return 0
}
// 替换父ID
for i := range childrens {
child := &childrens[i]
ancestors := strings.Replace(child.Ancestors, oldAncestors, newAncestors, -1)
child.Ancestors = ancestors
}
return r.sysTenantRepository.UpdateTenantChildren(childrens)
}
// DeleteTenantById 删除部门管理信息
func (r *SysTenantImpl) DeleteTenantById(tenantId string) int64 {
// 删除角色与部门关联
r.sysRoleTenantRepository.DeleteTenantRole([]string{tenantId})
return r.sysTenantRepository.DeleteTenantById(tenantId)
}
// SelectTenantTreeSelect 查询部门树结构信息
func (r *SysTenantImpl) SelectTenantTreeSelect(sysTenant model.SysTenant, dataScopeSQL string) []vo.TreeSelect {
sysTenants := r.sysTenantRepository.SelectTenantList(sysTenant, dataScopeSQL)
tenants := r.parseDataToTree(sysTenants)
tree := make([]vo.TreeSelect, 0)
for _, tenant := range tenants {
tree = append(tree, vo.SysTenantTreeSelect(tenant))
}
return tree
}
// parseDataToTree 将数据解析为树结构,构建前端所需要下拉树结构
func (r *SysTenantImpl) parseDataToTree(sysTenants []model.SysTenant) []model.SysTenant {
// 节点分组
nodesMap := make(map[string][]model.SysTenant)
// 节点id
treeIds := []string{}
// 树节点
tree := []model.SysTenant{}
for _, item := range sysTenants {
parentID := item.ParentID
// 分组
mapItem, ok := nodesMap[parentID]
if !ok {
mapItem = []model.SysTenant{}
}
mapItem = append(mapItem, item)
nodesMap[parentID] = mapItem
// 记录节点ID
treeIds = append(treeIds, item.TenantID)
}
for key, value := range nodesMap {
// 选择不是节点ID的作为树节点
found := false
for _, id := range treeIds {
if id == key {
found = true
break
}
}
if !found {
tree = append(tree, value...)
}
}
for i, node := range tree {
iN := r.parseDataToTreeComponet(node, &nodesMap)
tree[i] = iN
}
return tree
}
// parseDataToTreeComponet 递归函数处理子节点
func (r *SysTenantImpl) parseDataToTreeComponet(node model.SysTenant, nodesMap *map[string][]model.SysTenant) model.SysTenant {
id := node.TenantID
children, ok := (*nodesMap)[id]
if ok {
node.Children = children
}
if len(node.Children) > 0 {
for i, child := range node.Children {
icN := r.parseDataToTreeComponet(child, nodesMap)
node.Children[i] = icN
}
}
return node
}

View File

@@ -103,6 +103,46 @@ func Setup(router *gin.Engine) {
)
}
// 租户管理信息
sysTenantGroup := router.Group("/system/tenant")
{
sysTenantGroup.GET("/list",
middleware.PreAuthorize(map[string][]string{"hasPerms": {"system:tenant:list"}}),
controller.NewSysTenant.List,
)
sysTenantGroup.GET("/:tenantId",
middleware.PreAuthorize(map[string][]string{"hasPerms": {"system:tenant:query"}}),
controller.NewSysTenant.Info,
)
sysTenantGroup.POST("",
middleware.PreAuthorize(map[string][]string{"hasPerms": {"system:tenant:add"}}),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.sysTenant", collectlogs.BUSINESS_TYPE_INSERT)),
controller.NewSysTenant.Add,
)
sysTenantGroup.PUT("",
middleware.PreAuthorize(map[string][]string{"hasPerms": {"system:tenant:edit"}}),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.sysTenant", collectlogs.BUSINESS_TYPE_UPDATE)),
controller.NewSysTenant.Edit,
)
sysTenantGroup.DELETE("/:tenantId",
middleware.PreAuthorize(map[string][]string{"hasPerms": {"system:tenant:remove"}}),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.sysTenant", collectlogs.BUSINESS_TYPE_DELETE)),
controller.NewSysTenant.Remove,
)
sysTenantGroup.GET("/list/exclude/:tenantId",
middleware.PreAuthorize(map[string][]string{"hasPerms": {"system:tenant:list"}}),
controller.NewSysTenant.ExcludeChild,
)
sysTenantGroup.GET("/treeSelect",
middleware.PreAuthorize(map[string][]string{"hasPerms": {"system:tenant:list", "system:user:list"}}),
controller.NewSysTenant.TreeSelect,
)
sysTenantGroup.GET("/roleTenantTreeSelect/:roleId",
middleware.PreAuthorize(map[string][]string{"hasPerms": {"system:tenant:query", "system:user:edit"}}),
controller.NewSysTenant.RoleTenantTreeSelect,
)
}
// 字典数据信息
sysDictDataGroup := router.Group("/system/dict/data")
{