package handler import ( "encoding/json" "fmt" "net/http" "strconv" "strings" "github.com/gin-gonic/gin" "github.com/luxsin/app-api/pkg/encode" "github.com/redis/go-redis/v9" "go.uber.org/zap" ) const redisHeadphoneImpedancesKey = "headphone_impedances" type ImpedanceHandler struct { redis *redis.Client log *zap.Logger } func NewImpedanceHandler(redis *redis.Client, log *zap.Logger) *ImpedanceHandler { return &ImpedanceHandler{ redis: redis, log: log, } } // ReportImpedance 上报耳机阻抗 // // @Summary 上报耳机阻抗 // @Tags Device // @Produce json // @Param mac query string true "设备 MAC 地址" // @Param name query string true "设备型号" // @Param brand query string true "耳机品牌" // @Param model query string true "耳机型号" // @Param value query int true "阻抗值(整数)" // @Success 200 {object} map[string]any "操作成功" // @Failure 500 {object} object // @Router /audio/reportImpedance [get] func (h *ImpedanceHandler) ReportImpedance(c *gin.Context) { mac := strings.TrimSpace(c.Query("mac")) deviceModel := strings.TrimSpace(c.Query("name")) brand := strings.TrimSpace(c.Query("brand")) headphoneModel := strings.TrimSpace(c.Query("model")) valueStr := strings.TrimSpace(c.Query("value")) clientIP := encode.ClientPublicIP(c) if mac == "" || deviceModel == "" || brand == "" || headphoneModel == "" || valueStr == "" { c.JSON(http.StatusOK, gin.H{ "code": 400, "msg": "参数校验失败", }) return } // 品牌/型号含乱码或控制字符、超长属于无效数据,静默丢弃 if !isValidHeadphoneText(brand) || !isValidHeadphoneText(headphoneModel) { c.JSON(http.StatusOK, gin.H{ "code": 200, "msg": "操作成功", }) return } impedance, err := strconv.Atoi(valueStr) if err != nil { c.JSON(http.StatusOK, gin.H{ "code": 400, "msg": "参数校验失败", }) return } // 阻抗值为 0 属于无效数据,静默丢弃 if impedance == 0 { c.JSON(http.StatusOK, gin.H{ "code": 200, "msg": "操作成功", }) return } brandNorm := normalizeHeadphoneText(brand) modelNorm := normalizeHeadphoneText(headphoneModel) impedanceInfo := map[string]any{ "mac_addr": mac, "device_model": deviceModel, "impedance_ohm": impedance, "headphone_brand": brand, "headphone_model": headphoneModel, "headphone_brand_norm": brandNorm, "headphone_model_norm": modelNorm, "ip_addr": clientIP, } jsonData, err := json.Marshal(impedanceInfo) if err != nil { h.log.Error("marshal impedance info failed", zap.Error(err)) c.JSON(http.StatusOK, gin.H{ "code": 500, "msg": "系统错误", }) return } field := impedanceRedisField(mac, brandNorm, modelNorm) ctx := c.Request.Context() if err := h.redis.HSet(ctx, redisHeadphoneImpedancesKey, field, jsonData).Err(); err != nil { h.log.Error("redis hset failed", zap.Error(err)) c.JSON(http.StatusOK, gin.H{ "code": 500, "msg": "系统错误", }) return } c.JSON(http.StatusOK, gin.H{ "code": 200, "msg": "操作成功", }) } func normalizeHeadphoneText(s string) string { return strings.ToLower(strings.TrimSpace(s)) } // isValidHeadphoneText 校验耳机品牌/型号:拒绝含乱码字符(U+FFFD)、控制字符及超长(>100 字符)的输入 func isValidHeadphoneText(s string) bool { if len(s) > 100 { return false } for _, r := range s { if r == '\ufffd' || r < 0x20 || r == 0x7f { return false } } return true } func impedanceRedisField(mac, brandNorm, modelNorm string) string { return fmt.Sprintf("%s|%s|%s", mac, brandNorm, modelNorm) }