@@ -0,0 +1,28 @@ | |||||
package boc | |||||
const Version = "2.0" | |||||
const SignType = "RSA2" | |||||
//service类型 | |||||
const CreateBocCashier = "CreateBocCashier" //【中銀收銀台】創建支付交易 | |||||
const CreateAASAppTrade = "CreateAASAppTrade" //【中銀收银台】聚合APP支付統一下單 | |||||
const CardBinding = "CardBinding " //【商戶收銀台】银行卡綁定 | |||||
const CardBindingQueryList = "CardBindingQueryList " //【商戶收銀台】查询绑定银行卡列表 | |||||
const RemoveBankCard = "removeBankCard " //【商戶收銀台】移除綁定銀行卡 | |||||
const CardBindingNotify = "CardBindingNotify " //【商戶收銀台】银行卡绑定结果后台通知 | |||||
const CreateEMVTrade = "CreateEMVTrade " //【商戶收銀台】創建EMV二维码支付交易 | |||||
const CreateCardTrade = "CreateCardTrade " //【商戶收銀台】創建银行卡交易 | |||||
const CreateQRTrade = "CreateQRTrade " //【商戶收銀台】創建聚合碼支付交易 | |||||
const CreateWeChatTrade = "CreateWeChatTrade" //【商户收银台】微信公众号/小程序支付统一下单 | |||||
const CreateBocPayAppTrade = "CreateBocPayAppTrade" //【商户收银台】手機銀行APP支付統一下單 | |||||
const CreateWeChatAppTrade = "CreateWeChatAppTrade" //【商户收银台】微信APP支付統一下單 | |||||
const CreateAlipayAppTrade = "CreateAlipayAppTrade" //【商户收银台】支付寶APP支付統一下單 | |||||
const OrderRefund = "OrderRefund " //【基礎服務】退款 | |||||
const OrderQuery = "OrderQuery " //【基礎服務】訂單查詢 | |||||
const OfflineResult = "OfflineResult " //【基礎服務】支付結果後台通知 | |||||
const PageResult = "PageResult " //【基礎服務】支付結果頁面通知 | |||||
const Statement = "Statement " //【基礎服務】對賬文件下載 | |||||
const Settlement = "Settlement " //【基礎服務】結算單下載 | |||||
const OrderRelationQuery = "OrderRelationQuery " //【基礎服務】訂單关联查詢 |
@@ -1,12 +1,26 @@ | |||||
package boc | package boc | ||||
import ( | import ( | ||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" | |||||
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | ||||
"fmt" | "fmt" | ||||
"github.com/syyongx/php2go" | |||||
"time" | |||||
) | ) | ||||
func Send(prd bool, appId, privateKeyStr string, param map[string]string) (string, error) { | |||||
data := zhios_pay_utils.JoinStringsInASCII(param, "", "", false, false) | |||||
const ( | |||||
MerchantId = "888007050019400" | |||||
TerminalNo = "98036140" | |||||
PrivateKeyStr = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCSJk259Gd9IuvG\nRxYOma5vjo0+x2CTyP7m3uJ/epf+mwp/4XySbjVharBFmKk0XoSnZz/X/cYOgQXH\nPWPnBd0Y4qTl3xVf+DdZHF035KNG0LJPqvzqOSCuD5w32BHN0o9wFaR75PbWfsUl\nn2XqLVRFdv3IaKJYVWb8fM7VLT0aA+Rk81GXGfAbmXzNqX3lbOkqlTcBdzMHdE94\nqpAxtppVJ8JQqRJsWwzqJHo2koN+/kb3vL9IxeABJGUgU4Fy53MuC7JcvdYUoE7V\n92eD6A9g6ctSHP5y7olx9T7uMAaHk/TDv3+QNk6rI54cif7V7C5ykhtH5ZfHQ27C\nx929l7LZAgMBAAECggEAVZfVQajfyyvehbswMdDTlkYYdsHxz9uRXcj/QcWcquJG\nXerDOEcPQERpg/kKb79DVX9kpqzbh7cMmUlc7vpCKMbj6DSkohYO5EN2XLus3FZx\nd7MeeFaIyygdR59lnfNJSk2CjYTlA24vK/+UgneVuTEdJHgTTESIEitOPw9Fj6v+\n/+vRwuT6PVDm1FDUxFcYmJYzdMwJZoH0wDymofiEdn6beaHkqDXakrdY307cHOlq\ncrVQJKSybLwcBsbk9pgGrh6lCQLtQ8pHNtf6rE5FkEesO/VmdRFDGAK/9cuXEJ7u\nICdSESdfvnKl9BeN3MzSaeDSnh2IPoz13VVxXuJF+QKBgQDfHBVA/e1XFrLDM5IW\nO6nIxZeeZSufITDKHi7qRoS2mlOwjpMoIVAHCxlpnWd89YQ7NPdScEi6Z7l2sEET\n0vKfWsJ3ltowj5QUXS0XuJeAhkmK66lBNrICkatoAGeWxDrraCgkqLPNe40ydQ6h\nT2c3nR8tf++U2TzCpCXxzj6pgwKBgQCnsdTIWKwhRIltTgEzXTh3+JMQ36MH3sVt\nP14bxRN1hlR59yQvx0IEToXOHASSRU0fWxj3H8UewobYmE+2Ro+qNHFY7Do7Ypum\nIM7In00Tx4CKOVOkMZtNtR9hPk2/JUKpPSQBl7dSysBDngAwGhBhpwutWDpEiIfL\n+P4pAFuvcwKBgDrPUT7ncDdjkU1o+5oGshNtGjEzY9M3UYAFgsJYQkVL2sGQY3PR\njCG/KDyxtAS5RtjAdmzxkgooqzeA8DaEhVXB+77AMdVZE2RmxysYij7jpuEKi3vY\nKgYJVdA9QxD3urwwoVxAZ9zDM43YUznsDz4WmZBJx+JJfP2lfScHAXd3AoGAMpRR\noKgWKuaYKM7JT+vDF3RzLzi03K/VjnD5epjDmMQOgMcJNBv+jnv8ocETo+ahL17z\ntb3wX6m+sF+oU0sPFABDW9XMbpL8bWwh0qMxxVB8NbS6xKBtvk9uCo+XNZsQcylM\nzeor0h3zRbHHTrrdzAZtYDhnQrqHcv9D8av4+38CgYEAgyn1h+EompwBF4hKo6Dx\nVL/ZZYcE1E+XMzmHvP8C1sOVBSPZuCAnCx43cZiJZ94/5v8V3+4nKuWbeN1ar9BK\nXXSAVkk21qM8cv2IoDwi/DdBLXD302KMReAOWfJnQ+sbU1v2B2VPJoCwd4lSqauj\nVE4PCFhhrsou1VM1XbP8XsM=\n-----END PRIVATE KEY-----" | |||||
) | |||||
func Send(prd bool, privateKeyStr string, param map[string]string) (string, error) { | |||||
for key, value := range param { | |||||
if value == "" { | |||||
delete(param, key) | |||||
} | |||||
} | |||||
data := zhios_pay_utils.JoinStringsInASCII(param, "", "", true, false) | |||||
sign, err := zhios_pay_utils.GetSign(privateKeyStr, data) | sign, err := zhios_pay_utils.GetSign(privateKeyStr, data) | ||||
if err != nil { | if err != nil { | ||||
return "", err | return "", err | ||||
@@ -19,12 +33,68 @@ func Send(prd bool, appId, privateKeyStr string, param map[string]string) (strin | |||||
"Content-Type": "application/json", | "Content-Type": "application/json", | ||||
} | } | ||||
param["merchantSign"] = sign | param["merchantSign"] = sign | ||||
fmt.Println(zhios_pay_utils.SerializeStr(param)) | |||||
fmt.Println(url) | |||||
res, err := zhios_pay_utils.CurlPost(url, param, headers) | |||||
fmt.Println("参数:", zhios_pay_utils.SerializeStr(param)) | |||||
zhios_pay_utils.CurlDebug = true | |||||
res, err := zhios_pay_utils.CurlPost(url, zhios_pay_utils.SerializeStr(param), headers) | |||||
if err != nil { | if err != nil { | ||||
return "", err | return "", err | ||||
} | } | ||||
fmt.Println(string(res)) | fmt.Println(string(res)) | ||||
return string(res), nil | return string(res), nil | ||||
} | } | ||||
func CreateBocPayAppTradeReq(prd bool, privateKeyStr, merchantId, terminalNo string, createBocPayAppTradeReq *md.CreateBocPayAppTradeReq) (string, error) { | |||||
//基础信息,必填 | |||||
createBocPayAppTradeReq.Version = Version | |||||
createBocPayAppTradeReq.Service = CreateBocPayAppTrade | |||||
createBocPayAppTradeReq.SignType = SignType | |||||
createBocPayAppTradeReq.MerchantId = merchantId | |||||
createBocPayAppTradeReq.TerminalNo = terminalNo | |||||
createBocPayAppTradeReq.RequestId = createBocPayAppTradeReq.MercOrderNo + zhios_pay_utils.AnyToString(time.Now().Unix()) | |||||
//按照接口类型选填信息 | |||||
//createBocPayAppTradeReq.Amount = "100" | |||||
//createBocPayAppTradeReq.OriginalAmount = "100" | |||||
//createBocPayAppTradeReq.MercOrderNo = "2022101401" | |||||
//createBocPayAppTradeReq.OrderDate = "20221013" | |||||
//createBocPayAppTradeReq.OrderTime = "175600" | |||||
//createBocPayAppTradeReq.NotifyUrl = "/api/v1/mall/pay/ada/sub_callback" | |||||
//createBocPayAppTradeReq.Reserved1 = zhios_pay_utils.SerializeStr(map[string]string{ | |||||
// "mid":"123456", | |||||
//}) | |||||
param := make(map[string]string) | |||||
zhios_pay_utils.Unserialize(zhios_pay_utils.Serialize(createBocPayAppTradeReq), ¶m) | |||||
send, err := Send(prd, privateKeyStr, param) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
resp, _ := php2go.URLDecode(send) | |||||
return resp, nil | |||||
} | |||||
func CreateWeChatTradeReq(prd bool, privateKeyStr, merchantId, terminalNo string, createWeChatTradeReq *md.CreateWeChatTradeReq) (string, error) { | |||||
//基础信息,必填 | |||||
createWeChatTradeReq.Version = Version | |||||
createWeChatTradeReq.Service = CreateWeChatTrade | |||||
createWeChatTradeReq.SignType = SignType | |||||
createWeChatTradeReq.MerchantId = merchantId | |||||
createWeChatTradeReq.TerminalNo = terminalNo | |||||
createWeChatTradeReq.RequestId = createWeChatTradeReq.MercOrderNo + zhios_pay_utils.AnyToString(time.Now().Unix()) | |||||
//按照接口类型选填信息 | |||||
//createBocPayAppTradeReq.Amount = "100" | |||||
//createBocPayAppTradeReq.OriginalAmount = "100" | |||||
//createBocPayAppTradeReq.MercOrderNo = "2022101401" | |||||
//createBocPayAppTradeReq.OrderDate = "20221013" | |||||
//createBocPayAppTradeReq.OrderTime = "175600" | |||||
//createBocPayAppTradeReq.NotifyUrl = "/api/v1/mall/pay/ada/sub_callback" | |||||
//createBocPayAppTradeReq.Reserved1 = zhios_pay_utils.SerializeStr(map[string]string{ | |||||
// "mid":"123456", | |||||
//}) | |||||
param := make(map[string]string) | |||||
zhios_pay_utils.Unserialize(zhios_pay_utils.Serialize(createWeChatTradeReq), ¶m) | |||||
send, err := Send(prd, privateKeyStr, param) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
resp, _ := php2go.URLDecode(send) | |||||
return resp, nil | |||||
} |
@@ -44,3 +44,24 @@ type CreateBocPayAppTradeReq struct { | |||||
Reserved2 string `json:"reserved2" name:"商戶備用字段" label:"交易成功后原樣返回給商戶"` | Reserved2 string `json:"reserved2" name:"商戶備用字段" label:"交易成功后原樣返回給商戶"` | ||||
Reserved3 string `json:"reserved3" name:"商戶備用字段" label:"交易成功后原樣返回給商戶"` | Reserved3 string `json:"reserved3" name:"商戶備用字段" label:"交易成功后原樣返回給商戶"` | ||||
} | } | ||||
type CreateWeChatTradeReq struct { | |||||
MacaoBOCPublicParameter | |||||
Amount string `json:"amount" name:"订单金額" label:"以分爲單位,如1元表示爲100"` | |||||
OriginalAmount string `json:"originalAmount" name:"原订单金额" label:"以分爲單位,如1元表示爲100若无优惠请跟订单金额【amount】一致"` | |||||
MerchantPreferentialCnName string `json:"merchantPreferentialCnName" name:"商户系统优惠活动中文名称" label:"非必传"` | |||||
MerchantPreferentialEnName string `json:"merchantPreferentialEnName" name:"商户系统优惠活动英文名称" label:"非必传"` | |||||
Subject string `json:"subject" name:"訂單標題" label:"非必传,用戶支付完成後顯示在手機上的訂單名稱"` | |||||
ProductDesc string `json:"productDesc" name:"商品描述" label:"非必传,對一筆交易的具體描述信息,如果是多種商品,請將商品描述字符串累加傳給body。特殊字符不支持"` | |||||
MercOrderNo string `json:"mercOrderNo" name:"商戶系統消費訂單號" label:"商戶系統必須確保該訂單號在商戶系統是唯一的不能包含單引號“’”、尖括號“<”“>”和逗號“,”"` | |||||
OrderDate string `json:"orderDate" name:"訂單創建日期" label:"格式:yyyyMMdd"` | |||||
OrderTime string `json:"orderTime" name:"訂單創建時間" label:"格式:HHmmss"` | |||||
ValidNumber string `json:"validNumber" name:"有效期" label:"單位:秒 默認值1200"` | |||||
NotifyUrl string `json:"notifyUrl" name:"支付結果後台通知地址" label:"支付通知地址(后缀),商户在商戶服務管理後台配置好通知地址(域名)后,聚合平台会将域名和后缀拼接组成完整的通知地址,并在订单支付成后向此通知地址发送支付结果通知。"` | |||||
SubAppId string `json:"subAppId" name:"子商戶AppId" label:"微信分配的子商户公众账号或小程序Id。"` | |||||
SubOpenId string `json:"subOpenId" name:"子商戶用戶唯一Id" label:"用戶在子商戶appId下的唯一標識。下單前商戶需要調用微信【網頁授權獲取用戶信息】接口獲取到用戶的OpenId。詳情查看附錄5.5.1章節"` | |||||
TransWay string `json:"transWay" name:"交易方式" label:"B3-公众号支付B4-小程序支付"` | |||||
Reserved1 string `json:"reserved1" name:"商戶備用字段" label:"交易成功后原樣返回給商戶"` | |||||
Reserved2 string `json:"reserved2" name:"商戶備用字段" label:"交易成功后原樣返回給商戶"` | |||||
Reserved3 string `json:"reserved3" name:"商戶備用字段" label:"交易成功后原樣返回給商戶"` | |||||
} |
@@ -157,3 +157,23 @@ func TestCardBindlist(t *testing.T) { | |||||
} | } | ||||
t.Log(zhios_pay_utils.SerializeStr(data)) | t.Log(zhios_pay_utils.SerializeStr(data)) | ||||
} | } | ||||
func TestPaymentQueryList(t *testing.T) { | |||||
Adapay, err := adapay.GetAdapayByDBSysConfig("jiuhuicang", "app_a794b158-359b-4e5a-8464-5bd8f171bb6e", "api_live_419ec107-9854-45e8-af12-070efcef6ae9", "api_test_d6485653-c09e-405f-b386-8ef2936dfb97", "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwN6xgd6Ad8v2hIIsQVnbt8a3JituR8o4Tc3B5WlcFR55bz4OMqrG/356Ur3cPbc2Fe8ArNd/0gZbC9q56Eb16JTkVNA/fye4SXznWxdyBPR7+guuJZHc/VW2fKH2lfZ2P3Tt0QkKZZoawYOGSMdIvO+WqK44updyax0ikK6JlNQIDAQAB", | |||||
"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMxVAwWM79+2Lx1TYA4muHy40AbwcBwXmvgNJGp2gq7bcDaNd5cmvXndwS6exV2hIGo8er0Qu3myGrwD/4BIoqCDSuqYfJkrlKzKM30+WAfwRulrFW+SOr6qCvGFF2bMdL884NMgd5PCpayASONq/blefQ3/Ic+c0gtZQxJzYzxLAgMBAAECgYEAyS+rGfv68zpeiXFeUpCtdaBCs5Jp0D8txq6p/GlTovdNNWl1Js5uIV/GpjCOA/JyCCgkcaPX86r5x8Xn/FeIRt/U1kkGs2zvKcxQntkOhmzMdDe7/FuwbRUUeM6LWGIc5AeQcBWYZeS8HAFMnlWho+knMqqgHC/fgUyon6YgMAECQQDyjJYSp+hdhTnvo88GPisQEPOoZRbrUnVFbwjD/OrQT7s6uSlbTAJRXGEYfdeaWsh2CoFl986pOqc/84E/Lr3LAkEA16nd1J3stk9XtC4i+iiXY0codiK24kU+pMcEm8NNyMV1nQT5iTcDA1ZeJCgamJZFeqcfyusimBNbCXdBKk2rgQJBAIymodAntkOlIjepEkBYhLhIXENme6fypTaicL7WR4SM99HR1f2vUhjELTn6n7BOvLhW1zq+PQU9kgcvud9dx4kCQFSMXtBFHZEXp/2WfNXv5fHg6sbtsx8gIH//GhpqxerpJsPpOF8H9yFu0beBFXQurYx5SqiF6GkQZYdffme0TYECQQDgOdQxysI1FEM/l8BDnQgmnO7sJqUtP9HsVcKElGdkJMZGqb8B5rt12y4dxpyVfyaTMZZZjqIx8zgQBhn+l0NJ") | |||||
if err != nil { | |||||
t.Error(err) | |||||
} | |||||
createPaymentParams := map[string]interface{}{ | |||||
"app_id": Adapay.AppId, | |||||
"page_index": "1", | |||||
"page_size": "10", | |||||
} | |||||
data, apiError, err := Adapay.Payment().QueryList(createPaymentParams) | |||||
if err != nil || apiError != nil { // 网络或本应用异常 | |||||
t.Error(err) | |||||
t.Error(apiError) | |||||
return | |||||
} | |||||
t.Log(zhios_pay_utils.SerializeStr(data)) | |||||
} |
@@ -1,9 +1,13 @@ | |||||
package test | package test | ||||
import ( | import ( | ||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/lib/boc" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" | |||||
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | ||||
"fmt" | "fmt" | ||||
"github.com/syyongx/php2go" | |||||
"testing" | "testing" | ||||
"time" | |||||
) | ) | ||||
func TestJoinStringsInASCII(t *testing.T) { | func TestJoinStringsInASCII(t *testing.T) { | ||||
@@ -11,3 +15,73 @@ func TestJoinStringsInASCII(t *testing.T) { | |||||
"", "", false, false) | "", "", false, false) | ||||
fmt.Println(ascii) | fmt.Println(ascii) | ||||
} | } | ||||
func TestBocSend(t *testing.T) { | |||||
var createBocPayAppTradeReq md.CreateBocPayAppTradeReq | |||||
//基础信息,必填 | |||||
createBocPayAppTradeReq.Version = boc.Version | |||||
createBocPayAppTradeReq.Service = boc.CreateBocPayAppTrade | |||||
createBocPayAppTradeReq.SignType = boc.SignType | |||||
createBocPayAppTradeReq.MerchantId = boc.MerchantId | |||||
createBocPayAppTradeReq.TerminalNo = boc.TerminalNo | |||||
createBocPayAppTradeReq.RequestId = "2022101401" + zhios_pay_utils.AnyToString(time.Now().Unix()) | |||||
//按照接口类型选填信息 | |||||
createBocPayAppTradeReq.Amount = "100" | |||||
createBocPayAppTradeReq.OriginalAmount = "100" | |||||
createBocPayAppTradeReq.MercOrderNo = "2022101401" | |||||
createBocPayAppTradeReq.OrderDate = "20221013" | |||||
createBocPayAppTradeReq.OrderTime = "175600" | |||||
createBocPayAppTradeReq.NotifyUrl = "/api/v1/mall/pay/ada/sub_callback" | |||||
createBocPayAppTradeReq.Reserved1 = zhios_pay_utils.SerializeStr(map[string]string{ | |||||
"mid": "123456", | |||||
}) | |||||
param := make(map[string]string) | |||||
zhios_pay_utils.Unserialize(zhios_pay_utils.Serialize(createBocPayAppTradeReq), ¶m) | |||||
send, err := boc.Send(false, boc.PrivateKeyStr, param) | |||||
if err != nil { | |||||
t.Error(err) | |||||
} | |||||
t.Logf("回调信息1:%s", send) | |||||
resp, _ := php2go.URLDecode(send) | |||||
t.Logf("回调信息:%s", resp) | |||||
} | |||||
func TestCreateBocPayAppTradeReq(t *testing.T) { | |||||
var createBocPayAppTradeReq md.CreateBocPayAppTradeReq | |||||
//按照接口类型选填信息 | |||||
createBocPayAppTradeReq.Amount = "100" | |||||
createBocPayAppTradeReq.OriginalAmount = "100" | |||||
createBocPayAppTradeReq.MercOrderNo = "2022101402" | |||||
createBocPayAppTradeReq.OrderDate = "20221013" | |||||
createBocPayAppTradeReq.OrderTime = "175600" | |||||
createBocPayAppTradeReq.NotifyUrl = "/api/v1/mall/pay/ada/sub_callback" | |||||
createBocPayAppTradeReq.Reserved1 = zhios_pay_utils.SerializeStr(map[string]string{ | |||||
"mid": "123456", | |||||
}) | |||||
send, err := boc.CreateBocPayAppTradeReq(false, boc.PrivateKeyStr, boc.MerchantId, boc.TerminalNo, &createBocPayAppTradeReq) | |||||
if err != nil { | |||||
t.Error(err) | |||||
} | |||||
t.Logf("回调信息:%s", send) | |||||
} | |||||
func TestCreateWeChatTradeReq(t *testing.T) { | |||||
var CreateWeChatTradeReqReq md.CreateWeChatTradeReq | |||||
//按照接口类型选填信息 | |||||
CreateWeChatTradeReqReq.Amount = "100" | |||||
CreateWeChatTradeReqReq.OriginalAmount = "100" | |||||
CreateWeChatTradeReqReq.MercOrderNo = "2022101403" | |||||
CreateWeChatTradeReqReq.OrderDate = "20221013" | |||||
CreateWeChatTradeReqReq.OrderTime = "175600" | |||||
CreateWeChatTradeReqReq.NotifyUrl = "/api/v1/mall/pay/ada/sub_callback" | |||||
CreateWeChatTradeReqReq.SubAppId = "wx987580a9437354d1" | |||||
CreateWeChatTradeReqReq.SubOpenId = "oEk7V5hNLxQXoTpOcvzRdGJQKnjM" | |||||
CreateWeChatTradeReqReq.TransWay = "B3" | |||||
CreateWeChatTradeReqReq.Reserved1 = zhios_pay_utils.SerializeStr(map[string]string{ | |||||
"mid": "123456", | |||||
}) | |||||
send, err := boc.CreateWeChatTradeReq(false, boc.PrivateKeyStr, boc.MerchantId, boc.TerminalNo, &CreateWeChatTradeReqReq) | |||||
if err != nil { | |||||
t.Error(err) | |||||
} | |||||
t.Logf("回调信息:%s", send) | |||||
} |
@@ -0,0 +1,341 @@ | |||||
package zhios_pay_utils | |||||
import ( | |||||
"encoding/json" | |||||
"fmt" | |||||
"reflect" | |||||
"strconv" | |||||
"strings" | |||||
) | |||||
func Map2Struct(vals map[string]interface{}, dst interface{}) (err error) { | |||||
return Map2StructByTag(vals, dst, "json") | |||||
} | |||||
func Map2StructByTag(vals map[string]interface{}, dst interface{}, structTag string) (err error) { | |||||
defer func() { | |||||
e := recover() | |||||
if e != nil { | |||||
if v, ok := e.(error); ok { | |||||
err = fmt.Errorf("Panic: %v", v.Error()) | |||||
} else { | |||||
err = fmt.Errorf("Panic: %v", e) | |||||
} | |||||
} | |||||
}() | |||||
pt := reflect.TypeOf(dst) | |||||
pv := reflect.ValueOf(dst) | |||||
if pv.Kind() != reflect.Ptr || pv.Elem().Kind() != reflect.Struct { | |||||
return fmt.Errorf("not a pointer of struct") | |||||
} | |||||
var f reflect.StructField | |||||
var ft reflect.Type | |||||
var fv reflect.Value | |||||
for i := 0; i < pt.Elem().NumField(); i++ { | |||||
f = pt.Elem().Field(i) | |||||
fv = pv.Elem().Field(i) | |||||
ft = f.Type | |||||
if f.Anonymous || !fv.CanSet() { | |||||
continue | |||||
} | |||||
tag := f.Tag.Get(structTag) | |||||
name, option := parseTag(tag) | |||||
if name == "-" { | |||||
continue | |||||
} | |||||
if name == "" { | |||||
name = strings.ToLower(f.Name) | |||||
} | |||||
val, ok := vals[name] | |||||
if !ok { | |||||
if option == "required" { | |||||
return fmt.Errorf("'%v' not found", name) | |||||
} | |||||
if len(option) != 0 { | |||||
val = option // default value | |||||
} else { | |||||
//fv.Set(reflect.Zero(ft)) // TODO set zero value or just ignore it? | |||||
continue | |||||
} | |||||
} | |||||
// convert or set value to field | |||||
vv := reflect.ValueOf(val) | |||||
vt := reflect.TypeOf(val) | |||||
if vt.Kind() != reflect.String { | |||||
// try to assign and convert | |||||
if vt.AssignableTo(ft) { | |||||
fv.Set(vv) | |||||
continue | |||||
} | |||||
if vt.ConvertibleTo(ft) { | |||||
fv.Set(vv.Convert(ft)) | |||||
continue | |||||
} | |||||
return fmt.Errorf("value type not match: field=%v(%v) value=%v(%v)", f.Name, ft.Kind(), val, vt.Kind()) | |||||
} | |||||
s := strings.TrimSpace(vv.String()) | |||||
if len(s) == 0 && option == "required" { | |||||
return fmt.Errorf("value of required argument can't not be empty") | |||||
} | |||||
fk := ft.Kind() | |||||
// convert string to value | |||||
if fk == reflect.Ptr && ft.Elem().Kind() == reflect.String { | |||||
fv.Set(reflect.ValueOf(&s)) | |||||
continue | |||||
} | |||||
if fk == reflect.Ptr || fk == reflect.Struct { | |||||
err = convertJsonValue(s, name, fv) | |||||
} else if fk == reflect.Slice { | |||||
err = convertSlice(s, f.Name, ft, fv) | |||||
} else { | |||||
err = convertValue(fk, s, f.Name, fv) | |||||
} | |||||
if err != nil { | |||||
return err | |||||
} | |||||
continue | |||||
} | |||||
return nil | |||||
} | |||||
func Struct2Map(s interface{}) map[string]interface{} { | |||||
return Struct2MapByTag(s, "json") | |||||
} | |||||
func Struct2MapByTag(s interface{}, tagName string) map[string]interface{} { | |||||
t := reflect.TypeOf(s) | |||||
v := reflect.ValueOf(s) | |||||
if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { | |||||
t = t.Elem() | |||||
v = v.Elem() | |||||
} | |||||
if v.Kind() != reflect.Struct { | |||||
return nil | |||||
} | |||||
m := make(map[string]interface{}) | |||||
for i := 0; i < t.NumField(); i++ { | |||||
fv := v.Field(i) | |||||
ft := t.Field(i) | |||||
if !fv.CanInterface() { | |||||
continue | |||||
} | |||||
if ft.PkgPath != "" { // unexported | |||||
continue | |||||
} | |||||
var name string | |||||
var option string | |||||
tag := ft.Tag.Get(tagName) | |||||
if tag != "" { | |||||
ts := strings.Split(tag, ",") | |||||
if len(ts) == 1 { | |||||
name = ts[0] | |||||
} else if len(ts) > 1 { | |||||
name = ts[0] | |||||
option = ts[1] | |||||
} | |||||
if name == "-" { | |||||
continue // skip this field | |||||
} | |||||
if name == "" { | |||||
name = strings.ToLower(ft.Name) | |||||
} | |||||
if option == "omitempty" { | |||||
if isEmpty(&fv) { | |||||
continue // skip empty field | |||||
} | |||||
} | |||||
} else { | |||||
name = strings.ToLower(ft.Name) | |||||
} | |||||
if ft.Anonymous && fv.Kind() == reflect.Ptr && fv.IsNil() { | |||||
continue | |||||
} | |||||
if (ft.Anonymous && fv.Kind() == reflect.Struct) || | |||||
(ft.Anonymous && fv.Kind() == reflect.Ptr && fv.Elem().Kind() == reflect.Struct) { | |||||
// embedded struct | |||||
embedded := Struct2MapByTag(fv.Interface(), tagName) | |||||
for embName, embValue := range embedded { | |||||
m[embName] = embValue | |||||
} | |||||
} else if option == "string" { | |||||
kind := fv.Kind() | |||||
if kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64 { | |||||
m[name] = strconv.FormatInt(fv.Int(), 10) | |||||
} else if kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64 { | |||||
m[name] = strconv.FormatUint(fv.Uint(), 10) | |||||
} else if kind == reflect.Float32 || kind == reflect.Float64 { | |||||
m[name] = strconv.FormatFloat(fv.Float(), 'f', 2, 64) | |||||
} else { | |||||
m[name] = fv.Interface() | |||||
} | |||||
} else { | |||||
m[name] = fv.Interface() | |||||
} | |||||
} | |||||
return m | |||||
} | |||||
func isEmpty(v *reflect.Value) bool { | |||||
k := v.Kind() | |||||
if k == reflect.Bool { | |||||
return v.Bool() == false | |||||
} else if reflect.Int < k && k < reflect.Int64 { | |||||
return v.Int() == 0 | |||||
} else if reflect.Uint < k && k < reflect.Uintptr { | |||||
return v.Uint() == 0 | |||||
} else if k == reflect.Float32 || k == reflect.Float64 { | |||||
return v.Float() == 0 | |||||
} else if k == reflect.Array || k == reflect.Map || k == reflect.Slice || k == reflect.String { | |||||
return v.Len() == 0 | |||||
} else if k == reflect.Interface || k == reflect.Ptr { | |||||
return v.IsNil() | |||||
} | |||||
return false | |||||
} | |||||
func convertSlice(s string, name string, ft reflect.Type, fv reflect.Value) error { | |||||
var err error | |||||
et := ft.Elem() | |||||
if et.Kind() == reflect.Ptr || et.Kind() == reflect.Struct { | |||||
return convertJsonValue(s, name, fv) | |||||
} | |||||
ss := strings.Split(s, ",") | |||||
if len(s) == 0 || len(ss) == 0 { | |||||
return nil | |||||
} | |||||
fs := reflect.MakeSlice(ft, 0, len(ss)) | |||||
for _, si := range ss { | |||||
ev := reflect.New(et).Elem() | |||||
err = convertValue(et.Kind(), si, name, ev) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
fs = reflect.Append(fs, ev) | |||||
} | |||||
fv.Set(fs) | |||||
return nil | |||||
} | |||||
func convertJsonValue(s string, name string, fv reflect.Value) error { | |||||
var err error | |||||
d := StringToSlice(s) | |||||
if fv.Kind() == reflect.Ptr { | |||||
if fv.IsNil() { | |||||
fv.Set(reflect.New(fv.Type().Elem())) | |||||
} | |||||
} else { | |||||
fv = fv.Addr() | |||||
} | |||||
err = json.Unmarshal(d, fv.Interface()) | |||||
if err != nil { | |||||
return fmt.Errorf("invalid json '%v': %v, %v", name, err.Error(), s) | |||||
} | |||||
return nil | |||||
} | |||||
func convertValue(kind reflect.Kind, s string, name string, fv reflect.Value) error { | |||||
if !fv.CanAddr() { | |||||
return fmt.Errorf("can not addr: %v", name) | |||||
} | |||||
if kind == reflect.String { | |||||
fv.SetString(s) | |||||
return nil | |||||
} | |||||
if kind == reflect.Bool { | |||||
switch s { | |||||
case "true": | |||||
fv.SetBool(true) | |||||
case "false": | |||||
fv.SetBool(false) | |||||
case "1": | |||||
fv.SetBool(true) | |||||
case "0": | |||||
fv.SetBool(false) | |||||
default: | |||||
return fmt.Errorf("invalid bool: %v value=%v", name, s) | |||||
} | |||||
return nil | |||||
} | |||||
if reflect.Int <= kind && kind <= reflect.Int64 { | |||||
i, err := strconv.ParseInt(s, 10, 64) | |||||
if err != nil { | |||||
return fmt.Errorf("invalid int: %v value=%v", name, s) | |||||
} | |||||
fv.SetInt(i) | |||||
} else if reflect.Uint <= kind && kind <= reflect.Uint64 { | |||||
i, err := strconv.ParseUint(s, 10, 64) | |||||
if err != nil { | |||||
return fmt.Errorf("invalid int: %v value=%v", name, s) | |||||
} | |||||
fv.SetUint(i) | |||||
} else if reflect.Float32 == kind || kind == reflect.Float64 { | |||||
i, err := strconv.ParseFloat(s, 64) | |||||
if err != nil { | |||||
return fmt.Errorf("invalid float: %v value=%v", name, s) | |||||
} | |||||
fv.SetFloat(i) | |||||
} else { | |||||
// not support or just ignore it? | |||||
// return fmt.Errorf("type not support: field=%v(%v) value=%v(%v)", name, ft.Kind(), val, vt.Kind()) | |||||
} | |||||
return nil | |||||
} | |||||
func parseTag(tag string) (string, string) { | |||||
tags := strings.Split(tag, ",") | |||||
if len(tags) <= 0 { | |||||
return "", "" | |||||
} | |||||
if len(tags) == 1 { | |||||
return tags[0], "" | |||||
} | |||||
return tags[0], tags[1] | |||||
} |
@@ -0,0 +1,47 @@ | |||||
package zhios_pay_utils | |||||
import ( | |||||
"fmt" | |||||
"reflect" | |||||
"strings" | |||||
"unsafe" | |||||
) | |||||
// string与slice互转,零copy省内存 | |||||
// zero copy to change slice to string | |||||
func Slice2String(b []byte) (s string) { | |||||
pBytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) | |||||
pString := (*reflect.StringHeader)(unsafe.Pointer(&s)) | |||||
pString.Data = pBytes.Data | |||||
pString.Len = pBytes.Len | |||||
return | |||||
} | |||||
// no copy to change string to slice | |||||
func StringToSlice(s string) (b []byte) { | |||||
pBytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) | |||||
pString := (*reflect.StringHeader)(unsafe.Pointer(&s)) | |||||
pBytes.Data = pString.Data | |||||
pBytes.Len = pString.Len | |||||
pBytes.Cap = pString.Len | |||||
return | |||||
} | |||||
// 任意slice合并 | |||||
func SliceJoin(sep string, elems ...interface{}) string { | |||||
l := len(elems) | |||||
if l == 0 { | |||||
return "" | |||||
} | |||||
if l == 1 { | |||||
s := fmt.Sprint(elems[0]) | |||||
sLen := len(s) - 1 | |||||
if s[0] == '[' && s[sLen] == ']' { | |||||
return strings.Replace(s[1:sLen], " ", sep, -1) | |||||
} | |||||
return s | |||||
} | |||||
sep = strings.Replace(fmt.Sprint(elems), " ", sep, -1) | |||||
return sep[1 : len(sep)-1] | |||||
} |