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