package lakala import ( "bytes" "crypto" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "errors" "fmt" "git.rosy.net.cn/baseapi/utils" "io" "io/ioutil" "math/big" "mime/multipart" "net/http" "strings" "time" ) // IncomingToken 进件api,token获取 func (a *API) IncomingToken() (comingToken string, expiresIn int64, err error) { result, err := a.AccessAPI(IncomingUrlProd, TokenActive, http.MethodPost, "", map[string]interface{}{ "grant_type": "client_credentials", "client_id": a.clientId, "client_secret": a.clientSecret, }) if err != nil { return "", 0, err } expiresIn, err = utils.TryInterface2Int64(result["expires_in"]) if err != nil { return "", 0, err } a.incomingToken = result["access_token"].(string) a.incomingExpire = time.Now().Unix() + expiresIn return a.incomingToken, a.incomingExpire, nil } // ModifiedToken 更新token获取 func (a *API) ModifiedToken(userName string, password string) (modifiedToken string, modifiedExpire int64, err error) { result, err := a.AccessAPI(ModifiedUrlProd, TokenActive, http.MethodPost, "", map[string]interface{}{ "grant_type": "password", "username": userName, "password": password, }) if err != nil { return "", 0, err } modifiedExpire, err = utils.TryInterface2Int64(result["expires_in"]) if err != nil { return "", 0, err } a.modifiedToken = result["access_token"].(string) a.modifiedExpire = time.Now().Unix() + modifiedExpire return a.modifiedToken, a.modifiedExpire, nil } // FileUpload 文件上传下载 filePath:文件url,由于controller:swagger不支持文件类型 func (a *API) FileUpload(filePath, imgType, sourcechnl, isOcr string) (*UploadImgResp, error) { client := &http.Client{} // 获取问价 resp, err := client.Get(filePath) if err != nil { return nil, err } imgData, _ := ioutil.ReadAll(resp.Body) defer resp.Body.Close() // 创建一个缓冲区来构建 multipart 请求 body := &bytes.Buffer{} writer := multipart.NewWriter(body) // 添加文件到 multipart 请求 part, err := writer.CreateFormFile("file", filePath) if err != nil { return nil, err } _, err = io.Copy(part, strings.NewReader(string(imgData))) if err != nil { return nil, err } // 添加其他表单字段 writer.WriteField("imgType", imgType) writer.WriteField("sourcechnl", sourcechnl) writer.WriteField("isOcr", isOcr) // 关闭 writer err = writer.Close() if err != nil { return nil, err } // 创建 HTTP 请求 req, err := http.NewRequest("POST", fmt.Sprintf("%s/%s", BaseProdUrl, FileUpload), body) if err != nil { return nil, err } // 设置 Content-Type 头 a.CheckToken() req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Authorization", fmt.Sprintf("bearer %s", a.incomingToken)) // 发送请求 resp, err = client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() // 读取响应 data, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } img := &UploadImgResp{} json.Unmarshal(data, img) return img, err } type UploadImgResp struct { BatchNo interface{} `json:"batchNo"` // 批量号 Status string `json:"status"` // 00 成功, 01 正在处理中, 02 失败 Url string `json:"url"` // 拉卡拉文件地址 ShowUrl string `json:"showUrl"` // 拉卡拉文件url Result interface{} `json:"result"` // } // AttachmentUpload 附件上传 func (a *API) AttachmentUpload(param *AttachmentImg) (*AttachmentImgResp, error) { reqParameter := map[string]interface{}{ "reqData": param, "ver": Version, "timestamp": utils.Int64ToStr(time.Now().Unix()), "reqId": utils.GetUUID(), } result, err := a.AccessAPISign(SeparateAccountProdUrl, AttachmentUpload, http.MethodPost, "", reqParameter) if err != nil { return nil, err } if result["retCode"].(string) != Success { return nil, errors.New(result["retMsg"].(string)) } bodyResult, err := json.Marshal(result["respData"]) if err != nil { return nil, err } attachment := &AttachmentImgResp{} if err = json.Unmarshal(bodyResult, attachment); err != nil { return nil, err } return attachment, nil } // AttachmentImg 进件附件上传 type AttachmentImg struct { Version string `json:"version"` OrderNo string `json:"orderNo"` OrgCode string `json:"orgCode"` AttType string `json:"attType"` AttExtName string `json:"attExtName"` AttContext string `json:"attContext"` } // AttachmentImgResp 进件返回值 type AttachmentImgResp struct { AttType string `json:"attType"` OrderNo string `json:"orderNo"` OrgCode string `json:"orgCode"` AttFileId string `json:"attFileId"` } // signParamRSA 支付签名 func (a *API) signParamPrivateKey(params map[string]interface{}, RSAPrivate string) (sig string, err error) { block, _ := pem.Decode([]byte(RSAPrivate)) private, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { return "", err } // 签名参数 body, err := json.Marshal(params) if err != nil { return "", err } //bodyData := base64.StdEncoding.EncodeToString(body) nonceStr := GenerateSecureRandomString(12) timeStamp := utils.Int64ToStr(time.Now().Unix()) context := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n", a.appID, a.serialNo, timeStamp, nonceStr, string(body)) // 进行rsa加密签名 hashed := sha256.Sum256([]byte(context)) signedData, err := rsa.SignPKCS1v15(rand.Reader, private.(*rsa.PrivateKey), crypto.SHA256, hashed[:]) if err != nil { return "", err } signData := base64.StdEncoding.EncodeToString(signedData) authorization := fmt.Sprintf(`LKLAPI-SHA256withRSA appid="%s",serial_no="%s",timestamp="%s",nonce_str="%s",signature="%s"`, a.appID, a.serialNo, timeStamp, nonceStr, signData) return authorization, nil } // GenerateSecureRandomString 获取随机字符串 func GenerateSecureRandomString(length int) string { charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" bytes := make([]byte, length) charSetSize := big.NewInt(int64(len(charset))) for i := range bytes { num, _ := rand.Int(rand.Reader, charSetSize) bytes[i] = charset[num.Int64()] } return string(bytes) } func GetOrderNumber(length int) string { charset := "0123456789" bytes := make([]byte, length) charSetSize := big.NewInt(int64(len(charset))) for i := range bytes { num, _ := rand.Int(rand.Reader, charSetSize) bytes[i] = charset[num.Int64()] } return utils.Time2TimeStrByFormat(time.Now(), TimeFormat) + string(bytes) } type CallBackResult struct { Code string `json:"code"` Message string `json:"message"` } // CallBackResultInfo 失败回调返回 func CallBackResultInfo(err error) *CallBackResult { if err == nil { return &CallBackResult{ Code: "SUCCESS", Message: "执行成功", } } return &CallBackResult{ Code: "400", Message: err.Error(), } } /****************************/ const ( MAX_ENCRYPT_BLOCK = 117 MAX_DECRYPT_BLOCK = 128 ) func EncryptByPrivateKey(data []byte, privateKeyStr string) (string, error) { // 解析私钥 block, _ := pem.Decode([]byte(privateKeyStr)) if block == nil { return "", errors.New("failed to parse PEM block") } privKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { return "", err } rsaPrivateKey := privKey.(*rsa.PrivateKey) // 分段加密 var encrypted []byte for offset := 0; offset < len(data); offset += MAX_ENCRYPT_BLOCK { end := offset + MAX_ENCRYPT_BLOCK if end > len(data) { end = len(data) } chunk, err := rsa.SignPKCS1v15(nil, rsaPrivateKey, 0, data[offset:end]) if err != nil { return "", err } encrypted = append(encrypted, chunk...) } return base64.StdEncoding.EncodeToString(encrypted), nil } // DecryptByPublicKey 解密函数:分段解密(公钥解密,需手动实现模幂运算) func DecryptByPublicKey(ciphertext []byte, publicKeyStr string) ([]byte, error) { // 解析公钥 block, _ := pem.Decode([]byte(publicKeyStr)) if block == nil { return nil, errors.New("failed to parse PEM block") } pubKey, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, err } rsaPublicKey := pubKey.(*rsa.PublicKey) n := rsaPublicKey.N // 公钥模数 e := rsaPublicKey.E // 公钥指数 blockSize := rsaPublicKey.Size() // 密钥的字节长度(128字节) var result bytes.Buffer for len(ciphertext) > 0 { blockLen := blockSize if len(ciphertext) < blockLen { blockLen = len(ciphertext) } block := ciphertext[:blockLen] ciphertext = ciphertext[blockLen:] // 将密文块转换为大整数 c := new(big.Int).SetBytes(block) // 计算 m = c^e mod n (由于私钥加密,公钥解密时需要e和n) m := new(big.Int).Exp(c, big.NewInt(int64(e)), n) // 将结果转换为字节数组,并补零到固定长度 mBytes := m.Bytes() if len(mBytes) < blockSize { padding := make([]byte, blockSize-len(mBytes)) mBytes = append(padding, mBytes...) } // 写入最终结果 result.Write(mBytes) } // 移除PKCS1v15填充(需要实现填充剥离) // 注意:此处简化,实际需解析填充结构,比如去除00 02和填充字节 return stripPKCS1v15Padding(result.Bytes()), nil } // 移除PKCS1v15填充(示例逻辑,需根据实际填充调整) func stripPKCS1v15Padding(data []byte) []byte { var newData = make([]byte, 0, len(data)) for i := 0; i < len(data); i++ { if data[i] == 0x01 || data[i] == 0xFF || data[i] == 0x00 { continue } newData = append(newData, data[i]) } return newData }