zhangqian 1 hónapja
commit
498e1581a8

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/newaterobot-process.iml" filepath="$PROJECT_DIR$/.idea/newaterobot-process.iml" />
+    </modules>
+  </component>
+</project>

+ 9 - 0
.idea/newaterobot-process.iml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="Go" enabled="true" />
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 29 - 0
.idea/watcherTasks.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectTasksOptions">
+    <TaskOptions isEnabled="true">
+      <option name="arguments" value="fmt $FilePath$" />
+      <option name="checkSyntaxErrors" value="true" />
+      <option name="description" />
+      <option name="exitCodeBehavior" value="ERROR" />
+      <option name="fileExtension" value="go" />
+      <option name="immediateSync" value="false" />
+      <option name="name" value="go fmt" />
+      <option name="output" value="$FilePath$" />
+      <option name="outputFilters">
+        <array />
+      </option>
+      <option name="outputFromStdout" value="false" />
+      <option name="program" value="$GoExecPath$" />
+      <option name="runOnExternalChanges" value="false" />
+      <option name="scopeName" value="Project Files" />
+      <option name="trackOnlyRoot" value="true" />
+      <option name="workingDir" value="$ProjectFileDir$" />
+      <envs>
+        <env name="GOROOT" value="$GOROOT$" />
+        <env name="GOPATH" value="$GOPATH$" />
+        <env name="PATH" value="$GoBinDirs$" />
+      </envs>
+    </TaskOptions>
+  </component>
+</project>

+ 2 - 0
.lingma/rules/project_rule.md

@@ -0,0 +1,2 @@
+**添加规则文件可帮助模型精准理解你的编码偏好,如框架、代码风格等**
+**规则文件只对当前工程生效,单文件限制10000字符。如果无需将该文件提交到远程 Git 仓库,请将其添加到 .gitignore**

+ 126 - 0
README.md

@@ -0,0 +1,126 @@
+# Newaterobot Process 项目
+
+这是一个基于 Go 语言和 Gin 框架的后端项目,包含完整的项目架构设计。
+
+## 项目结构
+
+```
+newaterobot-process/
+├── config/          # 配置文件目录
+├── database/        # 数据库初始化
+├── entity/          # 实体层(数据模型)
+├── handler/         # 控制器层(处理HTTP请求)
+├── middleware/      # 中间件
+├── model/           # 模型层(数据访问)
+├── routes/          # 路由配置
+├── service/         # 服务层
+├── utils/           # 工具包
+├── go.mod           # Go模块文件
+└── main.go          # 程序入口
+```
+
+## 技术栈
+
+- Web框架: Gin
+- 配置文件格式: YAML
+- 数据库: MySQL
+- ORM框架: GORM
+- JWT认证: github.com/golang-jwt/jwt/v4
+- 密码加密: golang.org/x/crypto/bcrypt
+
+## 配置说明
+
+在 `config/config.yaml` 文件中配置以下内容:
+
+```yaml
+server:
+  port: 8080
+
+mysql:
+  host: 127.0.0.1
+  port: 3308
+  username: root
+  password: Greentech20200508**1
+  dbname: ws_deepseek
+  charset: utf8mb4
+  parseTime: True
+  loc: Local
+
+jwt:
+  secret: newaterobot_secret
+  expire: 3600
+```
+
+## 主要功能
+
+1. 用户注册和登录
+2. JWT Token 认证
+3. 用户信息获取
+4. 日志记录
+5. HTTP 请求工具
+6. 常用工具函数
+
+## 快速开始
+
+1. 确保已安装 Go 1.21+
+2. 创建 MySQL 数据库:
+
+```sql
+CREATE DATABASE IF NOT EXISTS ws_deepseek CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+```
+
+3. 修改 `config/config.yaml` 中的数据库配置
+4. 运行以下命令启动服务:
+
+```bash
+# 设置代理(如果需要)
+set GOPROXY=https://goproxy.cn,direct
+
+# 安装依赖
+go mod tidy
+
+# 运行程序
+go run main.go
+```
+
+## API 接口
+
+### 用户注册
+```
+POST /api/v1/register
+Content-Type: application/json
+
+{
+  "username": "testuser",
+  "password": "password123",
+  "email": "test@example.com",
+  "nickname": "Test User"
+}
+```
+
+### 用户登录
+```
+POST /api/v1/login
+Content-Type: application/json
+
+{
+  "username": "testuser",
+  "password": "password123"
+}
+```
+
+### 获取用户信息(需要认证)
+```
+GET /api/v1/user/info
+Authorization: Bearer <token>
+```
+
+## 中间件
+
+JWT 认证中间件会拦截需要认证的路由,验证请求头中的 token。
+
+## 工具包
+
+- 日志记录: utils/logger.go
+- HTTP 客户端: utils/http.go
+- 常用函数: utils/common.go

+ 176 - 0
app.log

