wework

package module
v1.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2022 License: MIT Imports: 11 Imported by: 0

README

github.com/Misakiz/wework

将企业微信接口与企业微信的加解密接口打包在一起,方便用户直接生成客户端使用

1. install

go get github.com/Misakiz/wework

2. Usage1 调用普通接口

初始化客户端*api.CorpAPI, 然后调接口即可:

package main

import (
	"encoding/json"
	"errors"
	"github.com/Misakiz/wework/api"
	"项目名称/conf"
)

var (
	CorpAPIOrder *api.CorpAPI
)

func InitWework() {
	CorpAPIOrder = api.NewCorpAPI(conf.Conf.Wework.CorpId, conf.Conf.Wework.OrderAppSecret)
}

// GetOrderDetail 获取工单详情
func GetOrderDetail(spNo uint64) (err error, res []byte) {
	if spNo == uint64(0) {
		return errors.New("工单号为空"), nil
	}
	response, err := CorpAPIOrder.GetApprovalDetail(map[string]interface{}{
		"sp_no": spNo,
	})
	if err != nil {
		return errors.New("fail to get approval detail: " + err.Error()), nil
	}

	res, err = json.Marshal(response)
	if err != nil {
		return
	}

	return
}

3. Usage2 接收企业微信服务端消息

企业微信服务器认证接口

// verifyURL 验证URL有效性[同步请求] 企业微信审批应用保存回调地址用
func verifyURL(ctx *gin.Context) {
	var err error
	echoStr := ctx.Query("echostr")
	// 验证签名
	if err = wework.ValidSignature(ctx.Query("timestamp"), ctx.Query("nonce"), ctx.Query("msg_signature"),
		conf.Conf.Wework.Token, echoStr); err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"errMsg": err, "status": http.StatusInternalServerError})
		return
	}

	// 解密
	var msg []byte
	if _, msg, _, err = wework.AESDecryptMsg(echoStr, conf.Conf.Wework.EncodingAeskey); err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"errMsg": err, "status": http.StatusInternalServerError})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{"msg": msg, "status": http.StatusOK})
	return
}

处理企业微信服务器post过来的消息

