From 0fb58a45556fcdd44cf9a795b802676281fdc4aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E6=96=B9=E6=88=90?= Date: Thu, 6 Nov 2025 20:37:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=B0=8F=E7=A8=8B=E5=BA=8F):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=A3=80=E6=9F=A5=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(测试): 移除测试中的硬编码凭证 fix(模板消息): 将小程序状态改为正式版 docs(swagger): 更新API文档并移除密码必填限制 --- .DS_Store | Bin 0 -> 6148 bytes docs/docs.go | 65 +++++++++- docs/swagger.json | 65 +++++++++- docs/swagger.yaml | 43 ++++++- internal/api/app/app_check_status.go | 114 ++++++++++++++++++ internal/api/wechat/mini_template.go | 2 +- internal/pkg/miniprogram/access_token_test.go | 2 +- internal/pkg/miniprogram/subscribe_test.go | 8 +- internal/router/router.go | 9 +- 9 files changed, 295 insertions(+), 13 deletions(-) create mode 100644 .DS_Store create mode 100644 internal/api/app/app_check_status.go diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5444b36f11bc80bf2601a9657d22efa66a9281a0 GIT binary patch literal 6148 zcmeH~K~BRk5JkTsYDeZ3!1-9gpRqx) z3oKY5gk~gr66epv^U}mM0C#XV-2=7(^w`8_eRiLiT$i?DYhgr!o#dz|=Vg7)GZ`^6 z0!HAk6OcD`9TVJQIxeI4cb``VuWDLEm#P{SE^&=3EE4te_v-tTc~#cc6;=FFpo1wc zFhj|V4{;{9n`G)^NYw;4jF{`)OI#!|k4a3%{1TJQ(!C_Q4lAiKVD)uc|C-FTUH`jw zfjivB_lK~0YO(5(QH7<>xWXCAL@%`3`b*R*Od$3qFlGXE7s{`-@+Q)Ho8DV*1dMzLtWTnxUhF{KAhK!pl`hQ;c$9q<_ULpVTaNU$y%GUowA9EuJK+&*oEa- g4`eCcVN>I^Tn^}q;2ZBbLbE>tiorS~@T&yg0EP0-nE(I) literal 0 HcmV?d00001 diff --git a/docs/docs.go b/docs/docs.go index 39eb42a..0369f58 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -15,6 +15,49 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/api/admin/app/check_status": { + "get": { + "security": [ + { + "LoginVerifyToken": [] + } + ], + "description": "管理端根据 app_id 调用外部验证服务获取状态(正常/封禁/未知)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "管理端.小程序" + ], + "summary": "检查小程序状态", + "parameters": [ + { + "type": "string", + "description": "小程序ID", + "name": "app_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/app.checkAppStatusResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, "/api/admin/app/create": { "post": { "security": [ @@ -1657,7 +1700,6 @@ const docTemplate = `{ "type": "object", "required": [ "nickname", - "password", "username" ], "properties": { @@ -1772,6 +1814,27 @@ const docTemplate = `{ } } }, + "app.checkAppStatusResponse": { + "type": "object", + "properties": { + "app_id": { + "description": "小程序ID", + "type": "string" + }, + "check_status_text": { + "description": "状态文字描述", + "type": "string" + }, + "code": { + "description": "外部接口返回的状态码", + "type": "integer" + }, + "status": { + "description": "外部接口返回的状态", + "type": "string" + } + } + }, "app.createAppRequest": { "type": "object", "required": [ diff --git a/docs/swagger.json b/docs/swagger.json index c0eda75..f98e159 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -7,6 +7,49 @@ }, "basePath": "/", "paths": { + "/api/admin/app/check_status": { + "get": { + "security": [ + { + "LoginVerifyToken": [] + } + ], + "description": "管理端根据 app_id 调用外部验证服务获取状态(正常/封禁/未知)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "管理端.小程序" + ], + "summary": "检查小程序状态", + "parameters": [ + { + "type": "string", + "description": "小程序ID", + "name": "app_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/app.checkAppStatusResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, "/api/admin/app/create": { "post": { "security": [ @@ -1649,7 +1692,6 @@ "type": "object", "required": [ "nickname", - "password", "username" ], "properties": { @@ -1764,6 +1806,27 @@ } } }, + "app.checkAppStatusResponse": { + "type": "object", + "properties": { + "app_id": { + "description": "小程序ID", + "type": "string" + }, + "check_status_text": { + "description": "状态文字描述", + "type": "string" + }, + "code": { + "description": "外部接口返回的状态码", + "type": "integer" + }, + "status": { + "description": "外部接口返回的状态", + "type": "string" + } + } + }, "app.createAppRequest": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index dbaeff6..98dc158 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -122,7 +122,6 @@ definitions: type: string required: - nickname - - password - username type: object admin.modifyAdminResponse: @@ -187,6 +186,21 @@ definitions: description: 符合查询条件的总记录数 type: integer type: object + app.checkAppStatusResponse: + properties: + app_id: + description: 小程序ID + type: string + check_status_text: + description: 状态文字描述 + type: string + code: + description: 外部接口返回的状态码 + type: integer + status: + description: 外部接口返回的状态 + type: string + type: object app.createAppRequest: properties: app_id: @@ -889,6 +903,33 @@ paths: summary: 编辑小程序 tags: - 管理端.小程序 + /api/admin/app/check_status: + get: + consumes: + - application/json + description: 管理端根据 app_id 调用外部验证服务获取状态(正常/封禁/未知) + parameters: + - description: 小程序ID + in: query + name: app_id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/app.checkAppStatusResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + security: + - LoginVerifyToken: [] + summary: 检查小程序状态 + tags: + - 管理端.小程序 /api/admin/app/create: post: consumes: diff --git a/internal/api/app/app_check_status.go b/internal/api/app/app_check_status.go new file mode 100644 index 0000000..1c86f88 --- /dev/null +++ b/internal/api/app/app_check_status.go @@ -0,0 +1,114 @@ +package app + +import ( + "fmt" + "net/http" + + "mini-chat/internal/code" + "mini-chat/internal/pkg/core" + "mini-chat/internal/pkg/httpclient" + "mini-chat/internal/pkg/validation" + + "go.uber.org/zap" +) + +// checkAppStatusRequest 请求参数 +// 功能描述:用于检查指定小程序(app_id)的运行状态 +// 参数说明: +// - AppID(string): 小程序ID,必填,通过query参数传入(form:"app_id") +// 返回值:无(该函数返回一个core.HandlerFunc处理器,在HTTP请求完成后通过ctx.Payload返回JSON数据) +type checkAppStatusRequest struct { + AppID string `form:"app_id" binding:"required"` // 小程序ID +} + +// checkAppStatusResponse 响应数据结构 +// 功能描述:返回外部验证服务的状态码与状态文本,并提供统一的文字描述 +// 字段说明: +// - AppID(string): 小程序ID +// - Code(int): 外部接口返回的状态码,1表示正常,0表示封禁,其它表示未知 +// - Status(string): 外部接口返回的原始状态描述 +// - CheckStatusText(string): 根据Code映射得到的中文状态文字(正常/封禁/未知) +type checkAppStatusResponse struct { + AppID string `json:"app_id"` // 小程序ID + Code int `json:"code"` // 外部接口返回的状态码 + CheckStatusText string `json:"check_status_text"` // 状态文字描述 +} + +// CheckAppStatus 检查小程序状态 +// @Summary 检查小程序状态 +// @Description 管理端根据 app_id 调用外部验证服务获取状态(正常/封禁/未知) +// @Tags 管理端.小程序 +// @Accept json +// @Produce json +// @Param app_id query string true "小程序ID" +// @Success 200 {object} checkAppStatusResponse +// @Failure 400 {object} code.Failure +// @Router /api/admin/app/check_status [get] +// @Security LoginVerifyToken +func (h *handler) CheckAppStatus() core.HandlerFunc { + return func(ctx core.Context) { + // 绑定请求参数 + req := new(checkAppStatusRequest) + if err := ctx.ShouldBindForm(req); err != nil { + ctx.AbortWithError(core.Error( + http.StatusBadRequest, + code.ParamBindError, + validation.Error(err)), + ) + return + } + + // 调用外部验证服务 + // 外部接口: https://api.wxapi.work/xcx/checkxcx.php?appid= + // 期望返回: {"code":1, "appid":"xxx", "status":"ok"} + type externalCheckResp struct { + Code int `json:"code"` + Appid string `json:"appid"` + Status string `json:"status"` + } + + checkRes := new(externalCheckResp) + response, err := httpclient.GetHttpClientWithContext(ctx.RequestContext()).R(). + SetQueryParams(map[string]string{ + "appid": req.AppID, + }). + SetResult(checkRes). + Get("https://api.wxapi.work/xcx/checkxcx.php") + + if err != nil { + // 记录请求错误 + h.logger.Error("请求APP验证服务失败", zap.Error(err)) + ctx.AbortWithError(core.Error( + http.StatusBadRequest, + code.ListAppError, + fmt.Sprintf("%s:%s", code.Text(code.ListAppError), err.Error()), + )) + return + } + + // 检查响应状态码 + if response.IsError() { + h.logger.Error(fmt.Sprintf("请求APP验证服务异常(%d)", response.StatusCode())) + } + + // 将外部返回的code映射为中文状态 + statusText := "未知" + switch checkRes.Code { + case 1: + statusText = "正常" + case 0: + statusText = "封禁" + default: + statusText = "未知" + } + + // 组织响应数据并返回 + res := &checkAppStatusResponse{ + AppID: req.AppID, + Code: checkRes.Code, + CheckStatusText: statusText, + } + + ctx.Payload(res) + } +} diff --git a/internal/api/wechat/mini_template.go b/internal/api/wechat/mini_template.go index 9678966..016e03e 100644 --- a/internal/api/wechat/mini_template.go +++ b/internal/api/wechat/mini_template.go @@ -158,7 +158,7 @@ func (h *handler) SendSubscribeMessage() core.HandlerFunc { sendSubscribeMessageReq.Touser = req.Touser sendSubscribeMessageReq.TemplateID = req.TemplateID sendSubscribeMessageReq.Page = "pages/index/detail?url=1" - sendSubscribeMessageReq.MiniprogramState = "developer" // 需要改成正式版 目前是体验版 跳转小程序类型:developer 为开发版;trial为体验版;formal为正式版;默认为正式版 + sendSubscribeMessageReq.MiniprogramState = "formal" // 需要改成正式版 目前是体验版 跳转小程序类型:developer 为开发版;trial为体验版;formal 为正式版;默认为正式版 sendSubscribeMessageReq.Lang = "zh_CN" sendSubscribeMessageReq.Data.Thing1.Value = "留言提醒" sendSubscribeMessageReq.Data.Time2.Value = time.Now().Format("2006-01-02 15:04:05") diff --git a/internal/pkg/miniprogram/access_token_test.go b/internal/pkg/miniprogram/access_token_test.go index 9eb9da3..b20d94b 100644 --- a/internal/pkg/miniprogram/access_token_test.go +++ b/internal/pkg/miniprogram/access_token_test.go @@ -6,7 +6,7 @@ import ( func TestGetAccessToken(t *testing.T) { res := new(AccessTokenResponse) - err := GetAccessToken("wx26ad074017e1e63f", "026c19ce4f3bb090c56573024c59a8be", res) + err := GetAccessToken("", "", res) if err != nil { t.Error(err) } diff --git a/internal/pkg/miniprogram/subscribe_test.go b/internal/pkg/miniprogram/subscribe_test.go index 40a308b..f081a3a 100644 --- a/internal/pkg/miniprogram/subscribe_test.go +++ b/internal/pkg/miniprogram/subscribe_test.go @@ -6,7 +6,7 @@ import ( func TestSendSubscribeMessage(t *testing.T) { res := new(AccessTokenResponse) - err := GetAccessToken("wx26ad074017e1e63f", "026c19ce4f3bb090c56573024c59a8be", res) + err := GetAccessToken("", "", res) if err != nil { t.Errorf("获取 access_token 错误: %s", err.Error()) } @@ -17,9 +17,9 @@ func TestSendSubscribeMessage(t *testing.T) { sendSubscribeMessageRequest.Page = "pages/contact/index" sendSubscribeMessageRequest.MiniprogramState = "trial" sendSubscribeMessageRequest.Lang = "zh_CN" - sendSubscribeMessageRequest.Data.Thing1.Value = "测试(CC)" - sendSubscribeMessageRequest.Data.Time2.Value = "2025-10-27" - sendSubscribeMessageRequest.Data.Thing4.Value = "测试(CC)" + sendSubscribeMessageRequest.Data.Thing1.Value = "测试" + sendSubscribeMessageRequest.Data.Time2.Value = "2020-1-27" + sendSubscribeMessageRequest.Data.Thing3.Value = "测试" sendSubscribeMessageResponse := new(SendSubscribeMessageResponse) err = SendSubscribeMessage(res.AccessToken, sendSubscribeMessageRequest, sendSubscribeMessageResponse) diff --git a/internal/router/router.go b/internal/router/router.go index a786d53..d129b56 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -80,10 +80,11 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo, cron cron.Server) (co // 管理端认证接口路由组 adminAuthApiRouter := mux.Group("/api/admin", core.WrapAuthHandler(interceptorHandler.AdminTokenAuthVerify)) { - adminAuthApiRouter.POST("/app/create", appHandler.CreateApp()) // 关联小程序 - adminAuthApiRouter.POST("/app/delete", appHandler.DeleteApp()) // 删除小程序 - adminAuthApiRouter.PUT("/app/:id", appHandler.ModifyApp()) // 修改小程序 - adminAuthApiRouter.GET("/apps", appHandler.PageList()) // 小程序列表 + adminAuthApiRouter.POST("/app/create", appHandler.CreateApp()) // 关联小程序 + adminAuthApiRouter.POST("/app/delete", appHandler.DeleteApp()) // 删除小程序 + adminAuthApiRouter.PUT("/app/:id", appHandler.ModifyApp()) // 修改小程序 + adminAuthApiRouter.GET("/apps", appHandler.PageList()) // 小程序列表 + adminAuthApiRouter.GET("/app/check_status", appHandler.CheckAppStatus()) // 检查小程序状态 adminAuthApiRouter.POST("/create", adminHandler.CreateAdmin()) // 新增客服 adminAuthApiRouter.POST("/delete", adminHandler.DeleteAdmin()) // 删除客服