package handler import ( "net/http" "strconv" "strings" "github.com/gin-gonic/gin" "github.com/luxsin/app-api/internal/repository" "github.com/luxsin/app-api/internal/response" "go.uber.org/zap" ) type OTAHandler struct { repo *repository.OTARepository log *zap.Logger } func NewOTAHandler(repo *repository.OTARepository, log *zap.Logger) *OTAHandler { return &OTAHandler{repo: repo, log: log} } // GetVerName 查询版本名称 // // @Summary 查询版本名称 // @Description 根据设备型号和版本号(verCode)定位唯一 OTA 记录,返回版本名称 verName // @Tags OTA // @Produce json // @Param model query string true "设备型号" // @Param version query int true "版本号 (verCode)" // @Success 200 {object} object "成功返回 verName" // @Failure 400 {object} object // @Failure 500 {object} object // @Router /audio/getVerName [get] func (h *OTAHandler) GetVerName(c *gin.Context) { modelVal := strings.TrimSpace(c.Query("model")) versionStr := strings.TrimSpace(c.Query("version")) // 参数校验 if modelVal == "" || versionStr == "" { response.BadRequest(c, "model和version参数不能为空") return } version, err := strconv.Atoi(versionStr) if err != nil { response.BadRequest(c, "version参数格式错误") return } ctx := c.Request.Context() verName, found, err := h.repo.GetVerNameByVersion(ctx, modelVal, version) if err != nil { h.log.Error("query ota verName failed", zap.Error(err)) response.InternalError(c, "查询版本名称失败") return } if !found { response.Fail(c, http.StatusOK, 40004, "无OTA版本信息") return } response.OK(c, gin.H{"verName": verName}) } // GetOTA 获取 OTA 升级信息 // // @Summary 获取 OTA 升级信息 // @Description 根据设备型号和硬件版本查询最新 OTA 记录,支持黑名单过滤和定向升级逻辑 // @Tags OTA // @Produce json // @Param model query string true "设备型号" // @Param hw query int true "硬件版本" // @Param mac query string false "设备 MAC 地址" // @Param beta query int false "是否 beta 通道 (0=否,1=是)" // @Success 200 {object} object "成功返回 OTA 信息" // @Failure 400 {object} object // @Failure 500 {object} object // @Router /audio/ota [get] func (h *OTAHandler) GetOTA(c *gin.Context) { modelVal := strings.TrimSpace(c.Query("model")) hwStr := strings.TrimSpace(c.Query("hw")) mac := strings.TrimSpace(c.Query("mac")) betaStr := strings.TrimSpace(c.Query("beta")) // 参数校验 if modelVal == "" || hwStr == "" { response.BadRequest(c, "model和hw参数不能为空") return } hw, err := strconv.Atoi(hwStr) if err != nil { response.BadRequest(c, "hw参数格式错误") return } beta := 0 if betaStr != "" { if b, err := strconv.Atoi(betaStr); err == nil { beta = b } } if mac == "" { mac = "null" } ctx := c.Request.Context() // 1. 查询最新 OTA 记录 ota, err := h.repo.GetLatestOTA(ctx, modelVal, hw, beta) if err != nil { h.log.Error("query ota failed", zap.Error(err)) response.InternalError(c, "查询OTA信息失败") return } if ota == nil { response.Fail(c, http.StatusOK, 40004, "无OTA升级信息") return } // 2. 检查黑名单 inBlackList, err := h.repo.IsInBlackList(ctx, ota.ID, mac) if err != nil { h.log.Error("check black list failed", zap.Error(err)) response.InternalError(c, "查询黑名单失败") return } if inBlackList { h.log.Info("mac命中OTA黑名单", zap.String("mac", mac), zap.Int("ota_id", ota.ID)) // 命中黑名单,返回一条不在黑名单中的最新记录 ota, err = h.repo.GetLatestOTANotInBlackList(ctx, modelVal, hw, beta, mac) if err != nil { h.log.Error("query ota not in blacklist failed", zap.Error(err)) response.InternalError(c, "查询OTA信息失败") return } if ota == nil { response.Fail(c, http.StatusOK, 40004, "无OTA升级信息") return } response.OK(c, ota) return } // 3. 检查是否为定向升级 if !bool(ota.Target) { // 非定向,直接返回 response.OK(c, ota) return } // 定向升级,检查 mac 是否在定向设备列表中 targetDevice, err := h.repo.FindTargetDevice(ctx, ota.ID, mac) if err != nil { h.log.Error("check target device failed", zap.Error(err)) response.InternalError(c, "查询定向设备失败") return } if targetDevice != nil { // 定向命中,返回该 OTA response.OK(c, ota) return } // 定向未命中,返回非定向的最新版本 notTargetOTA, err := h.repo.GetLatestOTANotTarget(ctx, modelVal, hw, beta) if err != nil { h.log.Error("query ota not target failed", zap.Error(err)) response.InternalError(c, "查询OTA信息失败") return } if notTargetOTA == nil { response.Fail(c, http.StatusOK, 40004, "无OTA升级信息") return } response.OK(c, notTargetOTA) }