From d26260b2a71983bd2c78c41841a1b546c563de97 Mon Sep 17 00:00:00 2001 From: douxu Date: Mon, 20 Jul 2026 11:32:40 +0800 Subject: [PATCH] feat(data-object)!: move recommendation API to data-object routes - move recommend endpoint to /data-object/recommend - rename measurement recommendation handlers and DTOs to data-object - update Swagger documentation - remove unused token argument from field validation BREAKING CHANGE: - /measurement/recommend has been removed - use /data-object/recommend instead --- docs/docs.go | 136 +++++++++--------- docs/swagger.json | 136 +++++++++--------- docs/swagger.yaml | 86 +++++------ handler/data_object_attribute_query.go | 4 +- handler/data_object_attribute_query_test.go | 2 +- ..._recommend.go => data_object_recommend.go} | 22 +-- handler/measurement_recommend_test.go | 2 +- network/data_object_request.go | 7 + network/measurement_request.go | 5 - network/response.go | 14 +- router/{data-object.go => data_object.go} | 1 + router/measurement.go | 1 - 12 files changed, 209 insertions(+), 207 deletions(-) rename handler/{measurement_recommend.go => data_object_recommend.go} (84%) create mode 100644 network/data_object_request.go rename router/{data-object.go => data_object.go} (89%) diff --git a/docs/docs.go b/docs/docs.go index a3451d5..51d1bc2 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -23,6 +23,57 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/data-object/recommend": { + "get": { + "description": "根据用户输入的字符串,从 Redis 中查询可能的测量点或结构路径,并提供推荐列表。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "DataObject Recommend" + ], + "summary": "测量点推荐(搜索框自动补全)", + "parameters": [ + { + "type": "string", + "example": "\"grid1\"", + "description": "推荐关键词,例如 'grid1' 或 'grid1.'", + "name": "input", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "返回推荐列表成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/network.SuccessResponse" + }, + { + "type": "object", + "properties": { + "payload": { + "$ref": "#/definitions/network.DataObjectRecommendPayload" + } + } + } + ] + } + }, + "400": { + "description": "返回推荐列表失败", + "schema": { + "$ref": "#/definitions/network.FailureResponse" + } + } + } + } + }, "/data/realtime": { "get": { "description": "根据用户输入的组件token,从 dataRT 服务中持续获取测点实时数据", @@ -87,57 +138,6 @@ const docTemplate = `{ } } }, - "/measurement/recommend": { - "get": { - "description": "根据用户输入的字符串,从 Redis 中查询可能的测量点或结构路径,并提供推荐列表。", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Measurement Recommend" - ], - "summary": "测量点推荐(搜索框自动补全)", - "parameters": [ - { - "type": "string", - "example": "\"grid1\"", - "description": "推荐关键词,例如 'grid1' 或 'grid1.'", - "name": "input", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "返回推荐列表成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/network.SuccessResponse" - }, - { - "type": "object", - "properties": { - "payload": { - "$ref": "#/definitions/network.MeasurementRecommendPayload" - } - } - } - ] - } - }, - "400": { - "description": "返回推荐列表失败", - "schema": { - "$ref": "#/definitions/network.FailureResponse" - } - } - } - } - }, "/model/diagram_load/{page_id}": { "get": { "description": "load circuit diagram info by page id", @@ -487,23 +487,7 @@ const docTemplate = `{ } } }, - "network.FailureResponse": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "example": 3000 - }, - "msg": { - "type": "string", - "example": "process completed with partial failures" - }, - "payload": { - "type": "object" - } - } - }, - "network.MeasurementRecommendPayload": { + "network.DataObjectRecommendPayload": { "type": "object", "properties": { "input": { @@ -527,6 +511,22 @@ const docTemplate = `{ } } }, + "network.FailureResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 3000 + }, + "msg": { + "type": "string", + "example": "process completed with partial failures" + }, + "payload": { + "type": "object" + } + } + }, "network.RealTimeDataPayload": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 14b2253..16b355a 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -17,6 +17,57 @@ "host": "localhost:8080", "basePath": "/api/v1", "paths": { + "/data-object/recommend": { + "get": { + "description": "根据用户输入的字符串,从 Redis 中查询可能的测量点或结构路径,并提供推荐列表。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "DataObject Recommend" + ], + "summary": "测量点推荐(搜索框自动补全)", + "parameters": [ + { + "type": "string", + "example": "\"grid1\"", + "description": "推荐关键词,例如 'grid1' 或 'grid1.'", + "name": "input", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "返回推荐列表成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/network.SuccessResponse" + }, + { + "type": "object", + "properties": { + "payload": { + "$ref": "#/definitions/network.DataObjectRecommendPayload" + } + } + } + ] + } + }, + "400": { + "description": "返回推荐列表失败", + "schema": { + "$ref": "#/definitions/network.FailureResponse" + } + } + } + } + }, "/data/realtime": { "get": { "description": "根据用户输入的组件token,从 dataRT 服务中持续获取测点实时数据", @@ -81,57 +132,6 @@ } } }, - "/measurement/recommend": { - "get": { - "description": "根据用户输入的字符串,从 Redis 中查询可能的测量点或结构路径,并提供推荐列表。", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Measurement Recommend" - ], - "summary": "测量点推荐(搜索框自动补全)", - "parameters": [ - { - "type": "string", - "example": "\"grid1\"", - "description": "推荐关键词,例如 'grid1' 或 'grid1.'", - "name": "input", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "返回推荐列表成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/network.SuccessResponse" - }, - { - "type": "object", - "properties": { - "payload": { - "$ref": "#/definitions/network.MeasurementRecommendPayload" - } - } - } - ] - } - }, - "400": { - "description": "返回推荐列表失败", - "schema": { - "$ref": "#/definitions/network.FailureResponse" - } - } - } - } - }, "/model/diagram_load/{page_id}": { "get": { "description": "load circuit diagram info by page id", @@ -481,23 +481,7 @@ } } }, - "network.FailureResponse": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "example": 3000 - }, - "msg": { - "type": "string", - "example": "process completed with partial failures" - }, - "payload": { - "type": "object" - } - } - }, - "network.MeasurementRecommendPayload": { + "network.DataObjectRecommendPayload": { "type": "object", "properties": { "input": { @@ -521,6 +505,22 @@ } } }, + "network.FailureResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 3000 + }, + "msg": { + "type": "string", + "example": "process completed with partial failures" + }, + "payload": { + "type": "object" + } + } + }, "network.RealTimeDataPayload": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 92cbdc8..4f0d2e2 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -59,18 +59,7 @@ definitions: example: 3 type: integer type: object - network.FailureResponse: - properties: - code: - example: 3000 - type: integer - msg: - example: process completed with partial failures - type: string - payload: - type: object - type: object - network.MeasurementRecommendPayload: + network.DataObjectRecommendPayload: properties: input: example: transformfeeder1_220. @@ -87,6 +76,17 @@ definitions: type: string type: array type: object + network.FailureResponse: + properties: + code: + example: 3000 + type: integer + msg: + example: process completed with partial failures + type: string + payload: + type: object + type: object network.RealTimeDataPayload: properties: sub_pos: @@ -169,6 +169,37 @@ info: title: ModelRT 实时模型服务 API 文档 version: "1.0" paths: + /data-object/recommend: + get: + consumes: + - application/json + description: 根据用户输入的字符串,从 Redis 中查询可能的测量点或结构路径,并提供推荐列表。 + parameters: + - description: 推荐关键词,例如 'grid1' 或 'grid1.' + example: '"grid1"' + in: query + name: input + required: true + type: string + produces: + - application/json + responses: + "200": + description: 返回推荐列表成功 + schema: + allOf: + - $ref: '#/definitions/network.SuccessResponse' + - properties: + payload: + $ref: '#/definitions/network.DataObjectRecommendPayload' + type: object + "400": + description: 返回推荐列表失败 + schema: + $ref: '#/definitions/network.FailureResponse' + summary: 测量点推荐(搜索框自动补全) + tags: + - DataObject Recommend /data/realtime: get: consumes: @@ -209,37 +240,6 @@ paths: summary: 获取实时测点数据 tags: - RealTime Component - /measurement/recommend: - get: - consumes: - - application/json - description: 根据用户输入的字符串,从 Redis 中查询可能的测量点或结构路径,并提供推荐列表。 - parameters: - - description: 推荐关键词,例如 'grid1' 或 'grid1.' - example: '"grid1"' - in: query - name: input - required: true - type: string - produces: - - application/json - responses: - "200": - description: 返回推荐列表成功 - schema: - allOf: - - $ref: '#/definitions/network.SuccessResponse' - - properties: - payload: - $ref: '#/definitions/network.MeasurementRecommendPayload' - type: object - "400": - description: 返回推荐列表失败 - schema: - $ref: '#/definitions/network.FailureResponse' - summary: 测量点推荐(搜索框自动补全) - tags: - - Measurement Recommend /model/diagram_load/{page_id}: get: consumes: diff --git a/handler/data_object_attribute_query.go b/handler/data_object_attribute_query.go index bf358ab..00e4950 100644 --- a/handler/data_object_attribute_query.go +++ b/handler/data_object_attribute_query.go @@ -38,7 +38,7 @@ func DataObjectAttributeQueryHandler(c *gin.Context) { return } - if err := validateDataObjectField(token, dataObjectType, field); err != nil { + if err := validateDataObjectField(dataObjectType, field); err != nil { logger.Warn(ctx, "validate data object field failed", "token", token, "field", field, "error", err) renderRespFailure(c, constants.RespCodeInvalidParams, err.Error(), nil) return @@ -202,7 +202,7 @@ type dataObjectAttributeQueryResult struct { Value any `json:"value"` } -func validateDataObjectField(token string, dataObjectType constants.DataObjectType, field string) error { +func validateDataObjectField(dataObjectType constants.DataObjectType, field string) error { field = strings.ToLower(field) switch dataObjectType { case constants.DataObjectTypeMeasurement: diff --git a/handler/data_object_attribute_query_test.go b/handler/data_object_attribute_query_test.go index 995eb73..c8120c0 100644 --- a/handler/data_object_attribute_query_test.go +++ b/handler/data_object_attribute_query_test.go @@ -104,7 +104,7 @@ func TestValidateDataObjectField(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := validateDataObjectField(tt.token, tt.dataObjectType, tt.field) + err := validateDataObjectField(tt.dataObjectType, tt.field) if tt.wantErr == nil { require.NoError(t, err) return diff --git a/handler/measurement_recommend.go b/handler/data_object_recommend.go similarity index 84% rename from handler/measurement_recommend.go rename to handler/data_object_recommend.go index 40ce7bd..75554d8 100644 --- a/handler/measurement_recommend.go +++ b/handler/data_object_recommend.go @@ -13,14 +13,14 @@ import ( "github.com/gin-gonic/gin" ) -// MeasurementRecommendHandler define measurement recommend API +// DataObjectRecommendHandler define data-object recommend API // @Summary 测量点推荐(搜索框自动补全) // @Description 根据用户输入的字符串,从 Redis 中查询可能的测量点或结构路径,并提供推荐列表。 -// @Tags Measurement Recommend +// @Tags DataObject Recommend // @Accept json // @Produce json // @Param input query string true "推荐关键词,例如 'grid1' 或 'grid1.'" Example("grid1") -// @Success 200 {object} network.SuccessResponse{payload=network.MeasurementRecommendPayload} "返回推荐列表成功" +// @Success 200 {object} network.SuccessResponse{payload=network.DataObjectRecommendPayload} "返回推荐列表成功" // // @Example 200 { // "code": 200, @@ -43,25 +43,25 @@ import ( // "msg": "failed to get recommend data from redis", // } // -// @Router /measurement/recommend [get] -func MeasurementRecommendHandler(c *gin.Context) { +// @Router /data-object/recommend [get] +func DataObjectRecommendHandler(c *gin.Context) { ctx := c.Request.Context() - var request network.MeasurementRecommendRequest + var request network.DataObjectRecommendRequest if err := c.ShouldBindQuery(&request); err != nil { - logger.Error(ctx, "failed to bind measurement recommend request", "error", err) + logger.Error(ctx, "failed to bind data object recommend request", "error", err) renderRespFailure(c, constants.RespCodeInvalidParams, err.Error(), nil) return } - if err := validateMeasurementRecommendInput(request.Input); err != nil { - logger.Warn(ctx, "invalid measurement recommend input", "input", request.Input, "error", err) + if err := validateDataObjectRecommendInput(request.Input); err != nil { + logger.Warn(ctx, "invalid data object recommend input", "input", request.Input, "error", err) renderRespFailure(c, constants.RespCodeInvalidParams, err.Error(), map[string]any{ "input": request.Input, }) return } recommendResults := model.RedisSearchRecommend(ctx, request.Input) - payload := network.MeasurementRecommendPayload{ + payload := network.DataObjectRecommendPayload{ Input: request.Input, RecommendedList: make([]string, 0), } @@ -117,7 +117,7 @@ func orderedRecommendResults(recommendResults map[string]model.SearchResult) []m return results } -func validateMeasurementRecommendInput(input string) error { +func validateDataObjectRecommendInput(input string) error { if strings.Contains(input, "..") { return errors.New("input contains continuous dots") } diff --git a/handler/measurement_recommend_test.go b/handler/measurement_recommend_test.go index 1b29aeb..f997bb1 100644 --- a/handler/measurement_recommend_test.go +++ b/handler/measurement_recommend_test.go @@ -32,7 +32,7 @@ func TestValidateMeasurementRecommendInput(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := validateMeasurementRecommendInput(tt.input) + err := validateDataObjectRecommendInput(tt.input) if tt.valid && err != nil { t.Fatalf("expected valid input, got error %v", err) } diff --git a/network/data_object_request.go b/network/data_object_request.go new file mode 100644 index 0000000..479fd1e --- /dev/null +++ b/network/data_object_request.go @@ -0,0 +1,7 @@ +// Package network define struct of network operation +package network + +// DataObjectRecommendRequest defines the request payload for an data object recommend +type DataObjectRecommendRequest struct { + Input string `form:"input,omitempty" example:"grid1"` +} diff --git a/network/measurement_request.go b/network/measurement_request.go index c04704a..c73c15d 100644 --- a/network/measurement_request.go +++ b/network/measurement_request.go @@ -6,8 +6,3 @@ type MeasurementGetRequest struct { 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 `form:"input,omitempty" example:"grid1"` -} diff --git a/network/response.go b/network/response.go index 10e7589..811bbb2 100644 --- a/network/response.go +++ b/network/response.go @@ -22,13 +22,6 @@ type WSResponse struct { Payload any `json:"payload,omitempty" swaggertype:"object"` } -// 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:"21"` - RecommendedList []string `json:"recommended_list" example:"[\"I_A_rms\", \"I_B_rms\",\"I_C_rms\"]"` -} - // TargetResult define struct of target item in real time data subscription response payload type TargetResult struct { ID string `json:"id" example:"grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_A_rms"` @@ -41,3 +34,10 @@ type RealTimeSubPayload struct { ClientID string `json:"client_id" example:"5d72f2d9-e33a-4f1b-9c76-88a44b9a953e" description:"用于标识不同client的监控请求ID"` TargetResults []TargetResult `json:"targets"` } + +// DataObjectRecommendPayload define struct of represents the data payload for the successful recommendation response +type DataObjectRecommendPayload struct { + Input string `json:"input" example:"transformfeeder1_220."` + Offset int `json:"offset" example:"21"` + RecommendedList []string `json:"recommended_list" example:"[\"I_A_rms\", \"I_B_rms\",\"I_C_rms\"]"` +} diff --git a/router/data-object.go b/router/data_object.go similarity index 89% rename from router/data-object.go rename to router/data_object.go index f90d8ba..f83af20 100644 --- a/router/data-object.go +++ b/router/data_object.go @@ -13,4 +13,5 @@ func registerDataObjectRoutes(rg *gin.RouterGroup, middlewares ...gin.HandlerFun g.Use(middlewares...) g.GET("attribute", handler.DataObjectAttributeQueryHandler) g.PATCH("attribute", handler.DataObjectAttributeUpdateHandler) + g.GET("recommend", handler.DataObjectRecommendHandler) } diff --git a/router/measurement.go b/router/measurement.go index 3d94078..9ef3307 100644 --- a/router/measurement.go +++ b/router/measurement.go @@ -12,5 +12,4 @@ func registerMeasurementRoutes(rg *gin.RouterGroup, middlewares ...gin.HandlerFu g := rg.Group("/measurement/") g.Use(middlewares...) g.GET("load", handler.MeasurementGetHandler) - g.GET("recommend", handler.MeasurementRecommendHandler) }