Skip to content

Commit

Permalink
feat(mq): add rabbitmq support
Browse files Browse the repository at this point in the history
  • Loading branch information
setcy committed Nov 27, 2023
1 parent 9b760b5 commit a03758d
Show file tree
Hide file tree
Showing 7 changed files with 483 additions and 0 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ require (
github.com/parnurzeal/gorequest v0.2.16
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/rabbitmq/amqp091-go v1.9.0
github.com/samber/lo v1.38.1
github.com/satori/go.uuid v1.2.0
github.com/sethvargo/go-envconfig v0.9.0
github.com/silenceper/wechat/v2 v2.1.6
github.com/soheilhy/cmux v0.1.5
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0
Expand Down
36 changes: 36 additions & 0 deletions go.sum

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions mq/consumer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package mq

import (
"fmt"
amqp "github.com/rabbitmq/amqp091-go"
)

type Consumer struct {
Conn *amqp.Connection
Channel *amqp.Channel
Tag string
Done chan error
Deliveries <-chan amqp.Delivery
}

func NewConsumer(amqpURI, key string) (*Consumer, error) {
c := &Consumer{
Conn: nil,
Channel: nil,
Tag: key,
Done: make(chan error),
}

var err error

c.Conn, c.Channel, err = initConnection(amqpURI)
if err != nil {
return nil, err
}

err = declareDirect(c.Channel, exchangeName, key)
if err != nil {
return nil, err
}

c.Deliveries, err = c.Channel.Consume(
key, // name
key, // consumerTag,
false, // autoAck
false, // exclusive
false, // noLocal
false, // noWait
nil, // arguments
)
if err != nil {
return nil, fmt.Errorf("queue Consume: %s", err)
}

return c, nil
}

func (c *Consumer) Shutdown() error {
// will close() the Deliveries Channel
if err := c.Channel.Cancel(c.Tag, true); err != nil {
return fmt.Errorf("consumer cancel failed: %s", err)
}

if err := c.Conn.Close(); err != nil {
return fmt.Errorf("AMQP connection close error: %s", err)
}

defer fmt.Printf("AMQP shutdown OK")

// wait for exit
return <-c.Done
}
65 changes: 65 additions & 0 deletions mq/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package mq

import (
"fmt"
amqp "github.com/rabbitmq/amqp091-go"
)

const (
exchangeName = "notify"

WechatKey = "notify.wechat"
DingTalkKey = "notify.dingtalk"

UserIdKey = "userId"
)

func initConnection(amqpURI string) (conn *amqp.Connection, channel *amqp.Channel, err error) {
config := amqp.Config{Properties: amqp.NewConnectionProperties()}
config.Properties.SetClientConnectionName("sample-consumer")
fmt.Printf("dialing %q", amqpURI)
conn, err = amqp.DialConfig(amqpURI, config)
if err != nil {
return nil, nil, fmt.Errorf("dial: %s", err)
}

go func() {
fmt.Printf("closing: %s", <-conn.NotifyClose(make(chan *amqp.Error)))
}()

fmt.Printf("got Connection, getting Channel")
channel, err = conn.Channel()
if err != nil {
return nil, nil, fmt.Errorf("channel: %s", err)
}

return conn, channel, nil
}

func declareDirect(channel *amqp.Channel, exchange, key string) error {
err := channel.ExchangeDeclare(
exchange,
"direct",
true,
false,
false,
false,
amqp.Table{},
)
if err != nil {
return err
}

_, err = channel.QueueDeclare(key,
true, false, false, false, nil)
if err != nil {
return err
}

err = channel.QueueBind(key, key, exchange, false, nil)
if err != nil {
return err
}

return nil
}
168 changes: 168 additions & 0 deletions mq/notice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package mq

import (
"context"
"encoding/json"
"github.com/silenceper/wechat/v2/officialaccount/message"
)

type NoticeTemplate struct {
ctx context.Context
StaffID []string `json:"StaffID"`
WeChat *WeChatNoticeBody `json:"template,omitempty"`
DingTalk *DingTalkNoticeBody `json:"dingtalk,omitempty"`
}

func New() *NoticeTemplate {
return &NoticeTemplate{}
}

func (t *NoticeTemplate) WithContext(ctx context.Context) *NoticeTemplate {
t.ctx = ctx
return t
}

func (t *NoticeTemplate) Receiver(staffId ...string) *NoticeTemplate {
t.StaffID = append(t.StaffID, staffId...)
return t
}

func (t *NoticeTemplate) NewWeChat() *WeChatNoticeBody {
t.WeChat = &WeChatNoticeBody{}
return t.WeChat
}

func (t *NoticeTemplate) NewDingTalk() *DingTalkNoticeBody {
t.DingTalk = &DingTalkNoticeBody{}
return t.DingTalk
}

type WeChatNoticeBody struct {
Data map[string]*message.TemplateDataItem `json:"data"`
TemplateID string `json:"template_id"`
ToUser string `json:"touser"`
URL string `json:"url"`
}

func (t *WeChatNoticeBody) SetTmpl(templateId string) *WeChatNoticeBody {
t.TemplateID = templateId
return t
}

func (t *WeChatNoticeBody) SetData(key, value, color string) *WeChatNoticeBody {
if t.Data == nil {
t.Data = make(map[string]*message.TemplateDataItem)
}
t.Data[key] = &message.TemplateDataItem{Value: value, Color: color}
return t
}

func (t *WeChatNoticeBody) Url(url string) *WeChatNoticeBody {
t.URL = url
return t
}

type DingTalkNoticeBody struct {
MsgType string `json:"msgtype"`
Text *DingTalkMsgPlainTextInput `json:"text,omitempty"`
Link *DingTalkMsgLinkInput `json:"link,omitempty"`
Markdown *DingTalkMsgMarkdownInput `json:"markdown,omitempty"`
}

func (t *DingTalkNoticeBody) PlainText(content string) {
t.MsgType = "text"
t.Text = &DingTalkMsgPlainTextInput{
Content: content,
}
}

func (t *DingTalkNoticeBody) LinkMsg(messageUrl, picUrl, title, text string) {
t.MsgType = "link"
t.Link = &DingTalkMsgLinkInput{
MessageUrl: messageUrl,
PicUrl: picUrl,
Title: title,
Text: text,
}
}

func (t *DingTalkNoticeBody) MarkdownMsg(title, text string) {
t.MsgType = "markdown"
t.Markdown = &DingTalkMsgMarkdownInput{
Title: title,
Text: text,
}
}

type DingTalkMsgPlainTextInput struct {
Content string `json:"content"`
}

type DingTalkMsgLinkInput struct {
MessageUrl string `json:"messageUrl"`
PicUrl string `json:"picUrl"`
Title string `json:"title"`
Text string `json:"text"`
}

type DingTalkMsgMarkdownInput struct {
Title string `json:"title"` // 这个是点进推送列表前显示的简短的消息(进入后不可见)
Text string `json:"text"` // 这个是进入后显示的详细的消息
}

type UniNotifyInput struct {
Title string
SubTitle string
Content string
Extra string
}

type WechatBody struct {
StaffId string `json:"staffId"`
*WeChatNoticeBody
MessageId string `json:"messageId"`
}

func MarshalWechatBody(staffId string, msg *WeChatNoticeBody) (data []byte, err error) {
body := &WechatBody{
StaffId: staffId,
WeChatNoticeBody: msg,
}
if data, err = json.Marshal(body); err != nil {
return nil, err
}
return data, nil
}

func UnmarshalWechatBody(data []byte) (*WechatBody, error) {
body := &WechatBody{}
if err := json.Unmarshal(data, body); err != nil {
return nil, err
}
return body, nil
}

type DingTalkBody struct {
StaffId string `json:"staffId"`
*DingTalkNoticeBody
MessageId string `json:"messageId"`
}

func MarshalDingTalkBody(staffId string, msg *DingTalkNoticeBody) (data []byte, err error) {
body := &DingTalkBody{
StaffId: staffId,
DingTalkNoticeBody: msg,
}
if data, err = json.Marshal(body); err != nil {
return nil, err
}
return data, nil
}

func UnmarshalDingTalkBody(data []byte) (*DingTalkNoticeBody, error) {
body := &DingTalkNoticeBody{}
if err := json.Unmarshal(data, body); err != nil {
return nil, err
}
return body, nil
}
Loading

0 comments on commit a03758d

Please sign in to comment.