Files
chat-deploy/internal/rpc/admin/system_config.go
kim.dev.6789 b7f8db7d08 复制项目
2026-01-14 22:35:45 +08:00

440 lines
13 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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(),
}
}