Files
yunzerwebsiteallinone/go/controllers/api_mini_subscribe.go
T
2026-10-31 19:17:37 +08:00

197 lines
5.3 KiB
Go

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, "没有可用的订阅:模板未配置,或你还没授权订阅消息")
}