复制项目

This commit is contained in:
kim.dev.6789
2026-01-14 22:35:45 +08:00
parent 305d526110
commit b7f8db7d08
297 changed files with 81784 additions and 0 deletions

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