新增了针对 mac 地址屏蔽webui 导航栏的接口
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -252,6 +252,47 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/audio/getNavConfig": {
|
||||||
|
"get": {
|
||||||
|
"description": "根据设备 MAC 查询需要隐藏的导航入口(nav key: community|hpEq|home|effects|myAi)。未命中返回空数组,前端应默认全部显示。",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"NavConfig"
|
||||||
|
],
|
||||||
|
"summary": "获取导航栏隐藏配置",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "设备 MAC 地址",
|
||||||
|
"name": "mac",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功返回 hidden 列表",
|
||||||
|
"schema": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/audio/getVerName": {
|
"/audio/getVerName": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "根据设备型号和版本号(verCode)定位唯一 OTA 记录,返回版本名称 verName",
|
"description": "根据设备型号和版本号(verCode)定位唯一 OTA 记录,返回版本名称 verName",
|
||||||
|
|||||||
@@ -245,6 +245,47 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/audio/getNavConfig": {
|
||||||
|
"get": {
|
||||||
|
"description": "根据设备 MAC 查询需要隐藏的导航入口(nav key: community|hpEq|home|effects|myAi)。未命中返回空数组,前端应默认全部显示。",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"NavConfig"
|
||||||
|
],
|
||||||
|
"summary": "获取导航栏隐藏配置",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "设备 MAC 地址",
|
||||||
|
"name": "mac",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功返回 hidden 列表",
|
||||||
|
"schema": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/audio/getVerName": {
|
"/audio/getVerName": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "根据设备型号和版本号(verCode)定位唯一 OTA 记录,返回版本名称 verName",
|
"description": "根据设备型号和版本号(verCode)定位唯一 OTA 记录,返回版本名称 verName",
|
||||||
|
|||||||
@@ -162,6 +162,33 @@ paths:
|
|||||||
summary: 获取耳机原始频响 CSV 数据
|
summary: 获取耳机原始频响 CSV 数据
|
||||||
tags:
|
tags:
|
||||||
- Curve
|
- Curve
|
||||||
|
/audio/getNavConfig:
|
||||||
|
get:
|
||||||
|
description: '根据设备 MAC 查询需要隐藏的导航入口(nav key: community|hpEq|home|effects|myAi)。未命中返回空数组,前端应默认全部显示。'
|
||||||
|
parameters:
|
||||||
|
- description: 设备 MAC 地址
|
||||||
|
in: query
|
||||||
|
name: mac
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 成功返回 hidden 列表
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
summary: 获取导航栏隐藏配置
|
||||||
|
tags:
|
||||||
|
- NavConfig
|
||||||
/audio/getVerName:
|
/audio/getVerName:
|
||||||
get:
|
get:
|
||||||
description: 根据设备型号和版本号(verCode)定位唯一 OTA 记录,返回版本名称 verName
|
description: 根据设备型号和版本号(verCode)定位唯一 OTA 记录,返回版本名称 verName
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"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 NavConfigHandler struct {
|
||||||
|
repo *repository.NavConfigRepository
|
||||||
|
log *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNavConfigHandler(repo *repository.NavConfigRepository, log *zap.Logger) *NavConfigHandler {
|
||||||
|
return &NavConfigHandler{repo: repo, log: log}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNavConfig 获取导航栏隐藏配置
|
||||||
|
//
|
||||||
|
// @Summary 获取导航栏隐藏配置
|
||||||
|
// @Description 根据设备 MAC 查询需要隐藏的导航入口(nav key: community|hpEq|home|effects|myAi)。未命中返回空数组,前端应默认全部显示。
|
||||||
|
// @Tags NavConfig
|
||||||
|
// @Produce json
|
||||||
|
// @Param mac query string true "设备 MAC 地址"
|
||||||
|
// @Success 200 {object} object "成功返回 hidden 列表"
|
||||||
|
// @Failure 400 {object} object
|
||||||
|
// @Failure 500 {object} object
|
||||||
|
// @Router /audio/getNavConfig [get]
|
||||||
|
func (h *NavConfigHandler) GetNavConfig(c *gin.Context) {
|
||||||
|
mac := strings.TrimSpace(c.Query("mac"))
|
||||||
|
if mac == "" {
|
||||||
|
response.BadRequest(c, "mac参数不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := c.Request.Context()
|
||||||
|
|
||||||
|
cfg, err := h.repo.FindByMac(ctx, mac)
|
||||||
|
if err != nil {
|
||||||
|
h.log.Error("query nav config failed", zap.String("mac", mac), zap.Error(err))
|
||||||
|
response.InternalError(c, "查询导航配置失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hidden := make([]string, 0)
|
||||||
|
if cfg != nil {
|
||||||
|
for _, key := range strings.Split(cfg.HiddenKeys, ",") {
|
||||||
|
if k := strings.TrimSpace(key); k != "" {
|
||||||
|
hidden = append(hidden, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(hidden) > 0 {
|
||||||
|
h.log.Info("mac命中导航隐藏配置",
|
||||||
|
zap.String("mac", repository.NormalizeMac(mac)),
|
||||||
|
zap.Strings("hidden", hidden),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功",
|
||||||
|
"data": gin.H{"hidden": hidden},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// NavHiddenConfig 描述某个 MAC 命中后要隐藏的导航入口。
|
||||||
|
// HiddenKeys 为逗号分隔的 nav key(community|hpEq|home|effects|myAi)。
|
||||||
|
type NavHiddenConfig struct {
|
||||||
|
ID int
|
||||||
|
Mac string
|
||||||
|
HiddenKeys string
|
||||||
|
Model *string
|
||||||
|
CreateAt time.Time
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/luxsin/app-api/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NavConfigRepository struct {
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNavConfigRepository(db *sql.DB) *NavConfigRepository {
|
||||||
|
return &NavConfigRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizeMac 将各种格式的 MAC 统一为大写冒号分隔格式(如 40:D9:5A:D8:06:9C)。
|
||||||
|
// 兼容设备可能上报的小写、无分隔符(40d95ad8069c)、或短横线分隔(40-d9-5a-...)等形式。
|
||||||
|
// 无法识别为 12 位十六进制时,返回去除首尾空白并大写后的原值,保证匹配行为可预期。
|
||||||
|
func NormalizeMac(raw string) string {
|
||||||
|
trimmed := strings.TrimSpace(raw)
|
||||||
|
if trimmed == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
hexOnly := strings.ToUpper(strings.NewReplacer(":", "", "-", "", ".", "", " ", "").Replace(trimmed))
|
||||||
|
if len(hexOnly) != 12 {
|
||||||
|
return strings.ToUpper(trimmed)
|
||||||
|
}
|
||||||
|
for _, c := range hexOnly {
|
||||||
|
if !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
|
||||||
|
return strings.ToUpper(trimmed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parts := make([]string, 0, 6)
|
||||||
|
for i := 0; i < 12; i += 2 {
|
||||||
|
parts = append(parts, hexOnly[i:i+2])
|
||||||
|
}
|
||||||
|
return strings.Join(parts, ":")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindByMac 按归一化后的 MAC 查询隐藏配置,未命中返回 (nil, nil)。
|
||||||
|
func (r *NavConfigRepository) FindByMac(ctx context.Context, mac string) (*model.NavHiddenConfig, error) {
|
||||||
|
const query = `SELECT id, mac, hidden_keys, model, create_at FROM nav_hidden_config WHERE mac = ? LIMIT 1`
|
||||||
|
|
||||||
|
normalized := NormalizeMac(mac)
|
||||||
|
if normalized == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfg model.NavHiddenConfig
|
||||||
|
var mdl sql.NullString
|
||||||
|
err := r.db.QueryRowContext(ctx, query, normalized).Scan(&cfg.ID, &cfg.Mac, &cfg.HiddenKeys, &mdl, &cfg.CreateAt)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("query nav_hidden_config: %w", err)
|
||||||
|
}
|
||||||
|
if mdl.Valid {
|
||||||
|
cfg.Model = &mdl.String
|
||||||
|
}
|
||||||
|
return &cfg, nil
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@ func New(log *zap.Logger, db *sql.DB, searchClient *search.Client, rdb *redis.Cl
|
|||||||
modelRepo := repository.NewModelRepository(db, modelCache)
|
modelRepo := repository.NewModelRepository(db, modelCache)
|
||||||
otaRepo := repository.NewOTARepository(db)
|
otaRepo := repository.NewOTARepository(db)
|
||||||
curveRepo := repository.NewCurveRepository(db)
|
curveRepo := repository.NewCurveRepository(db)
|
||||||
|
navConfigRepo := repository.NewNavConfigRepository(db)
|
||||||
|
|
||||||
shareCodeCache := cache.NewShareCodeCache(rdb, shareCodeTTL)
|
shareCodeCache := cache.NewShareCodeCache(rdb, shareCodeTTL)
|
||||||
|
|
||||||
@@ -50,6 +51,7 @@ func New(log *zap.Logger, db *sql.DB, searchClient *search.Client, rdb *redis.Cl
|
|||||||
curve := handler.NewCurveHandler(curveRepo, curveCache, eqCfg, s3, log)
|
curve := handler.NewCurveHandler(curveRepo, curveCache, eqCfg, s3, log)
|
||||||
modelCSV := handler.NewModelCSVHandler(s3, log)
|
modelCSV := handler.NewModelCSVHandler(s3, log)
|
||||||
shareCode := handler.NewShareCodeHandler(shareCodeCache, log, shareCodeMaxPerMac)
|
shareCode := handler.NewShareCodeHandler(shareCodeCache, log, shareCodeMaxPerMac)
|
||||||
|
navConfig := handler.NewNavConfigHandler(navConfigRepo, log)
|
||||||
|
|
||||||
v1 := r.Group("/api/v1")
|
v1 := r.Group("/api/v1")
|
||||||
{
|
{
|
||||||
@@ -78,6 +80,7 @@ func New(log *zap.Logger, db *sql.DB, searchClient *search.Client, rdb *redis.Cl
|
|||||||
audio.GET("/shareQuery", shareCode.QueryShareCode)
|
audio.GET("/shareQuery", shareCode.QueryShareCode)
|
||||||
audio.GET("/shareAccept", shareCode.ImportShareCode)
|
audio.GET("/shareAccept", shareCode.ImportShareCode)
|
||||||
audio.GET("/shareDelete", shareCode.DeleteShareCode)
|
audio.GET("/shareDelete", shareCode.DeleteShareCode)
|
||||||
|
audio.GET("/getNavConfig", navConfig.GetNavConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
Navicat Premium Dump SQL
|
||||||
|
|
||||||
|
Source Server : localhost
|
||||||
|
Source Server Type : MySQL
|
||||||
|
Source Server Version : 90600 (9.6.0)
|
||||||
|
Source Host : localhost:3306
|
||||||
|
Source Schema : audio
|
||||||
|
|
||||||
|
Target Server Type : MySQL
|
||||||
|
Target Server Version : 90600 (9.6.0)
|
||||||
|
File Encoding : 65001
|
||||||
|
|
||||||
|
Date: 09/10/2026 10:00:00
|
||||||
|
*/
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for nav_hidden_config
|
||||||
|
-- 一行 = 一个 MAC 命中后要隐藏的固定导航入口,hidden_keys 为逗号分隔的 nav key
|
||||||
|
-- nav key 取值: community | hpEq | home | effects | myAi
|
||||||
|
-- mac 统一存储为大写冒号格式,如 40:D9:5A:D8:06:9C
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `nav_hidden_config`;
|
||||||
|
CREATE TABLE `nav_hidden_config` (
|
||||||
|
`id` int NOT NULL AUTO_INCREMENT,
|
||||||
|
`mac` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
|
||||||
|
`hidden_keys` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
|
||||||
|
`model` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
|
||||||
|
`create_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
INDEX `idx_mac`(`mac` ASC) USING BTREE
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
Reference in New Issue
Block a user