This commit is contained in:
2024-08-26 17:20:13 +08:00
commit 51090658a2
39 changed files with 2231 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
main.exe
go_build_energy_management_system.exe
.idea

176
api/v1/enter.go Normal file
View File

@@ -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
// },
//}

62
api/v1/peak_valley.go Normal file
View File

@@ -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)
}

33
api/v1/test.go Normal file
View File

@@ -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)
//}

17
config.yaml Normal file
View File

@@ -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

19
config/config.go Normal file
View File

@@ -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"`
}

15
config/db.go Normal file
View File

@@ -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
}

10
config/service.go Normal file
View File

@@ -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"`
}

78
core/gorm.go Normal file
View File

@@ -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)
}
}

2
core/validator.go Normal file
View File

@@ -0,0 +1,2 @@
package core

59
core/viper.go Normal file
View File

@@ -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)
}

20
form/page.go Normal file
View File

@@ -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
}

42
form/peak_valley_rule.go Normal file
View File

@@ -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"`
}

17
global/var.go Normal file
View File

@@ -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
)

64
go.mod Normal file
View File

@@ -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
)

154
go.sum Normal file
View File

@@ -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=

138
main.go Normal file
View File

@@ -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")
}

25
middleware/cors.go Normal file
View File

@@ -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()
}
}

48
middleware/recovery.go Normal file
View File

@@ -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()
}
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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)
}
}

View File

@@ -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 // 升序排序
}

9
repository/enter.go Normal file
View File

@@ -0,0 +1,9 @@
package repository
import "energy-management-system/repository/peak-valley"
type groupRepository struct {
peak_valley.PeakValley
}
var GroupRepositorys = new(groupRepository)

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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 // 升序排序
//}

107
request/request.go Normal file
View File

@@ -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
}

30
response/response.go Normal file
View File

@@ -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)
}

43
router/base.go- Normal file
View File

@@ -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()
}

2
router/recovery.go- Normal file
View File

@@ -0,0 +1,2 @@
package router

40
router/router.go Normal file
View File

@@ -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
}

7
router/routes/enter.go Normal file
View File

@@ -0,0 +1,7 @@
package routes
type groupRoutes struct {
PeakValley
}
var GroupRoutes = new(groupRoutes)

View File

@@ -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)
}
}

7
service/enter.go Normal file
View File

@@ -0,0 +1,7 @@
package service
type groupService struct {
PeakValley
}
var GroupServices = new(groupService)

276
service/peak_valley.go Normal file
View File

@@ -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
}

47
utils/exception/code.go Normal file
View File

@@ -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 // 渠道非签到
)

View File

@@ -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})
}
}

189
utils/utils.go Normal file
View File

@@ -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
}