@@ -0,0 +1,176 @@
+INFO: 2025/09/02 10:24:49 logger.go:53: Starting application...
+INFO: 2025/09/02 10:24:49 logger.go:53: Config loaded successfully
+INFO: 2025/09/02 10:26:55 logger.go:53: Starting application...
+INFO: 2025/09/02 10:26:55 logger.go:53: Config loaded successfully
+INFO: 2025/09/02 10:26:55 logger.go:53: Database initialized successfully
+INFO: 2025/09/02 10:26:55 logger.go:53: Server starting on port 8080
+INFO: 2025/09/02 10:40:15 logger.go:53: Starting application...
+INFO: 2025/09/02 10:40:15 logger.go:53: Config loaded successfully
+INFO: 2025/09/02 10:40:15 logger.go:53: Database initialized successfully
+INFO: 2025/09/02 10:40:15 logger.go:53: Server starting on port 8080
+INFO: 2025/09/02 10:40:27 logger.go:53: Starting application...
+INFO: 2025/09/02 10:40:27 logger.go:53: Config loaded successfully
+INFO: 2025/09/02 10:40:27 logger.go:53: Database initialized successfully
+INFO: 2025/09/02 10:40:27 logger.go:53: Server starting on port 8080
+INFO: 2025/09/02 10:41:09 logger.go:53: Starting application...
+INFO: 2025/09/02 10:41:09 logger.go:53: Config loaded successfully
+INFO: 2025/09/02 10:41:09 logger.go:53: Database initialized successfully
+INFO: 2025/09/02 10:41:09 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 16:21:33 logger.go:53: Starting application...
+INFO: 2025/09/03 16:21:33 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 16:22:07 logger.go:53: Starting application...
+INFO: 2025/09/03 16:22:07 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 16:22:07 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 16:22:07 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 16:26:29 logger.go:53: Starting application...
+INFO: 2025/09/03 16:26:29 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 16:26:29 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 16:26:29 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 16:31:48 logger.go:53: Starting application...
+INFO: 2025/09/03 16:31:48 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 16:31:49 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 16:31:49 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 16:36:11 logger.go:53: Starting application...
+INFO: 2025/09/03 16:36:11 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 16:36:12 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 16:36:12 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 16:41:46 logger.go:53: Starting application...
+INFO: 2025/09/03 16:41:46 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 16:41:46 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 16:41:46 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 16:42:35 logger.go:53: Starting application...
+INFO: 2025/09/03 16:42:35 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 16:42:35 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 16:42:35 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 16:44:06 logger.go:53: Starting application...
+INFO: 2025/09/03 16:44:06 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 16:44:06 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 16:44:06 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 16:48:09 logger.go:53: Starting application...
+INFO: 2025/09/03 16:48:09 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 16:48:09 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 16:48:09 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 16:48:29 logger.go:53: Starting application...
+INFO: 2025/09/03 16:48:29 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 16:48:29 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 16:48:29 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 17:18:18 logger.go:53: Starting application...
+INFO: 2025/09/03 17:18:18 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 17:18:19 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 17:18:19 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 17:19:41 logger.go:53: Starting application...
+INFO: 2025/09/03 17:19:41 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 17:19:42 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 17:19:42 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 17:40:36 logger.go:53: Starting application...
+INFO: 2025/09/03 17:40:36 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 17:40:36 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 17:40:36 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 17:41:02 logger.go:53: Starting application...
+INFO: 2025/09/03 17:41:02 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 17:41:02 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 17:41:02 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 17:44:05 logger.go:53: Starting application...
+INFO: 2025/09/03 17:44:05 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 17:44:06 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 17:44:06 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 17:44:37 logger.go:53: Starting application...
+INFO: 2025/09/03 17:44:37 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 17:44:37 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 17:44:37 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 17:50:57 logger.go:53: Starting application...
+INFO: 2025/09/03 17:50:57 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 17:50:57 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 17:50:57 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 17:51:49 logger.go:53: Starting application...
+INFO: 2025/09/03 17:51:49 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 17:51:50 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 17:51:50 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 17:57:58 logger.go:53: Starting application...
+INFO: 2025/09/03 17:57:58 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 17:57:58 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 17:57:58 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 17:58:17 logger.go:53: Starting application...
+INFO: 2025/09/03 17:58:17 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 17:58:17 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 17:58:17 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 17:59:12 logger.go:53: Starting application...
+INFO: 2025/09/03 17:59:12 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 17:59:13 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 17:59:13 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 18:01:29 logger.go:53: Starting application...
+INFO: 2025/09/03 18:01:29 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 18:01:29 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 18:01:29 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 18:01:42 logger.go:53: Starting application...
+INFO: 2025/09/03 18:01:42 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 18:01:42 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 18:01:42 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 18:04:05 logger.go:53: Starting application...
+INFO: 2025/09/03 18:04:05 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 18:04:05 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 18:04:05 logger.go:53: Server starting on port 8080
+INFO: 2025/09/03 18:06:53 logger.go:53: Starting application...
+INFO: 2025/09/03 18:06:53 logger.go:53: Config loaded successfully
+INFO: 2025/09/03 18:06:53 logger.go:53: Database initialized successfully
+INFO: 2025/09/03 18:06:53 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 11:23:04 logger.go:53: Starting application...
+INFO: 2025/09/04 11:23:04 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 11:23:04 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 11:23:04 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 11:26:44 logger.go:53: Starting application...
+INFO: 2025/09/04 11:26:44 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 11:26:44 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 11:26:44 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 11:27:39 logger.go:53: Starting application...
+INFO: 2025/09/04 11:27:39 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 11:27:40 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 11:27:40 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 11:30:31 logger.go:53: Starting application...
+INFO: 2025/09/04 11:30:31 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 11:30:32 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 11:30:32 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 11:33:02 logger.go:53: Starting application...
+INFO: 2025/09/04 11:33:02 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 11:33:03 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 11:33:03 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 11:33:43 logger.go:53: Starting application...
+INFO: 2025/09/04 11:33:43 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 11:33:43 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 11:33:43 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 11:33:50 logger.go:53: Starting application...
+INFO: 2025/09/04 11:33:50 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 11:33:50 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 11:33:50 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 11:34:34 logger.go:53: Starting application...
+INFO: 2025/09/04 11:34:34 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 11:34:34 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 11:34:34 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 15:13:42 logger.go:53: Starting application...
+INFO: 2025/09/04 15:13:42 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 15:13:42 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 15:13:42 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 15:26:23 logger.go:53: Starting application...
+INFO: 2025/09/04 15:26:23 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 15:26:23 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 15:26:23 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 15:54:21 logger.go:53: Starting application...
+INFO: 2025/09/04 15:54:21 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 15:54:22 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 15:54:22 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 15:57:46 logger.go:53: Starting application...
+INFO: 2025/09/04 15:57:46 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 15:57:46 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 15:57:46 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 16:08:32 logger.go:53: Starting application...
+INFO: 2025/09/04 16:08:32 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 16:08:33 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 16:08:33 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 16:11:20 logger.go:53: Starting application...
+INFO: 2025/09/04 16:11:20 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 16:11:20 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 16:11:20 logger.go:53: Server starting on port 8080
+INFO: 2025/09/04 16:12:37 logger.go:53: Starting application...
+INFO: 2025/09/04 16:12:37 logger.go:53: Config loaded successfully
+INFO: 2025/09/04 16:12:38 logger.go:53: Database initialized successfully
+INFO: 2025/09/04 16:12:38 logger.go:53: Server starting on port 8080

+ 31 - 0
config/config.go

