复制项目
This commit is contained in:
439
internal/rpc/admin/system_config.go
Normal file
439
internal/rpc/admin/system_config.go
Normal file
@@ -0,0 +1,439 @@
|
||||
// Copyright © 2023 OpenIM open source community. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
chatdb "git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
"git.imall.cloud/openim/chat/pkg/common/mctx"
|
||||
adminpb "git.imall.cloud/openim/chat/pkg/protocol/admin"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
// ==================== 系统配置管理相关 RPC ====================
|
||||
|
||||
// convertValueToString 将任意类型的值转换为字符串(根据 ValueType)
|
||||
// 支持接收:字符串、数字、布尔值、JSON对象
|
||||
func convertValueToString(value interface{}, valueType int32) (string, error) {
|
||||
if value == nil {
|
||||
return "", errs.ErrArgs.WrapMsg("value cannot be nil")
|
||||
}
|
||||
|
||||
switch valueType {
|
||||
case chatdb.ConfigValueTypeString:
|
||||
// 字符串类型:直接转换为字符串
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return v, nil
|
||||
default:
|
||||
// 其他类型转为字符串
|
||||
return fmt.Sprintf("%v", v), nil
|
||||
}
|
||||
|
||||
case chatdb.ConfigValueTypeNumber:
|
||||
// 数字类型:转换为数字字符串
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
// 验证是否为有效数字
|
||||
if _, err := strconv.ParseFloat(v, 64); err != nil {
|
||||
return "", errs.ErrArgs.WrapMsg("value must be a valid number")
|
||||
}
|
||||
return v, nil
|
||||
case float64:
|
||||
return strconv.FormatFloat(v, 'f', -1, 64), nil
|
||||
case float32:
|
||||
return strconv.FormatFloat(float64(v), 'f', -1, 32), nil
|
||||
case int:
|
||||
return strconv.Itoa(v), nil
|
||||
case int64:
|
||||
return strconv.FormatInt(v, 10), nil
|
||||
case int32:
|
||||
return strconv.FormatInt(int64(v), 10), nil
|
||||
default:
|
||||
// 尝试转换为数字
|
||||
if num, ok := v.(float64); ok {
|
||||
return strconv.FormatFloat(num, 'f', -1, 64), nil
|
||||
}
|
||||
return "", errs.ErrArgs.WrapMsg("value must be a number")
|
||||
}
|
||||
|
||||
case chatdb.ConfigValueTypeBool:
|
||||
// 布尔类型:转换为 "true" 或 "false"
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
// 验证是否为有效的布尔字符串
|
||||
if _, err := strconv.ParseBool(v); err != nil {
|
||||
return "", errs.ErrArgs.WrapMsg("value must be 'true' or 'false'")
|
||||
}
|
||||
return v, nil
|
||||
case bool:
|
||||
return strconv.FormatBool(v), nil
|
||||
default:
|
||||
return "", errs.ErrArgs.WrapMsg("value must be a boolean")
|
||||
}
|
||||
|
||||
case chatdb.ConfigValueTypeJSON:
|
||||
// JSON类型:转换为 JSON 字符串
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
// 验证是否为有效的 JSON
|
||||
var js interface{}
|
||||
if err := json.Unmarshal([]byte(v), &js); err != nil {
|
||||
return "", errs.ErrArgs.WrapMsg("value must be a valid JSON string")
|
||||
}
|
||||
return v, nil
|
||||
default:
|
||||
// 将对象序列化为 JSON 字符串
|
||||
jsonBytes, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return "", errs.ErrArgs.WrapMsg("value must be a valid JSON object")
|
||||
}
|
||||
return string(jsonBytes), nil
|
||||
}
|
||||
|
||||
default:
|
||||
return "", errs.ErrArgs.WrapMsg("invalid value type")
|
||||
}
|
||||
}
|
||||
|
||||
// convertValueFromString 将字符串值转换为对应类型(用于返回给前端)
|
||||
func convertValueFromString(value string, valueType int32) (interface{}, error) {
|
||||
switch valueType {
|
||||
case chatdb.ConfigValueTypeString:
|
||||
return value, nil
|
||||
case chatdb.ConfigValueTypeNumber:
|
||||
// 尝试解析为数字
|
||||
if num, err := strconv.ParseFloat(value, 64); err == nil {
|
||||
// 如果是整数,返回整数;否则返回浮点数
|
||||
if num == float64(int64(num)) {
|
||||
return int64(num), nil
|
||||
}
|
||||
return num, nil
|
||||
}
|
||||
return nil, errs.ErrArgs.WrapMsg("invalid number format")
|
||||
case chatdb.ConfigValueTypeBool:
|
||||
return strconv.ParseBool(value)
|
||||
case chatdb.ConfigValueTypeJSON:
|
||||
var js interface{}
|
||||
if err := json.Unmarshal([]byte(value), &js); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return js, nil
|
||||
default:
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
// validateValueByType 根据 ValueType 验证 Value 的格式
|
||||
func validateValueByType(value string, valueType int32) error {
|
||||
switch valueType {
|
||||
case chatdb.ConfigValueTypeString:
|
||||
// 字符串类型:任何字符串都可以,无需验证
|
||||
return nil
|
||||
case chatdb.ConfigValueTypeNumber:
|
||||
// 数字类型:必须是有效的数字字符串
|
||||
if value == "" {
|
||||
return errs.ErrArgs.WrapMsg("value cannot be empty for number type")
|
||||
}
|
||||
_, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return errs.ErrArgs.WrapMsg("value must be a valid number string")
|
||||
}
|
||||
return nil
|
||||
case chatdb.ConfigValueTypeBool:
|
||||
// 布尔类型:必须是 "true" 或 "false"
|
||||
if value == "" {
|
||||
return errs.ErrArgs.WrapMsg("value cannot be empty for bool type")
|
||||
}
|
||||
_, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return errs.ErrArgs.WrapMsg("value must be 'true' or 'false' for bool type")
|
||||
}
|
||||
return nil
|
||||
case chatdb.ConfigValueTypeJSON:
|
||||
// JSON类型:必须是有效的 JSON 字符串
|
||||
if value == "" {
|
||||
return errs.ErrArgs.WrapMsg("value cannot be empty for JSON type")
|
||||
}
|
||||
var js interface{}
|
||||
if err := json.Unmarshal([]byte(value), &js); err != nil {
|
||||
return errs.ErrArgs.WrapMsg("value must be a valid JSON string")
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return errs.ErrArgs.WrapMsg("invalid value type")
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSystemConfig 创建系统配置
|
||||
func (o *adminServer) CreateSystemConfig(ctx context.Context, req *adminpb.CreateSystemConfigReq) (*adminpb.CreateSystemConfigResp, error) {
|
||||
// 检查管理员权限
|
||||
if _, err := mctx.CheckAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 验证必填字段
|
||||
if req.Key == "" {
|
||||
return nil, errs.ErrArgs.WrapMsg("config key is required")
|
||||
}
|
||||
|
||||
// 检查配置键是否已存在
|
||||
_, err := o.ChatDatabase.GetSystemConfig(ctx, req.Key)
|
||||
if err == nil {
|
||||
return nil, errs.ErrDuplicateKey.WrapMsg("config key already exists")
|
||||
}
|
||||
|
||||
// 创建配置对象
|
||||
config := &chatdb.SystemConfig{
|
||||
Key: req.Key,
|
||||
Title: req.Title,
|
||||
Value: req.Value,
|
||||
ValueType: req.ValueType,
|
||||
Description: req.Description,
|
||||
Enabled: req.Enabled,
|
||||
ShowInApp: req.ShowInApp,
|
||||
CreateTime: time.Now(),
|
||||
UpdateTime: time.Now(),
|
||||
}
|
||||
|
||||
// 如果未设置值类型,默认为字符串类型
|
||||
if config.ValueType == 0 {
|
||||
config.ValueType = chatdb.ConfigValueTypeString
|
||||
}
|
||||
|
||||
// 根据 ValueType 验证 Value 的格式
|
||||
if err := validateValueByType(config.Value, config.ValueType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
if err := o.ChatDatabase.CreateSystemConfig(ctx, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &adminpb.CreateSystemConfigResp{}, nil
|
||||
}
|
||||
|
||||
// GetSystemConfig 获取系统配置详情
|
||||
func (o *adminServer) GetSystemConfig(ctx context.Context, req *adminpb.GetSystemConfigReq) (*adminpb.GetSystemConfigResp, error) {
|
||||
// 检查管理员权限
|
||||
if _, err := mctx.CheckAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
config, err := o.ChatDatabase.GetSystemConfig(ctx, req.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &adminpb.GetSystemConfigResp{
|
||||
Config: convertSystemConfigToProto(config),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAllSystemConfigs 获取所有系统配置(分页)
|
||||
func (o *adminServer) GetAllSystemConfigs(ctx context.Context, req *adminpb.GetAllSystemConfigsReq) (*adminpb.GetAllSystemConfigsResp, error) {
|
||||
// 检查管理员权限
|
||||
if _, err := mctx.CheckAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取配置列表
|
||||
total, configs, err := o.ChatDatabase.GetAllSystemConfigs(ctx, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
configInfos := make([]*adminpb.SystemConfigInfo, 0, len(configs))
|
||||
for _, config := range configs {
|
||||
configInfos = append(configInfos, convertSystemConfigToProto(config))
|
||||
}
|
||||
|
||||
return &adminpb.GetAllSystemConfigsResp{
|
||||
Total: uint32(total),
|
||||
List: configInfos,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateSystemConfig 更新系统配置
|
||||
func (o *adminServer) UpdateSystemConfig(ctx context.Context, req *adminpb.UpdateSystemConfigReq) (*adminpb.UpdateSystemConfigResp, error) {
|
||||
// 检查管理员权限
|
||||
if _, err := mctx.CheckAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 验证配置是否存在
|
||||
_, err := o.ChatDatabase.GetSystemConfig(ctx, req.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取当前配置,用于验证
|
||||
currentConfig, err := o.ChatDatabase.GetSystemConfig(ctx, req.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 确定要使用的 ValueType(如果更新了 ValueType,使用新的;否则使用当前的)
|
||||
newValueType := currentConfig.ValueType
|
||||
if req.ValueType != nil {
|
||||
newValueType = req.ValueType.Value
|
||||
}
|
||||
|
||||
// 确定要使用的 Value(如果更新了 Value,使用新的;否则使用当前的)
|
||||
newValue := currentConfig.Value
|
||||
if req.Value != nil {
|
||||
newValue = req.Value.Value
|
||||
// 如果更新了 Value,需要根据 ValueType 验证
|
||||
if err := validateValueByType(newValue, newValueType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if req.ValueType != nil {
|
||||
// 如果只更新了 ValueType,需要验证当前 Value 是否符合新的 ValueType
|
||||
if err := validateValueByType(currentConfig.Value, newValueType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 构建更新数据
|
||||
updateData := make(map[string]any)
|
||||
if req.Title != nil {
|
||||
updateData["title"] = req.Title.Value
|
||||
}
|
||||
if req.Value != nil {
|
||||
updateData["value"] = req.Value.Value
|
||||
}
|
||||
if req.ValueType != nil {
|
||||
updateData["value_type"] = req.ValueType.Value
|
||||
}
|
||||
if req.Description != nil {
|
||||
updateData["description"] = req.Description.Value
|
||||
}
|
||||
if req.Enabled != nil {
|
||||
updateData["enabled"] = req.Enabled.Value
|
||||
}
|
||||
if req.ShowInApp != nil {
|
||||
updateData["show_in_app"] = req.ShowInApp.Value
|
||||
}
|
||||
|
||||
// 更新配置
|
||||
if err := o.ChatDatabase.UpdateSystemConfig(ctx, req.Key, updateData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &adminpb.UpdateSystemConfigResp{}, nil
|
||||
}
|
||||
|
||||
// UpdateSystemConfigValue 更新系统配置值
|
||||
func (o *adminServer) UpdateSystemConfigValue(ctx context.Context, req *adminpb.UpdateSystemConfigValueReq) (*adminpb.UpdateSystemConfigValueResp, error) {
|
||||
// 检查管理员权限
|
||||
if _, err := mctx.CheckAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取当前配置,用于获取 ValueType 进行验证
|
||||
config, err := o.ChatDatabase.GetSystemConfig(ctx, req.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 根据当前 ValueType 验证新 Value 的格式
|
||||
if err := validateValueByType(req.Value, config.ValueType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 更新配置值
|
||||
if err := o.ChatDatabase.UpdateSystemConfigValue(ctx, req.Key, req.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &adminpb.UpdateSystemConfigValueResp{}, nil
|
||||
}
|
||||
|
||||
// UpdateSystemConfigEnabled 更新系统配置启用状态
|
||||
func (o *adminServer) UpdateSystemConfigEnabled(ctx context.Context, req *adminpb.UpdateSystemConfigEnabledReq) (*adminpb.UpdateSystemConfigEnabledResp, error) {
|
||||
// 检查管理员权限
|
||||
if _, err := mctx.CheckAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 更新启用状态
|
||||
if err := o.ChatDatabase.UpdateSystemConfigEnabled(ctx, req.Key, req.Enabled); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &adminpb.UpdateSystemConfigEnabledResp{}, nil
|
||||
}
|
||||
|
||||
// DeleteSystemConfig 删除系统配置
|
||||
func (o *adminServer) DeleteSystemConfig(ctx context.Context, req *adminpb.DeleteSystemConfigReq) (*adminpb.DeleteSystemConfigResp, error) {
|
||||
// 检查管理员权限
|
||||
if _, err := mctx.CheckAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 删除配置
|
||||
if err := o.ChatDatabase.DeleteSystemConfig(ctx, req.Keys); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &adminpb.DeleteSystemConfigResp{}, nil
|
||||
}
|
||||
|
||||
// GetEnabledSystemConfigs 获取所有已启用的配置
|
||||
func (o *adminServer) GetEnabledSystemConfigs(ctx context.Context, req *adminpb.GetEnabledSystemConfigsReq) (*adminpb.GetEnabledSystemConfigsResp, error) {
|
||||
// 检查管理员权限
|
||||
if _, err := mctx.CheckAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取已启用的配置
|
||||
configs, err := o.ChatDatabase.GetEnabledSystemConfigs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
configInfos := make([]*adminpb.SystemConfigInfo, 0, len(configs))
|
||||
for _, config := range configs {
|
||||
configInfos = append(configInfos, convertSystemConfigToProto(config))
|
||||
}
|
||||
|
||||
return &adminpb.GetEnabledSystemConfigsResp{
|
||||
List: configInfos,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// convertSystemConfigToProto 将数据库模型转换为 protobuf 消息
|
||||
func convertSystemConfigToProto(config *chatdb.SystemConfig) *adminpb.SystemConfigInfo {
|
||||
return &adminpb.SystemConfigInfo{
|
||||
Key: config.Key,
|
||||
Title: config.Title,
|
||||
Value: config.Value,
|
||||
ValueType: config.ValueType,
|
||||
Description: config.Description,
|
||||
Enabled: config.Enabled,
|
||||
ShowInApp: config.ShowInApp,
|
||||
CreateTime: config.CreateTime.UnixMilli(),
|
||||
UpdateTime: config.UpdateTime.UnixMilli(),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user