add: multi-tenant
This commit is contained in:
@@ -208,7 +208,7 @@ func (r *RepoUdmSubUser) ClearAndInsert(neID string, subArr []model.UdmSubUser)
|
|||||||
batch := subArr[i:end]
|
batch := subArr[i:end]
|
||||||
|
|
||||||
// multi-tenancy
|
// multi-tenancy
|
||||||
r.SetTenantName(batch)
|
r.SetTenantName(&batch)
|
||||||
|
|
||||||
// 调用 InsertMulti 函数将批量数据插入数据库
|
// 调用 InsertMulti 函数将批量数据插入数据库
|
||||||
results, err := datasource.DefaultDB().Table("u_sub_user").InsertMulti(batch)
|
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]
|
batch := subUser[i:end]
|
||||||
|
|
||||||
// multi-tenancy
|
// multi-tenancy
|
||||||
r.SetTenantName(batch)
|
r.SetTenantName(&batch)
|
||||||
|
|
||||||
// 调用 InsertMulti 函数将批量数据插入数据库
|
// 调用 InsertMulti 函数将批量数据插入数据库
|
||||||
results, err := datasource.DefaultDB().Table("u_sub_user").InsertMulti(batch)
|
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
|
// multi-tenancy
|
||||||
subUserSlice := []model.UdmSubUser{subUser}
|
subUserSlice := []model.UdmSubUser{subUser}
|
||||||
r.SetTenantName(subUserSlice)
|
r.SetTenantName(&subUserSlice)
|
||||||
|
|
||||||
results, err := datasource.DefaultDB().Table("u_sub_user").Insert(subUser)
|
results, err := datasource.DefaultDB().Table("u_sub_user").Insert(subUser)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -475,15 +475,18 @@ func (r *RepoUdmSubUser) Deletes(neID, imsi, num string) int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// multi-tenancy solution, get tenant_name by imsi
|
// multi-tenancy solution, get tenant_name by imsi
|
||||||
func (r *RepoUdmSubUser) SetTenantName(subArr []model.UdmSubUser) {
|
func (r *RepoUdmSubUser) SetTenantName(subArr *[]model.UdmSubUser) {
|
||||||
for s := 0; s < len(subArr); s++ {
|
for s := 0; s < len(*subArr); s++ {
|
||||||
var tenantName string
|
var tenantName string
|
||||||
err := datasource.DefaultDB().Table("sys_tenant_map").
|
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 {
|
if err != nil {
|
||||||
log.Errorf("Find tenant_name err => %v", err)
|
log.Errorf("Find tenant_name err => %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
subArr[s].TenantName = tenantName
|
if tenantName != "" {
|
||||||
|
log.Infof("tenantName=%s imsi=%s", tenantName, (*subArr)[s].Imsi)
|
||||||
|
}
|
||||||
|
(*subArr)[s].TenantName = tenantName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,3 +49,21 @@ func SysDeptTreeSelect(sysDept systemModel.SysDept) TreeSelect {
|
|||||||
|
|
||||||
return t
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ var NewUDMSubImpl = &UDMSubImpl{
|
|||||||
"context_id": "ContextId",
|
"context_id": "ContextId",
|
||||||
"apn_context": "ApnContext",
|
"apn_context": "ApnContext",
|
||||||
"static_ip": "StaticIp",
|
"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
|
// for multi-tenancy solution
|
||||||
if v, ok := query["tenantName"]; ok && v != "" {
|
if v, ok := query["tenantName"]; ok && v != "" {
|
||||||
conditions = append(conditions, "tenant_name = '?'")
|
conditions = append(conditions, "tenant_name = ?")
|
||||||
params = append(params, v)
|
params = append(params, v)
|
||||||
|
fmt.Printf("tenantName = %s", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建查询条件语句
|
// 构建查询条件语句
|
||||||
|
|||||||
381
src/modules/system/controller/sys_tenant.go
Normal file
381
src/modules/system/controller/sys_tenant.go
Normal 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,
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -16,6 +16,8 @@ type SysRole struct {
|
|||||||
MenuCheckStrictly string `json:"menuCheckStrictly"`
|
MenuCheckStrictly string `json:"menuCheckStrictly"`
|
||||||
// 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示)
|
// 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示)
|
||||||
DeptCheckStrictly string `json:"deptCheckStrictly"`
|
DeptCheckStrictly string `json:"deptCheckStrictly"`
|
||||||
|
// 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示)
|
||||||
|
TenantCheckStrictly string `json:"tenantCheckStrictly"`
|
||||||
// 角色状态(0停用 1正常)
|
// 角色状态(0停用 1正常)
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
// 删除标志(0代表存在 1代表删除)
|
// 删除标志(0代表存在 1代表删除)
|
||||||
@@ -37,4 +39,6 @@ type SysRole struct {
|
|||||||
MenuIds []string `json:"menuIds,omitempty"`
|
MenuIds []string `json:"menuIds,omitempty"`
|
||||||
// 部门组(数据权限)
|
// 部门组(数据权限)
|
||||||
DeptIds []string `json:"deptIds,omitempty"`
|
DeptIds []string `json:"deptIds,omitempty"`
|
||||||
|
// 租户权限
|
||||||
|
TenantIds []string `json:"tenantIds,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/modules/system/model/sys_role_tenant.go
Normal file
15
src/modules/system/model/sys_role_tenant.go
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/modules/system/model/sys_tenant.go
Normal file
39
src/modules/system/model/sys_tenant.go
Normal 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"`
|
||||||
|
}
|
||||||
15
src/modules/system/repository/sys_role_tenant.go
Normal file
15
src/modules/system/repository/sys_role_tenant.go
Normal 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
|
||||||
|
}
|
||||||
58
src/modules/system/repository/sys_role_tenant.impl.go
Normal file
58
src/modules/system/repository/sys_role_tenant.impl.go
Normal 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
|
||||||
|
}
|
||||||
42
src/modules/system/repository/sys_tenant.go
Normal file
42
src/modules/system/repository/sys_tenant.go
Normal 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
|
||||||
|
}
|
||||||
386
src/modules/system/repository/sys_tenant.impl.go
Normal file
386
src/modules/system/repository/sys_tenant.impl.go
Normal 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
|
||||||
|
}
|
||||||
39
src/modules/system/service/sys_tenant.go
Normal file
39
src/modules/system/service/sys_tenant.go
Normal 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
|
||||||
|
}
|
||||||
202
src/modules/system/service/sys_tenant.impl.go
Normal file
202
src/modules/system/service/sys_tenant.impl.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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")
|
sysDictDataGroup := router.Group("/system/dict/data")
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user