package controllers import ( "encoding/json" "io" "strings" "server/pkg/identity" "server/pkg/jwtutil" "server/services/wechatmini" beego "github.com/beego/beego/v2/server/web" ) // AppMiniSubscribeController 小程序订阅消息(uniapp 调用) // // 订阅消息是「用户 → 授权 → 服务器主动推给用户」的链路: // 1. GET /api/mini/subscribe/templates 取当前可订阅的模板(含模板ID与跳转页) // 2. 小程序端调 uni.requestSubscribeMessage 弹授权窗,用户同意后 // 3. POST /api/mini/subscribe/authorize 上报授权结果,服务器记录一次发送机会 // 4. 之后业务侧发站内信/审批待办时,服务器自动用掉这次机会推送 type AppMiniSubscribeController struct { beego.Controller } func (c *AppMiniSubscribeController) appClaims() (*jwtutil.Claims, error) { return identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndApp, identity.EndBackend) } func (c *AppMiniSubscribeController) jsonErr(httpStatus, bizCode int, msg string) { c.Ctx.Output.SetStatus(httpStatus) c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} _ = c.ServeJSON() } func (c *AppMiniSubscribeController) ok(data interface{}, msg string) { if msg == "" { msg = "success" } c.Data["json"] = map[string]interface{}{"code": 200, "msg": msg, "data": data} _ = c.ServeJSON() } // Templates GET /api/mini/subscribe/templates // 返回当前启用的订阅模板:小程序端拿 template_id 去调 requestSubscribeMessage func (c *AppMiniSubscribeController) Templates() { claims, err := c.appClaims() if err != nil { code := identity.Status(err) c.jsonErr(code, code, err.Error()) return } identityID := uint64(claims.UserID) apps, err := wechatmini.ListApps() if err != nil { c.jsonErr(500, 500, "读取小程序配置失败") return } list := make([]map[string]interface{}, 0) for _, app := range apps { if app.Enabled != 1 { continue } tpls, terr := wechatmini.ListTemplates(app.AppID) if terr != nil { continue } for _, tpl := range tpls { if tpl.Enabled != 1 || tpl.TemplateID == "" { continue } list = append(list, map[string]interface{}{ "app_id": app.AppID, "code": tpl.Code, "title": tpl.Title, "template_id": tpl.TemplateID, "page": tpl.Page, "remain": wechatmini.SubscribeRemain(identityID, app.AppID, tpl.TemplateID), }) } } c.ok(map[string]interface{}{"list": list}, "获取成功") } // Authorize POST /api/mini/subscribe/authorize // 上报用户授权结果:每授权一次,服务器记录一次可发送机会 func (c *AppMiniSubscribeController) Authorize() { claims, err := c.appClaims() if err != nil { code := identity.Status(err) c.jsonErr(code, code, err.Error()) return } var p struct { TemplateIDs []string `json:"template_ids"` } raw, err := io.ReadAll(c.Ctx.Request.Body) if err != nil { c.jsonErr(400, 400, "参数错误") return } if err := json.Unmarshal(raw, &p); err != nil { c.jsonErr(400, 400, "参数错误") return } if len(p.TemplateIDs) == 0 { c.jsonErr(400, 400, "缺少 template_ids") return } identityID := uint64(claims.UserID) apps, err := wechatmini.ListApps() if err != nil { c.jsonErr(500, 500, "读取小程序配置失败") return } granted := 0 for _, app := range apps { if app.Enabled != 1 { continue } // 只记录平台确实配置过的模板 for _, tid := range p.TemplateIDs { tid = strings.TrimSpace(tid) if tid == "" { continue } tpls, terr := wechatmini.ListTemplates(app.AppID) if terr != nil { continue } matched := false for _, tpl := range tpls { if tpl.TemplateID == tid { matched = true break } } if !matched { continue } if gerr := wechatmini.GrantSubscribe(identityID, app.AppID, []string{tid}); gerr == nil { granted++ } } } c.ok(map[string]interface{}{"granted": granted}, "已记录授权") } // TestNotice POST /api/mini/subscribe/test // 给自己发一条测试订阅消息(用于验证模板配置是否正确) func (c *AppMiniSubscribeController) TestNotice() { claims, err := c.appClaims() if err != nil { code := identity.Status(err) c.jsonErr(code, code, err.Error()) return } var p struct { Title string `json:"title"` Content string `json:"content"` } raw, _ := io.ReadAll(c.Ctx.Request.Body) _ = json.Unmarshal(raw, &p) title := strings.TrimSpace(p.Title) if title == "" { title = "测试提醒" } content := strings.TrimSpace(p.Content) if content == "" { content = "这是一条订阅消息测试,收到说明配置成功。" } identityID := uint64(claims.UserID) if wechatmini.MiniOpenIDByIdentity(identityID) == "" { c.jsonErr(400, 400, "当前账号未绑定小程序,请先用小程序登录") return } res := wechatmini.PushSubscribe(wechatmini.SceneNotice, identityID, map[string]string{ "title": title, "content": content, }) if res.Sent > 0 { c.ok(map[string]interface{}{"sent": res.Sent}, "已发送") return } if len(res.Errors) > 0 { msg := res.Errors[0].Error() if hint := wechatmini.ExplainSubscribeError(res.Errors[0]); hint != "" { msg = hint } c.jsonErr(400, 400, msg) return } c.jsonErr(400, 400, "没有可用的订阅:模板未配置,或你还没授权订阅消息") }