feat: support shared tenant database sharding
This commit is contained in:
@@ -222,13 +222,23 @@ func (s *sSysImTenant) saveTenant(ctx context.Context, in *sysin.ImTenantEditInp
|
||||
super := isSuperTenantAdmin(ctx)
|
||||
isCreate := in.Id == 0
|
||||
tenantID := in.Id
|
||||
|
||||
// 业务库名与独立连接串仅超管可维护,非超管提交一律强制忽略
|
||||
if !super {
|
||||
requestedState := in.State
|
||||
if isCreate {
|
||||
if requestedState != consts.ImTenantStateActive && requestedState != consts.ImTenantStateSuspended {
|
||||
return gerror.New("新商户创建完成后的状态只能是正常或已停用")
|
||||
}
|
||||
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 = ""
|
||||
}
|
||||
|
||||
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 {
|
||||
return
|
||||
@@ -255,16 +265,19 @@ func (s *sSysImTenant) saveTenant(ctx context.Context, in *sysin.ImTenantEditInp
|
||||
return s.saveConfig(ctx, in, userId)
|
||||
}
|
||||
|
||||
in.Id, err = nextTenantID(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
in.CreatedBy = userId
|
||||
in.UpdatedBy = userId
|
||||
id, err := s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
_, err = tx.Model(dao.ImTenant.Table()).Safe().Ctx(ctx).
|
||||
Fields(sysin.ImTenantInsertFields{}).
|
||||
Data(in).InsertAndGetId()
|
||||
Data(in).Insert()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "新增IM租户失败,请稍后重试!")
|
||||
}
|
||||
tenantID = id
|
||||
in.Id = id
|
||||
tenantID = in.Id
|
||||
return s.saveConfig(ctx, in, userId)
|
||||
})
|
||||
if err != nil {
|
||||
@@ -277,13 +290,22 @@ func (s *sSysImTenant) saveTenant(ctx context.Context, in *sysin.ImTenantEditInp
|
||||
s.rollbackTenantCreation(ctx, tenantID)
|
||||
return err
|
||||
}
|
||||
if err = provisionTenantMessageTables(ctx, tenantID); err != nil {
|
||||
return gerror.Wrap(err, "商户已登记为初始化中,但业务分表创建失败")
|
||||
}
|
||||
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).WherePri(tenantID).Data(g.Map{
|
||||
dao.ImTenant.Columns().State: requestedState,
|
||||
dao.ImTenant.Columns().UpdatedBy: contexts.GetUserId(ctx),
|
||||
}).Update(); err != nil {
|
||||
return gerror.Wrap(err, "商户分表已创建,但更新商户状态失败")
|
||||
}
|
||||
in.State = requestedState
|
||||
} 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)
|
||||
return nil
|
||||
}
|
||||
@@ -550,6 +572,11 @@ func (s *sSysImTenant) Status(ctx context.Context, in *sysin.ImTenantStatusInp)
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
if in.State == consts.ImTenantStateActive {
|
||||
if err = provisionTenantMessageTables(ctx, in.Id); err != nil {
|
||||
return gerror.Wrap(err, "启用商户前创建业务分表失败")
|
||||
}
|
||||
}
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{
|
||||
dao.ImTenant.Columns().State: in.State,
|
||||
dao.ImTenant.Columns().UpdatedBy: contexts.GetUserId(ctx),
|
||||
@@ -601,18 +628,19 @@ func (s *sSysImTenant) Usage(ctx context.Context, in *sysin.ImTenantUsageInp) (r
|
||||
res.DbReachable = true
|
||||
|
||||
res.SchemaVersion = s.queryScalarString(ctx, db, "kvmeta.version", "SELECT value FROM kvmeta WHERE key_='version' LIMIT 1", &res.Warnings)
|
||||
res.UserCount = s.queryScalarInt64(ctx, db, "im_users.total", "SELECT COUNT(*) FROM im_users", &res.Warnings)
|
||||
res.ActiveUserCount = s.queryScalarInt64(ctx, db, "im_users.active", "SELECT COUNT(*) FROM im_users WHERE state=0", &res.Warnings)
|
||||
res.GroupCount = s.queryScalarInt64(ctx, db, "im_group_settings", "SELECT COUNT(*) FROM im_group_settings WHERE group_status<>'dissolved'", &res.Warnings)
|
||||
res.DeviceCount = s.queryScalarInt64(ctx, db, "im_devices", "SELECT COUNT(*) FROM im_devices", &res.Warnings)
|
||||
res.PushTokenCount = s.queryScalarInt64(ctx, db, "im_device_push_tokens", "SELECT COUNT(*) FROM im_device_push_tokens", &res.Warnings)
|
||||
res.InviteUseCount = s.queryScalarInt64(ctx, db, "im_registration_invite_uses", "SELECT COUNT(*) FROM im_registration_invite_uses", &res.Warnings)
|
||||
res.OutboxPendingCount = s.queryScalarInt64(ctx, db, "im_business_events_outbox", "SELECT COUNT(*) FROM im_business_events_outbox WHERE status='pending'", &res.Warnings)
|
||||
tenantId := tenant.Id
|
||||
res.UserCount = s.queryScalarInt64(ctx, db, "im_users.total", "SELECT COUNT(*) FROM im_users WHERE tenant_id=?", &res.Warnings, tenantId)
|
||||
res.ActiveUserCount = s.queryScalarInt64(ctx, db, "im_users.active", "SELECT COUNT(*) FROM im_users WHERE tenant_id=? AND state=0", &res.Warnings, tenantId)
|
||||
res.GroupCount = s.queryScalarInt64(ctx, db, "im_group_settings", "SELECT COUNT(*) FROM im_group_settings WHERE tenant_id=? AND group_status<>'dissolved'", &res.Warnings, tenantId)
|
||||
res.DeviceCount = s.queryScalarInt64(ctx, db, "im_devices", "SELECT COUNT(*) FROM im_devices WHERE tenant_id=?", &res.Warnings, tenantId)
|
||||
res.PushTokenCount = s.queryScalarInt64(ctx, db, "im_device_push_tokens", "SELECT COUNT(*) FROM im_device_push_tokens WHERE tenant_id=?", &res.Warnings, tenantId)
|
||||
res.InviteUseCount = s.queryScalarInt64(ctx, db, "im_registration_invite_uses", "SELECT COUNT(*) FROM im_registration_invite_uses WHERE tenant_id=?", &res.Warnings, tenantId)
|
||||
res.OutboxPendingCount = s.queryScalarInt64(ctx, db, "im_business_events_outbox", "SELECT COUNT(*) FROM im_business_events_outbox WHERE tenant_id=? AND status='pending'", &res.Warnings, tenantId)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysImTenant) queryScalarInt64(ctx context.Context, db gdb.DB, label, sql string, warnings *[]string) int64 {
|
||||
value, err := db.GetValue(ctx, sql)
|
||||
func (s *sSysImTenant) queryScalarInt64(ctx context.Context, db gdb.DB, label, sql string, warnings *[]string, args ...any) int64 {
|
||||
value, err := db.GetValue(ctx, sql, args...)
|
||||
if err != nil {
|
||||
*warnings = append(*warnings, label+": "+err.Error())
|
||||
return 0
|
||||
@@ -687,6 +715,9 @@ func (s *sSysImTenant) Restore(ctx context.Context, in *sysin.ImTenantLifecycleI
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
if err = provisionTenantMessageTables(ctx, in.Id); err != nil {
|
||||
return gerror.Wrap(err, "恢复商户前创建业务分表失败")
|
||||
}
|
||||
if _, err = s.Model(ctx).
|
||||
WherePri(in.Id).
|
||||
WhereIn(dao.ImTenant.Columns().State, []int{consts.ImTenantStateSuspended, consts.ImTenantStateDeleting}).
|
||||
|
||||
Reference in New Issue
Block a user