首次提交:初始化项目代码
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
// Package config 配置加载模块,基于 Viper 读取 YAML 配置并提供默认值
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultAdminPath = "/admin" // 默认后台管理路径
|
||||
DefaultMaxLoginAttempts = 5 // 默认最大登录尝试次数
|
||||
DefaultLockoutDuration = 15 // 默认锁定时长(分钟)
|
||||
DefaultServerHost = "0.0.0.0" // 默认服务监听地址
|
||||
DefaultServerPort = 8090 // 默认服务端口
|
||||
DefaultServerMode = "release" // 默认运行模式
|
||||
DefaultDatabaseType = "sqlite" // 默认数据库类型
|
||||
DefaultDatabasePath = "./data/resume.db" // 默认数据库文件路径
|
||||
DefaultLogLevel = "info" // 默认日志级别
|
||||
DefaultTemplateDefault = "modern" // 默认简历模板
|
||||
)
|
||||
|
||||
// Config 全局配置结构体
|
||||
type Config struct {
|
||||
Server ServerConfig `mapstructure:"server"` // 服务配置
|
||||
Database DatabaseConfig `mapstructure:"database"` // 数据库配置
|
||||
Auth AuthConfig `mapstructure:"auth"` // 认证配置
|
||||
Template TemplateConfig `mapstructure:"template"` // 模板配置
|
||||
Log LogConfig `mapstructure:"log"` // 日志配置
|
||||
AI AIConfig `mapstructure:"ai"` // AI配置
|
||||
}
|
||||
|
||||
// ServerConfig 服务配置
|
||||
type ServerConfig struct {
|
||||
Host string `mapstructure:"host"` // 监听地址
|
||||
Port int `mapstructure:"port"` // 监听端口
|
||||
Mode string `mapstructure:"mode"` // 运行模式(debug/release)
|
||||
Debug bool `mapstructure:"debug"` // 是否开启调试
|
||||
}
|
||||
|
||||
// DatabaseConfig 数据库配置
|
||||
type DatabaseConfig struct {
|
||||
Type string `mapstructure:"type"` // 数据库类型
|
||||
Path string `mapstructure:"path"` // 数据库文件路径
|
||||
Password string `mapstructure:"password"` // 数据库加密密码
|
||||
}
|
||||
|
||||
// AuthConfig 认证配置
|
||||
type AuthConfig struct {
|
||||
Enabled bool `mapstructure:"enabled"` // 是否启用认证
|
||||
Username string `mapstructure:"username"` // 管理员用户名
|
||||
Password string `mapstructure:"password"` // 管理员密码
|
||||
AdminPath string `mapstructure:"admin_path"` // 后台路径
|
||||
MaxLoginAttempts int `mapstructure:"max_login_attempts"` // 最大登录尝试
|
||||
LockoutDuration int `mapstructure:"lockout_duration"` // 锁定时长(分钟)
|
||||
}
|
||||
|
||||
// TemplateConfig 模板配置
|
||||
type TemplateConfig struct {
|
||||
Default string `mapstructure:"default"` // 默认模板名称
|
||||
}
|
||||
|
||||
// LogConfig 日志配置
|
||||
type LogConfig struct {
|
||||
Level string `mapstructure:"level"` // 日志级别
|
||||
Output string `mapstructure:"output"` // 输出文件路径
|
||||
}
|
||||
|
||||
// AIConfig AI 大模型配置
|
||||
type AIConfig struct {
|
||||
Provider string `mapstructure:"provider"` // 默认供应商
|
||||
Enabled bool `mapstructure:"enabled"` // 是否启用
|
||||
Spark SparkConfig `mapstructure:"spark"` // 讯飞星火配置
|
||||
Tongyi TongyiConfig `mapstructure:"tongyi"` // 通义千问配置
|
||||
Doubao DoubaoConfig `mapstructure:"doubao"` // 豆包配置
|
||||
DeepSeek DeepSeekConfig `mapstructure:"deepseek"` // DeepSeek配置
|
||||
}
|
||||
|
||||
// SparkConfig 讯飞星火配置
|
||||
type SparkConfig struct {
|
||||
APPID string `mapstructure:"app_id"` // APP ID
|
||||
APIKey string `mapstructure:"api_key"` // API Key
|
||||
APISecret string `mapstructure:"api_secret"` // API Secret
|
||||
APIPassword string `mapstructure:"api_password"` // API 密码(HTTP接口使用)
|
||||
BaseURL string `mapstructure:"base_url"` // 接口地址
|
||||
Model string `mapstructure:"model"` // 模型版本
|
||||
MaxTokens int `mapstructure:"max_tokens"` // 最大token数
|
||||
Timeout int `mapstructure:"timeout"` // 超时时间(秒)
|
||||
WebSearch bool `mapstructure:"web_search"` // 是否启用联网搜索
|
||||
ShowRefLabel bool `mapstructure:"show_ref_label"` // 是否显示引用标签
|
||||
}
|
||||
|
||||
// TongyiConfig 通义千问配置
|
||||
type TongyiConfig struct {
|
||||
APIKey string `mapstructure:"api_key"` // API Key
|
||||
BaseURL string `mapstructure:"base_url"` // 接口地址
|
||||
Model string `mapstructure:"model"` // 模型名称
|
||||
MaxTokens int `mapstructure:"max_tokens"` // 最大token数
|
||||
Timeout int `mapstructure:"timeout"` // 超时时间(秒)
|
||||
}
|
||||
|
||||
// DoubaoConfig 豆包配置
|
||||
type DoubaoConfig struct {
|
||||
APIKey string `mapstructure:"api_key"` // API Key
|
||||
BaseURL string `mapstructure:"base_url"` // 接口地址
|
||||
Model string `mapstructure:"model"` // 模型名称
|
||||
MaxTokens int `mapstructure:"max_tokens"` // 最大token数
|
||||
Timeout int `mapstructure:"timeout"` // 超时时间(秒)
|
||||
}
|
||||
|
||||
// DeepSeekConfig DeepSeek 配置
|
||||
type DeepSeekConfig struct {
|
||||
APIKey string `mapstructure:"api_key"` // API Key
|
||||
BaseURL string `mapstructure:"base_url"` // 接口地址
|
||||
Model string `mapstructure:"model"` // 模型名称
|
||||
MaxTokens int `mapstructure:"max_tokens"` // 最大token数
|
||||
Timeout int `mapstructure:"timeout"` // 超时时间(秒)
|
||||
}
|
||||
|
||||
// setDefaults 设置 Viper 默认值
|
||||
// @author sunct
|
||||
func setDefaults() {
|
||||
viper.SetDefault("server.host", DefaultServerHost)
|
||||
viper.SetDefault("server.port", DefaultServerPort)
|
||||
viper.SetDefault("server.mode", DefaultServerMode)
|
||||
viper.SetDefault("server.debug", false)
|
||||
|
||||
viper.SetDefault("database.type", DefaultDatabaseType)
|
||||
viper.SetDefault("database.path", DefaultDatabasePath)
|
||||
|
||||
viper.SetDefault("auth.enabled", true)
|
||||
viper.SetDefault("auth.admin_path", DefaultAdminPath)
|
||||
viper.SetDefault("auth.max_login_attempts", DefaultMaxLoginAttempts)
|
||||
viper.SetDefault("auth.lockout_duration", DefaultLockoutDuration)
|
||||
|
||||
viper.SetDefault("template.default", DefaultTemplateDefault)
|
||||
|
||||
viper.SetDefault("log.level", DefaultLogLevel)
|
||||
}
|
||||
|
||||
// LoadConfig 加载并解析配置文件,支持多路径查找
|
||||
// @return *Config 配置实例
|
||||
// @return error 加载错误
|
||||
// @author sunct
|
||||
func LoadConfig() (*Config, error) {
|
||||
setDefaults()
|
||||
|
||||
viper.SetConfigName("config")
|
||||
viper.SetConfigType("yaml")
|
||||
|
||||
viper.AddConfigPath("./config")
|
||||
viper.AddConfigPath(".")
|
||||
viper.AddConfigPath("../config")
|
||||
viper.AddConfigPath("../")
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
var configFileNotFoundError viper.ConfigFileNotFoundError
|
||||
if !errors.As(err, &configFileNotFoundError) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
if err := viper.Unmarshal(&cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := validateAndNormalize(&cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// validateAndNormalize 校验并规范化配置,补全默认值
|
||||
// @param cfg 配置实例
|
||||
// @return error 校验错误
|
||||
// @author sunct
|
||||
func validateAndNormalize(cfg *Config) error {
|
||||
if cfg.Server.Port <= 0 || cfg.Server.Port > 65535 {
|
||||
cfg.Server.Port = DefaultServerPort
|
||||
}
|
||||
if cfg.Server.Host == "" {
|
||||
cfg.Server.Host = DefaultServerHost
|
||||
}
|
||||
if cfg.Server.Mode == "" {
|
||||
cfg.Server.Mode = DefaultServerMode
|
||||
}
|
||||
|
||||
if cfg.Database.Type == "" {
|
||||
cfg.Database.Type = DefaultDatabaseType
|
||||
}
|
||||
if cfg.Database.Path == "" {
|
||||
cfg.Database.Path = DefaultDatabasePath
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(cfg.Auth.AdminPath, "/") {
|
||||
cfg.Auth.AdminPath = "/" + cfg.Auth.AdminPath
|
||||
}
|
||||
if cfg.Auth.AdminPath == "/" {
|
||||
cfg.Auth.AdminPath = DefaultAdminPath
|
||||
}
|
||||
if cfg.Auth.MaxLoginAttempts <= 0 {
|
||||
cfg.Auth.MaxLoginAttempts = DefaultMaxLoginAttempts
|
||||
}
|
||||
if cfg.Auth.LockoutDuration <= 0 {
|
||||
cfg.Auth.LockoutDuration = DefaultLockoutDuration
|
||||
}
|
||||
|
||||
if cfg.Template.Default == "" {
|
||||
cfg.Template.Default = DefaultTemplateDefault
|
||||
}
|
||||
|
||||
if cfg.Log.Level == "" {
|
||||
cfg.Log.Level = DefaultLogLevel
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
RequestIdKey = "request-id"
|
||||
AppNameKey = "app-name"
|
||||
RequestRouteKey = "request-route"
|
||||
ClientIP = "client-ip"
|
||||
)
|
||||
@@ -0,0 +1,76 @@
|
||||
package idgen
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
generator *FlakeIDGenerator
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
type FlakeIDGenerator struct {
|
||||
mu sync.Mutex
|
||||
lastTs int64
|
||||
seq int64
|
||||
nodeID int64
|
||||
}
|
||||
|
||||
func NewFlakeIDGenerator(nodeID int64) *FlakeIDGenerator {
|
||||
return &FlakeIDGenerator{
|
||||
nodeID: nodeID,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *FlakeIDGenerator) GetID() (string, error) {
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
|
||||
now := time.Now().UnixNano() / 1e6
|
||||
|
||||
if now < g.lastTs {
|
||||
return "", fmt.Errorf("clock moved backwards")
|
||||
}
|
||||
|
||||
if now == g.lastTs {
|
||||
g.seq++
|
||||
if g.seq >= 1024 {
|
||||
for now <= g.lastTs {
|
||||
now = time.Now().UnixNano() / 1e6
|
||||
}
|
||||
g.seq = 0
|
||||
}
|
||||
} else {
|
||||
g.seq = 0
|
||||
}
|
||||
|
||||
g.lastTs = now
|
||||
|
||||
id := (now << 22) | (g.nodeID << 10) | g.seq
|
||||
return fmt.Sprintf("%d", id), nil
|
||||
}
|
||||
|
||||
func GetID() (string, error) {
|
||||
once.Do(func() {
|
||||
nodeID := int64(1)
|
||||
b := make([]byte, 2)
|
||||
_, err := rand.Read(b)
|
||||
if err == nil {
|
||||
nodeID = int64(b[0])<<8 | int64(b[1])
|
||||
nodeID &= 0x3FF
|
||||
}
|
||||
generator = NewFlakeIDGenerator(nodeID)
|
||||
})
|
||||
return generator.GetID()
|
||||
}
|
||||
|
||||
func MustGetID() string {
|
||||
id, err := GetID()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
}
|
||||
return id
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
// Package logger 日志模块,基于 logrus 封装统一的日志接口
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"resume-platform/pkg/constant"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultLogger *logrus.Logger // 默认日志实例
|
||||
)
|
||||
|
||||
// Config 日志配置
|
||||
type Config struct {
|
||||
Level string // 日志级别
|
||||
Output string // 输出文件路径
|
||||
}
|
||||
|
||||
// Init 初始化日志系统
|
||||
// @param cfg 日志配置
|
||||
// @return error 初始化错误
|
||||
// @author sunct
|
||||
func Init(cfg Config) error {
|
||||
l := logrus.New()
|
||||
|
||||
level, err := logrus.ParseLevel(cfg.Level)
|
||||
if err != nil {
|
||||
level = logrus.InfoLevel
|
||||
}
|
||||
l.SetLevel(level)
|
||||
l.SetFormatter(&logrus.TextFormatter{
|
||||
FullTimestamp: true,
|
||||
TimestampFormat: "2006-01-02 15:04:05",
|
||||
})
|
||||
|
||||
if cfg.Output != "" {
|
||||
logDir := filepath.Dir(cfg.Output)
|
||||
if logDir != "." && logDir != "" {
|
||||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
file, err := os.OpenFile(cfg.Output, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw := io.MultiWriter(os.Stdout, file)
|
||||
l.SetOutput(mw)
|
||||
} else {
|
||||
l.SetOutput(os.Stdout)
|
||||
}
|
||||
|
||||
defaultLogger = l
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取默认日志实例
|
||||
// @return *logrus.Logger 日志实例
|
||||
// @author sunct
|
||||
func Get() *logrus.Logger {
|
||||
if defaultLogger == nil {
|
||||
defaultLogger = logrus.New()
|
||||
defaultLogger.SetFormatter(&logrus.TextFormatter{
|
||||
FullTimestamp: true,
|
||||
TimestampFormat: "2006-01-02 15:04:05",
|
||||
})
|
||||
}
|
||||
return defaultLogger
|
||||
}
|
||||
|
||||
// Debug 输出 Debug 级别日志
|
||||
// @param args 日志内容
|
||||
func Debug(args ...interface{}) { Get().Debug(args...) }
|
||||
|
||||
// Debugf 格式化输出 Debug 级别日志
|
||||
// @param format 格式字符串
|
||||
// @param args 格式化参数
|
||||
func Debugf(format string, args ...interface{}) { Get().Debugf(format, args...) }
|
||||
|
||||
// Info 输出 Info 级别日志
|
||||
// @param args 日志内容
|
||||
func Info(args ...interface{}) { Get().Info(args...) }
|
||||
|
||||
// Infof 格式化输出 Info 级别日志
|
||||
// @param format 格式字符串
|
||||
// @param args 格式化参数
|
||||
func Infof(format string, args ...interface{}) { Get().Infof(format, args...) }
|
||||
|
||||
// Warn 输出 Warn 级别日志
|
||||
// @param args 日志内容
|
||||
func Warn(args ...interface{}) { Get().Warn(args...) }
|
||||
|
||||
// Warnf 格式化输出 Warn 级别日志
|
||||
// @param format 格式字符串
|
||||
// @param args 格式化参数
|
||||
func Warnf(format string, args ...interface{}) { Get().Warnf(format, args...) }
|
||||
|
||||
// Error 输出 Error 级别日志
|
||||
// @param args 日志内容
|
||||
func Error(args ...interface{}) { Get().Error(args...) }
|
||||
|
||||
// Errorf 格式化输出 Error 级别日志
|
||||
// @param format 格式字符串
|
||||
// @param args 格式化参数
|
||||
func Errorf(format string, args ...interface{}) { Get().Errorf(format, args...) }
|
||||
|
||||
// Fatal 输出 Fatal 级别日志并退出
|
||||
// @param args 日志内容
|
||||
func Fatal(args ...interface{}) { Get().Fatal(args...) }
|
||||
|
||||
// Fatalf 格式化输出 Fatal 级别日志并退出
|
||||
// @param format 格式字符串
|
||||
// @param args 格式化参数
|
||||
func Fatalf(format string, args ...interface{}) { Get().Fatalf(format, args...) }
|
||||
|
||||
// WithField 添加单字段上下文
|
||||
// @param key 字段名
|
||||
// @param value 字段值
|
||||
// @return *logrus.Entry 带字段的日志条目
|
||||
// @author sunct
|
||||
func WithField(key string, value interface{}) *logrus.Entry {
|
||||
return Get().WithField(key, value)
|
||||
}
|
||||
|
||||
// WithFields 添加多字段上下文
|
||||
// @param fields 字段映射
|
||||
// @return *logrus.Entry 带字段的日志条目
|
||||
// @author sunct
|
||||
func WithFields(fields logrus.Fields) *logrus.Entry {
|
||||
return Get().WithFields(fields)
|
||||
}
|
||||
|
||||
func FromContext(ctx context.Context) *logrus.Entry {
|
||||
entry := Get().WithField("app", "resume-platform")
|
||||
|
||||
if reqId := ctx.Value(constant.RequestIdKey); reqId != nil {
|
||||
entry = entry.WithField("request-id", reqId)
|
||||
}
|
||||
if route := ctx.Value(constant.RequestRouteKey); route != nil {
|
||||
entry = entry.WithField("route", route)
|
||||
}
|
||||
if clientIP := ctx.Value(constant.ClientIP); clientIP != nil {
|
||||
entry = entry.WithField("client-ip", clientIP)
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
func CtxDebug(ctx context.Context, args ...interface{}) {
|
||||
FromContext(ctx).Debug(args...)
|
||||
}
|
||||
|
||||
func CtxDebugf(ctx context.Context, format string, args ...interface{}) {
|
||||
FromContext(ctx).Debugf(format, args...)
|
||||
}
|
||||
|
||||
func CtxInfo(ctx context.Context, args ...interface{}) {
|
||||
FromContext(ctx).Info(args...)
|
||||
}
|
||||
|
||||
func CtxInfof(ctx context.Context, format string, args ...interface{}) {
|
||||
FromContext(ctx).Infof(format, args...)
|
||||
}
|
||||
|
||||
func CtxWarn(ctx context.Context, args ...interface{}) {
|
||||
FromContext(ctx).Warn(args...)
|
||||
}
|
||||
|
||||
func CtxWarnf(ctx context.Context, format string, args ...interface{}) {
|
||||
FromContext(ctx).Warnf(format, args...)
|
||||
}
|
||||
|
||||
func CtxError(ctx context.Context, args ...interface{}) {
|
||||
FromContext(ctx).Error(args...)
|
||||
}
|
||||
|
||||
func CtxErrorf(ctx context.Context, format string, args ...interface{}) {
|
||||
FromContext(ctx).Errorf(format, args...)
|
||||
}
|
||||
|
||||
func CtxFatal(ctx context.Context, args ...interface{}) {
|
||||
FromContext(ctx).Fatal(args...)
|
||||
}
|
||||
|
||||
func CtxFatalf(ctx context.Context, format string, args ...interface{}) {
|
||||
FromContext(ctx).Fatalf(format, args...)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func CleanAIResponse(response string) string {
|
||||
result := strings.TrimSpace(response)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
changed := false
|
||||
|
||||
if strings.HasPrefix(result, "```json") {
|
||||
result = strings.TrimSpace(strings.TrimPrefix(result, "```json"))
|
||||
changed = true
|
||||
} else if strings.HasPrefix(result, "```") {
|
||||
result = strings.TrimSpace(strings.TrimPrefix(result, "```"))
|
||||
changed = true
|
||||
}
|
||||
|
||||
if strings.HasSuffix(result, "```") {
|
||||
result = strings.TrimSpace(strings.TrimSuffix(result, "```"))
|
||||
changed = true
|
||||
}
|
||||
|
||||
if !changed {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
firstBrace := strings.Index(result, "{")
|
||||
lastBrace := strings.LastIndex(result, "}")
|
||||
if firstBrace >= 0 && lastBrace >= 0 && lastBrace > firstBrace {
|
||||
result = result[firstBrace : lastBrace+1]
|
||||
}
|
||||
|
||||
result = strings.ReplaceAll(result, "`", "")
|
||||
|
||||
result = strings.ReplaceAll(result, "\\n", "\n")
|
||||
result = strings.ReplaceAll(result, "\\r", "\r")
|
||||
result = strings.ReplaceAll(result, "\\t", "\t")
|
||||
|
||||
return strings.TrimSpace(result)
|
||||
}
|
||||
|
||||
func IsValidJSON(s string) bool {
|
||||
var js json.RawMessage
|
||||
return json.Unmarshal([]byte(s), &js) == nil
|
||||
}
|
||||
|
||||
func ExtractJSONFromText(text string) string {
|
||||
firstBrace := strings.Index(text, "{")
|
||||
lastBrace := strings.LastIndex(text, "}")
|
||||
|
||||
if firstBrace >= 0 && lastBrace >= 0 && lastBrace > firstBrace {
|
||||
return text[firstBrace : lastBrace+1]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package utils
|
||||
|
||||
func Min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func Max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func Clamp(value, min, max int) int {
|
||||
if value < min {
|
||||
return min
|
||||
}
|
||||
if value > max {
|
||||
return max
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func Abs(n int) int {
|
||||
if n < 0 {
|
||||
return -n
|
||||
}
|
||||
return n
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func ConvertToString(v interface{}) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
return val
|
||||
case int, int8, int16, int32, int64:
|
||||
return fmt.Sprintf("%d", val)
|
||||
case float32, float64:
|
||||
return fmt.Sprintf("%.0f", val)
|
||||
default:
|
||||
return fmt.Sprintf("%v", val)
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertToStringSlice(v []interface{}) []interface{} {
|
||||
if v == nil {
|
||||
return []interface{}{}
|
||||
}
|
||||
result := make([]interface{}, 0, len(v))
|
||||
for _, item := range v {
|
||||
result = append(result, ConvertToString(item))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func CleanText(text string) string {
|
||||
var builder strings.Builder
|
||||
for _, r := range text {
|
||||
if unicode.IsPrint(r) || r == '\n' || r == '\r' || r == '\t' {
|
||||
builder.WriteRune(r)
|
||||
} else {
|
||||
builder.WriteRune(' ')
|
||||
}
|
||||
}
|
||||
|
||||
result := builder.String()
|
||||
result = strings.ReplaceAll(result, "\r\n", "\n")
|
||||
result = strings.ReplaceAll(result, "\r", "\n")
|
||||
|
||||
for strings.Contains(result, "\n\n\n") {
|
||||
result = strings.ReplaceAll(result, "\n\n\n", "\n\n")
|
||||
}
|
||||
|
||||
for strings.Contains(result, " ") {
|
||||
result = strings.ReplaceAll(result, " ", " ")
|
||||
}
|
||||
|
||||
return strings.TrimSpace(result)
|
||||
}
|
||||
Reference in New Issue
Block a user