diff --git a/README.md b/README.md index 4814996..712cbc3 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ ### 长期 +异步日志记录,统一收集 + 模型字段修改,特定枚举字段使用自定义类型代替通用 int32 更新接口可以传输更结构化的数据,直接区分不同类型以加快更新速度 diff --git a/go.mod b/go.mod index 4198e63..c402488 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/validator/v10 v10.26.0 - github.com/go-redsync/redsync v1.4.2 github.com/go-redsync/redsync/v4 v4.13.0 github.com/gofiber/fiber/v2 v2.52.6 github.com/google/uuid v1.6.0 @@ -43,7 +42,6 @@ require ( github.com/clbanning/mxj/v2 v2.7.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect - github.com/go-redis/redis v6.15.9+incompatible // indirect github.com/go-sql-driver/mysql v1.9.1 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gomodule/redigo v2.0.0+incompatible // indirect @@ -65,8 +63,6 @@ require ( github.com/mattn/go-sqlite3 v1.14.24 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect - github.com/onsi/gomega v1.37.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/smartwalle/ncrypto v1.0.4 // indirect diff --git a/go.sum b/go.sum index f08d8c0..c39c4fa 100644 --- a/go.sum +++ b/go.sum @@ -77,9 +77,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -96,14 +93,11 @@ github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOr github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-redsync/redsync v1.4.2 h1:KADEZ2rlaHMZWnlkthQCxfGP+8ZWwJLiSjOYN3mntKA= -github.com/go-redsync/redsync v1.4.2/go.mod h1:my8/M5YL986u2jBMtZTLkBIgBsKNNSixJWzWwISH6Uw= github.com/go-redsync/redsync/v4 v4.13.0 h1:49X6GJfnbLGaIpBBREM/zA4uIMDXKAh1NDkvQ1EkZKA= github.com/go-redsync/redsync/v4 v4.13.0/go.mod h1:HMW4Q224GZQz6x1Xc7040Yfgacukdzu7ifTDAKiyErQ= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI= github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI= github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= @@ -140,12 +134,10 @@ github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORR github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hibiken/asynq v0.25.1 h1:phj028N0nm15n8O2ims+IvJ2gz4k2auvermngh9JhTw= github.com/hibiken/asynq v0.25.1/go.mod h1:pazWNOLBu0FEynQRBvHA26qdIKRSmfdIfUm4HdsLmXg= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -200,17 +192,6 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= -github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -267,7 +248,6 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= @@ -292,7 +272,6 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -302,16 +281,13 @@ golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -332,7 +308,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= @@ -341,17 +316,12 @@ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -401,7 +371,6 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= @@ -411,7 +380,6 @@ golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -430,16 +398,11 @@ google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwl google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/env/env.go b/pkg/env/env.go index 439d966..61548ae 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -352,6 +352,7 @@ func loadAliyun() { var ( SftPayEnable = false SftPayAppId string + SftPayRouteId string SftPayAppPrivateKey string SftPayPublicKey string ) @@ -375,6 +376,11 @@ func loadSftPay() { SftPayAppId = value } + value = os.Getenv("SFTPAY_ROUTE_ID") + if value != "" { + SftPayRouteId = value + } + value = os.Getenv("SFTPAY_APP_PRIVATE_KEY") if value == "" { panic("环境变量 SFTPAY_APP_PRIVATE_KEY 的值不能为空") @@ -442,5 +448,5 @@ func Init() { loadAlipay() loadWechatPay() loadAliyun() - // loadSftPay() + loadSftPay() } diff --git a/scripts/sql/init.sql b/scripts/sql/init.sql index 7f54bce..cc9eaba 100644 --- a/scripts/sql/init.sql +++ b/scripts/sql/init.sql @@ -805,7 +805,8 @@ create table trade ( amount decimal(12, 2) not null default 0, payment decimal(12, 2) not null default 0, method int not null, - acquirer int not null, + platform int not null, + acquirer int , status int not null default 0, pay_url text, paid_at timestamp, @@ -832,6 +833,7 @@ comment on column trade.remark is '订单备注'; comment on column trade.amount is '订单总金额'; comment on column trade.payment is '支付金额'; comment on column trade.method is '支付方式:1-支付宝,2-微信,3-商福通渠道支付宝,4-商福通渠道微信'; +comment on column trade.platform is '支付平台:1-电脑网站,2-手机网站'; comment on column trade.acquirer is '收单机构:1-支付宝,2-微信,3-银联'; comment on column trade.status is '订单状态:0-待支付,1-已支付,2-已取消,3-已退款'; comment on column trade.pay_url is '支付链接'; diff --git a/web/core/types.go b/web/core/types.go index 68499b0..4b0d8e9 100644 --- a/web/core/types.go +++ b/web/core/types.go @@ -49,7 +49,7 @@ type PageResp struct { // region error -type BizErr struct { +type Err struct { msg string err error @@ -58,18 +58,18 @@ type BizErr struct { errFunc string } -func (e *BizErr) Error() string { +func (e *Err) Error() string { if e.err != nil { return e.msg + ":" + e.err.Error() } return e.msg } -func (e *BizErr) Unwrap() error { +func (e *Err) Unwrap() error { return e.err } -func (e *BizErr) Source() *slog.Source { +func (e *Err) Source() *slog.Source { return &slog.Source{ Function: e.errFunc, File: e.errFile, @@ -77,25 +77,37 @@ func (e *BizErr) Source() *slog.Source { } } -func NewBizErr(msg string, err ...error) (biz *BizErr) { - biz = &BizErr{ +func newErr(msg string, err ...error) Err { + o := Err{ msg: msg, } if len(err) > 0 { - biz.err = err[0] + o.err = err[0] } if env.RunMode == env.RunModeDev { - pc, file, line, ok := runtime.Caller(1) + pc, file, line, ok := runtime.Caller(2) if ok { - biz.errFile = file - biz.errLine = line - biz.errFunc = runtime.FuncForPC(pc).Name() + o.errFile = file + o.errLine = line + o.errFunc = runtime.FuncForPC(pc).Name() } } - return biz + return o +} + +type BizErr struct{ Err } + +func NewBizErr(msg string, err ...error) (biz *BizErr) { + return &BizErr{newErr(msg, err...)} +} + +type ServErr struct{ Err } + +func NewServErr(msg string, err ...error) *ServErr { + return &ServErr{newErr(msg, err...)} } // endregion diff --git a/web/domains/trade/types.go b/web/domains/trade/types.go index 35416be..decbd24 100644 --- a/web/domains/trade/types.go +++ b/web/domains/trade/types.go @@ -17,6 +17,13 @@ const ( MethodSftWeChat // 商福通渠道指定微信 ) +type Platform int32 + +const ( + PlatformDesktop Platform = iota + 1 // 桌面网站 + PlatformMobile // 手机网站 +) + type Acquirer int32 const ( diff --git a/web/error.go b/web/error.go index 13eeeba..d35b455 100644 --- a/web/error.go +++ b/web/error.go @@ -46,7 +46,7 @@ func ErrorHandler(c *fiber.Ctx, err error) error { // 所有未手动声明的错误类型 default: - slog.Debug("未处理的异常", slog.String("type", reflect.TypeOf(err).Name()), slog.String("error", err.Error())) + slog.Warn("未处理的异常", slog.String("type", reflect.TypeOf(err).Name()), slog.String("error", err.Error())) } c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) diff --git a/web/globals/asynq.go b/web/globals/asynq.go index b494482..d1c7679 100644 --- a/web/globals/asynq.go +++ b/web/globals/asynq.go @@ -6,7 +6,7 @@ import ( var Asynq *asynq.Client -func InitAsynq() { +func initAsynq() { var client = asynq.NewClientFromRedisClient(Redis) Asynq = client } diff --git a/web/globals/init.go b/web/globals/init.go index 7ad04d8..6e4a108 100644 --- a/web/globals/init.go +++ b/web/globals/init.go @@ -9,5 +9,6 @@ func Init() { initRedis() initOrm() initProxy() - InitAsynq() + initAsynq() + initSft() } diff --git a/web/globals/shangfutong.go b/web/globals/shangfutong.go index eb50cee..9ddc1fc 100644 --- a/web/globals/shangfutong.go +++ b/web/globals/shangfutong.go @@ -6,13 +6,15 @@ import ( "crypto/rsa" "crypto/sha256" "crypto/x509" + "encoding/base64" "encoding/json" - "encoding/pem" "fmt" "io" "net/http" + "net/http/httputil" "platform/pkg/env" "platform/pkg/u" + "platform/web/core" "strings" "time" ) @@ -21,29 +23,31 @@ var SFTPay SftClient type SftClient struct { appid string + routeId string privateKey *rsa.PrivateKey publicKey *rsa.PublicKey } -func init() { +func initSft() { if !env.SftPayEnable { - return + panic("商福通支付未启用,请检查环境变量 SFTPAY_ENABLE") } SFTPay = SftClient{ - appid: env.SftPayAppId, + appid: env.SftPayAppId, + routeId: env.SftPayRouteId, } // 加载私钥 - block, _ := pem.Decode([]byte(env.SftPayAppPrivateKey)) - if block == nil || block.Type != "RSA PRIVATE KEY" { - panic("加载商福通私钥失败") + private, err := base64.StdEncoding.DecodeString(env.SftPayAppPrivateKey) + if err != nil { + panic("解析商福通私钥失败: " + err.Error()) } var privateKey *rsa.PrivateKey - privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + privateKey, err = x509.ParsePKCS1PrivateKey(private) if err != nil { - pkcs8, err := x509.ParsePKCS8PrivateKey(block.Bytes) + pkcs8, err := x509.ParsePKCS8PrivateKey(private) if err != nil { panic("解析商福通私钥失败: " + err.Error()) } @@ -57,13 +61,13 @@ func init() { SFTPay.privateKey = privateKey // 加载公钥 - block, _ = pem.Decode([]byte(env.SftPayPublicKey)) - if block == nil || block.Type != "PUBLIC KEY" { - panic("加载商福通公钥失败") + public, err := base64.StdEncoding.DecodeString(env.SftPayPublicKey) + if err != nil { + panic("解析商福通公钥失败: " + err.Error()) } var publicKey *rsa.PublicKey - pkix, err := x509.ParsePKIXPublicKey(block.Bytes) + pkix, err := x509.ParsePKIXPublicKey(public) if err != nil { panic("解析商福通公钥失败: " + err.Error()) } @@ -78,11 +82,13 @@ func init() { func (s *SftClient) PaymentScanPay(req *PaymentScanPayReq) (*PaymentScanPayResp, error) { const url = "https://pay.rscygroup.com/api/open/payment/scanpay" + req.RouteNo = u.P(s.routeId) return call[PaymentScanPayResp](s, url, req) } func (s *SftClient) PaymentH5Pay(req *PaymentH5PayReq) (*PaymentH5PayResp, error) { const url = "https://pay.rscygroup.com/api/open/payment/h5pay" + req.RouteNo = u.P(s.routeId) return call[PaymentH5PayResp](s, url, req) } @@ -91,6 +97,11 @@ func (s *SftClient) OrderClose(req *OrderCloseReq) (*OrderCloseResp, error) { return call[OrderCloseResp](s, url, req) } +func (s *SftClient) QueryTrade(req *QueryTradeReq) (*QueryTradeResp, error) { + const url = "https://pay.rscygroup.com/api/open/query/trade" + return call[QueryTradeResp](s, url, req) +} + type PaymentScanPayReq struct { Subject string `json:"subject"` Body string `json:"body"` @@ -132,6 +143,11 @@ type PaymentH5PayReq struct { LimitPay *int `json:"limitPay"` } +type QueryTradeReq struct { + PayOrderId *string `json:"payOrderId"` + MchOrderNo *string `json:"mchOrderNo"` +} + type OrderCloseReq struct { MchOrderNo *string `json:"mchOrderNo"` PayOrderId *string `json:"payOrderId"` @@ -197,6 +213,29 @@ type PaymentH5PayResp struct { SettlementType *string `json:"settlementType"` } +type QueryTradeResp struct { + Amount int64 `json:"amount"` + ChannelSendNo *string `json:"channelSendNo"` + IfCode *string `json:"ifCode"` + MercNo string `json:"mercNo"` + MchOrderNo string `json:"mchOrderNo"` + PayOrderId *string `json:"payOrderId"` + PayType string `json:"payType"` + ChannelTradeNo *string `json:"channelTradeNo"` + State SftTradeState `json:"state"` + RefundAmt *int64 `json:"refundAmt"` + RefundState int32 `json:"refundState"` + DrType *string `json:"drType"` + ExtParam *string `json:"extParam"` + PayTime *string `json:"payTime"` + Subject string `json:"subject"` + TradeFee *int64 `json:"tradeFee"` + CashFee *int64 `json:"cashFee"` + StoreId *string `json:"storeId"` + UserId *string `json:"userId"` + SettlementType *string `json:"settlementType"` +} + type OrderCloseResp struct { MchOrderNo string `json:"mchOrderNo"` PayOrderId string `json:"payOrderId"` @@ -224,19 +263,30 @@ func call[T any](s *SftClient, url string, req any) (*T, error) { if err != nil { return nil, fmt.Errorf("创建请求失败:%w", err) } + request.Header.Set("Content-Type", "application/json") + + reqDump, err := httputil.DumpRequest(request, true) + if err != nil { + return nil, fmt.Errorf("请求内容转储失败:%w", err) + } + println(string(reqDump) + "\n\n") response, err := http.DefaultClient.Do(request) if err != nil { return nil, fmt.Errorf("请求失败:%w", err) } + + respDump, err := httputil.DumpResponse(response, true) + if err != nil { + return nil, fmt.Errorf("响应内容转储失败:%w", err) + } + println(string(respDump) + "\n\n") + if response.StatusCode != http.StatusOK { return nil, fmt.Errorf("请求响应失败:%d", response.StatusCode) } - defer func(Body io.ReadCloser) { - err := Body.Close() - if err != nil { - - } + defer func(body io.ReadCloser) { + _ = body.Close() }(response.Body) body, err := io.ReadAll(response.Body) @@ -248,15 +298,9 @@ func call[T any](s *SftClient, url string, req any) (*T, error) { if err != nil { return nil, fmt.Errorf("解密响应内容失败:%w", err) } - if decode.Code != "000000" { - return nil, fmt.Errorf("请求业务响应失败:%s", u.Z(decode.Msg)) - } - if decode.BizData == nil { - return nil, nil - } var resp = new(T) - err = json.Unmarshal([]byte(*decode.BizData), resp) + err = json.Unmarshal([]byte(decode), resp) if err != nil { return nil, fmt.Errorf("响应正文解析失败:%w", err) } @@ -290,24 +334,34 @@ func (s *SftClient) sign(msg any) (*request, error) { return &body, nil } -func (s *SftClient) verify(str []byte) (*response, error) { +func (s *SftClient) verify(str []byte) (string, error) { var resp = new(response) err := json.Unmarshal(str, resp) if err != nil { - return nil, fmt.Errorf("解析响应正文失败:%w", err) + return "", fmt.Errorf("解析响应正文失败:%w", err) } - if resp.Sign != nil || resp.SignType != nil || resp.BizData != nil { - - hashed := sha256.Sum256([]byte(resp.String())) - err := rsa.VerifyPKCS1v15(s.publicKey, crypto.SHA256, hashed[:], []byte(*resp.Sign)) - if err != nil { - return nil, fmt.Errorf("验签失败:%w", err) - } + if resp.Code != "000000" { + return "", fmt.Errorf("请求业务响应失败:%s", u.Z(resp.Msg)) } - return resp, err + if resp.Sign == nil { + return "", core.NewServErr("响应数据签名为空") + } + + ser, err := resp.String() + if err != nil { + return "", fmt.Errorf("格式化响应内容失败:%w", err) + } + + hashed := sha256.Sum256([]byte(ser)) + err = rsa.VerifyPKCS1v15(s.publicKey, crypto.SHA256, hashed[:], []byte(*resp.Sign)) + if err != nil { + return "", fmt.Errorf("验签失败:%w", err) + } + + return *resp.BizData, nil } type request struct { @@ -336,11 +390,17 @@ type response struct { Timestamp string `json:"timestamp"` } -func (r response) String() string { +func (r response) String() (string, error) { + if r.BizData == nil || r.Msg == nil || r.SignType == nil { + return "", core.NewServErr(fmt.Sprintf( + "上游数据返回有空值:BizData %v,Msg %v, SignType %v", + r.BizData == nil, r.Msg == nil, r.SignType == nil, + )) + } return fmt.Sprintf( "bizData=%s&code=%s&msg=%s&signType=%s×tamp=%s", u.Z(r.BizData), r.Code, u.Z(r.Msg), u.Z(r.SignType), r.Timestamp, - ) + ), nil } type SftPayType string @@ -350,3 +410,16 @@ const ( SftWeChat SftPayType = "WECHAT" SftUnionPay SftPayType = "UNIONPAY" ) + +type SftTradeState string + +const ( + SftInit SftTradeState = "INIT" + SftTradeAWAIT SftTradeState = "TRADE_WAIT" + SftTradeSuccess SftTradeState = "TRADE_SUCCESS" + SftTradeFail SftTradeState = "TRADE_FAIL" + SftTradeCancel SftTradeState = "TRADE_CANCEL" + SftTradeRefund SftTradeState = "TRADE_REFUND" + SftRefundIng SftTradeState = "REFUND_ING" + SftTradeClosed SftTradeState = "TRADE_CLOSED" +) diff --git a/web/handlers/resource.go b/web/handlers/resource.go index a1531b7..75d052b 100644 --- a/web/handlers/resource.go +++ b/web/handlers/resource.go @@ -293,7 +293,7 @@ func PrepareCreateResource(c *fiber.Ctx) error { } // 准备创建套餐 - result, err := s.Resource.PrepareResource(authCtx.Payload.Id, time.Now(), req.Method, &req.CreateResourceSerializer) + result, err := s.Resource.PrepareResource(authCtx.Payload.Id, time.Now(), &req.CreateResourceSerializer) if err != nil { return err } diff --git a/web/handlers/user.go b/web/handlers/user.go index 3e18945..36ba1a3 100644 --- a/web/handlers/user.go +++ b/web/handlers/user.go @@ -1,8 +1,10 @@ package handlers import ( + "fmt" "github.com/shopspring/decimal" "platform/web/auth" + "platform/web/core" trade2 "platform/web/domains/trade" m "platform/web/models" q "platform/web/queries" @@ -143,7 +145,9 @@ func UpdatePassword(c *fiber.Ctx) error { // region /recharge type RechargePrepareReq struct { - Amount string `json:"amount" validate:"required,numeric"` + Amount string `json:"amount" validate:"required,numeric"` + Platform trade2.Platform `json:"platform" validate:"required"` + Method trade2.Method `json:"method" validate:"required"` } type RechargePrepareResp struct { @@ -159,7 +163,6 @@ type RechargeConfirmResp struct { Status string `json:"status"` } -// RechargePrepareAlipay 通过支付宝充值 func RechargePrepareAlipay(c *fiber.Ctx) error { // 检查权限 authContext, err := auth.Protect(c, []auth.PayloadType{auth.PayloadUser}, []string{}) @@ -181,12 +184,13 @@ func RechargePrepareAlipay(c *fiber.Ctx) error { } var result *s.TradeCreateResult err = q.Q.Transaction(func(tx *q.Query) error { - result, err = s.Trade.SendCreateTradeByQrcode(tx, authContext.Payload.Id, now, &s.TradeCreateData{ + result, err = s.Trade.CreateTrade(tx, authContext.Payload.Id, now, &s.TradeCreateData{ Subject: "账户充值 - " + amount.StringFixed(2) + "元", Amount: amount, ExpireAt: time.Now().Add(30 * time.Minute), Type: trade2.TypeRecharge, Method: trade2.MethodAlipay, + Platform: req.Platform, }) return err }) @@ -215,7 +219,7 @@ func RechargeConfirmAlipay(c *fiber.Ctx) error { } // 验证支付结果 - result, err := s.Trade.VerifyCreateTrade(&s.TradeVerifyData{ + result, err := s.Trade.VerifyTrade(&s.TradeVerifyData{ TradeNo: req.TradeNo, Method: trade2.MethodAlipay, }) @@ -253,12 +257,13 @@ func RechargePrepareWechat(c *fiber.Ctx) error { } var result *s.TradeCreateResult err = q.Q.Transaction(func(tx *q.Query) error { - result, err = s.Trade.SendCreateTradeByQrcode(tx, authContext.Payload.Id, now, &s.TradeCreateData{ + result, err = s.Trade.CreateTrade(tx, authContext.Payload.Id, now, &s.TradeCreateData{ Subject: "账户充值 - " + amount.StringFixed(2) + "元", Amount: amount, ExpireAt: now.Add(30 * time.Minute), Type: trade2.TypeRecharge, Method: trade2.MethodWeChat, + Platform: req.Platform, }) return err }) @@ -289,7 +294,82 @@ func RechargeConfirmWechat(c *fiber.Ctx) error { } // 验证支付结果 - result, err := s.Trade.VerifyCreateTrade(&s.TradeVerifyData{ + result, err := s.Trade.VerifyTrade(&s.TradeVerifyData{ + TradeNo: req.TradeNo, + Method: trade2.MethodWeChat, + }) + if err != nil { + return err + } + + // 更新数据库 + err = s.User.RechargeConfirm(req.TradeNo, result) + if err != nil { + return err + } + + return c.JSON(fiber.Map{"status": "success"}) +} + +func RechargePrepare(c *fiber.Ctx) error { + // 检查权限 + authContext, err := auth.Protect(c, []auth.PayloadType{auth.PayloadUser}, []string{}) + if err != nil { + return err + } + + // 解析请求参数 + req := new(RechargePrepareReq) + if err := c.BodyParser(req); err != nil { + return err + } + + // 保存交易信息 + var now = time.Now() + amount, err := decimal.NewFromString(req.Amount) + if err != nil { + return core.NewBizErr(fmt.Sprintf("金额格式错误: %s", err.Error())) + } + var result *s.TradeCreateResult + err = q.Q.Transaction(func(tx *q.Query) error { + result, err = s.Trade.CreateTrade(tx, authContext.Payload.Id, now, &s.TradeCreateData{ + Subject: "账户充值 - " + amount.StringFixed(2) + "元", + Amount: amount, + ExpireAt: now.Add(30 * time.Minute), + Type: trade2.TypeRecharge, + Method: req.Method, + Platform: req.Platform, + }) + return err + }) + if err != nil { + return err + } + + // 返回结果 + return c.JSON(RechargePrepareResp{ + TradeNo: result.TradeNo, + PayURL: result.PayURL, + }) +} + +func RechargeConfirm(c *fiber.Ctx) error { + // 检查权限 + _, err := auth.Protect(c, []auth.PayloadType{auth.PayloadUser}, []string{}) + if err != nil { + return err + } + + // 解析请求参数 + req := new(struct { + TradeNo string `json:"trade_no" validate:"required"` + }) + if err := c.BodyParser(req); err != nil { + return err + } + + // 验证支付结果 + result, err := s.Trade.VerifyTrade(&s.TradeVerifyData{ TradeNo: req.TradeNo, Method: trade2.MethodWeChat, }) diff --git a/web/models/trade.gen.go b/web/models/trade.gen.go index 6f650a3..3ecc5b9 100644 --- a/web/models/trade.gen.go +++ b/web/models/trade.gen.go @@ -24,7 +24,7 @@ type Trade struct { Remark *string `gorm:"column:remark;type:character varying(255);comment:订单备注" json:"remark"` // 订单备注 Amount decimal.Decimal `gorm:"column:amount;type:numeric(12,2);not null;comment:订单总金额" json:"amount"` // 订单总金额 Payment decimal.Decimal `gorm:"column:payment;type:numeric(12,2);not null;comment:支付金额" json:"payment"` // 支付金额 - Method int32 `gorm:"column:method;type:integer;not null;comment:支付方式:1-支付宝,2-微信,3-商福通" json:"method"` // 支付方式:1-支付宝,2-微信,3-商福通 + Method int32 `gorm:"column:method;type:integer;not null;comment:支付方式:1-支付宝,2-微信,3-商福通渠道支付宝,4-商福通渠道微信" json:"method"` // 支付方式:1-支付宝,2-微信,3-商福通渠道支付宝,4-商福通渠道微信 Status int32 `gorm:"column:status;type:integer;not null;comment:订单状态:0-待支付,1-已支付,2-已取消,3-已退款" json:"status"` // 订单状态:0-待支付,1-已支付,2-已取消,3-已退款 PayURL *string `gorm:"column:pay_url;type:text;comment:支付链接" json:"pay_url"` // 支付链接 PaidAt *orm.LocalDateTime `gorm:"column:paid_at;type:timestamp without time zone;comment:支付时间" json:"paid_at"` // 支付时间 @@ -33,6 +33,7 @@ type Trade struct { UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间 Acquirer int32 `gorm:"column:acquirer;type:integer;not null;comment:收单机构:1-支付宝,2-微信,3-银联" json:"acquirer"` // 收单机构:1-支付宝,2-微信,3-银联 + Platform int32 `gorm:"column:platform;type:integer;not null;comment:支付平台:1-电脑网站,2-手机网站" json:"platform"` // 支付平台:1-电脑网站,2-手机网站 } // TableName Trade's table name diff --git a/web/queries/trade.gen.go b/web/queries/trade.gen.go index 33dc15c..1a639c1 100644 --- a/web/queries/trade.gen.go +++ b/web/queries/trade.gen.go @@ -45,6 +45,7 @@ func newTrade(db *gorm.DB, opts ...gen.DOOption) trade { _trade.UpdatedAt = field.NewField(tableName, "updated_at") _trade.DeletedAt = field.NewField(tableName, "deleted_at") _trade.Acquirer = field.NewInt32(tableName, "acquirer") + _trade.Platform = field.NewInt32(tableName, "platform") _trade.fillFieldMap() @@ -64,7 +65,7 @@ type trade struct { Remark field.String // 订单备注 Amount field.Field // 订单总金额 Payment field.Field // 支付金额 - Method field.Int32 // 支付方式:1-支付宝,2-微信,3-商福通 + Method field.Int32 // 支付方式:1-支付宝,2-微信,3-商福通渠道支付宝,4-商福通渠道微信 Status field.Int32 // 订单状态:0-待支付,1-已支付,2-已取消,3-已退款 PayURL field.String // 支付链接 PaidAt field.Field // 支付时间 @@ -73,6 +74,7 @@ type trade struct { UpdatedAt field.Field // 更新时间 DeletedAt field.Field // 删除时间 Acquirer field.Int32 // 收单机构:1-支付宝,2-微信,3-银联 + Platform field.Int32 // 支付平台:1-电脑网站,2-手机网站 fieldMap map[string]field.Expr } @@ -107,6 +109,7 @@ func (t *trade) updateTableName(table string) *trade { t.UpdatedAt = field.NewField(table, "updated_at") t.DeletedAt = field.NewField(table, "deleted_at") t.Acquirer = field.NewInt32(table, "acquirer") + t.Platform = field.NewInt32(table, "platform") t.fillFieldMap() @@ -123,7 +126,7 @@ func (t *trade) GetFieldByName(fieldName string) (field.OrderExpr, bool) { } func (t *trade) fillFieldMap() { - t.fieldMap = make(map[string]field.Expr, 18) + t.fieldMap = make(map[string]field.Expr, 19) t.fieldMap["id"] = t.ID t.fieldMap["user_id"] = t.UserID t.fieldMap["inner_no"] = t.InnerNo @@ -142,6 +145,7 @@ func (t *trade) fillFieldMap() { t.fieldMap["updated_at"] = t.UpdatedAt t.fieldMap["deleted_at"] = t.DeletedAt t.fieldMap["acquirer"] = t.Acquirer + t.fieldMap["platform"] = t.Platform } func (t trade) clone(db *gorm.DB) trade { diff --git a/web/router.go b/web/router.go index ed7c0f9..ca6cb85 100644 --- a/web/router.go +++ b/web/router.go @@ -28,6 +28,8 @@ func ApplyRouters(app *fiber.App) { user.Post("/recharge/confirm/alipay", handlers.RechargeConfirmAlipay) user.Post("/recharge/prepare/wechat", handlers.RechargePrepareWechat) user.Post("/recharge/confirm/wechat", handlers.RechargeConfirmWechat) + user.Post("/recharge/prepare", handlers.RechargePrepare) + user.Post("/recharge/confirm", handlers.RechargeConfirm) // 白名单 whitelist := api.Group("/whitelist") diff --git a/web/services/resource.go b/web/services/resource.go index 04e80b9..dfd563c 100644 --- a/web/services/resource.go +++ b/web/services/resource.go @@ -87,7 +87,7 @@ func (s *resourceService) CreateResource(uid int32, now time.Time, ser *CreateRe return nil } -func (s *resourceService) PrepareResource(uid int32, now time.Time, method trade2.Method, ser *CreateResourceSerializer) (*TradeCreateResult, error) { +func (s *resourceService) PrepareResource(uid int32, now time.Time, ser *CreateResourceSerializer) (*TradeCreateResult, error) { data, err := ser.ToData() if err != nil { @@ -97,18 +97,22 @@ func (s *resourceService) PrepareResource(uid int32, now time.Time, method trade name := data.GetName() amount := data.GetPrice() + method := ser.PaymentMethod + platform := ser.PaymentPlatform + // 保存到数据库 var result *TradeCreateResult err = q.Q.Transaction(func(q *q.Query) error { var err error // 生成交易订单 - result, err = Trade.SendCreateTradeByQrcode(q, uid, now, &TradeCreateData{ + result, err = Trade.CreateTrade(q, uid, now, &TradeCreateData{ Subject: "购买套餐 - " + name, Amount: amount, ExpireAt: time.Now().Add(30 * time.Minute), Type: trade2.TypeRecharge, Method: method, + Platform: platform, }) if err != nil { return err @@ -124,7 +128,6 @@ func (s *resourceService) PrepareResource(uid int32, now time.Time, method trade Uid: uid, TradeId: result.Trade.ID, BillId: result.Bill.ID, - Method: method, CreateResourceSerializer: resourceSerializer, }, 30*time.Minute).Err() if err != nil { @@ -158,9 +161,9 @@ func (s *resourceService) CompleteResource(tradeNo string, now time.Time, opResu rs = opResult[0] } else { var err error - rs, err = Trade.VerifyCreateTrade(&TradeVerifyData{ + rs, err = Trade.VerifyTrade(&TradeVerifyData{ TradeNo: tradeNo, - Method: cache.Method, + Method: cache.PaymentMethod, }) if err != nil { return err @@ -229,7 +232,7 @@ func (s *resourceService) CancelResource(tradeNo string, now time.Time, opRevoke // 取消交易 if len(opRevoked) <= 0 { - err = Trade.CancelTrade(tradeNo, cache.Method) + err = Trade.CancelTrade(tradeNo, cache.PaymentMethod) if err != nil { return err } @@ -403,9 +406,11 @@ func (data *CreateLongResourceData) GetPrice() decimal.Decimal { } type CreateResourceSerializer struct { - Type resource2.Type `json:"type" validate:"required"` - Short *CreateShortResourceData `json:"short,omitempty"` - Long *CreateLongResourceData `json:"long,omitempty"` + Type resource2.Type `json:"type" validate:"required"` + Short *CreateShortResourceData `json:"short,omitempty"` + Long *CreateLongResourceData `json:"long,omitempty"` + PaymentMethod trade2.Method `json:"payment_method" validate:"required"` + PaymentPlatform trade2.Platform `json:"payment_platform" validate:"required"` } func (s *CreateResourceSerializer) ToData() (CreateResourceData, error) { @@ -433,10 +438,9 @@ func (s *CreateResourceSerializer) ByData(data CreateResourceData) error { } type CreateResourceCache struct { - Uid int32 `json:"uid"` - TradeId int32 `json:"trade_id"` - BillId int32 `json:"bill_id"` - Method trade2.Method `json:"method"` + Uid int32 `json:"uid"` + TradeId int32 `json:"trade_id"` + BillId int32 `json:"bill_id"` *CreateResourceSerializer } diff --git a/web/services/trade.go b/web/services/trade.go index 98a72a7..5f2eb40 100644 --- a/web/services/trade.go +++ b/web/services/trade.go @@ -9,6 +9,7 @@ import ( "net/http" "platform/pkg/env" "platform/pkg/u" + "platform/web/core" bill2 "platform/web/domains/bill" coupon2 "platform/web/domains/coupon" trade2 "platform/web/domains/trade" @@ -29,11 +30,12 @@ var Trade = &tradeService{} type tradeService struct { } -func (s *tradeService) SendCreateTradeByQrcode(q *q.Query, uid int32, now time.Time, data *TradeCreateData) (*TradeCreateResult, error) { +func (s *tradeService) CreateTrade(q *q.Query, uid int32, now time.Time, data *TradeCreateData) (*TradeCreateResult, error) { var subject = data.Subject var expire = data.ExpireAt var tType = data.Type var method = data.Method + var platform = data.Platform var amount = data.Amount // 实际支付金额,只在创建真实订单时使用 @@ -103,11 +105,10 @@ func (s *tradeService) SendCreateTradeByQrcode(q *q.Query, uid int32, now time.T // 创建支付订单 var payUrl string - var acquirer trade2.Acquirer - switch method { + switch { - // 调用支付宝支付接口 - case trade2.MethodAlipay: + // 支付宝 + 电脑网站 + case method == trade2.MethodAlipay && platform == trade2.PlatformDesktop: resp, err := g.Alipay.TradePagePay(alipay.TradePagePay{ QRPayMode: "4", QRCodeWidth: "196", // 二维码宽度需要-4,支付宝页面布局有问题 @@ -123,10 +124,9 @@ func (s *tradeService) SendCreateTradeByQrcode(q *q.Query, uid int32, now time.T return nil, err } payUrl = resp.String() - acquirer = trade2.AcquirerAlipay - // 调用微信支付接口 - case trade2.MethodWeChat: + // 微信 + 电脑网站 + case method == trade2.MethodWeChat && platform == trade2.PlatformDesktop: resp, _, err := g.WechatPay.Native.Prepay(context.Background(), native.PrepayRequest{ Appid: &env.WechatPayAppId, Mchid: &env.WechatPayMchId, @@ -142,10 +142,9 @@ func (s *tradeService) SendCreateTradeByQrcode(q *q.Query, uid int32, now time.T return nil, err } payUrl = *resp.CodeUrl - acquirer = trade2.AcquirerWeChat - // 调用商福通接口 - case trade2.MethodSft: + // 商福通 + 电脑网站 + case method == trade2.MethodSft && platform == trade2.PlatformDesktop: resp, err := g.SFTPay.PaymentScanPay(&g.PaymentScanPayReq{ MchOrderNo: tradeNo, Subject: subject, @@ -159,155 +158,9 @@ func (s *tradeService) SendCreateTradeByQrcode(q *q.Query, uid int32, now time.T return nil, err } payUrl = u.Z(u.Z(resp.PayInfo).QrCodeUrl) - if payUrl == "" { - return nil, errors.New("支付接口未返回正确的二维码地址") - } - switch resp.PayType { - case g.SftAlipay: - acquirer = trade2.AcquirerAlipay - case g.SftWeChat: - acquirer = trade2.AcquirerWeChat - case g.SftUnionPay: - acquirer = trade2.AcquirerUnionPay - } - // 不支持的支付方式 - default: - return nil, ErrTransactionNotSupported - } - - // 保存交易订单 - var billType bill2.Type - switch tType { - case trade2.TypeRecharge: - billType = bill2.TypeRecharge - case trade2.TypePurchase: - billType = bill2.TypeConsume - } - - var trade = m.Trade{ - UserID: uid, - InnerNo: tradeNo, - Subject: subject, - Method: int32(method), - Type: int32(tType), - Amount: amount, - PayURL: &payUrl, - Acquirer: int32(acquirer), - } - err = q.Trade.Create(&trade) - if err != nil { - return nil, err - } - - // 保存用户帐单 - var bill = m.Bill{ - BillNo: ID.GenReadable("bil"), - UserID: uid, - TradeID: &trade.ID, - Info: &subject, - Type: int32(billType), - Amount: amount, - } - err = q.Bill. - Omit(q.Bill.ResourceID, q.Bill.RefundID). - Create(&bill) - if err != nil { - return nil, err - } - - // 提交异步任务更新订单状态 - _, err = g.Asynq.Enqueue(tasks.NewUpdateTrade(tradeNo, method)) - if err != nil { - return nil, err - } - - return &TradeCreateResult{ - TradeNo: tradeNo, - PayURL: payUrl, - Bill: &bill, - Trade: &trade, - }, nil -} -func (s *tradeService) SendCreateTradeByRedirect(q *q.Query, uid int32, now time.Time, data *TradeCreateData) (*TradeCreateResult, error) { - var subject = data.Subject - var expire = data.ExpireAt - var tType = data.Type - var method = data.Method - var amount = data.Amount - - // 实际支付金额,只在创建真实订单时使用 - var amountReal = data.Amount - if env.RunMode == "debug" { - amountReal = decimal.NewFromFloat(0.01) - } - - // 附加优惠券 - if data.CouponCode != "" { - coupon, err := q.Coupon. - Where( - q.Coupon.Code.Eq(data.CouponCode), - q.Coupon.Status.Eq(int32(coupon2.StatusUnused)), - ). - Take() - if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, errors.New("优惠券不存在或已失效") - } - return nil, err - } - - var expireAt = time.Time(u.Z(coupon.ExpireAt)) - if !expireAt.IsZero() && expireAt.Before(now) { - _, err = q.Coupon. - Where(q.Coupon.ID.Eq(coupon.ID)). - Update(q.Coupon.Status, coupon2.StatusExpired) - if err != nil { - return nil, err - } - return nil, errors.New("优惠券已过期") - } - - if amount.Cmp(coupon.MinAmount) < 0 { - return nil, errors.New("订单金额未达到使用优惠券的条件") - } - - if coupon.UserID != nil { - switch *coupon.UserID { - // 指定用户的优惠券 - case uid: - amount = amount.Sub(coupon.Amount) - if expireAt.IsZero() { - _, err = q.Coupon. - Where(q.Coupon.ID.Eq(coupon.ID)). - Update(q.Coupon.Status, int32(coupon2.StatusUsed)) - if err != nil { - return nil, err - } - } - // 该优惠券不属于当前用户 - default: - return nil, errors.New("优惠券不属于当前用户") - } - } else { - // 公开优惠券 - amount = amount.Sub(coupon.Amount) - } - } - - // 生成订单号 - tradeNo, err := ID.GenSerial(context.Background()) - if err != nil { - return nil, err - } - - // 创建支付订单 - var payUrl string - var acquirer trade2.Acquirer - switch method { - - // 调用商福通接口 - case trade2.MethodSftAlipay, trade2.MethodSftWeChat: + // 商福通 + 手机网站 + case (method == trade2.MethodSftAlipay || method == trade2.MethodSftWeChat) && platform == trade2.PlatformMobile: var payType g.SftPayType if method == trade2.MethodSftAlipay { payType = g.SftAlipay @@ -328,24 +181,31 @@ func (s *tradeService) SendCreateTradeByRedirect(q *q.Query, uid int32, now time return nil, err } payUrl = u.Z(u.Z(resp.PayInfo).PayUrl) - if payUrl == "" { - return nil, errors.New("支付接口未返回正确的二维码地址") - } - switch resp.PayType { - case g.SftAlipay: - acquirer = trade2.AcquirerAlipay - case g.SftWeChat: - acquirer = trade2.AcquirerWeChat - case g.SftUnionPay: - acquirer = trade2.AcquirerUnionPay - } // 不支持的支付方式 default: + slog.Warn(ErrTransactionNotSupported.Error(), "method", method, "platform", platform) return nil, ErrTransactionNotSupported } // 保存交易订单 + var trade = m.Trade{ + UserID: uid, + InnerNo: tradeNo, + Subject: subject, + Type: int32(tType), + Method: int32(method), + Platform: int32(platform), + Amount: amount, + PayURL: &payUrl, + } + + err = q.Trade.Create(&trade) + if err != nil { + return nil, err + } + + // 保存用户帐单 var billType bill2.Type switch tType { case trade2.TypeRecharge: @@ -354,22 +214,6 @@ func (s *tradeService) SendCreateTradeByRedirect(q *q.Query, uid int32, now time billType = bill2.TypeConsume } - var trade = m.Trade{ - UserID: uid, - InnerNo: tradeNo, - Subject: subject, - Method: int32(method), - Type: int32(tType), - Amount: amount, - PayURL: &payUrl, - Acquirer: int32(acquirer), - } - err = q.Trade.Create(&trade) - if err != nil { - return nil, err - } - - // 保存用户帐单 var bill = m.Bill{ BillNo: ID.GenReadable("bil"), UserID: uid, @@ -378,6 +222,7 @@ func (s *tradeService) SendCreateTradeByRedirect(q *q.Query, uid int32, now time Type: int32(billType), Amount: amount, } + err = q.Bill. Omit(q.Bill.ResourceID, q.Bill.RefundID). Create(&bill) @@ -398,78 +243,12 @@ func (s *tradeService) SendCreateTradeByRedirect(q *q.Query, uid int32, now time Trade: &trade, }, nil } -func (s *tradeService) VerifyCreateTrade(data *TradeVerifyData) (*TradeSuccessResult, error) { - var tradeNo = data.TradeNo - var method = data.Method - - // 检查交易号是否存在 - var transId string - var paidAt time.Time - var payment decimal.Decimal - switch method { - - // 检查支付宝交易 - case trade2.MethodAlipay: - resp, err := g.Alipay.TradeQuery(context.Background(), alipay.TradeQuery{ - OutTradeNo: tradeNo, - }) - if err != nil { - return nil, err - } - if resp.Code != alipay.CodeSuccess { - slog.Warn("支付宝交易查询失败", "code", resp.Code, "sub_code", resp.SubCode, "msg", resp.Msg) - return nil, errors.New("交易查询失败") - } - if resp.TradeStatus != alipay.TradeStatusSuccess { - return nil, ErrTransactionNotPaid - } - - transId = resp.TradeNo - payment, err = decimal.NewFromString(resp.TotalAmount) - if err != nil { - return nil, err - } - paidAt, err = time.Parse("2006-01-02 15:04:05", resp.SendPayDate) - if err != nil { - return nil, err - } - - // 检查微信交易 - case trade2.MethodWeChat: - resp, _, err := g.WechatPay.Native.QueryOrderByOutTradeNo(context.Background(), native.QueryOrderByOutTradeNoRequest{ - OutTradeNo: &tradeNo, - Mchid: &env.WechatPayMchId, - }) - if err != nil { - return nil, err - } - if *resp.TradeState != "SUCCESS" { - return nil, ErrTransactionNotPaid - } - - transId = *resp.TransactionId - payment = decimal.NewFromInt(*resp.Amount.PayerTotal).Div(decimal.NewFromInt(100)) - paidAt, err = time.Parse(time.RFC3339, *resp.SuccessTime) - if err != nil { - return nil, err - } - - // 不支持的支付方式 - default: - return nil, ErrTransactionNotSupported - } - - return &TradeSuccessResult{ - TransId: transId, - Payment: payment, - Time: paidAt, - }, nil -} func (s *tradeService) OnTradeCreated(q *q.Query, data *OnTradeCreateData) (*m.Trade, error) { var transId = data.TransId var tradeNo = data.TradeNo var payment = data.Payment var paidAt = data.Time + var acquirer = data.Acquirer // 获取交易信息 trade, err := q.Trade. @@ -491,6 +270,7 @@ func (s *tradeService) OnTradeCreated(q *q.Query, data *OnTradeCreateData) (*m.T trade.Status = int32(trade2.StatusSuccess) trade.OuterNo = &transId trade.Payment = payment + trade.Acquirer = int32(acquirer) trade.PaidAt = u.P(orm.LocalDateTime(paidAt)) trade.PayURL = u.P("") _, err = q.Trade.Updates(trade) @@ -574,12 +354,106 @@ func (s *tradeService) OnTradeRefunded(q *q.Query, tradeNo string, now time.Time panic("todo") } +func (s *tradeService) VerifyTrade(data *TradeVerifyData) (*TradeSuccessResult, error) { + var tradeNo = data.TradeNo + var method = data.Method + + // 检查交易号是否存在 + var transId string + var paidAt time.Time + var payment decimal.Decimal + switch method { + + // 检查支付宝交易 + case trade2.MethodAlipay: + resp, err := g.Alipay.TradeQuery(context.Background(), alipay.TradeQuery{ + OutTradeNo: tradeNo, + }) + if err != nil { + return nil, err + } + if resp.Code != alipay.CodeSuccess { + slog.Warn("支付宝交易查询失败", "code", resp.Code, "sub_code", resp.SubCode, "msg", resp.Msg) + return nil, errors.New("交易查询失败") + } + if resp.TradeStatus != alipay.TradeStatusSuccess { + return nil, ErrTransactionNotPaid + } + + transId = resp.TradeNo + payment, err = decimal.NewFromString(resp.TotalAmount) + if err != nil { + return nil, err + } + paidAt, err = time.Parse("2006-01-02 15:04:05", resp.SendPayDate) + if err != nil { + return nil, err + } + + // 检查微信交易 + case trade2.MethodWeChat: + resp, _, err := g.WechatPay.Native.QueryOrderByOutTradeNo(context.Background(), native.QueryOrderByOutTradeNoRequest{ + OutTradeNo: &tradeNo, + Mchid: &env.WechatPayMchId, + }) + if err != nil { + return nil, err + } + if *resp.TradeState != "SUCCESS" { + return nil, ErrTransactionNotPaid + } + + transId = *resp.TransactionId + payment = decimal.NewFromInt(*resp.Amount.PayerTotal).Div(decimal.NewFromInt(100)) + paidAt, err = time.Parse(time.RFC3339, *resp.SuccessTime) + if err != nil { + return nil, err + } + + // 检查商福通交易 + case trade2.MethodSft, trade2.MethodSftAlipay, trade2.MethodSftWeChat: + resp, err := g.SFTPay.QueryTrade(&g.QueryTradeReq{ + MchOrderNo: &tradeNo, + }) + if err != nil { + return nil, err + } + if resp.State != g.SftTradeSuccess { + return nil, ErrTransactionNotPaid + } + + if resp.PayOrderId == nil { + return nil, errors.New("商福通交易号不存在") + } + if resp.PayTime == nil { + return nil, errors.New("商福通交易时间不存在") + } + transId = *resp.PayOrderId + payment = decimal.NewFromInt(resp.Amount).Div(decimal.NewFromInt(100)) + paidAt, err = time.Parse("2006-01-02 15:04:05", *resp.PayTime) + if err != nil { + return nil, err + } + + // 不支持的支付方式 + default: + return nil, ErrTransactionNotSupported + } + + return &TradeSuccessResult{ + TransId: transId, + Payment: payment, + Time: paidAt, + }, nil +} + type TradeCreateData struct { Subject string Amount decimal.Decimal ExpireAt time.Time Type trade2.Type Method trade2.Method + Platform trade2.Platform CouponCode string } @@ -596,9 +470,10 @@ type TradeVerifyData struct { } type TradeSuccessResult struct { - TransId string - Payment decimal.Decimal - Time time.Time + TransId string + Payment decimal.Decimal + Time time.Time + Acquirer trade2.Acquirer } type OnTradeCreateData struct { @@ -606,13 +481,6 @@ type OnTradeCreateData struct { TradeSuccessResult } -type TradePlatform int - -const ( - TradePlatformDesktop TradePlatform = iota + 1 // 桌面端 - TradePlatformMobile // 移动端 -) - type TradeErr string func (e TradeErr) Error() string { @@ -620,6 +488,6 @@ func (e TradeErr) Error() string { } var ( - ErrTransactionNotPaid = TradeErr("交易未支付") - ErrTransactionNotSupported = TradeErr("不支持的支付方式") + ErrTransactionNotPaid = core.NewBizErr("交易未支付") + ErrTransactionNotSupported = core.NewBizErr("不支持的支付方式") ) diff --git a/web/web.go b/web/web.go index ab80db9..f05e819 100644 --- a/web/web.go +++ b/web/web.go @@ -126,50 +126,52 @@ func newLogger() fiber.Handler { return false }, Done: func(c *fiber.Ctx, logBytes []byte) { - var logStr = strings.TrimPrefix(string(logBytes), "🚀") - var logVars = strings.Split(logStr, "|") + go func(ip, ua, method, path string, status int, logBytes []byte) { + var logStr = strings.TrimPrefix(string(logBytes), "🚀") + var logVars = strings.Split(logStr, "|") - var reqTimeStr = strings.TrimSpace(logVars[0]) - reqTime, err := time.ParseInLocation("2006-01-02 15:04:05", reqTimeStr, time.Local) - if err != nil { - slog.Error("时间解析错误", slog.Any("err", err)) - return - } + var reqTimeStr = strings.TrimSpace(logVars[0]) + reqTime, err := time.ParseInLocation("2006-01-02 15:04:05", reqTimeStr, time.Local) + if err != nil { + slog.Error("时间解析错误", slog.Any("err", err)) + return + } - var authInfo = strings.Split(strings.TrimSpace(logVars[1]), " ") - var authType = auth.PayloadTypeFromStr(strings.TrimSpace(authInfo[0])) - authID, err := strconv.Atoi(strings.TrimSpace(authInfo[1])) - if err != nil { - slog.Error("负载ID解析错误", slog.Any("err", err)) - return - } + var authInfo = strings.Split(strings.TrimSpace(logVars[1]), " ") + var authType = auth.PayloadTypeFromStr(strings.TrimSpace(authInfo[0])) + authID, err := strconv.Atoi(strings.TrimSpace(authInfo[1])) + if err != nil { + slog.Error("负载ID解析错误", slog.Any("err", err)) + return + } - var latency = strings.TrimSpace(logVars[4]) + var latency = strings.TrimSpace(logVars[4]) - var errStr = strings.TrimSpace(logVars[5]) + var errStr = strings.TrimSpace(logVars[5]) - var item = &m.LogsRequest{ - IP: c.IP(), - Ua: u.P(c.Get("User-Agent")), - Method: c.Method(), - Path: c.Path(), - Latency: &latency, - Status: int32(c.Response().StatusCode()), - Error: &errStr, - Time: u.P(orm.LocalDateTime(reqTime)), - } - if authType != auth.PayloadNone { - item.Identity = u.P(int32(authType)) - } - if authID != 0 { - item.Visitor = u.P(int32(authID)) - } + var item = &m.LogsRequest{ + IP: ip, + Ua: u.P(ua), + Method: method, + Path: path, + Latency: &latency, + Status: int32(status), + Error: &errStr, + Time: u.P(orm.LocalDateTime(reqTime)), + } + if authType != auth.PayloadNone { + item.Identity = u.P(int32(authType)) + } + if authID != 0 { + item.Visitor = u.P(int32(authID)) + } - err = q.LogsRequest.Create(item) - if err != nil { - slog.Error("日志记录错误", slog.Any("err", err)) - return - } + err = q.LogsRequest.Create(item) + if err != nil { + slog.Error("日志记录错误", slog.Any("err", err)) + return + } + }(c.IP(), c.Get("User-Agent"), c.Method(), c.Path(), c.Response().StatusCode(), logBytes) }, }) }