package handler import ( "encoding/json" "log" "star-cloud-gateway/internal/bridge" "strings" mqtt "github.com/eclipse/paho.mqtt.golang" ) type MqttHandler struct { pusher *bridge.RedisPusher } func NewMqttHandler(pusher *bridge.RedisPusher) *MqttHandler { return &MqttHandler{pusher: pusher} } func (h *MqttHandler) HandleMessage(client mqtt.Client, msg mqtt.Message) { topic := msg.Topic() payload := msg.Payload() log.Printf("Received message on topic: %s", topic) var serialNo, msgType string var jsonPayload interface{} // 檢查是否為系統主題 ($SYS) if strings.HasPrefix(topic, "$SYS/") { parts := strings.Split(topic, "/") // 尋找事件類型 (connected 或 disconnected) var event string for _, p := range parts { if p == "connected" || p == "disconnected" { event = p break } } // 尋找 Client ID (通常位於 "clients" 的下一層) for i, p := range parts { if p == "clients" && i+1 < len(parts) { serialNo = parts[i+1] break } } if event != "" && serialNo != "" { // 解析系統 JSON Payload (補強:有些版本會把 ClientID 放在 Payload 裡) var sysData map[string]interface{} if err := json.Unmarshal(payload, &sysData); err == nil { if cid, ok := sysData["clientid"].(string); ok { serialNo = cid } } msgType = "status" statusValue := "online" if event == "disconnected" { statusValue = "offline" } jsonPayload = map[string]string{"status": statusValue} log.Printf("Detected system event: Client [%s] is now [%s]", serialNo, statusValue) } else { return } } else { // 一般業務主題 (machine/{serialNo}/{type}) parts := strings.Split(topic, "/") if len(parts) < 3 { return } serialNo = parts[1] msgType = parts[2] err := json.Unmarshal(payload, &jsonPayload) if err != nil { // 如果不是有效的 JSON,則視為純文字/數值 (Raw Value) jsonPayload = string(payload) } } bridgePayload := bridge.BridgePayload{ Type: msgType, SerialNo: cleanSerialNo(serialNo), Payload: jsonPayload, } h.pusher.Push(bridgePayload) } // cleanSerialNo 處理 Client ID 格式,將 SC_M0408_pndk 轉為 M0408 func cleanSerialNo(clientID string) string { if clientID == "" { return "" } // 1. 移除已知前綴 SC_ cleaned := strings.TrimPrefix(clientID, "SC_") // 2. 如果中間還有底線,通常代表隨機碼,只取前面部分 // 例如 M0408_test -> M0408 if idx := strings.Index(cleaned, "_"); idx != -1 { return cleaned[:idx] } return cleaned }