From 40ac52923ef82144a8b502f4b7ca5f8a8fa1d259 Mon Sep 17 00:00:00 2001 From: Austin Date: Mon, 21 Sep 2026 18:44:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(imTenant):=20=E6=96=B0=E5=A2=9E=E5=95=86?= =?UTF-8?q?=E6=88=B7=E7=AE=A1=E7=90=86=E5=91=98=E8=B4=A6=E5=8F=B7=E6=9F=A5?= =?UTF-8?q?=E7=9C=8B=E4=B8=8E=E5=AF=86=E7=A0=81=E4=BF=AE=E6=94=B9=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增编辑页管理员账号展示与密码修改字段,仅编辑态可见 2. 新增商户管理员密码重置逻辑,完善编辑场景密码校验 3. 补充超管可见控制:业务库、连接串、管理员信息非超管脱敏 4. 修复schema未使用响应式值的问题 --- server/internal/logic/sys/im_tenant.go | 68 ++++++++++++++++++- .../internal/model/input/sysin/im_tenant.go | 6 ++ web/src/views/imTenant/edit.vue | 15 ++++ web/src/views/imTenant/index.vue | 2 +- web/src/views/imTenant/model.ts | 4 +- 5 files changed, 89 insertions(+), 6 deletions(-) diff --git a/server/internal/logic/sys/im_tenant.go b/server/internal/logic/sys/im_tenant.go index 1745e39..23baa75 100644 --- a/server/internal/logic/sys/im_tenant.go +++ b/server/internal/logic/sys/im_tenant.go @@ -165,6 +165,7 @@ func (s *sSysImTenant) fillListConfig(ctx context.Context, list []*sysin.ImTenan dsnMap[item.Id] = item.DbDsn } + super := isSuperTenantAdmin(ctx) for _, item := range list { if conf, ok := configMap[item.Id]; ok { item.MaxUsers = conf.MaxUsers @@ -175,7 +176,7 @@ func (s *sSysImTenant) fillListConfig(ctx context.Context, list []*sysin.ImTenan item.MessageEncryptionEnabled = conf.MessageEncryptionEnabled item.RealnameReviewPolicy = conf.RealnameReviewPolicy } - item.DbDsnConfigured = dsnMap[item.Id] != "" + item.DbDsnConfigured = super && dsnMap[item.Id] != "" } return } @@ -276,6 +277,11 @@ func (s *sSysImTenant) saveTenant(ctx context.Context, in *sysin.ImTenantEditInp s.rollbackTenantCreation(ctx, tenantID) return err } + } else if in.AdminPassword != "" { + // 编辑商户时填写了新密码则重置管理员密码,留空不修改 + if err = s.resetTenantAdminPassword(ctx, tenantID, in.AdminPassword); err != nil { + return err + } } _ = publishImPolicyEvent(ctx, tenantID, in.Code, "tenant.features.updated", "moderation", 0) @@ -348,6 +354,41 @@ func (s *sSysImTenant) createTenantAdminMember(ctx context.Context, super bool, }) } +// resetTenantAdminPassword 重置商户管理员账号的密码(编辑弹窗填写新密码时调用) +func (s *sSysImTenant) resetTenantAdminPassword(ctx context.Context, tenantId int64, password string) (err error) { + roleVal, err := dao.AdminRole.Ctx(ctx). + Fields(dao.AdminRole.Columns().Id). + Where(dao.AdminRole.Columns().Key, imManageRoleKey). + Where(dao.AdminRole.Columns().Status, consts.StatusEnabled). + Limit(1). + Value() + if err != nil { + return gerror.Wrap(err, "查询IM管理员角色失败,请稍后重试!") + } + if roleVal.IsEmpty() { + return gerror.New("IM管理员角色(imManage)未配置,请联系运维") + } + + memberId, err := dao.AdminMember.Ctx(ctx). + Fields(dao.AdminMember.Columns().Id). + Where(dao.AdminMember.Columns().TenantId, tenantId). + Where(dao.AdminMember.Columns().RoleId, roleVal.Int64()). + OrderAsc(dao.AdminMember.Columns().Id). + Limit(1). + Value() + if err != nil { + return gerror.Wrap(err, "查询商户管理员账号失败,请稍后重试!") + } + if memberId.IsEmpty() { + return gerror.New("该商户暂无管理员账号,无法重置密码") + } + + return service.AdminMember().ResetPwd(ctx, &adminin.MemberResetPwdInp{ + Id: memberId.Int64(), + Password: password, + }) +} + // rollbackTenantCreation 商户创建补偿:删除已写入的商户及配置记录 func (s *sSysImTenant) rollbackTenantCreation(ctx context.Context, tenantId int64) { _ = imtenant.RegistryDB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { @@ -476,10 +517,30 @@ func (s *sSysImTenant) View(ctx context.Context, in *sysin.ImTenantViewInp) (res res.RealnameReviewPolicy = conf.RealnameReviewPolicy } res.DbDsnConfigured = res.DbDsn != "" - // 业务库名与独立连接串仅超管可见,非超管脱敏返回(保留是否已配置标记) + // 查询商户自动创建的管理员账号(编辑弹窗展示) + roleVal, roleErr := dao.AdminRole.Ctx(ctx). + Fields(dao.AdminRole.Columns().Id). + Where(dao.AdminRole.Columns().Key, imManageRoleKey). + Where(dao.AdminRole.Columns().Status, consts.StatusEnabled). + Limit(1). + Value() + if roleErr == nil && !roleVal.IsEmpty() { + userVal, userErr := dao.AdminMember.Ctx(ctx). + Fields(dao.AdminMember.Columns().Username). + Where(dao.AdminMember.Columns().TenantId, in.Id). + Where(dao.AdminMember.Columns().RoleId, roleVal.Int64()). + OrderAsc(dao.AdminMember.Columns().Id). + Limit(1). + Value() + if userErr == nil && !userVal.IsEmpty() { + res.AdminUsername = userVal.String() + } + } + // 业务库名与独立连接串仅超管可见,非超管全部脱敏 if !isSuperTenantAdmin(ctx) { res.DbName = "" res.DbDsn = "" + res.DbDsnConfigured = false } return } @@ -520,9 +581,10 @@ func (s *sSysImTenant) Usage(ctx context.Context, in *sysin.ImTenantUsageInp) (r DbDsnConfigured: tenant.DbDsn != "", Warnings: []string{}, } - // 业务库名仅超管可见,非超管脱敏返回 + // 业务库名与连接串信息仅超管可见,非超管全部脱敏 if !isSuperTenantAdmin(ctx) { res.DbName = "" + res.DbDsnConfigured = false } db, _, dbErr := imtenant.DB(ctx, tenant.Id) diff --git a/server/internal/model/input/sysin/im_tenant.go b/server/internal/model/input/sysin/im_tenant.go index 8fbeba4..2fe67f0 100644 --- a/server/internal/model/input/sysin/im_tenant.go +++ b/server/internal/model/input/sysin/im_tenant.go @@ -94,6 +94,11 @@ func (in *ImTenantEditInp) Filter(ctx context.Context) (err error) { if e := g.Validator().Rules("required|length:6,32").Data(in.AdminPassword).Messages("管理员密码不能为空|管理员密码长度应为6到32位").Run(ctx); e != nil { return e.Current() } + } else if in.AdminPassword != "" { + // 编辑时填写了新密码则重置,留空表示不修改 + if e := g.Validator().Rules("length:6,32").Data(in.AdminPassword).Messages("管理员密码长度应为6到32位").Run(ctx); e != nil { + return e.Current() + } } return } @@ -113,6 +118,7 @@ func (in *ImTenantViewInp) Filter(ctx context.Context) (err error) { type ImTenantViewModel struct { entity.ImTenant + AdminUsername string `json:"adminUsername" dc:"商户管理员账号"` MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"` MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"` MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"` diff --git a/web/src/views/imTenant/edit.vue b/web/src/views/imTenant/edit.vue index 373fa15..9aa57fc 100644 --- a/web/src/views/imTenant/edit.vue +++ b/web/src/views/imTenant/edit.vue @@ -56,6 +56,21 @@ /> + + + + + + + + + + item.field !== 'dbName' || isAdminSuper.value), + schemas: schemas.value.filter((item) => item.field !== 'dbName' || isAdminSuper.value), }); // 加载表格数据 diff --git a/web/src/views/imTenant/model.ts b/web/src/views/imTenant/model.ts index 598b6aa..5ca2cf4 100644 --- a/web/src/views/imTenant/model.ts +++ b/web/src/views/imTenant/model.ts @@ -297,12 +297,12 @@ const baseColumns = [ }, ]; -// 业务库名列仅admin超管可见 +// 业务库名与独立连接串列仅admin超管可见 export function buildColumns(isAdmin: boolean) { if (isAdmin) { return baseColumns; } - return baseColumns.filter((item) => item.key !== 'dbName'); + return baseColumns.filter((item) => item.key !== 'dbName' && item.key !== 'dbDsnConfigured'); } // 加载字典数据选项