telco_server/tools/sms/sohan.go
chenlin ef1a39a0a3 1、新增对外短信接口;
2、新增硕软相关接口;
2025-09-04 17:55:02 +08:00

210 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}