migu_server/app/admin/models/gdyd_model.go

445 lines
16 KiB
Go
Raw Permalink Normal View History

package models
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"github.com/google/uuid"
"io"
"net/http"
"sort"
"strings"
"time"
)
const (
AppId = "114320"
PrivateKeyPEM = `-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCoqhTbIPDpg8OzUDsJ8Po+UthsUWlugC+Ti7UCLHxp1Eask8UBOloOGWO9IUHS03y80SEWM+oy94Vb/g2QIe7Jk5QdfJzxa0cnptIzVxIRpyCFNlaSheNwCD4wjoZvXKXEoqb1J2C5U4ks0Jd9dYuUa7aLtwHBYLb2QL7hN9v7GyP4YOPU65pnHW6nrIDk6s+FZWkjdw6nT+dwCO3dl/l5MNbp0oaH+73xgxElOgABBStcelDEPc0YOwSPXlfwl68Ozd4cxwU5SGRH/zj1h7YBP+mwEOFUE62EO2mgc3JIF5YpaLsMHF960/VkM6j4qBM0Hds/WmxtJ1COXhVzZTWbAgMBAAECggEAYf8gSwlisGrMhBzzkJ0g6KE9+gF7Xqa//dxIeVDboKmjvpXE+yeqN1LtLnBqTFJwwUxJCxty0dYa+A4uVzZABYLnphJHJcYM+67jpszKRNN0A7JErrF4Khm/+Hp3BbEw54URuJL+ke9FXnJ78nsfdEb5M0hLjs3gwEdyIG1SNRzoI0VpcWxP430esn1vXTxfyWedLPhIhfdgVtzwLnaOrpRy5Qd4JiZacoSxCHnV3zsT6pp5QD1RwClg1UJbiH0B2TiUbBiyl5GJgMvVtOjj2yX80c8JbpiRxYMujIbHcmEjzqvbkCX5/q/WjxPkR5Fk4MlDAlogr68pOF3rL0H0oQKBgQDY5aARizxt6NABaZJtNElUqrjNBxQCaLMt7nMRGOjb5o7LKPRhwRcqUoTYvDIn4qKN0V8lLnvRJjT089WRwBHWqwJNfp14z3HqfJMWqLIFWnp1CaJCCusc6zdjTqu8KaztCyjlxlp0usZXfCw1SgKmSnziFOSRkxt1X0HFn/0WGQKBgQDHEmSoPH7MpqkrCWRvSysbbcdIbpYiBoru8+irdy0EVxzu5s9mGb6IncZ2Y41f+/T0cpPuNlcrJQfQ0tEKo0uDjpPmiKQa3AVlOElOJI9H/KOZQHHM835STSxjsOb5RjMdFBYbutzO5h7EAaQQEFWNfcNsbaSPQlXNttdWH8XX0wKBgQCr8z83KV868zsUI5IGKVGJYd8oC9h9IGwMmeF3SHwy+VFzFoDHjsDCuLDA8lIA9NdR/w6i93sJkHSjTTufVNnPibtFnH9S64KwFxq0+ABJ5jT23DBakzVZs9AxVoknnxKMyjAeGWZU7E2ZxcN2a7o2Aw+GXHHoRuuZ3W7TMcb+4QKBgQCdHTssPHKm+nJRcRw/akgfYckCtaTwPdGxPffIPErfPhGry6asomzqTfuwvGl789MkirmOLH0npBZDDd/GUZLrxb+dFwLN4BCyDnZsohYjbpWAAojOhO6R7i62j4v7+RemP5AjWpui/6QQdmsR8pJTFYsDLJXQKz6lGUVix7jR2QKBgQCztD1gFG2rMVvSiXqMbGfqjAvZjVygzBvpTidRB/ZKPcfXSdhrvEv1pYmrYoBjQ/oBM2HncyI7EsQsIed17n8r9tart4VMGCQfpQw3mneq4TZIjzqkpYP0qLY+mDS9v8l+FotRfVQtyOnff6xsTnWw3S9fTF5oSe5Yc9dnpxCCAw==
-----END PRIVATE KEY-----`
CreateOrderUrl = "https://221.179.11.204/eaop/rest/BSS/commodity/create_productorder_ckcommid/v1.1.1"
ApplySmsCodeUrl = "https://221.179.11.204/eaop/rest/BSS/service/smscodeapply/v1.1.1"
CheckSmsCodeCommitOrderUrl = "https://221.179.11.204/eaop/rest/BSS/commodity/smscodechkodcommitorder/v1.1.1"
QueryOrderUrl = "https://221.179.11.204/eaop/rest/BSS/commodity/odqryorderbaseinfo_orderid/v1.1.1"
)
var GdYdCodeMessageMap = map[string]string{
"0": "成功",
"500": "数据错误",
"600": "业务失败",
"999": "系统内部失败",
"1009999999": "默认异常编码",
"1009999000": "系统内部错误",
"1000010001": "请求IP缺失",
"1000010002": "请求IP在黑名单中",
"1000010003": "请求IP不在白名单中",
"1000010009": "解析能力请求报文出错",
"1000010010": "缺失APPID参数",
"1000010011": "应用不存在",
"1000010012": "应用状态不合法",
"1000010013": "应用关联不到有效商户",
"1000010031": "能力不存在",
"1000010035": "传入参数错误",
"1000010043": "缺少签名参数",
"1000010047": "数字签名验证不通过",
"1000010049": "时间戳和系统时间隔太大",
"1000010060": "无访问能力权限",
"1000020001": "流量控制超流量后停止服务",
"1000020002": "配额控制未找到配额规则",
"1000020003": "配额控制超配额后停止服务",
"1000030001": "能力不支持此协议",
"1000030002": "能力不支持此报文格式",
"1000030004": "能力参数未配置",
"1000030005": "请求参数为空",
"1000030007": "缺少必选参数",
"1000030009": "非法的数据格式,参数值与字段类型定义不符",
"1000030015": "获取服务参数信息失败",
"1000030016": "服务返回报文格式错误",
"1000030017": "参数模糊化错误",
"1000040001": "模拟应答编码缺失",
"1000040002": "环境标识不合法",
"1000050001": "服务流程模板调用出错",
"1000050002": "能力回调出错",
"1000050003": "反向能力调用出错",
"1000050004": "服务调用超时",
"1000050005": "调用服务失败",
"1000050006": "调用服务返回结果不完整",
"1000050007": "调用服务返回结果为空",
"1000050008": "根据服务id获取流程模板出错",
"1000050010": "本地自服务调用失败",
"1000060001": "获取路由关键信息失败",
"1000060002": "服务路由失败",
}
// CommonParams 公共参数
type CommonParams struct {
AppID string `json:"appId"` // 必选,开放平台分配给应用的唯一标识
Timestamp string `json:"timestamp"` // 必选请求时间戳格式yyyyMMddHHmmssSSS需重新生成服务器时间差不得超5分钟
BusiSerial string `json:"busiSerial"` // 必选32位UUID去掉4个减号作为请求流水号
Sign string `json:"sign"` // 必选,数字签名
Nonce string `json:"nonce"` // 必选32位数字+大写字母组成的随机字符串
AuthCode string `json:"authCode,omitempty"` // 可选,用户授权调用的验证码
OperatorID string `json:"operatorid,omitempty"` // 可选,操作员工号
ComFlowCode string `json:"comflowcode,omitempty"` // 可选,通信流程编号
InstanceID string `json:"instanceid,omitempty"` // 可选,流程实例唯一标识
RouteType string `json:"route_type,omitempty"` // 可选路由类型0=地市1=号码
RouteValue string `json:"route_value,omitempty"` // 可选,路由字段值(手机号码或区号)
UnitID string `json:"unitid,omitempty"` // 可选子渠道ID
}
// GenerateSignature 通用签名函数
func GenerateSignature(publicParams map[string]string, bizJson string, privateKeyPEM string) (string, error) {
// 删除 sign 字段(如果存在)
delete(publicParams, "sign")
// 1. 排序参数
keys := make([]string, 0, len(publicParams))
for k := range publicParams {
keys = append(keys, k)
}
sort.Strings(keys)
// 2. 拼接参数
var sb strings.Builder
for i, k := range keys {
if i > 0 {
sb.WriteString("&")
}
sb.WriteString(fmt.Sprintf("%s=%s", k, publicParams[k]))
}
sb.WriteString(bizJson)
signContent := sb.String()
// 3. SHA256withRSA签名
signature, err := rsaSignSHA256([]byte(signContent), privateKeyPEM)
if err != nil {
return "", err
}
return signature, nil
}
// 签名逻辑SHA256withRSA + Base64
func rsaSignSHA256(data []byte, privateKeyPEM string) (string, error) {
block, _ := pem.Decode([]byte(privateKeyPEM))
if block == nil {
return "", fmt.Errorf("invalid private key PEM")
}
priv, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return "", fmt.Errorf("parse private key error: %v", err)
}
rsaPriv, ok := priv.(*rsa.PrivateKey)
if !ok {
return "", fmt.Errorf("not RSA private key")
}
hash := sha256.Sum256(data)
signature, err := rsa.SignPKCS1v15(rand.Reader, rsaPriv, crypto.SHA256, hash[:])
if err != nil {
return "", fmt.Errorf("sign error: %v", err)
}
return base64.StdEncoding.EncodeToString(signature), nil
}
// CommonResponse 通用返回结构体
type CommonResponse struct {
RespCode string `json:"respcode"`
RespDesc string `json:"respdesc"`
RespType string `json:"resptype"`
Result json.RawMessage `json:"result,omitempty"`
}
// 32位由数字+大写字母组成的随机字符串,每次请求必须重新生成
func generateNonce(n int) string {
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, n)
for i := range b {
b[i] = charset[int(time.Now().UnixNano()+int64(i))%len(charset)]
}
return string(b)
}
// 请求报文流水号(UUID去掉其中的四个减号)
func generateUUID() string {
return strings.ReplaceAll(uuid.New().String(), "-", "")
}
// DoSecurePostRequest 发送签名后的通用 POST 请求
func DoSecurePostRequest(url, phone string, bizPayload interface{}) (*CommonResponse, error) {
fmt.Println("enter DoSecurePostRequest")
fmt.Println("请求地址(URL):", url)
// Marshal 业务参数
bizJSON, err := json.Marshal(bizPayload)
if err != nil {
return nil, fmt.Errorf("marshal biz payload failed: %w", err)
}
fmt.Println("请求体(业务参数 bizPayload):", string(bizJSON))
// 生成公共参数
publicParams := map[string]string{
"appId": AppId,
"busiSerial": generateUUID(),
"nonce": generateNonce(32),
"route_value": phone,
"route_type": "1",
"timestamp": time.Now().Format("20060102150405") + fmt.Sprintf("%03d", time.Now().Nanosecond()/1e6), // 精确到毫秒
}
fmt.Println("publicParams is:", publicParams)
// 生成签名
signature, err := GenerateSignature(publicParams, string(bizJSON), PrivateKeyPEM)
if err != nil {
return nil, fmt.Errorf("sign failed: %w", err)
}
fmt.Println("signature is:", signature)
// 添加签名
publicParams["sign"] = signature
// 准备请求
req, err := http.NewRequest("POST", url, bytes.NewBuffer(bizJSON))
if err != nil {
return nil, fmt.Errorf("create request failed: %w", err)
}
req.Header.Set("Content-Type", "application/json")
// 设置公共参数为 Header
for k, v := range publicParams {
req.Header.Set(k, v)
}
// 打印请求头
fmt.Println("请求头信息:")
for k, v := range req.Header {
fmt.Printf(" %s: %s\n", k, strings.Join(v, ","))
}
// 忽略 TLS 校验
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{
Timeout: 10 * time.Second,
Transport: tr,
}
// 发送请求
//client := http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("http request failed: %w", err)
}
defer resp.Body.Close()
// 读取响应
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read response failed: %w", err)
}
fmt.Println("响应内容(Response Body):", string(body))
// 解析返回体
var result CommonResponse
if err = json.Unmarshal(body, &result); err != nil {
fmt.Println("unmarshal response failed, err is:", err.Error())
return nil, fmt.Errorf("unmarshal response failed: %w", err)
}
fmt.Println("leave DoSecurePostRequest")
return &result, nil
}
// CreateProductOrderCKCommIDReq 商品订单生成 - 请求结构体JSON
type CreateProductOrderCKCommIDReq struct {
UserInfo struct {
ServerNum string `json:"servernum"` // 手机号码
Area string `json:"area,omitempty"` // 地市
Brand string `json:"brand,omitempty"` // 品牌
} `json:"userinfo"`
MoreCommOrder string `json:"morecommorder"` // 多商品订购0 或 1
ProductInfo struct {
ProductID string `json:"productid"` // 产品编号
ProductGroup string `json:"productgroup"` // 产品组
ProductType string `json:"producttype"` // 产品类别
ProductName string `json:"productname,omitempty"` // 产品名称
OrderType string `json:"ordertype"` // 订购类型1=办理 2=取消 3=修改
} `json:"productinfo"`
}
// CreateProductOrderCKCommIDResp 商品订单生成 - 响应结构体
type CreateProductOrderCKCommIDResp struct {
MsgBody struct {
OrderInfo struct {
OrderID string `json:"orderid"` // CRM订单号
OrderDQ string `json:"orderdq"` // 电渠订单号
} `json:"orderinfo"`
} `json:"msgbody"`
}
// SendCreateProductOrderCKCommIDRequest 商品订单生成请求接口
func SendCreateProductOrderCKCommIDRequest(payload *CreateProductOrderCKCommIDReq) (*CreateProductOrderCKCommIDResp, error) {
// 发送通用 POST 请求
commonResp, err := DoSecurePostRequest(CreateOrderUrl, payload.UserInfo.ServerNum, payload)
if err != nil {
return nil, err
}
// 检查返回是否成功
if commonResp == nil || commonResp.Result == nil {
fmt.Println("commonResp == nil || commonResp.Result == nil")
return nil, errors.New(fmt.Sprintf("%s", commonResp.RespDesc))
}
if commonResp.RespCode != "0" {
return nil, errors.New(fmt.Sprintf("%s", commonResp.RespDesc))
}
// 反序列化 Data 字段为目标响应结构
var resp CreateProductOrderCKCommIDResp
dataBytes, err := json.Marshal(commonResp.Result) // 先转成 JSON 字节
if err != nil {
fmt.Println("marshal CommonResponse.Data failed, err is:", err)
return nil, fmt.Errorf("marshal CommonResponse.Data failed: %v", err)
}
err = json.Unmarshal(dataBytes, &resp)
if err != nil {
fmt.Println("unmarshal to CreateProductOrderCKCommIDResp failed, err is:", err)
return nil, fmt.Errorf("unmarshal to CreateProductOrderCKCommIDResp failed: %v", err)
}
return &resp, nil
}
// ApplySmsCodeReq 短信验证码申请 - 请求结构体
type ApplySmsCodeReq struct {
Rectype string `json:"rectype,omitempty"` // 可选
OrderID string `json:"orderid"` // 固定为 "0" 时表示仅申请验证码
MobileNo string `json:"mobileno"` // 手机号
RectypeExtInfoList []ApplySmsCodeExtAttr `json:"rectypeextinfolist,omitempty"` // 可选
}
// ApplySmsCodeExtAttr 扩展属性
type ApplySmsCodeExtAttr struct {
ExtAttrID string `json:"extattrid,omitempty"`
ExtAttrValue string `json:"extattrvalue,omitempty"`
}
// ApplySmsCodeResp 短信验证码申请 - 响应结构体
type ApplySmsCodeResp struct {
MsgBody struct {
SmsSeq string `json:"smsseq"` // 短信流水
} `json:"msgbody"`
}
func SendApplySmsCodeRequest(payload *ApplySmsCodeReq) (*ApplySmsCodeResp, error) {
resp, err := DoSecurePostRequest(ApplySmsCodeUrl, payload.MobileNo, payload)
if err != nil {
return nil, err
}
if resp == nil || resp.Result == nil {
return nil, errors.New("empty response from smscodeapply")
}
var result ApplySmsCodeResp
raw, err := json.Marshal(resp.Result)
if err != nil {
return nil, fmt.Errorf("marshal ApplySmsCodeResp.Data failed: %v", err)
}
if err := json.Unmarshal(raw, &result); err != nil {
return nil, fmt.Errorf("unmarshal to ApplySmsCodeResp failed: %v", err)
}
return &result, nil
}
// CheckSmsCodeAndCommitOrderReq 短信验证及订单提交 - 请求结构体
type CheckSmsCodeAndCommitOrderReq struct {
OrderID string `json:"orderid"` // 订单编码
ServerNum string `json:"servernum"` // 手机号
SmsSeq string `json:"smsseq"` // 短信流水
SmsCode string `json:"smscode"` // 短信验证码
}
// CheckSmsCodeAndCommitOrderResp 响应结构体
type CheckSmsCodeAndCommitOrderResp struct {
Code int `json:"code"`
Message string `json:"message"`
}
func SendCheckSmsCodeAndCommitOrderRequest(payload *CheckSmsCodeAndCommitOrderReq) (*CheckSmsCodeAndCommitOrderResp, error) {
resp, err := DoSecurePostRequest(CheckSmsCodeCommitOrderUrl, payload.ServerNum, payload)
if err != nil {
return nil, err
}
if resp == nil || resp.Result == nil {
return nil, errors.New("empty response from CheckSmsCodeAndCommitOrder")
}
var result CheckSmsCodeAndCommitOrderResp
raw, err := json.Marshal(resp.Result)
if err != nil {
return nil, fmt.Errorf("marshal response data failed: %v", err)
}
if err := json.Unmarshal(raw, &result); err != nil {
return nil, fmt.Errorf("unmarshal to CheckSmsCodeAndCommitOrderResp failed: %v", err)
}
return &result, nil
}
// QueryOrderReq 查询订单状态 - 请求结构体
type QueryOrderReq struct {
ServerNum string `json:"servernum"` // 手机号
OrderID string `json:"orderid"` // CRM订单编码
}
// QueryOrderResp 查询订单状态 - 响应结构体
type QueryOrderResp struct {
MsgBody struct {
OrderInfoList []struct {
OrderID string `json:"orderid"` // 订单编号
RecDate string `json:"recdate"` // 订单产生时间
Status string `json:"status"` // 订单状态码
} `json:"orderinfolist"`
} `json:"msgbody"`
}
func SendQueryOrderRequest(payload *QueryOrderReq) (*QueryOrderResp, error) {
resp, err := DoSecurePostRequest(QueryOrderUrl, payload.ServerNum, payload)
if err != nil {
return nil, err
}
if resp == nil || resp.Result == nil {
return nil, errors.New("empty response from QueryOrder")
}
var result QueryOrderResp
raw, err := json.Marshal(resp.Result)
if err != nil {
return nil, fmt.Errorf("marshal response data failed: %v", err)
}
if err := json.Unmarshal(raw, &result); err != nil {
return nil, fmt.Errorf("unmarshal to QueryOrderResp failed: %v", err)
}
return &result, nil
}