소스 검색

澳门中行支付对接

tags/v1.4.8
jiaoboxiang 2 년 전
부모
커밋
f7428e9a5a
7개의 변경된 파일606개의 추가작업 그리고 5개의 파일을 삭제
  1. +28
    -0
      lib/boc/api.go
  2. +75
    -5
      lib/boc/api_macao.go
  3. +21
    -0
      md/boc.go
  4. +20
    -0
      test/ada_pay_test.go
  5. +74
    -0
      test/boc_pay_test.go
  6. +341
    -0
      utils/map_and_struct.go
  7. +47
    -0
      utils/slice_and_string.go

+ 28
- 0
lib/boc/api.go 파일 보기

@@ -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 " //【基礎服務】訂單关联查詢

+ 75
- 5
lib/boc/api_macao.go 파일 보기

@@ -1,12 +1,26 @@
package boc

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"
"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)
if err != nil {
return "", err
@@ -19,12 +33,68 @@ func Send(prd bool, appId, privateKeyStr string, param map[string]string) (strin
"Content-Type": "application/json",
}
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 {
return "", err
}
fmt.Println(string(res))
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), &param)
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), &param)
send, err := Send(prd, privateKeyStr, param)
if err != nil {
return "", err
}
resp, _ := php2go.URLDecode(send)
return resp, nil
}

+ 21
- 0
md/boc.go 파일 보기

@@ -44,3 +44,24 @@ type CreateBocPayAppTradeReq struct {
Reserved2 string `json:"reserved2" 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:"交易成功后原樣返回給商戶"`
}

+ 20
- 0
test/ada_pay_test.go 파일 보기

@@ -157,3 +157,23 @@ func TestCardBindlist(t *testing.T) {
}
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))
}

+ 74
- 0
test/boc_pay_test.go 파일 보기

@@ -1,9 +1,13 @@
package test

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"
"fmt"
"github.com/syyongx/php2go"
"testing"
"time"
)

func TestJoinStringsInASCII(t *testing.T) {
@@ -11,3 +15,73 @@ func TestJoinStringsInASCII(t *testing.T) {
"", "", false, false)
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), &param)
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)
}

+ 341
- 0
utils/map_and_struct.go 파일 보기

@@ -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]
}

+ 47
- 0
utils/slice_and_string.go 파일 보기

@@ -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]
}

불러오는 중...
취소
저장