Add RPC support and enhance MessageCenter with context

This commit is contained in:
renzhiyuan 2025-04-09 19:48:03 +08:00
parent 9bd21cf288
commit 5b12078be4
8 changed files with 415 additions and 42 deletions

View File

@ -52,6 +52,7 @@ type (
TaxAmount string `json:"taxAmount"` //税率 TaxAmount string `json:"taxAmount"` //税率
CallbackUrl string `json:"callbackUrl"` //回调地址 CallbackUrl string `json:"callbackUrl"` //回调地址
Remark string `json:"remark"` Remark string `json:"remark"`
Purpose string `json:"purpose"`
SystemName string `json:"systemName"` //业务系统名称 SystemName string `json:"systemName"` //业务系统名称
SubjectName string `json:"subjectName"` //户名 SubjectName string `json:"subjectName"` //户名
BankInfo BankInfo `json:"bankInfo"` BankInfo BankInfo `json:"bankInfo"`

View File

@ -1,5 +1,7 @@
package l_msg_api package l_msg_api
import "gitea.cdlsxd.cn/self-tools/l_msg_api/protoc/msg"
const ( const (
serverHost = "http://127.0.0.1:8001" serverHost = "http://127.0.0.1:8001"
timeOut = 60 timeOut = 60
@ -13,17 +15,26 @@ const (
DDDateField = "DDDateField" DDDateField = "DDDateField"
) )
type RequestPath string type requestPathIndex int32
const ( const (
accessToken RequestPath = "/oauth/v1/accesstoken" accessToken requestPathIndex = iota + 1
oaCreat RequestPath = "/msg/v1/dingtalk/oa/create" oaCreat
oaGet RequestPath = "/msg/v1/dingtalk/oa/get" oaGet
oaComment RequestPath = "/msg/v1/dingtalk/oa/comment" oaComment
sendSms RequestPath = "/msg/v1/sms/send" sendSms
sendSmsHs RequestPath = "/msg/v1/sms/send/hs" sendSmsHs
) )
var requestPath = map[requestPathIndex]map[RequestWay]string{
accessToken: {Http: "/oauth/v1/accesstoken", Rpc: msg.Msg_Oauth_FullMethodName},
oaCreat: {Http: "/msg/v1/dingtalk/oa/create", Rpc: msg.Msg_DingOACreate_FullMethodName},
oaGet: {Http: "/msg/v1/dingtalk/oa/get", Rpc: msg.Msg_DingOAGet_FullMethodName},
oaComment: {Http: "/msg/v1/dingtalk/oa/comment", Rpc: msg.Msg_DingOAComment_FullMethodName},
sendSms: {Http: "/msg/v1/sms/send", Rpc: msg.Msg_SmsSend_FullMethodName},
sendSmsHs: {Http: "/msg/v1/sms/send/hs", Rpc: msg.Msg_HsSmsSend_FullMethodName},
}
type SmsBusiness string type SmsBusiness string
const ( const (
@ -31,21 +42,14 @@ const (
SmsBusinessDefault SmsBusiness = "aliyun" SmsBusinessDefault SmsBusiness = "aliyun"
) )
var smsBusinessWithRequestPath = map[SmsBusiness]RequestPath{ var smsBusinessWithRequestPath = map[SmsBusiness]requestPathIndex{
SmsBusinessHs: sendSmsHs, SmsBusinessHs: sendSms,
SmsBusinessDefault: sendSms, SmsBusinessDefault: sendSmsHs,
} }
type ( type RequestWay int8
SmsOption func(*SmsOptionData)
SmsOptionData struct { const (
Business SmsBusiness Http RequestWay = iota + 1
} Rpc
) )
func WithBusiness(business SmsBusiness) SmsOption {
return func(OptionData *SmsOptionData) {
OptionData.Business = business
}
}

13
go.mod
View File

@ -2,7 +2,18 @@ module gitea.cdlsxd.cn/self-tools/l_msg_api
go 1.22.2 go 1.22.2
require github.com/valyala/fasthttp v1.59.0 require (
github.com/valyala/fasthttp v1.59.0
google.golang.org/grpc v1.71.1
)
require (
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/protobuf v1.36.4 // indirect
)
require ( require (
github.com/andybalholm/brotli v1.1.1 // indirect github.com/andybalholm/brotli v1.1.1 // indirect

38
msg.go
View File

@ -1,6 +1,7 @@
package l_msg_api package l_msg_api
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
) )
@ -10,10 +11,11 @@ type MessageCenter struct {
ClientKey string // 客户端id,获取授权token需要 ClientKey string // 客户端id,获取授权token需要
ClientSecret string ClientSecret string
header map[string]string header map[string]string
option *mesOptionData
base base
} }
func NewMessageCenter(host, clientKey, clientSecret, serverIndex, tempIndex string) (*MessageCenter, error) { func NewMessageCenter(host, clientKey, clientSecret, serverIndex, tempIndex string, args ...MesOption) (*MessageCenter, error) {
msg := &MessageCenter{ msg := &MessageCenter{
Host: host, Host: host,
ClientKey: clientKey, ClientKey: clientKey,
@ -22,21 +24,25 @@ func NewMessageCenter(host, clientKey, clientSecret, serverIndex, tempIndex stri
ServerIndex: serverIndex, ServerIndex: serverIndex,
TempIndex: tempIndex, TempIndex: tempIndex,
}, },
option: new(mesOptionData),
}
err := msg.setHeader()
if err != nil {
return nil, err
}
for _, arg := range args {
arg(msg.option)
} }
msg.header = map[string]string{"content-type": "application/json; charset=utf-8"}
token, err := msg.getAccessToken()
msg.header = map[string]string{"Authorization": token, "content-type": "application/json; charset=utf-8"}
return msg, err return msg, err
} }
// OACreate 发起OA审批 // OACreate 发起OA审批
func (m *MessageCenter) OACreate(dTalkUserId, treadNo string, formModel *FormsData) (data OAResponse, err error) { func (m *MessageCenter) OACreate(ctx context.Context, dTalkUserId, treadNo string, formModel *FormsData) (data OAResponse, err error) {
formModel.formBase = formBase{ formModel.formBase = formBase{
OutTradeNo: treadNo, OutTradeNo: treadNo,
OriginatorUserId: dTalkUserId, OriginatorUserId: dTalkUserId,
} }
err = m.post(oaCreat, m.parseOACreateParam(formModel), &data) err = m.send(ctx, oaCreat, m.parseOACreateParam(formModel), &data)
if err != nil { if err != nil {
return return
} }
@ -44,12 +50,12 @@ func (m *MessageCenter) OACreate(dTalkUserId, treadNo string, formModel *FormsDa
} }
// OAGetDetail OA详情 // OAGetDetail OA详情
func (m *MessageCenter) OAGetDetail(outTradeNo string) (data OAGetDetailData, err error) { func (m *MessageCenter) OAGetDetail(ctx context.Context, outTradeNo string) (data OAGetDetailData, err error) {
param, _ := json.Marshal(oAGetDetailRequest{ param, _ := json.Marshal(oAGetDetailRequest{
Base: m.base, Base: m.base,
OutTradeNo: outTradeNo, OutTradeNo: outTradeNo,
}) })
err = m.post(oaGet, param, &data) err = m.send(ctx, oaGet, param, &data)
if err != nil { if err != nil {
return return
} }
@ -58,9 +64,9 @@ func (m *MessageCenter) OAGetDetail(outTradeNo string) (data OAGetDetailData, er
// SendSms 短信 // SendSms 短信
// business SmsBusiness // business SmsBusiness
func (m *MessageCenter) SendSms(tels []string, jsonParam string, args ...SmsOption) (data SmsSend, err error) { func (m *MessageCenter) SendSms(ctx context.Context, tels []string, jsonParam string, args ...SmsOption) (data SmsSend, err error) {
var ( var (
e = new(SmsOptionData) e = new(smsOptionData)
) )
if len(tels) == 0 { if len(tels) == 0 {
err = errors.New("手机号不能为空") err = errors.New("手机号不能为空")
@ -80,7 +86,7 @@ func (m *MessageCenter) SendSms(tels []string, jsonParam string, args ...SmsOpti
path := smsBusinessWithRequestPath[e.Business] path := smsBusinessWithRequestPath[e.Business]
param := m.parseSmsSendParam(tels, jsonParam) param := m.parseSmsSendParam(tels, jsonParam)
err = m.post(path, param, &data) err = m.send(ctx, path, param, &data)
if err != nil { if err != nil {
return return
} }
@ -90,11 +96,11 @@ func (m *MessageCenter) SendSms(tels []string, jsonParam string, args ...SmsOpti
// SendBlackBoard 钉钉公告 // SendBlackBoard 钉钉公告
// deptidList //接收部门ID列表最大的列表长度为20。 // deptidList //接收部门ID列表最大的列表长度为20。
// UseridList //接收部用户ID列表最大的列表长度为20。 // UseridList //接收部用户ID列表最大的列表长度为20。
func (m *MessageCenter) SendBlackBoard(title, content string, deptidList []int, useridList []string) (data Default, err error) { func (m *MessageCenter) SendBlackBoard(ctx context.Context, title, content string, deptidList []int, useridList []string) (data Default, err error) {
receiver := blackboardReceiverView{ receiver := blackboardReceiverView{
deptidList, useridList, deptidList, useridList,
} }
err = m.post(sendSms, m.parseSendBlackBoardParam(title, content, receiver), &data) err = m.send(ctx, sendSms, m.parseSendBlackBoardParam(title, content, receiver), &data)
if err != nil { if err != nil {
return return
} }
@ -102,7 +108,7 @@ func (m *MessageCenter) SendBlackBoard(title, content string, deptidList []int,
} }
// OAComment OA评论,CommentUserId为空则默认审核发起人评论 // OAComment OA评论,CommentUserId为空则默认审核发起人评论
func (m *MessageCenter) OAComment(outTradeNo, text, commentUserId string, file *DingOACommentReqFile) (data OAResponse, err error) { func (m *MessageCenter) OAComment(ctx context.Context, outTradeNo, text, commentUserId string, file *DingOACommentReqFile) (data OAResponse, err error) {
req := &oACommentRequest{ req := &oACommentRequest{
Base: m.base, Base: m.base,
OutTradeNo: outTradeNo, OutTradeNo: outTradeNo,
@ -114,7 +120,7 @@ func (m *MessageCenter) OAComment(outTradeNo, text, commentUserId string, file *
} }
param, _ := json.Marshal(req) param, _ := json.Marshal(req)
err = m.post(oaComment, param, &data) err = m.send(ctx, oaComment, param, &data)
if err != nil { if err != nil {
return return
} }

28
option.go Normal file
View File

@ -0,0 +1,28 @@
package l_msg_api
type (
SmsOption func(*smsOptionData)
smsOptionData struct {
Business SmsBusiness
}
)
func WithBusiness(business SmsBusiness) SmsOption {
return func(OptionData *smsOptionData) {
OptionData.Business = business
}
}
type (
MesOption func(*mesOptionData)
mesOptionData struct {
RequestWay RequestWay
}
)
func WithRequestWay(RequestWay RequestWay) MesOption {
return func(OptionData *mesOptionData) {
OptionData.RequestWay = RequestWay
}
}

View File

@ -1,6 +1,7 @@
package l_msg_api package l_msg_api
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"gitea.cdlsxd.cn/self-tools/l_msg_api/cache" "gitea.cdlsxd.cn/self-tools/l_msg_api/cache"
@ -38,7 +39,6 @@ func (m *MessageCenter) parseSendBlackBoardParam(title, content string, receiver
} }
func (m *MessageCenter) getAccessToken() (string, error) { func (m *MessageCenter) getAccessToken() (string, error) {
if tokenInterface, exist := cache.InstanceCacheMap().Get(m.ClientKey); exist { if tokenInterface, exist := cache.InstanceCacheMap().Get(m.ClientKey); exist {
return tokenInterface.(string), nil return tokenInterface.(string), nil
} }
@ -53,7 +53,25 @@ func (m *MessageCenter) getAccessToken() (string, error) {
return data.AccessToken, err return data.AccessToken, err
} }
func (m *MessageCenter) post(path RequestPath, data []byte, resReflect interface{}) (err error) { func (m *MessageCenter) send(ctx context.Context, path requestPathIndex, data []byte, resReflect interface{}) (err error) {
switch m.option.RequestWay {
case Rpc:
//return m.rpc(ctx, requestAddr, data, resReflect)
default:
requestAddr := requestPath[path][m.option.RequestWay]
return m.post(ctx, requestAddr, data, resReflect)
}
return
}
//func (m *MessageCenter) rpc(ctx context.Context, path string, data []byte, resReflect interface{}) (err error) {
//
// client := protoc.InstanceMsgClient(m.Host)
// client.FinanceNotify(ctx, a)
// return
//}
func (m *MessageCenter) post(ctx context.Context, path string, data []byte, resReflect interface{}) (err error) {
var body responseBody var body responseBody
res, err := httpclient.FastHttpPost(fmt.Sprintf("%s%s", m.Host, path), m.header, data, timeOut) res, err := httpclient.FastHttpPost(fmt.Sprintf("%s%s", m.Host, path), m.header, data, timeOut)
if err != nil { if err != nil {
@ -77,7 +95,7 @@ func (m *MessageCenter) post(path RequestPath, data []byte, resReflect interface
return return
} }
func (m *MessageCenter) accessPost(path RequestPath, data []byte, resReflect interface{}) (err error) { func (m *MessageCenter) accessPost(path requestPathIndex, data []byte, resReflect interface{}) (err error) {
var body responseBody var body responseBody
res, err := httpclient.FastHttpPost(fmt.Sprintf("%s%s", m.Host, path), m.header, data, timeOut) res, err := httpclient.FastHttpPost(fmt.Sprintf("%s%s", m.Host, path), m.header, data, timeOut)
if err != nil { if err != nil {
@ -100,3 +118,13 @@ func (m *MessageCenter) accessPost(path RequestPath, data []byte, resReflect int
return return
} }
func (m *MessageCenter) setHeader() (err error) {
m.header = map[string]string{"content-type": "application/json; charset=utf-8"}
token, err := m.getAccessToken()
if err != nil {
return
}
m.header = map[string]string{"Authorization": token, "content-type": "application/json; charset=utf-8"}
return
}

274
protoc/msg/msg.proto Normal file
View File

@ -0,0 +1,274 @@
syntax = "proto3";
package msg;
option go_package = "/msg;msg";
message Empty{}
message BaseRes{
bool isSuccess = 1;
string Msg = 2;
}
message MsgCenterResp{
oneof Resp{
SmsSendRes smsSendRes = 1;
HsSmsSendRes HsSmsSendRes = 2;
BaseRes baseRes = 3;
}
}
message OauthReq {
string clientKey = 1;
string clientSecret = 2;
}
message OauthResp {
string accessToken = 2;
int64 AccessExpire = 3;
}
message CenterSendReq{
string severType = 1;
string msgType = 2;
BaseReq base = 3;
string config = 4;
}
message BaseReq{
string serverIndex = 1;
string tempIndex = 2;
}
message SmsSendReq{
BaseReq base = 1;
string param = 2;
string tels = 3;
}
message SmsSendRes{
repeated SmsSendResItem sendList = 1;
message SmsSendResItem{
string tel = 1;
uint32 isSuccess = 2;
}
}
message DingdingOASendReq {
BaseReq base = 1;
string originatorUserId = 2; //userId
FormComponentValuesView formComponentValues = 3;
message FormComponentValuesView {
string id = 1;
string bizAlias = 2;
string name = 3;
string value = 4;
}
}
message DingTalkBlackBoardSendReq {
BaseReq base = 1;
BlackboardReceiverView blackboardReceiver = 3;
message BlackboardReceiverView {
repeated int32 deptidList = 1;
repeated string useridList = 2;
}
string title = 4;
string content = 5;
}
message wxBizSendReq {
BaseReq base = 1;
string page = 2;//
string touser = 3; //open_id
string data = 4; // { "key1": { "value": any }, "key2": { "value": any } }object
string miniprogram = 5; //{ "appid": ,"pagepath": { "value": any } }
}
message callInfoReq {
BaseReq base = 1;
string calledNumber = 3; //
int32 playTimes = 4; //1->3
int32 volume = 5; //1->100
int32 speed = 6; //,-500->500
string outId = 8; // ID ID
}
message callCaptchaReq {
BaseReq base = 1;
string calledNumber = 3; //
string ttsParam = 4; //{"AckNum":"123456"}
int32 playTimes = 5; //1->3
int32 volume = 6; //1->100
int32 speed = 7; //,-500->500
string outId = 8; // ID ID
}
// DingTalk OA
message DingOACreateReq {
BaseReq base = 1;
string outTradeNo = 2; //
string originatorUserId = 3; //userId
repeated FormComponentValuesView formComponentValues = 5; //
message FormComponentValuesView {
string name = 1; //
string value = 2; //
string ComponentType = 3; //
string extValue = 5; //
string Id = 4; //id
}
Finance finance = 6; //
message Finance {
string checkUserId = 1; //
int64 sync =2;//
string amount = 3; //
string systemName = 13; //
string goodsInfo = 4; //
string paymentAccount=5; //
string taxAmount=6 ;//
string callbackUrl=7; //
string orderNo=8; //
string remark=9; //
string purpose=12;//
string subjectName=11; //
BankInfo bankInfo=10;
message BankInfo {
string resellerName = 1; //
string bankName = 2; //
string accountBankName = 3; //
string subjectName = 4; //
string bankAccount = 5; //
}
}
}
message DingOACreateResp {
bool isSuccess = 1;
string Msg = 2;
string instanceId = 3; // id
}
// DingTalk OA
message DingOAUpdateReq {
BaseReq base = 1;
string outTradeNo = 2; //
repeated FormComponentValuesView formComponentValues = 5; //
message FormComponentValuesView {
string name = 1; //
string value = 2; //
string ComponentType = 3; //
string extValue = 5; //
string Id = 4; //id
}
}
message DingOAUpdateResp {
bool isSuccess = 1;
string Msg = 2;
string instanceId = 3; // id
}
// DingTalk OA
message DingOACommentReq {
BaseReq base = 1;
string outTradeNo = 2; //
string text = 3; //
string commentUserId = 4; //
File file = 5; //
message File{ //
repeated string photos = 1; //URL地址
repeated Attachments attachments = 2; //URL地址
message Attachments{ //
string spaceId =1; //ID
string fileSize =2; //
string fileId =3; //ID
string fileName =4; //
string fileType =5; //
}
}
}
message DingOACommentResp {
bool isSuccess = 1;
string Msg = 2;
string instanceId = 3; // id
}
message DingOAGetReq {
BaseReq base = 1;
string processInstanceId = 2; //id
string outTradeNo = 3; //
}
message DingOAGetResp {
string OutTradeNo = 1; //
string processInstanceId = 2; //id
string Title = 3; //
int64 Result = 4; // 0 1: 2:
string Type = 5; //
string Remarks = 6; //
string CreateTime = 7; //
string UpdateTime = 8; //
}
message PaymentCallbackData {
string sign=1;
string instanceId=2;
string corpId=3;
string failReason =4;
string paymentTime=5; //
string userId=6;
string paymentStatus=7;
}
message PaymentCallbackRes {
bool isSuccess=1;
}
message HsSmsSendReq{
BaseReq base = 1;
string param = 2;
string tels = 3;
}
message HsSmsSendRes{
repeated SmsSendResItem sendList = 1;
message SmsSendResItem{
string tel = 1;
uint32 isSuccess = 2;
}
}
service Msg {
rpc oauth(OauthReq) returns(OauthResp);
rpc centerSend(CenterSendReq) returns(MsgCenterResp);
rpc smsSend(SmsSendReq) returns(SmsSendRes);
rpc dingTalkBlackBoardSend(DingTalkBlackBoardSendReq) returns(BaseRes);
rpc wxBizSend(wxBizSendReq) returns(BaseRes);
rpc callInfo(callInfoReq) returns(BaseRes);
rpc callCaptcha(callCaptchaReq) returns(BaseRes);
rpc dingOACreate(DingOACreateReq) returns(DingOACreateResp);
rpc dingOAUpdate(DingOAUpdateReq) returns(DingOAUpdateResp);
rpc dingOAComment(DingOACommentReq) returns(DingOACommentResp);
rpc dingOAGet(DingOAGetReq) returns(DingOAGetResp);
rpc FinanceNotify(PaymentCallbackData) returns(PaymentCallbackRes);
rpc hsSmsSend(HsSmsSendReq) returns(HsSmsSendRes);
}

21
protoc/rpc.go Normal file
View File

@ -0,0 +1,21 @@
package protoc
import (
"gitea.cdlsxd.cn/self-tools/l_msg_api/protoc/msg"
"google.golang.org/grpc"
"log"
)
var rpcMsgClient msg.MsgClient
func InstanceMsgClient(host string) msg.MsgClient {
if rpcMsgClient != nil {
return rpcMsgClient
}
conn, err := grpc.NewClient(host)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
rpcMsgClient = msg.NewMsgClient(conn)
return rpcMsgClient
}