@@ -0,0 +1,31 @@
+package config
+
+// ServerConfig 服务器配置
+type ServerConfig struct {
+	Port int `yaml:"port"`
+}
+
+// MySQLConfig MySQL数据库配置
+type MySQLConfig struct {
+	Host     string `yaml:"host"`
+	Port     int    `yaml:"port"`
+	Username string `yaml:"username"`
+	Password string `yaml:"password"`
+	Dbname   string `yaml:"dbname"`
+	Charset  string `yaml:"charset"`
+	ParseTime bool  `yaml:"parseTime"`
+	Loc      string `yaml:"loc"`
+}
+
+// JWTConfig JWT配置
+type JWTConfig struct {
+	Secret string `yaml:"secret"`
+	Expire int    `yaml:"expire"`
+}
+
+// Config 配置结构体
+type Config struct {
+	Server ServerConfig `yaml:"server"`
+	MySQL  MySQLConfig  `yaml:"mysql"`
+	JWT    JWTConfig    `yaml:"jwt"`
+}

+ 16 - 0
config/config.prod.yaml

@@ -0,0 +1,16 @@
+server:
+  port: 8080
+
+mysql:
+  host: 127.0.0.1
+  port: 3308
+  username: root
+  password: Greentech20200508**1
+  dbname: ws_deepseek
+  charset: utf8mb4
+  parseTime: True
+  loc: Local
+
+jwt:
+  secret: newaterobot_secret
+  expire: 3600

+ 16 - 0
config/config.yaml

@@ -0,0 +1,16 @@
+server:
+  port: 8080
+
+mysql:
+  host: 127.0.0.1
+  port: 3308
+  username: root
+  password: Greentech20200508**1
+  dbname: ws_deepseek
+  charset: utf8mb4
+  parseTime: True
+  loc: Local
+
+jwt:
+  secret: newaterobot_secret
+  expire: 3600

+ 40 - 0
config/load.go

@@ -0,0 +1,40 @@
+package config
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	
+	"gopkg.in/yaml.v2"
+)
+
+var GlobalConfig *Config
+
+// LoadConfig 加载配置文件
+func LoadConfig() error {
+	// 获取当前工作目录
+	dir, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+
+	// 构建配置文件路径
+	configPath := filepath.Join(dir, "config", "config.yaml")
+
+	// 读取配置文件
+	data, err := ioutil.ReadFile(configPath)
+	if err != nil {
+		return fmt.Errorf("failed to read config file: %v", err)
+	}
+
+	// 解析YAML配置
+	var config Config
+	err = yaml.Unmarshal(data, &config)
+	if err != nil {
+		return fmt.Errorf("failed to unmarshal config: %v", err)
+	}
+
+	GlobalConfig = &config
+	return nil
+}

+ 36 - 0
database/init.go

@@ -0,0 +1,36 @@
+package database
+
+import (
+	"fmt"
+	
+	"gorm.io/driver/mysql"
+	"gorm.io/gorm"
+	
+	"newaterobot-process/config"
+)
+
+var DB *gorm.DB
+
+// InitDB 初始化数据库连接
+func InitDB() error {
+	mysqlConfig := config.GlobalConfig.MySQL
+	
+	// 构建DSN
+	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=%t&loc=%s",
+		mysqlConfig.Username,
+		mysqlConfig.Password,
+		mysqlConfig.Host,
+		mysqlConfig.Port,
+		mysqlConfig.Dbname,
+		mysqlConfig.Charset,
+		mysqlConfig.ParseTime,
+		mysqlConfig.Loc)
+
+	var err error
+	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
+	if err != nil {
+		return fmt.Errorf("failed to connect to database: %v", err)
+	}
+
+	return nil
+}

+ 11 - 0
entity/base.go

@@ -0,0 +1,11 @@
+package entity
+
+import "time"
+
+// BaseModel 基础模型
+type BaseModel struct {
+	ID        uint           `gorm:"primaryKey" json:"id"`
+	CreatedAt time.Time      `json:"created_at"`
+	UpdatedAt time.Time      `json:"updated_at"`
+	DeletedAt *time.Time     `sql:"index" json:"deleted_at,omitempty"`
+}

+ 46 - 0
entity/metric_process_config.go

@@ -0,0 +1,46 @@
+package entity
+
+// MetricProcessConfig 指标处理配置实体
+type MetricProcessConfig struct {
+	BaseModel
+	Type               int8   `gorm:"type:tinyint;not null;default:0" json:"type"`           // 0 http 请求 1 直接回复
+	Name               string `gorm:"size:30;not null;default:''" json:"name"`               // 分支名称
+	Intent             int    `gorm:"not null;default:0" json:"intent"`                      //大类
+	MetricType         int    `gorm:"not null;default:0" json:"metric_type"`                 // 意图识别输出的类型编号
+	URLTemplate        string `gorm:"size:1000;not null;default:''" json:"url_template"`     // url模板
+	QueryTemplate      string `gorm:"size:1000;not null;default:''" json:"query_template"`   // GET POST请求模板
+	HTTPMethod         int8   `gorm:"type:tinyint;not null;default:0" json:"http_method"`    // 0 GET 1 POST
+	ResponseExtract    string `gorm:"size:1000;not null;default:''" json:"response_extract"` // 根据该字段配置提取对应的值
+	ContextTemplate    string `gorm:"size:2000;not null;default:''" json:"context_template"` // 输出的提示词模板
+	EngContextTemplate string `gorm:"size:2000;not null;default:''" json:"eng_context_template"`
+}
+
+func (m *MetricProcessConfig) TableName() string {
+	return "metric_process_config"
+}
+
+type MetricProcessRequest struct {
+	DateSrc    string `json:"date_src"`    //原始时间
+	DateTime   string `json:"datetime"`    //查询日期
+	STime      string `json:"s_time"`      //查询开始时间
+	ETime      string `json:"e_time"`      //查询结束时间
+	Intent     int    `json:"intent"`      //意图识别大类
+	MetricType int    `json:"metric_type"` //意图识别小类
+	Name       string `json:"name"`        //水厂名称
+	ID         int    `json:"id"`          //水厂id
+	LocalUrl   string `json:"local_url"`   //查询的url
+	Metric     string `json:"metric"`      //查询指标
+	JwtToken   string `json:"jwt_token"`
+	IsEnglish  int    `json:"is_english"`
+}
+type MetricProcessResponse struct {
+	Flag         int    `json:"flag"` // 0 直接回复 1 http查询上下文 2 无须执行体执行的分支
+	ReplyContext string `json:"reply_context"`
+}
+
+const (
+	NoHttpReply = iota
+	HttpReply
+	NoExecute
+	ThinkStr = `<details style=\"color:gray;background-color: #f8f8f8;padding: 8px;border-radius: 4px;\" open> <summary> Thinking... </summary>  好的,我们已在数据库中为您检索到相关内容,并已为您打开相应的菜单页面。 </details>`
+)

