From 9071be2e1463ecf820e892d29cddb237ab0ff482 Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Fri, 7 Mar 2025 16:52:29 +0800 Subject: [PATCH] first push --- README.md | 18 ++++ cache/cache.go | 12 +++ config.go | 187 +++++++++++++++++++++++++++++++++++++++++ const.go | 21 +++++ go.mod | 12 +++ httpclient/fasthttp.go | 96 +++++++++++++++++++++ msg.go | 152 +++++++++++++++++++++++++++++++++ test/msg_test.go | 17 ++++ 8 files changed, 515 insertions(+) create mode 100644 cache/cache.go create mode 100644 config.go create mode 100644 const.go create mode 100644 go.mod create mode 100644 httpclient/fasthttp.go create mode 100644 msg.go create mode 100644 test/msg_test.go diff --git a/README.md b/README.md index e69de29..c1bfa8d 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,18 @@ +## 安装 + + +```bash +$ go get gitea.cdlsxd.cn/self-tools/l_msg_api +``` + + +## 使用 +```go + msg, err := l_msg_api.NewMessageCenter(yourKey, yoursecret, ServerIndex, TempIndex) + if err != nil { + panic(err) + } + //短信 + res, err := msg.SendSms([]string{"15082245122"}, `{"content": "测试"}`) + +``` diff --git a/cache/cache.go b/cache/cache.go new file mode 100644 index 0000000..516301e --- /dev/null +++ b/cache/cache.go @@ -0,0 +1,12 @@ +package cache + +import "github.com/emirpasic/gods/maps/hashmap" + +var cache *hashmap.Map + +func InstanceCacheMap() *hashmap.Map { + if cache == nil { + cache = hashmap.New() + } + return cache +} diff --git a/config.go b/config.go new file mode 100644 index 0000000..655c4e5 --- /dev/null +++ b/config.go @@ -0,0 +1,187 @@ +package l_msg_api + +// 业务系统和模板配置 +type ( + Base struct { + ServerIndex string `json:"server_index"` // 业务系统 + TempIndex string `json:"temp_index"` // 配置模板 + } + + accessTokenResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data struct { + AccessToken string `json:"accessToken"` // 授权token + AccessExpire int `json:"accessExpire"` // 过期时间 + } `json:"data"` + } + + OACreatePurchaseResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data struct { + IsSuccess bool `json:"is_success"` // 是否成功 true 成功 false 失败 + Msg string `json:"msg"` + InstanceId string `json:"instance_id"` // 审批实例id + } `json:"data"` + } + + // 封装上游采购审批表单数据 + formDataRequest struct { + Base Base `json:"base"` + formsData + } + formsData struct { + FormBase + FormComponentValues []formComponentValues `json:"form_component_values"` // 审批表单数据 + } + formComponentValues struct { // 审批表单数据 + Name string `json:"name"` + Value string `json:"value"` + ComponentType string `json:"component_type,omitempty"` + ExtValue string `json:"extValue,omitempty"` + } + + // 创建OA基础数据 + FormBase struct { + OutTradeNo string `json:"out_trade_no"` // 流水号 + OriginatorUserId string `json:"originator_user_id"` // 钉钉申请人id + } + + // 创建采购审批表单数据 + CreatePurchaseForm struct { + FormBase + PurchaseNum string // 采购单号 + SupplierName string //供应商名称 + TotalCount string //采购总数量 + TotalAmount string //采购总金额 + WarehouseName string //入库仓库 + ExpectArrivalTime string //预计到货时间 + + Remark string //采购备注 + PurchaseType string //采购类型 + PayType string //支付类型 + TotalAmountUpperCase string //总金额大写 + SupplierSettlementBank string //结算账户开户行 + SupplierSettlementAccount string //结算账户 + + PurchaseInGoodsList []PurchaseInGoods + } + + // 采购单商品列表 + PurchaseInGoods struct { + GoodsCode string `json:"goods_code"` // 商品编码 + GoodsName string `json:"goods_name"` // 商品名称 + Count string `json:"count"` // 数量 + Price string `json:"price"` // 单价 + Total string `json:"total"` // 总价 + } + + // 获取审批实列详情 请求数据 + oAGetDetailRequest struct { + Base Base `json:"base"` + OutTradeNo string `json:"out_trade_no"` // 流水号 + ProcessInstanceId string `json:"process_instance_id"` // 审批实例id + } + + // 获取审批实列详情 返回数据 + OAGetDetailData struct { + OutTradeNo string `json:"out_trade_no"` // 流水号 + ProcessInstanceId string `json:"process_instance_id"` // 审批实例id + Title string `json:"title"` // 审批标题 + Result int `json:"result"` // 审批结果,(1:同意,2:拒绝) + Type string `json:"type"` // 审批类型 '任务状态变更类型,start:审批任务开始,finish:审批任务正常结束(完成或转交) + Remarks string `json:"remarks"` // 审批备注 + CreateTime string `json:"createTime"` // 审批创建时间 + UpdateTime string `json:"updateTime"` // 审批更新时间 + } + OAGetDetailResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data OAGetDetailData `json:"data"` + } + + // 创建客户充值审批表单数据 + CreateCustomerRechargeForm struct { + FormBase + Initiator string //发起人 + CustomerName string //客户名称 + RecipientAccountName string //收款账户名称 + RechargeType string //充值类型 + RechargeAmount string //充值金额 + Voucher string //充值凭证 + Remark string //备注 + CreatedAt string //创建时间 + } + + // 创建供应商充值审批表单数据 + CreateSupplierRechargeForm struct { + FormBase + Initiator string //发起人 + SupplierName string //供应商名称 + RecipientAccountName string //收款账户名称 + RechargeType string //充值类型 + RechargeAmount string //充值金额 + Voucher string //充值凭证 + Remark string //备注 + CreatedAt string //创建时间 + } + + /* + OaNotifyData 回调通知数据 + Method: POST + {"content-type": "application/json; charset=utf-8"}, + */ + OaNotifyData struct { + OutTradeNo string `json:"out_trade_no"` // 下游流水号 + ProcessInstanceId string `json:"process_instance_id"` // 创建审批任务返回的 审批实例id + Title string `json:"title"` // 实例标题 + StaffId string `json:"staff_id"` // 当前任务的审批人userId,操作转交动作的用户userId + Result int32 `json:"result"` // 审批结果,(1:同意,2:拒绝) + Type string `json:"type"` // 任务状态变更类型,start:审批任务开始,finish:审批任务正常结束(完成或转交),comment:审批任务评论,cancel:说明当前节点有多个审批人并且是或签,其中一个人执行了审批,其他审批人会推送cancel类型事件 + Remark string `json:"remark"` // 操作时写的评论内容 + CreatedAt string `json:"created_at"` // 创建时间 + UpdatedAt string `json:"updated_at"` // 结束时间 + } + + SmsRequest struct { + Base Base `json:"base"` + Param string `json:"param"` // 短信模板变量对应的实际值 + Tels string `json:"tels"` + } + + SmsSendRes struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data SmsSend `json:"data"` + } + SmsSend struct { + SendList []SmsSendResView `json:"send_list"` + } + + SmsSendResView struct { + Tel string `json:"tel"` + IsSuccess uint32 `json:"is_success"` + } + + DingTalkBlackBoardSendReq struct { + Base Base `json:"base"` + BlackboardReceiver BlackboardReceiverView `json:"blackboard_receiver"` //公告接收人。 + Title string `json:"title,default=公告"` //公告标题 + Content string `json:"content,default=大家好"` //公告内容 + } + BlackboardReceiverView struct { + DeptidList []int `json:"deptid_list"` //接收部门ID列表,最大的列表长度为20。 + UseridList []string `json:"userid_list"` //接收部用户ID列表,最大的列表长度为20。 + } + + DefaultRes struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data Default `json:"data"` + } + Default struct { + IsSuccess bool `json:"is_success,default=false"` + Msg string `json:"msg,default=ok"` + } +) diff --git a/const.go b/const.go new file mode 100644 index 0000000..4f25542 --- /dev/null +++ b/const.go @@ -0,0 +1,21 @@ +package l_msg_api + +const ( + ServerHost = "http://121.199.38.107:8000" + TimeOut = 60 +) + +const ( + DDSelectField = "DDSelectField" + DDTextField = "TextField" + DDTextareaField = "TextareaField" + DDNumberField = "NumberField" + DDDateField = "DDDateField" +) + +const ( + PathAccessToken = "/oauth/v1/accesstoken" + PathOaCreat = "/msg/v1/dingtalk/oa/create" + PathOaGet = "/msg/v1/dingtalk/oa/create" + SendSms = "/msg/v1//sms/send" +) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6b251a7 --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module gitea.cdlsxd.cn/self-tools/l_msg_api + +go 1.23.6 + +require github.com/valyala/fasthttp v1.59.0 + +require ( + github.com/andybalholm/brotli v1.1.1 // indirect + github.com/emirpasic/gods v1.18.1 + github.com/klauspost/compress v1.17.11 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect +) diff --git a/httpclient/fasthttp.go b/httpclient/fasthttp.go new file mode 100644 index 0000000..ccca12e --- /dev/null +++ b/httpclient/fasthttp.go @@ -0,0 +1,96 @@ +package httpclient + +import ( + "fmt" + "github.com/valyala/fasthttp" + "time" +) + +func FastHttpPost(url string, header map[string]string, body []byte, timeout int) ([]byte, error) { + req := fasthttp.AcquireRequest() + defer fasthttp.ReleaseRequest(req) // 用完需要释放资源 + // 默认是application/x-www-form-urlencoded + req.Header.SetMethod("POST") + for k, v := range header { + req.Header.Set(k, v) + } + req.SetRequestURI(url) + req.SetBody(body) + resp := fasthttp.AcquireResponse() + defer fasthttp.ReleaseResponse(resp) // 用完需要释放资源 + var err error + if timeout <= 0 { + if err = fasthttp.Do(req, resp); err != nil { + return nil, err + } + } else { + if err := fasthttp.DoTimeout(req, resp, time.Duration(timeout)*time.Second); err != nil { + return nil, err + } + } + b := resp.Body() + //fmt.Println(string(b),"http请求") + return b, nil +} + +func FastHttpPostForm(url string, header map[string]string, body map[string]string, timeout int) ([]byte, error) { + req := fasthttp.AcquireRequest() + defer fasthttp.ReleaseRequest(req) // 用完需要释放资源 + // 默认是application/x-www-form-urlencoded + req.Header.SetMethod("POST") + for k, v := range header { + req.Header.Set(k, v) + } + req.SetRequestURI(url) + args := &fasthttp.Args{} + for k, v := range body { + args.Add(k, v) + } + req.SetBody(args.QueryString()) + resp := fasthttp.AcquireResponse() + defer fasthttp.ReleaseResponse(resp) // 用完需要释放资源 + var err error + if timeout == 0 { + if err = fasthttp.Do(req, resp); err != nil { + return nil, err + } + } else { + if err := fasthttp.DoTimeout(req, resp, time.Duration(timeout)*time.Second); err != nil { + return nil, err + } + } + b := resp.Body() + return b, nil +} +func FastHttpGet(url string, header map[string]string, body map[string]string, timeout int) ([]byte, error) { + req := fasthttp.AcquireRequest() + defer fasthttp.ReleaseRequest(req) // 用完需要释放资源 + // 默认是application/x-www-form-urlencoded + req.Header.SetMethod("GET") + for k, v := range header { + req.Header.Set(k, v) + } + if len(body) > 0 { + url += "?" + for k, v := range body { + url += k + "=" + v + "&" + } + url = url[0 : len(url)-1] + } + fmt.Println(url) + req.SetRequestURI(url) + resp := fasthttp.AcquireResponse() + defer fasthttp.ReleaseResponse(resp) // 用完需要释放资源 + var err error + if timeout == 0 { + if err = fasthttp.Do(req, resp); err != nil { + return nil, err + } + } else { + if err := fasthttp.DoTimeout(req, resp, time.Duration(timeout)*time.Second); err != nil { + return nil, err + } + } + b := resp.Body() + return b, nil +} diff --git a/msg.go b/msg.go new file mode 100644 index 0000000..41c01e9 --- /dev/null +++ b/msg.go @@ -0,0 +1,152 @@ +package l_msg_api + +import ( + "encoding/json" + "errors" + "fmt" + "gitea.cdlsxd.cn/self-tools/l_msg_api/cache" + "gitea.cdlsxd.cn/self-tools/l_msg_api/httpclient" + "strings" +) + +type MessageCenter struct { + Host string // 消息中心地址 + ClientKey string // 客户端id,获取授权token需要 + ClientSecret string + header map[string]string + Base +} + +func NewMessageCenter(clientKey, clientSecret, serverIndex, tempIndex string) (*MessageCenter, error) { + msg := &MessageCenter{ + Host: ServerHost, + ClientKey: clientKey, + ClientSecret: clientSecret, + Base: Base{ + ServerIndex: serverIndex, + TempIndex: tempIndex, + }, + } + accessToken, err := msg.getAccessToken() + msg.header = map[string]string{"Authorization": accessToken, "content-type": "application/json; charset=utf-8"} + return msg, err +} + +/* +获取授权token +AccessExpire 有效期内可缓存起来使用 +*/ +func (m *MessageCenter) getAccessToken() (string, error) { + if tokenInterface, exist := cache.InstanceCacheMap().Get(m.ClientKey); exist { + return tokenInterface.(string), nil + } + var data accessTokenResponse + url := fmt.Sprintf("%s%s", m.Host, PathAccessToken) + var authParam, _ = json.Marshal(map[string]string{"client_key": m.ClientKey, "client_secret": m.ClientSecret}) + res, err := httpclient.FastHttpPost(url, map[string]string{"content-type": "application/json; charset=utf-8"}, authParam, TimeOut) + if err != nil { + return "", err + } + err = json.Unmarshal(res, &data) + if data.Code != 0 { + return "", errors.New(data.Msg) + } + cache.InstanceCacheMap().Put(m.ClientKey, data.Data.AccessToken) + return data.Data.AccessToken, err +} + +// 采购单OA审批 +func (m *MessageCenter) OACreate(dTalkUserId, treadNo string, checkForm []formComponentValues) (data OACreatePurchaseResponse, err error) { + res, err := httpclient.FastHttpPost( + fmt.Sprintf("%s%s", m.Host, PathOaCreat), + m.header, + m.parseOACreateParam(dTalkUserId, treadNo, checkForm), + TimeOut) + if err != nil { + return + } + err = json.Unmarshal(res, &data) + return +} + +func (m *MessageCenter) parseOACreateParam(dTalkUserId, treadNo string, checkForm []formComponentValues) (out []byte) { + req := formDataRequest{ + Base: m.Base, + } + req.FormBase = FormBase{ + OutTradeNo: treadNo, + OriginatorUserId: dTalkUserId, + } + req.FormComponentValues = checkForm + out, _ = json.Marshal(req) + return +} + +func (m *MessageCenter) OAGetDetail(outTradeNo string) (data OAGetDetailData, err error) { + param, _ := json.Marshal(oAGetDetailRequest{ + Base: m.Base, + OutTradeNo: outTradeNo, + }) + res, err := httpclient.FastHttpPost(fmt.Sprintf("%s%s", m.Host, PathOaGet), m.header, param, TimeOut) + if err != nil { + return + } + // 解析响应参数 + body := OAGetDetailResponse{} + err = json.Unmarshal(res, &body) + if err != nil { + return + } + if body.Code != 0 { + err = errors.New(body.Msg) + return + } + return body.Data, nil +} + +// 采购单OA审批 +func (m *MessageCenter) SendSms(tels []string, jsonParam string) (data SmsSendRes, err error) { + param := m.parseSmsSendParam(tels, jsonParam) + res, err := httpclient.FastHttpPost( + fmt.Sprintf("%s%s", m.Host, SendSms), + m.header, + param, + TimeOut) + if err != nil { + return + } + err = json.Unmarshal(res, &data) + return +} + +func (m *MessageCenter) parseSmsSendParam(tels []string, jsonParam string) (out []byte) { + out, _ = json.Marshal(SmsRequest{ + Base: m.Base, + Tels: strings.Join(tels, ","), + Param: jsonParam, + }) + return +} + +func (m *MessageCenter) SendBlackBoard(title, content string, receiver BlackboardReceiverView) (data DefaultRes, err error) { + res, err := httpclient.FastHttpPost( + fmt.Sprintf("%s%s", m.Host, SendSms), + m.header, + m.parseSendBlackBoardParam(title, content, receiver), + TimeOut) + if err != nil { + return + } + err = json.Unmarshal(res, &data) + return +} + +func (m *MessageCenter) parseSendBlackBoardParam(title, content string, receiver BlackboardReceiverView) (out []byte) { + out, _ = json.Marshal(DingTalkBlackBoardSendReq{ + Base: m.Base, + Content: content, + Title: title, + BlackboardReceiver: receiver, + }) + return +} diff --git a/test/msg_test.go b/test/msg_test.go new file mode 100644 index 0000000..578b29d --- /dev/null +++ b/test/msg_test.go @@ -0,0 +1,17 @@ +package test + +import ( + "fmt" + "gitea.cdlsxd.cn/self-tools/l_msg_api" + "testing" +) + +func TestBase(t *testing.T) { + msg, err := l_msg_api.NewMessageCenter("", "", "sms", "cron_sms") + if err != nil { + panic(err) + } + + res, err := msg.SendSms([]string{"15082245122"}, `{"content": "测试"}`) + fmt.Println(res, err) +}