210 lines
5.4 KiB
Go
210 lines
5.4 KiB
Go
|
package sms
|
|||
|
|
|||
|
import (
|
|||
|
"bytes"
|
|||
|
"crypto/md5"
|
|||
|
"encoding/hex"
|
|||
|
"encoding/json"
|
|||
|
"fmt"
|
|||
|
"net/http"
|
|||
|
"time"
|
|||
|
)
|
|||
|
|
|||
|
const (
|
|||
|
DefaultUserAccount = "8871f88e273edb8c20834c61ce97b287"
|
|||
|
DefaultUserSecret = "76d9ae4fe2ac3523d7c051e158c1477d"
|
|||
|
DefaultBaseURL = "https://apiext.szshanyun.com:8443"
|
|||
|
)
|
|||
|
|
|||
|
// ---------------- 工具函数 ----------------
|
|||
|
|
|||
|
// GenTimestamp 生成 13 位时间戳(毫秒)
|
|||
|
func GenTimestamp() string {
|
|||
|
return fmt.Sprintf("%d", time.Now().UnixNano()/1e6)
|
|||
|
}
|
|||
|
|
|||
|
// MD5 md5加密
|
|||
|
func MD5(s string) string {
|
|||
|
h := md5.Sum([]byte(s))
|
|||
|
return hex.EncodeToString(h[:])
|
|||
|
}
|
|||
|
|
|||
|
// SignWithBusinessBody 生成发送/状态查询接口签名:md5(businessBody + userSecret + timestamp)
|
|||
|
func SignWithBusinessBody(body interface{}, userSecret, timestamp string) (string, error) {
|
|||
|
b, err := json.Marshal(body)
|
|||
|
if err != nil {
|
|||
|
return "", err
|
|||
|
}
|
|||
|
return MD5(string(b) + userSecret + timestamp), nil
|
|||
|
}
|
|||
|
|
|||
|
// SignSimple 生成余额接口签名:md5(userAccount + userSecret + timestamp)
|
|||
|
func SignSimple(userAccount, userSecret, timestamp string) string {
|
|||
|
return MD5(userAccount + userSecret + timestamp)
|
|||
|
}
|
|||
|
|
|||
|
// ---------------- 数据结构 ----------------
|
|||
|
|
|||
|
// SendRequest ===== 短信发送 =====
|
|||
|
type SendRequest struct {
|
|||
|
BusinessBody BusinessBody `json:"businessBody"`
|
|||
|
Sign string `json:"sign"`
|
|||
|
Timestamp string `json:"timestamp"`
|
|||
|
UserAccount string `json:"userAccount"`
|
|||
|
}
|
|||
|
|
|||
|
type BusinessBody struct {
|
|||
|
ChildUserNumber *string `json:"childUserNumber,omitempty"`
|
|||
|
Content *string `json:"content,omitempty"`
|
|||
|
ModelID *string `json:"modelId,omitempty"`
|
|||
|
Number *string `json:"number,omitempty"`
|
|||
|
ReturnURL *string `json:"returnUrl,omitempty"`
|
|||
|
SendList []SendList `json:"sendList"`
|
|||
|
SignatureID *string `json:"signatureId,omitempty"`
|
|||
|
SignatureStr *string `json:"signatureStr,omitempty"`
|
|||
|
}
|
|||
|
|
|||
|
type SendList struct {
|
|||
|
Content *string `json:"content,omitempty"`
|
|||
|
ModelReplace *ModelReplace `json:"modelReplace,omitempty"`
|
|||
|
OutOrderID string `json:"outOrderId"`
|
|||
|
PhoneNumber string `json:"phoneNumber"`
|
|||
|
}
|
|||
|
|
|||
|
type ModelReplace struct {
|
|||
|
FlowSize *string `json:"FlowSize,omitempty"`
|
|||
|
ISPNumber *string `json:"ispNumber,omitempty"`
|
|||
|
Time *string `json:"Time,omitempty"`
|
|||
|
TimeLimit *string `json:"TimeLimit,omitempty"`
|
|||
|
}
|
|||
|
|
|||
|
type SendResponse struct {
|
|||
|
Message string `json:"message"`
|
|||
|
StatusCode int64 `json:"statusCode"`
|
|||
|
}
|
|||
|
|
|||
|
// BalanceRequest ===== 余额查询 =====
|
|||
|
type BalanceRequest struct {
|
|||
|
Sign string `json:"sign"`
|
|||
|
Timestamp string `json:"timestamp"`
|
|||
|
UserAccount string `json:"userAccount"`
|
|||
|
}
|
|||
|
|
|||
|
type BalanceResponse struct {
|
|||
|
Data BalanceData `json:"data"`
|
|||
|
Message string `json:"message"`
|
|||
|
StatusCode int64 `json:"statusCode"`
|
|||
|
}
|
|||
|
|
|||
|
type BalanceData struct {
|
|||
|
Balance string `json:"balance"`
|
|||
|
UserName string `json:"userName"`
|
|||
|
}
|
|||
|
|
|||
|
// StateRequest ===== 状态查询 =====
|
|||
|
type StateRequest struct {
|
|||
|
BusinessBody StateBusinessBody `json:"businessBody"`
|
|||
|
Sign string `json:"sign"`
|
|||
|
Timestamp string `json:"timestamp"`
|
|||
|
UserAccount string `json:"userAccount"`
|
|||
|
}
|
|||
|
|
|||
|
type StateBusinessBody struct {
|
|||
|
OutOrderID string `json:"outOrderId"`
|
|||
|
PhoneNumber string `json:"phoneNumber"`
|
|||
|
}
|
|||
|
|
|||
|
type StateResponse struct {
|
|||
|
Data StateData `json:"data"`
|
|||
|
Message string `json:"message"`
|
|||
|
StatusCode int64 `json:"statusCode"`
|
|||
|
}
|
|||
|
|
|||
|
type StateData struct {
|
|||
|
SendStatus string `json:"sendStatus"`
|
|||
|
}
|
|||
|
|
|||
|
// ---------------- Client 封装 ----------------
|
|||
|
|
|||
|
type Client struct {
|
|||
|
UserAccount string
|
|||
|
UserSecret string
|
|||
|
BaseURL string
|
|||
|
}
|
|||
|
|
|||
|
func NewClient() *Client {
|
|||
|
return &Client{
|
|||
|
UserAccount: DefaultUserAccount,
|
|||
|
UserSecret: DefaultUserSecret,
|
|||
|
BaseURL: DefaultBaseURL,
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// SendSMS 发送短信
|
|||
|
func (c *Client) SendSMS(body BusinessBody) (*SendResponse, error) {
|
|||
|
timestamp := GenTimestamp()
|
|||
|
sign, err := SignWithBusinessBody(body, c.UserSecret, timestamp)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
req := SendRequest{
|
|||
|
BusinessBody: body,
|
|||
|
Sign: sign,
|
|||
|
Timestamp: timestamp,
|
|||
|
UserAccount: c.UserAccount,
|
|||
|
}
|
|||
|
return post[SendResponse](c.BaseURL+"/receive", req)
|
|||
|
}
|
|||
|
|
|||
|
// GetBalance 查询余额
|
|||
|
func (c *Client) GetBalance() (*BalanceResponse, error) {
|
|||
|
timestamp := GenTimestamp()
|
|||
|
sign := SignSimple(c.UserAccount, c.UserSecret, timestamp)
|
|||
|
req := BalanceRequest{
|
|||
|
Sign: sign,
|
|||
|
Timestamp: timestamp,
|
|||
|
UserAccount: c.UserAccount,
|
|||
|
}
|
|||
|
return post[BalanceResponse](c.BaseURL+"/balance", req)
|
|||
|
}
|
|||
|
|
|||
|
// QueryState 查询状态
|
|||
|
func (c *Client) QueryState(outOrderID, phone string) (*StateResponse, error) {
|
|||
|
body := StateBusinessBody{
|
|||
|
OutOrderID: outOrderID,
|
|||
|
PhoneNumber: phone,
|
|||
|
}
|
|||
|
timestamp := GenTimestamp()
|
|||
|
sign, err := SignWithBusinessBody(body, c.UserSecret, timestamp)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
req := StateRequest{
|
|||
|
BusinessBody: body,
|
|||
|
Sign: sign,
|
|||
|
Timestamp: timestamp,
|
|||
|
UserAccount: c.UserAccount,
|
|||
|
}
|
|||
|
return post[StateResponse](c.BaseURL+"/state", req)
|
|||
|
}
|
|||
|
|
|||
|
// ---------------- POST工具 ----------------
|
|||
|
|
|||
|
func post[T any](url string, payload interface{}) (*T, error) {
|
|||
|
b, err := json.Marshal(payload)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
resp, err := http.Post(url, "application/json", bytes.NewBuffer(b))
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
defer resp.Body.Close()
|
|||
|
|
|||
|
var result T
|
|||
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
return &result, nil
|
|||
|
}
|