+ 11 - 0
entity/user.go

@@ -0,0 +1,11 @@
+package entity
+
+// User 用户实体
+type User struct {
+	BaseModel
+	Username string `gorm:"size:50;uniqueIndex" json:"username"`
+	Password string `gorm:"size:100" json:"-"`
+	Email    string `gorm:"size:100" json:"email"`
+	Nickname string `gorm:"size:50" json:"nickname"`
+	Status   int    `gorm:"default:1" json:"status"` // 1:正常 2:禁用
+}

+ 44 - 0
go.mod

@@ -0,0 +1,44 @@
+module newaterobot-process
+
+go 1.21
+
+require (
+	github.com/gin-gonic/gin v1.9.1
+	github.com/golang-jwt/jwt/v4 v4.5.0
+	github.com/tidwall/gjson v1.18.0
+	golang.org/x/crypto v0.11.0
+	gopkg.in/yaml.v2 v2.4.0
+	gorm.io/driver/mysql v1.5.1
+	gorm.io/gorm v1.25.1
+)
+
+require (
+	github.com/bytedance/sonic v1.9.1 // indirect
+	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.2 // 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.14.0 // indirect
+	github.com/go-sql-driver/mysql v1.7.0 // indirect
+	github.com/goccy/go-json v0.10.2 // 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.4 // indirect
+	github.com/leodido/go-urn v1.2.4 // indirect
+	github.com/mattn/go-isatty v0.0.19 // 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.0.8 // indirect
+	github.com/tidwall/match v1.1.1 // indirect
+	github.com/tidwall/pretty v1.2.0 // indirect
+	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+	github.com/ugorji/go/codec v1.2.11 // indirect
+	golang.org/x/arch v0.3.0 // indirect
+	golang.org/x/net v0.10.0 // indirect
+	golang.org/x/sys v0.10.0 // indirect
+	golang.org/x/text v0.11.0 // indirect
+	google.golang.org/protobuf v1.30.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)

+ 106 - 0
go.sum

@@ -0,0 +1,106 @@
+github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
+github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
+github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
+github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
+github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
+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.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
+github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
+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.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
+github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
+github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+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/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
+github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+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.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
+github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
+github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+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.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
+github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+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/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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
+github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+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.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
+github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
+golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
+golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
+golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
+golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+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/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
+gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
+gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64=
+gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

+ 42 - 0
handler/metric_process.go

@@ -0,0 +1,42 @@
+package handler
+
+import (
+	"net/http"
+	"newaterobot-process/entity"
+
+	"github.com/gin-gonic/gin"
+
+	"newaterobot-process/service"
+)
+
+var metricProcessConfigService = service.NewMetricProcessConfigService()
+
+// MetricProcess 指标处理处理器
+func MetricProcess(c *gin.Context) {
+	req := entity.MetricProcessRequest{}
+	// 绑定参数
+	if err := c.ShouldBindJSON(&req); err != nil {
+		c.JSON(http.StatusOK, gin.H{
+			"code":    400,
+			"message": "请求参数错误: " + err.Error(),
+		})
+		return
+	}
+
+	// 调用服务处理指标
+	result, err := metricProcessConfigService.ProcessMetric(req)
+	if err != nil {
+		c.JSON(http.StatusOK, gin.H{
+			"code":    500,
+			"message": "处理指标失败: " + err.Error(),
+		})
+		return
+	}
+
+	// 返回成功响应
+	c.JSON(http.StatusOK, gin.H{
+		"code":    200,
+		"message": "处理成功",
+		"data":    result,
+	})
+}

+ 138 - 0
handler/user.go

@@ -0,0 +1,138 @@
+package handler
+
+import (
+	"net/http"
+	
+	"github.com/gin-gonic/gin"
+	
+	"newaterobot-process/service"
+)
+
+var userService = service.NewUserService()
+
+// Register 用户注册处理器
+func Register(c *gin.Context) {
+	// 定义请求参数结构体
+	var req struct {
+		Username string `json:"username" binding:"required"`
+		Password string `json:"password" binding:"required"`
+		Email    string `json:"email"`
+		Nickname string `json:"nickname"`
+	}
+
+	// 绑定参数
+	if err := c.ShouldBindJSON(&req); err != nil {
+		c.JSON(http.StatusBadRequest, gin.H{
+			"code":    400,
+			"message": "请求参数错误: " + err.Error(),
+		})
+		return
+	}
+
+	// 调用服务注册用户
+	user, err := userService.Register(req.Username, req.Password, req.Email, req.Nickname)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"code":    500,
+			"message": "注册失败: " + err.Error(),
+		})
+		return
+	}
+
+	// 返回成功响应
+	c.JSON(http.StatusOK, gin.H{
+		"code":    200,
+		"message": "注册成功",
+		"data": gin.H{
+			"id":       user.ID,
+			"username": user.Username,
+			"email":    user.Email,
+			"nickname": user.Nickname,
+		},
+	})
+}
+
+// Login 用户登录处理器
+func Login(c *gin.Context) {
+	// 定义请求参数结构体
+	var req struct {
+		Username string `json:"username" binding:"required"`
+		Password string `json:"password" binding:"required"`
+	}
+
+	// 绑定参数
+	if err := c.ShouldBindJSON(&req); err != nil {
+		c.JSON(http.StatusBadRequest, gin.H{
+			"code":    400,
+			"message": "请求参数错误: " + err.Error(),
+		})
+		return
+	}
+
+	// 调用服务登录
+	token, err := userService.Login(req.Username, req.Password)
+	if err != nil {
+		c.JSON(http.StatusUnauthorized, gin.H{
+			"code":    401,
+			"message": "登录失败: " + err.Error(),
+		})
+		return
+	}
+
+	// 返回成功响应
+	c.JSON(http.StatusOK, gin.H{
+		"code":    200,
+		"message": "登录成功",
+		"data": gin.H{
+			"token": token,
+		},
+	})
+}
+
+// GetUserInfo 获取用户信息处理器
+func GetUserInfo(c *gin.Context) {
+	// 从上下文获取用户ID
+	userIDStr, exists := c.Get("user_id")
+	if !exists {
+		c.JSON(http.StatusUnauthorized, gin.H{
+			"code":    401,
+			"message": "用户未认证",
+		})
+		return
+	}
+
+	// 转换用户ID类型
+	userID, ok := userIDStr.(uint)
+	if !ok {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"code":    500,
+			"message": "用户ID类型错误",
+		})
+		return
+	}
+
+	// 调用服务获取用户信息
+	user, err := userService.GetUserByID(userID)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"code":    500,
+			"message": "获取用户信息失败: " + err.Error(),
+		})
+		return
+	}
+
+	// 返回成功响应
+	c.JSON(http.StatusOK, gin.H{
+		"code":    200,
+		"message": "获取成功",
+		"data": gin.H{
+			"id":        user.ID,
+			"username":  user.Username,
+			"email":     user.Email,
+			"nickname":  user.Nickname,
+			"status":    user.Status,
+			"createdat": user.CreatedAt,
+		},
+	})
+}
+

+ 49 - 0
main.go

@@ -0,0 +1,49 @@
+package main
+
+import (
+	"fmt"
+	"log"
+
+	"newaterobot-process/config"
+	"newaterobot-process/database"
+	"newaterobot-process/routes"
+	"newaterobot-process/utils"
+)
+
+func main() {
+	// 初始化日志组件
+	utils.InitLogger()
+	utils.Infof("Starting application...")
+
+	// 加载配置文件
+	err := config.LoadConfig()
+	if err != nil {
+		log.Fatalf("Failed to load config: %v", err)
+	}
+	utils.Infof("Config loaded successfully")
+
+	// 初始化数据库
+	err = database.InitDB()
+	if err != nil {
+		log.Fatalf("Failed to initialize database: %v", err)
+	}
+	utils.Infof("Database initialized successfully")
+
+	// 自动迁移数据库表
+	// err = database.DB.AutoMigrate(&entity.User{})
+	// if err != nil {
+	// 	log.Fatalf("Failed to auto migrate database tables: %v", err)
+	// }
+	// utils.Infof("Database tables migrated successfully")
+
+	// 初始化路由
+	r := routes.InitRouter()
+
+	// 启动服务
+	port := config.GlobalConfig.Server.Port
+	utils.Infof("Server starting on port %d", port)
+	err = r.Run(fmt.Sprintf(":%d", port))
+	if err != nil {
+		log.Fatalf("Failed to start server: %v", err)
+	}
+}

+ 53 - 0
middleware/jwt.go

@@ -0,0 +1,53 @@
+package middleware
+
+import (
+	"net/http"
+	"strings"
+	
+	"github.com/gin-gonic/gin"
+	
+	"newaterobot-process/service"
+)
+
+// JWTAuthMiddleware JWT认证中间件
+func JWTAuthMiddleware() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		// 获取token
+		authHeader := c.GetHeader("Authorization")
+		if authHeader == "" {
+			c.JSON(http.StatusUnauthorized, gin.H{
+				"code": 401,
+				"message": "请求未携带token,无权限访问",
+			})
+			c.Abort()
+			return
+		}
+
+		// 按空格分割
+		parts := strings.SplitN(authHeader, " ", 2)
+		if !(len(parts) == 2 && parts[0] == "Bearer") {
+			c.JSON(http.StatusUnauthorized, gin.H{
+				"code": 401,
+				"message": "请求头中auth格式有误",
+			})
+			c.Abort()
+			return
+		}
+
+		// 解析token
+		claims, err := service.GetJWTService().ParseToken(parts[1])
+		if err != nil {
+			c.JSON(http.StatusUnauthorized, gin.H{
+				"code": 401,
+				"message": "token无效或已过期",
+			})
+			c.Abort()
+			return
+		}
+
+		// 将用户信息保存到上下文
+		c.Set("user_id", claims.UserID)
+		c.Set("username", claims.Username)
+		c.Next()
+	}
+}

+ 38 - 0
model/metric_process_config.go

@@ -0,0 +1,38 @@
+package model
+
+import (
+	"newaterobot-process/database"
+	"newaterobot-process/entity"
+)
+
+// MetricProcessConfigModel 指标处理配置模型
+type MetricProcessConfigModel struct{}
+
+// Create 创建指标处理配置
+func (m *MetricProcessConfigModel) Create(config *entity.MetricProcessConfig) error {
+	return database.DB.Create(config).Error
+}
+
+// GetByID 根据ID获取指标处理配置
+func (m *MetricProcessConfigModel) GetByID(id uint) (*entity.MetricProcessConfig, error) {
+	var config entity.MetricProcessConfig
+	err := database.DB.First(&config, id).Error
+	return &config, err
+}
+
+// GetByMetricType 根据指标类型获取配置
+func (m *MetricProcessConfigModel) GetByMetricType(metricType, intent int) ([]entity.MetricProcessConfig, error) {
+	var configs []entity.MetricProcessConfig
+	err := database.DB.Where("metric_type = ? AND intent = ?", metricType, intent).Find(&configs).Error
+	return configs, err
+}
+
+// Update 更新指标处理配置
+func (m *MetricProcessConfigModel) Update(config *entity.MetricProcessConfig) error {
+	return database.DB.Save(config).Error
+}
+
+// Delete 删除指标处理配置
+func (m *MetricProcessConfigModel) Delete(id uint) error {
+	return database.DB.Delete(&entity.MetricProcessConfig{}, id).Error
+}

+ 38 - 0
model/user.go

@@ -0,0 +1,38 @@
+package model
+
+import (
+	"newaterobot-process/database"
+	"newaterobot-process/entity"
+)
+
+// UserModel 用户模型
+type UserModel struct{}
+
+// Create 创建用户
+func (m *UserModel) Create(user *entity.User) error {
+	return database.DB.Create(user).Error
+}
+
+// GetByUsername 根据用户名获取用户
+func (m *UserModel) GetByUsername(username string) (*entity.User, error) {
+	var user entity.User
+	err := database.DB.Where("username = ?", username).First(&user).Error
+	return &user, err
+}
+
+// GetByID 根据ID获取用户
+func (m *UserModel) GetByID(id uint) (*entity.User, error) {
+	var user entity.User
+	err := database.DB.First(&user, id).Error
+	return &user, err
+}
+
+// Update 更新用户信息
+func (m *UserModel) Update(user *entity.User) error {
+	return database.DB.Save(user).Error
+}
+
+// Delete 删除用户
+func (m *UserModel) Delete(id uint) error {
+	return database.DB.Delete(&entity.User{}, id).Error
+}

+ 30 - 0
routes/router.go

@@ -0,0 +1,30 @@
+package routes
+
+import (
+	"github.com/gin-gonic/gin"
+
+	"newaterobot-process/handler"
+	"newaterobot-process/middleware"
+)
+
+// InitRouter 初始化路由
+func InitRouter() *gin.Engine {
+	r := gin.Default()
+
+	// 公共路由
+	public := r.Group("/api/v1")
+	{
+		public.POST("/register", handler.Register)
+		public.POST("/login", handler.Login)
+		public.POST("/metric_process", handler.MetricProcess)
+	}
+
+	// 需要认证的路由
+	private := r.Group("/api/v1")
+	private.Use(middleware.JWTAuthMiddleware())
+	{
+		private.GET("/user/info", handler.GetUserInfo)
+	}
+
+	return r
+}

+ 70 - 0
service/jwt.go

@@ -0,0 +1,70 @@
+package service
+
+import (
+	"time"
+	
+	"github.com/golang-jwt/jwt/v4"
+	
+	"newaterobot-process/config"
+)
+
+// CustomClaims 自定义声明结构体
+type CustomClaims struct {
+	UserID   uint   `json:"user_id"`
+	Username string `json:"username"`
+	jwt.RegisteredClaims
+}
+
+// JWTService JWT服务
+type JWTService struct{}
+
+var jwtService = new(JWTService)
+
+// GenerateToken 生成token
+func (j *JWTService) GenerateToken(userID uint, username string) (string, error) {
+	// 获取JWT配置
+	jwtConfig := config.GlobalConfig.JWT
+	
+	// 创建声明
+	claims := CustomClaims{
+		userID,
+		username,
+		jwt.RegisteredClaims{
+			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(jwtConfig.Expire) * time.Second)),
+			IssuedAt:  jwt.NewNumericDate(time.Now()),
+			NotBefore: jwt.NewNumericDate(time.Now()),
+		},
+	}
+
+	// 创建token
+	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+
+	// 签名token
+	return token.SignedString([]byte(jwtConfig.Secret))
+}
+
+// ParseToken 解析token
+func (j *JWTService) ParseToken(tokenString string) (*CustomClaims, error) {
+	// 获取JWT配置
+	jwtConfig := config.GlobalConfig.JWT
+	
+	// 解析token
+	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
+		return []byte(jwtConfig.Secret), nil
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
+		return claims, nil
+	}
+
+	return nil, err
+}
+
+// GetJWTService 获取JWT服务实例
+func GetJWTService() *JWTService {
+	return jwtService
+}

+ 229 - 0
service/metric_process_config.go

