Browse Source

temp push

tags/v0.0.1
dengbiao 1 month ago
commit
624934897e
48 changed files with 8508 additions and 0 deletions
  1. +8
    -0
      .idea/.gitignore
  2. +8
    -0
      .idea/modules.xml
  3. +9
    -0
      .idea/system_rules.iml
  4. +6
    -0
      .idea/vcs.xml
  5. +35
    -0
      enum/user_virtual_amount_flow.go
  6. +14
    -0
      go.mod
  7. +670
    -0
      go.sum
  8. +14
    -0
      init.go
  9. +13
    -0
      md/app_redis_key.go
  10. +41
    -0
      md/public_platoon.go
  11. +14
    -0
      md/user_virtual_coin.go
  12. +58
    -0
      rule/egg_energy/activity_coin_auto_exchange_egg_person_energy.go
  13. +58
    -0
      rule/egg_energy/activity_coin_auto_exchange_egg_team_energy.go
  14. +129
    -0
      rule/egg_energy/activity_coin_ready_exchange_egg_energy.go
  15. +51
    -0
      rule/egg_energy/activity_coin_start_exchange_egg_person_energy.go
  16. +51
    -0
      rule/egg_energy/activity_coin_start_exchange_egg_team_energy.go
  17. +109
    -0
      rule/egg_energy/auto_adjust_prices.go
  18. +116
    -0
      rule/egg_energy/auto_release_green_energy.go
  19. +51
    -0
      rule/egg_energy/available_green_energy_settlement.go
  20. +106
    -0
      rule/egg_energy/community_dividends.go
  21. +493
    -0
      rule/egg_energy/deal_available_green_energy_points.go
  22. +41
    -0
      rule/egg_energy/enum/available_egg_energy_points_flow.go
  23. +244
    -0
      rule/egg_energy/give_activty_coin.go
  24. +276
    -0
      rule/egg_energy/green_energy_settlement.go
  25. +99
    -0
      rule/egg_energy/md/egg_energy_green_energy.go
  26. +53
    -0
      rule/egg_energy/md/mq.go
  27. +304
    -0
      rule/egg_energy/star_level_dividends.go
  28. +101
    -0
      rule/egg_energy/svc/svc_egg_energy_core_data.go
  29. +909
    -0
      rule/public_platoon_user_relation_commission.go
  30. +138
    -0
      rule/user_virtual_coin.go
  31. +84
    -0
      svc/svc_user_virtual_amount_redis_mutex_lock.go
  32. +421
    -0
      utils/cache/base.go
  33. +413
    -0
      utils/cache/redis.go
  34. +622
    -0
      utils/cache/redis_cluster.go
  35. +324
    -0
      utils/cache/redis_pool.go
  36. +617
    -0
      utils/cache/redis_pool_cluster.go
  37. +388
    -0
      utils/convert.go
  38. +209
    -0
      utils/curl.go
  39. +22
    -0
      utils/file.go
  40. +59
    -0
      utils/format.go
  41. +245
    -0
      utils/logx/log.go
  42. +105
    -0
      utils/logx/output.go
  43. +192
    -0
      utils/logx/sugar.go
  44. +23
    -0
      utils/serialize.go
  45. +203
    -0
      utils/string.go
  46. +295
    -0
      utils/time.go
  47. +29
    -0
      utils/time2s.go
  48. +38
    -0
      utils/uuid.go

+ 8
- 0
.idea/.gitignore View File

@@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

+ 8
- 0
.idea/modules.xml View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/system_rules.iml" filepath="$PROJECT_DIR$/.idea/system_rules.iml" />
</modules>
</component>
</project>

+ 9
- 0
.idea/system_rules.iml View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

+ 6
- 0
.idea/vcs.xml View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

+ 35
- 0
enum/user_virtual_amount_flow.go View File

@@ -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 "未知状态"
}
}

+ 14
- 0
go.mod View File

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

+ 670
- 0
go.sum View File

@@ -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=

+ 14
- 0
init.go View File

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

+ 13
- 0
md/app_redis_key.go View File

@@ -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" // 用户虚拟币更新锁(能拿到锁才能更新余额)

)

+ 41
- 0
md/public_platoon.go View File

@@ -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"`
}

+ 14
- 0
md/user_virtual_coin.go View File

@@ -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"`
}