// handleWeworkOrder 处理单个企业微信工单 [错误全部在这一层记录,千万不可以传到上层协程中,否则将会引发ctx并发安全问题]
func handleWeworkOrder(ctx *gin.Context) {
	reqData, err := ctx.GetRawData()
	if err != nil {
		log.Log.Error(errors.Wrapf(err, "%v", ErrParseMsgFromWeworkServer))
		return
	}
	if len(reqData) == 0 {
		log.Log.Error(errors.Wrapf(err, "%v", ErrGetNilMsgFromWeworkServer))
		return
	}

	// 解密消息
	msg, err := wework.DecryptMsg(ctx.Query("timestamp"), ctx.Query("nonce"), ctx.Query("msg_signature"),
		conf.Conf.Wework.Token, conf.Conf.Wework.EncodingAeskey, reqData)
	if err != nil {
		log.Log.Error(errors.Wrap(err, "文件解密失败"))
		return
	}

	// 判断工单不在自动工单清单中,直接返回 上层不打印这个错误
	topic, isOrderExist := cache.IsOrderExist(msg.ApprovalInfo.SpName)
	if !isOrderExist {
		if gin.IsDebugging() {
			log.Log.Warning(WarnNoRecordAutoOrder)
		}
		return
	}

	// 企业微信消息去重 使用FromUsername和CreateTime联合判断
	isReqExist, err := cache.ZExist(conf.Conf.Redis.ReqCacheKey, strconv.Itoa(int(msg.CreateTime)))
	if err != nil {
		log.Log.Error(err)
		return
	}
	if !isReqExist { // 若缓存中未存这个请求 若存在此请求直接忽略
		log.Log.Info(msg.ApprovalInfo.SpName, " ", msg.ToUsername, " ", msg.CreateTime)
		// 将工单企业微信ID+工单创建时间组成的唯一值存入缓存 用来去重
		if err = cache.ZAdd(conf.Conf.Redis.ReqCacheKey, msg.ToUsername+strconv.Itoa(int(msg.CreateTime))); err != nil {
			log.Log.Error(err)
			return
		}

		// 将工单详情存储到MQ
		err, data := GetOrderDetail(msg.ApprovalInfo.SpNo)
		if err != nil {
			log.Log.Error(errors.Wrap(err, "查询工单详情报错"))
			return
		}
		tag, err := utils.CovertHanzToPinYin(msg.ApprovalInfo.SpName)
		if err != nil {
			log.Log.Error(errors.Wrap(err, "转换汉字错误"))
			return
		}
		err = mq.Send(topic, tag, []string{msg.ToUsername + strconv.Itoa(int(msg.CreateTime))}, data)
		if err != nil {
			log.Log.Error(errors.Wrap(err, "存储到MQ报错"))
			return
		}
	}

	return
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AESDecryptData

func AESDecryptData(cipherText []byte, aesKey []byte, iv []byte) (rawData []byte, err error)

AESDecryptData 数据解密

func AESDecryptMsg

func AESDecryptMsg(base64CipherText string, encodingAESKey string) (random, rawXMLMsg, appId []byte, err error)

AESDecryptMsg 消息解密 ciphertext = AES_Encrypt[random(16B) + msg_len(4B) + rawXMLMsg + appId]

func AESEncryptMsg

func AESEncryptMsg(random, rawXMLMsg []byte, appId string, encodingAESKey string) (ciphertext string, err error)

AESEncryptMsg 消息加密 ciphertext = AES_Encrypt[random(16B) + msg_len(4B) + rawXMLMsg + appId]

func GetRandString

func GetRandString(length int) string

GetRandString 获取指定长度的随机字符串

func GetRandStringWithCharset

func GetRandStringWithCharset(length int, charset string) string

GetRandStringWithCharset 获取指定字符集下 指定长度的随机字符串

func TruncateRobotMsg

func TruncateRobotMsg(originalMsg, sep string) (resMsgSegments []string)

TruncateRobotMsg Truncate enterprise WeChat robot messages. Divide long messages by line judgment and return message slices.

func ValidSignature

func ValidSignature(reqTimestamp, reqNonce, reqMsgSign, token, encrypt string) (err error)

ValidSignature verify signature

Types

type MsgContent

type MsgContent struct {
	ToUsername   string `xml:"ToUserName" json:"ToUserName"`
	FromUsername string `xml:"FromUserName" json:"FromUserName"`
	CreateTime   uint32 `xml:"CreateTime" json:"CreateTime"`
	MsgType      string `xml:"MsgType" json:"MsgType"`
	Event        string `xml:"Event" json:"Event"`
	AgentID      uint32 `xml:"AgentID" json:"AgentID"`

	ApprovalInfo struct {
		SpNo       uint64 `xml:"SpNo" json:"SpNo"`
		SpName     string `xml:"SpName" json:"SpName"`
		SpStatus   uint8  `xml:"SpStatus" json:"SpStatus"`
		TemplateId string `xml:"TemplateId" json:"TemplateId"`
		ApplyTime  uint32 `xml:"ApplyTime" json:"ApplyTime"`
		Applyer    struct {
			UserId string `xml:"UserId" json:"UserId"`
			Party  uint32 `xml:"Party" json:"Party"`
		} `xml:"Applyer" json:"Applyer"`
		SpRecord struct {
			SpStatus     uint32 `xml:"SpStatus" json:"SpStatus"`
			ApproverAttr uint32 `xml:"ApproverAttr" json:"ApproverAttr"`
			Details      struct {
				Approver struct {
					UserId string `xml:"UserId" json:"UserId"`
				} `xml:"Approver" json:"Approver"`
				Speech   []string `xml:"Speech" json:"Speech"`
				SpStatus uint32   `xml:"SpStatus" json:"SpStatus"`
				SpTime   uint32   `xml:"SpTime" json:"SpTime"`
			} `xml:"Details" json:"Details"`
		} `xml:"SpRecord" json:"SpRecord"`
		StatuChangeEvent uint32 `xml:"StatuChangeEvent" json:"StatuChangeEvent"`
	} `xml:"ApprovalInfo" json:"ApprovalInfo"`
}

MsgContent 消息内容

func DecryptMsg

func DecryptMsg(reqTimestamp, reqNonce, reqMsgSign, token, aesKey string, reqData []byte) (res MsgContent, err error)

DecryptMsg decrypt message

func ParseMsgContent

func ParseMsgContent(reqData []byte) (res MsgContent, err error)

ParseMsgContent parse the message content

type RecMsg

type RecMsg struct {
	ToUsername string `xml:"ToUserName"`
	Encrypt    string `xml:"Encrypt"`
	AgentId    string `xml:"AgentID"`
}

RecMsg 企业微信系统发送的消息

func ParseRecMsg

func ParseRecMsg(reqData []byte) (res RecMsg, err error)

ParseRecMsg parse raw messages

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL