From 493a09c71521718e56ed283be10eeae043e9a96a Mon Sep 17 00:00:00 2001
From: shukan <755397041@qq.com>
Date: Mon, 11 Aug 2025 10:41:11 +0800
Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/.gitignore | 8 +
.idea/iot-mqtt-gin.iml | 9 +
.idea/modules.xml | 8 +
common/httpclient.go | 115 +++++++++
config/api.go | 6 +
config/db.go | 5 +
config/emqx.go | 9 +
config/mqsdk.go | 62 +++++
config/tdengine.go | 32 +++
controllers/common.go | 82 +++++++
controllers/device.go | 320 ++++++++++++++++++++++++
controllers/message.go | 3 +
controllers/mqttuser.go | 64 +++++
dao/dao.go | 42 ++++
dao/tdengine.go | 64 +++++
docs/docs.go | 174 +++++++++++++
docs/swagger.json | 154 ++++++++++++
docs/swagger.yaml | 105 ++++++++
dto/mqttuser_dto.go | 12 +
dto/sys_dto.go | 10 +
go.mod | 79 ++++++
go.sum | 224 +++++++++++++++++
main.go | 75 ++++++
models/baseDevice.go | 20 ++
models/device.go | 51 ++++
models/deviceMqttLog.go | 58 +++++
models/mqsdk.go | 69 ++++++
models/mqttuser.go | 34 +++
models/switch.go | 21 ++
mqtt/client.go | 109 +++++++++
msgtype/message.go | 100 ++++++++
pkg/logger/logger.go | 149 ++++++++++++
router/routers.go | 39 +++
runtime/log/error_2025-08-01.log | 40 +++
runtime/log/error_2025-08-02.log | 219 +++++++++++++++++
runtime/log/error_2025-08-04.log | 5 +
runtime/log/error_2025-08-07.log | 378 +++++++++++++++++++++++++++++
runtime/log/error_2025-08-08.log | 2 +
runtime/log/success_2025-07-30.log | 58 +++++
runtime/log/success_2025-07-31.log | 58 +++++
runtime/log/success_2025-08-01.log | 70 ++++++
runtime/log/success_2025-08-02.log | 61 +++++
runtime/log/success_2025-08-04.log | 21 ++
runtime/log/success_2025-08-07.log | 20 ++
runtime/log/success_2025-08-08.log | 22 ++
service/deviceService.go | 45 ++++
service/mq_service.go | 153 ++++++++++++
47 files changed, 3464 insertions(+)
create mode 100644 .idea/.gitignore
create mode 100644 .idea/iot-mqtt-gin.iml
create mode 100644 .idea/modules.xml
create mode 100644 common/httpclient.go
create mode 100644 config/api.go
create mode 100644 config/db.go
create mode 100644 config/emqx.go
create mode 100644 config/mqsdk.go
create mode 100644 config/tdengine.go
create mode 100644 controllers/common.go
create mode 100644 controllers/device.go
create mode 100644 controllers/message.go
create mode 100644 controllers/mqttuser.go
create mode 100644 dao/dao.go
create mode 100644 dao/tdengine.go
create mode 100644 docs/docs.go
create mode 100644 docs/swagger.json
create mode 100644 docs/swagger.yaml
create mode 100644 dto/mqttuser_dto.go
create mode 100644 dto/sys_dto.go
create mode 100644 go.mod
create mode 100644 go.sum
create mode 100644 main.go
create mode 100644 models/baseDevice.go
create mode 100644 models/device.go
create mode 100644 models/deviceMqttLog.go
create mode 100644 models/mqsdk.go
create mode 100644 models/mqttuser.go
create mode 100644 models/switch.go
create mode 100644 mqtt/client.go
create mode 100644 msgtype/message.go
create mode 100644 pkg/logger/logger.go
create mode 100644 router/routers.go
create mode 100644 runtime/log/error_2025-08-01.log
create mode 100644 runtime/log/error_2025-08-02.log
create mode 100644 runtime/log/error_2025-08-04.log
create mode 100644 runtime/log/error_2025-08-07.log
create mode 100644 runtime/log/error_2025-08-08.log
create mode 100644 runtime/log/success_2025-07-30.log
create mode 100644 runtime/log/success_2025-07-31.log
create mode 100644 runtime/log/success_2025-08-01.log
create mode 100644 runtime/log/success_2025-08-02.log
create mode 100644 runtime/log/success_2025-08-04.log
create mode 100644 runtime/log/success_2025-08-07.log
create mode 100644 runtime/log/success_2025-08-08.log
create mode 100644 service/deviceService.go
create mode 100644 service/mq_service.go
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/iot-mqtt-gin.iml b/.idea/iot-mqtt-gin.iml
new file mode 100644
index 0000000..5e764c4
--- /dev/null
+++ b/.idea/iot-mqtt-gin.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..6befa84
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/httpclient.go b/common/httpclient.go
new file mode 100644
index 0000000..2f7ef13
--- /dev/null
+++ b/common/httpclient.go
@@ -0,0 +1,115 @@
+package common
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "iot-mqtt-gin/config"
+ "net/http"
+ "time"
+)
+
+// HTTPClient 封装HTTP客户端
+type HTTPClient struct {
+ client *http.Client
+ headers map[string]string
+}
+
+// NewHTTPClient 创建新的HTTP客户端实例
+// timeout: 超时时间(秒)
+func NewHTTPClient(timeout int) *HTTPClient {
+ return &HTTPClient{
+ client: &http.Client{
+ Timeout: time.Duration(timeout) * time.Second,
+ },
+ headers: make(map[string]string),
+ }
+}
+
+// SetHeader 设置请求头
+func (c *HTTPClient) SetHeader(key, value string) {
+ c.headers[key] = value
+}
+
+// Get 发送GET请求
+func (c *HTTPClient) Get(url string, params map[string]string) (response []byte, statusCode int, err error) {
+ // 创建请求
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ return nil, 0, fmt.Errorf("创建GET请求失败: %v", err)
+ }
+
+ // 设置请求头
+ for k, v := range c.headers {
+ req.Header.Set(k, v)
+ }
+
+ // 添加查询参数
+ q := req.URL.Query()
+ for k, v := range params {
+ q.Add(k, v)
+ }
+ req.URL.RawQuery = q.Encode()
+
+ // 发送请求
+ resp, err := c.client.Do(req)
+ if err != nil {
+ return nil, 0, fmt.Errorf("发送GET请求失败: %v", err)
+ }
+ defer resp.Body.Close()
+
+ // 读取响应内容
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, resp.StatusCode, fmt.Errorf("读取GET响应失败: %v", err)
+ }
+
+ return body, resp.StatusCode, nil
+}
+
+// Post 发送POST请求
+func (c *HTTPClient) Post(url string, data interface{}) (response []byte, statusCode int, err error) {
+ // 序列化请求数据
+ var jsonData []byte
+ switch v := data.(type) {
+ case string:
+ jsonData = []byte(v)
+ default:
+ var err error
+ jsonData, err = json.Marshal(data)
+ if err != nil {
+ return nil, 0, fmt.Errorf("POST数据序列化失败: %v", err)
+ }
+ }
+
+ // 创建请求
+ req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
+ if err != nil {
+ return nil, 0, fmt.Errorf("创建POST请求失败: %v", err)
+ }
+
+ // 设置默认Header
+ if _, ok := c.headers["Content-Type"]; !ok {
+ c.SetHeader("Content-Type", "application/json")
+ c.SetHeader("Authorization", config.APIToken)
+ }
+ for k, v := range c.headers {
+ req.Header.Set(k, v)
+ }
+
+ // 发送请求
+ resp, err := c.client.Do(req)
+ if err != nil {
+ return nil, 0, fmt.Errorf("发送POST请求失败: %v", err)
+ }
+ defer resp.Body.Close()
+
+ // 读取响应内容
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, resp.StatusCode, fmt.Errorf("读取POST响应失败: %v", err)
+ }
+
+ return body, resp.StatusCode, nil
+}
diff --git a/config/api.go b/config/api.go
new file mode 100644
index 0000000..68005a8
--- /dev/null
+++ b/config/api.go
@@ -0,0 +1,6 @@
+package config
+
+const (
+ APIToken = "Bearer kCVAha_qe4wM6i6C_cwrlmxbtHR40yCJ"
+ APIBaseUrl = "https://www.cdsrh.top:443/"
+)
diff --git a/config/db.go b/config/db.go
new file mode 100644
index 0000000..2654eef
--- /dev/null
+++ b/config/db.go
@@ -0,0 +1,5 @@
+package config
+
+const (
+ Mysqldb = "device_management:12345678@tcp(124.220.236.38:3306)/device_management?charset=utf8"
+)
diff --git a/config/emqx.go b/config/emqx.go
new file mode 100644
index 0000000..3b2a422
--- /dev/null
+++ b/config/emqx.go
@@ -0,0 +1,9 @@
+package config
+
+const (
+ MQTTBroker = "tcp://124.220.236.38:1883"
+ MQTTClientID = "iot-server"
+ MQTTUsername = "emqx_u"
+ MQTTPassword = "public"
+ MQTTQoS = 1
+)
diff --git a/config/mqsdk.go b/config/mqsdk.go
new file mode 100644
index 0000000..2373688
--- /dev/null
+++ b/config/mqsdk.go
@@ -0,0 +1,62 @@
+package config
+
+import (
+ mqsd "github.com/yyboo586/MQSDK"
+)
+
+// MQ类型常量
+const (
+ MQTypeNSQ = "nsq"
+ MQTypeKafka = "kafka"
+ MQTypeRabbitMQ = "rabbitmq"
+)
+
+// 连接配置常量
+const (
+ // NSQ配置
+ NSQDAddress = "124.220.236.38:4150"
+ NSQLookupdAddres = "124.220.236.38:4161"
+
+ // Kafka配置
+ KafkaBrokerList = "124.220.236.38:9092"
+ KafkaConsumerGroup = "gin-demo-group"
+ KafkaVersion = "2.0.0"
+
+ // RabbitMQ配置
+ RabbitMQURL = "amqp://guest:guest@localhost:5672/"
+ RabbitMQExchange = "gin-demo-exchange"
+ RabbitMQRoutingKey = "demo.key"
+
+ //Topic
+ DeviceOnlineTopic = "core.device.online"
+ DeviceOfflineTopic = "core.device.offline"
+ DeviceAlarmTopic = "core.device.alarm"
+)
+
+// GetNSQConfig 获取NSQ配置
+func GetNSQConfig() *mqsd.NSQConfig {
+ return &mqsd.NSQConfig{
+ Type: MQTypeNSQ,
+ NSQDAddr: NSQDAddress,
+ NSQLookup: []string{NSQLookupdAddres},
+ }
+}
+
+// GetKafkaConfig 获取Kafka配置
+func GetKafkaConfig() *mqsd.KafkaConfig {
+ return &mqsd.KafkaConfig{
+ Type: MQTypeKafka,
+ Brokers: []string{KafkaBrokerList},
+ GroupID: KafkaConsumerGroup,
+ Version: KafkaVersion,
+ }
+}
+
+// GetRabbitMQConfig 获取RabbitMQ配置
+func GetRabbitMQConfig() *mqsd.RabbitMQConfig {
+ return &mqsd.RabbitMQConfig{
+ Type: MQTypeRabbitMQ,
+ URL: RabbitMQURL,
+ Exchange: RabbitMQExchange,
+ }
+}
diff --git a/config/tdengine.go b/config/tdengine.go
new file mode 100644
index 0000000..08eaaf2
--- /dev/null
+++ b/config/tdengine.go
@@ -0,0 +1,32 @@
+package config
+
+// TDengine数据库配置
+const (
+ TDengineHost = "123.56.105.137" // TDengine服务器地址
+ TDenginePort = 6030 // TDengine端口
+ TDengineUser = "root" // 用户名
+ TDenginePassword = "taosdata" // 密码
+ TDengineDatabase = "iotdb" // 数据库名
+ TDengineTimeout = 30 // 连接超时时间(秒)
+ TDengineCharset = "UTF8" // 字符集
+)
+
+// 数据表配置
+const (
+ StableName = "device_data_stable" // 超级表名(设备数据超级表)
+ SubTablePrefix = "device_" // 子表前缀(每个设备一个子表)
+)
+
+// 业务常量
+const (
+ MaxQueryRecords = 1000 // 最大查询记录数
+)
+
+// 设备数据字段常量
+const (
+ FieldOrgID = "org_id"
+ FieldDeviceID = "device_id"
+ FieldValue = "val"
+ FieldStatus = "status"
+ FieldTimestamp = "ts"
+)
diff --git a/controllers/common.go b/controllers/common.go
new file mode 100644
index 0000000..94f2496
--- /dev/null
+++ b/controllers/common.go
@@ -0,0 +1,82 @@
+package controllers
+
+import (
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/base64"
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "net/http"
+)
+
+type JsonStruct struct {
+ Code int `json:"code"`
+ Msg interface{} `json:"msg"`
+ Data interface{} `json:"data"`
+ Count int64 `json:"count"`
+}
+
+func ReturnSuccess(c *gin.Context, code int, msg interface{}, data interface{}, count int64) {
+ json := &JsonStruct{
+ Code: code,
+ Msg: msg,
+ Data: data,
+ Count: count,
+ }
+ c.JSON(http.StatusOK, json)
+}
+
+func ReturnError(c *gin.Context, code int, msg interface{}) {
+ json := &JsonErrStruct{
+ Code: code,
+ Msg: msg,
+ }
+ c.JSON(http.StatusBadRequest, json)
+}
+
+type JsonErrStruct struct {
+ Code int `json:"code"`
+ Msg interface{} `json:"msg"`
+}
+
+// 生成指定长度的随机盐值
+// length: 盐值的长度
+// 返回值: 生成的盐值字符串和可能的错误
+func GenerateRandomSalt(length int) (string, error) {
+ // 计算需要多少字节才能生成指定长度的base64字符串
+ // base64编码中,每3字节会被编码为4个字符
+ bytesNeeded := (length * 3) / 4
+ if (length*3)%4 != 0 {
+ bytesNeeded++
+ }
+
+ // 生成随机字节
+ randomBytes := make([]byte, bytesNeeded)
+ _, err := rand.Read(randomBytes)
+ if err != nil {
+ return "", err
+ }
+
+ // 编码为base64字符串,去掉可能的填充字符'='
+ salt := base64.URLEncoding.EncodeToString(randomBytes)
+ if len(salt) > length {
+ salt = salt[:length]
+ }
+
+ return salt, nil
+}
+
+// 生成带后缀盐的密码
+// password: 原始密码
+// salt: 盐值
+// 返回值: 密码+盐值的组合字符串sha256加密
+func Sha256Encrypt(password, salt string) string {
+ // 拼接密码和盐值(采用suffix模式:密码+盐值)
+ combined := []byte(password + salt)
+
+ // 计算SHA256哈希
+ hash := sha256.Sum256(combined)
+
+ // 转换为十六进制字符串(32字节哈希 -> 64字符)
+ return fmt.Sprintf("%x", hash)
+}
diff --git a/controllers/device.go b/controllers/device.go
new file mode 100644
index 0000000..34fc6cb
--- /dev/null
+++ b/controllers/device.go
@@ -0,0 +1,320 @@
+package controllers
+
+import (
+ "context"
+ "encoding/json"
+ "github.com/gin-gonic/gin"
+ mqsd "github.com/yyboo586/MQSDK"
+ "iot-mqtt-gin/common"
+ "iot-mqtt-gin/config"
+ "iot-mqtt-gin/dto"
+ "iot-mqtt-gin/models"
+ "iot-mqtt-gin/mqtt"
+ "iot-mqtt-gin/pkg/logger"
+ _ "iot-mqtt-gin/pkg/logger"
+ "iot-mqtt-gin/service"
+ "log"
+ "net/http"
+ "strings"
+ "sync"
+ "time"
+)
+
+// DeviceController 设备控制器
+type DeviceController struct {
+ devices map[string]models.Device
+ mqttClient *mqtt.Client // 注意:这里是指针类型,需要确保不为nil
+ mu sync.RWMutex
+ client *common.HTTPClient
+ nsqProducer mqsd.Producer
+ kafkaProducer mqsd.Producer
+ rabbitProducer mqsd.Producer
+}
+
+// NewDeviceController 创建设备控制器
+func NewDeviceController(
+ mqttClient *mqtt.Client,
+ nsqProducer mqsd.Producer,
+) *DeviceController {
+ httpClient := common.NewHTTPClient(10) // 10秒超时
+ controller := &DeviceController{
+ devices: make(map[string]models.Device),
+ mqttClient: mqttClient,
+ client: httpClient,
+ nsqProducer: nsqProducer,
+ }
+
+ return controller
+}
+
+var (
+ LogChan = make(chan models.DeviceMqttLog, 1000) // 日志通道,带缓冲
+)
+
+// 初始化批量写入协程
+func init() {
+ go StartLogWorker()
+}
+
+func StartLogWorker() {
+ ticker := time.NewTicker(100 * time.Millisecond) // 缩短定时周期支持1条也写入
+ defer ticker.Stop()
+ batchLogs := make([]models.DeviceMqttLog, 0, 10)
+ deviceLogModel := models.DeviceMqttLog{}
+ for {
+ select {
+ case log, ok := <-LogChan:
+ batchLogs = append(batchLogs, log)
+ if !ok {
+ // 数据通道已关闭,处理剩余数据
+ if len(batchLogs) > 0 {
+ if err := deviceLogModel.BatchAddLogs(batchLogs); err != nil {
+ logger.Error(map[string]interface{}{"批量写入日志失败": err.Error()})
+ }
+ }
+ return
+ }
+ // 当积累到1条时立即写入
+ if len(batchLogs) >= 1 {
+ if err := deviceLogModel.BatchAddLogs(batchLogs); err != nil {
+ logger.Error(map[string]interface{}{"批量写入日志失败": err.Error()})
+ }
+ // 如果只有一条就单条写入,如果多条就批量写入
+ if len(batchLogs) == 1 {
+ if err := deviceLogModel.AddLog(); err != nil {
+ logger.Error(map[string]interface{}{"写入日志失败": err.Error()})
+ }
+ } else {
+ if err := deviceLogModel.BatchAddLogs(batchLogs); err != nil {
+ logger.Error(map[string]interface{}{"批量写入日志失败": err.Error()})
+ }
+ }
+ batchLogs = batchLogs[:0] // 清空切片
+ }
+ case <-ticker.C:
+ // 定时写入剩余的日志
+ if len(batchLogs) > 0 {
+ if len(batchLogs) == 1 {
+ if err := deviceLogModel.AddLog(); err != nil {
+ logger.Error(map[string]interface{}{"定时写入日志失败": err.Error()})
+ }
+ } else {
+ if err := deviceLogModel.BatchAddLogs(batchLogs); err != nil {
+ logger.Error(map[string]interface{}{"定时批量写入日志失败": err.Error()})
+ }
+ }
+ batchLogs = batchLogs[:0] // 清空切片
+ }
+ }
+ }
+}
+
+// HandleMQTTMessage 处理MQTT消息
+func (d *DeviceController) HandleMQTTMessage(topic string, payload []byte) {
+ log.Printf("收到MQTT消息 - 主题: %s, 内容: %s", topic, payload)
+ parts := strings.Split(topic, "/")
+ if len(parts) < 3 {
+ log.Printf("无效的主题格式: %s", topic)
+ return
+ }
+ deviceID := parts[1]
+ action := parts[2]
+ deviceModel := models.Device{}
+ // 检查设备是否存在
+ d.mu.RLock()
+ deviceInfo, err := deviceModel.CheckDevice(deviceID)
+ d.mu.RUnlock()
+ if err != nil || deviceInfo.ID <= 0 {
+ return
+ }
+ switch action {
+ case "connected":
+
+ //d.handleConnectedMessage(deviceInfo.ID, deviceInfo.DeviceKey, deviceInfo.OrgId, topic, payload)
+ // 创建消息
+ // 构建日志数据
+ deviceLogData := models.DeviceMqSdkLog{
+ DeviceId: deviceInfo.ID,
+ DeviceKey: deviceInfo.DeviceKey,
+ DeviceName: deviceInfo.Name,
+ OrgId: deviceInfo.OrgId,
+ }
+ //body, _ := json.Marshal(deviceLogData)
+ msg := models.NewMessage(config.DeviceOnlineTopic, deviceLogData, "nsq-devonline")
+ // 发布消息
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+ if err := d.nsqProducer.Publish(ctx, config.DeviceOnlineTopic, msg.ToMQSDMessage()); err != nil {
+ logger.Error(map[string]interface{}{"发布NSQ消息失败: ": err.Error()})
+ return
+ }
+ case "disconnected":
+ //d.handleDisconnected(deviceInfo.ID, deviceInfo.DeviceKey, deviceInfo.OrgId, topic, payload)
+ // 构建日志数据
+ deviceLogData := models.DeviceMqSdkLog{
+ DeviceId: deviceInfo.ID,
+ DeviceKey: deviceInfo.DeviceKey,
+ DeviceName: deviceInfo.Name,
+ OrgId: deviceInfo.OrgId,
+ }
+ //body, _ := json.Marshal(deviceLogData)
+ msg := models.NewMessage(config.DeviceOfflineTopic, deviceLogData, "nsq-devonline")
+ // 发布消息
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+ if err := d.nsqProducer.Publish(ctx, config.DeviceOfflineTopic, msg.ToMQSDMessage()); err != nil {
+ logger.Error(map[string]interface{}{"发布NSQ消息失败: ": err.Error()})
+ return
+ }
+ default:
+ // 当所有case都不匹配时执行,不做处理
+ return
+ }
+}
+
+// 处理上线消息
+func (d *DeviceController) handleConnectedMessage(deviceID uint, deviceKey, orgId, topic string, payload []byte) {
+ // 转换payload为字符串并去除可能的空白字符
+ payloadStr := strings.TrimSpace(string(payload))
+ if payloadStr == "devconnected" {
+ // 构建日志数据
+ deviceLogData := models.DeviceMqttLog{
+ DeviceId: deviceID,
+ DeviceKey: deviceKey,
+ OrgId: orgId,
+ }
+ //fmt.Println("收到的消息", payload)
+ contentMap := make(map[string]interface{})
+ currentTime := time.Now()
+ contentMap["createtime"] = currentTime.Format("2006-01-02 15:04:05")
+ contentMap["msg"] = "设备:" + deviceKey + "上线"
+ jsonBytes, _ := json.Marshal(contentMap)
+ deviceLogData.Type = 1
+ deviceLogData.Content = string(jsonBytes)
+ /*if err := deviceLogData.AddLog(); err != nil {
+ logger.Error(map[string]interface{}{"写入日志失败": err.Error()})
+ }*/
+ select {
+ case LogChan <- deviceLogData:
+ default:
+ // 通道满时降级为直接写入
+ if err := deviceLogData.AddLog(); err != nil {
+ logger.Error(map[string]interface{}{"通道满,直接写入日志失败": err.Error()})
+ }
+ }
+ }
+}
+
+// 处理下线消息
+func (d *DeviceController) handleDisconnected(deviceID uint, deviceKey, orgId, topic string, payload []byte) {
+ // 转换payload为字符串并去除可能的空白字符
+ payloadStr := strings.TrimSpace(string(payload))
+ if payloadStr == "devdisconnected" {
+ // 构建日志数据
+ deviceLogData := models.DeviceMqttLog{
+ DeviceId: deviceID,
+ DeviceKey: deviceKey,
+ OrgId: orgId,
+ }
+ //fmt.Println("收到的消息", payload)
+ contentMap := make(map[string]interface{})
+ currentTime := time.Now()
+ contentMap["createtime"] = currentTime.Format("2006-01-02 15:04:05")
+ contentMap["msg"] = "设备:" + deviceKey + "下线"
+ jsonBytes, _ := json.Marshal(contentMap)
+ deviceLogData.Type = 2
+ deviceLogData.Content = string(jsonBytes)
+ /*if err := deviceLogData.AddLog(); err != nil {
+ logger.Error(map[string]interface{}{"写入日志失败": err.Error()})
+ }*/
+ select {
+ case LogChan <- deviceLogData:
+ default:
+ // 通道满时降级为直接写入
+ if err := deviceLogData.AddLog(); err != nil {
+ logger.Error(map[string]interface{}{"通道满,直接写入日志失败": err.Error()})
+ }
+ }
+ //调用设备告警接口写入数据
+ reqtUrl := config.APIBaseUrl + "api/v1/device-management/devices/logs"
+ // 构建POST请求参数
+ contentLogMap := make(map[string]interface{})
+ contentLogMap["details"] = contentMap
+ contentLogMap["message"] = "设备:" + deviceKey + "下线"
+ postData := make(map[string]interface{})
+ postData["type"] = 3
+ postData["created_at"] = currentTime.Format("2006-01-02 15:04:05")
+ postData["org_id"] = orgId
+ postData["device_id"] = deviceID
+ postData["device_key"] = deviceKey
+ postData["content"] = contentLogMap
+ go func() {
+ resp, _, _ := d.client.Post(reqtUrl, postData)
+ // 定义结构体对应JSON结构
+ type Response struct {
+ Code int `json:"code"`
+ Message string `json:"message"`
+ }
+ var respJson Response
+ _ = json.Unmarshal([]byte(resp), &respJson)
+ if respJson.Code != 0 {
+ logger.Error(map[string]interface{}{"写入报警日志失败": respJson.Message})
+ }
+ }()
+ }
+}
+
+// DeviceOnline 订阅设备上下线
+// @Summary 订阅设备上下线
+// @Description 订阅设备上下线
+// @Tags 设备管理
+// @Accept json
+// @Produce json
+// @Param device_number path string true "设备编号"
+// @Success 200 {object} map[string]interface{}
+// @Failure 400 {object} map[string]interface{}
+// @Failure 409 {object} map[string]interface{}
+// @Failure 500 {object} map[string]interface{}
+// @Router /devices/{device_number}/onoff [get]
+func (d *DeviceController) DeviceOnOff(c *gin.Context) {
+ var req dto.SystemDeviceRequest
+ if err := c.ShouldBindUri(&req); err != nil {
+ ReturnError(c, 400, "请求参数错误: "+err.Error())
+ }
+ //查询设备是否存在
+ // 检查设备是否已存在
+ deviceModel := models.Device{}
+ deviceInfo, err := deviceModel.CheckDevice(req.DeviceNumber)
+ if err != nil || deviceInfo.ID <= 0 {
+ ReturnError(c, 400, "设备不存在")
+ return
+ }
+ //订阅设备泛型
+ //topic := "sys/" + req.DeviceNumber + "/connected"
+ topic := "sys/#"
+ if err := d.mqttClient.Subscribe(topic); err != nil {
+ ReturnError(c, 400, "订阅主题: "+topic+"失败"+err.Error())
+ }
+ ReturnSuccess(c, 200, "订阅成功", "", 0)
+}
+
+func (d *DeviceController) TDengineTest(c *gin.Context) {
+ var data models.DeviceData
+ currentTime := time.Now()
+ data.OrgID = "1"
+ data.DeviceID = "2"
+ data.Status = true
+ data.Value = 64
+ data.Timestamp = currentTime
+
+ if err := service.InsertDeviceData(&data); err != nil {
+ c.JSON(http.StatusInternalServerError, gin.H{
+ "error": err.Error(),
+ })
+ return
+ }
+
+ c.JSON(http.StatusOK, gin.H{
+ "message": "数据插入成功",
+ })
+}
diff --git a/controllers/message.go b/controllers/message.go
new file mode 100644
index 0000000..26e9aef
--- /dev/null
+++ b/controllers/message.go
@@ -0,0 +1,3 @@
+package controllers
+
+type MessageController struct{}
diff --git a/controllers/mqttuser.go b/controllers/mqttuser.go
new file mode 100644
index 0000000..36a5260
--- /dev/null
+++ b/controllers/mqttuser.go
@@ -0,0 +1,64 @@
+package controllers
+
+import (
+ "github.com/gin-gonic/gin"
+ "iot-mqtt-gin/dto"
+ "iot-mqtt-gin/models"
+)
+
+type MqttuserController struct {
+ mqttuser map[string]models.Mqttuser
+}
+
+func NewMqttuserController() *MqttuserController {
+ return &MqttuserController{
+ mqttuser: make(map[string]models.Mqttuser),
+ }
+}
+
+// User 定义用户模型
+/*type User struct {
+ ID int `json:"id" example:"1"`
+ Username string `json:"username" example:"john_doe"`
+}*/
+
+// AddUser 添加mqtt认证用户
+// @Summary 添加mqtt认证用户
+// @Description 添加mqtt认证用户信息
+// @Tags mqtt认证用户管理
+// @Accept json
+// @Produce json
+// @Param user body dto.CreateMqttuserRequest true "用户信息"
+// @Success 200 {object} dto.CreateMqttuserResponse
+// @Failure 400 {object} map[string]interface{}
+// @Failure 409 {object} map[string]interface{}
+// @Failure 500 {object} map[string]interface{}
+// @Router /mqttuser/add [post]
+func (u MqttuserController) AddUser(c *gin.Context) {
+ // 绑定请求参数到DTO
+ var req dto.CreateMqttuserRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ ReturnError(c, 400, "请求参数错误: "+err.Error())
+ }
+ randomSalt, _ := GenerateRandomSalt(6)
+ passwordHash := Sha256Encrypt(req.PasswordHash, randomSalt)
+ mqttuser := models.Mqttuser{
+ Username: req.Username,
+ PasswordHash: passwordHash,
+ Salt: randomSalt,
+ }
+ // 检查用户是否已存在
+ existsFlag, msg := mqttuser.Exists()
+ if existsFlag {
+ ReturnError(c, 400, msg)
+ }
+ // 创建用户
+ if err := mqttuser.AddUser(); err != nil {
+ ReturnError(c, 400, "创建用户失败: "+err.Error())
+ }
+ // 转换数据模型到响应DTO
+ response := dto.CreateMqttuserResponse{
+ ID: mqttuser.ID,
+ }
+ ReturnSuccess(c, 200, "success", response, 0)
+}
diff --git a/dao/dao.go b/dao/dao.go
new file mode 100644
index 0000000..de06a76
--- /dev/null
+++ b/dao/dao.go
@@ -0,0 +1,42 @@
+package dao
+
+import (
+ "gorm.io/driver/mysql"
+ "gorm.io/gorm"
+ "iot-mqtt-gin/config"
+ "iot-mqtt-gin/pkg/logger"
+ "time"
+)
+
+var (
+ Db *gorm.DB
+ err error
+)
+
+func init() {
+ Db, err = gorm.Open(mysql.Open(config.Mysqldb), &gorm.Config{
+ //Logger: gormlog.Default.LogMode(gormlog.Info),//打印数据库sql
+ })
+ if err != nil {
+ //打印数据库连接错误信息
+ logger.Error(map[string]interface{}{"mysql connect error": err.Error()})
+ }
+ if Db.Error != nil {
+ //打印数据库连接错误信息
+ logger.Error(map[string]interface{}{"database error": Db.Error})
+ }
+ sqlDB, err := Db.DB()
+ if err != nil {
+ panic("获取数据库连接池失败: " + err.Error())
+ }
+ sqlDB.SetMaxIdleConns(10) // 最大空闲连接数
+ sqlDB.SetMaxOpenConns(100) // 最大打开连接数
+ sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大存活时间
+}
+func CloseDB() {
+ sqlDB, err := Db.DB()
+ if err != nil {
+ return
+ }
+ _ = sqlDB.Close()
+}
diff --git a/dao/tdengine.go b/dao/tdengine.go
new file mode 100644
index 0000000..1f71e9c
--- /dev/null
+++ b/dao/tdengine.go
@@ -0,0 +1,64 @@
+package dao
+
+import (
+ "database/sql"
+ "fmt"
+ "iot-mqtt-gin/config"
+ "sync"
+
+ _ "github.com/taosdata/driver-go/v3/taosSql"
+)
+
+var (
+ dbTdEngine *sql.DB // 全局TDengine连接实例
+ once sync.Once // 确保连接只初始化一次
+)
+
+// InitTDengine 初始化TDengine连接
+func InitTDengine() error {
+ var err error
+ once.Do(func() {
+ // 构建DSN
+ dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?timeout=%ds&charset=%s",
+ config.TDengineUser,
+ config.TDenginePassword,
+ config.TDengineHost,
+ config.TDenginePort,
+ config.TDengineDatabase,
+ config.TDengineTimeout,
+ config.TDengineCharset,
+ )
+ // 打开数据库连接
+ dbTdEngine, err = sql.Open("taosSql", dsn)
+ if err != nil {
+ err = fmt.Errorf("无法打开数据库连接: %v", err)
+ return
+ }
+ // 设置连接池参数
+ dbTdEngine.SetMaxOpenConns(20)
+ dbTdEngine.SetMaxIdleConns(5)
+ dbTdEngine.SetConnMaxLifetime(0) // 永不过期,TDengine长连接更高效
+ // 测试连接
+ if pingErr := dbTdEngine.Ping(); pingErr != nil {
+ err = fmt.Errorf("无法连接到TDengine: %v", pingErr)
+ return
+ }
+ })
+ return err
+}
+
+// GetDB 获取数据库连接
+func GetDB() (*sql.DB, error) {
+ if dbTdEngine == nil {
+ return nil, fmt.Errorf("数据库连接未初始化,请先调用InitTDengine")
+ }
+ return dbTdEngine, nil
+}
+
+// Close 关闭数据库连接
+func Close() error {
+ if dbTdEngine != nil {
+ return dbTdEngine.Close()
+ }
+ return nil
+}
diff --git a/docs/docs.go b/docs/docs.go
new file mode 100644
index 0000000..ada984e
--- /dev/null
+++ b/docs/docs.go
@@ -0,0 +1,174 @@
+// Package docs Code generated by swaggo/swag. DO NOT EDIT
+package docs
+
+import "github.com/swaggo/swag"
+
+const docTemplate = `{
+ "schemes": {{ marshal .Schemes }},
+ "swagger": "2.0",
+ "info": {
+ "description": "{{escape .Description}}",
+ "title": "{{.Title}}",
+ "contact": {},
+ "version": "{{.Version}}"
+ },
+ "host": "{{.Host}}",
+ "basePath": "{{.BasePath}}",
+ "paths": {
+ "/devices/{device_number}/onoff": {
+ "get": {
+ "description": "订阅设备上下线",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "设备管理"
+ ],
+ "summary": "订阅设备上下线",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "设备编号",
+ "name": "device_number",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "409": {
+ "description": "Conflict",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "500": {
+ "description": "Internal Server Error",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ }
+ }
+ }
+ },
+ "/mqttuser/add": {
+ "post": {
+ "description": "添加mqtt认证用户信息",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "mqtt认证用户管理"
+ ],
+ "summary": "添加mqtt认证用户",
+ "parameters": [
+ {
+ "description": "用户信息",
+ "name": "user",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/dto.CreateMqttuserRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/dto.CreateMqttuserResponse"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "409": {
+ "description": "Conflict",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "500": {
+ "description": "Internal Server Error",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "dto.CreateMqttuserRequest": {
+ "type": "object",
+ "required": [
+ "password_hash",
+ "username"
+ ],
+ "properties": {
+ "password_hash": {
+ "type": "string",
+ "minLength": 6
+ },
+ "username": {
+ "type": "string",
+ "maxLength": 50,
+ "minLength": 2
+ }
+ }
+ },
+ "dto.CreateMqttuserResponse": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+}`
+
+// SwaggerInfo holds exported Swagger Info so clients can modify it
+var SwaggerInfo = &swag.Spec{
+ Version: "1.0",
+ Host: "127.0.0.1:9999",
+ BasePath: "/api/v1",
+ Schemes: []string{"http", "https"},
+ Title: "物联网MQTT API",
+ Description: "物联网mqtt api接口",
+ InfoInstanceName: "swagger",
+ SwaggerTemplate: docTemplate,
+ LeftDelim: "{{",
+ RightDelim: "}}",
+}
+
+func init() {
+ swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
+}
diff --git a/docs/swagger.json b/docs/swagger.json
new file mode 100644
index 0000000..ae27aad
--- /dev/null
+++ b/docs/swagger.json
@@ -0,0 +1,154 @@
+{
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "swagger": "2.0",
+ "info": {
+ "description": "物联网mqtt api接口",
+ "title": "物联网MQTT API",
+ "contact": {},
+ "version": "1.0"
+ },
+ "host": "127.0.0.1:9999",
+ "basePath": "/api/v1",
+ "paths": {
+ "/devices/{device_number}/onoff": {
+ "get": {
+ "description": "订阅设备上下线",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "设备管理"
+ ],
+ "summary": "订阅设备上下线",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "设备编号",
+ "name": "device_number",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "409": {
+ "description": "Conflict",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "500": {
+ "description": "Internal Server Error",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ }
+ }
+ }
+ },
+ "/mqttuser/add": {
+ "post": {
+ "description": "添加mqtt认证用户信息",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "mqtt认证用户管理"
+ ],
+ "summary": "添加mqtt认证用户",
+ "parameters": [
+ {
+ "description": "用户信息",
+ "name": "user",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/dto.CreateMqttuserRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/dto.CreateMqttuserResponse"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "409": {
+ "description": "Conflict",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "500": {
+ "description": "Internal Server Error",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "dto.CreateMqttuserRequest": {
+ "type": "object",
+ "required": [
+ "password_hash",
+ "username"
+ ],
+ "properties": {
+ "password_hash": {
+ "type": "string",
+ "minLength": 6
+ },
+ "username": {
+ "type": "string",
+ "maxLength": 50,
+ "minLength": 2
+ }
+ }
+ },
+ "dto.CreateMqttuserResponse": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
new file mode 100644
index 0000000..64158b7
--- /dev/null
+++ b/docs/swagger.yaml
@@ -0,0 +1,105 @@
+basePath: /api/v1
+definitions:
+ dto.CreateMqttuserRequest:
+ properties:
+ password_hash:
+ minLength: 6
+ type: string
+ username:
+ maxLength: 50
+ minLength: 2
+ type: string
+ required:
+ - password_hash
+ - username
+ type: object
+ dto.CreateMqttuserResponse:
+ properties:
+ id:
+ type: integer
+ type: object
+host: 127.0.0.1:9999
+info:
+ contact: {}
+ description: 物联网mqtt api接口
+ title: 物联网MQTT API
+ version: "1.0"
+paths:
+ /devices/{device_number}/onoff:
+ get:
+ consumes:
+ - application/json
+ description: 订阅设备上下线
+ parameters:
+ - description: 设备编号
+ in: path
+ name: device_number
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ additionalProperties: true
+ type: object
+ "400":
+ description: Bad Request
+ schema:
+ additionalProperties: true
+ type: object
+ "409":
+ description: Conflict
+ schema:
+ additionalProperties: true
+ type: object
+ "500":
+ description: Internal Server Error
+ schema:
+ additionalProperties: true
+ type: object
+ summary: 订阅设备上下线
+ tags:
+ - 设备管理
+ /mqttuser/add:
+ post:
+ consumes:
+ - application/json
+ description: 添加mqtt认证用户信息
+ parameters:
+ - description: 用户信息
+ in: body
+ name: user
+ required: true
+ schema:
+ $ref: '#/definitions/dto.CreateMqttuserRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/dto.CreateMqttuserResponse'
+ "400":
+ description: Bad Request
+ schema:
+ additionalProperties: true
+ type: object
+ "409":
+ description: Conflict
+ schema:
+ additionalProperties: true
+ type: object
+ "500":
+ description: Internal Server Error
+ schema:
+ additionalProperties: true
+ type: object
+ summary: 添加mqtt认证用户
+ tags:
+ - mqtt认证用户管理
+schemes:
+- http
+- https
+swagger: "2.0"
diff --git a/dto/mqttuser_dto.go b/dto/mqttuser_dto.go
new file mode 100644
index 0000000..53a254f
--- /dev/null
+++ b/dto/mqttuser_dto.go
@@ -0,0 +1,12 @@
+package dto
+
+// CreateMqttuserRequest 用户创建请求结构体
+type CreateMqttuserRequest struct {
+ Username string `json:"username" binding:"required,min=2,max=50"`
+ PasswordHash string `json:"password_hash" binding:"required,min=6"`
+}
+
+// CreateMqttuserResponse 用户创建返回结构体
+type CreateMqttuserResponse struct {
+ ID uint `json:"id"`
+}
diff --git a/dto/sys_dto.go b/dto/sys_dto.go
new file mode 100644
index 0000000..d10aa8e
--- /dev/null
+++ b/dto/sys_dto.go
@@ -0,0 +1,10 @@
+package dto
+
+// SystemDeviceRequest 用户创建请求结构体
+type SystemDeviceRequest struct {
+ DeviceNumber string `uri:"device_number" binding:"required"`
+}
+
+// SystemDeviceResponse 用户创建返回结构体
+type SystemDeviceResponse struct {
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..04802e3
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,79 @@
+module iot-mqtt-gin
+
+go 1.23.7
+
+require (
+ github.com/eclipse/paho.mqtt.golang v1.5.0
+ github.com/gin-gonic/gin v1.10.1
+ github.com/sirupsen/logrus v1.9.3
+ github.com/swaggo/files v1.0.1
+ github.com/swaggo/gin-swagger v1.6.0
+ github.com/swaggo/swag v1.16.6
+ github.com/taosdata/driver-go/v3 v3.7.3
+ github.com/yyboo586/MQSDK v0.0.0-20250808084533-bf2cfee987c9
+ gorm.io/driver/mysql v1.6.0
+ gorm.io/gorm v1.30.1
+)
+
+require (
+ filippo.io/edwards25519 v1.1.0 // indirect
+ github.com/KyleBanks/depth v1.2.1 // indirect
+ github.com/Shopify/sarama v1.38.1 // indirect
+ github.com/bytedance/sonic v1.14.0 // indirect
+ github.com/bytedance/sonic/loader v0.3.0 // indirect
+ github.com/cloudwego/base64x v0.1.5 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/eapache/go-resiliency v1.7.0 // indirect
+ github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect
+ github.com/eapache/queue v1.1.0 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.9 // indirect
+ github.com/gin-contrib/sse v1.1.0 // indirect
+ github.com/go-openapi/jsonpointer v0.21.1 // indirect
+ github.com/go-openapi/jsonreference v0.21.0 // indirect
+ github.com/go-openapi/spec v0.21.0 // indirect
+ github.com/go-openapi/swag v0.23.1 // 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.27.0 // indirect
+ github.com/go-sql-driver/mysql v1.8.1 // indirect
+ github.com/goccy/go-json v0.10.5 // indirect
+ github.com/golang/snappy v1.0.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/gorilla/websocket v1.5.3 // indirect
+ github.com/hashicorp/errwrap v1.1.0 // indirect
+ github.com/hashicorp/go-multierror v1.1.1 // indirect
+ github.com/hashicorp/go-uuid v1.0.3 // indirect
+ github.com/jcmturner/aescts/v2 v2.0.0 // indirect
+ github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
+ github.com/jcmturner/gofork v1.7.6 // indirect
+ github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect
+ github.com/jcmturner/rpc/v2 v2.0.3 // indirect
+ github.com/jinzhu/inflection v1.0.0 // indirect
+ github.com/jinzhu/now v1.1.5 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/compress v1.18.0 // indirect
+ github.com/klauspost/cpuid/v2 v2.3.0 // indirect
+ github.com/leodido/go-urn v1.4.0 // indirect
+ github.com/mailru/easyjson v0.9.0 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/nsqio/go-nsq v1.1.0 // indirect
+ github.com/pelletier/go-toml/v2 v2.2.4 // indirect
+ github.com/pierrec/lz4/v4 v4.1.22 // indirect
+ github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect
+ github.com/streadway/amqp v1.1.0 // indirect
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.3.0 // indirect
+ golang.org/x/arch v0.19.0 // indirect
+ golang.org/x/crypto v0.41.0 // indirect
+ golang.org/x/mod v0.27.0 // indirect
+ golang.org/x/net v0.43.0 // indirect
+ golang.org/x/sync v0.16.0 // indirect
+ golang.org/x/sys v0.35.0 // indirect
+ golang.org/x/text v0.28.0 // indirect
+ golang.org/x/tools v0.36.0 // indirect
+ google.golang.org/protobuf v1.36.6 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..d1b1535
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,224 @@
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
+github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
+github.com/Shopify/sarama v1.38.1 h1:lqqPUPQZ7zPqYlWpTh+LQ9bhYNu2xJL6k1SJN4WVe2A=
+github.com/Shopify/sarama v1.38.1/go.mod h1:iwv9a67Ha8VNa+TifujYoWGxWnu2kNVAQdSdZ4X2o5g=
+github.com/Shopify/toxiproxy/v2 v2.5.0 h1:i4LPT+qrSlKNtQf5QliVjdP08GyAH8+BUIc9gT0eahc=
+github.com/Shopify/toxiproxy/v2 v2.5.0/go.mod h1:yhM2epWtAmel9CB8r2+L+PCmhH6yH2pITaPAo7jxJl0=
+github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
+github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
+github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
+github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
+github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
+github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
+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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/eapache/go-resiliency v1.7.0 h1:n3NRTnBn5N0Cbi/IeOHuQn9s2UwVUH7Ga0ZWcP+9JTA=
+github.com/eapache/go-resiliency v1.7.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho=
+github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws=
+github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0=
+github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
+github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
+github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
+github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
+github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
+github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
+github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
+github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
+github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
+github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
+github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
+github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
+github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
+github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
+github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
+github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
+github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
+github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
+github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
+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.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
+github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
+github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
+github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
+github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
+github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
+github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
+github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
+github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
+github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
+github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
+github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
+github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
+github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
+github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
+github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
+github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
+github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
+github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
+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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+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/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
+github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+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/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
+github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
+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/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/nsqio/go-nsq v1.1.0 h1:PQg+xxiUjA7V+TLdXw7nVrJ5Jbl3sN86EhGCQj4+FYE=
+github.com/nsqio/go-nsq v1.1.0/go.mod h1:vKq36oyeVXgsS5Q8YEO7WghqidAVXQlcFxzQbQTuDEY=
+github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
+github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
+github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
+github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+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/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=
+github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/streadway/amqp v1.1.0 h1:py12iX8XSyI7aN/3dUT8DFIDJazNJsVJdxNVEpnQTZM=
+github.com/streadway/amqp v1.1.0/go.mod h1:WYSrTEYHOXHd0nwFeUXAe2G2hRnQT+deZJJf88uS9Bg=
+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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
+github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
+github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
+github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
+github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
+github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
+github.com/taosdata/driver-go/v3 v3.7.3 h1:bnzHF/GsChvzhgjkKrZCpCQrOe2LjwluC7l2bPtRjUM=
+github.com/taosdata/driver-go/v3 v3.7.3/go.mod h1:gSxBEPOueMg0rTmMO1Ug6aeD7AwGdDGvUtLrsDTTpYc=
+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.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
+github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yyboo586/MQSDK v0.0.0-20250808084533-bf2cfee987c9 h1:bUy+CSJlAmN8wFIguwehXPprwLiX5tGU0usOwxqxAyU=
+github.com/yyboo586/MQSDK v0.0.0-20250808084533-bf2cfee987c9/go.mod h1:Z8U8wbzPVHJBlMRBW/NEaaPqPtery4dK8+E4LzZ5iPU=
+golang.org/x/arch v0.19.0 h1:LmbDQUodHThXE+htjrnmVD73M//D9GTH6wFZjyDkjyU=
+golang.org/x/arch v0.19.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
+golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
+golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
+golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
+golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+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.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
+golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
+golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
+golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
+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/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+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.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
+gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
+gorm.io/gorm v1.30.1 h1:lSHg33jJTBxs2mgJRfRZeLDG+WZaHYCk3Wtfl6Ngzo4=
+gorm.io/gorm v1.30.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
+nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..76542ca
--- /dev/null
+++ b/main.go
@@ -0,0 +1,75 @@
+package main
+
+import (
+ "iot-mqtt-gin/config"
+ "iot-mqtt-gin/controllers"
+ db "iot-mqtt-gin/dao"
+ _ "iot-mqtt-gin/docs"
+ "iot-mqtt-gin/mqtt"
+ "iot-mqtt-gin/router"
+ "iot-mqtt-gin/service"
+ "log"
+)
+
+// @title 物联网MQTT API
+// @version 1.0
+// @description 物联网mqtt api接口
+
+// @host 127.0.0.1:9999
+// @BasePath /api/v1
+// @schemes http https
+func main() {
+ // 初始化MQTT客户端
+ mqttConfig := mqtt.Config{
+ Broker: config.MQTTBroker,
+ ClientID: config.MQTTClientID,
+ Username: config.MQTTUsername,
+ Password: config.MQTTPassword,
+ QoS: config.MQTTQoS,
+ }
+ mqttClient, err := mqtt.NewClient(mqttConfig)
+ if err != nil {
+ log.Fatalf("无法初始化MQTT客户端: %v", err)
+ }
+ defer mqttClient.Disconnect(250)
+ // 连接到MQTT服务器
+ if err := mqttClient.Connect(); err != nil {
+ log.Fatalf("连接到MQTT服务器失败: %v", err)
+ }
+ log.Println("成功连接到MQTT服务器")
+
+ // 初始化TDengine连接
+ if err := db.InitTDengine(); err != nil {
+ log.Fatalf("初始化TDengine失败: %v", err)
+ return // 确保不执行后续DB操作
+ }
+ defer db.Close()
+ // 初始化MQ服务
+ mqService := service.NewMQService()
+ defer mqService.Close()
+
+ // 初始化生产者
+ nsqProducer, _, _, err := mqService.InitProducers()
+ if err != nil {
+ log.Fatalf("初始化生产者失败: %v", err)
+ }
+ defer nsqProducer.Close()
+ //defer kafkaProducer.Close()
+ //defer rabbitProducer.Close()
+
+ // 初始化消费者
+ /*if err := mqService.InitConsumers(); err != nil {
+ log.Fatalf("初始化消费者失败: %v", err)
+ }*/
+ // 启动消费
+ //mqService.StartConsuming()
+ // 初始化所有控制器
+ mqttUserCtrl := controllers.NewMqttuserController()
+ deviceCtrl := controllers.NewDeviceController(mqttClient, nsqProducer)
+
+ // 设置MQTT消息处理
+ mqttClient.SetMessageHandler(deviceCtrl.HandleMQTTMessage)
+ //引入路由
+ r := router.SetupRouter(mqttUserCtrl, deviceCtrl)
+ r.Run(":9999")
+}
diff --git a/models/baseDevice.go b/models/baseDevice.go
new file mode 100644
index 0000000..528f351
--- /dev/null
+++ b/models/baseDevice.go
@@ -0,0 +1,20 @@
+package models
+
+import "time"
+
+// BaseDevice 公共设备基础模板,包含所有设备共有的属性
+type BaseDevice struct {
+ ID string `json:"id" binding:"required"` // 设备唯一标识
+ OrgID string `json:"org_id" binding:"required"` // 所属组织ID
+ DeviceCode string `json:"device_code" binding:"required"` // 设备编号(物理编号)
+ Name string `json:"name" binding:"required"` // 设备名称
+ CreatedAt time.Time `json:"created_at"` // 创建时间
+ UpdatedAt time.Time `json:"updated_at"` // 更新时间
+ LastActive time.Time `json:"last_active"` // 最后活动时间
+ Metadata struct { // 设备元数据(制造商、型号等)
+ Manufacturer string `json:"manufacturer"`
+ Model string `json:"model"`
+ FirmwareVer string `json:"firmware_ver"`
+ Location string `json:"location"` // 安装位置
+ } `json:"metadata"`
+}
diff --git a/models/device.go b/models/device.go
new file mode 100644
index 0000000..ab28065
--- /dev/null
+++ b/models/device.go
@@ -0,0 +1,51 @@
+package models
+
+import (
+ "iot-mqtt-gin/dao"
+ "time"
+)
+
+type Device struct {
+ ID uint `gorm:"primaryKey" json:"id"`
+ Name string `json:"name" gorm:"not null"` // 名称不为空
+ DeviceKey string `json:"device_key" gorm:"unique;not null"` // 设备号唯一不为空
+ OrgId string `json:"org_id" gorm:"not null"` // 组织不为空
+}
+
+// DeviceData 设备数据模型
+type DeviceData struct {
+ OrgID string `json:"org_id" binding:"required"` // 组织ID
+ DeviceID string `json:"device_id" binding:"required"` // 设备ID
+ Value float64 `json:"value"` // 设备数值(如温度、湿度等)
+ Status bool `json:"status"` // 设备状态(开关等)
+ Timestamp time.Time `json:"timestamp"` // 时间戳,可选,默认使用当前时间
+}
+
+func (Device) TableName() string {
+ return "t_device"
+}
+
+// DeviceType 设备类型
+type DeviceType string
+
+// 定义设备类型常量
+const (
+ DeviceTypeSmokeAlarm DeviceType = "smoke_alarm" // 烟雾报警器
+ DeviceTypeDoorLock DeviceType = "door_lock" // 门锁
+)
+
+// Event 设备事件结构
+type Event struct {
+ EventType string `json:"event_type"` // 事件类型
+ TriggerTime int64 `json:"trigger_time"` // 触发时间戳
+ Priority int `json:"priority"` // 优先级: 1-紧急, 2-重要, 3-普通
+ Description string `json:"description"` // 事件描述
+ Parameters map[string]interface{} `json:"parameters"` // 事件参数
+}
+
+// Exists 检查设备是否存在
+func (d *Device) CheckDevice(device_key string) (Device, error) {
+ var device Device
+ err := dao.Db.Where("device_key = ?", device_key).First(&device).Error
+ return device, err
+}
diff --git a/models/deviceMqttLog.go b/models/deviceMqttLog.go
new file mode 100644
index 0000000..35a5f00
--- /dev/null
+++ b/models/deviceMqttLog.go
@@ -0,0 +1,58 @@
+package models
+
+import (
+ "gorm.io/gorm/clause"
+ "iot-mqtt-gin/dao"
+)
+
+type DeviceMqttLogContent struct {
+ Message string `json:"message" gorm:"not null"` // 消息
+ Details map[string]interface{} `json:"details" dc:"详情"` //详情
+}
+type DeviceMqttLog struct {
+ ID uint `gorm:"primaryKey" json:"id"`
+ DeviceId uint `json:"device_id" gorm:"not null"` // 设备不为空
+ DeviceKey string `json:"device_key" gorm:"not null"` // 设备号唯一不为空
+ DeviceName string `json:"device_name"` // 设备名称
+ OrgId string `json:"org_id" gorm:"not null"` // 组织不为空
+ Type uint `json:"type" gorm:"not null"` // 事件类型
+ Content string `json:"content"`
+}
+
+type DeviceMqSdkLog struct {
+ DeviceId uint `json:"device_id" gorm:"not null"` // 设备不为空
+ DeviceKey string `json:"device_key" gorm:"not null"` // 设备号唯一不为空
+ DeviceName string `json:"device_name"` // 设备名称
+ OrgId string `json:"org_id" gorm:"not null"` // 组织不为空
+}
+
+func (d *DeviceMqttLog) Validate() bool {
+ if d.DeviceId == 0 || d.DeviceKey == "" {
+ return false
+ }
+ return true
+}
+func (DeviceMqttLog) TableName() string {
+ return "t_device_log"
+}
+func (d *DeviceMqttLog) AddLog() error {
+ if !d.Validate() {
+ return nil // 直接返回nil表示成功处理(跳过)
+ }
+ return dao.Db.Clauses(clause.Insert{Modifier: "IGNORE"}).Create(d).Error
+}
+func (d *DeviceMqttLog) BatchAddLogs(logs []DeviceMqttLog) error {
+ // 过滤无效数据
+ validLogs := make([]DeviceMqttLog, 0, len(logs))
+ for _, log := range logs {
+ if !log.Validate() {
+ continue
+ }
+ validLogs = append(validLogs, log)
+ }
+
+ if len(validLogs) == 0 {
+ return nil // 没有有效数据,直接返回
+ }
+ return dao.Db.Clauses(clause.Insert{Modifier: "IGNORE"}).CreateInBatches(validLogs, len(validLogs)).Error
+}
diff --git a/models/mqsdk.go b/models/mqsdk.go
new file mode 100644
index 0000000..e3af1fb
--- /dev/null
+++ b/models/mqsdk.go
@@ -0,0 +1,69 @@
+package models
+
+import (
+ "time"
+
+ mqsd "github.com/yyboo586/MQSDK"
+)
+
+// Message 扩展消息结构
+type Message struct {
+ ID string `json:"id"`
+ Topic string `json:"topic"`
+ Body interface{} `json:"body"`
+ Headers map[string]string `json:"headers"`
+ Timestamp int64 `json:"timestamp"`
+ Source string `json:"source"` // 消息来源
+ RetryCount int `json:"retry_count"` // 重试次数
+}
+
+// ToMQSDMessage 转换为MQSDK所需的消息结构
+func (m *Message) ToMQSDMessage() *mqsd.Message {
+ return &mqsd.Message{
+ ID: m.ID,
+ Topic: m.Topic,
+ Body: m.Body,
+ Headers: m.Headers,
+ Timestamp: m.Timestamp,
+ }
+}
+
+// FromMQSDMessage 从MQSDK消息结构转换
+func FromMQSDMessage(msg *mqsd.Message) *Message {
+ return &Message{
+ ID: msg.ID,
+ Topic: msg.Topic,
+ Body: msg.Body,
+ Headers: msg.Headers,
+ Timestamp: msg.Timestamp,
+ }
+}
+
+// NewMessage 创建新消息
+func NewMessage(topic string, body interface{}, source string) *Message {
+ return &Message{
+ ID: generateMessageID(),
+ Topic: topic,
+ Body: body,
+ Headers: make(map[string]string),
+ Timestamp: time.Now().Unix(),
+ //Source: source,
+ //RetryCount: 0,
+ }
+}
+
+// 生成简单的消息ID
+func generateMessageID() string {
+ return time.Now().Format("20060102150405") + "-" + randString(8)
+}
+
+// 生成随机字符串
+func randString(n int) string {
+ const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ b := make([]byte, n)
+ for i := range b {
+ b[i] = letters[time.Now().UnixNano()%int64(len(letters))]
+ time.Sleep(time.Nanosecond)
+ }
+ return string(b)
+}
diff --git a/models/mqttuser.go b/models/mqttuser.go
new file mode 100644
index 0000000..7ff44e3
--- /dev/null
+++ b/models/mqttuser.go
@@ -0,0 +1,34 @@
+package models
+
+import (
+ "iot-mqtt-gin/dao"
+)
+
+type Mqttuser struct {
+ ID uint `gorm:"primaryKey" json:"id"`
+ Username string `json:"username" gorm:"unique;not null"` // 用户名唯一且不为空
+ PasswordHash string `json:"password_hash" gorm:"not null"` // 密码不为空
+ Salt string `json:"salt" gorm:"not null"` // 密码盐不为空
+}
+
+// createUserRequest 定义添加参数结构
+
+func (Mqttuser) TableName() string {
+ return "mqtt_user"
+}
+
+func (u *Mqttuser) AddUser() error {
+ return dao.Db.Create(u).Error
+}
+
+// Exists 检查用户名是否已存在
+func (u *Mqttuser) Exists() (bool, string) {
+ var count int64
+
+ // 检查用户名是否存在
+ dao.Db.Model(&Mqttuser{}).Where("username = ?", u.Username).Count(&count)
+ if count > 0 {
+ return true, "用户名已"
+ }
+ return false, ""
+}
diff --git a/models/switch.go b/models/switch.go
new file mode 100644
index 0000000..b1f1df6
--- /dev/null
+++ b/models/switch.go
@@ -0,0 +1,21 @@
+package models
+
+// SwitchDevice 开关设备物模型
+type SwitchDevice struct {
+ BaseDevice // 嵌入公共设备模板,继承所有公共属性
+ Status bool `json:"status"` // 开关状态:true-开,false-关
+ LastChanged string `json:"last_changed"` // 最后状态变更时间
+}
+
+// SwitchStatus 开关状态模型
+type SwitchStatus struct {
+ ID string `json:"id"` // 设备ID
+ Status bool `json:"status"` // 开关状态: true-开, false-关
+}
+
+// SwitchRequest 设置开关状态请求参数
+type SwitchRequest struct {
+ OrgID string `json:"org_id" binding:"required"`
+ DeviceID string `json:"device_id" binding:"required"`
+ Status bool `json:"status" binding:"required"`
+}
diff --git a/mqtt/client.go b/mqtt/client.go
new file mode 100644
index 0000000..0af54dc
--- /dev/null
+++ b/mqtt/client.go
@@ -0,0 +1,109 @@
+package mqtt
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "fmt"
+ "time"
+
+ paho "github.com/eclipse/paho.mqtt.golang"
+)
+
+// Config MQTT客户端配置
+type Config struct {
+ Broker string // MQTT服务器地址,如tcp://localhost:1883
+ ClientID string // 客户端ID
+ Username string // 用户名
+ Password string // 密码
+ QoS int // 消息质量等级
+}
+
+// Client MQTT客户端封装
+type Client struct {
+ config Config
+ client paho.Client
+ msgHandler MessageHandler
+}
+
+// MessageHandler 消息处理函数类型
+type MessageHandler func(topic string, payload []byte)
+
+// NewClient 创建新的MQTT客户端
+func NewClient(config Config) (*Client, error) {
+ if config.ClientID == "" {
+ config.ClientID = "iot-client-" + GenerateClientID()
+ }
+
+ opts := paho.NewClientOptions()
+ opts.AddBroker(config.Broker)
+ opts.SetClientID(config.ClientID)
+ opts.SetUsername(config.Username)
+ opts.SetPassword(config.Password)
+ opts.SetCleanSession(true)
+ opts.SetAutoReconnect(true)
+ opts.SetMaxReconnectInterval(10 * time.Second)
+
+ client := paho.NewClient(opts)
+
+ return &Client{
+ config: config,
+ client: client,
+ }, nil
+}
+
+// Connect 连接到MQTT服务器
+func (c *Client) Connect() error {
+ if token := c.client.Connect(); token.Wait() && token.Error() != nil {
+ return token.Error()
+ }
+ return nil
+}
+
+// Disconnect 断开与MQTT服务器的连接
+func (c *Client) Disconnect(quiesce uint) {
+ c.client.Disconnect(quiesce)
+}
+
+// Publish 发布消息到指定主题
+func (c *Client) Publish(topic string, payload interface{}) error {
+ token := c.client.Publish(topic, byte(c.config.QoS), false, payload)
+ token.Wait()
+ return token.Error()
+}
+
+// Subscribe 订阅指定主题
+func (c *Client) Subscribe(topic string) error {
+ fmt.Println("主题", topic)
+ token := c.client.Subscribe(topic, byte(c.config.QoS), c.defaultMessageHandler)
+ token.Wait()
+ return token.Error()
+}
+
+// Unsubscribe 取消订阅指定主题
+func (c *Client) Unsubscribe(topic string) error {
+ token := c.client.Unsubscribe(topic)
+ token.Wait()
+ return token.Error()
+}
+
+// SetMessageHandler 设置消息处理函数
+func (c *Client) SetMessageHandler(handler MessageHandler) {
+ c.msgHandler = handler
+}
+
+// defaultMessageHandler 默认消息处理函数
+func (c *Client) defaultMessageHandler(client paho.Client, msg paho.Message) {
+ if c.msgHandler != nil {
+ c.msgHandler(msg.Topic(), msg.Payload())
+ }
+}
+
+// GenerateClientID 生成随机客户端ID
+func GenerateClientID() string {
+ b := make([]byte, 8)
+ _, err := rand.Read(b)
+ if err != nil {
+ return fmt.Sprintf("client-%d", time.Now().UnixNano())
+ }
+ return hex.EncodeToString(b)
+}
diff --git a/msgtype/message.go b/msgtype/message.go
new file mode 100644
index 0000000..fada96a
--- /dev/null
+++ b/msgtype/message.go
@@ -0,0 +1,100 @@
+package message
+
+import (
+ "time"
+)
+
+// 设备类型定义
+type DeviceType string
+
+const (
+ DeviceTypeSmokeAlarm DeviceType = "smoke_alarm" // 烟雾报警器
+ DeviceTypeDoorLock DeviceType = "door_lock" // 门锁
+)
+
+type OnOffMessage struct {
+ state string `json:"version"` // 上下线状态
+}
+
+// 状态消息 - 设备上报的状态数据
+type StateMessage struct {
+ DeviceID string `json:"device_id"` // 设备ID
+ Properties map[string]interface{} `json:"properties"` // 设备属性键值对
+ Timestamp time.Time `json:"timestamp"` // 消息时间戳
+ Version string `json:"version"` // 消息格式版本,默认为v1
+}
+
+// 命令消息 - 发送给设备的控制命令
+type CommandMessage struct {
+ DeviceID string `json:"device_id"` // 设备ID
+ Command string `json:"command"` // 命令名称
+ Params map[string]interface{} `json:"params"` // 命令参数
+ RequestID string `json:"request_id"` // 命令请求ID,用于跟踪
+ ReceiveTime time.Time `json:"receive_time"` // 接收时间
+}
+
+// 事件消息 - 设备主动上报的事件
+type EventMessage struct {
+ DeviceID string `json:"device_id"` // 设备ID
+ EventType string `json:"event_type"` // 事件类型
+ Description string `json:"description"` // 事件描述
+ Parameters map[string]interface{} `json:"parameters"` // 事件参数
+ Priority int `json:"priority"` // 优先级:1-紧急,2-重要,3-普通
+ Timestamp int64 `json:"timestamp"` // 事件发生时间戳(秒)
+ ReceiveTime time.Time `json:"receive_time"` // 接收时间
+}
+
+// 响应消息 - 设备对命令的响应
+type ResponseMessage struct {
+ DeviceID string `json:"device_id"` // 设备ID
+ Command string `json:"command"` // 对应的命令
+ Result interface{} `json:"result"` // 命令执行结果
+ Success bool `json:"success"` // 是否成功
+ Error string `json:"error"` // 错误信息,如果失败
+ RequestID string `json:"request_id"` // 对应的请求ID
+ Timestamp int64 `json:"timestamp"` // 响应时间戳(秒)
+ ReceiveTime time.Time `json:"receive_time"` // 接收时间
+}
+
+// 设备注册消息 - 设备注册时的信息
+type RegisterMessage struct {
+ DeviceID string `json:"device_id"` // 设备ID
+ DeviceType DeviceType `json:"device_type"` // 设备类型
+ Model string `json:"model"` // 设备型号
+ Firmware string `json:"firmware"` // 固件版本
+ MacAddress string `json:"mac_address"` // MAC地址
+ Timestamp time.Time `json:"timestamp"` // 注册时间
+}
+
+// 设备心跳消息 - 设备定期发送的在线状态
+type HeartbeatMessage struct {
+ DeviceID string `json:"device_id"` // 设备ID
+ Status string `json:"status"` // 状态:online/offline
+ Battery int `json:"battery"` // 电池电量 电量(%),适用于电池设备
+ Signal int `json:"signal"` // 信号强度,0-100
+ Timestamp time.Time `json:"timestamp"` // 心跳时间
+}
+
+// 烟雾报警器特定状态 - 继承通用状态
+type SmokeAlarmState struct {
+ SmokeConcentration float64 `json:"smoke_concentration"` // 烟雾浓度(%)
+ AlarmStatus bool `json:"alarm_status"` // 报警状态
+ BatteryLevel int `json:"battery_level"` // 电池电量(%)
+ SensitivityLevel int `json:"sensitivity_level"` // 灵敏度级别1-5
+ AlarmThreshold float64 `json:"alarm_threshold"` // 报警阈值(%)
+}
+
+// 门锁特定状态 - 继承通用状态
+type DoorLockState struct {
+ Locked bool `json:"locked"` // 是否锁定
+ BatteryLevel int `json:"battery_level"` // 电池电量(%)
+ LastOpenedTime time.Time `json:"last_opened_time"` // 最后开锁时间
+ ErrorCode int `json:"error_code"` // 错误代码,0表示无错误
+}
+
+// 门锁事件参数
+type LockEventParams struct {
+ Operation string `json:"operation"` // 操作:lock/unlock
+ Operator string `json:"operator"` // 操作者
+ OperationTime time.Time `json:"operation_time"` // 操作时间
+}
diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go
new file mode 100644
index 0000000..db56eb4
--- /dev/null
+++ b/pkg/logger/logger.go
@@ -0,0 +1,149 @@
+package logger
+
+import (
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "github.com/sirupsen/logrus"
+ "io"
+ "net/http"
+ "os"
+ "path"
+ "runtime/debug"
+ "time"
+)
+
+func init() {
+ // 设置日志格式为json格式
+ logrus.SetFormatter(&logrus.JSONFormatter{
+ TimestampFormat: "2006-01-02 15:04:05",
+ })
+ logrus.SetReportCaller(false)
+}
+
+func Write(msg string, filename string) {
+ setOutPutFile(logrus.InfoLevel, filename)
+ logrus.Info(msg)
+}
+
+func Debug(fields logrus.Fields, args ...interface{}) {
+ setOutPutFile(logrus.DebugLevel, "debug")
+ logrus.WithFields(fields).Debug(args)
+}
+
+func Info(fields logrus.Fields, args ...interface{}) {
+ setOutPutFile(logrus.InfoLevel, "info")
+ logrus.WithFields(fields).Info(args)
+}
+
+func Warn(fields logrus.Fields, args ...interface{}) {
+ setOutPutFile(logrus.WarnLevel, "warn")
+ logrus.WithFields(fields).Warn(args)
+}
+
+func Fatal(fields logrus.Fields, args ...interface{}) {
+ setOutPutFile(logrus.FatalLevel, "fatal")
+ logrus.WithFields(fields).Fatal(args)
+}
+
+func Error(fields logrus.Fields, args ...interface{}) {
+ setOutPutFile(logrus.ErrorLevel, "error")
+ logrus.WithFields(fields).Error(args)
+}
+
+func Panic(fields logrus.Fields, args ...interface{}) {
+ setOutPutFile(logrus.PanicLevel, "panic")
+ logrus.WithFields(fields).Panic(args)
+}
+
+func Trace(fields logrus.Fields, args ...interface{}) {
+ setOutPutFile(logrus.TraceLevel, "trace")
+ logrus.WithFields(fields).Trace(args)
+}
+
+func setOutPutFile(level logrus.Level, logName string) {
+ if _, err := os.Stat("./runtime/log"); os.IsNotExist(err) {
+ err = os.MkdirAll("./runtime/log", 0777)
+ if err != nil {
+ panic(fmt.Errorf("create log dir '%s' error: %s", "./runtime/log", err))
+ }
+ }
+
+ timeStr := time.Now().Format("2006-01-02")
+ fileName := path.Join("./runtime/log", logName+"_"+timeStr+".log")
+
+ var err error
+ os.Stderr, err = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
+ if err != nil {
+ fmt.Println("open log file err", err)
+ }
+ logrus.SetOutput(os.Stderr)
+ logrus.SetLevel(level)
+ return
+}
+
+func LoggerToFile() gin.LoggerConfig {
+
+ if _, err := os.Stat("./runtime/log"); os.IsNotExist(err) {
+ err = os.MkdirAll("./runtime/log", 0777)
+ if err != nil {
+ panic(fmt.Errorf("create log dir '%s' error: %s", "./runtime/log", err))
+ }
+ }
+
+ timeStr := time.Now().Format("2006-01-02")
+ fileName := path.Join("./runtime/log", "success_"+timeStr+".log")
+
+ os.Stderr, _ = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
+
+ var conf = gin.LoggerConfig{
+ Formatter: func(param gin.LogFormatterParams) string {
+ return fmt.Sprintf("%s - %s \"%s %s %s %d %s \"%s\" %s\"\n",
+ param.TimeStamp.Format("2006-01-02 15:04:05"),
+ param.ClientIP,
+ param.Method,
+ param.Path,
+ param.Request.Proto,
+ param.StatusCode,
+ param.Latency,
+ param.Request.UserAgent(),
+ param.ErrorMessage,
+ )
+ },
+ Output: io.MultiWriter(os.Stdout, os.Stderr),
+ }
+
+ return conf
+}
+
+func Recover(c *gin.Context) {
+ defer func() {
+ if err := recover(); err != nil {
+ if _, errDir := os.Stat("./runtime/log"); os.IsNotExist(errDir) {
+ errDir = os.MkdirAll("./runtime/log", 0777)
+ if errDir != nil {
+ panic(fmt.Errorf("create log dir '%s' error: %s", "./runtime/log", errDir))
+ }
+ }
+
+ timeStr := time.Now().Format("2006-01-02")
+ fileName := path.Join("./runtime/log", "error_"+timeStr+".log")
+
+ f, errFile := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
+ if errFile != nil {
+ fmt.Println(errFile)
+ }
+ timeFileStr := time.Now().Format("2006-01-02 15:04:05")
+ f.WriteString("panic error time:" + timeFileStr + "\n")
+ f.WriteString(fmt.Sprintf("%v", err) + "\n")
+ f.WriteString("stacktrace from panic:" + string(debug.Stack()) + "\n")
+ f.Close()
+ c.JSON(http.StatusOK, gin.H{
+ "code": 500,
+ "msg": fmt.Sprintf("%v", err),
+ })
+ //终止后续接口调用,不加的话recover到异常后,还会继续执行接口里后续代码
+ c.Abort()
+ }
+ }()
+ c.Next()
+}
diff --git a/router/routers.go b/router/routers.go
new file mode 100644
index 0000000..29dc070
--- /dev/null
+++ b/router/routers.go
@@ -0,0 +1,39 @@
+package router
+
+import (
+ "github.com/gin-gonic/gin"
+ swaggerfiles "github.com/swaggo/files"
+ ginSwagger "github.com/swaggo/gin-swagger"
+ "iot-mqtt-gin/controllers"
+ "iot-mqtt-gin/pkg/logger"
+)
+
+func SetupRouter(
+ mqttuserCtrl *controllers.MqttuserController,
+ deviceCtrl *controllers.DeviceController,
+) *gin.Engine {
+ r := gin.Default()
+ r.Use(gin.LoggerWithConfig(logger.LoggerToFile()))
+ r.Use(logger.Recover)
+ r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
+ // 定义API路由组
+ v1 := r.Group("/api/v1")
+ {
+ user := v1.Group("/mqttuser")
+ {
+ user.POST("/add", mqttuserCtrl.AddUser)
+ }
+ // 设备相关路由
+ devices := v1.Group("/devices")
+ {
+ devices.GET("/:device_number/onoff", deviceCtrl.DeviceOnOff) // 设备上下线
+
+ devices.GET("/tdengine/test", deviceCtrl.TDengineTest) // TDengine测试
+ /*devices.GET("/:device_number/properties", controllers.DeviceController{}.DeviceOnOff) // 获取设备属性
+ devices.GET("/:device_number/properties/:property", controllers.DeviceController{}.DeviceOnOff) // 更新设备属性
+ devices.GET("/:device_number/command", controllers.DeviceController{}.DeviceOnOff) // 发送命令
+ devices.GET("/:device_number/events", controllers.DeviceController{}.DeviceOnOff) // 获取事件*/
+ }
+ }
+ return r
+}
diff --git a/runtime/log/error_2025-08-01.log b/runtime/log/error_2025-08-01.log
new file mode 100644
index 0000000..46c35ca
--- /dev/null
+++ b/runtime/log/error_2025-08-01.log
@@ -0,0 +1,40 @@
+{"level":"error","msg":"[]","time":"2025-08-01 14:13:51","写入上线日志失败":"Error 1366: Incorrect integer value: '' for column 'online_status' at row 1"}
+{"level":"error","msg":"[]","time":"2025-08-01 14:14:02","写入下线日志失败":"Error 1366: Incorrect integer value: '' for column 'online_status' at row 1"}
+{"level":"error","msg":"[]","time":"2025-08-01 16:56:16","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 16:56:28","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 16:56:57","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 16:57:07","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 16:57:21","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 16:57:24","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 16:57:25","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 16:57:30","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 16:57:33","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 16:57:35","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:05:31","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:05:36","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:14:31","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:14:36","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:23:47","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:23:52","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:30:31","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:30:36","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:39:32","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:39:37","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:47:31","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:47:36","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:55:31","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 17:55:36","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:02:31","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:02:36","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:10:31","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:10:36","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:17:31","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:17:36","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:26:32","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:26:37","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:28:07","写入日志失败":"Error 1062 (23000): Duplicate entry '30' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:38:08","写入日志失败":"Error 1062 (23000): Duplicate entry '65' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:38:12","写入日志失败":"Error 1062 (23000): Duplicate entry '65' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:42:07","定时写入日志失败":"Error 1062 (23000): Duplicate entry '68' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:42:10","定时写入日志失败":"Error 1062 (23000): Duplicate entry '68' for key 't_device_online_log.PRIMARY'"}
+{"level":"error","msg":"[]","time":"2025-08-01 18:42:14","定时写入日志失败":"Error 1062 (23000): Duplicate entry '68' for key 't_device_online_log.PRIMARY'"}
diff --git a/runtime/log/error_2025-08-02.log b/runtime/log/error_2025-08-02.log
new file mode 100644
index 0000000..bc25093
--- /dev/null
+++ b/runtime/log/error_2025-08-02.log
@@ -0,0 +1,219 @@
+{"level":"error","msg":"[]","time":"2025-08-02 14:07:05","批量写入日志失败":"failed to parse field: Content, error: invalid field found for struct iot-mqtt-gin/models.DeviceMqttLogContent's field Details: define a valid foreign key for relations or implement the Valuer/Scanner interface"}
+{"level":"error","msg":"[]","time":"2025-08-02 14:07:08","批量写入日志失败":"failed to parse field: Content, error: invalid field found for struct iot-mqtt-gin/models.DeviceMqttLogContent's field Details: define a valid foreign key for relations or implement the Valuer/Scanner interface"}
+{"level":"error","msg":"[]","time":"2025-08-02 14:07:11","批量写入日志失败":"failed to parse field: Content, error: invalid field found for struct iot-mqtt-gin/models.DeviceMqttLogContent's field Details: define a valid foreign key for relations or implement the Valuer/Scanner interface"}
+{"level":"error","msg":"[]","time":"2025-08-02 14:15:50","批量写入日志失败":"failed to parse field: Content, error: invalid field found for struct iot-mqtt-gin/models.DeviceMqttLogContent's field Details: define a valid foreign key for relations or implement the Valuer/Scanner interface"}
+{"level":"error","msg":"[]","time":"2025-08-02 14:15:55","批量写入日志失败":"failed to parse field: Content, error: invalid field found for struct iot-mqtt-gin/models.DeviceMqttLogContent's field Details: define a valid foreign key for relations or implement the Valuer/Scanner interface"}
+{"level":"error","msg":"[]","time":"2025-08-02 14:17:30","批量写入日志失败":"failed to parse field: Content, error: failed to parse field: Details, error: unsupported data type: \u0026map[]: Table not set, please set it like: db.Model(\u0026user) or db.Table(\"users\")"}
+{"level":"error","msg":"[]","time":"2025-08-02 14:18:51","批量写入日志失败":"failed to parse field: Content, error: failed to parse field: Details, error: unsupported data type: \u0026map[]: Table not set, please set it like: db.Model(\u0026user) or db.Table(\"users\")"}
+{"level":"error","msg":"[]","time":"2025-08-02 14:24:40","批量写入日志失败":"invalid field found for struct iot-mqtt-gin/models.DeviceMqttLog's field Content: define a valid foreign key for relations or implement the Valuer/Scanner interface"}
+{"level":"error","msg":"[]","time":"2025-08-02 14:29:06","批量写入日志失败":"invalid field found for struct iot-mqtt-gin/models.DeviceMqttLog's field Content: define a valid foreign key for relations or implement the Valuer/Scanner interface"}
+panic error time:2025-08-02 16:44:01
+runtime error: invalid memory address or nil pointer dereference
+stacktrace from panic:goroutine 39 [running]:
+runtime/debug.Stack()
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/debug/stack.go:26 +0x5e
+iot-mqtt-gin/pkg/logger.Recover.func1()
+ E:/iot-mqtt-gin/pkg/logger/logger.go:138 +0x26f
+panic({0xcd32c0?, 0x1e561e0?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/panic.go:791 +0x132
+iot-mqtt-gin/mqtt.(*Client).Subscribe(0x0, {0xdb41ac, 0x5})
+ E:/iot-mqtt-gin/mqtt/client.go:76 +0x5a
+iot-mqtt-gin/controllers.DeviceController.DeviceOnOff({0x0, 0x0, {{0x0, 0x0}, 0x0, 0x0, {{}, 0x0}, {{}, 0x0}}}, ...)
+ E:/iot-mqtt-gin/controllers/device.go:124 +0x2b6
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+iot-mqtt-gin/pkg/logger.Recover(0xc0002a8300)
+ E:/iot-mqtt-gin/pkg/logger/logger.go:148 +0x5c
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc0002a8300)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1(0xc0002a8300)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 +0x6f
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc0002a8300)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc000172000, 0xc0002a8300)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 +0x892
+github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc000172000, {0xfadf50, 0xc0000ae2a0}, 0xc0001ae000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 +0x1b2
+net/http.serverHandler.ServeHTTP({0xc00019a6f0?}, {0xfadf50?, 0xc0000ae2a0?}, 0x6?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3210 +0x8e
+net/http.(*conn).serve(0xc0005f01b0, {0xfafbc8, 0xc000111ce0})
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:2092 +0x5d0
+created by net/http.(*Server).Serve in goroutine 1
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3360 +0x485
+
+panic error time:2025-08-02 16:47:42
+runtime error: invalid memory address or nil pointer dereference
+stacktrace from panic:goroutine 49 [running]:
+runtime/debug.Stack()
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/debug/stack.go:26 +0x5e
+iot-mqtt-gin/pkg/logger.Recover.func1()
+ E:/iot-mqtt-gin/pkg/logger/logger.go:138 +0x26f
+panic({0x19a32c0?, 0x2b251e0?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/panic.go:791 +0x132
+iot-mqtt-gin/mqtt.(*Client).Subscribe(0x0, {0x1a841ac, 0x5})
+ E:/iot-mqtt-gin/mqtt/client.go:77 +0xbc
+iot-mqtt-gin/controllers.DeviceController.DeviceOnOff({0x0, 0x0, {{0x0, 0x0}, 0x0, 0x0, {{}, 0x0}, {{}, 0x0}}}, ...)
+ E:/iot-mqtt-gin/controllers/device.go:124 +0x2b6
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+iot-mqtt-gin/pkg/logger.Recover(0xc000764100)
+ E:/iot-mqtt-gin/pkg/logger/logger.go:148 +0x5c
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc000764100)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1(0xc000764100)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 +0x6f
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc000764100)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc000172000, 0xc000764100)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 +0x892
+github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc000172000, {0x1c7ded0, 0xc0006ee2a0}, 0xc0001a0b40)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 +0x1b2
+net/http.serverHandler.ServeHTTP({0xc000111c80?}, {0x1c7ded0?, 0xc0006ee2a0?}, 0x6?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3210 +0x8e
+net/http.(*conn).serve(0xc0000b0510, {0x1c7fb48, 0xc00019a210})
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:2092 +0x5d0
+created by net/http.(*Server).Serve in goroutine 1
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3360 +0x485
+
+panic error time:2025-08-02 16:49:40
+runtime error: invalid memory address or nil pointer dereference
+stacktrace from panic:goroutine 60 [running]:
+runtime/debug.Stack()
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/debug/stack.go:26 +0x5e
+iot-mqtt-gin/pkg/logger.Recover.func1()
+ E:/iot-mqtt-gin/pkg/logger/logger.go:138 +0x26f
+panic({0x11a32c0?, 0x23251e0?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/panic.go:791 +0x132
+iot-mqtt-gin/mqtt.(*Client).Subscribe(0x0, {0x12841ac, 0x5})
+ E:/iot-mqtt-gin/mqtt/client.go:77 +0xbc
+iot-mqtt-gin/controllers.DeviceController.DeviceOnOff({0x0, 0x0, {{0x0, 0x0}, 0x0, 0x0, {{}, 0x0}, {{}, 0x0}}}, ...)
+ E:/iot-mqtt-gin/controllers/device.go:124 +0x2b6
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+iot-mqtt-gin/pkg/logger.Recover(0xc000670000)
+ E:/iot-mqtt-gin/pkg/logger/logger.go:148 +0x5c
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc000670000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1(0xc000670000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 +0x6f
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc000670000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc00014a000, 0xc000670000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 +0x892
+github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc00014a000, {0x147ded0, 0xc0000ae1c0}, 0xc0001a2780)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 +0x1b2
+net/http.serverHandler.ServeHTTP({0xc00011c1b0?}, {0x147ded0?, 0xc0000ae1c0?}, 0x6?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3210 +0x8e
+net/http.(*conn).serve(0xc0000b03f0, {0x147fb48, 0xc0001ce1e0})
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:2092 +0x5d0
+created by net/http.(*Server).Serve in goroutine 1
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3360 +0x485
+
+panic error time:2025-08-02 16:51:01
+runtime error: invalid memory address or nil pointer dereference
+stacktrace from panic:goroutine 53 [running]:
+runtime/debug.Stack()
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/debug/stack.go:26 +0x5e
+iot-mqtt-gin/pkg/logger.Recover.func1()
+ E:/iot-mqtt-gin/pkg/logger/logger.go:138 +0x26f
+panic({0x13732c0?, 0x24f51e0?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/panic.go:791 +0x132
+iot-mqtt-gin/mqtt.(*Client).Subscribe(0x0, {0x14541ac, 0x5})
+ E:/iot-mqtt-gin/mqtt/client.go:77 +0xbc
+iot-mqtt-gin/controllers.DeviceController.DeviceOnOff({0x0, 0x0, {{0x0, 0x0}, 0x0, 0x0, {{}, 0x0}, {{}, 0x0}}}, ...)
+ E:/iot-mqtt-gin/controllers/device.go:124 +0x2b2
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+iot-mqtt-gin/pkg/logger.Recover(0xc000296400)
+ E:/iot-mqtt-gin/pkg/logger/logger.go:148 +0x5c
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc000296400)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1(0xc000296400)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 +0x6f
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc000296400)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc00016c000, 0xc000296400)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 +0x892
+github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc00016c000, {0x164de70, 0xc0000ae1c0}, 0xc0001e6780)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 +0x1b2
+net/http.serverHandler.ServeHTTP({0xc00025de30?}, {0x164de70?, 0xc0000ae1c0?}, 0x6?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3210 +0x8e
+net/http.(*conn).serve(0xc000152120, {0x164fae8, 0xc00019b8f0})
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:2092 +0x5d0
+created by net/http.(*Server).Serve in goroutine 1
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3360 +0x485
+
+panic error time:2025-08-02 16:52:39
+runtime error: invalid memory address or nil pointer dereference
+stacktrace from panic:goroutine 50 [running]:
+runtime/debug.Stack()
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/debug/stack.go:26 +0x5e
+iot-mqtt-gin/pkg/logger.Recover.func1()
+ E:/iot-mqtt-gin/pkg/logger/logger.go:138 +0x26f
+panic({0xf91f00?, 0x2113180?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/panic.go:791 +0x132
+iot-mqtt-gin/mqtt.(*Client).Subscribe(0x0, {0x1072c8c, 0x5})
+ E:/iot-mqtt-gin/mqtt/client.go:77 +0xbc
+iot-mqtt-gin/controllers.DeviceController.DeviceOnOff({0x0, 0x0, {{0x0, 0x0}, 0x0, 0x0, {{}, 0x0}, {{}, 0x0}}}, ...)
+ E:/iot-mqtt-gin/controllers/device.go:124 +0x2b2
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+iot-mqtt-gin/pkg/logger.Recover(0xc00058c400)
+ E:/iot-mqtt-gin/pkg/logger/logger.go:148 +0x5c
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc00058c400)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1(0xc00058c400)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 +0x6f
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc00058c400)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc000172000, 0xc00058c400)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 +0x892
+github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc000172000, {0x126c700, 0xc0000ae1c0}, 0xc00016ab40)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 +0x1b2
+net/http.serverHandler.ServeHTTP({0xc0002b5a70?}, {0x126c700?, 0xc0000ae1c0?}, 0x6?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3210 +0x8e
+net/http.(*conn).serve(0xc0002fc000, {0x126e5d0, 0xc00019bce0})
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:2092 +0x5d0
+created by net/http.(*Server).Serve in goroutine 1
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3360 +0x485
+
diff --git a/runtime/log/error_2025-08-04.log b/runtime/log/error_2025-08-04.log
new file mode 100644
index 0000000..1619949
--- /dev/null
+++ b/runtime/log/error_2025-08-04.log
@@ -0,0 +1,5 @@
+{"level":"error","msg":"[]","time":"2025-08-04 15:35:18","写入报警日志失败":"reflect.Value.Convert: value of type string cannot be converted to type model.DeviceLogContent"}
+{"level":"error","msg":"[]","time":"2025-08-04 15:37:35","写入报警日志失败":"reflect.Value.Convert: value of type string cannot be converted to type model.DeviceLogContent"}
+{"level":"error","msg":"[]","time":"2025-08-04 15:39:20","写入报警日志失败":"reflect.Value.Convert: value of type string cannot be converted to type model.DeviceLogContent"}
+{"level":"error","msg":"[]","time":"2025-08-04 15:49:51","写入报警日志失败":"gomail: could not send email 1: gomail: invalid address \"11111111111\": mail: missing '@' or angle-addr"}
+{"level":"error","msg":"[]","time":"2025-08-04 15:52:36","写入报警日志失败":"OK"}
diff --git a/runtime/log/error_2025-08-07.log b/runtime/log/error_2025-08-07.log
new file mode 100644
index 0000000..9070ac1
--- /dev/null
+++ b/runtime/log/error_2025-08-07.log
@@ -0,0 +1,378 @@
+panic error time:2025-08-07 11:36:10
+runtime error: invalid memory address or nil pointer dereference
+stacktrace from panic:goroutine 55 [running]:
+runtime/debug.Stack()
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/debug/stack.go:26 +0x5e
+iot-mqtt-gin/pkg/logger.Recover.func1()
+ E:/iot-mqtt-gin/pkg/logger/logger.go:138 +0x26f
+panic({0x7ff63a0dd8c0?, 0x7ff63a002f70?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/panic.go:791 +0x132
+database/sql.(*DB).conn(0x0, {0x7ff63a3e71c0, 0x7ff63ab29f40}, 0x1)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1309 +0x54
+database/sql.(*DB).exec(0x0, {0x7ff63a3e71c0, 0x7ff63ab29f40}, {0xc00118a0f0, 0x4a}, {0x0, 0x0, 0x0}, 0x65?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1681 +0x54
+database/sql.(*DB).ExecContext.func1(0xfd?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1664 +0x4f
+database/sql.(*DB).retry(0x3b?, 0xc0011b3368)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1568 +0x42
+database/sql.(*DB).ExecContext(0xc0011820e0?, {0x7ff63a3e71c0?, 0x7ff63ab29f40?}, {0xc00118a0f0?, 0x4?}, {0x0?, 0xc00118a0f0?, 0xc00117e0d0?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1663 +0xc8
+database/sql.(*DB).Exec(0x7ff63a2135b2?, {0xc00118a0f0?, 0xc0011b34a0?}, {0x0?, 0x4?, 0x6?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1677 +0x3a
+iot-mqtt-gin/service.InsertDeviceData(0xc0011b3530)
+ E:/iot-mqtt-gin/service/deviceService.go:30 +0x1e7
+iot-mqtt-gin/controllers.(*DeviceController).TDengineTest(0x7ff638ef6653?, 0xc001192200)
+ E:/iot-mqtt-gin/controllers/device.go:275 +0x9e
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+iot-mqtt-gin/pkg/logger.Recover(0xc001192200)
+ E:/iot-mqtt-gin/pkg/logger/logger.go:148 +0x5c
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc001192200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1(0xc001192200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 +0x6f
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc001192200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0002e6000, 0xc001192200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 +0x892
+github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0002e6000, {0x7ff63a3e5950, 0xc00014c460}, 0xc0002da3c0)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 +0x1b2
+net/http.serverHandler.ServeHTTP({0xc0001bd680?}, {0x7ff63a3e5950?, 0xc00014c460?}, 0x6?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3210 +0x8e
+net/http.(*conn).serve(0xc00014e5a0, {0x7ff63a3e7ab8, 0xc0001bd440})
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:2092 +0x5d0
+created by net/http.(*Server).Serve in goroutine 1
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3360 +0x485
+
+panic error time:2025-08-07 11:48:21
+runtime error: invalid memory address or nil pointer dereference
+stacktrace from panic:goroutine 39 [running]:
+runtime/debug.Stack()
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/debug/stack.go:26 +0x5e
+iot-mqtt-gin/pkg/logger.Recover.func1()
+ E:/iot-mqtt-gin/pkg/logger/logger.go:138 +0x26f
+panic({0x7ff7aca2d8c0?, 0x7ff7ac952f70?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/panic.go:791 +0x132
+database/sql.(*DB).conn(0x0, {0x7ff7acd37180, 0x7ff7ad479f40}, 0x1)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1309 +0x54
+database/sql.(*DB).exec(0x0, {0x7ff7acd37180, 0x7ff7ad479f40}, {0xc001118400, 0x37}, {0xc001135470, 0x3, 0x3}, 0x59?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1681 +0x54
+database/sql.(*DB).ExecContext.func1(0x28?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1664 +0x4f
+database/sql.(*DB).retry(0x7ff7ab612acb?, 0xc001135348)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1568 +0x42
+database/sql.(*DB).ExecContext(0x0?, {0x7ff7acd37180?, 0x7ff7ad479f40?}, {0xc001118400?, 0x7ff7ab60a49b?}, {0xc001135470?, 0x7ff7ac9e26c0?, 0xc001100000?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1663 +0xc8
+database/sql.(*DB).Exec(0x4050000000000000?, {0xc001118400?, 0x7ff7ad432900?}, {0xc001135470?, 0x4?, 0xc0000a6c58?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1677 +0x3a
+iot-mqtt-gin/service.InsertDeviceData(0xc001135530)
+ E:/iot-mqtt-gin/service/deviceService.go:31 +0x2ed
+iot-mqtt-gin/controllers.(*DeviceController).TDengineTest(0x7ff7ab846653?, 0xc001114200)
+ E:/iot-mqtt-gin/controllers/device.go:275 +0x9e
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+iot-mqtt-gin/pkg/logger.Recover(0xc001114200)
+ E:/iot-mqtt-gin/pkg/logger/logger.go:148 +0x5c
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc001114200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1(0xc001114200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 +0x6f
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc001114200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0000b6000, 0xc001114200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 +0x892
+github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0000b6000, {0x7ff7acd35910, 0xc00014c380}, 0xc00008e3c0)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 +0x1b2
+net/http.serverHandler.ServeHTTP({0xc000283050?}, {0x7ff7acd35910?, 0xc00014c380?}, 0x6?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3210 +0x8e
+net/http.(*conn).serve(0xc0000b2360, {0x7ff7acd37a78, 0xc000282f60})
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:2092 +0x5d0
+created by net/http.(*Server).Serve in goroutine 1
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3360 +0x485
+
+panic error time:2025-08-07 11:55:17
+runtime error: invalid memory address or nil pointer dereference
+stacktrace from panic:goroutine 59 [running]:
+runtime/debug.Stack()
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/debug/stack.go:26 +0x5e
+iot-mqtt-gin/pkg/logger.Recover.func1()
+ E:/iot-mqtt-gin/pkg/logger/logger.go:138 +0x26f
+panic({0x7ff6031fd8c0?, 0x7ff603122f70?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/panic.go:791 +0x132
+database/sql.(*DB).conn(0x0, {0x7ff603507160, 0x7ff603c49f40}, 0x1)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1309 +0x54
+database/sql.(*DB).exec(0x0, {0x7ff603507160, 0x7ff603c49f40}, {0xc00002ea00, 0x35}, {0xc000047480, 0x3, 0x3}, 0x59?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1681 +0x54
+database/sql.(*DB).ExecContext.func1(0x38?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1664 +0x4f
+database/sql.(*DB).retry(0x7ff601de2acb?, 0xc000047358)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1568 +0x42
+database/sql.(*DB).ExecContext(0x0?, {0x7ff603507160?, 0x7ff603c49f40?}, {0xc00002ea00?, 0x7ff601dda49b?}, {0xc000047480?, 0x7ff6031b26c0?, 0x0?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1663 +0xc8
+database/sql.(*DB).Exec(0x4050000000000000?, {0xc00002ea00?, 0x7ff603c02900?}, {0xc000047480?, 0x2?, 0x30?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1677 +0x3a
+iot-mqtt-gin/service.InsertDeviceData(0xc000047530)
+ E:/iot-mqtt-gin/service/deviceService.go:31 +0x285
+iot-mqtt-gin/controllers.(*DeviceController).TDengineTest(0x7ff602016653?, 0xc00041a500)
+ E:/iot-mqtt-gin/controllers/device.go:275 +0x9e
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+iot-mqtt-gin/pkg/logger.Recover(0xc00041a500)
+ E:/iot-mqtt-gin/pkg/logger/logger.go:148 +0x5c
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc00041a500)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1(0xc00041a500)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 +0x6f
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc00041a500)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0000b6000, 0xc00041a500)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 +0x892
+github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0000b6000, {0x7ff6035058f0, 0xc000168380}, 0xc00020e8c0)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 +0x1b2
+net/http.serverHandler.ServeHTTP({0xc0001fa570?}, {0x7ff6035058f0?, 0xc000168380?}, 0x6?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3210 +0x8e
+net/http.(*conn).serve(0xc00014c120, {0x7ff603507a58, 0xc0001fa3f0})
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:2092 +0x5d0
+created by net/http.(*Server).Serve in goroutine 1
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3360 +0x485
+
+panic error time:2025-08-07 11:59:48
+runtime error: invalid memory address or nil pointer dereference
+stacktrace from panic:goroutine 38 [running]:
+runtime/debug.Stack()
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/debug/stack.go:26 +0x5e
+iot-mqtt-gin/pkg/logger.Recover.func1()
+ E:/iot-mqtt-gin/pkg/logger/logger.go:138 +0x26f
+panic({0x7ff7e366d8c0?, 0x7ff7e3592f70?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/panic.go:791 +0x132
+database/sql.(*DB).conn(0x0, {0x7ff7e3977160, 0x7ff7e40b9f40}, 0x1)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1309 +0x54
+database/sql.(*DB).exec(0x0, {0x7ff7e3977160, 0x7ff7e40b9f40}, {0xc00002e480, 0x33}, {0xc000047480, 0x3, 0x3}, 0x59?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1681 +0x54
+database/sql.(*DB).ExecContext.func1(0x38?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1664 +0x4f
+database/sql.(*DB).retry(0x7ff7e2252acb?, 0xc000047358)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1568 +0x42
+database/sql.(*DB).ExecContext(0x0?, {0x7ff7e3977160?, 0x7ff7e40b9f40?}, {0xc00002e480?, 0x7ff7e224a49b?}, {0xc000047480?, 0x7ff7e36226c0?, 0x0?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1663 +0xc8
+database/sql.(*DB).Exec(0x4050000000000000?, {0xc00002e480?, 0x7ff7e4072900?}, {0xc000047480?, 0x2?, 0x30?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1677 +0x3a
+iot-mqtt-gin/service.InsertDeviceData(0xc000047530)
+ E:/iot-mqtt-gin/service/deviceService.go:31 +0x285
+iot-mqtt-gin/controllers.(*DeviceController).TDengineTest(0x7ff7e2486653?, 0xc001214000)
+ E:/iot-mqtt-gin/controllers/device.go:275 +0x9e
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+iot-mqtt-gin/pkg/logger.Recover(0xc001214000)
+ E:/iot-mqtt-gin/pkg/logger/logger.go:148 +0x5c
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc001214000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1(0xc001214000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 +0x6f
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc001214000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc000172000, 0xc001214000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 +0x892
+github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc000172000, {0x7ff7e39758f0, 0xc0000b0380}, 0xc000234140)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 +0x1b2
+net/http.serverHandler.ServeHTTP({0xc00003c5d0?}, {0x7ff7e39758f0?, 0xc0000b0380?}, 0x6?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3210 +0x8e
+net/http.(*conn).serve(0xc0001181b0, {0x7ff7e3977a58, 0xc000244f90})
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:2092 +0x5d0
+created by net/http.(*Server).Serve in goroutine 1
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3360 +0x485
+
+panic error time:2025-08-07 12:00:18
+runtime error: invalid memory address or nil pointer dereference
+stacktrace from panic:goroutine 38 [running]:
+runtime/debug.Stack()
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/debug/stack.go:26 +0x5e
+iot-mqtt-gin/pkg/logger.Recover.func1()
+ E:/iot-mqtt-gin/pkg/logger/logger.go:138 +0x26f
+panic({0x7ff7e366d8c0?, 0x7ff7e3592f70?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/panic.go:791 +0x132
+database/sql.(*DB).conn(0x0, {0x7ff7e3977160, 0x7ff7e40b9f40}, 0x1)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1309 +0x54
+database/sql.(*DB).exec(0x0, {0x7ff7e3977160, 0x7ff7e40b9f40}, {0xc00002e640, 0x33}, {0xc000047480, 0x3, 0x3}, 0x59?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1681 +0x54
+database/sql.(*DB).ExecContext.func1(0x38?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1664 +0x4f
+database/sql.(*DB).retry(0x7ff7e2252acb?, 0xc000047358)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1568 +0x42
+database/sql.(*DB).ExecContext(0x0?, {0x7ff7e3977160?, 0x7ff7e40b9f40?}, {0xc00002e640?, 0x7ff7e224a49b?}, {0xc000047480?, 0x7ff7e36226c0?, 0x0?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1663 +0xc8
+database/sql.(*DB).Exec(0x4050000000000000?, {0xc00002e640?, 0x7ff7e4072900?}, {0xc000047480?, 0x2?, 0x650000006e?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1677 +0x3a
+iot-mqtt-gin/service.InsertDeviceData(0xc000047530)
+ E:/iot-mqtt-gin/service/deviceService.go:31 +0x285
+iot-mqtt-gin/controllers.(*DeviceController).TDengineTest(0x7ff7e2486653?, 0xc001214000)
+ E:/iot-mqtt-gin/controllers/device.go:275 +0x9e
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+iot-mqtt-gin/pkg/logger.Recover(0xc001214000)
+ E:/iot-mqtt-gin/pkg/logger/logger.go:148 +0x5c
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc001214000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1(0xc001214000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 +0x6f
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc001214000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc000172000, 0xc001214000)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 +0x892
+github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc000172000, {0x7ff7e39758f0, 0xc0000b0460}, 0xc000234280)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 +0x1b2
+net/http.serverHandler.ServeHTTP({0xc00003c5d0?}, {0x7ff7e39758f0?, 0xc0000b0460?}, 0x6?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3210 +0x8e
+net/http.(*conn).serve(0xc0001181b0, {0x7ff7e3977a58, 0xc000244f90})
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:2092 +0x5d0
+created by net/http.(*Server).Serve in goroutine 1
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3360 +0x485
+
+panic error time:2025-08-07 13:43:26
+runtime error: invalid memory address or nil pointer dereference
+stacktrace from panic:goroutine 57 [running]:
+runtime/debug.Stack()
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/debug/stack.go:26 +0x5e
+iot-mqtt-gin/pkg/logger.Recover.func1()
+ E:/iot-mqtt-gin/pkg/logger/logger.go:138 +0x26f
+panic({0x7ff7958dd8c0?, 0x7ff795802f70?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/panic.go:791 +0x132
+database/sql.(*DB).conn(0x0, {0x7ff795be71c0, 0x7ff796329f40}, 0x1)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1309 +0x54
+database/sql.(*DB).exec(0x0, {0x7ff795be71c0, 0x7ff796329f40}, {0xc000122b40, 0x33}, {0xc0010c1480, 0x3, 0x3}, 0x59?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1681 +0x54
+database/sql.(*DB).ExecContext.func1(0xf8?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1664 +0x4f
+database/sql.(*DB).retry(0x7ff7944c2acb?, 0xc0010c1318)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1568 +0x42
+database/sql.(*DB).ExecContext(0x0?, {0x7ff795be71c0?, 0x7ff796329f40?}, {0xc000122b40?, 0x7ff7944ba49b?}, {0xc0010c1480?, 0x7ff7958926c0?, 0x0?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1663 +0xc8
+database/sql.(*DB).Exec(0x4050000000000000?, {0xc000122b40?, 0x7ff7962e2900?}, {0xc0010c1480?, 0x2?, 0xc0001ce660?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1677 +0x3a
+iot-mqtt-gin/service.InsertDeviceData(0xc0010c1530)
+ E:/iot-mqtt-gin/service/deviceService.go:31 +0x2a6
+iot-mqtt-gin/controllers.(*DeviceController).TDengineTest(0x7ff7946f6653?, 0xc0010a4200)
+ E:/iot-mqtt-gin/controllers/device.go:275 +0x9e
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+iot-mqtt-gin/pkg/logger.Recover(0xc0010a4200)
+ E:/iot-mqtt-gin/pkg/logger/logger.go:148 +0x5c
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc0010a4200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1(0xc0010a4200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 +0x6f
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc0010a4200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0000b64e0, 0xc0010a4200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 +0x892
+github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0000b64e0, {0x7ff795be5950, 0xc0000b02a0}, 0xc00008e3c0)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 +0x1b2
+net/http.serverHandler.ServeHTTP({0xc0001ce540?}, {0x7ff795be5950?, 0xc0000b02a0?}, 0x6?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3210 +0x8e
+net/http.(*conn).serve(0xc0000b25a0, {0x7ff795be7ab8, 0xc0001ce2a0})
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:2092 +0x5d0
+created by net/http.(*Server).Serve in goroutine 1
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3360 +0x485
+
+panic error time:2025-08-07 13:43:41
+runtime error: invalid memory address or nil pointer dereference
+stacktrace from panic:goroutine 57 [running]:
+runtime/debug.Stack()
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/debug/stack.go:26 +0x5e
+iot-mqtt-gin/pkg/logger.Recover.func1()
+ E:/iot-mqtt-gin/pkg/logger/logger.go:138 +0x26f
+panic({0x7ff7958dd8c0?, 0x7ff795802f70?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/runtime/panic.go:791 +0x132
+database/sql.(*DB).conn(0x0, {0x7ff795be71c0, 0x7ff796329f40}, 0x1)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1309 +0x54
+database/sql.(*DB).exec(0x0, {0x7ff795be71c0, 0x7ff796329f40}, {0xc000122d00, 0x33}, {0xc0010c1480, 0x3, 0x3}, 0x59?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1681 +0x54
+database/sql.(*DB).ExecContext.func1(0xf8?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1664 +0x4f
+database/sql.(*DB).retry(0x7ff7944c2acb?, 0xc0010c1318)
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1568 +0x42
+database/sql.(*DB).ExecContext(0x0?, {0x7ff795be71c0?, 0x7ff796329f40?}, {0xc000122d00?, 0x7ff7944ba49b?}, {0xc0010c1480?, 0x7ff7958926c0?, 0x0?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1663 +0xc8
+database/sql.(*DB).Exec(0x4050000000000000?, {0xc000122d00?, 0x7ff7962e2900?}, {0xc0010c1480?, 0x2?, 0x2f00000031?})
+ E:/go-version/go1.23.7.windows-amd64/go/src/database/sql/sql.go:1677 +0x3a
+iot-mqtt-gin/service.InsertDeviceData(0xc0010c1530)
+ E:/iot-mqtt-gin/service/deviceService.go:31 +0x2a6
+iot-mqtt-gin/controllers.(*DeviceController).TDengineTest(0x7ff7946f6653?, 0xc0010a4200)
+ E:/iot-mqtt-gin/controllers/device.go:275 +0x9e
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+iot-mqtt-gin/pkg/logger.Recover(0xc0010a4200)
+ E:/iot-mqtt-gin/pkg/logger/logger.go:148 +0x5c
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc0010a4200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1(0xc0010a4200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 +0x6f
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc0010a4200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 +0xe5
+github.com/gin-gonic/gin.(*Context).Next(...)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185
+github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0000b64e0, 0xc0010a4200)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 +0x892
+github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0000b64e0, {0x7ff795be5950, 0xc0000b0380}, 0xc00008e640)
+ C:/Users/admin/go/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 +0x1b2
+net/http.serverHandler.ServeHTTP({0xc0001ce540?}, {0x7ff795be5950?, 0xc0000b0380?}, 0x6?)
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3210 +0x8e
+net/http.(*conn).serve(0xc0000b25a0, {0x7ff795be7ab8, 0xc0001ce2a0})
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:2092 +0x5d0
+created by net/http.(*Server).Serve in goroutine 1
+ E:/go-version/go1.23.7.windows-amd64/go/src/net/http/server.go:3360 +0x485
+
diff --git a/runtime/log/error_2025-08-08.log b/runtime/log/error_2025-08-08.log
new file mode 100644
index 0000000..8f6a5af
--- /dev/null
+++ b/runtime/log/error_2025-08-08.log
@@ -0,0 +1,2 @@
+{"level":"error","msg":"[]","time":"2025-08-08 16:25:45","发布NSQ消息失败: ":"aaa"}
+{"level":"error","msg":"[]","time":"2025-08-08 16:27:47","发布NSQ消息失败: ":"aaa"}
diff --git a/runtime/log/success_2025-07-30.log b/runtime/log/success_2025-07-30.log
new file mode 100644
index 0000000..6ef060b
--- /dev/null
+++ b/runtime/log/success_2025-07-30.log
@@ -0,0 +1,58 @@
+2025-07-30 15:55:07 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 525.3µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 15:55:07 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 532.1µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 15:55:07 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 2.4203ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 15:55:07 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 9.1765ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 15:55:08 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:01:00 - 127.0.0.1 "POST /api/v1/users/add HTTP/1.1 404 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:01:57 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 531.1µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:01:57 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 529.1µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:01:57 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.055ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:01:57 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.5446ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:01:58 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:02:09 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 500 142.4599ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:24:11 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 500 147.1968ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:28:03 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.0611ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:28:03 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 527.7µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:28:03 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.5885ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:28:03 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 4.2883ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:28:04 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:28:25 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 500 148.9433ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:30:04 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 2.6989ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:30:04 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 2.3968ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:30:04 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 2.1781ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:30:04 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 7.1488ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:30:05 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 517µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:30:18 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 500 158.6516ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:40:22 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 4.289ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:40:23 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 4.3853ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:40:23 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 6.3975ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:40:23 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 18.1871ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:40:23 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:40:36 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 500 175.6903ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:42:22 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 558.5µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:42:22 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 999.6µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:42:22 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.0007ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:42:22 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 2.9985ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:42:23 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 1.0009ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:42:39 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 500 179.8003ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:43:23 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 532.7µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:43:23 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 526.4µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:43:23 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.5411ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:43:23 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 2.5451ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:43:23 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 1.7835ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:43:36 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 409 78.6288ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:55:00 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.6122ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:55:00 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.1278ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:55:00 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.5251ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:55:00 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 9.1279ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:55:01 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 998.9µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:55:34 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 409 69.8489ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:55:44 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 200 211.0508ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:57:46 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 520.3µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:57:46 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.1222ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:57:46 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.7364ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:57:46 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 5.1217ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:57:47 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:58:09 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 409 76.324ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:58:10 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 409 76.6761ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-30 16:58:18 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 200 236.4315ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
diff --git a/runtime/log/success_2025-07-31.log b/runtime/log/success_2025-07-31.log
new file mode 100644
index 0000000..7219813
--- /dev/null
+++ b/runtime/log/success_2025-07-31.log
@@ -0,0 +1,58 @@
+2025-07-31 15:18:09 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.0964ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:18:09 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 684.9µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:18:09 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.0442ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:18:09 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.8016ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:18:10 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 524.2µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:18:48 - 127.0.0.1 "GET /api/v1/devices/online/{device_id} HTTP/1.1 400 117.2224ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:20:03 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 525.1µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:20:03 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 527.5µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:20:03 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 528.7µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:20:03 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 4.0033ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:20:03 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 524.3µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:20:16 - 127.0.0.1 "GET /api/v1/devices/online/test001 HTTP/1.1 400 108.5836ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:22:26 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 531.4µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:22:26 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 524.9µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:22:26 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.0694ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:22:26 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 4.3704ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:22:26 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 519.6µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:22:41 - 127.0.0.1 "GET /api/v1/devices/online/test001 HTTP/1.1 400 115.5238ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:25:21 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.0442ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:25:21 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.0553ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:25:21 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.5844ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:25:21 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 4.248ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:25:21 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:25:34 - 127.0.0.1 "GET /api/v1/devices/online/test001 HTTP/1.1 404 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:26:16 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.0511ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:26:16 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 524.5µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:26:16 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.0965ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:26:16 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.9223ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:26:16 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:26:28 - 127.0.0.1 "GET /api/v1/devices/online HTTP/1.1 400 110.6415ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:31:51 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.0959ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:31:51 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.5932ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:31:51 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.0889ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:31:51 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.7093ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:31:52 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 526.7µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:32:04 - 127.0.0.1 "GET /api/v1/devices/online/test001 HTTP/1.1 400 110.9269ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:38:09 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.5942ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:38:09 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.1239ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:38:09 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.0688ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:38:09 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 4.2281ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:38:09 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:38:19 - 127.0.0.1 "GET /api/v1/devices/online/test001 HTTP/1.1 400 135.1098ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:41:18 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.0881ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:41:18 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.0507ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:41:18 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.0603ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:41:18 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.7328ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:41:19 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:41:28 - 127.0.0.1 "GET /api/v1/devices/online/test001 HTTP/1.1 400 108.302ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:49:47 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.0515ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:49:47 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.0509ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:49:47 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.6686ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:49:47 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 2.7679ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:49:47 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:49:59 - 127.0.0.1 "GET /api/v1/devices/online/test001 HTTP/1.1 200 110.5417ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:56:25 - 127.0.0.1 "GET /api/v1/devices/online/test001 HTTP/1.1 200 108.9329ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 15:59:00 - 127.0.0.1 "GET /api/v1/devices/online/test001 HTTP/1.1 200 122.054ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 16:06:06 - 127.0.0.1 "GET /api/v1/devices/online/test001 HTTP/1.1 200 115.5261ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-07-31 16:17:59 - 127.0.0.1 "GET /api/v1/devices/online/test001 HTTP/1.1 200 113.0256ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
diff --git a/runtime/log/success_2025-08-01.log b/runtime/log/success_2025-08-01.log
new file mode 100644
index 0000000..e6624ac
--- /dev/null
+++ b/runtime/log/success_2025-08-01.log
@@ -0,0 +1,70 @@
+2025-08-01 09:44:50 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.0465ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:44:50 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 522.1µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:44:50 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.053ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:44:50 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.7618ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:44:50 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:44:51 - 127.0.0.1 "GET /swagger/favicon-32x32.png HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:51:49 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 517.8µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:51:49 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 527.2µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:51:49 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.0578ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:51:49 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.825ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:51:50 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 999.1µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:51:50 - 127.0.0.1 "GET /swagger/favicon-32x32.png HTTP/1.1 200 1.0008ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:52:04 - 127.0.0.1 "POST /api/v1/devices/test001/online HTTP/1.1 404 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:53:58 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.147ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:53:59 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 524.9µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:53:59 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.5735ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:53:59 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.2014ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:53:59 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 09:54:10 - 127.0.0.1 "GET /api/v1/devices/test001/online HTTP/1.1 200 34.9332ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 10:29:03 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 524.2µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 10:29:03 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 524.3µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 10:29:03 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.05ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 10:29:03 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 2.0992ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 10:29:04 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 10:29:20 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 37.1695ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 10:35:40 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 34.481ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:09:29 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 71.7295ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:11:16 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 2.0953ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:11:16 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.0678ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:11:16 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 655.6µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:11:16 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 2.7891ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:11:17 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:12:00 - 127.0.0.1 "GET /api/v1/devices/test002/onoff HTTP/1.1 400 108.7722ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:13:14 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 517.7µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:13:14 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 523.7µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:13:14 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.6992ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:13:14 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.8067ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:13:15 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 522.6µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:13:26 - 127.0.0.1 "GET /api/v1/devices/test002/onoff HTTP/1.1 400 70.9611ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:13:37 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 113.9314ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:15:22 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:15:22 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 304 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:15:22 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 304 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:15:22 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 304 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:15:22 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:17:06 - 127.0.0.1 "GET /api/v1/devices/test002/onoff HTTP/1.1 400 74.8769ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:17:10 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 114.3223ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:35:40 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 524.9µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:35:40 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:35:40 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.9987ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:35:40 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.9976ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 14:35:40 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 16:16:34 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 530.6µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 16:16:34 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.058ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 16:16:34 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.169ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 16:16:34 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 2.7306ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 16:16:35 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 16:16:35 - 127.0.0.1 "GET /swagger/favicon-32x32.png HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 16:16:49 - 127.0.0.1 "GET /api/v1/devices/test002/onoff HTTP/1.1 400 69.9941ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 16:16:55 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 106.1202ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 16:55:58 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 103.7162ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 18:37:41 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 104.5181ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 18:41:59 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 107.9305ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 18:45:33 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 105.9445ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 18:46:22 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 105.2668ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 18:49:03 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 105.9009ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 18:51:15 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 107.6924ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 18:52:59 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 106.6643ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 18:54:05 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 115.5263ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-01 19:02:29 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 108.9289ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
diff --git a/runtime/log/success_2025-08-02.log b/runtime/log/success_2025-08-02.log
new file mode 100644
index 0000000..e23c308
--- /dev/null
+++ b/runtime/log/success_2025-08-02.log
@@ -0,0 +1,61 @@
+2025-08-02 14:06:38 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 14:06:38 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 578.1µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 14:06:38 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 667.5µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 14:06:38 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.4661ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 14:06:38 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 14:06:48 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 113.3982ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 14:17:21 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 106.055ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 14:18:40 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 106.9011ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 14:24:37 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 107.0917ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 14:29:02 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 106.3639ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 14:31:01 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 119.6805ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 14:36:41 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 108.6759ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 14:39:35 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 117.6633ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:43:45 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 2.1487ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:43:45 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.7422ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:43:45 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 2.252ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:43:45 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 6.2183ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:43:46 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:43:58 - 127.0.0.1 "GET /api/v1/devices/test0010/onoff HTTP/1.1 400 80.8702ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:44:01 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 74.5893ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:47:25 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 2.9825ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:47:25 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.7645ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:47:25 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 2.9536ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:47:25 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 12.1051ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:47:26 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:47:37 - 127.0.0.1 "GET /api/v1/devices/test0010/onoff HTTP/1.1 400 68.5067ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:47:42 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 66.9079ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:49:40 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 75.1122ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:51:01 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 68.6448ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 16:52:39 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 73.9212ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:05:25 - 127.0.0.1 "GET /api/v1/devices/test0010/onoff HTTP/1.1 400 73.5588ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:05:28 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 103.794ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:14:47 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 3.1093ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:14:47 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.0088ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:14:47 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 2.011ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:14:47 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 4.9854ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:14:48 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 999.1µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:18:58 - 127.0.0.1 "POST /api/v1/mqttuser/add HTTP/1.1 200 222.8678ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:19:25 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:19:25 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 304 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:19:25 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 304 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:19:25 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 304 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:19:26 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:19:37 - 127.0.0.1 "GET /api/v1/devices/test0010/onoff HTTP/1.1 400 69.9026ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:19:41 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 106.4687ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:32:22 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 116.1631ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:33:20 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 109.6692ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:35:25 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 117.2604ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:38:31 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 103.1939ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:41:25 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 116.5049ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:43:02 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 109.9111ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:44:07 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 104.1896ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:46:00 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 111.3403ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:48:45 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 107.9489ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:49:32 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 113.6651ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 17:55:06 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 110.7404ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 18:18:08 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 123.6243ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 18:21:58 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 105.7457ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 18:31:28 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 117.9846ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 18:42:41 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 112.0142ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-02 18:52:08 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 111.8547ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
diff --git a/runtime/log/success_2025-08-04.log b/runtime/log/success_2025-08-04.log
new file mode 100644
index 0000000..1577998
--- /dev/null
+++ b/runtime/log/success_2025-08-04.log
@@ -0,0 +1,21 @@
+2025-08-04 11:20:23 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.0458ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 11:20:23 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 545.6µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 11:20:23 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.0304ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 11:20:23 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 4.5128ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 11:20:23 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 11:20:34 - 127.0.0.1 "GET /api/v1/devices/test002/onoff HTTP/1.1 400 70.8045ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 11:20:39 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 104.5479ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 11:28:08 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 112.1495ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 11:49:14 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 122.1715ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 11:53:05 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 108.8308ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 11:54:19 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 105.4534ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 12:02:59 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 112.8117ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 13:44:23 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 106.4547ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 15:35:09 - 127.0.0.1 "GET /api/v1/devices/test0011/onoff HTTP/1.1 400 78.2686ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 15:35:12 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 113.7714ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 15:37:23 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 109.1676ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 15:39:12 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 110.891ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 15:49:38 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 112.2678ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 15:52:26 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 109.8225ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 16:38:59 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 114.4061ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-04 17:23:06 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 102.0925ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
diff --git a/runtime/log/success_2025-08-07.log b/runtime/log/success_2025-08-07.log
new file mode 100644
index 0000000..40b0392
--- /dev/null
+++ b/runtime/log/success_2025-08-07.log
@@ -0,0 +1,20 @@
+2025-08-07 11:33:04 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.0496ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-07 11:33:04 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.0535ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-07 11:33:04 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.5898ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-07 11:33:04 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 4.2544ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-07 11:33:04 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-07 11:33:10 - 127.0.0.1 "GET /swagger/favicon-32x32.png HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-07 11:33:22 - 127.0.0.1 "GET /api/v1/devices/t/onoff HTTP/1.1 400 84.1097ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-07 11:34:09 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 400 0s "PostmanRuntime-ApipostRuntime/1.1.0" "
+2025-08-07 11:36:10 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 200 1.5637ms "PostmanRuntime-ApipostRuntime/1.1.0" "
+2025-08-07 11:48:21 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 200 1.04ms "PostmanRuntime-ApipostRuntime/1.1.0" "
+2025-08-07 11:55:17 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 200 1.0318ms "PostmanRuntime-ApipostRuntime/1.1.0" "
+2025-08-07 11:59:48 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 200 1.0328ms "PostmanRuntime-ApipostRuntime/1.1.0" "
+2025-08-07 12:00:18 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 200 608.2µs "PostmanRuntime-ApipostRuntime/1.1.0" "
+2025-08-07 13:43:26 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 200 2.0987ms "PostmanRuntime-ApipostRuntime/1.1.0" "
+2025-08-07 13:43:41 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 200 999.6µs "PostmanRuntime-ApipostRuntime/1.1.0" "
+2025-08-07 14:17:39 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 500 525.1µs "PostmanRuntime-ApipostRuntime/1.1.0" "
+2025-08-07 14:19:25 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 500 0s "PostmanRuntime-ApipostRuntime/1.1.0" "
+2025-08-07 14:30:39 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 500 70.0139ms "PostmanRuntime-ApipostRuntime/1.1.0" "
+2025-08-07 14:48:19 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 500 69.4181ms "PostmanRuntime-ApipostRuntime/1.1.0" "
+2025-08-07 15:14:02 - 127.0.0.1 "GET /api/v1/devices/tdengine/test HTTP/1.1 200 111.1398ms "PostmanRuntime-ApipostRuntime/1.1.0" "
diff --git a/runtime/log/success_2025-08-08.log b/runtime/log/success_2025-08-08.log
new file mode 100644
index 0000000..65c7d15
--- /dev/null
+++ b/runtime/log/success_2025-08-08.log
@@ -0,0 +1,22 @@
+2025-08-08 15:38:24 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 1.0571ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 15:38:24 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 527.4µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 15:38:24 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.3394ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 15:38:24 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 4.7699ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 15:38:24 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 15:38:59 - 127.0.0.1 "GET /api/v1/devices/test0010/onoff HTTP/1.1 400 72.4429ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 15:39:05 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 70.7438ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 15:39:36 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 104.0549ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 15:59:16 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 112.2301ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 16:19:13 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 106.9259ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 16:21:14 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 107.4204ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 16:23:51 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 107.17ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 16:25:40 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 108.815ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 16:27:43 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 108.3744ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 16:29:24 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 108.4883ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 17:32:58 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 110.1685ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 18:13:45 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 106.1567ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 18:19:27 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 105.6406ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 18:29:50 - 127.0.0.1 "GET /api/v1/devices/test0010/onoff HTTP/1.1 400 76.495ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 18:29:57 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 70.7311ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 18:29:59 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 69.6559ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
+2025-08-08 18:30:45 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 104.4584ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "
diff --git a/service/deviceService.go b/service/deviceService.go
new file mode 100644
index 0000000..0bbdcfd
--- /dev/null
+++ b/service/deviceService.go
@@ -0,0 +1,45 @@
+package service
+
+import (
+ "fmt"
+ "iot-mqtt-gin/config"
+ "iot-mqtt-gin/dao"
+ "iot-mqtt-gin/models"
+ "time"
+)
+
+// InsertDeviceData 插入设备数据
+func InsertDeviceData(data *models.DeviceData) error {
+ db, err := dao.GetDB()
+ if err != nil {
+ return err
+ }
+ // 如果未提供时间戳,使用当前时间
+ if data.Timestamp.IsZero() {
+ data.Timestamp = time.Now()
+ }
+
+ // 表名规则:使用设备ID作为子表名
+ tableName := "t"
+
+ // 插入数据
+ insertSQL := fmt.Sprintf(`
+ INSERT INTO %s (%s, %s, %s) VALUES ('%s', %v, %v)`,
+ tableName,
+ config.FieldTimestamp,
+ config.FieldValue,
+ config.FieldStatus,
+ data.Timestamp.Format("2006-01-02 15:04:05"), // 注意这里添加了单引号
+ data.Value,
+ data.Status,
+ )
+
+ fmt.Println("插入sql语句", insertSQL)
+ _, err = db.Exec(insertSQL)
+
+ if err != nil {
+ return fmt.Errorf("插入数据失败: %v", err)
+ }
+
+ return nil
+}
diff --git a/service/mq_service.go b/service/mq_service.go
new file mode 100644
index 0000000..ebeab98
--- /dev/null
+++ b/service/mq_service.go
@@ -0,0 +1,153 @@
+package service
+
+import (
+ "context"
+ "fmt"
+ "log"
+
+ mqsd "github.com/yyboo586/MQSDK"
+ "iot-mqtt-gin/config"
+ "iot-mqtt-gin/models"
+)
+
+// MQService 消息队列服务
+type MQService struct {
+ factory *mqsd.Factory
+ nsqConsumer mqsd.Consumer
+ kafkaConsumer mqsd.Consumer
+ rabbitConsumer mqsd.Consumer
+}
+
+// NewMQService 创建消息队列服务
+func NewMQService() *MQService {
+ return &MQService{
+ factory: mqsd.NewFactory(),
+ }
+}
+
+// InitProducers 初始化生产者
+func (s *MQService) InitProducers() (
+ nsqProducer mqsd.Producer,
+ kafkaProducer mqsd.Producer,
+ rabbitProducer mqsd.Producer,
+ err error,
+) {
+ // 初始化NSQ生产者
+ nsqProducer, err = s.factory.NewProducer(config.GetNSQConfig())
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("初始化NSQ生产者失败: %w", err)
+ }
+
+ // 初始化Kafka生产者
+ /*kafkaProducer, err = s.factory.NewProducer(config.GetKafkaConfig())
+ if err != nil {
+ nsqProducer.Close()
+ return nil, nil, nil, fmt.Errorf("初始化Kafka生产者失败: %w", err)
+ }
+
+ // 初始化RabbitMQ生产者
+ rabbitProducer, err = s.factory.NewProducer(config.GetRabbitMQConfig())
+ if err != nil {
+ nsqProducer.Close()
+ kafkaProducer.Close()
+ return nil, nil, nil, fmt.Errorf("初始化RabbitMQ生产者失败: %w", err)
+ }*/
+
+ //return nsqProducer, kafkaProducer, rabbitProducer, nil
+ return nsqProducer, nil, nil, nil
+}
+
+// InitConsumers 初始化消费者
+func (s *MQService) InitConsumers() error {
+ var err error
+
+ // 初始化NSQ消费者
+ s.nsqConsumer, err = s.factory.NewConsumer(config.GetNSQConfig())
+ if err != nil {
+ return fmt.Errorf("初始化NSQ消费者失败: %w", err)
+ }
+
+ // 初始化Kafka消费者
+ s.kafkaConsumer, err = s.factory.NewConsumer(config.GetKafkaConfig())
+ if err != nil {
+ s.nsqConsumer.Close()
+ return fmt.Errorf("初始化Kafka消费者失败: %w", err)
+ }
+
+ // 初始化RabbitMQ消费者
+ s.rabbitConsumer, err = s.factory.NewConsumer(config.GetRabbitMQConfig())
+ if err != nil {
+ s.nsqConsumer.Close()
+ s.kafkaConsumer.Close()
+ return fmt.Errorf("初始化RabbitMQ消费者失败: %w", err)
+ }
+
+ return nil
+}
+
+// StartConsuming 开始消费消息
+func (s *MQService) StartConsuming() {
+ // 启动NSQ消费
+ go func() {
+ if err := s.nsqConsumer.Subscribe(context.Background(), "test-topic", handleNSQMessage); err != nil {
+ log.Printf("NSQ消费失败: %v", err)
+ }
+ }()
+
+ // 启动Kafka消费
+ go func() {
+ if err := s.kafkaConsumer.Subscribe(context.Background(), "test-topic", handleKafkaMessage); err != nil {
+ log.Printf("Kafka消费失败: %v", err)
+ }
+ }()
+
+ // 启动RabbitMQ消费
+ go func() {
+ if err := s.rabbitConsumer.Subscribe(context.Background(), "test-topic", handleRabbitMessage); err != nil {
+ log.Printf("RabbitMQ消费失败: %v", err)
+ }
+ }()
+
+ log.Println("所有消费者已启动")
+}
+
+// Close 关闭所有连接
+func (s *MQService) Close() {
+ if s.nsqConsumer != nil {
+ s.nsqConsumer.Close()
+ }
+ if s.kafkaConsumer != nil {
+ s.kafkaConsumer.Close()
+ }
+ if s.rabbitConsumer != nil {
+ s.rabbitConsumer.Close()
+ }
+ log.Println("所有MQ连接已关闭")
+}
+
+// 处理NSQ消息
+func handleNSQMessage(msg *mqsd.Message) error {
+ message := models.FromMQSDMessage(msg)
+ log.Printf("[NSQ] 收到消息 - ID: %s, Topic: %s, Content: %s",
+ message.ID, message.Topic, message.Body)
+ // 这里添加消息处理逻辑
+ return nil
+}
+
+// 处理Kafka消息
+func handleKafkaMessage(msg *mqsd.Message) error {
+ message := models.FromMQSDMessage(msg)
+ log.Printf("[Kafka] 收到消息 - ID: %s, Topic: %s, Content: %s",
+ message.ID, message.Topic, message.Body)
+ // 这里添加消息处理逻辑
+ return nil
+}
+
+// 处理RabbitMQ消息
+func handleRabbitMessage(msg *mqsd.Message) error {
+ message := models.FromMQSDMessage(msg)
+ log.Printf("[RabbitMQ] 收到消息 - ID: %s, Topic: %s, Content: %s",
+ message.ID, message.Topic, message.Body)
+ // 这里添加消息处理逻辑
+ return nil
+}