diff --git a/docs/docs.go b/docs/docs.go index e78a526..df31eed 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -582,6 +582,57 @@ const docTemplate = `{ } } }, + "/audio/shareDelete": { + "get": { + "description": "根据 MAC 与分享码删除 Redis 中的分享码数据(仅创建者可删除)", + "produces": [ + "application/json" + ], + "tags": [ + "ShareCode" + ], + "summary": "删除 EQ 分享码", + "parameters": [ + { + "type": "string", + "description": "设备 MAC 地址", + "name": "mac", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "5 位分享码", + "name": "shareCode", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "删除成功", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "参数校验失败", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "500": { + "description": "系统错误", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, "/audio/shareList": { "get": { "description": "根据设备 MAC 地址查询该设备尚未过期的所有分享码(仅查询 Redis,依赖 TTL 自动过期)", diff --git a/docs/swagger.json b/docs/swagger.json index 78045d3..2908a7b 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -575,6 +575,57 @@ } } }, + "/audio/shareDelete": { + "get": { + "description": "根据 MAC 与分享码删除 Redis 中的分享码数据(仅创建者可删除)", + "produces": [ + "application/json" + ], + "tags": [ + "ShareCode" + ], + "summary": "删除 EQ 分享码", + "parameters": [ + { + "type": "string", + "description": "设备 MAC 地址", + "name": "mac", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "5 位分享码", + "name": "shareCode", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "删除成功", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "参数校验失败", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "500": { + "description": "系统错误", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, "/audio/shareList": { "get": { "description": "根据设备 MAC 地址查询该设备尚未过期的所有分享码(仅查询 Redis,依赖 TTL 自动过期)", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 4f2176c..6373f39 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -384,6 +384,41 @@ paths: summary: 创建 EQ 分享码 tags: - ShareCode + /audio/shareDelete: + get: + description: 根据 MAC 与分享码删除 Redis 中的分享码数据(仅创建者可删除) + parameters: + - description: 设备 MAC 地址 + in: query + name: mac + required: true + type: string + - description: 5 位分享码 + in: query + name: shareCode + required: true + type: string + produces: + - application/json + responses: + "200": + description: 删除成功 + schema: + additionalProperties: true + type: object + "400": + description: 参数校验失败 + schema: + additionalProperties: true + type: object + "500": + description: 系统错误 + schema: + additionalProperties: true + type: object + summary: 删除 EQ 分享码 + tags: + - ShareCode /audio/shareList: get: description: 根据设备 MAC 地址查询该设备尚未过期的所有分享码(仅查询 Redis,依赖 TTL 自动过期) diff --git a/internal/cache/share_code_cache.go b/internal/cache/share_code_cache.go index 0a3f662..6a17df0 100644 --- a/internal/cache/share_code_cache.go +++ b/internal/cache/share_code_cache.go @@ -28,6 +28,7 @@ // 创建: Lua 原子写入 hash + SADD pending + ZADD mac 索引 // 查询: ZREMRANGEBYSCORE 清过期索引 → ZRANGE 取剩余 → HGETALL 兆底校验 // 导入: HGETALL hash → HSET import:pending (幂等) +// 删除: 校验 mac+code → DEL hash + SREM pending + ZREM mac 索引 // 持久化: 每 5 分钟任务 SMEMBERS/HGETALL pending → INSERT DB → 移除 pending 条目 package cache @@ -84,6 +85,27 @@ redis.call('EXPIRE', KEYS[3], tonumber(ARGV[9])) return 1 `) +var shareDeleteScript = redis.NewScript(` +if redis.call('EXISTS', KEYS[1]) == 0 then + return 0 +end +if redis.call('HGET', KEYS[1], 'mac_addr') ~= ARGV[1] then + return -1 +end +redis.call('DEL', KEYS[1]) +redis.call('SREM', KEYS[2], ARGV[2]) +redis.call('ZREM', KEYS[3], ARGV[2]) +return 1 +`) + +type ShareDeleteResult int + +const ( + ShareDeleteNotFound ShareDeleteResult = 0 + ShareDeleteForbidden ShareDeleteResult = -1 + ShareDeleteOK ShareDeleteResult = 1 +) + type ShareImportPendingLog struct { MacAddr string `json:"mac_addr"` ShareCode string `json:"share_code"` @@ -296,6 +318,18 @@ func (c *ShareCodeCache) ListByMac(ctx context.Context, macAddr string) ([]*Shar return result, nil } +func (c *ShareCodeCache) Delete(ctx context.Context, macAddr, shareCode string) (ShareDeleteResult, error) { + key := shareCodeKey(shareCode) + macIdxKey := shareMacIndexKey(macAddr) + ok, err := shareDeleteScript.Run(ctx, c.rdb, []string{key, sharePendingSet, macIdxKey}, + macAddr, shareCode, + ).Int() + if err != nil { + return ShareDeleteNotFound, fmt.Errorf("delete share code in redis: %w", err) + } + return ShareDeleteResult(ok), nil +} + func shareMacIndexKey(macAddr string) string { return shareMacIndexPrefix + macAddr } diff --git a/internal/handler/share_code.go b/internal/handler/share_code.go index 0eed3b4..c9f19cb 100644 --- a/internal/handler/share_code.go +++ b/internal/handler/share_code.go @@ -313,3 +313,70 @@ func (h *ShareCodeHandler) lookupShareCode(c *gin.Context, shareCode string) (*c } return data, true } + +// DeleteShareCode 删除分享码 +// +// @Summary 删除 EQ 分享码 +// @Description 根据 MAC 与分享码删除 Redis 中的分享码数据(仅创建者可删除) +// @Tags ShareCode +// @Produce json +// @Param mac query string true "设备 MAC 地址" +// @Param shareCode query string true "5 位分享码" +// @Success 200 {object} map[string]any "删除成功" +// @Failure 400 {object} map[string]any "参数校验失败" +// @Failure 500 {object} map[string]any "系统错误" +// @Router /audio/shareDelete [get] +// +// GET /audio/shareDelete?mac=xx&shareCode=ABC12 +func (h *ShareCodeHandler) DeleteShareCode(c *gin.Context) { + mac := strings.TrimSpace(c.Query("mac")) + shareCode := strings.TrimSpace(c.Query("shareCode")) + + if mac == "" || shareCode == "" { + c.JSON(http.StatusOK, gin.H{ + "code": 400, + "msg": "参数校验失败", + }) + return + } + if len(shareCode) != 5 { + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "msg": "分享码无效或已过期", + }) + return + } + + ctx := c.Request.Context() + result, err := h.cache.Delete(ctx, mac, shareCode) + if err != nil { + h.log.Error("delete share code failed", + zap.String("mac", mac), + zap.String("share_code", shareCode), + zap.Error(err), + ) + c.JSON(http.StatusOK, gin.H{ + "code": 500, + "msg": "系统错误", + }) + return + } + + switch result { + case cache.ShareDeleteOK: + c.JSON(http.StatusOK, gin.H{ + "code": 200, + "msg": "操作成功", + }) + case cache.ShareDeleteForbidden: + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "msg": "无权删除该分享码", + }) + default: + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "msg": "分享码无效或已过期", + }) + } +} diff --git a/internal/router/router.go b/internal/router/router.go index a8e06c1..449ea2c 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -74,6 +74,7 @@ func New(log *zap.Logger, db *sql.DB, searchClient *search.Client, rdb *redis.Cl audio.GET("/shareList", shareCode.ListShareCodesByMac) audio.GET("/shareQuery", shareCode.QueryShareCode) audio.GET("/shareAccept", shareCode.ImportShareCode) + audio.GET("/shareDelete", shareCode.DeleteShareCode) } return r