From 85b564e2ac5ddc719b9211a575e16f106d95e105 Mon Sep 17 00:00:00 2001 From: richjohnrambo Date: Tue, 22 Sep 2026 16:47:40 +0800 Subject: [PATCH] refactor: merge tenant configuration into tenant registry --- .../api/admin/imconfiginternal/im_config.go | 2 - .../internalapi/imconfig/im_config.go | 28 +-- server/internal/dao/im_tenant_config.go | 22 -- server/internal/dao/internal/im_tenant.go | 70 ++++-- .../internal/dao/internal/im_tenant_config.go | 103 -------- server/internal/library/imtenant/db.go | 40 +-- .../logic/sys/im_message_encryption.go | 17 +- .../internal/logic/sys/im_security_policy.go | 4 +- server/internal/logic/sys/im_tenant.go | 237 +++--------------- server/internal/logic/sys/im_tenant_tables.go | 11 - server/internal/model/do/im_tenant.go | 37 ++- server/internal/model/do/im_tenant_config.go | 28 --- server/internal/model/entity/im_tenant.go | 35 ++- .../internal/model/entity/im_tenant_config.go | 26 -- .../internal/model/input/sysin/im_tenant.go | 64 +---- .../data/generate/im_message_encryption.sql | 4 +- .../data/generate/im_tenant_merge_config.sql | 38 +++ .../im_tenant_merge_config_cleanup.sql | 9 + web/src/views/imTenant/edit.vue | 16 +- web/src/views/imTenant/index.vue | 8 +- web/src/views/imTenant/model.ts | 47 +--- web/src/views/imTenant/usage.vue | 4 - 22 files changed, 216 insertions(+), 634 deletions(-) delete mode 100644 server/internal/dao/im_tenant_config.go delete mode 100644 server/internal/dao/internal/im_tenant_config.go delete mode 100644 server/internal/model/do/im_tenant_config.go delete mode 100644 server/internal/model/entity/im_tenant_config.go create mode 100644 server/storage/data/generate/im_tenant_merge_config.sql create mode 100644 server/storage/data/generate/im_tenant_merge_config_cleanup.sql diff --git a/server/api/admin/imconfiginternal/im_config.go b/server/api/admin/imconfiginternal/im_config.go index c001eef..15f55f8 100644 --- a/server/api/admin/imconfiginternal/im_config.go +++ b/server/api/admin/imconfiginternal/im_config.go @@ -35,8 +35,6 @@ type TenantModel struct { Code string `json:"code"` Name string `json:"name"` TenantDesc *string `json:"tenant_desc,omitempty"` - DBName string `json:"db_name"` - DBDSN string `json:"db_dsn"` State int `json:"state"` CreatedAt string `json:"created_at"` CreatedBy int64 `json:"created_by"` diff --git a/server/internal/controller/internalapi/imconfig/im_config.go b/server/internal/controller/internalapi/imconfig/im_config.go index 7d8f2c8..8e803d8 100644 --- a/server/internal/controller/internalapi/imconfig/im_config.go +++ b/server/internal/controller/internalapi/imconfig/im_config.go @@ -178,17 +178,8 @@ func (c *cConfig) ModerationPolicy(ctx context.Context, req *api.ModerationPolic return nil, err } res = defaultModerationPolicy(tenant) - var tenantConfig struct { - AliyunModerationEnabled int `json:"aliyun_moderation_enabled"` - LocalModerationEnabled int `json:"local_moderation_enabled"` - } - if err = imtenant.RegistryDB().Model(dao.ImTenantConfig.Table()).Safe().Ctx(ctx). - Fields("aliyun_moderation_enabled", "local_moderation_enabled"). - Where(dao.ImTenantConfig.Columns().TenantId, tenant.Id).Scan(&tenantConfig); err != nil { - return nil, gerror.Wrap(err, "获取商户内容审核开关失败") - } - res.AliyunEnabled = tenantConfig.AliyunModerationEnabled == 1 - res.LocalWordsEnabled = tenantConfig.LocalModerationEnabled == 1 + res.AliyunEnabled = tenant.AliyunModerationEnabled == 1 + res.LocalWordsEnabled = tenant.LocalModerationEnabled == 1 res.Enabled = res.AliyunEnabled || res.LocalWordsEnabled columns := dao.ImSecurityPolicy.Columns() var row struct { @@ -333,18 +324,7 @@ func (c *cConfig) RealnamePolicy(ctx context.Context, req *api.RealnamePolicyReq return nil, err } res = defaultRealnamePolicy(tenant.Id) - var row *struct { - RealnameReviewPolicy int `json:"realname_review_policy"` - } - if err = imtenant.RegistryDB().Model(dao.ImTenantConfig.Table()).Safe().Ctx(ctx). - Fields("realname_review_policy"). - Where(dao.ImTenantConfig.Columns().TenantId, tenant.Id).Scan(&row); err != nil { - return nil, gerror.Wrap(err, "获取租户实名认证策略失败") - } - if row == nil { - return res, nil - } - if row.RealnameReviewPolicy == 1 { + if tenant.RealnameReviewPolicy == 1 { res.RealnameReviewPolicy = 1 } return res, nil @@ -553,8 +533,6 @@ func tenantToModel(tenant *entity.ImTenant) *api.TenantModel { Code: tenant.Code, Name: tenant.Name, TenantDesc: &desc, - DBName: tenant.DbName, - DBDSN: tenant.DbDsn, State: tenant.State, CreatedAt: gtimeToRFC3339(tenant.CreatedAt), CreatedBy: tenant.CreatedBy, diff --git a/server/internal/dao/im_tenant_config.go b/server/internal/dao/im_tenant_config.go deleted file mode 100644 index bb54153..0000000 --- a/server/internal/dao/im_tenant_config.go +++ /dev/null @@ -1,22 +0,0 @@ -// ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. -// ================================================================================= - -package dao - -import ( - "hotgo/internal/dao/internal" -) - -// imTenantConfigDao is the data access object for the table im_tenant_config. -// You can define custom methods on it to extend its functionality as needed. -type imTenantConfigDao struct { - *internal.ImTenantConfigDao -} - -var ( - // ImTenantConfig is a globally accessible object for table im_tenant_config operations. - ImTenantConfig = imTenantConfigDao{internal.NewImTenantConfigDao()} -) - -// Add your custom methods and functionality below. diff --git a/server/internal/dao/internal/im_tenant.go b/server/internal/dao/internal/im_tenant.go index f86ea9a..676156b 100644 --- a/server/internal/dao/internal/im_tenant.go +++ b/server/internal/dao/internal/im_tenant.go @@ -21,32 +21,58 @@ type ImTenantDao struct { // ImTenantColumns defines and stores column names for the table im_tenant. type ImTenantColumns struct { - Id string // - Code string // - Name string // - TenantDesc string // - DbName string // - DbDsn string // - State string // - CreatedAt string // - CreatedBy string // - UpdatedAt string // - UpdatedBy string // + Id string // + Code string // + Name string // + TenantDesc string // + MaxUsers string // 0表示不限制 + MaxGroups string // 0表示不限制 + MaxGroupMembers string // 0表示不限制 + RealnameReviewPolicy string // 实名审核策略:0自动通过,1转人工审核 + RealnameProviderMode string // 实名认证模式 + RealnameRequired string // 是否强制实名 + RealnameAllowSkip string // 是否允许跳过实名 + RealnameRemindAfter string // 实名提醒延迟 + AliyunModerationEnabled string // 是否启用阿里云内容审核 + LocalModerationEnabled string // 是否启用本地内容审核 + MessageEncryptionEnabled string // 是否启用消息加密 + DefaultPolicyGroupId string // 默认用户策略组 + SecurityPolicyVersion string // 已发布安全策略版本 + GlobalFriendEnabled string // 全局加好友开关 + MessageCurfewEnabled string // 消息宵禁开关 + State string // + CreatedAt string // + CreatedBy string // + UpdatedAt string // + UpdatedBy string // } // imTenantColumns holds the columns for the table im_tenant. var imTenantColumns = ImTenantColumns{ - Id: "id", - Code: "code", - Name: "name", - TenantDesc: "tenant_desc", - DbName: "db_name", - DbDsn: "db_dsn", - State: "state", - CreatedAt: "created_at", - CreatedBy: "created_by", - UpdatedAt: "updated_at", - UpdatedBy: "updated_by", + Id: "id", + Code: "code", + Name: "name", + TenantDesc: "tenant_desc", + MaxUsers: "max_users", + MaxGroups: "max_groups", + MaxGroupMembers: "max_group_members", + RealnameReviewPolicy: "realname_review_policy", + RealnameProviderMode: "realname_provider_mode", + RealnameRequired: "realname_required", + RealnameAllowSkip: "realname_allow_skip", + RealnameRemindAfter: "realname_remind_after", + AliyunModerationEnabled: "aliyun_moderation_enabled", + LocalModerationEnabled: "local_moderation_enabled", + MessageEncryptionEnabled: "message_encryption_enabled", + DefaultPolicyGroupId: "default_policy_group_id", + SecurityPolicyVersion: "security_policy_version", + GlobalFriendEnabled: "global_friend_enabled", + MessageCurfewEnabled: "message_curfew_enabled", + State: "state", + CreatedAt: "created_at", + CreatedBy: "created_by", + UpdatedAt: "updated_at", + UpdatedBy: "updated_by", } // NewImTenantDao creates and returns a new DAO object for table data access. diff --git a/server/internal/dao/internal/im_tenant_config.go b/server/internal/dao/internal/im_tenant_config.go deleted file mode 100644 index 16bc159..0000000 --- a/server/internal/dao/internal/im_tenant_config.go +++ /dev/null @@ -1,103 +0,0 @@ -// ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. -// ========================================================================== - -package internal - -import ( - "context" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/frame/g" -) - -// ImTenantConfigDao is the data access object for the table im_tenant_config. -type ImTenantConfigDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of the current DAO. - columns ImTenantConfigColumns // columns contains all the column names of Table for convenient usage. - handlers []gdb.ModelHandler // handlers for customized model modification. -} - -// ImTenantConfigColumns defines and stores column names for the table im_tenant_config. -type ImTenantConfigColumns struct { - Id string // - TenantId string // - MaxUsers string // 0 means unlimited - MaxGroups string // 0 means unlimited - MaxGroupMembers string // 0 means unlimited - DefaultPolicyGroupId string // Default user policy group - SecurityPolicyVersion string // Published security policy version - GlobalFriendEnabled string // Published global friend switch - MessageCurfewEnabled string // Published message curfew switch - CreatedAt string // - CreatedBy string // - UpdatedAt string // - UpdatedBy string // -} - -// imTenantConfigColumns holds the columns for the table im_tenant_config. -var imTenantConfigColumns = ImTenantConfigColumns{ - Id: "id", - TenantId: "tenant_id", - MaxUsers: "max_users", - MaxGroups: "max_groups", - MaxGroupMembers: "max_group_members", - DefaultPolicyGroupId: "default_policy_group_id", - SecurityPolicyVersion: "security_policy_version", - GlobalFriendEnabled: "global_friend_enabled", - MessageCurfewEnabled: "message_curfew_enabled", - CreatedAt: "created_at", - CreatedBy: "created_by", - UpdatedAt: "updated_at", - UpdatedBy: "updated_by", -} - -// NewImTenantConfigDao creates and returns a new DAO object for table data access. -func NewImTenantConfigDao(handlers ...gdb.ModelHandler) *ImTenantConfigDao { - return &ImTenantConfigDao{ - group: "default", - table: "im_tenant_config", - columns: imTenantConfigColumns, - handlers: handlers, - } -} - -// DB retrieves and returns the underlying raw database management object of the current DAO. -func (dao *ImTenantConfigDao) DB() gdb.DB { - return g.DB(dao.group) -} - -// Table returns the table name of the current DAO. -func (dao *ImTenantConfigDao) Table() string { - return dao.table -} - -// Columns returns all column names of the current DAO. -func (dao *ImTenantConfigDao) Columns() ImTenantConfigColumns { - return dao.columns -} - -// Group returns the database configuration group name of the current DAO. -func (dao *ImTenantConfigDao) Group() string { - return dao.group -} - -// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. -func (dao *ImTenantConfigDao) Ctx(ctx context.Context) *gdb.Model { - model := dao.DB().Model(dao.table) - for _, handler := range dao.handlers { - model = handler(model) - } - return model.Safe().Ctx(ctx) -} - -// Transaction wraps the transaction logic using function f. -// It rolls back the transaction and returns the error if function f returns a non-nil error. -// It commits the transaction and returns nil if function f returns nil. -// -// Note: Do not commit or roll back the transaction in function f, -// as it is automatically handled by this function. -func (dao *ImTenantConfigDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { - return dao.Ctx(ctx).Transaction(ctx, f) -} diff --git a/server/internal/library/imtenant/db.go b/server/internal/library/imtenant/db.go index e774f14..6310993 100644 --- a/server/internal/library/imtenant/db.go +++ b/server/internal/library/imtenant/db.go @@ -20,6 +20,7 @@ import ( var tenantDbGroups sync.Map const registryDbGroup = "imRegistry" +const sharedBusinessDatabase = "meeting_im" var registryDbGroupOnce sync.Once @@ -47,19 +48,15 @@ func DB(ctx context.Context, tenantId int64) (db gdb.DB, tenant *entity.ImTenant // Tenant isolation inside this database is implemented by tenant_id predicates // and the three tenant-partitioned high-volume table families. func SharedDB(ctx context.Context) (gdb.DB, error) { - var sharedTenant *entity.ImTenant - if err := RegistryDB().Model(dao.ImTenant.Table()).Safe().Ctx(ctx). - Where(dao.ImTenant.Columns().Code, "default").Scan(&sharedTenant); err != nil { - return nil, gerror.Wrap(err, "获取共享业务库配置失败") + defaultLink := defaultDbLink(ctx) + if defaultLink == "" { + return nil, gerror.New("默认数据库连接未配置,无法连接共享业务库") } - if sharedTenant == nil || sharedTenant.Id <= 0 { - return nil, gerror.New("默认商户不存在,无法确定共享业务库") - } - link, err := tenantLink(ctx, sharedTenant) + link, err := replaceMySQLDatabase(defaultLink, sharedBusinessDatabase) if err != nil { return nil, err } - return dbForLink(link, sharedTenant.Id) + return dbForLink(link, 0) } func dbForLink(link string, ownerID int64) (gdb.DB, error) { @@ -111,31 +108,6 @@ func ensureRegistryDBGroup() { _ = gdb.SetConfigGroup(registryDbGroup, cloned) } -func tenantLink(ctx context.Context, tenant *entity.ImTenant) (string, error) { - if tenant.DbDsn != "" { - return normalizeMySQLTimeOptions(tenant.DbDsn) - } - if tenant.DbName == "" { - // 未配置独立业务库的商户回落共享默认库 - defaultLink := defaultDbLink(ctx) - if defaultLink == "" { - return "", gerror.New("默认数据库连接未配置,无法回落共享业务库") - } - return normalizeMySQLTimeOptions(defaultLink) - } - - defaultLink := defaultDbLink(ctx) - if defaultLink == "" { - return "", gerror.New("默认数据库连接未配置,无法推导租户业务库连接") - } - - link, err := replaceMySQLDatabase(defaultLink, tenant.DbName) - if err != nil { - return "", err - } - return link, nil -} - func defaultDbLink(ctx context.Context) string { if configs, err := gdb.GetConfigGroup("default"); err == nil && len(configs) > 0 { if configs[0].Link != "" { diff --git a/server/internal/logic/sys/im_message_encryption.go b/server/internal/logic/sys/im_message_encryption.go index e0f5710..c99f47b 100644 --- a/server/internal/logic/sys/im_message_encryption.go +++ b/server/internal/logic/sys/im_message_encryption.go @@ -40,10 +40,9 @@ func (s *sSysImMessageEncryption) List(ctx context.Context, in *sysin.ImMessageE } db := imtenant.RegistryDB() mod := db.Model(dao.ImTenant.Table()+" t").Safe().Ctx(ctx). - LeftJoin(dao.ImTenantConfig.Table()+" c", "c.tenant_id=t.id"). Fields( "t.id tenant_id", "t.code tenant_code", "t.name tenant_name", - "COALESCE(c."+imTenantMessageEncryptionColumn+",0) message_encryption_enabled", + "t."+imTenantMessageEncryptionColumn+" message_encryption_enabled", "COALESCE((SELECT MAX(k.key_version) FROM "+imMessageEncryptionKeyTable+" k WHERE k.tenant_id=t.id AND k.status='active'),0) active_version", "COALESCE((SELECT MAX(k.key_version) FROM "+imMessageEncryptionKeyTable+" k WHERE k.tenant_id=t.id AND k.status='pending'),0) pending_version", "(SELECT COUNT(1) FROM "+imMessageEncryptionKeyTable+" k WHERE k.tenant_id=t.id) key_count", @@ -56,7 +55,7 @@ func (s *sSysImMessageEncryption) List(ctx context.Context, in *sysin.ImMessageE mod = mod.Where("t.code LIKE ? OR t.name LIKE ?", "%"+keyword+"%", "%"+keyword+"%") } if in.Enabled != nil { - mod = mod.Where("COALESCE(c."+imTenantMessageEncryptionColumn+",0)=?", *in.Enabled) + mod = mod.Where("t."+imTenantMessageEncryptionColumn, *in.Enabled) } if err = mod.Page(in.Page, in.PerPage).OrderAsc("t.id").ScanAndCount(&list, &total, false); err != nil { return nil, 0, gerror.Wrap(err, "获取商户消息加密配置失败") @@ -185,16 +184,16 @@ func (s *sSysImMessageEncryption) Switch(ctx context.Context, in *sysin.ImMessag return gerror.New("启用消息加密前必须先初始化有效密钥") } } - result, err := imtenant.RegistryDB().Model(dao.ImTenantConfig.Table()).Safe().Ctx(ctx). - Where(dao.ImTenantConfig.Columns().TenantId, tenant.Id). - Data(g.Map{imTenantMessageEncryptionColumn: in.Enabled, dao.ImTenantConfig.Columns().UpdatedBy: contexts.GetUserId(ctx)}).Update() + result, err := imtenant.RegistryDB().Model(dao.ImTenant.Table()).Safe().Ctx(ctx). + Where(dao.ImTenant.Columns().Id, tenant.Id). + Data(g.Map{dao.ImTenant.Columns().MessageEncryptionEnabled: in.Enabled, dao.ImTenant.Columns().UpdatedBy: contexts.GetUserId(ctx)}).Update() if err != nil { return gerror.Wrap(err, "更新商户消息加密开关失败") } if affected, _ := result.RowsAffected(); affected == 0 { return gerror.New("商户配置不存在") } - _ = writeImAdminAudit(ctx, tenant.Id, "message_encryption_switch", "im_tenant_config", tenant.Id, "success", g.Map{"enabled": in.Enabled}) + _ = writeImAdminAudit(ctx, tenant.Id, "message_encryption_switch", dao.ImTenant.Table(), tenant.Id, "success", g.Map{"enabled": in.Enabled}) return publishImPolicyEvent(ctx, tenant.Id, tenant.Code, "tenant.features.updated", "message_encryption", 0) } @@ -203,8 +202,8 @@ func (s *sSysImMessageEncryption) GetKey(ctx context.Context, in *sysin.ImMessag if err != nil { return nil, err } - enabledValue, err := imtenant.RegistryDB().Model(dao.ImTenantConfig.Table()).Safe().Ctx(ctx). - Fields(imTenantMessageEncryptionColumn).Where(dao.ImTenantConfig.Columns().TenantId, tenant.Id).Value() + enabledValue, err := imtenant.RegistryDB().Model(dao.ImTenant.Table()).Safe().Ctx(ctx). + Fields(dao.ImTenant.Columns().MessageEncryptionEnabled).Where(dao.ImTenant.Columns().Id, tenant.Id).Value() if err != nil { return nil, gerror.Wrap(err, "获取商户消息加密开关失败") } diff --git a/server/internal/logic/sys/im_security_policy.go b/server/internal/logic/sys/im_security_policy.go index e4e9920..d30d029 100644 --- a/server/internal/logic/sys/im_security_policy.go +++ b/server/internal/logic/sys/im_security_policy.go @@ -207,8 +207,8 @@ func (s *sSysImSecurityPolicy) Publish(ctx context.Context, in *sysin.ImSecurity return updateErr } if tenantID > 0 { - tc := dao.ImTenantConfig.Columns() - if _, updateErr := tx.Model(dao.ImTenantConfig.Table()).Safe().Ctx(ctx).Where(tc.TenantId, tenantID).Data(g.Map{tc.SecurityPolicyVersion: row[c.PolicyVersion].Uint(), tc.GlobalFriendEnabled: row[c.GlobalFriendEnabled].Int(), tc.MessageCurfewEnabled: row[c.MessageCurfewEnabled].Int(), tc.UpdatedAt: now, tc.UpdatedBy: operator}).Update(); updateErr != nil { + tc := dao.ImTenant.Columns() + if _, updateErr := tx.Model(dao.ImTenant.Table()).Safe().Ctx(ctx).Where(tc.Id, tenantID).Data(g.Map{tc.SecurityPolicyVersion: row[c.PolicyVersion].Uint(), tc.GlobalFriendEnabled: row[c.GlobalFriendEnabled].Int(), tc.MessageCurfewEnabled: row[c.MessageCurfewEnabled].Int(), tc.UpdatedAt: now, tc.UpdatedBy: operator}).Update(); updateErr != nil { return updateErr } } diff --git a/server/internal/logic/sys/im_tenant.go b/server/internal/logic/sys/im_tenant.go index 442f127..5ab9726 100644 --- a/server/internal/logic/sys/im_tenant.go +++ b/server/internal/logic/sys/im_tenant.go @@ -27,28 +27,9 @@ import ( type sSysImTenant struct{} -const ( - tenantConfigAliyunModerationEnabled = "aliyun_moderation_enabled" - tenantConfigLocalModerationEnabled = "local_moderation_enabled" - tenantConfigMessageEncryptionEnabled = "message_encryption_enabled" - tenantConfigRealnameReviewPolicy = "realname_review_policy" -) - // imManageRoleKey 商户级IM管理员角色标识 const imManageRoleKey = "imManage" -type imTenantConfigModeration struct { - Id int64 `json:"id" orm:"id"` - TenantId int64 `json:"tenant_id" orm:"tenant_id"` - MaxUsers uint64 `json:"max_users" orm:"max_users"` - MaxGroups uint64 `json:"max_groups" orm:"max_groups"` - MaxGroupMembers uint `json:"max_group_members" orm:"max_group_members"` - AliyunModerationEnabled int `json:"aliyun_moderation_enabled" orm:"aliyun_moderation_enabled"` - LocalModerationEnabled int `json:"local_moderation_enabled" orm:"local_moderation_enabled"` - MessageEncryptionEnabled int `json:"message_encryption_enabled" orm:"message_encryption_enabled"` - RealnameReviewPolicy int `json:"realname_review_policy" orm:"realname_review_policy"` -} - func NewSysImTenant() *sSysImTenant { return &sSysImTenant{} } @@ -62,10 +43,6 @@ func (s *sSysImTenant) Model(ctx context.Context, option ...*handler.Option) *gd return handler.Model(imtenant.RegistryDB().Model(dao.ImTenant.Table()).Safe().Ctx(ctx), option...) } -func (s *sSysImTenant) configModel(ctx context.Context) *gdb.Model { - return imtenant.RegistryDB().Model(dao.ImTenantConfig.Table()).Safe().Ctx(ctx) -} - // List 获取IM租户列表 func (s *sSysImTenant) List(ctx context.Context, in *sysin.ImTenantListInp) (list []*sysin.ImTenantListModel, totalCount int, err error) { if err = in.Filter(ctx); err != nil { @@ -77,7 +54,13 @@ func (s *sSysImTenant) List(ctx context.Context, in *sysin.ImTenantListInp) (lis dao.ImTenant.Columns().Code, dao.ImTenant.Columns().Name, dao.ImTenant.Columns().TenantDesc, - dao.ImTenant.Columns().DbName, + dao.ImTenant.Columns().MaxUsers, + dao.ImTenant.Columns().MaxGroups, + dao.ImTenant.Columns().MaxGroupMembers, + dao.ImTenant.Columns().AliyunModerationEnabled, + dao.ImTenant.Columns().LocalModerationEnabled, + dao.ImTenant.Columns().MessageEncryptionEnabled, + dao.ImTenant.Columns().RealnameReviewPolicy, dao.ImTenant.Columns().State, dao.ImTenant.Columns().CreatedBy, dao.ImTenant.Columns().UpdatedBy, @@ -102,9 +85,6 @@ func (s *sSysImTenant) List(ctx context.Context, in *sysin.ImTenantListInp) (lis if in.Name != "" { mod = mod.WhereLike(dao.ImTenant.Columns().Name, "%"+in.Name+"%") } - if in.DbName != "" && isSuperTenantAdmin(ctx) { - mod = mod.WhereLike(dao.ImTenant.Columns().DbName, "%"+in.DbName+"%") - } if in.State != nil { mod = mod.Where(dao.ImTenant.Columns().State, *in.State) } @@ -120,64 +100,6 @@ func (s *sSysImTenant) List(ctx context.Context, in *sysin.ImTenantListInp) (lis err = gerror.Wrap(err, "获取IM租户列表失败,请稍后重试!") return } - if len(list) == 0 { - return - } - - if err = s.fillListConfig(ctx, list); err != nil { - return - } - // 业务库名仅超管可见,非超管脱敏返回 - if !isSuperTenantAdmin(ctx) { - for _, item := range list { - item.DbName = "" - } - } - return -} - -func (s *sSysImTenant) fillListConfig(ctx context.Context, list []*sysin.ImTenantListModel) (err error) { - ids := make([]int64, 0, len(list)) - for _, item := range list { - ids = append(ids, item.Id) - } - - var configs []*imTenantConfigModeration - if err = s.configModel(ctx). - WhereIn(dao.ImTenantConfig.Columns().TenantId, ids). - Scan(&configs); err != nil { - return gerror.Wrap(err, "获取IM租户配置失败,请稍后重试!") - } - configMap := make(map[int64]*imTenantConfigModeration, len(configs)) - for _, item := range configs { - configMap[item.TenantId] = item - } - - var tenants []*entity.ImTenant - if err = s.Model(ctx). - Fields(dao.ImTenant.Columns().Id, dao.ImTenant.Columns().DbDsn). - WhereIn(dao.ImTenant.Columns().Id, ids). - Scan(&tenants); err != nil { - return gerror.Wrap(err, "获取IM租户连接配置失败,请稍后重试!") - } - dsnMap := make(map[int64]string, len(tenants)) - for _, item := range tenants { - dsnMap[item.Id] = item.DbDsn - } - - super := isSuperTenantAdmin(ctx) - for _, item := range list { - if conf, ok := configMap[item.Id]; ok { - item.MaxUsers = conf.MaxUsers - item.MaxGroups = conf.MaxGroups - item.MaxGroupMembers = conf.MaxGroupMembers - item.AliyunModerationEnabled = conf.AliyunModerationEnabled - item.LocalModerationEnabled = conf.LocalModerationEnabled - item.MessageEncryptionEnabled = conf.MessageEncryptionEnabled - item.RealnameReviewPolicy = conf.RealnameReviewPolicy - } - item.DbDsnConfigured = super && dsnMap[item.Id] != "" - } return } @@ -230,14 +152,10 @@ func (s *sSysImTenant) saveTenant(ctx context.Context, in *sysin.ImTenantEditInp if err = ensureTenantIDSequence(ctx); err != nil { return err } - if in.DbName, in.DbDsn, err = sharedDatabaseConfig(ctx); err != nil { - return err - } in.State = consts.ImTenantStateProvisioning - } else if !super { - // 业务库名与独立连接串仅超管可维护,非超管提交一律强制忽略。 - in.DbName = "" - in.DbDsn = "" + } + if err = validateTenantMessageEncryption(ctx, in.Id, in.MessageEncryptionEnabled); err != nil { + return err } err = imtenant.RegistryDB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { if err = s.checkCodeUnique(ctx, in.Id, in.Code); err != nil { @@ -248,21 +166,24 @@ func (s *sSysImTenant) saveTenant(ctx context.Context, in *sysin.ImTenantEditInp if in.Id > 0 { in.UpdatedBy = userId data := g.Map{ - dao.ImTenant.Columns().Code: in.Code, - dao.ImTenant.Columns().Name: in.Name, - dao.ImTenant.Columns().TenantDesc: in.TenantDesc, - dao.ImTenant.Columns().State: in.State, - dao.ImTenant.Columns().UpdatedBy: userId, - } - if super { - data[dao.ImTenant.Columns().DbName] = in.DbName - data[dao.ImTenant.Columns().DbDsn] = in.DbDsn + dao.ImTenant.Columns().Code: in.Code, + dao.ImTenant.Columns().Name: in.Name, + dao.ImTenant.Columns().TenantDesc: in.TenantDesc, + dao.ImTenant.Columns().MaxUsers: in.MaxUsers, + dao.ImTenant.Columns().MaxGroups: in.MaxGroups, + dao.ImTenant.Columns().MaxGroupMembers: in.MaxGroupMembers, + dao.ImTenant.Columns().AliyunModerationEnabled: in.AliyunModerationEnabled, + dao.ImTenant.Columns().LocalModerationEnabled: in.LocalModerationEnabled, + dao.ImTenant.Columns().MessageEncryptionEnabled: in.MessageEncryptionEnabled, + dao.ImTenant.Columns().RealnameReviewPolicy: in.RealnameReviewPolicy, + dao.ImTenant.Columns().State: in.State, + dao.ImTenant.Columns().UpdatedBy: userId, } if _, err = s.Model(ctx). WherePri(in.Id).Data(data).Update(); err != nil { return gerror.Wrap(err, "修改IM租户失败,请稍后重试!") } - return s.saveConfig(ctx, in, userId) + return nil } in.Id, err = nextTenantID(tx) @@ -278,7 +199,7 @@ func (s *sSysImTenant) saveTenant(ctx context.Context, in *sysin.ImTenantEditInp return gerror.Wrap(err, "新增IM租户失败,请稍后重试!") } tenantID = in.Id - return s.saveConfig(ctx, in, userId) + return nil }) if err != nil { return err @@ -413,15 +334,7 @@ func (s *sSysImTenant) resetTenantAdminPassword(ctx context.Context, tenantId in // rollbackTenantCreation 商户创建补偿:删除已写入的商户及配置记录 func (s *sSysImTenant) rollbackTenantCreation(ctx context.Context, tenantId int64) { - _ = imtenant.RegistryDB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { - if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}). - WherePri(tenantId).Delete(); err != nil { - return - } - _, err = s.configModel(ctx). - Where(dao.ImTenantConfig.Columns().TenantId, tenantId).Delete() - return - }) + _, _ = s.Model(ctx, &handler.Option{FilterAuth: false}).WherePri(tenantId).Delete() } func (s *sSysImTenant) checkCodeUnique(ctx context.Context, id int64, code string) (err error) { @@ -439,10 +352,10 @@ func (s *sSysImTenant) checkCodeUnique(ctx context.Context, id int64, code strin return } -func (s *sSysImTenant) saveConfig(ctx context.Context, in *sysin.ImTenantEditInp, userId int64) (err error) { - if in.MessageEncryptionEnabled == 1 { +func validateTenantMessageEncryption(ctx context.Context, tenantId int64, enabled int) error { + if enabled == 1 { count, queryErr := imtenant.RegistryDB().Model(imMessageEncryptionKeyTable).Safe().Ctx(ctx). - Where("tenant_id", in.Id). + Where("tenant_id", tenantId). Where("status", consts.ImMessageKeyStatusActive). Count() if queryErr != nil { @@ -452,61 +365,7 @@ func (s *sSysImTenant) saveConfig(ctx context.Context, in *sysin.ImTenantEditInp return gerror.New("启用消息加密前必须先在消息密钥管理中初始化有效密钥") } } - var conf *entity.ImTenantConfig - if err = s.configModel(ctx). - Where(dao.ImTenantConfig.Columns().TenantId, in.Id). - Scan(&conf); err != nil { - return gerror.Wrap(err, "获取IM租户配置失败,请稍后重试!") - } - - data := g.Map{ - dao.ImTenantConfig.Columns().MaxUsers: in.MaxUsers, - dao.ImTenantConfig.Columns().MaxGroups: in.MaxGroups, - dao.ImTenantConfig.Columns().MaxGroupMembers: in.MaxGroupMembers, - tenantConfigAliyunModerationEnabled: in.AliyunModerationEnabled, - tenantConfigLocalModerationEnabled: in.LocalModerationEnabled, - tenantConfigMessageEncryptionEnabled: in.MessageEncryptionEnabled, - tenantConfigRealnameReviewPolicy: in.RealnameReviewPolicy, - } - if conf != nil && conf.Id > 0 { - data[dao.ImTenantConfig.Columns().UpdatedBy] = userId - if _, err = s.configModel(ctx). - Fields( - dao.ImTenantConfig.Columns().MaxUsers, - dao.ImTenantConfig.Columns().MaxGroups, - dao.ImTenantConfig.Columns().MaxGroupMembers, - tenantConfigAliyunModerationEnabled, - tenantConfigLocalModerationEnabled, - tenantConfigMessageEncryptionEnabled, - tenantConfigRealnameReviewPolicy, - dao.ImTenantConfig.Columns().UpdatedBy, - ). - WherePri(conf.Id).Data(data).Update(); err != nil { - return gerror.Wrap(err, "修改IM租户配置失败,请稍后重试!") - } - return - } - - data[dao.ImTenantConfig.Columns().TenantId] = in.Id - data[dao.ImTenantConfig.Columns().CreatedBy] = userId - data[dao.ImTenantConfig.Columns().UpdatedBy] = userId - if _, err = s.configModel(ctx). - Fields( - dao.ImTenantConfig.Columns().TenantId, - dao.ImTenantConfig.Columns().MaxUsers, - dao.ImTenantConfig.Columns().MaxGroups, - dao.ImTenantConfig.Columns().MaxGroupMembers, - tenantConfigAliyunModerationEnabled, - tenantConfigLocalModerationEnabled, - tenantConfigMessageEncryptionEnabled, - tenantConfigRealnameReviewPolicy, - dao.ImTenantConfig.Columns().CreatedBy, - dao.ImTenantConfig.Columns().UpdatedBy, - ). - Data(data).Insert(); err != nil { - return gerror.Wrap(err, "新增IM租户配置失败,请稍后重试!") - } - return + return nil } // View 获取IM租户指定信息 @@ -522,23 +381,6 @@ func (s *sSysImTenant) View(ctx context.Context, in *sysin.ImTenantViewInp) (res return } - var conf *imTenantConfigModeration - if err = s.configModel(ctx). - Where(dao.ImTenantConfig.Columns().TenantId, in.Id). - Scan(&conf); err != nil { - err = gerror.Wrap(err, "获取IM租户配置失败,请稍后重试!") - return - } - if conf != nil { - res.MaxUsers = conf.MaxUsers - res.MaxGroups = conf.MaxGroups - res.MaxGroupMembers = conf.MaxGroupMembers - res.AliyunModerationEnabled = conf.AliyunModerationEnabled - res.LocalModerationEnabled = conf.LocalModerationEnabled - res.MessageEncryptionEnabled = conf.MessageEncryptionEnabled - res.RealnameReviewPolicy = conf.RealnameReviewPolicy - } - res.DbDsnConfigured = res.DbDsn != "" // 查询商户自动创建的管理员账号(编辑弹窗展示) roleVal, roleErr := dao.AdminRole.Ctx(ctx). Fields(dao.AdminRole.Columns().Id). @@ -558,12 +400,6 @@ func (s *sSysImTenant) View(ctx context.Context, in *sysin.ImTenantViewInp) (res res.AdminUsername = userVal.String() } } - // 业务库名与独立连接串仅超管可见,非超管全部脱敏 - if !isSuperTenantAdmin(ctx) { - res.DbName = "" - res.DbDsn = "" - res.DbDsnConfigured = false - } return } @@ -601,17 +437,10 @@ func (s *sSysImTenant) Usage(ctx context.Context, in *sysin.ImTenantUsageInp) (r } res = &sysin.ImTenantUsageModel{ - Id: tenant.Id, - Code: tenant.Code, - Name: tenant.Name, - DbName: tenant.DbName, - DbDsnConfigured: tenant.DbDsn != "", - Warnings: []string{}, - } - // 业务库名与连接串信息仅超管可见,非超管全部脱敏 - if !isSuperTenantAdmin(ctx) { - res.DbName = "" - res.DbDsnConfigured = false + Id: tenant.Id, + Code: tenant.Code, + Name: tenant.Name, + Warnings: []string{}, } db, _, dbErr := imtenant.DB(ctx, tenant.Id) diff --git a/server/internal/logic/sys/im_tenant_tables.go b/server/internal/logic/sys/im_tenant_tables.go index f5fa8b0..a9b83df 100644 --- a/server/internal/logic/sys/im_tenant_tables.go +++ b/server/internal/logic/sys/im_tenant_tables.go @@ -12,17 +12,6 @@ import ( const imTenantIDSequenceTable = "im_tenant_id_sequence" -func sharedDatabaseConfig(ctx context.Context) (dbName, dbDSN string, err error) { - row, err := imtenant.RegistryDB().GetOne(ctx, "SELECT db_name,db_dsn FROM im_tenant WHERE code='default' LIMIT 1") - if err != nil { - return "", "", gerror.Wrap(err, "获取共享业务库配置失败") - } - if row.IsEmpty() { - return "", "", gerror.New("默认商户不存在,无法确定共享业务库") - } - return row["db_name"].String(), row["db_dsn"].String(), nil -} - func ensureTenantIDSequence(ctx context.Context) error { db := imtenant.RegistryDB() if _, err := db.Exec(ctx, `CREATE TABLE IF NOT EXISTS im_tenant_id_sequence ( diff --git a/server/internal/model/do/im_tenant.go b/server/internal/model/do/im_tenant.go index 1600c02..e884864 100644 --- a/server/internal/model/do/im_tenant.go +++ b/server/internal/model/do/im_tenant.go @@ -11,16 +11,29 @@ import ( // ImTenant is the golang structure of table im_tenant for DAO operations like Where/Data. type ImTenant struct { - g.Meta `orm:"table:im_tenant, do:true"` - Id any // - Code any // - Name any // - TenantDesc any // - DbName any // - DbDsn any // - State any // - CreatedAt *gtime.Time // - CreatedBy any // - UpdatedAt *gtime.Time // - UpdatedBy any // + g.Meta `orm:"table:im_tenant, do:true"` + Id any // + Code any // + Name any // + TenantDesc any // + MaxUsers any // 0表示不限制 + MaxGroups any // 0表示不限制 + MaxGroupMembers any // 0表示不限制 + RealnameReviewPolicy any // 实名审核策略:0自动通过,1转人工审核 + RealnameProviderMode any // 实名认证模式 + RealnameRequired any // 是否强制实名 + RealnameAllowSkip any // 是否允许跳过实名 + RealnameRemindAfter any // 实名提醒延迟 + AliyunModerationEnabled any // 是否启用阿里云内容审核 + LocalModerationEnabled any // 是否启用本地内容审核 + MessageEncryptionEnabled any // 是否启用消息加密 + DefaultPolicyGroupId any // 默认用户策略组 + SecurityPolicyVersion any // 已发布安全策略版本 + GlobalFriendEnabled any // 全局加好友开关 + MessageCurfewEnabled any // 消息宵禁开关 + State any // + CreatedAt *gtime.Time // + CreatedBy any // + UpdatedAt *gtime.Time // + UpdatedBy any // } diff --git a/server/internal/model/do/im_tenant_config.go b/server/internal/model/do/im_tenant_config.go deleted file mode 100644 index 63edd98..0000000 --- a/server/internal/model/do/im_tenant_config.go +++ /dev/null @@ -1,28 +0,0 @@ -// ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package do - -import ( - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" -) - -// ImTenantConfig is the golang structure of table im_tenant_config for DAO operations like Where/Data. -type ImTenantConfig struct { - g.Meta `orm:"table:im_tenant_config, do:true"` - Id any // - TenantId any // - MaxUsers any // 0 means unlimited - MaxGroups any // 0 means unlimited - MaxGroupMembers any // 0 means unlimited - DefaultPolicyGroupId any // Default user policy group - SecurityPolicyVersion any // Published security policy version - GlobalFriendEnabled any // Published global friend switch - MessageCurfewEnabled any // Published message curfew switch - CreatedAt *gtime.Time // - CreatedBy any // - UpdatedAt *gtime.Time // - UpdatedBy any // -} diff --git a/server/internal/model/entity/im_tenant.go b/server/internal/model/entity/im_tenant.go index dc12c0c..8305dc6 100644 --- a/server/internal/model/entity/im_tenant.go +++ b/server/internal/model/entity/im_tenant.go @@ -10,15 +10,28 @@ import ( // ImTenant is the golang structure for table im_tenant. type ImTenant struct { - Id int64 `json:"id" orm:"id" description:""` - Code string `json:"code" orm:"code" description:""` - Name string `json:"name" orm:"name" description:""` - TenantDesc string `json:"tenantDesc" orm:"tenant_desc" description:""` - DbName string `json:"dbName" orm:"db_name" description:""` - DbDsn string `json:"dbDsn" orm:"db_dsn" description:""` - State int `json:"state" orm:"state" description:""` - CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` - CreatedBy int64 `json:"createdBy" orm:"created_by" description:""` - UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` - UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:""` + Id int64 `json:"id" orm:"id" description:""` + Code string `json:"code" orm:"code" description:""` + Name string `json:"name" orm:"name" description:""` + TenantDesc string `json:"tenantDesc" orm:"tenant_desc" description:""` + MaxUsers uint64 `json:"maxUsers" orm:"max_users" description:"0表示不限制"` + MaxGroups uint64 `json:"maxGroups" orm:"max_groups" description:"0表示不限制"` + MaxGroupMembers uint `json:"maxGroupMembers" orm:"max_group_members" description:"0表示不限制"` + RealnameReviewPolicy int `json:"realnameReviewPolicy" orm:"realname_review_policy" description:"实名审核策略:0自动通过,1转人工审核"` + RealnameProviderMode string `json:"realnameProviderMode" orm:"realname_provider_mode" description:"实名认证模式"` + RealnameRequired int `json:"realnameRequired" orm:"realname_required" description:"是否强制实名"` + RealnameAllowSkip int `json:"realnameAllowSkip" orm:"realname_allow_skip" description:"是否允许跳过实名"` + RealnameRemindAfter string `json:"realnameRemindAfter" orm:"realname_remind_after" description:"实名提醒延迟"` + AliyunModerationEnabled int `json:"aliyunModerationEnabled" orm:"aliyun_moderation_enabled" description:"是否启用阿里云内容审核"` + LocalModerationEnabled int `json:"localModerationEnabled" orm:"local_moderation_enabled" description:"是否启用本地内容审核"` + MessageEncryptionEnabled int `json:"messageEncryptionEnabled" orm:"message_encryption_enabled" description:"是否启用消息加密"` + DefaultPolicyGroupId int64 `json:"defaultPolicyGroupId" orm:"default_policy_group_id" description:"默认用户策略组"` + SecurityPolicyVersion uint `json:"securityPolicyVersion" orm:"security_policy_version" description:"已发布安全策略版本"` + GlobalFriendEnabled int `json:"globalFriendEnabled" orm:"global_friend_enabled" description:"全局加好友开关"` + MessageCurfewEnabled int `json:"messageCurfewEnabled" orm:"message_curfew_enabled" description:"消息宵禁开关"` + State int `json:"state" orm:"state" description:""` + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` + CreatedBy int64 `json:"createdBy" orm:"created_by" description:""` + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` + UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:""` } diff --git a/server/internal/model/entity/im_tenant_config.go b/server/internal/model/entity/im_tenant_config.go deleted file mode 100644 index e8caeb9..0000000 --- a/server/internal/model/entity/im_tenant_config.go +++ /dev/null @@ -1,26 +0,0 @@ -// ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package entity - -import ( - "github.com/gogf/gf/v2/os/gtime" -) - -// ImTenantConfig is the golang structure for table im_tenant_config. -type ImTenantConfig struct { - Id int64 `json:"id" orm:"id" description:""` - TenantId int64 `json:"tenantId" orm:"tenant_id" description:""` - MaxUsers uint64 `json:"maxUsers" orm:"max_users" description:"0 means unlimited"` - MaxGroups uint64 `json:"maxGroups" orm:"max_groups" description:"0 means unlimited"` - MaxGroupMembers uint `json:"maxGroupMembers" orm:"max_group_members" description:"0 means unlimited"` - DefaultPolicyGroupId int64 `json:"defaultPolicyGroupId" orm:"default_policy_group_id" description:"Default user policy group"` - SecurityPolicyVersion uint `json:"securityPolicyVersion" orm:"security_policy_version" description:"Published security policy version"` - GlobalFriendEnabled int `json:"globalFriendEnabled" orm:"global_friend_enabled" description:"Published global friend switch"` - MessageCurfewEnabled int `json:"messageCurfewEnabled" orm:"message_curfew_enabled" description:"Published message curfew switch"` - CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` - CreatedBy int64 `json:"createdBy" orm:"created_by" description:""` - UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` - UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:""` -} diff --git a/server/internal/model/input/sysin/im_tenant.go b/server/internal/model/input/sysin/im_tenant.go index f4af04a..6d42fd8 100644 --- a/server/internal/model/input/sysin/im_tenant.go +++ b/server/internal/model/input/sysin/im_tenant.go @@ -20,32 +20,10 @@ import ( // ImTenantInsertFields 新增IM租户字段过滤 type ImTenantInsertFields struct { - Id int64 `json:"id" dc:"商户ID"` - Code string `json:"code" dc:"租户编码"` - Name string `json:"name" dc:"租户名称"` - TenantDesc string `json:"tenantDesc" dc:"租户描述"` - DbName string `json:"dbName" dc:"业务库名"` - DbDsn string `json:"dbDsn" dc:"独立连接串"` - State int `json:"state" dc:"状态"` - CreatedBy int64 `json:"createdBy" dc:"创建者"` - UpdatedBy int64 `json:"updatedBy" dc:"更新者"` -} - -// ImTenantConfigUpdateFields 修改IM租户配置字段过滤 -type ImTenantConfigUpdateFields struct { - MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"` - MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"` - MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"` - AliyunModerationEnabled int `json:"aliyunModerationEnabled" dc:"是否启用阿里云内容审核"` - LocalModerationEnabled int `json:"localModerationEnabled" dc:"是否启用本地内容审核"` - MessageEncryptionEnabled int `json:"messageEncryptionEnabled" dc:"是否启用消息加密"` - RealnameReviewPolicy int `json:"realnameReviewPolicy" dc:"实名审核策略"` - UpdatedBy int64 `json:"updatedBy" dc:"更新者"` -} - -// ImTenantConfigInsertFields 新增IM租户配置字段过滤 -type ImTenantConfigInsertFields struct { - TenantId int64 `json:"tenantId" dc:"租户ID"` + Id int64 `json:"id" dc:"商户ID"` + Code string `json:"code" dc:"商户编码"` + Name string `json:"name" dc:"商户名称"` + TenantDesc string `json:"tenantDesc" dc:"商户描述"` MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"` MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"` MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"` @@ -53,21 +31,16 @@ type ImTenantConfigInsertFields struct { LocalModerationEnabled int `json:"localModerationEnabled" dc:"是否启用本地内容审核"` MessageEncryptionEnabled int `json:"messageEncryptionEnabled" dc:"是否启用消息加密"` RealnameReviewPolicy int `json:"realnameReviewPolicy" dc:"实名审核策略"` + State int `json:"state" dc:"状态"` CreatedBy int64 `json:"createdBy" dc:"创建者"` + UpdatedBy int64 `json:"updatedBy" dc:"更新者"` } // ImTenantEditInp 修改/新增IM租户 type ImTenantEditInp struct { entity.ImTenant - MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"` - MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"` - MaxGroupMembers uint `json:"maxGroupMembers" dc:"最大群成员数"` - AliyunModerationEnabled int `json:"aliyunModerationEnabled" dc:"是否启用阿里云内容审核"` - LocalModerationEnabled int `json:"localModerationEnabled" dc:"是否启用本地内容审核"` - MessageEncryptionEnabled int `json:"messageEncryptionEnabled" dc:"是否启用消息加密"` - RealnameReviewPolicy int `json:"realnameReviewPolicy" dc:"实名审核策略"` - AdminUsername string `json:"adminUsername" dc:"新增商户时自动创建的后台管理员账号名"` - AdminPassword string `json:"adminPassword" dc:"新增商户时自动创建的后台管理员初始密码"` + AdminUsername string `json:"adminUsername" dc:"新增商户时自动创建的后台管理员账号名"` + AdminPassword string `json:"adminPassword" dc:"新增商户时自动创建的后台管理员初始密码"` } func (in *ImTenantEditInp) Filter(ctx context.Context) (err error) { @@ -119,19 +92,9 @@ 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:"最大群成员数"` - AliyunModerationEnabled int `json:"aliyunModerationEnabled" dc:"是否启用阿里云内容审核"` - LocalModerationEnabled int `json:"localModerationEnabled" dc:"是否启用本地内容审核"` - MessageEncryptionEnabled int `json:"messageEncryptionEnabled" dc:"是否启用消息加密"` - RealnameReviewPolicy int `json:"realnameReviewPolicy" dc:"实名审核策略"` - CreatedBySumma *hook.MemberSumma `json:"createdBySumma" dc:"创建者摘要信息"` - UpdatedBySumma *hook.MemberSumma `json:"updatedBySumma" dc:"更新者摘要信息"` - DbDsnConfigured bool `json:"dbDsnConfigured" dc:"是否配置独立连接串"` - TenantDbReachable bool `json:"tenantDbReachable" dc:"业务库是否可达"` - TenantDbCheckError string `json:"tenantDbCheckError" dc:"业务库检查错误"` + AdminUsername string `json:"adminUsername" dc:"商户管理员账号"` + CreatedBySumma *hook.MemberSumma `json:"createdBySumma" dc:"创建者摘要信息"` + UpdatedBySumma *hook.MemberSumma `json:"updatedBySumma" dc:"更新者摘要信息"` } // ImTenantListInp 获取IM租户列表 @@ -140,7 +103,6 @@ type ImTenantListInp struct { Id int64 `json:"id" dc:"ID"` Code string `json:"code" dc:"租户编码"` Name string `json:"name" dc:"租户名称"` - DbName string `json:"dbName" dc:"业务库名"` State *int `json:"state" dc:"状态"` CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"` } @@ -154,7 +116,6 @@ type ImTenantListModel struct { Code string `json:"code" dc:"租户编码"` Name string `json:"name" dc:"租户名称"` TenantDesc string `json:"tenantDesc" dc:"租户描述"` - DbName string `json:"dbName" dc:"业务库名"` State int `json:"state" dc:"状态"` MaxUsers uint64 `json:"maxUsers" dc:"最大用户数"` MaxGroups uint64 `json:"maxGroups" dc:"最大群组数"` @@ -163,7 +124,6 @@ type ImTenantListModel struct { LocalModerationEnabled int `json:"localModerationEnabled" dc:"是否启用本地内容审核"` MessageEncryptionEnabled int `json:"messageEncryptionEnabled" dc:"是否启用消息加密"` RealnameReviewPolicy int `json:"realnameReviewPolicy" dc:"实名审核策略"` - DbDsnConfigured bool `json:"dbDsnConfigured" dc:"是否配置独立连接串"` CreatedBy int64 `json:"createdBy" dc:"创建者"` CreatedBySumma *hook.MemberSumma `json:"createdBySumma" dc:"创建者摘要信息"` UpdatedBy int64 `json:"updatedBy" dc:"更新者"` @@ -205,8 +165,6 @@ type ImTenantUsageModel struct { Id int64 `json:"id" dc:"租户ID"` Code string `json:"code" dc:"租户编码"` Name string `json:"name" dc:"租户名称"` - DbName string `json:"dbName" dc:"业务库名"` - DbDsnConfigured bool `json:"dbDsnConfigured" dc:"是否配置独立连接串"` DbReachable bool `json:"dbReachable" dc:"业务库是否可达"` DbCheckError string `json:"dbCheckError" dc:"业务库检查错误"` SchemaVersion string `json:"schemaVersion" dc:"Schema版本"` diff --git a/server/storage/data/generate/im_message_encryption.sql b/server/storage/data/generate/im_message_encryption.sql index 219690d..9aa3423 100644 --- a/server/storage/data/generate/im_message_encryption.sql +++ b/server/storage/data/generate/im_message_encryption.sql @@ -6,9 +6,9 @@ SET @now := NOW(); SET @im_sql = IF( (SELECT COUNT(*) FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config' + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant' AND COLUMN_NAME = 'message_encryption_enabled') = 0, - 'ALTER TABLE `im_tenant_config` ADD COLUMN `message_encryption_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''是否启用消息加密'' AFTER `local_moderation_enabled`', + 'ALTER TABLE `im_tenant` ADD COLUMN `message_encryption_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''是否启用消息加密'' AFTER `local_moderation_enabled`', 'SELECT 1' ); PREPARE im_stmt FROM @im_sql; diff --git a/server/storage/data/generate/im_tenant_merge_config.sql b/server/storage/data/generate/im_tenant_merge_config.sql new file mode 100644 index 0000000..bb50e95 --- /dev/null +++ b/server/storage/data/generate/im_tenant_merge_config.sql @@ -0,0 +1,38 @@ +-- 第一阶段:将商户配置扩展并回填到 im_tenant。 +-- 必须先执行本文件,再发布新版 manager 与 IM Server。 +-- 本阶段保留 im_tenant_config、db_name、db_dsn,确保旧实例在滚动发布期间仍可运行。 + +ALTER TABLE `im_tenant` + ADD COLUMN `max_users` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0表示不限制' AFTER `tenant_desc`, + ADD COLUMN `max_groups` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0表示不限制' AFTER `max_users`, + ADD COLUMN `max_group_members` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0表示不限制' AFTER `max_groups`, + ADD COLUMN `realname_review_policy` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '实名审核策略:0自动通过,1转人工审核' AFTER `max_group_members`, + ADD COLUMN `realname_provider_mode` VARCHAR(32) NOT NULL DEFAULT 'id_card_2factor' COMMENT '实名认证模式' AFTER `realname_review_policy`, + ADD COLUMN `realname_required` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否强制实名' AFTER `realname_provider_mode`, + ADD COLUMN `realname_allow_skip` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许跳过实名' AFTER `realname_required`, + ADD COLUMN `realname_remind_after` VARCHAR(32) NOT NULL DEFAULT '24h' COMMENT '实名提醒延迟' AFTER `realname_allow_skip`, + ADD COLUMN `aliyun_moderation_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否启用阿里云内容审核' AFTER `realname_remind_after`, + ADD COLUMN `local_moderation_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否启用本地内容审核' AFTER `aliyun_moderation_enabled`, + ADD COLUMN `message_encryption_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否启用消息加密' AFTER `local_moderation_enabled`, + ADD COLUMN `default_policy_group_id` BIGINT DEFAULT NULL COMMENT '默认用户策略组' AFTER `message_encryption_enabled`, + ADD COLUMN `security_policy_version` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已发布安全策略版本' AFTER `default_policy_group_id`, + ADD COLUMN `global_friend_enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '全局加好友开关' AFTER `security_policy_version`, + ADD COLUMN `message_curfew_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '消息宵禁开关' AFTER `global_friend_enabled`; + +UPDATE `im_tenant` t +INNER JOIN `im_tenant_config` c ON c.`tenant_id`=t.`id` +SET t.`max_users`=c.`max_users`, + t.`max_groups`=c.`max_groups`, + t.`max_group_members`=c.`max_group_members`, + t.`realname_review_policy`=c.`realname_review_policy`, + t.`realname_provider_mode`=c.`realname_provider_mode`, + t.`realname_required`=c.`realname_required`, + t.`realname_allow_skip`=c.`realname_allow_skip`, + t.`realname_remind_after`=c.`realname_remind_after`, + t.`aliyun_moderation_enabled`=c.`aliyun_moderation_enabled`, + t.`local_moderation_enabled`=c.`local_moderation_enabled`, + t.`message_encryption_enabled`=c.`message_encryption_enabled`, + t.`default_policy_group_id`=c.`default_policy_group_id`, + t.`security_policy_version`=c.`security_policy_version`, + t.`global_friend_enabled`=c.`global_friend_enabled`, + t.`message_curfew_enabled`=c.`message_curfew_enabled`; diff --git a/server/storage/data/generate/im_tenant_merge_config_cleanup.sql b/server/storage/data/generate/im_tenant_merge_config_cleanup.sql new file mode 100644 index 0000000..0099463 --- /dev/null +++ b/server/storage/data/generate/im_tenant_merge_config_cleanup.sql @@ -0,0 +1,9 @@ +-- 第二阶段:新版 manager 与 IM Server 全部发布并验证后执行。 +-- 清理已废弃的商户配置表和按商户选库字段。 + +DROP TABLE `im_tenant_config`; + +ALTER TABLE `im_tenant` + DROP INDEX `idx_im_tenant_db_name`, + DROP COLUMN `db_name`, + DROP COLUMN `db_dsn`; diff --git a/web/src/views/imTenant/edit.vue b/web/src/views/imTenant/edit.vue index 9aa57fc..ef026fb 100644 --- a/web/src/views/imTenant/edit.vue +++ b/web/src/views/imTenant/edit.vue @@ -42,20 +42,6 @@ /> - - - - - - - - - - @@ -171,7 +157,7 @@ import { computed, ref, watch } from 'vue'; import { useDictStore } from '@/store/modules/dict'; import { Add, Edit, View } from '@/api/imTenant'; - import { isAdminSuper, newState, rules, State } from './model'; + import { newState, rules, State } from './model'; import { useProjectSettingStore } from '@/store/modules/projectSetting'; import { useMessage } from 'naive-ui'; import { adaModalWidth } from '@/utils/hotgo'; diff --git a/web/src/views/imTenant/index.vue b/web/src/views/imTenant/index.vue index c6c1469..4de83f5 100644 --- a/web/src/views/imTenant/index.vue +++ b/web/src/views/imTenant/index.vue @@ -52,7 +52,7 @@ import { useDictStore } from '@/store/modules/dict'; import { List, Restore, Status } from '@/api/imTenant'; import { PlusOutlined } from '@vicons/antd'; - import { buildColumns, isAdminSuper, loadOptions, schemas, State } from './model'; + import { buildColumns, loadOptions, schemas, State } from './model'; import { adaTableScrollX } from '@/utils/hotgo'; import Edit from './edit.vue'; import UsageModal from './usage.vue'; @@ -116,8 +116,7 @@ }, }); - // 业务库名列仅admin超管可见 - const columns = computed(() => buildColumns(isAdminSuper.value)); + const columns = computed(() => buildColumns()); const scrollX = computed(() => { return adaTableScrollX(columns.value, actionColumn.width); @@ -126,8 +125,7 @@ const [register, {}] = useForm({ gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' }, labelWidth: 90, - // 业务库名搜索项仅admin超管可见 - schemas: schemas.value.filter((item) => item.field !== 'dbName' || isAdminSuper.value), + schemas: schemas.value, }); // 加载表格数据 diff --git a/web/src/views/imTenant/model.ts b/web/src/views/imTenant/model.ts index 5ca2cf4..0c12da3 100644 --- a/web/src/views/imTenant/model.ts +++ b/web/src/views/imTenant/model.ts @@ -1,25 +1,18 @@ -import { computed, h, ref } from 'vue'; +import { h, ref } from 'vue'; import { NTag } from 'naive-ui'; import { cloneDeep } from 'lodash-es'; import { FormSchema } from '@/components/Form'; import { defRangeShortcuts } from '@/utils/dateUtil'; import { renderPopoverMemberSumma, MemberSumma } from '@/utils'; import { useDictStore } from '@/store/modules/dict'; -import { useUserStore } from '@/store/modules/user'; const dict = useDictStore(); -const userStore = useUserStore(); - -// 是否admin超管(业务库名/独立连接串仅超管可见可编辑) -export const isAdminSuper = computed(() => userStore.getUserInfo?.username === 'admin'); export class State { public id = 0; // ID public code = ''; // 商户编码 public name = ''; // 商户名称 public tenantDesc = ''; // 商户描述 - public dbName = ''; // 业务库名 - public dbDsn = ''; // 独立连接串 public adminUsername = ''; // 新增商户时创建的后台管理员账号 public adminPassword = ''; // 新增商户时创建的后台管理员初始密码 public state = 1; // 状态 @@ -30,7 +23,6 @@ export class State { public localModerationEnabled = 0; // 是否启用本地内容审核 public messageEncryptionEnabled = 0; // 是否启用消息加密 public realnameReviewPolicy = 0; // 实名审核策略 - public dbDsnConfigured = false; // 是否配置独立连接串 public createdBy = 0; // 创建者 public createdBySumma?: null | MemberSumma = null; // 创建者摘要信息 public updatedBy = 0; // 更新者 @@ -49,8 +41,6 @@ export class UsageState { public id = 0; public code = ''; public name = ''; - public dbName = ''; - public dbDsnConfigured = false; public dbReachable = false; public dbCheckError = ''; public schemaVersion = ''; @@ -126,14 +116,6 @@ export const schemas = ref([ placeholder: '请输入商户名称', }, }, - { - field: 'dbName', - component: 'NInput', - label: '业务库名', - componentProps: { - placeholder: '请输入业务库名', - }, - }, { field: 'state', component: 'NSelect', @@ -185,25 +167,6 @@ const baseColumns = [ return dict.getLabel('ImTenantStateOptions', row.state); }, }, - { - title: '业务库名', - key: 'dbName', - align: 'left', - width: 160, - }, - { - title: '独立连接串', - key: 'dbDsnConfigured', - align: 'left', - width: 120, - render(row: State) { - return h( - NTag, - { type: row.dbDsnConfigured ? 'success' : 'default', bordered: false }, - { default: () => (row.dbDsnConfigured ? '已配置' : '未配置') } - ); - }, - }, { title: '最大用户', key: 'maxUsers', @@ -297,12 +260,8 @@ const baseColumns = [ }, ]; -// 业务库名与独立连接串列仅admin超管可见 -export function buildColumns(isAdmin: boolean) { - if (isAdmin) { - return baseColumns; - } - return baseColumns.filter((item) => item.key !== 'dbName' && item.key !== 'dbDsnConfigured'); +export function buildColumns() { + return baseColumns; } // 加载字典数据选项 diff --git a/web/src/views/imTenant/usage.vue b/web/src/views/imTenant/usage.vue index c1ac175..fbee632 100644 --- a/web/src/views/imTenant/usage.vue +++ b/web/src/views/imTenant/usage.vue @@ -11,10 +11,6 @@ {{ usage.name || current.name }} {{ usage.code || current.code }} - {{ usage.dbName || '-' }} - - {{ usage.dbDsnConfigured ? '已配置' : '未配置' }} - {{ usage.dbReachable ? '可达' : '不可达' }}