From 54128bedac8d4e293e2149c9c2f4ad1935b4f5c5 Mon Sep 17 00:00:00 2001 From: douxu Date: Mon, 20 Oct 2025 15:06:23 +0800 Subject: [PATCH] fix bug of measurement recommend api --- docs/docs.go | 135 +++++++++++++++++++++++-------- docs/swagger.json | 130 ++++++++++++++++++++++------- docs/swagger.yaml | 95 +++++++++++++++++----- handler/measurement_recommend.go | 19 ++--- main.go | 16 ++++ model/redis_recommend.go | 70 ++++++++-------- network/measurement_request.go | 6 +- network/response.go | 15 +++- 8 files changed, 347 insertions(+), 139 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 373b381..73df182 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -9,12 +9,54 @@ const docTemplate = `{ "info": { "description": "{{escape .Description}}", "title": "{{.Title}}", - "contact": {}, + "contact": { + "name": "douxu", + "url": "http://www.swagger.io/support", + "email": "douxu@clea.com.cn" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, "version": "{{.Version}}" }, "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/api/v1/recommend/measurement": { + "post": { + "description": "根据用户输入的字符串,从 Redis 中查询可能的测量点或结构路径,并提供推荐列表。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Recommend" + ], + "summary": "测量点推荐(搜索框自动补全)", + "parameters": [ + { + "description": "查询输入参数,例如 'trans' 或 'grid_key.'", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/network.MeasurementRecommendRequest" + } + } + ], + "responses": { + "200": { + "description": "请求或查询失败。**注意**:HTTP 状态码始终为 200,但内部 Code 为 400 (参数错误) 或 500 (内部错误)。", + "schema": { + "$ref": "#/definitions/network.FailureResponse" + } + } + } + } + }, "/model/diagram_load/{page_id}": { "get": { "description": "load circuit diagram info by page id", @@ -55,35 +97,16 @@ const docTemplate = `{ } }, "definitions": { - "network.FailResponseHeader": { - "type": "object", - "properties": { - "err_msg": { - "type": "string" - }, - "status": { - "type": "integer", - "example": 400 - } - } - }, "network.FailureResponse": { "type": "object", "properties": { - "header": { - "$ref": "#/definitions/network.FailResponseHeader" + "code": { + "type": "integer", + "example": 500 }, - "payload": { - "type": "object", - "additionalProperties": true - } - } - }, - "network.SuccessResponse": { - "type": "object", - "properties": { - "header": { - "$ref": "#/definitions/network.SuccessResponseHeader" + "msg": { + "type": "string", + "example": "failed to get recommend data from redis" }, "payload": { "type": "object", @@ -96,15 +119,57 @@ const docTemplate = `{ } } }, - "network.SuccessResponseHeader": { + "network.MeasurementRecommendPayload": { "type": "object", "properties": { - "err_msg": { - "type": "string" + "input": { + "type": "string", + "example": "transformfeeder1_220." }, - "status": { + "offset": { + "type": "integer", + "example": 20 + }, + "recommended_list": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"I_A_rms\"", + " \"I_B_rms\"]" + ] + } + } + }, + "network.MeasurementRecommendRequest": { + "type": "object", + "properties": { + "input": { + "type": "string", + "example": "trans" + } + } + }, + "network.SuccessResponse": { + "type": "object", + "properties": { + "code": { "type": "integer", "example": 200 + }, + "msg": { + "type": "string", + "example": "success" + }, + "payload": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "key": "value" + } } } } @@ -113,12 +178,12 @@ const docTemplate = `{ // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = &swag.Spec{ - Version: "", - Host: "", - BasePath: "", + Version: "1.0", + Host: "localhost:8080", + BasePath: "/api/v1", Schemes: []string{}, - Title: "", - Description: "", + Title: "ModelRT 实时模型服务 API 文档", + Description: "实时数据计算和模型运行服务的 API 服务", InfoInstanceName: "swagger", SwaggerTemplate: docTemplate, LeftDelim: "{{", diff --git a/docs/swagger.json b/docs/swagger.json index f29bdd4..8ca1c0f 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1,9 +1,56 @@ { "swagger": "2.0", "info": { - "contact": {} + "description": "实时数据计算和模型运行服务的 API 服务", + "title": "ModelRT 实时模型服务 API 文档", + "contact": { + "name": "douxu", + "url": "http://www.swagger.io/support", + "email": "douxu@clea.com.cn" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0" }, + "host": "localhost:8080", + "basePath": "/api/v1", "paths": { + "/api/v1/recommend/measurement": { + "post": { + "description": "根据用户输入的字符串,从 Redis 中查询可能的测量点或结构路径,并提供推荐列表。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Recommend" + ], + "summary": "测量点推荐(搜索框自动补全)", + "parameters": [ + { + "description": "查询输入参数,例如 'trans' 或 'grid_key.'", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/network.MeasurementRecommendRequest" + } + } + ], + "responses": { + "200": { + "description": "请求或查询失败。**注意**:HTTP 状态码始终为 200,但内部 Code 为 400 (参数错误) 或 500 (内部错误)。", + "schema": { + "$ref": "#/definitions/network.FailureResponse" + } + } + } + } + }, "/model/diagram_load/{page_id}": { "get": { "description": "load circuit diagram info by page id", @@ -44,35 +91,16 @@ } }, "definitions": { - "network.FailResponseHeader": { - "type": "object", - "properties": { - "err_msg": { - "type": "string" - }, - "status": { - "type": "integer", - "example": 400 - } - } - }, "network.FailureResponse": { "type": "object", "properties": { - "header": { - "$ref": "#/definitions/network.FailResponseHeader" + "code": { + "type": "integer", + "example": 500 }, - "payload": { - "type": "object", - "additionalProperties": true - } - } - }, - "network.SuccessResponse": { - "type": "object", - "properties": { - "header": { - "$ref": "#/definitions/network.SuccessResponseHeader" + "msg": { + "type": "string", + "example": "failed to get recommend data from redis" }, "payload": { "type": "object", @@ -85,15 +113,57 @@ } } }, - "network.SuccessResponseHeader": { + "network.MeasurementRecommendPayload": { "type": "object", "properties": { - "err_msg": { - "type": "string" + "input": { + "type": "string", + "example": "transformfeeder1_220." }, - "status": { + "offset": { + "type": "integer", + "example": 20 + }, + "recommended_list": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"I_A_rms\"", + " \"I_B_rms\"]" + ] + } + } + }, + "network.MeasurementRecommendRequest": { + "type": "object", + "properties": { + "input": { + "type": "string", + "example": "trans" + } + } + }, + "network.SuccessResponse": { + "type": "object", + "properties": { + "code": { "type": "integer", "example": 200 + }, + "msg": { + "type": "string", + "example": "success" + }, + "payload": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "key": "value" + } } } } diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 815dd16..25cbe3c 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,24 +1,13 @@ +basePath: /api/v1 definitions: - network.FailResponseHeader: - properties: - err_msg: - type: string - status: - example: 400 - type: integer - type: object network.FailureResponse: properties: - header: - $ref: '#/definitions/network.FailResponseHeader' - payload: - additionalProperties: true - type: object - type: object - network.SuccessResponse: - properties: - header: - $ref: '#/definitions/network.SuccessResponseHeader' + code: + example: 500 + type: integer + msg: + example: failed to get recommend data from redis + type: string payload: additionalProperties: type: string @@ -26,17 +15,79 @@ definitions: key: value type: object type: object - network.SuccessResponseHeader: + network.MeasurementRecommendPayload: properties: - err_msg: + input: + example: transformfeeder1_220. type: string - status: + offset: + example: 20 + type: integer + recommended_list: + example: + - '["I_A_rms"' + - ' "I_B_rms"]' + items: + type: string + type: array + type: object + network.MeasurementRecommendRequest: + properties: + input: + example: trans + type: string + type: object + network.SuccessResponse: + properties: + code: example: 200 type: integer + msg: + example: success + type: string + payload: + additionalProperties: + type: string + example: + key: value + type: object type: object +host: localhost:8080 info: - contact: {} + contact: + email: douxu@clea.com.cn + name: douxu + url: http://www.swagger.io/support + description: 实时数据计算和模型运行服务的 API 服务 + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + title: ModelRT 实时模型服务 API 文档 + version: "1.0" paths: + /api/v1/recommend/measurement: + post: + consumes: + - application/json + description: 根据用户输入的字符串,从 Redis 中查询可能的测量点或结构路径,并提供推荐列表。 + parameters: + - description: 查询输入参数,例如 'trans' 或 'grid_key.' + in: body + name: request + required: true + schema: + $ref: '#/definitions/network.MeasurementRecommendRequest' + produces: + - application/json + responses: + "200": + description: 请求或查询失败。**注意**:HTTP 状态码始终为 200,但内部 Code 为 400 (参数错误) 或 500 + (内部错误)。 + schema: + $ref: '#/definitions/network.FailureResponse' + summary: 测量点推荐(搜索框自动补全) + tags: + - Recommend /model/diagram_load/{page_id}: get: consumes: diff --git a/handler/measurement_recommend.go b/handler/measurement_recommend.go index 7c9bb31..c9cf038 100644 --- a/handler/measurement_recommend.go +++ b/handler/measurement_recommend.go @@ -2,7 +2,6 @@ package handler import ( - "fmt" "net/http" "modelRT/logger" @@ -13,6 +12,16 @@ import ( ) // MeasurementRecommendHandler define measurement recommend API +// MeasurementRecommendHandler define measurement recommend API +// @Summary 测量点推荐(搜索框自动补全) +// @Description 根据用户输入的字符串,从 Redis 中查询可能的测量点或结构路径,并提供推荐列表。 +// @Tags Recommend +// @Accept json +// @Produce json +// @Param request body network.MeasurementRecommendRequest true "查询输入参数,例如 'trans' 或 'grid_key.'" +// @Success 200 {object} network.SuccessResponse{payload=network.MeasurementRecommendPayload} "成功返回推荐列表" +// @Failure 200 {object} network.FailureResponse "请求或查询失败。**注意**:HTTP 状态码始终为 200,但内部 Code 为 400 (参数错误) 或 500 (内部错误)。" +// @Router /api/v1/recommend/measurement [post] func MeasurementRecommendHandler(c *gin.Context) { var request network.MeasurementRecommendRequest @@ -26,8 +35,6 @@ func MeasurementRecommendHandler(c *gin.Context) { } recommends, isFuzzy, err := model.RedisSearchRecommend(c, request.Input) - // TODO delete debug info - fmt.Printf("recommends in handler:%+v\n", recommends) if err != nil { logger.Error(c, "failed to get recommend data from redis", "input", request.Input, "error", err) c.JSON(http.StatusOK, network.FailureResponse{ @@ -40,8 +47,6 @@ func MeasurementRecommendHandler(c *gin.Context) { return } - fmt.Printf("isFuzzy:%v\n", isFuzzy) - var finalOffset int if isFuzzy { var maxOffset int @@ -63,15 +68,11 @@ func MeasurementRecommendHandler(c *gin.Context) { finalOffset = minOffset } - fmt.Printf("finalOffset:%v\n", finalOffset) - resultRecommends := make([]string, 0, len(recommends)) seen := make(map[string]struct{}) for _, recommend := range recommends { recommendTerm := recommend[finalOffset:] - fmt.Printf("resultRecommend:%s\n", recommendTerm) - fmt.Printf("len of resultRecommend:%d\n", len(recommendTerm)) if len(recommendTerm) != 0 { if _, exists := seen[recommendTerm]; !exists { seen[recommendTerm] = struct{}{} diff --git a/main.go b/main.go index df55d40..4ac3ac3 100644 --- a/main.go +++ b/main.go @@ -53,6 +53,22 @@ var ( ) // TODO 使用 wire 依赖注入管理 DVIE 面板注册的 panel +// @title ModelRT 实时模型服务 API 文档 +// @version 1.0 +// @description 实时数据计算和模型运行服务的 API 服务 +// TODO termsOfService服务条款待后续优化 +// // @termsOfService http://swagger.io/terms/ +// +// @contact.name douxu +// TODO 修改支持的文档地址 +// @contact.url http://www.swagger.io/support +// @contact.email douxu@clea.com.cn +// +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html +// +// @host localhost:8080 +// @BasePath /api/v1 func main() { flag.Parse() ctx := context.TODO() diff --git a/model/redis_recommend.go b/model/redis_recommend.go index 016fd99..b01750e 100644 --- a/model/redis_recommend.go +++ b/model/redis_recommend.go @@ -31,10 +31,11 @@ func RedisSearchRecommend(ctx context.Context, input string) ([]string, bool, er return getAllGridKeys(ctx, constants.RedisAllGridSetKey) } - inputs := strings.Split(input, ".") - inputLen := len(inputs) - fmt.Printf("inputLen:%d\n", inputLen) - switch inputLen { + inputSlice := strings.Split(input, ".") + inputSliceLen := len(inputSlice) + originInputLen := len(inputSlice) + + switch inputSliceLen { case 1: // TODO 优化成NewSet的形式 gridExist, err := rdb.SIsMember(ctx, constants.RedisAllGridSetKey, input).Result() @@ -43,11 +44,8 @@ func RedisSearchRecommend(ctx context.Context, input string) ([]string, bool, er return []string{}, false, err } - // TODO delete debug info - fmt.Println("gridExist", gridExist) - searchInput := input - inputLen := len(searchInput) + inputLen := inputSliceLen for inputLen != 0 && !gridExist { results, err := ac.SuggestOpts(searchInput, redisearch.SuggestOptions{ Num: math.MaxInt16, @@ -55,9 +53,6 @@ func RedisSearchRecommend(ctx context.Context, input string) ([]string, bool, er WithScores: false, WithPayloads: false, }) - // TODO delete debug info - fmt.Printf("results:%+v\n", results) - fmt.Println("err", err) if err != nil { logger.Error(ctx, "query info by fuzzy failed", "query_key", input, "error", err) return []string{}, false, err @@ -65,50 +60,48 @@ func RedisSearchRecommend(ctx context.Context, input string) ([]string, bool, er if len(results) == 0 { // TODO 构建 for 循环返回所有可能的补全 - searchInput = input[:inputLen-1] + searchInput = searchInput[:len(searchInput)-1] inputLen = len(searchInput) - fmt.Printf("next search input:%s\n", searchInput) - fmt.Printf("next search input len:%d\n", inputLen) continue } - var grids []string + var recommends []string for _, result := range results { - grids = append(grids, result.Term) + termSlice := strings.Split(result.Term, ".") + if len(termSlice) <= originInputLen { + recommends = append(recommends, result.Term) + } } // 返回模糊查询结果 - return grids, true, nil + return recommends, true, nil } // 处理 input 不为空、不含有.并且 input 是一个完整的 grid key 的情况 if strings.HasSuffix(input, ".") == false { - return []string{"."}, false, nil - } - - // 处理 input 不为空并且以.结尾的情况 - if strings.HasSuffix(input, ".") == true { - setKey := fmt.Sprintf(constants.RedisSpecGridZoneSetKey, input) - return getSpecificZoneKeys(ctx, setKey) + recommend := input + "." + return []string{recommend}, false, nil } default: - lastInput := inputs[inputLen-1] + lastInput := inputSlice[inputSliceLen-1] // 判断 queryKey 是否是空值,空值则返回上一级别下的所有key if lastInput == "" { - fmt.Println("last token is empty") - // TODO 根据所在层级拼接相关setKey - setKey := getCombinedConstantsKeyByLength(inputs[inputLen-2], inputLen) - fmt.Printf("default case set key:%s\n", setKey) + setKey := getCombinedConstantsKeyByLength(inputSlice[inputSliceLen-2], inputSliceLen) targetSet := diagram.NewRedisSet(ctx, setKey, 10, true) - results, err := targetSet.SMembers(setKey) + keys, err := targetSet.SMembers(setKey) if err != nil { logger.Error(ctx, "get all recommend key by setKey failed", "set_key", setKey, "error", err) return []string{}, false, fmt.Errorf("get all recommend key by setKey failed,%w", err) } + + var results []string + for _, key := range keys { + result := input + key + results = append(results, result) + } return results, false, nil } - setKey := getCombinedConstantsKeyByLength(inputs[inputLen-2], inputLen) - fmt.Printf("default case set key:%s\n", setKey) + setKey := getCombinedConstantsKeyByLength(inputSlice[inputSliceLen-2], inputSliceLen) targetSet := diagram.NewRedisSet(ctx, setKey, 10, true) exist, err := targetSet.SIsMember(setKey, lastInput) if err != nil { @@ -134,12 +127,9 @@ func RedisSearchRecommend(ctx context.Context, input string) ([]string, bool, er // TODO 构建 for 循环返回所有可能的补全 searchInput = input[:inputLen-1] inputLen = len(searchInput) - fmt.Printf("next search input:%s\n", searchInput) - fmt.Printf("next search input len:%d\n", inputLen) continue } - fmt.Printf("default fuzzy search results:%v\n", results) var terms []string for _, result := range results { terms = append(terms, result.Term) @@ -162,14 +152,20 @@ func getAllGridKeys(ctx context.Context, setKey string) ([]string, bool, error) return keys, false, nil } -func getSpecificZoneKeys(ctx context.Context, setKey string) ([]string, bool, error) { +func getSpecificZoneKeys(ctx context.Context, input string) ([]string, bool, error) { + setKey := fmt.Sprintf(constants.RedisSpecGridZoneSetKey, input) // TODO 从redis set 中获取指定 grid 下的 zone key zoneSets := diagram.NewRedisSet(ctx, setKey, 10, true) keys, err := zoneSets.SMembers(setKey) if err != nil { return []string{}, false, fmt.Errorf("get all root keys failed, error: %v", err) } - return keys, false, nil + var results []string + for _, key := range keys { + result := input + "." + key + results = append(results, result) + } + return results, false, nil } func getConstantsKeyByLength(inputLen int) string { diff --git a/network/measurement_request.go b/network/measurement_request.go index a6ebbeb..f69b86e 100644 --- a/network/measurement_request.go +++ b/network/measurement_request.go @@ -3,11 +3,11 @@ package network // MeasurementGetRequest defines the request payload for getting an measurement type MeasurementGetRequest struct { - MeasurementID int64 `json:"measurement_id"` - MeasurementToken string `json:"token"` + MeasurementID int64 `json:"measurement_id" example:"1001"` + MeasurementToken string `json:"token" example:"some-token"` } // MeasurementRecommendRequest defines the request payload for an measurement recommend type MeasurementRecommendRequest struct { - Input string `json:"input"` + Input string `json:"input" example:"trans"` } diff --git a/network/response.go b/network/response.go index 7792bf5..6fe73e8 100644 --- a/network/response.go +++ b/network/response.go @@ -1,14 +1,23 @@ // Package network define struct of network operation package network +// FailureResponse define struct of standard failure API response format type FailureResponse struct { - Code int `json:"code" example:"200"` - Msg string `json:"msg"` + Code int `json:"code" example:"500"` + Msg string `json:"msg" example:"failed to get recommend data from redis"` PayLoad map[string]interface{} `json:"payload" swaggertype:"object,string" example:"key:value"` } +// SuccessResponse define struct of standard successful API response format type SuccessResponse struct { Code int `json:"code" example:"200"` - Msg string `json:"msg"` + Msg string `json:"msg" example:"success"` PayLoad map[string]interface{} `json:"payload" swaggertype:"object,string" example:"key:value"` } + +// MeasurementRecommendPayload define struct of represents the data payload for the successful recommendation response. +type MeasurementRecommendPayload struct { + Input string `json:"input" example:"transformfeeder1_220."` + Offset int `json:"offset" example:"20"` + RecommendedList []string `json:"recommended_list" example:"[\"I_A_rms\", \"I_B_rms\"]"` +}