commit 51090658a2f3629351935fc22e163c57e7830810 Author: iuu <2167162990@qq.com> Date: Mon Aug 26 17:20:13 2024 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5470aa0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +main.exe +go_build_energy_management_system.exe +.idea diff --git a/api/v1/enter.go b/api/v1/enter.go new file mode 100644 index 0000000..249d12d --- /dev/null +++ b/api/v1/enter.go @@ -0,0 +1,176 @@ +package v1 + +type Controller struct { + TestApi + PeakValley // 谷峰规则 + //UserApi // 用户管理 + //DictApi // 字典管理 + //ProductApi // 产品管理 + //OTAApi // ota管理 + //UpLoadApi // 上传 + //ProtocolPluginApi // 协议插件 + //DeviceApi // 设备 + //DeviceModelApi // 设备物模型 + //UiElementsApi // UI元素控制 + //BoardApi // 首页 + //TelemetryDataApi // 遥测数据 + //AttributeDataApi // 属性数据 + //EventDataApi // 事件数据 + //CommandSetLogApi // 命令下发记录 + //OperationLogsApi // 系统日志 + //LogoApi // 站标 + //DataPolicyApi // 数据清理 + //DeviceConfigApi // 设备配置 + //DataScriptApi // 数据处理脚本 + //RoleApi // 用户管理 + //CasbinApi // 权限管理 + //NotificationGroupApi // 通知组 + //NotificationHistoryApi // 通知历史 + //NotificationServicesConfigApi // 通知服务配置 + //AlarmApi // 告警 + //SceneAutomationsApi //场景联动 + //SceneApi //场景 + //SystemApi //系统相关 + //SysFunctionApi //功能设置 + //VisPluginApi //可视化插件 + //ServicePluginApi //插件管理 + //ServiceAccessApi //服务接入管理 +} + +var Controllers = new(Controller) + +//var Validate *validator.Validate + +func init() { + //Validate = validator.New() +} + +// ValidateStruct validates the request structure +//func ValidateStruct(i interface{}) error { +// err := Validate.Struct(i) +// if err != nil { +// if _, ok := err.(*validator.InvalidValidationError); ok { +// return err +// } +// +// var errors []string +// for _, err := range err.(validator.ValidationErrors) { +// var error string +// switch err.Tag() { +// case "required": +// error = fmt.Sprintf("Field '%s' is required", err.Field()) +// case "email": +// error = fmt.Sprintf("Field '%s' must be a valid email address", err.Field()) +// case "gte": +// error = fmt.Sprintf("The value of field '%s' must be at least %s", err.Field(), err.Param()) +// case "lte": +// error = fmt.Sprintf("The value of field '%s' must be at most %s", err.Field(), err.Param()) +// default: +// error = fmt.Sprintf("Field '%s' failed validation (%s)", err.Field(), validationErrorToText(err)) +// } +// errors = append(errors, error) +// } +// +// return fmt.Errorf("%s", errors[0]) +// } +// return nil +//} + +// validationErrorToText converts validation errors to more descriptive text +//func validationErrorToText(e validator.FieldError) string { +// switch e.Tag() { +// case "min": +// return fmt.Sprintf("At least %s characters", e.Param()) +// case "max": +// return fmt.Sprintf("At most %s characters", e.Param()) +// case "len": +// return fmt.Sprintf("Must be %s characters", e.Param()) +// // Add more cases as needed +// default: +// return "Does not meet validation rules" +// } +//} + +// 定义统一的HTTP响应结构 +//type ApiResponse struct { +// Code int `json:"code"` +// Message string `json:"message"` +// Data interface{} `json:"data,omitempty"` +//} + +// ErrorHandler 统一错误处理 +//func ErrorHandler(c *gin.Context, code int, err error) { +// if strings.Contains(err.Error(), "SQLSTATE 23503") { +// // 处理外键约束违反 +// err = fmt.Errorf("操作无法完成:请先删除与此项相关联的数据后再进行尝试") +// } +// if strings.Contains(err.Error(), "SQLSTATE 23505") { +// // 处理唯一键约束违反 +// err = fmt.Errorf("操作无法完成:已存在相同的数据") +// } +// // fmt.Printf("%T\n", err) +// // // 检查这个错误是否是 *pgconn.PgError +// // var pgErr *pgconn.PgError +// // if errors.As(err, &pgErr) { +// // logrus.Error("-----------------") +// // // 现在 pgErr 是 err 中的 *pgconn.PgError 部分(如果存在) +// // if pgErr.SQLState() == "23503" { +// // // 这就是一个外键约束违反错误 +// // err = fmt.Errorf("外键约束违反: %w", err) +// // } +// // } +// logrus.Error(err) +// c.JSON(http.StatusOK, ApiResponse{ +// Code: code, +// Message: err.Error(), +// }) +//} +// +//// SuccessHandler 统一成功响应 +//func SuccessHandler(c *gin.Context, message string, data interface{}) { +// c.JSON(http.StatusOK, ApiResponse{ +// Code: http.StatusOK, +// Message: message, +// Data: data, +// }) +//} +// +//// SuccessOK 统一成功响应 +//func SuccessOK(c *gin.Context) { +// c.JSON(http.StatusOK, ApiResponse{ +// Code: http.StatusOK, +// Message: "Success", +// }) +//} +// +//func BindAndValidate(c *gin.Context, obj interface{}) bool { +// // 判断请求方法 +// if c.Request.Method == http.MethodGet { +// if err := c.ShouldBindQuery(obj); err != nil { +// ErrorHandler(c, http.StatusBadRequest, err) +// return false +// } +// } else if c.Request.Method == http.MethodPost || c.Request.Method == http.MethodPut || c.Request.Method == http.MethodDelete { +// if err := c.ShouldBindJSON(obj); err != nil { +// ErrorHandler(c, http.StatusBadRequest, err) +// return false +// } +// } +// +// if err := ValidateStruct(obj); err != nil { +// // 如果是验证错误,返回422 Unprocessable Entity +// ErrorHandler(c, http.StatusUnprocessableEntity, err) +// return false +// } +// +// return true +//} +// +//var Wsupgrader = websocket.Upgrader{ +// ReadBufferSize: 1024, +// WriteBufferSize: 1024, +// CheckOrigin: func(r *http.Request) bool { +// // 不做跨域检查 +// return true +// }, +//} diff --git a/api/v1/peak_valley.go b/api/v1/peak_valley.go new file mode 100644 index 0000000..bc788df --- /dev/null +++ b/api/v1/peak_valley.go @@ -0,0 +1,62 @@ +package v1 + +import ( + "energy-management-system/form" + "energy-management-system/request" + "energy-management-system/response" + "energy-management-system/service" + "energy-management-system/utils/exception" + "github.com/gin-gonic/gin" + "strconv" +) + +type PeakValley struct{} + +// GetPeakValleyTypes 获取峰谷类型 +func (r *PeakValley) GetPeakValleyTypes(c *gin.Context) { + types := service.GroupServices.PeakValley.GetPeakValleyType() + response.SuccessData(gin.H{"types": types}, c) +} + +// PeakValleyRuleList 谷峰规则列表 +func (r *PeakValley) PeakValleyRuleList(c *gin.Context) { + var req form.PeakValleyRuleListReq + request.BindParam(c, &req) + respData := service.GroupServices.PeakValley.PeakValleyRuleList(&req) + response.SuccessData(respData, c) +} + +// CreatePeakValleyRule 创建谷峰规则 +func (r *PeakValley) CreatePeakValleyRule(c *gin.Context) { + var req form.CreatePeakValleyRuleReq + request.BindJson(c, &req) + service.GroupServices.PeakValley.CreatePeakValleyRule(&req) + response.Success(c) +} + +// UpdatePeakValleyRule 修改谷峰规则 +func (r *PeakValley) UpdatePeakValleyRule(c *gin.Context) { + var req form.UpdatePeakValleyRuleReq + request.BindJson(c, &req) + service.GroupServices.PeakValley.UpdatePeakValleyRule(&req) + //response.Success(c) +} + +// PeakValleyRuleDetail 谷峰规则详情 +func (r *PeakValley) PeakValleyRuleDetail(c *gin.Context) { + var req form.PeakValleyRuleDetailReq + id, err := strconv.Atoi(c.Param("id")) + exception.PanicMsgBool(err != nil, "参数有误") + req.RuleId = id + respData := service.GroupServices.PeakValley.PeakValleyRuleDetail(&req) + response.SuccessData(respData, c) +} + +func (r *PeakValley) PeakValleyRuleEditDetail(c *gin.Context) { + var req form.PeakValleyRuleEditDetailReq + id, err := strconv.Atoi(c.Param("id")) + exception.PanicMsgBool(err != nil, "参数有误") + req.RuleId = id + respData := service.GroupServices.PeakValley.PeakValleyRuleEditDetail(&req) + response.SuccessData(respData, c) +} diff --git a/api/v1/test.go b/api/v1/test.go new file mode 100644 index 0000000..9c993fe --- /dev/null +++ b/api/v1/test.go @@ -0,0 +1,33 @@ +package v1 + +import ( + "energy-management-system/response" + "github.com/gin-gonic/gin" +) + +type TestApi struct { +} + +func (r *TestApi) Test(c *gin.Context) { + response.Success(c) +} + +func (r *TestApi) TestData(c *gin.Context) { + response.SuccessData(gin.H{"userName": "iuu"}, c) +} + +//func (a *AlarmApi) CreateAlarmConfig(c *gin.Context) { +// var req model.CreateAlarmConfigReq +// if !BindAndValidate(c, &req) { +// return +// } +// var userClaims = c.MustGet("claims").(*utils.UserClaims) +// req.TenantID = userClaims.TenantID +// data, err := service.GroupApp.Alarm.CreateAlarmConfig(&req) +// if err != nil { +// ErrorHandler(c, http.StatusInternalServerError, err) +// return +// } +// +// SuccessHandler(c, "Create successfully", data) +//} diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..d71163a --- /dev/null +++ b/config.yaml @@ -0,0 +1,17 @@ +service: + http: + host: 0.0.0.0 # 默认localhost + port: 9999 # 默认9999 + +db: + db_host: "192.168.0.9" + db_port: "15432" + db_name: "nengyuan" + db_user: "iuu_postgres" + db_pass: "iuu_postgres" + table_prefix: "energy_" + time_zone: "Asia/Shanghai" + log_level: 4 + slow_threshold: 200 + idle_conns: 10 + open_conns: 50 \ No newline at end of file diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..425208d --- /dev/null +++ b/config/config.go @@ -0,0 +1,19 @@ +package config + +//环境 全称 简写 +//开发环境 development dev +//测试环境 testing test +//灰度/预发布环境 staging/pre-production stag +//生产环境 production prod + +const ( + // AppEnv 应用环境 dev,test,stag,prod + AppEnv = "dev" + // EnvConfig 环境配置 + EnvConfig = "config.yaml" +) + +type Config struct { + Db DbConf `mapstructure:"db" json:"db" yaml:"db"` + Service ServiceConf `mapstructure:"service" json:"service" yaml:"service"` +} diff --git a/config/db.go b/config/db.go new file mode 100644 index 0000000..93c5f0b --- /dev/null +++ b/config/db.go @@ -0,0 +1,15 @@ +package config + +type DbConf struct { + DbHost string `mapstructure:"db_host" json:"db_host" yaml:"db_host"` + DbPort int `mapstructure:"db_port" json:"db_port" yaml:"db_port"` + DbName string `mapstructure:"db_name" json:"db_name" yaml:"db_name"` + DbUser string `mapstructure:"db_user" json:"db_user" yaml:"db_user"` + DbPass string `mapstructure:"db_pass" json:"db_pass" yaml:"db_pass"` + TablePrefix string `mapstructure:"table_prefix" json:"table_prefix" yaml:"table_prefix"` + TimeZone string `mapstructure:"time_zone" json:"time_zone" yaml:"time_zone"` + LogLevel int `mapstructure:"log_level" json:"log_level" yaml:"log_level"` // LogLevel SQL日志级别 (1-静音 2-错误 3-警告 4-信息) + SlowThreshold int `mapstructure:"slow_threshold" json:"slow_threshold" yaml:"slow_threshold"` // SlowThreshold 慢SQL阈值(毫秒)。慢SQL会在log_level大于等于3时输出。 + IdleConns int `mapstructure:"idle_conns" json:"idle_conns" yaml:"idle_conns"` // 空闲连接池中的最大连接数,建议为open_conns的百分之5-20之间 + OpenConns int `mapstructure:"open_conns" json:"open_conns" yaml:"open_conns"` // 最大打开连接数,建议这里设置为50 +} diff --git a/config/service.go b/config/service.go new file mode 100644 index 0000000..339c3d7 --- /dev/null +++ b/config/service.go @@ -0,0 +1,10 @@ +package config + +type ServiceConf struct { + Http HttpConf `mapstructure:"http" json:"http" yaml:"http"` +} + +type HttpConf struct { + Host string `mapstructure:"host" json:"host" yaml:"host"` + Port string `mapstructure:"port" json:"port" yaml:"port"` +} diff --git a/core/gorm.go b/core/gorm.go new file mode 100644 index 0000000..fc6483b --- /dev/null +++ b/core/gorm.go @@ -0,0 +1,78 @@ +package core + +import ( + "energy-management-system/global" + peak_valley "energy-management-system/model/peak-valley" + "fmt" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "gorm.io/gorm/schema" + "log" + "os" + "time" +) + +func InitGorm() { + dsn := fmt.Sprintf("host=%s port=%d dbname=%s user=%s password=%s sslmode=disable TimeZone=%s", + global.AppConf.Db.DbHost, global.AppConf.Db.DbPort, global.AppConf.Db.DbName, global.AppConf.Db.DbUser, global.AppConf.Db.DbPass, global.AppConf.Db.TimeZone) + postgresConfig := postgres.Config{ + DSN: dsn, // DSN data source name + PreferSimpleProtocol: true, // 禁用隐式 prepared statement + } + + // 使用标准日志库的New方法创建日志输出 + newLogger := logger.New( + log.New(os.Stdout, "\r\n", log.LstdFlags), + logger.Config{ + SlowThreshold: time.Duration(global.AppConf.Db.SlowThreshold) * time.Millisecond, // 慢SQL阈值 + LogLevel: logger.LogLevel(global.AppConf.Db.LogLevel), // 日志级别 + IgnoreRecordNotFoundError: true, + Colorful: true, + }) + + PGDB, err := gorm.Open(postgres.New(postgresConfig), &gorm.Config{ + //Logger: newLogger, + //Logger: logger.Default.LogMode(logger.Info), + Logger: newLogger, + NamingStrategy: schema.NamingStrategy{ + SingularTable: true, // 使用单数表名,在启用此选项的情况下,“user”的表将是“user” + TablePrefix: global.AppConf.Db.TablePrefix, // 表前缀 + }, + }) + + DB := PGDB + + if err != nil { + fmt.Println("数据库链接失败") + os.Exit(0) + } + //AutoMigrate(DB) + sqlDB, _ := DB.DB() + sqlDB.SetMaxIdleConns(global.AppConf.Db.IdleConns) + sqlDB.SetMaxOpenConns(global.AppConf.Db.OpenConns) + // 设置数据库连接池中连接的最大生命周期 + sqlDB.SetConnMaxLifetime(time.Hour) + fmt.Println("[+]PG连接成功!") + global.Db = DB +} + +// AutoMigrate 自动迁移 +func AutoMigrate(db *gorm.DB) { + err := db.AutoMigrate( + new(peak_valley.PeakValleyTimeBlock), + new(peak_valley.PeakValleyTimeBlockPrice), + new(peak_valley.PeakValleyQuarterRule), + new(peak_valley.PeakValleyRule), + //new(model.Role), + //new(model.UserRole), + //new(model.Api), + //new(model.Menu), + //new(model.RoleApi), + //new(model.RoleMenu), + ) + if err != nil { + fmt.Println("[-] 迁移数据表失败:", err.Error()) + os.Exit(0) + } +} diff --git a/core/validator.go b/core/validator.go new file mode 100644 index 0000000..25dcb76 --- /dev/null +++ b/core/validator.go @@ -0,0 +1,2 @@ +package core + diff --git a/core/viper.go b/core/viper.go new file mode 100644 index 0000000..6569dc7 --- /dev/null +++ b/core/viper.go @@ -0,0 +1,59 @@ +package core + +import ( + "energy-management-system/config" + "energy-management-system/global" + "flag" + "fmt" + "github.com/fsnotify/fsnotify" + "github.com/spf13/viper" + "os" +) + +// InitViper 解析yaml格式文件 +func InitViper(path ...string) { + var confPath string + if len(path) == 0 { + flag.StringVar(&confPath, "c", "", "choose config file.") + flag.Parse() + if confPath == "" { + if AppEnv := os.Getenv(config.AppEnv); AppEnv == "" { + confPath = config.EnvConfig + } else { + confPath = AppEnv + } + } else { + } + } else { + confPath = path[0] + } + + v := viper.New() + // 指定配置文件路径 + v.SetConfigFile(confPath) + // 如果配置文件的名称中没有扩展名,则需要配置此项 + v.SetConfigType("yaml") + // 查找并读取配置文件 + err := v.ReadInConfig() + if err != nil { + fmt.Printf("[-]读取配置文件错误: %s \n", err) + os.Exit(0) + } + // 监控并重新读取配置文件 + v.WatchConfig() + v.OnConfigChange(func(e fsnotify.Event) { + if err = v.Unmarshal(&global.AppConf); err != nil { + fmt.Printf("[-]重新解析配置文件失败: %s \n", err) + os.Exit(0) + } + fmt.Println("[+]重新加载配置文件完成") + }) + if err = v.Unmarshal(&global.AppConf); err != nil { + fmt.Printf("[-]解析配置文件失败: %s \n", err) + os.Exit(0) + } + fmt.Println("[+]加载配置文件完成") + + //dump.P(global.AppConf) + +} diff --git a/form/page.go b/form/page.go new file mode 100644 index 0000000..f0940e9 --- /dev/null +++ b/form/page.go @@ -0,0 +1,20 @@ +package form + +type Page struct { + PageIndex int `form:"page_index" json:"page_index"` + PageSize int `form:"page_size" json:"page_size"` +} + +func (m *Page) GetPageIndex() int { + if m.PageIndex <= 0 { + m.PageIndex = 1 + } + return m.PageIndex +} + +func (m *Page) GetPageSize() int { + if m.PageSize <= 0 { + m.PageSize = 10 + } + return m.PageSize +} diff --git a/form/peak_valley_rule.go b/form/peak_valley_rule.go new file mode 100644 index 0000000..dabbc9e --- /dev/null +++ b/form/peak_valley_rule.go @@ -0,0 +1,42 @@ +package form + +// 一天的完整谷峰规则 +// 发电 (光伏) +// 用电 (电机、生产) + +//新建规则 +// +//选择时间段 设置电价 +//(将时间段 转化为十分钟区块 查找到对应十分钟区块 创建该规则的电价) + +type PeakValleyRuleListReq struct { + Page `json:"page"` +} + +type PeakValleyRuleDetailReq struct { + RuleId int `form:"rule_id" json:"rule_id"` +} + +type PeakValleyRuleEditDetailReq struct { + RuleId int `form:"rule_id" json:"rule_id"` +} + +type CreatePeakValleyRuleReq struct { + RuleName string `json:"rule_name" binding:"required"` + Description string `json:"description" binding:"required"` + RuleItem []TimeBlockPriceReq `json:"rule_item"` +} +type UpdatePeakValleyRuleReq struct { + RuleId int `json:"rule_id" binding:"required"` + RuleName string `json:"rule_name" binding:"required"` + Description string `json:"description" binding:"required"` + RuleItem []TimeBlockPriceReq `json:"rule_item"` +} + +type TimeBlockPriceReq struct { + CustomName string `json:"custom_name"` + PeakValleyType uint `json:"peak_valley_type"` + StartTime string `json:"start_time"` + EndTime string `json:"end_time"` + Price int `json:"price"` +} diff --git a/global/var.go b/global/var.go new file mode 100644 index 0000000..970e468 --- /dev/null +++ b/global/var.go @@ -0,0 +1,17 @@ +package global + +import ( + "energy-management-system/config" + "gorm.io/gorm" +) + +var ( + // AppConf 配置信息 + AppConf *config.Config + // Db 数据库 + Db *gorm.DB + + // Trans 定义一个全局翻译器T + //Trans ut.Translator + //Validate *validator.Validate +) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..52aec34 --- /dev/null +++ b/go.mod @@ -0,0 +1,64 @@ +module energy-management-system + +go 1.22 + +require ( + github.com/fsnotify/fsnotify v1.7.0 + github.com/gin-gonic/gin v1.10.0 + github.com/gookit/goutil v0.6.16 + github.com/spf13/viper v1.19.0 + gorm.io/driver/postgres v1.5.9 + gorm.io/gorm v1.25.11 +) + +require ( + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/gookit/color v1.5.4 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.5.5 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9a21552 --- /dev/null +++ b/go.sum @@ -0,0 +1,154 @@ +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/gookit/goutil v0.6.16 h1:9fRMCF4X9abdRD5+2HhBS/GwafjBlTUBjRtA5dgkvuw= +github.com/gookit/goutil v0.6.16/go.mod h1:op2q8AoPDFSiY2+qkHxcBWQMYxOLQ1GbLXqe7vrwscI= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= +gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= +gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/main.go b/main.go new file mode 100644 index 0000000..edc1304 --- /dev/null +++ b/main.go @@ -0,0 +1,138 @@ +package main + +import ( + "context" + "energy-management-system/core" + "energy-management-system/global" + "energy-management-system/request" + "energy-management-system/router" + "errors" + "fmt" + "net/http" + "os" + "os/signal" + "time" +) + +func init() { + core.InitViper() + core.InitGorm() + //init_model_data.InitDbData() + //core.InitCasbin() + request.InitTrans() + //core.AutoMigrate(global.Db) + +} +func main() { + + r := router.InitRouter() + host := global.AppConf.Service.Http.Host + port := global.AppConf.Service.Http.Port + srv := initServer(host, port, r) + + // 启动服务 + go startServer(srv, host, port) + + // 优雅关闭 + gracefulShutdown(srv) + + //// 定义一个切片来接收查询结果 + //var users []peaks_valleys.PeakValleyTimeBlock + // + //// 查询所有用户 + //result := global.Db.Find(&users) + //if result.Error != nil { + // fmt.Println("Error:", result.Error) + //} + // + //// 打印查询结果 + //for _, user := range users { + // + // if user.BlockIndex < 20 { + // global.Db.Create(&peaks_valleys.PeakValleyTimeBlockPrice{ + // BlockId: user.BlockIndex, + // Price: 35, + // PeakValleyType: 5, + // }) + // } else if user.BlockIndex < 40 { + // global.Db.Create(&peaks_valleys.PeakValleyTimeBlockPrice{ + // BlockId: user.BlockIndex, + // Price: 45, + // PeakValleyType: 4, + // }) + // } else if user.BlockIndex < 60 { + // global.Db.Create(&peaks_valleys.PeakValleyTimeBlockPrice{ + // BlockId: user.BlockIndex, + // Price: 55, + // PeakValleyType: 3, + // }) + // } else if user.BlockIndex < 80 { + // global.Db.Create(&peaks_valleys.PeakValleyTimeBlockPrice{ + // BlockId: user.BlockIndex, + // Price: 65, + // PeakValleyType: 2, + // }) + // } else if user.BlockIndex < 100 { + // global.Db.Create(&peaks_valleys.PeakValleyTimeBlockPrice{ + // BlockId: user.BlockIndex, + // Price: 85, + // PeakValleyType: 1, + // }) + // } else if user.BlockIndex <= 144 { + // global.Db.Create(&peaks_valleys.PeakValleyTimeBlockPrice{ + // BlockId: user.BlockIndex, + // Price: 35, + // PeakValleyType: 5, + // }) + // } + // + // //fmt.Printf("ID: %d, Name: %s, Email: %s\n", user.BlockIndex) + //} + +} + +//func loadConfig() (host, port string) { +// host = viper.GetString("service.http.host") +// if host == "" { +// host = "localhost" +// fmt.Println("Using default host:", host) +// } +// +// port = viper.GetString("service.http.port") +// if port == "" { +// port = "9999" +// fmt.Println("Using default port:", port) +// } +// +// return host, port +//} + +func initServer(host, port string, handler http.Handler) *http.Server { + return &http.Server{ + Addr: fmt.Sprintf("%s:%s", host, port), + Handler: handler, + ReadTimeout: 60 * time.Second, + WriteTimeout: 60 * time.Second, + } +} + +func startServer(srv *http.Server, host, port string) { + fmt.Println("Listening and serving HTTP on", host, ":", port) + if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + fmt.Printf("listen: %s\n", err) + } +} + +func gracefulShutdown(srv *http.Server) { + quit := make(chan os.Signal, 1) + signal.Notify(quit, os.Interrupt) + <-quit + fmt.Println("Shutdown Server ...") + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := srv.Shutdown(ctx); err != nil { + fmt.Println("Server Shutdown:", err) + } + fmt.Println("Server exiting") +} diff --git a/middleware/cors.go b/middleware/cors.go new file mode 100644 index 0000000..8e67562 --- /dev/null +++ b/middleware/cors.go @@ -0,0 +1,25 @@ +package middleware + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +// Cors 处理跨域请求,支持options访问 +func Cors() gin.HandlerFunc { + return func(c *gin.Context) { + method := c.Request.Method + origin := c.Request.Header.Get("Origin") + c.Writer.Header().Set("Access-Control-Allow-Origin", origin) + c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id") + c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT") + c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type") + c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") + // 放行所有OPTIONS方法 + if method == "OPTIONS" { + c.AbortWithStatus(http.StatusNoContent) + } + // 处理请求 + c.Next() + } +} diff --git a/middleware/recovery.go b/middleware/recovery.go new file mode 100644 index 0000000..6894d59 --- /dev/null +++ b/middleware/recovery.go @@ -0,0 +1,48 @@ +package middleware + +import ( + "energy-management-system/utils" + "energy-management-system/utils/exception" + "fmt" + "github.com/gin-gonic/gin" + "net" + "os" + "strings" + "time" +) + +func Recovery() gin.HandlerFunc { + return func(c *gin.Context) { + defer func() { + if err := recover(); err != nil { + var brokenPipe bool + if ne, ok := err.(*net.OpError); ok { + if se, ok := ne.Err.(*os.SyscallError); ok { + if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") { + brokenPipe = true + } + } + } + if brokenPipe { + c.Error(err.(error)) + } else { + if h, ok := err.(*exception.Exception); ok { + exception.Panic(c, h) + c.Errors = append(c.Errors, &gin.Error{Meta: h}) + } else if _, ok = err.(error); ok { + if gin.IsDebugging() { + fmt.Printf("[Recovery] %s : %s", utils.TimeFormat(time.Now()), err) + utils.Stack(3) + } + exception.Unknow(c) + } else { + fmt.Print(err) + exception.Server(c) + } + } + c.Abort() + } + }() + c.Next() + } +} diff --git a/model/peak-valley/peak_valley_quarter_rule.go b/model/peak-valley/peak_valley_quarter_rule.go new file mode 100644 index 0000000..55919fa --- /dev/null +++ b/model/peak-valley/peak_valley_quarter_rule.go @@ -0,0 +1,26 @@ +package peak_valley + +import ( + "energy-management-system/global" + "gorm.io/gorm" + "time" +) + +// 季度谷峰规则 + +// 开始时间 +// 结束时间 +// 谷峰规则ID + +type PeakValleyQuarterRule struct { + RuleId uint `gorm:"column:rule_id;primaryKey" json:"rule_id"` + StartTime uint `gorm:"column:start_time;comment:开始时间" json:"start_time"` + EndTime uint `gorm:"column:end_time;comment:结束时间" json:"end_time"` + Created time.Time `gorm:"column:created;autoCreateTime;comment:创建时间" json:"created"` + Updated time.Time `gorm:"column:updated;autoUpdateTime;comment:修改时间" json:"updated"` + DeletedAt gorm.DeletedAt `gorm:"index;comment:删除时间" json:"-"` +} + +func (r *PeakValleyQuarterRule) TableName() string { + return global.AppConf.Db.TablePrefix + "peak_valley_quarter_rules" +} diff --git a/model/peak-valley/peak_valley_rule.go b/model/peak-valley/peak_valley_rule.go new file mode 100644 index 0000000..a41b4e3 --- /dev/null +++ b/model/peak-valley/peak_valley_rule.go @@ -0,0 +1,29 @@ +package peak_valley + +import ( + "energy-management-system/global" + "gorm.io/gorm" + "time" +) + +// 一天的完整谷峰规则 +// 发电 (光伏) +// 用电 (电机、生产) + +//新建规则 +// +//选择时间段 设置电价 +//(将时间段 转化为十分钟区块 查找到对应十分钟区块 创建该规则的电价) + +type PeakValleyRule struct { + RuleId uint `gorm:"column:rule_id;primaryKey" json:"rule_id"` + RuleName string `gorm:"column:rule_name;comment:规则名称" json:"rule_name"` + Description string `gorm:"column:description;comment:描述" json:"description"` + Created time.Time `gorm:"column:created;autoCreateTime;comment:创建时间" json:"created"` + Updated time.Time `gorm:"column:updated;autoUpdateTime;comment:修改时间" json:"updated"` + DeletedAt gorm.DeletedAt `gorm:"index;comment:删除时间" json:"-"` +} + +func (r *PeakValleyRule) TableName() string { + return global.AppConf.Db.TablePrefix + "peak_valley_rules" +} diff --git a/model/peak-valley/peak_valley_time_block.go b/model/peak-valley/peak_valley_time_block.go new file mode 100644 index 0000000..4118e60 --- /dev/null +++ b/model/peak-valley/peak_valley_time_block.go @@ -0,0 +1,58 @@ +package peak_valley + +import ( + "energy-management-system/global" + "fmt" + "gorm.io/gorm" + "time" +) + +const MinutesInADay = 24 * 60 + +// PeakValleyTimeBlock 峰谷时间区块 +type PeakValleyTimeBlock struct { + BlockIndex uint `gorm:"column:block_index;primaryKey;comment:区块编号" json:"block_index"` + StartTime uint `gorm:"column:start_time;comment:开始时间" json:"start_time"` + EndTime uint `gorm:"column:end_time;comment:结束时间" json:"end_time"` + Created time.Time `gorm:"column:created;autoCreateTime;comment:创建时间" json:"created"` + Updated time.Time `gorm:"column:updated;autoUpdateTime;comment:修改时间" json:"updated"` + DeletedAt gorm.DeletedAt `gorm:"index;comment:删除时间" json:"-"` +} + +func (r *PeakValleyTimeBlock) TableName() string { + return global.AppConf.Db.TablePrefix + "peak_valley_time_blocks" +} + +func Init() { + // 一天有24小时,即1440分钟 + totalMinutes := 24 * 60 + // 时间块的持续时间:10分钟 + blockDuration := 10 + + // 遍历一天中的所有时间块 + for i := 0; i < totalMinutes; i += blockDuration { + startTime := i + endTime := i + blockDuration + + fmt.Println("start", startTime) + fmt.Println("start", endTime) + + global.Db.Create(&PeakValleyTimeBlock{ + StartTime: uint(startTime), + EndTime: uint(endTime), + }) + + // 打印开始时间和结束时间,格式为 HH:MM + //fmt.Printf("时间块 %d: 开始时间: %02d:%02d, 结束时间: %02d:%02d\n", + // i/blockDuration+1, + // startTime/60, startTime%60, + // endTime/60, endTime%60) + + // 使用整数存储时间块的开始和结束时间(以分钟为单位) + //startTime := 0 // 00:00 + //endTime := 10 // 00:10 + // + //fmt.Printf("开始时间: %02d:%02d\n", startTime/60, startTime%60) + //fmt.Printf("结束时间: %02d:%02d\n", endTime/60, endTime%60) + } +} diff --git a/model/peak-valley/peak_valley_time_block_price.go b/model/peak-valley/peak_valley_time_block_price.go new file mode 100644 index 0000000..00a0c1b --- /dev/null +++ b/model/peak-valley/peak_valley_time_block_price.go @@ -0,0 +1,86 @@ +package peak_valley + +import ( + "energy-management-system/global" + "energy-management-system/utils" + "gorm.io/gorm" + "time" +) + +const ( + // POINTED 尖 + POINTED = iota + 1 + // PEAK 峰 + PEAK + // FLAT 平 + FLAT + // VALLEY 谷 + VALLEY + // FUKAYA 深谷 + FUKAYA +) + +var PeakValleyTypes = map[int]string{ + POINTED: "尖", + PEAK: "峰", + FLAT: "平", + VALLEY: "谷", + FUKAYA: "深谷", +} + +type PeakValleyType struct { + Value int `json:"value"` + Title string `json:"title"` +} + +// PeakValleyTimeBlockPrice 峰谷时间区块价格 +type PeakValleyTimeBlockPrice struct { + Id uint `gorm:"column:id;primaryKey" json:"id"` + BlockId uint `gorm:"column:block_id;comment:时间区块编号" json:"block_id"` + Price int `gorm:"column:price;comment:价格" json:"price"` + CustomName string `gorm:"column:custom_name;comment:自定义名称" json:"custom_name"` + PeakValleyRuleId uint `gorm:"column:peak_valley_rule_id;comment:峰谷规则" json:"peak_valley_rule_id"` + PeakValleyType uint `gorm:"column:peak_valley_type;default:1;size:10;comment:峰谷类型[1:尖,2:峰,3:平,4:谷,5:深谷]" json:"peak_valley_type"` // 峰谷类型 + Created time.Time `gorm:"column:created;autoCreateTime;comment:创建时间" json:"created"` + Updated time.Time `gorm:"column:updated;autoUpdateTime;comment:修改时间" json:"updated"` + DeletedAt gorm.DeletedAt `gorm:"index;comment:删除时间" json:"-"` +} + +func (r *PeakValleyTimeBlockPrice) TableName() string { + return global.AppConf.Db.TablePrefix + "peak_valley_time_block_prices" +} + +type PeakValleyRuleCustomName struct { + PeakValleyType uint `json:"peak_valley_type"` + Price int `json:"price"` + CustomName string `json:"custom_name"` + StartTime uint `json:"start_time"` + EndTime uint `json:"end_time"` + StartTimeStr string `json:"start_time_str"` + EndTimeStr string `json:"end_time_str"` +} + +type PeakValleyRuleType struct { + PeakValleyType uint `json:"peak_valley_type"` + Price int `json:"price"` + RuleTimeBlocks []RuleTimeBlock `gorm:"-" json:"rule_time_blocks"` +} + +type RuleTimeBlock struct { + StartTime uint `json:"start_time"` + EndTime uint `json:"end_time"` + StartTimeStr string `json:"start_time_str"` + EndTimeStr string `json:"end_time_str"` + CustomName string `json:"custom_name"` + TimeBlockIds []int `json:"-"` +} + +type ByCustomNameNumber []*PeakValleyRuleCustomName + +func (a ByCustomNameNumber) Len() int { return len(a) } +func (a ByCustomNameNumber) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByCustomNameNumber) Less(i, j int) bool { + numI, _ := utils.ExtractNumber(a[i].CustomName) + numJ, _ := utils.ExtractNumber(a[j].CustomName) + return numI < numJ // 升序排序 +} diff --git a/repository/enter.go b/repository/enter.go new file mode 100644 index 0000000..0e51f44 --- /dev/null +++ b/repository/enter.go @@ -0,0 +1,9 @@ +package repository + +import "energy-management-system/repository/peak-valley" + +type groupRepository struct { + peak_valley.PeakValley +} + +var GroupRepositorys = new(groupRepository) diff --git a/repository/peak-valley/peak_valley_rule.go b/repository/peak-valley/peak_valley_rule.go new file mode 100644 index 0000000..82a1aa3 --- /dev/null +++ b/repository/peak-valley/peak_valley_rule.go @@ -0,0 +1,53 @@ +package peak_valley + +import ( + "energy-management-system/form" + "energy-management-system/global" + peak_valley_model "energy-management-system/model/peak-valley" + "gorm.io/gorm" +) + +type PeakValley struct { +} + +// 一天的完整谷峰规则 +// 发电 (光伏) +// 用电 (电机、生产) + +//新建规则 +// +//选择时间段 设置电价 +//(将时间段 转化为十分钟区块 查找到对应十分钟区块 创建该规则的电价) + +func (r *PeakValley) CreatePeakValleyRule(tx *gorm.DB, d *peak_valley_model.PeakValleyRule) error { + return tx.Create(d).Error +} + +func (r *PeakValley) UpdatePeakValleyRule(tx *gorm.DB, d *peak_valley_model.PeakValleyRule) error { + return tx.Save(d).Error +} + +func (r *PeakValley) GetOnePeakValleyRule(qr map[string]interface{}) (d *peak_valley_model.PeakValleyRule, err error) { + db := global.Db + for key, value := range qr { + db = db.Where(key, value) + } + err = db.First(&d).Error + //if errors.Is(err, gorm.ErrRecordNotFound) { + // d = nil + //} + return +} + +func (r *PeakValley) GetPeakValleyRulePage(req *form.PeakValleyRuleListReq) (count int64, list []*peak_valley_model.PeakValleyRule, err error) { + db := global.Db.Model(&peak_valley_model.PeakValleyRule{}) + //for key, value := range qr { + // db = db.Where(key, value) + //} + err = db.Count(&count).Error + if err != nil { + return + } + err = db.Offset((req.Page.GetPageIndex() - 1) * req.Page.GetPageSize()).Limit(req.Page.GetPageSize()).Order("rule_id desc").Find(&list).Error + return +} diff --git a/repository/peak-valley/peak_valley_time_block.go b/repository/peak-valley/peak_valley_time_block.go new file mode 100644 index 0000000..aca9436 --- /dev/null +++ b/repository/peak-valley/peak_valley_time_block.go @@ -0,0 +1,34 @@ +package peak_valley + +import ( + "energy-management-system/global" + peak_valley_model "energy-management-system/model/peak-valley" + "gorm.io/gorm" +) + +func (r *PeakValley) GetTimeBlockIdsByTimeBlock(tx *gorm.DB, startTime, endTime uint) (ids []uint, err error) { + err = tx.Model(&peak_valley_model.PeakValleyTimeBlock{}). + Where("start_time < ? AND end_time > ?", endTime, startTime). + Pluck("block_index", &ids).Error + return ids, err +} + +// GetBlockStartTime 获取最小开始时间 +func (r *PeakValley) GetBlockStartTime(blockId int) (startTime uint, err error) { + var peakValleyTimeBlockModel peak_valley_model.PeakValleyTimeBlock + err = global.Db.First(&peakValleyTimeBlockModel, blockId).Error + if err != nil { + return 0, err + } + return peakValleyTimeBlockModel.StartTime, nil +} + +// GetBlockEndTime 获取最大结束时间 +func (r *PeakValley) GetBlockEndTime(blockId int) (endTime uint, err error) { + var peakValleyTimeBlockModel peak_valley_model.PeakValleyTimeBlock + err = global.Db.First(&peakValleyTimeBlockModel, blockId).Error + if err != nil { + return 0, err + } + return peakValleyTimeBlockModel.EndTime, nil +} diff --git a/repository/peak-valley/peak_valley_time_block_price.go b/repository/peak-valley/peak_valley_time_block_price.go new file mode 100644 index 0000000..5fb40ae --- /dev/null +++ b/repository/peak-valley/peak_valley_time_block_price.go @@ -0,0 +1,87 @@ +package peak_valley + +import ( + "energy-management-system/global" + peak_valley_model "energy-management-system/model/peak-valley" + "gorm.io/gorm" +) + +func (r *PeakValley) CreatePeakValleyTimeBlockPrices(tx *gorm.DB, ds []*peak_valley_model.PeakValleyTimeBlockPrice) error { + return tx.Create(ds).Error +} + +func (r *PeakValley) DeletePeakValleyTimeBlockPricesByRuleId(tx *gorm.DB, ruleId uint, unscoped bool) error { + db := tx.Where("peak_valley_rule_id", ruleId) + if unscoped { + db = db.Unscoped() + } + return db.Delete(&peak_valley_model.PeakValleyTimeBlockPrice{}).Error +} + +func (r *PeakValley) GetPeakValleyTypes() map[int]string { + return peak_valley_model.PeakValleyTypes +} + +// 获取指定规则的某个类型的时间段 +func (r *PeakValley) GetPeakValleyRulePriceByTypeAndRuleID(ruleId uint, pvType uint) (blockIds []int, err error) { + db := global.Db.Model(&peak_valley_model.PeakValleyTimeBlockPrice{}) + //for key, value := range qr { + db = db.Where("peak_valley_rule_id = ?", ruleId) + db = db.Where("peak_valley_type = ?", pvType) + err = db.Order("block_id ASC").Pluck("block_id", &blockIds).Error + return +} + +// 获取指定规则的某个类型的时间段 +func (r *PeakValley) GetPeakValleyRulePriceByCustomNameAndRuleID(ruleId uint, customName string) (blockIds []int, err error) { + db := global.Db.Model(&peak_valley_model.PeakValleyTimeBlockPrice{}) + //for key, value := range qr { + db = db.Where("peak_valley_rule_id = ?", ruleId) + db = db.Where("custom_name = ?", customName) + err = db.Order("block_id ASC").Pluck("block_id", &blockIds).Error + return +} + +func (r *PeakValley) GetPeakValleyRulePriceCustomName(blockId int, ruleId uint) (customName string, err error) { + var peakValleyTimeBlockPriceModel peak_valley_model.PeakValleyTimeBlockPrice + err = global.Db.Model(&peak_valley_model.PeakValleyTimeBlockPrice{}). + Where("block_id = ?", blockId). + Where("peak_valley_rule_id = ?", ruleId). + First(&peakValleyTimeBlockPriceModel).Error + if err != nil { + return "", err + } + return peakValleyTimeBlockPriceModel.CustomName, nil +} + +// GetPeakValleyRuleTypes 获取规则下 五种类型 +func (r *PeakValley) GetPeakValleyRuleTypes(ruleId int) (list []*peak_valley_model.PeakValleyRuleType, err error) { + // 查询指定规则下的所有不同的 peak_valley_type + err = global.Db.Model(&peak_valley_model.PeakValleyTimeBlockPrice{}). + //Select("DISTINCT peak_valley_type,custom_name"). + Select("DISTINCT peak_valley_type,price"). + Where("peak_valley_rule_id = ?", ruleId). + Find(&list).Error + return +} + +// GetPeakValleyRuleCustomName 规则下 五种类型 + 自定义名称 + 价格 +func (r *PeakValley) GetPeakValleyRuleCustomName(ruleId int) (list []*peak_valley_model.PeakValleyRuleCustomName, err error) { + // 查询指定规则下的所有不同的 peak_valley_type + err = global.Db.Model(&peak_valley_model.PeakValleyTimeBlockPrice{}). + //Select("DISTINCT peak_valley_type,custom_name"). + Select("DISTINCT custom_name,peak_valley_type,price"). + Where("peak_valley_rule_id = ?", ruleId). + Find(&list).Error + return +} + +//type ByCustomNameNumber []*peak_valley_model.PeakValleyRuleCustomName +// +//func (a ByCustomNameNumber) Len() int { return len(a) } +//func (a ByCustomNameNumber) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +//func (a ByCustomNameNumber) Less(i, j int) bool { +// numI, _ := utils.ExtractNumber(a[i].CustomName) +// numJ, _ := utils.ExtractNumber(a[j].CustomName) +// return numI < numJ // 升序排序 +//} diff --git a/request/request.go b/request/request.go new file mode 100644 index 0000000..7edf05e --- /dev/null +++ b/request/request.go @@ -0,0 +1,107 @@ +package request + +import ( + "encoding/json" + "energy-management-system/utils/exception" + "errors" + "fmt" + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "github.com/go-playground/locales/en" + "github.com/go-playground/locales/zh" + ut "github.com/go-playground/universal-translator" + "github.com/go-playground/validator/v10" + zhTranslations "github.com/go-playground/validator/v10/translations/zh" + "os" + "reflect" + "regexp" + "strconv" + "strings" +) + +// Trans 定义一个全局翻译器T +var trans ut.Translator +var validate *validator.Validate +var ok bool + +// InitTrans 初始化翻译器 +func InitTrans(locale ...string) { + if len(locale) == 0 { + locale = []string{"zh"} + } + // 修改gin框架中的Validator引擎属性,实现自定制 + if validate, ok = binding.Validator.Engine().(*validator.Validate); ok { + + // zi + //validate.SetTagName("ss") + + validate.RegisterTagNameFunc(func(fld reflect.StructField) string { + name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] + if name == "-" || name == "_" { + return "" + } + return name + }) + + // 第一个参数是备用(fallback)的语言环境 + uni := ut.New(en.New(), zh.New()) + trans, _ = uni.FindTranslator(locale[0]) + // 注册翻译器 + err := zhTranslations.RegisterDefaultTranslations(validate, trans) + + // 注册自定义验证 + validate.RegisterValidation("mobile", func(fl validator.FieldLevel) bool { + regRuler := "^1[3456789]{1}\\d{9}$" + reg := regexp.MustCompile(regRuler) + return reg.MatchString(fl.Field().String()) + }) + + // 注册自定义验证的翻译 + validate.RegisterTranslation("mobile", trans, func(ut ut.Translator) error { + return ut.Add("mobile", "手机号错误!", true) + }, func(ut ut.Translator, fe validator.FieldError) string { + t, _ := ut.T("mobile", fe.Field()) + return t + }) + + if err != nil { + fmt.Printf("[-]自定义gin验证器失败: %s \n", err) + os.Exit(0) + } + } +} + +func BindJson(c *gin.Context, f interface{}) { + dealWithError(c.ShouldBindJSON(f), "JSON") +} +func BindJsonWith(c *gin.Context, f interface{}) { + dealWithError(c.ShouldBindBodyWith(f, binding.JSON), "JSON") +} +func BindParam(c *gin.Context, f interface{}) { + dealWithError(c.ShouldBindQuery(f), "FORM") +} + +func dealWithError(err error, t string) { + if err != nil { + var validationErrors validator.ValidationErrors + var unmarshalTypeError *json.UnmarshalTypeError + var numError *strconv.NumError + switch { + case errors.As(err, &validationErrors): + exception.PanicMsg(removeTopStruct(err.(validator.ValidationErrors).Translate(trans))) + case errors.As(err, &unmarshalTypeError): + exception.PanicCodeMsg(exception.PARAMETER_ERROR, err.(*json.UnmarshalTypeError).Field+": 类型错误") + case errors.As(err, &numError): + exception.PanicCodeMsg(exception.PARAMETER_ERROR, err.(*strconv.NumError).Num+": 类型错误") + default: + exception.PanicCodeMsg(exception.PARAMETER_ERROR, fmt.Sprintf("请将参数放于%s中传递: ", t)+err.Error()) + } + } +} + +func removeTopStruct(fields map[string]string) (re []string) { + for _, err := range fields { + re = append(re, err) + } + return +} diff --git a/response/response.go b/response/response.go new file mode 100644 index 0000000..80e1705 --- /dev/null +++ b/response/response.go @@ -0,0 +1,30 @@ +package response + +import ( + "energy-management-system/utils/exception" + "github.com/gin-gonic/gin" + "net/http" +) + +type Response struct { + Code int `json:"code"` + Data interface{} `json:"data"` + Msg string `json:"msg"` +} + +// Result ... 正常业务获得结果 +func Result(code int, data interface{}, msg string, c *gin.Context) { + c.JSON(http.StatusOK, Response{code, data, msg}) +} + +func Success(c *gin.Context) { + SuccessMsg("操作成功", c) +} + +func SuccessMsg(message string, c *gin.Context) { + Result(exception.SUCCESS, map[string]interface{}{}, message, c) +} + +func SuccessData(data interface{}, c *gin.Context) { + Result(exception.SUCCESS, data, "操作成功", c) +} diff --git a/router/base.go- b/router/base.go- new file mode 100644 index 0000000..cd35a87 --- /dev/null +++ b/router/base.go- @@ -0,0 +1,43 @@ +package router + +import ( + "energy-management-system/utils/exception" + "github.com/gin-gonic/gin" + "net/http" +) + +type ExceptionResponse struct { + Code int `json:"code"` + Msg interface{} `json:"msg"` + Path string `json:"path"` +} + +type Exception struct { + HttpCode int `json:"-"` + Code int `json:"code"` + Msg interface{} `json:"msg"` + Err error `json:"err"` +} + +func Panic(c *gin.Context, e *Exception) { + c.JSON(e.HttpCode, ExceptionResponse{e.Code, e.Msg, GetReqPath(c)}) +} +func Unknow(c *gin.Context) { + c.JSON(http.StatusForbidden, ExceptionResponse{exception.UNKNOW_ERROR, "未知错误", GetReqPath(c)}) +} +func Server(c *gin.Context) { + c.JSON(http.StatusInternalServerError, ExceptionResponse{exception.SERVER_ERROR, "服务器错误", GetReqPath(c)}) +} + +func NotFoundR(c *gin.Context) { + c.JSON(http.StatusForbidden, ExceptionResponse{exception.NOT_FOUND_ROUTE, "Not Found Route", GetReqPath(c)}) +} + +func NotFoundM(c *gin.Context) { + c.JSON(http.StatusForbidden, ExceptionResponse{exception.NOT_FOUND_METH, "Not Found Method", GetReqPath(c)}) +} + +// GetReqPath 获取请求路径 +func GetReqPath(c *gin.Context) string { + return c.Request.Method + " " + c.Request.URL.String() +} diff --git a/router/recovery.go- b/router/recovery.go- new file mode 100644 index 0000000..cfe286e --- /dev/null +++ b/router/recovery.go- @@ -0,0 +1,2 @@ +package router + diff --git a/router/router.go b/router/router.go new file mode 100644 index 0000000..68170f5 --- /dev/null +++ b/router/router.go @@ -0,0 +1,40 @@ +package router + +import ( + v1 "energy-management-system/api/v1" + "energy-management-system/middleware" + "energy-management-system/router/routes" + "energy-management-system/utils/exception" + "github.com/gin-gonic/gin" +) + +func InitRouter() *gin.Engine { + gin.SetMode(gin.DebugMode) + r := gin.Default() + r.NoRoute(exception.NotFoundR) + r.NoMethod(exception.NotFoundM) + r.Use(middleware.Cors(), middleware.Recovery()) + api := r.Group("api") + { + controllersV1 := new(v1.Controller) + apiV1 := api.Group("v1") + { + apiV1.GET("test/test", controllersV1.TestApi.Test) + apiV1.GET("test/testData", controllersV1.TestApi.TestData) + } + //// 需要权限校验 + //apiV1.Use(middleware.JWTAuth()) + //{ + // apiV1.GET("user/test_d", controllersV1.TestD) + //} + + // 需要权限校验 + //apiV1.Use(middleware.CasbinRBAC()) + + { + routes.GroupRoutes.PeakValley.InitPeakValley(apiV1) + } + + } + return r +} diff --git a/router/routes/enter.go b/router/routes/enter.go new file mode 100644 index 0000000..4856981 --- /dev/null +++ b/router/routes/enter.go @@ -0,0 +1,7 @@ +package routes + +type groupRoutes struct { + PeakValley +} + +var GroupRoutes = new(groupRoutes) diff --git a/router/routes/peak_valley.go b/router/routes/peak_valley.go new file mode 100644 index 0000000..d75ace9 --- /dev/null +++ b/router/routes/peak_valley.go @@ -0,0 +1,38 @@ +package routes + +import ( + v1 "energy-management-system/api/v1" + "github.com/gin-gonic/gin" +) + +type PeakValley struct{} + +func (r *PeakValley) InitPeakValley(Router *gin.RouterGroup) { + peakValleyApi := Router.Group("peakValley") + { + + peakValleyApi.GET("types", v1.Controllers.PeakValley.GetPeakValleyTypes) + peakValleyApi.GET("ruleList", v1.Controllers.PeakValley.PeakValleyRuleList) + peakValleyApi.POST("createRule", v1.Controllers.PeakValley.CreatePeakValleyRule) + peakValleyApi.PUT("updateRule", v1.Controllers.PeakValley.UpdatePeakValleyRule) + peakValleyApi.GET("ruleDetail/:id", v1.Controllers.PeakValley.PeakValleyRuleDetail) + peakValleyApi.GET("ruleEditDetail/:id", v1.Controllers.PeakValley.PeakValleyRuleEditDetail) + + //userapi.DELETE(":id", api.Controllers.UserApi.DeleteUser) + + //// 个人信息管理 + //userapi.GET("detail", api.Controllers.UserApi.GetUserDetail) + //userapi.PUT("update", api.Controllers.UserApi.UpdateUsers) + //userapi.GET("logout", api.Controllers.UserApi.Logout) + //userapi.GET("refresh", api.Controllers.UserApi.RefreshToken) + // + //// 用户管理 + //userapi.GET("", api.Controllers.UserApi.GetUserListByPage) + //userapi.POST("", api.Controllers.UserApi.CreateUser) + //userapi.PUT("", api.Controllers.UserApi.UpdateUser) + //userapi.DELETE(":id", api.Controllers.UserApi.DeleteUser) + //userapi.GET(":id", api.Controllers.UserApi.GetUser) + //userapi.POST("transform", api.Controllers.UserApi.TransformUser) + + } +} diff --git a/service/enter.go b/service/enter.go new file mode 100644 index 0000000..c404c99 --- /dev/null +++ b/service/enter.go @@ -0,0 +1,7 @@ +package service + +type groupService struct { + PeakValley +} + +var GroupServices = new(groupService) diff --git a/service/peak_valley.go b/service/peak_valley.go new file mode 100644 index 0000000..64351c6 --- /dev/null +++ b/service/peak_valley.go @@ -0,0 +1,276 @@ +package service + +import ( + "energy-management-system/form" + "energy-management-system/global" + peak_valley_model "energy-management-system/model/peak-valley" + "energy-management-system/repository" + "energy-management-system/utils" + "energy-management-system/utils/exception" + "errors" + "fmt" + "sort" + "strconv" +) + +type PeakValley struct{} + +// GetPeakValleyType 获取峰谷类型 +func (r *PeakValley) GetPeakValleyType() (typeList []*peak_valley_model.PeakValleyType) { + types := repository.GroupRepositorys.PeakValley.GetPeakValleyTypes() + for key, val := range types { + typeList = append(typeList, &peak_valley_model.PeakValleyType{ + Value: key, + Title: val, + }) + } + return +} + +// CreatePeakValleyRule 创建峰谷规则 +func (r *PeakValley) CreatePeakValleyRule(req *form.CreatePeakValleyRuleReq) { + var err error + tx := global.Db.Begin() + // 确保在结束时提交或回滚事务 + defer func() { + if rec := recover(); rec != nil { + tx.Rollback() // 如果发生错误,回滚事务 + fmt.Println("事务回滚") + exception.PanicMsg(rec) + } else if err != nil { + tx.Rollback() // 错误时回滚事务 + fmt.Println("事务回滚") + exception.PanicMsgErr(err, err.Error()) + } else { + err = tx.Commit().Error // 提交事务 + if err != nil { + exception.PanicMsgErr(err, "事务提交失败") + } + } + }() + pvr := &peak_valley_model.PeakValleyRule{} + pvr.RuleName = req.RuleName + pvr.Description = req.Description + err = repository.GroupRepositorys.PeakValley.CreatePeakValleyRule(tx, pvr) + if err != nil { + return + } + var total, start, end, blockTotal = 0, 0, 0, 0 + + // 每个时间段的时间区块ID + var timeBlockIds []uint + var timeBlockIdsMap = make(map[string][]uint) + + for _, item := range req.RuleItem { + start, end, blockTotal, err = utils.GetMinutesFromTimeRange(item.StartTime, item.EndTime) + if err != nil { + return + } + total += blockTotal + if total > peak_valley_model.MinutesInADay { + err = errors.New("选择的时间段超过一天") + return + } + timeBlockIds, err = repository.GroupRepositorys.PeakValley.GetTimeBlockIdsByTimeBlock(tx, uint(start), uint(end)) + if err != nil { + return + } + timeBlockIdsMap[item.StartTime+item.EndTime] = timeBlockIds + } + if total < peak_valley_model.MinutesInADay { + err = errors.New("各个时间段累加不足一天") + return + } + var timeBlockPrices []*peak_valley_model.PeakValleyTimeBlockPrice + for index, item := range req.RuleItem { + // 获取某个区块的具体时间区块编号 + timeBlockIds = timeBlockIdsMap[item.StartTime+item.EndTime] + for _, timeBlockId := range timeBlockIds { + timeBlockPrices = append(timeBlockPrices, &peak_valley_model.PeakValleyTimeBlockPrice{ + CustomName: strconv.Itoa(index+1) + "_TIME_" + item.CustomName, + BlockId: timeBlockId, + Price: item.Price, + PeakValleyRuleId: pvr.RuleId, + PeakValleyType: item.PeakValleyType, + }) + } + } + err = repository.GroupRepositorys.PeakValley.CreatePeakValleyTimeBlockPrices(tx, timeBlockPrices) + if err != nil { + return + } + return +} + +// UpdatePeakValleyRule 修改峰谷规则 +func (r *PeakValley) UpdatePeakValleyRule(req *form.UpdatePeakValleyRuleReq) { + var err error + tx := global.Db.Begin() + // 确保在结束时提交或回滚事务 + defer func() { + if rec := recover(); rec != nil { + tx.Rollback() // 如果发生错误,回滚事务 + fmt.Println("事务回滚1", rec) + exception.PanicMsg(rec) + } else if err != nil { + tx.Rollback() // 错误时回滚事务 + fmt.Println("事务回滚2", err) + exception.PanicMsgErr(err, err.Error()) + } else { + err = tx.Commit().Error // 提交事务 + if err != nil { + exception.PanicMsgErr(err, "事务提交失败") + } + } + }() + + pvr, err := repository.GroupRepositorys.PeakValley.GetOnePeakValleyRule(map[string]interface{}{"rule_id": req.RuleId}) + if err != nil { + return + } + pvr.RuleName = req.RuleName + pvr.Description = req.Description + err = repository.GroupRepositorys.PeakValley.UpdatePeakValleyRule(tx, pvr) + if err != nil { + return + } + + var total, start, end, blockTotal = 0, 0, 0, 0 + + // 每个时间段的时间区块ID + var timeBlockIds []uint + var timeBlockIdsMap = make(map[string][]uint) + + for _, item := range req.RuleItem { + start, end, blockTotal, err = utils.GetMinutesFromTimeRange(item.StartTime, item.EndTime) + if err != nil { + return + } + total += blockTotal + if total > peak_valley_model.MinutesInADay { + err = errors.New("选择的时间段超过一天") + return + } + timeBlockIds, err = repository.GroupRepositorys.PeakValley.GetTimeBlockIdsByTimeBlock(tx, uint(start), uint(end)) + exception.PanicMsgBool(err != nil, "获取时间区块ID列表失败") + timeBlockIdsMap[item.StartTime+item.EndTime] = timeBlockIds + } + if total < peak_valley_model.MinutesInADay { + err = errors.New("各个时间段累加不足一天") + return + } + // 删除旧的 + err = repository.GroupRepositorys.PeakValley.DeletePeakValleyTimeBlockPricesByRuleId(tx, uint(req.RuleId), true) + if err != nil { + return + } + // + var timeBlockPrices []*peak_valley_model.PeakValleyTimeBlockPrice + for index, item := range req.RuleItem { + // 获取某个区块的具体时间区块编号 + timeBlockIds = timeBlockIdsMap[item.StartTime+item.EndTime] + for _, timeBlockId := range timeBlockIds { + timeBlockPrices = append(timeBlockPrices, &peak_valley_model.PeakValleyTimeBlockPrice{ + CustomName: strconv.Itoa(index+1) + "_TIME_" + item.CustomName, + BlockId: timeBlockId, + Price: item.Price, + PeakValleyRuleId: pvr.RuleId, + PeakValleyType: item.PeakValleyType, + }) + } + } + err = repository.GroupRepositorys.PeakValley.CreatePeakValleyTimeBlockPrices(tx, timeBlockPrices) + if err != nil { + return + } + return +} + +// PeakValleyRuleList 峰谷规则列表 +func (r *PeakValley) PeakValleyRuleList(req *form.PeakValleyRuleListReq) map[string]interface{} { + count, list, err := repository.GroupRepositorys.PeakValley.GetPeakValleyRulePage(req) + if err != nil { + exception.PanicMsgErr(err, "获取列表失败") + //return + } + ListRsp := make(map[string]interface{}) + ListRsp["total"] = count + ListRsp["list"] = list + return ListRsp +} + +// PeakValleyRuleDetail 获取规则详情吧 +func (r *PeakValley) PeakValleyRuleDetail(req *form.PeakValleyRuleDetailReq) map[string]interface{} { + // 获取分了多少个时间段 以及每个时间段的自定义名称 峰谷类型 + ruleTypes, err := repository.GroupRepositorys.PeakValley.GetPeakValleyRuleTypes(req.RuleId) + fmt.Println(ruleTypes) + exception.PanicMsgBool(err != nil, "获取规则区块类型失败1") + // 查询指定规则 指定峰谷类型的区块 + for idx, ruleType := range ruleTypes { + // 查询当前类型的时间区块 + typeBlockList, err := repository.GroupRepositorys.PeakValley.GetPeakValleyRulePriceByTypeAndRuleID(uint(req.RuleId), ruleType.PeakValleyType) + exception.PanicMsgBool(err != nil, "获取规则区块类型失败2") + // 根据连贯性 小区块 组合成大区块 (一个区块 或者多个区块) + blocks := utils.SplitIntoGroups(typeBlockList) + for _, block := range blocks { + maxBlockId, minBlockId, err := utils.GetSplitIntMaxMin(block) + exception.PanicMsgBool(err != nil, "获取规则区块类型失败3") + customName, err := repository.GroupRepositorys.PeakValley.GetPeakValleyRulePriceCustomName(minBlockId, uint(req.RuleId)) + exception.PanicMsgBool(err != nil, "获取规则区块类型失败4") + startTime, err := repository.GroupRepositorys.PeakValley.GetBlockStartTime(minBlockId) + exception.PanicMsgBool(err != nil, "获取规则区块类型失败5") + endTime, err := repository.GroupRepositorys.PeakValley.GetBlockEndTime(maxBlockId) + exception.PanicMsgBool(err != nil, "获取规则区块类型失败6") + startTimeStr := fmt.Sprintf("%02d:%02d", startTime/60, startTime%60) + endTimeStr := fmt.Sprintf("%02d:%02d", endTime/60, endTime%60) + ruleTimeBlock := peak_valley_model.RuleTimeBlock{ + TimeBlockIds: block, + StartTime: startTime, + EndTime: endTime, + StartTimeStr: startTimeStr, + EndTimeStr: endTimeStr, + CustomName: customName, + } + ruleTypes[idx].RuleTimeBlocks = append(ruleTypes[idx].RuleTimeBlocks, ruleTimeBlock) + } + } + DetailRsp := make(map[string]interface{}) + DetailRsp["rule_id"] = req.RuleId + DetailRsp["detail"] = ruleTypes + return DetailRsp +} + +// PeakValleyRuleEditDetail 获取规则详情-编辑使用 +func (r *PeakValley) PeakValleyRuleEditDetail(req *form.PeakValleyRuleEditDetailReq) map[string]interface{} { + // 获取分了多少个时间段 以及每个时间段的自定义名称 峰谷类型 + customNames, err := repository.GroupRepositorys.PeakValley.GetPeakValleyRuleCustomName(req.RuleId) + exception.PanicMsgBool(err != nil, "获取规则区块类型失败1") + // 查询指定规则 指定峰谷类型的区块 + for idx, customName := range customNames { + // 查询当前类型的时间区块 + customNameBlockList, err := repository.GroupRepositorys.PeakValley.GetPeakValleyRulePriceByCustomNameAndRuleID(uint(req.RuleId), customName.CustomName) + exception.PanicMsgBool(err != nil, "获取规则区块类型失败2") + maxBlockId, minBlockId, err := utils.GetSplitIntMaxMin(customNameBlockList) + exception.PanicMsgBool(err != nil, "获取规则区块类型失败3") + startTime, err := repository.GroupRepositorys.PeakValley.GetBlockStartTime(minBlockId) + exception.PanicMsgBool(err != nil, "获取规则区块类型失败4") + endTime, err := repository.GroupRepositorys.PeakValley.GetBlockEndTime(maxBlockId) + exception.PanicMsgBool(err != nil, "获取规则区块类型失败5") + startTimeStr := fmt.Sprintf("%02d:%02d", startTime/60, startTime%60) + endTimeStr := fmt.Sprintf("%02d:%02d", endTime/60, endTime%60) + //dump.P(customNameBlockList) + customNames[idx].StartTimeStr = startTimeStr + customNames[idx].StartTime = startTime + customNames[idx].EndTimeStr = endTimeStr + customNames[idx].EndTime = endTime + customNames[idx].EndTimeStr = endTimeStr + } + + // 根据 自定义名称 中的数字部分进行升序排序 + sort.Sort(peak_valley_model.ByCustomNameNumber(customNames)) + + DetailRsp := make(map[string]interface{}) + DetailRsp["rule_id"] = req.RuleId + DetailRsp["detail"] = customNames + return DetailRsp +} diff --git a/utils/exception/code.go b/utils/exception/code.go new file mode 100644 index 0000000..156f46c --- /dev/null +++ b/utils/exception/code.go @@ -0,0 +1,47 @@ +package exception + +const ( + SUCCESS = 0 // 操作成功 + ERROR = 10000 // 默认错误返回 + + SERVER_ERROR = 10001 // 服务器错误 + UNKNOW_ERROR = 10002 // 未知错误 + REMOTE_ERROR = 10003 // 远程服务错误 + IP_LINMIT = 10004 // 地址限制 + IP_LINMIT_CODE = 10014 // 地址限制需要验证码 + NO_PERMISSION = 10005 // 未拥有授权 + PARAMETER_ERROR = 10008 // 参数错误 + RPC_ERROR = 10013 // RPC通讯错误 + NOT_FOUND_ROUTE = 10020 // 未查询到路由 + NOT_FOUND_METH = 10021 // 未查询到方式 + + NOT_FOUND = 10022 // 未查询到 + AUTH_ERROR = 10023 // 认证错误 + NO_VALID_TOKEN = 10024 // token无效 + REPEAD = 10025 // 重复数据或操作 + OUT_SLINCE = 10026 // 超出限制 + DB_ERRROR = 10027 // 数据库错误 + SENSITIVE = 10028 // 敏感词语 +) +const ( + //用户 + NOT_FOUND_USER = 20001 // 未查询到用户 + EXIST_USER = 20002 // 用户已存在 + NO_BELONG_ACTION = 20003 // 越权操作 + NO_PHONE = 20004 // 未验证手机号 + + //订单 + ORDER_ERROR = 20100 // 订单统用错误 + NO_ORDER = 20101 // 订单不存在 + NO_PAID_ORDER = 20102 // 订单未支付 + PAID_ORDER = 20103 // 订单已支付 + EXPIRE_ORDER = 20104 // 订单超时 + DONE_ORDER = 20105 // 订单已完结 + NO_BELONG_ORDER = 20106 // 不属于自己的订单 + + //积分 + SIGNED = 20200 // 已签到 + NO_ENOUGH_POINT = 20201 // 积分不足 + NO_ILLEGAL_CHANNEL = 20202 // 渠道非法 + NO_ILLEGAL_SIGN = 20203 // 渠道非签到 +) diff --git a/utils/exception/exception.go b/utils/exception/exception.go new file mode 100644 index 0000000..7440a60 --- /dev/null +++ b/utils/exception/exception.go @@ -0,0 +1,81 @@ +package exception + +import ( + "energy-management-system/utils" + "net/http" + + "github.com/gin-gonic/gin" +) + +type Exception struct { + HttpCode int `json:"-"` + Code int `json:"code"` + Msg interface{} `json:"msg"` + Err error `json:"err"` +} + +type ExceptionResponse struct { + Code int `json:"code"` + Msg interface{} `json:"msg"` + Path string `json:"path"` +} + +func (r *Exception) Error() interface{} { + return r.Msg +} + +func NotFoundR(c *gin.Context) { + c.JSON(http.StatusForbidden, ExceptionResponse{NOT_FOUND_ROUTE, "Not Found Route", utils.GetReqPath(c)}) +} + +func NotFoundM(c *gin.Context) { + c.JSON(http.StatusForbidden, ExceptionResponse{NOT_FOUND_METH, "Not Found Method", utils.GetReqPath(c)}) +} + +func Panic(c *gin.Context, e *Exception) { + c.JSON(e.HttpCode, ExceptionResponse{e.Code, e.Msg, utils.GetReqPath(c)}) +} +func Unknow(c *gin.Context) { + c.JSON(http.StatusForbidden, ExceptionResponse{UNKNOW_ERROR, "未知错误", utils.GetReqPath(c)}) +} +func Server(c *gin.Context) { + c.JSON(http.StatusInternalServerError, ExceptionResponse{SERVER_ERROR, "服务器错误", utils.GetReqPath(c)}) +} + +// FailMsg 主动抛出错误Exception类型 +func FailMsg(msg interface{}) *Exception { + return &Exception{http.StatusOK, ERROR, msg, nil} +} + +func FailCodeMsg(code int, msg string) *Exception { + return &Exception{http.StatusOK, code, msg, nil} +} + +func PanicMsg(msg interface{}) { + PanicMsgBool(true, msg) +} +func PanicCodeMsg(code int, msg string) { + PanicCodeMsgBool(true, code, msg) +} + +func PanicMsgBool(flag bool, msg interface{}) { + if flag { + panic(&Exception{http.StatusOK, ERROR, msg, nil}) + } +} +func PanicCodeMsgBool(flag bool, code int, msg string) { + if flag { + panic(&Exception{http.StatusOK, code, msg, nil}) + } +} + +func PanicMsgErr(err error, msg interface{}) { + if err != nil { + panic(&Exception{http.StatusOK, ERROR, msg, err}) + } +} +func PanicCodeMsgErr(err error, code int, msg string) { + if err != nil { + panic(&Exception{http.StatusOK, code, msg, err}) + } +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..cbc6cb5 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,189 @@ +package utils + +import ( + "bytes" + "fmt" + "github.com/gin-gonic/gin" + "math" + "os" + "runtime" + "sort" + "strconv" + "strings" + "time" +) + +func GetReqPath(c *gin.Context) string { + return c.Request.Method + " " + c.Request.URL.String() +} + +func TimeFormat(t time.Time) string { + timeString := t.Format("2006/01/02 - 15:04:05") + return timeString +} + +// GetMinutesFromTimeRange 接受时间范围字符串,返回开始分钟、结束分钟和总分钟数 +func GetMinutesFromTimeRange(startTimeStr, endTimeStr string) (int, int, int, error) { + startTimeParts := strings.Split(startTimeStr, ":") + endTimeParts := strings.Split(endTimeStr, ":") + if len(startTimeParts) != 2 || len(endTimeParts) != 2 { + return 0, 0, 0, fmt.Errorf("时间格式不正确,应该是 HH:MM") + } + startHour, err := strconv.Atoi(startTimeParts[0]) + if err != nil { + return 0, 0, 0, fmt.Errorf("无效的开始小时: %s", startTimeParts[0]) + } + startMinute, err := strconv.Atoi(startTimeParts[1]) + if err != nil { + return 0, 0, 0, fmt.Errorf("无效的开始分钟: %s", startTimeParts[1]) + } + endHour, err := strconv.Atoi(endTimeParts[0]) + if err != nil { + return 0, 0, 0, fmt.Errorf("无效的结束小时: %s", endTimeParts[0]) + } + endMinute, err := strconv.Atoi(endTimeParts[1]) + if err != nil { + return 0, 0, 0, fmt.Errorf("无效的结束分钟: %s", endTimeParts[1]) + } + // 计算开始和结束的总分钟 + startMinutes := startHour*60 + startMinute + endMinutes := endHour*60 + endMinute + // 判断开始时间和结束时间的大小关系 + if startMinutes >= endMinutes { + return 0, 0, 0, fmt.Errorf("开始时间必须小于结束时间") + } + // 计算总分钟数 + totalMinutes := endMinutes - startMinutes + return startMinutes, endMinutes, totalMinutes, nil +} + +var ( + dunno = []byte("???") + centerDot = []byte("·") + dot = []byte(".") + slash = []byte("/") +) + +func Stack(skip int) { + var lines [][]byte + var lastFile string + for i := skip; ; i++ { // Skip the expected number of frames + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + fmt.Printf("%s:%d (0x%x)\n", file, line, pc) + if file != lastFile { + data, err := os.ReadFile(file) + if err != nil { + continue + } + lines = bytes.Split(data, []byte{'\n'}) + lastFile = file + } + fmt.Printf("\t%s: %s\n", function(pc), source(lines, line)) + } +} + +func source(lines [][]byte, n int) []byte { + n-- // in stack trace, lines are 1-indexed but our array is 0-indexed + if n < 0 || n >= len(lines) { + return dunno + } + return bytes.TrimSpace(lines[n]) +} + +// function returns, if possible, the name of the function containing the PC. +func function(pc uintptr) []byte { + fn := runtime.FuncForPC(pc) + if fn == nil { + return dunno + } + name := []byte(fn.Name()) + // The name includes the path name to the package, which is unnecessary + // since the file name is already included. Plus, it has center dots. + // That is, we see + // runtime/debug.*T·ptrmethod + // and want + // *T.ptrmethod + // Also the package path might contains dot (e.g. code.google.com/...), + // so first eliminate the path prefix + if lastSlash := bytes.LastIndex(name, slash); lastSlash >= 0 { + name = name[lastSlash+1:] + } + if period := bytes.Index(name, dot); period >= 0 { + name = name[period+1:] + } + name = bytes.Replace(name, centerDot, dot, -1) + return name +} + +// ExtractNumber 提取数字并返回整数 +func ExtractNumber(id string) (int, error) { + parts := strings.Split(id, "_") + if len(parts) == 0 { + return 0, fmt.Errorf("无效的 Id: %s", id) + } + numStr := parts[0] + num, err := strconv.Atoi(numStr) + if err != nil { + return 0, fmt.Errorf("转换错误: %v", err) + } + return num, nil +} + +// GetSplitIntMaxMin 函数返回切片中的最大值和最小值 +func GetSplitIntMaxMin(nums []int) (max int, min int, err error) { + if len(nums) == 0 { + return 0, 0, fmt.Errorf("切片为空") + } + + // 初始化最大值和最小值 + max = math.MinInt64 // 设置为最小整数 + min = math.MaxInt64 // 设置为最大整数 + + // 遍历切片,找到最大值和最小值 + for _, num := range nums { + if num > max { + max = num + } + if num < min { + min = num + } + } + + return max, min, nil +} + +// SplitIntoGroups 切片根据是否连续来进行分组 +func SplitIntoGroups(nums []int) [][]int { + if len(nums) == 0 { + return nil + } + + // 将切片排序 + sort.Ints(nums) + + var groups [][]int + var currentGroup []int + + // 初始化第一组 + currentGroup = append(currentGroup, nums[0]) + + for i := 1; i < len(nums); i++ { + // 检查当前数字与前一个数字的差值 + if nums[i] == nums[i-1]+1 { + // 如果是连续的,添加到当前组 + currentGroup = append(currentGroup, nums[i]) + } else { + // 如果不连续,保存当前组并开始新的一组 + groups = append(groups, currentGroup) + currentGroup = []int{nums[i]} // 开始新的一组 + } + } + + // 添加最后一组 + groups = append(groups, currentGroup) + + return groups +}