chore: 并入 iotlcm-admin-svr 历史到 admin-svr/(原提交 49818c6)
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
|
||||
"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/xgorm/handler"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
type agentBase struct{}
|
||||
|
||||
func newAgentBase() *agentBase {
|
||||
return &agentBase{}
|
||||
}
|
||||
|
||||
func (s *agentBase) deptModel(ctx context.Context) *gdb.Model {
|
||||
return service.SysDept().Model(ctx, &handler.Option{FilterTenant: true, FilterAuth: false})
|
||||
}
|
||||
|
||||
func (s *agentBase) roleModel(ctx context.Context) *gdb.Model {
|
||||
return service.SysRole().ModelQuery(ctx, &handler.Option{FilterTenant: true, FilterAuth: false})
|
||||
}
|
||||
|
||||
func (s *agentBase) userModel(ctx context.Context) *gdb.Model {
|
||||
return service.SysUser().Model(ctx, &handler.Option{FilterTenant: true, FilterAuth: false})
|
||||
}
|
||||
|
||||
// scopeDeptIds 获取当前登录组织本级及下一级组织的部门ID列表
|
||||
func (s *agentBase) scopeDeptIds(ctx context.Context) (deptIds []int64, err error) {
|
||||
currentDeptId := contexts.GetDeptId(ctx)
|
||||
if currentDeptId == 0 {
|
||||
return nil, gerror.New("当前用户未绑定组织")
|
||||
}
|
||||
deptIds = []int64{currentDeptId}
|
||||
var children []*entity.SysDept
|
||||
err = service.SysDept().Model(ctx, &handler.Option{FilterTenant: true, FilterAuth: false}).
|
||||
Where(dao.SysDept.Columns().ParentId, currentDeptId).
|
||||
Order(dao.SysDept.Columns().OrderNum, "ASC").
|
||||
Scan(&children)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, dept := range children {
|
||||
deptIds = append(deptIds, dept.DeptId)
|
||||
}
|
||||
slices.Sort(deptIds)
|
||||
return slices.Compact(deptIds), nil
|
||||
}
|
||||
|
||||
// ensureDeptInScope 确保部门在当前机构的权限范围内
|
||||
func (s *agentBase) ensureDeptInScope(ctx context.Context, deptId int64) error {
|
||||
deptIds, err := s.scopeDeptIds(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !slices.Contains(deptIds, deptId) {
|
||||
return gerror.New("只能操作本级及下一级组织")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureRoleInScope 确保角色在当前机构的权限范围内
|
||||
func (s *agentBase) ensureRoleInScope(ctx context.Context, roleId int64) error {
|
||||
if roleId == 0 {
|
||||
return nil
|
||||
}
|
||||
deptIds, err := s.scopeDeptIds(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count, err := s.roleModel(ctx).
|
||||
Where(dao.SysRole.Columns().RoleId, roleId).
|
||||
WhereIn(dao.SysRole.Columns().DeptId, deptIds).
|
||||
Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return gerror.New("只能绑定本级及下一级组织的角色")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureDeptRoleUnchangedForSelf 确保当前机构的权限未被修改
|
||||
func (s *agentBase) ensureDeptRoleUnchangedForSelf(ctx context.Context, deptId, deptRole int64) error {
|
||||
currentDeptId := contexts.GetDeptId(ctx)
|
||||
if deptId == 0 || deptId != currentDeptId {
|
||||
return nil
|
||||
}
|
||||
var dept *entity.SysDept
|
||||
err := s.deptModel(ctx).
|
||||
Where(dao.SysDept.Columns().DeptId, currentDeptId).
|
||||
Scan(&dept)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dept == nil || dept.DeptId == 0 {
|
||||
return gerror.New("组织不存在")
|
||||
}
|
||||
if dept.DeptRole != deptRole {
|
||||
return gerror.New("不能修改本级组织的权限")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// currentDeptDefaultRoleId 获取当前机构的默认角色ID
|
||||
func (s *agentBase) currentDeptDefaultRoleId(ctx context.Context) (int64, error) {
|
||||
currentDeptId := contexts.GetDeptId(ctx)
|
||||
if currentDeptId == 0 {
|
||||
return 0, gerror.New("当前用户未绑定组织")
|
||||
}
|
||||
var dept *entity.SysDept
|
||||
err := s.deptModel(ctx).
|
||||
Where(dao.SysDept.Columns().DeptId, currentDeptId).
|
||||
Scan(&dept)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if dept == nil || dept.DeptId == 0 {
|
||||
return 0, gerror.New("组织不存在")
|
||||
}
|
||||
return dept.DeptRole, nil
|
||||
}
|
||||
|
||||
// ensureRoleNotCurrentDeptDefault 确保角色不是当前机构的默认角色
|
||||
func (s *agentBase) ensureRoleNotCurrentDeptDefault(ctx context.Context, roleId int64) error {
|
||||
if roleId == 0 {
|
||||
return nil
|
||||
}
|
||||
defaultRoleId, err := s.currentDeptDefaultRoleId(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if defaultRoleId != 0 && roleId == defaultRoleId {
|
||||
return gerror.New("不能修改本级机构绑定的默认角色权限")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,72 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type paginationDeptService struct{ service.ISysDept }
|
||||
|
||||
func (paginationDeptService) Model(ctx context.Context, _ ...*handler.Option) *gdb.Model {
|
||||
return g.DB().Model("sys_dept").Ctx(ctx).Handler(handler.FilterTenant)
|
||||
}
|
||||
|
||||
type paginationUserService struct{ service.ISysUser }
|
||||
|
||||
func (paginationUserService) Model(ctx context.Context, _ ...*handler.Option) *gdb.Model {
|
||||
return g.DB().Model("sys_user").Ctx(ctx).Handler(handler.FilterTenant)
|
||||
}
|
||||
|
||||
func TestEnterpriseCustomerDatabasePaginationAndDefaultUsers(t *testing.T) {
|
||||
setupAgentRoleSyncTestDB(t)
|
||||
ctx := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{TenantId: "000000", DeptId: 1}}})
|
||||
service.RegisterSysDept(paginationDeptService{})
|
||||
service.RegisterSysUser(paginationUserService{})
|
||||
t.Cleanup(func() {
|
||||
service.RegisterSysDept(nil)
|
||||
service.RegisterSysUser(nil)
|
||||
})
|
||||
statements := []string{
|
||||
"ALTER TABLE sys_dept ADD COLUMN dept_name TEXT",
|
||||
"CREATE TABLE sys_user (user_id INTEGER PRIMARY KEY, tenant_id TEXT, dept_id INTEGER, user_name TEXT, nick_name TEXT, deleted_at DATETIME)",
|
||||
"INSERT INTO sys_dept (dept_id,tenant_id,parent_id,dept_type,dept_role,dept_name) VALUES (2,'000000',1,1,12,'企业A'),(3,'000000',1,1,13,'企业B'),(4,'000000',1,1,14,'企业C'),(5,'000000',2,1,15,'孙级企业'),(6,'other',1,1,16,'其他租户'),(7,'000000',1,0,17,'内部部门'),(8,'000000',1,1,18,'已删除企业'),(9,'000000',1,1,0,'无账号企业')",
|
||||
"UPDATE sys_dept SET deleted_at=CURRENT_TIMESTAMP WHERE dept_id=8",
|
||||
"INSERT INTO sys_user VALUES (20,'000000',2,'fallback-a','回退A',NULL),(21,'000000',2,'admin-a','管理员A',NULL),(30,'000000',3,'deleted-admin','已删除',CURRENT_TIMESTAMP),(31,'000000',3,'fallback-b','回退B',NULL),(40,'000000',4,'fallback-c','回退C',NULL),(50,'other',4,'other-user','跨租户',NULL)",
|
||||
"INSERT INTO sys_user_role (tenant_id,user_id,role_id) VALUES ('000000',21,12),('000000',30,13),('other',50,14)",
|
||||
}
|
||||
for _, statement := range statements {
|
||||
mustExecAgentRoleSyncTest(t, ctx, statement)
|
||||
}
|
||||
subject := NewAgentDept()
|
||||
params := &model.AgentDeptListParam{Page: 1, PageSize: 2}
|
||||
list, total, err := subject.DeptList(ctx, params)
|
||||
if err != nil || total != 4 || len(list) != 2 {
|
||||
t.Fatalf("page 1: total=%d list=%+v err=%v", total, list, err)
|
||||
}
|
||||
if list[0].DeptId != 2 || list[0].UserName != "admin-a" || list[1].DeptId != 3 || list[1].UserName != "fallback-b" {
|
||||
t.Fatalf("unexpected account selection: %+v %+v", list[0], list[1])
|
||||
}
|
||||
params.Page = 2
|
||||
list, total, err = subject.DeptList(ctx, params)
|
||||
if err != nil || total != 4 || len(list) != 2 || list[0].DeptId != 4 || list[0].UserName != "fallback-c" || list[1].DeptId != 9 || list[1].UserName != "" {
|
||||
t.Fatalf("page 2: total=%d list=%+v err=%v", total, list, err)
|
||||
}
|
||||
params.Page = 3
|
||||
list, total, err = subject.DeptList(ctx, params)
|
||||
if err != nil || total != 4 || len(list) != 0 || list == nil {
|
||||
t.Fatalf("out of range: total=%d list=%+v err=%v", total, list, err)
|
||||
}
|
||||
list, total, err = subject.DeptList(ctx, &model.AgentDeptListParam{DeptName: "企业A", Status: "0"})
|
||||
if err != nil || total != 1 || len(list) != 1 || list[0].DeptId != 2 {
|
||||
t.Fatalf("filtered list: total=%d list=%+v err=%v", total, list, err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcfg"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
|
||||
_ "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/logic/memorydb"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/logic/system"
|
||||
)
|
||||
|
||||
// 复现生产形态:模板角色已配置(role 18 @ dept 100,is_pass=1),
|
||||
// 一级代理的默认角色是创建代理时复制下来的(is_pass=0)。
|
||||
// 二级代理(dept 1862, parent 128)要再创建下级时,必须能从本机构默认角色拿到权限。
|
||||
func TestPassRoleByDeptWorksForChildAgentWithoutTemplateFlag(t *testing.T) {
|
||||
setupPassRoleTestDB(t)
|
||||
ctx := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{
|
||||
ID: 1,
|
||||
TenantId: "000000",
|
||||
DeptId: 128, // 一级代理,要继续创建二级代理
|
||||
}}})
|
||||
|
||||
// 三级结构:平台 100(模板 18,is_pass=1)→ 一级代理 128(默认角色 2321,is_pass=0)→ 二级代理 1862(默认角色 1779,is_pass=0)
|
||||
mustExec(t, ctx, `INSERT INTO sys_dept (dept_id, tenant_id, parent_id, dept_type, status, dept_role) VALUES
|
||||
(100,'000000',0,1,'0',1),
|
||||
(128,'000000',100,1,'0',2321),
|
||||
(1862,'000000',128,1,'0',1779)`)
|
||||
mustExec(t, ctx, `INSERT INTO sys_role (role_id, tenant_id, dept_id, role_name, role_key, status, data_scope, is_pass, created_dept) VALUES
|
||||
(18,'000000',100,'代理商','dl','0','4',1,100),
|
||||
(2321,'000000',128,'代理商','dl','0','4',0,128),
|
||||
(1779,'000000',1862,'代理商','dl','0','4',0,100)`)
|
||||
mustExec(t, ctx, `INSERT INTO sys_role_menu (tenant_id, role_id, menu_id, data_scope) VALUES
|
||||
('000000',18,100,'0'),('000000',2321,100,'0'),('000000',1779,100,'0')`)
|
||||
mustExec(t, ctx, `INSERT INTO sys_config (config_id, tenant_id, config_key, config_value) VALUES (1,'000000','agent.role.template.role_id','18')`)
|
||||
|
||||
service := NewAgentDept()
|
||||
role, menus, err := service.currentDeptPassRole(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("一级代理创建下级时应能取到本机构默认角色权限,实际报错: %v", err)
|
||||
}
|
||||
if role == nil || role.RoleId != 2321 {
|
||||
t.Fatalf("应以本机构默认角色 2321 作为下发来源,实际: %+v", role)
|
||||
}
|
||||
if len(menus) != 1 || menus[0].MenuId != 100 {
|
||||
t.Fatalf("应复制本机构默认角色的菜单,实际: %+v", menus)
|
||||
}
|
||||
}
|
||||
|
||||
func setupPassRoleTestDB(t *testing.T) {
|
||||
t.Helper()
|
||||
service.RegisterSysDept(system.NewSysDept())
|
||||
service.RegisterSysRole(system.NewSysRole())
|
||||
if err := gdb.SetConfig(gdb.Config{
|
||||
"default": gdb.ConfigGroup{
|
||||
{Type: "sqlite", Link: "sqlite::@file(:memory:)", Debug: false},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("configure sqlite failed: %v", err)
|
||||
}
|
||||
// 走真实部署形态:内存库指向主库(config.yaml 里 link 为空时回落主库)
|
||||
if err := g.Cfg().GetAdapter().(*gcfg.AdapterFile).Set("database.memorydb.link", ""); err != nil {
|
||||
t.Fatalf("override memorydb link failed: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
t.Cleanup(func() { _ = g.DB().Close(ctx) })
|
||||
mustExec(t, ctx, `CREATE TABLE sys_dept (dept_id INTEGER PRIMARY KEY, tenant_id TEXT, parent_id INTEGER, dept_name TEXT, dept_type INTEGER, status TEXT DEFAULT '0', dept_role INTEGER, deleted_at DATETIME)`)
|
||||
mustExec(t, ctx, `CREATE TABLE sys_config (config_id INTEGER PRIMARY KEY, tenant_id TEXT, config_name TEXT, config_key TEXT, config_value TEXT, config_type TEXT, created_dept INTEGER, created_by INTEGER, created_at DATETIME, updated_by INTEGER, updated_at DATETIME, remark TEXT)`)
|
||||
mustExec(t, ctx, `CREATE TABLE sys_role (
|
||||
role_id INTEGER PRIMARY KEY AUTOINCREMENT, tenant_id TEXT, dept_id INTEGER, role_name TEXT, role_key TEXT, role_sort INTEGER, data_scope TEXT,
|
||||
menu_check_strictly INTEGER, dept_check_strictly INTEGER, status TEXT, is_pass INTEGER, created_dept INTEGER, created_by INTEGER, created_at DATETIME, updated_by INTEGER, updated_at DATETIME, deleted_by INTEGER, deleted_at DATETIME, remark TEXT)`)
|
||||
mustExec(t, ctx, `CREATE TABLE sys_role_menu (tenant_id TEXT, role_id INTEGER, menu_id INTEGER, data_scope TEXT)`)
|
||||
mustExec(t, ctx, `CREATE TABLE sys_role_dept (tenant_id TEXT, role_id INTEGER, dept_id INTEGER)`)
|
||||
mustExec(t, ctx, `CREATE TABLE sys_user_role (tenant_id TEXT DEFAULT '000000', user_id INTEGER, role_id INTEGER)`)
|
||||
_ = consts.SysRoleDataScopeDeptAndBelow
|
||||
}
|
||||
|
||||
func mustExec(t *testing.T, ctx context.Context, sql string) {
|
||||
t.Helper()
|
||||
if _, err := g.DB().Exec(ctx, sql); err != nil {
|
||||
t.Fatalf("execute sql failed: %v\nsql=%s", err, sql)
|
||||
}
|
||||
}
|
||||
|
||||
// 平台自身(dept 100 = 模板所属机构)仍以模板角色为下发来源。
|
||||
func TestPassRoleByDeptTemplateOwnerUsesTemplateRole(t *testing.T) {
|
||||
setupPassRoleTestDB(t)
|
||||
ctx := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{
|
||||
ID: 1, TenantId: "000000", DeptId: 100,
|
||||
}}})
|
||||
mustExec(t, ctx, `INSERT INTO sys_dept (dept_id, tenant_id, parent_id, dept_type, status, dept_role) VALUES (100,'000000',0,1,'0',1)`)
|
||||
mustExec(t, ctx, `INSERT INTO sys_role (role_id, tenant_id, dept_id, role_name, role_key, status, data_scope, is_pass, created_dept) VALUES
|
||||
(1,'000000',100,'超级管理员','admin','0','1',0,100),
|
||||
(18,'000000',100,'代理商','dl','0','4',1,100)`)
|
||||
mustExec(t, ctx, `INSERT INTO sys_role_menu (tenant_id, role_id, menu_id, data_scope) VALUES ('000000',18,100,'0')`)
|
||||
mustExec(t, ctx, `INSERT INTO sys_config (config_id, tenant_id, config_key, config_value) VALUES (1,'000000','agent.role.template.role_id','18')`)
|
||||
|
||||
role, menus, err := NewAgentDept().currentDeptPassRole(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("平台创建一级代理应成功,实际: %v", err)
|
||||
}
|
||||
if role.RoleId != 18 {
|
||||
t.Fatalf("平台下发来源应为模板角色 18,实际: %d", role.RoleId)
|
||||
}
|
||||
if len(menus) != 1 || menus[0].MenuId != 100 {
|
||||
t.Fatalf("应复制模板菜单,实际: %+v", menus)
|
||||
}
|
||||
}
|
||||
|
||||
// 默认角色缺失/停用/归属不符时必须明确报错,不能静默 0 权限。
|
||||
func TestPassRoleByDeptRejectsBrokenDefaultRole(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
sql []string
|
||||
}{
|
||||
{
|
||||
name: "默认角色不存在",
|
||||
sql: []string{
|
||||
`INSERT INTO sys_dept (dept_id, tenant_id, parent_id, dept_type, status, dept_role) VALUES (128,'000000',100,1,'0',9999)`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "默认角色已停用",
|
||||
sql: []string{
|
||||
`INSERT INTO sys_dept (dept_id, tenant_id, parent_id, dept_type, status, dept_role) VALUES (128,'000000',100,1,'0',2321)`,
|
||||
`INSERT INTO sys_role (role_id, tenant_id, dept_id, role_name, role_key, status, data_scope, is_pass, created_dept) VALUES (2321,'000000',128,'代理商','dl','1','4',0,128)`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "默认角色归属其它机构",
|
||||
sql: []string{
|
||||
`INSERT INTO sys_dept (dept_id, tenant_id, parent_id, dept_type, status, dept_role) VALUES (128,'000000',100,1,'0',2321)`,
|
||||
`INSERT INTO sys_role (role_id, tenant_id, dept_id, role_name, role_key, status, data_scope, is_pass, created_dept) VALUES (2321,'000000',129,'代理商','dl','0','4',0,100)`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "本机构未绑定默认角色",
|
||||
sql: []string{
|
||||
`INSERT INTO sys_dept (dept_id, tenant_id, parent_id, dept_type, status, dept_role) VALUES (128,'000000',100,1,'0',0)`,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
setupPassRoleTestDB(t)
|
||||
ctx := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{
|
||||
ID: 1, TenantId: "000000", DeptId: 128,
|
||||
}}})
|
||||
mustExec(t, ctx, `INSERT INTO sys_dept (dept_id, tenant_id, parent_id, dept_type, status, dept_role) VALUES (100,'000000',0,1,'0',1)`)
|
||||
mustExec(t, ctx, `INSERT INTO sys_role (role_id, tenant_id, dept_id, role_name, role_key, status, data_scope, is_pass, created_dept) VALUES (18,'000000',100,'代理商','dl','0','4',1,100)`)
|
||||
mustExec(t, ctx, `INSERT INTO sys_role_menu (tenant_id, role_id, menu_id, data_scope) VALUES ('000000',18,100,'0')`)
|
||||
mustExec(t, ctx, `INSERT INTO sys_config (config_id, tenant_id, config_key, config_value) VALUES (1,'000000','agent.role.template.role_id','18')`)
|
||||
for _, sql := range tc.sql {
|
||||
mustExec(t, ctx, sql)
|
||||
}
|
||||
if _, _, err := NewAgentDept().currentDeptPassRole(ctx); err == nil {
|
||||
t.Fatal("默认角色异常时必须报错,不能静默下发空权限")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 未配置平台模板时,保留原 is_pass=1 唯一角色的下发流程。
|
||||
func TestPassRoleByDeptKeepsLegacyFlowWithoutTemplate(t *testing.T) {
|
||||
setupPassRoleTestDB(t)
|
||||
ctx := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{
|
||||
ID: 1, TenantId: "000000", DeptId: 128,
|
||||
}}})
|
||||
mustExec(t, ctx, `INSERT INTO sys_dept (dept_id, tenant_id, parent_id, dept_type, status, dept_role) VALUES (128,'000000',100,1,'0',2321)`)
|
||||
mustExec(t, ctx, `INSERT INTO sys_role (role_id, tenant_id, dept_id, role_name, role_key, status, data_scope, is_pass, created_dept) VALUES (2321,'000000',128,'代理商','dl','0','4',1,128)`)
|
||||
mustExec(t, ctx, `INSERT INTO sys_role_menu (tenant_id, role_id, menu_id, data_scope) VALUES ('000000',2321,100,'0')`)
|
||||
|
||||
role, menus, err := NewAgentDept().currentDeptPassRole(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("未配模板时应走原有 is_pass 流程,实际: %v", err)
|
||||
}
|
||||
if role.RoleId != 2321 || len(menus) != 1 {
|
||||
t.Fatalf("旧流程结果不符: role=%+v menus=%+v", role, menus)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/exportcenter"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
// agentExportColumns 只允许列表字段,列标题和映射由服务端确定。
|
||||
func agentExportColumns(raw string, defaults []model.ExportColumn) ([]model.ExportColumn, error) {
|
||||
if strings.TrimSpace(raw) == "" {
|
||||
return nil, gerror.New("请选择导出列")
|
||||
}
|
||||
var requested []model.ExportColumn
|
||||
if err := json.Unmarshal([]byte(raw), &requested); err != nil {
|
||||
return nil, gerror.New("导出列格式错误")
|
||||
}
|
||||
allowed := make(map[string]model.ExportColumn, len(defaults))
|
||||
for _, col := range defaults {
|
||||
allowed[col.Field] = col
|
||||
}
|
||||
result := make([]model.ExportColumn, 0, len(requested))
|
||||
seen := make(map[string]bool)
|
||||
for _, col := range requested {
|
||||
safe, ok := allowed[col.Field]
|
||||
if !ok {
|
||||
return nil, gerror.New("包含不允许导出的字段")
|
||||
}
|
||||
if !seen[col.Field] {
|
||||
result = append(result, safe)
|
||||
seen[col.Field] = true
|
||||
}
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil, gerror.New("请选择导出列")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *sAgentUser) UserExport(ctx context.Context, in *model.AgentUserListParam) error {
|
||||
columns, err := agentExportColumns(in.ExportColumnsJson, []model.ExportColumn{
|
||||
{Field: "userName", Label: "用户名称"}, {Field: "nickName", Label: "用户昵称"},
|
||||
{Field: "roles", Label: "角色"}, {Field: "phonenumber", Label: "联系电话"},
|
||||
{Field: "email", Label: "邮箱"}, {Field: "status", Label: "状态"}, {Field: "createdAt", Label: "创建时间"},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = exportcenter.Submit(ctx, &exportcenter.Request{
|
||||
BizKey: "agentUser", BizName: "用户管理", PermCode: "cpm:agent:user:export", QueryParam: in, Columns: columns,
|
||||
RowFields: agentExportFields(columns),
|
||||
ToRow: func(value interface{}) []interface{} {
|
||||
row := value.(map[string]interface{})
|
||||
values := make([]interface{}, 0, len(columns))
|
||||
for _, column := range columns {
|
||||
values = append(values, row[column.Field])
|
||||
}
|
||||
return values
|
||||
},
|
||||
Tags: func() ([]string, error) {
|
||||
labels := make([]string, 0, len(columns))
|
||||
for _, col := range columns {
|
||||
labels = append(labels, col.Label)
|
||||
}
|
||||
return labels, nil
|
||||
},
|
||||
Fetch: func(ctx context.Context, page, pageSize int) ([]interface{}, int, error) {
|
||||
query := *in
|
||||
query.Page = page
|
||||
query.PageSize = pageSize
|
||||
list, total, err := s.UserList(ctx, &query)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
rows := make([]interface{}, 0, len(list))
|
||||
for _, item := range list {
|
||||
roles := make([]string, 0, len(item.Roles))
|
||||
for _, role := range item.Roles {
|
||||
if role != nil && role.RoleName != "" {
|
||||
roles = append(roles, role.RoleName)
|
||||
}
|
||||
}
|
||||
roleNames := strings.Join(roles, "、")
|
||||
if roleNames == "" {
|
||||
roleNames = "-"
|
||||
}
|
||||
createdAt := ""
|
||||
if item.CreatedAt != nil {
|
||||
createdAt = item.CreatedAt.Format("Y-m-d H:i:s")
|
||||
}
|
||||
rows = append(rows, map[string]interface{}{"userName": item.UserName, "nickName": item.NickName, "roles": roleNames, "phonenumber": item.Phonenumber, "email": item.Email, "status": agentExportStatus(item.Status), "createdAt": createdAt})
|
||||
}
|
||||
return rows, total, nil
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *sAgentDept) DeptExport(ctx context.Context, in *model.AgentDeptListParam) error {
|
||||
columns, err := agentExportColumns(in.ExportColumnsJson, []model.ExportColumn{
|
||||
{Field: "deptName", Label: "企业名称"}, {Field: "userName", Label: "登录账号"}, {Field: "nickName", Label: "负责人"},
|
||||
{Field: "phone", Label: "联系电话"}, {Field: "email", Label: "联系人邮箱"}, {Field: "areaCode", Label: "组织所在地"},
|
||||
{Field: "address", Label: "详细地址"}, {Field: "createdAt", Label: "创建时间"}, {Field: "status", Label: "状态"},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = exportcenter.Submit(ctx, &exportcenter.Request{
|
||||
BizKey: "agentDept", BizName: "企业客户", PermCode: "cpm:agent:dept:export", QueryParam: in, Columns: columns,
|
||||
RowFields: agentExportFields(columns),
|
||||
ToRow: func(value interface{}) []interface{} {
|
||||
row := value.(map[string]interface{})
|
||||
values := make([]interface{}, 0, len(columns))
|
||||
for _, column := range columns {
|
||||
values = append(values, row[column.Field])
|
||||
}
|
||||
return values
|
||||
},
|
||||
Tags: func() ([]string, error) {
|
||||
labels := make([]string, 0, len(columns))
|
||||
for _, col := range columns {
|
||||
labels = append(labels, col.Label)
|
||||
}
|
||||
return labels, nil
|
||||
},
|
||||
Fetch: func(ctx context.Context, page, pageSize int) ([]interface{}, int, error) {
|
||||
query := *in
|
||||
query.Page = page
|
||||
query.PageSize = pageSize
|
||||
list, total, err := s.DeptList(ctx, &query)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
rows := make([]interface{}, 0, len(list))
|
||||
for _, item := range list {
|
||||
createdAt := ""
|
||||
if item.CreatedAt != nil {
|
||||
createdAt = item.CreatedAt.Format("Y-m-d H:i:s")
|
||||
}
|
||||
rows = append(rows, map[string]interface{}{"deptName": item.DeptName, "userName": item.UserName, "nickName": item.NickName, "phone": item.Phone, "email": item.Email, "areaCode": item.AreaCode, "address": item.Address, "createdAt": createdAt, "status": agentExportStatus(item.Status)})
|
||||
}
|
||||
return rows, total, nil
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func agentExportFields(columns []model.ExportColumn) []string {
|
||||
fields := make([]string, 0, len(columns))
|
||||
for _, column := range columns {
|
||||
fields = append(fields, column.Field)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
func agentExportStatus(status string) string {
|
||||
if status == "0" {
|
||||
return "正常"
|
||||
}
|
||||
if status == "1" {
|
||||
return "停用"
|
||||
}
|
||||
return "未知"
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
|
||||
"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/rolepolicy"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sAgentMenu struct {
|
||||
*agentBase
|
||||
}
|
||||
|
||||
func NewAgentMenu() *sAgentMenu {
|
||||
return &sAgentMenu{
|
||||
agentBase: newAgentBase(),
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAgentMenu(NewAgentMenu())
|
||||
}
|
||||
|
||||
// AllowedMenuIdsByDeptRole 获取当前机构允许的菜单ID
|
||||
func (s *sAgentMenu) AllowedMenuIdsByDeptRole(ctx context.Context, deptId int64) (menuIds []int64, err error) {
|
||||
if err = s.ensureDeptInScope(ctx, deptId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var dept *entity.SysDept
|
||||
err = service.SysDept().Model(ctx, &handler.Option{FilterTenant: true, FilterAuth: false}).
|
||||
Where(dao.SysDept.Columns().DeptId, deptId).
|
||||
Scan(&dept)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dept == nil || dept.DeptRole == 0 {
|
||||
return []int64{}, nil
|
||||
}
|
||||
if dept.DeptRole == consts.SuperAdminRoleId {
|
||||
menuIds, err = s.allMenuIds(ctx)
|
||||
} else {
|
||||
var roleMenus []*entity.SysRoleMenu
|
||||
roleMenus, err = service.SysRole().GetRoleListMenu(ctx, []int64{dept.DeptRole})
|
||||
for _, item := range roleMenus {
|
||||
menuIds = append(menuIds, item.MenuId)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
slices.Sort(menuIds)
|
||||
menuIds = slices.Compact(menuIds)
|
||||
if !contexts.IsSuperAdmin(ctx) {
|
||||
return rolepolicy.RestrictAssignableMenus(ctx, dept.TenantId, dept.DeptId, contexts.GetUserId(ctx), menuIds)
|
||||
}
|
||||
return rolepolicy.RestrictMenus(ctx, dept.TenantId, dept.DeptId, menuIds)
|
||||
}
|
||||
|
||||
func (s *sAgentMenu) allMenuIds(ctx context.Context) (menuIds []int64, err error) {
|
||||
var menus []*entity.SysMenu
|
||||
err = service.SysMenu().Model(ctx).
|
||||
Where(dao.SysMenu.Columns().Status, consts.SysMenuStatusNormal).
|
||||
Order(dao.SysMenu.Columns().OrderNum, "ASC").
|
||||
Scan(&menus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, item := range menus {
|
||||
menuIds = append(menuIds, item.MenuId)
|
||||
}
|
||||
slices.Sort(menuIds)
|
||||
return slices.Compact(menuIds), nil
|
||||
}
|
||||
|
||||
func (s *sAgentMenu) getRoleById(ctx context.Context, roleId int64) (role *entity.SysRole, err error) {
|
||||
err = service.SysRole().Model(ctx, &handler.Option{FilterTenant: true, FilterAuth: false}).
|
||||
Where(dao.SysRole.Columns().RoleId, roleId).
|
||||
Scan(&role)
|
||||
return role, err
|
||||
}
|
||||
|
||||
func (s *sAgentMenu) AllowedMenuTree(ctx context.Context, deptId int64) (tree []*model.SysMenuTreeModel, menuIds []int64, err error) {
|
||||
deptId = contexts.GetDeptId(ctx)
|
||||
menuIds, err = s.AllowedMenuIdsByDeptRole(ctx, deptId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(menuIds) == 0 && !contexts.IsSuperAdmin(ctx) {
|
||||
return []*model.SysMenuTreeModel{}, []int64{}, nil
|
||||
}
|
||||
visibleMenuIds, err := rolepolicy.WithMenuAncestors(ctx, menuIds)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
mod := service.SysMenu().Model(ctx)
|
||||
if !contexts.IsSuperAdmin(ctx) {
|
||||
mod = mod.WhereIn(dao.SysMenu.Columns().MenuId, visibleMenuIds)
|
||||
}
|
||||
var menus []*entity.SysMenu
|
||||
err = mod.Where(dao.SysMenu.Columns().Status, consts.SysMenuStatusNormal).
|
||||
Order(dao.SysMenu.Columns().OrderNum, "ASC").
|
||||
Scan(&menus)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
tree = s.buildMenuList(menus)
|
||||
activeMenuIds := make([]int64, 0, len(menus))
|
||||
for _, menu := range menus {
|
||||
activeMenuIds = append(activeMenuIds, menu.MenuId)
|
||||
}
|
||||
if contexts.IsSuperAdmin(ctx) {
|
||||
menuIds = activeMenuIds
|
||||
} else {
|
||||
menuIds = rolepolicy.Intersect(menuIds, activeMenuIds)
|
||||
}
|
||||
return tree, menuIds, nil
|
||||
}
|
||||
|
||||
func (s *sAgentMenu) buildMenuList(menus []*entity.SysMenu) (list []*model.SysMenuTreeModel) {
|
||||
for _, menu := range menus {
|
||||
list = append(list, &model.SysMenuTreeModel{
|
||||
Title: menu.MenuName,
|
||||
Key: gconv.String(menu.MenuId),
|
||||
ParentId: menu.ParentId,
|
||||
MenuId: menu.MenuId,
|
||||
MenuName: menu.MenuName,
|
||||
MenuType: menu.MenuType,
|
||||
Perms: menu.Perms,
|
||||
})
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (s *sAgentMenu) buildMenuTree(parentId int64, menus []*entity.SysMenu) (tree []*model.SysMenuTreeModel) {
|
||||
for _, menu := range menus {
|
||||
if menu.ParentId != parentId {
|
||||
continue
|
||||
}
|
||||
item := &model.SysMenuTreeModel{
|
||||
Title: menu.MenuName,
|
||||
Key: gconv.String(menu.MenuId),
|
||||
ParentId: menu.ParentId,
|
||||
MenuId: menu.MenuId,
|
||||
MenuName: menu.MenuName,
|
||||
MenuType: menu.MenuType,
|
||||
Perms: menu.Perms,
|
||||
}
|
||||
item.Children = s.buildMenuTree(menu.MenuId, menus)
|
||||
tree = append(tree, item)
|
||||
}
|
||||
if parentId == 0 && len(tree) == 0 {
|
||||
for _, menu := range menus {
|
||||
item := &model.SysMenuTreeModel{
|
||||
Title: menu.MenuName,
|
||||
Key: gconv.String(menu.MenuId),
|
||||
ParentId: menu.ParentId,
|
||||
MenuId: menu.MenuId,
|
||||
MenuName: menu.MenuName,
|
||||
MenuType: menu.MenuType,
|
||||
Perms: menu.Perms,
|
||||
}
|
||||
item.Children = s.buildMenuTree(menu.MenuId, menus)
|
||||
tree = append(tree, item)
|
||||
}
|
||||
}
|
||||
return tree
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"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/rolepolicy"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rolesync"
|
||||
"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"
|
||||
|
||||
"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 sAgentRole struct {
|
||||
*sAgentMenu
|
||||
}
|
||||
|
||||
// NewAgentRole 创建代理商角色管理服务实例。
|
||||
func NewAgentRole() *sAgentRole {
|
||||
return &sAgentRole{
|
||||
sAgentMenu: &sAgentMenu{
|
||||
agentBase: newAgentBase(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAgentRole(NewAgentRole())
|
||||
}
|
||||
|
||||
// RoleList 查询当前登录组织本级角色列表。
|
||||
func (s *sAgentRole) RoleList(ctx context.Context, in *model.AgentRoleListParam) (list []*model.AgentRoleListModel, total int, err error) {
|
||||
currentDeptId := contexts.GetDeptId(ctx)
|
||||
if currentDeptId == 0 {
|
||||
return nil, 0, gerror.New("当前用户未绑定组织")
|
||||
}
|
||||
if err = service.AgentDept().EnsureCurrentDeptDefaultRole(ctx); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
m := s.roleModel(ctx).Where(dao.SysRole.Columns().DeptId, currentDeptId)
|
||||
if in.RoleName != "" {
|
||||
m = m.WhereLike(dao.SysRole.Columns().RoleName, "%"+in.RoleName+"%")
|
||||
}
|
||||
if in.RoleKey != "" {
|
||||
m = m.WhereLike(dao.SysRole.Columns().RoleKey, "%"+in.RoleKey+"%")
|
||||
}
|
||||
if in.Status != "" {
|
||||
m = m.Where(dao.SysRole.Columns().Status, in.Status)
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
begin, end := gtime.NewFromStr(in.CreatedAt[0]), gtime.NewFromStr(in.CreatedAt[1])
|
||||
m = m.WhereBetween(dao.SysRole.Columns().CreatedAt, begin, end.EndOfDay())
|
||||
}
|
||||
total, err = m.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
err = m.Order(dao.SysRole.Columns().RoleId, "ASC").
|
||||
Scan(&list)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defaultRoleId, err := s.currentDeptDefaultRoleId(ctx)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
for _, role := range list {
|
||||
role.IsDefaultRole = defaultRoleId != 0 && role.RoleId == defaultRoleId
|
||||
}
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
// RoleView 查询角色详情,并返回当前登录组织可分配的菜单树。
|
||||
func (s *sAgentRole) RoleView(ctx context.Context, roleId int64) (res *model.AgentRoleViewModel, err error) {
|
||||
role, err := service.SysRole().View(ctx, &model.SysRoleViewParam{RoleId: roleId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if role == nil || role.RoleId == 0 {
|
||||
return nil, gerror.New("角色不存在")
|
||||
}
|
||||
if role.DeptId != contexts.GetDeptId(ctx) {
|
||||
return nil, gerror.New("只能操作本级角色")
|
||||
}
|
||||
tree, allowed, err := s.AllowedMenuTree(ctx, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
isDefaultRole, err := s.isCurrentDeptDefaultRole(ctx, role.RoleId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.AgentRoleViewModel{
|
||||
SysRoleViewModel: *role,
|
||||
AllowedMenuIds: allowed,
|
||||
MenuTree: tree,
|
||||
IsDefaultRole: isDefaultRole,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RoleSave 保存代理商角色,按角色ID区分新增和修改流程。
|
||||
func (s *sAgentRole) RoleSave(ctx context.Context, in *model.AgentRoleSaveParam) (roleId int64, err error) {
|
||||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||
var saveErr error
|
||||
roleId, saveErr = s.saveTemplateRole(rolesync.DeferRecording(ctx), in)
|
||||
if saveErr != nil {
|
||||
return saveErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := rolesync.EnqueueRole(ctx, roleId, contexts.GetUserId(ctx)); err != nil {
|
||||
return roleId, err
|
||||
}
|
||||
return roleId, nil
|
||||
}
|
||||
|
||||
func (s *sAgentRole) saveTemplateRole(ctx context.Context, in *model.AgentRoleSaveParam) (roleId int64, err error) {
|
||||
user := contexts.GetUser(ctx)
|
||||
if user == nil {
|
||||
return 0, gerror.New("登录已过期")
|
||||
}
|
||||
if in.DeptId == 0 {
|
||||
in.DeptId = user.DeptId
|
||||
}
|
||||
if in.DeptId != user.DeptId {
|
||||
return 0, gerror.New("只能维护本级角色")
|
||||
}
|
||||
if err = s.ensureRoleNotCurrentDeptDefault(ctx, in.RoleId); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if in.RoleId > 0 {
|
||||
if err = s.ensureRoleNotInherited(ctx, in.RoleId); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if in.Status == "" {
|
||||
in.Status = "0"
|
||||
}
|
||||
if in.DataScope == "" {
|
||||
in.DataScope = consts.SysRoleDataScopeDeptAndBelow
|
||||
}
|
||||
if !contexts.IsSuperAdmin(ctx) {
|
||||
allowed, err := rolepolicy.NormalizeAssignableMenus(ctx, user.TenantId, in.DeptId, user.ID, in.MenuIds)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
in.MenuIds = allowed
|
||||
switch in.DataScope {
|
||||
case consts.SysRoleDataScopeDept, consts.SysRoleDataScopeDeptAndBelow, consts.SysRoleDataScopePersonal, consts.SysRoleDataScopeDeptAndBelowOrPersonal, consts.SysRoleDataScopeOrgAndNextLevel, consts.SysRoleDataScopeOrgNextLevel:
|
||||
default:
|
||||
return 0, gerror.New("代理角色数据权限不能超出本机构及下级范围")
|
||||
}
|
||||
}
|
||||
template, templateErr := rolepolicy.TemplateForRoles(ctx, in.RoleId)
|
||||
if templateErr != nil {
|
||||
return 0, templateErr
|
||||
}
|
||||
if in.IsPass != 0 && in.IsPass != 1 {
|
||||
return 0, gerror.New("传递标记无效")
|
||||
}
|
||||
if template != nil {
|
||||
in.DataScope = consts.SysRoleDataScopeDeptAndBelow
|
||||
}
|
||||
legacyPass := in.IsPass == 1
|
||||
if legacyPass {
|
||||
configured, err := dao.SysConfig.Ctx(ctx).Master().Where("tenant_id", consts.DefaultSystemTenantCode).Where("config_key", rolepolicy.TemplateConfigKey).Count()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
legacyPass = configured == 0
|
||||
}
|
||||
if legacyPass {
|
||||
in.DataScope = consts.SysRoleDataScopeDeptAndBelow
|
||||
var dept *entity.SysDept
|
||||
deptModel := s.deptModel(ctx).Where(dao.SysDept.Columns().DeptId, in.DeptId)
|
||||
if dao.SysDept.DB().GetConfig().Type != "sqlite" {
|
||||
deptModel = deptModel.LockUpdate()
|
||||
}
|
||||
if err := deptModel.Scan(&dept); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if dept == nil {
|
||||
return 0, gerror.New("当前机构不存在")
|
||||
}
|
||||
count, err := s.roleModel(ctx).
|
||||
Where(dao.SysRole.Columns().DeptId, in.DeptId).
|
||||
Where("is_pass", 1).
|
||||
WhereNot(dao.SysRole.Columns().RoleId, in.RoleId).
|
||||
Count()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if count > 0 {
|
||||
return 0, gerror.New("每个机构只能配置一个下级管理员角色")
|
||||
}
|
||||
}
|
||||
|
||||
if in.RoleId == 0 {
|
||||
return s.addRole(ctx, in)
|
||||
}
|
||||
return s.editRole(ctx, in)
|
||||
}
|
||||
|
||||
// addRole 新增代理商角色,并同步角色的数据权限配置。
|
||||
func (s *sAgentRole) addRole(ctx context.Context, in *model.AgentRoleSaveParam) (int64, error) {
|
||||
deptId := in.DeptId
|
||||
role, err := service.SysRole().Add(ctx, &model.SysRoleAddParam{
|
||||
RoleName: in.RoleName,
|
||||
RoleKey: in.RoleKey,
|
||||
RoleSort: in.RoleSort,
|
||||
DataScope: in.DataScope,
|
||||
MenuCheckStrictly: in.MenuCheckStrictly,
|
||||
Status: in.Status,
|
||||
Remark: in.Remark,
|
||||
MenuIds: in.MenuIds,
|
||||
DeptId: &deptId,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if role == nil {
|
||||
return 0, nil
|
||||
}
|
||||
if _, err = dao.SysRole.Ctx(ctx).
|
||||
Where(dao.SysRole.Columns().RoleId, role.RoleId).
|
||||
Data(do.SysRole{IsPass: in.IsPass}).Update(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err = s.updateRoleDataScope(ctx, role.RoleId, in); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return role.RoleId, nil
|
||||
}
|
||||
|
||||
// editRole 修改代理商角色,并禁止在代理商页面移动角色所属组织。
|
||||
func (s *sAgentRole) editRole(ctx context.Context, in *model.AgentRoleSaveParam) (int64, error) {
|
||||
current, err := s.RoleView(ctx, in.RoleId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if current.DeptId != in.DeptId {
|
||||
return 0, gerror.New("不能在代理商页面移动角色所属组织")
|
||||
}
|
||||
deptId := in.DeptId
|
||||
roleName := in.RoleName
|
||||
roleKey := in.RoleKey
|
||||
roleSort := in.RoleSort
|
||||
menuCheckStrictly := in.MenuCheckStrictly
|
||||
status := in.Status
|
||||
remark := in.Remark
|
||||
role, err := service.SysRole().Edit(ctx, &model.SysRoleEditParam{
|
||||
RoleId: in.RoleId,
|
||||
RoleName: &roleName,
|
||||
RoleKey: &roleKey,
|
||||
RoleSort: &roleSort,
|
||||
MenuCheckStrictly: &menuCheckStrictly,
|
||||
Status: &status,
|
||||
Remark: &remark,
|
||||
MenuIds: in.MenuIds,
|
||||
DeptId: &deptId,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err = s.updateRoleDataScope(ctx, in.RoleId, in); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if _, err = dao.SysRole.Ctx(ctx).
|
||||
Where(dao.SysRole.Columns().RoleId, in.RoleId).
|
||||
Data(do.SysRole{IsPass: in.IsPass}).Update(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if role == nil {
|
||||
return in.RoleId, nil
|
||||
}
|
||||
return role.RoleId, nil
|
||||
}
|
||||
|
||||
// updateRoleDataScope 更新角色数据权限和组织树勾选配置。
|
||||
func (s *sAgentRole) updateRoleDataScope(ctx context.Context, roleId int64, in *model.AgentRoleSaveParam) error {
|
||||
deptCheckStrictly := in.DeptCheckStrictly
|
||||
return service.SysRole().EditRoleDataScope(ctx, &model.SysRoleDataScopeEditParam{
|
||||
RoleId: roleId,
|
||||
DataScope: in.DataScope,
|
||||
DeptCheckStrictly: &deptCheckStrictly,
|
||||
DeptIds: []int64{in.DeptId},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *sAgentRole) isCurrentDeptDefaultRole(ctx context.Context, roleId int64) (bool, error) {
|
||||
defaultRoleId, err := s.currentDeptDefaultRoleId(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return defaultRoleId != 0 && roleId == defaultRoleId, nil
|
||||
}
|
||||
|
||||
func (s *sAgentRole) ensureRoleInCurrentDept(ctx context.Context, roleId int64) error {
|
||||
currentDeptId := contexts.GetDeptId(ctx)
|
||||
if currentDeptId == 0 {
|
||||
return gerror.New("当前用户未绑定组织")
|
||||
}
|
||||
count, err := s.roleModel(ctx).
|
||||
Where(dao.SysRole.Columns().RoleId, roleId).
|
||||
Where(dao.SysRole.Columns().DeptId, currentDeptId).
|
||||
Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return gerror.New("只能操作本级角色")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sAgentRole) ensureRoleNotInherited(ctx context.Context, roleId int64) error {
|
||||
if roleId <= 0 {
|
||||
return nil
|
||||
}
|
||||
if contexts.IsSuperAdmin(ctx) {
|
||||
return nil
|
||||
}
|
||||
count, err := s.inheritedRoleModel(ctx).
|
||||
Where("r.role_id", roleId).
|
||||
Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return gerror.New("上级下发角色由上级角色统一维护,当前组织只能查看")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sAgentRole) ensureRoleNoUsers(ctx context.Context, roleId int64) error {
|
||||
count, err := dao.SysUserRole.Ctx(ctx).
|
||||
Where(dao.SysUserRole.Columns().RoleId, roleId).
|
||||
Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return gerror.New("该角色已绑定用户,不能删除")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RoleDelete 删除代理商角色,删除前校验本级机构默认角色不可被删除。
|
||||
func (s *sAgentRole) RoleDelete(ctx context.Context, roleIds []int64) (err error) {
|
||||
for _, roleId := range roleIds {
|
||||
if err = s.ensureRoleInCurrentDept(ctx, roleId); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = s.ensureRoleNotCurrentDeptDefault(ctx, roleId); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = s.ensureRoleNotInherited(ctx, roleId); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = s.ensureRoleNoUsers(ctx, roleId); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = service.SysRole().Delete(ctx, &model.SysRoleDeleteParam{RoleIds: roleIds})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *sAgentRole) inheritedRoleModel(ctx context.Context) *gdb.Model {
|
||||
m := g.Model(dao.SysRole.Table()+" r").
|
||||
LeftJoin(dao.SysDept.Table()+" d", "d.dept_id = r.dept_id AND d.tenant_id = r.tenant_id").
|
||||
Where("r.deleted_at IS NULL").
|
||||
Where("d.deleted_at IS NULL").
|
||||
Where("r.created_dept > 0").
|
||||
Where("r.dept_id <> r.created_dept").
|
||||
Where("d.parent_id = r.created_dept")
|
||||
if tenantId := contexts.GetTenantId(ctx); tenantId != "" {
|
||||
m = m.Where("r.tenant_id", tenantId)
|
||||
}
|
||||
return m
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcfg"
|
||||
|
||||
_ "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/logic/memorydb"
|
||||
_ "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/logic/system"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
)
|
||||
|
||||
// 复现生产形态:dept 100 同时存在模板角色 18(is_pass=1)和普通角色 79(运营人员,is_pass=0)。
|
||||
// 给 79 勾权限保存时,旧逻辑强制把 in.IsPass 置 1 再查重,必然报
|
||||
// 「每个机构只能配置一个下级管理员角色」,导致普通角色永远改不了。
|
||||
func TestSaveTemplateRoleAllowsOrdinaryRoleWithTemplateConfigured(t *testing.T) {
|
||||
setupRoleSaveTestDB(t)
|
||||
ctx := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{
|
||||
ID: 1,
|
||||
TenantId: "000000",
|
||||
DeptId: 100,
|
||||
Roles: []model.Role{{RoleId: 1}},
|
||||
}}})
|
||||
// 模板已配置为 18,机构 100 已有 is_pass=1 的 18
|
||||
mustExecRole(t, ctx, `INSERT INTO sys_dept (dept_id, tenant_id, parent_id, dept_type, status, dept_role) VALUES (100,'000000',0,1,'0',1)`)
|
||||
mustExecRole(t, ctx, `INSERT INTO sys_role (role_id, tenant_id, dept_id, role_name, role_key, status, data_scope, is_pass, created_dept) VALUES
|
||||
(1,'000000',100,'超级管理员','admin','0','1',0,100),
|
||||
(18,'000000',100,'代理商','dl','0','4',1,100),
|
||||
(79,'000000',100,'运营人员','yunying','0','4',0,100)`)
|
||||
mustExecRole(t, ctx, `INSERT INTO sys_role_menu (tenant_id, role_id, menu_id, data_scope) VALUES ('000000',18,100,'0')`)
|
||||
mustExecRole(t, ctx, `INSERT INTO sys_config (config_id, tenant_id, config_key, config_value) VALUES (1,'000000','agent.role.template.role_id','18')`)
|
||||
|
||||
// 编辑普通角色 79:勾一个菜单
|
||||
_, err := NewAgentRole().saveTemplateRole(ctx, &model.AgentRoleSaveParam{
|
||||
RoleId: 79,
|
||||
DeptId: 100,
|
||||
RoleName: "运营人员",
|
||||
RoleKey: "yunying",
|
||||
Status: "0",
|
||||
DataScope: "4",
|
||||
MenuIds: []int64{100},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("已配置模板时普通角色必须能保存,实际报错: %v", err)
|
||||
}
|
||||
|
||||
// 关键断言:79 不能被悄悄改成 is_pass=1
|
||||
var isPass int
|
||||
v, qErr := g.DB().GetValue(ctx, `SELECT is_pass FROM sys_role WHERE role_id = 79`)
|
||||
if qErr != nil {
|
||||
t.Fatalf("query is_pass failed: %v", qErr)
|
||||
}
|
||||
isPass = v.Int()
|
||||
if isPass != 0 {
|
||||
t.Fatalf("普通角色 79 的 is_pass 被改成了 %d,应保持 0", isPass)
|
||||
}
|
||||
// 模板 18 的 is_pass 也不能被动到
|
||||
v18, _ := g.DB().GetValue(ctx, `SELECT is_pass FROM sys_role WHERE role_id = 18`)
|
||||
if v18.Int() != 1 {
|
||||
t.Fatalf("模板角色 18 的 is_pass 变成了 %d,应保持 1", v18.Int())
|
||||
}
|
||||
}
|
||||
|
||||
// 未配置平台模板时,原有「本级唯一 is_pass=1 角色」限制必须保留。
|
||||
func TestSaveTemplateRoleKeepsLegacyUniquenessWithoutTemplate(t *testing.T) {
|
||||
setupRoleSaveTestDB(t)
|
||||
ctx := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{
|
||||
ID: 1, TenantId: "000000", DeptId: 200,
|
||||
}}})
|
||||
mustExecRole(t, ctx, `INSERT INTO sys_dept (dept_id, tenant_id, parent_id, dept_type, status, dept_role) VALUES (200,'000000',0,1,'0',1)`)
|
||||
mustExecRole(t, ctx, `INSERT INTO sys_role (role_id, tenant_id, dept_id, role_name, role_key, status, data_scope, is_pass, created_dept) VALUES
|
||||
(1,'000000',200,'超级管理员','admin','0','1',0,200),
|
||||
(90,'000000',200,'已有的下发角色','dl','0','4',1,200)`)
|
||||
// 没有 sys_config 模板参数 → 走 legacy 分支
|
||||
_, err := NewAgentRole().saveTemplateRole(ctx, &model.AgentRoleSaveParam{
|
||||
RoleId: 0,
|
||||
DeptId: 200,
|
||||
RoleName: "第二个下发角色",
|
||||
RoleKey: "dl2",
|
||||
Status: "0",
|
||||
DataScope: "4",
|
||||
MenuIds: []int64{},
|
||||
IsPass: 1,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("未配置模板时,第二个 is_pass=1 角色必须被拒绝")
|
||||
}
|
||||
if err.Error() != "每个机构只能配置一个下级管理员角色" {
|
||||
t.Fatalf("错误信息不符,实际: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func setupRoleSaveTestDB(t *testing.T) {
|
||||
t.Helper()
|
||||
if err := gdb.SetConfig(gdb.Config{
|
||||
"default": gdb.ConfigGroup{
|
||||
{Type: "sqlite", Link: "sqlite::@file(:memory:)", Debug: false},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("configure sqlite failed: %v", err)
|
||||
}
|
||||
_ = g.Cfg().GetAdapter().(*gcfg.AdapterFile).Set("database.memorydb.link", "")
|
||||
ctx := context.Background()
|
||||
t.Cleanup(func() { _ = g.DB().Close(ctx) })
|
||||
mustExecRole(t, ctx, `CREATE TABLE sys_dept (dept_id INTEGER PRIMARY KEY, tenant_id TEXT, parent_id INTEGER, dept_type INTEGER, status TEXT DEFAULT '0', dept_role INTEGER, order_num INTEGER DEFAULT 0, ancestors TEXT, dept_name TEXT, deleted_at DATETIME)`)
|
||||
mustExecRole(t, ctx, `CREATE TABLE sys_config (config_id INTEGER PRIMARY KEY, tenant_id TEXT, config_name TEXT, config_key TEXT, config_value TEXT, config_type TEXT, created_dept INTEGER, created_by INTEGER, created_at DATETIME, updated_by INTEGER, updated_at DATETIME, remark TEXT)`)
|
||||
mustExecRole(t, ctx, `CREATE TABLE sys_role (
|
||||
role_id INTEGER PRIMARY KEY AUTOINCREMENT, tenant_id TEXT, dept_id INTEGER, role_name TEXT, role_key TEXT, role_sort INTEGER, data_scope TEXT,
|
||||
menu_check_strictly INTEGER, dept_check_strictly INTEGER, status TEXT, is_pass INTEGER, created_dept INTEGER, created_by INTEGER, created_at DATETIME, updated_by INTEGER, updated_at DATETIME, deleted_by INTEGER, deleted_at DATETIME, remark TEXT)`)
|
||||
mustExecRole(t, ctx, `CREATE TABLE sys_role_menu (tenant_id TEXT, role_id INTEGER, menu_id INTEGER, data_scope TEXT)`)
|
||||
mustExecRole(t, ctx, `CREATE TABLE sys_role_dept (tenant_id TEXT, role_id INTEGER, dept_id INTEGER)`)
|
||||
mustExecRole(t, ctx, `CREATE TABLE sys_user_role (tenant_id TEXT DEFAULT '000000', user_id INTEGER, role_id INTEGER)`)
|
||||
mustExecRole(t, ctx, `CREATE TABLE sys_menu (menu_id INTEGER PRIMARY KEY, menu_name TEXT, parent_id INTEGER, level INTEGER, tree TEXT, order_num INTEGER DEFAULT 0, path TEXT, component TEXT, query_param TEXT, is_frame INTEGER, is_cache INTEGER, menu_type TEXT, visible TEXT, status TEXT DEFAULT '0', perms TEXT, icon TEXT, created_dept INTEGER, created_by INTEGER, created_at DATETIME, updated_by INTEGER, updated_at DATETIME, remark TEXT)`)
|
||||
mustExecRole(t, ctx, `INSERT INTO sys_menu (menu_id, menu_name, parent_id, status) VALUES (100,'卡片信息',0,'0')`)
|
||||
}
|
||||
|
||||
func mustExecRole(t *testing.T, ctx context.Context, sql string) {
|
||||
t.Helper()
|
||||
if _, err := g.DB().Exec(ctx, sql); err != nil {
|
||||
t.Fatalf("execute sql failed: %v\nsql=%s", err, sql)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rolepolicy"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
func TestEnsureRoleNotInheritedAllowsSuperAdmin(t *testing.T) {
|
||||
ctx := contexts.Set(context.Background(), &model.Context{
|
||||
User: &model.Identity{BaseClaims: model.BaseClaims{
|
||||
TenantId: "000000",
|
||||
Roles: []model.Role{{RoleId: consts.SuperAdminRoleId}},
|
||||
}},
|
||||
})
|
||||
|
||||
if err := NewAgentRole().ensureRoleNotInherited(ctx, 100); err != nil {
|
||||
t.Fatalf("super admin should edit inherited roles, got error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentSuperAdminChecksLiveRoleAssignment(t *testing.T) {
|
||||
setupAgentRoleSyncTestDB(t)
|
||||
ctx := context.Background()
|
||||
mustExecAgentRoleSyncTest(t, ctx, "INSERT INTO sys_role (role_id,tenant_id,dept_id,status) VALUES (1,'000000',1,'0')")
|
||||
mustExecAgentRoleSyncTest(t, ctx, "INSERT INTO sys_user_role (tenant_id,user_id,role_id) VALUES ('000000',99,1)")
|
||||
isAdmin, err := rolepolicy.CurrentSuperAdmin(ctx, 99)
|
||||
if err != nil || !isAdmin {
|
||||
t.Fatalf("expected active administrator: %v %v", isAdmin, err)
|
||||
}
|
||||
mustExecAgentRoleSyncTest(t, ctx, "DELETE FROM sys_user_role WHERE user_id=99")
|
||||
isAdmin, err = rolepolicy.CurrentSuperAdmin(ctx, 99)
|
||||
if err != nil || isAdmin {
|
||||
t.Fatalf("revoked administrator must lose access: %v %v", isAdmin, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoActiveRolesCannotReadFilteredData(t *testing.T) {
|
||||
setupAgentRoleSyncTestDB(t)
|
||||
ctx := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 99, TenantId: "000000", DeptId: 2}}})
|
||||
mustExecAgentRoleSyncTest(t, ctx, "INSERT INTO sys_role (role_id,tenant_id,dept_id,status) VALUES (21,'000000',2,'0')")
|
||||
count, err := g.DB().Model("sys_role").Ctx(ctx).Handler(handler.FilterAuth).Count()
|
||||
if err != nil || count != 0 {
|
||||
t.Fatalf("user without roles must not read filtered data: %d %v", count, err)
|
||||
}
|
||||
}
|
||||
|
||||
func setupAgentRoleSyncTestDB(t *testing.T) {
|
||||
t.Helper()
|
||||
if err := gdb.SetConfig(gdb.Config{
|
||||
"default": gdb.ConfigGroup{
|
||||
{Type: "sqlite", Link: "sqlite::@file(:memory:)", Debug: false},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("configure sqlite failed: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
t.Cleanup(func() { _ = g.DB().Close(ctx) })
|
||||
mustExecAgentRoleSyncTest(t, ctx, `CREATE TABLE sys_dept (dept_id INTEGER PRIMARY KEY, tenant_id TEXT, parent_id INTEGER, dept_type INTEGER, status TEXT DEFAULT '0', dept_role INTEGER, deleted_at DATETIME)`)
|
||||
mustExecAgentRoleSyncTest(t, ctx, `CREATE TABLE sys_config (config_id INTEGER PRIMARY KEY, tenant_id TEXT, config_name TEXT, config_key TEXT, config_value TEXT, config_type TEXT, created_dept INTEGER, created_by INTEGER, created_at DATETIME, updated_by INTEGER, updated_at DATETIME, remark TEXT)`)
|
||||
mustExecAgentRoleSyncTest(t, ctx, `CREATE TABLE sys_role (
|
||||
role_id INTEGER PRIMARY KEY AUTOINCREMENT, tenant_id TEXT, dept_id INTEGER, role_name TEXT, role_key TEXT, role_sort INTEGER, data_scope TEXT,
|
||||
menu_check_strictly INTEGER, dept_check_strictly INTEGER, status TEXT, is_pass INTEGER, created_dept INTEGER, created_by INTEGER, created_at DATETIME, updated_by INTEGER, updated_at DATETIME, deleted_by INTEGER, deleted_at DATETIME, remark TEXT)`)
|
||||
mustExecAgentRoleSyncTest(t, ctx, `CREATE TABLE sys_role_menu (tenant_id TEXT, role_id INTEGER, menu_id INTEGER, data_scope TEXT)`)
|
||||
mustExecAgentRoleSyncTest(t, ctx, `CREATE TABLE sys_role_dept (tenant_id TEXT, role_id INTEGER, dept_id INTEGER)`)
|
||||
mustExecAgentRoleSyncTest(t, ctx, `CREATE TABLE sys_user_role (tenant_id TEXT DEFAULT '000000', user_id INTEGER, role_id INTEGER)`)
|
||||
}
|
||||
|
||||
func mustExecAgentRoleSyncTest(t *testing.T, ctx context.Context, sql string) {
|
||||
t.Helper()
|
||||
if _, err := g.DB().Exec(ctx, sql); err != nil {
|
||||
t.Fatalf("execute sql failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func assertAgentRoleSyncCount(t *testing.T, ctx context.Context, sql string, expected int) {
|
||||
t.Helper()
|
||||
count, err := g.DB().GetValue(ctx, sql)
|
||||
if err != nil {
|
||||
t.Fatalf("query count failed: %v", err)
|
||||
}
|
||||
if count.Int() != expected {
|
||||
t.Fatalf("count = %d, want %d, sql=%s", count.Int(), expected, sql)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
)
|
||||
|
||||
type sAgentScope struct {
|
||||
*agentBase
|
||||
}
|
||||
|
||||
func NewAgentScope() *sAgentScope {
|
||||
return &sAgentScope{
|
||||
agentBase: newAgentBase(),
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAgentScope(NewAgentScope())
|
||||
}
|
||||
|
||||
func (s *sAgentScope) ScopeDeptIds(ctx context.Context) (deptIds []int64, err error) {
|
||||
return s.scopeDeptIds(ctx)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
)
|
||||
|
||||
type templateRoleService struct{ service.ISysRole }
|
||||
|
||||
func (templateRoleService) ModelQuery(ctx context.Context, _ ...*handler.Option) *gdb.Model {
|
||||
return g.DB().Model("sys_role").Ctx(ctx).Handler(handler.FilterTenant)
|
||||
}
|
||||
|
||||
func TestConfiguredTemplateUsesInheritedDefaultAtEveryLevel(t *testing.T) {
|
||||
setupAgentRoleSyncTestDB(t)
|
||||
service.RegisterSysDept(paginationDeptService{})
|
||||
service.RegisterSysRole(templateRoleService{})
|
||||
t.Cleanup(func() {
|
||||
service.RegisterSysDept(nil)
|
||||
service.RegisterSysRole(nil)
|
||||
})
|
||||
ctx := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 99, TenantId: "000000", DeptId: 2}}})
|
||||
for _, statement := range []string{
|
||||
"ALTER TABLE sys_dept ADD COLUMN dept_name TEXT",
|
||||
"INSERT INTO sys_dept(dept_id,tenant_id,parent_id,dept_type,dept_role) VALUES(1,'000000',0,1,0),(2,'000000',1,1,21),(3,'000000',2,1,31)",
|
||||
"INSERT INTO sys_role(role_id,tenant_id,dept_id,status,is_pass,created_dept,data_scope) VALUES(10,'000000',1,'0',0,1,'4'),(21,'000000',2,'0',0,1,'4'),(31,'000000',3,'0',0,2,'4')",
|
||||
"INSERT INTO sys_config(tenant_id,config_key,config_value) VALUES('000000','agent.role.template.role_id','10')",
|
||||
"INSERT INTO sys_role_menu(tenant_id,role_id,menu_id,data_scope) VALUES('000000',10,100,'0'),('000000',10,200,'0'),('000000',21,100,'0'),('000000',21,300,'0'),('000000',31,100,'0')",
|
||||
} {
|
||||
mustExecAgentRoleSyncTest(t, ctx, statement)
|
||||
}
|
||||
for _, deptID := range []int64{1, 2, 3} {
|
||||
source, menus, err := NewAgentDept().passRoleByDept(ctx, deptID)
|
||||
if err != nil || source == nil {
|
||||
t.Fatalf("source for department %d: %+v %v", deptID, source, err)
|
||||
}
|
||||
expected := map[int64][]int64{1: {100, 200}, 2: {100, 300}, 3: {100}}[deptID]
|
||||
got := make([]int64, 0, len(menus))
|
||||
for _, menu := range menus {
|
||||
got = append(got, menu.MenuId)
|
||||
}
|
||||
slices.Sort(got)
|
||||
if !slices.Equal(got, expected) {
|
||||
t.Fatalf("department %d must copy its current default role including platform expansion: %v, want %v", deptID, got, expected)
|
||||
}
|
||||
}
|
||||
mustExecAgentRoleSyncTest(t, ctx, "UPDATE sys_dept SET dept_role=0 WHERE dept_id=2")
|
||||
if _, _, err := NewAgentDept().passRoleByDept(ctx, 2); err == nil {
|
||||
t.Fatal("missing default role must not fall back to full platform permissions")
|
||||
}
|
||||
if err := NewAgentDept().EnsureCurrentDeptDefaultRole(ctx); err == nil {
|
||||
t.Fatal("read request must report missing default role")
|
||||
}
|
||||
assertAgentRoleSyncCount(t, ctx, "SELECT COUNT(*) FROM sys_role", 3)
|
||||
assertAgentRoleSyncCount(t, ctx, "SELECT COUNT(*) FROM sys_user_role", 0)
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"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/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sAgentUser struct {
|
||||
*agentBase
|
||||
}
|
||||
|
||||
func NewAgentUser() *sAgentUser {
|
||||
return &sAgentUser{
|
||||
agentBase: newAgentBase(),
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAgentUser(NewAgentUser())
|
||||
}
|
||||
|
||||
// UserList 查询用户列表
|
||||
func (s *sAgentUser) UserList(ctx context.Context, in *model.AgentUserListParam) (list []*model.AgentUserListModel, total int, err error) {
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
if deptId == 0 {
|
||||
return nil, 0, gerror.New("当前用户未绑定组织")
|
||||
}
|
||||
if in.Page <= 0 {
|
||||
in.Page = 1
|
||||
}
|
||||
if in.PageSize <= 0 {
|
||||
in.PageSize = 20
|
||||
}
|
||||
m := s.userModel(ctx).Where(dao.SysUser.Columns().DeptId, deptId)
|
||||
if in.UserName != "" {
|
||||
m = m.WhereLike(dao.SysUser.Columns().UserName, "%"+in.UserName+"%")
|
||||
}
|
||||
if in.NickName != "" {
|
||||
m = m.WhereLike(dao.SysUser.Columns().NickName, "%"+in.NickName+"%")
|
||||
}
|
||||
if in.Email != "" {
|
||||
m = m.WhereLike(dao.SysUser.Columns().Email, "%"+in.Email+"%")
|
||||
}
|
||||
if in.Phonenumber != "" {
|
||||
m = m.WhereLike(dao.SysUser.Columns().Phonenumber, "%"+in.Phonenumber+"%")
|
||||
}
|
||||
if in.Status != "" {
|
||||
m = m.Where(dao.SysUser.Columns().Status, in.Status)
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
begin, end := gtime.NewFromStr(in.CreatedAt[0]), gtime.NewFromStr(in.CreatedAt[1])
|
||||
m = m.WhereBetween(dao.SysUser.Columns().CreatedAt, begin, end.EndOfDay())
|
||||
}
|
||||
total, err = m.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var sysList []*model.SysUserListModel
|
||||
err = m.Order(dao.SysUser.Columns().UserId, "ASC").WithAll().Page(in.Page, in.PageSize).Scan(&sysList)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if err = s.fillRolesAndDefaultFlag(ctx, sysList); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if err = gconv.Structs(sysList, &list); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
// UserView 查看用户详情
|
||||
func (s *sAgentUser) UserView(ctx context.Context, userId int64) (res *model.AgentUserViewModel, err error) {
|
||||
sysUser, err := service.SysUser().GetUserById(ctx, userId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sysUser == nil || sysUser.UserId == 0 {
|
||||
return nil, gerror.New("用户不存在")
|
||||
}
|
||||
if sysUser.DeptId != contexts.GetDeptId(ctx) {
|
||||
return nil, gerror.New("只能操作本级用户")
|
||||
}
|
||||
res = &model.AgentUserViewModel{}
|
||||
if err = gconv.Struct(sysUser, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.IsDefaultUser, err = s.isDefaultUser(ctx, userId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// filterRoleIdsByDept 过滤角色ID,只保留本级及下一级角色
|
||||
func (s *sAgentUser) filterRoleIdsByDept(ctx context.Context, deptId int64, roleIds []int64) (res []int64, err error) {
|
||||
if len(roleIds) == 0 {
|
||||
return roleIds, nil
|
||||
}
|
||||
var roles []*entity.SysRole
|
||||
err = s.roleModel(ctx).
|
||||
WhereIn(dao.SysRole.Columns().RoleId, roleIds).
|
||||
Where(dao.SysRole.Columns().DeptId, deptId).
|
||||
Where("is_pass", 0).
|
||||
Scan(&roles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, role := range roles {
|
||||
res = append(res, role.RoleId)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// fillRolesAndDefaultFlag 填充用户角色和默认标志
|
||||
func (s *sAgentUser) fillRolesAndDefaultFlag(ctx context.Context, list []*model.SysUserListModel) error {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
userIds := make([]int64, 0, len(list))
|
||||
deptIds := make([]int64, 0, len(list))
|
||||
userById := make(map[int64]*model.SysUserListModel, len(list))
|
||||
for _, user := range list {
|
||||
userIds = append(userIds, user.UserId)
|
||||
deptIds = append(deptIds, user.DeptId)
|
||||
userById[user.UserId] = user
|
||||
}
|
||||
|
||||
var userRoles []*entity.SysUserRole
|
||||
err := dao.SysUserRole.Ctx(ctx).
|
||||
WhereIn(dao.SysUserRole.Columns().UserId, userIds).
|
||||
Scan(&userRoles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
roleIds := make([]int64, 0, len(userRoles))
|
||||
userRoleMap := make(map[int64][]int64, len(userRoles))
|
||||
for _, userRole := range userRoles {
|
||||
roleIds = append(roleIds, userRole.RoleId)
|
||||
userRoleMap[userRole.UserId] = append(userRoleMap[userRole.UserId], userRole.RoleId)
|
||||
}
|
||||
if len(roleIds) > 0 {
|
||||
var roles []*entity.SysRole
|
||||
if err = s.roleModel(ctx).
|
||||
WhereIn(dao.SysRole.Columns().RoleId, roleIds).
|
||||
Scan(&roles); err != nil {
|
||||
return err
|
||||
}
|
||||
roleMap := make(map[int64]*entity.SysRole, len(roles))
|
||||
for _, role := range roles {
|
||||
roleMap[role.RoleId] = role
|
||||
}
|
||||
for _, userRole := range userRoles {
|
||||
role := roleMap[userRole.RoleId]
|
||||
user := userById[userRole.UserId]
|
||||
if role == nil || user == nil {
|
||||
continue
|
||||
}
|
||||
user.Roles = append(user.Roles, &model.SysRoleMiniModel{
|
||||
RoleId: role.RoleId,
|
||||
RoleName: role.RoleName,
|
||||
DataScope: role.DataScope,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var depts []*entity.SysDept
|
||||
if err = s.deptModel(ctx).
|
||||
WhereIn(dao.SysDept.Columns().DeptId, deptIds).
|
||||
Scan(&depts); err != nil {
|
||||
return err
|
||||
}
|
||||
deptRoleMap := make(map[int64]int64, len(depts))
|
||||
for _, dept := range depts {
|
||||
if dept.DeptRole > 0 {
|
||||
deptRoleMap[dept.DeptId] = dept.DeptRole
|
||||
}
|
||||
}
|
||||
for _, user := range list {
|
||||
deptRoleId := deptRoleMap[user.DeptId]
|
||||
if deptRoleId == 0 {
|
||||
continue
|
||||
}
|
||||
for _, roleId := range userRoleMap[user.UserId] {
|
||||
if roleId == deptRoleId {
|
||||
user.IsDefaultUser = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isDefaultUser 判断用户是否为默认用户
|
||||
func (s *sAgentUser) isDefaultUser(ctx context.Context, userId int64) (bool, error) {
|
||||
var user *entity.SysUser
|
||||
if err := s.userModel(ctx).
|
||||
Where(dao.SysUser.Columns().UserId, userId).
|
||||
Scan(&user); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if user == nil || user.UserId == 0 {
|
||||
return false, gerror.New("用户不存在")
|
||||
}
|
||||
var dept *entity.SysDept
|
||||
if err := s.deptModel(ctx).
|
||||
Where(dao.SysDept.Columns().DeptId, user.DeptId).
|
||||
Scan(&dept); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if dept == nil || dept.DeptRole == 0 {
|
||||
return false, nil
|
||||
}
|
||||
count, err := dao.SysUserRole.Ctx(ctx).
|
||||
Where(dao.SysUserRole.Columns().UserId, userId).
|
||||
Where(dao.SysUserRole.Columns().RoleId, dept.DeptRole).
|
||||
Count()
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
// UserSave 新增/修改用户
|
||||
func (s *sAgentUser) UserSave(ctx context.Context, in *model.SysUserAddModel) (res *model.AgentUserViewModel, err error) {
|
||||
user := contexts.GetUser(ctx)
|
||||
if user == nil {
|
||||
return nil, gerror.New("登录已过期")
|
||||
}
|
||||
in.TenantId = user.TenantId
|
||||
in.DeptId = user.DeptId
|
||||
in.UserType = consts.SysUserTypeSys
|
||||
if in.DeptId == 0 {
|
||||
return nil, gerror.New("当前用户未绑定组织")
|
||||
}
|
||||
if len(in.RoleIds) == 0 {
|
||||
return nil, gerror.New("请选择角色")
|
||||
}
|
||||
in.RoleIds, err = s.filterRoleIdsByDept(ctx, in.DeptId, in.RoleIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(in.RoleIds) == 0 {
|
||||
return nil, gerror.New("请选择本级角色")
|
||||
}
|
||||
// 验证角色的DeptId和用户的deptId一致
|
||||
if err = s.ensureRoleDeptInScope(ctx, in.DeptId, in.RoleIds); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sysUser, err := service.SysUser().AddUser(ctx, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = &model.AgentUserViewModel{}
|
||||
if err = gconv.Struct(sysUser, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.IsDefaultUser, err = s.isDefaultUser(ctx, res.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// UserUpdate 更新用户
|
||||
func (s *sAgentUser) UserUpdate(ctx context.Context, in *model.SysUserUpdateModel) (err error) {
|
||||
current, err := s.UserView(ctx, in.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentDeptId := contexts.GetDeptId(ctx)
|
||||
if currentDeptId == 0 {
|
||||
return gerror.New("当前用户未绑定组织")
|
||||
}
|
||||
if current.DeptId != currentDeptId {
|
||||
return gerror.New("只能修改本级用户")
|
||||
}
|
||||
in.DeptId = ¤t.DeptId
|
||||
if len(in.RoleIds) == 0 {
|
||||
return gerror.New("请选择角色")
|
||||
}
|
||||
in.RoleIds, err = s.filterRoleIdsByDept(ctx, *in.DeptId, in.RoleIds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(in.RoleIds) == 0 {
|
||||
return gerror.New("请选择本级角色")
|
||||
}
|
||||
userType := consts.SysUserTypeSys
|
||||
in.UserType = &userType
|
||||
// 验证角色的DeptId和用户的deptId一致
|
||||
if err = s.ensureRoleDeptInScope(ctx, *in.DeptId, in.RoleIds); err != nil {
|
||||
return err
|
||||
}
|
||||
return service.SysUser().UpdateUser(ctx, in)
|
||||
}
|
||||
|
||||
// UserDelete 删除用户
|
||||
func (s *sAgentUser) UserDelete(ctx context.Context, userIds []int64) (err error) {
|
||||
for _, userId := range userIds {
|
||||
if _, err = s.UserView(ctx, userId); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return service.SysUser().DeleteUser(ctx, userIds)
|
||||
}
|
||||
|
||||
// UserResetPassword 重置用户密码
|
||||
func (s *sAgentUser) UserResetPassword(ctx context.Context, userId int64, password string) (err error) {
|
||||
if _, err = s.UserView(ctx, userId); err != nil {
|
||||
return err
|
||||
}
|
||||
return service.SysUser().ResetPassword(ctx, userId, password)
|
||||
}
|
||||
|
||||
// 验证角色部门和用户部门是否一致
|
||||
func (s *sAgentUser) ensureRoleDeptInScope(ctx context.Context, deptId int64, roleIds []int64) (err error) {
|
||||
// 验证角色的DeptId和用户的deptId一致
|
||||
var roles []*entity.SysRole
|
||||
err = s.roleModel(ctx).
|
||||
WhereIn(dao.SysRole.Columns().RoleId, roleIds).
|
||||
Scan(&roles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, role := range roles {
|
||||
if role.DeptId != deptId {
|
||||
return gerror.New("角色部门与用户部门不一致")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
// Package iot
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <xxx@example.com>
|
||||
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||
// @AutoGenerate Version
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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/xgorm"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/hook"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel"
|
||||
|
||||
"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/gctx"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sAgentEnterprise struct{}
|
||||
|
||||
func NewAgentEnterprise() *sAgentEnterprise {
|
||||
return &sAgentEnterprise{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAgentEnterprise(NewAgentEnterprise())
|
||||
}
|
||||
|
||||
// Model 企业客户单位信息ORM模型
|
||||
func (s *sAgentEnterprise) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
if len(option) == 0 {
|
||||
option = append(option, &handler.Option{
|
||||
FilterTenant: true,
|
||||
FilterAuth: true,
|
||||
})
|
||||
}
|
||||
return handler.Model(dao.TAgentEnterprise.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取企业客户单位信息列表
|
||||
func (s *sAgentEnterprise) List(ctx context.Context, in *model.TAgentEnterpriseListParam) (list []*model.TAgentEnterpriseListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(model.TAgentEnterpriseListModel{})
|
||||
|
||||
// 查询主键ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TAgentEnterprise.Columns().Id, in.Id)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.TAgentEnterprise.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TAgentEnterprise.Columns().Id)
|
||||
|
||||
// 操作人摘要信息
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取企业客户单位信息列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出企业客户单位信息
|
||||
func (s *sAgentEnterprise) Export(ctx context.Context, in *model.TAgentEnterpriseListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TAgentEnterpriseExportModel{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
fileName = "导出企业客户单位信息-" + gctx.CtxId(ctx)
|
||||
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, (totalCount+in.PageSize-1)/in.PageSize, in.Page, len(list))
|
||||
exports []model.TAgentEnterpriseExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增企业客户单位信息
|
||||
func (s *sAgentEnterprise) Edit(ctx context.Context, in *model.TAgentEnterpriseEditParam) (err error) {
|
||||
// 验证'LegalPersonIdCard'唯一
|
||||
if err = xgorm.IsUnique(ctx, &dao.TAgentEnterprise, g.Map{dao.TAgentEnterprise.Columns().LegalPersonIdCard: in.LegalPersonIdCard}, "法人身份证号已存在", in.Id); err != nil {
|
||||
return
|
||||
}
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
userId := contexts.GetUserId(ctx)
|
||||
in.UpdatedBy = &userId
|
||||
in.UpdatedAt = gtime.Now()
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(model.TAgentEnterpriseUpdateFields{}).
|
||||
WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "修改企业客户单位信息失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
in.TenantId = &tenantId
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
in.CreatedDept = &deptId
|
||||
userId := contexts.GetUserId(ctx)
|
||||
in.CreatedBy = &userId
|
||||
in.CreatedAt = gtime.Now()
|
||||
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Fields(model.TAgentEnterpriseInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增企业客户单位信息失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除企业客户单位信息
|
||||
func (s *sAgentEnterprise) Delete(ctx context.Context, in *model.TAgentEnterpriseDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).Where(dao.TAgentEnterprise.Columns().DeptId, in.DeptId).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除企业客户单位信息失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取企业客户单位信息指定信息
|
||||
func (s *sAgentEnterprise) View(ctx context.Context, in *model.TAgentEnterpriseViewParam) (res *model.TAgentEnterpriseViewModel, err error) {
|
||||
if err = s.Model(ctx).Where(dao.TAgentEnterprise.Columns().DeptId, in.DeptId).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取企业客户单位信息信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Package iot
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <xxx@example.com>
|
||||
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||
// @AutoGenerate Version
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/exportcenter"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/hook"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
|
||||
"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 sAgentTApi struct{}
|
||||
|
||||
func NewAgentTApi() *sAgentTApi {
|
||||
return &sAgentTApi{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAgentTApi(NewAgentTApi())
|
||||
}
|
||||
|
||||
// Model API集成ORM模型
|
||||
func (s *sAgentTApi) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
if len(option) == 0 {
|
||||
option = append(option, &handler.Option{
|
||||
FilterTenant: true,
|
||||
FilterAuth: true,
|
||||
})
|
||||
}
|
||||
return handler.Model(dao.TApi.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取API集成列表
|
||||
func (s *sAgentTApi) List(ctx context.Context, in *model.TApiListParam) (list []*model.TApiListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
tApiTable := dao.TApi.Table()
|
||||
sysDeptTable := dao.SysDept.Table()
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.FieldsPrefix(tApiTable, model.TApiListModel{})
|
||||
mod = mod.Fields(
|
||||
sysDeptTable+"."+dao.SysDept.Columns().DeptKey+" AS deptKey",
|
||||
sysDeptTable+"."+dao.SysDept.Columns().DeptName+" AS deptName",
|
||||
)
|
||||
mod = mod.LeftJoin(
|
||||
sysDeptTable,
|
||||
sysDeptTable+"."+dao.SysDept.Columns().DeptId+"="+tApiTable+"."+dao.TApi.Columns().DeptId,
|
||||
)
|
||||
|
||||
// 查询APPKey
|
||||
if in.AppKey != "" {
|
||||
mod = mod.Where(dao.TApi.Columns().AppKey+" like ?", "%"+in.AppKey+"%")
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TApi.Columns().Id)
|
||||
|
||||
// 操作人摘要信息
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取API集成列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出API集成(异步:提交任务后到下载中心取文件)
|
||||
func (s *sAgentTApi) Export(ctx context.Context, in *model.TApiListParam) (err error) {
|
||||
_, err = exportcenter.Submit(ctx, &exportcenter.Request{
|
||||
BizKey: "tApi",
|
||||
BizName: "API集成",
|
||||
PermCode: "cpm:agent:tApi:export",
|
||||
QueryParam: in,
|
||||
Tags: func() ([]string, error) {
|
||||
return convert.GetEntityDescTags(model.TApiListModel{})
|
||||
},
|
||||
Fetch: func(ctx context.Context, page, pageSize int) (rows []interface{}, total int, err error) {
|
||||
query := *in
|
||||
query.Page = page
|
||||
query.PageSize = pageSize
|
||||
list, count, err := s.List(ctx, &query)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
rows = make([]interface{}, 0, len(list))
|
||||
for _, item := range list {
|
||||
rows = append(rows, item)
|
||||
}
|
||||
return rows, count, nil
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Edit 修改/新增API集成
|
||||
func (s *sAgentTApi) Edit(ctx context.Context, in *model.TApiEditParam) (err error) {
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
userId := contexts.GetUserId(ctx)
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
in.TenantId = &tenantId
|
||||
in.UpdatedBy = &userId
|
||||
in.UpdatedAt = gtime.Now()
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(model.TApiUpdateFields{}).
|
||||
WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "修改API集成失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
in.TenantId = &tenantId
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
in.CreatedDept = &deptId
|
||||
userId := contexts.GetUserId(ctx)
|
||||
in.CreatedBy = &userId
|
||||
in.CreatedAt = gtime.Now()
|
||||
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Fields(model.TApiInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增API集成失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除API集成
|
||||
func (s *sAgentTApi) Delete(ctx context.Context, in *model.TApiDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除API集成失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取API集成指定信息
|
||||
func (s *sAgentTApi) View(ctx context.Context, in *model.TApiViewParam) (res *model.TApiViewModel, err error) {
|
||||
if err = s.Model(ctx).Where(dao.TApi.Columns().AppKey, in.AppKey).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取API集成信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/exportcenter"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/hook"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
|
||||
"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"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sAgentTApiGroup struct{}
|
||||
|
||||
// NewAgentTApiGroup 创建开放接口权限组后台服务实例。
|
||||
func NewAgentTApiGroup() *sAgentTApiGroup {
|
||||
return &sAgentTApiGroup{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAgentTApiGroup(NewAgentTApiGroup())
|
||||
}
|
||||
|
||||
// Model 返回开放接口权限组 ORM 模型,默认启用租户和数据权限过滤。
|
||||
func (s *sAgentTApiGroup) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
if len(option) == 0 {
|
||||
option = append(option, &handler.Option{
|
||||
FilterTenant: true,
|
||||
FilterAuth: true,
|
||||
})
|
||||
}
|
||||
return handler.Model(dao.TApiGroup.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 分页查询开放接口权限组列表。
|
||||
func (s *sAgentTApiGroup) List(ctx context.Context, in *model.TApiGroupListParam) (list []*model.TApiGroupListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx).Fields(model.TApiGroupListModel{})
|
||||
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TApiGroup.Columns().Id, in.Id)
|
||||
}
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where(dao.TApiGroup.Columns().Status, in.Status)
|
||||
}
|
||||
if in.GroupName != "" {
|
||||
mod = mod.Where(dao.TApiGroup.Columns().GroupName+" LIKE ?", "%"+in.GroupName+"%")
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
mod = mod.WhereBetween(dao.TApiGroup.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
mod = mod.Page(in.Page, in.PageSize).
|
||||
OrderDesc(dao.TApiGroup.Columns().Id).
|
||||
Hook(hook.MemberSummary)
|
||||
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
return nil, 0, gerror.Wrap(err, "获取开放接口权限组列表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出开放接口权限组(异步:提交任务后到下载中心取文件)
|
||||
func (s *sAgentTApiGroup) Export(ctx context.Context, in *model.TApiGroupListParam) (err error) {
|
||||
_, err = exportcenter.Submit(ctx, &exportcenter.Request{
|
||||
BizKey: "tApiGroup",
|
||||
BizName: "开放接口权限组",
|
||||
PermCode: "cpm:agent:tApiGroup:export",
|
||||
QueryParam: in,
|
||||
Tags: func() ([]string, error) {
|
||||
return convert.GetEntityDescTags(model.TApiGroupListModel{})
|
||||
},
|
||||
Fetch: func(ctx context.Context, page, pageSize int) (rows []interface{}, total int, err error) {
|
||||
query := *in
|
||||
query.Page = page
|
||||
query.PageSize = pageSize
|
||||
list, count, err := s.List(ctx, &query)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
rows = make([]interface{}, 0, len(list))
|
||||
for _, item := range list {
|
||||
rows = append(rows, item)
|
||||
}
|
||||
return rows, count, nil
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Edit 新增或修改开放接口权限组,并同步维护权限组与接口资源的关联。
|
||||
func (s *sAgentTApiGroup) Edit(ctx context.Context, in *model.TApiGroupEditParam) (err error) {
|
||||
if in.GroupCode != nil {
|
||||
if err = xgorm.IsUnique(ctx, &dao.TApiGroup, g.Map{dao.TApiGroup.Columns().GroupCode: *in.GroupCode}, "权限组编码已存在", in.Id); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
groupId := in.Id
|
||||
|
||||
if in.Id > 0 {
|
||||
updatedBy := contexts.GetUserId(ctx)
|
||||
in.UpdatedBy = &updatedBy
|
||||
in.UpdatedAt = gtime.Now()
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
in.TenantId = &tenantId
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(model.TApiGroupUpdateFields{}).
|
||||
WherePri(in.Id).
|
||||
Data(in).
|
||||
Update(); err != nil {
|
||||
return gerror.Wrap(err, "修改开放接口权限组失败,请稍后重试!")
|
||||
}
|
||||
return s.replaceGroupResources(ctx, tx, groupId, in.ApiIds)
|
||||
}
|
||||
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
in.TenantId = &tenantId
|
||||
createdDept := contexts.GetDeptId(ctx)
|
||||
in.CreatedDept = &createdDept
|
||||
createdBy := contexts.GetUserId(ctx)
|
||||
in.CreatedBy = &createdBy
|
||||
in.CreatedAt = gtime.Now()
|
||||
groupId, err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Fields(model.TApiGroupInsertFields{}).
|
||||
Data(in).
|
||||
InsertAndGetId()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "新增开放接口权限组失败,请稍后重试!")
|
||||
}
|
||||
return s.replaceGroupResources(ctx, tx, groupId, in.ApiIds)
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除开放接口权限组,并删除该权限组下的资源关联。
|
||||
func (s *sAgentTApiGroup) Delete(ctx context.Context, in *model.TApiGroupDeleteParam) (err error) {
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
groupIds := gconv.Int64s(in.Id)
|
||||
if len(groupIds) > 0 {
|
||||
if _, err = tx.Ctx(ctx).Model(dao.TApiGroupResource.Table()).
|
||||
WhereIn(dao.TApiGroupResource.Columns().GroupId, groupIds).
|
||||
Delete(); err != nil {
|
||||
return gerror.Wrap(err, "删除开放接口权限组资源关联失败,请稍后重试!")
|
||||
}
|
||||
}
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
return gerror.Wrap(err, "删除开放接口权限组失败,请稍后重试!")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// View returns the API group detail with bound resource IDs and resource list.
|
||||
func (s *sAgentTApiGroup) View(ctx context.Context, in *model.TApiGroupViewParam) (res *model.TApiGroupViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
return nil, gerror.Wrap(err, "get open api group failed")
|
||||
}
|
||||
if res == nil {
|
||||
return
|
||||
}
|
||||
apiIds, err := dao.TApiGroupResource.Ctx(ctx).
|
||||
Fields(dao.TApiGroupResource.Columns().ApiId).
|
||||
Where(dao.TApiGroupResource.Columns().GroupId, in.Id).
|
||||
Array()
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "get open api group resource relations failed")
|
||||
}
|
||||
res.ApiIds = gconv.Int64s(apiIds)
|
||||
res.Apis = []*model.TApiResourceListModel{}
|
||||
if len(res.ApiIds) > 0 {
|
||||
if err = handler.Model(dao.TApiResource.Ctx(ctx), &handler.Option{FilterTenant: true, FilterAuth: false}).
|
||||
Fields(model.TApiResourceListModel{}).
|
||||
WhereIn(dao.TApiResource.Columns().Id, res.ApiIds).
|
||||
OrderAsc(dao.TApiResource.Columns().Id).
|
||||
Hook(hook.MemberSummary).
|
||||
Scan(&res.Apis); err != nil {
|
||||
return nil, gerror.Wrap(err, "get open api group resources failed")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新开放接口权限组启停状态。
|
||||
func (s *sAgentTApiGroup) Status(ctx context.Context, in *model.TApiGroupStatusParam) (err error) {
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{
|
||||
dao.TApiGroup.Columns().Status: in.Status,
|
||||
dao.TApiGroup.Columns().UpdatedBy: contexts.GetUserId(ctx),
|
||||
dao.TApiGroup.Columns().UpdatedAt: gtime.Now(),
|
||||
}).Update(); err != nil {
|
||||
return gerror.Wrap(err, "更新开放接口权限组状态失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// replaceGroupResources 替换权限组绑定的接口资源,先清空旧关联再批量写入新关联。
|
||||
func (s *sAgentTApiGroup) replaceGroupResources(ctx context.Context, tx gdb.TX, groupId int64, apiIds []int64) (err error) {
|
||||
if groupId <= 0 {
|
||||
return gerror.New("权限组ID不能为空")
|
||||
}
|
||||
if _, err = tx.Ctx(ctx).Model(dao.TApiGroupResource.Table()).
|
||||
Where(dao.TApiGroupResource.Columns().GroupId, groupId).
|
||||
Delete(); err != nil {
|
||||
return gerror.Wrap(err, "删除开放接口权限组资源关联失败,请稍后重试!")
|
||||
}
|
||||
|
||||
seen := make(map[int64]struct{}, len(apiIds))
|
||||
data := g.List{}
|
||||
for _, apiId := range apiIds {
|
||||
if apiId <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[apiId]; ok {
|
||||
continue
|
||||
}
|
||||
seen[apiId] = struct{}{}
|
||||
data = append(data, g.Map{
|
||||
dao.TApiGroupResource.Columns().GroupId: groupId,
|
||||
dao.TApiGroupResource.Columns().ApiId: apiId,
|
||||
})
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
if _, err = tx.Ctx(ctx).Model(dao.TApiGroupResource.Table()).Data(data).Insert(); err != nil {
|
||||
return gerror.Wrap(err, "保存开放接口权限组资源关联失败,请稍后重试!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"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/exportcenter"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/hook"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
|
||||
"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"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sAgentTApiResource struct{}
|
||||
|
||||
// NewAgentTApiResource 创建开放接口资源后台服务实例。
|
||||
func NewAgentTApiResource() *sAgentTApiResource {
|
||||
return &sAgentTApiResource{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAgentTApiResource(NewAgentTApiResource())
|
||||
}
|
||||
|
||||
// Model 返回开放接口资源 ORM 模型,默认启用租户和数据权限过滤。
|
||||
func (s *sAgentTApiResource) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
if len(option) == 0 {
|
||||
option = append(option, &handler.Option{
|
||||
FilterTenant: true,
|
||||
FilterAuth: true,
|
||||
})
|
||||
}
|
||||
return handler.Model(dao.TApiResource.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 分页查询开放接口资源列表。
|
||||
func (s *sAgentTApiResource) List(ctx context.Context, in *model.TApiResourceListParam) (list []*model.TApiResourceListModel, totalCount int, err error) {
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
if deptId <= 0 {
|
||||
return []*model.TApiResourceListModel{}, 0, nil
|
||||
}
|
||||
|
||||
mod := s.Model(ctx, &handler.Option{FilterTenant: true, FilterAuth: false}).
|
||||
Fields(model.TApiResourceListModel{})
|
||||
|
||||
isTop, dept, err := service.AgentDept().IsTopDept(ctx)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if dept != nil && (dept.DeptId == 100 || dept.ParentId == 100) {
|
||||
isTop = true
|
||||
}
|
||||
|
||||
if !isTop {
|
||||
var apiInfo struct {
|
||||
GroupId int64 `orm:"group_id"`
|
||||
}
|
||||
if err = dao.TApi.Ctx(ctx).
|
||||
Fields(dao.TApi.Columns().GroupId).
|
||||
Where(dao.TApi.Columns().DeptId, deptId).
|
||||
Where(dao.TApi.Columns().TenantId, contexts.GetTenantId(ctx)).
|
||||
OrderDesc(dao.TApi.Columns().Id).
|
||||
Scan(&apiInfo); err != nil {
|
||||
return nil, 0, gerror.Wrap(err, "get current dept open api group failed")
|
||||
}
|
||||
if apiInfo.GroupId <= 0 {
|
||||
return []*model.TApiResourceListModel{}, 0, nil
|
||||
}
|
||||
|
||||
apiIds, err := dao.TApiGroupResource.Ctx(ctx).
|
||||
Fields(dao.TApiGroupResource.Columns().ApiId).
|
||||
Where(dao.TApiGroupResource.Columns().GroupId, apiInfo.GroupId).
|
||||
Array()
|
||||
if err != nil {
|
||||
return nil, 0, gerror.Wrap(err, "get open api group resources failed")
|
||||
}
|
||||
if len(apiIds) == 0 {
|
||||
return []*model.TApiResourceListModel{}, 0, nil
|
||||
}
|
||||
mod = mod.WhereIn(dao.TApiResource.Columns().Id, gconv.Int64s(apiIds))
|
||||
}
|
||||
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TApiResource.Columns().Id, in.Id)
|
||||
}
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where(dao.TApiResource.Columns().Status, in.Status)
|
||||
}
|
||||
if in.ApiName != "" {
|
||||
mod = mod.Where(dao.TApiResource.Columns().ApiName+" LIKE ?", "%"+in.ApiName+"%")
|
||||
}
|
||||
if in.Version != "" {
|
||||
mod = mod.Where(dao.TApiResource.Columns().Version+" LIKE ?", "%"+in.Version+"%")
|
||||
}
|
||||
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
mod = mod.WhereBetween(dao.TApiResource.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
mod = mod.Page(in.Page, in.PageSize).
|
||||
OrderDesc(dao.TApiResource.Columns().Id).
|
||||
Hook(hook.MemberSummary)
|
||||
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
return nil, 0, gerror.Wrap(err, "获取开放接口资源列表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出开放接口资源(异步:提交任务后到下载中心取文件)
|
||||
func (s *sAgentTApiResource) Export(ctx context.Context, in *model.TApiResourceListParam) (err error) {
|
||||
_, err = exportcenter.Submit(ctx, &exportcenter.Request{
|
||||
BizKey: "tApiResource",
|
||||
BizName: "开放接口资源",
|
||||
PermCode: "cpm:agent:tApiResource:export",
|
||||
QueryParam: in,
|
||||
Tags: func() ([]string, error) {
|
||||
return convert.GetEntityDescTags(model.TApiResourceListModel{})
|
||||
},
|
||||
Fetch: func(ctx context.Context, page, pageSize int) (rows []interface{}, total int, err error) {
|
||||
query := *in
|
||||
query.Page = page
|
||||
query.PageSize = pageSize
|
||||
list, count, err := s.List(ctx, &query)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
rows = make([]interface{}, 0, len(list))
|
||||
for _, item := range list {
|
||||
rows = append(rows, item)
|
||||
}
|
||||
return rows, count, nil
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Edit 新增或修改开放接口资源。
|
||||
func (s *sAgentTApiResource) Edit(ctx context.Context, in *model.TApiResourceEditParam) (err error) {
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
if in.Status == nil {
|
||||
status := 0
|
||||
in.Status = &status
|
||||
}
|
||||
if in.Method != nil {
|
||||
method := *in.Method
|
||||
in.Method = &method
|
||||
}
|
||||
|
||||
if in.ApiCode != nil && gstr.Trim(*in.ApiCode) != "" {
|
||||
if err = xgorm.IsUnique(ctx, &dao.TApiResource, g.Map{dao.TApiResource.Columns().ApiCode: gstr.Trim(*in.ApiCode)}, "接口权限标识已存在", in.Id); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
if in.Id > 0 {
|
||||
updatedBy := contexts.GetUserId(ctx)
|
||||
in.UpdatedBy = &updatedBy
|
||||
in.UpdatedAt = gtime.Now()
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
in.TenantId = &tenantId
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(model.TApiResourceUpdateFields{}).
|
||||
WherePri(in.Id).
|
||||
Data(in).
|
||||
Update(); err != nil {
|
||||
return gerror.Wrap(err, "修改开放接口资源失败,请稍后重试!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
in.TenantId = &tenantId
|
||||
createdDept := contexts.GetDeptId(ctx)
|
||||
in.CreatedDept = &createdDept
|
||||
createdBy := contexts.GetUserId(ctx)
|
||||
in.CreatedBy = &createdBy
|
||||
in.CreatedAt = gtime.Now()
|
||||
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Fields(model.TApiResourceInsertFields{}).
|
||||
Data(in).
|
||||
Insert(); err != nil {
|
||||
return gerror.Wrap(err, "新增开放接口资源失败,请稍后重试!")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除开放接口资源,并清理权限组资源关联。
|
||||
func (s *sAgentTApiResource) Delete(ctx context.Context, in *model.TApiResourceDeleteParam) (err error) {
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
apiIds := gconv.Int64s(in.Id)
|
||||
if len(apiIds) > 0 {
|
||||
if _, err = tx.Ctx(ctx).Model(dao.TApiGroupResource.Table()).
|
||||
WhereIn(dao.TApiGroupResource.Columns().ApiId, apiIds).
|
||||
Delete(); err != nil {
|
||||
return gerror.Wrap(err, "删除开放接口权限组资源关联失败,请稍后重试!")
|
||||
}
|
||||
}
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
return gerror.Wrap(err, "删除开放接口资源失败,请稍后重试!")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// View 查询开放接口资源详情。
|
||||
func (s *sAgentTApiResource) View(ctx context.Context, in *model.TApiResourceViewParam) (res *model.TApiResourceViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
return nil, gerror.Wrap(err, "获取开放接口资源信息失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新开放接口资源启停状态。
|
||||
func (s *sAgentTApiResource) Status(ctx context.Context, in *model.TApiResourceStatusParam) (err error) {
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{
|
||||
dao.TApiResource.Columns().Status: in.Status,
|
||||
dao.TApiResource.Columns().UpdatedBy: contexts.GetUserId(ctx),
|
||||
dao.TApiResource.Columns().UpdatedAt: gtime.Now(),
|
||||
}).Update(); err != nil {
|
||||
return gerror.Wrap(err, "更新开放接口资源状态失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// Package iot
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <xxx@example.com>
|
||||
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||
// @AutoGenerate Version
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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/xgorm/handler"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/hook"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel"
|
||||
|
||||
"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/gctx"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sAgentTCustWithdrawAccount struct{}
|
||||
|
||||
func NewAgentTCustWithdrawAccount() *sAgentTCustWithdrawAccount {
|
||||
return &sAgentTCustWithdrawAccount{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAgentTCustWithdrawAccount(NewAgentTCustWithdrawAccount())
|
||||
}
|
||||
|
||||
// Model 客户提现账户表ORM模型
|
||||
func (s *sAgentTCustWithdrawAccount) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
if len(option) == 0 {
|
||||
option = append(option, &handler.Option{
|
||||
FilterTenant: true,
|
||||
FilterAuth: true,
|
||||
})
|
||||
}
|
||||
return handler.Model(dao.TCustWithdrawAccount.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取客户提现账户表列表
|
||||
func (s *sAgentTCustWithdrawAccount) List(ctx context.Context, in *model.TCustWithdrawAccountListParam) (list []*model.TCustWithdrawAccountListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(model.TCustWithdrawAccountListModel{})
|
||||
|
||||
// 查询主键ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TCustWithdrawAccount.Columns().Id, in.Id)
|
||||
}
|
||||
|
||||
// 查询状态:0-禁用 1-正常
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where(dao.TCustWithdrawAccount.Columns().Status, in.Status)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.TCustWithdrawAccount.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TCustWithdrawAccount.Columns().Id)
|
||||
|
||||
// 操作人摘要信息
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取客户提现账户表列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出客户提现账户表
|
||||
func (s *sAgentTCustWithdrawAccount) Export(ctx context.Context, in *model.TCustWithdrawAccountListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TCustWithdrawAccountExportModel{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
fileName = "导出客户提现账户表-" + gctx.CtxId(ctx)
|
||||
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, (totalCount+in.PageSize-1)/in.PageSize, in.Page, len(list))
|
||||
exports []model.TCustWithdrawAccountExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增客户提现账户表
|
||||
func (s *sAgentTCustWithdrawAccount) Edit(ctx context.Context, in *model.TCustWithdrawAccountEditParam) (err error) {
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
userId := contexts.GetUserId(ctx)
|
||||
in.UpdatedBy = &userId
|
||||
in.UpdatedAt = gtime.Now()
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(model.TCustWithdrawAccountUpdateFields{}).
|
||||
WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "修改客户提现账户表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
in.TenantId = &tenantId
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
in.CreatedDept = &deptId
|
||||
userId := contexts.GetUserId(ctx)
|
||||
in.CreatedBy = &userId
|
||||
in.CreatedAt = gtime.Now()
|
||||
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Fields(model.TCustWithdrawAccountInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增客户提现账户表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除客户提现账户表
|
||||
func (s *sAgentTCustWithdrawAccount) Delete(ctx context.Context, in *model.TCustWithdrawAccountDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除客户提现账户表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取客户提现账户表指定信息
|
||||
func (s *sAgentTCustWithdrawAccount) View(ctx context.Context, in *model.TCustWithdrawAccountViewParam) (res *model.TCustWithdrawAccountViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取客户提现账户表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新客户提现账户表状态
|
||||
func (s *sAgentTCustWithdrawAccount) Status(ctx context.Context, in *model.TCustWithdrawAccountStatusParam) (err error) {
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{
|
||||
dao.TCustWithdrawAccount.Columns().Status: in.Status,
|
||||
dao.TCustWithdrawAccount.Columns().UpdatedBy: contexts.GetUserId(ctx),
|
||||
}).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "更新客户提现账户表状态失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user