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 }