@@ -0,0 +1,229 @@
+package service
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"io"
+	"net/http"
+	"net/url"
+	"newaterobot-process/entity"
+	"newaterobot-process/model"
+	"newaterobot-process/utils"
+	"strings"
+	"text/template"
+	"time"
+)
+
+// MetricProcessConfigService 指标处理配置服务
+type MetricProcessConfigService struct {
+	configModel *model.MetricProcessConfigModel
+	httpClient  *http.Client
+}
+
+// NewMetricProcessConfigService 创建指标处理配置服务实例
+func NewMetricProcessConfigService() *MetricProcessConfigService {
+	return &MetricProcessConfigService{
+		configModel: &model.MetricProcessConfigModel{},
+		httpClient: &http.Client{
+			Timeout: 30 * time.Second,
+		},
+	}
+}
+
+// ProcessMetric 处理指标
+func (s *MetricProcessConfigService) ProcessMetric(inputData entity.MetricProcessRequest) (entity.MetricProcessResponse, error) {
+	ret := entity.MetricProcessResponse{}
+	// 根据指标类型获取配置
+	configs, err := s.configModel.GetByMetricType(inputData.MetricType, inputData.Intent)
+	if err != nil {
+		return ret, err
+	}
+
+	configsMap := make(map[string]entity.MetricProcessConfig)
+	for _, config := range configs {
+		key := fmt.Sprintf("%d_%d", config.Intent, config.MetricType)
+		configsMap[key] = config
+	}
+
+	key := fmt.Sprintf("%d_%d", inputData.Intent, inputData.MetricType)
+	var config entity.MetricProcessConfig
+	var ok bool
+
+	//未配置该处理分支直接返回,在dify中处理
+	if config, ok = configsMap[key]; !ok {
+		ret.Flag = entity.NoExecute
+		return ret, nil
+	}
+	//直接回复分支
+	templateVarMap := s.objToMap(inputData)
+	if config.Type == 1 {
+		contextTemplate := config.ContextTemplate
+		if inputData.IsEnglish == 1 {
+			contextTemplate = config.EngContextTemplate
+		}
+		replyContext, err := s.parseTemplate(contextTemplate, templateVarMap)
+		if err != nil {
+			return ret, err
+		}
+		ret.ReplyContext = replyContext
+		return ret, nil
+	}
+
+	//发送http 请求
+	responseData, err := s.sendHTTPRequest(&config, templateVarMap, inputData.JwtToken)
+	if err != nil {
+		return ret, err
+	}
+
+	//抽取字段
+	responseExtract := make(map[string]string)
+	err = json.Unmarshal([]byte(config.ResponseExtract), &responseExtract)
+	if err != nil {
+		return ret, err
+	}
+	extractMap := s.extractFields(responseExtract, responseData)
+	for k, v := range extractMap {
+		templateVarMap[k] = v
+	}
+
+	//解析返回模板
+	replyContext, err := s.parseTemplate(config.ContextTemplate, templateVarMap)
+	if err != nil {
+		return ret, err
+	}
+	ret.Flag = entity.HttpReply
+	ret.ReplyContext = replyContext
+	return ret, nil
+}
+func (s *MetricProcessConfigService) objToMap(req entity.MetricProcessRequest) map[string]interface{} {
+	ret := make(map[string]interface{})
+	ret["name"] = req.Name
+	ret["think_str"] = entity.ThinkStr
+	ret["date_src"] = req.DateSrc
+	ret["s_time"] = req.STime
+	ret["e_time"] = req.ETime
+	ret["local_url"] = req.LocalUrl
+	ret["id"] = req.ID
+	ret["datetime"] = req.DateTime
+	return ret
+}
+
+// sendHTTPRequest 发送HTTP请求
+func (s *MetricProcessConfigService) sendHTTPRequest(config *entity.MetricProcessConfig, inputData map[string]interface{}, jwtToken string) (string, error) {
+	// 解析URL模板
+	reqUrl, err := s.parseTemplate(config.URLTemplate, inputData)
+	if err != nil {
+		return "", fmt.Errorf("解析URL模板失败: %v", err)
+	}
+
+	var req *http.Request
+	switch config.HTTPMethod {
+	case 0: // GET
+		// 解析查询参数模板
+		query, err := s.parseTemplate(config.QueryTemplate, inputData)
+		if err != nil {
+			return "", fmt.Errorf("解析查询参数模板失败: %v", err)
+		}
+
+		// 对查询参数进行urlencode处理
+		if query != "" {
+			pairs := strings.Split(query, "&")
+			encodedQuery := ""
+			for i, pair := range pairs {
+				if i > 0 {
+					encodedQuery += "&"
+				}
+				// 分割键值对
+				kv := strings.Split(pair, "=")
+				if len(kv) == 2 {
+					// 对键和值都进行urlencode
+					encodedKey := url.QueryEscape(kv[0])
+					encodedValue := url.QueryEscape(kv[1])
+					encodedQuery += encodedKey + "=" + encodedValue
+				}
+
+			}
+			reqUrl += "?" + encodedQuery
+		}
+
+		req, err = http.NewRequest("GET", reqUrl, nil)
+		if err != nil {
+			return "", fmt.Errorf("创建GET请求失败: %v", err)
+		}
+
+	case 1: // POST
+
+		// 解析请求体模板
+		bodyStr, err := s.parseTemplate(config.QueryTemplate, inputData)
+		if err != nil {
+			return "", fmt.Errorf("解析请求体模板失败: %v", err)
+		}
+
+		body := strings.NewReader(bodyStr)
+		req, err = http.NewRequest("POST", reqUrl, body)
+		if err != nil {
+			return "", fmt.Errorf("创建POST请求失败: %v", err)
+		}
+
+		// 设置默认内容类型
+		req.Header.Set("Content-Type", "application/json")
+	default:
+		return "", fmt.Errorf("不支持的HTTP方法: %d", config.HTTPMethod)
+	}
+	req.Header.Set("JWT-TOKEN", jwtToken)
+	// 发送请求
+	resp, err := s.httpClient.Do(req)
+	if err != nil {
+		return "", fmt.Errorf("发送请求失败: %v", err)
+	}
+	defer resp.Body.Close()
+
+	// 读取响应
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return "", fmt.Errorf("读取响应失败: %v", err)
+	}
+
+	responseString := string(body)
+
+	if resp.StatusCode != http.StatusOK {
+		return "", fmt.Errorf("请求失败,状态码: %d,响应: %s", resp.StatusCode, responseString)
+	}
+
+	return responseString, nil
+}
+
+// extractFields 提取指定字段
+func (s *MetricProcessConfigService) extractFields(rules map[string]string, responseBody string) map[string]interface{} {
+	ret := make(map[string]interface{})
+	for field, rule := range rules {
+		result := gjson.Get(responseBody, rule)
+		ret[field] = result.Value()
+	}
+	return ret
+}
+
+// parseTemplate 解析模板
+func (s *MetricProcessConfigService) parseTemplate(tpl string, data map[string]interface{}) (string, error) {
+	if tpl == "" {
+		return "", nil
+	}
+	funcMap := template.FuncMap{
+		"SplitAndFetch": utils.SplitAndFetch,
+	}
+	// 创建模板
+	t, err := template.New("").Funcs(funcMap).Parse(tpl)
+	if err != nil {
+		return "", fmt.Errorf("解析模板失败: %v", err)
+	}
+
+	// 执行模板
+	var buf bytes.Buffer
+	if err := t.Execute(&buf, data); err != nil {
+		return "", fmt.Errorf("执行模板失败: %v", err)
+	}
+
+	return buf.String(), nil
+}

+ 81 - 0
service/user.go

@@ -0,0 +1,81 @@
+package service
+
+import (
+	"errors"
+	
+	"golang.org/x/crypto/bcrypt"
+	
+	"newaterobot-process/entity"
+	"newaterobot-process/model"
+)
+
+// UserService 用户服务
+type UserService struct {
+	userModel *model.UserModel
+}
+
+// NewUserService 创建用户服务实例
+func NewUserService() *UserService {
+	return &UserService{
+		userModel: &model.UserModel{},
+	}
+}
+
+// Register 用户注册
+func (s *UserService) Register(username, password, email, nickname string) (*entity.User, error) {
+	// 检查用户名是否已存在
+	_, err := s.userModel.GetByUsername(username)
+	if err == nil {
+		return nil, errors.New("用户名已存在")
+	}
+
+	// 密码加密
+	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
+	if err != nil {
+		return nil, err
+	}
+
+	// 创建用户
+	user := &entity.User{
+		Username: username,
+		Password: string(hashedPassword),
+		Email:    email,
+		Nickname: nickname,
+		Status:   1,
+	}
+
+	err = s.userModel.Create(user)
+	if err != nil {
+		return nil, err
+	}
+
+	return user, nil
+}
+
+// Login 用户登录
+func (s *UserService) Login(username, password string) (string, error) {
+	// 获取用户信息
+	user, err := s.userModel.GetByUsername(username)
+	if err != nil {
+		return "", errors.New("用户名或密码错误")
+	}
+
+	// 验证密码
+	err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
+	if err != nil {
+		return "", errors.New("用户名或密码错误")
+	}
+
+	// 生成token
+	token, err := GetJWTService().GenerateToken(user.ID, user.Username)
+	if err != nil {
+		return "", err
+	}
+
+	return token, nil
+}
+
+// GetUserByID 根据ID获取用户信息
+func (s *UserService) GetUserByID(id uint) (*entity.User, error) {
+	return s.userModel.GetByID(id)
+}

