增加微信通知功能
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
package wechatmp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
)
|
||||
|
||||
// =============================================================
|
||||
// 消息推送(认证后可用):
|
||||
// 未认证服务号微信不允许主动推送(模板消息/订阅通知/客服消息均受限),
|
||||
// 本层只负责在认证后通过模板消息把提醒/公告发给已关注粉丝;
|
||||
// 未认证时返回 ErrNotVerified,由上层给出明确提示。
|
||||
// =============================================================
|
||||
|
||||
// ErrNotVerified 公众号未认证,无法主动推送
|
||||
var ErrNotVerified = errors.New("公众号未认证:模板消息推送需认证后使用(当前仅支持关注与被动回复)")
|
||||
|
||||
// PushMessage 推送内容(对应「标题/内容/备注」通用模板字段)
|
||||
type PushMessage struct {
|
||||
Title string // first:首行说明
|
||||
Content string // keyword1:主体内容
|
||||
Remark string // remark:备注
|
||||
URL string // 点击跳转地址(可选)
|
||||
}
|
||||
|
||||
// PushToOpenID 向指定粉丝推送一条模板消息
|
||||
func PushToOpenID(cfg *Config, openid string, msg PushMessage) error {
|
||||
if cfg == nil {
|
||||
return ErrNotConfigured
|
||||
}
|
||||
if !cfg.Verified {
|
||||
return ErrNotVerified
|
||||
}
|
||||
if openid == "" {
|
||||
return errors.New("openid 为空")
|
||||
}
|
||||
if msg.Title == "" {
|
||||
msg.Title = "您有一条新的消息"
|
||||
}
|
||||
if len([]rune(msg.Title)) > 100 {
|
||||
msg.Title = string([]rune(msg.Title)[:100])
|
||||
}
|
||||
if len([]rune(msg.Content)) > 200 {
|
||||
msg.Content = string([]rune(msg.Content)[:200])
|
||||
}
|
||||
if len([]rune(msg.Remark)) > 100 {
|
||||
msg.Remark = string([]rune(msg.Remark)[:100])
|
||||
}
|
||||
return SendTemplateMessage(cfg, openid, msg.URL, map[string]string{
|
||||
"first": msg.Title,
|
||||
"keyword1": msg.Content,
|
||||
"remark": msg.Remark,
|
||||
})
|
||||
}
|
||||
|
||||
// PushScope 推送范围
|
||||
type PushScope struct {
|
||||
// BindType 为空表示全部已关注粉丝;platform_user / tenant_user 只推绑定对应类型账号的粉丝
|
||||
BindType string
|
||||
// BindID 非 0 时只推给该绑定账号(与 BindType 组合使用)
|
||||
BindID uint64
|
||||
// Tid 非 0 时只推给绑定该租户的用户(tenant_user 场景)
|
||||
Tid uint64
|
||||
}
|
||||
|
||||
// MaxBatchPush 单次批量推送上限
|
||||
const MaxBatchPush = 500
|
||||
|
||||
// PushToFollowers 按范围批量推送,返回成功/失败数量。
|
||||
// 仅推送给「已关注(subscribe=1)且未删除」的粉丝。
|
||||
func PushToFollowers(cfg *Config, scope PushScope, msg PushMessage) (sent, failed int, err error) {
|
||||
if cfg == nil {
|
||||
return 0, 0, ErrNotConfigured
|
||||
}
|
||||
if !cfg.Verified {
|
||||
return 0, 0, ErrNotVerified
|
||||
}
|
||||
qs := models.Orm.QueryTable(new(models.WechatMpFollower)).
|
||||
Filter("subscribe", 1).
|
||||
Filter("delete_time__isnull", true)
|
||||
if scope.BindType != "" {
|
||||
qs = qs.Filter("bind_type", scope.BindType)
|
||||
}
|
||||
if scope.BindID > 0 {
|
||||
qs = qs.Filter("bind_id", scope.BindID)
|
||||
}
|
||||
if scope.Tid > 0 {
|
||||
qs = qs.Filter("bind_tid", scope.Tid)
|
||||
}
|
||||
|
||||
var rows []models.WechatMpFollower
|
||||
if _, qerr := qs.OrderBy("-id").Limit(MaxBatchPush).All(&rows); qerr != nil {
|
||||
return 0, 0, qerr
|
||||
}
|
||||
if len(rows) == 0 {
|
||||
return 0, 0, errors.New("没有匹配的已关注粉丝")
|
||||
}
|
||||
|
||||
for i := range rows {
|
||||
if perr := PushToOpenID(cfg, rows[i].OpenID, msg); perr != nil {
|
||||
failed++
|
||||
continue
|
||||
}
|
||||
sent++
|
||||
// 简单节流,避免触发微信频率限制
|
||||
if i%20 == 19 {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
return sent, failed, nil
|
||||
}
|
||||
Reference in New Issue
Block a user