From 624934897e992ba8ba95d2d78c92dda7e040fc94 Mon Sep 17 00:00:00 2001 From: dengbiao Date: Tue, 12 Nov 2024 11:44:37 +0800 Subject: [PATCH] temp push --- .idea/.gitignore | 8 + .idea/modules.xml | 8 + .idea/system_rules.iml | 9 + .idea/vcs.xml | 6 + enum/user_virtual_amount_flow.go | 35 + go.mod | 14 + go.sum | 670 +++++++++++++ init.go | 14 + md/app_redis_key.go | 13 + md/public_platoon.go | 41 + md/user_virtual_coin.go | 14 + ...ty_coin_auto_exchange_egg_person_energy.go | 58 ++ ...vity_coin_auto_exchange_egg_team_energy.go | 58 ++ ...activity_coin_ready_exchange_egg_energy.go | 129 +++ ...y_coin_start_exchange_egg_person_energy.go | 51 + ...ity_coin_start_exchange_egg_team_energy.go | 51 + rule/egg_energy/auto_adjust_prices.go | 109 +++ rule/egg_energy/auto_release_green_energy.go | 116 +++ .../available_green_energy_settlement.go | 51 + rule/egg_energy/community_dividends.go | 106 ++ .../deal_available_green_energy_points.go | 493 ++++++++++ .../enum/available_egg_energy_points_flow.go | 41 + rule/egg_energy/give_activty_coin.go | 244 +++++ rule/egg_energy/green_energy_settlement.go | 276 ++++++ rule/egg_energy/md/egg_energy_green_energy.go | 99 ++ rule/egg_energy/md/mq.go | 53 + rule/egg_energy/star_level_dividends.go | 304 ++++++ .../svc/svc_egg_energy_core_data.go | 101 ++ ...public_platoon_user_relation_commission.go | 909 ++++++++++++++++++ rule/user_virtual_coin.go | 138 +++ ...vc_user_virtual_amount_redis_mutex_lock.go | 84 ++ utils/cache/base.go | 421 ++++++++ utils/cache/redis.go | 413 ++++++++ utils/cache/redis_cluster.go | 622 ++++++++++++ utils/cache/redis_pool.go | 324 +++++++ utils/cache/redis_pool_cluster.go | 617 ++++++++++++ utils/convert.go | 388 ++++++++ utils/curl.go | 209 ++++ utils/file.go | 22 + utils/format.go | 59 ++ utils/logx/log.go | 245 +++++ utils/logx/output.go | 105 ++ utils/logx/sugar.go | 192 ++++ utils/serialize.go | 23 + utils/string.go | 203 ++++ utils/time.go | 295 ++++++ utils/time2s.go | 29 + utils/uuid.go | 38 + 48 files changed, 8508 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/modules.xml create mode 100644 .idea/system_rules.iml create mode 100644 .idea/vcs.xml create mode 100644 enum/user_virtual_amount_flow.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 init.go create mode 100644 md/app_redis_key.go create mode 100644 md/public_platoon.go create mode 100644 md/user_virtual_coin.go create mode 100644 rule/egg_energy/activity_coin_auto_exchange_egg_person_energy.go create mode 100644 rule/egg_energy/activity_coin_auto_exchange_egg_team_energy.go create mode 100644 rule/egg_energy/activity_coin_ready_exchange_egg_energy.go create mode 100644 rule/egg_energy/activity_coin_start_exchange_egg_person_energy.go create mode 100644 rule/egg_energy/activity_coin_start_exchange_egg_team_energy.go create mode 100644 rule/egg_energy/auto_adjust_prices.go create mode 100644 rule/egg_energy/auto_release_green_energy.go create mode 100644 rule/egg_energy/available_green_energy_settlement.go create mode 100644 rule/egg_energy/community_dividends.go create mode 100644 rule/egg_energy/deal_available_green_energy_points.go create mode 100644 rule/egg_energy/enum/available_egg_energy_points_flow.go create mode 100644 rule/egg_energy/give_activty_coin.go create mode 100644 rule/egg_energy/green_energy_settlement.go create mode 100644 rule/egg_energy/md/egg_energy_green_energy.go create mode 100644 rule/egg_energy/md/mq.go create mode 100644 rule/egg_energy/star_level_dividends.go create mode 100644 rule/egg_energy/svc/svc_egg_energy_core_data.go create mode 100644 rule/public_platoon_user_relation_commission.go create mode 100644 rule/user_virtual_coin.go create mode 100644 svc/svc_user_virtual_amount_redis_mutex_lock.go create mode 100644 utils/cache/base.go create mode 100644 utils/cache/redis.go create mode 100644 utils/cache/redis_cluster.go create mode 100644 utils/cache/redis_pool.go create mode 100644 utils/cache/redis_pool_cluster.go create mode 100644 utils/convert.go create mode 100644 utils/curl.go create mode 100644 utils/file.go create mode 100644 utils/format.go create mode 100644 utils/logx/log.go create mode 100644 utils/logx/output.go create mode 100644 utils/logx/sugar.go create mode 100644 utils/serialize.go create mode 100644 utils/string.go create mode 100644 utils/time.go create mode 100644 utils/time2s.go create mode 100644 utils/uuid.go diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a09eead --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/system_rules.iml b/.idea/system_rules.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/system_rules.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/enum/user_virtual_amount_flow.go b/enum/user_virtual_amount_flow.go new file mode 100644 index 0000000..367d430 --- /dev/null +++ b/enum/user_virtual_amount_flow.go @@ -0,0 +1,35 @@ +package enum + +// UserVirtualAmountFlowTransferType 用户虚拟币流水-类型 +type UserVirtualAmountFlowTransferType int + +const ( + EggEnergyCommunityDividends UserVirtualAmountFlowTransferType = iota + 1 + EggEnergyWatchAdRewardPersonalActiveCoin + EggEnergyWatchAdRewardTeamActiveCoin + EggPointCoinToExchangeToPersonEggEnergy + EggPointCoinToExchangeToTeamEggEnergy + PersonEggGetByEnergyEggPointCoin + TeamEggGetByEnergyEggPointCoin +) + +func (kind UserVirtualAmountFlowTransferType) String() string { + switch kind { + case EggEnergyCommunityDividends: + return "蛋蛋能量-社区分红" + case EggEnergyWatchAdRewardPersonalActiveCoin: + return "浏览视频" //浏览视频奖励-个人蛋蛋积分 + case EggEnergyWatchAdRewardTeamActiveCoin: + return "圈层奖励" //浏览视频奖励-团队蛋蛋积分 + case EggPointCoinToExchangeToPersonEggEnergy: + return "个人蛋蛋积分兑换" + case EggPointCoinToExchangeToTeamEggEnergy: + return "团队蛋蛋积分兑换" + case PersonEggGetByEnergyEggPointCoin: + return "个人蛋蛋积分兑换蛋蛋能量" + case TeamEggGetByEnergyEggPointCoin: + return "团队蛋蛋积分兑换蛋蛋能量" + default: + return "未知状态" + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..323b7f1 --- /dev/null +++ b/go.mod @@ -0,0 +1,14 @@ +module code.fnuoos.com/EggPlanet/egg_system_rules.git + +go 1.19 + +// go.mod文件中 +//replace code.fnuoos.com/EggPlanet/egg_models.git => E:/company/Egg/egg_models + +require ( + code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git v0.0.5 + github.com/shopspring/decimal v1.3.1 + xorm.io/xorm v1.3.1 +) + +require code.fnuoos.com/EggPlanet/egg_models.git v0.2.1-0.20241112032738-ca9b07ba7b24 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9d0a348 --- /dev/null +++ b/go.sum @@ -0,0 +1,670 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +code.fnuoos.com/EggPlanet/egg_models.git v0.2.1-0.20241112032738-ca9b07ba7b24 h1:zH+UExmxPnD0vUTy2aGXG/sGH+g/oLtMXbO/c8iRDz4= +code.fnuoos.com/EggPlanet/egg_models.git v0.2.1-0.20241112032738-ca9b07ba7b24/go.mod h1:Ktgi+Hk87UlF7/7wg1BvQnAbXWxGa3y+OqDXSD0AA4Q= +code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git v0.0.5/go.mod h1:TTcCnFn/LhBGapnutpezlW+GXkLRNPMWkziOoCsXQqY= +code.fnuoos.com/go_rely_warehouse/zyos_model.git v0.0.4-0.20241017011352-f7f92494c0f4/go.mod h1:nT2x13YFgrS3tS1fDyUR6q/GNIK+hPw7bdzZXz99SM0= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= +gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= +github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= +github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= +github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= +github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE= +github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= +github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= +github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= +github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc= +github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/syyongx/php2go v0.9.8/go.mod h1:meN2eIhhUoxOd2nMxbpe8g6cFPXI5O9/UAAuz7oDdzw= +github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +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/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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-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/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/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-20201126233918-771906719818/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-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/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/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +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= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= +modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= +modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= +modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= +modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= +modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= +modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= +modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= +modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= +modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= +modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= +modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= +modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= +modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= +modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= +modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= +modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= +modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= +modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= +modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= +modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= +modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= +modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= +modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= +modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= +modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= +modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= +modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= +modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= +modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4= +modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= +modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= +modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= +modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= +modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= +modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= +modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= +modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= +modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= +modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= +modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= +modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= +modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= +modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= +modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= +modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= +modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= +modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= +modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= +modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= +modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= +modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= +modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= +modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= +modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= +modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= +modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= +modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= +modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= +modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= +modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= +modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= +modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= +modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= +modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= +modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= +modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= +modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= +xorm.io/xorm v1.3.1/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw= diff --git a/init.go b/init.go new file mode 100644 index 0000000..e04493a --- /dev/null +++ b/init.go @@ -0,0 +1,14 @@ +package egg_system_rules + +import ( + "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/cache" +) + +func Init(redisAddr string) (err error) { + if redisAddr != "" { + cache.NewRedis(redisAddr) + } + _, err = cache.SelectDb(md.RedisDataBase) + return +} diff --git a/md/app_redis_key.go b/md/app_redis_key.go new file mode 100644 index 0000000..f870c6a --- /dev/null +++ b/md/app_redis_key.go @@ -0,0 +1,13 @@ +package md + +const RedisDataBase = 0 //Redis 0 号库 + +// 缓存key统一管理 +const ( + EggEnergyNowPriceUpdateLock = "egg_energy_core_data_update_lock" // 当前价格(能拿到锁才能更新价格) + DealEggEnergyNowPriceRequestIdPrefix = "egg_energy_core_data:%d" + + UserFinValidUpdateLock = "%s:user_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额) + UserVirtualAmountUpdateLock = "%s:user_virtual_amount_update_lock:%s" // 用户虚拟币更新锁(能拿到锁才能更新余额) + +) diff --git a/md/public_platoon.go b/md/public_platoon.go new file mode 100644 index 0000000..d1e01a0 --- /dev/null +++ b/md/public_platoon.go @@ -0,0 +1,41 @@ +package md + +type StandardPublicPlatoonRelation struct { + FatherUid1 int64 `json:"father_uid1"` + FatherUid2 int64 `json:"father_uid2"` + FatherUid3 int64 `json:"father_uid3"` + FatherUid4 int64 `json:"father_uid4"` + FatherUid5 int64 `json:"father_uid5"` + FatherUid6 int64 `json:"father_uid6"` + FatherUid7 int64 `json:"father_uid7"` + FatherUid8 int64 `json:"father_uid8"` + FatherUid9 int64 `json:"father_uid9"` + Pid1 int64 `json:"pid1"` + Pid2 int64 `json:"pid2"` + Pid3 int64 `json:"pid3"` + Pid4 int64 `json:"pid4"` + Pid5 int64 `json:"pid5"` + Pid6 int64 `json:"pid6"` + Pid7 int64 `json:"pid7"` + Pid8 int64 `json:"pid8"` + Pid9 int64 `json:"pid9"` + Position1 int64 `json:"position1"` + Position2 int64 `json:"position2"` + Position3 int64 `json:"position3"` + Position4 int64 `json:"position4"` + Position5 int64 `json:"position5"` + Position6 int64 `json:"position6"` + Position7 int64 `json:"position7"` + Position8 int64 `json:"position8"` + Position9 int64 `json:"position9"` + Level1 int `json:"level1"` + Level2 int `json:"level2"` + Level3 int `json:"level3"` + Level4 int `json:"level4"` + Level5 int `json:"level5"` + Level6 int `json:"level6"` + Level7 int `json:"level7"` + Level8 int `json:"level8"` + Level9 int `json:"level9"` + LevelTotal int `json:"level_total"` +} diff --git a/md/user_virtual_coin.go b/md/user_virtual_coin.go new file mode 100644 index 0000000..5ed6a69 --- /dev/null +++ b/md/user_virtual_coin.go @@ -0,0 +1,14 @@ +package md + +const ( + UserVirtualAmountRedisKey = "user_virtual_amount:%d:user:%d" +) + +type DealUserVirtualCoinReq struct { + Kind string `json:"kind"` + Title string `json:"title"` + TransferType int `json:"transfer_type"` + CoinId int `json:"coin_id"` + Uid int64 `json:"uid"` + Amount float64 `json:"amount"` +} diff --git a/rule/egg_energy/activity_coin_auto_exchange_egg_person_energy.go b/rule/egg_energy/activity_coin_auto_exchange_egg_person_energy.go new file mode 100644 index 0000000..382b751 --- /dev/null +++ b/rule/egg_energy/activity_coin_auto_exchange_egg_person_energy.go @@ -0,0 +1,58 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_system_rules.git/enum" + md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/svc" + egg_system_rules "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils" + zhios_order_relate_logx "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/logx" + "code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" + "fmt" + "xorm.io/xorm" +) + +// ActivityCoinAutoExchangeEggPersonEnergy 个人蛋蛋积分【开始】兑换成个人蛋蛋能量 +func ActivityCoinAutoExchangeEggPersonEnergy(engine *xorm.Engine, req md.EggEnergyStructForAutoExchangeGreenEnergy, ch *rabbit.Channel) (err error) { + session := engine.NewSession() + defer func() { + session.Close() + if err := recover(); err != nil { + _ = zhios_order_relate_logx.Error(err) + } + }() + session.Begin() + + eggEnergyCoreData, cb, err := svc.GetEggEnergyCoreData(engine) + if err != nil { + return err + } + if cb != nil { + defer cb() // 释放锁 + } + + //1、计算涨价公式 + err1, calcPriceIncreaseFormulaResp := CalcPriceIncreaseFormula(req.AutoExchangeNumsAmount, eggEnergyCoreData) + if err1 != nil { + _ = session.Rollback() + return err1 + } + + //2、增加用户对应蛋蛋能量数量 + err = rule.DealUserVirtualCoin(session, md2.DealUserVirtualCoinReq{ + Kind: "add", + Title: enum.PersonEggGetByEnergyEggPointCoin.String(), + TransferType: int(enum.PersonEggGetByEnergyEggPointCoin), + CoinId: req.EnergyCoinId, + Uid: req.Uid, + Amount: egg_system_rules.StrToFloat64(calcPriceIncreaseFormulaResp.GetEggEnergyNums), + }) + if err != nil { + fmt.Println("ActivityCoinAutoExchangeEggPersonEnergy:::::err111:::", err) + _ = session.Rollback() + return + } + + return session.Commit() +} diff --git a/rule/egg_energy/activity_coin_auto_exchange_egg_team_energy.go b/rule/egg_energy/activity_coin_auto_exchange_egg_team_energy.go new file mode 100644 index 0000000..27c9553 --- /dev/null +++ b/rule/egg_energy/activity_coin_auto_exchange_egg_team_energy.go @@ -0,0 +1,58 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_system_rules.git/enum" + md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/svc" + egg_system_rules "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils" + zhios_order_relate_logx "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/logx" + "code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" + "fmt" + "xorm.io/xorm" +) + +// ActivityCoinAutoExchangeEggTeamEnergy 团队蛋蛋积分【开始】兑换成团队蛋蛋能量 +func ActivityCoinAutoExchangeEggTeamEnergy(engine *xorm.Engine, req md.EggEnergyStructForAutoExchangeGreenEnergy, ch *rabbit.Channel) (err error) { + session := engine.NewSession() + defer func() { + session.Close() + if err := recover(); err != nil { + _ = zhios_order_relate_logx.Error(err) + } + }() + session.Begin() + + eggEnergyCoreData, cb, err := svc.GetEggEnergyCoreData(engine) + if err != nil { + return err + } + if cb != nil { + defer cb() // 释放锁 + } + + //1、计算涨价公式 + err1, calcPriceIncreaseFormulaResp := CalcPriceIncreaseFormula(req.AutoExchangeNumsAmount, eggEnergyCoreData) + if err1 != nil { + _ = session.Rollback() + return err1 + } + + //2、增加用户对应蛋蛋能量数量 + err = rule.DealUserVirtualCoin(session, md2.DealUserVirtualCoinReq{ + Kind: "add", + Title: enum.TeamEggGetByEnergyEggPointCoin.String(), + TransferType: int(enum.TeamEggGetByEnergyEggPointCoin), + CoinId: req.EnergyCoinId, + Uid: req.Uid, + Amount: egg_system_rules.StrToFloat64(calcPriceIncreaseFormulaResp.GetEggEnergyNums), + }) + if err != nil { + fmt.Println("ActivityCoinAutoExchangeEggTeamEnergy:::::err111:::", err) + _ = session.Rollback() + return + } + + return session.Commit() +} diff --git a/rule/egg_energy/activity_coin_ready_exchange_egg_energy.go b/rule/egg_energy/activity_coin_ready_exchange_egg_energy.go new file mode 100644 index 0000000..29f26b3 --- /dev/null +++ b/rule/egg_energy/activity_coin_ready_exchange_egg_energy.go @@ -0,0 +1,129 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_models.git/src/implement" + "code.fnuoos.com/EggPlanet/egg_models.git/src/model" + md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md" + egg_system_rules "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils" + "code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" + "encoding/json" + "errors" + "fmt" + "github.com/shopspring/decimal" + "time" + "xorm.io/xorm" +) + +// ActivityCoinReadyExchangeEggEnergy 蛋蛋积分【准备】兑换成蛋蛋能量 +func ActivityCoinReadyExchangeEggEnergy(engine *xorm.Engine, uid int64, ch *rabbit.Channel) (err error) { + //1、查找`egg_energy_basic_setting` 基础设置 + eggEnergyBasicSettingDb := implement.NewEggEnergyBasicSettingDb(engine) + eggEnergyBasicSetting, err := eggEnergyBasicSettingDb.EggEnergyBasicSettingGetOneByParams(map[string]interface{}{ + "key": "is_open", + "value": 1, + }) + if err != nil { + return + } + if eggEnergyBasicSetting.ExchangeRules == "" { + err = errors.New("自动兑换未设置!") + return + } + + var exchangeRulesStruct *md2.ExchangeRulesStruct + err = json.Unmarshal([]byte(eggEnergyBasicSetting.ExchangeRules), &exchangeRulesStruct) + if err != nil { + return + } + var videoRewardSystem *md2.VideoRewardSystemStruct + err = json.Unmarshal([]byte(eggEnergyBasicSetting.VideoRewardSystem), &videoRewardSystem) + if err != nil { + return + } + if videoRewardSystem.RewardValue == "" || videoRewardSystem.RewardTotalNum == "" || videoRewardSystem.IntervalMinutes == "" || videoRewardSystem.EachRoundHour == "" { + err = errors.New("视屏奖励机制设置未完全!") + return + } + + var autoExchangeNumsByPerson = egg_system_rules.StrToFloat64(exchangeRulesStruct.AutoExchangeNumsByPerson) //个人活跃积分X个自动兑换 + fmt.Println("autoExchangeNumsByPerson>>>>>>>>>>>>", autoExchangeNumsByPerson) + var autoExchangeNumsByTeam = egg_system_rules.StrToFloat64(exchangeRulesStruct.AutoExchangeNumsByTeam) //个人活跃积分X个自动兑换 + fmt.Println("autoExchangeNumsByTeam>>>>>>>>>>>>", autoExchangeNumsByTeam) + + //2、获取"个人蛋蛋活跃积分" && 获取"团队蛋蛋活跃积分" + var coin1, coin2 model.VirtualCoin + _, err = engine.Where("id = ?", eggEnergyBasicSetting.PersonEggPointsCoinId).Get(&coin1) + if err != nil { + return err + } + _, err = engine.Where("id = ?", eggEnergyBasicSetting.TeamEggPointsCoinId).Get(&coin2) + if err != nil { + return err + } + personActivePointsCoinExchangeRatioValue, _ := decimal.NewFromString(coin1.ExchangeRatio) + teamActivePointsCoinExchangeRatioValue, _ := decimal.NewFromString(coin2.ExchangeRatio) + + //3、当前 "个人活跃积分"可以自动兑换的用户数据 && "团队活跃积分"可以自动兑换的用户数据 + var personUserVirtualAmount model.UserVirtualAmount + _, err = engine.Where("coin_id = ?", eggEnergyBasicSetting.PersonEggPointsCoinId).Get(&personUserVirtualAmount) + if err != nil { + fmt.Println("err:::::1111", err) + return + } + var teamUserVirtualAmount model.UserVirtualAmount + _, err = engine.Where("coin_id = ?", eggEnergyBasicSetting.TeamEggPointsCoinId).Get(&teamUserVirtualAmount) + if err != nil { + fmt.Println("err:::::2222", err) + return + } + + if egg_system_rules.StrToFloat64(personUserVirtualAmount.Amount) < autoExchangeNumsByPerson && egg_system_rules.StrToFloat64(teamUserVirtualAmount.Amount) < autoExchangeNumsByTeam { + return errors.New("当前活跃值无法发起挖掘~") + } + + //session := engine.NewSession() + //defer func() { + // session.Close() + // if err := recover(); err != nil { + // _ = zhios_order_relate_logx.Error(err) + // } + //}() + //session.Begin() + now := time.Now() + + //4、计算能兑换的个人活跃积分数量 + if egg_system_rules.StrToFloat64(personUserVirtualAmount.Amount) >= autoExchangeNumsByPerson { + autoExchangeNumsByPersonValue, _ := decimal.NewFromString(personUserVirtualAmount.Amount) + autoExchangeRateByPersonValue, _ := decimal.NewFromString(exchangeRulesStruct.AutoExchangeRateByPerson) + autoExchangeNumsByPersonAmount := autoExchangeNumsByPersonValue.Mul(autoExchangeRateByPersonValue).Div(personActivePointsCoinExchangeRatioValue).String() //个人活跃积分数量*兑换百分比/兑换比例(与金额) + //TODO::推入rabbitmq 异步处理 + ch.Publish(md2.EggEnergyExchange, md2.EggEnergyStructForStartExchangeGreenEnergy{ + Uid: uid, + Amount: autoExchangeNumsByPersonValue.Mul(autoExchangeRateByPersonValue).String(), + PointCoinId: coin1.Id, + EnergyCoinId: eggEnergyBasicSetting.PersonEggEnergyCoinId, + AutoExchangeNumsAmount: autoExchangeNumsByPersonAmount, + StartExchangeTime: now.Format("2006-01-02 15:04:05"), + EndExchangeTime: now.Add(time.Hour * time.Duration(egg_system_rules.StrToInt64(videoRewardSystem.EachRoundHour))).Format("2006-01-02 15:04:05"), + }, md2.EggEnergyRoutKeyForStartExchangeGreenEnergyToPerson) + } + + //5、计算能兑换的团队活跃积分数量 + if egg_system_rules.StrToFloat64(teamUserVirtualAmount.Amount) >= autoExchangeNumsByTeam { + autoExchangeNumsByTeamValue, _ := decimal.NewFromString(teamUserVirtualAmount.Amount) + autoExchangeRateByTeamValue, _ := decimal.NewFromString(exchangeRulesStruct.AutoExchangeRateByTeam) + autoExchangeNumsByTeamAmount := autoExchangeNumsByTeamValue.Mul(autoExchangeRateByTeamValue).Div(teamActivePointsCoinExchangeRatioValue).String() //团队活跃积分数量*兑换百分比/兑换比例(与金额) + //TODO::推入rabbitmq 异步处理 + ch.Publish(md2.EggEnergyExchange, md2.EggEnergyStructForStartExchangeGreenEnergy{ + Uid: uid, + Amount: autoExchangeNumsByTeamValue.Mul(autoExchangeRateByTeamValue).String(), + PointCoinId: coin2.Id, + EnergyCoinId: eggEnergyBasicSetting.TeamEggEnergyCoinId, + AutoExchangeNumsAmount: autoExchangeNumsByTeamAmount, + StartExchangeTime: now.Format("2006-01-02 15:04:05"), + EndExchangeTime: now.Add(time.Hour * time.Duration(egg_system_rules.StrToInt64(videoRewardSystem.EachRoundHour))).Format("2006-01-02 15:04:05"), + }, md2.EggEnergyRoutKeyForStartExchangeGreenEnergyToTeam) + } + + return +} diff --git a/rule/egg_energy/activity_coin_start_exchange_egg_person_energy.go b/rule/egg_energy/activity_coin_start_exchange_egg_person_energy.go new file mode 100644 index 0000000..1419c3c --- /dev/null +++ b/rule/egg_energy/activity_coin_start_exchange_egg_person_energy.go @@ -0,0 +1,51 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_system_rules.git/enum" + md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md" + egg_system_rules "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils" + zhios_order_relate_logx "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/logx" + "code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" + "fmt" + "xorm.io/xorm" +) + +// ActivityCoinStartExchangeEggPersonEnergy 个人蛋蛋积分【开始】兑换成个人蛋蛋能量 +func ActivityCoinStartExchangeEggPersonEnergy(engine *xorm.Engine, req md.EggEnergyStructForStartExchangeGreenEnergy, ch *rabbit.Channel) (err error) { + session := engine.NewSession() + defer func() { + session.Close() + if err := recover(); err != nil { + _ = zhios_order_relate_logx.Error(err) + } + }() + session.Begin() + + //1、扣除用户对应蛋蛋活跃积分数量 + err = rule.DealUserVirtualCoin(session, md2.DealUserVirtualCoinReq{ + Kind: "sub", + Title: enum.EggPointCoinToExchangeToPersonEggEnergy.String(), + TransferType: int(enum.EggPointCoinToExchangeToPersonEggEnergy), + CoinId: req.PointCoinId, + Uid: req.Uid, + Amount: egg_system_rules.StrToFloat64(req.Amount), + }) + if err != nil { + fmt.Println("ActivityCoinStartExchangeEggPersonEnergy:::::err111:::", err) + _ = session.Rollback() + return + } + + //TODO::推入rabbitmq 异步处理 + ch.Publish(md.EggEnergyExchange, md.EggEnergyStructForAutoExchangeGreenEnergy{ + Uid: req.Uid, + EnergyCoinId: req.EnergyCoinId, + AutoExchangeNumsAmount: req.AutoExchangeNumsAmount, + StartExchangeTime: req.StartExchangeTime, + EndExchangeTime: req.StartExchangeTime, + }, md.EggEnergyRoutKeyForAutoExchangeGreenEnergyToPerson) + + return session.Commit() +} diff --git a/rule/egg_energy/activity_coin_start_exchange_egg_team_energy.go b/rule/egg_energy/activity_coin_start_exchange_egg_team_energy.go new file mode 100644 index 0000000..37a5812 --- /dev/null +++ b/rule/egg_energy/activity_coin_start_exchange_egg_team_energy.go @@ -0,0 +1,51 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_system_rules.git/enum" + md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md" + egg_system_rules "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils" + zhios_order_relate_logx "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/logx" + "code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" + "fmt" + "xorm.io/xorm" +) + +// ActivityCoinStartExchangeEggTeamEnergy 团队蛋蛋积分【开始】兑换成团队蛋蛋能量 +func ActivityCoinStartExchangeEggTeamEnergy(engine *xorm.Engine, req md.EggEnergyStructForStartExchangeGreenEnergy, ch *rabbit.Channel) (err error) { + session := engine.NewSession() + defer func() { + session.Close() + if err := recover(); err != nil { + _ = zhios_order_relate_logx.Error(err) + } + }() + session.Begin() + + //1、扣除用户对应蛋蛋活跃积分数量 + err = rule.DealUserVirtualCoin(session, md2.DealUserVirtualCoinReq{ + Kind: "sub", + Title: enum.EggPointCoinToExchangeToTeamEggEnergy.String(), + TransferType: int(enum.EggPointCoinToExchangeToTeamEggEnergy), + CoinId: req.PointCoinId, + Uid: req.Uid, + Amount: egg_system_rules.StrToFloat64(req.Amount), + }) + if err != nil { + fmt.Println("ActivityCoinStartExchangeEggTeamEnergy:::::err111:::", err) + _ = session.Rollback() + return + } + + //TODO::推入rabbitmq 异步处理 + ch.Publish(md.EggEnergyExchange, md.EggEnergyStructForAutoExchangeGreenEnergy{ + Uid: req.Uid, + EnergyCoinId: req.EnergyCoinId, + AutoExchangeNumsAmount: req.AutoExchangeNumsAmount, + StartExchangeTime: req.StartExchangeTime, + EndExchangeTime: req.StartExchangeTime, + }, md.EggEnergyRoutKeyForAutoExchangeGreenEnergyToTeam) + + return session.Commit() +} diff --git a/rule/egg_energy/auto_adjust_prices.go b/rule/egg_energy/auto_adjust_prices.go new file mode 100644 index 0000000..9c18125 --- /dev/null +++ b/rule/egg_energy/auto_adjust_prices.go @@ -0,0 +1,109 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_models.git/src/implement" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/enum" + md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/svc" + egg_system_rules "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils" + zhios_order_relate_logx "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/logx" + "encoding/json" + "errors" + "fmt" + "github.com/shopspring/decimal" + "time" + "xorm.io/xorm" +) + +// AutoAdjustPrice 自动调整价格 +func AutoAdjustPrice(engine *xorm.Engine) (err error) { + now := time.Now() + fmt.Println(now.Hour()) + + //1、查找 `egg_energy_core_data` && `egg_energy_basic_setting` + eggEnergyCoreData, cb, err := svc.GetEggEnergyCoreData(engine) + if err != nil { + return + } + if cb != nil { + defer cb() // 释放锁 + } + + eggEnergyBasicSettingDb := implement.NewEggEnergyBasicSettingDb(engine) + eggEnergyBasicSetting, err := eggEnergyBasicSettingDb.EggEnergyBasicSettingGetOneByParams(map[string]interface{}{ + "key": "is_open", + "value": 1, + }) + if err != nil { + return + } + if eggEnergyBasicSetting == nil { + err = errors.New("蛋蛋能量设置未开启!") + return + } + var priceSettingStruct *md2.PriceSettingStruct + err = json.Unmarshal([]byte(eggEnergyBasicSetting.PriceSetting), &priceSettingStruct) + if err != nil { + return + } + if priceSettingStruct.PriceHigherThanValue == "" || priceSettingStruct.MarketplaceMerchantsNumsExchangeMarketplaceMerchantsFundValue == "" || priceSettingStruct.PriceBelowValue == "" || priceSettingStruct.MarketplaceMerchantsFundExchangeMarketplaceMerchantsNumsValue == "" { + err = errors.New("价格设置未完全!") + return + } + + session := engine.NewSession() + defer func() { + session.Close() + if err := recover(); err != nil { + _ = zhios_order_relate_logx.Error(err) + } + }() + session.Begin() + + priceHigherThanValue, _ := decimal.NewFromString(priceSettingStruct.PriceHigherThanValue) + marketplaceMerchantsNumsExchangeMarketplaceMerchantsFundValue, _ := decimal.NewFromString(priceSettingStruct.MarketplaceMerchantsNumsExchangeMarketplaceMerchantsFundValue) + priceBelowValue, _ := decimal.NewFromString(priceSettingStruct.PriceBelowValue) + marketplaceMerchantsFundExchangeMarketplaceMerchantsNumsValue, _ := decimal.NewFromString(priceSettingStruct.MarketplaceMerchantsFundExchangeMarketplaceMerchantsNumsValue) + nowPriceValue, _ := decimal.NewFromString(eggEnergyCoreData.NowPrice) + if nowPriceValue.GreaterThan(priceHigherThanValue) { + //当价格涨到设置某个价格时,自动执行 市商数量 兑换 市商资金 (降价公式) + err1, resp := CalcPriceReductionFormula(marketplaceMerchantsNumsExchangeMarketplaceMerchantsFundValue.String(), eggEnergyCoreData) + if err1 != nil { + fmt.Println("err1111:::", err1) + _ = session.Rollback() + return err1 + } + + // 市商数量 减少、市商资金 增加 + err = DealAvailableGreenEnergyCoin(session, int(enum.MarketplaceMerchantNumsAutoExchangeMarketplaceMerchantFunds), egg_system_rules.StrToFloat64(marketplaceMerchantsNumsExchangeMarketplaceMerchantsFundValue.String()), egg_system_rules.StrToFloat64(amount), enum.MarketplaceMerchantNumsAutoExchangeMarketplaceMerchantFunds.String(), oneCirclesGreenEnergyBasicSetting, afterPriceValue, masterId) + if err != nil { + fmt.Println("err:::::22222", err) + _ = session.Rollback() + return err + } + } + if nowPriceValue.LessThan(priceBelowValue) { + //当价格降到设置某个价格时,自动执行 市商资金 兑换 市商数量 (涨价公式) + err1, resp := CalcPriceReductionFormula(marketplaceMerchantsNumsExchangeMarketplaceMerchantsFundValue.String(), eggEnergyCoreData) + if err1 != nil { + fmt.Println("err1111:::", err1) + _ = session.Rollback() + return err1 + } + + // 市商数量 增加、市商资金 减少 + err = DealAvailableGreenEnergyCoin(session, int(enum.MarketplaceMerchantFundsAutoExchangeMarketplaceMerchantNums), egg_system_rules.StrToFloat64(greenEnergy), egg_system_rules.StrToFloat64(marketplaceMerchantsFundExchangeMarketplaceMerchantsNumsValue.String()), enum.MarketplaceMerchantFundsAutoExchangeMarketplaceMerchantNums.String(), oneCirclesGreenEnergyBasicSetting, afterPriceValue, masterId) + if err != nil { + fmt.Println("err:::::44444", err) + _ = session.Rollback() + return err + } + } + + err = session.Commit() + if err != nil { + _ = session.Rollback() + return errors.New("事务提交失败") + } + return +} diff --git a/rule/egg_energy/auto_release_green_energy.go b/rule/egg_energy/auto_release_green_energy.go new file mode 100644 index 0000000..6f55d01 --- /dev/null +++ b/rule/egg_energy/auto_release_green_energy.go @@ -0,0 +1,116 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_system_rules.git/db" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/db/model" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/cache" + zhios_order_relate_logx "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/logx" + "errors" + "fmt" + "github.com/shopspring/decimal" + "time" + "xorm.io/xorm" +) + +const LockKey = "auto_release_exchange_green_energy_lock_key" + +// AutoReleaseExchangeGreenEnergy 结算绿色能量自动释放成可用绿色能量 +func AutoReleaseExchangeGreenEnergy(engine *xorm.Engine, masterId string) (err error) { + now := time.Now() + fmt.Println(now.Hour()) + if !(now.Hour() > 1 && now.Hour() < 8) { + //TODO::只在凌晨一点 ~ 凌晨 8 点运行 + return errors.New("非运行时间") + } + //TODO::增加“悲观锁”防止串行 + getString, _ := cache.GetString(LockKey) + //if err != nil { + // return err + //} + if getString != "" { + fmt.Println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", "上一次结算未执行完") + return errors.New("上一次结算未执行完") + } + cache.SetEx(LockKey, "running", 3600*8) //8小时 + + //1、查找 `one_circles_green_energy_basic_setting` 基础设置 && `one_circles_public_platoon_basic_setting` 基础设置 + oneCirclesGreenEnergyBasicSetting, err := db.OneCirclesGreenEnergyBasicSettingGetOneByParams(engine, map[string]interface{}{ + "key": "is_open", + "value": 1, + }) + if err != nil { + return + } + + settlementQuantity := oneCirclesGreenEnergyBasicSetting.SettlementQuantity + fmt.Println("settlementQuantity>>>>>>>>>>>>", settlementQuantity) + + //2、当前 "可用"绿色能量可以兑换的用户数据 + var list1 []model.UserVirtualAmount + err = engine.Where("coin_id = ?", oneCirclesGreenEnergyBasicSetting.TeamGreenEnergyCoinId).And("amount > ?", 0).Find(&list1) + if err != nil { + fmt.Println("err:::::1111", err) + return + } + + //4、处理释放 + settlementQuantityRadio := decimal.NewFromFloat(100) + settlementQuantityValue := decimal.NewFromInt(int64(settlementQuantity)).Div(settlementQuantityRadio) + for _, v := range list1 { + userAmount, _ := decimal.NewFromString(v.Amount) + settlementQuantityAmount, _ := userAmount.Mul(settlementQuantityValue).Float64() + if settlementQuantityAmount > 0 { + session := engine.NewSession() + defer func() { + session.Close() + if err := recover(); err != nil { + _ = zhios_order_relate_logx.Error(err) + } + }() + session.Begin() + + //4.2给相应的用户加上个人的绿色积分(可用数量) + err = DealUserCoin(session, md.DealUserCoinReq{ + Kind: "add", + Mid: masterId, + Title: md.OneCirclesSettlementGreenEnergyExchangeGreenEnergy, + TransferType: md.OneCirclesSettlementGreenEnergyExchangeGreenEnergyForUserVirtualCoinFlow, + OrdId: "", + CoinId: oneCirclesGreenEnergyBasicSetting.PersonGreenEnergyCoinId, + Uid: v.Uid, + Amount: settlementQuantityAmount, + }) + if err != nil { + _ = session.Rollback() + fmt.Println("err:::::22222", err) + continue + } + + //4.2给相应的用户减少个人的绿色积分(结算数量) + err = DealUserCoin(session, md.DealUserCoinReq{ + Kind: "sub", + Mid: masterId, + Title: md.OneCirclesSettlementGreenEnergyExchangeTobeGreenEnergy, + TransferType: md.OneCirclesSettlementGreenEnergyExchangeTobeGreenEnergyForUserVirtualCoinFlow, + OrdId: "", + CoinId: oneCirclesGreenEnergyBasicSetting.TeamGreenEnergyCoinId, + Uid: v.Uid, + Amount: settlementQuantityAmount, + }) + if err != nil { + _ = session.Rollback() + fmt.Println("err:::::33333", err) + continue + } + + err = session.Commit() + if err != nil { + _ = session.Rollback() + continue + } + } + } + + return +} diff --git a/rule/egg_energy/available_green_energy_settlement.go b/rule/egg_energy/available_green_energy_settlement.go new file mode 100644 index 0000000..d4f7f58 --- /dev/null +++ b/rule/egg_energy/available_green_energy_settlement.go @@ -0,0 +1,51 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_models.git/src/model" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md" + egg_system_rules "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils" + "github.com/shopspring/decimal" +) + +// CalcPriceIncreaseFormula 计算涨价公式(【用户资金 ÷(用户资金+星球价值)÷ 现行数量】 = 用户获得蛋蛋能量个数) +func CalcPriceIncreaseFormula(userAmountValue string, eggEnergyCoreData *model.EggEnergyCoreData) (err error, resp md.CalcPriceIncreaseFormulaResp) { + userAmount, _ := decimal.NewFromString(userAmountValue) //用户资金 + nowPrice, _ := decimal.NewFromString(eggEnergyCoreData.NowPrice) //当前价格 + nowEnergyTotalNums, _ := decimal.NewFromString(eggEnergyCoreData.NowEnergyTotalNums) //现行数量 + planetTotalValue, _ := decimal.NewFromString(eggEnergyCoreData.PlanetTotalValue) //星球价值 + afterPrice := (planetTotalValue.Add(userAmount)).Div(nowEnergyTotalNums).Truncate(8) //涨价后的价格 + getEggEnergyNumsValue := userAmount.Div(afterPrice).Truncate(8) //用户得到绿色能量个数 + afterEnergyTotalNumsValue := nowEnergyTotalNums.Add(getEggEnergyNumsValue) //变更后-现行数量 + afterPlanetTotalValue := afterEnergyTotalNumsValue.Mul(afterPrice) //变更后-星球价值 + + resp.GetEggEnergyNums = getEggEnergyNumsValue.String() + resp.BeforePrice = nowPrice.String() + resp.AfterPrice = afterPrice.String() + resp.BeforePlanetTotalValue = eggEnergyCoreData.PlanetTotalValue + resp.AfterPlanetTotalValue = afterPlanetTotalValue.String() + resp.BeforeEnergyTotalNums = eggEnergyCoreData.NowEnergyTotalNums + resp.AfterEnergyTotalNums = afterEnergyTotalNumsValue.String() + egg_system_rules.FilePutContents("CalcPriceIncreaseFormula", egg_system_rules.SerializeStr(resp)) + return +} + +// CalcPriceReductionFormula 计算降价公式(【用户需兑换蛋蛋能量数量*{星球价值 ÷(用户需兑换蛋蛋能量数量+现行数量} *(1 - 扣比例50% ~ 23%) = 用户获得钱) +func CalcPriceReductionFormula(userExchangeNumsValue string, eggEnergyCoreData *model.EggEnergyCoreData) (err error, resp md.CalcPriceReductionFormulaResp) { + userExchangeNums, _ := decimal.NewFromString(userExchangeNumsValue) //用户兑换绿色能量 + nowEnergyTotalNums, _ := decimal.NewFromString(eggEnergyCoreData.NowEnergyTotalNums) //现行数量 + planetTotalValue, _ := decimal.NewFromString(eggEnergyCoreData.PlanetTotalValue) //星球价值 + afterPrice := planetTotalValue.Div(userExchangeNums.Add(nowEnergyTotalNums)) //降价后的价格 + greenEnergyValues := userExchangeNums.Mul(afterPrice) //用户获得的钱 + afterEnergyTotalNumsValue := nowEnergyTotalNums.Sub(userExchangeNums) //变更后-现行数量 + afterPlanetTotalValue := afterEnergyTotalNumsValue.Mul(afterPrice) //变更后-星球价值 + + resp.GetEggEnergyAmount = greenEnergyValues.Truncate(8).String() + resp.BeforePrice = eggEnergyCoreData.NowPrice + resp.AfterPrice = afterPrice.String() + resp.BeforePlanetTotalValue = eggEnergyCoreData.PlanetTotalValue + resp.AfterPlanetTotalValue = afterPlanetTotalValue.String() + resp.BeforeEnergyTotalNums = eggEnergyCoreData.NowEnergyTotalNums + resp.AfterEnergyTotalNums = afterEnergyTotalNumsValue.String() + egg_system_rules.FilePutContents("CalcPriceReductionFormula", egg_system_rules.SerializeStr(resp)) + return +} diff --git a/rule/egg_energy/community_dividends.go b/rule/egg_energy/community_dividends.go new file mode 100644 index 0000000..7085244 --- /dev/null +++ b/rule/egg_energy/community_dividends.go @@ -0,0 +1,106 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_models.git/src/model" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/enum" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule" + enum2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/enum" + egg_system_rules "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils" + zhios_order_relate_logx "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/logx" + "errors" + "fmt" + "github.com/shopspring/decimal" + "time" + "xorm.io/xorm" +) + +// CommunityDividends 社区分红 +func CommunityDividends(engine *xorm.Engine, masterId string) (err error) { + //1、查找当前是否有待分红的记录 + var m model.EggEnergyCommunityDividends + has, err := engine.Where("is_over = 0").Get(&m) + if err != nil { + fmt.Println("err::::::1111", err.Error()) + return + } + if has { + //3、查找 `egg_energy_community_dividends_with_user` 数据 + var list []model.EggEnergyCommunityDividendsWithUser + err = engine.Where("1 = 1").Find(&list) + totalValue := decimal.NewFromInt(int64(m.Nums)) + singleValue, _ := totalValue.Div(decimal.NewFromInt(int64(len(list)))).Float64() + session := engine.NewSession() + defer func() { + session.Close() + if err := recover(); err != nil { + + _ = zhios_order_relate_logx.Error(err) + } + }() + session.Begin() + now := time.Now() + + for _, v := range list { + //4、 给相应的用户加上分红权益 + err = rule.DealUserVirtualCoin(session, md.DealUserVirtualCoinReq{ + Kind: "add", + Title: enum.EggEnergyCommunityDividends.String(), + TransferType: int(enum.EggEnergyCommunityDividends), + CoinId: m.CoinId, + Uid: v.Uid, + Amount: singleValue, + }) + if err != nil { + _ = session.Rollback() + fmt.Println("err:::::33333", err) + return err + } + + //5、增加 egg_energy_community_dividends_with_user_records 记录 + _, err = session.InsertOne(&model.EggEnergyCommunityDividendsWithUserRecords{ + Uid: v.Uid, + Value: egg_system_rules.Float64ToStr(singleValue), + RecordsId: m.Id, + CoinId: m.CoinId, + CreateAt: now.Format("2006-01-02 15:04:05"), + UpdateAt: now.Format("2006-01-02 15:04:05"), + }) + if err != nil { + _ = session.Rollback() + fmt.Println("err:::::insert", err) + return err + } + } + //6、 增加“社区分红”钱包中的记录值 + totalDividend, _ := totalValue.Float64() + err = DealAvailableGreenEnergyCoin(session, int(enum2.CommunityDividends), totalDividend, 0, enum2.CommunityDividends.String(), nil, "", masterId) + if err != nil { + _ = session.Rollback() + fmt.Println("err:::::444444", err) + return err + } + + //7、修改 egg_energy_community_dividends 记录 + m.IsOver = 1 + m.PersonsNum = len(list) + affected, err := session.Where("id=?", m.Id).Cols("is_over", "persons_num").Update(m) + if err != nil { + _ = session.Rollback() + fmt.Println("err:::::5555555", err) + return err + } + if affected == 0 { + _ = session.Rollback() + fmt.Println("err:::::6666666", errors.New("更新 egg_energy_community_dividends 记录失败")) + return err + } + + err = session.Commit() + if err != nil { + _ = session.Rollback() + return errors.New("事务提交失败") + } + } + return +} diff --git a/rule/egg_energy/deal_available_green_energy_points.go b/rule/egg_energy/deal_available_green_energy_points.go new file mode 100644 index 0000000..0f8b978 --- /dev/null +++ b/rule/egg_energy/deal_available_green_energy_points.go @@ -0,0 +1,493 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_models.git/src/model" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/enum" + md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/svc" + "encoding/json" + "github.com/shopspring/decimal" + "time" + "xorm.io/xorm" +) + +func DealAvailableGreenEnergyCoin(session *xorm.Session, kind int, amount, amountFee float64, title string, eggEnergyCoreData *model.EggEnergyCoreData, nowPriceValue string) error { + if eggEnergyCoreData == nil { + // 查找 `egg_energy_core_data` 基础设置 + oneCirclesGreenEnergyBasicSetting, cb, err := svc.GetEggEnergyCoreData(session.Engine()) + nowPriceValue = oneCirclesGreenEnergyBasicSetting.NowPrice + if err != nil { + return err + } + if cb != nil { + defer cb() // 释放锁 + } + chain = oneCirclesGreenEnergyBasicSetting + } + amountValue := decimal.NewFromFloat(amount) + now := time.Now() + var oneCirclesAvailableGreenEnergyPointsFlow model.OneCirclesAvailableGreenEnergyPointsFlow + oneCirclesAvailableGreenEnergyPointsFlow.CoinId = chain.PersonGreenEnergyCoinId + oneCirclesAvailableGreenEnergyPointsFlow.Kind = kind + oneCirclesAvailableGreenEnergyPointsFlow.Title = title + oneCirclesAvailableGreenEnergyPointsFlow.Amount = amountValue.RoundFloor(8).String() + oneCirclesAvailableGreenEnergyPointsFlow.CreateTime = now + + nowPrice, _ := decimal.NewFromString(nowPriceValue) + switch kind { + case int(enum.PersonalActivePointRedemption): //个人活跃积分兑换 + beforeOriginalQuantity, _ := decimal.NewFromString(chain.OriginalQuantityNums) + oneCirclesAvailableGreenEnergyPointsFlow.Direction = 1 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeEcologicalApplicationValues = chain.EcologicalApplication //生态应用 + oneCirclesAvailableGreenEnergyPointsFlow.AfterEcologicalApplicationValues = chain.EcologicalApplication + oneCirclesAvailableGreenEnergyPointsFlow.BeforeTechnicalTeamValues = chain.TotalTechnologyTeam //技术团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterTechnicalTeamValues = chain.TotalTechnologyTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOperateTeamValues = chain.TotalOperateTeam //运营团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOperateTeamValues = chain.TotalOperateTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeActiveGiveawaysValues = chain.TotalActiveGiveaways //活跃赠送 + oneCirclesAvailableGreenEnergyPointsFlow.AfterActiveGiveawaysValues = chain.TotalActiveGiveaways + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityValues = chain.OriginalQuantityNums //原始数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityValues = beforeOriginalQuantity.Sub(amountValue).RoundFloor(8).String() + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantValues = chain.MarketplaceMerchantNums //市商数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantValues = chain.MarketplaceMerchantNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDevelopmentCommitteeValues = chain.DevelopmentCommittee //发展委员会 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDevelopmentCommitteeValues = chain.DevelopmentCommittee + oneCirclesAvailableGreenEnergyPointsFlow.BeforePublicWelfareAndCharityValues = chain.PublicWelfareAndCharity //公益慈善 + oneCirclesAvailableGreenEnergyPointsFlow.AfterPublicWelfareAndCharityValues = chain.PublicWelfareAndCharity + oneCirclesAvailableGreenEnergyPointsFlow.BeforeStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterStarLevelDividendsValues = chain.StarLevelDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeCommunityDividends = chain.CommunityDividends //社区分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterCommunityDividends = chain.CommunityDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDestructionQuantityValues = chain.DestructionQuantityNums //销毁数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDestructionQuantityValues = chain.DestructionQuantityNums + + amountFeeValue := decimal.NewFromFloat(amountFee) //积分价值 + originalFunds, _ := decimal.NewFromString(chain.OriginalFunds) //原始资金 + afterOriginalFundValues := originalFunds.Add(amountFeeValue).RoundFloor(8).String() + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityFundValues = chain.OriginalFunds //原始资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityFundValues = afterOriginalFundValues + + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds //市商资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds + break + case int(enum.TeamActivePointRedemption): //团队活跃积分兑换 + beforeOriginalQuantity, _ := decimal.NewFromString(chain.OriginalQuantityNums) + oneCirclesAvailableGreenEnergyPointsFlow.Direction = 1 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeEcologicalApplicationValues = chain.EcologicalApplication //生态应用 + oneCirclesAvailableGreenEnergyPointsFlow.AfterEcologicalApplicationValues = chain.EcologicalApplication + oneCirclesAvailableGreenEnergyPointsFlow.BeforeTechnicalTeamValues = chain.TotalTechnologyTeam //技术团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterTechnicalTeamValues = chain.TotalTechnologyTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOperateTeamValues = chain.TotalOperateTeam //运营团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOperateTeamValues = chain.TotalOperateTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeActiveGiveawaysValues = chain.TotalActiveGiveaways //活跃赠送 + oneCirclesAvailableGreenEnergyPointsFlow.AfterActiveGiveawaysValues = chain.TotalActiveGiveaways + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityValues = chain.OriginalQuantityNums //原始数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityValues = beforeOriginalQuantity.Sub(amountValue).RoundFloor(8).String() + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantValues = chain.MarketplaceMerchantNums //市商数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantValues = chain.MarketplaceMerchantNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDevelopmentCommitteeValues = chain.DevelopmentCommittee //发展委员会 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDevelopmentCommitteeValues = chain.DevelopmentCommittee + oneCirclesAvailableGreenEnergyPointsFlow.BeforePublicWelfareAndCharityValues = chain.PublicWelfareAndCharity //公益慈善 + oneCirclesAvailableGreenEnergyPointsFlow.AfterPublicWelfareAndCharityValues = chain.PublicWelfareAndCharity + oneCirclesAvailableGreenEnergyPointsFlow.BeforeStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterStarLevelDividendsValues = chain.StarLevelDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeCommunityDividends = chain.CommunityDividends //社区分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterCommunityDividends = chain.CommunityDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDestructionQuantityValues = chain.DestructionQuantityNums //销毁数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDestructionQuantityValues = chain.DestructionQuantityNums + + amountFeeValue := decimal.NewFromFloat(amountFee) //积分价值 + originalFunds, _ := decimal.NewFromString(chain.OriginalFunds) //原始资金 + afterOriginalFundValues := originalFunds.Add(amountFeeValue).RoundFloor(8).String() + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityFundValues = chain.OriginalFunds //原始资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityFundValues = afterOriginalFundValues + + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds //市商资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds + break + case int(enum.SettlementOfGreenEnergyRelease): //结算绿色能量释放 + oneCirclesAvailableGreenEnergyPointsFlow.Direction = 1 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeEcologicalApplicationValues = chain.EcologicalApplication //生态应用 + oneCirclesAvailableGreenEnergyPointsFlow.AfterEcologicalApplicationValues = chain.EcologicalApplication + oneCirclesAvailableGreenEnergyPointsFlow.BeforeTechnicalTeamValues = chain.TotalTechnologyTeam //技术团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterTechnicalTeamValues = chain.TotalTechnologyTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOperateTeamValues = chain.TotalOperateTeam //运营团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOperateTeamValues = chain.TotalOperateTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeActiveGiveawaysValues = chain.TotalActiveGiveaways //活跃赠送 + oneCirclesAvailableGreenEnergyPointsFlow.AfterActiveGiveawaysValues = chain.TotalActiveGiveaways + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityValues = chain.OriginalQuantityNums //原始数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityValues = chain.OriginalQuantityNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantValues = chain.MarketplaceMerchantNums //市商数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantValues = chain.MarketplaceMerchantNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDevelopmentCommitteeValues = chain.DevelopmentCommittee //发展委员会 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDevelopmentCommitteeValues = chain.DevelopmentCommittee + oneCirclesAvailableGreenEnergyPointsFlow.BeforePublicWelfareAndCharityValues = chain.PublicWelfareAndCharity //公益慈善 + oneCirclesAvailableGreenEnergyPointsFlow.AfterPublicWelfareAndCharityValues = chain.PublicWelfareAndCharity + oneCirclesAvailableGreenEnergyPointsFlow.BeforeStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterStarLevelDividendsValues = chain.StarLevelDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeCommunityDividends = chain.CommunityDividends //社区分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterCommunityDividends = chain.CommunityDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDestructionQuantityValues = chain.DestructionQuantityNums //销毁数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDestructionQuantityValues = chain.DestructionQuantityNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityFundValues = chain.OriginalFunds //原始资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityFundValues = chain.OriginalFunds + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds //市商资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds + break + case int(enum.SignInReward): //签到奖励 + beforeTotalActiveGiveaways, _ := decimal.NewFromString(chain.TotalActiveGiveaways) + oneCirclesAvailableGreenEnergyPointsFlow.Direction = 1 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeEcologicalApplicationValues = chain.EcologicalApplication //生态应用 + oneCirclesAvailableGreenEnergyPointsFlow.AfterEcologicalApplicationValues = chain.EcologicalApplication + oneCirclesAvailableGreenEnergyPointsFlow.BeforeTechnicalTeamValues = chain.TotalTechnologyTeam //技术团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterTechnicalTeamValues = chain.TotalTechnologyTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOperateTeamValues = chain.TotalOperateTeam //运营团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOperateTeamValues = chain.TotalOperateTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeActiveGiveawaysValues = chain.TotalActiveGiveaways //活跃赠送 + oneCirclesAvailableGreenEnergyPointsFlow.AfterActiveGiveawaysValues = beforeTotalActiveGiveaways.Sub(amountValue).RoundFloor(8).String() + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityValues = chain.OriginalQuantityNums //原始数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityValues = chain.OriginalQuantityNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantValues = chain.MarketplaceMerchantNums //市商数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantValues = chain.MarketplaceMerchantNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDevelopmentCommitteeValues = chain.DevelopmentCommittee //发展委员会 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDevelopmentCommitteeValues = chain.DevelopmentCommittee + oneCirclesAvailableGreenEnergyPointsFlow.BeforePublicWelfareAndCharityValues = chain.PublicWelfareAndCharity //公益慈善 + oneCirclesAvailableGreenEnergyPointsFlow.AfterPublicWelfareAndCharityValues = chain.PublicWelfareAndCharity + oneCirclesAvailableGreenEnergyPointsFlow.BeforeStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterStarLevelDividendsValues = chain.StarLevelDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeCommunityDividends = chain.CommunityDividends //社区分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterCommunityDividends = chain.CommunityDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDestructionQuantityValues = chain.DestructionQuantityNums //销毁数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDestructionQuantityValues = chain.DestructionQuantityNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds //市商资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityFundValues = chain.OriginalFunds //原始资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityFundValues = chain.OriginalFunds + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds //市商资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds + break + case int(enum.AccountBalanceExchange): //账户余额兑换 + beforeOriginalQuantity, _ := decimal.NewFromString(chain.OriginalQuantityNums) + oneCirclesAvailableGreenEnergyPointsFlow.Direction = 1 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeEcologicalApplicationValues = chain.EcologicalApplication //生态应用 + oneCirclesAvailableGreenEnergyPointsFlow.AfterEcologicalApplicationValues = chain.EcologicalApplication + oneCirclesAvailableGreenEnergyPointsFlow.BeforeTechnicalTeamValues = chain.TotalTechnologyTeam //技术团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterTechnicalTeamValues = chain.TotalTechnologyTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOperateTeamValues = chain.TotalOperateTeam //运营团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOperateTeamValues = chain.TotalOperateTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeActiveGiveawaysValues = chain.TotalActiveGiveaways //活跃赠送 + oneCirclesAvailableGreenEnergyPointsFlow.AfterActiveGiveawaysValues = chain.TotalActiveGiveaways + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityValues = chain.OriginalQuantityNums //原始数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityValues = beforeOriginalQuantity.Sub(amountValue).RoundFloor(8).String() + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantValues = chain.MarketplaceMerchantNums //市商数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantValues = chain.MarketplaceMerchantNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDevelopmentCommitteeValues = chain.DevelopmentCommittee //发展委员会 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDevelopmentCommitteeValues = chain.DevelopmentCommittee + oneCirclesAvailableGreenEnergyPointsFlow.BeforePublicWelfareAndCharityValues = chain.PublicWelfareAndCharity //公益慈善 + oneCirclesAvailableGreenEnergyPointsFlow.AfterPublicWelfareAndCharityValues = chain.PublicWelfareAndCharity + oneCirclesAvailableGreenEnergyPointsFlow.BeforeStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterStarLevelDividendsValues = chain.StarLevelDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeCommunityDividends = chain.CommunityDividends //社区分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterCommunityDividends = chain.CommunityDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDestructionQuantityValues = chain.DestructionQuantityNums //销毁数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDestructionQuantityValues = chain.DestructionQuantityNums + + amountFeeValue := decimal.NewFromFloat(amountFee) //余额价值 + originalFunds, _ := decimal.NewFromString(chain.OriginalFunds) //原始资金 + afterOriginalFundValues := originalFunds.Add(amountFeeValue).RoundFloor(8).String() + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityFundValues = chain.OriginalFunds //原始资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityFundValues = afterOriginalFundValues + + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds //市商资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds + break + case int(enum.GreenEnergyExchangeBalance): //兑换账户余额 + var destructionSetting *md2.DestructionSettingStruct + err := json.Unmarshal([]byte(chain.DestructionSetting), &destructionSetting) + if err != nil { + return err + } + decimalRate := decimal.NewFromInt(100) //百分比 + amountFeeValue := decimal.NewFromFloat(amountFee) //手续费 + + marketplaceMerchantNums, _ := decimal.NewFromString(chain.MarketplaceMerchantNums) + marketplaceMerchant, _ := decimal.NewFromString(destructionSetting.MarketplaceMerchant) //市商数量百分比 + afterMarketplaceMerchantValues := marketplaceMerchantNums.Add(amountFeeValue.Mul(marketplaceMerchant.Div(decimalRate))).RoundFloor(8).String() + + developmentCommitteeNums, _ := decimal.NewFromString(chain.DevelopmentCommittee) + developmentCommittee, _ := decimal.NewFromString(destructionSetting.DevelopmentCommittee) //发展委员会百分比 + afterDevelopmentCommitteeValues := developmentCommitteeNums.Add(amountFeeValue.Mul(developmentCommittee.Div(decimalRate))).RoundFloor(8).String() + + publicWelfareAndCharityNums, _ := decimal.NewFromString(chain.PublicWelfareAndCharity) + publicWelfareAndCharity, _ := decimal.NewFromString(destructionSetting.PublicWelfareAndCharity) //公益慈善百分比 + afterPublicWelfareAndCharityValues := publicWelfareAndCharityNums.Add(amountFeeValue.Mul(publicWelfareAndCharity.Div(decimalRate))).RoundFloor(8).String() + + starLevelDividendsNums, _ := decimal.NewFromString(chain.StarLevelDividends) + starLevelDividends, _ := decimal.NewFromString(destructionSetting.StarLevelDividends) //星级分红百分比 + afterStarLevelDividendsValues := starLevelDividendsNums.Add(amountFeeValue.Mul(starLevelDividends.Div(decimalRate))).RoundFloor(8).String() + + var afterCommunityDividendsValues = chain.CommunityDividends + if chain.CommunityDividends != "" { + communityDividendsNums, _ := decimal.NewFromString(chain.CommunityDividends) + communityDividends, _ := decimal.NewFromString(destructionSetting.CommunityDividends) //社区分红百分比 + afterCommunityDividendsValues = communityDividendsNums.Add(amountFeeValue.Mul(communityDividends.Div(decimalRate))).RoundFloor(8).String() + } + + destructionQuantityNums, _ := decimal.NewFromString(chain.DestructionQuantityNums) + destructionQuantity, _ := decimal.NewFromString(destructionSetting.DestructionQuantity) //销毁百分比 + afterDestructionQuantityValues := destructionQuantityNums.Add(amountFeeValue.Mul(destructionQuantity.Div(decimalRate))).RoundFloor(8).String() + + originalQuantityNums, _ := decimal.NewFromString(chain.OriginalQuantityNums) //原始数量 + afterOriginalQuantityValues := originalQuantityNums.Add(amountValue.Sub(amountFeeValue)).RoundFloor(8).String() + + originalFunds, _ := decimal.NewFromString(chain.OriginalFunds) //原始资金 + afterOriginalFundValues := originalFunds.Sub(nowPrice.Mul(amountValue.Sub(amountFeeValue))).RoundFloor(8).String() + + oneCirclesAvailableGreenEnergyPointsFlow.Direction = 1 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeEcologicalApplicationValues = chain.EcologicalApplication //生态应用 + oneCirclesAvailableGreenEnergyPointsFlow.AfterEcologicalApplicationValues = chain.EcologicalApplication + oneCirclesAvailableGreenEnergyPointsFlow.BeforeTechnicalTeamValues = chain.TotalTechnologyTeam //技术团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterTechnicalTeamValues = chain.TotalTechnologyTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOperateTeamValues = chain.TotalOperateTeam //运营团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOperateTeamValues = chain.TotalOperateTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeActiveGiveawaysValues = chain.TotalActiveGiveaways //活跃赠送 + oneCirclesAvailableGreenEnergyPointsFlow.AfterActiveGiveawaysValues = chain.TotalActiveGiveaways + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityValues = chain.OriginalQuantityNums //原始数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityValues = afterOriginalQuantityValues + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantValues = chain.MarketplaceMerchantNums //市商数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantValues = afterMarketplaceMerchantValues + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantValues = afterMarketplaceMerchantValues + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDevelopmentCommitteeValues = chain.DevelopmentCommittee //发展委员会 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDevelopmentCommitteeValues = afterDevelopmentCommitteeValues + oneCirclesAvailableGreenEnergyPointsFlow.BeforePublicWelfareAndCharityValues = chain.PublicWelfareAndCharity //公益慈善 + oneCirclesAvailableGreenEnergyPointsFlow.AfterPublicWelfareAndCharityValues = afterPublicWelfareAndCharityValues + oneCirclesAvailableGreenEnergyPointsFlow.BeforeStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterStarLevelDividendsValues = afterStarLevelDividendsValues + oneCirclesAvailableGreenEnergyPointsFlow.BeforeCommunityDividends = chain.CommunityDividends //社区分红百分比 + oneCirclesAvailableGreenEnergyPointsFlow.AfterCommunityDividends = afterCommunityDividendsValues + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDestructionQuantityValues = chain.DestructionQuantityNums //销毁数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDestructionQuantityValues = afterDestructionQuantityValues + + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds //市商资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityFundValues = chain.OriginalFunds //原始资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityFundValues = afterOriginalFundValues + + //TODO::新增 / 更新 one_circles_star_level_dividends_records 记录 + oneCirclesStarLevelDividendsRecords, err := db.OneCirclesStarLevelDividendsRecordsGetOneByParamsBySession(session, map[string]interface{}{ + "key": "date", + "value": now.Format("2006-01-02"), + }) + if err != nil { + return err + } + if oneCirclesStarLevelDividendsRecords == nil { + oneCirclesStarLevelDividendsRecords = &model.OneCirclesStarLevelDividendsRecords{ + Amount: amountFeeValue.Mul(starLevelDividends.Div(decimalRate)).String(), + AlreadyDividendsAmount: "", + NotDividendsAmount: "", + Date: now.Format("2006-01-02"), + } + _, err = db.OneCirclesStarLevelDividendsRecordsInsertBySession(session, oneCirclesStarLevelDividendsRecords) + if err != nil { + return err + } + } else { + oneCirclesStarLevelDividendsRecordsAmountValue, _ := decimal.NewFromString(oneCirclesStarLevelDividendsRecords.Amount) + oneCirclesStarLevelDividendsRecordsAmountValue = oneCirclesStarLevelDividendsRecordsAmountValue.Add(amountFeeValue.Mul(starLevelDividends.Div(decimalRate))) + oneCirclesStarLevelDividendsRecords.Amount = oneCirclesStarLevelDividendsRecordsAmountValue.String() + _, err = db.OneCirclesStarLevelDividendsRecordsUpdateBySession(session, oneCirclesStarLevelDividendsRecords.Id, oneCirclesStarLevelDividendsRecords, "amount") + if err != nil { + return err + } + } + break + case int(enum.SettlementStarLevelDividends): //星级分红 + beforeStarLevelDividends, _ := decimal.NewFromString(chain.StarLevelDividends) + oneCirclesAvailableGreenEnergyPointsFlow.Direction = 1 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeEcologicalApplicationValues = chain.EcologicalApplication //生态应用 + oneCirclesAvailableGreenEnergyPointsFlow.AfterEcologicalApplicationValues = chain.EcologicalApplication + oneCirclesAvailableGreenEnergyPointsFlow.BeforeTechnicalTeamValues = chain.TotalTechnologyTeam //技术团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterTechnicalTeamValues = chain.TotalTechnologyTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOperateTeamValues = chain.TotalOperateTeam //运营团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOperateTeamValues = chain.TotalOperateTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeActiveGiveawaysValues = chain.TotalActiveGiveaways //活跃赠送 + oneCirclesAvailableGreenEnergyPointsFlow.AfterActiveGiveawaysValues = chain.TotalActiveGiveaways + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityValues = chain.OriginalQuantityNums //原始数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityValues = chain.OriginalQuantityNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantValues = chain.MarketplaceMerchantNums //市商数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantValues = chain.MarketplaceMerchantNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDevelopmentCommitteeValues = chain.DevelopmentCommittee //发展委员会 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDevelopmentCommitteeValues = chain.DevelopmentCommittee + oneCirclesAvailableGreenEnergyPointsFlow.BeforePublicWelfareAndCharityValues = chain.PublicWelfareAndCharity //公益慈善 + oneCirclesAvailableGreenEnergyPointsFlow.AfterPublicWelfareAndCharityValues = chain.PublicWelfareAndCharity + oneCirclesAvailableGreenEnergyPointsFlow.BeforeStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterStarLevelDividendsValues = beforeStarLevelDividends.Sub(amountValue).RoundFloor(8).String() + oneCirclesAvailableGreenEnergyPointsFlow.BeforeCommunityDividends = chain.CommunityDividends //社区分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterCommunityDividends = chain.CommunityDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDestructionQuantityValues = chain.DestructionQuantityNums //销毁数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDestructionQuantityValues = chain.DestructionQuantityNums + + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityFundValues = chain.OriginalFunds //原始资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityFundValues = chain.OriginalFunds + + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds //市商资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds + break + case int(enum.MarketplaceMerchantNumsAutoExchangeMarketplaceMerchantFunds): //市商数量自动兑换市商资金 + beforeOriginalQuantityNums, _ := decimal.NewFromString(chain.OriginalQuantityNums) + beforeMarketplaceMerchantNums, _ := decimal.NewFromString(chain.MarketplaceMerchantNums) + beforeOriginalFunds, _ := decimal.NewFromString(chain.OriginalFunds) + beforeMarketplaceMerchantFunds, _ := decimal.NewFromString(chain.MarketplaceMerchantFunds) + + oneCirclesAvailableGreenEnergyPointsFlow.Direction = 1 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeEcologicalApplicationValues = chain.EcologicalApplication //生态应用 + oneCirclesAvailableGreenEnergyPointsFlow.AfterEcologicalApplicationValues = chain.EcologicalApplication + oneCirclesAvailableGreenEnergyPointsFlow.BeforeTechnicalTeamValues = chain.TotalTechnologyTeam //技术团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterTechnicalTeamValues = chain.TotalTechnologyTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOperateTeamValues = chain.TotalOperateTeam //运营团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOperateTeamValues = chain.TotalOperateTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeActiveGiveawaysValues = chain.TotalActiveGiveaways //活跃赠送 + oneCirclesAvailableGreenEnergyPointsFlow.AfterActiveGiveawaysValues = chain.TotalActiveGiveaways + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityValues = chain.OriginalQuantityNums //原始数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityValues = beforeOriginalQuantityNums.Add(amountValue).String() + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantValues = chain.MarketplaceMerchantNums //市商数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantValues = beforeMarketplaceMerchantNums.Sub(amountValue).String() + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDevelopmentCommitteeValues = chain.DevelopmentCommittee //发展委员会 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDevelopmentCommitteeValues = chain.DevelopmentCommittee + oneCirclesAvailableGreenEnergyPointsFlow.BeforePublicWelfareAndCharityValues = chain.PublicWelfareAndCharity //公益慈善 + oneCirclesAvailableGreenEnergyPointsFlow.AfterPublicWelfareAndCharityValues = chain.PublicWelfareAndCharity + oneCirclesAvailableGreenEnergyPointsFlow.BeforeStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeCommunityDividends = chain.CommunityDividends //社区分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterCommunityDividends = chain.CommunityDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDestructionQuantityValues = chain.DestructionQuantityNums //销毁数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDestructionQuantityValues = chain.DestructionQuantityNums + + amountFeeValue := decimal.NewFromFloat(amountFee) //金额 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityFundValues = chain.OriginalFunds //原始资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityFundValues = beforeOriginalFunds.Sub(amountFeeValue).String() + + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds //市商资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantFundValues = beforeMarketplaceMerchantFunds.Add(amountFeeValue).String() + break + case int(enum.MarketplaceMerchantFundsAutoExchangeMarketplaceMerchantNums): //市商资金自动兑换市商数量 + beforeOriginalQuantityNums, _ := decimal.NewFromString(chain.OriginalQuantityNums) + beforeMarketplaceMerchantNums, _ := decimal.NewFromString(chain.MarketplaceMerchantNums) + beforeOriginalFunds, _ := decimal.NewFromString(chain.OriginalFunds) + beforeMarketplaceMerchantFunds, _ := decimal.NewFromString(chain.MarketplaceMerchantFunds) + + oneCirclesAvailableGreenEnergyPointsFlow.Direction = 1 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeEcologicalApplicationValues = chain.EcologicalApplication //生态应用 + oneCirclesAvailableGreenEnergyPointsFlow.AfterEcologicalApplicationValues = chain.EcologicalApplication + oneCirclesAvailableGreenEnergyPointsFlow.BeforeTechnicalTeamValues = chain.TotalTechnologyTeam //技术团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterTechnicalTeamValues = chain.TotalTechnologyTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOperateTeamValues = chain.TotalOperateTeam //运营团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOperateTeamValues = chain.TotalOperateTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeActiveGiveawaysValues = chain.TotalActiveGiveaways //活跃赠送 + oneCirclesAvailableGreenEnergyPointsFlow.AfterActiveGiveawaysValues = chain.TotalActiveGiveaways + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityValues = chain.OriginalQuantityNums //原始数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityValues = beforeOriginalQuantityNums.Sub(amountValue).String() + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantValues = chain.MarketplaceMerchantNums //市商数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantValues = beforeMarketplaceMerchantNums.Add(amountValue).String() + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDevelopmentCommitteeValues = chain.DevelopmentCommittee //发展委员会 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDevelopmentCommitteeValues = chain.DevelopmentCommittee + oneCirclesAvailableGreenEnergyPointsFlow.BeforePublicWelfareAndCharityValues = chain.PublicWelfareAndCharity //公益慈善 + oneCirclesAvailableGreenEnergyPointsFlow.AfterPublicWelfareAndCharityValues = chain.PublicWelfareAndCharity + oneCirclesAvailableGreenEnergyPointsFlow.BeforeStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeCommunityDividends = chain.CommunityDividends //社区分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterCommunityDividends = chain.CommunityDividends + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDestructionQuantityValues = chain.DestructionQuantityNums //销毁数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDestructionQuantityValues = chain.DestructionQuantityNums + + amountFeeValue := decimal.NewFromFloat(amountFee) //金额 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityFundValues = chain.OriginalFunds //原始资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityFundValues = beforeOriginalFunds.Add(amountFeeValue).String() + + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds //市商资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantFundValues = beforeMarketplaceMerchantFunds.Sub(amountFeeValue).String() + break + case int(enum.CommunityDividends): //社区分红 + beforeCommunityDividends, _ := decimal.NewFromString(chain.CommunityDividends) + + oneCirclesAvailableGreenEnergyPointsFlow.Direction = 2 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeEcologicalApplicationValues = chain.EcologicalApplication //生态应用 + oneCirclesAvailableGreenEnergyPointsFlow.AfterEcologicalApplicationValues = chain.EcologicalApplication + oneCirclesAvailableGreenEnergyPointsFlow.BeforeTechnicalTeamValues = chain.TotalTechnologyTeam //技术团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterTechnicalTeamValues = chain.TotalTechnologyTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOperateTeamValues = chain.TotalOperateTeam //运营团队 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOperateTeamValues = chain.TotalOperateTeam + oneCirclesAvailableGreenEnergyPointsFlow.BeforeActiveGiveawaysValues = chain.TotalActiveGiveaways //活跃赠送 + oneCirclesAvailableGreenEnergyPointsFlow.AfterActiveGiveawaysValues = chain.TotalActiveGiveaways + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityValues = chain.OriginalQuantityNums //原始数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityValues = chain.OriginalQuantityNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantValues = chain.MarketplaceMerchantNums //市商数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantValues = chain.MarketplaceMerchantNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDevelopmentCommitteeValues = chain.DevelopmentCommittee //发展委员会 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDevelopmentCommitteeValues = chain.DevelopmentCommittee + oneCirclesAvailableGreenEnergyPointsFlow.BeforePublicWelfareAndCharityValues = chain.PublicWelfareAndCharity //公益慈善 + oneCirclesAvailableGreenEnergyPointsFlow.AfterPublicWelfareAndCharityValues = chain.PublicWelfareAndCharity + oneCirclesAvailableGreenEnergyPointsFlow.BeforeStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterStarLevelDividendsValues = chain.StarLevelDividends //星级分红 + oneCirclesAvailableGreenEnergyPointsFlow.BeforeDestructionQuantityValues = chain.DestructionQuantityNums //销毁数量 + oneCirclesAvailableGreenEnergyPointsFlow.AfterDestructionQuantityValues = chain.DestructionQuantityNums + oneCirclesAvailableGreenEnergyPointsFlow.BeforeOriginalQuantityFundValues = chain.OriginalFunds //原始资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityFundValues = chain.OriginalFunds + oneCirclesAvailableGreenEnergyPointsFlow.BeforeMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds //市商资金 + oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantFundValues = chain.MarketplaceMerchantFunds + + oneCirclesAvailableGreenEnergyPointsFlow.BeforeCommunityDividends = chain.CommunityDividends //社区分红 + oneCirclesAvailableGreenEnergyPointsFlow.AfterCommunityDividends = beforeCommunityDividends.Sub(amountValue).RoundFloor(8).String() + break + } + + chain.EcologicalApplication = oneCirclesAvailableGreenEnergyPointsFlow.AfterEcologicalApplicationValues //生态应用 + chain.TotalTechnologyTeam = oneCirclesAvailableGreenEnergyPointsFlow.AfterTechnicalTeamValues //技术团队 + chain.TotalOperateTeam = oneCirclesAvailableGreenEnergyPointsFlow.AfterOperateTeamValues //运营团队 + chain.TotalActiveGiveaways = oneCirclesAvailableGreenEnergyPointsFlow.AfterActiveGiveawaysValues //活跃赠送 + chain.OriginalQuantityNums = oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityValues //原始数量 + chain.MarketplaceMerchantNums = oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantValues //市商数量 + chain.DevelopmentCommittee = oneCirclesAvailableGreenEnergyPointsFlow.AfterDevelopmentCommitteeValues //发展委员会 + chain.PublicWelfareAndCharity = oneCirclesAvailableGreenEnergyPointsFlow.AfterPublicWelfareAndCharityValues //公益慈善 + chain.StarLevelDividends = oneCirclesAvailableGreenEnergyPointsFlow.AfterStarLevelDividendsValues //星级分红 + chain.CommunityDividends = oneCirclesAvailableGreenEnergyPointsFlow.AfterCommunityDividends //社区分红 + chain.DestructionQuantityNums = oneCirclesAvailableGreenEnergyPointsFlow.AfterDestructionQuantityValues //销毁数量 + + chain.OriginalFunds = oneCirclesAvailableGreenEnergyPointsFlow.AfterOriginalQuantityFundValues //原始资金 + chain.MarketplaceMerchantFunds = oneCirclesAvailableGreenEnergyPointsFlow.AfterMarketplaceMerchantFundValues //市商资金 + + oneCirclesAvailableGreenEnergyPointsFlow.BeforePrice = chain.NowPrice //价格 + oneCirclesAvailableGreenEnergyPointsFlow.AfterPrice = nowPriceValue + + //更新 `one_circles_green_energy_basic_setting` 表 + if chain.NowPrice != nowPriceValue { + chain.NowPrice = nowPriceValue + } + _, err := db.OneCirclesGreenEnergyBasicSettingUpdate(session, chain.Id, chain, + "ecological_application", + "total_technology_team", + "total_operate_team", + "total_active_giveaways", + "original_quantity_nums", + "now_price", + "original_funds", + "marketplace_merchant_nums", + "marketplace_merchant_funds", + "development_committee", + "public_welfare_and_charity", + "star_level_dividends", + "community_dividends", + "destruction_quantity_nums", + ) + if err != nil { + return err + } + + //插入 `one_circles_available_green_energy_points_flow` 记录 + _, err = db.OneCirclesAvailableGreenEnergyPointsFlowInsert(session, &oneCirclesAvailableGreenEnergyPointsFlow) + if err != nil { + return err + } + return nil +} diff --git a/rule/egg_energy/enum/available_egg_energy_points_flow.go b/rule/egg_energy/enum/available_egg_energy_points_flow.go new file mode 100644 index 0000000..e54df00 --- /dev/null +++ b/rule/egg_energy/enum/available_egg_energy_points_flow.go @@ -0,0 +1,41 @@ +package enum + +// 可用绿色能量流水-种类 +type EggEnergyAvailableEnergyFlowKind int + +const ( + PersonalActivePointRedemption EggEnergyAvailableEnergyFlowKind = iota + 1 + TeamActivePointRedemption + AccountBalanceExchangeEggEnergy + EggEnergyExchangeAccountBalance + SettlementStarLevelDividends + CommunityDividends + PlatformProfitability + MarketplaceMerchantNumsAutoExchangeMarketplaceMerchantFunds + MarketplaceMerchantFundsAutoExchangeMarketplaceMerchantNums +) + +func (kind EggEnergyAvailableEnergyFlowKind) String() string { + switch kind { + case PersonalActivePointRedemption: + return "个人蛋蛋积分兑换" + case TeamActivePointRedemption: + return "团队蛋蛋积分兑换" + case AccountBalanceExchangeEggEnergy: + return "余额兑换蛋蛋能量" + case EggEnergyExchangeAccountBalance: + return "蛋蛋能量兑换余额" + case SettlementStarLevelDividends: + return "星级分红" + case CommunityDividends: + return "社区分红" + case PlatformProfitability: + return "平台盈利" + case MarketplaceMerchantNumsAutoExchangeMarketplaceMerchantFunds: + return "市商数量自动兑换市商资金" + case MarketplaceMerchantFundsAutoExchangeMarketplaceMerchantNums: + return "市商资金自动兑换市商数量" + default: + return "未知状态" + } +} diff --git a/rule/egg_energy/give_activty_coin.go b/rule/egg_energy/give_activty_coin.go new file mode 100644 index 0000000..14267ba --- /dev/null +++ b/rule/egg_energy/give_activty_coin.go @@ -0,0 +1,244 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_models.git/src/implement" + "code.fnuoos.com/EggPlanet/egg_models.git/src/model" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/enum" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule" + md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md" + egg_system_rules "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/cache" + zhios_order_relate_logx "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/logx" + "code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" + "encoding/json" + "errors" + "fmt" + "strings" + "time" + "xorm.io/xorm" +) + +// SettlementGiveActivityCoin 结算观看激励广告得到蛋蛋积分 +func SettlementGiveActivityCoin(engine *xorm.Engine, uid int64, ch *rabbit.Channel) (err error) { + //1、查找 `egg_energy_basic_setting` 基础设置 + eggEnergyBasicSettingDb := implement.NewEggEnergyBasicSettingDb(engine) + eggEnergyBasicSetting, err := eggEnergyBasicSettingDb.EggEnergyBasicSettingGetOneByParams(map[string]interface{}{ + "key": "is_open", + "value": 1, + }) + if err != nil { + return + } + if eggEnergyBasicSetting == nil { + err = errors.New("蛋蛋能量设置未开启!") + return + } + if eggEnergyBasicSetting.VideoRewardIsOpen == 1 { + var videoRewardSystem *md2.VideoRewardSystemStruct + err = json.Unmarshal([]byte(eggEnergyBasicSetting.VideoRewardSystem), &videoRewardSystem) + if err != nil { + return + } + if videoRewardSystem.RewardValue == "" || videoRewardSystem.RewardTotalNum == "" || videoRewardSystem.IntervalMinutes == "" || videoRewardSystem.EachRoundHour == "" { + err = errors.New("视屏奖励机制设置未完全!") + return + } + var rewardSystem []*md2.RewardSystemStruct + err = json.Unmarshal([]byte(eggEnergyBasicSetting.RewardSystem), &rewardSystem) + if err != nil { + return + } + if len(rewardSystem) == 0 { + err = errors.New("未设置奖励机制!") + return + } + rewardValue := egg_system_rules.StrToFloat64(videoRewardSystem.RewardValue) //奖励多少个活跃积分 + var rewardSystemMap = map[int]*md2.RewardSystemStruct{} + for _, v := range rewardSystem { + rewardSystemMap[v.Level] = v + } + publicPlatoonUserRelationDb := implement.NewPublicPlatoonUserRelationDb(engine) + publicPlatoonUserRelation, err1 := publicPlatoonUserRelationDb.PublicPlatoonUserRelationGetOneByParams(map[string]interface{}{ + "key": "uid", + "value": uid, + }) + if err1 != nil { + return err1 + } + + var rewardFather []struct { + Uid int64 `json:"uid"` //用户id + RewardValue float64 `json:"reward_value"` //奖励值 + } + + var fatherUids []string + if publicPlatoonUserRelation != nil { + fatherUids = strings.Split(publicPlatoonUserRelation.FatherUid, "-") + } + for k, id := range fatherUids { + tmpOneCirclesPublicPlatoonUserRelation, err11 := publicPlatoonUserRelationDb.PublicPlatoonUserRelationGetOneByParams(map[string]interface{}{ + "key": "id", + "value": id, + }) + if err11 != nil { + return err11 + } + if tmpOneCirclesPublicPlatoonUserRelation == nil { + continue + } + if tmpOneCirclesPublicPlatoonUserRelation.Uid <= 0 { + //待填充位 + continue + } + + fatherUid := egg_system_rules.Int64ToStr(tmpOneCirclesPublicPlatoonUserRelation.Uid) + fatherReward := rewardSystemMap[k+1] + //TODO::判断是否活跃 + var m model.EggSignIn + has, err33 := engine.Where("uid =?", fatherUid).And("end_time >=?", time.Now().Format("2006-01-02 15:04:05")).Get(&m) + if err33 != nil { + return err33 + } + if !has { + //不活跃不需要奖励 + continue + } + //判断是否满足奖励条件 + userRelateDb := implement.NewUserRelateDb(engine) + userCount, _, err2 := userRelateDb.SumUserRelateByParentUid(fatherUid) + if err2 != nil { + return err2 + } + + if fatherReward != nil && userCount >= egg_system_rules.StrToInt64(fatherReward.RewardCondition) { + fatherRewardValue := rewardValue * (egg_system_rules.StrToFloat64(fatherReward.RewardValue) / 100) + rewardFather = append(rewardFather, struct { + Uid int64 `json:"uid"` //用户id + RewardValue float64 `json:"reward_value"` //奖励值 + }{ + Uid: egg_system_rules.StrToInt64(fatherUid), + RewardValue: fatherRewardValue, + }) + } + } + + //增加 egg_user_watch_records 记录 + eggEnergyUserWatchRecordsDb := implement.NewEggEnergyUserWatchRecordsDb(engine) + eggEnergyUserWatchRecords, err1 := eggEnergyUserWatchRecordsDb.EggEnergyUserWatchRecordsGetOneByParams(map[string]interface{}{ + "key": "uid", + "value": uid, + }) + if err1 != nil { + return err1 + } + now := time.Now() + if eggEnergyUserWatchRecords == nil { + residueWatchAdNum := egg_system_rules.StrToInt(videoRewardSystem.RewardTotalNum) - 1 + if residueWatchAdNum < 0 { + residueWatchAdNum = egg_system_rules.StrToInt(videoRewardSystem.RewardTotalNum) + } + _, err2 := eggEnergyUserWatchRecordsDb.EggEnergyUserWatchRecordsInsert(&model.EggEnergyUserWatchRecords{ + Uid: uid, + NextWatchAdDate: now.Add(time.Hour * time.Duration(egg_system_rules.StrToInt(videoRewardSystem.EachRoundHour))), + ResidueWatchAdNum: egg_system_rules.StrToInt(videoRewardSystem.RewardTotalNum) - 1, + CreateAt: now, + UpdateAt: now, + }) + if err2 != nil { + return err2 + } + } else { + residueWatchAdNum := eggEnergyUserWatchRecords.ResidueWatchAdNum - 1 + nextWatchAdDate := eggEnergyUserWatchRecords.NextWatchAdDate + if residueWatchAdNum == 0 { //最后一条广告 + residueWatchAdNum = egg_system_rules.StrToInt(videoRewardSystem.RewardTotalNum) + } + if residueWatchAdNum == egg_system_rules.StrToInt(videoRewardSystem.RewardTotalNum)-1 { //第一条广告 + nextWatchAdDate = now.Add(time.Hour * time.Duration(egg_system_rules.StrToInt(videoRewardSystem.EachRoundHour))) + } + eggEnergyUserWatchRecords.ResidueWatchAdNum = residueWatchAdNum + eggEnergyUserWatchRecords.NextWatchAdDate = nextWatchAdDate + _, err2 := eggEnergyUserWatchRecordsDb.EggEnergyUserWatchRecordsUpdate(eggEnergyUserWatchRecords.Id, eggEnergyUserWatchRecords, "residue_watch_ad_num", "next_watch_ad_date") + if err2 != nil { + return err2 + } + } + + session := engine.NewSession() + defer func() { + session.Close() + if err := recover(); err != nil { + _ = zhios_order_relate_logx.Error(err) + } + }() + session.Begin() + + //给相应的用户加上"个人"活跃积分 + err = rule.DealUserVirtualCoin(session, md.DealUserVirtualCoinReq{ + Kind: "add", + Title: enum.UserVirtualAmountFlowTransferType.String(enum.EggEnergyWatchAdRewardPersonalActiveCoin), + TransferType: int(enum.EggEnergyWatchAdRewardPersonalActiveCoin), + CoinId: eggEnergyBasicSetting.PersonEggPointsCoinId, + Uid: uid, + Amount: rewardValue, + }) + if err != nil { + _ = session.Rollback() + fmt.Println("err:::::2222", err) + return err + } + + //给相应的用户加上"团队"活跃积分 + for _, vv := range rewardFather { + //TODO::推入rabbitmq 异步处理 + ch.Publish(md2.EggEnergyExchange, md.DealUserVirtualCoinReq{ + Kind: "add", + Title: enum.UserVirtualAmountFlowTransferType.String(enum.EggEnergyWatchAdRewardTeamActiveCoin), + TransferType: int(enum.EggEnergyWatchAdRewardTeamActiveCoin), + CoinId: eggEnergyBasicSetting.TeamEggPointsCoinId, + Uid: vv.Uid, + Amount: vv.RewardValue, + }, md2.EggEnergyRoutKeyForSettlementPublicGiveActivityCoin) + } + err = session.Commit() + if err != nil { + _ = session.Rollback() + return errors.New("事务提交失败") + } + + redisKey := fmt.Sprintf(md2.UserNextWatchAdDate, uid) + var watchAdDate string + if eggEnergyUserWatchRecords.ResidueWatchAdNum == egg_system_rules.StrToInt(videoRewardSystem.RewardTotalNum) { + if eggEnergyUserWatchRecords.NextWatchAdDate.Before(time.Now()) { + watchAdDate = "" + } else { + watchAdDate = eggEnergyUserWatchRecords.NextWatchAdDate.Format("2006-01-02 15:04:05") + } + } else { + watchAdDate = time.Now().Add(time.Duration(egg_system_rules.StrToInt64(videoRewardSystem.IntervalMinutes)) * time.Second).Format("2006-01-02 15:04:05") + } + cache.SetEx(redisKey, watchAdDate, 60*60*24) //TODO::默认缓存1小时 + } + return +} + +// CalcUserContinuousDailyActivityDays 计算用户连续活跃天数 +func CalcUserContinuousDailyActivityDays(engine *xorm.Engine, uid int, startDate string, endDate string) (err error, days int, isContinuousDailyActivity bool) { + startAt, err := time.ParseInLocation("2006-01-02", startDate, time.Local) //起始时间 + endAt, err := time.ParseInLocation("2006-01-02", endDate, time.Local) //起始时间 + var list []model.EggEnergyUserActivity + err = engine.Where("date >= ?", startAt.Format("2006-01-02")). + And("date < ?", endAt.Format("2006-01-02")). + And("uid =?", uid).Find(&list) + if err != nil { + fmt.Println("err:::::1111", err) + return + } + days = len(list) + diffDays := egg_system_rules.GetDiffDays(endAt, startAt) //相差天数 + if days == diffDays { + isContinuousDailyActivity = true + } + return +} diff --git a/rule/egg_energy/green_energy_settlement.go b/rule/egg_energy/green_energy_settlement.go new file mode 100644 index 0000000..0ab1547 --- /dev/null +++ b/rule/egg_energy/green_energy_settlement.go @@ -0,0 +1,276 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_system_rules.git/db" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/db/model" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/one_circles/enum" + md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/one_circles/md" + egg_system_rules "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/cache" + "code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" + "encoding/json" + "errors" + "fmt" + "github.com/shopspring/decimal" + "math/rand" + "time" + "xorm.io/xorm" +) + +// SettlementSignInGreenEnergy 计算签到得到绿色能量 +func SettlementSignInGreenEnergy(engine *xorm.Engine, masterId string, ch *rabbit.Channel) (err error) { + //1、查找 `one_circles_green_energy_basic_setting` 基础设置 + oneCirclesGreenEnergyBasicSetting, err := db.OneCirclesGreenEnergyBasicSettingGetOneByParams(engine, map[string]interface{}{ + "key": "is_open", + "value": 1, + }) + if err != nil { + return + } + if oneCirclesGreenEnergyBasicSetting == nil { + return errors.New("开关未开!!") + } + //3、统计签到结束的用户数据 + now := time.Now() + var page = 1 + var pageSize = 100 + for { + var list []model.OneCirclesGreenEnergySignIn + err = engine.Where("end_time <= ?", now.Format("2006-01-02 15:04:05")).And("is_completed =?", 0).Limit(pageSize, (page-1)*pageSize).Asc("id").Find(&list) + if err != nil { + fmt.Println("err:::::1111", err) + return + } + if len(list) <= 0 { + break + } + + var ids []int64 + for _, v := range list { + //TODO::推入rabbitmq 异步处理 + ch.Publish(md.OneCirclesExchange, md.OneCirclesStructForSignIn{ + MasterId: masterId, + Uid: v.Uid, + Id: v.Id, + EndTime: v.EndTime, + }, md.OneCirclesRoutKeyForSignIn) + ids = append(ids, v.Id) + } + //6、更新 `one_circles_green_energy_sign_in` 中的 is_completed 状态 + //if len(ids) > 0 { + // _, err = engine.In("id", ids).Update(&model.OneCirclesGreenEnergySignIn{ + // IsCompleted: 1, + // }) + //} + page++ + time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000))) + } + + return +} + +func HandleSettlementSignInGreenEnergy(ch *rabbit.Channel, engine *xorm.Engine, masterId string, id int64, uid int, endTime string) (err error) { + //1、查找 `one_circles_green_energy_basic_setting` 基础设置 + //oneCirclesGreenEnergyBasicSetting, cb, err := svc.GetPrice(engine, masterId) + //if err != nil { + // return + //} + //if cb != nil { + // defer cb() // 释放锁 + //} + + oneCirclesGreenEnergyBasicSetting, err := db.OneCirclesGreenEnergyBasicSettingGetOneByParams(engine, map[string]interface{}{ + "key": "is_open", + "value": 1, + }) + + var teamRewardSetting *md2.TeamRewardSettingStruct + err = json.Unmarshal([]byte(oneCirclesGreenEnergyBasicSetting.TeamReward), &teamRewardSetting) + if err != nil { + return + } + if teamRewardSetting.RewardDecrementValue == "" || teamRewardSetting.RewardEndValue == "" || teamRewardSetting.MemberSelfIsOpenGetTeamReward == "" || teamRewardSetting.OneRoundDuration == "" { + err = errors.New("团队奖励设置未完全!") + return + } + var oneRoundDuration = egg_system_rules.StrToInt(teamRewardSetting.OneRoundDuration) + var rewardDecrement = egg_system_rules.StrToFloat64(teamRewardSetting.RewardDecrementValue) / 100 //递减百分比 + var rewardEndValue = egg_system_rules.StrToFloat64(teamRewardSetting.RewardEndValue) //奖励结束值 + fmt.Println("rewardDecrement>>>>>>>>>>>>", rewardDecrement) + fmt.Println("rewardEndValue>>>>>>>>>>>>", rewardEndValue) + + //2、获取当前签到收益 + err, reward := CalcNowSignInGreenEnergy(engine, oneCirclesGreenEnergyBasicSetting) + if err != nil { + return err + } + rewardValue := egg_system_rules.Float64ToStrPrec8(egg_system_rules.StrToFloat64(reward) * float64(oneRoundDuration) * 60 * 60) + + //3、统计签到结束的用户数据 + + var reduceTotalGreenEnergy float64 + //4.2给相应的用户加上个人的绿色积分(可用数量) + var oneCirclesDealUserCoinData = md.DealUserCoinReq{ + Kind: "add", + Mid: masterId, + Title: md.OneCirclesGreenEnergySignInSettlementPersonalReward, + TransferType: md.OneCirclesGreenEnergySignInSettlementPersonalRewardForUserVirtualCoinFlow, + OrdId: "", + CoinId: oneCirclesGreenEnergyBasicSetting.PersonGreenEnergyCoinId, + Uid: uid, + Amount: egg_system_rules.StrToFloat64(rewardValue), + } + + reduceTotalGreenEnergy += egg_system_rules.StrToFloat64(rewardValue) + //4.2给相应的上级用户加上团队奖励的绿色积分(结算数量) + relates, err1 := db.DbsUserRelate(engine, uid, 0) + if err1 != nil { + fmt.Println("err:::::3333", err1) + return err1 + } + var oneCirclesDealUserCoinDataForParents []md.DealUserCoinReq + var parentRewardValue = egg_system_rules.StrToFloat64(rewardValue) + if relates != nil { + for _, relate := range *relates { + parentRewardValue = parentRewardValue * rewardDecrement + if parentRewardValue <= rewardEndValue { + break + } + //TODO::判断是否活跃 + var m model.OneCirclesGreenEnergySignIn + if endTime != "" { + has, err3333333 := engine.Where("uid =?", relate.ParentUid).And("end_time >=?", endTime).Get(&m) + if err3333333 != nil { + fmt.Println("err:::::3333333", err3333333) + return err3333333 + } + if !has { + //不活跃不需要奖励 + continue + } + } else { + has, err3333333 := engine.Where("uid =?", relate.ParentUid).And("end_time >=?", time.Now().Format("2006-01-02 15:04:05")).Get(&m) + if err3333333 != nil { + fmt.Println("err:::::3333333", err3333333) + return err3333333 + } + if !has { + //不活跃不需要奖励 + continue + } + } + + oneCirclesDealUserCoinDataForParents = append(oneCirclesDealUserCoinDataForParents, md.DealUserCoinReq{ + Kind: "add", + Mid: masterId, + Title: md.OneCirclesGreenEnergySignInSettlementTeamReward, + TransferType: md.OneCirclesGreenEnergySignInSettlementTeamRewardForUserVirtualCoinFlow, + OrdId: "", + CoinId: oneCirclesGreenEnergyBasicSetting.TeamGreenEnergyCoinId, + Uid: relate.ParentUid, + Amount: parentRewardValue, + }) + reduceTotalGreenEnergy += parentRewardValue + } + } + + //5、减少“活跃赠送” 中的绿色能量 + if reduceTotalGreenEnergy > 0 { + err = DealAvailableGreenEnergyCoin(engine.NewSession(), int(enum.SignInReward), reduceTotalGreenEnergy, 0, enum.SignInReward.String(), oneCirclesGreenEnergyBasicSetting, oneCirclesGreenEnergyBasicSetting.NowPrice, masterId) + if err != nil { + fmt.Println("err:::::55555", err) + return err + } + } + + //6、更新 `one_circles_green_energy_sign_in` 中的 is_completed 状态 + _, err = engine.Where("id = ?", id).Update(&model.OneCirclesGreenEnergySignIn{ + IsCompleted: 1, + }) + if err != nil { + fmt.Println("err:::::66666", err) + return err + } + + ////TODO::推入rabbitmq 异步处理 + ch.Publish(md.OneCirclesExchange, oneCirclesDealUserCoinData, md.OneCirclesRoutKeyForRewardUserCoin) //个人的奖励 + for _, oneCirclesDealUserCoinDataForParent := range oneCirclesDealUserCoinDataForParents { + ch.Publish(md.OneCirclesExchange, oneCirclesDealUserCoinDataForParent, md.OneCirclesRoutKeyForRewardUserCoin) //上级的奖励 + } + + return +} + +// CalcNowSignInGreenEnergy 计算当前签到拿多少绿色能量/秒 +func CalcNowSignInGreenEnergy(engine *xorm.Engine, oneCirclesGreenEnergyBasicSetting *model.OneCirclesGreenEnergyBasicSetting) (err error, rewardValue string) { + rewardValue, _ = cache.GetString("one_circle_reward_value") + if rewardValue == "" { + if oneCirclesGreenEnergyBasicSetting == nil { + //1、查找 `one_circles_green_energy_basic_setting` 基础设置 + oneCirclesGreenEnergyBasicSetting, err = db.OneCirclesGreenEnergyBasicSettingGetOneByParams(engine, map[string]interface{}{ + "key": "is_open", + "value": 1, + }) + if err != nil { + return + } + } + + var signInRewards []*md2.SignInRewardStruct + err = json.Unmarshal([]byte(oneCirclesGreenEnergyBasicSetting.SignInReward), &signInRewards) + if err != nil { + return + } + if len(signInRewards) == 0 { + err = errors.New("未设置签到奖励!") + return + } + + //2、统计全网用户数 + sqlStr := "SELECT COUNT(*) AS total FROM user " + nativeString, _ := db.QueryNativeString(engine, sqlStr) + userCount := egg_system_rules.StrToInt64(nativeString[0]["total"]) + + for _, v := range signInRewards { + if egg_system_rules.StrToInt64(v.VipMemberEndNums) >= userCount && userCount >= egg_system_rules.StrToInt64(v.VipMemberStartNums) { + //rewardValue = egg_system_rules.Float64ToStrPrec8(egg_system_rules.StrToFloat64(v.RewardValue) * float64(oneRoundDuration) * 60 * 60) + rewardValue = v.RewardValue + } + } + cache.SetEx("one_circle_reward_value", rewardValue, 3600*8) //8小时 + } + + return +} + +// CalcTodayGreenEnergyPriceRises 计算绿色能量今日价格涨幅 +func CalcTodayGreenEnergyPriceRises(engine *xorm.Engine, oneCirclesGreenEnergyBasicSetting *model.OneCirclesGreenEnergyBasicSetting) (err error, rises float64, isRises bool) { + now := time.Now() + if oneCirclesGreenEnergyBasicSetting == nil { + //1、查找 `one_circles_green_energy_basic_setting` 基础设置 + oneCirclesGreenEnergyBasicSetting, err = db.OneCirclesGreenEnergyBasicSettingGetOneByParams(engine, map[string]interface{}{ + "key": "is_open", + "value": 1, + }) + if err != nil { + return + } + } + + //2、查找昨日价格 + var m model.OneCirclesGreenEnergyPrice + if has, err1 := engine.Where("date =?", now.AddDate(0, 0, -1).Format("2006-01-02")).And("hour = 23").Get(&m); err1 != nil || has == false { + err = err1 + return + } + yesterdayPrice, _ := decimal.NewFromString(m.Price) + todayPrice, _ := decimal.NewFromString(oneCirclesGreenEnergyBasicSetting.NowPrice) + if todayPrice.GreaterThanOrEqual(yesterdayPrice) { + isRises = true + rises, _ = todayPrice.Sub(yesterdayPrice).Div(yesterdayPrice).Float64() + } else { + rises, _ = yesterdayPrice.Sub(todayPrice).Div(todayPrice).Float64() + } + return +} diff --git a/rule/egg_energy/md/egg_energy_green_energy.go b/rule/egg_energy/md/egg_energy_green_energy.go new file mode 100644 index 0000000..bd49554 --- /dev/null +++ b/rule/egg_energy/md/egg_energy_green_energy.go @@ -0,0 +1,99 @@ +package md + +type VipEquitySettingStruct struct { + VipLevelId string `json:"vip_level_id"` //会员等级id + ExchangeAccountBalanceFee string `json:"exchange_account_balance_fee"` //兑换余额手续费 + DividendRatio string `json:"dividend_ratio"` //分红比例 +} + +type TeamRewardSettingStruct struct { + RewardDecrementValue string `json:"reward_decrement_value"` //递减百分比 + RewardEndValue string `json:"reward_end_value"` //奖励结束值 + MemberSelfIsOpenGetTeamReward string `json:"member_self_is_open_get_team_reward"` //会员是否活跃得到团队奖励 + OneRoundDuration string `json:"one_round_duration"` //一轮持续时间 +} + +type SignInRewardStruct struct { + VipMemberStartNums string `json:"vip_member_start_nums"` //会员起始数量 + VipMemberEndNums string `json:"vip_member_end_nums"` //会员结束数量 + RewardValue string `json:"reward_value"` //奖励值 +} + +type PriceSettingStruct struct { + PriceHigherThanValue string `json:"price_higher_than_value"` //高于x元 + MarketplaceMerchantsNumsExchangeMarketplaceMerchantsFundValue string `json:"marketplace_merchants_nums_exchange_marketplace_merchants_fund_value"` //市商数量单笔x数量自动兑换 + PriceBelowValue string `json:"price_below_value"` //低于x元 + MarketplaceMerchantsFundExchangeMarketplaceMerchantsNumsValue string `json:"marketplace_merchants_fund_exchange_marketplace_merchants_nums_value"` //市商资金单笔x元自动兑换 +} + +type DestructionSettingStruct struct { + DestructionQuantity string `json:"destruction_quantity"` //销毁百分比 + PublicWelfareAndCharity string `json:"public_welfare_and_charity"` //公益慈善百分比 + DevelopmentCommittee string `json:"development_committee"` //发展委员会百分比 + StarLevelDividends string `json:"star_level_dividends"` //星级分红百分比 + MarketplaceMerchant string `json:"marketplace_merchant"` //市商数量百分比 + CommunityDividends string `json:"community_dividends"` //社区分红百分比 +} + +type WelfareOrdersLimit struct { + ContinuousDailyActivityNums string `json:"continuous_daily_activity_nums"` //连续日活 + DirectPushLimit struct { + DirectPushUserNums string `json:"direct_push_user_nums"` //直推用户数 + ContinuousDailyActivityNums string `json:"continuous_daily_activity_nums"` //连续日活 + } `json:"direct_push_limit"` //直推机制 + WelfareOrdersProfitExchange string `json:"welfare_orders_profit_exchange"` //福利订单利润兑换率 + SingleUserLimitBuyNums string `json:"single_user_limit_buy_nums"` //单个用户限购 + PlatformMembers []struct { + From string `json:"from"` + To string `json:"to"` + } `json:"platform_members"` //平台会员 +} + +const DealUserCoinRequestIdPrefix = "egg_deal_user_coin:%d:uid:%d" + +type AddPublicPlatoonUserRelationCommissionReq struct { + Uid string `json:"uid"` + RecommendUid string `json:"recommend_uid"` //推荐人uid +} + +type VideoRewardSystemStruct struct { + RewardValue string `json:"reward_value"` //奖励X个活跃积分 + RewardTotalNum string `json:"reward_total_num"` //一共X个奖励视屏 + IntervalMinutes string `json:"interval_minutes"` //间隔X秒 + EachRoundHour string `json:"each_round_hour"` //每一轮X个小时 +} + +type RewardSystemStruct struct { + Level int `json:"level"` //圈层 + RewardCondition string `json:"reward_condition"` //奖励条件 + RewardValue string `json:"reward_value"` //奖励值 +} + +type ExchangeRulesStruct struct { + AutoExchangeNumsByPerson string `json:"auto_exchange_nums_by_person"` //个人数量X个,自动兑换可用蛋蛋能量 + AutoExchangeRateByPerson string `json:"auto_exchange_rate_by_person"` //个人数量百分比,自动兑换可用蛋蛋能量 + AutoExchangeNumsByTeam string `json:"auto_exchange_nums_by_team"` //团队数量X个,自动兑换团队蛋蛋能量 + AutoExchangeRateByTeam string `json:"auto_exchange_rate_by_team"` //团队数量百分比,自动兑换团队蛋蛋能量 +} + +const UserNextWatchAdDate = "user_next_watch_date:%d" + +type CalcPriceIncreaseFormulaResp struct { + GetEggEnergyNums string `json:"get_egg_energy_nums"` //得到蛋蛋能量的数量 + BeforePrice string `json:"before_price"` //变更前-价格 + AfterPrice string `json:"after_price"` //变更后-价格 + BeforePlanetTotalValue string `json:"before_planet_total_value"` //变更前-星球总价值 + AfterPlanetTotalValue string `json:"after_planet_total_value"` //变更后-星球总价值 + BeforeEnergyTotalNums string `json:"before_energy_total_nums"` //变更前-现行总量 + AfterEnergyTotalNums string `json:"after_energy_total_nums"` //变更后-现行总量 +} + +type CalcPriceReductionFormulaResp struct { + GetEggEnergyAmount string `json:"get_egg_energy_amount"` //得到蛋蛋能量的钱 + BeforePrice string `json:"before_price"` //变更前-价格 + AfterPrice string `json:"after_price"` //变更后-价格 + BeforePlanetTotalValue string `json:"before_planet_total_value"` //变更前-星球总价值 + AfterPlanetTotalValue string `json:"after_planet_total_value"` //变更后-星球总价值 + BeforeEnergyTotalNums string `json:"before_energy_total_nums"` //变更前-现行总量 + AfterEnergyTotalNums string `json:"after_energy_total_nums"` //变更后-现行总量 +} diff --git a/rule/egg_energy/md/mq.go b/rule/egg_energy/md/mq.go new file mode 100644 index 0000000..2df241d --- /dev/null +++ b/rule/egg_energy/md/mq.go @@ -0,0 +1,53 @@ +package md + +const EggEnergyExchange = "egg.energy" + +const ( + EggEnergyRoutKeyForStarLevelDividend = "star_level_dividend" // 星级分红 + EggEnergyRoutKeyForSettlementPublicGiveActivityCoin = "settlement_public_give_activity_coin" // 计算观看激励视屏得到活跃积分 + EggEnergyRoutKeyForReadyExchangeGreenEnergyToPerson = "ready_exchange_egg_energy_to_person" // 准备兑换个人蛋蛋能量 + EggEnergyRoutKeyForStartExchangeGreenEnergyToPerson = "start_exchange_egg_energy_to_person" // 开始兑换个人蛋蛋能量 + EggEnergyRoutKeyForAutoExchangeGreenEnergyToPerson = "auto_exchange_egg_energy_to_person" // 自动兑换个人蛋蛋能量 + EggEnergyRoutKeyForReadyExchangeGreenEnergyToTeam = "ready_exchange_egg_energy_to_team" // 自动兑换团队蛋蛋能量 + EggEnergyRoutKeyForStartExchangeGreenEnergyToTeam = "start_exchange_egg_energy_to_team" // 开始兑换团队蛋蛋能量 + EggEnergyRoutKeyForAutoExchangeGreenEnergyToTeam = "auto_exchange_egg_energy_to_team" // 自动兑换团队蛋蛋能量 + + EggEnergyRoutKeyForSignIn = "sign_in" // 签到 + EggEnergyRoutKeyForSignInUpdateRecords = "sign_in_update_records" // 签到更新记录 + EggEnergyRoutKeyForRewardUserCoin = "reward_user_coin" // 奖励用户虚拟币 + +) + +type EggEnergyStructForSignIn struct { + MasterId string `json:"master_id"` + Uid int `json:"uid"` + Id int64 `json:"id"` + EndTime string `json:"end_time"` +} +type UpdateEggEnergyStructForSignInRecords struct { + Id int64 `json:"id"` + Uid int `json:"uid"` +} + +type EggEnergyStructForStarLevelDividends struct { + Uid int64 `json:"uid"` + SignDividend float64 `json:"sign_dividend"` +} + +type EggEnergyStructForStartExchangeGreenEnergy struct { + Uid int64 `json:"uid"` + Amount string `json:"amount"` + PointCoinId int `json:"point_coin_id"` + EnergyCoinId int `json:"energy_coin_id"` + AutoExchangeNumsAmount string `json:"auto_exchange_nums_amount"` + StartExchangeTime string `json:"start_exchange_time"` //发起兑换时间 + EndExchangeTime string `json:"end_exchange_time"` //截止兑换时间 +} + +type EggEnergyStructForAutoExchangeGreenEnergy struct { + Uid int64 `json:"uid"` + EnergyCoinId int `json:"energy_coin_id"` + AutoExchangeNumsAmount string `json:"auto_exchange_nums_amount"` + StartExchangeTime string `json:"start_exchange_time"` //发起兑换时间 + EndExchangeTime string `json:"end_exchange_time"` //截止兑换时间 +} diff --git a/rule/egg_energy/star_level_dividends.go b/rule/egg_energy/star_level_dividends.go new file mode 100644 index 0000000..119b8f1 --- /dev/null +++ b/rule/egg_energy/star_level_dividends.go @@ -0,0 +1,304 @@ +package egg_energy + +import ( + "code.fnuoos.com/EggPlanet/egg_models.git/src/implement" + "code.fnuoos.com/EggPlanet/egg_models.git/src/model" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/enum" + md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md" + egg_system_rules "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/cache" + zhios_order_relate_logx "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/logx" + "code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit" + "encoding/json" + "errors" + "fmt" + "github.com/shopspring/decimal" + "time" + "xorm.io/xorm" +) + +const SettlementStarLevelDividendsLockKey = "settlement_star_level_dividends_lock_key" + +// SettlementStarLevelDividends 蛋蛋能量星级分红 +func SettlementStarLevelDividends(engine *xorm.Engine, masterId string, ch *rabbit.Channel) (err error) { + now := time.Now() + fmt.Println(now.Hour()) + if !(now.Hour() > 2 && now.Hour() < 8) { + //TODO::只在凌晨一点 ~ 凌晨 8 点运行 + return errors.New("非运行时间") + } + //TODO::增加“悲观锁”防止串行 + getString, _ := cache.GetString(SettlementStarLevelDividendsLockKey) + if getString != "" { + fmt.Println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", "上一次结算未执行完") + return errors.New("上一次结算未执行完") + } + cache.SetEx(SettlementStarLevelDividendsLockKey, "running", 3600*8) //8小时 + + //1、查找 `egg_energy_basic_setting` 基础设置 + eggEnergyBasicSettingDb := implement.NewEggEnergyBasicSettingDb(engine) + eggEnergyBasicSetting, err := eggEnergyBasicSettingDb.EggEnergyBasicSettingGetOneByParams(map[string]interface{}{ + "key": "is_open", + "value": 1, + }) + if err != nil { + return + } + + isLimitDividend := eggEnergyBasicSetting.IsLimitDividend + if isLimitDividend != 1 { + return errors.New("必须开启限制分红!") + } + startDate := now.AddDate(0, 0, -1).Format("2006-01-02") + " 00:00:00" + endDate := now.Format("2006-01-02") + " 00:00:00" + var list []model.EggSignIn + err = engine.Where("start_time >= ?", startDate).And("start_time <=?", endDate).Find(&list) + if err != nil { + fmt.Println("err:::::1111", err) + return + } + if len(list) <= 0 { + return errors.New("无须参与星级分红用户!") + } + + var vipEquitySetting []*md2.VipEquitySettingStruct + err = json.Unmarshal([]byte(eggEnergyBasicSetting.VipEquitySetting), &vipEquitySetting) + if err != nil { + fmt.Println("err:::::22222", err) + return + } + + userLevelDb := implement.NewUserLevelDb(engine) + allUserLevel, err := userLevelDb.UserLevelAllByAsc() + if err != nil { + return err + } + var allUserLevelMap = map[int]model.UserLevel{} + for _, v := range allUserLevel { + allUserLevelMap[v.Id] = *v + } + + session := engine.NewSession() + defer func() { + session.Close() + if err := recover(); err != nil { + _ = zhios_order_relate_logx.Error(err) + } + }() + session.Begin() + + //2、查找 `egg_star_level_dividends_records` 基础设置 + eggStarLevelDividendsRecordsDb := implement.NewEggStarLevelDividendsRecordsDb(engine) + eggStarLevelDividendsRecords, err := eggStarLevelDividendsRecordsDb.EggStarLevelDividendsRecordsGetOneByParamsBySession(session, map[string]interface{}{ + "key": "date", + "value": now.AddDate(0, 0, -1).Format("2006-01-02"), + }) + if err != nil { + fmt.Println("err:::::33333", err) + return + } + if eggStarLevelDividendsRecords == nil { + return errors.New("今日无分红积分!") + } + + //3、统计各等级人数 + dividendAmountValue, _ := decimal.NewFromString(eggStarLevelDividendsRecords.Amount) + decimalRate := decimal.NewFromInt(100) //百分比 + var vipLevelUserTotalMap = map[string]map[string]int64{} + var vipEquitySettingMap = map[string]string{} + for _, v := range vipEquitySetting { + dividendRatioValue, _ := decimal.NewFromString(v.DividendRatio) + dividendRatioValue = dividendRatioValue.Div(decimalRate) //分红比例 + vipEquitySettingMap[v.VipLevelId] = dividendAmountValue.Mul(dividendRatioValue).String() //TODO::计算各会员等级能得到多少分红 + + userLevel, err1 := userLevelDb.UserLevelByID(v.VipLevelId) + if err1 != nil { + fmt.Println("err:::::444444", err1) + return err1 + } + var ms []*model.UserLevel + err1 = session.Where("is_use=1").Where("level_weight > ?", userLevel.LevelWeight).Find(&ms) + if err1 != nil { + fmt.Println("err:::::55555", err1) + return err1 + } + + var tmpVipLevelId = []string{v.VipLevelId} + for _, m := range ms { + tmpVipLevelId = append(tmpVipLevelId, egg_system_rules.IntToStr(m.Id)) + } + var users model.User + count, err1 := session.In("level", tmpVipLevelId).Count(&users) + if err1 != nil { + fmt.Println("err:::::66666", err1) + return err1 + } + vipLevelUserTotalMap[v.VipLevelId] = map[string]int64{} + vipLevelUserTotalMap[v.VipLevelId]["count"] = count + vipLevelUserTotalMap[v.VipLevelId]["weight"] = int64(userLevel.LevelWeight) + } + + //4、处理分红 + var userSignInArr []int64 + var totalDividendValue = decimal.Decimal{} + userRelateDb := implement.NewUserRelateDb(engine) + for _, l := range list { + userSignInArr = append(userSignInArr, l.Uid) + if len(userSignInArr) >= 10000 { + var users []*model.User + err = session.In("uid", userSignInArr).Find(&users) + if err != nil { + fmt.Println("err:::::7777777", err) + return err + } + for _, item := range users { + var siginDividendValue = decimal.Decimal{} + for k, v := range vipLevelUserTotalMap { + if int(v["weight"]) > allUserLevelMap[item.Level].LevelWeight { + continue + } + if vipEquitySettingMap[k] != "" && vipLevelUserTotalMap[k]["count"] > 0 { + dividendValue, _ := decimal.NewFromString(vipEquitySettingMap[k]) + userTotal := decimal.NewFromInt(vipLevelUserTotalMap[k]["count"]) + siginDividendValue = siginDividendValue.Add(dividendValue.Div(userTotal)) + } + } + + if siginDividendValue.GreaterThan(decimal.NewFromInt(0)) { + //TODO::统计当前有多少直推用户昨天签到了 + userRelates, err1 := userRelateDb.FindUserRelateByParentUid(item.Id, 1) + if err1 != nil { + fmt.Println("err:::::88888888", err1) + return err + } + var userRelatesUids []int64 + if userRelates != nil { + for _, userRelate := range *userRelates { + userRelatesUids = append(userRelatesUids, userRelate.Uid) + } + } + if len(userRelatesUids) > 0 { + var eggEnergySignIns []*model.EggSignIn + err = engine.Where("start_time >= ?", startDate).And("start_time <=?", endDate).In("uid", userRelatesUids).And("is_completed =?", 0).Find(&eggEnergySignIns) + if err != nil { + fmt.Println("err:::::999999999", err) + return + } + if len(eggEnergySignIns) > 0 { + userRelatesUidsValue := decimal.NewFromInt(int64(len(userRelatesUids))) + eggEnergySignInsValue := decimal.NewFromInt(int64(len(eggEnergySignIns))) + siginDividendValue = siginDividendValue.Mul(eggEnergySignInsValue.Div(userRelatesUidsValue)) + } else { + siginDividendValue = decimal.NewFromFloat(0) + } + } else { + siginDividendValue = decimal.NewFromFloat(0) + } + + siginDividend, _ := siginDividendValue.Float64() + if siginDividend > 0 { + //TODO::推入rabbitmq 异步处理 + ch.Publish(md2.EggEnergyExchange, md2.EggEnergyStructForStarLevelDividends{ + Uid: item.Id, + SignDividend: siginDividend, + }, md2.EggEnergyRoutKeyForStarLevelDividend) + + totalDividendValue = totalDividendValue.Add(siginDividendValue) + } + } + } + userSignInArr = []int64{} + } + } + + if len(userSignInArr) > 0 { + var users []*model.User + err = session.In("uid", userSignInArr).Find(&users) + if err != nil { + fmt.Println("err:::::7777777", err) + return err + } + + for _, item := range users { + var siginDividendValue = decimal.Decimal{} + for k, v := range vipLevelUserTotalMap { + if int(v["weight"]) > allUserLevelMap[item.Level].LevelWeight { + continue + } + if vipEquitySettingMap[k] != "" && vipLevelUserTotalMap[k]["count"] > 0 { + dividendValue, _ := decimal.NewFromString(vipEquitySettingMap[k]) + userTotal := decimal.NewFromInt(vipLevelUserTotalMap[k]["count"]) + siginDividendValue = siginDividendValue.Add(dividendValue.Div(userTotal)) + } + } + + if siginDividendValue.GreaterThan(decimal.NewFromInt(0)) { + //TODO::统计当前有多少直推用户昨天签到了 + userRelates, err1 := userRelateDb.FindUserRelateByParentUid(item.Id, 1) + if err1 != nil { + fmt.Println("err:::::88888888", err1) + return err + } + var userRelatesUids []int64 + if userRelates != nil { + for _, userRelate := range *userRelates { + userRelatesUids = append(userRelatesUids, userRelate.Uid) + } + } + if len(userRelatesUids) > 0 { + var eggEnergySignIns []*model.EggSignIn + err = engine.Where("start_time >= ?", startDate).And("start_time <=?", endDate).In("uid", userRelatesUids).And("is_completed =?", 0).Find(&eggEnergySignIns) + if err != nil { + fmt.Println("err:::::999999999", err) + return + } + if len(eggEnergySignIns) > 0 { + userRelatesUidsValue := decimal.NewFromInt(int64(len(userRelatesUids))) + eggEnergySignInsValue := decimal.NewFromInt(int64(len(eggEnergySignIns))) + siginDividendValue = siginDividendValue.Mul(eggEnergySignInsValue.Div(userRelatesUidsValue)) + } else { + siginDividendValue = decimal.NewFromFloat(0) + } + } else { + siginDividendValue = decimal.NewFromFloat(0) + } + + siginDividend, _ := siginDividendValue.Float64() + if siginDividend > 0 { + //TODO::推入rabbitmq 异步处理 + ch.Publish(md2.EggEnergyExchange, md2.EggEnergyStructForStarLevelDividends{ + Uid: item.Id, + SignDividend: siginDividend, + }, md2.EggEnergyRoutKeyForStarLevelDividend) + + totalDividendValue = totalDividendValue.Add(siginDividendValue) + } + } + } + } + + //5、更新 egg_star_level_dividends_records 记录 + eggStarLevelDividendsRecords.AlreadyDividendsAmount = totalDividendValue.String() + eggStarLevelDividendsRecords.NotDividendsAmount = dividendAmountValue.Sub(totalDividendValue).String() + _, err = eggStarLevelDividendsRecordsDb.EggStarLevelDividendsRecordsUpdateBySession(session, eggStarLevelDividendsRecords.Id, eggStarLevelDividendsRecords, "already_dividends_amount", "not_dividends_amount") + if err != nil { + _ = session.Rollback() + fmt.Println("err:::::8888888", err) + return err + } + + //6、 减少“星级分红”中的绿色能量 + totalDividend, _ := totalDividendValue.Float64() + err = DealAvailableGreenEnergyCoin(session, int(enum.SettlementStarLevelDividends), totalDividend, 0, enum.SettlementStarLevelDividends.String(), nil, "", masterId) + if err != nil { + _ = session.Rollback() + fmt.Println("err:::::9999999", err) + return err + } + err = session.Commit() + if err != nil { + _ = session.Rollback() + return errors.New("事务提交失败") + } + return +} diff --git a/rule/egg_energy/svc/svc_egg_energy_core_data.go b/rule/egg_energy/svc/svc_egg_energy_core_data.go new file mode 100644 index 0000000..7cd1130 --- /dev/null +++ b/rule/egg_energy/svc/svc_egg_energy_core_data.go @@ -0,0 +1,101 @@ +package svc + +import ( + "code.fnuoos.com/EggPlanet/egg_models.git/src/implement" + "code.fnuoos.com/EggPlanet/egg_models.git/src/model" + zhios_order_relate_utils "code.fnuoos.com/EggPlanet/egg_models.git/utils" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/cache" + "errors" + "fmt" + "math/rand" + "reflect" + "time" + "xorm.io/xorm" +) + +const redisMutexLockExpTime = 15 + +// TryGetDistributedLock 分布式锁获取 +// requestId 用于标识请求客户端,可以是随机字符串,需确保唯一 +func TryGetDistributedLock(lockKey, requestId string, isNegative bool) bool { + if isNegative { // 多次尝试获取 + retry := 1 + for { + ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX") + // 获取锁成功 + if err == nil && ok == "OK" { + return true + } + // 尝试多次没获取成功 + if retry > 99 { + return false + } + time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) + retry += 1 + } + } else { // 只尝试一次 + ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX") + // 获取锁成功 + if err == nil && ok == "OK" { + return true + } + + return false + } +} + +// ReleaseDistributedLock 释放锁,通过比较requestId,用于确保客户端只释放自己的锁,使用lua脚本保证操作的原子型 +func ReleaseDistributedLock(lockKey, requestId string) (bool, error) { + luaScript := ` + if redis.call("get",KEYS[1]) == ARGV[1] + then + return redis.call("del",KEYS[1]) + else + return 0 + end` + + do, err := cache.Do("eval", luaScript, 1, lockKey, requestId) + fmt.Println(reflect.TypeOf(do)) + fmt.Println(do) + if zhios_order_relate_utils.AnyToInt64(do) == 1 { + return true, err + } else { + return false, err + } +} + +func GetDistributedLockRequestId(prefix string) string { + return prefix + zhios_order_relate_utils.IntToStr(rand.Intn(100000000)) +} + +// HandleDistributedLock 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁 +func handleDistributedLock(requestIdPrefix string) (cb func(), err error) { + // 获取余额更新锁 + balanceLockKey := fmt.Sprintf(md.EggEnergyNowPriceUpdateLock) + requestId := GetDistributedLockRequestId(requestIdPrefix) + balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true) + if !balanceLockOk { + return nil, errors.New("获取价格系统繁忙,请稍后再试") + } + + cb = func() { + _, _ = ReleaseDistributedLock(balanceLockKey, requestId) + } + + return cb, nil +} + +func GetEggEnergyCoreData(engine *xorm.Engine) (coreData *model.EggEnergyCoreData, cb func(), err error) { + requestIdPrefix := fmt.Sprintf(md.DealEggEnergyNowPriceRequestIdPrefix, time.Now().UnixMicro()) + cb, err = handleDistributedLock(requestIdPrefix) + if err != nil { + return + } + eggEnergyCoreDataDb := implement.NewEggEnergyCoreDataDb(engine) + coreData, err = eggEnergyCoreDataDb.EggEnergyCoreDataGet() + if err != nil { + return + } + return +} diff --git a/rule/public_platoon_user_relation_commission.go b/rule/public_platoon_user_relation_commission.go new file mode 100644 index 0000000..9519154 --- /dev/null +++ b/rule/public_platoon_user_relation_commission.go @@ -0,0 +1,909 @@ +package rule + +import ( + "code.fnuoos.com/EggPlanet/egg_models.git/src/implement" + "code.fnuoos.com/EggPlanet/egg_models.git/src/model" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/cache" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/logx" + "errors" + "fmt" + "math" + "strconv" + "strings" + "time" + "xorm.io/xorm" +) + +// AddPublicPlatoonUserRelationCommission 新增公排用户关系记录 +func AddPublicPlatoonUserRelationCommission(engine *xorm.Engine, AddPublicPlatoonUserRelationCommissionReqList []*md.AddPublicPlatoonUserRelationCommissionReq) (map[string]*model.PublicPlatoonUserRelation, error) { + var resp = map[string]*model.PublicPlatoonUserRelation{} + //查找 `public_platoon_basic_setting` 基础设置 + publicPlatoonBasicSettingDb := implement.NewPublicPlatoonBasicSettingDb(engine) + publicPlatoonBasicSetting, err := publicPlatoonBasicSettingDb.PublicPlatoonBasicSettingGetOneByParams(map[string]interface{}{ + "key": "is_open", + "value": 1, + }) + if err != nil { + return nil, err + } + if publicPlatoonBasicSetting == nil { + return nil, errors.New("公排制度未开启") + } + + publicPlatoonUserRelationDb := implement.NewPublicPlatoonUserRelationDb(engine) + for _, param := range AddPublicPlatoonUserRelationCommissionReqList { + isHasPublicPlatoonUserRelation, err11 := publicPlatoonUserRelationDb.PublicPlatoonUserRelationGetOneByParams(map[string]interface{}{ + "key": "uid", + "value": param.Uid, + }) + if err11 != nil { + return nil, err11 + } + if isHasPublicPlatoonUserRelation != nil { + return nil, errors.New("当前用户已加入公排") + } + + //1、判断是否有推荐人 + if param.RecommendUid != "" { + //2、有推荐人 + //判断是否有uid为-1 (代表等待新用户填充) 的记录 + PublicPlatoonUserRelation, err1 := publicPlatoonUserRelationDb.PublicPlatoonUserRelationGetOneByPid(param.RecommendUid, map[string]interface{}{ + "key": "uid", + "value": -1, + }) + if err1 != nil { + return nil, err1 + } + if PublicPlatoonUserRelation != nil { + now := time.Now() + oldUid := PublicPlatoonUserRelation.Uid + PublicPlatoonUserRelation.Uid = egg_system_rules.StrToInt64(param.Uid) + PublicPlatoonUserRelation.RecommendUid = egg_system_rules.StrToInt64(param.RecommendUid) + PublicPlatoonUserRelation.CreateAt = now.Format("2006-01-02 15:04:05") + PublicPlatoonUserRelation.UpdateAt = now.Format("2006-01-02 15:04:05") + PublicPlatoonUserRelation.JoinAt = now.Format("2006-01-02 15:04:05") + updateAffected, err2 := publicPlatoonUserRelationDb.PublicPlatoonUserRelationUpdate(engine.NewSession(), PublicPlatoonUserRelation.Id, PublicPlatoonUserRelation) + if err2 != nil { + return nil, err2 + } + if updateAffected == 0 { + err = errors.New("更新 public_platoon_user_relation 记录失败") + return nil, err + } else { + for n := 1; n <= 9; n++ { + str := "father_uid" + strconv.Itoa(n) + sql := fmt.Sprintf("UPDATE `public_platoon_user_relation` SET %s=%s where %s=%s", str, param.Uid, str, egg_system_rules.Int64ToStr(oldUid)) + fmt.Println(">>>>>>>sql>>>>>>>", sql) + _, err = engine.Exec(sql) + if err != nil { + return nil, err + } + } + resp[param.Uid] = PublicPlatoonUserRelation + } + continue + } + res, err := PublicPlatoon(engine, egg_system_rules.StrToInt64(param.Uid), egg_system_rules.StrToInt64(param.RecommendUid), *publicPlatoonBasicSetting) + if err != nil { + return nil, err + } + resp[param.Uid] = &res + } + } + return resp, nil +} + +/* + 公排方法 + +TODO 相关公式: +1: 每个等级的起始值(1+5^0+5^1+5^2+...+5^x-2), 每个等级的结束值(`5^0+5^1+5^2+...+5^x-1) +2: 根据position查找父级position { (position-1)/5 } +3: 根据position查找等级level {position-5^0-5^1-5^2-...-5^x 是否 <0 ? => x+1 } +4: 根据最新自增`id` 逆向推导 position {levelFirstPosition + (position-1)%5} +*/ +func PublicPlatoon(engine *xorm.Engine, uid, recommendUid int64, PublicPlatoonBasicSetting model.PublicPlatoonBasicSetting) (model.PublicPlatoonUserRelation, error) { + var standardPublicPlatoonRelation md.StandardPublicPlatoonRelation + var PublicPlatoonUserRelation model.PublicPlatoonUserRelation + var fatherUid int64 + //1、 查找当前 public_platoon_user_relation 中 recommendUid 的记录 + publicPlatoonUserRelationDb := implement.NewPublicPlatoonUserRelationDb(engine) + m, err := publicPlatoonUserRelationDb.PublicPlatoonUserRelationGetOneByParams(map[string]interface{}{ + "key": "uid", + "value": recommendUid, + }) + if err != nil { + return model.PublicPlatoonUserRelation{}, err + } + + //2、 查找当前 public_platoon_user_relation 中 recommendUid 所有的子记录 + PublicPlatoonUserRelations, err := publicPlatoonUserRelationDb.PublicPlatoonUserRelationFindByPid(m.Uid, "father_uid1", "position1") + if err != nil { + return model.PublicPlatoonUserRelation{}, err + } + + if len(PublicPlatoonUserRelations) == 0 { + fatherUid = m.Uid + + //证明是第一个 + standardPublicPlatoonRelation.FatherUid1 = m.Uid + standardPublicPlatoonRelation.FatherUid2 = m.FatherUid1 + standardPublicPlatoonRelation.FatherUid3 = m.FatherUid2 + standardPublicPlatoonRelation.FatherUid4 = m.FatherUid3 + standardPublicPlatoonRelation.FatherUid5 = m.FatherUid4 + standardPublicPlatoonRelation.FatherUid6 = m.FatherUid5 + standardPublicPlatoonRelation.FatherUid7 = m.FatherUid6 + standardPublicPlatoonRelation.FatherUid8 = m.FatherUid7 + standardPublicPlatoonRelation.FatherUid9 = m.FatherUid8 + + standardPublicPlatoonRelation.Pid1 = m.Position + standardPublicPlatoonRelation.Pid2 = m.Position1 + standardPublicPlatoonRelation.Pid3 = m.Position2 + standardPublicPlatoonRelation.Pid4 = m.Position3 + standardPublicPlatoonRelation.Pid5 = m.Position4 + standardPublicPlatoonRelation.Pid6 = m.Position5 + standardPublicPlatoonRelation.Pid7 = m.Position6 + standardPublicPlatoonRelation.Pid8 = m.Position7 + standardPublicPlatoonRelation.Pid9 = m.Position8 + + standardPublicPlatoonRelation.Position1 = SearchPositionFirstSonPosition(m.Position, int64(PublicPlatoonBasicSetting.SeveralTimes)) + standardPublicPlatoonRelation.Position2 = ReverseDeductionPositionByFather(m.Position1, int64(m.Level1), int64(PublicPlatoonBasicSetting.SeveralTimes), 0) + standardPublicPlatoonRelation.Position3 = ReverseDeductionPositionByFather(m.Position2, int64(m.Level2), int64(PublicPlatoonBasicSetting.SeveralTimes), 0) + standardPublicPlatoonRelation.Position4 = ReverseDeductionPositionByFather(m.Position3, int64(m.Level3), int64(PublicPlatoonBasicSetting.SeveralTimes), 0) + standardPublicPlatoonRelation.Position5 = ReverseDeductionPositionByFather(m.Position4, int64(m.Level4), int64(PublicPlatoonBasicSetting.SeveralTimes), 0) + standardPublicPlatoonRelation.Position6 = ReverseDeductionPositionByFather(m.Position5, int64(m.Level5), int64(PublicPlatoonBasicSetting.SeveralTimes), 0) + standardPublicPlatoonRelation.Position7 = ReverseDeductionPositionByFather(m.Position6, int64(m.Level6), int64(PublicPlatoonBasicSetting.SeveralTimes), 0) + standardPublicPlatoonRelation.Position8 = ReverseDeductionPositionByFather(m.Position7, int64(m.Level7), int64(PublicPlatoonBasicSetting.SeveralTimes), 0) + standardPublicPlatoonRelation.Position9 = ReverseDeductionPositionByFather(m.Position8, int64(m.Level8), int64(PublicPlatoonBasicSetting.SeveralTimes), 0) + + if m.Level1 == 0 { + m.Level1-- + } + if m.Level2 == 0 { + m.Level2-- + } + if m.Level3 == 0 { + m.Level3-- + } + if m.Level4 == 0 { + m.Level4-- + } + if m.Level5 == 0 { + m.Level5-- + } + if m.Level6 == 0 { + m.Level6-- + } + if m.Level7 == 0 { + m.Level7-- + } + if m.Level8 == 0 { + m.Level8-- + } + if m.Level9 == 0 { + m.Level9-- + } + + standardPublicPlatoonRelation.Level1 = m.Level + 1 + standardPublicPlatoonRelation.Level2 = m.Level1 + 1 + standardPublicPlatoonRelation.Level3 = m.Level2 + 1 + standardPublicPlatoonRelation.Level4 = m.Level3 + 1 + standardPublicPlatoonRelation.Level5 = m.Level4 + 1 + standardPublicPlatoonRelation.Level6 = m.Level5 + 1 + standardPublicPlatoonRelation.Level7 = m.Level6 + 1 + standardPublicPlatoonRelation.Level8 = m.Level7 + 1 + standardPublicPlatoonRelation.Level9 = m.Level8 + 1 + standardPublicPlatoonRelation.LevelTotal = m.LevelTotal + 1 + } else { + //TODO::判断直属下级是否排满 + if len(PublicPlatoonUserRelations) >= PublicPlatoonBasicSetting.SeveralTimes { + var fatherLevel float64 + makeSearchLevel(&m.Position, float64(PublicPlatoonBasicSetting.SeveralTimes), &fatherLevel) + var times = 0 + data, father, err := FindSuitablePosition(engine, &m.Position, float64(PublicPlatoonBasicSetting.SeveralTimes), m.Uid, int64(fatherLevel), ×) + if err != nil { + return model.PublicPlatoonUserRelation{}, err + } + fatherUid = father + standardPublicPlatoonRelation = data + } else { + fatherUid = m.Uid + + standardPublicPlatoonRelation.FatherUid1 = PublicPlatoonUserRelations[0].FatherUid1 + standardPublicPlatoonRelation.FatherUid2 = PublicPlatoonUserRelations[0].FatherUid2 + standardPublicPlatoonRelation.FatherUid3 = PublicPlatoonUserRelations[0].FatherUid3 + standardPublicPlatoonRelation.FatherUid4 = PublicPlatoonUserRelations[0].FatherUid4 + standardPublicPlatoonRelation.FatherUid5 = PublicPlatoonUserRelations[0].FatherUid5 + standardPublicPlatoonRelation.FatherUid6 = PublicPlatoonUserRelations[0].FatherUid6 + standardPublicPlatoonRelation.FatherUid7 = PublicPlatoonUserRelations[0].FatherUid7 + standardPublicPlatoonRelation.FatherUid8 = PublicPlatoonUserRelations[0].FatherUid8 + standardPublicPlatoonRelation.FatherUid9 = PublicPlatoonUserRelations[0].FatherUid9 + + standardPublicPlatoonRelation.Pid1 = PublicPlatoonUserRelations[0].Pid1 + standardPublicPlatoonRelation.Pid2 = PublicPlatoonUserRelations[0].Pid2 + standardPublicPlatoonRelation.Pid3 = PublicPlatoonUserRelations[0].Pid3 + standardPublicPlatoonRelation.Pid4 = PublicPlatoonUserRelations[0].Pid4 + standardPublicPlatoonRelation.Pid5 = PublicPlatoonUserRelations[0].Pid5 + standardPublicPlatoonRelation.Pid6 = PublicPlatoonUserRelations[0].Pid6 + standardPublicPlatoonRelation.Pid7 = PublicPlatoonUserRelations[0].Pid7 + standardPublicPlatoonRelation.Pid8 = PublicPlatoonUserRelations[0].Pid8 + standardPublicPlatoonRelation.Pid9 = PublicPlatoonUserRelations[0].Pid9 + + standardPublicPlatoonRelation.Position1 = PublicPlatoonUserRelations[0].Position1 + 1 + if PublicPlatoonUserRelations[0].Position2 != 0 { + standardPublicPlatoonRelation.Position2 = PublicPlatoonUserRelations[0].Position2 + 1 + } + if PublicPlatoonUserRelations[0].Position3 != 0 { + standardPublicPlatoonRelation.Position3 = PublicPlatoonUserRelations[0].Position3 + 1 + } + if PublicPlatoonUserRelations[0].Position4 != 0 { + standardPublicPlatoonRelation.Position4 = PublicPlatoonUserRelations[0].Position4 + 1 + } + if PublicPlatoonUserRelations[0].Position5 != 0 { + standardPublicPlatoonRelation.Position5 = PublicPlatoonUserRelations[0].Position5 + 1 + } + if PublicPlatoonUserRelations[0].Position6 != 0 { + standardPublicPlatoonRelation.Position6 = PublicPlatoonUserRelations[0].Position6 + 1 + } + if PublicPlatoonUserRelations[0].Position7 != 0 { + standardPublicPlatoonRelation.Position7 = PublicPlatoonUserRelations[0].Position7 + 1 + } + if PublicPlatoonUserRelations[0].Position8 != 0 { + standardPublicPlatoonRelation.Position8 = PublicPlatoonUserRelations[0].Position8 + 1 + } + if PublicPlatoonUserRelations[0].Position9 != 0 { + standardPublicPlatoonRelation.Position9 = PublicPlatoonUserRelations[0].Position9 + 1 + } + + standardPublicPlatoonRelation.Level1 = PublicPlatoonUserRelations[0].Level1 + standardPublicPlatoonRelation.Level2 = PublicPlatoonUserRelations[0].Level2 + standardPublicPlatoonRelation.Level3 = PublicPlatoonUserRelations[0].Level3 + standardPublicPlatoonRelation.Level4 = PublicPlatoonUserRelations[0].Level4 + standardPublicPlatoonRelation.Level5 = PublicPlatoonUserRelations[0].Level5 + standardPublicPlatoonRelation.Level6 = PublicPlatoonUserRelations[0].Level6 + standardPublicPlatoonRelation.Level7 = PublicPlatoonUserRelations[0].Level7 + standardPublicPlatoonRelation.Level8 = PublicPlatoonUserRelations[0].Level8 + standardPublicPlatoonRelation.Level9 = PublicPlatoonUserRelations[0].Level9 + standardPublicPlatoonRelation.LevelTotal = PublicPlatoonUserRelations[0].LevelTotal + + } + + } + + //2、查找对应pid的 public_platoon_user_relation 记录 + var m1 model.PublicPlatoonUserRelation + if has, err := engine.Where("uid=?", fatherUid).Get(&m1); err != nil || has == false { + return PublicPlatoonUserRelation, err + } + var fatherUidStr string + if m1.FatherUid == "" { + //TODO::顶级 + fatherUidStr = egg_system_rules.IntToStr(m1.Id) + } else { + fatherUids := strings.Split(m1.FatherUid, "-") + if len(fatherUids) >= PublicPlatoonBasicSetting.SeveralRows { + fatherUidStr = egg_system_rules.IntToStr(m1.Id) + "-" + strings.Join(fatherUids[0:PublicPlatoonBasicSetting.SeveralRows:len(fatherUids)], "-") + } else { + fatherUidStr = egg_system_rules.IntToStr(m1.Id) + "-" + m1.FatherUid + } + } + + // 3、插入 public_platoon_user_relation 记录 + now := time.Now() + PublicPlatoonUserRelation.Uid = uid + PublicPlatoonUserRelation.FatherUid = fatherUidStr + + PublicPlatoonUserRelation.FatherUid1 = standardPublicPlatoonRelation.FatherUid1 + PublicPlatoonUserRelation.FatherUid2 = standardPublicPlatoonRelation.FatherUid2 + PublicPlatoonUserRelation.FatherUid3 = standardPublicPlatoonRelation.FatherUid3 + PublicPlatoonUserRelation.FatherUid4 = standardPublicPlatoonRelation.FatherUid4 + PublicPlatoonUserRelation.FatherUid5 = standardPublicPlatoonRelation.FatherUid5 + PublicPlatoonUserRelation.FatherUid6 = standardPublicPlatoonRelation.FatherUid6 + PublicPlatoonUserRelation.FatherUid7 = standardPublicPlatoonRelation.FatherUid7 + PublicPlatoonUserRelation.FatherUid8 = standardPublicPlatoonRelation.FatherUid8 + PublicPlatoonUserRelation.FatherUid9 = standardPublicPlatoonRelation.FatherUid9 + PublicPlatoonUserRelation.Pid1 = standardPublicPlatoonRelation.Pid1 + PublicPlatoonUserRelation.Pid2 = standardPublicPlatoonRelation.Pid2 + PublicPlatoonUserRelation.Pid3 = standardPublicPlatoonRelation.Pid3 + PublicPlatoonUserRelation.Pid4 = standardPublicPlatoonRelation.Pid4 + PublicPlatoonUserRelation.Pid5 = standardPublicPlatoonRelation.Pid5 + PublicPlatoonUserRelation.Pid6 = standardPublicPlatoonRelation.Pid6 + PublicPlatoonUserRelation.Pid7 = standardPublicPlatoonRelation.Pid7 + PublicPlatoonUserRelation.Pid8 = standardPublicPlatoonRelation.Pid8 + PublicPlatoonUserRelation.Pid9 = standardPublicPlatoonRelation.Pid9 + PublicPlatoonUserRelation.Position = 1 + PublicPlatoonUserRelation.Position1 = standardPublicPlatoonRelation.Position1 + PublicPlatoonUserRelation.Position2 = standardPublicPlatoonRelation.Position2 + PublicPlatoonUserRelation.Position3 = standardPublicPlatoonRelation.Position3 + PublicPlatoonUserRelation.Position4 = standardPublicPlatoonRelation.Position4 + PublicPlatoonUserRelation.Position5 = standardPublicPlatoonRelation.Position5 + PublicPlatoonUserRelation.Position6 = standardPublicPlatoonRelation.Position6 + PublicPlatoonUserRelation.Position7 = standardPublicPlatoonRelation.Position7 + PublicPlatoonUserRelation.Position8 = standardPublicPlatoonRelation.Position8 + PublicPlatoonUserRelation.Position9 = standardPublicPlatoonRelation.Position9 + PublicPlatoonUserRelation.Level = 1 + PublicPlatoonUserRelation.Level1 = standardPublicPlatoonRelation.Level1 + PublicPlatoonUserRelation.Level2 = standardPublicPlatoonRelation.Level2 + PublicPlatoonUserRelation.Level3 = standardPublicPlatoonRelation.Level3 + PublicPlatoonUserRelation.Level4 = standardPublicPlatoonRelation.Level4 + PublicPlatoonUserRelation.Level5 = standardPublicPlatoonRelation.Level5 + PublicPlatoonUserRelation.Level6 = standardPublicPlatoonRelation.Level6 + PublicPlatoonUserRelation.Level7 = standardPublicPlatoonRelation.Level7 + PublicPlatoonUserRelation.Level8 = standardPublicPlatoonRelation.Level8 + PublicPlatoonUserRelation.Level9 = standardPublicPlatoonRelation.Level9 + PublicPlatoonUserRelation.LevelTotal = standardPublicPlatoonRelation.LevelTotal + PublicPlatoonUserRelation.RecommendUid = recommendUid + PublicPlatoonUserRelation.JoinAt = now.Format("2006-01-02 15:04:05") + PublicPlatoonUserRelation.CreateAt = now.Format("2006-01-02 15:04:05") + PublicPlatoonUserRelation.UpdateAt = now.Format("2006-01-02 15:04:05") + PublicPlatoonUserRelation.HasSonNum = 0 + _, err = publicPlatoonUserRelationDb.PublicPlatoonUserRelationInsert(&PublicPlatoonUserRelation) + if err != nil { + return model.PublicPlatoonUserRelation{}, err + } + + // 4、改变直属父级的 has_son_num 数量 + update, err := engine.Where("uid = ?", fatherUid).SetExpr("has_son_num", "has_son_num + 1").Update(new(model.PublicPlatoonUserRelation)) + if err != nil { + return PublicPlatoonUserRelation, err + } + if update == 0 { + return PublicPlatoonUserRelation, errors.New("更新has_son_num数据出错") + } + return PublicPlatoonUserRelation, nil +} + +// ReverseDeductionPosition 逆向推导 position +func ReverseDeductionPosition(calcPosition, levelFirstPosition, severalTimes int) (position int) { + remainder := (calcPosition - 1) % severalTimes + position = levelFirstPosition + remainder + return +} + +func ReverseDeductionPositionByFather(fatherPosition, level, severalTimes, remainder int64) (position int64) { + if fatherPosition == 0 { + position = 0 + return + } + firstPosition := GetLevelForFirstPosition(level, severalTimes) + endPosition := firstPosition + int64(math.Pow(float64(severalTimes), float64(level-1))) - 1 + position = fatherPosition + (fatherPosition-firstPosition)*severalTimes + (endPosition - fatherPosition) + 1 + return +} + +func FindSuitablePosition(engine *xorm.Engine, position *int64, severalTimes float64, fuid, fatherLevel int64, times *int) (standardPublicPlatoonRelation md.StandardPublicPlatoonRelation, fatherUid int64, err error) { + *times++ + positionName := "position" + egg_system_rules.IntToStr(*times) + fatherName := "father_uid" + egg_system_rules.IntToStr(*times) + firstSonPosition := SearchPositionFirstSonPosition(*position, int64(severalTimes)) + var tempLevel float64 + makeSearchLevel(&firstSonPosition, severalTimes, &tempLevel) + firstSonLevel := int64(tempLevel) + endSonPosition := firstSonPosition + int64(math.Pow(severalTimes, float64(firstSonLevel-fatherLevel))) - 1 + publicPlatoonUserRelationDb := implement.NewPublicPlatoonUserRelationDb(engine) + list, _, err := publicPlatoonUserRelationDb.PublicPlatoonUserRelationFindCountByPosition(fuid, fatherName, positionName, int64(firstSonPosition), int64(endSonPosition)) + if err != nil { + return standardPublicPlatoonRelation, fatherUid, err + } + + if list[0].HasSonNum == int(severalTimes) { + //TODO::则证明该层直属下级无空位 + position = &firstSonPosition + return FindSuitablePosition(engine, position, severalTimes, fuid, fatherLevel, times) + } else if list[0].HasSonNum == 0 { + fatherUid = list[0].Uid + + //TODO::新的一层 + standardPublicPlatoonRelation.FatherUid1 = list[0].Uid + standardPublicPlatoonRelation.FatherUid2 = list[0].FatherUid1 + standardPublicPlatoonRelation.FatherUid3 = list[0].FatherUid2 + standardPublicPlatoonRelation.FatherUid4 = list[0].FatherUid3 + standardPublicPlatoonRelation.FatherUid5 = list[0].FatherUid4 + standardPublicPlatoonRelation.FatherUid6 = list[0].FatherUid5 + standardPublicPlatoonRelation.FatherUid7 = list[0].FatherUid6 + standardPublicPlatoonRelation.FatherUid8 = list[0].FatherUid7 + standardPublicPlatoonRelation.FatherUid9 = list[0].FatherUid8 + + standardPublicPlatoonRelation.Pid1 = list[0].Position + standardPublicPlatoonRelation.Pid2 = list[0].Position1 + standardPublicPlatoonRelation.Pid3 = list[0].Position2 + standardPublicPlatoonRelation.Pid4 = list[0].Position3 + standardPublicPlatoonRelation.Pid5 = list[0].Position4 + standardPublicPlatoonRelation.Pid6 = list[0].Position5 + standardPublicPlatoonRelation.Pid7 = list[0].Position6 + standardPublicPlatoonRelation.Pid8 = list[0].Position7 + standardPublicPlatoonRelation.Pid9 = list[0].Position8 + + standardPublicPlatoonRelation.Position1 = SearchPositionFirstSonPosition(list[0].Position, int64(severalTimes)) + standardPublicPlatoonRelation.Position2 = ReverseDeductionPositionByFather(list[0].Position1, int64(list[0].Level1), int64(severalTimes), 0) + standardPublicPlatoonRelation.Position3 = ReverseDeductionPositionByFather(list[0].Position2, int64(list[0].Level2), int64(severalTimes), 0) + standardPublicPlatoonRelation.Position4 = ReverseDeductionPositionByFather(list[0].Position3, int64(list[0].Level3), int64(severalTimes), 0) + standardPublicPlatoonRelation.Position5 = ReverseDeductionPositionByFather(list[0].Position4, int64(list[0].Level4), int64(severalTimes), 0) + standardPublicPlatoonRelation.Position6 = ReverseDeductionPositionByFather(list[0].Position5, int64(list[0].Level5), int64(severalTimes), 0) + standardPublicPlatoonRelation.Position7 = ReverseDeductionPositionByFather(list[0].Position6, int64(list[0].Level6), int64(severalTimes), 0) + standardPublicPlatoonRelation.Position8 = ReverseDeductionPositionByFather(list[0].Position7, int64(list[0].Level7), int64(severalTimes), 0) + standardPublicPlatoonRelation.Position9 = ReverseDeductionPositionByFather(list[0].Position8, int64(list[0].Level8), int64(severalTimes), 0) + + if list[0].Level1 == 0 { + list[0].Level1-- + } + if list[0].Level2 == 0 { + list[0].Level2-- + } + if list[0].Level3 == 0 { + list[0].Level3-- + } + if list[0].Level4 == 0 { + list[0].Level4-- + } + if list[0].Level5 == 0 { + list[0].Level5-- + } + if list[0].Level6 == 0 { + list[0].Level6-- + } + if list[0].Level7 == 0 { + list[0].Level7-- + } + if list[0].Level8 == 0 { + list[0].Level8-- + } + if list[0].Level9 == 0 { + list[0].Level9-- + } + standardPublicPlatoonRelation.Level1 = list[0].Level + 1 + standardPublicPlatoonRelation.Level2 = list[0].Level1 + 1 + standardPublicPlatoonRelation.Level3 = list[0].Level2 + 1 + standardPublicPlatoonRelation.Level4 = list[0].Level3 + 1 + standardPublicPlatoonRelation.Level5 = list[0].Level4 + 1 + standardPublicPlatoonRelation.Level6 = list[0].Level5 + 1 + standardPublicPlatoonRelation.Level7 = list[0].Level6 + 1 + standardPublicPlatoonRelation.Level8 = list[0].Level7 + 1 + standardPublicPlatoonRelation.Level9 = list[0].Level8 + 1 + standardPublicPlatoonRelation.LevelTotal = list[0].LevelTotal + 1 + } else { + fatherUid = list[0].Uid + + _, _, _, _, getPositionName, _, _, _ := GetPosition(list[0], *times+1) + //PublicPlatoonUserRelations, err := db.PublicPlatoonUserRelationFindByPid(engine, m.Position, m.Uid, "pid1", "father_uid1", "position1") + + PublicPlatoonUserRelations, err := publicPlatoonUserRelationDb.PublicPlatoonUserRelationFindByPid(list[0].Uid, "father_uid1", getPositionName) + + if err != nil { + return standardPublicPlatoonRelation, fatherUid, err + } + + if PublicPlatoonUserRelations[0].Position2 == 0 { + PublicPlatoonUserRelations[0].Position2 = -1 + } + if PublicPlatoonUserRelations[0].Position3 == 0 { + standardPublicPlatoonRelation.Position3 = -1 + } + if PublicPlatoonUserRelations[0].Position4 == 0 { + standardPublicPlatoonRelation.Position4 = -1 + } + if PublicPlatoonUserRelations[0].Position5 == 0 { + standardPublicPlatoonRelation.Position5 = -1 + } + if PublicPlatoonUserRelations[0].Position6 == 0 { + standardPublicPlatoonRelation.Position6 = -1 + } + if PublicPlatoonUserRelations[0].Position7 == 0 { + standardPublicPlatoonRelation.Position7 = -1 + } + if PublicPlatoonUserRelations[0].Position8 == 0 { + standardPublicPlatoonRelation.Position8 = -1 + } + if PublicPlatoonUserRelations[0].Position9 == 0 { + standardPublicPlatoonRelation.Position9 = -1 + } + + standardPublicPlatoonRelation = md.StandardPublicPlatoonRelation{ + FatherUid1: PublicPlatoonUserRelations[0].FatherUid1, + FatherUid2: PublicPlatoonUserRelations[0].FatherUid2, + FatherUid3: PublicPlatoonUserRelations[0].FatherUid3, + FatherUid4: PublicPlatoonUserRelations[0].FatherUid4, + FatherUid5: PublicPlatoonUserRelations[0].FatherUid5, + FatherUid6: PublicPlatoonUserRelations[0].FatherUid6, + FatherUid7: PublicPlatoonUserRelations[0].FatherUid7, + FatherUid8: PublicPlatoonUserRelations[0].FatherUid8, + FatherUid9: PublicPlatoonUserRelations[0].FatherUid9, + Pid1: PublicPlatoonUserRelations[0].Pid1, + Pid2: PublicPlatoonUserRelations[0].Pid2, + Pid3: PublicPlatoonUserRelations[0].Pid3, + Pid4: PublicPlatoonUserRelations[0].Pid4, + Pid5: PublicPlatoonUserRelations[0].Pid5, + Pid6: PublicPlatoonUserRelations[0].Pid6, + Pid7: PublicPlatoonUserRelations[0].Pid7, + Pid8: PublicPlatoonUserRelations[0].Pid8, + Pid9: PublicPlatoonUserRelations[0].Pid9, + Position1: PublicPlatoonUserRelations[0].Position1 + 1, + Position2: PublicPlatoonUserRelations[0].Position2 + 1, + Position3: PublicPlatoonUserRelations[0].Position3 + 1, + Position4: PublicPlatoonUserRelations[0].Position4 + 1, + Position5: PublicPlatoonUserRelations[0].Position5 + 1, + Position6: PublicPlatoonUserRelations[0].Position6 + 1, + Position7: PublicPlatoonUserRelations[0].Position7 + 1, + Position8: PublicPlatoonUserRelations[0].Position8 + 1, + Position9: PublicPlatoonUserRelations[0].Position9 + 1, + Level1: PublicPlatoonUserRelations[0].Level1, + Level2: PublicPlatoonUserRelations[0].Level2, + Level3: PublicPlatoonUserRelations[0].Level3, + Level4: PublicPlatoonUserRelations[0].Level4, + Level5: PublicPlatoonUserRelations[0].Level5, + Level6: PublicPlatoonUserRelations[0].Level6, + Level7: PublicPlatoonUserRelations[0].Level7, + Level8: PublicPlatoonUserRelations[0].Level8, + Level9: PublicPlatoonUserRelations[0].Level9, + LevelTotal: PublicPlatoonUserRelations[0].LevelTotal, + } + return standardPublicPlatoonRelation, fatherUid, nil + } + return +} + +func GetPosition(m model.PublicPlatoonUserRelation, times int) (position, pid int64, level int, fatherUid int64, positionName, pidName, levelName, fatherUidName string) { + if times == 1 { + position = m.Position1 + positionName = "position1" + pid = m.Pid1 + pidName = "pid1" + level = m.Level1 + levelName = "level1" + fatherUid = m.FatherUid1 + fatherUidName = "father_uid1" + } + if times == 2 { + position = m.Position2 + positionName = "position2" + pid = m.Pid2 + pidName = "pid2" + level = m.Level2 + levelName = "level2" + fatherUid = m.FatherUid2 + fatherUidName = "father_uid2" + } + if times == 3 { + position = m.Position3 + positionName = "position3" + pid = m.Pid3 + pidName = "pid3" + level = m.Level3 + levelName = "level3" + fatherUid = m.FatherUid3 + fatherUidName = "father_uid3" + } + if times == 4 { + position = m.Position4 + positionName = "position4" + pid = m.Pid4 + pidName = "pid4" + level = m.Level4 + levelName = "level4" + fatherUid = m.FatherUid4 + fatherUidName = "father_uid4" + } + if times == 5 { + position = m.Position5 + positionName = "position5" + pid = m.Pid5 + pidName = "pid5" + level = m.Level5 + levelName = "level5" + fatherUid = m.FatherUid5 + fatherUidName = "father_uid5" + } + if times == 6 { + position = m.Position6 + positionName = "position6" + pid = m.Pid6 + pidName = "pid6" + level = m.Level6 + levelName = "level6" + fatherUid = m.FatherUid6 + fatherUidName = "father_uid6" + } + if times == 7 { + position = m.Position7 + positionName = "position7" + pid = m.Pid7 + pidName = "pid7" + level = m.Level7 + levelName = "level7" + fatherUid = m.FatherUid7 + fatherUidName = "father_uid7" + } + if times == 8 { + position = m.Position8 + positionName = "position8" + pid = m.Pid8 + pidName = "pid8" + level = m.Level8 + levelName = "level8" + fatherUid = m.FatherUid8 + fatherUidName = "father_uid8" + } + if times == 9 { + position = m.Position9 + positionName = "position9" + pid = m.Pid9 + pidName = "pid9" + level = m.Level9 + levelName = "level9" + fatherUid = m.FatherUid9 + fatherUidName = "father_uid9" + } + + return +} + +// GetLevelForFirstPosition 返回当前等级的起始值 +func GetLevelForFirstPosition(level, severalTimes int64) (position int64) { + position = position + 1 + for n := int64(0); n <= (level - 2); n++ { + position += int64(math.Pow(float64(severalTimes), float64(n))) + } + return +} + +// 查找归属父级id +func MakeSearchPid(position int, row int) (pid int) { + divisionValue := (position - 1) / row + if divisionValue == 0 { + pid = 1 + return + } else { + if (divisionValue*row + 1) == position { + pid = divisionValue + return + } else { + pid = divisionValue + 1 + return + } + } +} + +// 查找当前位置下的第一个子集位置 +func SearchPositionFirstSonPosition(position int64, times int64) (sonPosition int64) { + sonPosition = (position * times) - (times - 2) + return +} + +// 查找当前位位置相当于父级的等级 +func SearchPositionSonForLevel(fatherUid string, fatherPosition int64) (level int) { + fatherUids := strings.Split(fatherUid, "-") + for key, item := range fatherUids { + if item == egg_system_rules.Int64ToStr(fatherPosition) { + level = key + 1 + break + } + } + return +} + +// FindWaitForDealUsers 查询待处理的用户 +func FindWaitForDealUsers(engine *xorm.Engine, page, pageSize int) (err error, resp []int64) { + now := time.Now().Format("2006-01-02") + publicPlatoonUserRelationDb := implement.NewPublicPlatoonUserRelationDb(engine) + lists, err := publicPlatoonUserRelationDb.PublicPlatoonUserRelationFindByParamsByPage(map[string]interface{}{ + "key": "wait_for_settlement_date", + "value": now, + }, page, pageSize) + if err != nil { + return + } + for _, list := range *lists { + resp = append(resp, list.Uid) + } + return +} + +// 递归查找等级 +func makeSearchLevel(position *int64, rows float64, times *float64) (level int) { + difference := *position - int64(math.Pow(rows, *times)) + *times++ + if difference <= 0 { + return int(*times) + } else { + position = &difference + return makeSearchLevel(position, rows, times) + } +} + +// DealCommonWealthPunish 处理公排处罚 +func DealCommonWealthPunish(engine *xorm.Engine, uid int64, reason string) (err error, resp []map[string]string) { + session := engine.NewSession() + defer func() { + session.Close() + if err := recover(); err != nil { + _ = zhios_order_relate_logx.Error(err) + } + }() + session.Begin() + + //1、查找 `user_public_platoon_setting` 基础设置 + publicPlatoonBasicSettingDb := implement.NewPublicPlatoonBasicSettingDb(engine) + PublicPlatoonBasicSetting, err := publicPlatoonBasicSettingDb.PublicPlatoonBasicSettingGetOneByParams(map[string]interface{}{ + "key": "is_open", + "value": 1, + }) + if err != nil { + _ = session.Rollback() + return + } + + //2、查询出 `public_platoon_user_relation` 中相关记录 && 将该记录的uid置为 -1 + publicPlatoonUserRelationDb := implement.NewPublicPlatoonUserRelationDb(engine) + params, err := publicPlatoonUserRelationDb.PublicPlatoonUserRelationGetOneByParams(map[string]interface{}{ + "key": "uid", + "value": uid, + }) + if err != nil { + _ = session.Rollback() + return + } + if params == nil { + fmt.Println("未查询到公排关系记录!!!!!!!", uid) + return + } + + //TODO::判断是否为推荐用户 + if params.RecommendUid == 0 { + params.Uid = params.Uid - int64(time.Now().Unix()) + } else { + params.Uid = params.Uid - int64(time.Now().Unix()) + } + + for n := 1; n <= 9; n++ { + str := "father_uid" + strconv.Itoa(n) + sql := fmt.Sprintf("UPDATE `public_platoon_user_relation` SET %s=%s where %s=%s", str, egg_system_rules.Int64ToStr(params.Uid), str, egg_system_rules.Int64ToStr(uid)) + fmt.Println(">>>>>>>sql>>>>>>>", sql) + _, err = session.Exec(sql) + if err != nil { + _ = session.Rollback() + return + } + } + + updateAffected, err := publicPlatoonUserRelationDb.PublicPlatoonUserRelationUpdate(session, params.Id, params) + if err != nil { + _ = session.Rollback() + return + } + if updateAffected == 0 { + err = errors.New("更新 public_platoon_user_relation 记录失败") + _ = session.Rollback() + return + } + + //3、新增一条 `public_platoon_user_relation` 记录 + params.Uid = uid + res, err := PublicPlatoon(engine, params.Uid, params.RecommendUid, *PublicPlatoonBasicSetting) + if err != nil { + _ = session.Rollback() + return + } + + //4、新增一条 `public_platoon_user_system_punish_records` 记录 + now := time.Now() + publicPlatoonUserSystemPunishRecordsDb := implement.NewPublicPlatoonUserSystemPunishRecordsDb(engine) + insertAffected, err := publicPlatoonUserSystemPunishRecordsDb.PublicPlatoonUserSystemPunishRecordsInsert(session, &model.PublicPlatoonUserSystemPunishRecords{ + Uid: params.Uid, + OldPosition: params.Position, + NewPosition: res.Position, + Date: now.AddDate(0, 0, 30).Format("2006-01-02"), + Title: "系统处罚记录", + Reason: reason, + Type: 2, + CreateAt: now.Format("2006-01-02 15:04:05"), + UpdateAt: now.Format("2006-01-02 15:04:05"), + }) + if err != nil { + _ = session.Rollback() + return + } + if insertAffected == 0 { + err = errors.New("新增 public_platoon_user_system_punish_records 记录失败") + _ = session.Rollback() + return + } + + err = session.Commit() + return +} + +const DealUserPublicPlatoonPunishLockKey = "deal_user_public_platoon_lock_key" + +// DealUserPublicPlatoonPunish 处理公排处罚 +func DealUserPublicPlatoonPunish(engine *xorm.Engine) (err error) { + now := time.Now() + fmt.Println(now.Hour()) + if !(now.Hour() > 22 && now.Hour() < 24) { + //TODO::只在凌晨一点 ~ 凌晨 8 点运行 + return errors.New("非运行时间") + } + //TODO::增加“悲观锁”防止串行 + getString, _ := cache.GetString(DealUserPublicPlatoonPunishLockKey) + if getString != "" { + fmt.Println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", "上一次结算未执行完") + return errors.New("上一次结算未执行完") + } + cache.SetEx(DealUserPublicPlatoonPunishLockKey, "running", 3600*8) //8小时 + + //查找 `public_platoon_basic_setting` 基础设置 + publicPlatoonBasicSettingDb := implement.NewPublicPlatoonBasicSettingDb(engine) + PublicPlatoonBasicSetting, err := publicPlatoonBasicSettingDb.PublicPlatoonBasicSettingGetOneByParams(map[string]interface{}{ + "key": "is_open", + "value": 1, + }) + if err != nil { + return err + } + if PublicPlatoonBasicSetting == nil { + return errors.New("公排制度未开启") + } + publicPlatoonFreePunishWithUserDb := implement.NewPublicPlatoonFreePunishWithUserDb(engine) + mapPublicPlatoonFreePunishWithUser, err := publicPlatoonFreePunishWithUserDb.FindAllPublicPlatoonFreePunishWithUser() + if err != nil { + return err + } + if PublicPlatoonBasicSetting.SystemPunishReplace == 1 { + systemPunishReplaceValue := PublicPlatoonBasicSetting.SystemPunishReplaceValue + startDate := now.AddDate(0, 0, -systemPunishReplaceValue).Format("2006-01-02") + " 00:00:00" + var page = 1 + var pageSize = 100 + for { + var users []model.User + err = engine.Limit(pageSize, (page-1)*pageSize).Desc("uid").Find(&users) + if err != nil { + return err + } + if len(users) <= 0 { + break + } + for _, user := range users { + var list []model.EggSignIn + err = engine.Where("start_time >= ?", startDate).And("uid = ?", user.Id).Find(&list) + if len(list) <= 0 && mapPublicPlatoonFreePunishWithUser[user.Id] == nil { + var PublicPlatoonRecordsPunishWithUser model.PublicPlatoonRecordsPunishWithUser + has, err1 := engine.Where("uid = ?", user.Id).Get(&PublicPlatoonRecordsPunishWithUser) + if err1 != nil { + return err1 + } + PublicPlatoonRecordsPunishWithUserDate, _ := time.ParseInLocation("2006-01-02", PublicPlatoonRecordsPunishWithUser.Date, time.Local) + if has && now.AddDate(0, 0, -7).After(PublicPlatoonRecordsPunishWithUserDate) { + //TODO::不进行重复处罚 + continue + } + //进行处罚 + err, _ = DealCommonWealthPunish(engine, user.Id, "公排处罚") + if err != nil { + return err + } + + //添加 public_platoon_records_punish_with_user 记录 + publicPlatoonRecordsPunishWithUserDb := implement.NewPublicPlatoonRecordsPunishWithUserDb(engine) + if has { + PublicPlatoonRecordsPunishWithUser.Date = now.Format("2006-01-02") + _, err2 := publicPlatoonRecordsPunishWithUserDb.PublicPlatoonRecordsPunishWithUserUpdate(PublicPlatoonRecordsPunishWithUser.Id, &PublicPlatoonRecordsPunishWithUser, "date") + if err2 != nil { + return err2 + } + } else { + _, err2 := publicPlatoonRecordsPunishWithUserDb.PublicPlatoonRecordsPunishWithUserInsert(&model.PublicPlatoonRecordsPunishWithUser{ + Uid: user.Id, + Date: now.Format("2006-01-02"), + }) + if err2 != nil { + return err2 + } + } + } + } + page++ + } + } + return +} diff --git a/rule/user_virtual_coin.go b/rule/user_virtual_coin.go new file mode 100644 index 0000000..33be61e --- /dev/null +++ b/rule/user_virtual_coin.go @@ -0,0 +1,138 @@ +package rule + +import ( + "code.fnuoos.com/EggPlanet/egg_models.git/src/implement" + "code.fnuoos.com/EggPlanet/egg_models.git/src/model" + zhios_order_relate_utils "code.fnuoos.com/EggPlanet/egg_models.git/utils" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + md2 "code.fnuoos.com/EggPlanet/egg_system_rules.git/rule/egg_energy/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/svc" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/cache" + "errors" + "fmt" + "github.com/shopspring/decimal" + "strconv" + "time" + "xorm.io/xorm" +) + +// DealUserVirtualCoin 处理给用户虚拟币积分 +func DealUserVirtualCoin(session *xorm.Session, req md.DealUserVirtualCoinReq) (err error) { + if req.Amount < 0 { + req.Amount = 0 + } + //1、分布式锁阻拦 + requestIdPrefix := fmt.Sprintf(md2.DealUserCoinRequestIdPrefix, req.CoinId, req.Uid) + cb, err := svc.HandleDistributedLock(zhios_order_relate_utils.Int64ToStr(req.Uid), requestIdPrefix, strconv.Itoa(req.CoinId)) + if err != nil { + return err + } + if cb != nil { + defer cb() // 释放锁 + } + + //2、计算&&组装数据 + now := time.Now() + coinAmount, err := GetUserCoinAmount(session, req.CoinId, req.Uid) + if err != nil { + return err + } + coinAmountValue := decimal.NewFromFloat(zhios_order_relate_utils.StrToFloat64(coinAmount)) + amountValue := decimal.NewFromFloat(req.Amount).RoundFloor(4) + + var userVirtualCoinFlow model.UserVirtualCoinFlow + userVirtualCoinFlow.CoinId = req.CoinId + userVirtualCoinFlow.Title = req.Title + userVirtualCoinFlow.TransferType = req.TransferType + userVirtualCoinFlow.Uid = req.Uid + userVirtualCoinFlow.BeforeAmount = coinAmount + userVirtualCoinFlow.Amount = amountValue.String() + userVirtualCoinFlow.CreateAt = now.Format("2006-01-02 15:04:05") + + if req.Kind == "add" { + userVirtualCoinFlow.Direction = 1 + userVirtualCoinFlow.AfterAmount = coinAmountValue.Add(amountValue).RoundFloor(8).String() + } else if req.Kind == "sub" { + userVirtualCoinFlow.Direction = 2 + userVirtualCoinFlow.AfterAmount = coinAmountValue.Sub(amountValue).RoundFloor(8).String() + if zhios_order_relate_utils.StrToFloat64(userVirtualCoinFlow.AfterAmount) < 0 { + var coin model.VirtualCoin + _, err = session.Where("id = ?", req.CoinId).Get(&coin) + if err != nil { + return err + } + zhios_order_relate_utils.FilePutContents("virtual_coin_not", zhios_order_relate_utils.SerializeStr(map[string]interface{}{ + "uid": userVirtualCoinFlow.Uid, + "amount": userVirtualCoinFlow.Amount, + "before_amount": userVirtualCoinFlow.BeforeAmount, + "after_amount": userVirtualCoinFlow.AfterAmount, + "coin_id": userVirtualCoinFlow.CoinId, + })) + return errors.New("用户" + zhios_order_relate_utils.Int64ToStr(userVirtualCoinFlow.Uid) + "的" + coin.Name + "不足") + } + } else { + err = errors.New("错误的kind类型") + return err + } + + //3、插入 `user_virtual_coin_flow` 记录 + userVirtualCoinFlowDb := implement.NewUserVirtualCoinFlowDb(session.Engine()) + _, err = userVirtualCoinFlowDb.UserVirtualCoinFlowInsertBySession(session, &userVirtualCoinFlow) + if err != nil { + return err + } + + //4、修改 `user_virtual_amount`的amount值 && 及缓存 + err = SetCacheUserVirtualAmount(session, userVirtualCoinFlow.AfterAmount, req.CoinId, req.Uid, true) + if err != nil { + return err + } + + return nil +} + +// GetUserCoinAmount 获取用户虚拟积分余额 +func GetUserCoinAmount(session *xorm.Session, coinId int, uid int64) (amount string, err error) { + redisKey := fmt.Sprintf(md.UserVirtualAmountRedisKey, coinId, uid) + amount, err = cache.GetString(redisKey) + if err != nil { + if err.Error() == "redigo: nil returned" { + userVirtualAmountDb := implement.NewUserVirtualAmountDb(session.Engine()) + userVirtualAmount, err := userVirtualAmountDb.GetUserVirtualWalletBySession(uid, coinId) + if err != nil { + return amount, err + } + if userVirtualAmount == nil { + amount = "0" + } else { + amount = userVirtualAmount.Amount + } + //将获取到的余额值缓存至redis + _ = SetCacheUserVirtualAmount(session, amount, coinId, uid, false) + return amount, nil + } + return amount, err + } + return amount, nil +} + +// SetCacheUserVirtualAmount 设置缓存的用户虚拟币积分余额 +func SetCacheUserVirtualAmount(session *xorm.Session, amount string, coinId int, uid int64, isUpdateDb bool) error { + redisKey := fmt.Sprintf(md.UserVirtualAmountRedisKey, coinId, uid) + if isUpdateDb { + _, err := session.Where("uid=?", uid).And("coin_id=?", coinId).Update(model.UserVirtualAmount{ + Uid: uid, + CoinId: coinId, + Amount: amount, + }) + if err != nil { + return err + } + } + //TODO::默认缓存1小时 (先调整为 20 min) + _, err := cache.SetEx(redisKey, zhios_order_relate_utils.StrToFloat64(amount), 60*20) + if err != nil { + return err + } + return nil +} diff --git a/svc/svc_user_virtual_amount_redis_mutex_lock.go b/svc/svc_user_virtual_amount_redis_mutex_lock.go new file mode 100644 index 0000000..d70121b --- /dev/null +++ b/svc/svc_user_virtual_amount_redis_mutex_lock.go @@ -0,0 +1,84 @@ +package svc + +import ( + zhios_order_relate_utils "code.fnuoos.com/EggPlanet/egg_models.git/utils" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/md" + "code.fnuoos.com/EggPlanet/egg_system_rules.git/utils/cache" + "errors" + "fmt" + "math/rand" + "reflect" + "time" +) + +const redisMutexLockExpTime = 15 + +// TryGetDistributedLock 分布式锁获取 +// requestId 用于标识请求客户端,可以是随机字符串,需确保唯一 +func TryGetDistributedLock(lockKey, requestId string, isNegative bool) bool { + if isNegative { // 多次尝试获取 + retry := 1 + for { + ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX") + // 获取锁成功 + if err == nil && ok == "OK" { + return true + } + // 尝试多次没获取成功 + if retry > 50 { + return false + } + time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) + retry += 1 + } + } else { // 只尝试一次 + ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX") + // 获取锁成功 + if err == nil && ok == "OK" { + return true + } + + return false + } +} + +// ReleaseDistributedLock 释放锁,通过比较requestId,用于确保客户端只释放自己的锁,使用lua脚本保证操作的原子型 +func ReleaseDistributedLock(lockKey, requestId string) (bool, error) { + luaScript := ` + if redis.call("get",KEYS[1]) == ARGV[1] + then + return redis.call("del",KEYS[1]) + else + return 0 + end` + + do, err := cache.Do("eval", luaScript, 1, lockKey, requestId) + fmt.Println(reflect.TypeOf(do)) + fmt.Println(do) + if zhios_order_relate_utils.AnyToInt64(do) == 1 { + return true, err + } else { + return false, err + } +} + +func GetDistributedLockRequestId(prefix string) string { + return prefix + zhios_order_relate_utils.IntToStr(rand.Intn(100000000)) +} + +// HandleDistributedLock 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁 +func HandleDistributedLock(uid, coinId, requestIdPrefix string) (cb func(), err error) { + // 获取余额更新锁 + balanceLockKey := fmt.Sprintf(md.UserVirtualAmountUpdateLock, uid, coinId) + requestId := GetDistributedLockRequestId(requestIdPrefix) + balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true) + if !balanceLockOk { + return nil, errors.New("系统繁忙,请稍后再试") + } + + cb = func() { + _, _ = ReleaseDistributedLock(balanceLockKey, requestId) + } + + return cb, nil +} diff --git a/utils/cache/base.go b/utils/cache/base.go new file mode 100644 index 0000000..64648dd --- /dev/null +++ b/utils/cache/base.go @@ -0,0 +1,421 @@ +package cache + +import ( + "errors" + "fmt" + "strconv" + "time" +) + +const ( + redisDialTTL = 10 * time.Second + redisReadTTL = 3 * time.Second + redisWriteTTL = 3 * time.Second + redisIdleTTL = 10 * time.Second + redisPoolTTL = 10 * time.Second + redisPoolSize int = 512 + redisMaxIdleConn int = 64 + redisMaxActive int = 512 +) + +var ( + ErrNil = errors.New("nil return") + ErrWrongArgsNum = errors.New("args num error") + ErrNegativeInt = errors.New("redis cluster: unexpected value for Uint64") +) + +// 以下为提供类型转换 + +func Int(reply interface{}, err error) (int, error) { + if err != nil { + return 0, err + } + switch reply := reply.(type) { + case int: + return reply, nil + case int8: + return int(reply), nil + case int16: + return int(reply), nil + case int32: + return int(reply), nil + case int64: + x := int(reply) + if int64(x) != reply { + return 0, strconv.ErrRange + } + return x, nil + case uint: + n := int(reply) + if n < 0 { + return 0, strconv.ErrRange + } + return n, nil + case uint8: + return int(reply), nil + case uint16: + return int(reply), nil + case uint32: + n := int(reply) + if n < 0 { + return 0, strconv.ErrRange + } + return n, nil + case uint64: + n := int(reply) + if n < 0 { + return 0, strconv.ErrRange + } + return n, nil + case []byte: + data := string(reply) + if len(data) == 0 { + return 0, ErrNil + } + + n, err := strconv.ParseInt(data, 10, 0) + return int(n), err + case string: + if len(reply) == 0 { + return 0, ErrNil + } + + n, err := strconv.ParseInt(reply, 10, 0) + return int(n), err + case nil: + return 0, ErrNil + case error: + return 0, reply + } + return 0, fmt.Errorf("redis cluster: unexpected type for Int, got type %T", reply) +} + +func Int64(reply interface{}, err error) (int64, error) { + if err != nil { + return 0, err + } + switch reply := reply.(type) { + case int: + return int64(reply), nil + case int8: + return int64(reply), nil + case int16: + return int64(reply), nil + case int32: + return int64(reply), nil + case int64: + return reply, nil + case uint: + n := int64(reply) + if n < 0 { + return 0, strconv.ErrRange + } + return n, nil + case uint8: + return int64(reply), nil + case uint16: + return int64(reply), nil + case uint32: + return int64(reply), nil + case uint64: + n := int64(reply) + if n < 0 { + return 0, strconv.ErrRange + } + return n, nil + case []byte: + data := string(reply) + if len(data) == 0 { + return 0, ErrNil + } + + n, err := strconv.ParseInt(data, 10, 64) + return n, err + case string: + if len(reply) == 0 { + return 0, ErrNil + } + + n, err := strconv.ParseInt(reply, 10, 64) + return n, err + case nil: + return 0, ErrNil + case error: + return 0, reply + } + return 0, fmt.Errorf("redis cluster: unexpected type for Int64, got type %T", reply) +} + +func Uint64(reply interface{}, err error) (uint64, error) { + if err != nil { + return 0, err + } + switch reply := reply.(type) { + case uint: + return uint64(reply), nil + case uint8: + return uint64(reply), nil + case uint16: + return uint64(reply), nil + case uint32: + return uint64(reply), nil + case uint64: + return reply, nil + case int: + if reply < 0 { + return 0, ErrNegativeInt + } + return uint64(reply), nil + case int8: + if reply < 0 { + return 0, ErrNegativeInt + } + return uint64(reply), nil + case int16: + if reply < 0 { + return 0, ErrNegativeInt + } + return uint64(reply), nil + case int32: + if reply < 0 { + return 0, ErrNegativeInt + } + return uint64(reply), nil + case int64: + if reply < 0 { + return 0, ErrNegativeInt + } + return uint64(reply), nil + case []byte: + data := string(reply) + if len(data) == 0 { + return 0, ErrNil + } + + n, err := strconv.ParseUint(data, 10, 64) + return n, err + case string: + if len(reply) == 0 { + return 0, ErrNil + } + + n, err := strconv.ParseUint(reply, 10, 64) + return n, err + case nil: + return 0, ErrNil + case error: + return 0, reply + } + return 0, fmt.Errorf("redis cluster: unexpected type for Uint64, got type %T", reply) +} + +func Float64(reply interface{}, err error) (float64, error) { + if err != nil { + return 0, err + } + + var value float64 + err = nil + switch v := reply.(type) { + case float32: + value = float64(v) + case float64: + value = v + case int: + value = float64(v) + case int8: + value = float64(v) + case int16: + value = float64(v) + case int32: + value = float64(v) + case int64: + value = float64(v) + case uint: + value = float64(v) + case uint8: + value = float64(v) + case uint16: + value = float64(v) + case uint32: + value = float64(v) + case uint64: + value = float64(v) + case []byte: + data := string(v) + if len(data) == 0 { + return 0, ErrNil + } + value, err = strconv.ParseFloat(string(v), 64) + case string: + if len(v) == 0 { + return 0, ErrNil + } + value, err = strconv.ParseFloat(v, 64) + case nil: + err = ErrNil + case error: + err = v + default: + err = fmt.Errorf("redis cluster: unexpected type for Float64, got type %T", v) + } + + return value, err +} + +func Bool(reply interface{}, err error) (bool, error) { + if err != nil { + return false, err + } + switch reply := reply.(type) { + case bool: + return reply, nil + case int64: + return reply != 0, nil + case []byte: + data := string(reply) + if len(data) == 0 { + return false, ErrNil + } + + return strconv.ParseBool(data) + case string: + if len(reply) == 0 { + return false, ErrNil + } + + return strconv.ParseBool(reply) + case nil: + return false, ErrNil + case error: + return false, reply + } + return false, fmt.Errorf("redis cluster: unexpected type for Bool, got type %T", reply) +} + +func Bytes(reply interface{}, err error) ([]byte, error) { + if err != nil { + return nil, err + } + switch reply := reply.(type) { + case []byte: + if len(reply) == 0 { + return nil, ErrNil + } + return reply, nil + case string: + data := []byte(reply) + if len(data) == 0 { + return nil, ErrNil + } + return data, nil + case nil: + return nil, ErrNil + case error: + return nil, reply + } + return nil, fmt.Errorf("redis cluster: unexpected type for Bytes, got type %T", reply) +} + +func String(reply interface{}, err error) (string, error) { + if err != nil { + return "", err + } + + value := "" + err = nil + switch v := reply.(type) { + case string: + if len(v) == 0 { + return "", ErrNil + } + + value = v + case []byte: + if len(v) == 0 { + return "", ErrNil + } + + value = string(v) + case int: + value = strconv.FormatInt(int64(v), 10) + case int8: + value = strconv.FormatInt(int64(v), 10) + case int16: + value = strconv.FormatInt(int64(v), 10) + case int32: + value = strconv.FormatInt(int64(v), 10) + case int64: + value = strconv.FormatInt(v, 10) + case uint: + value = strconv.FormatUint(uint64(v), 10) + case uint8: + value = strconv.FormatUint(uint64(v), 10) + case uint16: + value = strconv.FormatUint(uint64(v), 10) + case uint32: + value = strconv.FormatUint(uint64(v), 10) + case uint64: + value = strconv.FormatUint(v, 10) + case float32: + value = strconv.FormatFloat(float64(v), 'f', -1, 32) + case float64: + value = strconv.FormatFloat(v, 'f', -1, 64) + case bool: + value = strconv.FormatBool(v) + case nil: + err = ErrNil + case error: + err = v + default: + err = fmt.Errorf("redis cluster: unexpected type for String, got type %T", v) + } + + return value, err +} + +func Strings(reply interface{}, err error) ([]string, error) { + if err != nil { + return nil, err + } + switch reply := reply.(type) { + case []interface{}: + result := make([]string, len(reply)) + for i := range reply { + if reply[i] == nil { + continue + } + switch subReply := reply[i].(type) { + case string: + result[i] = subReply + case []byte: + result[i] = string(subReply) + default: + return nil, fmt.Errorf("redis cluster: unexpected element type for String, got type %T", reply[i]) + } + } + return result, nil + case []string: + return reply, nil + case nil: + return nil, ErrNil + case error: + return nil, reply + } + return nil, fmt.Errorf("redis cluster: unexpected type for Strings, got type %T", reply) +} + +func Values(reply interface{}, err error) ([]interface{}, error) { + if err != nil { + return nil, err + } + switch reply := reply.(type) { + case []interface{}: + return reply, nil + case nil: + return nil, ErrNil + case error: + return nil, reply + } + return nil, fmt.Errorf("redis cluster: unexpected type for Values, got type %T", reply) +} diff --git a/utils/cache/redis.go b/utils/cache/redis.go new file mode 100644 index 0000000..55d60a5 --- /dev/null +++ b/utils/cache/redis.go @@ -0,0 +1,413 @@ +package cache + +import ( + "encoding/json" + "errors" + "log" + "strings" + "time" + + redigo "github.com/gomodule/redigo/redis" +) + +// configuration +type Config struct { + Server string + Password string + MaxIdle int // Maximum number of idle connections in the pool. + + // Maximum number of connections allocated by the pool at a given time. + // When zero, there is no limit on the number of connections in the pool. + MaxActive int + + // Close connections after remaining idle for this duration. If the value + // is zero, then idle connections are not closed. Applications should set + // the timeout to a value less than the server's timeout. + IdleTimeout time.Duration + + // If Wait is true and the pool is at the MaxActive limit, then Get() waits + // for a connection to be returned to the pool before returning. + Wait bool + KeyPrefix string // prefix to all keys; example is "dev environment name" + KeyDelimiter string // delimiter to be used while appending keys; example is ":" + KeyPlaceholder string // placeholder to be parsed using given arguments to obtain a final key; example is "?" +} + +var pool *redigo.Pool +var conf *Config + +func NewRedis(addr string) { + if addr == "" { + panic("\nredis connect string cannot be empty\n") + } + pool = &redigo.Pool{ + MaxIdle: redisMaxIdleConn, + IdleTimeout: redisIdleTTL, + MaxActive: redisMaxActive, + // MaxConnLifetime: redisDialTTL, + Wait: true, + Dial: func() (redigo.Conn, error) { + c, err := redigo.Dial("tcp", addr, + redigo.DialConnectTimeout(redisDialTTL), + redigo.DialReadTimeout(redisReadTTL), + redigo.DialWriteTimeout(redisWriteTTL), + //redigo.DialDatabase(RedisDataBase), + ) + if err != nil { + log.Println("Redis Dial failed: ", err) + return nil, err + } + return c, err + }, + TestOnBorrow: func(c redigo.Conn, t time.Time) error { + _, err := c.Do("PING") + if err != nil { + log.Println("Unable to ping to redis server:", err) + } + return err + }, + } + conn := pool.Get() + defer conn.Close() + if conn.Err() != nil { + println("\nredis connect " + addr + " error: " + conn.Err().Error()) + } else { + println("\nredis connect " + addr + " success!\n") + } +} + +func Do(cmd string, args ...interface{}) (reply interface{}, err error) { + conn := pool.Get() + defer conn.Close() + return conn.Do(cmd, args...) +} + +func SelectDb(db int) (interface{}, error) { + return Do("SELECT", db) +} + +func GetPool() *redigo.Pool { + return pool +} + +func ParseKey(key string, vars []string) (string, error) { + arr := strings.Split(key, conf.KeyPlaceholder) + actualKey := "" + if len(arr) != len(vars)+1 { + return "", errors.New("redis/connection.go: Insufficient arguments to parse key") + } else { + for index, val := range arr { + if index == 0 { + actualKey = arr[index] + } else { + actualKey += vars[index-1] + val + } + } + } + return getPrefixedKey(actualKey), nil +} + +func getPrefixedKey(key string) string { + return conf.KeyPrefix + conf.KeyDelimiter + key +} +func StripEnvKey(key string) string { + return strings.TrimLeft(key, conf.KeyPrefix+conf.KeyDelimiter) +} +func SplitKey(key string) []string { + return strings.Split(key, conf.KeyDelimiter) +} +func Expire(key string, ttl int) (interface{}, error) { + return Do("EXPIRE", key, ttl) +} +func Persist(key string) (interface{}, error) { + return Do("PERSIST", key) +} + +func Del(key string) (interface{}, error) { + return Do("DEL", key) +} +func Set(key string, data interface{}) (interface{}, error) { + // set + return Do("SET", key, data) +} +func SetNX(key string, data interface{}) (interface{}, error) { + return Do("SETNX", key, data) +} +func SetEx(key string, data interface{}, ttl int) (interface{}, error) { + return Do("SETEX", key, ttl, data) +} + +func SetJson(key string, data interface{}, ttl int) bool { + c, err := json.Marshal(data) + if err != nil { + return false + } + if ttl < 1 { + _, err = Set(key, c) + } else { + _, err = SetEx(key, c, ttl) + } + if err != nil { + return false + } + return true +} + +func GetJson(key string, dst interface{}) error { + b, err := GetBytes(key) + if err != nil { + return err + } + if err = json.Unmarshal(b, dst); err != nil { + return err + } + return nil +} + +func Get(key string) (interface{}, error) { + // get + return Do("GET", key) +} +func GetTTL(key string) (time.Duration, error) { + ttl, err := redigo.Int64(Do("TTL", key)) + return time.Duration(ttl) * time.Second, err +} +func GetBytes(key string) ([]byte, error) { + return redigo.Bytes(Do("GET", key)) +} +func GetString(key string) (string, error) { + return redigo.String(Do("GET", key)) +} +func GetStringMap(key string) (map[string]string, error) { + return redigo.StringMap(Do("GET", key)) +} +func GetInt(key string) (int, error) { + return redigo.Int(Do("GET", key)) +} +func GetInt64(key string) (int64, error) { + return redigo.Int64(Do("GET", key)) +} +func GetStringLength(key string) (int, error) { + return redigo.Int(Do("STRLEN", key)) +} +func ZAdd(key string, score float64, data interface{}) (interface{}, error) { + return Do("ZADD", key, score, data) +} +func ZAddNX(key string, score float64, data interface{}) (interface{}, error) { + return Do("ZADD", key, "NX", score, data) +} +func ZRem(key string, data interface{}) (interface{}, error) { + return Do("ZREM", key, data) +} +func ZRange(key string, start int, end int, withScores bool) ([]interface{}, error) { + if withScores { + return redigo.Values(Do("ZRANGE", key, start, end, "WITHSCORES")) + } + return redigo.Values(Do("ZRANGE", key, start, end)) +} +func ZRemRangeByScore(key string, start int64, end int64) ([]interface{}, error) { + return redigo.Values(Do("ZREMRANGEBYSCORE", key, start, end)) +} +func ZCard(setName string) (int64, error) { + return redigo.Int64(Do("ZCARD", setName)) +} +func ZScan(setName string) (int64, error) { + return redigo.Int64(Do("ZCARD", setName)) +} +func SAdd(setName string, data interface{}) (interface{}, error) { + return Do("SADD", setName, data) +} +func SCard(setName string) (int64, error) { + return redigo.Int64(Do("SCARD", setName)) +} +func SIsMember(setName string, data interface{}) (bool, error) { + return redigo.Bool(Do("SISMEMBER", setName, data)) +} +func SMembers(setName string) ([]string, error) { + return redigo.Strings(Do("SMEMBERS", setName)) +} +func SRem(setName string, data interface{}) (interface{}, error) { + return Do("SREM", setName, data) +} +func HSet(key string, HKey string, data interface{}) (interface{}, error) { + return Do("HSET", key, HKey, data) +} + +func HGet(key string, HKey string) (interface{}, error) { + return Do("HGET", key, HKey) +} + +func HMGet(key string, hashKeys ...string) ([]interface{}, error) { + ret, err := Do("HMGET", key, hashKeys) + if err != nil { + return nil, err + } + reta, ok := ret.([]interface{}) + if !ok { + return nil, errors.New("result not an array") + } + return reta, nil +} + +func HMSet(key string, hashKeys []string, vals []interface{}) (interface{}, error) { + if len(hashKeys) == 0 || len(hashKeys) != len(vals) { + var ret interface{} + return ret, errors.New("bad length") + } + input := []interface{}{key} + for i, v := range hashKeys { + input = append(input, v, vals[i]) + } + return Do("HMSET", input...) +} + +func HGetString(key string, HKey string) (string, error) { + return redigo.String(Do("HGET", key, HKey)) +} +func HGetFloat(key string, HKey string) (float64, error) { + f, err := redigo.Float64(Do("HGET", key, HKey)) + return f, err +} +func HGetInt(key string, HKey string) (int, error) { + return redigo.Int(Do("HGET", key, HKey)) +} +func HGetInt64(key string, HKey string) (int64, error) { + return redigo.Int64(Do("HGET", key, HKey)) +} +func HGetBool(key string, HKey string) (bool, error) { + return redigo.Bool(Do("HGET", key, HKey)) +} +func HDel(key string, HKey string) (interface{}, error) { + return Do("HDEL", key, HKey) +} + +func HGetAll(key string) (map[string]interface{}, error) { + vals, err := redigo.Values(Do("HGETALL", key)) + if err != nil { + return nil, err + } + num := len(vals) / 2 + result := make(map[string]interface{}, num) + for i := 0; i < num; i++ { + key, _ := redigo.String(vals[2*i], nil) + result[key] = vals[2*i+1] + } + return result, nil +} + +func FlushAll() bool { + res, _ := redigo.String(Do("FLUSHALL")) + if res == "" { + return false + } + return true +} + +// NOTE: Use this in production environment with extreme care. +// Read more here:https://redigo.io/commands/keys +func Keys(pattern string) ([]string, error) { + return redigo.Strings(Do("KEYS", pattern)) +} + +func HKeys(key string) ([]string, error) { + return redigo.Strings(Do("HKEYS", key)) +} + +func Exists(key string) bool { + count, err := redigo.Int(Do("EXISTS", key)) + if count == 0 || err != nil { + return false + } + return true +} + +func Incr(key string) (int64, error) { + return redigo.Int64(Do("INCR", key)) +} + +func Decr(key string) (int64, error) { + return redigo.Int64(Do("DECR", key)) +} + +func IncrBy(key string, incBy int64) (int64, error) { + return redigo.Int64(Do("INCRBY", key, incBy)) +} + +func DecrBy(key string, decrBy int64) (int64, error) { + return redigo.Int64(Do("DECRBY", key, decrBy)) +} + +func IncrByFloat(key string, incBy float64) (float64, error) { + return redigo.Float64(Do("INCRBYFLOAT", key, incBy)) +} + +func DecrByFloat(key string, decrBy float64) (float64, error) { + return redigo.Float64(Do("DECRBYFLOAT", key, decrBy)) +} + +// use for message queue +func LPush(key string, data interface{}) (interface{}, error) { + // set + return Do("LPUSH", key, data) +} + +func LPop(key string) (interface{}, error) { + return Do("LPOP", key) +} + +func LPopString(key string) (string, error) { + return redigo.String(Do("LPOP", key)) +} +func LPopFloat(key string) (float64, error) { + f, err := redigo.Float64(Do("LPOP", key)) + return f, err +} +func LPopInt(key string) (int, error) { + return redigo.Int(Do("LPOP", key)) +} +func LPopInt64(key string) (int64, error) { + return redigo.Int64(Do("LPOP", key)) +} + +func RPush(key string, data interface{}) (interface{}, error) { + // set + return Do("RPUSH", key, data) +} + +func RPop(key string) (interface{}, error) { + return Do("RPOP", key) +} + +func RPopString(key string) (string, error) { + return redigo.String(Do("RPOP", key)) +} +func RPopFloat(key string) (float64, error) { + f, err := redigo.Float64(Do("RPOP", key)) + return f, err +} +func RPopInt(key string) (int, error) { + return redigo.Int(Do("RPOP", key)) +} +func RPopInt64(key string) (int64, error) { + return redigo.Int64(Do("RPOP", key)) +} + +func Scan(cursor int64, pattern string, count int64) (int64, []string, error) { + var items []string + var newCursor int64 + + values, err := redigo.Values(Do("SCAN", cursor, "MATCH", pattern, "COUNT", count)) + if err != nil { + return 0, nil, err + } + values, err = redigo.Scan(values, &newCursor, &items) + if err != nil { + return 0, nil, err + } + return newCursor, items, nil +} + +func LPushMax(key string, data ...interface{}) (interface{}, error) { + // set + return Do("LPUSH", key, data) +} diff --git a/utils/cache/redis_cluster.go b/utils/cache/redis_cluster.go new file mode 100644 index 0000000..901f30c --- /dev/null +++ b/utils/cache/redis_cluster.go @@ -0,0 +1,622 @@ +package cache + +import ( + "strconv" + "time" + + "github.com/go-redis/redis" +) + +var pools *redis.ClusterClient + +func NewRedisCluster(addrs []string) error { + opt := &redis.ClusterOptions{ + Addrs: addrs, + PoolSize: redisPoolSize, + PoolTimeout: redisPoolTTL, + IdleTimeout: redisIdleTTL, + DialTimeout: redisDialTTL, + ReadTimeout: redisReadTTL, + WriteTimeout: redisWriteTTL, + } + pools = redis.NewClusterClient(opt) + if err := pools.Ping().Err(); err != nil { + return err + } + return nil +} + +func RCGet(key string) (interface{}, error) { + res, err := pools.Get(key).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func RCSet(key string, value interface{}) error { + err := pools.Set(key, value, 0).Err() + return convertError(err) +} +func RCGetSet(key string, value interface{}) (interface{}, error) { + res, err := pools.GetSet(key, value).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func RCSetNx(key string, value interface{}) (int64, error) { + res, err := pools.SetNX(key, value, 0).Result() + if err != nil { + return 0, convertError(err) + } + if res { + return 1, nil + } + return 0, nil +} +func RCSetEx(key string, value interface{}, timeout int64) error { + _, err := pools.Set(key, value, time.Duration(timeout)*time.Second).Result() + if err != nil { + return convertError(err) + } + return nil +} + +// nil表示成功,ErrNil表示数据库内已经存在这个key,其他表示数据库发生错误 +func RCSetNxEx(key string, value interface{}, timeout int64) error { + res, err := pools.SetNX(key, value, time.Duration(timeout)*time.Second).Result() + if err != nil { + return convertError(err) + } + if res { + return nil + } + return ErrNil +} +func RCMGet(keys ...string) ([]interface{}, error) { + res, err := pools.MGet(keys...).Result() + return res, convertError(err) +} + +// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test} +func RCMSet(kvs map[string]interface{}) error { + pairs := make([]string, 0, len(kvs)*2) + for k, v := range kvs { + val, err := String(v, nil) + if err != nil { + return err + } + pairs = append(pairs, k, val) + } + return convertError(pools.MSet(pairs).Err()) +} + +// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test} +func RCMSetNX(kvs map[string]interface{}) (bool, error) { + pairs := make([]string, 0, len(kvs)*2) + for k, v := range kvs { + val, err := String(v, nil) + if err != nil { + return false, err + } + pairs = append(pairs, k, val) + } + res, err := pools.MSetNX(pairs).Result() + return res, convertError(err) +} +func RCExpireAt(key string, timestamp int64) (int64, error) { + res, err := pools.ExpireAt(key, time.Unix(timestamp, 0)).Result() + if err != nil { + return 0, convertError(err) + } + if res { + return 1, nil + } + return 0, nil +} +func RCDel(keys ...string) (int64, error) { + args := make([]interface{}, 0, len(keys)) + for _, key := range keys { + args = append(args, key) + } + res, err := pools.Del(keys...).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCIncr(key string) (int64, error) { + res, err := pools.Incr(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCIncrBy(key string, delta int64) (int64, error) { + res, err := pools.IncrBy(key, delta).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCExpire(key string, duration int64) (int64, error) { + res, err := pools.Expire(key, time.Duration(duration)*time.Second).Result() + if err != nil { + return 0, convertError(err) + } + if res { + return 1, nil + } + return 0, nil +} +func RCExists(key string) (bool, error) { + res, err := pools.Exists(key).Result() + if err != nil { + return false, convertError(err) + } + if res > 0 { + return true, nil + } + return false, nil +} +func RCHGet(key string, field string) (interface{}, error) { + res, err := pools.HGet(key, field).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func RCHLen(key string) (int64, error) { + res, err := pools.HLen(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCHSet(key string, field string, val interface{}) error { + value, err := String(val, nil) + if err != nil && err != ErrNil { + return err + } + _, err = pools.HSet(key, field, value).Result() + if err != nil { + return convertError(err) + } + return nil +} +func RCHDel(key string, fields ...string) (int64, error) { + args := make([]interface{}, 0, len(fields)+1) + args = append(args, key) + for _, field := range fields { + args = append(args, field) + } + res, err := pools.HDel(key, fields...).Result() + if err != nil { + return 0, convertError(err) + } + return res, nil +} + +func RCHMGet(key string, fields ...string) (interface{}, error) { + args := make([]interface{}, 0, len(fields)+1) + args = append(args, key) + for _, field := range fields { + args = append(args, field) + } + if len(fields) == 0 { + return nil, ErrNil + } + res, err := pools.HMGet(key, fields...).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCHMSet(key string, kvs ...interface{}) error { + if len(kvs) == 0 { + return nil + } + if len(kvs)%2 != 0 { + return ErrWrongArgsNum + } + var err error + v := map[string]interface{}{} // todo change + v["field"], err = String(kvs[0], nil) + if err != nil && err != ErrNil { + return err + } + v["value"], err = String(kvs[1], nil) + if err != nil && err != ErrNil { + return err + } + pairs := make([]string, 0, len(kvs)-2) + if len(kvs) > 2 { + for _, kv := range kvs[2:] { + kvString, err := String(kv, nil) + if err != nil && err != ErrNil { + return err + } + pairs = append(pairs, kvString) + } + } + v["paris"] = pairs + _, err = pools.HMSet(key, v).Result() + if err != nil { + return convertError(err) + } + return nil +} + +func RCHKeys(key string) ([]string, error) { + res, err := pools.HKeys(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCHVals(key string) ([]interface{}, error) { + res, err := pools.HVals(key).Result() + if err != nil { + return nil, convertError(err) + } + rs := make([]interface{}, 0, len(res)) + for _, res := range res { + rs = append(rs, res) + } + return rs, nil +} +func RCHGetAll(key string) (map[string]string, error) { + vals, err := pools.HGetAll(key).Result() + if err != nil { + return nil, convertError(err) + } + return vals, nil +} +func RCHIncrBy(key, field string, delta int64) (int64, error) { + res, err := pools.HIncrBy(key, field, delta).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCZAdd(key string, kvs ...interface{}) (int64, error) { + args := make([]interface{}, 0, len(kvs)+1) + args = append(args, key) + args = append(args, kvs...) + if len(kvs) == 0 { + return 0, nil + } + if len(kvs)%2 != 0 { + return 0, ErrWrongArgsNum + } + zs := make([]redis.Z, len(kvs)/2) + for i := 0; i < len(kvs); i += 2 { + idx := i / 2 + score, err := Float64(kvs[i], nil) + if err != nil && err != ErrNil { + return 0, err + } + zs[idx].Score = score + zs[idx].Member = kvs[i+1] + } + res, err := pools.ZAdd(key, zs...).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCZRem(key string, members ...string) (int64, error) { + args := make([]interface{}, 0, len(members)) + args = append(args, key) + for _, member := range members { + args = append(args, member) + } + res, err := pools.ZRem(key, members).Result() + if err != nil { + return res, convertError(err) + } + return res, err +} + +func RCZRange(key string, min, max int64, withScores bool) (interface{}, error) { + res := make([]interface{}, 0) + if withScores { + zs, err := pools.ZRangeWithScores(key, min, max).Result() + if err != nil { + return nil, convertError(err) + } + for _, z := range zs { + res = append(res, z.Member, strconv.FormatFloat(z.Score, 'f', -1, 64)) + } + } else { + ms, err := pools.ZRange(key, min, max).Result() + if err != nil { + return nil, convertError(err) + } + for _, m := range ms { + res = append(res, m) + } + } + return res, nil +} +func RCZRangeByScoreWithScore(key string, min, max int64) (map[string]int64, error) { + opt := new(redis.ZRangeBy) + opt.Min = strconv.FormatInt(int64(min), 10) + opt.Max = strconv.FormatInt(int64(max), 10) + opt.Count = -1 + opt.Offset = 0 + vals, err := pools.ZRangeByScoreWithScores(key, *opt).Result() + if err != nil { + return nil, convertError(err) + } + res := make(map[string]int64, len(vals)) + for _, val := range vals { + key, err := String(val.Member, nil) + if err != nil && err != ErrNil { + return nil, err + } + res[key] = int64(val.Score) + } + return res, nil +} +func RCLRange(key string, start, stop int64) (interface{}, error) { + res, err := pools.LRange(key, start, stop).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCLSet(key string, index int, value interface{}) error { + err := pools.LSet(key, int64(index), value).Err() + return convertError(err) +} +func RCLLen(key string) (int64, error) { + res, err := pools.LLen(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCLRem(key string, count int, value interface{}) (int, error) { + val, _ := value.(string) + res, err := pools.LRem(key, int64(count), val).Result() + if err != nil { + return int(res), convertError(err) + } + return int(res), nil +} +func RCTTl(key string) (int64, error) { + duration, err := pools.TTL(key).Result() + if err != nil { + return int64(duration.Seconds()), convertError(err) + } + return int64(duration.Seconds()), nil +} +func RCLPop(key string) (interface{}, error) { + res, err := pools.LPop(key).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCRPop(key string) (interface{}, error) { + res, err := pools.RPop(key).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCBLPop(key string, timeout int) (interface{}, error) { + res, err := pools.BLPop(time.Duration(timeout)*time.Second, key).Result() + if err != nil { + // 兼容redis 2.x + if err == redis.Nil { + return nil, ErrNil + } + return nil, err + } + return res[1], nil +} +func RCBRPop(key string, timeout int) (interface{}, error) { + res, err := pools.BRPop(time.Duration(timeout)*time.Second, key).Result() + if err != nil { + // 兼容redis 2.x + if err == redis.Nil { + return nil, ErrNil + } + return nil, convertError(err) + } + return res[1], nil +} +func RCLPush(key string, value ...interface{}) error { + args := make([]interface{}, 0, len(value)+1) + args = append(args, key) + args = append(args, value...) + vals := make([]string, 0, len(value)) + for _, v := range value { + val, err := String(v, nil) + if err != nil && err != ErrNil { + return err + } + vals = append(vals, val) + } + _, err := pools.LPush(key, vals).Result() // todo ... + if err != nil { + return convertError(err) + } + return nil +} +func RCRPush(key string, value ...interface{}) error { + args := make([]interface{}, 0, len(value)+1) + args = append(args, key) + args = append(args, value...) + vals := make([]string, 0, len(value)) + for _, v := range value { + val, err := String(v, nil) + if err != nil && err != ErrNil { + if err == ErrNil { + continue + } + return err + } + if val == "" { + continue + } + vals = append(vals, val) + } + _, err := pools.RPush(key, vals).Result() // todo ... + if err != nil { + return convertError(err) + } + return nil +} + +// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test} +func RCBRPopLPush(srcKey string, destKey string, timeout int) (interface{}, error) { + res, err := pools.BRPopLPush(srcKey, destKey, time.Duration(timeout)*time.Second).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} + +// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test} +func RCRPopLPush(srcKey string, destKey string) (interface{}, error) { + res, err := pools.RPopLPush(srcKey, destKey).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCSAdd(key string, members ...interface{}) (int64, error) { + args := make([]interface{}, 0, len(members)+1) + args = append(args, key) + args = append(args, members...) + ms := make([]string, 0, len(members)) + for _, member := range members { + m, err := String(member, nil) + if err != nil && err != ErrNil { + return 0, err + } + ms = append(ms, m) + } + res, err := pools.SAdd(key, ms).Result() // todo ... + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCSPop(key string) ([]byte, error) { + res, err := pools.SPop(key).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func RCSIsMember(key string, member interface{}) (bool, error) { + m, _ := member.(string) + res, err := pools.SIsMember(key, m).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCSRem(key string, members ...interface{}) (int64, error) { + args := make([]interface{}, 0, len(members)+1) + args = append(args, key) + args = append(args, members...) + ms := make([]string, 0, len(members)) + for _, member := range members { + m, err := String(member, nil) + if err != nil && err != ErrNil { + return 0, err + } + ms = append(ms, m) + } + res, err := pools.SRem(key, ms).Result() // todo ... + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCSMembers(key string) ([]string, error) { + res, err := pools.SMembers(key).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCScriptLoad(luaScript string) (interface{}, error) { + res, err := pools.ScriptLoad(luaScript).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCEvalSha(sha1 string, numberKeys int, keysArgs ...interface{}) (interface{}, error) { + vals := make([]interface{}, 0, len(keysArgs)+2) + vals = append(vals, sha1, numberKeys) + vals = append(vals, keysArgs...) + keys := make([]string, 0, numberKeys) + args := make([]string, 0, len(keysArgs)-numberKeys) + for i, value := range keysArgs { + val, err := String(value, nil) + if err != nil && err != ErrNil { + return nil, err + } + if i < numberKeys { + keys = append(keys, val) + } else { + args = append(args, val) + } + } + res, err := pools.EvalSha(sha1, keys, args).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCEval(luaScript string, numberKeys int, keysArgs ...interface{}) (interface{}, error) { + vals := make([]interface{}, 0, len(keysArgs)+2) + vals = append(vals, luaScript, numberKeys) + vals = append(vals, keysArgs...) + keys := make([]string, 0, numberKeys) + args := make([]string, 0, len(keysArgs)-numberKeys) + for i, value := range keysArgs { + val, err := String(value, nil) + if err != nil && err != ErrNil { + return nil, err + } + if i < numberKeys { + keys = append(keys, val) + } else { + args = append(args, val) + } + } + res, err := pools.Eval(luaScript, keys, args).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func RCGetBit(key string, offset int64) (int64, error) { + res, err := pools.GetBit(key, offset).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func RCSetBit(key string, offset uint32, value int) (int, error) { + res, err := pools.SetBit(key, int64(offset), value).Result() + return int(res), convertError(err) +} +func RCGetClient() *redis.ClusterClient { + return pools +} +func convertError(err error) error { + if err == redis.Nil { + // 为了兼容redis 2.x,这里不返回 ErrNil,ErrNil在调用redis_cluster_reply函数时才返回 + return nil + } + return err +} diff --git a/utils/cache/redis_pool.go b/utils/cache/redis_pool.go new file mode 100644 index 0000000..ca38b3f --- /dev/null +++ b/utils/cache/redis_pool.go @@ -0,0 +1,324 @@ +package cache + +import ( + "errors" + "log" + "strings" + "time" + + redigo "github.com/gomodule/redigo/redis" +) + +type RedisPool struct { + *redigo.Pool +} + +func NewRedisPool(cfg *Config) *RedisPool { + return &RedisPool{&redigo.Pool{ + MaxIdle: cfg.MaxIdle, + IdleTimeout: cfg.IdleTimeout, + MaxActive: cfg.MaxActive, + Wait: cfg.Wait, + Dial: func() (redigo.Conn, error) { + c, err := redigo.Dial("tcp", cfg.Server) + if err != nil { + log.Println("Redis Dial failed: ", err) + return nil, err + } + if cfg.Password != "" { + if _, err := c.Do("AUTH", cfg.Password); err != nil { + c.Close() + log.Println("Redis AUTH failed: ", err) + return nil, err + } + } + return c, err + }, + TestOnBorrow: func(c redigo.Conn, t time.Time) error { + _, err := c.Do("PING") + if err != nil { + log.Println("Unable to ping to redis server:", err) + } + return err + }, + }} +} + +func (p *RedisPool) Do(cmd string, args ...interface{}) (reply interface{}, err error) { + conn := pool.Get() + defer conn.Close() + return conn.Do(cmd, args...) +} + +func (p *RedisPool) GetPool() *redigo.Pool { + return pool +} + +func (p *RedisPool) ParseKey(key string, vars []string) (string, error) { + arr := strings.Split(key, conf.KeyPlaceholder) + actualKey := "" + if len(arr) != len(vars)+1 { + return "", errors.New("redis/connection.go: Insufficient arguments to parse key") + } else { + for index, val := range arr { + if index == 0 { + actualKey = arr[index] + } else { + actualKey += vars[index-1] + val + } + } + } + return getPrefixedKey(actualKey), nil +} + +func (p *RedisPool) getPrefixedKey(key string) string { + return conf.KeyPrefix + conf.KeyDelimiter + key +} +func (p *RedisPool) StripEnvKey(key string) string { + return strings.TrimLeft(key, conf.KeyPrefix+conf.KeyDelimiter) +} +func (p *RedisPool) SplitKey(key string) []string { + return strings.Split(key, conf.KeyDelimiter) +} +func (p *RedisPool) Expire(key string, ttl int) (interface{}, error) { + return Do("EXPIRE", key, ttl) +} +func (p *RedisPool) Persist(key string) (interface{}, error) { + return Do("PERSIST", key) +} + +func (p *RedisPool) Del(key string) (interface{}, error) { + return Do("DEL", key) +} +func (p *RedisPool) Set(key string, data interface{}) (interface{}, error) { + // set + return Do("SET", key, data) +} +func (p *RedisPool) SetNX(key string, data interface{}) (interface{}, error) { + return Do("SETNX", key, data) +} +func (p *RedisPool) SetEx(key string, data interface{}, ttl int) (interface{}, error) { + return Do("SETEX", key, ttl, data) +} +func (p *RedisPool) Get(key string) (interface{}, error) { + // get + return Do("GET", key) +} +func (p *RedisPool) GetStringMap(key string) (map[string]string, error) { + // get + return redigo.StringMap(Do("GET", key)) +} + +func (p *RedisPool) GetTTL(key string) (time.Duration, error) { + ttl, err := redigo.Int64(Do("TTL", key)) + return time.Duration(ttl) * time.Second, err +} +func (p *RedisPool) GetBytes(key string) ([]byte, error) { + return redigo.Bytes(Do("GET", key)) +} +func (p *RedisPool) GetString(key string) (string, error) { + return redigo.String(Do("GET", key)) +} +func (p *RedisPool) GetInt(key string) (int, error) { + return redigo.Int(Do("GET", key)) +} +func (p *RedisPool) GetStringLength(key string) (int, error) { + return redigo.Int(Do("STRLEN", key)) +} +func (p *RedisPool) ZAdd(key string, score float64, data interface{}) (interface{}, error) { + return Do("ZADD", key, score, data) +} +func (p *RedisPool) ZRem(key string, data interface{}) (interface{}, error) { + return Do("ZREM", key, data) +} +func (p *RedisPool) ZRange(key string, start int, end int, withScores bool) ([]interface{}, error) { + if withScores { + return redigo.Values(Do("ZRANGE", key, start, end, "WITHSCORES")) + } + return redigo.Values(Do("ZRANGE", key, start, end)) +} +func (p *RedisPool) SAdd(setName string, data interface{}) (interface{}, error) { + return Do("SADD", setName, data) +} +func (p *RedisPool) SCard(setName string) (int64, error) { + return redigo.Int64(Do("SCARD", setName)) +} +func (p *RedisPool) SIsMember(setName string, data interface{}) (bool, error) { + return redigo.Bool(Do("SISMEMBER", setName, data)) +} +func (p *RedisPool) SMembers(setName string) ([]string, error) { + return redigo.Strings(Do("SMEMBERS", setName)) +} +func (p *RedisPool) SRem(setName string, data interface{}) (interface{}, error) { + return Do("SREM", setName, data) +} +func (p *RedisPool) HSet(key string, HKey string, data interface{}) (interface{}, error) { + return Do("HSET", key, HKey, data) +} + +func (p *RedisPool) HGet(key string, HKey string) (interface{}, error) { + return Do("HGET", key, HKey) +} + +func (p *RedisPool) HMGet(key string, hashKeys ...string) ([]interface{}, error) { + ret, err := Do("HMGET", key, hashKeys) + if err != nil { + return nil, err + } + reta, ok := ret.([]interface{}) + if !ok { + return nil, errors.New("result not an array") + } + return reta, nil +} + +func (p *RedisPool) HMSet(key string, hashKeys []string, vals []interface{}) (interface{}, error) { + if len(hashKeys) == 0 || len(hashKeys) != len(vals) { + var ret interface{} + return ret, errors.New("bad length") + } + input := []interface{}{key} + for i, v := range hashKeys { + input = append(input, v, vals[i]) + } + return Do("HMSET", input...) +} + +func (p *RedisPool) HGetString(key string, HKey string) (string, error) { + return redigo.String(Do("HGET", key, HKey)) +} +func (p *RedisPool) HGetFloat(key string, HKey string) (float64, error) { + f, err := redigo.Float64(Do("HGET", key, HKey)) + return float64(f), err +} +func (p *RedisPool) HGetInt(key string, HKey string) (int, error) { + return redigo.Int(Do("HGET", key, HKey)) +} +func (p *RedisPool) HGetInt64(key string, HKey string) (int64, error) { + return redigo.Int64(Do("HGET", key, HKey)) +} +func (p *RedisPool) HGetBool(key string, HKey string) (bool, error) { + return redigo.Bool(Do("HGET", key, HKey)) +} +func (p *RedisPool) HDel(key string, HKey string) (interface{}, error) { + return Do("HDEL", key, HKey) +} +func (p *RedisPool) HGetAll(key string) (map[string]interface{}, error) { + vals, err := redigo.Values(Do("HGETALL", key)) + if err != nil { + return nil, err + } + num := len(vals) / 2 + result := make(map[string]interface{}, num) + for i := 0; i < num; i++ { + key, _ := redigo.String(vals[2*i], nil) + result[key] = vals[2*i+1] + } + return result, nil +} + +// NOTE: Use this in production environment with extreme care. +// Read more here:https://redigo.io/commands/keys +func (p *RedisPool) Keys(pattern string) ([]string, error) { + return redigo.Strings(Do("KEYS", pattern)) +} + +func (p *RedisPool) HKeys(key string) ([]string, error) { + return redigo.Strings(Do("HKEYS", key)) +} + +func (p *RedisPool) Exists(key string) (bool, error) { + count, err := redigo.Int(Do("EXISTS", key)) + if count == 0 { + return false, err + } else { + return true, err + } +} + +func (p *RedisPool) Incr(key string) (int64, error) { + return redigo.Int64(Do("INCR", key)) +} + +func (p *RedisPool) Decr(key string) (int64, error) { + return redigo.Int64(Do("DECR", key)) +} + +func (p *RedisPool) IncrBy(key string, incBy int64) (int64, error) { + return redigo.Int64(Do("INCRBY", key, incBy)) +} + +func (p *RedisPool) DecrBy(key string, decrBy int64) (int64, error) { + return redigo.Int64(Do("DECRBY", key)) +} + +func (p *RedisPool) IncrByFloat(key string, incBy float64) (float64, error) { + return redigo.Float64(Do("INCRBYFLOAT", key, incBy)) +} + +func (p *RedisPool) DecrByFloat(key string, decrBy float64) (float64, error) { + return redigo.Float64(Do("DECRBYFLOAT", key, decrBy)) +} + +// use for message queue +func (p *RedisPool) LPush(key string, data interface{}) (interface{}, error) { + // set + return Do("LPUSH", key, data) +} + +func (p *RedisPool) LPop(key string) (interface{}, error) { + return Do("LPOP", key) +} + +func (p *RedisPool) LPopString(key string) (string, error) { + return redigo.String(Do("LPOP", key)) +} +func (p *RedisPool) LPopFloat(key string) (float64, error) { + f, err := redigo.Float64(Do("LPOP", key)) + return float64(f), err +} +func (p *RedisPool) LPopInt(key string) (int, error) { + return redigo.Int(Do("LPOP", key)) +} +func (p *RedisPool) LPopInt64(key string) (int64, error) { + return redigo.Int64(Do("LPOP", key)) +} + +func (p *RedisPool) RPush(key string, data interface{}) (interface{}, error) { + // set + return Do("RPUSH", key, data) +} + +func (p *RedisPool) RPop(key string) (interface{}, error) { + return Do("RPOP", key) +} + +func (p *RedisPool) RPopString(key string) (string, error) { + return redigo.String(Do("RPOP", key)) +} +func (p *RedisPool) RPopFloat(key string) (float64, error) { + f, err := redigo.Float64(Do("RPOP", key)) + return float64(f), err +} +func (p *RedisPool) RPopInt(key string) (int, error) { + return redigo.Int(Do("RPOP", key)) +} +func (p *RedisPool) RPopInt64(key string) (int64, error) { + return redigo.Int64(Do("RPOP", key)) +} + +func (p *RedisPool) Scan(cursor int64, pattern string, count int64) (int64, []string, error) { + var items []string + var newCursor int64 + + values, err := redigo.Values(Do("SCAN", cursor, "MATCH", pattern, "COUNT", count)) + if err != nil { + return 0, nil, err + } + values, err = redigo.Scan(values, &newCursor, &items) + if err != nil { + return 0, nil, err + } + + return newCursor, items, nil +} diff --git a/utils/cache/redis_pool_cluster.go b/utils/cache/redis_pool_cluster.go new file mode 100644 index 0000000..cd1911b --- /dev/null +++ b/utils/cache/redis_pool_cluster.go @@ -0,0 +1,617 @@ +package cache + +import ( + "strconv" + "time" + + "github.com/go-redis/redis" +) + +type RedisClusterPool struct { + client *redis.ClusterClient +} + +func NewRedisClusterPool(addrs []string) (*RedisClusterPool, error) { + opt := &redis.ClusterOptions{ + Addrs: addrs, + PoolSize: 512, + PoolTimeout: 10 * time.Second, + IdleTimeout: 10 * time.Second, + DialTimeout: 10 * time.Second, + ReadTimeout: 3 * time.Second, + WriteTimeout: 3 * time.Second, + } + c := redis.NewClusterClient(opt) + if err := c.Ping().Err(); err != nil { + return nil, err + } + return &RedisClusterPool{client: c}, nil +} + +func (p *RedisClusterPool) Get(key string) (interface{}, error) { + res, err := p.client.Get(key).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func (p *RedisClusterPool) Set(key string, value interface{}) error { + err := p.client.Set(key, value, 0).Err() + return convertError(err) +} +func (p *RedisClusterPool) GetSet(key string, value interface{}) (interface{}, error) { + res, err := p.client.GetSet(key, value).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func (p *RedisClusterPool) SetNx(key string, value interface{}) (int64, error) { + res, err := p.client.SetNX(key, value, 0).Result() + if err != nil { + return 0, convertError(err) + } + if res { + return 1, nil + } + return 0, nil +} +func (p *RedisClusterPool) SetEx(key string, value interface{}, timeout int64) error { + _, err := p.client.Set(key, value, time.Duration(timeout)*time.Second).Result() + if err != nil { + return convertError(err) + } + return nil +} + +// nil表示成功,ErrNil表示数据库内已经存在这个key,其他表示数据库发生错误 +func (p *RedisClusterPool) SetNxEx(key string, value interface{}, timeout int64) error { + res, err := p.client.SetNX(key, value, time.Duration(timeout)*time.Second).Result() + if err != nil { + return convertError(err) + } + if res { + return nil + } + return ErrNil +} +func (p *RedisClusterPool) MGet(keys ...string) ([]interface{}, error) { + res, err := p.client.MGet(keys...).Result() + return res, convertError(err) +} + +// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test} +func (p *RedisClusterPool) MSet(kvs map[string]interface{}) error { + pairs := make([]string, 0, len(kvs)*2) + for k, v := range kvs { + val, err := String(v, nil) + if err != nil { + return err + } + pairs = append(pairs, k, val) + } + return convertError(p.client.MSet(pairs).Err()) +} + +// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test} +func (p *RedisClusterPool) MSetNX(kvs map[string]interface{}) (bool, error) { + pairs := make([]string, 0, len(kvs)*2) + for k, v := range kvs { + val, err := String(v, nil) + if err != nil { + return false, err + } + pairs = append(pairs, k, val) + } + res, err := p.client.MSetNX(pairs).Result() + return res, convertError(err) +} +func (p *RedisClusterPool) ExpireAt(key string, timestamp int64) (int64, error) { + res, err := p.client.ExpireAt(key, time.Unix(timestamp, 0)).Result() + if err != nil { + return 0, convertError(err) + } + if res { + return 1, nil + } + return 0, nil +} +func (p *RedisClusterPool) Del(keys ...string) (int64, error) { + args := make([]interface{}, 0, len(keys)) + for _, key := range keys { + args = append(args, key) + } + res, err := p.client.Del(keys...).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) Incr(key string) (int64, error) { + res, err := p.client.Incr(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) IncrBy(key string, delta int64) (int64, error) { + res, err := p.client.IncrBy(key, delta).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) Expire(key string, duration int64) (int64, error) { + res, err := p.client.Expire(key, time.Duration(duration)*time.Second).Result() + if err != nil { + return 0, convertError(err) + } + if res { + return 1, nil + } + return 0, nil +} +func (p *RedisClusterPool) Exists(key string) (bool, error) { // todo (bool, error) + res, err := p.client.Exists(key).Result() + if err != nil { + return false, convertError(err) + } + if res > 0 { + return true, nil + } + return false, nil +} +func (p *RedisClusterPool) HGet(key string, field string) (interface{}, error) { + res, err := p.client.HGet(key, field).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func (p *RedisClusterPool) HLen(key string) (int64, error) { + res, err := p.client.HLen(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) HSet(key string, field string, val interface{}) error { + value, err := String(val, nil) + if err != nil && err != ErrNil { + return err + } + _, err = p.client.HSet(key, field, value).Result() + if err != nil { + return convertError(err) + } + return nil +} +func (p *RedisClusterPool) HDel(key string, fields ...string) (int64, error) { + args := make([]interface{}, 0, len(fields)+1) + args = append(args, key) + for _, field := range fields { + args = append(args, field) + } + res, err := p.client.HDel(key, fields...).Result() + if err != nil { + return 0, convertError(err) + } + return res, nil +} + +func (p *RedisClusterPool) HMGet(key string, fields ...string) (interface{}, error) { + args := make([]interface{}, 0, len(fields)+1) + args = append(args, key) + for _, field := range fields { + args = append(args, field) + } + if len(fields) == 0 { + return nil, ErrNil + } + res, err := p.client.HMGet(key, fields...).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) HMSet(key string, kvs ...interface{}) error { + if len(kvs) == 0 { + return nil + } + if len(kvs)%2 != 0 { + return ErrWrongArgsNum + } + var err error + v := map[string]interface{}{} // todo change + v["field"], err = String(kvs[0], nil) + if err != nil && err != ErrNil { + return err + } + v["value"], err = String(kvs[1], nil) + if err != nil && err != ErrNil { + return err + } + pairs := make([]string, 0, len(kvs)-2) + if len(kvs) > 2 { + for _, kv := range kvs[2:] { + kvString, err := String(kv, nil) + if err != nil && err != ErrNil { + return err + } + pairs = append(pairs, kvString) + } + } + v["paris"] = pairs + _, err = p.client.HMSet(key, v).Result() + if err != nil { + return convertError(err) + } + return nil +} + +func (p *RedisClusterPool) HKeys(key string) ([]string, error) { + res, err := p.client.HKeys(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) HVals(key string) ([]interface{}, error) { + res, err := p.client.HVals(key).Result() + if err != nil { + return nil, convertError(err) + } + rs := make([]interface{}, 0, len(res)) + for _, res := range res { + rs = append(rs, res) + } + return rs, nil +} +func (p *RedisClusterPool) HGetAll(key string) (map[string]string, error) { + vals, err := p.client.HGetAll(key).Result() + if err != nil { + return nil, convertError(err) + } + return vals, nil +} +func (p *RedisClusterPool) HIncrBy(key, field string, delta int64) (int64, error) { + res, err := p.client.HIncrBy(key, field, delta).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) ZAdd(key string, kvs ...interface{}) (int64, error) { + args := make([]interface{}, 0, len(kvs)+1) + args = append(args, key) + args = append(args, kvs...) + if len(kvs) == 0 { + return 0, nil + } + if len(kvs)%2 != 0 { + return 0, ErrWrongArgsNum + } + zs := make([]redis.Z, len(kvs)/2) + for i := 0; i < len(kvs); i += 2 { + idx := i / 2 + score, err := Float64(kvs[i], nil) + if err != nil && err != ErrNil { + return 0, err + } + zs[idx].Score = score + zs[idx].Member = kvs[i+1] + } + res, err := p.client.ZAdd(key, zs...).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) ZRem(key string, members ...string) (int64, error) { + args := make([]interface{}, 0, len(members)) + args = append(args, key) + for _, member := range members { + args = append(args, member) + } + res, err := p.client.ZRem(key, members).Result() + if err != nil { + return res, convertError(err) + } + return res, err +} + +func (p *RedisClusterPool) ZRange(key string, min, max int64, withScores bool) (interface{}, error) { + res := make([]interface{}, 0) + if withScores { + zs, err := p.client.ZRangeWithScores(key, min, max).Result() + if err != nil { + return nil, convertError(err) + } + for _, z := range zs { + res = append(res, z.Member, strconv.FormatFloat(z.Score, 'f', -1, 64)) + } + } else { + ms, err := p.client.ZRange(key, min, max).Result() + if err != nil { + return nil, convertError(err) + } + for _, m := range ms { + res = append(res, m) + } + } + return res, nil +} +func (p *RedisClusterPool) ZRangeByScoreWithScore(key string, min, max int64) (map[string]int64, error) { + opt := new(redis.ZRangeBy) + opt.Min = strconv.FormatInt(int64(min), 10) + opt.Max = strconv.FormatInt(int64(max), 10) + opt.Count = -1 + opt.Offset = 0 + vals, err := p.client.ZRangeByScoreWithScores(key, *opt).Result() + if err != nil { + return nil, convertError(err) + } + res := make(map[string]int64, len(vals)) + for _, val := range vals { + key, err := String(val.Member, nil) + if err != nil && err != ErrNil { + return nil, err + } + res[key] = int64(val.Score) + } + return res, nil +} +func (p *RedisClusterPool) LRange(key string, start, stop int64) (interface{}, error) { + res, err := p.client.LRange(key, start, stop).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) LSet(key string, index int, value interface{}) error { + err := p.client.LSet(key, int64(index), value).Err() + return convertError(err) +} +func (p *RedisClusterPool) LLen(key string) (int64, error) { + res, err := p.client.LLen(key).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) LRem(key string, count int, value interface{}) (int, error) { + val, _ := value.(string) + res, err := p.client.LRem(key, int64(count), val).Result() + if err != nil { + return int(res), convertError(err) + } + return int(res), nil +} +func (p *RedisClusterPool) TTl(key string) (int64, error) { + duration, err := p.client.TTL(key).Result() + if err != nil { + return int64(duration.Seconds()), convertError(err) + } + return int64(duration.Seconds()), nil +} +func (p *RedisClusterPool) LPop(key string) (interface{}, error) { + res, err := p.client.LPop(key).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) RPop(key string) (interface{}, error) { + res, err := p.client.RPop(key).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) BLPop(key string, timeout int) (interface{}, error) { + res, err := p.client.BLPop(time.Duration(timeout)*time.Second, key).Result() + if err != nil { + // 兼容redis 2.x + if err == redis.Nil { + return nil, ErrNil + } + return nil, err + } + return res[1], nil +} +func (p *RedisClusterPool) BRPop(key string, timeout int) (interface{}, error) { + res, err := p.client.BRPop(time.Duration(timeout)*time.Second, key).Result() + if err != nil { + // 兼容redis 2.x + if err == redis.Nil { + return nil, ErrNil + } + return nil, convertError(err) + } + return res[1], nil +} +func (p *RedisClusterPool) LPush(key string, value ...interface{}) error { + args := make([]interface{}, 0, len(value)+1) + args = append(args, key) + args = append(args, value...) + vals := make([]string, 0, len(value)) + for _, v := range value { + val, err := String(v, nil) + if err != nil && err != ErrNil { + return err + } + vals = append(vals, val) + } + _, err := p.client.LPush(key, vals).Result() // todo ... + if err != nil { + return convertError(err) + } + return nil +} +func (p *RedisClusterPool) RPush(key string, value ...interface{}) error { + args := make([]interface{}, 0, len(value)+1) + args = append(args, key) + args = append(args, value...) + vals := make([]string, 0, len(value)) + for _, v := range value { + val, err := String(v, nil) + if err != nil && err != ErrNil { + if err == ErrNil { + continue + } + return err + } + if val == "" { + continue + } + vals = append(vals, val) + } + _, err := p.client.RPush(key, vals).Result() // todo ... + if err != nil { + return convertError(err) + } + return nil +} + +// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test} +func (p *RedisClusterPool) BRPopLPush(srcKey string, destKey string, timeout int) (interface{}, error) { + res, err := p.client.BRPopLPush(srcKey, destKey, time.Duration(timeout)*time.Second).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} + +// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test} +func (p *RedisClusterPool) RPopLPush(srcKey string, destKey string) (interface{}, error) { + res, err := p.client.RPopLPush(srcKey, destKey).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) SAdd(key string, members ...interface{}) (int64, error) { + args := make([]interface{}, 0, len(members)+1) + args = append(args, key) + args = append(args, members...) + ms := make([]string, 0, len(members)) + for _, member := range members { + m, err := String(member, nil) + if err != nil && err != ErrNil { + return 0, err + } + ms = append(ms, m) + } + res, err := p.client.SAdd(key, ms).Result() // todo ... + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) SPop(key string) ([]byte, error) { + res, err := p.client.SPop(key).Result() + if err != nil { + return nil, convertError(err) + } + return []byte(res), nil +} +func (p *RedisClusterPool) SIsMember(key string, member interface{}) (bool, error) { + m, _ := member.(string) + res, err := p.client.SIsMember(key, m).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) SRem(key string, members ...interface{}) (int64, error) { + args := make([]interface{}, 0, len(members)+1) + args = append(args, key) + args = append(args, members...) + ms := make([]string, 0, len(members)) + for _, member := range members { + m, err := String(member, nil) + if err != nil && err != ErrNil { + return 0, err + } + ms = append(ms, m) + } + res, err := p.client.SRem(key, ms).Result() // todo ... + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) SMembers(key string) ([]string, error) { + res, err := p.client.SMembers(key).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) ScriptLoad(luaScript string) (interface{}, error) { + res, err := p.client.ScriptLoad(luaScript).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) EvalSha(sha1 string, numberKeys int, keysArgs ...interface{}) (interface{}, error) { + vals := make([]interface{}, 0, len(keysArgs)+2) + vals = append(vals, sha1, numberKeys) + vals = append(vals, keysArgs...) + keys := make([]string, 0, numberKeys) + args := make([]string, 0, len(keysArgs)-numberKeys) + for i, value := range keysArgs { + val, err := String(value, nil) + if err != nil && err != ErrNil { + return nil, err + } + if i < numberKeys { + keys = append(keys, val) + } else { + args = append(args, val) + } + } + res, err := p.client.EvalSha(sha1, keys, args).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) Eval(luaScript string, numberKeys int, keysArgs ...interface{}) (interface{}, error) { + vals := make([]interface{}, 0, len(keysArgs)+2) + vals = append(vals, luaScript, numberKeys) + vals = append(vals, keysArgs...) + keys := make([]string, 0, numberKeys) + args := make([]string, 0, len(keysArgs)-numberKeys) + for i, value := range keysArgs { + val, err := String(value, nil) + if err != nil && err != ErrNil { + return nil, err + } + if i < numberKeys { + keys = append(keys, val) + } else { + args = append(args, val) + } + } + res, err := p.client.Eval(luaScript, keys, args).Result() + if err != nil { + return nil, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) GetBit(key string, offset int64) (int64, error) { + res, err := p.client.GetBit(key, offset).Result() + if err != nil { + return res, convertError(err) + } + return res, nil +} +func (p *RedisClusterPool) SetBit(key string, offset uint32, value int) (int, error) { + res, err := p.client.SetBit(key, int64(offset), value).Result() + return int(res), convertError(err) +} +func (p *RedisClusterPool) GetClient() *redis.ClusterClient { + return pools +} diff --git a/utils/convert.go b/utils/convert.go new file mode 100644 index 0000000..6e278b8 --- /dev/null +++ b/utils/convert.go @@ -0,0 +1,388 @@ +package egg_system_rules + +import ( + "encoding/binary" + "encoding/json" + "fmt" + "math" + "reflect" + "strconv" + "strings" +) + +func ToString(raw interface{}, e error) (res string) { + if e != nil { + return "" + } + return AnyToString(raw) +} + +func ToInt64(raw interface{}, e error) int64 { + if e != nil { + return 0 + } + return AnyToInt64(raw) +} +func IsNil(i interface{}) bool { + vi := reflect.ValueOf(i) + if vi.Kind() == reflect.Ptr { + return vi.IsNil() + } + return false +} +func AnyToBool(raw interface{}) bool { + switch i := raw.(type) { + case float32, float64, int, int64, uint, uint8, uint16, uint32, uint64, int8, int16, int32: + return i != 0 + case []byte: + return i != nil + case string: + if i == "false" { + return false + } + return i != "" + case error: + return false + case nil: + return true + } + val := fmt.Sprint(raw) + val = strings.TrimLeft(val, "&") + if strings.TrimLeft(val, "{}") == "" { + return false + } + if strings.TrimLeft(val, "[]") == "" { + return false + } + // ptr type + b, err := json.Marshal(raw) + if err != nil { + return false + } + if strings.TrimLeft(string(b), "\"\"") == "" { + return false + } + if strings.TrimLeft(string(b), "{}") == "" { + return false + } + return true +} + +func AnyToInt64(raw interface{}) int64 { + switch i := raw.(type) { + case string: + res, _ := strconv.ParseInt(i, 10, 64) + return res + case []byte: + return BytesToInt64(i) + case int: + return int64(i) + case int64: + return i + case uint: + return int64(i) + case uint8: + return int64(i) + case uint16: + return int64(i) + case uint32: + return int64(i) + case uint64: + return int64(i) + case int8: + return int64(i) + case int16: + return int64(i) + case int32: + return int64(i) + case float32: + return int64(i) + case float64: + return int64(i) + case error: + return 0 + case bool: + if i { + return 1 + } + return 0 + } + return 0 +} + +func AnyToString(raw interface{}) string { + switch i := raw.(type) { + case []byte: + return string(i) + case int: + return strconv.FormatInt(int64(i), 10) + case int64: + return strconv.FormatInt(i, 10) + case float32: + return Float64ToStr(float64(i)) + case float64: + return Float64ToStr(i) + case uint: + return strconv.FormatInt(int64(i), 10) + case uint8: + return strconv.FormatInt(int64(i), 10) + case uint16: + return strconv.FormatInt(int64(i), 10) + case uint32: + return strconv.FormatInt(int64(i), 10) + case uint64: + return strconv.FormatInt(int64(i), 10) + case int8: + return strconv.FormatInt(int64(i), 10) + case int16: + return strconv.FormatInt(int64(i), 10) + case int32: + return strconv.FormatInt(int64(i), 10) + case string: + return i + case error: + return i.Error() + case bool: + return strconv.FormatBool(i) + } + return fmt.Sprintf("%#v", raw) +} + +func AnyToFloat64(raw interface{}) float64 { + switch i := raw.(type) { + case []byte: + f, _ := strconv.ParseFloat(string(i), 64) + return f + case int: + return float64(i) + case int64: + return float64(i) + case float32: + return float64(i) + case float64: + return i + case uint: + return float64(i) + case uint8: + return float64(i) + case uint16: + return float64(i) + case uint32: + return float64(i) + case uint64: + return float64(i) + case int8: + return float64(i) + case int16: + return float64(i) + case int32: + return float64(i) + case string: + f, _ := strconv.ParseFloat(i, 64) + return f + case bool: + if i { + return 1 + } + } + return 0 +} + +func ToByte(raw interface{}, e error) []byte { + if e != nil { + return []byte{} + } + switch i := raw.(type) { + case string: + return []byte(i) + case int: + return Int64ToBytes(int64(i)) + case int64: + return Int64ToBytes(i) + case float32: + return Float32ToByte(i) + case float64: + return Float64ToByte(i) + case uint: + return Int64ToBytes(int64(i)) + case uint8: + return Int64ToBytes(int64(i)) + case uint16: + return Int64ToBytes(int64(i)) + case uint32: + return Int64ToBytes(int64(i)) + case uint64: + return Int64ToBytes(int64(i)) + case int8: + return Int64ToBytes(int64(i)) + case int16: + return Int64ToBytes(int64(i)) + case int32: + return Int64ToBytes(int64(i)) + case []byte: + return i + case error: + return []byte(i.Error()) + case bool: + if i { + return []byte("true") + } + return []byte("false") + } + return []byte(fmt.Sprintf("%#v", raw)) +} + +func Int64ToBytes(i int64) []byte { + var buf = make([]byte, 8) + binary.BigEndian.PutUint64(buf, uint64(i)) + return buf +} + +func BytesToInt64(buf []byte) int64 { + return int64(binary.BigEndian.Uint64(buf)) +} + +func StrToInt(s string) int { + res, _ := strconv.Atoi(s) + return res +} + +func StrToInt64(s string) int64 { + res, _ := strconv.ParseInt(s, 10, 64) + return res +} + +func Float32ToByte(float float32) []byte { + bits := math.Float32bits(float) + bytes := make([]byte, 4) + binary.LittleEndian.PutUint32(bytes, bits) + + return bytes +} + +func ByteToFloat32(bytes []byte) float32 { + bits := binary.LittleEndian.Uint32(bytes) + return math.Float32frombits(bits) +} + +func Float64ToByte(float float64) []byte { + bits := math.Float64bits(float) + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, bits) + return bytes +} + +func ByteToFloat64(bytes []byte) float64 { + bits := binary.LittleEndian.Uint64(bytes) + return math.Float64frombits(bits) +} + +func Float64ToStr(f float64) string { + return strconv.FormatFloat(f, 'f', 2, 64) +} +func Float64ToStrPrec1(f float64) string { + return strconv.FormatFloat(f, 'f', 1, 64) +} + +func Float64ToStrPrec4(f float64) string { + return strconv.FormatFloat(f, 'f', 4, 64) +} +func Float64ToStrPrec10(f float64) string { + return strconv.FormatFloat(f, 'f', 10, 64) +} +func Float64ToStrPrec8(f float64) string { + return strconv.FormatFloat(f, 'f', 8, 64) +} + +func Float64ToStrPrec6(f float64) string { + return strconv.FormatFloat(f, 'f', 6, 64) +} + +func Float64ToStrByPrec(f float64, prec int) string { + return strconv.FormatFloat(f, 'f', prec, 64) +} + +func Float32ToStr(f float32) string { + return Float64ToStr(float64(f)) +} + +func StrToFloat64(s string) float64 { + res, err := strconv.ParseFloat(s, 64) + if err != nil { + return 0 + } + return res +} +func StrToFormat(s string, prec int) string { + ex := strings.Split(s, ".") + if len(ex) == 2 { + if StrToFloat64(ex[1]) == 0 { //小数点后面为空就是不要小数点了 + return ex[0] + } + //看取多少位 + str := ex[1] + str1 := str + if prec < len(str) { + str1 = str[0:prec] + } else { + for i := 0; i < prec-len(str); i++ { + str1 += "0" + } + } + if prec > 0 { + return ex[0] + "." + str1 + } else { + return ex[0] + } + } + return s +} + +func StrToFloat32(s string) float32 { + res, err := strconv.ParseFloat(s, 32) + if err != nil { + return 0 + } + return float32(res) +} + +func StrToBool(s string) bool { + b, _ := strconv.ParseBool(s) + return b +} + +func BoolToStr(b bool) string { + if b { + return "true" + } + return "false" +} + +func FloatToInt64(f float64) int64 { + return int64(f) +} + +func IntToStr(i int) string { + return strconv.Itoa(i) +} + +func Int64ToStr(i int64) string { + return strconv.FormatInt(i, 10) +} + +func IntToFloat64(i int) float64 { + s := strconv.Itoa(i) + res, err := strconv.ParseFloat(s, 64) + if err != nil { + return 0 + } + return res +} +func Int64ToFloat64(i int64) float64 { + s := strconv.FormatInt(i, 10) + res, err := strconv.ParseFloat(s, 64) + if err != nil { + return 0 + } + return res +} diff --git a/utils/curl.go b/utils/curl.go new file mode 100644 index 0000000..a50c9e0 --- /dev/null +++ b/utils/curl.go @@ -0,0 +1,209 @@ +package egg_system_rules + +import ( + "bytes" + "crypto/tls" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "sort" + "strings" + "time" +) + +var CurlDebug bool + +func CurlGet(router string, header map[string]string) ([]byte, error) { + return curl(http.MethodGet, router, nil, header) +} +func CurlGetJson(router string, body interface{}, header map[string]string) ([]byte, error) { + return curl_new(http.MethodGet, router, body, header) +} + +// 只支持form 与json 提交, 请留意body的类型, 支持string, []byte, map[string]string +func CurlPost(router string, body interface{}, header map[string]string) ([]byte, error) { + return curl(http.MethodPost, router, body, header) +} + +func CurlPut(router string, body interface{}, header map[string]string) ([]byte, error) { + return curl(http.MethodPut, router, body, header) +} + +// 只支持form 与json 提交, 请留意body的类型, 支持string, []byte, map[string]string +func CurlPatch(router string, body interface{}, header map[string]string) ([]byte, error) { + return curl(http.MethodPatch, router, body, header) +} + +// CurlDelete is curl delete +func CurlDelete(router string, body interface{}, header map[string]string) ([]byte, error) { + return curl(http.MethodDelete, router, body, header) +} + +func curl(method, router string, body interface{}, header map[string]string) ([]byte, error) { + var reqBody io.Reader + contentType := "application/json" + switch v := body.(type) { + case string: + reqBody = strings.NewReader(v) + case []byte: + reqBody = bytes.NewReader(v) + case map[string]string: + val := url.Values{} + for k, v := range v { + val.Set(k, v) + } + reqBody = strings.NewReader(val.Encode()) + contentType = "application/x-www-form-urlencoded" + case map[string]interface{}: + val := url.Values{} + for k, v := range v { + val.Set(k, v.(string)) + } + reqBody = strings.NewReader(val.Encode()) + contentType = "application/x-www-form-urlencoded" + } + if header == nil { + header = map[string]string{"Content-Type": contentType} + } + if _, ok := header["Content-Type"]; !ok { + header["Content-Type"] = contentType + } + resp, er := CurlReq(method, router, reqBody, header) + if er != nil { + return nil, er + } + res, err := ioutil.ReadAll(resp.Body) + if CurlDebug { + blob := SerializeStr(body) + if contentType != "application/json" { + blob = HttpBuild(body) + } + fmt.Printf("\n\n=====================\n[url]: %s\n[time]: %s\n[method]: %s\n[content-type]: %v\n[req_header]: %s\n[req_body]: %#v\n[resp_err]: %v\n[resp_header]: %v\n[resp_body]: %v\n=====================\n\n", + router, + time.Now().Format("2006-01-02 15:04:05.000"), + method, + contentType, + HttpBuildQuery(header), + blob, + err, + SerializeStr(resp.Header), + string(res), + ) + } + resp.Body.Close() + return res, err +} + +func curl_new(method, router string, body interface{}, header map[string]string) ([]byte, error) { + var reqBody io.Reader + contentType := "application/json" + + if header == nil { + header = map[string]string{"Content-Type": contentType} + } + if _, ok := header["Content-Type"]; !ok { + header["Content-Type"] = contentType + } + resp, er := CurlReq(method, router, reqBody, header) + if er != nil { + return nil, er + } + res, err := ioutil.ReadAll(resp.Body) + if CurlDebug { + blob := SerializeStr(body) + if contentType != "application/json" { + blob = HttpBuild(body) + } + fmt.Printf("\n\n=====================\n[url]: %s\n[time]: %s\n[method]: %s\n[content-type]: %v\n[req_header]: %s\n[req_body]: %#v\n[resp_err]: %v\n[resp_header]: %v\n[resp_body]: %v\n=====================\n\n", + router, + time.Now().Format("2006-01-02 15:04:05.000"), + method, + contentType, + HttpBuildQuery(header), + blob, + err, + SerializeStr(resp.Header), + string(res), + ) + } + resp.Body.Close() + return res, err +} + +func CurlReq(method, router string, reqBody io.Reader, header map[string]string) (*http.Response, error) { + req, _ := http.NewRequest(method, router, reqBody) + if header != nil { + for k, v := range header { + req.Header.Set(k, v) + } + } + // 绕过github等可能因为特征码返回503问题 + // https://www.imwzk.com/posts/2021-03-14-why-i-always-get-503-with-golang/ + defaultCipherSuites := []uint16{0xc02f, 0xc030, 0xc02b, 0xc02c, 0xcca8, 0xcca9, 0xc013, 0xc009, + 0xc014, 0xc00a, 0x009c, 0x009d, 0x002f, 0x0035, 0xc012, 0x000a} + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + CipherSuites: append(defaultCipherSuites[8:], defaultCipherSuites[:8]...), + }, + }, + // 获取301重定向 + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + return client.Do(req) +} + +// 组建get请求参数,sortAsc true为小到大,false为大到小,nil不排序 a=123&b=321 +func HttpBuildQuery(args map[string]string, sortAsc ...bool) string { + str := "" + if len(args) == 0 { + return str + } + if len(sortAsc) > 0 { + keys := make([]string, 0, len(args)) + for k := range args { + keys = append(keys, k) + } + if sortAsc[0] { + sort.Strings(keys) + } else { + sort.Sort(sort.Reverse(sort.StringSlice(keys))) + } + for _, k := range keys { + str += "&" + k + "=" + args[k] + } + } else { + for k, v := range args { + str += "&" + k + "=" + v + } + } + return str[1:] +} + +func HttpBuild(body interface{}, sortAsc ...bool) string { + params := map[string]string{} + if args, ok := body.(map[string]interface{}); ok { + for k, v := range args { + params[k] = AnyToString(v) + } + return HttpBuildQuery(params, sortAsc...) + } + if args, ok := body.(map[string]string); ok { + for k, v := range args { + params[k] = AnyToString(v) + } + return HttpBuildQuery(params, sortAsc...) + } + if args, ok := body.(map[string]int); ok { + for k, v := range args { + params[k] = AnyToString(v) + } + return HttpBuildQuery(params, sortAsc...) + } + return AnyToString(body) +} diff --git a/utils/file.go b/utils/file.go new file mode 100644 index 0000000..b30fc2f --- /dev/null +++ b/utils/file.go @@ -0,0 +1,22 @@ +package egg_system_rules + +import ( + "os" + "path" + "strings" + "time" +) + +// 获取文件后缀 +func FileExt(fname string) string { + return strings.ToLower(strings.TrimLeft(path.Ext(fname), ".")) +} + +func FilePutContents(fileName string, content string) { + fd, _ := os.OpenFile("./tmp/"+fileName+".logs", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) + fd_time := time.Now().Format("2006-01-02 15:04:05") + fd_content := strings.Join([]string{"[", fd_time, "] ", content, "\n"}, "") + buf := []byte(fd_content) + fd.Write(buf) + fd.Close() +} diff --git a/utils/format.go b/utils/format.go new file mode 100644 index 0000000..706888c --- /dev/null +++ b/utils/format.go @@ -0,0 +1,59 @@ +package egg_system_rules + +import ( + "math" +) + +func CouponFormat(data string) string { + switch data { + case "0.00", "0", "": + return "" + default: + return Int64ToStr(FloatToInt64(StrToFloat64(data))) + } +} +func CommissionFormat(data string) string { + if data != "" && data != "0" { + return data + } + + return "" +} + +func HideString(src string, hLen int) string { + str := []rune(src) + if hLen == 0 { + hLen = 4 + } + hideStr := "" + for i := 0; i < hLen; i++ { + hideStr += "*" + } + hideLen := len(str) / 2 + showLen := len(str) - hideLen + if hideLen == 0 || showLen == 0 { + return hideStr + } + subLen := showLen / 2 + if subLen == 0 { + return string(str[:showLen]) + hideStr + } + s := string(str[:subLen]) + s += hideStr + s += string(str[len(str)-subLen:]) + return s +} + +// SaleCountFormat is 格式化销量 +func SaleCountFormat(s string) string { + return s + "已售" +} + +// 小数格式化 +func FloatFormat(f float64, i int) float64 { + if i > 14 { + return f + } + p := math.Pow10(i) + return float64(int64((f+0.000000000000009)*p)) / p +} diff --git a/utils/logx/log.go b/utils/logx/log.go new file mode 100644 index 0000000..586e4ab --- /dev/null +++ b/utils/logx/log.go @@ -0,0 +1,245 @@ +package zhios_order_relate_logx + +import ( + "os" + "strings" + "time" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type LogConfig struct { + AppName string `yaml:"app_name" json:"app_name" toml:"app_name"` + Level string `yaml:"level" json:"level" toml:"level"` + StacktraceLevel string `yaml:"stacktrace_level" json:"stacktrace_level" toml:"stacktrace_level"` + IsStdOut bool `yaml:"is_stdout" json:"is_stdout" toml:"is_stdout"` + TimeFormat string `yaml:"time_format" json:"time_format" toml:"time_format"` // second, milli, nano, standard, iso, + Encoding string `yaml:"encoding" json:"encoding" toml:"encoding"` // console, json + Skip int `yaml:"skip" json:"skip" toml:"skip"` + + IsFileOut bool `yaml:"is_file_out" json:"is_file_out" toml:"is_file_out"` + FileDir string `yaml:"file_dir" json:"file_dir" toml:"file_dir"` + FileName string `yaml:"file_name" json:"file_name" toml:"file_name"` + FileMaxSize int `yaml:"file_max_size" json:"file_max_size" toml:"file_max_size"` + FileMaxAge int `yaml:"file_max_age" json:"file_max_age" toml:"file_max_age"` +} + +var ( + l *LogX = defaultLogger() + conf *LogConfig +) + +// default logger setting +func defaultLogger() *LogX { + conf = &LogConfig{ + Level: "debug", + StacktraceLevel: "error", + IsStdOut: true, + TimeFormat: "standard", + Encoding: "console", + Skip: 2, + } + writers := []zapcore.WriteSyncer{os.Stdout} + lg, lv := newZapLogger(setLogLevel(conf.Level), setLogLevel(conf.StacktraceLevel), conf.Encoding, conf.TimeFormat, conf.Skip, zapcore.NewMultiWriteSyncer(writers...)) + zap.RedirectStdLog(lg) + return &LogX{logger: lg, atomLevel: lv} +} + +// initial standard log, if you don't init, it will use default logger setting +func InitDefaultLogger(cfg *LogConfig) { + var writers []zapcore.WriteSyncer + if cfg.IsStdOut || (!cfg.IsStdOut && !cfg.IsFileOut) { + writers = append(writers, os.Stdout) + } + if cfg.IsFileOut { + writers = append(writers, NewRollingFile(cfg.FileDir, cfg.FileName, cfg.FileMaxSize, cfg.FileMaxAge)) + } + + lg, lv := newZapLogger(setLogLevel(cfg.Level), setLogLevel(cfg.StacktraceLevel), cfg.Encoding, cfg.TimeFormat, cfg.Skip, zapcore.NewMultiWriteSyncer(writers...)) + zap.RedirectStdLog(lg) + if cfg.AppName != "" { + lg = lg.With(zap.String("app", cfg.AppName)) // 加上应用名称 + } + l = &LogX{logger: lg, atomLevel: lv} +} + +// create a new logger +func NewLogger(cfg *LogConfig) *LogX { + var writers []zapcore.WriteSyncer + if cfg.IsStdOut || (!cfg.IsStdOut && !cfg.IsFileOut) { + writers = append(writers, os.Stdout) + } + if cfg.IsFileOut { + writers = append(writers, NewRollingFile(cfg.FileDir, cfg.FileName, cfg.FileMaxSize, cfg.FileMaxAge)) + } + + lg, lv := newZapLogger(setLogLevel(cfg.Level), setLogLevel(cfg.StacktraceLevel), cfg.Encoding, cfg.TimeFormat, cfg.Skip, zapcore.NewMultiWriteSyncer(writers...)) + zap.RedirectStdLog(lg) + if cfg.AppName != "" { + lg = lg.With(zap.String("app", cfg.AppName)) // 加上应用名称 + } + return &LogX{logger: lg, atomLevel: lv} +} + +// create a new zaplog logger +func newZapLogger(level, stacktrace zapcore.Level, encoding, timeType string, skip int, output zapcore.WriteSyncer) (*zap.Logger, *zap.AtomicLevel) { + encCfg := zapcore.EncoderConfig{ + TimeKey: "T", + LevelKey: "L", + NameKey: "N", + CallerKey: "C", + MessageKey: "M", + StacktraceKey: "S", + LineEnding: zapcore.DefaultLineEnding, + EncodeCaller: zapcore.ShortCallerEncoder, + EncodeDuration: zapcore.NanosDurationEncoder, + EncodeLevel: zapcore.LowercaseLevelEncoder, + } + setTimeFormat(timeType, &encCfg) // set time type + atmLvl := zap.NewAtomicLevel() // set level + atmLvl.SetLevel(level) + encoder := zapcore.NewJSONEncoder(encCfg) // 确定encoder格式 + if encoding == "console" { + encoder = zapcore.NewConsoleEncoder(encCfg) + } + return zap.New(zapcore.NewCore(encoder, output, atmLvl), zap.AddCaller(), zap.AddStacktrace(stacktrace), zap.AddCallerSkip(skip)), &atmLvl +} + +// set log level +func setLogLevel(lvl string) zapcore.Level { + switch strings.ToLower(lvl) { + case "panic": + return zapcore.PanicLevel + case "fatal": + return zapcore.FatalLevel + case "error": + return zapcore.ErrorLevel + case "warn", "warning": + return zapcore.WarnLevel + case "info": + return zapcore.InfoLevel + default: + return zapcore.DebugLevel + } +} + +// set time format +func setTimeFormat(timeType string, z *zapcore.EncoderConfig) { + switch strings.ToLower(timeType) { + case "iso": // iso8601 standard + z.EncodeTime = zapcore.ISO8601TimeEncoder + case "sec": // only for unix second, without millisecond + z.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendInt64(t.Unix()) + } + case "second": // unix second, with millisecond + z.EncodeTime = zapcore.EpochTimeEncoder + case "milli", "millisecond": // millisecond + z.EncodeTime = zapcore.EpochMillisTimeEncoder + case "nano", "nanosecond": // nanosecond + z.EncodeTime = zapcore.EpochNanosTimeEncoder + default: // standard format + z.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(t.Format("2006-01-02 15:04:05.000")) + } + } +} + +func GetLevel() string { + switch l.atomLevel.Level() { + case zapcore.PanicLevel: + return "panic" + case zapcore.FatalLevel: + return "fatal" + case zapcore.ErrorLevel: + return "error" + case zapcore.WarnLevel: + return "warn" + case zapcore.InfoLevel: + return "info" + default: + return "debug" + } +} + +func SetLevel(lvl string) { + l.atomLevel.SetLevel(setLogLevel(lvl)) +} + +// temporary add call skip +func AddCallerSkip(skip int) *LogX { + l.logger.WithOptions(zap.AddCallerSkip(skip)) + return l +} + +// permanent add call skip +func AddDepth(skip int) *LogX { + l.logger = l.logger.WithOptions(zap.AddCallerSkip(skip)) + return l +} + +// permanent add options +func AddOptions(opts ...zap.Option) *LogX { + l.logger = l.logger.WithOptions(opts...) + return l +} + +func AddField(k string, v interface{}) { + l.logger.With(zap.Any(k, v)) +} + +func AddFields(fields map[string]interface{}) *LogX { + for k, v := range fields { + l.logger.With(zap.Any(k, v)) + } + return l +} + +// Normal log +func Debug(e interface{}, args ...interface{}) error { + return l.Debug(e, args...) +} +func Info(e interface{}, args ...interface{}) error { + return l.Info(e, args...) +} +func Warn(e interface{}, args ...interface{}) error { + return l.Warn(e, args...) +} +func Error(e interface{}, args ...interface{}) error { + return l.Error(e, args...) +} +func Panic(e interface{}, args ...interface{}) error { + return l.Panic(e, args...) +} +func Fatal(e interface{}, args ...interface{}) error { + return l.Fatal(e, args...) +} + +// Format logs +func Debugf(format string, args ...interface{}) error { + return l.Debugf(format, args...) +} +func Infof(format string, args ...interface{}) error { + return l.Infof(format, args...) +} +func Warnf(format string, args ...interface{}) error { + return l.Warnf(format, args...) +} +func Errorf(format string, args ...interface{}) error { + return l.Errorf(format, args...) +} +func Panicf(format string, args ...interface{}) error { + return l.Panicf(format, args...) +} +func Fatalf(format string, args ...interface{}) error { + return l.Fatalf(format, args...) +} + +func formatFieldMap(m FieldMap) []Field { + var res []Field + for k, v := range m { + res = append(res, zap.Any(k, v)) + } + return res +} diff --git a/utils/logx/output.go b/utils/logx/output.go new file mode 100644 index 0000000..1f41c04 --- /dev/null +++ b/utils/logx/output.go @@ -0,0 +1,105 @@ +package zhios_order_relate_logx + +import ( + "bytes" + "io" + "os" + "path/filepath" + "time" + + "gopkg.in/natefinch/lumberjack.v2" +) + +// output interface +type WriteSyncer interface { + io.Writer + Sync() error +} + +// split writer +func NewRollingFile(dir, filename string, maxSize, MaxAge int) WriteSyncer { + s, err := os.Stat(dir) + if err != nil || !s.IsDir() { + os.RemoveAll(dir) + if err := os.MkdirAll(dir, 0766); err != nil { + panic(err) + } + } + return newLumberjackWriteSyncer(&lumberjack.Logger{ + Filename: filepath.Join(dir, filename), + MaxSize: maxSize, // megabytes, MB + MaxAge: MaxAge, // days + LocalTime: true, + Compress: false, + }) +} + +type lumberjackWriteSyncer struct { + *lumberjack.Logger + buf *bytes.Buffer + logChan chan []byte + closeChan chan interface{} + maxSize int +} + +func newLumberjackWriteSyncer(l *lumberjack.Logger) *lumberjackWriteSyncer { + ws := &lumberjackWriteSyncer{ + Logger: l, + buf: bytes.NewBuffer([]byte{}), + logChan: make(chan []byte, 5000), + closeChan: make(chan interface{}), + maxSize: 1024, + } + go ws.run() + return ws +} + +func (l *lumberjackWriteSyncer) run() { + ticker := time.NewTicker(1 * time.Second) + + for { + select { + case <-ticker.C: + if l.buf.Len() > 0 { + l.sync() + } + case bs := <-l.logChan: + _, err := l.buf.Write(bs) + if err != nil { + continue + } + if l.buf.Len() > l.maxSize { + l.sync() + } + case <-l.closeChan: + l.sync() + return + } + } +} + +func (l *lumberjackWriteSyncer) Stop() { + close(l.closeChan) +} + +func (l *lumberjackWriteSyncer) Write(bs []byte) (int, error) { + b := make([]byte, len(bs)) + for i, c := range bs { + b[i] = c + } + l.logChan <- b + return 0, nil +} + +func (l *lumberjackWriteSyncer) Sync() error { + return nil +} + +func (l *lumberjackWriteSyncer) sync() error { + defer l.buf.Reset() + _, err := l.Logger.Write(l.buf.Bytes()) + if err != nil { + return err + } + return nil +} diff --git a/utils/logx/sugar.go b/utils/logx/sugar.go new file mode 100644 index 0000000..5beae07 --- /dev/null +++ b/utils/logx/sugar.go @@ -0,0 +1,192 @@ +package zhios_order_relate_logx + +import ( + "errors" + "fmt" + "strconv" + + "go.uber.org/zap" +) + +type LogX struct { + logger *zap.Logger + atomLevel *zap.AtomicLevel +} + +type Field = zap.Field +type FieldMap map[string]interface{} + +// 判断其他类型--start +func getFields(msg string, format bool, args ...interface{}) (string, []Field) { + var str []interface{} + var fields []zap.Field + if len(args) > 0 { + for _, v := range args { + if f, ok := v.(Field); ok { + fields = append(fields, f) + } else if f, ok := v.(FieldMap); ok { + fields = append(fields, formatFieldMap(f)...) + } else { + str = append(str, AnyToString(v)) + } + } + if format { + return fmt.Sprintf(msg, str...), fields + } + str = append([]interface{}{msg}, str...) + return fmt.Sprintln(str...), fields + } + return msg, []Field{} +} + +func (l *LogX) Debug(s interface{}, args ...interface{}) error { + es, e := checkErr(s) + if es != "" { + msg, field := getFields(es, false, args...) + l.logger.Debug(msg, field...) + } + return e +} +func (l *LogX) Info(s interface{}, args ...interface{}) error { + es, e := checkErr(s) + if es != "" { + msg, field := getFields(es, false, args...) + l.logger.Info(msg, field...) + } + return e +} +func (l *LogX) Warn(s interface{}, args ...interface{}) error { + es, e := checkErr(s) + if es != "" { + msg, field := getFields(es, false, args...) + l.logger.Warn(msg, field...) + } + return e +} +func (l *LogX) Error(s interface{}, args ...interface{}) error { + es, e := checkErr(s) + if es != "" { + msg, field := getFields(es, false, args...) + l.logger.Error(msg, field...) + } + return e +} +func (l *LogX) DPanic(s interface{}, args ...interface{}) error { + es, e := checkErr(s) + if es != "" { + msg, field := getFields(es, false, args...) + l.logger.DPanic(msg, field...) + } + return e +} +func (l *LogX) Panic(s interface{}, args ...interface{}) error { + es, e := checkErr(s) + if es != "" { + msg, field := getFields(es, false, args...) + l.logger.Panic(msg, field...) + } + return e +} +func (l *LogX) Fatal(s interface{}, args ...interface{}) error { + es, e := checkErr(s) + if es != "" { + msg, field := getFields(es, false, args...) + l.logger.Fatal(msg, field...) + } + return e +} + +func checkErr(s interface{}) (string, error) { + switch e := s.(type) { + case error: + return e.Error(), e + case string: + return e, errors.New(e) + case []byte: + return string(e), nil + default: + return "", nil + } +} + +func (l *LogX) LogError(err error) error { + return l.Error(err.Error()) +} + +func (l *LogX) Debugf(msg string, args ...interface{}) error { + s, f := getFields(msg, true, args...) + l.logger.Debug(s, f...) + return errors.New(s) +} + +func (l *LogX) Infof(msg string, args ...interface{}) error { + s, f := getFields(msg, true, args...) + l.logger.Info(s, f...) + return errors.New(s) +} + +func (l *LogX) Warnf(msg string, args ...interface{}) error { + s, f := getFields(msg, true, args...) + l.logger.Warn(s, f...) + return errors.New(s) +} + +func (l *LogX) Errorf(msg string, args ...interface{}) error { + s, f := getFields(msg, true, args...) + l.logger.Error(s, f...) + return errors.New(s) +} + +func (l *LogX) DPanicf(msg string, args ...interface{}) error { + s, f := getFields(msg, true, args...) + l.logger.DPanic(s, f...) + return errors.New(s) +} + +func (l *LogX) Panicf(msg string, args ...interface{}) error { + s, f := getFields(msg, true, args...) + l.logger.Panic(s, f...) + return errors.New(s) +} + +func (l *LogX) Fatalf(msg string, args ...interface{}) error { + s, f := getFields(msg, true, args...) + l.logger.Fatal(s, f...) + return errors.New(s) +} + +func AnyToString(raw interface{}) string { + switch i := raw.(type) { + case []byte: + return string(i) + case int: + return strconv.FormatInt(int64(i), 10) + case int64: + return strconv.FormatInt(i, 10) + case float32: + return strconv.FormatFloat(float64(i), 'f', 2, 64) + case float64: + return strconv.FormatFloat(i, 'f', 2, 64) + case uint: + return strconv.FormatInt(int64(i), 10) + case uint8: + return strconv.FormatInt(int64(i), 10) + case uint16: + return strconv.FormatInt(int64(i), 10) + case uint32: + return strconv.FormatInt(int64(i), 10) + case uint64: + return strconv.FormatInt(int64(i), 10) + case int8: + return strconv.FormatInt(int64(i), 10) + case int16: + return strconv.FormatInt(int64(i), 10) + case int32: + return strconv.FormatInt(int64(i), 10) + case string: + return i + case error: + return i.Error() + } + return fmt.Sprintf("%#v", raw) +} diff --git a/utils/serialize.go b/utils/serialize.go new file mode 100644 index 0000000..cbf0171 --- /dev/null +++ b/utils/serialize.go @@ -0,0 +1,23 @@ +package egg_system_rules + +import ( + "encoding/json" +) + +func Serialize(data interface{}) []byte { + res, err := json.Marshal(data) + if err != nil { + return []byte{} + } + return res +} + +func Unserialize(b []byte, dst interface{}) { + if err := json.Unmarshal(b, dst); err != nil { + dst = nil + } +} + +func SerializeStr(data interface{}, arg ...interface{}) string { + return string(Serialize(data)) +} diff --git a/utils/string.go b/utils/string.go new file mode 100644 index 0000000..d0a8f73 --- /dev/null +++ b/utils/string.go @@ -0,0 +1,203 @@ +package egg_system_rules + +import ( + "fmt" + "github.com/syyongx/php2go" + "math/rand" + "reflect" + "sort" + "strings" + "time" +) + +func Implode(glue string, args ...interface{}) string { + data := make([]string, len(args)) + for i, s := range args { + data[i] = fmt.Sprint(s) + } + return strings.Join(data, glue) +} + +// 字符串是否在数组里 +func InArr(target string, str_array []string) bool { + for _, element := range str_array { + if target == element { + return true + } + } + return false +} +func InArrToInt(target int, str_array []int) bool { + for _, element := range str_array { + if target == element { + return true + } + } + return false +} + +// 把数组的值放到key里 +func ArrayColumn(array interface{}, key string) (result map[string]interface{}, err error) { + result = make(map[string]interface{}) + t := reflect.TypeOf(array) + v := reflect.ValueOf(array) + if t.Kind() != reflect.Slice { + return nil, nil + } + if v.Len() == 0 { + return nil, nil + } + for i := 0; i < v.Len(); i++ { + indexv := v.Index(i) + if indexv.Type().Kind() != reflect.Struct { + return nil, nil + } + mapKeyInterface := indexv.FieldByName(key) + if mapKeyInterface.Kind() == reflect.Invalid { + return nil, nil + } + mapKeyString, err := InterfaceToString(mapKeyInterface.Interface()) + if err != nil { + return nil, err + } + result[mapKeyString] = indexv.Interface() + } + return result, err +} + +// 转string +func InterfaceToString(v interface{}) (result string, err error) { + switch reflect.TypeOf(v).Kind() { + case reflect.Int64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32: + result = fmt.Sprintf("%v", v) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + result = fmt.Sprintf("%v", v) + case reflect.String: + result = v.(string) + default: + err = nil + } + return result, err +} + +func HideTrueName(name string) string { + res := "**" + if name != "" { + runs := []rune(name) + leng := len(runs) + if leng <= 3 { + res = string(runs[0:1]) + res + } else if leng < 5 { + res = string(runs[0:2]) + res + } else if leng < 10 { + res = string(runs[0:2]) + "***" + string(runs[leng-2:leng]) + } else if leng < 16 { + res = string(runs[0:3]) + "****" + string(runs[leng-3:leng]) + } else { + res = string(runs[0:4]) + "*****" + string(runs[leng-4:leng]) + } + } + return res +} + +func GetQueryParam(uri string) map[string]string { + //根据问号分割路由还是query参数 + uriList := strings.Split(uri, "?") + var query = make(map[string]string, 0) + //有参数才处理 + if len(uriList) == 2 { + //分割query参数 + var queryList = strings.Split(uriList[1], "&") + if len(queryList) > 0 { + //key value 分别赋值 + for _, v := range queryList { + var valueList = strings.Split(v, "=") + if len(valueList) == 2 { + value, _ := php2go.URLDecode(valueList[1]) + if value == "" { + value = valueList[1] + } + query[valueList[0]] = value + } + } + } + } + return query +} + +// JoinStringsInASCII 按照规则,参数名ASCII码从小到大排序后拼接 +// data 待拼接的数据 +// sep 连接符 +// onlyValues 是否只包含参数值,true则不包含参数名,否则参数名和参数值均有 +// includeEmpty 是否包含空值,true则包含空值,否则不包含,注意此参数不影响参数名的存在 +// exceptKeys 被排除的参数名,不参与排序及拼接 +func JoinStringsInASCII(data map[string]string, sep string, onlyValues, includeEmpty bool, exceptKeys ...string) string { + var list []string + var keyList []string + m := make(map[string]int) + if len(exceptKeys) > 0 { + for _, except := range exceptKeys { + m[except] = 1 + } + } + for k := range data { + if _, ok := m[k]; ok { + continue + } + value := data[k] + if !includeEmpty && value == "" { + continue + } + if onlyValues { + keyList = append(keyList, k) + } else { + list = append(list, fmt.Sprintf("%s=%s", k, value)) + } + } + if onlyValues { + sort.Strings(keyList) + for _, v := range keyList { + list = append(list, AnyToString(data[v])) + } + } else { + sort.Strings(list) + } + return strings.Join(list, sep) +} + +// x的y次方 +func RandPow(l int) string { + var i = "1" + for j := 0; j < l; j++ { + i += "0" + } + k := StrToInt64(i) + n := rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(k) + ls := "%0" + IntToStr(l) + "v" + str := fmt.Sprintf(ls, n) + //min := int(math.Pow10(l - 1)) + //max := int(math.Pow10(l) - 1) + return str +} + +// 根据显示长度截取字符串 +func ShowSubstr(s string, l int) string { + if len(s) <= l { + return s + } + ss, sl, rl, rs := "", 0, 0, []rune(s) + for _, r := range rs { + rint := int(r) + if rint < 128 { + rl = 1 + } else { + rl = 2 + } + if sl+rl > l { + break + } + sl += rl + ss += string(r) + } + return ss +} diff --git a/utils/time.go b/utils/time.go new file mode 100644 index 0000000..5b9b739 --- /dev/null +++ b/utils/time.go @@ -0,0 +1,295 @@ +package egg_system_rules + +import ( + "errors" + "fmt" + "strconv" + "strings" + "time" +) + +func StrToTime(s string) (int64, error) { + // delete all not int characters + if s == "" { + return time.Now().Unix(), nil + } + r := make([]rune, 14) + l := 0 + // 过滤除数字以外的字符 + for _, v := range s { + if '0' <= v && v <= '9' { + r[l] = v + l++ + if l == 14 { + break + } + } + } + for l < 14 { + r[l] = '0' // 补0 + l++ + } + t, err := time.Parse("20060102150405", string(r)) + if err != nil { + return 0, err + } + return t.Unix(), nil +} +func String2TimeV2(timeStr string) time.Time { + toTime, err := time.ParseInLocation("2006-01-02", timeStr, time.Local) + if err != nil { + return time.Now() + } + return toTime +} +func StringToTime(timeStr string) (time.Time, error) { + toTime, err := time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local) + return toTime, err +} +func TimeToStr(unixSecTime interface{}, layout ...string) string { + i := AnyToInt64(unixSecTime) + if i == 0 { + return "" + } + f := "2006-01-02 15:04:05" + if len(layout) > 0 { + f = layout[0] + } + return time.Unix(i, 0).Format(f) +} + +func FormatNanoUnix() string { + return strings.Replace(time.Now().Format("20060102150405.0000000"), ".", "", 1) +} + +func TimeParse(format, src string) (time.Time, error) { + return time.ParseInLocation(format, src, time.Local) +} + +func TimeParseStd(src string) time.Time { + t, _ := TimeParse("2006-01-02 15:04:05", src) + return t +} + +func TimeStdParseUnix(src string) int64 { + t, err := TimeParse("2006-01-02 15:04:05", src) + if err != nil { + return 0 + } + return t.Unix() +} +func TimeStdParseUnixDate(src string) int64 { + t, err := TimeParse("2006-01-02", src) + if err != nil { + return 0 + } + return t.Unix() +} + +// 获取一个当前时间 时间间隔 时间戳 +func GetTimeInterval(unit string, amount int) (startTime, endTime int64) { + t := time.Now() + nowTime := t.Unix() + tmpTime := int64(0) + switch unit { + case "years": + tmpTime = time.Date(t.Year()+amount, t.Month(), t.Day(), t.Hour(), 0, 0, 0, t.Location()).Unix() + case "months": + tmpTime = time.Date(t.Year(), t.Month()+time.Month(amount), t.Day(), t.Hour(), 0, 0, 0, t.Location()).Unix() + case "days": + tmpTime = time.Date(t.Year(), t.Month(), t.Day()+amount, t.Hour(), 0, 0, 0, t.Location()).Unix() + case "hours": + tmpTime = time.Date(t.Year(), t.Month(), t.Day(), t.Hour()+amount, 0, 0, 0, t.Location()).Unix() + } + if amount > 0 { + startTime = nowTime + endTime = tmpTime + } else { + startTime = tmpTime + endTime = nowTime + } + return +} + +// 几天前 +func TimeInterval(newTime int) string { + now := time.Now().Unix() + newTime64 := AnyToInt64(newTime) + if newTime64 >= now { + return "刚刚" + } + interval := now - newTime64 + switch { + case interval < 60: + return AnyToString(interval) + "秒前" + case interval < 60*60: + return AnyToString(interval/60) + "分前" + case interval < 60*60*24: + return AnyToString(interval/60/60) + "小时前" + case interval < 60*60*24*30: + return AnyToString(interval/60/60/24) + "天前" + case interval < 60*60*24*30*12: + return AnyToString(interval/60/60/24/30) + "月前" + default: + return AnyToString(interval/60/60/24/30/12) + "年前" + } +} + +// 时分秒字符串转时间戳,传入示例:8:40 or 8:40:10 +func HmsToUnix(str string) (int64, error) { + t := time.Now() + arr := strings.Split(str, ":") + if len(arr) < 3 { + return 0, errors.New("Time format error") + } + h, _ := strconv.Atoi(arr[0]) + m, _ := strconv.Atoi(arr[1]) + s := 0 + if len(arr) == 3 { + s, _ = strconv.Atoi(arr[2]) + } + formatted1 := fmt.Sprintf("%d%02d%02d%02d%02d%02d", t.Year(), t.Month(), t.Day(), h, m, s) + res, err := time.ParseInLocation("20060102150405", formatted1, time.Local) + if err != nil { + return 0, err + } else { + return res.Unix(), nil + } +} + +// 获取特定时间范围 +func GetTimeRange(s string) map[string]int64 { + t := time.Now() + var stime, etime time.Time + + switch s { + case "today": + stime = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) + etime = time.Date(t.Year(), t.Month(), t.Day()+1, 0, 0, 0, 0, t.Location()) + case "yesterday": + stime = time.Date(t.Year(), t.Month(), t.Day()-1, 0, 0, 0, 0, t.Location()) + etime = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) + case "within_seven_days": + // 前6天0点 + stime = time.Date(t.Year(), t.Month(), t.Day()-6, 0, 0, 0, 0, t.Location()) + // 明天 0点 + etime = time.Date(t.Year(), t.Month(), t.Day()+1, 0, 0, 0, 0, t.Location()) + case "current_month": + stime = time.Date(t.Year(), t.Month(), 0, 0, 0, 0, 0, t.Location()) + etime = time.Date(t.Year(), t.Month()+1, 0, 0, 0, 0, 0, t.Location()) + case "last_month": + stime = time.Date(t.Year(), t.Month()-1, 0, 0, 0, 0, 0, t.Location()) + etime = time.Date(t.Year(), t.Month(), 0, 0, 0, 0, 0, t.Location()) + } + + return map[string]int64{ + "start": stime.Unix(), + "end": etime.Unix(), + } +} + +// 获取特定时间范围 +func GetTimes(s string) map[string]string { + t := time.Now() + var stime, etime time.Time + + switch s { + case "today": + stime = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) + etime = time.Date(t.Year(), t.Month(), t.Day()+1, 0, 0, 0, 0, t.Location()) + case "yesterday": + stime = time.Date(t.Year(), t.Month(), t.Day()-1, 0, 0, 0, 0, t.Location()) + etime = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) + case "within_seven_days": + // 前6天0点 + stime = time.Date(t.Year(), t.Month(), t.Day()-6, 0, 0, 0, 0, t.Location()) + // 明天 0点 + etime = time.Date(t.Year(), t.Month(), t.Day()+1, 0, 0, 0, 0, t.Location()) + case "current_month": + stime = time.Date(t.Year(), t.Month(), 0, 0, 0, 0, 0, t.Location()) + etime = time.Date(t.Year(), t.Month()+1, 0, 0, 0, 0, 0, t.Location()) + case "last_month": + stime = time.Date(t.Year(), t.Month()-1, 0, 0, 0, 0, 0, t.Location()) + etime = time.Date(t.Year(), t.Month(), 0, 0, 0, 0, 0, t.Location()) + } + + return map[string]string{ + "start": stime.Format("2006-01-02 15:04:05"), + "end": etime.Format("2006-01-02 15:04:05"), + } +} + +// 获取传入的时间所在月份的第一天,即某月第一天的0点。如传入time.Now(), 返回当前月份的第一天0点时间。 +func GetFirstDateOfMonth(d time.Time) time.Time { + d = d.AddDate(0, 0, -d.Day()+1) + return GetZeroTime(d) +} + +// 获取传入的时间所在月份的最后一天,即某月最后一天的0点。如传入time.Now(), 返回当前月份的最后一天0点时间。 +func GetLastDateOfMonth(d time.Time) time.Time { + return GetFirstDateOfMonth(d).AddDate(0, 1, -1) +} +func GetAnyFirstDateOfMonth(d time.Time, monthDiff int) time.Time { + year, month, _ := d.Date() + thisMonth := time.Date(year, month, 1, 0, 0, 0, 0, time.Local) + monthOneDay := thisMonth.AddDate(0, monthDiff, 0) + return monthOneDay +} + +// 当天时间戳 +func GetDateTime(date string) (int64, int64) { + //获取当前时区 + loc, _ := time.LoadLocation("Local") + + //日期当天0点时间戳(拼接字符串) + startDate := date + "_00:00:00" + startTime, _ := time.ParseInLocation("2006-01-02_15:04:05", startDate, loc) + + //日期当天23时59分时间戳 + endDate := date + "_23:59:59" + end, _ := time.ParseInLocation("2006-01-02_15:04:05", endDate, loc) + + //返回当天0点和23点59分的时间戳 + return startTime.Unix(), end.Unix() +} + +// 获取某一天的0点时间 +func GetZeroTime(d time.Time) time.Time { + return time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, d.Location()) +} + +// getYearMonthToDay 查询指定年份指定月份有多少天 +// @params year int 指定年份 +// @params month int 指定月份 +func GetYearMonthToDay(year int, month int) int { + // 有31天的月份 + day31 := map[int]bool{ + 1: true, + 3: true, + 5: true, + 7: true, + 8: true, + 10: true, + 12: true, + } + if day31[month] == true { + return 31 + } + // 有30天的月份 + day30 := map[int]bool{ + 4: true, + 6: true, + 9: true, + 11: true, + } + if day30[month] == true { + return 30 + } + // 计算是平年还是闰年 + if (year%4 == 0 && year%100 != 0) || year%400 == 0 { + // 得出2月的天数 + return 29 + } + // 得出2月的天数 + return 28 +} diff --git a/utils/time2s.go b/utils/time2s.go new file mode 100644 index 0000000..ecaa601 --- /dev/null +++ b/utils/time2s.go @@ -0,0 +1,29 @@ +package egg_system_rules + +import "time" + +func Time2String(date time.Time, format string) string { + if format == "" { + format = "2006-01-02 15:04:05" + } + timeS := date.Format(format) + if timeS == "0001-01-01 00:00:00" { + return "" + } + return timeS +} +func String2Time(timeStr string) time.Time { + toTime, err := time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local) + if err != nil { + return time.Now() + } + return toTime +} + +// GetDiffDays 获取两个时间相差的天数,0表同一天,正数表t1>t2,负数表t1 2 || kind < 0 + rand.Seed(time.Now().UnixNano()) + for i := 0; i < size; i++ { + if isAll { // random ikind + ikind = rand.Intn(3) + } + scope, base := kinds[ikind][0], kinds[ikind][1] + result[i] = uint8(base + rand.Intn(scope)) + } + return result +}