+ 58 - 0
utils/common.go

@@ -0,0 +1,58 @@
+package utils
+
+import (
+	"crypto/md5"
+	"fmt"
+	"regexp"
+	"strings"
+)
+
+// MD5 字符串MD5加密
+func MD5(s string) string {
+	data := []byte(s)
+	has := md5.Sum(data)
+	return fmt.Sprintf("%x", has)
+}
+
+// IsEmail 验证邮箱格式
+func IsEmail(email string) bool {
+	pattern := `^[0-9a-z][_.0-9a-z-]{0,31}@([0-9a-z][0-9a-z-]{0,30}[0-9a-z]\.){1,4}[a-z]{2,4}$`
+	reg := regexp.MustCompile(pattern)
+	return reg.MatchString(email)
+}
+
+// IsMobile 验证手机号格式
+func IsMobile(mobile string) bool {
+	pattern := `^1[3-9]\d{9}$`
+	reg := regexp.MustCompile(pattern)
+	return reg.MatchString(mobile)
+}
+
+// RemoveSpaces 去除字符串中的空格
+func RemoveSpaces(s string) string {
+	return strings.ReplaceAll(s, " ", "")
+}
+
+// Substr 字符串截取
+func Substr(s string, start, length int) string {
+	runes := []rune(s)
+	total := len(runes)
+
+	if start >= total {
+		return ""
+	}
+
+	end := start + length
+	if end > total {
+		end = total
+	}
+
+	return string(runes[start:end])
+}
+func SplitAndFetch(s, sep string, index int) string {
+	arr := strings.Split(s, sep)
+	if index >= len(arr) {
+		return ""
+	}
+	return arr[index]
+}

+ 134 - 0
utils/http.go

@@ -0,0 +1,134 @@
+package utils
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"time"
+)
+
+// HTTPClient HTTP客户端工具
+type HTTPClient struct {
+	client *http.Client
+}
+
+// NewHTTPClient 创建HTTP客户端实例
+func NewHTTPClient(timeout int) *HTTPClient {
+	return &HTTPClient{
+		client: &http.Client{
+			Timeout: time.Duration(timeout) * time.Second,
+		},
+	}
+}
+
+// Get 发送GET请求
+func (h *HTTPClient) Get(url string, headers map[string]string) ([]byte, error) {
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	// 添加请求头
+	for key, value := range headers {
+		req.Header.Set(key, value)
+	}
+
+	return h.doRequest(req)
+}
+
+// Post 发送POST请求
+func (h *HTTPClient) Post(url string, data interface{}, headers map[string]string) ([]byte, error) {
+	// 序列化数据
+	jsonData, err := json.Marshal(data)
+	if err != nil {
+		return nil, err
+	}
+
+	// 创建请求
+	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
+	if err != nil {
+		return nil, err
+	}
+
+	// 设置Content-Type
+	req.Header.Set("Content-Type", "application/json")
+
+	// 添加其他请求头
+	for key, value := range headers {
+		req.Header.Set(key, value)
+	}
+
+	return h.doRequest(req)
+}
+
+// Put 发送PUT请求
+func (h *HTTPClient) Put(url string, data interface{}, headers map[string]string) ([]byte, error) {
+	// 序列化数据
+	jsonData, err := json.Marshal(data)
+	if err != nil {
+		return nil, err
+	}
+
+	// 创建请求
+	req, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonData))
+	if err != nil {
+		return nil, err
+	}
+
+	// 设置Content-Type
+	req.Header.Set("Content-Type", "application/json")
+
+	// 添加其他请求头
+	for key, value := range headers {
+		req.Header.Set(key, value)
+	}
+
+	return h.doRequest(req)
+}
+
+// Delete 发送DELETE请求
+func (h *HTTPClient) Delete(url string, headers map[string]string) ([]byte, error) {
+	req, err := http.NewRequest("DELETE", url, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	// 添加请求头
+	for key, value := range headers {
+		req.Header.Set(key, value)
+	}
+
+	return h.doRequest(req)
+}
+
+// doRequest 执行HTTP请求
+func (h *HTTPClient) doRequest(req *http.Request) ([]byte, error) {
+	// 创建带超时的上下文
+	ctx, cancel := context.WithTimeout(context.Background(), h.client.Timeout)
+	defer cancel()
+	
+	req = req.WithContext(ctx)
+
+	// 发送请求
+	resp, err := h.client.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	// 读取响应
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	// 检查响应状态码
+	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
+		return nil, fmt.Errorf("HTTP请求失败,状态码: %d,响应内容: %s", resp.StatusCode, string(body))
+	}
+
+	return body, nil
+}

+ 66 - 0
utils/logger.go

@@ -0,0 +1,66 @@
+package utils
+
+import (
+	"fmt"
+	"log"
+	"os"
+)
+
+// Logger 日志结构体
+type Logger struct {
+	infoLogger  *log.Logger
+	errorLogger *log.Logger
+}
+
+var logger *Logger
+
+// InitLogger 初始化日志组件
+func InitLogger() {
+	// 创建日志文件
+	file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
+	if err != nil {
+		log.Fatalln("无法创建日志文件:", err)
+	}
+
+	// 初始化日志记录器
+	logger = &Logger{
+		infoLogger:  log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile),
+		errorLogger: log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile),
+	}
+}
+
+// Info 记录信息日志
+func Info(v ...interface{}) {
+	if logger != nil {
+		logger.infoLogger.Println(v...)
+	} else {
+		log.Println("[INFO]", fmt.Sprint(v...))
+	}
+}
+
+// Error 记录错误日志
+func Error(v ...interface{}) {
+	if logger != nil {
+		logger.errorLogger.Println(v...)
+	} else {
+		log.Println("[ERROR]", fmt.Sprint(v...))
+	}
+}
+
+// Infof 记录格式化信息日志
+func Infof(format string, v ...interface{}) {
+	if logger != nil {
+		logger.infoLogger.Printf(format, v...)
+	} else {
+		log.Printf("[INFO] "+format, v...)
+	}
+}
+
+// Errorf 记录格式化错误日志
+func Errorf(format string, v ...interface{}) {
+	if logger != nil {
+		logger.errorLogger.Printf(format, v...)
+	} else {
+		log.Printf("[ERROR] "+format, v...)
+	}
+}