telco_server/tools/sms/sohan.go

210 lines
5.4 KiB
Go
Raw Permalink Normal View History

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
}