360 lines
9.4 KiB
Go
360 lines
9.4 KiB
Go
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
|
||
}
|