+ 58
- 0
rule/egg_energy/activity_coin_auto_exchange_egg_person_energy.go View File

@@ -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()
}

+ 58
- 0
rule/egg_energy/activity_coin_auto_exchange_egg_team_energy.go View File

@@ -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()
}

+ 129
- 0
rule/egg_energy/activity_coin_ready_exchange_egg_energy.go View File

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

+ 51
- 0
rule/egg_energy/activity_coin_start_exchange_egg_person_energy.go View File

@@ -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()
}

+ 51
- 0
rule/egg_energy/activity_coin_start_exchange_egg_team_energy.go View File

@@ -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()
}

+ 109
- 0
rule/egg_energy/auto_adjust_prices.go View File

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

+ 116
- 0
rule/egg_energy/auto_release_green_energy.go View File

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

+ 51
- 0
rule/egg_energy/available_green_energy_settlement.go View File

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

+ 106
- 0
rule/egg_energy/community_dividends.go View File

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

+ 493
- 0
rule/egg_energy/deal_available_green_energy_points.go View File

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

+ 41
- 0
rule/egg_energy/enum/available_egg_energy_points_flow.go View File

@@ -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 "未知状态"
}
}

+ 244
- 0
rule/egg_energy/give_activty_coin.go View File

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

+ 276
- 0
rule/egg_energy/green_energy_settlement.go View File

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

+ 99
- 0
rule/egg_energy/md/egg_energy_green_energy.go View File

@@ -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"` //变更后-现行总量
}

+ 53
- 0
rule/egg_energy/md/mq.go View File

@@ -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"` //截止兑换时间
}

+ 304
- 0
rule/egg_energy/star_level_dividends.go View File

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

+ 101
- 0
rule/egg_energy/svc/svc_egg_energy_core_data.go View File

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

+ 909
- 0
rule/public_platoon_user_relation_commission.go View File

@@ -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), &times)
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
}

+ 138
- 0
rule/user_virtual_coin.go View File

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

+ 84
- 0
svc/svc_user_virtual_amount_redis_mutex_lock.go View File

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

+ 421
- 0
utils/cache/base.go View File

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

+ 413
- 0
utils/cache/redis.go View File

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

+ 622
- 0
utils/cache/redis_cluster.go View File

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

+ 324
- 0
utils/cache/redis_pool.go View File

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

+ 617
- 0
utils/cache/redis_pool_cluster.go View File

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

+ 388
- 0
utils/convert.go View File

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

+ 209
- 0
utils/curl.go View File

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

+ 22
- 0
utils/file.go View File

@@ -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()
}

+ 59
- 0
utils/format.go View File

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

+ 245
- 0
utils/logx/log.go View File

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

+ 105
- 0
utils/logx/output.go View File

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

+ 192
- 0
utils/logx/sugar.go View File

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

+ 23
- 0
utils/serialize.go View File

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

+ 203
- 0
utils/string.go View File

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

+ 295
- 0
utils/time.go View File

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

+ 29
- 0
utils/time2s.go View File

@@ -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<t2
func GetDiffDays(t1, t2 time.Time) int {
t1 = time.Date(t1.Year(), t1.Month(), t1.Day(), 0, 0, 0, 0, time.Local)
t2 = time.Date(t2.Year(), t2.Month(), t2.Day(), 0, 0, 0, 0, time.Local)

return int(t1.Sub(t2).Hours() / 24)
}

+ 38
- 0
utils/uuid.go View File

@@ -0,0 +1,38 @@
package egg_system_rules

import (
"math/rand"
"time"
)

const (
KC_RAND_KIND_NUM = 0 // 纯数字
KC_RAND_KIND_LOWER = 1 // 小写字母
KC_RAND_KIND_UPPER = 2 // 大写字母
KC_RAND_KIND_ALL = 3 // 数字、大小写字母
)

// OrderUUID is only num for uuid
func OrderUUID(uid int) string {
ustr := IntToStr(uid)
tstr := Int64ToStr(time.Now().Unix())
ulen := len(ustr)
tlen := len(tstr)
rlen := 18 - ulen - tlen
krb := Krand(rlen, KC_RAND_KIND_NUM)
return ustr + tstr + string(krb)
}

func Krand(size int, kind int) []byte {
ikind, kinds, result := kind, [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}, make([]byte, size)
isAll := kind > 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
}

Loading…
Cancel
Save