From 04059685974e82baa299ca8e29215bd31f4c6963 Mon Sep 17 00:00:00 2001 From: Blizzard Date: Thu, 12 Feb 2026 14:34:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BE=BD=E7=AB=A0api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v1/plant/badge_config.go | 119 ++++++++++++ api/v1/plant/enter.go | 2 + api/v1/plant/topic.go | 2 +- docs/docs.go | 276 ++++++++++++++++++++++++++-- docs/swagger.json | 276 ++++++++++++++++++++++++++-- docs/swagger.yaml | 178 +++++++++++++++++- global/enums.go | 1 - initialize/gorm.go | 2 + initialize/router.go | 1 + model/plant/request/badge.go | 30 +++ model/plant/response/badge.go | 19 +- model/plant/sys_badge_config.go | 33 +++- model/plant/user_badge.go | 13 ++ router/plant/badge_config_router.go | 17 ++ router/plant/enter.go | 2 + router/plant/level_config_router.go | 2 +- router/plant/user_profile_router.go | 2 +- service/plant/badge_config.go | 196 ++++++++++++++++++++ service/plant/enter.go | 1 + service/plant/my_plant.go | 6 +- service/plant/topic.go | 4 +- service/plant/user_profile.go | 30 ++- 22 files changed, 1150 insertions(+), 62 deletions(-) create mode 100644 api/v1/plant/badge_config.go create mode 100644 model/plant/request/badge.go create mode 100644 model/plant/user_badge.go create mode 100644 router/plant/badge_config_router.go create mode 100644 service/plant/badge_config.go diff --git a/api/v1/plant/badge_config.go b/api/v1/plant/badge_config.go new file mode 100644 index 0000000..c929d51 --- /dev/null +++ b/api/v1/plant/badge_config.go @@ -0,0 +1,119 @@ +package plant + +import ( + "sundynix-go/global" + "sundynix-go/model/commom/response" + plantReq "sundynix-go/model/plant/request" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type BadgeConfigApi struct{} + +// GetBadgeTree 获取徽章配置树 (不分页) +// @Tags 徽章配置 +// @Summary 获取徽章配置树形结构 +// @Security BearerAuth +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /config/badge/tree [get] +func (a *BadgeConfigApi) GetBadgeTree(c *gin.Context) { + // 不需要 bind query 了,因为没有参数 + tree, err := badgeConfigService.GetBadgeTree() + if err != nil { + global.Logger.Error("获取徽章树失败", zap.Error(err)) + response.FailWithMsg("获取数据失败", c) + return + } + // 直接返回 data = tree + response.OkWithData(tree, c) +} + +// AddBadgeConfig 添加徽章配置 +// @Tags 徽章配置 +// @Summary 添加徽章配置 +// @Security BearerAuth +// @accept application/json +// @Produce application/json +// @Param data body plantReq.CreateBadge true "添加徽章配置" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"添加成功"}" +// @Router /config/badge/add [post] +func (a *BadgeConfigApi) AddBadgeConfig(c *gin.Context) { + var req plantReq.CreateBadge + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误", c) + return + } + err = badgeConfigService.AddBadgeConfig(req) + if err != nil { + global.Logger.Error("添加徽章失败", zap.Error(err)) + response.FailWithMsg("添加失败", c) + return + } + response.OkWithMsg("添加成功", c) +} + +// UpdateBadgeConfig 更新徽章配置 +// @Tags 徽章配置 +// @Summary 更新徽章配置 +// @Security BearerAuth +// @accept application/json +// @Produce application/json +// @Param data body plantReq.UpdateBadge true "更新参数" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /config/badge/update [post] +func (a *BadgeConfigApi) UpdateBadgeConfig(c *gin.Context) { + var req plantReq.UpdateBadge + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误", c) + return + } + err = badgeConfigService.UpdateBadgeConfig(req) + if err != nil { + global.Logger.Error("更新失败", zap.Error(err)) + response.FailWithMsg("更新失败", c) + return + } + response.OkWithMsg("更新成功", c) +} + +// FindBadgeConfig 根据ID获取徽章配置 +// @Tags 徽章配置 +// @Summary 根据ID获取徽章配置 +// @Security BearerAuth +// @Produce application/json +// @Param id query string true "id" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /config/badge/find [get] +func (a *BadgeConfigApi) FindBadgeConfig(c *gin.Context) { + id := c.Query("id") + badge, err := badgeConfigService.GetBadgeConfig(id) + if err != nil { + global.Logger.Error("查询失败", zap.Error(err)) + response.FailWithMsg("查询失败", c) + return + } + response.OkWithData(badge, c) +} + +// DeleteBadgeConfig 删除徽章配置 +// @Tags 徽章配置 +// @Summary 删除徽章配置 +// @Security BearerAuth +// @Produce application/json +// @Param id query string true "id" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /config/badge/delete [get] +func (a *BadgeConfigApi) DeleteBadgeConfig(c *gin.Context) { + id := c.Query("id") + err := badgeConfigService.DeleteBadgeConfig(id) + if err != nil { + global.Logger.Error("删除失败", zap.Error(err)) + response.FailWithMsg("删除失败", c) + return + } + response.OkWithMsg("删除成功", c) +} diff --git a/api/v1/plant/enter.go b/api/v1/plant/enter.go index 992ebb7..072e293 100644 --- a/api/v1/plant/enter.go +++ b/api/v1/plant/enter.go @@ -11,6 +11,7 @@ type ApiGroup struct { OcrApi LevelConfigApi UserProfileApi + BadgeConfigApi } var ( @@ -22,4 +23,5 @@ var ( ocrService = service.GroupApp.PlantServiceGroup.OcrService levelConfigService = service.GroupApp.PlantServiceGroup.LevelConfigService userProfileService = service.GroupApp.PlantServiceGroup.UserProfileService + badgeConfigService = service.GroupApp.PlantServiceGroup.BadgeConfigService ) diff --git a/api/v1/plant/topic.go b/api/v1/plant/topic.go index dcd7e5f..05de543 100644 --- a/api/v1/plant/topic.go +++ b/api/v1/plant/topic.go @@ -132,7 +132,7 @@ func (a *TopicApi) TopicDetail(c *gin.Context) { // @Tags 帖子话题 // @Summary 删除任务 // @Security BearerAuth -// @accept json +// @accept application/json // @Produce application/json // @Param data body request.IdsReq true "删除话题" // @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" diff --git a/docs/docs.go b/docs/docs.go index cbc071a..010eaca 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -594,6 +594,172 @@ const docTemplate = `{ } } }, + "/config/badge/add": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章配置" + ], + "summary": "添加徽章配置", + "parameters": [ + { + "description": "添加徽章配置", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateBadge" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/badge/delete": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章配置" + ], + "summary": "删除徽章配置", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/badge/find": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章配置" + ], + "summary": "根据ID获取徽章配置", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/badge/tree": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章配置" + ], + "summary": "获取徽章配置树形结构", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/badge/update": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章配置" + ], + "summary": "更新徽章配置", + "parameters": [ + { + "description": "更新参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateBadge" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"更新成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, "/config/level/add": { "post": { "security": [ @@ -3066,6 +3232,48 @@ const docTemplate = `{ } } }, + "request.CreateBadge": { + "type": "object", + "properties": { + "comparator": { + "type": "string" + }, + "description": { + "type": "string" + }, + "dimension": { + "description": "维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录)", + "type": "string" + }, + "groupId": { + "description": "组ID: 用于前端聚合显示 (e.g. \"fertilizerMaster\")", + "type": "string" + }, + "iconId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "rewardSunlight": { + "type": "integer" + }, + "sort": { + "type": "integer" + }, + "targetAction": { + "description": "触发动作: ACT_FERTILIZE, ACT_WATER...", + "type": "string" + }, + "threshold": { + "type": "integer" + }, + "tier": { + "description": "等级: 1=铜, 2=银, 3=金", + "type": "integer" + } + } + }, "request.CreateCarePlan": { "type": "object", "required": [ @@ -3230,13 +3438,13 @@ const docTemplate = `{ "request.CreateTopic": { "type": "object", "properties": { - "end_time": { + "endTime": { "type": "string" }, "remark": { "type": "string" }, - "start_time": { + "startTime": { "type": "string" }, "title": { @@ -3583,6 +3791,54 @@ const docTemplate = `{ } } }, + "request.UpdateBadge": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "comparator": { + "type": "string" + }, + "description": { + "type": "string" + }, + "dimension": { + "description": "维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录)", + "type": "string" + }, + "groupId": { + "description": "组Id: 用于前端聚合显示 (e.g. \"fertilizerMaster\")", + "type": "string" + }, + "iconId": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "rewardSunlight": { + "type": "integer" + }, + "sort": { + "type": "integer" + }, + "targetAction": { + "description": "触发动作: ACT_FERTILIZE, ACT_WATER...", + "type": "string" + }, + "threshold": { + "type": "integer" + }, + "tier": { + "description": "等级: 1=铜, 2=银, 3=金", + "type": "integer" + } + } + }, "request.UpdateLevelConf": { "type": "object", "required": [ @@ -3680,17 +3936,8 @@ const docTemplate = `{ "avatarId": { "type": "string" }, - "currentSunlight": { - "type": "integer" - }, - "levelId": { - "type": "string" - }, "nickname": { "type": "string" - }, - "totalSunlight": { - "type": "integer" } } }, @@ -3700,7 +3947,7 @@ const docTemplate = `{ "id" ], "properties": { - "end_time": { + "endTime": { "type": "string" }, "id": { @@ -3709,7 +3956,7 @@ const docTemplate = `{ "remark": { "type": "string" }, - "start_time": { + "startTime": { "type": "string" }, "title": { @@ -3989,6 +4236,9 @@ const docTemplate = `{ "parentId": { "type": "string" }, + "path": { + "type": "string" + }, "permission": { "type": "string" }, diff --git a/docs/swagger.json b/docs/swagger.json index d52b127..36d7968 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -586,6 +586,172 @@ } } }, + "/config/badge/add": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章配置" + ], + "summary": "添加徽章配置", + "parameters": [ + { + "description": "添加徽章配置", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateBadge" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/badge/delete": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章配置" + ], + "summary": "删除徽章配置", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/badge/find": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章配置" + ], + "summary": "根据ID获取徽章配置", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/badge/tree": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章配置" + ], + "summary": "获取徽章配置树形结构", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/badge/update": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章配置" + ], + "summary": "更新徽章配置", + "parameters": [ + { + "description": "更新参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateBadge" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"更新成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, "/config/level/add": { "post": { "security": [ @@ -3058,6 +3224,48 @@ } } }, + "request.CreateBadge": { + "type": "object", + "properties": { + "comparator": { + "type": "string" + }, + "description": { + "type": "string" + }, + "dimension": { + "description": "维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录)", + "type": "string" + }, + "groupId": { + "description": "组ID: 用于前端聚合显示 (e.g. \"fertilizerMaster\")", + "type": "string" + }, + "iconId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "rewardSunlight": { + "type": "integer" + }, + "sort": { + "type": "integer" + }, + "targetAction": { + "description": "触发动作: ACT_FERTILIZE, ACT_WATER...", + "type": "string" + }, + "threshold": { + "type": "integer" + }, + "tier": { + "description": "等级: 1=铜, 2=银, 3=金", + "type": "integer" + } + } + }, "request.CreateCarePlan": { "type": "object", "required": [ @@ -3222,13 +3430,13 @@ "request.CreateTopic": { "type": "object", "properties": { - "end_time": { + "endTime": { "type": "string" }, "remark": { "type": "string" }, - "start_time": { + "startTime": { "type": "string" }, "title": { @@ -3575,6 +3783,54 @@ } } }, + "request.UpdateBadge": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "comparator": { + "type": "string" + }, + "description": { + "type": "string" + }, + "dimension": { + "description": "维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录)", + "type": "string" + }, + "groupId": { + "description": "组Id: 用于前端聚合显示 (e.g. \"fertilizerMaster\")", + "type": "string" + }, + "iconId": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "rewardSunlight": { + "type": "integer" + }, + "sort": { + "type": "integer" + }, + "targetAction": { + "description": "触发动作: ACT_FERTILIZE, ACT_WATER...", + "type": "string" + }, + "threshold": { + "type": "integer" + }, + "tier": { + "description": "等级: 1=铜, 2=银, 3=金", + "type": "integer" + } + } + }, "request.UpdateLevelConf": { "type": "object", "required": [ @@ -3672,17 +3928,8 @@ "avatarId": { "type": "string" }, - "currentSunlight": { - "type": "integer" - }, - "levelId": { - "type": "string" - }, "nickname": { "type": "string" - }, - "totalSunlight": { - "type": "integer" } } }, @@ -3692,7 +3939,7 @@ "id" ], "properties": { - "end_time": { + "endTime": { "type": "string" }, "id": { @@ -3701,7 +3948,7 @@ "remark": { "type": "string" }, - "start_time": { + "startTime": { "type": "string" }, "title": { @@ -3981,6 +4228,9 @@ "parentId": { "type": "string" }, + "path": { + "type": "string" + }, "permission": { "type": "string" }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index cd9e8a2..0f4c487 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -27,6 +27,35 @@ definitions: required: - taskId type: object + request.CreateBadge: + properties: + comparator: + type: string + description: + type: string + dimension: + description: '维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录)' + type: string + groupId: + description: '组ID: 用于前端聚合显示 (e.g. "fertilizerMaster")' + type: string + iconId: + type: string + name: + type: string + rewardSunlight: + type: integer + sort: + type: integer + targetAction: + description: '触发动作: ACT_FERTILIZE, ACT_WATER...' + type: string + threshold: + type: integer + tier: + description: '等级: 1=铜, 2=银, 3=金' + type: integer + type: object request.CreateCarePlan: properties: icon: @@ -142,11 +171,11 @@ definitions: type: object request.CreateTopic: properties: - end_time: + endTime: type: string remark: type: string - start_time: + startTime: type: string title: type: string @@ -389,6 +418,39 @@ definitions: description: 标题 type: string type: object + request.UpdateBadge: + properties: + comparator: + type: string + description: + type: string + dimension: + description: '维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录)' + type: string + groupId: + description: '组Id: 用于前端聚合显示 (e.g. "fertilizerMaster")' + type: string + iconId: + type: string + id: + type: string + name: + type: string + rewardSunlight: + type: integer + sort: + type: integer + targetAction: + description: '触发动作: ACT_FERTILIZE, ACT_WATER...' + type: string + threshold: + type: integer + tier: + description: '等级: 1=铜, 2=银, 3=金' + type: integer + required: + - id + type: object request.UpdateLevelConf: properties: id: @@ -457,24 +519,18 @@ definitions: properties: avatarId: type: string - currentSunlight: - type: integer - levelId: - type: string nickname: type: string - totalSunlight: - type: integer type: object request.UpdateTopic: properties: - end_time: + endTime: type: string id: type: integer remark: type: string - start_time: + startTime: type: string title: type: string @@ -671,6 +727,8 @@ definitions: type: string parentId: type: string + path: + type: string permission: type: string sort: @@ -1102,6 +1160,106 @@ paths: summary: 更新client tags: - 客户端管理 + /config/badge/add: + post: + consumes: + - application/json + parameters: + - description: 添加徽章配置 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreateBadge' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"添加成功"}' + schema: + type: string + security: + - BearerAuth: [] + summary: 添加徽章配置 + tags: + - 徽章配置 + /config/badge/delete: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - BearerAuth: [] + summary: 删除徽章配置 + tags: + - 徽章配置 + /config/badge/find: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - BearerAuth: [] + summary: 根据ID获取徽章配置 + tags: + - 徽章配置 + /config/badge/tree: + get: + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - BearerAuth: [] + summary: 获取徽章配置树形结构 + tags: + - 徽章配置 + /config/badge/update: + post: + consumes: + - application/json + parameters: + - description: 更新参数 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UpdateBadge' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"更新成功"}' + schema: + type: string + security: + - BearerAuth: [] + summary: 更新徽章配置 + tags: + - 徽章配置 /config/level/add: post: consumes: diff --git a/global/enums.go b/global/enums.go index b116bb8..dcd6990 100644 --- a/global/enums.go +++ b/global/enums.go @@ -15,7 +15,6 @@ const ( ActionPrune = "ACT_PRUNE" // 修剪 ActionRepot = "ACT_REPOT" // 换盆 ActionPhoto = "ACT_PHOTO" // 拍照 - ActionLogin = "ACT_LOGIN" // 签到 ActionKillPlant = "ACT_KILL" // 把植物养死了(作为彩蛋徽章) ) diff --git a/initialize/gorm.go b/initialize/gorm.go index 02c1116..e2c2932 100644 --- a/initialize/gorm.go +++ b/initialize/gorm.go @@ -53,7 +53,9 @@ func MigrateTable() { plant.ClassifyRecord{}, //植物识别记录 plant.LevelConfig{}, //等级配置 + plant.BadgeConfig{}, //徽章配置 plant.UserProfile{}, //用户资料 + plant.UserBadge{}, //用户徽章 ) if err != nil { diff --git a/initialize/router.go b/initialize/router.go index 2dfe962..13defb6 100644 --- a/initialize/router.go +++ b/initialize/router.go @@ -56,6 +56,7 @@ func Routers() { plantGroup.InitWikiRouter(NeedAuthGroup) //百科 plantGroup.InitOcrRouter(NeedAuthGroup) // ocr识别 plantGroup.InitLevelConfigRouter(NeedAuthGroup) //等级配置 + plantGroup.InitBadgeConfigRouter(NeedAuthGroup) //徽章配置 plantGroup.InitUserProfileRouter(NeedAuthGroup) //用户资料 } diff --git a/model/plant/request/badge.go b/model/plant/request/badge.go new file mode 100644 index 0000000..2c9d56a --- /dev/null +++ b/model/plant/request/badge.go @@ -0,0 +1,30 @@ +package request + +type CreateBadge struct { + Name string `json:"name"` + Description string `json:"description"` + IconId string `json:"iconId"` + Dimension string `json:"dimension"` // 维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录) + GroupId string `json:"groupId"` // 组ID: 用于前端聚合显示 (e.g. "fertilizerMaster") + Tier int `json:"tier"` // 等级: 1=铜, 2=银, 3=金 + TargetAction string `json:"targetAction"` // 触发动作: ACT_FERTILIZE, ACT_WATER... + Threshold int64 `json:"threshold"` + Comparator string `json:"comparator"` + RewardSunlight int64 `json:"rewardSunlight"` + Sort int `json:"sort"` +} + +type UpdateBadge struct { + Id string `json:"id" binding:"required"` + Name string `json:"name"` + Description string `json:"description"` + IconId string `json:"iconId"` + Dimension string `json:"dimension"` // 维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录) + GroupId string `json:"groupId"` // 组Id: 用于前端聚合显示 (e.g. "fertilizerMaster") + Tier int `json:"tier"` // 等级: 1=铜, 2=银, 3=金 + TargetAction string `json:"targetAction"` // 触发动作: ACT_FERTILIZE, ACT_WATER... + Threshold int64 `json:"threshold"` + Comparator string `json:"comparator"` + RewardSunlight int64 `json:"rewardSunlight"` + Sort int `json:"sort"` +} diff --git a/model/plant/response/badge.go b/model/plant/response/badge.go index ed513db..599c437 100644 --- a/model/plant/response/badge.go +++ b/model/plant/response/badge.go @@ -1,6 +1,17 @@ package response -//type BadgeGroup struct { -// CategoryName string `json:"categoryName" comment:"分类名称"` -// BadgeList []plant.Badge `json:"badgeList"` -//} +import "sundynix-go/model/plant" + +// BadgeDimensionNode 1. 最外层:维度节点 (例如: "专家成就") +type BadgeDimensionNode struct { + Dimension string `json:"dimension"` // 维度 Key (e.g., "EXPERTISE") + Label string `json:"label"` // 维度显示名 (前端映射或后端返回) + Groups []BadgeGroupNode `json:"groups"` // 该维度下的徽章组 +} + +// BadgeGroupNode 2. 中间层:组节点 (例如: "炼金术士系列") +type BadgeGroupNode struct { + GroupId string `json:"groupId"` // 组 ID (e.g., "fertilizer_master") + GroupLabel string `json:"groupLabel"` // 中文显示名 (新增) + Badges []plant.BadgeConfig `json:"badges"` // 该组下的具体徽章 (铜/银/金) +} diff --git a/model/plant/sys_badge_config.go b/model/plant/sys_badge_config.go index 12ca847..0394853 100644 --- a/model/plant/sys_badge_config.go +++ b/model/plant/sys_badge_config.go @@ -1,18 +1,35 @@ package plant import ( - "sundynix-go/config" "sundynix-go/global" + "sundynix-go/model/system" ) // BadgeConfig 徽章配置 type BadgeConfig struct { global.BaseModel - GroupId string `json:"group_id"` // 徽章组 (用于系列徽章,如浇水铜/银/金) - Name string `json:"name"` // 徽章名称 - Description string `json:"description"` // 描述 - Tier config.BadgeTier `json:"tier"` // 稀有度 - TargetAction config.ActionType `json:"target_action"` // 关联的行为 - TargetCount int `json:"target_count"` // 需要完成该行为的次数 - IconURL string `json:"icon_url"` // 徽章图标地址 + Name string `gorm:"type:varchar(64);not null;comment:徽章名称" json:"name"` + Description string `gorm:"type:varchar(255);comment:徽章描述" json:"description"` + IconId string `gorm:"type:varchar(50);comment:图标资源id" json:"iconId"` + // --- 核心维度与分组 --- + // 维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录) + Dimension string `gorm:"type:varchar(32);index;not null;column:dimension;comment:维度分类" json:"dimension"` + // 组ID: 用于前端聚合显示 (e.g. "fertilizerMaster") + GroupId string `gorm:"type:varchar(64);index;not null;column:group_id;comment:系列组Id" json:"groupId"` + // 等级: 1=铜, 2=银, 3=金 + Tier int `gorm:"type:tinyint;default:1;column:tier;comment:等级权重" json:"tier"` + // --- 触发条件逻辑 --- + // 触发动作: ACT_FERTILIZE, ACT_WATER... + TargetAction string `gorm:"type:varchar(32);index;not null;column:target_action;comment:触发动作代码" json:"targetAction"` + // 目标阈值 + Threshold int64 `gorm:"not null;column:threshold;comment:所需次数或数值" json:"threshold"` + // 比较操作符: ">=", "=" + Comparator string `gorm:"type:varchar(10);default:'>=';column:comparator;comment:比较条件" json:"comparator"` + // --- 奖励 --- + RewardSunlight int64 `gorm:"not null;column:reward_sunlight;comment:奖励阳光值" json:"rewardSunlight"` + // --- 显示控制 --- + Sort int `gorm:"default:1;column:sort;comment:排序优先级" json:"sort"` + IsHidden bool `gorm:"-" json:"isHidden"` + //图标 + Icon *system.Oss `json:"icon" gorm:"foreignKey:IconId"` } diff --git a/model/plant/user_badge.go b/model/plant/user_badge.go new file mode 100644 index 0000000..fd37906 --- /dev/null +++ b/model/plant/user_badge.go @@ -0,0 +1,13 @@ +package plant + +import ( + "sundynix-go/global" + "time" +) + +type UserBadge struct { + global.BaseModel + UserId string `gorm:"type:varchar(50);index;not null;column:user_id;comment:用户id" json:"userId"` + BadgeId uint `gorm:"index:idx_user_badge,unique;not null;column:badge_id;comment:徽章配置ID" json:"badgeId"` + AcquiredAt time.Time `gorm:"autoCreateTime;column:acquired_at;comment:获得时间" json:"acquiredAt"` +} diff --git a/router/plant/badge_config_router.go b/router/plant/badge_config_router.go new file mode 100644 index 0000000..0ab5b66 --- /dev/null +++ b/router/plant/badge_config_router.go @@ -0,0 +1,17 @@ +package plant + +import "github.com/gin-gonic/gin" + +type BadgeConfigRouter struct{} + +func (c *BadgeConfigRouter) InitBadgeConfigRouter(Router *gin.RouterGroup) { + badgeConfigRouter := Router.Group("config/badge") + { + badgeConfigRouter.GET("/tree", badgeConfigApi.GetBadgeTree) + badgeConfigRouter.POST("/add", badgeConfigApi.AddBadgeConfig) + badgeConfigRouter.POST("/update", badgeConfigApi.UpdateBadgeConfig) + badgeConfigRouter.GET("/find", badgeConfigApi.FindBadgeConfig) + badgeConfigRouter.GET("/delete", badgeConfigApi.DeleteBadgeConfig) + } + +} diff --git a/router/plant/enter.go b/router/plant/enter.go index f401924..c19e5f8 100644 --- a/router/plant/enter.go +++ b/router/plant/enter.go @@ -10,6 +10,7 @@ type RouterGroup struct { WikiRouter OcrRouter LevelConfigRouter + BadgeConfigRouter UserProfileRouter } @@ -23,4 +24,5 @@ var ( ocrApi = v1.ApiGroupApp.PlantApiGroup.OcrApi levelConfigApi = v1.ApiGroupApp.PlantApiGroup.LevelConfigApi userProfileApi = v1.ApiGroupApp.PlantApiGroup.UserProfileApi + badgeConfigApi = v1.ApiGroupApp.PlantApiGroup.BadgeConfigApi ) diff --git a/router/plant/level_config_router.go b/router/plant/level_config_router.go index 3934f52..936dd4c 100644 --- a/router/plant/level_config_router.go +++ b/router/plant/level_config_router.go @@ -4,7 +4,7 @@ import "github.com/gin-gonic/gin" type LevelConfigRouter struct{} -func (c *OcrRouter) InitLevelConfigRouter(Router *gin.RouterGroup) { +func (c *LevelConfigRouter) InitLevelConfigRouter(Router *gin.RouterGroup) { levelConfigRouter := Router.Group("config/level") { levelConfigRouter.POST("/add", levelConfigApi.AddLevelConfig) diff --git a/router/plant/user_profile_router.go b/router/plant/user_profile_router.go index 4e71ee0..27dbcfe 100644 --- a/router/plant/user_profile_router.go +++ b/router/plant/user_profile_router.go @@ -4,7 +4,7 @@ import "github.com/gin-gonic/gin" type UserProfileRouter struct{} -func (c *OcrRouter) InitUserProfileRouter(Router *gin.RouterGroup) { +func (c *UserProfileRouter) InitUserProfileRouter(Router *gin.RouterGroup) { userProfileRouter := Router.Group("profile") { userProfileRouter.POST("/update", userProfileApi.UpdateProfile) diff --git a/service/plant/badge_config.go b/service/plant/badge_config.go new file mode 100644 index 0000000..9d3394f --- /dev/null +++ b/service/plant/badge_config.go @@ -0,0 +1,196 @@ +package plant + +import ( + "errors" + "sundynix-go/global" + "sundynix-go/model/plant" + plantReq "sundynix-go/model/plant/request" + plantRes "sundynix-go/model/plant/response" + + "gorm.io/gorm" +) + +type BadgeConfigService struct{} + +var BadgeConfigServiceApp = new(BadgeConfigService) + +// GetBadgeTree 获取徽章树形结构 (无分页) +// GetBadgeTree 获取徽章树形结构 (维度和组ID都转换为中文) +func (s *BadgeConfigService) GetBadgeTree() (tree []plantRes.BadgeDimensionNode, err error) { + var allBadges []plant.BadgeConfig + + // 1. 查询所有配置 + err = global.DB.Order("dimension desc, group_id asc, tier asc, sort asc").Preload("Icon").Find(&allBadges).Error + if err != nil { + return nil, err + } + + // --- 定义映射字典 --- + + // 维度中文映射 + dimLabelMap := map[string]string{ + "PERSISTENCE": "勤勉成就", + "EXPERTISE": "专家成就", + "JOURNAL": "岁月记录", + "DISCOVERY": "探索发现", + } + + // 组ID中文映射 (对应之前的 SQL 数据) + groupLabelMap := map[string]string{ + // 勤勉系 + "water_master": "雨露均沾", // 浇水 + "alive_master": "长情陪伴", // 存活天数 + + // 专家系 + "fert_master": "炼金术士", // 施肥 + "prune_master": "园艺理发师", // 修剪 + "repot_master": "乔迁之喜", // 换盆 + "doctor_master": "植物医生", // 医生 + + // 记录系 + "photo_master": "光影捕手", // 拍照 + + // 发现系 + "night_owl": "守夜人", // 深夜养护 + } + + // 定义维度的固定展示顺序 + dimOrder := []string{"PERSISTENCE", "EXPERTISE", "JOURNAL", "DISCOVERY"} + + // --- 数据处理 --- + + // 辅助 Map: map[Dimension] -> map[GroupId] -> []Badge + tempMap := make(map[string]map[string][]plant.BadgeConfig) + + for _, badge := range allBadges { + dim := badge.Dimension + group := badge.GroupId + + if tempMap[dim] == nil { + tempMap[dim] = make(map[string][]plant.BadgeConfig) + } + tempMap[dim][group] = append(tempMap[dim][group], badge) + } + + // --- 构建返回树 --- + + // 1. 遍历维度顺序 + for _, dimKey := range dimOrder { + if groupMap, exists := tempMap[dimKey]; exists { + var groupNodes []plantRes.BadgeGroupNode + + // 2. 遍历该维度下的所有组 + for groupKey, badges := range groupMap { + // 获取组中文名,如果没有则回退到英文 ID + gLabel := groupLabelMap[groupKey] + if gLabel == "" { + gLabel = groupKey // Fallback + } + + groupNodes = append(groupNodes, plantRes.BadgeGroupNode{ + GroupId: groupKey, + GroupLabel: gLabel, // 赋值中文名 + Badges: badges, + }) + } + + // 获取维度中文名 + dLabel := dimLabelMap[dimKey] + if dLabel == "" { + dLabel = dimKey + } + + tree = append(tree, plantRes.BadgeDimensionNode{ + Dimension: dimKey, + Label: dLabel, + Groups: groupNodes, + }) + + delete(tempMap, dimKey) + } + } + + // 3. 处理剩余的未知维度 (防止数据丢失) + for dimKey, groupMap := range tempMap { + var groupNodes []plantRes.BadgeGroupNode + for groupKey, badges := range groupMap { + // 未知组也尝试映射一下,不行就用 key + gLabel := groupLabelMap[groupKey] + if gLabel == "" { + gLabel = groupKey + } + + groupNodes = append(groupNodes, plantRes.BadgeGroupNode{ + GroupId: groupKey, + GroupLabel: gLabel, + Badges: badges, + }) + } + + tree = append(tree, plantRes.BadgeDimensionNode{ + Dimension: dimKey, + Label: dimKey, + Groups: groupNodes, + }) + } + + return tree, nil +} + +// AddBadgeConfig 添加徽章配置 +func (s *BadgeConfigService) AddBadgeConfig(req plantReq.CreateBadge) error { + // 判断是否已存在相同配置(可选逻辑,例如同一组同一等级只能有一个) + if !errors.Is(global.DB.Where("group_id = ? AND tier = ?", req.GroupId, req.Tier).First(&plant.BadgeConfig{}).Error, gorm.ErrRecordNotFound) { + return errors.New("该组下该等级徽章已存在") + } + badge := plant.BadgeConfig{ + Dimension: req.Dimension, + GroupId: req.GroupId, + Name: req.Name, + Description: req.Description, + IconId: req.IconId, + Tier: req.Tier, + TargetAction: req.TargetAction, + Threshold: req.Threshold, + Comparator: req.Comparator, + RewardSunlight: req.RewardSunlight, + Sort: req.Sort, + } + return global.DB.Create(&badge).Error +} + +// UpdateBadgeConfig 修改徽章配置 +func (s *BadgeConfigService) UpdateBadgeConfig(req plantReq.UpdateBadge) error { + // 使用 Map 更新可以避免 0 值问题,或者直接更新结构体 + // 这里简单起见,先查再更,或者直接 Model update + var badge plant.BadgeConfig + if err := global.DB.Where("id = ?", req.Id).First(&badge).Error; err != nil { + return errors.New("记录不存在") + } + // 手动映射需要更新的字段 (更安全) + updateMap := map[string]interface{}{ + "name": req.Name, + "description": req.Description, + "icon_id": req.IconId, + "dimension": req.Dimension, + "group_id": req.GroupId, + "tier": req.Tier, + "target_action": req.TargetAction, + "threshold": req.Threshold, + "reward_sunlight": req.RewardSunlight, + "sort": req.Sort, + } + return global.DB.Model(&badge).Updates(updateMap).Error +} + +// GetBadgeConfig 根据ID获取单条 +func (s *BadgeConfigService) GetBadgeConfig(id string) (plant.BadgeConfig, error) { + var badge plant.BadgeConfig + err := global.DB.Where("id = ?", id).Preload("Icon").First(&badge).Error + return badge, err +} + +// DeleteBadgeConfig 删除徽章配置 +func (s *BadgeConfigService) DeleteBadgeConfig(id string) error { + return global.DB.Where("id = ?", id).Delete(&plant.BadgeConfig{}).Error +} diff --git a/service/plant/enter.go b/service/plant/enter.go index d6fbbd8..e9781f3 100644 --- a/service/plant/enter.go +++ b/service/plant/enter.go @@ -8,5 +8,6 @@ type ServiceGroup struct { WikiService OcrService LevelConfigService + BadgeConfigService UserProfileService } diff --git a/service/plant/my_plant.go b/service/plant/my_plant.go index be08a6f..5946ae2 100644 --- a/service/plant/my_plant.go +++ b/service/plant/my_plant.go @@ -100,11 +100,13 @@ func (s *MyPlantService) PlantDetail(id string) (p plant.MyPlant, err error) { return db.Order("created_at desc") }). Preload("CarePlans"). - Preload("CareRecords"). + Preload("CareRecords", func(db *gorm.DB) *gorm.DB { + return db.Order("created_at desc") + }). Preload("GrowthRecords", func(db *gorm.DB) *gorm.DB { return db.Preload("ImgList", func(db *gorm.DB) *gorm.DB { return db.Order("created_at desc") - }) + }).Order("created_at desc") }). First(&res).Error diff --git a/service/plant/topic.go b/service/plant/topic.go index 495dd6e..068cd38 100644 --- a/service/plant/topic.go +++ b/service/plant/topic.go @@ -79,11 +79,11 @@ func (s *TopicService) Detail(id string) (t plant.Topic, err error) { // DeleteTopics 删除话题 func (s *TopicService) DeleteTopics(req common.IdsReq) error { var topics []plant.Topic - err := global.DB.Where("id in (?)", req.Ids).Find(&topics).Error + err := global.DB.Where("id in ?", req.Ids).Find(&topics).Error if err != nil { return err } - err = global.DB.Unscoped().Delete(&plant.Topic{}).Error + err = global.DB.Unscoped().Delete(&topics).Error if err != nil { return err } diff --git a/service/plant/user_profile.go b/service/plant/user_profile.go index 02f2315..041a9f9 100644 --- a/service/plant/user_profile.go +++ b/service/plant/user_profile.go @@ -4,18 +4,36 @@ import ( "sundynix-go/global" "sundynix-go/model/plant" plantReq "sundynix-go/model/plant/request" + "sundynix-go/model/system" + + "gorm.io/gorm" ) type UserProfileService struct{} // UpdateProfile 修改用户信息 func (s *UserProfileService) UpdateProfile(req plantReq.UpdateProfile, userId string) error { - updateMap := map[string]interface{}{ - "nick_name": req.Nickname, - "avatar_id": req.AvatarId, - } - return global.DB.Model(&plant.UserProfile{}).Where("user_id = ?", userId).Updates(updateMap).Error - + return global.DB.Transaction(func(tx *gorm.DB) error { + //1.更新profile + updateMap := map[string]interface{}{ + "nick_name": req.Nickname, + "avatar_id": req.AvatarId, + } + err := tx.Model(&plant.UserProfile{}).Where("user_id = ?", userId).Updates(updateMap).Error + if err != nil { + return err + } + //2.更新user表 + updateMap = map[string]interface{}{ + "name": req.Nickname, + "avatar_id": req.AvatarId, + } + err = tx.Model(&system.User{}).Where("id = ?", userId).Updates(updateMap).Error + if err != nil { + return err + } + return nil + }) } // ProfileDetail 获取用户详情