From 74aa6cbd3d12c09abe99870c36a315f61c58352c Mon Sep 17 00:00:00 2001 From: dayunzhangyunfeng Date: Sun, 17 Dec 2023 20:09:52 +0800 Subject: [PATCH 01/12] sync with inner version --- .gitignore | 1 + tg-core/go.mod | 99 ++++-- tg-core/go.sum | 295 +++++++++++++----- tg-core/model/strategy_context.go | 8 +- tg-core/wfengine/engine.go | 174 ++++------- ...ow_apollo_dao.go => engine_from_apollo.go} | 106 ++++--- ...{workflow_store.go => engine_from_file.go} | 0 ...e_module_store.go => engine_from_redis.go} | 82 +++-- tg-core/wfengine/module_base.go | 27 +- tg-core/wfengine/workflow.go | 257 ++++++++------- tg-core/wfengine/workflow_dao.go | 46 --- 11 files changed, 659 insertions(+), 436 deletions(-) rename tg-core/wfengine/{workflow_apollo_dao.go => engine_from_apollo.go} (40%) rename tg-core/wfengine/{workflow_store.go => engine_from_file.go} (100%) rename tg-core/wfengine/{scene_module_store.go => engine_from_redis.go} (35%) delete mode 100644 tg-core/wfengine/workflow_dao.go diff --git a/.gitignore b/.gitignore index f20ecee..f3d19ac 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ rebuild.sh tg-flow log/ app.conf +tg-example diff --git a/tg-core/go.mod b/tg-core/go.mod index a68ba4e..1bc6ac6 100644 --- a/tg-core/go.mod +++ b/tg-core/go.mod @@ -1,36 +1,89 @@ module github.com/didi/tg-flow/tg-core -go 1.14 +go 1.21.5 require ( - git.apache.org/thrift.git v0.0.0-20151001171628-53dd39833a08 - git.xiaojukeji.com/foundation/thrift v0.9.3 - git.xiaojukeji.com/gobiz/cache v1.0.2 - git.xiaojukeji.com/gobiz/config v0.0.7 + git.xiaojukeji.com/gobiz/config v0.0.10 git.xiaojukeji.com/gobiz/logger v1.4.7 git.xiaojukeji.com/gobiz/utils v1.0.1 - git.xiaojukeji.com/lego/context-go v3.0.4+incompatible - git.xiaojukeji.com/lego/dirpc-go v1.16.24 - git.xiaojukeji.com/nuwa/go-monitor v1.1.5 - git.xiaojukeji.com/nuwa/golibs/redis v0.4.14 - git.xiaojukeji.com/nuwa/nuwa-go-httpclient v1.3.14 - git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2 v2.8.11 + git.xiaojukeji.com/lego/context-go v3.3.0+incompatible + git.xiaojukeji.com/nuwa/go-monitor v1.1.6 + git.xiaojukeji.com/nuwa/golibs/redis v0.5.10 + git.xiaojukeji.com/nuwa/nuwa-go-httpclient v1.3.19 + git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2 v2.8.14 + github.com/coocood/freecache v1.2.4 + github.com/didi/gendry v1.8.2 + github.com/go-sql-driver/mysql v1.7.1 + github.com/robfig/cron v1.2.0 + go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2 v2.7.7+incompatible + go.intra.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20230228024253-24fcac901643 + go.intra.xiaojukeji.com/platform-ha/onekey-degrade_sdk_go v3.2.9+incompatible +) + +require ( + git.xiaojukeji.com/devops/statsd v0.2.1 // indirect + git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.53 // indirect + git.xiaojukeji.com/foundation/thrift v0.9.3 // indirect + git.xiaojukeji.com/gobiz/elapsed v0.0.2 // indirect + git.xiaojukeji.com/lego/common-go v0.1.24 // indirect + git.xiaojukeji.com/lego/dirpc-go v1.24.2 // indirect + git.xiaojukeji.com/lego/ruleEngine v1.0.0 // indirect + git.xiaojukeji.com/lego/sentinel-golang v1.1.3 // indirect + git.xiaojukeji.com/lego/thrift16 v1.0.1 // indirect + git.xiaojukeji.com/nuwa/binding v0.1.4 // indirect + git.xiaojukeji.com/nuwa/golibs/discover v0.0.5 // indirect + git.xiaojukeji.com/nuwa/golibs/httprouter v0.0.11 // indirect + git.xiaojukeji.com/nuwa/golibs/metrics v0.2.4 // indirect + git.xiaojukeji.com/nuwa/golibs/redigo v1.8.9 // indirect + git.xiaojukeji.com/nuwa/gorequest v1.0.5 // indirect + git.xiaojukeji.com/nuwa/trace v1.3.12 // indirect git.xiaojukeji.com/nuwa/trace/v2 v2.0.7 // indirect - github.com/coocood/freecache v1.1.1 - github.com/didi/gendry v1.7.0 + git.xiaojukeji.com/observe-trace/didi-open-telemetry/opentelemetry-go v1.0.0 // indirect + git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.2.0 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/caio/go-tdigest v3.1.0+incompatible // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 // indirect + github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434 // indirect + github.com/facebookgo/httpdown v0.0.0-20180706035922-5979d39b15c2 // indirect github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect + github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4 // indirect github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect - github.com/go-sql-driver/mysql v1.6.0 - github.com/golang/protobuf v1.5.2 - github.com/gomodule/redigo/redis v0.0.1 // indirect - github.com/robfig/cron v1.2.0 - go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2 v2.7.7+incompatible - go.intra.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20201123053335-e67edadc52bf - go.intra.xiaojukeji.com/platform-ha/onekey-degrade_sdk_go v3.2.8+incompatible - golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect - golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect - google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/parnurzeal/gorequest v0.2.16 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/shirou/gopsutil/v3 v3.23.11 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/tklauser/go-sysconf v0.3.13 // indirect + github.com/tklauser/numcpus v0.7.0 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.9.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect + google.golang.org/grpc v1.60.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect + gopkg.in/go-playground/validator.v8 v8.18.2 // indirect + gopkg.in/validator.v2 v2.0.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + moul.io/http2curl v1.0.1-0.20190925090545-5cd742060b0e // indirect ) diff --git a/tg-core/go.sum b/tg-core/go.sum index 2ba3b34..a666edf 100644 --- a/tg-core/go.sum +++ b/tg-core/go.sum @@ -1,13 +1,14 @@ -bou.ke/monkey v1.0.2 h1:kWcnsrCNUatbxncxR/ThdYqbytgOIArtYWqcQLQzKLI= bou.ke/monkey v1.0.2/go.mod h1:OqickVX3tNx6t33n1xvtTtu85YN5s6cKwVug+oHMaIA= 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= -git.apache.org/thrift.git v0.0.0-20151001171628-53dd39833a08 h1:goxS3HZARSCj217iYEtwVahA/7WJ9MbZR2QJMeMDmxM= -git.apache.org/thrift.git v0.0.0-20151001171628-53dd39833a08/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +git.xiaojukeji.com/devops/statsd v0.0.5/go.mod h1:VwNi66bpKq2QSqq8xYHEhl1QVkaKWRqH9nXUPbRnMhM= git.xiaojukeji.com/devops/statsd v0.0.10/go.mod h1:VwNi66bpKq2QSqq8xYHEhl1QVkaKWRqH9nXUPbRnMhM= git.xiaojukeji.com/devops/statsd v0.0.13 h1:sdZPslY/C6bVEHQuqomgVNkbuuAfQfM4Zwsa0gwXA2c= git.xiaojukeji.com/devops/statsd v0.0.13/go.mod h1:F9Q3cQhEZpMV+4iElWE6Oth6CW0Rk3bxMy6UbQhiHMA= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.36 h1:UxBWzR9+7VLrpQWXgin1efxcP+vHf0/U/8Msbov1NDA= +git.xiaojukeji.com/devops/statsd v0.2.1 h1:JsqNCvP6qNwBAvhK98JcvBf2mfxmtNgzPhloZkpkyRg= +git.xiaojukeji.com/devops/statsd v0.2.1/go.mod h1:F9Q3cQhEZpMV+4iElWE6Oth6CW0Rk3bxMy6UbQhiHMA= git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.36/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi5XlD96ZFqTAuG4EKCiv0= git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.37/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi5XlD96ZFqTAuG4EKCiv0= git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.39/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi5XlD96ZFqTAuG4EKCiv0= @@ -15,20 +16,24 @@ git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.40-0.20210819030951-70dcfc268e9a/go. git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.40/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi5XlD96ZFqTAuG4EKCiv0= git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.43 h1:xqvDUnCIcZlgqgIYc0ZFkEDIJ6JPLVQG5UREXg0S/P4= git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.43/go.mod h1:KVTUFxHUPxQVrzedCj1/3/u8IeWQM8Ka5aC2neD8TWY= +git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.44/go.mod h1:KVTUFxHUPxQVrzedCj1/3/u8IeWQM8Ka5aC2neD8TWY= +git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.47/go.mod h1:KVTUFxHUPxQVrzedCj1/3/u8IeWQM8Ka5aC2neD8TWY= +git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.48/go.mod h1:Czv16HGVcZWhv4YzONQuFlGu4WBaOWJ22U0F/JFp21M= +git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.53 h1:nGd+7jQXOrSTeIM3vdgxclvD6kNZWKdAyFdic8caI1A= +git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.53/go.mod h1:Czv16HGVcZWhv4YzONQuFlGu4WBaOWJ22U0F/JFp21M= +git.xiaojukeji.com/disfv4/commlib v0.0.0-20230515071326-a183b2265970/go.mod h1:hhyhpVcle1TfEdE9h9GVTq89Z6Tiu1jPl2fF2Jwyklg= git.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20201123053335-e67edadc52bf h1:IqH2bM38ZLmza3beTh2c4Ay/CfVN2ILjNJt4cXuXy2g= git.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20201123053335-e67edadc52bf/go.mod h1:BL9PrFeJAMNJrTbAKJA4dCVUUw4GWE6QuWSwFjxPm4k= git.xiaojukeji.com/foundation/thrift v0.9.3 h1:LCcExsZL5T/bTnGhVyNHfsjs5+MucWm+eCFa6R/Iq+A= git.xiaojukeji.com/foundation/thrift v0.9.3/go.mod h1:mr0sUXUW5iUveD+BQlkx2kam22mYnzc9plIAhQkg+1U= -git.xiaojukeji.com/gobiz/cache v1.0.2 h1:J7zmMPrQkgbuouVYyXub8AX53+ONSU14bj6ed2V5qxk= -git.xiaojukeji.com/gobiz/cache v1.0.2/go.mod h1:kTCNYF32G1nXdAR3DpgXxK6isKCh4iluWmemDnZmg2Y= -git.xiaojukeji.com/gobiz/config v0.0.7 h1:f96cMLEjIKrh/Hmy5xPN+ccHZhmLV992l0OGsFfqD9I= git.xiaojukeji.com/gobiz/config v0.0.7/go.mod h1:FJEpa4infv+1bQ09dywzUWAVtjejzzWWL9968tPVzt8= +git.xiaojukeji.com/gobiz/config v0.0.10 h1:QB9JgGl/cGN4OTK9gsj3Xkmu6pmU4LU8iHx6sD9iYvU= +git.xiaojukeji.com/gobiz/config v0.0.10/go.mod h1:TJd2sFc9JFf01ZEnZIx/2sAlvlfzOUuKlB7rYF1xp7k= git.xiaojukeji.com/gobiz/elapsed v0.0.2 h1:JARCp5+9pU+FzOiNE4y6zkax+J+hkNxpucS38oSnTKI= git.xiaojukeji.com/gobiz/elapsed v0.0.2/go.mod h1:GGQzLBmcvN0WOQ91cNqvg8Lg/+Z2Hrd6empMYH+lY2Q= git.xiaojukeji.com/gobiz/logger v1.2.28/go.mod h1:wcP4mP7epj0D2er88Try58mCZB7goPf4Cjykd6YkWKc= git.xiaojukeji.com/gobiz/logger v1.3.3/go.mod h1:RFNcCXNy3+Rz+TkKQZvi7g3PKT/EyG3sxW+Jr7d+pG8= git.xiaojukeji.com/gobiz/logger v1.3.4/go.mod h1:qI83N+0IDFBYtja87v+ah3js4ANg+Jpl8/8QzVFTSjc= -git.xiaojukeji.com/gobiz/logger v1.4.4 h1:ZTet5AoL6RLx9yHDCtHVnDKhXzTi+BNFzlY+4p7/gik= git.xiaojukeji.com/gobiz/logger v1.4.4/go.mod h1:qI83N+0IDFBYtja87v+ah3js4ANg+Jpl8/8QzVFTSjc= git.xiaojukeji.com/gobiz/logger v1.4.5/go.mod h1:qI83N+0IDFBYtja87v+ah3js4ANg+Jpl8/8QzVFTSjc= git.xiaojukeji.com/gobiz/logger v1.4.7 h1:CwOQTMimodav3l+Fh6WmKOrX1XHYDbIZAqOjy8Stcfk= @@ -37,15 +42,18 @@ git.xiaojukeji.com/gobiz/utils v0.0.7/go.mod h1:myE5vDJLUH3ME5Rc7ebg+7QSVusHABrH git.xiaojukeji.com/gobiz/utils v1.0.1 h1:BftvQ8VdFC1KqE5gfkJ08XiM5DWmMyCbBNrrS2eGvhw= git.xiaojukeji.com/gobiz/utils v1.0.1/go.mod h1:myE5vDJLUH3ME5Rc7ebg+7QSVusHABrHwbgYxE8YCWI= git.xiaojukeji.com/golang/go-tdigest v0.0.0-20160413204300-577940117044/go.mod h1:C7L62J5e+T2mAUXpgJck6EakqV3aMEz4UAzfN7Br2ak= -git.xiaojukeji.com/lego/common-go v0.1.19 h1:2lNnuHj62ZhwpqpHQ7c7TfOfPrqjSJYJo7EFrONb0pg= git.xiaojukeji.com/lego/common-go v0.1.19/go.mod h1:6KXHn+oe36kXyiImOGwjSCoVT7lrkRUqm/OjWClCVhQ= git.xiaojukeji.com/lego/common-go v0.1.22 h1:y9/AJdigi4eljGC3ZSLhOSb0annaCKKqml6pu3N56E4= git.xiaojukeji.com/lego/common-go v0.1.22/go.mod h1:0E8emkc2GJPf2zSclkMMCTcrZkEHX00EuDxxXvSE5JM= +git.xiaojukeji.com/lego/common-go v0.1.24 h1:OisrYkP2DejFYdA8A8Q6LD4iCXU06oLB69SmrLDNSKY= +git.xiaojukeji.com/lego/common-go v0.1.24/go.mod h1:0E8emkc2GJPf2zSclkMMCTcrZkEHX00EuDxxXvSE5JM= git.xiaojukeji.com/lego/context-go v3.0.1+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= git.xiaojukeji.com/lego/context-go v3.0.3-0.20210204053446-cc0c77017173+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= -git.xiaojukeji.com/lego/context-go v3.0.3+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= -git.xiaojukeji.com/lego/context-go v3.0.4+incompatible h1:J0RE54vG7heU4LlrLQx+oAmRoaEe+Uki1EoRs8xcMOc= git.xiaojukeji.com/lego/context-go v3.0.4+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= +git.xiaojukeji.com/lego/context-go v3.2.1+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= +git.xiaojukeji.com/lego/context-go v3.2.4+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= +git.xiaojukeji.com/lego/context-go v3.3.0+incompatible h1:FuagXoU771fadZagGj+wBtUiIm+V9TBTSb6TxzXTEzU= +git.xiaojukeji.com/lego/context-go v3.3.0+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= git.xiaojukeji.com/lego/dirpc-go v1.16.9/go.mod h1:HbVRjEnrfpYTM2nwqqTwJaKZwmfRyEsVa8cjBscaeh4= git.xiaojukeji.com/lego/dirpc-go v1.16.11-0.20210816074537-6e2a7ab987c1/go.mod h1:EfOBTBplIfu8ua3WpX1OT621kaeHO+lErjngU9xvF8U= git.xiaojukeji.com/lego/dirpc-go v1.16.11-0.20210819033055-b145eb63fb00/go.mod h1:IY4Eps2zn266XXqnvM6q4mbqjJByNjwRmDVAt/CFXtQ= @@ -53,30 +61,38 @@ git.xiaojukeji.com/lego/dirpc-go v1.16.11-0.20210820022224-2f84346236de/go.mod h git.xiaojukeji.com/lego/dirpc-go v1.16.13/go.mod h1:GFpAKh+xbcCp3eKTbSKODHHRh6FbuzKlFr/Rnotftqs= git.xiaojukeji.com/lego/dirpc-go v1.16.24 h1:TnbnxvVy6Y11i8trSNkyZfv4rMzezQGoVtT12glqM8M= git.xiaojukeji.com/lego/dirpc-go v1.16.24/go.mod h1:rfAMEZLwY9h96yvgIC4/dFN/gHZaAfpVBdjkqiqYXpc= +git.xiaojukeji.com/lego/dirpc-go v1.17.1-0.20220929063334-53711c3febc5/go.mod h1:XK/k7QyF2tmZJ0kmZzh5t5z3XKvCUsweDC2mgasdr40= +git.xiaojukeji.com/lego/dirpc-go v1.22.4/go.mod h1:JQ1uPSpsIJcRKsFkjnA/DDmcxqRiXn3JB7vda7cvO38= +git.xiaojukeji.com/lego/dirpc-go v1.24.2 h1:EPtA7YMj7/C9NmdN/x7Cj01P8bMv2mH7fSCoqOa832c= +git.xiaojukeji.com/lego/dirpc-go v1.24.2/go.mod h1:Pe3JJiqY6PkaI37hykbWve4nPI2hHDJnf0v0RJXONH8= git.xiaojukeji.com/lego/ruleEngine v1.0.0 h1:bLaRaf44ckhFe+vLKcfGYu7gu8+DJh8xJUw8Z95CQ7A= git.xiaojukeji.com/lego/ruleEngine v1.0.0/go.mod h1:RRcCo5URt7nhBMQcpEnI780n0rlpeND4B+gVKczuV7U= git.xiaojukeji.com/lego/sentinel-golang v1.1.3 h1:0Am+H/M5vHA09CFr2XQSvNpbEA2Txp+E+y0TFlnkeqA= git.xiaojukeji.com/lego/sentinel-golang v1.1.3/go.mod h1:zAu2UQPlhAnXSt8ZjQMqG6Z2KMo8jyytmh3SVwXJii4= +git.xiaojukeji.com/lego/thrift16 v1.0.1 h1:N3DNsT+ZR4lbGMT4o//ReA6c7Rlufrs9HhtdMrMbyS0= +git.xiaojukeji.com/lego/thrift16 v1.0.1/go.mod h1:t8gPK22SMa8dXww/qpmsTLUfMLhUYTBoFxFN3lVi8GA= git.xiaojukeji.com/nuwa/binding v0.1.2 h1:PE4l6gZIYTnuUHe/SXJQxJL3T124vay6gtvHgwLOfps= git.xiaojukeji.com/nuwa/binding v0.1.2/go.mod h1:yRWzl8Cjj62nCxibZvXKbLeDTcO1dToXlu0f2xHaHms= -git.xiaojukeji.com/nuwa/go-monitor v1.1.5 h1:3YNQJ3IKn5sRA81A1z9xBmFGwJV1BllFv7ziJTcuJSU= -git.xiaojukeji.com/nuwa/go-monitor v1.1.5/go.mod h1:C0m8zJlL59dpoiQSR8T1RqLHOLB2MeMnI8o2YYFRgkg= +git.xiaojukeji.com/nuwa/binding v0.1.4 h1:8hdqh8T3sjDmesB1a/xhtPLkOLraGeuAMhciQc+uwzg= +git.xiaojukeji.com/nuwa/binding v0.1.4/go.mod h1:yRWzl8Cjj62nCxibZvXKbLeDTcO1dToXlu0f2xHaHms= +git.xiaojukeji.com/nuwa/go-monitor v1.1.6 h1:TZZMcIskQiJZLW8pIAd/Ox0pMTooDAb1olH9MsigBAE= +git.xiaojukeji.com/nuwa/go-monitor v1.1.6/go.mod h1:C0m8zJlL59dpoiQSR8T1RqLHOLB2MeMnI8o2YYFRgkg= git.xiaojukeji.com/nuwa/golibs/discover v0.0.5 h1:yflPBzKHJ2IcwWobmf8E3J1Bay1YZliXRWQmLc+N6Qw= git.xiaojukeji.com/nuwa/golibs/discover v0.0.5/go.mod h1:7nTcaUccQIpjHWfmocU6Htny8j7vpPfSAfo1cGPjiDg= git.xiaojukeji.com/nuwa/golibs/httprouter v0.0.11 h1:5PpQzsYLDIMGgSwTq9BjaMdHEBgGOf468BaFVmKRWyc= git.xiaojukeji.com/nuwa/golibs/httprouter v0.0.11/go.mod h1:LpU0X+hTVxQ6yY8vEg+FrmpaB7DAUPxuFShbZaj8yYs= git.xiaojukeji.com/nuwa/golibs/metrics v0.2.4 h1:ZQdZxSBiOvLqTzgie780B4LQZPyBRw37f0JNT65pe34= git.xiaojukeji.com/nuwa/golibs/metrics v0.2.4/go.mod h1:qy3ra5x3CM8zPCDdGjAvqGv9P1amYXTCf6cv4C7rWVI= -git.xiaojukeji.com/nuwa/golibs/redigo v1.8.8 h1:6VkPRL5/PfmCiiPqxyAp8O+q2d7MwEUUq8MwvvtfS/U= -git.xiaojukeji.com/nuwa/golibs/redigo v1.8.8/go.mod h1:AceIcnRHXupwGQ6IF4HL/iTi/tLxcBOPQfIHGlhpKmk= -git.xiaojukeji.com/nuwa/golibs/redis v0.4.14 h1:YTW07Y558d9EmR0PVpOVNrBUfbtTFhPIR1A1dEIpJYs= -git.xiaojukeji.com/nuwa/golibs/redis v0.4.14/go.mod h1:Wjd1J4OBY9Ay2uDRE67SO3y5v8dg3IYC1Poa8ls3wT8= -git.xiaojukeji.com/nuwa/gorequest v1.0.3 h1:sIICDH0TYPK8XMELrhMjXBt15j61kwpxpi3gIiS7tJ0= -git.xiaojukeji.com/nuwa/gorequest v1.0.3/go.mod h1:ritycdzL+I1Dipfd7P3dzS5lTU+lbEPBFga0bj0aMcI= -git.xiaojukeji.com/nuwa/nuwa-go-httpclient v1.3.14 h1:fFf+2im576SS7p+t4OB5eDFBB5C1pxwNoyVCy/qIA+I= -git.xiaojukeji.com/nuwa/nuwa-go-httpclient v1.3.14/go.mod h1:URmom/bcADz5m0hFmNIRpxKosi80imYuva3w8tzEGEM= -git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2 v2.8.11 h1:lUBwsPyvom6CHnG5Vy6Rrd2eB6vrLSGyd3NzPLXdeQ4= -git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2 v2.8.11/go.mod h1:WJI5y1Dy+6y8BClx9SYzUXETVwmRR7sTFbrmyVGCOlA= +git.xiaojukeji.com/nuwa/golibs/redigo v1.8.9 h1:DvRrJIzI8ix+hsmYY6h31Zo9Wi8FhXt7HhnU2kupKFk= +git.xiaojukeji.com/nuwa/golibs/redigo v1.8.9/go.mod h1:+2yBHoZLPIIauBdl+B3lFypf7luJpDFDz8VbS+wNnU8= +git.xiaojukeji.com/nuwa/golibs/redis v0.5.10 h1:Ak6E66X3xhHxmfRdwBUGc4c6g2g92Gk5omGn9eHJ6RY= +git.xiaojukeji.com/nuwa/golibs/redis v0.5.10/go.mod h1:1n+f9NrLYc18zapwbrIJDslXyyT+IHhu8ewmN2kXcls= +git.xiaojukeji.com/nuwa/gorequest v1.0.5 h1:zqxlREsrHebH/mJAStgHSRZJ3L11/i4DnLQQCJqmxII= +git.xiaojukeji.com/nuwa/gorequest v1.0.5/go.mod h1:ritycdzL+I1Dipfd7P3dzS5lTU+lbEPBFga0bj0aMcI= +git.xiaojukeji.com/nuwa/nuwa-go-httpclient v1.3.19 h1:U9WSMg6enWv3lMEdF17lm3CtDkEhMNH8xbKdl0Jo8zk= +git.xiaojukeji.com/nuwa/nuwa-go-httpclient v1.3.19/go.mod h1:ClQRt6GLEkY5yrBCLHEk607f5R0hSFi3btkqkVC3kTM= +git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2 v2.8.14 h1:k8Pfn3ziPh0WKFf8jNjOqPg4tDou/AMMDqn4+BBM6OU= +git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2 v2.8.14/go.mod h1:WJI5y1Dy+6y8BClx9SYzUXETVwmRR7sTFbrmyVGCOlA= git.xiaojukeji.com/nuwa/protoc-dirpc v1.2.0/go.mod h1:gzfVNNsbUshc1LPU4FIWSVeRwqL2arLuc3o/G785hME= git.xiaojukeji.com/nuwa/trace v1.3.12 h1:15K0Y+PG3p1lSCpwdhRlQM/mN7NwravpeY/l+rmqu0g= git.xiaojukeji.com/nuwa/trace v1.3.12/go.mod h1:8Og3kmWTAaXezXM4Aq3HWe75sMY+d3jA77nmyn4c9DA= @@ -84,25 +100,38 @@ git.xiaojukeji.com/nuwa/trace/v2 v2.0.3 h1:40KrHWzHMiGzBPSYPhwXoXKL1Ifni9SnPV4IS git.xiaojukeji.com/nuwa/trace/v2 v2.0.3/go.mod h1:Mlg8+AlIxlUh7l0Vc+sVhx8Ef6G9Q0UKTURUCV8rC6g= git.xiaojukeji.com/nuwa/trace/v2 v2.0.7 h1:4SmofQ5PUxn43JozBVYc5qA3s5JvWByqaZgkMehk0JU= git.xiaojukeji.com/nuwa/trace/v2 v2.0.7/go.mod h1:bj9wOA2DCLwBG1Dv5dl+zD4n3EikxolGooXPzCyJWP4= +git.xiaojukeji.com/observe-trace/didi-open-telemetry/opentelemetry-go v1.0.0 h1:35o8w+wEoBWzll5advs81JvN66wUpHaYLP1bR3VsSvE= +git.xiaojukeji.com/observe-trace/didi-open-telemetry/opentelemetry-go v1.0.0/go.mod h1:ky1/wTDa+siozh9jqNJRnB/CAxZlZm4EYw325++3yo8= git.xiaojukeji.com/ops-sec/disf_kms_sdk_go v0.0.0-20190107121632-c61f10c1e31c/go.mod h1:iIi/pKbE7lHEEoae1JMXQFKljtvF7Fadc030GjKDB3E= -git.xiaojukeji.com/pt-arch/warden-client-go v0.0.2 h1:T+OD/jrsTtuWQfI+5x6QrpJlAZYMCTqaAGgkgJ5ipz8= +git.xiaojukeji.com/pearls/tlog v0.0.4/go.mod h1:DzQ156tfv5IVzVQOR0WCGGDefMu1pfbFq6OkO7GlFCs= +git.xiaojukeji.com/pearls/tlog v0.1.0/go.mod h1:DzQ156tfv5IVzVQOR0WCGGDefMu1pfbFq6OkO7GlFCs= git.xiaojukeji.com/pt-arch/warden-client-go v0.0.2/go.mod h1:YUic+w1o/mEreI9DnNSIBoc31uU6A7Jl7pZZXT3CWOc= git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.0-20210804142554-1df30aae0688/go.mod h1:pWxnUiWor02/XhYPNRibAmm1sp5k3AltYx6WB460Fvg= git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.0-20210816124301-0acc0ae1c20f/go.mod h1:5f/Rzc4tA9h9CoFduCpcSsfHU35fR3kzTZ7qOTnp75U= git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.0-20210819121323-90575b150ac4/go.mod h1:xPRjOpqUXSC5piE5IPRtFz8h+m8sfpMFUkmnhmDAgbg= git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.0-20210831103212-e97e2e927a7c/go.mod h1:uphE+MJrMys/D7Yw18y05N3IzrgZsx1xnCrM0haxjQI= -git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.4 h1:oJoEwZzAOgB+sF3VxrGaUXLf5+tb/drOpJ/4D21h2Oo= git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.4/go.mod h1:3ori74OkxOH/XgqL+FGU9ldXIMfZEyYLRI02f8d0r/M= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.6/go.mod h1:3ori74OkxOH/XgqL+FGU9ldXIMfZEyYLRI02f8d0r/M= +git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.10/go.mod h1:Wae6/Gb+RYkUViUQaXkZc7gJ9TQPjHZnRbox0wT9Kho= +git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.17/go.mod h1:rbqsC9No/XJ42EJrGunTKzLi2EsL2hRo4lMhiCVtbIw= +git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.1.0 h1:QBiZJrtS216J7UUFD3FpogYSLPDbRltfj0iXX64WuUQ= +git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.1.0/go.mod h1:rB20mmovWOnp7p/mRvdLXA8g2G3E2ZO6cdxhvJy/pdE= +git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.2.0 h1:AQhyLGsHdPTLyIzofg4MhKnzY4ZHVuNm/csXNelnAqs= +git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.2.0/go.mod h1:XEVTaVFcGb7yXJlcLg688HoU855DoYFhe6Dzj2XpriA= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.4.0/go.mod h1:3TucWNLPFOLcHhha1CPp7Kis1UG2h/AqGROPyOeZzsM= +github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 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/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= 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/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -121,11 +150,14 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l 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/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= 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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 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.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/caio/go-tdigest v3.1.0+incompatible h1:uoVMJ3Q5lXmVLCCqaMGHLBWnbGoN6Lpu7OAUPR60cds= github.com/caio/go-tdigest v3.1.0+incompatible/go.mod h1:sHQM/ubZStBUmF1WbB8FAm8q9GjDajLC5T7ydxE3JHI= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -133,44 +165,46 @@ github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QH github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 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/coocood/freecache v1.1.1 h1:uukNF7QKCZEdZ9gAV7WQzvh0SbjwdMF6m3x3rxEkaPc= -github.com/coocood/freecache v1.1.1/go.mod h1:OKrEjkGVoxZhyWAJoeFi5BMLUJm2Tit0kpGkIr7NGYY= +github.com/coocood/freecache v1.2.4 h1:UdR6Yz/X1HW4fZOuH0Z94KwG851GWOSknua5VUbb/5M= +github.com/coocood/freecache v1.2.4/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk= 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/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 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/didi/gendry v1.7.0 h1:dFR6+TVCnbjvLfNiGN53xInG/C5HqG7u0gfnkF5J/Vo= -github.com/didi/gendry v1.7.0/go.mod h1:cSLuShZ1Zbs1S05RIOLNQv616aBaOQ1BDrXJP9A3J+M= +github.com/dgryski/go-rendezvous v0.0.0-20200609043717-5ab96a526299/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/didi/gendry v1.8.2 h1:rqeMwz9CI4GdKX2fDg13xba5f/45lJdf4bttrXi+smQ= +github.com/didi/gendry v1.8.2/go.mod h1:cSLuShZ1Zbs1S05RIOLNQv616aBaOQ1BDrXJP9A3J+M= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/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/elazarl/goproxy v0.0.0-20201021153353-00ad82a08272 h1:Am81SElhR3XCQBunTisljzNkNese2T1FiV8jP79+dqg= github.com/elazarl/goproxy v0.0.0-20201021153353-00ad82a08272/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e h1:/cwV7t2xezilMljIftb7WlFtzGANRCnoOhPjtl2ifcs= github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= -github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM= +github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= @@ -194,17 +228,32 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB 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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 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-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-redis/redis/v8 v8.0.0-beta.5/go.mod h1:Mm9EH/5UMRx680UIryN6rd5XFn/L7zORPqLV+1D5thQ= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 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= @@ -215,6 +264,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU 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/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -232,9 +282,9 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo/redis v0.0.1 h1:tQQSZyg4O0N0Dh2hli1pOrRdj+WHl1xf3w/x7olDgu0= -github.com/gomodule/redigo/redis v0.0.1/go.mod h1:QhGMo2EGfdSsmrYDENZq12/Y23fRP6X6nyFZ4TGSUvM= 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= @@ -242,20 +292,28 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.4/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/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.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-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= 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/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -283,6 +341,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO 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/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 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/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -290,12 +350,14 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV 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.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= 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/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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -304,17 +366,30 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 h1:X/79QL0b4YJVO5+OsPH9rF2u428CIrGL/jLmPsoOQQ4= github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U= 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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0= +github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= 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.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 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.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= 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= @@ -341,12 +416,15 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ 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/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/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/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/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= @@ -367,6 +445,9 @@ github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6J github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 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= @@ -374,6 +455,9 @@ github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeD github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU= github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= 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= @@ -381,12 +465,17 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= 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/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= 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= @@ -394,9 +483,13 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= @@ -407,12 +500,18 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil/v3 v3.21.6 h1:vU7jrp1Ic/2sHB7w6UNs7MIkn7ebVtTb5D9j45o9VYE= github.com/shirou/gopsutil/v3 v3.21.6/go.mod h1:JfVbDpIBLVzT8oKbvMg9P3wEIMDDpVn+LwHTKj0ST88= +github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= +github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= 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.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 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/smartystreets/goconvey v1.6.6/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/spaolacci/murmur3 v0.0.0-20170819071325-9f5d223c6079/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -425,40 +524,65 @@ github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3 github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/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 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 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.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4= github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= +github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= +github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/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/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 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.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2 v2.7.7+incompatible h1:f4YXPEGDypeNbQp2vG8j5AJIK35Aezq5fiYbj8Nl6Bc= go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2 v2.7.7+incompatible/go.mod h1:1fUxSjrbbsP3ktoGq3RtmeFvSZNEKfJhiXwQKlPLGBo= -go.intra.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20201123053335-e67edadc52bf h1:UbZ983164rDlXHI+NwhyJuZl2qY2eE55dFWbDXfnxg0= -go.intra.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20201123053335-e67edadc52bf/go.mod h1:xq25s/hZNGEYYgLfZAwF64xgQAivPGNZYij6/4RiuRo= -go.intra.xiaojukeji.com/platform-ha/onekey-degrade_sdk_go v3.2.8+incompatible h1:TiwDYfzeNZ7YCMol+vNXpprXX4kuvc1htbgJaNE/7Sw= +go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.8.7/go.mod h1:FOVCHswBfssBnx/PvA0VIiaYUayetSavG1ymwcRUxqk= +go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.8.9 h1:PSUkWRnCmE9lpa9CHXRbrZB/4VPBI3eb1rlCWvViLqw= +go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.8.9/go.mod h1:FOVCHswBfssBnx/PvA0VIiaYUayetSavG1ymwcRUxqk= +go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.9.0 h1:MgEj0JXHMFZoPQ40XiSGYHDPtrLdtR0gf67E4USrMyA= +go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.9.0/go.mod h1:FOVCHswBfssBnx/PvA0VIiaYUayetSavG1ymwcRUxqk= +go.intra.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20230228024253-24fcac901643 h1:otYMsv3wSriOsUNVuHUMzVTx+QujPugKIE3twZKGPIk= +go.intra.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20230228024253-24fcac901643/go.mod h1:xq25s/hZNGEYYgLfZAwF64xgQAivPGNZYij6/4RiuRo= go.intra.xiaojukeji.com/platform-ha/onekey-degrade_sdk_go v3.2.8+incompatible/go.mod h1:4nbUEJpW13dUlRufRpPNyOFnpAo+UGS2MGiVq59P4sY= +go.intra.xiaojukeji.com/platform-ha/onekey-degrade_sdk_go v3.2.9+incompatible h1:uDEL0y4ADgQXDafXQHAVNpgdczwVMkwKAnvbmstr88Y= +go.intra.xiaojukeji.com/platform-ha/onekey-degrade_sdk_go v3.2.9+incompatible/go.mod h1:4nbUEJpW13dUlRufRpPNyOFnpAo+UGS2MGiVq59P4sY= 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.opentelemetry.io/otel v0.6.0/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek= 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= @@ -467,6 +591,7 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 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/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= @@ -477,17 +602,22 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2 h1:y102fOLFqhV41b+4GPiJoa0k/x+pJcEi2/HB1Y5T6fU= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 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/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 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.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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= @@ -505,17 +635,19 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR 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-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191207000613-e7e4b65ae663/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= 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/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -525,6 +657,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ 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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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= @@ -535,34 +668,44 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h 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-20190312061237-fead79001313/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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= 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-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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= @@ -581,9 +724,8 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn 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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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= @@ -592,7 +734,6 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= @@ -604,11 +745,10 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn 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/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced h1:c5geK1iMU3cDKtFrCVQIcjR3W+JOZMuhIyICMCTbtus= -google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= 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= @@ -620,11 +760,13 @@ google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.60.0 h1:6FQAR0kM31P6MRdeluor2w2gPaS4SVNrD/DNTxrQ15k= +google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -638,8 +780,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= @@ -654,10 +797,11 @@ gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2G gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= 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/validator.v2 v2.0.0-20200605151824-2b28d334fa05 h1:l9eKDCWy9n7C5NAiQAMvDePh0vyLAweR6LcSUVXFUGg= gopkg.in/validator.v2 v2.0.0-20200605151824-2b28d334fa05/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc= gopkg.in/validator.v2 v2.0.0-20210331031555-b37d688a7fb0 h1:EFLtLCwd8tGN+r/ePz3cvRtdsfYNhDEdt/vp6qsT+0A= gopkg.in/validator.v2 v2.0.0-20210331031555-b37d688a7fb0/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc= +gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY= +gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8= 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= @@ -665,12 +809,19 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.1.2/go.mod h1:4P/X9vSc3WTrhTLZ259cpFd6xKNYiSSdSZngkSBGIMM= +gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8= +gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= +gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= +gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= +gorm.io/hints v1.1.0/go.mod h1:lKQ0JjySsPBj3uslFzY3JhYDtqEwzm+G1hv8rWujB6Y= 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= diff --git a/tg-core/model/strategy_context.go b/tg-core/model/strategy_context.go index a3e8a36..65b46fe 100644 --- a/tg-core/model/strategy_context.go +++ b/tg-core/model/strategy_context.go @@ -22,7 +22,6 @@ type ModuleResultInfo struct { Id string StrategyName string CostTime int64 - ResultInfo interface{} } type StrategyContext struct { @@ -65,7 +64,7 @@ func NewStrategyContext(ctx context.Context) *StrategyContext { } func (this *StrategyContext) Set(key string, value interface{}) { - this.ContextMap.Store(key,value) + this.ContextMap.Store(key, value) } func (this *StrategyContext) SetDebug(actionName string, key string, value interface{}) { @@ -126,7 +125,7 @@ func (this *StrategyContext) AddTimeoutAction(actionName string) { func (this *StrategyContext) GetTimeoutActions() []string { itf, err := this.GetArray(KEY_TIMEOUT_ACTION) - if err != nil || len(itf)==0 { + if err != nil || len(itf) == 0 { return nil } @@ -137,12 +136,11 @@ func (this *StrategyContext) GetTimeoutActions() []string { return actionNames } -func (this *StrategyContext) SetModuleResult(actionId, actionName string, costTime int64, resultInfo interface{}) { +func (this *StrategyContext) SetModuleResult(actionId, actionName string, costTime int64) { mri := &ModuleResultInfo{ Id: actionId, StrategyName: actionName, CostTime: costTime, - ResultInfo: resultInfo, } this.moduleResultMap.Store(actionId, mri) diff --git a/tg-core/wfengine/engine.go b/tg-core/wfengine/engine.go index 2d84e3a..bec0623 100644 --- a/tg-core/wfengine/engine.go +++ b/tg-core/wfengine/engine.go @@ -1,4 +1,5 @@ -/** +/* +* Description : workflow engine v3.1 Author : dayunzhangyunfeng@didiglobal.com Date : 2021-05-14 @@ -25,37 +26,13 @@ const ( type WorkflowEngine struct { sceneModuleMap map[int64]*SceneModule `json:"scene_module_map"` workflowMap map[int64]*Workflow `json:"workflows"` - modelBaseMap map[string]IModelBase `json:"modules"` + modelBaseMap map[string]IModelBase `json:"modules"` updateTime string `json:"update_time"` condExecutors *CondExecutors `json:"cond_executors"` } -func NewWorkflowEngine(moduleObj ModuleObjBase, appId int64) (*WorkflowEngine, error) { - //更新系统下全部场景的节点对象 - smMap, err := LoadSceneModuleMap(appId) - if err != nil { - return nil, err - } - - //load workflow - wfMap, err := LoadWorkflow(appId, smMap) - if err != nil { - return nil, err - } - - //TODO ZYF err - resetWorkflows(wfMap) - - mbMap, err := createModelMap(moduleObj, wfMap) - if err != nil { - return nil, err - } - - workfowEngine := newWorkflowEngine(smMap, wfMap, mbMap) - return workfowEngine, nil -} - func resetWorkflows(wfMap map[int64]*Workflow) { + //fmt.Println("wfMap.len=", len(wfMap)) if len(wfMap) == 0 { return } @@ -73,22 +50,23 @@ func resetWorkflow(wfMap map[int64]*Workflow, workflow *Workflow) error { if workflow == nil { return errors.New("workflow is nil:%v") } - + //fmt.Println("开始for循环处理flowAction") for { flowActionMap := make(map[string]*Action) + //fmt.Println("workflow.FlowCharts.ActionMap.len===>", len(workflow.FlowCharts.ActionMap), workflow.FlowCharts.HashCondition) for _, action := range workflow.FlowCharts.ActionMap { if action.ActionType == ActionTypeFlow { if action.RefWorkflowId <= 0 { tlog.ErrorCount(context.TODO(), "resetWorkflow_err", fmt.Sprintf("ref_workflow_id must >0, flowAction.Action=%+v", action)) - continue + continue } flowActionMap[action.ActionId] = action } } if len(flowActionMap) < 1 { - return nil + break } - + //fmt.Println() //对所有flowAction for _, flowAction := range flowActionMap { //get workflowid @@ -108,13 +86,13 @@ func resetWorkflow(wfMap map[int64]*Workflow, workflow *Workflow) error { firstActionId := refWf.FlowCharts.FirstActionId if flowAction.ActionId == workflow.FlowCharts.FirstActionId { workflow.FlowCharts.FirstActionId = firstActionId - }else { + } else { for _, prevId := range flowAction.PrevActionIds { strNextActionIds := strings.Join(workflow.FlowCharts.ActionMap[prevId].NextActionIds, ",") strNewNextActionIds := "" - if flowAction.Timeout >0 { + if flowAction.Timeout > 0 { strNewNextActionIds = strNextActionIds + "," + firstActionId - }else { + } else { strNewNextActionIds = strings.ReplaceAll(strNextActionIds, flowAction.ActionId, firstActionId) } workflow.FlowCharts.ActionMap[prevId].NextActionIds = strings.Split(strNewNextActionIds, ",") @@ -128,7 +106,7 @@ func resetWorkflow(wfMap map[int64]*Workflow, workflow *Workflow) error { lastActionId := refWf.FlowCharts.LastActionId if flowAction.ActionId == workflow.FlowCharts.LastActionId { workflow.FlowCharts.LastActionId = lastActionId - }else { + } else { for _, nextId := range flowAction.NextActionIds { strPrevActionIds := strings.Join(workflow.FlowCharts.ActionMap[nextId].PrevActionIds, ",") strNewPrevActionIds := "" @@ -146,68 +124,33 @@ func resetWorkflow(wfMap map[int64]*Workflow, workflow *Workflow) error { //删除 if flowAction.Timeout > 0 { flowAction.ActionType = ActionTypeTimeout - }else{ + } else { delete(workflow.FlowCharts.ActionMap, flowAction.ActionId) } } } + //fmt.Println("before workflow.FlowCharts.HashCondition=>", workflow.FlowCharts.HashCondition) for _, action := range workflow.FlowCharts.ActionMap { + //fmt.Println("actionId=", action.ActionId, "action.ActionType=", action.ActionType) if action.ActionType == ActionTypeCond { workflow.FlowCharts.HashCondition = true break } - } - return nil -} - -func NewWorkflowEngineFromFile(moduleObj ModuleObjBase, configPath string) (*WorkflowEngine, error) { - smMap, err := LoadSceneModuleMapFromFile(configPath + "/scene.json") - if err != nil { - return nil, err - } - - //更新workflow - wfMap, err := LoadWorkflowFromFile(configPath, smMap) - if err != nil { - return nil, err - } - resetWorkflows(wfMap) - - mbMap, err := createModelMap(moduleObj, wfMap) - if err != nil { - return nil, err - } - - workfowEngine := newWorkflowEngine(smMap, wfMap, mbMap) - return workfowEngine, nil -} - -func NewWorkflowEngineFromApollo(moduleObj ModuleObjBase, namespace, configName string) (*WorkflowEngine, error) { - smMap, wfMap, err := LoadSceneModuleWorkflowFromApollo(namespace, configName) - if err != nil { - return nil, err - } - resetWorkflows(wfMap) - - mbMap, err := createModelMap(moduleObj, wfMap) - if err != nil { - return nil, err } - - workfowEngine := newWorkflowEngine(smMap, wfMap, mbMap) - return workfowEngine, nil + //fmt.Println("after workflow.FlowCharts.HashCondition, workflowId=",workflow.Id, "hasCondition=",workflow.FlowCharts.HashCondition) + return nil } -func newWorkflowEngine(sceneModuleMap map[int64]*SceneModule, workflowMap map[int64]*Workflow, modelBaseMap map[string]IModelBase) *WorkflowEngine { - ut := fmt.Sprintf("%v", time.Now().Format("2006-01-02 15:04:05")) +func newWorkflowEngine(sceneModuleMap map[int64]*SceneModule, workflowMap map[int64]*Workflow, modelBaseMap map[string]IModelBase, version string) *WorkflowEngine { + //ut := fmt.Sprintf("%v", time.Now().Format("2006-01-02 15:04:05")) cExecutors := GetCondExecutors() wfe := &WorkflowEngine{ sceneModuleMap: sceneModuleMap, workflowMap: workflowMap, modelBaseMap: modelBaseMap, - updateTime: ut, + updateTime: version, condExecutors: cExecutors, } @@ -256,18 +199,25 @@ func createModelBase(moduleObj ModuleObjBase, action *Action) (IModelBase, error vMap[param.Name] = param.Value } } - vMap["Name"] = action.ActionName + //vMap["Name"] = action.ActionName mb := moduleObj.NewObj(action.ActionName, vMap) if mb == nil { return mb, fmt.Errorf("create ModelBase instance error, action:%v, vMap:%v", action, vMap) } + mb.SetName(action.ActionName) + err := reflectModuleField(mb, vMap) + if err != nil { + tlog.ErrorCount(context.TODO(), "createModelBase_err", fmt.Sprintf("set module field fail, actionName:%v, vMap:%v, error:%v", action.ActionName, vMap, err)) + } + return mb, nil } -func (w *WorkflowEngine) GetUpdateTime() string { +func (w *WorkflowEngine) GetVersion() string { return w.updateTime } + func (w *WorkflowEngine) RegisterCondExecutor(conditionName string, executor interface{}) { w.condExecutors.RegisterCondExecutor(conditionName, executor) } @@ -286,13 +236,15 @@ func (w *WorkflowEngine) Run(ctx context.Context, sc *model.StrategyContext) { return } + //fmt.Println(fmt.Sprintf("准备Run,先获取图,flow=%+v", flow.FlowCharts)) flowChart := flow.GetWorkflowChart() + //fmt.Println(fmt.Sprintf("准备Run,先获取图,flowChart=%+v", flowChart)) if flowChart == nil { sc.SetError(action0, errors.New("flowChart is nil")) return } - wgMap,tsMap := flowChart.CreateWaitMap() + wgMap, tsMap := flowChart.CreateWaitMap() waitedMap := &sync.Map{} wgn := &sync.WaitGroup{} @@ -344,15 +296,21 @@ func (w *WorkflowEngine) selectWorkFlow(ctx context.Context, sc *model.StrategyC return flow, err } -/** +/* +* + 新版本(v1.2.11)后因为有动态条件节点,原流程执行模式有较大变化,不再包含预处理,需实时判断节点走向和执行、跳过节点等操作。 -**/ -func (w *WorkflowEngine) doExecuteModule(ctx context.Context, sc *model.StrategyContext, flowChart *WorkflowChart, skipedActionIdPairs *sync.Map, wgMap map[string]*sync.WaitGroup, tsMap map[string]*TimeWaiter, waitedMap *sync.Map, actionId string, wgn *sync.WaitGroup) { + +* +*/ +func (w *WorkflowEngine) doExecuteModule(ctx context.Context, sc *model.StrategyContext, flowChart *WorkflowChart, skipedActionIdPairs *sync.Map, wgMap map[string]*sync.WaitGroup, tsMap map[string]*TimeWaiter, waitedMap *sync.Map, actionId string, wgn *sync.WaitGroup) { action, ok := flowChart.ActionMap[actionId] + //fmt.Println("开始执行actionId===>", fmt.Sprintf("%+v",action), ok) if !ok { return } + //fmt.Println("action.PrevActionIds===>", strings.Join(action.PrevActionIds, ",")) //is merge if len(action.PrevActionIds) > 1 { _, ok := waitedMap.LoadOrStore(action.ActionId, true) @@ -370,7 +328,7 @@ func (w *WorkflowEngine) doExecuteModule(ctx context.Context, sc *model.Strategy if timeoutActionId == "" { wgMap[action.ActionId].Wait() - }else { + } else { go func() { wgMap[action.ActionId].Wait() if tsMap[timeoutActionId] != nil { @@ -382,7 +340,7 @@ func (w *WorkflowEngine) doExecuteModule(ctx context.Context, sc *model.Strategy if mb, _ := w.modelBaseMap[timeoutActionId]; mb != nil { if flowChart.ActionMap[timeoutActionId].TimeoutAsync { go mb.OnTimeout(ctx, sc) - }else{ + } else { mb.OnTimeout(ctx, sc) } } @@ -412,13 +370,13 @@ func (w *WorkflowEngine) doExecuteModule(ctx context.Context, sc *model.Strategy } -func (w *WorkflowEngine) skipBranch(flowChart *WorkflowChart, wgMap map[string]*sync.WaitGroup, skipedActionIdPairs *sync.Map, toExcludeActionId string, prevAction *Action, action *Action){ +func (w *WorkflowEngine) skipBranch(flowChart *WorkflowChart, wgMap map[string]*sync.WaitGroup, skipedActionIdPairs *sync.Map, toExcludeActionId string, prevAction *Action, action *Action) { if prevAction == nil || action == nil { return } - if len(action.PrevActionIds)>1 { - if _, ok := skipedActionIdPairs.LoadOrStore(prevAction.ActionId + "_" + action.ActionId, ""); !ok { + if len(action.PrevActionIds) > 1 { + if _, ok := skipedActionIdPairs.LoadOrStore(prevAction.ActionId+"_"+action.ActionId, ""); !ok { wgMap[action.ActionId].Done() } } @@ -429,13 +387,15 @@ func (w *WorkflowEngine) skipBranch(flowChart *WorkflowChart, wgMap map[string]* return } - for _, nextActionId := range action.NextActionIds { + nextCount := len(action.NextActionIds) + for i := nextCount - 1; i >= 0; i-- { + nextActionId := action.NextActionIds[i] nextAction := flowChart.ActionMap[nextActionId] w.skipBranch(flowChart, wgMap, skipedActionIdPairs, toExcludeActionId, action, nextAction) } } -func (w *WorkflowEngine) executeModule(ctx context.Context, sc *model.StrategyContext, flowChart *WorkflowChart, action *Action, skipedActionIdPairs *sync.Map, wgMap map[string]*sync.WaitGroup, tsMap map[string]*TimeWaiter, wgn *sync.WaitGroup) string { +func (w *WorkflowEngine) executeModule(ctx context.Context, sc *model.StrategyContext, flowChart *WorkflowChart, action *Action, skipedActionIdPairs *sync.Map, wgMap map[string]*sync.WaitGroup, tsMap map[string]*TimeWaiter, wgn *sync.WaitGroup) string { toExeActionId := "" defer func() { @@ -445,30 +405,33 @@ func (w *WorkflowEngine) executeModule(ctx context.Context, sc *model.StrategyCo sc.Skip(ErrNoUnknown, fmt.Sprintf("executeModule_err, actionId=%+v, err=%+v", action.ActionId, err)) } - for _, nextActionId := range action.NextActionIds { + nextCount := len(action.NextActionIds) + for i := nextCount - 1; i >= 0; i-- { + nextActionId := action.NextActionIds[i] nextAction, ok := flowChart.ActionMap[nextActionId] if !ok { continue } if action.ActionType == ActionTypeCond { - //condition if nextActionId != toExeActionId { w.skipBranch(flowChart, wgMap, skipedActionIdPairs, toExeActionId, action, nextAction) } - }else{ - //task - if len(nextAction.PrevActionIds) > 1 { + } + + if len(nextAction.PrevActionIds) > 1 { + if _, ok := skipedActionIdPairs.LoadOrStore(action.ActionId+"_"+nextActionId, ""); !ok { wgMap[nextActionId].Done() } } + } if action.ActionId == flowChart.LastActionId { wgn.Done() } - //fmt.Println(fmt.Sprintf("====>Finish time=%v ,actionId=%v, actionName=%v", time.Now().Format("2006-01-02 15:04:05.000"), action.ActionId, action.ActionName)) + //fmt.Println(fmt.Sprintf("====>Finish time=%v ,actionId=%v, actionName=%v, action=%+v", time.Now().Format("2006-01-02 15:04:05.000"), action.ActionId, action.ActionName,action)) }() if action == nil { @@ -488,10 +451,9 @@ func (w *WorkflowEngine) executeModule(ctx context.Context, sc *model.StrategyCo if moduleBase, ok := w.modelBaseMap[action.ActionId]; ok { if !sc.IsSkip() { startTime := time.Now().UnixNano() / 1e6 - var result interface{} - if action.Timeout>0 && action.ActionType != ActionTypeTimeout { + if action.Timeout > 0 && action.ActionType != ActionTypeTimeout { go func() { - result = moduleBase.DoAction(ctx, sc) + moduleBase.DoAction(ctx, sc) if tsMap[action.ActionId] != nil { tsMap[action.ActionId].Done() } @@ -500,10 +462,10 @@ func (w *WorkflowEngine) executeModule(ctx context.Context, sc *model.StrategyCo sc.AddTimeoutAction(action.ActionName) if action.TimeoutAsync { go moduleBase.OnTimeout(ctx, sc) - }else{ + } else { moduleBase.OnTimeout(ctx, sc) } - }else{ + } else { if action.TimeoutDynamic { for _, nextActionId := range action.NextActionIds { if ts, ok := tsMap[nextActionId]; ok { @@ -513,12 +475,13 @@ func (w *WorkflowEngine) executeModule(ctx context.Context, sc *model.StrategyCo } } } - }else{ - result = moduleBase.DoAction(ctx, sc) + } else { + moduleBase.DoAction(ctx, sc) } endTime := time.Now().UnixNano() / 1e6 - sc.SetModuleResult(action.ActionId, action.ActionName, endTime-startTime, result) + sc.SetModuleResult(action.ActionId, action.ActionName, endTime-startTime) + } } else { sc.SetError(action.ActionId, fmt.Errorf("module not found in map, moduleMap:%v, moduleName:%v", w.modelBaseMap, action.ActionName)) @@ -527,4 +490,3 @@ func (w *WorkflowEngine) executeModule(ctx context.Context, sc *model.StrategyCo return "" } - diff --git a/tg-core/wfengine/workflow_apollo_dao.go b/tg-core/wfengine/engine_from_apollo.go similarity index 40% rename from tg-core/wfengine/workflow_apollo_dao.go rename to tg-core/wfengine/engine_from_apollo.go index b1374ce..a24ba1e 100644 --- a/tg-core/wfengine/workflow_apollo_dao.go +++ b/tg-core/wfengine/engine_from_apollo.go @@ -1,7 +1,7 @@ /** - Description : loader of workflow config info from apollo config - Author : dayunzhangyunfeng@didiglobal.com - Date : 2023-01-09 14:30 +Description : loader of workflow config info from apollo config +Author : dayunzhangyunfeng@didiglobal.com +Date : 2023-01-09 14:30 */ package wfengine @@ -17,45 +17,73 @@ import ( ) const ( - dftRange = "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99" - sceneKey = "scene" - ) + dftRange = "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99" + sceneKey = "scene" + versionKey = "version" +) -func LoadSceneModuleWorkflowFromApollo(namespace, configName string) (map[int64]*SceneModule, map[int64]*Workflow, error) { +func GetLatestVersionFromApollo(namespace, configName string) (string, error) { apolloConfig, err := apollo.NewApolloConfig(namespace, configName) if err != nil || apolloConfig == nil { - return nil, nil, fmt.Errorf("init apolloConfig fail, namespace=%v, configName=%v, err=%v", namespace, configName, err) + return "", fmt.Errorf("NewApolloConfig error, namespace=%v, configName=%v, err=%v", namespace, configName, err) } configParams := apolloConfig.GetConfigs() - if configParams == nil || configParams[sceneKey] == "" { - return nil, nil, fmt.Errorf("apollo GetConfigs fail, some keys not found namespace=%v, configName=%v, configParams=%v", namespace, configName, configParams) + if configParams == nil || configParams[versionKey] == "" { + return "", fmt.Errorf("GetConfigs fail, configParam is nil or version empty, namespace=%v, configName=%v, configParams.lenth=%v", namespace, configName, len(configParams)) } - sceneModuleString := "" - for key, val := range configParams { - if key == sceneKey { - sceneModuleString = val - break - } + return configParams[versionKey], nil +} + +func NewWorkflowEngineFromApollo(moduleObj ModuleObjBase, namespace, configName string) (*WorkflowEngine, error) { + smMap, wfMap, version, err := loadSceneModuleWorkflowFromApollo(namespace, configName) + if err != nil { + return nil, err + } + + resetWorkflows(wfMap) + + mbMap, err := createModelMap(moduleObj, wfMap) + if err != nil { + return nil, err + } + + workfowEngine := newWorkflowEngine(smMap, wfMap, mbMap, version) + return workfowEngine, nil +} + +func loadSceneModuleWorkflowFromApollo(namespace, configName string) (map[int64]*SceneModule, map[int64]*Workflow, string, error) { + apolloConfig, err := apollo.NewApolloConfig(namespace, configName) + if err != nil || apolloConfig == nil { + return nil, nil, "", fmt.Errorf("NewApolloConfig error, namespace=%v, configName=%v, err=%v", namespace, configName, err) } - smMap, err := loadSceneModuleString(sceneModuleString) + configParams := apolloConfig.GetConfigs() + if configParams == nil { + return nil, nil, "", fmt.Errorf("GetConfigs error, configParam is nil, namespace=%v, configName=%v", namespace, configName) + } + + if configParams[sceneKey] == "" { + return nil, nil, "", fmt.Errorf("configParams error, sceneKey is empty, namespace=%v, configName=%v, sceneKey=%v", namespace, configName, sceneKey) + } + + smMap, err := loadSceneModuleString(configParams[sceneKey]) if err != nil { - return nil, nil, err + return nil, nil, "", err } //更新workflow wfMap, err := LoadWorkflowFromApollo(configParams) if err != nil { - return nil, nil, err + return nil, nil, "", err } - return smMap, wfMap, nil + return smMap, wfMap, configParams[versionKey], nil } func loadSceneModuleString(sceneModuleMapString string) (map[int64]*SceneModule, error) { - if len(sceneModuleMapString) <=0 { + if len(sceneModuleMapString) <= 0 { return nil, fmt.Errorf("the param sceneModuleMapString:[%v] must not be empty", sceneModuleMapString) } @@ -69,24 +97,24 @@ func loadSceneModuleString(sceneModuleMapString string) (map[int64]*SceneModule, } func LoadWorkflowFromApollo(configParams map[string]string) (map[int64]*Workflow, error) { - if configParams == nil || len(configParams)<=0 { + if configParams == nil || len(configParams) <= 0 { return nil, fmt.Errorf("configParam is empty:%v", configParams) } //对每个文件逐个加到map wfMap := make(map[int64]*Workflow) for key, val := range configParams { - if key == sceneKey { + if key == sceneKey || key == versionKey { continue } wf, err := createWorkflowFromKV(key, val) if wf == nil || err != nil { - tlog.ErrorCount(context.TODO(),"loadWorkflowFromKV_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) + tlog.ErrorCount(context.TODO(), "loadWorkflowFromKV_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) continue } wf.FlowCharts, err = NewWorkflowChart(wf.FlowChart) if err != nil { - tlog.ErrorCount(context.TODO(),"NewWorkflowChart_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) + tlog.ErrorCount(context.TODO(), "NewWorkflowChart_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) continue } wfMap[wf.Id] = wf @@ -97,7 +125,7 @@ func LoadWorkflowFromApollo(configParams map[string]string) (map[int64]*Workflow func createWorkflowFromKV(key, val string) (*Workflow, error) { strs := strings.Split(key, "-") - if len(strs)<3 { + if len(strs) < 3 { return nil, fmt.Errorf("invalid key:%v, it must start with :workflow-sceneId-workflowId", key) } @@ -105,25 +133,25 @@ func createWorkflowFromKV(key, val string) (*Workflow, error) { if err != nil { return nil, fmt.Errorf("invalid sceneId:%v in key:%v,err=%v", strs[1], key, err) } - workflowId, err := strconv.ParseInt(strs[2], 10, 64) + workflowId, err := strconv.ParseInt(strs[2], 10, 64) if err != nil { return nil, fmt.Errorf("invalid workflowId:%v in key:%v,err=%v", strs[2], key, err) } workflow := &Workflow{ - Id : workflowId, - DimensionId : -1, - SceneId : sceneId, - FlowChart : val, - FlowCharts : nil, - FlowBranch : nil, - IsDefault : 1, - Range1 : dftRange, - Range2 : "", - Remark : "", + Id: workflowId, + DimensionId: -1, + SceneId: sceneId, + FlowChart: val, + FlowCharts: nil, + FlowBranch: nil, + IsDefault: 1, + Range1: dftRange, + Range2: "", + Remark: "", //UpdateTime:, - GroupName : "", + GroupName: "", } return workflow, nil -} \ No newline at end of file +} diff --git a/tg-core/wfengine/workflow_store.go b/tg-core/wfengine/engine_from_file.go similarity index 100% rename from tg-core/wfengine/workflow_store.go rename to tg-core/wfengine/engine_from_file.go diff --git a/tg-core/wfengine/scene_module_store.go b/tg-core/wfengine/engine_from_redis.go similarity index 35% rename from tg-core/wfengine/scene_module_store.go rename to tg-core/wfengine/engine_from_redis.go index ee8ddd3..5734732 100644 --- a/tg-core/wfengine/scene_module_store.go +++ b/tg-core/wfengine/engine_from_redis.go @@ -1,7 +1,7 @@ /** - Description : loader of scene and module config info - Author : dayunzhangyunfeng@didiglobal.com - Date : 2021-05-14 +Description : loader of workflow config info from redis +Author : dayunzhangyunfeng@didiglobal.com +Date : 2021-07-12 */ package wfengine @@ -11,14 +11,50 @@ import ( "encoding/json" "fmt" "github.com/didi/tg-flow/tg-core/common/redis" - "io/ioutil" - "os" + "github.com/didi/tg-flow/tg-core/common/tlog" ) const ( - //新版redis key - RedisKeySceneModule = "scene_module_app_" + RedisKeySceneModule = "scene_module_app_" + RedisKeyWorkflow = "workflow_app_" + RedisKeyVersion = "version_app_" ) + +func GetLatestVersionFromRedis(appId int64) (string, error) { + return redis.Handler.Get(context.TODO(), fmt.Sprintf("%v%v", RedisKeyVersion, appId)) +} + +func NewWorkflowEngine(moduleObj ModuleObjBase, appId int64) (*WorkflowEngine, error) { + //更新系统下全部场景的节点对象 + smMap, err := LoadSceneModuleMap(appId) + if err != nil { + return nil, err + } + + //load workflow + wfMap, err := LoadWorkflow(appId, smMap) + if err != nil { + return nil, err + } + + //版本号不是必须,兼容吧 + version, err1 := GetLatestVersionFromRedis(appId) + if err1 != nil { + tlog.ErrorCount(context.TODO(), "GetLatestVersionFromRedis_err", fmt.Sprintf("appId=%v, err=%v", appId, err1)) + } + + //TODO ZYF err + resetWorkflows(wfMap) + + mbMap, err := createModelMap(moduleObj, wfMap) + if err != nil { + return nil, err + } + + workfowEngine := newWorkflowEngine(smMap, wfMap, mbMap, version) + return workfowEngine, nil +} + func LoadSceneModuleMap(appId int64) (map[int64]*SceneModule, error) { //加载redis中的数据到内存 sceneModuleMap, err := loadSceneModules(appId) @@ -36,7 +72,7 @@ func LoadSceneModuleMap(appId int64) (map[int64]*SceneModule, error) { return sceneModuleMap, nil } -//获取redis中的数据 +// 获取redis中的数据 func loadSceneModules(appId int64) (map[int64]*SceneModule, error) { sceneModuleMapString, err := redis.Handler.Get(context.TODO(), fmt.Sprintf("%v%v", RedisKeySceneModule, appId)) if err != nil && err.Error() != redis.ErrNil { @@ -52,23 +88,29 @@ func loadSceneModules(appId int64) (map[int64]*SceneModule, error) { return sceneModuleMap, nil } -func LoadSceneModuleMapFromFile(path string) (map[int64]*SceneModule, error) { - jsonFile, err := os.Open(path) +func LoadWorkflow(appId int64, smMap map[int64]*SceneModule) (map[int64]*Workflow, error) { + workflowMapStr, err := redis.Handler.Get(context.TODO(), fmt.Sprintf("%v%v", RedisKeyWorkflow, appId)) if err != nil { - return nil, fmt.Errorf("os.open error, file:%v, err:%v", path, err) + return nil, err } - defer jsonFile.Close() - byteValue, err := ioutil.ReadAll(jsonFile) + var workflowMap map[int64]*Workflow + err = json.Unmarshal([]byte(workflowMapStr), &workflowMap) if err != nil { - return nil, fmt.Errorf("ioutil.ReadAll error, byteValue:%v,err=%v", string(byteValue), err) + return nil, fmt.Errorf("err:%v, workflow:%v", err, workflowMapStr) } - var sceneModuleMap map[int64]*SceneModule - err = json.Unmarshal(byteValue, &sceneModuleMap) - if err != nil { - return nil, fmt.Errorf("json.Unmarshal error, byteValue:%v,err=%v", string(byteValue), err) + wfMap := make(map[int64]*Workflow) + for workflowId, workflow := range workflowMap { + if _, ok := smMap[workflow.SceneId]; ok { + workflow.FlowCharts, err = NewWorkflowChart(workflow.FlowChart) + if err != nil { + tlog.ErrorCount(context.TODO(), "NewWorkflowChart_err", fmt.Sprintf("wf:%v,err:%v", workflow, err)) + continue + } + wfMap[workflowId] = workflow + } } - return sceneModuleMap, nil -} \ No newline at end of file + return wfMap, nil +} diff --git a/tg-core/wfengine/module_base.go b/tg-core/wfengine/module_base.go index 2bad9b2..6b5e9a4 100644 --- a/tg-core/wfengine/module_base.go +++ b/tg-core/wfengine/module_base.go @@ -1,7 +1,7 @@ /** - Description : ModelBase interface define - Author : dayunzhangyunfeng@didiglobal.com - Date : 2021-05-14 +Description : ModelBase interface define +Author : dayunzhangyunfeng@didiglobal.com +Date : 2021-05-14 */ package wfengine @@ -15,7 +15,7 @@ import ( "strconv" ) -type IModelBase interface{ +type IModelBase interface { DoAction(context.Context, *model.StrategyContext) interface{} OnTimeout(context.Context, *model.StrategyContext) SetName(string) @@ -24,10 +24,10 @@ type IModelBase interface{ type ModelBase struct { IModelBase - Name string + Name string } -func (m *ModelBase) DoAction(context.Context, *model.StrategyContext) interface{}{ +func (m *ModelBase) DoAction(context.Context, *model.StrategyContext) interface{} { return nil } @@ -47,21 +47,26 @@ type ModuleObjBase interface { NewObj(moduleName string, vMap map[string]string) IModelBase } -func ReflectModuleField(obj interface{}, reflectType reflect.Type, vMap map[string]string) error { +func reflectModuleField(obj interface{}, vMap map[string]string) error { + if len(vMap) == 0 { + return nil + } + v := reflect.ValueOf(obj) if v.Kind() == reflect.Ptr && !v.Elem().CanSet() { err := fmt.Errorf("this obj is not match reflect") - tlog.ErrorCount(context.TODO(),"ReflectModuleField_err", fmt.Sprintf("obj:%v, err:%v", obj, err)) + tlog.ErrorCount(context.TODO(), "ReflectModuleField_err", fmt.Sprintf("obj:%v, err:%v", obj, err)) return err } + reflectType := reflect.Indirect(v).Type() v = v.Elem() for i := 0; i < reflectType.NumField(); i++ { field := reflectType.Field(i) fieldValue := v.FieldByName(field.Name) if !fieldValue.IsValid() { err := fmt.Errorf("this obj(" + fmt.Sprintf("%v", obj) + ") field(" + field.Name + ")") - tlog.ErrorCount(context.TODO(),"ReflectModuleField_err", fmt.Sprintf("%v", err)) + tlog.ErrorCount(context.TODO(), "ReflectModuleField_err", fmt.Sprintf("%v", err)) continue } @@ -76,7 +81,7 @@ func ReflectModuleField(obj interface{}, reflectType reflect.Type, vMap map[stri tempInt, err := strconv.ParseInt(vMap[field.Name], 10, 64) if err != nil { err := fmt.Errorf("obj(" + fmt.Sprintf("%v", obj) + ") field(" + field.Name + ")'s value(" + vMap[field.Name] + ")") - tlog.ErrorCount(context.TODO(),"ReflectModuleField_err", fmt.Sprintf("%v", err)) + tlog.ErrorCount(context.TODO(), "ReflectModuleField_err", fmt.Sprintf("%v", err)) continue } fieldValue.SetInt(tempInt) @@ -85,7 +90,7 @@ func ReflectModuleField(obj interface{}, reflectType reflect.Type, vMap map[stri tempFolat, err := strconv.ParseFloat(vMap[field.Name], 64) if err != nil { err := fmt.Errorf("obj(" + fmt.Sprintf("%v", obj) + ") field(" + field.Name + ")'s value(" + vMap[field.Name] + ")") - tlog.ErrorCount(context.TODO(),"ReflectModuleField_err", fmt.Sprintf("%v", err)) + tlog.ErrorCount(context.TODO(), "ReflectModuleField_err", fmt.Sprintf("%v", err)) continue } fieldValue.SetFloat(tempFolat) diff --git a/tg-core/wfengine/workflow.go b/tg-core/wfengine/workflow.go index f580d08..07b857c 100644 --- a/tg-core/wfengine/workflow.go +++ b/tg-core/wfengine/workflow.go @@ -1,8 +1,9 @@ -/** - Description : workflow v3.0 with branch - Author : dayunzhangyunfeng@didiglobal.com - Date : 2021-05-14 - */ +/* +* +Description : workflow v3.0 with branch +Author : dayunzhangyunfeng@didiglobal.com +Date : 2021-05-14 +*/ package wfengine import ( @@ -18,56 +19,56 @@ import ( ) const ( - ActionTypeTask = "task" - ActionTypeCond = "condition" - ActionTypeFlow = "flow" - ActionTypeTimeout= "timeout" - BranchKeyDefault = "default" - BranchKeyJoiner = "_" - defaultBranch = "*" + ActionTypeTask = "task" + ActionTypeCond = "condition" + ActionTypeFlow = "flow" + ActionTypeTimeout = "timeout" + BranchKeyDefault = "default" + BranchKeyJoiner = "_" + defaultBranch = "*" ) type Workflow struct { - Id int64 `json:"id"` - DimensionId int64 `json:"dimension_id"` - SceneId int64 `json:"scene_id"` - FlowChart string `json:"flow_chart"` - FlowCharts *WorkflowChart `json:"flow_charts"` - FlowBranch *WorkflowBranch `json:"flow_branch"` - IsDefault int `json:"is_default"` - Range1 string `json:"range1"` - Range2 string `json:"range2"` - Remark string `json:"remark"` - UpdateTime time.Time `json:"update_time"` - GroupName string `json:"group_name"` + Id int64 `json:"id"` + DimensionId int64 `json:"dimension_id"` + SceneId int64 `json:"scene_id"` + FlowChart string `json:"flow_chart"` + FlowCharts *WorkflowChart `json:"flow_charts"` + FlowBranch *WorkflowBranch `json:"flow_branch"` + IsDefault int `json:"is_default"` + Range1 string `json:"range1"` + Range2 string `json:"range2"` + Remark string `json:"remark"` + UpdateTime time.Time `json:"update_time"` + GroupName string `json:"group_name"` } type WorkflowChart struct { - FirstActionId string `json:"first_action_id"` - LastActionId string `json:"last_action_id"` - HashCondition bool `json:"has_condition"` - ActionMap map[string]*Action `json:"actions"` + FirstActionId string `json:"first_action_id"` + LastActionId string `json:"last_action_id"` + HashCondition bool `json:"has_condition"` + ActionMap map[string]*Action `json:"actions"` } type Action struct { - ActionType string `json:"action_type"` - ActionId string `json:"action_id"` - ActionName string `json:"action_name"` - Params []*Param `json:"params"` - NextActionIds []string `json:"next_action_ids"` - NextConditions []string `json:"next_conditions"` - PrevActionIds []string `json:"prev_action_ids"` - Timeout int64 `json:"timeout"` - TimeoutAsync bool `json:"timeout_async"` - TimeoutDynamic bool `json:"timeout_dynamic"` - RefWorkflowId int64 `json:"ref_workflow_id"` - Description string `json:"description"` + ActionType string `json:"action_type"` + ActionId string `json:"action_id"` + ActionName string `json:"action_name"` + Params []*Param `json:"params"` + NextActionIds []string `json:"next_action_ids"` + NextConditions []string `json:"next_conditions"` + PrevActionIds []string `json:"prev_action_ids"` + Timeout int64 `json:"timeout"` + TimeoutAsync bool `json:"timeout_async"` + TimeoutDynamic bool `json:"timeout_dynamic"` + RefWorkflowId int64 `json:"ref_workflow_id"` + Description string `json:"description"` } type Param struct { - Name string `json:"name"` - Value string `json:"value"` - Type string `json:"type"` + Name string `json:"name"` + Value string `json:"value"` + Type string `json:"type"` } type WorkflowBranch struct { @@ -87,25 +88,25 @@ func NewWorkflowChart(flowChartStr string) (*WorkflowChart, error) { if len(flowChartStr) == 0 { return nil, errors.New("create WorkflowChart fail, flowChartStr is empty") } - + flowChart := &WorkflowChart{} - //读取的数据为json格式,需要进行解码 - err := json.Unmarshal([]byte(flowChartStr), flowChart) - if err != nil { - return nil, fmt.Errorf("create WorkflowChart fail, invalid json:%v, err:%v", flowChartStr, err) - } - - err = flowChart.setFirstActionId() - if err != nil { - return nil, fmt.Errorf("create WorkflowChart fail, err:%v", err) - } + //读取的数据为json格式,需要进行解码 + err := json.Unmarshal([]byte(flowChartStr), flowChart) + if err != nil { + return nil, fmt.Errorf("create WorkflowChart fail, invalid json:%v, err:%v", flowChartStr, err) + } + + err = flowChart.setFirstActionId() + if err != nil { + return nil, fmt.Errorf("create WorkflowChart fail, err:%v", err) + } flowChart.setPrevActionIds(flowChart.FirstActionId) - return flowChart, nil + return flowChart, nil } -func (w *WorkflowChart) setPrevActionIds(actionId string){ +func (w *WorkflowChart) setPrevActionIds(actionId string) { action, ok := w.ActionMap[actionId] if !ok { return @@ -114,7 +115,7 @@ func (w *WorkflowChart) setPrevActionIds(actionId string){ if actionId == w.FirstActionId { action.PrevActionIds = []string{} } - + nextActionIds := action.NextActionIds for _, nextActionId := range nextActionIds { nextAction, ok := w.ActionMap[nextActionId] @@ -122,10 +123,10 @@ func (w *WorkflowChart) setPrevActionIds(actionId string){ //报个error? continue } - + if nextAction.PrevActionIds == nil || len(nextAction.PrevActionIds) == 0 { nextAction.PrevActionIds = []string{actionId} - }else{ + } else { isAlreadyAdd := false var prevActionId string for _, prevActionId = range nextAction.PrevActionIds { @@ -138,13 +139,13 @@ func (w *WorkflowChart) setPrevActionIds(actionId string){ nextAction.PrevActionIds = append(nextAction.PrevActionIds, actionId) } } - + w.setPrevActionIds(nextActionId) } } /* - 耗时搜索 +耗时搜索 */ func (w *WorkflowChart) setFirstActionId() error { nextActionIds := make(map[string]bool) @@ -153,46 +154,46 @@ func (w *WorkflowChart) setFirstActionId() error { w.LastActionId = action.ActionId continue } - + for _, nextId := range action.NextActionIds { nextActionIds[nextId] = true } } - - for actionId,_ := range w.ActionMap { + + for actionId, _ := range w.ActionMap { if _, ok := nextActionIds[actionId]; !ok { w.FirstActionId = actionId return nil } } - + return errors.New("first action not found") } func (w *WorkflowChart) CreateWaitMap() (map[string]*sync.WaitGroup, map[string]*TimeWaiter) { - wgMap:= make(map[string]*sync.WaitGroup) - tsMap:= make(map[string]*TimeWaiter) + wgMap := make(map[string]*sync.WaitGroup) + tsMap := make(map[string]*TimeWaiter) for actionId, action := range w.ActionMap { prevCount := len(action.PrevActionIds) - if prevCount >1 { + if prevCount > 1 { wg := &sync.WaitGroup{} wg.Add(prevCount) - wgMap[actionId]= wg + wgMap[actionId] = wg } if action.Timeout > 0 { tsMap[actionId] = NewTimeWaiter(action.Timeout) } } - - return wgMap,tsMap + + return wgMap, tsMap } func (p *Param) clone() *Param { return &Param{ - Name: p.Name, - Value: p.Value, - Type: p.Type, + Name: p.Name, + Value: p.Value, + Type: p.Type, } } @@ -206,7 +207,7 @@ func (a *Action) createParamSlice(paramValues *sync.Map) ([]interface{}, error) for idx, param := range a.Params { //获取实际值 str := param.Value - if strings.HasPrefix(param.Value,"$") { + if strings.HasPrefix(param.Value, "$") { paramValue, _ := paramValues.Load(str[1:]) str = fmt.Sprintf("%v", paramValue) } @@ -214,33 +215,33 @@ func (a *Action) createParamSlice(paramValues *sync.Map) ([]interface{}, error) //获取参数 if param.Type == "string" { p[idx] = str - }else if param.Type == "int" { + } else if param.Type == "int" { val, err := strconv.ParseInt(str, 10, 64) if err != nil { return nil, err } p[idx] = val - }else if param.Type == "float" { + } else if param.Type == "float" { val, err := strconv.ParseFloat(str, 64) if err != nil { return nil, err } p[idx] = val - }else if param.Type == "bool" { + } else if param.Type == "bool" { var val bool strl := strings.ToLower(str) if strl == "true" { val = true - }else if strl == "false" { + } else if strl == "false" { val = false - }else { + } else { return nil, fmt.Errorf("invalid bool value, it must be: true or false") } p[idx] = val - }else if param.Type == "interface" { + } else if param.Type == "interface" { var val interface{} = str p[idx] = val - }else{ + } else { //TODO ZYF 先暂时支持4种最常见的类型 return nil, fmt.Errorf("unknown param type:%v", param.Type) } @@ -250,30 +251,45 @@ func (a *Action) createParamSlice(paramValues *sync.Map) ([]interface{}, error) } func (a *Action) Detach(prevAction *Action) { - if prevAction == nil { + //fmt.Println("\nstart detach, prevActionId, actionId:", prevAction.ActionId, a.ActionId) + if prevAction == nil || len(a.PrevActionIds) == 0 { //todo error return } - pActionIds := make([]string, len(a.PrevActionIds)-1) - previ := 0 - for _, prevActionId := range a.PrevActionIds { - if prevActionId != prevAction.ActionId { - pActionIds[previ] = prevActionId - previ++ + prevId := -1 + for pId, prevActionId := range a.PrevActionIds { + if prevActionId == prevAction.ActionId { + prevId = pId + break } } - a.PrevActionIds = pActionIds + if prevId > -1 { + a.PrevActionIds = append(a.PrevActionIds[:prevId], a.PrevActionIds[prevId+1:]...) + } - nActionIds := make([]string, len(prevAction.NextActionIds)-1) - nexti := 0 - for _, nextActionId := range prevAction.NextActionIds { - if nextActionId != a.ActionId { - nActionIds[nexti] = nextActionId - nexti++ + nextId := -1 + for nId, nextActionId := range prevAction.NextActionIds { + if nextActionId == a.ActionId { + nextId = nId + break } } - prevAction.NextActionIds = nActionIds + //fmt.Println("nextId============>", nextId) + //fmt.Println("before set prevAction.NextActionIds===>", strings.Join(prevAction.NextActionIds,",")) + if nextId > -1 { + prevAction.NextActionIds = append(prevAction.NextActionIds[:nextId], prevAction.NextActionIds[nextId+1:]...) + //fmt.Println("after set prevAction.NextActionIds===>", strings.Join(prevAction.NextActionIds,",")) + if len(prevAction.NextConditions) > nextId { + prevAction.NextConditions = append(prevAction.NextConditions[:nextId], prevAction.NextConditions[nextId+1:]...) + } + //fmt.Println("after set prevAction.NextConditions===>", strings.Join(prevAction.NextConditions,",")) + } + //fmt.Println("\nfinish detach, prevActionId, actionId:", prevAction.ActionId, a.ActionId) +} + +func (a *Action) toString() string { + return fmt.Sprintf("actionId:%+v, nextActionIds:%+v, nextConditions:%+v, prevActionIds:%+v", a.ActionId, strings.Join(a.NextActionIds, ","), strings.Join(a.NextConditions, ",")) } func (a *Action) clone() *Action { @@ -285,19 +301,19 @@ func (a *Action) clone() *Action { } } - act:= &Action{ - ActionType: a.ActionType, - ActionId: a.ActionId, - ActionName: a.ActionName, - Params: params, - NextActionIds: copyStringArray(a.NextActionIds), - NextConditions: copyStringArray(a.NextConditions), - PrevActionIds: copyStringArray(a.PrevActionIds), - Timeout: a.Timeout, + act := &Action{ + ActionType: a.ActionType, + ActionId: a.ActionId, + ActionName: a.ActionName, + Params: params, + NextActionIds: copyStringArray(a.NextActionIds), + NextConditions: copyStringArray(a.NextConditions), + PrevActionIds: copyStringArray(a.PrevActionIds), + Timeout: a.Timeout, TimeoutAsync: a.TimeoutAsync, TimeoutDynamic: a.TimeoutDynamic, RefWorkflowId: a.RefWorkflowId, - Description: a.Description, + Description: a.Description, } return act } @@ -305,7 +321,8 @@ func (a *Action) clone() *Action { func (w *WorkflowChart) clone() *WorkflowChart { chart := &WorkflowChart{} chart.FirstActionId = w.FirstActionId - chart.LastActionId = w.LastActionId + chart.LastActionId = w.LastActionId + chart.HashCondition = w.HashCondition actionMap := make(map[string]*Action) for actionId, action := range w.ActionMap { @@ -321,7 +338,7 @@ func copyStringArray(sources []string) []string { return nil } - dests:=make([]string, len(sources)) + dests := make([]string, len(sources)) for i, source := range sources { dests[i] = source } @@ -329,6 +346,7 @@ func copyStringArray(sources []string) []string { } func (a *Action) executeCond(paramValues *sync.Map) (retActionId string, err error) { + //fmt.Println("\n\n\n开始执行executeCond:",fmt.Sprintf("action:%+v", a)) //若报错,有缺省用缺省,无缺省用最后一个。条件必含后继,配置保存时校验 defaultIndex := len(a.NextActionIds) - 1 for idx, cdt := range a.NextConditions { @@ -336,36 +354,47 @@ func (a *Action) executeCond(paramValues *sync.Map) (retActionId string, err err defaultIndex = idx } } + //fmt.Println("defaultIndex=======>", defaultIndex) retActionId = a.NextActionIds[defaultIndex] + //fmt.Println("retActionId=>", retActionId) err = nil defer func() { if err0 := recover(); err0 != nil { err = fmt.Errorf("executeCond error, default value: %v used, a.Params:%v, paramValues:%v, err0:%v", a.NextActionIds[defaultIndex], a.Params, paramValues, err0) } }() - + //fmt.Println("\nexecuteCond start:", a.ActionId, a.ActionName, "\n") + //fmt.Println(fmt.Sprintf("\nparamValues:%+v", paramValues)) params, err := a.createParamSlice(paramValues) + //fmt.Println(fmt.Sprintf("\n获取到的参数值params:%+v", params), "err===>", err) if err != nil { err = fmt.Errorf("createParamSlice error, default value: %v used, a.Params:%v, paramValues:%v, err:%v", a.NextActionIds[defaultIndex], a.Params, paramValues, err) + //fmt.Println("\nerr==========>", err) return } - + //fmt.Println("prepare to exe:", a.ActionName, params) val, err := GetCondExecutors().Execute(a.ActionName, params) + //fmt.Println("条件执行结果 result: val====>", val, "err====>", err) if err != nil { err = fmt.Errorf("GetCondExecutors().Execute error, default value: %v used, actionName:%v, params:%v, err:%v", a.NextActionIds[defaultIndex], a.ActionName, params, err) return } + //fmt.Println("遍历比较,a和val ===>a=", fmt.Sprintf("%+v", a), "val=", val) for idx, cdt := range a.NextConditions { + //fmt.Println("cdt===",cdt, " val===", val) if cdt == val { retActionId = a.NextActionIds[idx] err = nil + //fmt.Println("retActionId===>", retActionId) return } } err = fmt.Errorf("no matched value error, default value:%v used, a.ActionId:%v, execute result:%v, nextActionIds:%v", a.NextActionIds[defaultIndex], a.ActionId, val, a.NextConditions) + //fmt.Println("eeeeeeeeeeeeeerrrrrrrrrrrrr====>", err) + //fmt.Println("\n\n\nexecuteCond end\n\n", a.ActionId, a.ActionName, "\n\n") return } @@ -413,7 +442,7 @@ func (w *WorkflowBranch) getCurrentBranchKey() string { } func (w *WorkflowBranch) getBranchKey(branchMap map[string]int) string { - if len(w.SortedBranch) ==0 || len(branchMap) == 0 { + if len(w.SortedBranch) == 0 || len(branchMap) == 0 { return BranchKeyDefault } @@ -437,7 +466,7 @@ func (w *WorkflowBranch) currentIndex(actionId string) int { } func (w *WorkflowBranch) hasBranch() bool { - if w.SortedBranch == nil || len(w.SortedBranch) ==0 { + if w.SortedBranch == nil || len(w.SortedBranch) == 0 { return false } @@ -451,7 +480,7 @@ func (w *WorkflowBranch) nextStep() bool { return true } - for i:=0;i<=idx;i++ { + for i := 0; i <= idx; i++ { branchId := w.SortedBranch[i] w.CurrentBranch[branchId] = 0 } diff --git a/tg-core/wfengine/workflow_dao.go b/tg-core/wfengine/workflow_dao.go deleted file mode 100644 index a83a575..0000000 --- a/tg-core/wfengine/workflow_dao.go +++ /dev/null @@ -1,46 +0,0 @@ -/** -Description : loader of workflow config info from redis -Author : dayunzhangyunfeng@didiglobal.com -Date : 2021-07-12 -*/ - -package wfengine - -import ( - "context" - "encoding/json" - "fmt" - "github.com/didi/tg-flow/tg-core/common/redis" - "github.com/didi/tg-flow/tg-core/common/tlog" -) - -const ( - RedisKeyWorkflow = "workflow_app_" -) - -func LoadWorkflow(appId int64, smMap map[int64]*SceneModule) (map[int64]*Workflow, error) { - workflowMapStr, err := redis.Handler.Get(context.TODO(), fmt.Sprintf("%v%v",RedisKeyWorkflow, appId)) - if err != nil { - return nil, err - } - - var workflowMap map[int64]*Workflow - err = json.Unmarshal([]byte(workflowMapStr), &workflowMap) - if err != nil { - return nil, fmt.Errorf("err:%v, workflow:%v", err, workflowMapStr) - } - - wfMap := make(map[int64]*Workflow) - for workflowId, workflow := range workflowMap { - if _, ok := smMap[workflow.SceneId]; ok { - workflow.FlowCharts, err = NewWorkflowChart(workflow.FlowChart) - if err != nil { - tlog.ErrorCount(context.TODO(),"NewWorkflowChart_err", fmt.Sprintf("wf:%v,err:%v", workflow, err)) - continue - } - wfMap[workflowId] = workflow - } - } - - return wfMap, nil -} \ No newline at end of file -- Gitee From 8b65dd28d3ed89daacd37410cb625fe80cd8e196 Mon Sep 17 00:00:00 2001 From: dayunzhangyunfeng Date: Mon, 18 Dec 2023 14:14:26 +0800 Subject: [PATCH 02/12] update engine_from_file --- .gitignore | 2 +- tg-core/go.mod | 2 - tg-core/go.sum | 49 +++-------- tg-core/wfengine/engine_from_file.go | 118 +++++++++++++++++++++------ 4 files changed, 104 insertions(+), 67 deletions(-) diff --git a/.gitignore b/.gitignore index f3d19ac..f9cb365 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,4 @@ rebuild.sh tg-flow log/ app.conf -tg-example +tg-example/ diff --git a/tg-core/go.mod b/tg-core/go.mod index 1bc6ac6..ed5ddf8 100644 --- a/tg-core/go.mod +++ b/tg-core/go.mod @@ -41,7 +41,6 @@ require ( git.xiaojukeji.com/observe-trace/didi-open-telemetry/opentelemetry-go v1.0.0 // indirect git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.2.0 // indirect github.com/BurntSushi/toml v1.3.2 // indirect - github.com/StackExchange/wmi v1.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/caio/go-tdigest v3.1.0+incompatible // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -60,7 +59,6 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/google/uuid v1.5.0 // indirect github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/parnurzeal/gorequest v0.2.16 // indirect github.com/pkg/errors v0.9.1 // indirect diff --git a/tg-core/go.sum b/tg-core/go.sum index a666edf..6f24ad3 100644 --- a/tg-core/go.sum +++ b/tg-core/go.sum @@ -5,7 +5,6 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= git.xiaojukeji.com/devops/statsd v0.0.5/go.mod h1:VwNi66bpKq2QSqq8xYHEhl1QVkaKWRqH9nXUPbRnMhM= git.xiaojukeji.com/devops/statsd v0.0.10/go.mod h1:VwNi66bpKq2QSqq8xYHEhl1QVkaKWRqH9nXUPbRnMhM= -git.xiaojukeji.com/devops/statsd v0.0.13 h1:sdZPslY/C6bVEHQuqomgVNkbuuAfQfM4Zwsa0gwXA2c= git.xiaojukeji.com/devops/statsd v0.0.13/go.mod h1:F9Q3cQhEZpMV+4iElWE6Oth6CW0Rk3bxMy6UbQhiHMA= git.xiaojukeji.com/devops/statsd v0.2.1 h1:JsqNCvP6qNwBAvhK98JcvBf2mfxmtNgzPhloZkpkyRg= git.xiaojukeji.com/devops/statsd v0.2.1/go.mod h1:F9Q3cQhEZpMV+4iElWE6Oth6CW0Rk3bxMy6UbQhiHMA= @@ -14,7 +13,6 @@ git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.37/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.39/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi5XlD96ZFqTAuG4EKCiv0= git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.40-0.20210819030951-70dcfc268e9a/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi5XlD96ZFqTAuG4EKCiv0= git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.40/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi5XlD96ZFqTAuG4EKCiv0= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.43 h1:xqvDUnCIcZlgqgIYc0ZFkEDIJ6JPLVQG5UREXg0S/P4= git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.43/go.mod h1:KVTUFxHUPxQVrzedCj1/3/u8IeWQM8Ka5aC2neD8TWY= git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.44/go.mod h1:KVTUFxHUPxQVrzedCj1/3/u8IeWQM8Ka5aC2neD8TWY= git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.47/go.mod h1:KVTUFxHUPxQVrzedCj1/3/u8IeWQM8Ka5aC2neD8TWY= @@ -43,7 +41,6 @@ git.xiaojukeji.com/gobiz/utils v1.0.1 h1:BftvQ8VdFC1KqE5gfkJ08XiM5DWmMyCbBNrrS2e git.xiaojukeji.com/gobiz/utils v1.0.1/go.mod h1:myE5vDJLUH3ME5Rc7ebg+7QSVusHABrHwbgYxE8YCWI= git.xiaojukeji.com/golang/go-tdigest v0.0.0-20160413204300-577940117044/go.mod h1:C7L62J5e+T2mAUXpgJck6EakqV3aMEz4UAzfN7Br2ak= git.xiaojukeji.com/lego/common-go v0.1.19/go.mod h1:6KXHn+oe36kXyiImOGwjSCoVT7lrkRUqm/OjWClCVhQ= -git.xiaojukeji.com/lego/common-go v0.1.22 h1:y9/AJdigi4eljGC3ZSLhOSb0annaCKKqml6pu3N56E4= git.xiaojukeji.com/lego/common-go v0.1.22/go.mod h1:0E8emkc2GJPf2zSclkMMCTcrZkEHX00EuDxxXvSE5JM= git.xiaojukeji.com/lego/common-go v0.1.24 h1:OisrYkP2DejFYdA8A8Q6LD4iCXU06oLB69SmrLDNSKY= git.xiaojukeji.com/lego/common-go v0.1.24/go.mod h1:0E8emkc2GJPf2zSclkMMCTcrZkEHX00EuDxxXvSE5JM= @@ -59,7 +56,6 @@ git.xiaojukeji.com/lego/dirpc-go v1.16.11-0.20210816074537-6e2a7ab987c1/go.mod h git.xiaojukeji.com/lego/dirpc-go v1.16.11-0.20210819033055-b145eb63fb00/go.mod h1:IY4Eps2zn266XXqnvM6q4mbqjJByNjwRmDVAt/CFXtQ= git.xiaojukeji.com/lego/dirpc-go v1.16.11-0.20210820022224-2f84346236de/go.mod h1:+PjDjcXqeTsv6jAF+GlzDL8u6lbbRcoRqelIo/1hDVw= git.xiaojukeji.com/lego/dirpc-go v1.16.13/go.mod h1:GFpAKh+xbcCp3eKTbSKODHHRh6FbuzKlFr/Rnotftqs= -git.xiaojukeji.com/lego/dirpc-go v1.16.24 h1:TnbnxvVy6Y11i8trSNkyZfv4rMzezQGoVtT12glqM8M= git.xiaojukeji.com/lego/dirpc-go v1.16.24/go.mod h1:rfAMEZLwY9h96yvgIC4/dFN/gHZaAfpVBdjkqiqYXpc= git.xiaojukeji.com/lego/dirpc-go v1.17.1-0.20220929063334-53711c3febc5/go.mod h1:XK/k7QyF2tmZJ0kmZzh5t5z3XKvCUsweDC2mgasdr40= git.xiaojukeji.com/lego/dirpc-go v1.22.4/go.mod h1:JQ1uPSpsIJcRKsFkjnA/DDmcxqRiXn3JB7vda7cvO38= @@ -71,7 +67,6 @@ git.xiaojukeji.com/lego/sentinel-golang v1.1.3 h1:0Am+H/M5vHA09CFr2XQSvNpbEA2Txp git.xiaojukeji.com/lego/sentinel-golang v1.1.3/go.mod h1:zAu2UQPlhAnXSt8ZjQMqG6Z2KMo8jyytmh3SVwXJii4= git.xiaojukeji.com/lego/thrift16 v1.0.1 h1:N3DNsT+ZR4lbGMT4o//ReA6c7Rlufrs9HhtdMrMbyS0= git.xiaojukeji.com/lego/thrift16 v1.0.1/go.mod h1:t8gPK22SMa8dXww/qpmsTLUfMLhUYTBoFxFN3lVi8GA= -git.xiaojukeji.com/nuwa/binding v0.1.2 h1:PE4l6gZIYTnuUHe/SXJQxJL3T124vay6gtvHgwLOfps= git.xiaojukeji.com/nuwa/binding v0.1.2/go.mod h1:yRWzl8Cjj62nCxibZvXKbLeDTcO1dToXlu0f2xHaHms= git.xiaojukeji.com/nuwa/binding v0.1.4 h1:8hdqh8T3sjDmesB1a/xhtPLkOLraGeuAMhciQc+uwzg= git.xiaojukeji.com/nuwa/binding v0.1.4/go.mod h1:yRWzl8Cjj62nCxibZvXKbLeDTcO1dToXlu0f2xHaHms= @@ -96,7 +91,6 @@ git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2 v2.8.14/go.mod h1:WJI5y1Dy+6y8BClx git.xiaojukeji.com/nuwa/protoc-dirpc v1.2.0/go.mod h1:gzfVNNsbUshc1LPU4FIWSVeRwqL2arLuc3o/G785hME= git.xiaojukeji.com/nuwa/trace v1.3.12 h1:15K0Y+PG3p1lSCpwdhRlQM/mN7NwravpeY/l+rmqu0g= git.xiaojukeji.com/nuwa/trace v1.3.12/go.mod h1:8Og3kmWTAaXezXM4Aq3HWe75sMY+d3jA77nmyn4c9DA= -git.xiaojukeji.com/nuwa/trace/v2 v2.0.3 h1:40KrHWzHMiGzBPSYPhwXoXKL1Ifni9SnPV4ISSO+TKg= git.xiaojukeji.com/nuwa/trace/v2 v2.0.3/go.mod h1:Mlg8+AlIxlUh7l0Vc+sVhx8Ef6G9Q0UKTURUCV8rC6g= git.xiaojukeji.com/nuwa/trace/v2 v2.0.7 h1:4SmofQ5PUxn43JozBVYc5qA3s5JvWByqaZgkMehk0JU= git.xiaojukeji.com/nuwa/trace/v2 v2.0.7/go.mod h1:bj9wOA2DCLwBG1Dv5dl+zD4n3EikxolGooXPzCyJWP4= @@ -114,7 +108,6 @@ git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.4/go.mod h1:3ori74OkxOH/XgqL+FG git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.6/go.mod h1:3ori74OkxOH/XgqL+FGU9ldXIMfZEyYLRI02f8d0r/M= git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.10/go.mod h1:Wae6/Gb+RYkUViUQaXkZc7gJ9TQPjHZnRbox0wT9Kho= git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.17/go.mod h1:rbqsC9No/XJ42EJrGunTKzLi2EsL2hRo4lMhiCVtbIw= -git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.1.0 h1:QBiZJrtS216J7UUFD3FpogYSLPDbRltfj0iXX64WuUQ= git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.1.0/go.mod h1:rB20mmovWOnp7p/mRvdLXA8g2G3E2ZO6cdxhvJy/pdE= git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.2.0 h1:AQhyLGsHdPTLyIzofg4MhKnzY4ZHVuNm/csXNelnAqs= git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.2.0/go.mod h1:XEVTaVFcGb7yXJlcLg688HoU855DoYFhe6Dzj2XpriA= @@ -128,10 +121,7 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 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/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= -github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= 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/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -166,7 +156,6 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -197,8 +186,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 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/elazarl/goproxy v0.0.0-20201021153353-00ad82a08272/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= -github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e h1:/cwV7t2xezilMljIftb7WlFtzGANRCnoOhPjtl2ifcs= github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 h1:lS3P5Nw3oPO05Lk2gFiYUOL3QPaH+fRoI1wFOc4G1UY= github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= @@ -237,15 +226,12 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= -github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= @@ -280,7 +266,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -295,15 +280,14 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/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/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -361,8 +345,9 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 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.3/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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -384,10 +369,8 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -453,7 +436,6 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod 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_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU= github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= @@ -463,7 +445,6 @@ github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1: 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/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= @@ -471,7 +452,6 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 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/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= @@ -481,7 +461,6 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R 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/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= @@ -494,16 +473,18 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 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/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shirou/gopsutil/v3 v3.21.6 h1:vU7jrp1Ic/2sHB7w6UNs7MIkn7ebVtTb5D9j45o9VYE= github.com/shirou/gopsutil/v3 v3.21.6/go.mod h1:JfVbDpIBLVzT8oKbvMg9P3wEIMDDpVn+LwHTKj0ST88= github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= 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= @@ -538,12 +519,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4= github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= -github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= @@ -551,7 +530,6 @@ github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDgu 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 v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= -github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= @@ -568,7 +546,6 @@ go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mI go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2 v2.7.7+incompatible h1:f4YXPEGDypeNbQp2vG8j5AJIK35Aezq5fiYbj8Nl6Bc= go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2 v2.7.7+incompatible/go.mod h1:1fUxSjrbbsP3ktoGq3RtmeFvSZNEKfJhiXwQKlPLGBo= go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.8.7/go.mod h1:FOVCHswBfssBnx/PvA0VIiaYUayetSavG1ymwcRUxqk= -go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.8.9 h1:PSUkWRnCmE9lpa9CHXRbrZB/4VPBI3eb1rlCWvViLqw= go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.8.9/go.mod h1:FOVCHswBfssBnx/PvA0VIiaYUayetSavG1ymwcRUxqk= go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.9.0 h1:MgEj0JXHMFZoPQ40XiSGYHDPtrLdtR0gf67E4USrMyA= go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.9.0/go.mod h1:FOVCHswBfssBnx/PvA0VIiaYUayetSavG1ymwcRUxqk= @@ -644,7 +621,6 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= @@ -692,7 +668,6 @@ golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -704,8 +679,9 @@ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 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= @@ -729,7 +705,6 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM= @@ -763,7 +738,6 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.60.0 h1:6FQAR0kM31P6MRdeluor2w2gPaS4SVNrD/DNTxrQ15k= google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= @@ -778,15 +752,15 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.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/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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= @@ -798,7 +772,6 @@ gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/R 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/validator.v2 v2.0.0-20200605151824-2b28d334fa05/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc= -gopkg.in/validator.v2 v2.0.0-20210331031555-b37d688a7fb0 h1:EFLtLCwd8tGN+r/ePz3cvRtdsfYNhDEdt/vp6qsT+0A= gopkg.in/validator.v2 v2.0.0-20210331031555-b37d688a7fb0/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc= gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY= gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8= diff --git a/tg-core/wfengine/engine_from_file.go b/tg-core/wfengine/engine_from_file.go index cb96a1a..85da373 100644 --- a/tg-core/wfengine/engine_from_file.go +++ b/tg-core/wfengine/engine_from_file.go @@ -1,13 +1,14 @@ /** - Description : loader of workflow config info from files - Author : dayunzhangyunfeng@didiglobal.com - Date : 2021-05-14 +Description : loader of workflow config info from files +Author : dayunzhangyunfeng@didiglobal.com +Date : 2021-05-14 */ package wfengine import ( "context" + "encoding/json" "fmt" "github.com/didi/tg-flow/tg-core/common/tlog" "io/ioutil" @@ -17,12 +18,77 @@ import ( ) const ( - defaultRange = "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99" + defaultRange = "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99" + versionFileName = "/version" ) +func GetLatestVersionFromFile(path string) (string, error) { + jsonFile, err := os.Open(path + versionFileName) + if err != nil { + return "", fmt.Errorf("os.open error, file:%v, err:%v", path, err) + } + defer jsonFile.Close() + + byteValue, err := ioutil.ReadAll(jsonFile) + if err != nil { + return "", fmt.Errorf("ioutil.ReadAll error, byteValue:%v,err=%v", string(byteValue), err) + } + + return string(byteValue), nil +} + +func NewWorkflowEngineFromFile(moduleObj ModuleObjBase, configPath string) (*WorkflowEngine, error) { + smMap, err := LoadSceneModuleMapFromFile(configPath + "/scene.json") + if err != nil { + return nil, err + } + + //更新workflow + wfMap, err := LoadWorkflowFromFile(configPath, smMap) + if err != nil { + return nil, err + } + + version, err1 := GetLatestVersionFromFile(configPath) + if err1 != nil { + tlog.ErrorCount(context.TODO(), "GetLatestVersionFromRedis_err", fmt.Sprintf("configPath=%v, err=%v", configPath, err1)) + } + + resetWorkflows(wfMap) + + mbMap, err := createModelMap(moduleObj, wfMap) + if err != nil { + return nil, err + } + + workfowEngine := newWorkflowEngine(smMap, wfMap, mbMap, version) + return workfowEngine, nil +} + +func LoadSceneModuleMapFromFile(path string) (map[int64]*SceneModule, error) { + jsonFile, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("os.open error, file:%v, err:%v", path, err) + } + defer jsonFile.Close() + + byteValue, err := ioutil.ReadAll(jsonFile) + if err != nil { + return nil, fmt.Errorf("ioutil.ReadAll error, byteValue:%v,err=%v", string(byteValue), err) + } + + var sceneModuleMap map[int64]*SceneModule + err = json.Unmarshal(byteValue, &sceneModuleMap) + if err != nil { + return nil, fmt.Errorf("json.Unmarshal error, byteValue:%v,err=%v", string(byteValue), err) + } + + return sceneModuleMap, nil +} + func LoadWorkflowFromFile(dirPath string, smMap map[int64]*SceneModule) (map[int64]*Workflow, error) { //1.遍历目录 - filePaths:= make(map[string]string) + filePaths := make(map[string]string) err := getWorkflowFiles(dirPath, filePaths) if err != nil { return nil, err @@ -33,12 +99,12 @@ func LoadWorkflowFromFile(dirPath string, smMap map[int64]*SceneModule) (map[int for filePath, _ := range filePaths { wf, err := createWorkflowFromFile(filePath) if wf == nil || err != nil { - tlog.ErrorCount(context.TODO(),"loadWorkflowFromFile_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) + tlog.ErrorCount(context.TODO(), "loadWorkflowFromFile_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) continue } wf.FlowCharts, err = NewWorkflowChart(wf.FlowChart) if err != nil { - tlog.ErrorCount(context.TODO(),"NewWorkflowChart_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) + tlog.ErrorCount(context.TODO(), "NewWorkflowChart_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) continue } wfMap[wf.Id] = wf @@ -48,8 +114,8 @@ func LoadWorkflowFromFile(dirPath string, smMap map[int64]*SceneModule) (map[int } func getWorkflowFiles(path string, filePaths map[string]string) error { - if !strings.HasSuffix(path,"/"){ - path = path +"/" + if !strings.HasSuffix(path, "/") { + path = path + "/" } rd, err := ioutil.ReadDir(path) @@ -60,9 +126,9 @@ func getWorkflowFiles(path string, filePaths map[string]string) error { for _, fi := range rd { if fi.IsDir() { getWorkflowFiles(path+fi.Name(), filePaths) - }else if strings.HasPrefix(fi.Name(),"workflow-") { + } else if strings.HasPrefix(fi.Name(), "workflow-") { filePath := path + fi.Name() - filePaths[filePath]= "" + filePaths[filePath] = "" } } @@ -80,14 +146,14 @@ func createWorkflowFromFile(path string) (*Workflow, error) { idx := strings.Index(fileName, ".") fileName = fileName[:idx] strs := strings.Split(fileName, "-") - if len(strs)<3 { - return nil, fmt.Errorf("invalid workflow file name:%v, it must start with :workflow-sceneId-workflowId",jsonFile.Name()) + if len(strs) < 3 { + return nil, fmt.Errorf("invalid workflow file name:%v, it must start with :workflow-sceneId-workflowId", jsonFile.Name()) } sceneId, err := strconv.ParseInt(strs[1], 10, 64) if err != nil { return nil, fmt.Errorf("invalid sceneId:%v in file name:%v,err=%v", strs[1], fileName, err) } - workflowId, err := strconv.ParseInt(strs[2], 10, 64) + workflowId, err := strconv.ParseInt(strs[2], 10, 64) if err != nil { return nil, fmt.Errorf("invalid workflowId:%v in file name:%v,err=%v", strs[2], fileName, err) } @@ -99,19 +165,19 @@ func createWorkflowFromFile(path string) (*Workflow, error) { flowChartStr := string(byteValue) workflow := &Workflow{ - Id : workflowId, - DimensionId : -1, - SceneId : sceneId, - FlowChart : flowChartStr, - FlowCharts : nil, - FlowBranch : nil, - IsDefault : 1, - Range1 : defaultRange, - Range2 : "", - Remark : "", + Id: workflowId, + DimensionId: -1, + SceneId: sceneId, + FlowChart: flowChartStr, + FlowCharts: nil, + FlowBranch: nil, + IsDefault: 1, + Range1: defaultRange, + Range2: "", + Remark: "", //UpdateTime:, - GroupName : "", + GroupName: "", } return workflow, nil -} \ No newline at end of file +} -- Gitee From fc4257b38fab5045a45be91383f41cd8ceeaefe0 Mon Sep 17 00:00:00 2001 From: dayunzhangyunfeng Date: Sat, 14 Sep 2024 10:42:08 +0800 Subject: [PATCH 03/12] v1.5.0 --- .gitignore | 2 +- common/crontask/new_register_task.go | 41 + common/crontask/register_task.go | 96 + {tg-core/common => common}/path/init.go | 11 +- .../timeutils/time_coster.go | 84 +- common/tlog/count_logger.go | 140 + common/tlog/see_count_logger.go | 50 + common/utils/recover.go | 13 + conf/tlog.xml | 27 + consts/global_c.go | 7 + go.mod | 8 + go.sum | 4 + tg-core/main.go => main.go | 90 +- {tg-core/model => model}/experiment.go | 0 {tg-core/model => model}/strategy_context.go | 118 +- tg-core/README.md | 0 tg-core/common/cache/dicache.go | 107 - tg-core/common/crontask/new_register_task.go | 35 - tg-core/common/crontask/register_task.go | 97 - tg-core/common/httpserv/init.go | 41 - tg-core/common/ipaddrs/ips.go | 84 - tg-core/common/lock/lease_lock.go | 9 - tg-core/common/lock/lock_message.go | 55 - tg-core/common/lock/redis_lease_lock.go | 133 - tg-core/common/metric/metric_service.go | 123 - tg-core/common/mysql/init.go | 56 - tg-core/common/redis/init.go | 235 - tg-core/common/tlog/init.go | 227 - tg-core/common/utils/convert.go | 83 - tg-core/common/utils/recover.go | 37 - tg-core/common/utils/stringsUtil.go | 166 - tg-core/conf/init.go | 105 - tg-core/consts/dltag.go | 27 - tg-core/consts/genrec_config_c.go | 19 - tg-core/consts/global_c.go | 32 - tg-core/consts/ranker_config_status.go | 15 - tg-core/consts/redis_keys.go | 29 - tg-core/dispatcher/base_dispatcher.go | 196 - tg-core/dispatcher/public_log.go | 29 - tg-core/go.mod | 87 - tg-core/go.sum | 807 ---- tg-core/model/algo_model.go | 18 - tg-core/model/apollo_config.go | 148 - tg-core/model/dimension.go | 20 - tg-core/model/down_grade.go | 17 - tg-core/model/omega_info.go | 12 - tg-core/model/recall_config.go | 16 - tg-core/model/system_config.go | 13 - tg-core/model/trace_info.go | 14 - tg-core/model/workflow.go | 137 - tg-core/reset_import.sh | 9 - tg-core/router/router.go | 48 - tg-core/store/algomodel_index_map.go | 28 - tg-core/store/debug_info_list.go | 69 - tg-core/store/dimension_index_map.go | 29 - tg-core/store/machine_map.go | 43 - tg-core/store/workflow_map.go | 28 - tg-core/wfengine/action_counter.go | 9 - tg-core/wfengine/apollo/apollo_config.go | 31 - tg-core/wfengine/logger_interface.go | 37 - tg-service/README.md | 18 - tg-service/common/logs/dltag.go | 22 - tg-service/common/template/init.go | 63 - tg-service/conf/init.go | 42 - tg-service/conf/mysql_compile/app_config | 11 - tg-service/conf/mysql_compile/scene_config | 13 - tg-service/conf/mysql_compile/workflow | 19 - tg-service/constant/const.go | 46 - tg-service/controller/controller.go | 147 - tg-service/controller/workflow_controller.go | 167 - tg-service/cron/cron_task.go | 22 - tg-service/go.mod | 8 - tg-service/idl/types.go | 718 --- tg-service/idl/workflow.go | 91 - .../appconfigadmin/add_update_app_config.go | 60 - .../logic/appconfigadmin/delete_app_config.go | 26 - .../logic/appconfigadmin/export_app_config.go | 169 - .../logic/appconfigadmin/select_app_config.go | 111 - tg-service/logic/common.go | 367 -- tg-service/logic/login.go | 63 - .../logic/sceneadmin/add_update_scene.go | 126 - tg-service/logic/sceneadmin/delete_scene.go | 54 - .../logic/sceneadmin/select_scene_conf.go | 146 - tg-service/logic/select.go | 131 - .../workflowadmin/add_update_workflow.go | 112 - .../logic/workflowadmin/add_workflow.go | 126 - .../logic/workflowadmin/delete_workflow.go | 73 - .../logic/workflowadmin/export_workflow.go | 97 - .../workflowadmin/flowchecker/checker.go | 131 - .../workflowadmin/flowchecker/checker_test.go | 156 - .../logic/workflowadmin/get_save_workflow.go | 190 - .../workflowadmin/select_workflow_config.go | 163 - .../logic/workflowadmin/update_workflow.go | 160 - tg-service/main.go | 21 - tg-web/CHANGELOG.md | 0 tg-web/LICENSE | 21 - tg-web/README.md | 31 - tg-web/build-exclude-list.txt | 1 - tg-web/env/.env | 2 - tg-web/env/.env.alpha | 2 - tg-web/env/.env.pre | 2 - tg-web/env/.env.production | 1 - tg-web/index.html | 13 - tg-web/package-lock.json | 3943 ----------------- tg-web/package.json | 98 - tg-web/plugins.d.ts | 4 - tg-web/server.conf | 8 - tg-web/shim.d.ts | 14 - tg-web/source.d.ts | 6 - tg-web/src/App.vue | 122 - tg-web/src/api/request.ts | 42 - tg-web/src/api/strategy/index.ts | 3 - tg-web/src/api/strategy/services.ts | 401 -- tg-web/src/api/strategy/types.ts | 221 - tg-web/src/api/strategy/utils.ts | 115 - tg-web/src/components/g6/flow.vue | 255 -- tg-web/src/components/g6/g6.ts | 1158 ----- tg-web/src/components/svgIcon/index.vue | 42 - tg-web/src/config.ts | 5 - tg-web/src/i18n/index.ts | 67 - tg-web/src/i18n/lang/en.ts | 180 - tg-web/src/i18n/lang/zh-cn.ts | 180 - tg-web/src/i18n/lang/zh-tw.ts | 180 - tg-web/src/i18n/pages/formI18n/en.ts | 13 - tg-web/src/i18n/pages/formI18n/zh-cn.ts | 13 - tg-web/src/i18n/pages/formI18n/zh-tw.ts | 13 - tg-web/src/i18n/pages/login/en.ts | 29 - tg-web/src/i18n/pages/login/zh-cn.ts | 28 - tg-web/src/i18n/pages/login/zh-tw.ts | 28 - tg-web/src/layout/component/aside.vue | 163 - tg-web/src/layout/component/columnsAside.vue | 292 -- tg-web/src/layout/component/header.vue | 34 - tg-web/src/layout/component/main.vue | 101 - tg-web/src/layout/footer/index.vue | 47 - tg-web/src/layout/index.vue | 54 - tg-web/src/layout/lockScreen/index.vue | 372 -- tg-web/src/layout/logo/index.vue | 81 - tg-web/src/layout/main/classic.vue | 35 - tg-web/src/layout/main/columns.vue | 41 - tg-web/src/layout/main/defaults.vue | 47 - tg-web/src/layout/main/transverse.vue | 17 - .../layout/navBars/breadcrumb/breadcrumb.vue | 163 - .../layout/navBars/breadcrumb/business.vue | 55 - .../layout/navBars/breadcrumb/closeFull.vue | 61 - .../src/layout/navBars/breadcrumb/index.vue | 119 - .../src/layout/navBars/breadcrumb/search.vue | 138 - .../src/layout/navBars/breadcrumb/setings.vue | 817 ---- tg-web/src/layout/navBars/breadcrumb/user.vue | 317 -- .../layout/navBars/breadcrumb/userNews.vue | 115 - tg-web/src/layout/navBars/index.vue | 40 - .../layout/navBars/tagsView/contextmenu.vue | 138 - .../src/layout/navBars/tagsView/tagsView.vue | 734 --- tg-web/src/layout/navMenu/horizontal.vue | 157 - tg-web/src/layout/navMenu/subItem.vue | 48 - tg-web/src/layout/navMenu/vertical.vue | 101 - tg-web/src/layout/routerView/iframes.vue | 66 - tg-web/src/layout/routerView/link.vue | 61 - tg-web/src/layout/routerView/parent.vue | 88 - tg-web/src/main.ts | 23 - tg-web/src/plugins/naive.ts | 107 - tg-web/src/router/backEnd.ts | 145 - tg-web/src/router/frontEnd.ts | 134 - tg-web/src/router/index.ts | 140 - tg-web/src/router/route.ts | 190 - tg-web/src/stores/index.ts | 8 - tg-web/src/stores/interface/index.ts | 91 - tg-web/src/stores/keepAliveNames.ts | 37 - tg-web/src/stores/requestOldRoutes.ts | 17 - tg-web/src/stores/routesList.ts | 27 - tg-web/src/stores/tagsViewRoutes.ts | 24 - tg-web/src/stores/themeConfig.ts | 146 - tg-web/src/stores/userInfo.ts | 72 - tg-web/src/theme/app.scss | 286 -- tg-web/src/theme/common/transition.scss | 94 - tg-web/src/theme/dark.scss | 236 - tg-web/src/theme/element.scss | 293 -- tg-web/src/theme/iconSelector.scss | 70 - tg-web/src/theme/index.scss | 8 - tg-web/src/theme/loading.scss | 51 - tg-web/src/theme/media/chart.scss | 94 - tg-web/src/theme/media/cityLinkage.scss | 10 - tg-web/src/theme/media/date.scss | 25 - tg-web/src/theme/media/dialog.scss | 12 - tg-web/src/theme/media/error.scss | 45 - tg-web/src/theme/media/form.scss | 18 - tg-web/src/theme/media/home.scss | 23 - tg-web/src/theme/media/index.scss | 15 - tg-web/src/theme/media/layout.scss | 55 - tg-web/src/theme/media/login.scss | 63 - tg-web/src/theme/media/media.scss | 13 - tg-web/src/theme/media/pagination.scss | 15 - tg-web/src/theme/media/personal.scss | 16 - tg-web/src/theme/media/scrollbar.scss | 56 - tg-web/src/theme/media/tagsView.scss | 11 - tg-web/src/theme/mixins/index.scss | 56 - tg-web/src/theme/other.scss | 36 - tg-web/src/theme/waves.scss | 101 - tg-web/src/types/globalInterface.ts | 4 - tg-web/src/utils/arrayOperation.ts | 67 - tg-web/src/utils/authDirective.ts | 40 - tg-web/src/utils/authFunction.ts | 38 - tg-web/src/utils/commonFunction.ts | 65 - tg-web/src/utils/copy.ts | 47 - tg-web/src/utils/customDirective.ts | 178 - tg-web/src/utils/debounce.ts | 11 - tg-web/src/utils/directive.ts | 18 - tg-web/src/utils/download.ts | 17 - tg-web/src/utils/filter.ts | 45 - tg-web/src/utils/formatTime.ts | 148 - tg-web/src/utils/getStyleSheets.ts | 101 - tg-web/src/utils/index.ts | 18 - tg-web/src/utils/loading.ts | 42 - tg-web/src/utils/menu.ts | 45 - tg-web/src/utils/normalRules.ts | 5 - tg-web/src/utils/other.ts | 200 - tg-web/src/utils/param.ts | 15 - tg-web/src/utils/random.ts | 7 - tg-web/src/utils/renderComponents.ts | 109 - tg-web/src/utils/setIconfont.ts | 48 - tg-web/src/utils/storage.ts | 59 - tg-web/src/utils/theme.ts | 59 - tg-web/src/utils/toolsValidate.ts | 378 -- tg-web/src/utils/wartermark.ts | 47 - tg-web/src/views/error/401.vue | 90 - tg-web/src/views/error/404.vue | 91 - tg-web/src/views/home/index.vue | 24 - .../views/strategy/experiment/experiment.vue | 934 ---- .../strategy/experiment/historyVersion.vue | 109 - .../strategy/experiment/requireConfig.vue | 51 - .../strategy/experiment/showX6params.vue | 131 - .../views/strategy/experiment/updateForm.vue | 62 - tg-web/src/views/strategy/experiment/vis.vue | 680 --- .../src/views/strategy/module/ModuleBox.vue | 201 - tg-web/src/views/strategy/module/module.vue | 126 - tg-web/src/views/strategy/scenes/formData.vue | 245 - tg-web/src/views/strategy/scenes/scenes.vue | 347 -- tg-web/src/views/strategy/system/formData.vue | 187 - tg-web/src/views/strategy/system/system.vue | 333 -- tg-web/tsconfig.json | 73 - tg-web/vite.config.ts | 71 - {tg-core/wfengine => wfengine}/condition.go | 0 {tg-core/wfengine => wfengine}/engine.go | 158 +- .../engine_from_apollo.go | 61 +- .../wfengine => wfengine}/engine_from_file.go | 18 +- .../engine_from_redis.go | 85 +- wfengine/flow_selector.go | 15 + wfengine/group_selector.go | 39 + .../inneraction/timeout_action.go | 6 +- {tg-core/wfengine => wfengine}/module_base.go | 14 +- .../random_selector.go | 49 +- .../wfengine => wfengine}/scene_module.go | 0 .../test/condition_external.go | 0 {tg-core/wfengine => wfengine}/time_waiter.go | 0 {tg-core/wfengine => wfengine}/workflow.go | 217 +- {tg-core/wfengine => wfengine}/workflow.json | 0 255 files changed, 851 insertions(+), 28103 deletions(-) create mode 100644 common/crontask/new_register_task.go create mode 100644 common/crontask/register_task.go rename {tg-core/common => common}/path/init.go (60%) rename {tg-core/common => common}/timeutils/time_coster.go (49%) create mode 100644 common/tlog/count_logger.go create mode 100644 common/tlog/see_count_logger.go create mode 100644 common/utils/recover.go create mode 100644 conf/tlog.xml create mode 100644 consts/global_c.go create mode 100644 go.mod create mode 100644 go.sum rename tg-core/main.go => main.go (60%) rename {tg-core/model => model}/experiment.go (100%) rename {tg-core/model => model}/strategy_context.go (38%) delete mode 100644 tg-core/README.md delete mode 100644 tg-core/common/cache/dicache.go delete mode 100644 tg-core/common/crontask/new_register_task.go delete mode 100644 tg-core/common/crontask/register_task.go delete mode 100644 tg-core/common/httpserv/init.go delete mode 100644 tg-core/common/ipaddrs/ips.go delete mode 100644 tg-core/common/lock/lease_lock.go delete mode 100644 tg-core/common/lock/lock_message.go delete mode 100644 tg-core/common/lock/redis_lease_lock.go delete mode 100644 tg-core/common/metric/metric_service.go delete mode 100644 tg-core/common/mysql/init.go delete mode 100644 tg-core/common/redis/init.go delete mode 100644 tg-core/common/tlog/init.go delete mode 100644 tg-core/common/utils/convert.go delete mode 100644 tg-core/common/utils/recover.go delete mode 100644 tg-core/common/utils/stringsUtil.go delete mode 100644 tg-core/conf/init.go delete mode 100644 tg-core/consts/dltag.go delete mode 100644 tg-core/consts/genrec_config_c.go delete mode 100644 tg-core/consts/global_c.go delete mode 100644 tg-core/consts/ranker_config_status.go delete mode 100644 tg-core/consts/redis_keys.go delete mode 100644 tg-core/dispatcher/base_dispatcher.go delete mode 100644 tg-core/dispatcher/public_log.go delete mode 100644 tg-core/go.mod delete mode 100644 tg-core/go.sum delete mode 100644 tg-core/model/algo_model.go delete mode 100644 tg-core/model/apollo_config.go delete mode 100644 tg-core/model/dimension.go delete mode 100644 tg-core/model/down_grade.go delete mode 100644 tg-core/model/omega_info.go delete mode 100644 tg-core/model/recall_config.go delete mode 100644 tg-core/model/system_config.go delete mode 100644 tg-core/model/trace_info.go delete mode 100644 tg-core/model/workflow.go delete mode 100755 tg-core/reset_import.sh delete mode 100644 tg-core/router/router.go delete mode 100644 tg-core/store/algomodel_index_map.go delete mode 100644 tg-core/store/debug_info_list.go delete mode 100644 tg-core/store/dimension_index_map.go delete mode 100644 tg-core/store/machine_map.go delete mode 100644 tg-core/store/workflow_map.go delete mode 100644 tg-core/wfengine/action_counter.go delete mode 100644 tg-core/wfengine/apollo/apollo_config.go delete mode 100644 tg-core/wfengine/logger_interface.go delete mode 100644 tg-service/README.md delete mode 100644 tg-service/common/logs/dltag.go delete mode 100644 tg-service/common/template/init.go delete mode 100644 tg-service/conf/init.go delete mode 100644 tg-service/conf/mysql_compile/app_config delete mode 100644 tg-service/conf/mysql_compile/scene_config delete mode 100644 tg-service/conf/mysql_compile/workflow delete mode 100644 tg-service/constant/const.go delete mode 100644 tg-service/controller/controller.go delete mode 100644 tg-service/controller/workflow_controller.go delete mode 100644 tg-service/cron/cron_task.go delete mode 100644 tg-service/go.mod delete mode 100644 tg-service/idl/types.go delete mode 100644 tg-service/idl/workflow.go delete mode 100644 tg-service/logic/appconfigadmin/add_update_app_config.go delete mode 100644 tg-service/logic/appconfigadmin/delete_app_config.go delete mode 100644 tg-service/logic/appconfigadmin/export_app_config.go delete mode 100644 tg-service/logic/appconfigadmin/select_app_config.go delete mode 100644 tg-service/logic/common.go delete mode 100644 tg-service/logic/login.go delete mode 100644 tg-service/logic/sceneadmin/add_update_scene.go delete mode 100644 tg-service/logic/sceneadmin/delete_scene.go delete mode 100644 tg-service/logic/sceneadmin/select_scene_conf.go delete mode 100644 tg-service/logic/select.go delete mode 100644 tg-service/logic/workflowadmin/add_update_workflow.go delete mode 100644 tg-service/logic/workflowadmin/add_workflow.go delete mode 100644 tg-service/logic/workflowadmin/delete_workflow.go delete mode 100644 tg-service/logic/workflowadmin/export_workflow.go delete mode 100644 tg-service/logic/workflowadmin/flowchecker/checker.go delete mode 100644 tg-service/logic/workflowadmin/flowchecker/checker_test.go delete mode 100644 tg-service/logic/workflowadmin/get_save_workflow.go delete mode 100644 tg-service/logic/workflowadmin/select_workflow_config.go delete mode 100644 tg-service/logic/workflowadmin/update_workflow.go delete mode 100644 tg-service/main.go delete mode 100644 tg-web/CHANGELOG.md delete mode 100644 tg-web/LICENSE delete mode 100644 tg-web/README.md delete mode 100644 tg-web/build-exclude-list.txt delete mode 100644 tg-web/env/.env delete mode 100644 tg-web/env/.env.alpha delete mode 100644 tg-web/env/.env.pre delete mode 100644 tg-web/env/.env.production delete mode 100644 tg-web/index.html delete mode 100644 tg-web/package-lock.json delete mode 100644 tg-web/package.json delete mode 100644 tg-web/plugins.d.ts delete mode 100644 tg-web/server.conf delete mode 100644 tg-web/shim.d.ts delete mode 100644 tg-web/source.d.ts delete mode 100644 tg-web/src/App.vue delete mode 100644 tg-web/src/api/request.ts delete mode 100644 tg-web/src/api/strategy/index.ts delete mode 100644 tg-web/src/api/strategy/services.ts delete mode 100644 tg-web/src/api/strategy/types.ts delete mode 100644 tg-web/src/api/strategy/utils.ts delete mode 100644 tg-web/src/components/g6/flow.vue delete mode 100644 tg-web/src/components/g6/g6.ts delete mode 100644 tg-web/src/components/svgIcon/index.vue delete mode 100644 tg-web/src/config.ts delete mode 100644 tg-web/src/i18n/index.ts delete mode 100644 tg-web/src/i18n/lang/en.ts delete mode 100644 tg-web/src/i18n/lang/zh-cn.ts delete mode 100644 tg-web/src/i18n/lang/zh-tw.ts delete mode 100644 tg-web/src/i18n/pages/formI18n/en.ts delete mode 100644 tg-web/src/i18n/pages/formI18n/zh-cn.ts delete mode 100644 tg-web/src/i18n/pages/formI18n/zh-tw.ts delete mode 100644 tg-web/src/i18n/pages/login/en.ts delete mode 100644 tg-web/src/i18n/pages/login/zh-cn.ts delete mode 100644 tg-web/src/i18n/pages/login/zh-tw.ts delete mode 100644 tg-web/src/layout/component/aside.vue delete mode 100644 tg-web/src/layout/component/columnsAside.vue delete mode 100644 tg-web/src/layout/component/header.vue delete mode 100644 tg-web/src/layout/component/main.vue delete mode 100644 tg-web/src/layout/footer/index.vue delete mode 100644 tg-web/src/layout/index.vue delete mode 100644 tg-web/src/layout/lockScreen/index.vue delete mode 100644 tg-web/src/layout/logo/index.vue delete mode 100644 tg-web/src/layout/main/classic.vue delete mode 100644 tg-web/src/layout/main/columns.vue delete mode 100644 tg-web/src/layout/main/defaults.vue delete mode 100644 tg-web/src/layout/main/transverse.vue delete mode 100644 tg-web/src/layout/navBars/breadcrumb/breadcrumb.vue delete mode 100644 tg-web/src/layout/navBars/breadcrumb/business.vue delete mode 100644 tg-web/src/layout/navBars/breadcrumb/closeFull.vue delete mode 100644 tg-web/src/layout/navBars/breadcrumb/index.vue delete mode 100644 tg-web/src/layout/navBars/breadcrumb/search.vue delete mode 100644 tg-web/src/layout/navBars/breadcrumb/setings.vue delete mode 100644 tg-web/src/layout/navBars/breadcrumb/user.vue delete mode 100644 tg-web/src/layout/navBars/breadcrumb/userNews.vue delete mode 100644 tg-web/src/layout/navBars/index.vue delete mode 100644 tg-web/src/layout/navBars/tagsView/contextmenu.vue delete mode 100644 tg-web/src/layout/navBars/tagsView/tagsView.vue delete mode 100644 tg-web/src/layout/navMenu/horizontal.vue delete mode 100644 tg-web/src/layout/navMenu/subItem.vue delete mode 100644 tg-web/src/layout/navMenu/vertical.vue delete mode 100644 tg-web/src/layout/routerView/iframes.vue delete mode 100644 tg-web/src/layout/routerView/link.vue delete mode 100644 tg-web/src/layout/routerView/parent.vue delete mode 100644 tg-web/src/main.ts delete mode 100644 tg-web/src/plugins/naive.ts delete mode 100644 tg-web/src/router/backEnd.ts delete mode 100644 tg-web/src/router/frontEnd.ts delete mode 100644 tg-web/src/router/index.ts delete mode 100644 tg-web/src/router/route.ts delete mode 100644 tg-web/src/stores/index.ts delete mode 100644 tg-web/src/stores/interface/index.ts delete mode 100644 tg-web/src/stores/keepAliveNames.ts delete mode 100644 tg-web/src/stores/requestOldRoutes.ts delete mode 100644 tg-web/src/stores/routesList.ts delete mode 100644 tg-web/src/stores/tagsViewRoutes.ts delete mode 100644 tg-web/src/stores/themeConfig.ts delete mode 100644 tg-web/src/stores/userInfo.ts delete mode 100644 tg-web/src/theme/app.scss delete mode 100644 tg-web/src/theme/common/transition.scss delete mode 100644 tg-web/src/theme/dark.scss delete mode 100644 tg-web/src/theme/element.scss delete mode 100644 tg-web/src/theme/iconSelector.scss delete mode 100644 tg-web/src/theme/index.scss delete mode 100644 tg-web/src/theme/loading.scss delete mode 100644 tg-web/src/theme/media/chart.scss delete mode 100644 tg-web/src/theme/media/cityLinkage.scss delete mode 100644 tg-web/src/theme/media/date.scss delete mode 100644 tg-web/src/theme/media/dialog.scss delete mode 100644 tg-web/src/theme/media/error.scss delete mode 100644 tg-web/src/theme/media/form.scss delete mode 100644 tg-web/src/theme/media/home.scss delete mode 100644 tg-web/src/theme/media/index.scss delete mode 100644 tg-web/src/theme/media/layout.scss delete mode 100644 tg-web/src/theme/media/login.scss delete mode 100644 tg-web/src/theme/media/media.scss delete mode 100644 tg-web/src/theme/media/pagination.scss delete mode 100644 tg-web/src/theme/media/personal.scss delete mode 100644 tg-web/src/theme/media/scrollbar.scss delete mode 100644 tg-web/src/theme/media/tagsView.scss delete mode 100644 tg-web/src/theme/mixins/index.scss delete mode 100644 tg-web/src/theme/other.scss delete mode 100644 tg-web/src/theme/waves.scss delete mode 100644 tg-web/src/types/globalInterface.ts delete mode 100644 tg-web/src/utils/arrayOperation.ts delete mode 100644 tg-web/src/utils/authDirective.ts delete mode 100644 tg-web/src/utils/authFunction.ts delete mode 100644 tg-web/src/utils/commonFunction.ts delete mode 100644 tg-web/src/utils/copy.ts delete mode 100644 tg-web/src/utils/customDirective.ts delete mode 100644 tg-web/src/utils/debounce.ts delete mode 100644 tg-web/src/utils/directive.ts delete mode 100644 tg-web/src/utils/download.ts delete mode 100644 tg-web/src/utils/filter.ts delete mode 100644 tg-web/src/utils/formatTime.ts delete mode 100644 tg-web/src/utils/getStyleSheets.ts delete mode 100644 tg-web/src/utils/index.ts delete mode 100644 tg-web/src/utils/loading.ts delete mode 100644 tg-web/src/utils/menu.ts delete mode 100644 tg-web/src/utils/normalRules.ts delete mode 100644 tg-web/src/utils/other.ts delete mode 100644 tg-web/src/utils/param.ts delete mode 100644 tg-web/src/utils/random.ts delete mode 100644 tg-web/src/utils/renderComponents.ts delete mode 100644 tg-web/src/utils/setIconfont.ts delete mode 100644 tg-web/src/utils/storage.ts delete mode 100644 tg-web/src/utils/theme.ts delete mode 100644 tg-web/src/utils/toolsValidate.ts delete mode 100644 tg-web/src/utils/wartermark.ts delete mode 100644 tg-web/src/views/error/401.vue delete mode 100644 tg-web/src/views/error/404.vue delete mode 100644 tg-web/src/views/home/index.vue delete mode 100644 tg-web/src/views/strategy/experiment/experiment.vue delete mode 100644 tg-web/src/views/strategy/experiment/historyVersion.vue delete mode 100644 tg-web/src/views/strategy/experiment/requireConfig.vue delete mode 100644 tg-web/src/views/strategy/experiment/showX6params.vue delete mode 100644 tg-web/src/views/strategy/experiment/updateForm.vue delete mode 100644 tg-web/src/views/strategy/experiment/vis.vue delete mode 100644 tg-web/src/views/strategy/module/ModuleBox.vue delete mode 100644 tg-web/src/views/strategy/module/module.vue delete mode 100644 tg-web/src/views/strategy/scenes/formData.vue delete mode 100644 tg-web/src/views/strategy/scenes/scenes.vue delete mode 100644 tg-web/src/views/strategy/system/formData.vue delete mode 100644 tg-web/src/views/strategy/system/system.vue delete mode 100644 tg-web/tsconfig.json delete mode 100644 tg-web/vite.config.ts rename {tg-core/wfengine => wfengine}/condition.go (100%) rename {tg-core/wfengine => wfengine}/engine.go (81%) rename {tg-core/wfengine => wfengine}/engine_from_apollo.go (61%) rename {tg-core/wfengine => wfengine}/engine_from_file.go (88%) rename {tg-core/wfengine => wfengine}/engine_from_redis.go (31%) create mode 100644 wfengine/flow_selector.go create mode 100644 wfengine/group_selector.go rename {tg-core/wfengine => wfengine}/inneraction/timeout_action.go (84%) rename {tg-core/wfengine => wfengine}/module_base.go (82%) rename tg-core/wfengine/flow_splitter.go => wfengine/random_selector.go (30%) rename {tg-core/wfengine => wfengine}/scene_module.go (100%) rename {tg-core/wfengine => wfengine}/test/condition_external.go (100%) rename {tg-core/wfengine => wfengine}/time_waiter.go (100%) rename {tg-core/wfengine => wfengine}/workflow.go (65%) rename {tg-core/wfengine => wfengine}/workflow.json (100%) diff --git a/.gitignore b/.gitignore index f9cb365..61ebe7d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +bin/ vendor/ .idea/ output/ @@ -9,4 +10,3 @@ rebuild.sh tg-flow log/ app.conf -tg-example/ diff --git a/common/crontask/new_register_task.go b/common/crontask/new_register_task.go new file mode 100644 index 0000000..be8b96e --- /dev/null +++ b/common/crontask/new_register_task.go @@ -0,0 +1,41 @@ +package crontask +// +//import ( +// "context" +// "github.com/didi/tg-flow/common/tlog" +// "reflect" +// "time" +//) +// +//const( +// TagCronTask = " _cron_task" +//) +//// 定时任务接口 +//type TaskInterface interface { +// Run() +//} +// +///* +//********************参数含义*************** +// +// task:定时任务结构体 +// period:定时周期的单位:秒 +// flag:是否在系统启动前启动定时任务 +// +//****************************************** +//*/ +//func StartTask(task TaskInterface, period time.Duration, flag bool) { +// ctx := context.TODO() +// taskName := reflect.TypeOf(task).String() +// +// defer RecoverTaskRun(taskName, ctx) +// if flag { //如果是需要在系统启动前启动定时任务,那么定时任务正式开始执行时间后延一个周期 +// time.Sleep(period * time.Second) +// } +// +// for { +// tlog.Handler.Infof(ctx, TagCronTask, "taskName=%v is start.", taskName) +// task.Run() +// time.Sleep(period * time.Second) +// } +//} diff --git a/common/crontask/register_task.go b/common/crontask/register_task.go new file mode 100644 index 0000000..c937e55 --- /dev/null +++ b/common/crontask/register_task.go @@ -0,0 +1,96 @@ +package crontask +// +//import ( +// "context" +// "crypto/rand" +// "fmt" +// "github.com/didi/tg-flow/common/tlog" +// "github.com/robfig/cron" +// "math" +// "math/big" +// "reflect" +// "strconv" +// "strings" +//) +// +////定时任务管理实例 +//var CronTask *cron.Cron = cron.New() +// +////定时任务管道 +//var ChanMap map[string]chan interface{} = make(map[string]chan interface{}) +// +///**注册定时任务 +// taskTime:定时任务的周期 +// job:具体的定时任务对象 +// ch:job任务中的定时管道 +// flag:是否随系统启动时,执行一次该任务 +//**/ +//func RegisterTask(taskTime string, job cron.Job, flag bool) { +// //定时任务管道初始化 +// name := reflect.TypeOf(job) +// ChanMap[name.Name()] = make(chan interface{}, 1) +// +// //是否随系统启动执行 +// if flag { +// job.Run() +// } +// +// //如果是秒以上的定时周期,则给一个随机的秒,以避免同台机器并发执行任务,给相应机器造成并发压力 +// newTaskTime := "" +// tempTimes := strings.Split(taskTime, " ") +// if tempTimes[0] == "0" || tempTimes[0] == "*" { +// for i := 1; i < len(tempTimes); i++ { +// newTaskTime += " " + tempTimes[i] +// } +// newTaskTime = strconv.FormatInt(RangeRand(0, 59), 10) + newTaskTime +// // time.Sleep(1 * time.Second) +// // newTaskTime = strconv.Itoa(time.Now().Second()) + newTaskTime +// } else { +// newTaskTime = taskTime +// } +// +// //注册任务 +// CronTask.AddJob(newTaskTime, job) +//} +// +///** +// 启动定时任务 +//**/ +//func TaskStart() { +// defer recoverTaskStart() +// CronTask.Start() +//} +// +////定时任务运行异常,捕获异常后,需要释放该任务的任务管道 +//func RecoverTaskRun(taskName string, ctx context.Context) { +// if err := recover(); err != nil { +// content := fmt.Sprintf("taskName=%v||err=%v", taskName, err) +// tlog.Handler.ErrorCount(ctx, "crontask.recoverTaskRun", content) +// } +//} +// +////所有定时任务启动失败 +//func recoverTaskStart() { +// if err := recover(); err != nil { +// content := fmt.Sprintf("all task start fail||err=%v", err) +// tlog.Handler.ErrorCount(context.TODO(), "crontask.recoverTaskStart", content) +// } +//} +// +//// 生成区间[-m, n]的安全随机数 +//func RangeRand(min, max int64) int64 { +// if min > max { +// panic("the min is greater than max!") +// } +// +// if min < 0 { +// f64Min := math.Abs(float64(min)) +// i64Min := int64(f64Min) +// result, _ := rand.Int(rand.Reader, big.NewInt(max+1+i64Min)) +// +// return result.Int64() - i64Min +// } else { +// result, _ := rand.Int(rand.Reader, big.NewInt(max-min+1)) +// return min + result.Int64() +// } +//} diff --git a/tg-core/common/path/init.go b/common/path/init.go similarity index 60% rename from tg-core/common/path/init.go rename to common/path/init.go index c7ac8bc..6c4c220 100644 --- a/tg-core/common/path/init.go +++ b/common/path/init.go @@ -8,20 +8,21 @@ import ( const ( TEST_MAPBASE_PATH = "TEST_MAPBASE_PATH" ) + // Root current dir var Root string func init() { - log.Println("tg-core path init start...") + log.Println("tg-flow path init start...") - if rootPath := os.Getenv(TEST_MAPBASE_PATH);rootPath != ""{ + if rootPath := os.Getenv(TEST_MAPBASE_PATH); rootPath != "" { Root = rootPath - }else { + } else { var err error Root, err = os.Getwd() if err != nil { log.Fatal("Initialize Root error: ", err) } } - log.Println("tg-core path init successful, path="+Root) -} \ No newline at end of file + log.Println("tg-flow path init successful, path=" + Root) +} diff --git a/tg-core/common/timeutils/time_coster.go b/common/timeutils/time_coster.go similarity index 49% rename from tg-core/common/timeutils/time_coster.go rename to common/timeutils/time_coster.go index df192ef..5da9412 100644 --- a/tg-core/common/timeutils/time_coster.go +++ b/common/timeutils/time_coster.go @@ -15,36 +15,68 @@ import ( ) type TimeCoster struct { - startTime time.Time - costMap *sync.Map + startTime time.Time + timeUnit string + costMap *sync.Map } const ( - totalCost = "totalCost" + TotalCost = "totalCost" + TimeUnitSecond = "s" + TimeUnitMillSecond = "ms" + TimeUnitNanoSecond = "ns" ) +// 老的,即将被下面NewTimeCosterUnit替换 func NewTimeCoster() *TimeCoster { t := new(TimeCoster) - t.costMap = &sync.Map{} + t.timeUnit = TimeUnitMillSecond + t.costMap = &sync.Map{} + t.StartCount() + return t +} + +/* +* + + timeUnit三种取值: + s : 秒 + ms:毫秒,缺省 + ns: 纳秒 +*/ +func NewTimeCosterUnit(timeUnit string) *TimeCoster { + t := new(TimeCoster) + //不想panc, 兼容一下 + if timeUnit != TimeUnitNanoSecond && timeUnit != TimeUnitMillSecond && timeUnit != TimeUnitSecond { + //todo add warning + timeUnit = TimeUnitMillSecond + } + t.timeUnit = timeUnit + t.costMap = &sync.Map{} t.StartCount() return t } func (t *TimeCoster) StartCount() { t.startTime = time.Now() - t.costMap.Store(totalCost, t.startTime.UnixNano()) + t.costMap.Store(TotalCost, t.startTime.UnixNano()) } func (t *TimeCoster) StopCount() { - st, ok := t.costMap.Load(totalCost) + st, ok := t.costMap.Load(TotalCost) if !ok { - t.costMap.Delete(totalCost) + t.costMap.Delete(TotalCost) return } startTime := st.(int64) - costTime := (time.Now().UnixNano() - startTime)/1000000 - t.costMap.Store(totalCost, costTime) + costTime := time.Now().UnixNano() - startTime + if t.timeUnit == TimeUnitMillSecond { + costTime = costTime / 1000000 + } else if t.timeUnit == TimeUnitSecond { + costTime = costTime / 1000000000 + } + t.costMap.Store(TotalCost, costTime) } func (t *TimeCoster) StartSectionCount(key string) { @@ -59,21 +91,27 @@ func (t *TimeCoster) StopSectionCount(key string) { } startTime := st.(int64) - costTime := (time.Now().UnixNano() - startTime)/1000000 + costTime := time.Now().UnixNano() - startTime + if t.timeUnit == TimeUnitMillSecond { + costTime = costTime / 1000000 + } else if t.timeUnit == TimeUnitSecond { + costTime = costTime / 1000000000 + } + t.costMap.Store(key, costTime) } func (t *TimeCoster) GetSectionCount(key string) (int64, error) { - ct,oks := t.costMap.Load(key) + ct, oks := t.costMap.Load(key) if !oks { return 0, fmt.Errorf("no time count info:%v", key) } - costTime:=ct.(int64) + costTime := ct.(int64) return costTime, nil } -func (t *TimeCoster) GetAllCounts() (map[string]int64) { +func (t *TimeCoster) GetAllCounts() map[string]int64 { kv := make(map[string]int64) t.costMap.Range(func(k, v interface{}) bool { key := fmt.Sprintf("%v", k) @@ -98,14 +136,14 @@ func (t *TimeCoster) ToCountString() string { }) strs := buffer.String() - if strings.HasSuffix(strs,"||") { - strs = strs[0:len(strs)-2] + if strings.HasSuffix(strs, "||") { + strs = strs[0 : len(strs)-2] } return strs } -func (t *TimeCoster) SetSectionCount(key string, cost int64){ +func (t *TimeCoster) SetSectionCount(key string, cost int64) { t.costMap.Store(key, cost) } @@ -113,14 +151,20 @@ func (t *TimeCoster) GetStartTime() time.Time { return t.startTime } -//目前没用,就不要了 -/*func (t *TimeCoster) Merge(t1 *TimeCoster){ +func (t *TimeCoster) Merge(t1 *TimeCoster) { if t1 == nil { return } + if t.timeUnit != t1.timeUnit { + //TODO 不允许,先忽略吧 + return + } + t1.costMap.Range(func(k, v interface{}) bool { - t.costMap.Store(k, v) + if k != TotalCost { + t.costMap.Store(k, v) + } return true }) -}*/ +} diff --git a/common/tlog/count_logger.go b/common/tlog/count_logger.go new file mode 100644 index 0000000..c4062a2 --- /dev/null +++ b/common/tlog/count_logger.go @@ -0,0 +1,140 @@ +package tlog + +import ( + "context" + "fmt" + "path/filepath" + "runtime" + "strings" +) + +// should be assigned when service init +var Handler *CountLogger + +type ICounter interface{ + Count(metricName string) + CounterByTags(metricName string, tags map[string]string) +} + +type ILogger interface { + /*Trace(tag string, args ...interface{}) + Tracef(ctx context.Context, tag string, format string, args ...interface{}) + Warn(tag string, args ...interface{}) + Warnf(ctx context.Context, tag string, format string, args ...interface{}) + Fatal(tag string, args ...interface{}) + Fatalf(ctx context.Context, tag string, format string, args ...interface{})*/ + + Debug(tag string, args ...interface{}) + Debugf(ctx context.Context, tag string, format string, args ...interface{}) + + Info(tag string, args ...interface{}) + Infof(ctx context.Context, tag string, format string, args ...interface{}) + + Error(tag string, args ...interface{}) + Errorf(ctx context.Context, tag string, format string, args ...interface{}) + //Public(ctx context.Context, key string, pairs map[string]interface{}, isPLid bool) + + // ErrorCount :print error log and send count to monitor server if exist + // ctx : context.Context + // tag: key which to be counted by + //ErrorCount(ctx context.Context, metricName string, content string) + + // ErrorCountWithTags :print error log and send count to monitor server if exist + // ctx : context.Context + // tag: keys which to be counted by + //ErrorCountWithTags(ctx context.Context, metricName string, tags map[string]string, content string) +} + +type CountLogger struct { + log ILogger + count ICounter +} + +func SetCountLogger(logger ILogger, counter ICounter){ + Handler = NewCountLogger(logger, counter) +} + +func NewCountLogger(logger ILogger, counter ICounter) *CountLogger { + return &CountLogger{ + log: logger, + count: counter, + } +} + +func (c *CountLogger) Debug(tag string, args ...interface{}) { + c.log.Debug(tag, args) +} + +func (c *CountLogger) Debugf(ctx context.Context, tag string, format string, args ...interface{}) { + c.log.Debugf(ctx, tag, format, args) +} + +func (c *CountLogger) Info(tag string, args ...interface{}) { + c.log.Info(tag, args) +} + +func (c *CountLogger) Infof(ctx context.Context, tag string, format string, args ...interface{}) { + c.log.Infof(ctx, tag, format, args) +} + +func (c *CountLogger) Error(tag string, args ...interface{}) { + c.log.Error(tag, args) +} + +func (c *CountLogger) Errorf(ctx context.Context, tag string, format string, args ...interface{}) { + c.log.Errorf(ctx, tag, format, args) +} + +func (c *CountLogger) Public(ctx context.Context, key string, pairs map[string]interface{}, isPLid bool) { + //公司内部业务日志打印专用,可根据业务需要在继承struct中自行实现,也可忽略 +} + +// ErrorCount :print error log and send count to monitor server if exist +// ctx : context.Context +// tag: key which to be counted by +func (c *CountLogger) ErrorCount(ctx context.Context, metricName string, content string) { + path := c.FormatLogPath() + c.Errorf(ctx, metricName, "etype=%v||log_path=%v||error=%v", metricName, path, strings.Replace(content, "\n", "", -1)) + //按metricName上报错误统计,如果有就加,没有就省略吧。 + if c.count != nil { + c.count.Count(metricName) + } +} + +// ErrorCountWithTags :print error log and send count to monitor server if exist +// ctx : context.Context +// tag: keys which to be counted by +func (c *CountLogger) ErrorCountWithTags(ctx context.Context, metricName string, tags map[string]string, content string) { + path := c.FormatLogPath() + c.log.Errorf(ctx, metricName, "etype=%v||log_path=%v||error=%v", metricName, path, strings.Replace(content, "\n", "", -1)) + //按metricName上报错误统计,如果有就加,没有就省略吧。 + if c.count != nil { + c.count.CounterByTags(metricName, tags) + } +} + +func (c *CountLogger) FormatLogPath() string { + filename, line, path, ok, index := "", 0, "", false, 1 + for { + if index >= 10 { + break + } + _, filename, line, ok = runtime.Caller(index) + if ok { + filename = filepath.Base(filename) + } + if filename == "" { + break + } + index++ + if strings.Contains(filename, "autogenerated") || strings.Contains(filename, "asm_amd64") { + continue + } + if len(path) > 0 { + path = filename + ":" + fmt.Sprintf("%v", line) + "/" + path + } else { + path = filename + ":" + fmt.Sprintf("%v", line) + } + } + return path +} diff --git a/common/tlog/see_count_logger.go b/common/tlog/see_count_logger.go new file mode 100644 index 0000000..ced3a52 --- /dev/null +++ b/common/tlog/see_count_logger.go @@ -0,0 +1,50 @@ +/* + This is a sample class which show you how to implement the interface ICountLogger, and you can implement it by yourself +*/ +package tlog + +import ( + "context" + "github.com/cihub/seelog" + "log" +) + +type SeeCountLogger struct { + handler seelog.LoggerInterface + counter ICounter +} + +func InitCountLoggerFromSeelog(seeLogger seelog.LoggerInterface, counter ICounter) { + log.Println("tg-flow CountLogger init start...") + seeCountLogger := &SeeCountLogger{handler:seeLogger} + Handler = NewCountLogger(seeCountLogger,counter) + log.Println("tg-flow CountLogger init finished!!!") +} + +func (d *SeeCountLogger) Debug(tag string, args ...interface{}) { + d.handler.Debug(tag, args) +} + +func (d *SeeCountLogger) Debugf(ctx context.Context, tag string, format string, args ...interface{}) { + d.handler.Debugf(format, args) +} + +func (d *SeeCountLogger) Info(tag string, args ...interface{}) { + d.handler.Info(tag, args) +} + +func (d *SeeCountLogger) Infof(ctx context.Context, tag string, format string, args ...interface{}) { + d.handler.Infof(format, args) +} + +func (d *SeeCountLogger) Error(tag string, args ...interface{}) { + d.handler.Error(args) +} + +func (d *SeeCountLogger) Errorf(ctx context.Context, tag string, format string, args ...interface{}) { + d.handler.Errorf(format, args) +} + +func (d *SeeCountLogger) Public(ctx context.Context, key string, pairs map[string]interface{}, isPLid bool) { + // you can fullfill business log printer here +} diff --git a/common/utils/recover.go b/common/utils/recover.go new file mode 100644 index 0000000..c7bd065 --- /dev/null +++ b/common/utils/recover.go @@ -0,0 +1,13 @@ +package utils + +import ( + "context" + "fmt" + "github.com/didi/tg-flow/common/tlog" +) + +func Recover(ctx context.Context, tag string) { + if err := recover(); err != nil { + tlog.Handler.ErrorCount(ctx, tag, fmt.Sprintf("Recover system panic : %v", err)) + } +} diff --git a/conf/tlog.xml b/conf/tlog.xml new file mode 100644 index 0000000..8befcde --- /dev/null +++ b/conf/tlog.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/consts/global_c.go b/consts/global_c.go new file mode 100644 index 0000000..1e642f1 --- /dev/null +++ b/consts/global_c.go @@ -0,0 +1,7 @@ +package consts + +const ( + FLOW_BY_ONLINE_RANDOM = 0 + FLOW_BY_CUSTOM = 1 + FLOW_BY_APOLLO = 3 +) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b981939 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/didi/tg-flow + +go 1.21.5 + +require ( + github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 + github.com/robfig/cron v1.2.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..83f069f --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= +github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= +github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= diff --git a/tg-core/main.go b/main.go similarity index 60% rename from tg-core/main.go rename to main.go index a506432..b4a8546 100644 --- a/tg-core/main.go +++ b/main.go @@ -1,11 +1,15 @@ package main import ( + "encoding/json" "fmt" + "github.com/didi/tg-flow/wfengine" + "github.com/didi/tg-flow/wfengine/test" + "io/ioutil" ) -/*func main(){ - path := "/Users/didi/gopath/src/github.com/didi/tg-flow/tg-core/wfengine/workflow.json" +func main() { + path := "/Users/didi/gopath/src/github.com/didi/tg-flow/wfengine/workflow.json" flowStr, err := readStrFromFile(path) if err != nil { fmt.Println("flowStr,err", flowStr, err) @@ -20,7 +24,7 @@ import ( fmt.Println("fcm===============>", fmt.Sprintf("%+v", string(fcm))) action := getAction() - params :=make([]interface{}, 2) + params := make([]interface{}, 2) params[0] = 4.4 params[1] = "4.4" ce := wfengine.GetCondExecutors() @@ -87,7 +91,7 @@ func getInAction() *wfengine.Action { return action } -func readStrFromFile(path string) (string, error){ +func readStrFromFile(path string) (string, error) { f, err := ioutil.ReadFile(path) if err != nil { return "", err @@ -95,46 +99,46 @@ func readStrFromFile(path string) (string, error){ return string(f), nil } -*/ type Action struct { - ActionType string `json:"action_type"` - ActionId string `json:"action_id"` - ActionName string `json:"action_name"` - NextActionIds []string `json:"next_action_ids"` - NextConditions []string `json:"next_conditions"` - PrevActionIds []string `json:"prev_action_ids"` - Description string `json:"description"` + ActionType string `json:"action_type"` + ActionId string `json:"action_id"` + ActionName string `json:"action_name"` + NextActionIds []string `json:"next_action_ids"` + NextConditions []string `json:"next_conditions"` + PrevActionIds []string `json:"prev_action_ids"` + Description string `json:"description"` } -func main() { - - //创建和初始化数组 - //使用简写声明 - action1 := Action{ - ActionType: "action-1", - ActionId: "", - ActionName: "", - NextActionIds: nil, - NextConditions: nil, - PrevActionIds: []string{"aaa","bbb","ccc"}, - Description: "", - } - - action2 := Action{ - ActionType: "action-2", - ActionId: "", - ActionName: "", - NextActionIds: nil, - NextConditions: nil, - PrevActionIds: action1.PrevActionIds, - Description: "", - } - - fmt.Println("action1: ", action1) - fmt.Println("action2:", action2) - - action1.PrevActionIds = nil - fmt.Println("action11: ", action1) - fmt.Println("action22:", action2) -} \ No newline at end of file +// +//func main() { +// +// //创建和初始化数组 +// //使用简写声明 +// action1 := Action{ +// ActionType: "action-1", +// ActionId: "", +// ActionName: "", +// NextActionIds: nil, +// NextConditions: nil, +// PrevActionIds: []string{"aaa", "bbb", "ccc"}, +// Description: "", +// } +// +// action2 := Action{ +// ActionType: "action-2", +// ActionId: "", +// ActionName: "", +// NextActionIds: nil, +// NextConditions: nil, +// PrevActionIds: action1.PrevActionIds, +// Description: "", +// } +// +// fmt.Println("action1: ", action1) +// fmt.Println("action2:", action2) +// +// action1.PrevActionIds = nil +// fmt.Println("action11: ", action1) +// fmt.Println("action22:", action2) +//} diff --git a/tg-core/model/experiment.go b/model/experiment.go similarity index 100% rename from tg-core/model/experiment.go rename to model/experiment.go diff --git a/tg-core/model/strategy_context.go b/model/strategy_context.go similarity index 38% rename from tg-core/model/strategy_context.go rename to model/strategy_context.go index 65b46fe..b9d453c 100644 --- a/tg-core/model/strategy_context.go +++ b/model/strategy_context.go @@ -10,7 +10,7 @@ import ( "context" "encoding/json" "errors" - trace "git.xiaojukeji.com/lego/context-go" + "github.com/didi/tg-flow/common/timeutils" "sync" ) @@ -18,113 +18,105 @@ const ( KEY_TIMEOUT_ACTION = "timeout_action" ) -type ModuleResultInfo struct { - Id string - StrategyName string - CostTime int64 -} - type StrategyContext struct { - AppId int64 - AppName string - SceneId int64 - FlowId int64 - IsLimited bool - IsDebug bool - CtxTrace *trace.DefaultTrace + AppId int64 + AppName string + SceneId int64 + FlowId int64 + IsLimited bool + IsDebug bool + //CtxTrace *trace.DefaultTrace UserId string Phone string + GroupName string ContextMap *sync.Map ActionResultMap map[string]map[string]string contextMutex sync.Mutex - - moduleResultMap *sync.Map + TC *timeutils.TimeCoster errMap *sync.Map ErrNo int32 ErrMsg string skipFlag bool skipMutex sync.Mutex - - ApolloInfo *ApolloConfig } func NewStrategyContext(ctx context.Context) *StrategyContext { strategyContext := new(StrategyContext) strategyContext.ContextMap = &sync.Map{} strategyContext.ActionResultMap = make(map[string]map[string]string) - strategyContext.moduleResultMap = &sync.Map{} strategyContext.errMap = &sync.Map{} - if ctxTrace, ok := trace.GetCtxTrace(ctx); ok { - strategyContext.CtxTrace = ctxTrace - } else { - strategyContext.CtxTrace = trace.NewDefaultTrace() - } + strategyContext.TC = timeutils.NewTimeCosterUnit(timeutils.TimeUnitMillSecond) + //if ctxTrace, ok := trace.GetCtxTrace(ctx); ok { + // strategyContext.CtxTrace = ctxTrace + //} else { + // strategyContext.CtxTrace = trace.NewDefaultTrace() + //} return strategyContext } -func (this *StrategyContext) Set(key string, value interface{}) { - this.ContextMap.Store(key, value) +func (s *StrategyContext) Set(key string, value interface{}) { + s.ContextMap.Store(key, value) } -func (this *StrategyContext) SetDebug(actionName string, key string, value interface{}) { - if !this.IsDebug { +func (s *StrategyContext) SetDebug(actionName string, key string, value interface{}) { + if !s.IsDebug { return } - this.contextMutex.Lock() + s.contextMutex.Lock() var debugStr string if resultJson, err := json.Marshal(value); err == nil { debugStr = string(resultJson) } - if _, ok := this.ActionResultMap[actionName]; !ok { - this.ActionResultMap[actionName] = make(map[string]string) + if _, ok := s.ActionResultMap[actionName]; !ok { + s.ActionResultMap[actionName] = make(map[string]string) } - this.ActionResultMap[actionName][key] = debugStr - this.contextMutex.Unlock() + s.ActionResultMap[actionName][key] = debugStr + s.contextMutex.Unlock() } -func (this *StrategyContext) Get(key string) interface{} { - value, _ := this.ContextMap.Load(key) +func (s *StrategyContext) Get(key string) interface{} { + value, _ := s.ContextMap.Load(key) return value } -func (this *StrategyContext) Skip(errNo int32, errMsg string) { - this.skipMutex.Lock() - this.ErrNo = errNo - this.ErrMsg = errMsg - this.skipFlag = true - this.skipMutex.Unlock() +func (s *StrategyContext) Skip(errNo int32, errMsg string) { + s.skipMutex.Lock() + s.ErrNo = errNo + s.ErrMsg = errMsg + s.skipFlag = true + s.skipMutex.Unlock() } -func (this *StrategyContext) IsSkip() bool { - return this.skipFlag +func (s *StrategyContext) IsSkip() bool { + return s.skipFlag } -func (this *StrategyContext) AddToArray(arrayKey string, value interface{}) { +func (s *StrategyContext) AddToArray(arrayKey string, value interface{}) { var itArray []interface{} - if itr, ok := this.ContextMap.Load(arrayKey); ok { + if itr, ok := s.ContextMap.Load(arrayKey); ok { itArray = itr.([]interface{}) itArray = append(itArray, value) } else { itArray = make([]interface{}, 0, 10) itArray = append(itArray, value) } - this.ContextMap.Store(arrayKey, itArray) + s.ContextMap.Store(arrayKey, itArray) } -func (this *StrategyContext) GetArray(arrayKey string) ([]interface{}, error) { - if itf, ok := this.ContextMap.Load(arrayKey); ok { +func (s *StrategyContext) GetArray(arrayKey string) ([]interface{}, error) { + if itf, ok := s.ContextMap.Load(arrayKey); ok { return itf.([]interface{}), nil } return nil, errors.New("No such key:" + arrayKey) } -func (this *StrategyContext) AddTimeoutAction(actionName string) { - this.AddToArray(KEY_TIMEOUT_ACTION, actionName) +func (s *StrategyContext) AddTimeoutAction(actionName string) { + s.AddToArray(KEY_TIMEOUT_ACTION, actionName) } -func (this *StrategyContext) GetTimeoutActions() []string { - itf, err := this.GetArray(KEY_TIMEOUT_ACTION) +func (s *StrategyContext) GetTimeoutActions() []string { + itf, err := s.GetArray(KEY_TIMEOUT_ACTION) if err != nil || len(itf) == 0 { return nil } @@ -136,24 +128,10 @@ func (this *StrategyContext) GetTimeoutActions() []string { return actionNames } -func (this *StrategyContext) SetModuleResult(actionId, actionName string, costTime int64) { - mri := &ModuleResultInfo{ - Id: actionId, - StrategyName: actionName, - CostTime: costTime, - } - - this.moduleResultMap.Store(actionId, mri) -} - -func (this *StrategyContext) GetModuleResultMap() *sync.Map { - return this.moduleResultMap -} - -func (this *StrategyContext) SetError(actionId string, err error) { - this.errMap.Store(actionId, err) +func (s *StrategyContext) SetError(actionId string, err error) { + s.errMap.Store(actionId, err) } -func (this *StrategyContext) GetErrorMap() *sync.Map { - return this.errMap +func (s *StrategyContext) GetErrorMap() *sync.Map { + return s.errMap } diff --git a/tg-core/README.md b/tg-core/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/tg-core/common/cache/dicache.go b/tg-core/common/cache/dicache.go deleted file mode 100644 index ea59481..0000000 --- a/tg-core/common/cache/dicache.go +++ /dev/null @@ -1,107 +0,0 @@ -/** -* function: local cache based on FreeCache -* Author : dayunzhangyunfeng@didiglobal.com -* Since : 2019-05-31 17:25:26 - */ - -package cache - -import ( - "context" - "fmt" - "git.xiaojukeji.com/nuwa/golibs/redis" - "github.com/coocood/freecache" -) - -const ( - LOCALCACHE_TTL = 1800 //unit:s -) - -type DiCache struct { - RedisCache *redis.Manager - Cache *freecache.Cache - Size int - TTL int -} - -func NewDiCache(redisCache *redis.Manager, size int, ttl int) *DiCache { - var freeCache = freecache.NewCache(size) - return &DiCache{ - RedisCache: redisCache, - Cache: freeCache, - Size: size, - TTL: ttl} -} - -func (this *DiCache) Get(ctx context.Context, key string) (string, error) { - vBytes, err := this.Cache.Get([]byte(key)) - if err == nil { - return string(vBytes), nil - } - - val, err := this.RedisCache.Get(ctx, key) - if err == nil { - this.Cache.Set([]byte(key), []byte(val), this.TTL) - } - - return val, err -} - -func (this *DiCache) GetMust(ctx context.Context, key, defaultValue string) string { - vBytes, err := this.Cache.Get([]byte(key)) - if err == nil { - return string(vBytes) - } - - val, err := this.RedisCache.Get(ctx, key) - if err == nil { - this.Cache.Set([]byte(key), []byte(val), this.TTL) - return val - } - - return defaultValue -} - -func (this *DiCache) MGet(ctx context.Context, keys []string) (map[string]string, error) { - valMap := make(map[string]string) - if keys == nil || len(keys) == 0 { - return valMap, nil - } - - notHitKeys := make([]string, 0, len(keys)) - for _, key := range keys { - vBytes, err := this.Cache.Get([]byte(key)) - if err == nil { - valMap[key] = string(vBytes) - } else { - notHitKeys = append(notHitKeys, key) - } - } - - notHitCount := len(notHitKeys) - if notHitCount == 0 { - return valMap, nil - } - - //不知道err不空时tempMap还是否可能有值,兜一下 - tempMap, err := this.RedisCache.MGet(ctx, notHitKeys) - if tempMap == nil || len(tempMap) == 0 { - return valMap, fmt.Errorf("redis keys not found all keys:%v, err:%v", notHitKeys, err) - } - - tempKeys := make([]string, 0, notHitCount) - for _, notHitKey := range notHitKeys { - if notHitVal, ok := tempMap[notHitKey]; ok { - valMap[notHitKey] = notHitVal - this.Cache.Set([]byte(notHitKey), []byte(notHitVal), this.TTL) - } else { - tempKeys = append(tempKeys, notHitKey) - } - } - - if len(tempKeys) > 0 { - err = fmt.Errorf("redis keys not found keys:%v", tempKeys) - } - - return valMap, err -} diff --git a/tg-core/common/crontask/new_register_task.go b/tg-core/common/crontask/new_register_task.go deleted file mode 100644 index a04b6af..0000000 --- a/tg-core/common/crontask/new_register_task.go +++ /dev/null @@ -1,35 +0,0 @@ -package crontask - -import ( - "context" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/consts" - "reflect" - "time" -) - -//定时任务接口 -type TaskInterface interface { - Run() -} - -/*********************参数含义*************** - task:定时任务结构体 - period:定时周期的单位:秒 - flag:是否在系统启动前启动定时任务 -*******************************************/ -func StartTask(task TaskInterface, period time.Duration, flag bool) { - ctx := context.TODO() - taskName := reflect.TypeOf(task).String() - - defer RecoverTaskRun(taskName, ctx) - if flag { //如果是需要在系统启动前启动定时任务,那么定时任务正式开始执行时间后延一个周期 - time.Sleep(period * time.Second) - } - - for { - tlog.Handler.Infof(ctx, consts.DLTagCronTask, "taskName=%v is start.", taskName) - task.Run() - time.Sleep(period * time.Second) - } -} diff --git a/tg-core/common/crontask/register_task.go b/tg-core/common/crontask/register_task.go deleted file mode 100644 index da80e06..0000000 --- a/tg-core/common/crontask/register_task.go +++ /dev/null @@ -1,97 +0,0 @@ -package crontask - -import ( - "context" - "crypto/rand" - "fmt" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/consts" - "github.com/robfig/cron" - "math" - "math/big" - "reflect" - "strconv" - "strings" -// "time" -) - -//定时任务管理实例 -var CronTask *cron.Cron = cron.New() - -//定时任务管道 -var ChanMap map[string]chan interface{} = make(map[string]chan interface{}) - -/**注册定时任务 - taskTime:定时任务的周期 - job:具体的定时任务对象 - ch:job任务中的定时管道 - flag:是否随系统启动时,执行一次该任务 -**/ -func RegisterTask(taskTime string, job cron.Job, flag bool) { - //定时任务管道初始化 - name := reflect.TypeOf(job) - ChanMap[name.Name()] = make(chan interface{}, 1) - - //是否随系统启动执行 - if flag { - job.Run() - } - - //如果是秒以上的定时周期,则给一个随机的秒,以避免同台机器并发执行任务,给相应机器造成并发压力 - newTaskTime := "" - tempTimes := strings.Split(taskTime, " ") - if tempTimes[0] == "0" || tempTimes[0] == "*" { - for i := 1; i < len(tempTimes); i++ { - newTaskTime += " " + tempTimes[i] - } - newTaskTime = strconv.FormatInt(RangeRand(0, 59), 10) + newTaskTime -// time.Sleep(1 * time.Second) -// newTaskTime = strconv.Itoa(time.Now().Second()) + newTaskTime - } else { - newTaskTime = taskTime - } - - //注册任务 - CronTask.AddJob(newTaskTime, job) -} - -/** - 启动定时任务 -**/ -func TaskStart() { - defer recoverTaskStart() - CronTask.Start() -} - -//定时任务运行异常,捕获异常后,需要释放该任务的任务管道 -func RecoverTaskRun(taskName string, ctx context.Context) { - if err := recover(); err != nil { - content := fmt.Sprintf("taskName=%v", taskName) - tlog.LogError(ctx, nil, consts.DLTagCronTask, "crontask.recoverTaskRun", content, fmt.Errorf("%v", err)) - } -} - -//所有定时任务启动失败 -func recoverTaskStart() { - if err := recover(); err != nil { - tlog.LogError(context.TODO(), nil, consts.DLTagCronTask, "crontask.recoverTaskStart", "all task start fail", fmt.Errorf("%v", err)) - } -} - -// 生成区间[-m, n]的安全随机数 -func RangeRand(min, max int64) int64 { - if min > max { - panic("the min is greater than max!") - } - - if min < 0 { - f64Min := math.Abs(float64(min)) - i64Min := int64(f64Min) - result, _ := rand.Int(rand.Reader, big.NewInt(max+1+i64Min)) - - return result.Int64() - i64Min - } else { - result, _ := rand.Int(rand.Reader, big.NewInt(max-min+1)) - return min + result.Int64() - } -} diff --git a/tg-core/common/httpserv/init.go b/tg-core/common/httpserv/init.go deleted file mode 100644 index 2d183ba..0000000 --- a/tg-core/common/httpserv/init.go +++ /dev/null @@ -1,41 +0,0 @@ -package httpserv - -import ( - "context" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/conf" - "github.com/didi/tg-flow/tg-core/router" - "git.xiaojukeji.com/nuwa/nuwa-go-httpclient" - ngs "git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2" - "log" - "time" -) - -var Handler ngs.HTTPServer - -func init() { - log.Println("tg-core httpserv init start...") - Handler = ngs.New(conf.Handler) - Handler.SetLogger(tlog.Handler) - Handler.SetRouter(router.Handler) - - Handler.RegisterOnShutdown(func() { - }) -} - -func SendHttpRequest(url string, timeOut int64, parmasMap interface{}) ([]byte, error) { - _, resp, err := httpclient.PostJSON(context.TODO(), url, parmasMap, httpclient.SetTimeout(time.Millisecond*time.Duration(timeOut))) - if err != nil { //重试2次 - for i := 0; i < 2; i++ { - _, resp, err = httpclient.PostJSON(context.TODO(), url, parmasMap, httpclient.SetTimeout(time.Millisecond*time.Duration(timeOut))) - if err == nil { - break - } - } - } - - if err != nil { - return nil, err - } - return resp, nil -} diff --git a/tg-core/common/ipaddrs/ips.go b/tg-core/common/ipaddrs/ips.go deleted file mode 100644 index 655b334..0000000 --- a/tg-core/common/ipaddrs/ips.go +++ /dev/null @@ -1,84 +0,0 @@ -package ipaddrs - -import ( - "context" - "fmt" - "git.xiaojukeji.com/gobiz/utils" - "net" - "net/http" - "strings" -) - -//ZYF 以下从nuwa-go-httpserver/middleware/trace.go里面拷贝过来的,便于实现GetClientIp -type ctxKey struct { - name string -} - -var ( - // RequestTimeKey ... - RequestTimeKey = ctxKey{"requestTime"} - // TraceRecordKey ... - TraceRecordKey = ctxKey{"traceRecord"} - // ExtraInfoReqOut ... - ExtraInfoReqOut = ctxKey{"extraReqOut"} -) - -// TraceRecord ... -type TraceRecord struct { - TraceId string - SpanId string - HintCode string - HintContent string - URL string - Params string - Method string - Host string - From string - FormatString string -} -//ZYF 以上从nuwa-go-httpserver/middleware/trace.go里面拷贝过来的,便于实现GetClientIp,跟nuwa/trace/trace.go中的Trace定义高度相似,field是后者的子集. - -func GetLocalIp() (string, error) { - addrs, err := net.InterfaceAddrs() - if err != nil { - return "", err - } - for _, value := range addrs { - if ipnet, ok := value.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - return ipnet.IP.String(), nil - } - } - } - return "", fmt.Errorf("get local ip is fail,addrs is %v", addrs) -} - -func GetClientIp(ctx context.Context) string { - rec, ok := ctx.Value(TraceRecordKey).(TraceRecord) - var clientIp string - if ok { - clientIp = formatIp(rec.From) - } else { - clientIp = "0.0.0.0" - } - return clientIp -} - -func GetClientAddr(r *http.Request) string { - remoteAddr := utils.GetClientAddr(r) - return formatIp(remoteAddr) -} - -func formatIp(remoteAddr string) string { - if remoteAddr == "::1" { - return "127.0.0.1" - } - - strs := strings.Split(remoteAddr, ":") - if strs != nil && len(strs) > 0 { - remoteAddr = strs[0] - } - - return remoteAddr -} - diff --git a/tg-core/common/lock/lease_lock.go b/tg-core/common/lock/lease_lock.go deleted file mode 100644 index f4305e1..0000000 --- a/tg-core/common/lock/lease_lock.go +++ /dev/null @@ -1,9 +0,0 @@ -package lock - -type LeaseLock interface { - - RegisterLock(key string, value string) - - TryLock(key string) - -} \ No newline at end of file diff --git a/tg-core/common/lock/lock_message.go b/tg-core/common/lock/lock_message.go deleted file mode 100644 index eb5a5bc..0000000 --- a/tg-core/common/lock/lock_message.go +++ /dev/null @@ -1,55 +0,0 @@ -package lock -/** -* function: 锁信息 -* Author : dayunzhangyunfeng@didiglobal.com -* Since : 2019-11-25 19:50:00 -*/ -import ( - "sync/atomic" -) - -type LockMessage struct { - - Key string - - Value string - - ExpireTime int64 - - LockStatus int32 - - ChangeTimes int64 -} - -func NewLockMessage(key, value string) *LockMessage { - lockMessage := &LockMessage{} - lockMessage.Key = key - lockMessage.Value = value - return lockMessage -} - -func (this *LockMessage) GetKey() string { - return this.Key; -} - -func (this *LockMessage) GetValue() string { - return this.Value; -} - -func (this *LockMessage) SetNewStatus(newStatus int32) bool { - return atomic.CompareAndSwapInt32(&this.LockStatus, 1-newStatus, newStatus) -} - -/** - * 锁过期时间 - * 如果当前节点持有锁,则expireTime和redis中的锁过期时间保持一致 - * 如果当前节点不持有锁,则expireTime是当前节点最后一次获取到锁的过期时间 - */ -func (this *LockMessage) GetExpireTime() int64 { - return atomic.LoadInt64(&this.ExpireTime) -} - -func (this *LockMessage) SetExpireTime(expireTime int64) { - atomic.StoreInt64(&this.ExpireTime, expireTime) -} - \ No newline at end of file diff --git a/tg-core/common/lock/redis_lease_lock.go b/tg-core/common/lock/redis_lease_lock.go deleted file mode 100644 index f88ec2d..0000000 --- a/tg-core/common/lock/redis_lease_lock.go +++ /dev/null @@ -1,133 +0,0 @@ -package lock -/** -* function: 租约锁 -* Author : dayunzhangyunfeng@didiglobal.com -* Since : 2019-11-25 19:50:00 -*/ -import ( - "fmt" - "time" - "github.com/didi/tg-flow/tg-core/common/redis" - "github.com/didi/tg-flow/tg-core/common/utils" - "github.com/didi/tg-flow/tg-core/consts" - "context" -) - - -const ( - DEFAULT_LEASE_EXPIRE_TIME = 20 - DEFAULT_LEASE_PERIOD = 2 -) - -type RedisLeaseLock struct { - LeaseLock - - LockMap map[string]*LockMessage - - LeaseExpireTime int64 // 过期时长(单位秒) - - LeasePeriod int64 // 间隔(单位秒) -} - -func NewRedisLeaseLock(leasePeriod int64, leaseExpireTime int64) (*RedisLeaseLock, error) { - rll := &RedisLeaseLock{} - // 租约过期时长 至少 是续租间隔的4倍,否则抛出异常 - if (leaseExpireTime <= leasePeriod * 4) { - return nil, fmt.Errorf("leaseExpireTime is too small, it must great than %v" , leasePeriod * 4) - } - - rll.LeasePeriod = leasePeriod - rll.LeaseExpireTime = leaseExpireTime - rll.LockMap = make(map[string]*LockMessage) - return rll, nil -} - -func (this *RedisLeaseLock) RegisterLock(key, value string) error { - if _, ok := this.LockMap[key]; ok { - return fmt.Errorf("register lease lock error, key is exist! key=", key); - } - - this.LockMap[key]= &LockMessage{ - Key : key, - Value : value, - ExpireTime : 0, - LockStatus : 0, - ChangeTimes : 0} - fmt.Println("register lease lock, key=" + key + ", value=" + value) - - // 注册时需要阻塞调用一次 - this.setLeaseLocks() - - go this.initTryLock(); - - return nil -} - -func (this *RedisLeaseLock) initTryLock() { - for { - time.Sleep(time.Duration(1) * time.Second) - this.setLeaseLocks() - } -} - -func (this *RedisLeaseLock) setLeaseLocks() { - for _, lockMessage :=range this.LockMap { - this.setLeaseLock(lockMessage) - } -} - -func (this *RedisLeaseLock) setLeaseLock(lockMessage *LockMessage) { - defer utils.Recover(context.TODO(), nil, consts.DLTagCronTask, "RedisLeaseLock.setLeaseLock") - // 锁已过期,尝试nx操作获取锁 - if time.Now().Unix() >= lockMessage.GetExpireTime() { - isOk, err := redis.Handler.SetNEx(context.TODO(), lockMessage.GetKey(), int(this.LeaseExpireTime), lockMessage.GetValue()) - if isOk == "OK" && err == nil { - lockMessage.SetExpireTime(time.Now().Unix() + this.LeaseExpireTime); - } else if isOk == "" { - // 如果NX不能获取锁,需要进一步确认此锁是否还属于自己 - // 如果属于自己,则取出当前锁的TTL赋给内存对象 - serverLockMessage, err := redis.Handler.GetString(context.TODO(), lockMessage.GetKey()) - if serverLockMessage != "" && err ==nil && serverLockMessage == lockMessage.GetValue() { - ttl, err := redis.Handler.TTL(context.TODO(), lockMessage.GetKey()) - if err == nil { - ttl64 := int64(ttl) - if ttl64 > this.LeasePeriod && err == nil { - lockMessage.SetExpireTime(time.Now().Unix() + ttl64) - } - } - } - } - } else { - // 锁还未过期,尝试expire - serverLockMessage, err := redis.Handler.GetString(context.TODO(), lockMessage.GetKey()) - if serverLockMessage == "" || serverLockMessage != lockMessage.GetValue() { - return; - } - - isOk, err := redis.Handler.Expire(context.TODO(), lockMessage.GetKey(), this.LeaseExpireTime) - if isOk == 1 && err == nil { - lockMessage.SetExpireTime(time.Now().Unix() + this.LeaseExpireTime); - } - } -} - -/** - 获取任务对应的锁 - 如果key不存在,抛异常 - 如果获取成功,则返回true - 如果获取失败,则返回false -**/ -func (this *RedisLeaseLock) TryLock(key string) bool { - lockMessage, ok := this.LockMap[key] - if !ok { - panic("not registered key:"+ key) - } - - if time.Now().Unix() < lockMessage.GetExpireTime() { - lockMessage.SetNewStatus(int32(1)) - return true - } else{ - lockMessage.SetNewStatus(int32(0)) - return false - } -} diff --git a/tg-core/common/metric/metric_service.go b/tg-core/common/metric/metric_service.go deleted file mode 100644 index 5801d92..0000000 --- a/tg-core/common/metric/metric_service.go +++ /dev/null @@ -1,123 +0,0 @@ -/** - description: 统计结构上报工具 - 后续将删除或抽象为接口供外部实现 -**/ - -package metric - -import ( - "context" - "fmt" - "github.com/didi/tg-flow/tg-core/common/timeutils" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/consts" - statsd "go.intra.xiaojukeji.com/foundation/didi-standard-lib/metric/golang-v2/statsdlib" - "time" -) - -const ( - dlTagTimeCost = " time_cost_ms" - eTag = "time_cost_ms" -) - -/** - metricName:统计的名称 - source:数据来源 - metricType:统计的类型 -**/ -func SendMetric(startTime time.Time, metricName string, source string, metricType string) { - latency := time.Now().Sub(startTime) - statsd.RpcMetric(metricName, source, metricType, latency, "ok") -} - -/** - metricName:统计的名称 - caller: 调用方 - callee: 被调用方 - startTime: 开始计时时间内 - code: 调用结果, 取值 "ok" "0" "200" "201" "203"为成功、其他均为失败 -**/ -func RpcMetric(metricName string, caller string, callee string, startTime time.Time, code interface{}) { - latency := time.Now().Sub(startTime) - statsd.RpcMetric(metricName, caller, callee, latency, code) -} - -//打印各个策略节点耗时,并上报metrics -func PrintSectionTime(startTime time.Time, traceId string, tc *timeutils.TimeCoster, metricsName string, sceneId int64) { - tlog.Handler.Infof(context.TODO(), consts.DLTagControll, "etype=%v||%v||traceid=%v", eTag, tc.ToCountString(), traceId) - - //上报系统,包含所有场景,作为一个整体耗时给metrcis,便于配置报警策略 - go SendMetric(startTime, fmt.Sprintf("%v_total", metricsName), "all", "rt") - - //上报系统,分每个场景各自的耗时给metrcis,便于配置报警策略 - go SendMetric(startTime, fmt.Sprintf("%v_%v_total", metricsName, sceneId), "all", "rt") - - //系统内部各节点分段耗时上报metrics统计服务 - for key, useTime := range tc.GetAllCounts() { - go statsd.RpcMetric(fmt.Sprintf("%v_%v", metricsName, key), "all", "rt", time.Millisecond*time.Duration(useTime), "ok") - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//上面是base库中老的metric封装, metricName由自己命名,无product和hintcode,将废弃 -//下面是按点架构新出的规范做的的metric封装,用于替换上线老的metric封装。 -//点架构metric规范: http://wiki.intra.xiaojukeji.com/pages/viewpage.action?pageId=609594742 -/////////////////////////////////////////////////////////////////////////////////////////////////// -func PrintAllMetrics(caller, callee string, sceneId int64, errCode, productId, hintCode string, traceId string, tc *timeutils.TimeCoster) { - latency := time.Now().Sub(tc.GetStartTime()) - tlog.Handler.Infof(context.TODO(), dlTagTimeCost, "etype=%v||%v||traceid=%v", eTag, tc.ToCountString(), traceId) - - //接口 - tags := getTagMap(productId, hintCode) - go SendRpcMetric("rpc_outer", caller, callee, latency, errCode, tags) - - //场景 - go SendRpcMetric("rpc_outer_scene", caller, fmt.Sprintf("scene_%v", sceneId), latency, errCode, nil) - - //节点 - for key, useTime := range tc.GetAllCounts() { - go SendRpcMetric("rpc_outer_section", caller, key, time.Millisecond*time.Duration(useTime), errCode, nil) - } -} - -/** - 标签,注意返回内容必须不为空 - */ -func getTagMap(productId, hintCode string) map[string]string { - tags := make(map[string]string) - if productId == "" { - productId = "unknown" - } - if hintCode == "" { - hintCode = "unknown" - } - tags["product"] = productId - tags["hintcode"] = hintCode - return tags -} -/** - 上报服务被调用的请求量、延时、错误率指标,打印耗时日志 -*/ -func PrintOuterMetric(caller, callee string, errCode, productId, hintCode string, traceId string, tc *timeutils.TimeCoster) { - latency := time.Now().Sub(tc.GetStartTime()) - tlog.Handler.Infof(context.TODO(), dlTagTimeCost, "etype=%v||%v||traceid=%v", eTag, tc.ToCountString(), traceId) - tags := getTagMap(productId, hintCode) - SendRpcMetric("rpc_outer", caller, callee, latency, errCode, tags) -} - -func SendRpcMetric(metricsName, caller, callee string, latency time.Duration, errCode string, tags map[string]string) { - if len(tags) > 0 { - statsd.RpcMetric(metricsName, caller, callee, latency, errCode, tags) - }else{ - statsd.RpcMetric(metricsName, caller, callee, latency, errCode) - } - -} - -/** -上报服务调用下游的请求量、延时、错误率指标 -*/ -func PrintAccessMetric(fun, callee string, latency time.Duration, errCode, productId, hintCode string) { - tags := getTagMap(productId, hintCode) - SendRpcMetric("rpc_access", fun, callee, latency, errCode, tags) -} diff --git a/tg-core/common/mysql/init.go b/tg-core/common/mysql/init.go deleted file mode 100644 index 138b977..0000000 --- a/tg-core/common/mysql/init.go +++ /dev/null @@ -1,56 +0,0 @@ -package mysql - -import ( - "database/sql" - "log" - "time" - - "github.com/didi/tg-flow/tg-core/conf" - - "github.com/didi/gendry/manager" - _ "github.com/go-sql-driver/mysql" -) - -var Handler *sql.DB - -const ( - SECTION = "mysql" -) - -func InitMySqlHandler() { - log.Println("tg-core mysql init start...") - sec, err := conf.Handler.GetSection(SECTION) - if err != nil { - log.Fatal("MySQL init error: ", err) - } - host := sec.GetStringMust("host", "localhost") - port := sec.GetIntMust("port", 3306) - user := sec.GetStringMust("user", "root") - pwd := sec.GetStringMust("password", "") - charset := sec.GetStringMust("charset", "utf8") - dbName := sec.GetStringMust("db_name", "") - timeout := sec.GetIntMust("conn_timeout", 500) - readTimeout := sec.GetIntMust("read_timeout", 500) - writeTimeout := sec.GetIntMust("write_timeout", 500) - connMaxLifetime := sec.GetIntMust("conn_max_lifetime", 0) - maxIdleConns := sec.GetIntMust("max_idle_conns", 2) - maxOpenConns := sec.GetIntMust("max_open_conns", 2) - - Handler, err = manager.New(dbName, user, pwd, host). - Set( - manager.SetCharset(charset), - manager.SetTimeout(time.Duration(timeout)*time.Millisecond), - manager.SetReadTimeout(time.Duration(readTimeout)*time.Millisecond), - manager.SetWriteTimeout(time.Duration(writeTimeout)*time.Millisecond), - manager.SetParseTime(true), - manager.SetLoc("Local"), - ).Port(int(port)).Open(true) - if err != nil { - log.Fatal("Init mysql error: ", err) - } - - Handler.SetConnMaxLifetime(time.Duration(connMaxLifetime)) - Handler.SetMaxIdleConns(int(maxIdleConns)) - Handler.SetMaxOpenConns(int(maxOpenConns)) - log.Println("tg-core mysql init successful!") -} diff --git a/tg-core/common/redis/init.go b/tg-core/common/redis/init.go deleted file mode 100644 index 53127a9..0000000 --- a/tg-core/common/redis/init.go +++ /dev/null @@ -1,235 +0,0 @@ -package redis - -import ( - "context" - "encoding/json" - "fmt" - "git.xiaojukeji.com/gobiz/config" - "git.xiaojukeji.com/nuwa/golibs/redis" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/conf" - "github.com/didi/tg-flow/tg-core/consts" - "github.com/didi/tg-flow/tg-core/model" - "log" - "strings" - "time" -) - -var Handler *redis.Manager -var FeatureRedisHandler *redis.Manager -var Handlers map[string]*redis.Manager - -const ( - SECTION = "redis" - FEATURE_REDIS_SECTION = "feature_redis" - ErrNil = "redigo: nil returned" - //notFindKey = "can not find key" -) - -func InitRedisHandlers(sections []string) { - if len(sections) <1 { - return - } - - handlers := make(map[string]*redis.Manager) - for _, section := range sections { - redisManager, err := NewManagerFromConf(conf.Handler, section) - if err != nil || redisManager == nil { - log.Fatal("Init redis client["+ section + "] error: ", err) - } - - handlers[section] = redisManager - //为兼容内置Handler,此处先特殊处理一下 - if section == SECTION { - Handler = redisManager - }else if section == FEATURE_REDIS_SECTION { - FeatureRedisHandler = redisManager - } - log.Println("Init redis client["+ section + "] successful !!!") - } - Handlers = handlers -} - -//redis.go支持的参数太少,包一下 -func NewManagerFromConf(cfg config.Configer, sec string, opt ...redis.Option) (*redis.Manager, error) { - var opts []redis.Option - addrs, err := cfg.GetSetting(sec, "addrs") - if err != nil { - return nil, err - } - - servers := strings.Split(addrs, ",") - auth, err := cfg.GetSetting(sec, "auth") - if err != nil { - return nil, err - } - disfEnable, _ := cfg.GetBoolSetting(sec, "disf_enable") - if disfEnable { - sn, err := cfg.GetSetting(sec, "service_name") - if err != nil { - return nil, err - } - opts = append(opts, redis.EnableDisf()) - opts = append(opts, redis.DisfServiceName(sn)) - } - - poolSize, err := cfg.GetIntSetting(sec, "pool_size") - if err == nil { - opts = append(opts, redis.SetPoolSize(poolSize)) - } - - maxConn, err := cfg.GetIntSetting(sec, "max_conn") - if err == nil { - opts = append(opts, redis.SetMaxConn(maxConn)) - } - - connTimeout, err := cfg.GetIntSetting(sec, "conn_timeout") - if err == nil { - opts = append(opts, redis.SetConnectTimeout(time.Millisecond*time.Duration(connTimeout))) - } - - readTimeout, err := cfg.GetIntSetting(sec, "read_timeout") - if err == nil { - opts = append(opts, redis.SetReadTimeout(time.Millisecond*time.Duration(readTimeout))) - } - - writeTimeout, err := cfg.GetIntSetting(sec, "write_timeout") - if err == nil { - opts = append(opts, redis.SetWriteTimeout(time.Millisecond*time.Duration(writeTimeout))) - } - - maxTryTimes, err := cfg.GetIntSetting(sec, "max_try_times") - if err == nil { - opts = append(opts, redis.SetMaxTryTimes(maxTryTimes)) - } - - opts = append(opts, opt...) - - return redis.NewManager(servers, auth, opts...) -} - -//TODO:write to redis -func WriteRedis(sc *model.StrategyContext, redisKey string, info interface{}, etype string) { - infoByte, err := json.Marshal(info) - if err != nil { - tlog.LogError(context.TODO(), sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.jsonMarshal", etype), fmt.Sprintf("info=%v", info), err) - return - } - if _, err := Handler.Set(context.TODO(), redisKey, infoByte); err != nil { - tlog.LogError(context.TODO(), sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.WriteRedis", etype), fmt.Sprintf("info=%v", info), err) - return - } -} - -//write to fusion -func SetFusion(ctx context.Context, sc *model.StrategyContext, redisKey string, info interface{}, etype string) { - infoByte, err := json.Marshal(info) - if err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.jsonMarshal", etype), fmt.Sprintf("info=%v", info), err) - return - } - WriteFushion(ctx, sc, redisKey, infoByte, etype) -} - -func WriteFushion(ctx context.Context, sc *model.StrategyContext, redisKey string, info []byte, etype string) { - if _, err := Handler.Set(ctx, redisKey, info); err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.WriteFushion", etype), fmt.Sprintf("info=%v", info), err) - } -} - -//write to fusion -func WriteFushionEx(ctx context.Context, sc *model.StrategyContext, redisKey string, info []byte, expireTime int, etype string) { - if _, err := Handler.SetEx(ctx, redisKey, expireTime, info); err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.WriteFushionEx", etype), fmt.Sprintf("info=%v", info), err) - } -} - -//get from fusion -func GetFushion(ctx context.Context, sc *model.StrategyContext, redisKey string, etype string) string { - info, err := Handler.Get(ctx, redisKey) - if err != nil && err.Error() != ErrNil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.GetFushion", etype), fmt.Sprintf("info=%v", info), err) - return "" - } - return info -} - -func WToFusion(ctx context.Context, sc *model.StrategyContext, redisKey string, info interface{}, etype string) { - if _, err := Handler.Set(ctx, redisKey, info); err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.WriteFushion", etype), fmt.Sprintf("info=%v", info), err) - } -} - -//write to fusion -func WToFusionEx(ctx context.Context, sc *model.StrategyContext, redisKey string, info interface{}, expireTime int, etype string) { - if _, err := Handler.SetEx(ctx, redisKey, expireTime, info); err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.WriteFushionEx", etype), fmt.Sprintf("info=%v", info), err) - } -} - -//复制以上方法,新增一个redis.handle参数 -func HSetRedis(ctx context.Context, sc *model.StrategyContext, fusionHandler *redis.Manager, redisKey string, subKey string, info interface{}, etype string) { - infoByte, err := json.Marshal(info) - if err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.HSetRedisMarshal", etype), fmt.Sprintf("info=%v", info), err) - return - } - if _, err = fusionHandler.HSet(ctx, redisKey, subKey, infoByte); err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.HSetRedis", etype), fmt.Sprintf("info=%v", info), err) - } -} - -//write to fusion -func SetRedis(ctx context.Context, sc *model.StrategyContext, fusionHandler *redis.Manager, redisKey string, info interface{}, etype string) { - infoByte, err := json.Marshal(info) - if err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.SetRedis", etype), fmt.Sprintf("info=%v", info), err) - return - } - WriteFushionByte(ctx, sc, fusionHandler, redisKey, infoByte, etype) -} - -func WriteFushionByte(ctx context.Context, sc *model.StrategyContext, fusionHandler *redis.Manager, redisKey string, info []byte, etype string) { - if _, err := fusionHandler.Set(ctx, redisKey, info); err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.WriteFushionByte", etype), fmt.Sprintf("info=%v", info), err) - } -} - -func WriteFushionInterface(ctx context.Context, sc *model.StrategyContext, fusionHandler *redis.Manager, redisKey string, info interface{}, etype string) { - if _, err := fusionHandler.Set(ctx, redisKey, info); err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.WriteFushionInterface", etype), fmt.Sprintf("info=%v", info), err) - } -} - -func SetRedisEx(ctx context.Context, sc *model.StrategyContext, fusionHandler *redis.Manager, redisKey string, info interface{}, expireTime int, etype string) { - infoByte, err := json.Marshal(info) - if err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.SetRedisEx", etype), fmt.Sprintf("info=%v", info), err) - return - } - WriteFushionByteEx(ctx, sc, fusionHandler, redisKey, infoByte, expireTime, etype) -} - -//write to fusion -func WriteFushionByteEx(ctx context.Context, sc *model.StrategyContext, fusionHandler *redis.Manager, redisKey string, info []byte, expireTime int, etype string) { - if _, err := fusionHandler.SetEx(ctx, redisKey, expireTime, info); err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.WriteFushionByteEx", etype), fmt.Sprintf("info=%v", info), err) - } -} - -//write to fusion -func WriteFushionInterfaceEx(ctx context.Context, sc *model.StrategyContext, fusionHandler *redis.Manager, redisKey string, info interface{}, expireTime int, etype string) { - if _, err := fusionHandler.SetEx(ctx, redisKey, expireTime, info); err != nil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.WriteFushionInterfaceEx", etype), fmt.Sprintf("info=%v", info), err) - } -} - -//get from fusion -func GetRedis(ctx context.Context, sc *model.StrategyContext, fusionHandler *redis.Manager, redisKey string, etype string) string { - info, err := fusionHandler.Get(ctx, redisKey) - if err != nil && err.Error() != ErrNil { - tlog.LogError(ctx, sc, consts.DLTagFushion, fmt.Sprintf("%v.redis.GetRedis", etype), fmt.Sprintf("info=%v", info), err) - return "" - } - return info -} diff --git a/tg-core/common/tlog/init.go b/tg-core/common/tlog/init.go deleted file mode 100644 index 37d2ecf..0000000 --- a/tg-core/common/tlog/init.go +++ /dev/null @@ -1,227 +0,0 @@ -package tlog - -import ( - "context" - "fmt" - "github.com/didi/tg-flow/tg-core/conf" - "github.com/didi/tg-flow/tg-core/model" - "git.xiaojukeji.com/gobiz/logger" - trace "git.xiaojukeji.com/lego/context-go" - "log" - "path/filepath" - "runtime" - "strings" - statsd "go.intra.xiaojukeji.com/foundation/didi-standard-lib/metric/golang-v2/statsdlib" -) - -var Handler logger.Tracer - -const ( - DIDI_HINT_CODE = "1" // 压测 - DIDI_HINT_CODE_TEST = "2" // 线上巡检流量 -) - -func init() { - log.Println("tg-core tlog init start...") - var err error - Handler, err = logger.NewTracerWithConfig(conf.Handler) - if err != nil { - log.Fatal("Init log error: ", err) - } - - Handler.RegisterContextFormat(trace.FormatCtx) - logger.RegisterContextFormat(trace.FormatCtx) - - - err = logger.NewLoggerWithConfig(conf.Handler) - if err != nil { - log.Fatal("NewLoggerWithConfig Init log error: ", err) - } - initHintLog() -} - -// didi 日志跟压测日志分离,注册压测日志句柄&回调函数 -func initHintLog() { - enable, err := conf.Handler.GetBoolSetting("log", "hint_log.enable") - if err != nil || !enable { - return - } - - prefix, _ := conf.Handler.GetSetting("log","hint_log.prefix") - dir, _ := conf.Handler.GetSetting("log","hint_log.dir") - formatInfo, _ := conf.Handler.GetSetting("log","hint_log.format") - levelInfo, _ := conf.Handler.GetSetting("log","hint_log.level") - levelVal := getLogLevel(levelInfo) - async, _ := conf.Handler.GetBoolSetting("log","hint_log.async") - disableLink, _ := conf.Handler.GetBoolSetting("log","hint_log.disable_link") - autoClear, _ := conf.Handler.GetBoolSetting("log","hint_log.auto_clear") - clearHour, _ := conf.Handler.GetIntSetting("log","hint_log.clear_hours") - seprated, _ := conf.Handler.GetBoolSetting("log","hint_log.seprated") - - err = logger.NewShadowWithSetting( &logger.LogSetting{ - LogT: logger.LogTypeFile, - Enable: enable, - Prefix: prefix, - AsyncWrite: async, - Dir: dir, - DisableLink: disableLink, - Format: formatInfo, - Level: levelVal, - AutoClear: autoClear, - ClearHours: int32(clearHour), - Seprated: seprated, - RMode: logger.RotateModeHour, - }) - - // 设置压测识别回调函数,这里主要是从ctx 中识别出流量是否含有压测 - logger.RegisterGetShadowFromContext(GetShadowFromContext) -} - -// GetShadowFromContext ctx 中放的是lego/trace -func GetShadowFromContext(ctx context.Context) (ret bool) { - traceInfo := trace.GetTrace(ctx) - if traceInfo == nil { - return false - } - if traceInfo.GetHintCode() == DIDI_HINT_CODE || traceInfo.GetHintCode() == DIDI_HINT_CODE_TEST{ - return true - } - return false -} - - -func getLogLevel(l string) logger.Level { - switch strings.ToUpper(l) { - case "TRACE": - return logger.TRACE - case "DEBUG": - return logger.DEBUG - case "INFO": - return logger.INFO - case "WARNING": - return logger.WARNING - case "ERROR": - return logger.ERROR - case "FATAL": - return logger.FATAL - default: - return logger.ERROR - } -} - -/** - ctx: 请求上下文信息 - sc: 业务逻辑中间结果 - tag: 日志的标识,便于查找日志 - etype: 日志类型,主要包含日志所在的函数名,及唯一标识,便于odin监控分类统计 - content:日志内容 - err: 日志对应的错误信息,可为nil -**/ -func LogError(ctx context.Context, sc *model.StrategyContext, tag string, etype string, content string, err error) { - filename, line, path, ok, index := "", 0, "", false, 1 - for { - if index >= 10 { - break - } - _, filename, line, ok = runtime.Caller(index) - if ok { - filename = filepath.Base(filename) - } - if filename == "" { - break - } - index++ - if strings.Contains(filename, "autogenerated") || strings.Contains(filename, "asm_amd64") { - continue - } - if len(path) > 0 { - path = filename + ":" + fmt.Sprintf("%v", line) + "/" + path - } else { - path = filename + ":" + fmt.Sprintf("%v", line) - } - } - Handler.Errorf(ctx, tag, "etype=%v||log_path=%v||%v||error=%v", strings.Replace(etype, "\n", "", -1), path, strings.Replace(content, "\n", "", -1), err) -} - -/** - ctx: 请求上下文信息 - sc: 业务逻辑中间结果 - tag: 日志的标识,便于查找日志 - etype: 日志类型,主要包含日志所在的函数名,及唯一标识,便于odin监控分类统计 - content:日志内容 -**/ -func LogErrorInfo(ctx context.Context, sc *model.StrategyContext, tag string, etype string, content string) { - filename, line, path, ok, index := "", 0, "", false, 1 - for { - if index >= 10 { - break - } - _, filename, line, ok = runtime.Caller(index) - if ok { - filename = filepath.Base(filename) - } - if filename == "" { - break - } - index++ - if strings.Contains(filename, "autogenerated") || strings.Contains(filename, "asm_amd64") { - continue - } - if len(path) > 0 { - path = filename + ":" + fmt.Sprintf("%v", line) + "/" + path - } else { - path = filename + ":" + fmt.Sprintf("%v", line) - } - } - Handler.Errorf(ctx, tag, "etype=%v||log_path=%v||error=%v", strings.Replace(etype, "\n", "", -1), path, strings.Replace(content, "\n", "", -1)) - //上报error统计 - statsd.Counter(etype) -} - -/** - 打错误日志,并上报错误 - 后续将替代上面2个函数s - */ -func ErrorCount(ctx context.Context, metricName string, content string) { - path := FormatLogPath() - Handler.Errorf(ctx, " "+metricName, "etype=%v||log_path=%v||error=%v", metricName, path, strings.Replace(content, "\n", "", -1)) - //上报error统计 - statsd.Counter(metricName) -} - -/** - 打印错误日志,根据tags上报错误 - */ -func ErrorCountWithTags(ctx context.Context, metricName string, tags map[string]string, content string) { - path := FormatLogPath() - Handler.Errorf(ctx, " "+metricName, "etype=%v||log_path=%v||error=%v", metricName, path, strings.Replace(content, "\n", "", -1)) - //上报error统计 - statsd.Counter(metricName, tags) -} - - -func FormatLogPath() string { - filename, line, path, ok, index := "", 0, "", false, 1 - for { - if index >= 10 { - break - } - _, filename, line, ok = runtime.Caller(index) - if ok { - filename = filepath.Base(filename) - } - if filename == "" { - break - } - index++ - if strings.Contains(filename, "autogenerated") || strings.Contains(filename, "asm_amd64") { - continue - } - if len(path) > 0 { - path = filename + ":" + fmt.Sprintf("%v", line) + "/" + path - } else { - path = filename + ":" + fmt.Sprintf("%v", line) - } - } - return path -} \ No newline at end of file diff --git a/tg-core/common/utils/convert.go b/tg-core/common/utils/convert.go deleted file mode 100644 index f13534c..0000000 --- a/tg-core/common/utils/convert.go +++ /dev/null @@ -1,83 +0,0 @@ -package utils - -import "strconv" - -func Str2Int(s string) (int, error) { - i, err := strconv.Atoi(s) - if err != nil { - return 0, err - } - return i, nil -} - -func Str2IntMust(s string, defaultValue int) int { - i, err := strconv.Atoi(s) - if err != nil { - return defaultValue - } - return i -} - -func Str2Int32(s string) (int32, error) { - i64, err := strconv.ParseInt(s, 10, 32) - if err != nil { - return 0, err - } - return int32(i64), nil -} - -func Str2Int32Must(s string, defaultValue int32) int32 { - i64, err := strconv.ParseInt(s, 10, 32) - if err != nil { - return defaultValue - } - return int32(i64) -} - -func Str2Int64(s string) (int64, error) { - i64, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return 0, err - } - return i64, nil -} - -func Str2Int64Must(s string, defaultValue int64) int64 { - i64, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return defaultValue - } - return i64 -} - -func Str2Float32(s string) (float32, error) { - f32, err := strconv.ParseFloat(s, 32) - if err != nil { - return 0, err - } - return float32(f32), nil -} - -func Str2Float32Must(s string, defaultValue float32) float32 { - f32, err := strconv.ParseFloat(s, 32) - if err != nil { - return defaultValue - } - return float32(f32) -} - -func Str2Float64(s string) (float64, error) { - f64, err := strconv.ParseFloat(s, 64) - if err != nil { - return 0, err - } - return float64(f64), nil -} - -func Str2Float64Must(s string, defaultValue float64) float64 { - f64, err := strconv.ParseFloat(s, 64) - if err != nil { - return defaultValue - } - return float64(f64) -} \ No newline at end of file diff --git a/tg-core/common/utils/recover.go b/tg-core/common/utils/recover.go deleted file mode 100644 index 31197ae..0000000 --- a/tg-core/common/utils/recover.go +++ /dev/null @@ -1,37 +0,0 @@ -package utils - -import ( - "context" - "fmt" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/model" - "sync" -) -//////////////////////////以下都是带删除 -func Recover(ctx context.Context, sc *model.StrategyContext, tag string, subType string) { - if err := recover(); err != nil { - tlog.LogError(ctx, sc, tag, subType, "Recover system panic", fmt.Errorf("%v", err)) - } -} - -func RecoverThread(ctx context.Context, sc *model.StrategyContext, tag string, subType string, ch chan int) { - if err := recover(); err != nil { - tlog.LogError(ctx, sc, tag, subType, "RecoverThread system panic", fmt.Errorf("%v", err)) - ch <- 1 - } -} - -func RecoverThreadByWg(ctx context.Context, sc *model.StrategyContext, tag string, subType string, wg *sync.WaitGroup) { - if err := recover(); err != nil { - tlog.LogError(ctx, sc, tag, subType, "RecoverThreadByWg system panic", fmt.Errorf("%v", err)) - wg.Done() - } -} - -////////////////////////////////////以上都是待删除////////////////// - -func RecoverPanic(ctx context.Context, tag string) { - if err := recover(); err != nil { - tlog.ErrorCount(ctx, tag, fmt.Sprintf("Recover system panic : %v", err)) - } -} \ No newline at end of file diff --git a/tg-core/common/utils/stringsUtil.go b/tg-core/common/utils/stringsUtil.go deleted file mode 100644 index dc4a11d..0000000 --- a/tg-core/common/utils/stringsUtil.go +++ /dev/null @@ -1,166 +0,0 @@ -package utils - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "strconv" - "crypto/md5" - "encoding/hex" - "time" - "strings" -) - -const ( - timeLayout = "2006-01-02 15:04:05" -) - -/** - 生成32位MD5 -**/ -func MD5(text string) string { - ctx := md5.New() - ctx.Write([]byte(text)) - return hex.EncodeToString(ctx.Sum(nil)) -} - -func GetCurrentTimeString() string { - return time.Now().Format(timeLayout) -} - -/** - 将原始的time格式转换成标准格式,如: - 原始格式:2020-08-17T14:36:31+08:00 ===> 转换后的标准格式 :2020-08-17 14:36:31 -**/ -func FormatDateTime(updateTime string) string { - length := len(updateTime) - if length > 19 { - updateTime = updateTime[:19] - } - - updateTime = strings.ReplaceAll(updateTime, "T", " ") - - return updateTime -} - -//截取字符串 start 起点下标 end 终点下标(不包括) -func SubStr(str string, start int, end int) (string, error) { - rs := []rune(str) - length := len(rs) - if start < 0 || start > length { - err := errors.New("SubStr start is wrong") - return "", err - } - if end < 0 || end > length { - err := errors.New("SubStr end is wrong.") - return "", err - } - return string(rs[start:end]), nil -} - -//将int64数组,转化为string字符串 -func Int64ArrayTOString(array []int64) string { - resultStr := "" - if array == nil || len(array) <= 0 { - return resultStr - } - for _, data := range array { - if len(resultStr) <= 0 { - resultStr += strconv.FormatInt(data, 10) - } else { - resultStr += "," + strconv.FormatInt(data, 10) - } - } - return resultStr -} - -//map[string]string,转化为string -func StringMapToString(strMap map[string]string) string { - resultStr := "" - if strMap == nil || len(strMap) <= 0 { - return resultStr - } - for key, value := range strMap { - if len(resultStr) <= 0 { - resultStr += key + ":" + value - } else { - resultStr += "," + key + ":" + value - } - } - return resultStr -} - -//map[int64]int64,转化为string -func Int64MapToString(int64Map map[int64]int64) string { - resultStr := "" - if int64Map == nil || len(int64Map) <= 0 { - return resultStr - } - for key, value := range int64Map { - if len(resultStr) <= 0 { - resultStr += strconv.FormatInt(key, 10) + ":" + strconv.FormatInt(value, 10) - } else { - resultStr += "," + strconv.FormatInt(key, 10) + ":" + strconv.FormatInt(value, 10) - } - } - return resultStr -} - -//map[int64]int,转化为string -func IntMapToString(intMap map[int64]int) string { - resultStr := "" - if intMap == nil || len(intMap) <= 0 { - return resultStr - } - for key, value := range intMap { - if len(resultStr) <= 0 { - resultStr += strconv.FormatInt(key, 10) + ":" + strconv.Itoa(value) - } else { - resultStr += "," + strconv.FormatInt(key, 10) + ":" + strconv.Itoa(value) - } - } - return resultStr -} - -//map[int64]map[string]float64,转化为string -func Float64MapToString(float64Map map[int64]map[string]float64) string { - resultStr := "" - if float64Map == nil || len(float64Map) <= 0 { - return resultStr - } - for key, hourMap := range float64Map { - hourStr := "" - if hourMap == nil || len(hourMap) <= 0 { - hourStr += "nil" - } else { - for hour, value := range hourMap { - if len(hourStr) <= 0 { - hourStr += hour + ":" + strconv.FormatFloat(value, 'f', -1, 64) - } else { - hourStr += "," + hour + ":" + strconv.FormatFloat(value, 'f', -1, 64) - } - } - } - hourStr = strconv.FormatInt(key, 10) + ":{" + hourStr + "}" - if len(resultStr) <= 0 { - resultStr += hourStr - } else { - resultStr += "," + hourStr - } - } - return resultStr -} - -func ToString(itr interface{}) string { - b, err := json.Marshal(itr) - if err != nil { - return fmt.Sprintf("%+v", itr) - } - var out bytes.Buffer - err = json.Indent(&out, b, "", " ") - if err != nil { - return fmt.Sprintf("%+v", itr) - } - return out.String() -} diff --git a/tg-core/conf/init.go b/tg-core/conf/init.go deleted file mode 100644 index 744f605..0000000 --- a/tg-core/conf/init.go +++ /dev/null @@ -1,105 +0,0 @@ -package conf - -import ( - "git.xiaojukeji.com/gobiz/config" - "github.com/didi/tg-flow/tg-core/common/path" - "git.xiaojukeji.com/nuwa/go-monitor" - "go.intra.xiaojukeji.com/platform-ha/onekey-degrade_sdk_go/degrade" - "log" - _ "net/http/pprof" -) - -var Handler config.Configer - -//center服务url地址 -var CENTER_URL string -var CENTER_HTTP_TIMEOUT int64 - -//911限流预案名称 -var RateLimitName string - -//911降级预案名称 -var DownGradeName string - -//系统运行环境:正式、预发、线下 -var ENV string - -func init() { - log.Println("tg-core conf init start, path.Root=" + path.Root) - var err error - Handler, err = config.New(path.Root + "/conf/app.conf") - if err != nil { - log.Println("Configer init error:", err, "(本地环境及单元测试可忽略该错误)") - // 适配读取配置文件 解决本地单元测试中无法正确读取配置路径的问题 - /*Handler, err = config.New(path.ConfLocal + "/conf/app.conf") - if err != nil { - log.Fatal("读取本地配置失败 =>", path.ConfLocal) - }*/ - } - - initRateLimitName() - - initDownGradeName() - - initEnv() - - nuwaMonitor() -} - -//初始化系统911限流预案名称 -func initRateLimitName() { - var err error - if RateLimitName, err = Handler.GetSetting("ratelimit", "name"); err != nil { - log.Println("Fail to get ratelimit name!", err) - return - } -} - -//初始化系统911降级预案名称 -func initDownGradeName() { - var downGradeName string - var err error - //获取降级预案配置 - if downGradeName, err = Handler.GetSetting("downgrade", "name"); err != nil { - log.Println("Fail to get downgrade name!", err) - return - } else { - DownGradeName = downGradeName - } - - //初始化911sdk - configMeta := degrade.NewConfigMeta() - configMeta.Add(DownGradeName, degrade.SWITCH_CONFIG) - if err := degrade.Init(configMeta); err != nil { - log.Println("Fail to init 911 sdk!", err) - return - } -} - -//性能监控 -func nuwaMonitor() { - var isopen bool - var port string - var err error - log.Println("tg-core nuwaMonitor init start...") - if isopen, err = Handler.GetBoolSetting("pprof", "isopen"); err != nil { - log.Println("Fail to get nuwaMonitor isopen config!", err) - return - } - if port, err = Handler.GetSetting("pprof", "port"); err != nil { - log.Println("Fail to get nuwaMonitor port config!", err) - return - } - if isopen { - go monitor.Start(":"+port, monitor.AllPlugin) - } -} - -//初始化系统运行环境变量 -func initEnv() { - var err error - if ENV, err = Handler.GetSetting("env", "name"); err != nil { - return - } - log.Println("tg-core env init end") -} diff --git a/tg-core/consts/dltag.go b/tg-core/consts/dltag.go deleted file mode 100644 index beec5b3..0000000 --- a/tg-core/consts/dltag.go +++ /dev/null @@ -1,27 +0,0 @@ -package consts - -const ( - DLTagSystemPanic = " _com_strategybase_system_panic" - DLTagControll = " _com_strategy_controll" - DLTagDispatcher = " _com_strategybase_dispatcher" - DLTagDispatcherPanic = " _com_strategybase_dispatcher_panic" - DLTagDispatcherFail = " _com_strategybase_dispatcher_fail" - DLTagRanker = " _com_strategybase_ranker" - DLTagCronTask = " _com_strategybase_cron_task" - DLTagFushion = " _com_strategybase_fushion" - DLTagLocalCache = " _com_strategybase_localcache" - DLTagLocalCacheFail = " _com_strategybase_localcache_fail" - DLTagOdin = " _com_strategybase_odin" - DLTagModuleInitStore = " _com_strategybase_module_init_store" - DLTagUfsClientInit = " _com_strategybase_ufs_client_init" - DLTagDfsClientInit = " _com_strategybase_dfs_client_init" - DLTagUserCenterClientInit = " _com_strategybase_usercenter_client_init" - DLTagRankerClientInit = " _com_strategybase_ranker_client_init" - DLTagDufeClientInit = " _com_strategybase_dufe_client_init" - - //要执行的维度类型集合 - DISPATCHERDIMENSION = "dispatcher_dimension" - - //要执行的维度名称 - DIMENSION_CITY = "city" -) diff --git a/tg-core/consts/genrec_config_c.go b/tg-core/consts/genrec_config_c.go deleted file mode 100644 index 8eb6187..0000000 --- a/tg-core/consts/genrec_config_c.go +++ /dev/null @@ -1,19 +0,0 @@ -package consts - -const ( - FEATURE_KEY_ITEM = "feature_key_item" - FEATURE_KEY_USER = "feature_key_user" - FEATURE_KEY_CONTEXT = "feature_key_context" - FEATURE_KEY_CONTEXTEXT = "feature_key_contextext" - FEATURE_KEY_USERTYPE = "feature_key_usertype" - FEATURE_KEY_CROSS = "feature_key_cross" - FEATURE_KEY_EVENTATTRS = "feature_key_eventAttrs" - FEATURE_KEY_EVENTIDS = "feature_key_eventIds" - FEATURE_KEY_BURYDATA = "feature_key_buryData" - FEATURE_KEY_FEATUREOPERATION = "feature_key_featureOperation" - - //fusion中的key - REC_SCENE_CONFIG = "rec_scene_config" - REC_SCENE_ITEM = "rec_scene_item" - BUSINESS_GROUP = "rec_business_group" -) diff --git a/tg-core/consts/global_c.go b/tg-core/consts/global_c.go deleted file mode 100644 index 45851d6..0000000 --- a/tg-core/consts/global_c.go +++ /dev/null @@ -1,32 +0,0 @@ -package consts - -import () - -const ( - FEATURE_VALUE_DEFAULT_FLOAT = -1 //TODO 全部工程用NUMBER替换后删除 - - FEATURE_VALUE_NEGATIVE_ONE = "-1" //TODO 全部工程用STRING替换后删除 - - FEATURE_VALUE_DEFAULT_NUMBER = -1 - - FEATURE_VALUE_DEFAULT_STRING = "-1" - - LOCALCACHE_TTL = 1800 //unit:s - - USER_LOCALCACHE_TTL = 3600 //unit:s - - FLOW_BY_ONLINE_RANDOM = 0 //在线随机分流 - FLOW_BY_ONLINE_SALT = 1 //在线salt分流 - FLOW_BY_OFFLINE = 2 //离线分流 - FLOW_BY_APOLLO = 3 //apollo分流 - - DefaultWorkflowVersion = "1.0" - - //strategycontext中set的key常量 - RANKER_IP = "ranker_ip" - -) - -//fusion中按在线salt分流和离线分流数据的表名 -var SALT_ONLINE_KEY string -var OFFLINE_KEY string diff --git a/tg-core/consts/ranker_config_status.go b/tg-core/consts/ranker_config_status.go deleted file mode 100644 index 2cdb95d..0000000 --- a/tg-core/consts/ranker_config_status.go +++ /dev/null @@ -1,15 +0,0 @@ -package consts - -const ( - RANKER_STATUS_UNKNOWN = -1 //未知 - RANKER_STATUS_TOONLINE = 1 //模型待上线 - RANKER_STATUS_ONLINE_FAIL = 2 //模型上线失败 - RANKER_STATUS_ONLINE = 3 //模型上线成功 -) - -/** -1. 新增配置时,scene_id、algo_name、model_name(以后简称sam)均不许为空,初始状态统一为status=1 -2. 更新配置时,sam改为非空的sam,则状态置为模型待加载;sam改为空的sam,则状态置为模型加载成功; -3. 删除配置时,必须满足当前状态为空闲时才允许删除(当ranker发现过期模型卸载成功且sam均为空时,会将状态置为空闲) -4. 更新配置时,不允许更新ip字段,如果要删除该ip对应的记录,需要先使之为空闲状态 -**/ \ No newline at end of file diff --git a/tg-core/consts/redis_keys.go b/tg-core/consts/redis_keys.go deleted file mode 100644 index 64ffb74..0000000 --- a/tg-core/consts/redis_keys.go +++ /dev/null @@ -1,29 +0,0 @@ -package consts - -import () - -/** - 策略服务公用业务相关的key,一律以"strategy_"作为前缀 -**/ -const ( - //TODO ZYF待删除 - StrategySceneAndModuleMap = "strategy_sceneAndModule_map" - StrategyWorkflowMap = "strategy_workflow_map" - StrategyDimensionMap = "strategy_dimension_map" - - //算法模型索引key - RedisKeyAlgoModelConfig = "strategy_algo_model_config" - RedisKeyRankerConfigMap = "strategy_rediskey_ranker_config_map" - - //机器ip - RedisKeyMachineIp = "rediskey_machine_ip" - - //systemConfig信息 - RedisKeySystemConf = "strategy_system_conf" - - //特征数据类型前缀 - FeatureDataType = "feature_data_type_scene_%v" - - //召回配置 - RedisKeyRecallConfig = "strategy_recall_config" -) diff --git a/tg-core/dispatcher/base_dispatcher.go b/tg-core/dispatcher/base_dispatcher.go deleted file mode 100644 index a33142c..0000000 --- a/tg-core/dispatcher/base_dispatcher.go +++ /dev/null @@ -1,196 +0,0 @@ -/** -Description : dispatcher of workflow engine -Author: dayunzhangyunfeng@didiglobal.com -Date: 2021-07-20 -*/ -package dispatcher - -import ( - "context" - "fmt" - trace "git.xiaojukeji.com/lego/context-go" - "github.com/didi/tg-flow/tg-core/common/timeutils" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/common/utils" - "github.com/didi/tg-flow/tg-core/model" - "github.com/didi/tg-flow/tg-core/wfengine" - "sync" -) - -type Dispatcher interface { - BuildRequest(ctx context.Context, requestParam interface{}) *model.StrategyContext - BuildResponse(sc *model.StrategyContext) interface{} - WriteLog(ctx context.Context, sc *model.StrategyContext) map[string]interface{} - GetWorkflowEngine() *wfengine.WorkflowEngine - GetPublicKey() string - GetInterfaceName() string -} - -func getInterfaceErrorTag(interfaceName string) string { - return interfaceName + "_err" -} - -/**最新逻辑 - moduleName:业务模块名称 - systemName:系统名称 - tags:public日志类型标记 -*/ -func DoStrategy(ctx context.Context, requestParam interface{}, d Dispatcher, tc *timeutils.TimeCoster) (interface{}, *model.StrategyContext) { - errTag := getInterfaceErrorTag(d.GetInterfaceName()) - defer utils.RecoverPanic(ctx, errTag) - - //1、请求参数解析 - tc.StartSectionCount("BuildRequest") - sc := d.BuildRequest(ctx, requestParam) - tc.StopSectionCount("BuildRequest") - - //2. 执行业务逻辑 - d.GetWorkflowEngine().Run(ctx, sc) - errMap := sc.GetErrorMap() - errMap.Range(func(key, val interface{}) bool { - tlog.ErrorCount(ctx, "WorkflowEngine.Run_err", fmt.Sprintf("workflowengine run error, key=%v, val=%v", key, val)) - return true - }) - - //3. 耗时 - resultMap := sc.GetModuleResultMap() - resultMap.Range(func(key, val interface{}) bool { - moduleResult, ok := val.(*model.ModuleResultInfo) - if ok && moduleResult != nil { - tc.SetSectionCount(moduleResult.StrategyName, moduleResult.CostTime) - return true - } - return false - }) - - //3、组装返回结果 - tc.StartSectionCount("BuildResponse") - responseInfo := d.BuildResponse(sc) - tc.StopSectionCount("BuildResponse") - - //4、异步日志 - go writeLog(ctx, sc, d, errTag) - - return responseInfo, sc -} - -// DoStrategyBatch 批量执行 Workflow 的接口,根据 requestParam 批量执行多次 Workflow,并将结果返回,返回结果的数量总是和请求的数量相同。 -func DoStrategyBatch(ctx context.Context, requestParams []interface{}, d Dispatcher) ([]interface{}, []*model.StrategyContext, []*timeutils.TimeCoster) { - errTag := getInterfaceErrorTag(d.GetInterfaceName()) - defer utils.RecoverPanic(ctx, errTag) - - responseInfos := make([]interface{}, len(requestParams)) - scs := make([]*model.StrategyContext, len(requestParams)) - tcs := make([]*timeutils.TimeCoster, len(requestParams)) - - wg := sync.WaitGroup{} - wg.Add(len(requestParams)) - - for i, req := range requestParams { - - tcs[i] = timeutils.NewTimeCoster() - - go func(curIdx int, curReq interface{}) { - - tc := tcs[curIdx] - tc.StartCount() - - defer func() { - - if err := recover(); err != nil { - tlog.ErrorCount(ctx, errTag, fmt.Sprintf("Recover system panic : %v", err)) - // 如果某个出现 panic,停止计时 - for section := range tc.GetAllCounts() { - tc.StopSectionCount(section) - } - } else { - tc.StopCount() - } - - wg.Done() - - }() - - //1、请求参数解析 - tc.StartSectionCount("BuildRequest") - sc := d.BuildRequest(ctx, curReq) - tc.StopSectionCount("BuildRequest") - - //2. 执行业务逻辑 - d.GetWorkflowEngine().Run(ctx, sc) - errMap := sc.GetErrorMap() - errMap.Range(func(key, val interface{}) bool { - tlog.ErrorCount(ctx, "WorkflowEngine.BatchRun_err", fmt.Sprintf("workflowengine batch run error, key=%v, val=%v, reqNum=%v", key, val, curIdx)) - return true - }) - - //3. 耗时 - resultMap := sc.GetModuleResultMap() - resultMap.Range(func(key, val interface{}) bool { - moduleResult, ok := val.(*model.ModuleResultInfo) - if ok && moduleResult != nil { - tc.SetSectionCount(moduleResult.StrategyName, moduleResult.CostTime) - return true - } - return false - }) - - //3、组装返回结果 - tc.StartSectionCount("BuildResponse") - responseInfo := d.BuildResponse(sc) - tc.StopSectionCount("BuildResponse") - - //4、异步日志,每个请求单独打日志 - go writeLog(ctx, sc, d, errTag) - - responseInfos[curIdx] = responseInfo - scs[curIdx] = sc - - }(i, req) - } - - wg.Wait() - - return responseInfos, scs, tcs -} - -func GetCtxTrace(ctx context.Context) *trace.DefaultTrace { - if ctxTrace, ok := trace.GetCtxTrace(ctx); ok { - return ctxTrace - } else { - return trace.NewDefaultTrace() - } -} - -/****************************************************记录public日志时,调用该方法 - ctx: 上下文环境 - sc: base框架中存放的临时数据,可以传nil - publicKey: 数据采集平台的唯一表名( 建议保持格式: g_系统名_服务接口名 ) - d: base框架中的处理流程的结构体,不同系统都实现base中这个结构体内的方法 - tagName: 数据采集平台的唯一标识( 建议保持格式: 系统名_服务接口名, 注:本标识 全部大写 ) -*************************************************************************************/ -func writeLog(ctx context.Context, sc *model.StrategyContext, d Dispatcher, tagName string) { - defer utils.RecoverPanic(ctx, tagName) - params := d.WriteLog(ctx, sc) - if params == nil { - return - } - - //公有日志信息 - params["uid"] = sc.UserId - params["scene_id"] = sc.SceneId - params["is_rateLimit"] = sc.IsLimited - params["workflow_id"] = sc.FlowId - - mergeLog(params) - tlog.Handler.Public(ctx, d.GetPublicKey(), params, false) -} - -//将要记录到日志中的内容,再做一次非nil的过滤 -func mergeLog(pairs map[string]interface{}) { - for k, v := range pairs { - if v == nil { - pairs[k] = "NULL" - } - } -} diff --git a/tg-core/dispatcher/public_log.go b/tg-core/dispatcher/public_log.go deleted file mode 100644 index b8c83ef..0000000 --- a/tg-core/dispatcher/public_log.go +++ /dev/null @@ -1,29 +0,0 @@ -package dispatcher - -import ( - "context" - "fmt" - "git.xiaojukeji.com/gobiz/logger" - ctxtrace "git.xiaojukeji.com/lego/context-go" - "github.com/didi/tg-flow/tg-core/common/utils" - "strings" - "time" -) - -func WriteCustomLog(ctx context.Context, publicKey string, filePrefix string, pairs map[string]interface{}, tagName string) { - defer utils.RecoverPanic(ctx, tagName) - mergeLog(pairs) - var kvs []string - kvs = append(kvs, publicKey) - kvs = append(kvs, "timestamp="+time.Now().Format("2006-01-02 15:04:05")) - kvs = append(kvs, ctxtrace.FormatCtx(ctx)) - for k, v := range pairs { - if v == nil { - v = "NULL" - } - kvs = append(kvs, k+"="+fmt.Sprint(v)) - } - //记本地日志 - logger.Track(filePrefix, strings.Join(kvs, "||")) -} - diff --git a/tg-core/go.mod b/tg-core/go.mod deleted file mode 100644 index ed5ddf8..0000000 --- a/tg-core/go.mod +++ /dev/null @@ -1,87 +0,0 @@ -module github.com/didi/tg-flow/tg-core - -go 1.21.5 - -require ( - git.xiaojukeji.com/gobiz/config v0.0.10 - git.xiaojukeji.com/gobiz/logger v1.4.7 - git.xiaojukeji.com/gobiz/utils v1.0.1 - git.xiaojukeji.com/lego/context-go v3.3.0+incompatible - git.xiaojukeji.com/nuwa/go-monitor v1.1.6 - git.xiaojukeji.com/nuwa/golibs/redis v0.5.10 - git.xiaojukeji.com/nuwa/nuwa-go-httpclient v1.3.19 - git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2 v2.8.14 - github.com/coocood/freecache v1.2.4 - github.com/didi/gendry v1.8.2 - github.com/go-sql-driver/mysql v1.7.1 - github.com/robfig/cron v1.2.0 - go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2 v2.7.7+incompatible - go.intra.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20230228024253-24fcac901643 - go.intra.xiaojukeji.com/platform-ha/onekey-degrade_sdk_go v3.2.9+incompatible -) - -require ( - git.xiaojukeji.com/devops/statsd v0.2.1 // indirect - git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.53 // indirect - git.xiaojukeji.com/foundation/thrift v0.9.3 // indirect - git.xiaojukeji.com/gobiz/elapsed v0.0.2 // indirect - git.xiaojukeji.com/lego/common-go v0.1.24 // indirect - git.xiaojukeji.com/lego/dirpc-go v1.24.2 // indirect - git.xiaojukeji.com/lego/ruleEngine v1.0.0 // indirect - git.xiaojukeji.com/lego/sentinel-golang v1.1.3 // indirect - git.xiaojukeji.com/lego/thrift16 v1.0.1 // indirect - git.xiaojukeji.com/nuwa/binding v0.1.4 // indirect - git.xiaojukeji.com/nuwa/golibs/discover v0.0.5 // indirect - git.xiaojukeji.com/nuwa/golibs/httprouter v0.0.11 // indirect - git.xiaojukeji.com/nuwa/golibs/metrics v0.2.4 // indirect - git.xiaojukeji.com/nuwa/golibs/redigo v1.8.9 // indirect - git.xiaojukeji.com/nuwa/gorequest v1.0.5 // indirect - git.xiaojukeji.com/nuwa/trace v1.3.12 // indirect - git.xiaojukeji.com/nuwa/trace/v2 v2.0.7 // indirect - git.xiaojukeji.com/observe-trace/didi-open-telemetry/opentelemetry-go v1.0.0 // indirect - git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.2.0 // indirect - github.com/BurntSushi/toml v1.3.2 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/caio/go-tdigest v3.1.0+incompatible // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect - github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect - github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 // indirect - github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434 // indirect - github.com/facebookgo/httpdown v0.0.0-20180706035922-5979d39b15c2 // indirect - github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect - github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4 // indirect - github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect - github.com/go-logr/logr v1.3.0 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.3.0 // indirect - github.com/golang/mock v1.6.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/uuid v1.5.0 // indirect - github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect - github.com/parnurzeal/gorequest v0.2.16 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect - github.com/prometheus/client_golang v1.17.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect - github.com/shirou/gopsutil/v3 v3.23.11 // indirect - github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/tklauser/go-sysconf v0.3.13 // indirect - github.com/tklauser/numcpus v0.7.0 // indirect - github.com/ugorji/go/codec v1.2.12 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect - go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.9.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/sys v0.15.0 // indirect - google.golang.org/grpc v1.60.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect - gopkg.in/go-playground/assert.v1 v1.2.1 // indirect - gopkg.in/go-playground/validator.v8 v8.18.2 // indirect - gopkg.in/validator.v2 v2.0.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - moul.io/http2curl v1.0.1-0.20190925090545-5cd742060b0e // indirect -) diff --git a/tg-core/go.sum b/tg-core/go.sum deleted file mode 100644 index 6f24ad3..0000000 --- a/tg-core/go.sum +++ /dev/null @@ -1,807 +0,0 @@ -bou.ke/monkey v1.0.2/go.mod h1:OqickVX3tNx6t33n1xvtTtu85YN5s6cKwVug+oHMaIA= -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= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -git.xiaojukeji.com/devops/statsd v0.0.5/go.mod h1:VwNi66bpKq2QSqq8xYHEhl1QVkaKWRqH9nXUPbRnMhM= -git.xiaojukeji.com/devops/statsd v0.0.10/go.mod h1:VwNi66bpKq2QSqq8xYHEhl1QVkaKWRqH9nXUPbRnMhM= -git.xiaojukeji.com/devops/statsd v0.0.13/go.mod h1:F9Q3cQhEZpMV+4iElWE6Oth6CW0Rk3bxMy6UbQhiHMA= -git.xiaojukeji.com/devops/statsd v0.2.1 h1:JsqNCvP6qNwBAvhK98JcvBf2mfxmtNgzPhloZkpkyRg= -git.xiaojukeji.com/devops/statsd v0.2.1/go.mod h1:F9Q3cQhEZpMV+4iElWE6Oth6CW0Rk3bxMy6UbQhiHMA= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.36/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi5XlD96ZFqTAuG4EKCiv0= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.37/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi5XlD96ZFqTAuG4EKCiv0= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.39/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi5XlD96ZFqTAuG4EKCiv0= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.40-0.20210819030951-70dcfc268e9a/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi5XlD96ZFqTAuG4EKCiv0= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.40/go.mod h1:B4hJumWMTtOP4X4vm1jqPOi5XlD96ZFqTAuG4EKCiv0= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.43/go.mod h1:KVTUFxHUPxQVrzedCj1/3/u8IeWQM8Ka5aC2neD8TWY= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.44/go.mod h1:KVTUFxHUPxQVrzedCj1/3/u8IeWQM8Ka5aC2neD8TWY= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.47/go.mod h1:KVTUFxHUPxQVrzedCj1/3/u8IeWQM8Ka5aC2neD8TWY= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.48/go.mod h1:Czv16HGVcZWhv4YzONQuFlGu4WBaOWJ22U0F/JFp21M= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.53 h1:nGd+7jQXOrSTeIM3vdgxclvD6kNZWKdAyFdic8caI1A= -git.xiaojukeji.com/disf/disf-go-spl-v2 v0.1.53/go.mod h1:Czv16HGVcZWhv4YzONQuFlGu4WBaOWJ22U0F/JFp21M= -git.xiaojukeji.com/disfv4/commlib v0.0.0-20230515071326-a183b2265970/go.mod h1:hhyhpVcle1TfEdE9h9GVTq89Z6Tiu1jPl2fF2Jwyklg= -git.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20201123053335-e67edadc52bf h1:IqH2bM38ZLmza3beTh2c4Ay/CfVN2ILjNJt4cXuXy2g= -git.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20201123053335-e67edadc52bf/go.mod h1:BL9PrFeJAMNJrTbAKJA4dCVUUw4GWE6QuWSwFjxPm4k= -git.xiaojukeji.com/foundation/thrift v0.9.3 h1:LCcExsZL5T/bTnGhVyNHfsjs5+MucWm+eCFa6R/Iq+A= -git.xiaojukeji.com/foundation/thrift v0.9.3/go.mod h1:mr0sUXUW5iUveD+BQlkx2kam22mYnzc9plIAhQkg+1U= -git.xiaojukeji.com/gobiz/config v0.0.7/go.mod h1:FJEpa4infv+1bQ09dywzUWAVtjejzzWWL9968tPVzt8= -git.xiaojukeji.com/gobiz/config v0.0.10 h1:QB9JgGl/cGN4OTK9gsj3Xkmu6pmU4LU8iHx6sD9iYvU= -git.xiaojukeji.com/gobiz/config v0.0.10/go.mod h1:TJd2sFc9JFf01ZEnZIx/2sAlvlfzOUuKlB7rYF1xp7k= -git.xiaojukeji.com/gobiz/elapsed v0.0.2 h1:JARCp5+9pU+FzOiNE4y6zkax+J+hkNxpucS38oSnTKI= -git.xiaojukeji.com/gobiz/elapsed v0.0.2/go.mod h1:GGQzLBmcvN0WOQ91cNqvg8Lg/+Z2Hrd6empMYH+lY2Q= -git.xiaojukeji.com/gobiz/logger v1.2.28/go.mod h1:wcP4mP7epj0D2er88Try58mCZB7goPf4Cjykd6YkWKc= -git.xiaojukeji.com/gobiz/logger v1.3.3/go.mod h1:RFNcCXNy3+Rz+TkKQZvi7g3PKT/EyG3sxW+Jr7d+pG8= -git.xiaojukeji.com/gobiz/logger v1.3.4/go.mod h1:qI83N+0IDFBYtja87v+ah3js4ANg+Jpl8/8QzVFTSjc= -git.xiaojukeji.com/gobiz/logger v1.4.4/go.mod h1:qI83N+0IDFBYtja87v+ah3js4ANg+Jpl8/8QzVFTSjc= -git.xiaojukeji.com/gobiz/logger v1.4.5/go.mod h1:qI83N+0IDFBYtja87v+ah3js4ANg+Jpl8/8QzVFTSjc= -git.xiaojukeji.com/gobiz/logger v1.4.7 h1:CwOQTMimodav3l+Fh6WmKOrX1XHYDbIZAqOjy8Stcfk= -git.xiaojukeji.com/gobiz/logger v1.4.7/go.mod h1:qI83N+0IDFBYtja87v+ah3js4ANg+Jpl8/8QzVFTSjc= -git.xiaojukeji.com/gobiz/utils v0.0.7/go.mod h1:myE5vDJLUH3ME5Rc7ebg+7QSVusHABrHwbgYxE8YCWI= -git.xiaojukeji.com/gobiz/utils v1.0.1 h1:BftvQ8VdFC1KqE5gfkJ08XiM5DWmMyCbBNrrS2eGvhw= -git.xiaojukeji.com/gobiz/utils v1.0.1/go.mod h1:myE5vDJLUH3ME5Rc7ebg+7QSVusHABrHwbgYxE8YCWI= -git.xiaojukeji.com/golang/go-tdigest v0.0.0-20160413204300-577940117044/go.mod h1:C7L62J5e+T2mAUXpgJck6EakqV3aMEz4UAzfN7Br2ak= -git.xiaojukeji.com/lego/common-go v0.1.19/go.mod h1:6KXHn+oe36kXyiImOGwjSCoVT7lrkRUqm/OjWClCVhQ= -git.xiaojukeji.com/lego/common-go v0.1.22/go.mod h1:0E8emkc2GJPf2zSclkMMCTcrZkEHX00EuDxxXvSE5JM= -git.xiaojukeji.com/lego/common-go v0.1.24 h1:OisrYkP2DejFYdA8A8Q6LD4iCXU06oLB69SmrLDNSKY= -git.xiaojukeji.com/lego/common-go v0.1.24/go.mod h1:0E8emkc2GJPf2zSclkMMCTcrZkEHX00EuDxxXvSE5JM= -git.xiaojukeji.com/lego/context-go v3.0.1+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= -git.xiaojukeji.com/lego/context-go v3.0.3-0.20210204053446-cc0c77017173+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= -git.xiaojukeji.com/lego/context-go v3.0.4+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= -git.xiaojukeji.com/lego/context-go v3.2.1+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= -git.xiaojukeji.com/lego/context-go v3.2.4+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= -git.xiaojukeji.com/lego/context-go v3.3.0+incompatible h1:FuagXoU771fadZagGj+wBtUiIm+V9TBTSb6TxzXTEzU= -git.xiaojukeji.com/lego/context-go v3.3.0+incompatible/go.mod h1:PEQ2BpFDQs/uAWpGO/tC9bMfWrnptw3M9MO2DaY1KVY= -git.xiaojukeji.com/lego/dirpc-go v1.16.9/go.mod h1:HbVRjEnrfpYTM2nwqqTwJaKZwmfRyEsVa8cjBscaeh4= -git.xiaojukeji.com/lego/dirpc-go v1.16.11-0.20210816074537-6e2a7ab987c1/go.mod h1:EfOBTBplIfu8ua3WpX1OT621kaeHO+lErjngU9xvF8U= -git.xiaojukeji.com/lego/dirpc-go v1.16.11-0.20210819033055-b145eb63fb00/go.mod h1:IY4Eps2zn266XXqnvM6q4mbqjJByNjwRmDVAt/CFXtQ= -git.xiaojukeji.com/lego/dirpc-go v1.16.11-0.20210820022224-2f84346236de/go.mod h1:+PjDjcXqeTsv6jAF+GlzDL8u6lbbRcoRqelIo/1hDVw= -git.xiaojukeji.com/lego/dirpc-go v1.16.13/go.mod h1:GFpAKh+xbcCp3eKTbSKODHHRh6FbuzKlFr/Rnotftqs= -git.xiaojukeji.com/lego/dirpc-go v1.16.24/go.mod h1:rfAMEZLwY9h96yvgIC4/dFN/gHZaAfpVBdjkqiqYXpc= -git.xiaojukeji.com/lego/dirpc-go v1.17.1-0.20220929063334-53711c3febc5/go.mod h1:XK/k7QyF2tmZJ0kmZzh5t5z3XKvCUsweDC2mgasdr40= -git.xiaojukeji.com/lego/dirpc-go v1.22.4/go.mod h1:JQ1uPSpsIJcRKsFkjnA/DDmcxqRiXn3JB7vda7cvO38= -git.xiaojukeji.com/lego/dirpc-go v1.24.2 h1:EPtA7YMj7/C9NmdN/x7Cj01P8bMv2mH7fSCoqOa832c= -git.xiaojukeji.com/lego/dirpc-go v1.24.2/go.mod h1:Pe3JJiqY6PkaI37hykbWve4nPI2hHDJnf0v0RJXONH8= -git.xiaojukeji.com/lego/ruleEngine v1.0.0 h1:bLaRaf44ckhFe+vLKcfGYu7gu8+DJh8xJUw8Z95CQ7A= -git.xiaojukeji.com/lego/ruleEngine v1.0.0/go.mod h1:RRcCo5URt7nhBMQcpEnI780n0rlpeND4B+gVKczuV7U= -git.xiaojukeji.com/lego/sentinel-golang v1.1.3 h1:0Am+H/M5vHA09CFr2XQSvNpbEA2Txp+E+y0TFlnkeqA= -git.xiaojukeji.com/lego/sentinel-golang v1.1.3/go.mod h1:zAu2UQPlhAnXSt8ZjQMqG6Z2KMo8jyytmh3SVwXJii4= -git.xiaojukeji.com/lego/thrift16 v1.0.1 h1:N3DNsT+ZR4lbGMT4o//ReA6c7Rlufrs9HhtdMrMbyS0= -git.xiaojukeji.com/lego/thrift16 v1.0.1/go.mod h1:t8gPK22SMa8dXww/qpmsTLUfMLhUYTBoFxFN3lVi8GA= -git.xiaojukeji.com/nuwa/binding v0.1.2/go.mod h1:yRWzl8Cjj62nCxibZvXKbLeDTcO1dToXlu0f2xHaHms= -git.xiaojukeji.com/nuwa/binding v0.1.4 h1:8hdqh8T3sjDmesB1a/xhtPLkOLraGeuAMhciQc+uwzg= -git.xiaojukeji.com/nuwa/binding v0.1.4/go.mod h1:yRWzl8Cjj62nCxibZvXKbLeDTcO1dToXlu0f2xHaHms= -git.xiaojukeji.com/nuwa/go-monitor v1.1.6 h1:TZZMcIskQiJZLW8pIAd/Ox0pMTooDAb1olH9MsigBAE= -git.xiaojukeji.com/nuwa/go-monitor v1.1.6/go.mod h1:C0m8zJlL59dpoiQSR8T1RqLHOLB2MeMnI8o2YYFRgkg= -git.xiaojukeji.com/nuwa/golibs/discover v0.0.5 h1:yflPBzKHJ2IcwWobmf8E3J1Bay1YZliXRWQmLc+N6Qw= -git.xiaojukeji.com/nuwa/golibs/discover v0.0.5/go.mod h1:7nTcaUccQIpjHWfmocU6Htny8j7vpPfSAfo1cGPjiDg= -git.xiaojukeji.com/nuwa/golibs/httprouter v0.0.11 h1:5PpQzsYLDIMGgSwTq9BjaMdHEBgGOf468BaFVmKRWyc= -git.xiaojukeji.com/nuwa/golibs/httprouter v0.0.11/go.mod h1:LpU0X+hTVxQ6yY8vEg+FrmpaB7DAUPxuFShbZaj8yYs= -git.xiaojukeji.com/nuwa/golibs/metrics v0.2.4 h1:ZQdZxSBiOvLqTzgie780B4LQZPyBRw37f0JNT65pe34= -git.xiaojukeji.com/nuwa/golibs/metrics v0.2.4/go.mod h1:qy3ra5x3CM8zPCDdGjAvqGv9P1amYXTCf6cv4C7rWVI= -git.xiaojukeji.com/nuwa/golibs/redigo v1.8.9 h1:DvRrJIzI8ix+hsmYY6h31Zo9Wi8FhXt7HhnU2kupKFk= -git.xiaojukeji.com/nuwa/golibs/redigo v1.8.9/go.mod h1:+2yBHoZLPIIauBdl+B3lFypf7luJpDFDz8VbS+wNnU8= -git.xiaojukeji.com/nuwa/golibs/redis v0.5.10 h1:Ak6E66X3xhHxmfRdwBUGc4c6g2g92Gk5omGn9eHJ6RY= -git.xiaojukeji.com/nuwa/golibs/redis v0.5.10/go.mod h1:1n+f9NrLYc18zapwbrIJDslXyyT+IHhu8ewmN2kXcls= -git.xiaojukeji.com/nuwa/gorequest v1.0.5 h1:zqxlREsrHebH/mJAStgHSRZJ3L11/i4DnLQQCJqmxII= -git.xiaojukeji.com/nuwa/gorequest v1.0.5/go.mod h1:ritycdzL+I1Dipfd7P3dzS5lTU+lbEPBFga0bj0aMcI= -git.xiaojukeji.com/nuwa/nuwa-go-httpclient v1.3.19 h1:U9WSMg6enWv3lMEdF17lm3CtDkEhMNH8xbKdl0Jo8zk= -git.xiaojukeji.com/nuwa/nuwa-go-httpclient v1.3.19/go.mod h1:ClQRt6GLEkY5yrBCLHEk607f5R0hSFi3btkqkVC3kTM= -git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2 v2.8.14 h1:k8Pfn3ziPh0WKFf8jNjOqPg4tDou/AMMDqn4+BBM6OU= -git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2 v2.8.14/go.mod h1:WJI5y1Dy+6y8BClx9SYzUXETVwmRR7sTFbrmyVGCOlA= -git.xiaojukeji.com/nuwa/protoc-dirpc v1.2.0/go.mod h1:gzfVNNsbUshc1LPU4FIWSVeRwqL2arLuc3o/G785hME= -git.xiaojukeji.com/nuwa/trace v1.3.12 h1:15K0Y+PG3p1lSCpwdhRlQM/mN7NwravpeY/l+rmqu0g= -git.xiaojukeji.com/nuwa/trace v1.3.12/go.mod h1:8Og3kmWTAaXezXM4Aq3HWe75sMY+d3jA77nmyn4c9DA= -git.xiaojukeji.com/nuwa/trace/v2 v2.0.3/go.mod h1:Mlg8+AlIxlUh7l0Vc+sVhx8Ef6G9Q0UKTURUCV8rC6g= -git.xiaojukeji.com/nuwa/trace/v2 v2.0.7 h1:4SmofQ5PUxn43JozBVYc5qA3s5JvWByqaZgkMehk0JU= -git.xiaojukeji.com/nuwa/trace/v2 v2.0.7/go.mod h1:bj9wOA2DCLwBG1Dv5dl+zD4n3EikxolGooXPzCyJWP4= -git.xiaojukeji.com/observe-trace/didi-open-telemetry/opentelemetry-go v1.0.0 h1:35o8w+wEoBWzll5advs81JvN66wUpHaYLP1bR3VsSvE= -git.xiaojukeji.com/observe-trace/didi-open-telemetry/opentelemetry-go v1.0.0/go.mod h1:ky1/wTDa+siozh9jqNJRnB/CAxZlZm4EYw325++3yo8= -git.xiaojukeji.com/ops-sec/disf_kms_sdk_go v0.0.0-20190107121632-c61f10c1e31c/go.mod h1:iIi/pKbE7lHEEoae1JMXQFKljtvF7Fadc030GjKDB3E= -git.xiaojukeji.com/pearls/tlog v0.0.4/go.mod h1:DzQ156tfv5IVzVQOR0WCGGDefMu1pfbFq6OkO7GlFCs= -git.xiaojukeji.com/pearls/tlog v0.1.0/go.mod h1:DzQ156tfv5IVzVQOR0WCGGDefMu1pfbFq6OkO7GlFCs= -git.xiaojukeji.com/pt-arch/warden-client-go v0.0.2/go.mod h1:YUic+w1o/mEreI9DnNSIBoc31uU6A7Jl7pZZXT3CWOc= -git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.0-20210804142554-1df30aae0688/go.mod h1:pWxnUiWor02/XhYPNRibAmm1sp5k3AltYx6WB460Fvg= -git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.0-20210816124301-0acc0ae1c20f/go.mod h1:5f/Rzc4tA9h9CoFduCpcSsfHU35fR3kzTZ7qOTnp75U= -git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.0-20210819121323-90575b150ac4/go.mod h1:xPRjOpqUXSC5piE5IPRtFz8h+m8sfpMFUkmnhmDAgbg= -git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.0-20210831103212-e97e2e927a7c/go.mod h1:uphE+MJrMys/D7Yw18y05N3IzrgZsx1xnCrM0haxjQI= -git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.4/go.mod h1:3ori74OkxOH/XgqL+FGU9ldXIMfZEyYLRI02f8d0r/M= -git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.6/go.mod h1:3ori74OkxOH/XgqL+FGU9ldXIMfZEyYLRI02f8d0r/M= -git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.10/go.mod h1:Wae6/Gb+RYkUViUQaXkZc7gJ9TQPjHZnRbox0wT9Kho= -git.xiaojukeji.com/sre-ha/chaos-interceptor v0.0.17/go.mod h1:rbqsC9No/XJ42EJrGunTKzLi2EsL2hRo4lMhiCVtbIw= -git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.1.0/go.mod h1:rB20mmovWOnp7p/mRvdLXA8g2G3E2ZO6cdxhvJy/pdE= -git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.2.0 h1:AQhyLGsHdPTLyIzofg4MhKnzY4ZHVuNm/csXNelnAqs= -git.xiaojukeji.com/sre-ha/chaos-sdk-go v0.2.0/go.mod h1:XEVTaVFcGb7yXJlcLg688HoU855DoYFhe6Dzj2XpriA= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DATA-DOG/go-sqlmock v1.4.0/go.mod h1:3TucWNLPFOLcHhha1CPp7Kis1UG2h/AqGROPyOeZzsM= -github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -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/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -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/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -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/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -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/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -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.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= -github.com/caio/go-tdigest v3.1.0+incompatible h1:uoVMJ3Q5lXmVLCCqaMGHLBWnbGoN6Lpu7OAUPR60cds= -github.com/caio/go-tdigest v3.1.0+incompatible/go.mod h1:sHQM/ubZStBUmF1WbB8FAm8q9GjDajLC5T7ydxE3JHI= -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 v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -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/coocood/freecache v1.2.4 h1:UdR6Yz/X1HW4fZOuH0Z94KwG851GWOSknua5VUbb/5M= -github.com/coocood/freecache v1.2.4/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk= -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/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-rendezvous v0.0.0-20200609043717-5ab96a526299/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/didi/gendry v1.8.2 h1:rqeMwz9CI4GdKX2fDg13xba5f/45lJdf4bttrXi+smQ= -github.com/didi/gendry v1.8.2/go.mod h1:cSLuShZ1Zbs1S05RIOLNQv616aBaOQ1BDrXJP9A3J+M= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/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/elazarl/goproxy v0.0.0-20201021153353-00ad82a08272/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= -github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= -github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 h1:lS3P5Nw3oPO05Lk2gFiYUOL3QPaH+fRoI1wFOc4G1UY= -github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= -github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= -github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= -github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= -github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= -github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 h1:wWke/RUCl7VRjQhwPlR/v0glZXNYzBHdNUzf/Am2Nmg= -github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9/go.mod h1:uPmAp6Sws4L7+Q/OokbWDAK1ibXYhB3PXFP1kol5hPg= -github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434 h1:mOp33BLbcbJ8fvTAmZacbBiOASfxN+MLcLxymZCIrGE= -github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434/go.mod h1:KigFdumBXUPSwzLDbeuzyt0elrL7+CP7TKuhrhT4bcU= -github.com/facebookgo/httpdown v0.0.0-20180706035922-5979d39b15c2 h1:nXeeRHmgNgjLxi+7dY9l9aDvSS1uwVlNLqUWIY4Ath0= -github.com/facebookgo/httpdown v0.0.0-20180706035922-5979d39b15c2/go.mod h1:TUV/fX3XrTtBQb5+ttSUJzcFgLNpILONFTKmBuk5RSw= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= -github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4 h1:0YtRCqIZs2+Tz49QuH6cJVw/IFqzo39gEqZ0iYLxD2M= -github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4/go.mod h1:vsJz7uE339KUCpBXx3JAJzSRH7Uk4iGGyJzR529qDIA= -github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= -github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -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-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= -github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-redis/redis/v8 v8.0.0-beta.5/go.mod h1:Mm9EH/5UMRx680UIryN6rd5XFn/L7zORPqLV+1D5thQ= -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.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -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/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -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/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -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.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/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/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -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.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.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-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -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/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -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/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -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/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -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.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -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/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -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/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.3/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/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 h1:X/79QL0b4YJVO5+OsPH9rF2u428CIrGL/jLmPsoOQQ4= -github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U= -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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0= -github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= -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.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -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.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= -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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/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/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/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/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/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/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ= -github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= -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/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/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= -github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -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_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= -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/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -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/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= -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/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= -github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= -github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -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/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shirou/gopsutil/v3 v3.21.6/go.mod h1:JfVbDpIBLVzT8oKbvMg9P3wEIMDDpVn+LwHTKj0ST88= -github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= -github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= -github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= -github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= -github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= -github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= -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.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -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/smartystreets/goconvey v1.6.6/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/spaolacci/murmur3 v0.0.0-20170819071325-9f5d223c6079/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -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/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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -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.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= -github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= -github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= -github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= -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 v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= -github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= -github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= -github.com/ugorji/go/codec v1.2.12/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/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -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.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2 v2.7.7+incompatible h1:f4YXPEGDypeNbQp2vG8j5AJIK35Aezq5fiYbj8Nl6Bc= -go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2 v2.7.7+incompatible/go.mod h1:1fUxSjrbbsP3ktoGq3RtmeFvSZNEKfJhiXwQKlPLGBo= -go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.8.7/go.mod h1:FOVCHswBfssBnx/PvA0VIiaYUayetSavG1ymwcRUxqk= -go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.8.9/go.mod h1:FOVCHswBfssBnx/PvA0VIiaYUayetSavG1ymwcRUxqk= -go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.9.0 h1:MgEj0JXHMFZoPQ40XiSGYHDPtrLdtR0gf67E4USrMyA= -go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/v2 v2.9.0/go.mod h1:FOVCHswBfssBnx/PvA0VIiaYUayetSavG1ymwcRUxqk= -go.intra.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20230228024253-24fcac901643 h1:otYMsv3wSriOsUNVuHUMzVTx+QujPugKIE3twZKGPIk= -go.intra.xiaojukeji.com/foundation/didi-standard-lib v0.0.0-20230228024253-24fcac901643/go.mod h1:xq25s/hZNGEYYgLfZAwF64xgQAivPGNZYij6/4RiuRo= -go.intra.xiaojukeji.com/platform-ha/onekey-degrade_sdk_go v3.2.8+incompatible/go.mod h1:4nbUEJpW13dUlRufRpPNyOFnpAo+UGS2MGiVq59P4sY= -go.intra.xiaojukeji.com/platform-ha/onekey-degrade_sdk_go v3.2.9+incompatible h1:uDEL0y4ADgQXDafXQHAVNpgdczwVMkwKAnvbmstr88Y= -go.intra.xiaojukeji.com/platform-ha/onekey-degrade_sdk_go v3.2.9+incompatible/go.mod h1:4nbUEJpW13dUlRufRpPNyOFnpAo+UGS2MGiVq59P4sY= -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.opentelemetry.io/otel v0.6.0/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek= -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.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/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-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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -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/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -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.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -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-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191207000613-e7e4b65ae663/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -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/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-20190312061237-fead79001313/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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/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-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -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-20180525024113-a5b4c53f6e8b/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-20190206041539-40960b6deb8e/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-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-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-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -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= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -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/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -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.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.60.0 h1:6FQAR0kM31P6MRdeluor2w2gPaS4SVNrD/DNTxrQ15k= -google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.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/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -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/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -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/validator.v2 v2.0.0-20200605151824-2b28d334fa05/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc= -gopkg.in/validator.v2 v2.0.0-20210331031555-b37d688a7fb0/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc= -gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY= -gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8= -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.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/mysql v1.1.2/go.mod h1:4P/X9vSc3WTrhTLZ259cpFd6xKNYiSSdSZngkSBGIMM= -gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8= -gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/hints v1.1.0/go.mod h1:lKQ0JjySsPBj3uslFzY3JhYDtqEwzm+G1hv8rWujB6Y= -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= -moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= -moul.io/http2curl v1.0.1-0.20190925090545-5cd742060b0e h1:C7q+e9M5nggAvWfVg9Nl66kebKeuJlP3FD58V4RR5wo= -moul.io/http2curl v1.0.1-0.20190925090545-5cd742060b0e/go.mod h1:nejbQVfXh96n9dSF6cH3Jsk/QI1Z2oEL7sSI2ifXFNA= -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= diff --git a/tg-core/model/algo_model.go b/tg-core/model/algo_model.go deleted file mode 100644 index 311a785..0000000 --- a/tg-core/model/algo_model.go +++ /dev/null @@ -1,18 +0,0 @@ -package model - -import ( - "time" -) - -//算法模型配置索引 -type AlgoModelIndex struct { - IndexVersion string `json:"index_version"` - IndexMap map[string][]string `json:"index_map"` - UpdateTime time.Time `json:"updateTime"` -} - -//索引更新后,向center回报状态的返回结果:ErrNo=0,表示操作成功 -type CenterServerReportResponseInfo struct { - ErrNo int32 `thrift:"err_no,1,required" json:"err_no"` - ErrMsg string `thrift:"err_msg,2,required" json:"err_msg"` -} diff --git a/tg-core/model/apollo_config.go b/tg-core/model/apollo_config.go deleted file mode 100644 index 0a6b174..0000000 --- a/tg-core/model/apollo_config.go +++ /dev/null @@ -1,148 +0,0 @@ -/** - Description: 存放apollo实验相关的配置信息,提供相关访问方法的封装 - Author: dayunzhangyunfeng@didiglobal.com - Date: 2020-08-31 -**/ - -package model - -import ( - "fmt" - "go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2" - apolloModel "go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/model" -) - -type ApolloConfig struct { - ApolloUsers map[string]*apolloModel.User - dispatchUser *apolloModel.User - dispatchExperimentName string -} - -func NewApolloConfig() *ApolloConfig { - apolloUsers := make(map[string]*apolloModel.User) - return &ApolloConfig{ApolloUsers: apolloUsers} -} - -// SetDispatchUser 设置分流 User -func (this *ApolloConfig) SetDispatchUser(apolloUser *apolloModel.User) { - this.dispatchUser = apolloUser -} - -// SetDispatchExperimentName 设置分流实验名,如果分流实验名保存在数据库中,则不需要 SetDispatchExperimentName -func (this *ApolloConfig) SetDispatchExperimentName(experimentName string) { - this.dispatchExperimentName = experimentName -} - -func (this *ApolloConfig) GetDispatchExperimentName() string { - return this.dispatchExperimentName -} - -func (this *ApolloConfig) GetDispatchGroupName() (string, error) { - - if this.dispatchUser == nil || this.dispatchExperimentName == "" { - return "", fmt.Errorf("dispatch info not correct, user=%v, exp_name=%v", this.dispatchUser, this.dispatchExperimentName) - } - - toggle, err := apollo.FeatureToggle(this.dispatchExperimentName, this.dispatchUser) - if err != nil { - return "", err - } - - if toggle.IsAllow() { - return toggle.GetAssignment().GetGroupName(), nil - } else { - //The individual which is not allowed to enter the experiment using the default strategy - return "", fmt.Errorf("the user is not permitted to enter the apollo experiment: %v", this.dispatchExperimentName) - } -} - -// SetApolloUser 已经弃用,建议自己采用 Apollo SDK 执行实验相关操作 -func (this *ApolloConfig) SetApolloUser(experimentName string, apolloUser *apolloModel.User) { - this.ApolloUsers[experimentName] = apolloUser -} - -// GetGroupName 已经弃用,建议自己采用 Apollo SDK 执行实验相关操作 -func (this *ApolloConfig) GetGroupName(experimentName string) (string, error) { - toggleResult, err := apollo.FeatureToggle(experimentName, this.ApolloUsers[experimentName]) - if err != nil { - return "", err - } - - //If toggle.allow is true, this individual is allowed to enter the experiment - if toggleResult.IsAllow() { - assignment := toggleResult.GetAssignment() - return assignment.GetGroupName(), nil - } else { - //The individual which is not allowed to enter the experiment using the default strategy - return "", fmt.Errorf("the user is not permitted to enter the apollo experiment: %v", experimentName) - } -} - -// GetApolloParam 取指定实验的指定参数值,注意:此函数有可能由于缺省值的补漏而隐藏取参数值失败的错误, -// 已经弃用,建议自己采用 Apollo SDK 执行实验相关操作 -func (this *ApolloConfig) GetApolloParam(experimentName, paramName, defaultParamValue string) (string, error) { - apolloUser, ok := this.ApolloUsers[experimentName] - if !ok { - return defaultParamValue, fmt.Errorf("apollo user for experimentName:%v not initialzed!", experimentName) - } - - toggleResult, err := apollo.FeatureToggle(experimentName, apolloUser) - if err != nil { - return defaultParamValue, err - } - - //If toggle.allow is true, this individual is allowed to enter the experiment - if toggleResult.IsAllow() { - //The sample were divided into different groups and used different strategies. - assignment := toggleResult.GetAssignment() - return assignment.GetParameter(paramName, defaultParamValue), nil - } else { - return defaultParamValue, fmt.Errorf("user not allowed in apollo experiment:%v", experimentName) - } -} - -// GetApolloParams 取指定实验的全部参数并以map[string]string格式返回,如value不为string类型,则转为string -// 已经弃用,建议自己采用 Apollo SDK 执行实验相关操作 -func (this *ApolloConfig) GetApolloParams(experimentName string) (map[string]string, error) { - apolloUser, ok := this.ApolloUsers[experimentName] - if !ok { - return nil, fmt.Errorf("apollo user for experimentName:%v not initialzed!", experimentName) - } - - toggleResult, err := apollo.FeatureToggle(experimentName, apolloUser) - if err != nil { - return nil, err - } - - //If toggle.allow is true, this individual is allowed to enter the experiment - if toggleResult.IsAllow() { - //The sample were divided into different groups and used different strategies. - assignment := toggleResult.GetAssignment() - return assignment.GetParameters(), nil - } else { - return nil, fmt.Errorf("user not allowed in apollo experiment:%v", experimentName) - } -} - -// GetRawApolloParams 取指定实验的全部原始参数 -// 已经弃用,建议自己采用 Apollo SDK 执行实验相关操作 -func (this *ApolloConfig) GetRawApolloParams(experimentName string) (map[string]interface{}, error) { - apolloUser, ok := this.ApolloUsers[experimentName] - if !ok { - return nil, fmt.Errorf("apollo user for experimentName:%v not initialzed!", experimentName) - } - - toggleResult, err := apollo.FeatureToggle(experimentName, apolloUser) - if err != nil { - return nil, err - } - - //If toggle.allow is true, this individual is allowed to enter the experiment - if toggleResult.IsAllow() { - //The sample were divided into different groups and used different strategies. - assignment := toggleResult.GetAssignment() - return assignment.GetRawParameters(), nil - } else { - return nil, fmt.Errorf("user not allowed in apollo experiment:%v", experimentName) - } -} diff --git a/tg-core/model/dimension.go b/tg-core/model/dimension.go deleted file mode 100644 index 5e7e75f..0000000 --- a/tg-core/model/dimension.go +++ /dev/null @@ -1,20 +0,0 @@ -package model - -import ( - "time" -) - -type Dimension struct { - Id int64 `json:"id"` - Name string `json:"name"` - SceneId int64 `json:"sceneId"` - //第一层key:维度(如"city");value:值的数组 - ContentMap map[string][]string `json:"contentMap"` - UpdateTime time.Time `json:"updateTime"` -} - -type DimensionIndex struct { - //一级key->场景id;二级key->维度类型(如"city");三级key->维度值;value->维度id - DimensionMap map[int64]map[string]map[string]int64 `json:"dimensionMap"` - UpdateTime time.Time `json:"updateTime"` -} diff --git a/tg-core/model/down_grade.go b/tg-core/model/down_grade.go deleted file mode 100644 index fb42e42..0000000 --- a/tg-core/model/down_grade.go +++ /dev/null @@ -1,17 +0,0 @@ -package model - -import ( - "time" -) - -type DowngradeStrategyInfo struct { - LevelList []*DowngradeLevel `json:"levels"` - SceneIds []int64 `json:"scene_ids"` - UpdateTime time.Time -} - -type DowngradeLevel struct { - Level int `json:"level"` - Rate int64 `json:"rate"` - Status bool `json:"status"` -} diff --git a/tg-core/model/omega_info.go b/tg-core/model/omega_info.go deleted file mode 100644 index 47266fa..0000000 --- a/tg-core/model/omega_info.go +++ /dev/null @@ -1,12 +0,0 @@ -package model - -type OmegaInfo struct { - Bootstrap string `json:"boot_strap"` - User string `json:"user"` - Password string `json:"pass_word"` - Topic string `json:"topic"` - SceneId int64 `json:"scene_id"` - EventId map[string]string `json:"event_id"` - BuryData map[string]string `json:"bury_data"` - IsCollectUserAction bool `json:"is_collect_user_action"` -} diff --git a/tg-core/model/recall_config.go b/tg-core/model/recall_config.go deleted file mode 100644 index eda6530..0000000 --- a/tg-core/model/recall_config.go +++ /dev/null @@ -1,16 +0,0 @@ -package model - -import ( - -) - -type RecallConfig struct { - Id int64 `json:"id"` - SceneId int64 `json:"scene_id"` - RecallRule []RecallRuleType `json:"recall_rule"` -} - -type RecallRuleType struct { - RecallType string `json:"recall_type"` - TopN int64 `json:"top_n"` -} \ No newline at end of file diff --git a/tg-core/model/system_config.go b/tg-core/model/system_config.go deleted file mode 100644 index 93f4bd4..0000000 --- a/tg-core/model/system_config.go +++ /dev/null @@ -1,13 +0,0 @@ -package model - -import ( - "time" -) - -type SystemConfigInfo struct { - Id int64 `json:"id"` - Type int `json:"type"` - KeyName string `json:"keyname"` - Content string `json:"content"` - UpdateTime time.Time `json:"updateTime"` -} diff --git a/tg-core/model/trace_info.go b/tg-core/model/trace_info.go deleted file mode 100644 index 68581f3..0000000 --- a/tg-core/model/trace_info.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -import ( - -) - -type TraceInfo struct { - TraceId string `thrift:"traceId,1,required" json:"traceId"` - Caller string `thrift:"caller,2,required" json:"caller"` - SpanId string `thrift:"spanId,3" json:"spanId,omitempty"` - SrcMethod string `thrift:"srcMethod,4" json:"srcMethod,omitempty"` - HintCode int64 `thrift:"hintCode,5" json:"hintCode,omitempty"` - HintContent string `thrift:"hintContent,6" json:"hintContent,omitempty"` -} \ No newline at end of file diff --git a/tg-core/model/workflow.go b/tg-core/model/workflow.go deleted file mode 100644 index e930c15..0000000 --- a/tg-core/model/workflow.go +++ /dev/null @@ -1,137 +0,0 @@ -package model - -import ( - "time" - "sync" - "errors" - "encoding/json" - "fmt" -) - -type Workflow struct { - Id int64 `json:"id"` - DimensionId int64 `json:"dimensionId"` - ExperimentId int64 `json:"experimentId"` - ModulesArray [][]string `json:"modulesArray"` - FlowChart *WorkflowChart `json:"flow_chart"` - IsDefault int `json:"isDefault"` - Range1 string `json:"range1"` - Range2 string `json:"range2"` - Remark string `json:"remark"` - UpdateTime time.Time `json:"updateTime"` - ManualSlotIds []int64 `json:"manual_slot_ids"` - GroupName string `json:"group_name"` -} - -type WorkflowChart struct { - FirstActionId string `json:"first_action_id"` - ActionMap map[string]*Action `json:"actions"` - ParamMap map[string]string `json:"params"` -} - -type Action struct { - ActionId string `json:"action_id"` - ActionName string `json:"action_name"` - Params map[string]string `json:"params"` - NextActionIds []string `json:"next_action_ids"` - PrevActionIds []string `json:"prev_action_ids"` -} - -func NewWorkflowChart(flowChartStr string) (*WorkflowChart, error) { - if len(flowChartStr) ==0 { - return nil, errors.New("create WorkflowChart fail, flowChartStr is empty") - } - - flowChart := &WorkflowChart{} - //读取的数据为json格式,需要进行解码 - err := json.Unmarshal([]byte(flowChartStr), flowChart) - if err != nil { - return nil, fmt.Errorf("create WorkflowChart fail, invalid json:%v, err:%v", flowChartStr, err) - } - - err = flowChart.setFirstActionId() - if err != nil { - return nil, fmt.Errorf("create WorkflowChart fail, err:%v", err) - } - - //setFirstActionId first, then setPrevActionIds - flowChart.setPrevActionIds(flowChart.FirstActionId) - return flowChart, nil -} - -func (this *WorkflowChart) setPrevActionIds(actionId string){ - action, ok := this.ActionMap[actionId] - if !ok { - return - } - - //默认第一个action的id为1,没有前驱节点 - if actionId == this.FirstActionId { - action.PrevActionIds = []string{} - } - - nextActionIds := action.NextActionIds - for _, nextActionId := range nextActionIds { - nextAction, ok := this.ActionMap[nextActionId] - if !ok { - //报个error? - continue - } - - if nextAction.PrevActionIds == nil || len(nextAction.PrevActionIds) == 0 { - nextAction.PrevActionIds = []string{actionId} - }else{ - isAlreadyAdd := false - var prevActionId string - for _, prevActionId = range nextAction.PrevActionIds { - if prevActionId == actionId { - isAlreadyAdd = true - } - } - if !isAlreadyAdd { - nextAction.PrevActionIds = append(nextAction.PrevActionIds, actionId) - } - } - - this.setPrevActionIds(nextActionId) - } -} - -/* - 耗时搜索 -*/ -func (this *WorkflowChart) setFirstActionId() error { - nextActionIds := make(map[string]bool) - for _, action := range this.ActionMap { - if len(action.NextActionIds) <= 0 { - continue - } - - for _, nextId := range action.NextActionIds { - nextActionIds[nextId] = true - } - } - - for actionId,_ := range this.ActionMap { - if _, ok := nextActionIds[actionId]; !ok { - this.FirstActionId = actionId - return nil - } - } - - return errors.New("first action not found") -} - -func (this *WorkflowChart) CreateWaitMap() map[string]*sync.WaitGroup { - wgMap:= make(map[string]*sync.WaitGroup) - for actionId, action := range this.ActionMap { - prevCount := len(action.PrevActionIds) - if prevCount >1 { - wg := &sync.WaitGroup{} - wg.Add(prevCount) - wgMap[actionId]= wg - } - } - - return wgMap -} diff --git a/tg-core/reset_import.sh b/tg-core/reset_import.sh deleted file mode 100755 index 9db82f3..0000000 --- a/tg-core/reset_import.sh +++ /dev/null @@ -1,9 +0,0 @@ -rm -rf go.mod -rm -rf go.sum -rm -rf vendor -go mod init -go get -u -t ./... -sed -i "___bak" "s/v0.14.1/0.9.3/g" go.mod -rm -rf go.mod___bak -go mod download -go mod vendor diff --git a/tg-core/router/router.go b/tg-core/router/router.go deleted file mode 100644 index f17515c..0000000 --- a/tg-core/router/router.go +++ /dev/null @@ -1,48 +0,0 @@ -/** - * This file is auto-generated by dirpcgen don't modify manaully - * - * Copyright (c) 2018 didichuxing.com, Inc. All Rights Reserved - * - * Generated-date: 2018-07-13 - */ - -package router - -import ( - lego "git.xiaojukeji.com/lego/context-go" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/conf" - ngs "git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2" - "git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2/middleware" - "log" - "net/http" -) - -var Handler ngs.Route -var IdlHandler *ngs.IDLHandler - -func init() { - log.Println("tg-core router init start...") - Handler = ngs.NewRouter() - logLimit, err := conf.Handler.GetBoolSetting("log", "log_limit") - tlog.Handler.RegisterContextFormat(lego.FormatCtx) - if err == nil && logLimit { - Handler = Handler.Use( - middleware.RecoveryWithConfig(middleware.RecoveryConfig{tlog.Handler}), - middleware.TraceWithConfig(middleware.TraceConfig{Log: nil}), - middleware.RateLimitWithConfig(middleware.RateLimitConfig{Log: tlog.Handler, RateLimitKeyName: conf.RateLimitName})) - } else { - Handler = Handler.Use( - middleware.RecoveryWithConfig(middleware.RecoveryConfig{tlog.Handler}), - middleware.TraceWithConfig(middleware.TraceConfig{Log: tlog.Handler}), - middleware.RateLimitWithConfig(middleware.RateLimitConfig{Log: tlog.Handler, RateLimitKeyName: conf.RateLimitName})) - } - IdlHandler = ngs.NewDiRPC(tlog.Handler) - Handler.GET("/checkstatus", func(writer http.ResponseWriter, request *http.Request) { - writer.WriteHeader(200) - }) -} - -func AddRouter(requestType string, routerPath string, handlerMethod interface{}) { - Handler.Handler(requestType, routerPath, IdlHandler.Handle(handlerMethod)) -} diff --git a/tg-core/store/algomodel_index_map.go b/tg-core/store/algomodel_index_map.go deleted file mode 100644 index e1fea43..0000000 --- a/tg-core/store/algomodel_index_map.go +++ /dev/null @@ -1,28 +0,0 @@ -package store - -import ( - "context" - "encoding/json" - "github.com/didi/tg-flow/tg-core/common/redis" - "github.com/didi/tg-flow/tg-core/consts" - "github.com/didi/tg-flow/tg-core/model" -) - -var AlgoModelIndexObj *model.AlgoModelIndex = &model.AlgoModelIndex{} - -func LoadAlgoModelIndex() error { - algoModelIndexByte, err := redis.Handler.Get(context.TODO(), consts.RedisKeyAlgoModelConfig) - if err != nil && err.Error() != redis.ErrNil { - return err - } - - var algoModelIndexTemp *model.AlgoModelIndex - err = json.Unmarshal([]byte(algoModelIndexByte), &algoModelIndexTemp) - if err != nil { - return err - } - AlgoModelIndexObj.IndexVersion = algoModelIndexTemp.IndexVersion - AlgoModelIndexObj.IndexMap = algoModelIndexTemp.IndexMap - AlgoModelIndexObj.UpdateTime = algoModelIndexTemp.UpdateTime - return nil -} diff --git a/tg-core/store/debug_info_list.go b/tg-core/store/debug_info_list.go deleted file mode 100644 index f766a4f..0000000 --- a/tg-core/store/debug_info_list.go +++ /dev/null @@ -1,69 +0,0 @@ -/** - @Description: - @Author:zhouzichun - @Date:2022/4/25 -**/ - -package store - -import ( - "context" - "encoding/json" - "fmt" - "github.com/didi/tg-flow/tg-core/common/redis" - "github.com/didi/tg-flow/tg-core/model" - "go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2" - apolloModel "go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/model" - "strconv" - "time" -) - -type DebugResponse struct { - UserId string `json:"user_id"` - AppId int64 `json:"app_id"` - SceneId int64 `json:"scene_id"` - FlowId int64 `json:"flow_id"` - TimeStamp int64 `json:"time_stamp"` - ActionResultMap map[string]map[string]string `json:"action_result_map"` -} - -func Debug(sc *model.StrategyContext) error { - redisKey := fmt.Sprintf("debug_info_%v_%v", sc.AppName, sc.Phone) - debugResponse := &DebugResponse{ - AppId: sc.AppId, - SceneId: sc.SceneId, - FlowId: sc.FlowId, - TimeStamp: time.Now().Unix(), - ActionResultMap: sc.ActionResultMap, - } - debugResponseStr, err := json.Marshal(debugResponse) - if err != nil { - return err - } - var limitLength int64 - limitLength = 100 - user := apolloModel.NewUser(sc.Phone).With("phone", sc.Phone) - apolloToggle, apolloErr := apollo.FeatureToggle("online_debug_config", user) - if apolloErr != nil || !apolloToggle.IsAllow() { - return apolloErr - } else { - limitLengthStr := apolloToggle.GetAssignment().GetParameter("limit_num", "100") - limitLength, _ = strconv.ParseInt(limitLengthStr, 10, 64) - } - ctx := context.TODO() - length, err := redis.Handler.LPush(ctx, redisKey, string(debugResponseStr)) - if err != nil { - return err - } - _, err = redis.Handler.Expire(ctx, redisKey, 24*60*60) - if err != nil { - return err - } - if length > limitLength { - _, err = redis.Handler.LTrim(ctx, redisKey, int(length-limitLength), int(length-1)) - if err != nil { - return err - } - } - return nil -} diff --git a/tg-core/store/dimension_index_map.go b/tg-core/store/dimension_index_map.go deleted file mode 100644 index 4b3af8d..0000000 --- a/tg-core/store/dimension_index_map.go +++ /dev/null @@ -1,29 +0,0 @@ -package store - -import ( - "context" - "encoding/json" - "github.com/didi/tg-flow/tg-core/common/redis" - "github.com/didi/tg-flow/tg-core/consts" - "github.com/didi/tg-flow/tg-core/model" -) - -var DimensionIndexObj *model.DimensionIndex = &model.DimensionIndex{} - -func LoadDimensionIndex() error { - dimensionIndexString, err := redis.Handler.Get(context.TODO(), consts.StrategyDimensionMap) - if err != nil && err.Error() != redis.ErrNil { - return err - } - - var dimensionIndexTemp *model.DimensionIndex - err = json.Unmarshal([]byte(dimensionIndexString), &dimensionIndexTemp) - if err != nil { - return err - } - - DimensionIndexObj.DimensionMap = dimensionIndexTemp.DimensionMap - DimensionIndexObj.UpdateTime = dimensionIndexTemp.UpdateTime - - return nil -} diff --git a/tg-core/store/machine_map.go b/tg-core/store/machine_map.go deleted file mode 100644 index 41d9576..0000000 --- a/tg-core/store/machine_map.go +++ /dev/null @@ -1,43 +0,0 @@ -package store - -import ( - "context" - "encoding/json" - "github.com/didi/tg-flow/tg-core/common/redis" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/consts" -) - -var MachineIpMap map[string][]string = make(map[string][]string) - -func LoadMachineIp(appId int) error { - machineIpStr := redis.GetRedis(context.TODO(), nil, redis.Handler, consts.RedisKeyMachineIp, "task.LoadMachineIp") - - if machineIpStr == "" { - return nil - } - - var machineIpMap map[int]map[string][]string - err := json.Unmarshal([]byte(machineIpStr), &machineIpMap) - if err != nil { - return err - } - - //只获取本系统下的机器ip - tempMachineIpMap := make(map[string][]string) - //appId := *(*int)(unsafe.Pointer(&module.AppId)) - - if tempMap := machineIpMap[appId]; tempMap != nil { - for machineRoom, ipList := range tempMap { - tempMachineIpMap[machineRoom] = ipList - } - } - - if len(tempMachineIpMap) <= 0 { - return nil - } - - MachineIpMap = tempMachineIpMap - tlog.Handler.Infof(context.TODO(), consts.DLTagCronTask, "etype=task.LoadMachineIp||machineIpMap=%v", MachineIpMap) - return nil -} diff --git a/tg-core/store/workflow_map.go b/tg-core/store/workflow_map.go deleted file mode 100644 index 36669d5..0000000 --- a/tg-core/store/workflow_map.go +++ /dev/null @@ -1,28 +0,0 @@ -package store - -import ( - "context" - "encoding/json" - "github.com/didi/tg-flow/tg-core/common/redis" - "github.com/didi/tg-flow/tg-core/consts" - "github.com/didi/tg-flow/tg-core/model" -) - -var WorkflowMap map[int64]model.Workflow = make(map[int64]model.Workflow) - -func LoadWorkflow() error { - workflowMapByte, err := redis.Handler.Get(context.TODO(), consts.StrategyWorkflowMap) - if err != nil && err.Error() != redis.ErrNil { - return err - } - - var workflowMap map[int64]model.Workflow - err = json.Unmarshal([]byte(workflowMapByte), &workflowMap) - if err != nil { - return err - } - - WorkflowMap = workflowMap - - return nil -} diff --git a/tg-core/wfengine/action_counter.go b/tg-core/wfengine/action_counter.go deleted file mode 100644 index aa4cc1b..0000000 --- a/tg-core/wfengine/action_counter.go +++ /dev/null @@ -1,9 +0,0 @@ -package wfengine - -import "sync" - -type ActionCounter struct { - wgMap map[string]*sync.WaitGroup - skipedActionIdPairs *sync.Map - -} diff --git a/tg-core/wfengine/apollo/apollo_config.go b/tg-core/wfengine/apollo/apollo_config.go deleted file mode 100644 index ad97603..0000000 --- a/tg-core/wfengine/apollo/apollo_config.go +++ /dev/null @@ -1,31 +0,0 @@ -package apollo - -import ( - "go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2" - "go.intra.xiaojukeji.com/apollo/apollo-golang-sdk-v2/model" -) - -type AppConfig struct { - config *model.ConfResult -} - -func NewApolloConfig(namespace string, configName string) (*AppConfig, error){ - cfg, err := apollo.GetConfig(namespace, configName) - if err != nil { - return nil, err - } - - return &AppConfig{config: cfg}, nil -} - -func (a *AppConfig) IsVersion(version string) bool { - if version == a.config.GetVersion(){ - return true - } - - return false -} - -func (a *AppConfig) GetConfigs() map[string]string { - return a.config.GetConfigs() -} \ No newline at end of file diff --git a/tg-core/wfengine/logger_interface.go b/tg-core/wfengine/logger_interface.go deleted file mode 100644 index 090acdd..0000000 --- a/tg-core/wfengine/logger_interface.go +++ /dev/null @@ -1,37 +0,0 @@ -package wfengine -/* -import ( - "bytes" - "context" - "log" - "sync" -) - -type Logger interface { - Error(ctx context.Context, tag string, errMsg string) -} - -var Log Logger -var logOnce sync.Once -func SetLogger(logger Logger){ - Log = logger -} - -func ConfirmLoggerInit() { - logOnce.Do(func(){ - if Log == nil { - Log = &DefaultLogger{} - } - }) -} - -type DefaultLogger struct {} - -func (i *DefaultLogger) Error(ctx context.Context, tag string, errMsg string){ - buf := bytes.Buffer{} - buf.WriteString("[ERROR]||tag=") - buf.WriteString(tag) - buf.WriteString("||errMsg=") - buf.WriteString(errMsg) - log.Println(buf.String()) -}*/ \ No newline at end of file diff --git a/tg-service/README.md b/tg-service/README.md deleted file mode 100644 index c6e6792..0000000 --- a/tg-service/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# tg-service -算法平台管理系统 - -## 系统介绍 -对包含接入算法平台的在线策略服务和模型服务在内的所有系统进行分模块的统一管理,同时用于连接在线与离线,模型与特征,实现各个子系统之间数据的联通和流程的联动,以提升算法平台使用的便捷性。 - -## 部署步骤 -##### 1. 进入项目目录 -cd tg-service - -##### 2. 编译源代码 -make - -##### 3. 部署服务 -./tg-service(加载编译好的二进制文件) - -##### 4. 检查服务 -查看相应的端口号是否被占用 diff --git a/tg-service/common/logs/dltag.go b/tg-service/common/logs/dltag.go deleted file mode 100644 index 314f020..0000000 --- a/tg-service/common/logs/dltag.go +++ /dev/null @@ -1,22 +0,0 @@ -package logs - -const ( - DLTagLoadExperimentWorkflowFail = " _com_strategyadmin_load_experiment_workflow_fail" - DLTagLoadOldWorkflowFail = " _com_strategyadmin_load_old_workflow_fail" - DLTagLoadDBFail = " _com_strategyadmin_load_db_fail" - - DLTagSystemPanic = " _com_strategyadmin_system_panic" - - DLTagLoginSetRedisKey = " _com_strategyadmin_login_set_redis_key" - DLTagProcessLog = " _com_strategyadmin_process_log" - - DLTagLoadRankerHost = " _com_strategyadmin_load_ranker_host" - DLTagLoadRankerHostFail = " _com_strategyadmin_load_ranker_host_fail" - DLTagDelRankerHostFail = " _com_strategyadmin_del_ranker_host_fail" - - DLTagSearchDebugInfoFail = " _com_strategyadmin_search_debug_info_fail" - - DLTagEventPost = "_com_strategyadmin_event_post_failed" - - DLTagGitApiFail = " com_strategyadmin_git_api_failed" -) diff --git a/tg-service/common/template/init.go b/tg-service/common/template/init.go deleted file mode 100644 index 8fcd5cb..0000000 --- a/tg-service/common/template/init.go +++ /dev/null @@ -1,63 +0,0 @@ -package template - -import ( - "github.com/didi/tg-flow/tg-core/common/path" - "html/template" - "net/http" -) - -var Handler *template.Template - -//模型状态编号和中文映射关系 -var ModelNameMap map[string]int = make(map[string]int) -var ModelStatusMap map[int]string = make(map[int]string) - -//场景编号和中文映射关系 -var AppNameMap map[string]int = make(map[string]int) -var AppIdMap map[int]string = make(map[int]string) - -//索引状态编号和中文映射关系 -var IndexNameMap map[string]int = make(map[string]int) -var IndexIdMap map[int]string = make(map[int]string) - -//场景编号和场景名称映射关系 -var SceneNameAndIdMap map[string]int = make(map[string]int) -var SceneIdAndNameMap map[int]string = make(map[int]string) - -//场景组编号和场景组名称映射关系 -var SceneNameAndIdsMap map[string]int = make(map[string]int) -var SceneIdsAndNameMap map[int]string = make(map[int]string) - -//流量主从和主从编号映射关系 -var DefultNameAndIdMap map[string]int = make(map[string]int) -var DefultIdAndNameMap map[int]string = make(map[int]string) - -func init() { - Handler = template.Must(template.ParseGlob(path.Root + "/template/*.html")) - ModelNameMap["未知"] = -1 - ModelNameMap["待上线"] = 1 - ModelNameMap["上线失败"] = 2 - ModelNameMap["已上线"] = 3 - - ModelStatusMap[-1] = "未知" - ModelStatusMap[1] = "待上线" - ModelStatusMap[2] = "上线失败" - ModelStatusMap[3] = "已上线" - - IndexNameMap["离线"] = 0 - IndexNameMap["在线"] = 1 - - IndexIdMap[0] = "离线" - IndexIdMap[1] = "在线" - - DefultNameAndIdMap["从"] = 0 - DefultNameAndIdMap["主"] = 1 - - DefultIdAndNameMap[0] = "从" - DefultIdAndNameMap[1] = "主" -} - -func Render(w http.ResponseWriter, name string, data interface{}) error { - w.Header().Set("Content-Type", "text/html; charset=utf-8") - return Handler.ExecuteTemplate(w, name, data) -} diff --git a/tg-service/conf/init.go b/tg-service/conf/init.go deleted file mode 100644 index a657df8..0000000 --- a/tg-service/conf/init.go +++ /dev/null @@ -1,42 +0,0 @@ -/** - @Description: - @Author:zhouzichun - @Date:2023/5/7 -**/ - -package conf - -import ( - "github.com/didi/tg-flow/tg-core/conf" - "github.com/didi/tg-flow/tg-core/router" - "log" - "tg-service/constant" - "tg-service/controller" -) - -func init() { - log.Println("point-admin router init...") - svc := new(controller.RPCService) - router.Handler.POST("/checklogin", svc.CheckLogin) - router.Handler.POST("/login", svc.Login) - router.Handler.POST("/logout", svc.Logout) - - //workflow相关操作(新) - router.Handler.POST("/searchWorkFlow", svc.SearchWorkFlow) - router.Handler.POST("/addOrUpdateWorkFlow", svc.AddOrUpdateWorkFlow) - router.Handler.POST("/deleteWorkFlow", svc.DeleteWorkFlow) - router.Handler.POST("/exportWorkFlow", svc.ExportWorkFlow) - router.Handler.POST("/getWorkFlowChart", svc.GetWorkFlowChart) - router.Handler.POST("/saveWorkFlowChart", svc.SaveWorkFlowChart) - router.Handler.POST("/importWorkFlow", svc.ImportWorkFlow) -} - -func InitConfig() { - //初始化:获取配置文件中sso服务接口地址 - var err error - if constant.Env, err = conf.Handler.GetSetting("env", "type"); err != nil { - log.Println("Fail to init env!", err) - return - } - log.Println("point-admin config init finished") -} diff --git a/tg-service/conf/mysql_compile/app_config b/tg-service/conf/mysql_compile/app_config deleted file mode 100644 index c8fe50b..0000000 --- a/tg-service/conf/mysql_compile/app_config +++ /dev/null @@ -1,11 +0,0 @@ -CREATE TABLE `your_table_name` ( - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `app_name` varchar(50) NOT NULL, - `machine_room` varchar(50) NOT NULL, - `node_name` varchar(50) NOT NULL, - `operator` varchar(20) NOT NULL, - `create_time` datetime NOT NULL DEFAULT '1970-01-01 08:00:00', - `update_time` datetime NOT NULL DEFAULT '1970-01-01 08:00:00', - `git_url` varchar(100) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/tg-service/conf/mysql_compile/scene_config b/tg-service/conf/mysql_compile/scene_config deleted file mode 100644 index cda3a1b..0000000 --- a/tg-service/conf/mysql_compile/scene_config +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE `scene_config` ( - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL, - `app_id` int(11) NOT NULL DEFAULT 0, - `bucket_type` int(11) NOT NULL DEFAULT 0, - `create_time` datetime NOT NULL DEFAULT '1971-01-01 08:00:00', - `update_time` datetime NOT NULL DEFAULT '1971-01-01 08:00:00', - `operator` varchar(20) NOT NULL, - `flow_type` int(4) NOT NULL DEFAULT 0, - `name_zh` varchar(8192) NOT NULL, - `exp_name` varchar(128) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/tg-service/conf/mysql_compile/workflow b/tg-service/conf/mysql_compile/workflow deleted file mode 100644 index 11d7c9e..0000000 --- a/tg-service/conf/mysql_compile/workflow +++ /dev/null @@ -1,19 +0,0 @@ -CREATE TABLE `workflow` ( - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `dimension_id` int(11) NOT NULL DEFAULT -1, - `experiment_id` bigint(20) NOT NULL DEFAULT 0, - `modules` varchar(8192) NOT NULL, - `flow_chart` varchar(10240) NOT NULL, - `is_default` int(11) NOT NULL DEFAULT 0, - `status` int(11) NOT NULL DEFAULT 1, - `range1` varchar(300) NOT NULL DEFAULT '0,1', - `range2` varchar(300) NOT NULL DEFAULT '[0-1]', - `remark` text NOT NULL, - `create_time` datetime NOT NULL DEFAULT '2018-09-20 10:00:00', - `update_time` datetime NOT NULL DEFAULT '2018-09-20 10:00:00', - `operator` varchar(50) NOT NULL DEFAULT 'admin', - `manual_slot_ids` varchar(200) NOT NULL, - `group_name` varchar(100) NOT NULL, - PRIMARY KEY (`id`), - KEY `experiment_id` (`experiment_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/tg-service/constant/const.go b/tg-service/constant/const.go deleted file mode 100644 index fcdbc12..0000000 --- a/tg-service/constant/const.go +++ /dev/null @@ -1,46 +0,0 @@ -package constant - -var Env string - -var SsoAppId string -var SsoAppKey string -var LoginUrl string -var LogoutUrl string -var CheckCodeUrl string -var CheckTicketUrl string -var GetUserNameUrl string - -var OldSsoAppId string -var OldSsoAppKey string -var OldLoginUrl string -var OldLogoutUrl string - -var RdId string -var AdminId string -var UserInfoPermissUrl string -var UpmPermissUrl string - -var HttpTimeOut int64 = 500 - -var Msg string = "请申请相应角色,获取权限:" - -const NameSpaceOnlineDebug = "140" -const NameSpacePointSys = "point_sys" -const ToggleIdOnlineDebug = "155765" -const WhiteKeyOnlineDebug = "phone" - -const ToggleNotFound = "toggle not found" - -const ApolloSettingNameSpace = "point_arch" -const ApolloTypeAB = "ab" -const ApolloTypeGray = "gray" - -// WorkflowConfigName Apollo workflow 配置名称 -const WorkflowConfigName = "workflow" - -// ConfigBlacklist 不允许在自定义配置中使用的 Key -var ConfigBlacklist = map[string]struct{}{ - WorkflowConfigName: {}, -} - -const ApolloServicePrefix = "map_base_" diff --git a/tg-service/controller/controller.go b/tg-service/controller/controller.go deleted file mode 100644 index 7d9203d..0000000 --- a/tg-service/controller/controller.go +++ /dev/null @@ -1,147 +0,0 @@ -/** - * This file is auto-generated by dirpcgen don't modify manaully - * - * Copyright (c) 2018 didichuxing.com, Inc. All Rights Reserved - * - * Generated-date: 2018-07-13 - */ - -package controller - -import ( - "context" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/common/utils" - ngs "git.xiaojukeji.com/nuwa/nuwa-go-httpserver/v2" - "net/http" - "tg-service/common/logs" - "tg-service/constant" - "tg-service/idl" - "tg-service/logic" -) - -type RPCService struct { - ngs.BaseController -} - -var isNewAdmin bool - -/** - * 登录校验1 - */ -func (h RPCService) CheckLogin(w http.ResponseWriter, r *http.Request) { - defer utils.Recover(context.TODO(), nil, logs.DLTagSystemPanic, "logic_CheckLogin") - responseInfo := &idl.ResponseInfo{} - data := logic.ParsRequestParam(r) - tlog.Handler.Infof(context.TODO(), logs.DLTagProcessLog, "etype=controller_CheckLogin||econtent=%v||err=", data) - if data.UserCookie == "" { - responseInfo.Tag = false - responseInfo.ErrMsg = "用户cookie为空,需要登录" - } else { - tag, err := logic.Login(r.FormValue("usercookie"), r.FormValue("passcookie")) - if tag { - responseInfo.Tag = true - responseInfo.ErrMsg = "success" - } else { - responseInfo.Tag = false - responseInfo.TypeNum = 1 //登录校验不通过 - responseInfo.ErrMsg = err - } - } - logic.EchoJSON(w, r, responseInfo) -} - -/** - * 登录校验2 - */ -func (h RPCService) Login(w http.ResponseWriter, r *http.Request) { - defer utils.Recover(context.TODO(), nil, logs.DLTagSystemPanic, "logic_Login") - responseInfo := &idl.ResponseInfo{} - data := logic.ParsRequestParam(r) - data.UserCookie = r.FormValue("username") - tlog.Handler.Infof(context.TODO(), logs.DLTagProcessLog, "etype=controller_Login||econtent=%v||err=", data) - tag, err := logic.Login(r.FormValue("username"), r.FormValue("password")) - if tag { - responseInfo.Tag = true - responseInfo.ErrMsg = "success" - } else { - responseInfo.Tag = false - responseInfo.ErrMsg = err - } - logic.EchoJSON(w, r, responseInfo) -} - -//退出系统 -func (h RPCService) Logout(w http.ResponseWriter, r *http.Request) { - logOutUrl := constant.LogoutUrl - hashCache, _ := r.Cookie("__hash__cache") - referer := r.Referer() - if hashCache == nil || referer != "http://strategy-arch-platform.intra.xiaojukeji.com/" { - logOutUrl = constant.OldLogoutUrl - } - responseInfo := &idl.ResponseInfo{ - Tag: false, - ErrMsg: logOutUrl, - } - logic.EchoJSON(w, r, responseInfo) -} - -func ReturnOpFailMsg(w http.ResponseWriter, r *http.Request, errMsg string) { - responseInfo := &idl.ResponseInfo{ - Tag: false, - TypeNum: 2, //权限校验不通过 - ErrMsg: errMsg, - } - w.WriteHeader(http.StatusFound) - logic.EchoJSON(w, r, responseInfo) -} - -//如果登录校验通过,则统一返回信息 -func ReturnLoginSuccessMsg(w http.ResponseWriter, r *http.Request, content interface{}) { - responseInfo := &idl.ResponseInfo{ - Tag: true, - Content: content, - } - logic.EchoJSON(w, r, responseInfo) -} - -// -//如果登录校验通过,则统一返回信息 -func ReturnLoginSuccessMessage(w http.ResponseWriter, r *http.Request, content interface{}) { - - responseMsg := &idl.ResponseMsg{ - Code: 0, - Message: "Sucess!", - Data: content, - } - - logic.EchoToJSON(w, r, responseMsg) -} - -func ReturnOpFailMessage(w http.ResponseWriter, r *http.Request, errMsg string) { - responseMsg := &idl.ResponseMsg{ - Code: 1, - Message: "权限校验不通过", //权限校验不通过 - } - w.WriteHeader(http.StatusFound) - logic.EchoToJSON(w, r, responseMsg) -} - -//如果登录校验不通过,则统一返回提示信息 -func ReturnLoginFailMessage(w http.ResponseWriter, r *http.Request) { - responseMsg := &idl.ResponseMsg{ - Code: 2, - Message: "登录校验不通过", //登录校验不通过 - } - w.WriteHeader(http.StatusFound) - logic.EchoToJSON(w, r, responseMsg) -} - -func ReturnFailMessage(w http.ResponseWriter, r *http.Request, errMsg string) { - responseMsg := &idl.ResponseInfo{ - Tag: false, - ErrMsg: errMsg, - } - w.WriteHeader(http.StatusFound) - logic.EchoToJSON(w, r, responseMsg) -} diff --git a/tg-service/controller/workflow_controller.go b/tg-service/controller/workflow_controller.go deleted file mode 100644 index 042db23..0000000 --- a/tg-service/controller/workflow_controller.go +++ /dev/null @@ -1,167 +0,0 @@ -/** - * This file is auto-generated by dirpcgen don't modify manaully - * - * Copyright (c) 2018 didichuxing.com, Inc. All Rights Reserved - * - * Generated-date: 2018-07-13 - */ - -package controller - -import ( - "context" - "encoding/json" - "fmt" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/common/utils" - "net/http" - "strconv" - "tg-service/common/logs" - "tg-service/idl" - "tg-service/logic" - "tg-service/logic/workflowadmin" -) - -//查询workflow信息 -func (h RPCService) SearchWorkFlow(w http.ResponseWriter, r *http.Request) { - defer utils.Recover(context.TODO(), nil, logs.DLTagSystemPanic, "controller.SearchWorkFlow") - data, err := logic.ParsRequsetWorkFlowParam(r) - tlog.Handler.Infof(context.TODO(), logs.DLTagProcessLog, "etype=controller.SearchWorkFlow||data=%v||err=%v", data, err) - if err != nil { - workFlowList := make([]*idl.WorkFlowConfig, 0) - logic.EchoJSON(w, r, workFlowList) - } else { - if data.Operator == "" { - ReturnLoginFailMessage(w, r) - } else { - pageLimit, pageNum := logic.ParsRequestPageParam(r) - useLimit := false - if pageLimit != 0 && pageNum != 0 { - useLimit = true - } - workFlowList, _ := workflowadmin.SelectWorkFlowConfig(data, pageLimit, pageNum, useLimit) - ReturnLoginSuccessMsg(w, r, workFlowList) - } - } -} - -//新增和修改workflow信息 -func (h RPCService) AddOrUpdateWorkFlow(w http.ResponseWriter, r *http.Request) { - defer utils.Recover(context.TODO(), nil, logs.DLTagSystemPanic, "controller.AddOrUpdateWorkFlow") - - data, err := logic.ParsRequsetWorkFlowParam(r) - - tlog.Handler.Infof(context.TODO(), logs.DLTagProcessLog, "etype=controller.AddOrUpdateWorkFlow||data=%v||err=%v", data, err) - - if err != nil { - logic.EchoJSON(w, r, &idl.ResponseInfo{Tag: false, ErrMsg: err.Error()}) - } else { - if data.Operator == "" { - ReturnLoginFailMessage(w, r) - } else { - logic.EchoJSON(w, r, workflowadmin.AddOrUpdateWorkFlowConfig(data)) - } - } -} - -//删除workflow信息 -func (h RPCService) DeleteWorkFlow(w http.ResponseWriter, r *http.Request) { - defer utils.Recover(context.TODO(), nil, logs.DLTagSystemPanic, "controller.DeleteWorkFlow") - data, err := logic.ParsRequsetWorkFlowParam(r) - tlog.Handler.Infof(context.TODO(), logs.DLTagProcessLog, "etype=controller.DeleteWorkFlow||data=%v||err=%v", data, err) - if err != nil { - logic.EchoJSON(w, r, &idl.ResponseInfo{Tag: false, ErrMsg: err.Error()}) - } else { - if data.Operator == "" { - ReturnLoginFailMessage(w, r) - } else { - logic.EchoJSON(w, r, workflowadmin.DeleteWorkflowConfig(data)) - } - } -} - -//导出workflow信息 -func (h RPCService) ExportWorkFlow(w http.ResponseWriter, r *http.Request) { - defer utils.Recover(context.TODO(), nil, logs.DLTagSystemPanic, "controller.ExportWorkFlow") - data, err := logic.ParsRequsetWorkFlowParam(r) - tlog.Handler.Infof(context.TODO(), logs.DLTagProcessLog, "etype=controller.ExportWorkFlow||data=%v||err=%v", data, err) - if err != nil { - logic.EchoJSON(w, r, &idl.ResponseInfo{Tag: false, ErrMsg: err.Error()}) - } else { - if data.Operator == "" { - ReturnLoginFailMessage(w, r) - } else { - logic.EchoJSON(w, r, workflowadmin.ExportWorkFlow(data)) - } - } -} - -// ImportWorkFlow 导入workflow信息 -func (h RPCService) ImportWorkFlow(w http.ResponseWriter, r *http.Request) { - // - defer utils.Recover(context.TODO(), nil, logs.DLTagSystemPanic, "controller.ImportWorkFlow") - // 获取流程图信息和操作者信息,以及workflowId - dataJson := logic.CancelEmptyStr(r.FormValue("dataJson")) - username, _ := r.Cookie("username") - var operator string - if username != nil { - operator = logic.CancelEmptyStr(username.Value) - } - workflowId, _ := strconv.ParseInt(r.FormValue("workflowid"), 10, 64) - // - tlog.Handler.Infof(context.TODO(), logs.DLTagProcessLog, "etype=controller.importWorkFlow||data=%v", dataJson) - - if operator == "" { - ReturnLoginFailMessage(w, r) - } else { - workflowChart := new(idl.WorkflowChart) - err := json.Unmarshal([]byte(dataJson), &workflowChart) - if err != nil { - logic.EchoJSON(w, r, &idl.ResponseInfo{Tag: false, ErrMsg: err.Error()}) - } else { - logic.EchoJSON(w, r, workflowadmin.SaveImportWorkFlowChart(workflowChart, workflowId, operator)) - } - } - -} - -//获取流程图信息 -func (h RPCService) GetWorkFlowChart(w http.ResponseWriter, r *http.Request) { - defer utils.Recover(context.TODO(), nil, logs.DLTagSystemPanic, "controller.GetWorkFlowChart") - data, err := logic.ParsRequsetWorkFlowParam(r) - tlog.Handler.Infof(context.TODO(), logs.DLTagProcessLog, "etype=controller.getWorkFlowChart||data=%v||err=%v", data, err) - if err != nil { - logic.EchoJSON(w, r, &idl.ResponseInfo{Tag: false, ErrMsg: err.Error()}) - } else { - if data.Operator == "" { - ReturnLoginFailMessage(w, r) - } else { - logic.EchoJSON(w, r, workflowadmin.GetWorkFlowChart(data)) - } - } -} - -//保存流程图修改 -func (h RPCService) SaveWorkFlowChart(w http.ResponseWriter, r *http.Request) { - defer utils.Recover(context.TODO(), nil, logs.DLTagSystemPanic, "controller.SaveWorkFlowChart") - dataJson := logic.CancelEmptyStr(r.FormValue("dataJson")) - operator := logic.CancelEmptyStr(r.FormValue("operator")) - err := logic.CheckParam(r.FormValue("workflowid")) - if err != nil { - logic.EchoJSON(w, r, &idl.ResponseInfo{Tag: false, ErrMsg: fmt.Errorf("实验编号需要是数字").Error()}) - } - workflowId, _ := strconv.ParseInt(r.FormValue("workflowid"), 10, 64) - tlog.Handler.Infof(context.TODO(), logs.DLTagProcessLog, "etype=controller.saveWorkFlowChart||data=%v", dataJson) - - flowChart := new(idl.ChartG6) - err = json.Unmarshal([]byte(dataJson), &flowChart) - if err != nil { - logic.EchoJSON(w, r, &idl.ResponseInfo{Tag: false, ErrMsg: err.Error()}) - } else { - if operator == "" { - ReturnLoginFailMessage(w, r) - } else { - logic.EchoJSON(w, r, workflowadmin.SaveWorkFlowChart(flowChart, workflowId, operator)) - } - } -} diff --git a/tg-service/cron/cron_task.go b/tg-service/cron/cron_task.go deleted file mode 100644 index d272f5d..0000000 --- a/tg-service/cron/cron_task.go +++ /dev/null @@ -1,22 +0,0 @@ -/** - author:dingyi - time:2019-08-28 -**/ - -package cron - -import ( - "github.com/didi/tg-flow/tg-core/common/crontask" -) - -const ( - //定时任务周期 - BASE_TASK_TIME = "0 0/2 * * * ?" - HOUR_TASK_TIME = "0 0 0/4 * * ?" -) - -func StartCronTask() { - - //启动所有定时任务 - crontask.TaskStart() -} diff --git a/tg-service/go.mod b/tg-service/go.mod deleted file mode 100644 index 58c1a8f..0000000 --- a/tg-service/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module tg-service - -go 1.17 - -require ( - github.com/didi/tg-flow/tg-core v0.0.0-20230518032332-3b95688f6d4f - github.com/gin-gonic/gin v1.9.0 -) \ No newline at end of file diff --git a/tg-service/idl/types.go b/tg-service/idl/types.go deleted file mode 100644 index 7893794..0000000 --- a/tg-service/idl/types.go +++ /dev/null @@ -1,718 +0,0 @@ -/** - * This file is auto-generated by dirpcgen - * - * Copyright (c) 2018 didichuxing.com, Inc. All Rights Reserved - * - * Generated-date: 2018-07-13 - */ - -package idl - -import ( - "github.com/didi/tg-flow/tg-core/wfengine" -) - -type NoticeRequestInfo struct { - RequestId string `thrift:"request_id,1,required" json:"request_id"` - NoticeType string `thrift:"notice_type,2,required" json:"notice_type"` - NoticeInfo string `thrift:"notice_info,3,required" json:"notice_info"` -} - -type LoginInfo struct { - UserName string `thrift:"user_name,1,required" json:"user_name"` - PassWord string `thrift:"pass_word,2,required" json:"pass_word"` -} - -type ResponseInfo struct { - Tag bool `thrift:"request_id,1,required" json:"tag"` - TypeNum int `thrift:"err_msg,2,required" json:"typenum"` - ErrMsg string `thrift:"err_msg,3,required" json:"err_msg"` - Content interface{} `thrift:"err_msg,4,required" json:"content"` -} - -type ResponseMsg struct { - Code int `thrift:"request_id,1,required" json:"code"` - Message string `thrift:"err_msg,2,required" json:"message"` - Data interface{} `thrift:"err_msg,3,required" json:"data"` -} - -type WorkFlowData struct { - ExperimentId string `json:"experimentid"` - OldWorkFlowId string `json:"oldworkflowid"` - WorkFlowId string `json:"workflowid"` - Modules string `json:"modules"` - Proportion string `json:"proportion"` - Defult string `json:"defult"` - Remark string `json:"remark"` - UserCookie string `json:"usercookie"` - CreateTime string `json:"createtime"` - UpdateTime string `json:"updatetime"` - Operator string `json:"operator"` - ManualSlotIds string `json:"manual_slot_ids"` -} - -type SenceData struct { - ExperimentId string `json:"experimentid"` - Name string `json:"name"` - AppName string `json:"appname"` - BucketType string `json:"buckettype"` - UserCookie string `json:"usercookie"` -} - -type WorkFlow struct { - Id int64 `json:"id"` - DimensionId int64 `json:"dimension_id"` - ExperimentId int64 `json:"experimentId"` - Modules string `json:"modules"` - IsDefault int `json:"isDefault"` - Range1 string `json:"range1"` - Range2 string `json:"range2"` - Remark string `json:"remark"` - CreateTime string `json:"createtime"` - UpdateTime string `json:"updatetime"` - Operator string `json:"operator"` - ManualSlotIds string `json:"manual_slot_ids"` - GroupName string `json:"group_name"` - FlowCharts string `json:"flow_charts"` -} - -type Asd struct { - Experimentid string - Usercookie string - Passcookie string -} - -type UserInfo struct { - UserName string - PassWord string - RoleId int -} - -type RankConfig struct { - Id int64 `json:"id"` - Ip string `json:"ip"` - SceneId string `json:"sceneId"` - AlgoName string `json:"algoname"` - ModelName string `json:"modelname"` - ModelVersion string `json:"modelversion"` - Status string `json:"status"` - HistoryConfig string `json:"historyconfig"` - Operator string `json:"operator"` - CreateTime string `json:"createtime"` - UpdateTime string `json:"updatetime"` - AutoUpdate string `json:"autoupdate"` - MachineRoom string `json:"machineRoom"` -} - -type RankerService struct { - Id int64 `json:"id"` - AppId string `json:"app_id"` - SceneId string `json:"scene_id"` - ServiceUsn string `json:"service_usn"` - AlgoName string `json:"algo_name"` - ModelName string `json:"model_name"` - ModelVersion string `json:"model_version"` - HistoryConfig string `json:"history_config"` - Operator string `json:"operator"` - CreateTime string `json:"create_time"` - UpdateTime string `json:"update_time"` - AutoUpdateVersion string `json:"auto_update_version"` - UsnType string `json:"usn_type"` - HdfsPath string `json:"hdfs_path"` -} - -type ServerConfig struct { - Id int64 `json:"id"` - MachineRoom string `json:"machine_room"` - Ip string `json:"ip"` - Role string `json:"role"` - IndexVersion string `json:"indexversion"` - UpdateTime string `json:"updatetime"` - Status string `json:"status"` -} - -type SceneConfig struct { - OldId int64 `json:"oldid"` - Id int64 `json:"id"` - Name string `json:"name"` - NameZh string `json:"namezh"` - AppId int `json:"appid"` - AppName string `json:"appname"` - BucketType int64 `json:"buckettype"` - Operator string `json:"operator"` - CreateTime string `json:"createtime"` - UpdateTime string `json:"updatetime"` - BifrostConfig string `json:"bifrost_config"` - FlowType int64 `json:"flow_type"` - ExpName string `json:"expname"` -} - -type SceneFeature struct { - Id int64 `json:"id"` - SceneName string `json:"scene_name"` - FeatureList string `json:"feature_list"` - Operator string `json:"operator"` - CreateTime string `json:"createtime"` - UpdateTime string `json:"updatetime"` -} - -type WorkFlowConfig struct { - SceneName string `json:"scenename"` - OldWorkFlowId string `json:"oldworkflowid"` - WorkFlowId string `json:"workflowid"` - DimensionId int64 `json:"dimension_id"` - Modules string `json:"modules"` - ShowModules string `json:"showmodules"` - Proportion string `json:"proportion"` - Defult string `json:"defult"` - Range1 string `json:"range1"` - Range2 string `json:"range2"` - Remark string `json:"remark"` - CreateTime string `json:"createtime"` - UpdateTime string `json:"updatetime"` - Operator string `json:"operator"` - ManualSlotIds string `json:"manual_slot_ids"` - GroupName string `json:"groupname"` - SceneId int64 `json:"scene_id"` - Configured bool `json:"configured"` - FlowChartJson string `json:"flow_charts_json"` -} - -type RecSceneConfig struct { - OldId int64 `json:"oldid"` - Id int64 `json:"id"` - SceneId int64 `json:"scene_id"` - SceneName string `json:"scene_name"` - RecallRule string `json:"recall_rule"` - Operator string `json:"operator"` - CreateTime string `json:"createtime"` - UpdateTime string `json:"updatetime"` -} - -type MachineConfigInfo struct { - Id int `json:"id"` - AppId int `json:"app_id"` - AppName string `json:"app_name"` - MachineRoom string `json:"machine_room"` - DockerIp string `json:"docker_ip"` - DockerName string `json:"docker_name"` - Operator string `json:"operator"` - UpdateTime string `json:"update_time"` -} - -type SystemConfigInfo struct { - Id int `json:"id"` - AppId int `json:"app_id"` - KeyName string `json:"key_name"` - Content string `json:"content"` - Operator string `json:"operator"` - CreateTime string `json:"create_time"` - UpdateTime string `json:"update_time"` - Remark string `json:"remark"` -} - -type BifrostConfigInfo struct { - Id int `json:"id"` - SceneId int `json:"scene_id"` - BifrostValue string `json:"bifrost_value"` - Operator string `json:"operator"` - CreateTime string `json:"create_time"` - UpdateTime string `json:"update_time"` -} - -type AppConfigInfo struct { - OldId int `json:"old_id"` - Id int `json:"id"` - AppName string `json:"app_name"` - MachineRoom string `json:"machine_room"` - NodeName string `json:"node_name"` - Operator string `json:"operator"` - CreateTime string `json:"create_time"` - UpdateTime string `json:"update_time"` - Description string `json:"description"` - GitUrl string `json:"git_url"` -} - -type ResourceToSceneInfo struct { - ResourceId string `json:"resource_id"` - ResourceName string `json:"resource_name"` - SceneId string `json:"scene_id"` - SceneName string `json:"scene_name"` - ResourceInfo string `json:"resource_info"` - Operator string `json:"operator"` -} - -type DimensionConfig struct { - OldId int64 `json:"old_id"` - Id int64 `json:"id"` - Name string `json:"name"` - SceneName string `json:"scene_name"` - Content string `json:"content"` - Operator string `json:"operator"` - CreateTime string `json:"createtime"` - UpdateTime string `json:"updatetime"` -} - -type OldWorkFlowVersionConfig struct { - SceneName string `json:"scenename"` - DimensionId int64 `json:"dimension_id"` - VersionId int64 `json:"version_id"` - VersionCreateTime string `json:"version_create_time"` - Operator string `json:"operator"` -} - -type ModelConfig struct { - UserName string `json:"username"` - Id int64 `json:"id"` - SceneId int64 `json:"scene_id"` - AlgoName string `json:"algo_name"` - ModelName string `json:"model_name"` - BifrostKeyName string `json:"bifrost_key_name"` - BifrostJobId int64 `json:"bifrost_job_id"` - BifrostJobName string `json:"bifrost_job_name"` - Path string `json:"path"` - Period int64 `json:"period"` - LastTransferTime string `json:"last_transfer_time"` - LastTransferVersion int64 `json:"last_transfer_version"` - LastTransferResult string `json:"last_transfer_result"` - Operator string `json:"operator"` - CreateTime string `json:"create_time"` - UpdateTime string `json:"update_time"` - Status int64 `json:"status"` - Limit int64 `json:"limit"` -} -type DdlConfig struct { - Id string `json:"id"` - RegionName string `json:"region_name"` - ProjectUUID string `json:"projectUUID"` - Token string `json:"token"` - UserUUID string `json:"userUUID"` - Operator string `json:"operator"` - CreateTime string `json:"create_time"` - UpdateTime string `json:"update_time"` -} -type CityOpenConfig struct { - Id int64 `json:"id"` - CityId int64 `json:"city_id"` - SceneId int64 `json:"scene_id"` - IsOpen int64 `json:"is_open"` - BegDate string `json:"beg_date"` - EndDate string `json:"end_date"` - RotationPattern string `json:"rotation_pattern"` - Budget int64 `json:"budget"` - Operator string `json:"operator"` - CreateTime string `json:"create_time"` - UpdateTime string `json:"update_time"` -} -type GitLabFileConfig struct { - FileName string `json:"file_name"` - FilePath string `json:"file_path"` - Size string `json:"size"` - Encoding string `json:"encoding"` - ContentSha256 string `json:"beg_date"` - Ref string `json:"ref"` - BlobId string `json:"blob_id"` - CommitId string `json:"commit_id"` - LastCommitId string `json:"last_commit_id"` - Content string `json:"content"` -} - -type SceneName struct { - SceneName string -} -type NodeTypeMenu struct { - NodeType string -} -type NodeType struct { - NodeType string `json:"node_type"` - NodeName []string `json:"node_name"` -} - -//前端传入时 NodeName是 NodeType.NodeName 返回结果时是NodeName -type NodeNameMenu struct { - NodeName string -} - -type NodeParamMenu struct { - ParamName string - ParamType string -} - -type ChartG6 struct { - Nodes []*Node `json:"nodes"` - Edges []*Edge `json:"edges"` -} - -type Node struct { - Id string `json:"id"` - Label string `json:"label"` - NodeType string `json:"type"` - Params []*wfengine.Param `json:"params"` - Timeout int `json:"timeout"` - RefWorkflowId int `json:"ref_workflow_id"` - TimeoutAsync bool `json:"timeout_async"` - TimeoutDynamic bool `json:"timeout_dynamic"` - Location string `json:"location"` -} - -type Edge struct { - Id string `json:"id"` - EdgeType string `json:"type"` - Source string `json:"source"` - Target string `json:"target"` - Label string `json:"label,omitempty"` -} - -type RankerHost struct { - MachineRoom string `json:"machine_room"` - ServiceUsn string `json:"service_usn"` - Ip string `json:"ip"` - AppId int64 `json:"app_id"` - SceneId int64 `json:"scene_id"` - AlgoName string `json:"algo_name"` - ModelName string `json:"model_name"` - ModelVersion string `json:"model_version"` - ExpectVersion string `json:"expect_version"` - Status int64 `json:"status"` - UpdateTime string `json:"update_time"` - Operator string `json:"operator"` - ShowStatus string `json:"show_status"` -} - -type DebugInfo struct { - UserId string `json:"user_id"` - AppName string `json:"app_name"` - AppId int64 `json:"app_id"` - SceneId int64 `json:"scene_id"` - FlowId int64 `json:"flow_id"` - TimeStamp int64 `json:"time_stamp"` - ActionResultMap map[string]map[string]string `json:"action_result_map"` - Operator string `json:"operator"` -} - -type DebugUserInfo struct { - AppName string `json:"app_name"` - UserIdList string `json:"user_id"` - LimitNum int64 `json:"limit_num"` - Operator string `json:"operator"` - ModifyAppName string `json:"modify_app_name"` -} - -//Apollo Open API 相关 - -type ApolloLaunchInfo struct { - Code int64 `json:"code"` - Message string `json:"message"` - Data ApolloLaunchDataInfo `json:"data"` -} - -type ApolloLaunchDataInfo struct { - ToggleInfo ApolloLaunchToggleInfo `json:"toggleInfo"` - Rules [][]ApolloLaunchRuleInfo `json:"rules"` - Groups []ApolloLaunchGroupInfo `json:"groups"` -} - -type ApolloLaunchToggleInfo struct { - Name string `json:"name"` - Description string `json:"description"` - VersionDescription string `json:"versionDescription"` -} - -type ApolloLaunchRuleInfo struct { - Key string `json:"key"` - Value [][]int64 `json:"value"` - Operator string `json:"operator"` - //IsWhiteList bool `json:"isWhiteList"` -} - -type ApolloLaunchGroupInfo struct { - Name string `json:"name"` - Rule ApolloLaunchRuleInfo `json:"rule"` - WhiteList *ApolloLaunchParamInfo `json:"whiteList"` - Params []ApolloLaunchParamInfo `json:"params"` - Remark string `json:"remark"` -} - -type ApolloLaunchParamInfo struct { - Key string `json:"key"` - Type string `json:"type"` - Value interface{} `json:"value"` -} - -type ApolloResponse struct { - Code int64 `json:"code"` - Message string `json:"message"` - Data interface{} `json:"data"` -} - -type ApolloAbResponse struct { - Code int64 `json:"code"` - Message string `json:"message"` - Data ApolloAbDataInfo `json:"data"` -} - -type ApolloAbDataInfo struct { - Name string `json:"name"` - Owner string `json:"owner"` - CountryCode string `json:"countryCode"` - Experiment AbExperimentInfo `json:"experiment"` -} - -type AbExperimentInfo struct { - Partition ApolloPartitionInfo `json:"partition"` - Groups []ApolloAbGroupInfo `json:"groups"` -} - -type ApolloAbGroupInfo struct { - Name string `json:"name"` - Description string `json:"description"` - WhiteList ApolloWhiteListInfo `json:"whitelist"` - Params []ApolloLaunchParamInfo `json:"params"` -} - -type ApolloPartitionInfo struct { - Type string `json:"type"` - Value []int64 `json:"value"` -} - -type ApolloWhiteListInfo struct { - Type string `json:"type"` - Value []string `json:"value"` -} - -type Template struct { - ID int `json:"id"` - Name string `json:"name"` -} - -type ValueMeta struct { - Key string `json:"key"` - Value string `json:"value"` - Type string `json:"type"` - KeyType string `json:"keyType"` -} - -type ApolloConfigResponse struct { - Code int `json:"code"` - Message string `json:"message"` - Data []ApolloConfigData `json:"data"` -} - -type ApolloConfigUpdateResponse struct { - Code int `json:"code"` - Message string `json:"message"` - Data ApolloConfigData `json:"data"` -} -type TemplateInfo struct { -} -type Value struct { - KeyType string `json:"keyType"` - Key string `json:"key"` - Type string `json:"type"` - Value interface{} `json:"value"` -} -type Servers struct { - ID int `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Type int `json:"type"` - CreateTime int64 `json:"createTime"` - EnvID int `json:"envId"` - IP bool `json:"ip"` - Cluster bool `json:"cluster"` - Service bool `json:"service"` -} -type Handler struct { -} -type Region struct { - ID int `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Type int `json:"type"` - CreateTime int64 `json:"createTime"` - ServiceID int `json:"serviceId"` - PublishType int `json:"publishType"` - Servers []Servers `json:"servers"` - OdinNodes interface{} `json:"odinNodes"` - Handler Handler `json:"handler"` - FullTraffic bool `json:"fullTraffic"` - ApolloServerFullTraffic bool `json:"apolloServerFullTraffic"` - PublishName string `json:"publishName"` -} -type MultiLangStatus struct { -} -type PublishRegions struct { - ID int `json:"id"` - ConfigID int `json:"configId"` - EnvID int `json:"envId"` - ConfigVersionID int `json:"configVersionId"` - PublishOrder int `json:"publishOrder"` - Status int `json:"status"` - Region Region `json:"region"` - MultiLangStatus MultiLangStatus `json:"multiLangStatus"` - CreateTime interface{} `json:"createTime"` - UpdateTime int64 `json:"updateTime"` - CheckTimeRemaining int `json:"checkTimeRemaining"` - DoubleCheckers string `json:"doubleCheckers"` - CurrentPublishHistory interface{} `json:"currentPublishHistory"` - DoubleCheckerList []interface{} `json:"doubleCheckerList"` - Offline bool `json:"offline"` - Published bool `json:"published"` - Publishing bool `json:"publishing"` - PublishUnfinished bool `json:"publishUnfinished"` -} -type Environments struct { - ID int `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Type int `json:"type"` - CreateTime int64 `json:"createTime"` - ServiceID int `json:"serviceId"` - PublishType int `json:"publishType"` - Servers []Servers `json:"servers"` - OdinNodes interface{} `json:"odinNodes"` - Handler Handler `json:"handler"` - FullTraffic bool `json:"fullTraffic"` - ApolloServerFullTraffic bool `json:"apolloServerFullTraffic"` - PublishName string `json:"publishName"` -} -type Services struct { - ID int `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - CreatedBy string `json:"createdBy"` - CreateTime int64 `json:"createTime"` - DefaultEnvID int `json:"defaultEnvId"` - AppID int `json:"appId"` - Icon string `json:"icon"` - Owner string `json:"owner"` - Version string `json:"version"` - NeedApprove int `json:"needApprove"` - ConfigType int `json:"configType"` - Mine interface{} `json:"mine"` - Permission interface{} `json:"permission"` - Environments []Environments `json:"environments"` - Machines interface{} `json:"machines"` - DingdingGroup string `json:"dingdingGroup"` - MinPublishStep int `json:"minPublishStep"` - ConfigNameRule string `json:"configNameRule"` - DisplayName string `json:"displayName"` - LastSyncTime int64 `json:"lastSyncTime"` - PublishCheckInterval int `json:"publishCheckInterval"` - EnableDelete interface{} `json:"enableDelete"` - Stat int `json:"stat"` - V2 bool `json:"v2"` - ConfigPublishNeedDoubleCheck bool `json:"configPublishNeedDoubleCheck"` - V1 bool `json:"v1"` -} -type Namespace struct { - ID int `json:"id"` - Name string `json:"name"` - DisplayName string `json:"displayName"` - ConfigNameRule interface{} `json:"configNameRule"` - Description interface{} `json:"description"` - CreateTime int64 `json:"createTime"` - ConfigType int `json:"configType"` - Shared int `json:"shared"` - CreatedBy string `json:"createdBy"` - EnableScenes int `json:"enableScenes"` - SceneFields interface{} `json:"sceneFields"` - Stat int `json:"stat"` - IncConfigID int `json:"incConfigId"` - ChannelVersion int `json:"channelVersion"` - Services interface{} `json:"services"` - Permissions interface{} `json:"permissions"` - EnableAutoPublish int `json:"enableAutoPublish"` - IncField string `json:"incField"` - ConfigValueFormat interface{} `json:"configValueFormat"` - V2Channel bool `json:"v2Channel"` - OperationTemplateConfig bool `json:"operationTemplateConfig"` - EnableAutoIncConfID bool `json:"enableAutoIncConfId"` - TemplateConfig bool `json:"templateConfig"` - V1Channel bool `json:"v1Channel"` -} -type ApolloConfigData struct { - ID int `json:"id"` - Name string `json:"name"` - CanChangePublishType bool `json:"canChangePublishType"` - Template interface{} `json:"template"` - IsMultiLang int `json:"isMultiLang"` - UsedByDict bool `json:"usedByDict"` - Description string `json:"description"` - CurrentVersion int `json:"currentVersion"` - CreatedBy string `json:"createdBy"` - LockedBy string `json:"lockedBy"` - VersionID int `json:"versionId"` - VersionCreateTime int64 `json:"versionCreateTime"` - TemplateInfo TemplateInfo `json:"templateInfo"` - Value []Value `json:"value"` - OriginValue []interface{} `json:"originValue"` - KeyDesc []interface{} `json:"keyDesc"` - EnableFrom interface{} `json:"enableFrom"` - Type int `json:"type"` - Status int `json:"status"` - ErrorTypeParams []interface{} `json:"errorTypeParams"` - ConfigStatus string `json:"configStatus"` - DeleteStatus int `json:"deleteStatus"` - OfflineStatus int `json:"offlineStatus"` - PublishApproval interface{} `json:"publishApproval"` - OperatingTime int64 `json:"operatingTime"` - Operator string `json:"operator"` - PublishRegions PublishRegions `json:"publishRegions"` - CreateTime int64 `json:"createTime"` - ModifyTime int64 `json:"modifyTime"` - ModifiedBy string `json:"modifiedBy"` - Services []Services `json:"services"` - IsDefault int `json:"isDefault"` - Namespace Namespace `json:"namespace"` - VersionDescription string `json:"versionDescription"` - IsSyncOverwrite int `json:"isSyncOverwrite"` - DisplayName interface{} `json:"displayName"` - Permission []interface{} `json:"permission"` - MarketingActivity interface{} `json:"marketingActivity"` - EnableApproval bool `json:"enableApproval"` - Approver string `json:"approver"` - Regions []string `json:"regions"` -} - -type ApolloConfigRequest struct { - Name string `json:"name"` - Description string `json:"description"` - Type int `json:"type"` - EnableApproval bool `json:"enableApproval"` - Approver string `json:"approver"` - Regions []string `json:"regions"` - Value []Value `json:"value"` - VersionDescription string `json:"versionDescription"` -} - -//model-repository 返回结构体 - -type ModelRepositoryRequest struct { - ModelEntryList []*ModelEntry `json:"models"` -} - -type ModelEntry struct { - AppId int64 `json:"appId"` - ModelName string `json:"modelName"` - ModelType string `json:"modelType"` -} - -type ModelRepositoryResponse struct { -} - -type ModelInfo struct { - Id int64 `json:"id"` - Name string `json:"name"` - Version string `json:"version"` - ProduceType string `json:"produceType"` - ModelType string `json:"modelType"` - AppId int64 `json:"appId"` - HdfsPath string `json:"hdfsPath"` -} - -type AppModuleData struct { - AppName string `json:"app_name"` - ProjectBranch string `json:"project_branch"` - Operator string `json:"operator"` -} diff --git a/tg-service/idl/workflow.go b/tg-service/idl/workflow.go deleted file mode 100644 index 3c9805f..0000000 --- a/tg-service/idl/workflow.go +++ /dev/null @@ -1,91 +0,0 @@ -package idl - -import ( - "errors" - "fmt" - "github.com/didi/tg-flow/tg-core/wfengine" - "reflect" -) - -type WorkflowExport struct { - Id int64 `json:"id"` - SceneId int64 `json:"scene_id"` - FlowCharts *WorkflowChart `json:"flow_charts"` - SceneName string `json:"scene_name"` - GroupName string `json:"group_name"` -} - -type Workflow struct { - Id int64 `json:"id"` - SceneId int64 `json:"scene_id"` - FlowCharts map[string]*WorkflowChart `json:"flow_charts"` - SceneName string `json:"scene_name"` -} - -type WorkflowChart struct { - ActionMap map[string]*Action `json:"actions"` -} - -type Action struct { - ActionType string `json:"action_type"` - ActionId string `json:"action_id"` - ActionName string `json:"action_name"` - Params []*wfengine.Param `json:"params"` - NextActionIds []string `json:"next_action_ids,omitempty"` - NextConditions []string `json:"next_conditions,omitempty"` - Description string `json:"description"` - Timeout int `json:"timeout"` - RefWorkflowId int `json:"ref_workflow_id"` - TimeoutAsync bool `json:"timeout_async"` - TimeoutDynamic bool `json:"timeout_dynamic"` - Location string `json:"location"` -} - -func SimpleCopyProperties(dst, src interface{}) (err error) { - // 防止意外panic - defer func() { - if e := recover(); e != nil { - err = errors.New(fmt.Sprintf("%v", e)) - } - }() - - dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst) - srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src) - - // dst必须结构体指针类型 - if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct { - return errors.New("dst type should be a struct pointer") - } - - // src必须为结构体或者结构体指针,.Elem()类似于*ptr的操作返回指针指向的地址反射类型 - if srcType.Kind() == reflect.Ptr { - srcType, srcValue = srcType.Elem(), srcValue.Elem() - } - if srcType.Kind() != reflect.Struct { - return errors.New("src type should be a struct or a struct pointer") - } - - // 取具体内容 - dstType, dstValue = dstType.Elem(), dstValue.Elem() - - // 属性个数 - propertyNums := dstType.NumField() - - for i := 0; i < propertyNums; i++ { - // 属性 - property := dstType.Field(i) - // 待填充属性值 - propertyValue := srcValue.FieldByName(property.Name) - - // 无效,说明src没有这个属性 || 属性同名但类型不同 - if !propertyValue.IsValid() || property.Type != propertyValue.Type() { - continue - } - - if dstValue.Field(i).CanSet() { - dstValue.Field(i).Set(propertyValue) - } - } - - return nil -} diff --git a/tg-service/logic/appconfigadmin/add_update_app_config.go b/tg-service/logic/appconfigadmin/add_update_app_config.go deleted file mode 100644 index 728329f..0000000 --- a/tg-service/logic/appconfigadmin/add_update_app_config.go +++ /dev/null @@ -1,60 +0,0 @@ -package appconfigadmin - -import ( - "context" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "tg-service/common/logs" - "tg-service/idl" - "time" -) - -//新增数据 -func AddOrUpdateAppConfig(addData *idl.AppConfigInfo) *idl.ResponseInfo { - responseInfo := &idl.ResponseInfo{ - Tag: true, - ErrMsg: "", - } - - nowTime := time.Now().Format("2006-01-02 15:04:05") - var sql string - var err error - - if addData.AppName == "" { - responseInfo.Tag = false - responseInfo.ErrMsg = "系统名称 不能为空,或空串" - return responseInfo - } - if addData.MachineRoom == "" { - responseInfo.Tag = false - responseInfo.ErrMsg = "部署机房 不能为空,或空串" - return responseInfo - } - if addData.NodeName == "" { - responseInfo.Tag = false - responseInfo.ErrMsg = "节点名称 不能为空,或空串" - return responseInfo - } - - //先判断数据库中是否有相同记录 - appConfigList := SelectAppConf(addData, -1, 0, false) - - if addData.OldId <= 0 { //添加数据 - if len(appConfigList) > 0 { - responseInfo.Tag = false - responseInfo.ErrMsg = "数据库中已存在 相同app id 的记录,无法添加本条数据!" - return responseInfo - } - sql = "insert into app_config(id, app_name, machine_room, node_name, git_url, operator, create_time, update_time) values(?,?,?,?,?,?,?)" - _, err = mysql.Handler.Exec(sql, addData.Id, addData.AppName, addData.MachineRoom, addData.NodeName, addData.GitUrl, addData.Operator, nowTime, nowTime) - } else { //更新数据 - sql = "update app_config set app_name=?,machine_room=?,node_name=?, git_url=?, operator=?,update_time=? where id=?" - _, err = mysql.Handler.Exec(sql, addData.AppName, addData.MachineRoom, addData.NodeName, addData.GitUrl, addData.Operator, nowTime, addData.Id) - } - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=appconfigadmin.AddOrUpdateSystemConfig||sql=%v||err=%v", sql, err) - } - return responseInfo -} diff --git a/tg-service/logic/appconfigadmin/delete_app_config.go b/tg-service/logic/appconfigadmin/delete_app_config.go deleted file mode 100644 index 94c5a89..0000000 --- a/tg-service/logic/appconfigadmin/delete_app_config.go +++ /dev/null @@ -1,26 +0,0 @@ -package appconfigadmin - -import ( - "context" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "tg-service/common/logs" - "tg-service/idl" -) - -//删除数据 -func DeleteAppConfig(deleteData *idl.AppConfigInfo) *idl.ResponseInfo { - responseInfo := &idl.ResponseInfo{ - Tag: true, - ErrMsg: "", - } - sql := "delete from app_config where id = ?" - _, err := mysql.Handler.Exec(sql, deleteData.Id) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=appconfigadmin.DeleteSystemConfig||sql=%v||err=%v", sql, err) - } - - return responseInfo -} diff --git a/tg-service/logic/appconfigadmin/export_app_config.go b/tg-service/logic/appconfigadmin/export_app_config.go deleted file mode 100644 index f74b216..0000000 --- a/tg-service/logic/appconfigadmin/export_app_config.go +++ /dev/null @@ -1,169 +0,0 @@ -package appconfigadmin - -import ( - "container/list" - "context" - "encoding/json" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/wfengine" - "tg-service/common/logs" - "tg-service/idl" - - "strconv" -) - -//导出数据 -func ExportAppConfig(exportData *idl.AppConfigInfo) *idl.ResponseInfo { - responseInfo := &idl.ResponseInfo{ - Tag: true, - ErrMsg: "", - } - sceneConfigList, err := getSceneListByAppId(exportData) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=appconfigadmin.getSceneIdErr||err=%v", err) - return responseInfo - } - sceneMap := make(map[string]*wfengine.SceneModule) - workflowList := list.New() - for scene := sceneConfigList.Front(); scene != nil; scene = scene.Next() { - sceneModule, ok := scene.Value.(*wfengine.SceneModule) - if ok { - sceneMap[strconv.FormatInt(sceneModule.Id, 10)] = sceneModule - workflowListByScene, err := getSceneWorkflowList(sceneModule) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=appconfigadmin.getSceneWorkflowErr||err=%v", err) - return responseInfo - } - workflowList.PushBackList(workflowListByScene) - } - } - content := make(map[string]interface{}) - workflowStr := "[" - for workflow := workflowList.Front(); workflow != nil; workflow = workflow.Next() { - json, err := json.Marshal(workflow.Value) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=appconfigadmin.getSceneWorkflowErr||err=%v", err) - return responseInfo - } - workflowStr = workflowStr + "'" + string(json) + "'," - } - workflowStr = workflowStr + "]" - sceneJson, err := json.Marshal(sceneMap) - content["scene"] = string(sceneJson) - content["workflowList"] = workflowStr - response, err := json.Marshal(content) - responseInfo.Content = string(response) - return responseInfo -} - -func ExportAppConfigForApollo(exportData *idl.AppConfigInfo) map[string]interface{} { - appWorkflowMap := make(map[string]interface{}) - sceneConfigList, err := getSceneListByAppId(exportData) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=asyncWorkflowToApollo.getSceneIdErr||err=%v", err) - return appWorkflowMap - } - sceneMap := make(map[string]*wfengine.SceneModule) - workflowList := list.New() - for scene := sceneConfigList.Front(); scene != nil; scene = scene.Next() { - sceneModule, ok := scene.Value.(*wfengine.SceneModule) - if ok { - sceneMap[strconv.FormatInt(sceneModule.Id, 10)] = sceneModule - workflowListByScene, err := getSceneWorkflowList(sceneModule) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=asyncWorkflowToApollo.getSceneWorkflowErr||err=%v", err) - return appWorkflowMap - } - workflowList.PushBackList(workflowListByScene) - } - } - appWorkflowMap["scene"] = sceneMap - for workflow := workflowList.Front(); workflow != nil; workflow = workflow.Next() { - workflowValue, ok := workflow.Value.(idl.WorkflowExport) - if ok { - workflowKey := "workflow-" + strconv.FormatInt(workflowValue.SceneId, 10) + "-" + strconv.FormatInt(workflowValue.Id, 10) + "-" + workflowValue.SceneName - appWorkflowMap[workflowKey] = workflowValue.FlowCharts - } - } - return appWorkflowMap -} - -func getSceneListByAppId(exportData *idl.AppConfigInfo) (*list.List, error) { - sql := "select id,`name`,app_id,bucket_type,update_time,flow_type,exp_name from scene_config where app_id = ?" - rows, err := mysql.Handler.Query(sql, exportData.Id) - if err != nil { - return nil, err - } - defer rows.Close() - err = rows.Err() - if err != nil { - return nil, err - } - var sceneConfigList = list.New() - for rows.Next() { - var sceneConfig wfengine.SceneModule - err := rows.Scan(&sceneConfig.Id, &sceneConfig.Name, &sceneConfig.AppId, &sceneConfig.BucketType, &sceneConfig.UpdateTime, &sceneConfig.FlowType, &sceneConfig.DispatchExperimentName) - if err != nil { - return nil, err - } - sceneConfigList.PushBack(&sceneConfig) - } - return sceneConfigList, nil -} - -func getSceneWorkflowList(sceneModule *wfengine.SceneModule) (*list.List, error) { - sql := "select id,experiment_id,flow_chart,group_name,is_default from workflow where status = 1 and experiment_id = ?" - sceneWorkflowList := list.New() - - var sceneId = sceneModule.Id - groupWorkflowMap := make(map[string]int64) - rows, err := mysql.Handler.Query(sql, sceneId) - if err != nil { - return list.New(), err - } - defer rows.Close() - err = rows.Err() - if err != nil { - return list.New(), err - } - var defaultWorkflowId int64 - for rows.Next() { - var workflow = new(wfengine.Workflow) - var flowChartStr string - err := rows.Scan(&workflow.Id, &workflow.SceneId, &flowChartStr, &workflow.GroupName, &workflow.IsDefault) - if err != nil { - return list.New(), err - } - if defaultWorkflowId == 0 && workflow.IsDefault == 1 { - defaultWorkflowId = workflow.Id - } - var actionMap = new(idl.WorkflowChart) - if len(flowChartStr) == 0 { - actionMap.ActionMap = make(map[string]*idl.Action) - } else { - err = json.Unmarshal([]byte(flowChartStr), actionMap) - } - if err != nil { - return list.New(), err - } - var exportWorkflow = idl.WorkflowExport{ - Id: workflow.Id, - SceneId: workflow.SceneId, - FlowCharts: actionMap, - SceneName: sceneModule.Name, - GroupName: workflow.GroupName, - } - sceneWorkflowList.PushBack(exportWorkflow) - groupWorkflowMap[workflow.GroupName] = workflow.Id - } - sceneModule.GroupWorkflowMap = groupWorkflowMap - sceneModule.DefaultWorkflowId = defaultWorkflowId - return sceneWorkflowList, nil -} diff --git a/tg-service/logic/appconfigadmin/select_app_config.go b/tg-service/logic/appconfigadmin/select_app_config.go deleted file mode 100644 index 699348f..0000000 --- a/tg-service/logic/appconfigadmin/select_app_config.go +++ /dev/null @@ -1,111 +0,0 @@ -package appconfigadmin - -import ( - "context" - "database/sql" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "tg-service/common/logs" - "tg-service/common/template" - "tg-service/idl" - "tg-service/logic" - "time" -) - -//查询AppConf数据 -func SelectAppConf(selectData *idl.AppConfigInfo, pageLimit int64, pageNum int64, useLimit bool) []*idl.AppConfigInfo { - sqlStr := "select id, app_name, machine_room, node_name, git_url, operator, create_time, update_time from app_config" - tempSql := " order by id desc" - - if selectData.Id > 0 { //按条件查询 - sqlStr += " where id = ?" + tempSql - } else { //全量️查询 - sqlStr += tempSql - } - - if useLimit { - sqlStr = logic.AddSelectLimit(sqlStr, pageLimit, pageNum) - } - - return SelectAppConfPre(sqlStr, selectData) -} - -//查询数据库 -func SelectAppConfPre(sqlStr string, selectData *idl.AppConfigInfo) []*idl.AppConfigInfo { - stmt, err := GetAppConfStmt(sqlStr) - if err != nil { - return make([]*idl.AppConfigInfo, 0) - } - return QueryAppConf(stmt, selectData) -} - -func GetAppConfStmt(sqlStr string) (*sql.Stmt, error) { - stmt, err := mysql.Handler.Prepare(sqlStr) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=appconfigadmin.GetAppConfStmt||sql=%v||err=%v", sqlStr, err) - } - return stmt, err -} - -//数据库查询 -func QueryAppConf(stmt *sql.Stmt, selectData *idl.AppConfigInfo) []*idl.AppConfigInfo { - defer stmt.Close() - var rows *sql.Rows - var err error - if selectData.Id > 0 { //按条件查询 - rows, err = stmt.Query(selectData.Id) - } else { - rows, err = stmt.Query() - } - - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=appconfigadmin.Query||stmt query is fail||err=%v", err) - return make([]*idl.AppConfigInfo, 0) - } - - err = rows.Err() - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=appconfigadmin.RowsErr||respones row is err||err=%v", err) - return make([]*idl.AppConfigInfo, 0) - } - - return parasRowAppConf(rows) -} - -func parasRowAppConf(rows *sql.Rows) []*idl.AppConfigInfo { - defer rows.Close() - appConfigList := make([]*idl.AppConfigInfo, 0) - for rows.Next() { - var appConfig = new(idl.AppConfigInfo) - var createTime time.Time - var updateTime time.Time - err := rows.Scan(&appConfig.Id, &appConfig.AppName, &appConfig.MachineRoom, &appConfig.NodeName, &appConfig.GitUrl, &appConfig.Operator, &createTime, &updateTime) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=appconfigadmin.RowsScan||rows=%v||err=%v", rows, err) - return appConfigList - } - //时间格式转化 - appConfig.CreateTime = createTime.Format(logic.TIME_FORMAT) - appConfig.UpdateTime = updateTime.Format(logic.TIME_FORMAT) - - appConfigList = append(appConfigList, appConfig) - } - - return appConfigList -} - -//更新系统id和对应中文映射关系 -func UpdateAppMenuCacheData(appConfigList []*idl.AppConfigInfo) { - logic.Mutex.Lock() - var appNameMap map[string]int = make(map[string]int) - var appIdMap map[int]string = make(map[int]string) - for _, appConfig := range appConfigList { - appNameMap[appConfig.AppName] = appConfig.Id - appIdMap[appConfig.Id] = appConfig.AppName - } - if len(appConfigList) > 0 { - template.AppNameMap = appNameMap - template.AppIdMap = appIdMap - } - logic.Mutex.Unlock() -} diff --git a/tg-service/logic/common.go b/tg-service/logic/common.go deleted file mode 100644 index 829b9d5..0000000 --- a/tg-service/logic/common.go +++ /dev/null @@ -1,367 +0,0 @@ -/** - @Description: - @Author:zhouzichun - @Date:2023/5/8 -**/ - -package logic - -import ( - "container/list" - "encoding/json" - "errors" - "fmt" - "github.com/didi/tg-flow/tg-core/common/tlog" - "math/rand" - "net/http" - "sort" - "strconv" - "strings" - "sync" - "tg-service/idl" - "time" -) - -const TIME_FORMAT = "2006-01-02 15:04:05" - -var Mutex sync.Mutex - -//解析请求参数 -func ParsRequestParam(r *http.Request) *idl.WorkFlowData { - data := &idl.WorkFlowData{ - ExperimentId: CancelEmptyStr(r.FormValue("experimentid")), - OldWorkFlowId: CancelEmptyStr(r.FormValue("oldworkflowid")), - WorkFlowId: CancelEmptyStr(r.FormValue("workflowid")), - Modules: CancelEmptyStr(r.FormValue("modules")), - Proportion: CancelEmptyStr(r.FormValue("proportion")), - Defult: CancelEmptyStr(r.FormValue("defult")), - Remark: CancelEmptyStr(r.FormValue("remark")), - UserCookie: CancelEmptyStr(r.FormValue("usercookie")), - } - return data -} - -//解析workflow请求参数 -func ParsRequsetWorkFlowParam(r *http.Request) (*idl.WorkFlowConfig, error) { - err := CheckParam(r.FormValue("oldworkflowid")) - if err != nil { - return &idl.WorkFlowConfig{}, fmt.Errorf("实验编号需要是数字") - } - err = CheckParam(r.FormValue("workflowid")) - if err != nil { - return &idl.WorkFlowConfig{}, fmt.Errorf("新增实验编号需要是数字") - } - - //去掉流量占比数字后面的“%”号 - proportion := strings.Split(CancelEmptyStr(r.FormValue("proportion")), "%")[0] - err = CheckParam(proportion) - if err != nil { - return &idl.WorkFlowConfig{}, fmt.Errorf("流量占比需要是数字") - } - - dimensionId, _ := strconv.ParseInt(r.FormValue("dimension_id"), 10, 64) - dataJson := CancelEmptyStr(r.FormValue("dataJson")) - var updateJson string - if dataJson != "" { - flowChart := new(idl.ChartG6) - err = json.Unmarshal([]byte(dataJson), &flowChart) - if err != nil { - return &idl.WorkFlowConfig{}, fmt.Errorf("流程图不符合规范") - } - workflowChart := formatChartG6(flowChart) - - updateJsonByte, err := json.Marshal(workflowChart) - updateJson = string(updateJsonByte) - if err != nil { - return &idl.WorkFlowConfig{}, fmt.Errorf("流程图不符合规范") - } - } - - data := &idl.WorkFlowConfig{ - SceneName: CancelEmptyStr(r.FormValue("scenename")), - OldWorkFlowId: CancelEmptyStr(r.FormValue("oldworkflowid")), - WorkFlowId: CancelEmptyStr(r.FormValue("workflowid")), - DimensionId: dimensionId, - Modules: CancelEmptyStr(r.FormValue("modules")), - Proportion: proportion, - Defult: CancelEmptyStr(r.FormValue("defult")), - Remark: CancelEmptyStr(r.FormValue("remark")), - CreateTime: r.FormValue("createtime"), - Operator: CancelEmptyStr(r.FormValue("operator")), - ManualSlotIds: CancelEmptyStr(r.FormValue("manual_slot_ids")), - GroupName: r.FormValue("groupname"), - FlowChartJson: updateJson, - } - return data, nil -} - -//解析请求中的页码参数 -func ParsRequestPageParam(r *http.Request) (pageLimit int64, pageNum int64) { - pageNum, _ = strconv.ParseInt(r.FormValue("page_num"), 10, 64) - pageLimit, _ = strconv.ParseInt(r.FormValue("page_limit"), 10, 64) - return pageLimit, pageNum -} - -func formatChartG6(saveData *idl.ChartG6) *idl.WorkflowChart { - nodes := saveData.Nodes - edges := saveData.Edges - workflowChart := new(idl.WorkflowChart) - actionMap := make(map[string]*idl.Action) - typeMap := map[string]string{"rect": "task", "diamond": "condition", "circle": "flow", "clock": "timeout"} - for _, node := range nodes { - action := idl.Action{ - ActionType: typeMap[node.NodeType], - ActionId: node.Id, - ActionName: node.Label, - Params: node.Params, - Timeout: node.Timeout, - RefWorkflowId: node.RefWorkflowId, - TimeoutAsync: node.TimeoutAsync, - TimeoutDynamic: node.TimeoutDynamic, - Location: node.Location, - } - deleteIndex := list.New() - for edgeIndex, edge := range edges { - if edge.Source == node.Id { - action.NextActionIds = append(action.NextActionIds, edge.Target) - if action.ActionType == "condition" { - action.NextConditions = append(action.NextConditions, edge.Label) - } - deleteIndex.PushBack(edgeIndex) - } - } - for i := deleteIndex.Back(); i != nil; i = i.Prev() { - index := i.Value.(int) - edges = append(edges[:index], edges[index+1:]...) - } - actionMap[node.Id] = &action - } - workflowChart.ActionMap = actionMap - return workflowChart - -} - -//检验请求参数 -func CheckParam(param string) error { - if param == "" { - param = "0" - } - _, err := strconv.ParseInt(param, 10, 64) - if err != nil { - return err - } - return nil -} - -//去除换行符和空字符串 -func CancelEmptyStr(str string) string { - str = strings.Replace(str, "\n", "", -1) - str = strings.Replace(str, " ", "", -1) - return str -} - -// EchoJSON json格式输出 -func EchoJSON(w http.ResponseWriter, r *http.Request, data interface{}) { - if cType := w.Header().Get("Content-Type"); cType == "" { - w.Header().Set("Content-Type", "application/json") - } - b, err := json.Marshal(data) - if err != nil { - Echo(w, r, []byte(`{"errno":1, "errmsg":"`+err.Error()+`"}`)) - } else { - Echo(w, r, b) - } -} - -func EchoToJSON(w http.ResponseWriter, r *http.Request, data interface{}) { - if cType := w.Header().Get("Content-Type"); cType == "" { - w.Header().Set("Content-Type", "application/json") - } - b, err := json.Marshal(data) - if err != nil { - EchoTo(w, r, []byte(`{"errno":1, "errmsg":"`+err.Error()+`"}`)) - } else { - EchoTo(w, r, b) - } -} - -func EchoTo(w http.ResponseWriter, req *http.Request, body []byte) { - - if cType := w.Header().Get("Content-Type"); cType == "" { - w.Header().Set("Content-Type", "text/html") - } - if req.Method == "options" { - return - } - if origin := req.Header.Get("Origin"); origin != "" { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Authorization,X-Token") - } - w.Write(body) -} - -// Echo 原始输出 -func Echo(w http.ResponseWriter, req *http.Request, body []byte) { - if cType := w.Header().Get("Content-Type"); cType == "" { - w.Header().Set("Content-Type", "text/plain") - } - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Write(body) -} - -//为Sql语句加上页码限制 -func AddSelectLimit(sqlStr string, pageLimit int64, pageNum int64) (newSqlStr string) { - startIndex := (pageNum - 1) * pageLimit - newSqlStr = sqlStr + " limit " + strconv.FormatInt(startIndex, 10) + ", " + strconv.FormatInt(pageLimit, 10) + ";" - tlog.Handler.Info(newSqlStr) - return newSqlStr -} - -//生成count个[start,end)结束的不重复的随机数 -func GenerateRandomNumber(start int, end int, count int) []int { - //范围检查 - if end < start || (end-start) < count { - return nil - } - //存放结果的slice - nums := make([]int, 0) - //随机数生成器,加入时间戳保证每次生成的随机数不一样 - r := rand.New(rand.NewSource(time.Now().UnixNano())) - for len(nums) < count { - //生成随机数 - num := r.Intn((end - start)) + start - //查重 - exist := false - for _, v := range nums { - if v == num { - exist = true - break - } - } - if !exist { - nums = append(nums, num) - } - } - return nums -} - -//校验添加操作后,流量占比是否为100 -func CheckAfterAddOP(experimentId string, dimendionId int64, mainWorkFlowId string, mainWorkFlowRange string, addRange string) error { - sql := "select id,dimension_id,experiment_id, modules,is_default,range1, remark,create_time,update_time,operator,manual_slot_ids from workflow where status=1 and experiment_id = ? and dimension_id = ?" - experimentMap := SelectWorkFlow(sql, experimentId, dimendionId) - sum := 0 - for _, workFlowMap := range experimentMap { - for workFlowId, workFlow := range workFlowMap { - if strconv.FormatInt(workFlowId, 10) == mainWorkFlowId { - continue - } - if workFlow.Range1 != "" { - sum += len(strings.Split(workFlow.Range1, ",")) - } - } - } - if len(mainWorkFlowRange) > 0 { - if mainWorkFlowRange != "" { - sum += len(strings.Split(mainWorkFlowRange, ",")) - } - } - - if len(addRange) > 0 { - sum += len(strings.Split(addRange, ",")) - } - - if sum > 100 { - err := errors.New("添加操作造成该场景下所有实验的流量占比超过了100,添加操作失败.") - return err - } - return nil -} - -//根据字符串,生成区间表达式,如:"1,2,5,6,7,8,9" -> "[1-2][5-9]" -func CreateRangeStr(str string) string { - resultStr := "" - if len(str) <= 0 { - return resultStr - } - list := make([]string, 0) - addStr := strings.Split(str, ",") - tempAddStr := addStr[0] - for i := 1; i < len(addStr); i++ { - before, _ := strconv.Atoi(addStr[i-1]) - after, _ := strconv.Atoi(addStr[i]) - if after-1 == before { - if len(tempAddStr) <= 0 { - tempAddStr += addStr[i] - } else { - tempAddStr += "," + addStr[i] - } - } else { - list = append(list, tempAddStr) - tempAddStr = addStr[i] - } - } - list = append(list, tempAddStr) - - for _, v := range list { - tempArry := strings.Split(v, ",") - if len(tempArry) == 1 { - resultStr += "[" + v + "]" - } else { - resultStr += "[" + tempArry[0] + "-" + tempArry[len(tempArry)-1] + "]" - } - } - return resultStr -} - -func CheckDeleteOP(mainWorkFlowRange string) error { - if len(strings.Split(mainWorkFlowRange, ",")) > 100 { - err := errors.New("删除操作造成该场景下主流量占比超过了100,删除操作失败.") - return err - } - return nil -} - -func CheckModifyOP(haveUseProportion int, mainWorkFlowRange string, addRange string) error { - mainLen := 0 - if mainWorkFlowRange != "" { - mainLen = len(strings.Split(mainWorkFlowRange, ",")) - - } - modifyLen := 0 - if addRange != "" { - modifyLen = len(strings.Split(addRange, ",")) - } - totalSum := haveUseProportion + mainLen + modifyLen - if totalSum > 100 { - err := errors.New("修改操作,造成该场景下所有实验的流量占比之和,超过了100,修改操作失败.") - return err - } - return nil -} - -//对字符串中的数字排序,如:“4,3,1,5,8,6” -> "1,3,4,5,6,8" -func SortStr(str string) string { - // 去除空格 - str = strings.Replace(str, " ", "", -1) - // 去除换行符 - str = strings.Replace(str, "\n", "", -1) - resultStr := "" - strs := strings.Split(str, ",") - array := make([]int, 0) - for _, v := range strs { - temp, err := strconv.Atoi(v) - if err == nil { - array = append(array, temp) - } - } - sort.Ints(array) - for _, v := range array { - if len(resultStr) <= 0 { - resultStr += strconv.Itoa(v) - } else { - resultStr += "," + strconv.Itoa(v) - } - } - return resultStr -} diff --git a/tg-service/logic/login.go b/tg-service/logic/login.go deleted file mode 100644 index 929f0ec..0000000 --- a/tg-service/logic/login.go +++ /dev/null @@ -1,63 +0,0 @@ -package logic - -import ( - "context" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "tg-service/common/logs" - "tg-service/constant" - "tg-service/idl" -) - -//登录校验 -func Login(username string, password string) (bool, string) { - if constant.Env == "test" { - return true, "login success." - } - //将请求密码,转为md5值,进行校验 - // md5PassWord := MD5(password) - md5PassWord := "" - sqlStr := "select user_name,pass_word,role_id from user_info where user_name=? and pass_word = ?" - tag, err := selectUserInfo(sqlStr, username, md5PassWord) - return tag, err -} - -//数据库查询 -func selectUserInfo(sqlStr string, userName string, passWord string) (bool, string) { - stmt, err := mysql.Handler.Prepare(sqlStr) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagProcessLog, "etype=logic_selectUserInfo||econtent=sql:%v||err=%v", sqlStr, err) - return false, err.Error() - } - defer stmt.Close() - - rows, err := stmt.Query(userName, passWord) - defer rows.Close() - - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagProcessLog, "etype=logic_SelectUserInfo_Query||econtent=sql:%v||err=%v", sqlStr, err) - return false, err.Error() - } - - if rows.Err() != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagProcessLog, "etype=logic_UserInforowsErr||econtent=||err=%v", err) - return false, err.Error() - } - - tag := false - for rows.Next() { - var userInfo = new(idl.UserInfo) - err := rows.Scan(&userInfo.UserName, &userInfo.PassWord, &userInfo.RoleId) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagProcessLog, "etype=logic_UserInforowsScan||econtent=rows:%v||err=%v", rows, err) - return false, err.Error() - } - tag = true - } - - msg := "login success." - if !tag { - msg = "userName or passWord is error." - } - return tag, msg -} diff --git a/tg-service/logic/sceneadmin/add_update_scene.go b/tg-service/logic/sceneadmin/add_update_scene.go deleted file mode 100644 index 0dd5a62..0000000 --- a/tg-service/logic/sceneadmin/add_update_scene.go +++ /dev/null @@ -1,126 +0,0 @@ -package sceneadmin - -import ( - "context" - "fmt" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "strconv" - "tg-service/common/logs" - "tg-service/common/template" - "tg-service/idl" - "time" - "unicode" -) - -//新增 数据 -func AddAddOrUpdateSceneConfig(addData *idl.SceneConfig) *idl.ResponseInfo { - responseInfo := &idl.ResponseInfo{ - Tag: true, - ErrMsg: "", - } - - if len(addData.Name) <= 0 { - responseInfo.Tag = false - responseInfo.ErrMsg = "场景名称 不能为空或者空格,无法添加本条数据!" - return responseInfo - } - - for _, v := range addData.Name { - if unicode.Is(unicode.Han, v) { - responseInfo.Tag = false - responseInfo.ErrMsg = "场景名称 不支持中文,请输入英文!" - return responseInfo - } - } - if len(addData.NameZh) > 0 { - hasZh := false - for _, zhv := range addData.NameZh { - if unicode.Is(unicode.Han, zhv) { - hasZh = true - break - } - } - if !hasZh { - responseInfo.Tag = false - responseInfo.ErrMsg = "中文场景名称不为空时则至少输入一个中文字符!" - return responseInfo - } - } - nowTime := time.Now().Format("2006-01-02 15:04:05") - var sql string - var err error - //判断数据库中是否已经存在该场景id - selectData := &idl.SceneConfig{ - Id: addData.Id, - } - sceneConfigIdList := SelectSceneConf(selectData, -1, 0, false) - //判断数据库中是否已经存在该场景名称 - selectData = &idl.SceneConfig{ - Name: addData.Name, - } - sceneConfigNameList := SelectSceneConf(selectData, -1, 0, false) - - if len(sceneConfigNameList) > 0 && sceneConfigNameList[0].Id != addData.Id { - responseInfo.Tag = false - responseInfo.ErrMsg = "数据库中已存在 其他不同场景编号,但相同场景名称 的记录,无法添加本条数据!" - return responseInfo - } - - appId := template.AppNameMap[addData.AppName] - - if addData.OldId <= 0 { //添加数据 - if len(sceneConfigIdList) > 0 { - responseInfo.Tag = false - responseInfo.ErrMsg = "数据库中已存在 相同场景编号 的记录,无法添加本条数据!" - return responseInfo - } - sql = "insert into scene_config(id,name,app_id,bucket_type,operator,create_time,update_time,flow_type,name_zh,exp_name) values(?,?,?,?,?,?,?,?,?,?)" - _, err = mysql.Handler.Exec(sql, addData.Id, addData.Name, appId, addData.BucketType, addData.Operator, nowTime, nowTime, addData.FlowType, addData.NameZh, addData.ExpName) - } else { //更新数据 - sql = "UPDATE scene_config SET name=?,app_id= ?, bucket_type=?, operator=?, update_time= ?, flow_type=?, name_zh=?, exp_name=? WHERE id=?" - _, err = mysql.Handler.Exec(sql, addData.Name, appId, addData.BucketType, addData.Operator, nowTime, addData.FlowType, addData.NameZh, addData.ExpName, addData.Id) - } - - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=sceneadmin.AddAddOrUpdateSceneConfig||sql=%v||err=%v", sql, err) - return responseInfo - } - - //如果是新增场景,需要向workflow表中插入一条该场景的主流量,且占比100% - if addData.OldId <= 0 { - var range1 string //流量占比 - for i := 0; i < 100; i++ { - value := strconv.Itoa(i) - if len(range1) <= 0 { - range1 = value - } else { - range1 += "," + value - } - } - addSql := "insert into workflow(dimension_id, experiment_id, modules,is_default,status,range1,range2,remark,create_time,update_time,operator,flow_chart) values(-1,?,'',1,1,?,'[0-99]','',?,?,?,'')" - _, addErr := mysql.Handler.Exec(addSql, addData.Id, range1, nowTime, nowTime, addData.Operator) - if addErr != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = fmt.Sprintf("插入数据失败:%v", addErr.Error()) - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=sceneadmin.AddOrUpdateSceneConfig||sql=%v||err=%v", addSql, err) - } - } - - //对于推荐引擎,即appid=1的场景,需要向rec_scene_config表中插入一条该场景和场景组的对应关系 - /* if appId == 1 { - recSceneAddData := &idl.RecSceneConfig{ - SceneId: addData.Id, - SceneName: addData.Name, - Operator: addData.Operator, - } - recSceneConfigList := recalladmin.SelectRecSceneConf(recSceneAddData) - if len(recSceneConfigList) > 0 { - recSceneAddData.Id = recSceneConfigList[0].Id - } - responseInfo = recalladmin.AddOrUpdateRecSceneConfig(recSceneAddData) - }*/ - return responseInfo -} diff --git a/tg-service/logic/sceneadmin/delete_scene.go b/tg-service/logic/sceneadmin/delete_scene.go deleted file mode 100644 index df59aac..0000000 --- a/tg-service/logic/sceneadmin/delete_scene.go +++ /dev/null @@ -1,54 +0,0 @@ -package sceneadmin - -import ( - "context" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "tg-service/common/logs" - "tg-service/idl" -) - -//删除数据 -func DeleteSceneConfig(deleteData *idl.SceneConfig) *idl.ResponseInfo { - responseInfo := &idl.ResponseInfo{ - Tag: true, - ErrMsg: "", - } - - //删除workflow表配置 - sql := "delete from workflow where experiment_id=?" - _, err := mysql.Handler.Exec(sql, deleteData.Id) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = "删除workflow表 该场景的配置 失败!" + err.Error() - return responseInfo - } - - //删除rec_scene_config表配置 - //sql = "delete from rec_scene_config where scene_id = ?" - //_, err = mysql.Handler.Exec(sql, deleteData.Id) - //if err != nil { - // responseInfo.Tag = false - // responseInfo.ErrMsg = "删除rec_scene_config表 该场景的配置 失败!" + err.Error() - // return responseInfo - // } - - //删除dimension表配置 - sql = "delete from dimension where scene_id = ?" - _, err = mysql.Handler.Exec(sql, deleteData.Id) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = "删除 dimension表 该场景的配置 失败!" + err.Error() - return responseInfo - } - - //删除场景配置 - sql = "delete from scene_config where id=?" - _, err = mysql.Handler.Exec(sql, deleteData.Id) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = "删除该场景的配置 失败!" + err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=sceneadmin.DeleteSceneConfig||sql=%v||err=%v", sql, err) - } - return responseInfo -} diff --git a/tg-service/logic/sceneadmin/select_scene_conf.go b/tg-service/logic/sceneadmin/select_scene_conf.go deleted file mode 100644 index 1b10ed8..0000000 --- a/tg-service/logic/sceneadmin/select_scene_conf.go +++ /dev/null @@ -1,146 +0,0 @@ -package sceneadmin - -import ( - "context" - "database/sql" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "tg-service/common/logs" - "tg-service/common/template" - "tg-service/idl" - "tg-service/logic" - "tg-service/logic/appconfigadmin" - - "time" -) - -//查询sceneconfig数据 -func SelectSceneConf(selectData *idl.SceneConfig, pageLimit int64, pageNum int64, useLimit bool) []*idl.SceneConfig { - sqlStr := "select id,name,app_id,bucket_type,operator,create_time,update_time,flow_type,name_zh,exp_name from scene_config" - tempSql := " order by id desc" - if selectData.Id > 0 { //按场景编号查询 - sqlStr += " where id = ?" + tempSql - } else if selectData.Name != "" && selectData.Name != "-1" && selectData.Name != "全部" { //按场景名称查询 - sqlStr += " where name = ?" + tempSql - } else if selectData.AppName != "" && selectData.AppName != "-1" { //按系统名称查询 - //传过来的是系统名称,但是系统相关信息已经拆分到app_config表,因此,此处需要将名称转化成系统id查询 - sqlStr += " where app_id = ?" + tempSql - } else if selectData.BucketType > 0 { //按分桶类型查询 - sqlStr += " where bucket_type = ?" + tempSql - } else { //全量️查询 - sqlStr += tempSql - } - - if useLimit { - sqlStr = logic.AddSelectLimit(sqlStr, pageLimit, pageNum) - } - - return SelectSceneConfPre(sqlStr, selectData) -} - -func SelectSceneConfPre(sqlStr string, selectData *idl.SceneConfig) []*idl.SceneConfig { - //查询数据库 - sceneConfigList := SelectServerDB(sqlStr, selectData) - return sceneConfigList -} - -//数据库查询 -func SelectServerDB(sqlStr string, selectData *idl.SceneConfig) []*idl.SceneConfig { - sceneConfigList := make([]*idl.SceneConfig, 0) - stmt, err := mysql.Handler.Prepare(sqlStr) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=sceneadmin.SelectServerDB||sql=%v||err=%v", sqlStr, err) - return sceneConfigList - } - defer stmt.Close() - - var rows *sql.Rows - if selectData.Id > 0 { //按场景编号查询 - rows, err = stmt.Query(selectData.Id) - } else if selectData.Name != "" && selectData.Name != "-1" && selectData.Name != "全部" { //按场景名称查询 - rows, err = stmt.Query(selectData.Name) - } else if selectData.AppName != "" && selectData.AppName != "-1" { //按系统名称查询 - appId := template.AppNameMap[selectData.AppName] - rows, err = stmt.Query(appId) - } else if selectData.BucketType > 0 { //按分桶类型查询 - rows, err = stmt.Query(selectData.BucketType) - } else { - rows, err = stmt.Query() - } - - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=sceneadmin.SelectServerDB||sql=%v||err=%v", sqlStr, err) - return sceneConfigList - } - defer rows.Close() - - for rows.Next() { - var createTime time.Time - var updateTime time.Time - var sceneConfig = new(idl.SceneConfig) - err := rows.Scan(&sceneConfig.Id, &sceneConfig.Name, &sceneConfig.AppId, &sceneConfig.BucketType, &sceneConfig.Operator, &createTime, &updateTime, &sceneConfig.FlowType, &sceneConfig.NameZh, &sceneConfig.ExpName) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=sceneadmin.SceneRowsScan||rows=%v||err=%v", rows, err) - return sceneConfigList - } - - //根据appid,查询app_config表,获取appname - appData := &idl.AppConfigInfo{ - Id: sceneConfig.AppId, - } - appInfo := appconfigadmin.SelectAppConf(appData, -1, 0, false) - - if len(appInfo) > 0 { - sceneConfig.AppName = appInfo[0].AppName - } else { - sceneConfig.AppName = "数据异常" - } - - //时间格式转化 - sceneConfig.CreateTime = createTime.Format(logic.TIME_FORMAT) - sceneConfig.UpdateTime = updateTime.Format(logic.TIME_FORMAT) - - sceneConfigList = append(sceneConfigList, sceneConfig) - } - err = rows.Err() - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=sceneadmin.SceneRowsErr||respones row is err||err=%v", err) - return sceneConfigList - } - return sceneConfigList -} - -//查询数据库中最大的appid -func SelectMaxAppId() int { - sqlStr := "select MAX(app_id) from scene_config" - stmt, err := mysql.Handler.Prepare(sqlStr) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=sceneadmin.SelectMaxAppId||sql=%v||err=%v", sqlStr, err) - return -1 - } - defer stmt.Close() - - var rows *sql.Rows - rows, err = stmt.Query() - - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=sceneadmin.SelectMaxAppId||sql=%v||err=%v", sqlStr, err) - return -1 - } - defer rows.Close() - - var maxAppId int - for rows.Next() { - err := rows.Scan(&maxAppId) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=sceneadmin.SelectMaxAppIdRowsScan||rows=%v||err=%v", rows, err) - return -1 - } - } - err = rows.Err() - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=sceneadmin.SelectMaxAppIdRowsErr||respones row is err||err=%v", err) - return -1 - } - return maxAppId -} diff --git a/tg-service/logic/select.go b/tg-service/logic/select.go deleted file mode 100644 index 40048e7..0000000 --- a/tg-service/logic/select.go +++ /dev/null @@ -1,131 +0,0 @@ -package logic - -import ( - "context" - "database/sql" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "sort" - "strconv" - "strings" - "tg-service/common/logs" - "tg-service/idl" -) - -//查询数据 -func Select(selectData *idl.WorkFlowData) []*idl.WorkFlowData { - var sqlStr string - if selectData.ExperimentId == "" { //全量️查询 - sqlStr = "select id,experiment_id, modules,is_default,range1,remark,create_time,update_time,operator,manual_slot_ids from workflow where status=1" - } else { //按场景id查询 - _, err := strconv.ParseInt(selectData.ExperimentId, 10, 64) - if err != nil { - return make([]*idl.WorkFlowData, 0) - } - sqlStr = "select id,experiment_id, modules,is_default,range1, remark,create_time,update_time,operator,manual_slot_ids from workflow where status=1 and experiment_id = ?" - } - return SelectWorkFlowPre(sqlStr, selectData.ExperimentId, 0) -} - -func SelectWorkFlowPre(sqlStr string, experimentId string, dimendionId int64) []*idl.WorkFlowData { - //查询数据库 - workFlowMap := SelectWorkFlow(sqlStr, experimentId, dimendionId) - //组装返回结果 - workFlowDataList := Process(workFlowMap) - return workFlowDataList -} - -//数据库查询 -func SelectWorkFlow(sqlStr string, id string, dimendionId int64) map[int64]map[int64]*idl.WorkFlow { - workFlowMap := make(map[int64]map[int64]*idl.WorkFlow) - stmt, err := mysql.Handler.Prepare(sqlStr) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadExperimentWorkflowFail, "etype=logic_SelectWorkFlow_Prepare||econtent=sql:%v||err=%v", sqlStr, err) - return workFlowMap - } - defer stmt.Close() - - var rows *sql.Rows - if id == "" { - rows, err = stmt.Query() - } else { - rows, err = stmt.Query(id, dimendionId) - } - - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadExperimentWorkflowFail, "etype=logic_SelectWorkFlow_Query||econtent=sql:%v||err=%v", sqlStr, err) - return workFlowMap - } - defer rows.Close() - - for rows.Next() { - var workFlow = new(idl.WorkFlow) - err := rows.Scan(&workFlow.Id, &workFlow.DimensionId, &workFlow.ExperimentId, &workFlow.Modules, &workFlow.IsDefault, &workFlow.Range1, &workFlow.Remark, &workFlow.CreateTime, &workFlow.UpdateTime, &workFlow.Operator, &workFlow.ManualSlotIds) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadExperimentWorkflowFail, "etype=logic_workflowrowsScan||econtent=rows:%v||err=%v", rows, err) - return workFlowMap - } - tempMap := workFlowMap[workFlow.ExperimentId] - if tempMap == nil { - tempMap = make(map[int64]*idl.WorkFlow) - workFlowMap[workFlow.ExperimentId] = tempMap - } - tempMap[workFlow.Id] = workFlow - } - err = rows.Err() - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadExperimentWorkflowFail, "etype=logic_workflowrowsErr||econtent=||err=%v", err) - return workFlowMap - } - return workFlowMap -} - -//对查询的数据库数据整理 -func Process(workFlowMap map[int64]map[int64]*idl.WorkFlow) []*idl.WorkFlowData { - workFlowDataList := make([]*idl.WorkFlowData, 0) - if workFlowMap == nil || len(workFlowMap) <= 0 { - return workFlowDataList - } - - keys := make([]int, 0) - for key := range workFlowMap { - keys = append(keys, int(key)) - } - // 给key排序,从小到大 - sort.Sort(sort.IntSlice(keys)) - - for _, key := range keys { - tempMap := workFlowMap[int64(key)] - if len(tempMap) <= 0 { - continue - } - tempKeys := make([]int, 0) - for tempkey := range tempMap { - tempKeys = append(tempKeys, int(tempkey)) - } - // 给key排序,从小到大 - sort.Sort(sort.IntSlice(tempKeys)) - - for _, k := range tempKeys { - value := tempMap[int64(k)] - var data = new(idl.WorkFlowData) - data.ExperimentId = strconv.FormatInt(value.ExperimentId, 10) - data.WorkFlowId = strconv.FormatInt(value.Id, 10) - data.Modules = value.Modules - data.Defult = strconv.Itoa(value.IsDefault) - data.Remark = value.Remark - data.CreateTime = value.CreateTime - data.UpdateTime = value.UpdateTime - data.Operator = value.Operator - data.ManualSlotIds = value.ManualSlotIds - proportionStr := strings.Split(value.Range1, ",") - if len(proportionStr) == 1 && proportionStr[0] == "" { - data.Proportion = "0%" - } else { - data.Proportion = strconv.Itoa(len(proportionStr)) + "%" - } - workFlowDataList = append(workFlowDataList, data) - } - } - return workFlowDataList -} diff --git a/tg-service/logic/workflowadmin/add_update_workflow.go b/tg-service/logic/workflowadmin/add_update_workflow.go deleted file mode 100644 index 0567ae9..0000000 --- a/tg-service/logic/workflowadmin/add_update_workflow.go +++ /dev/null @@ -1,112 +0,0 @@ -package workflowadmin - -import ( - "fmt" - "sort" - "strconv" - "strings" - "tg-service/common/template" - "tg-service/idl" - "tg-service/logic" -) - -func AddOrUpdateWorkFlowConfig(addData *idl.WorkFlowConfig) *idl.ResponseInfo { - responseInfo := &idl.ResponseInfo{ - Tag: true, - ErrMsg: "", - } - - if addData.OldWorkFlowId == "" { //新增数据 - AddWorkFlow(addData, responseInfo) - } else { //更新数据 - UpdateDBWorkFlow(addData, responseInfo) - } - return responseInfo -} - -//查询指定场景id下的主流量、非主流量占比等信息 -func SelectDBMainAndThisWorkFlow(data *idl.WorkFlowConfig) (*idl.WorkFlowConfig, *idl.WorkFlowConfig, int, string, error) { - //根据场景名称,获取场景id - var sceneId int - sceneId, ok := template.SceneNameAndIdMap[data.SceneName] - if !ok { - return nil, nil, 0, "", fmt.Errorf("SceneNameAndIdMap not has sceneid=%v", data.SceneName) - } - selectSql := "select id,dimension_id,experiment_id, modules,is_default,range1,range2, remark , create_time ,update_time , operator, manual_slot_ids,group_name, flow_chart from workflow where status=1 and experiment_id = ? and dimension_id = ?" - sceneMap, err := SelectDBWorkFlow(selectSql, sceneId, data.DimensionId) - if err != nil { - return nil, nil, 0, "", err - } - - haveUseProportion := 0 //不包括该非主流量的,其他非主流量的总占比百分数 - haveUseRange1 := "" //不包括该非主流量的,其他非主流量的总占比离散数字值 - var defaultWorkFlow = new(idl.WorkFlowConfig) //主流量 - var thisWorkFlow = new(idl.WorkFlowConfig) //本条流量在数据库中的记录 - for _, workFlowMap := range sceneMap { - for _, workFlow := range workFlowMap { - if workFlow.IsDefault == 1 { //主流量 - defaultWorkFlow.WorkFlowId = strconv.FormatInt(workFlow.Id, 10) - defaultWorkFlow.Modules = workFlow.Modules - defaultWorkFlow.Proportion = workFlow.Range1 - defaultWorkFlow.Remark = workFlow.Remark - defaultWorkFlow.ManualSlotIds = workFlow.ManualSlotIds - } else { - //未修改workflow id时,需要将数据库中的流量占比离散数字,赋值给请求的记录中,便于后续计算新的流量占比离散数字 - if strconv.FormatInt(workFlow.Id, 10) == data.WorkFlowId { - data.Proportion = workFlow.Range1 - } - //修改了workflow id时,需要将数据库中该条流量对应的原始数据返回 - if strconv.FormatInt(workFlow.Id, 10) == data.OldWorkFlowId { - thisWorkFlow.WorkFlowId = strconv.FormatInt(workFlow.Id, 10) - thisWorkFlow.Modules = workFlow.Modules - thisWorkFlow.Proportion = workFlow.Range1 - thisWorkFlow.Remark = workFlow.Remark - thisWorkFlow.Defult = strconv.Itoa(workFlow.IsDefault) - thisWorkFlow.ManualSlotIds = workFlow.ManualSlotIds - } else { - //将其他非主流量的占比,离散数字加在一起 - if len(haveUseRange1) <= 0 { - haveUseRange1 += workFlow.Range1 - } else { - haveUseRange1 += "," + workFlow.Range1 - } - //将其他非主流量的占比,百分数加在一起 - if workFlow.Range1 != "" { - proportionStr := strings.Split(workFlow.Range1, ",") - haveUseProportion += len(proportionStr) - } - } - } - } - } - return defaultWorkFlow, thisWorkFlow, haveUseProportion, haveUseRange1, nil -} - -//调整主流量占比值和给新增流量分配占比离散数字(0-99) -func ModifyWorkFlowRange(proportionStr []string, proportion int) (string, string) { - var addRangeStr string //新增的分桶流量占比 - var rangeStr string //更新主流量占比 - index := 0 - //在主流量proportionStr中,随机挑选proportion个新增的流量占比值 - indexArry := logic.GenerateRandomNumber(0, len(proportionStr), proportion) - //对indexArry数组从小到大排序 - sort.Ints(indexArry) - for i := 0; i < len(proportionStr); i++ { - if index < len(indexArry) && i == indexArry[index] { - if len(addRangeStr) <= 0 { - addRangeStr += proportionStr[i] - } else { - addRangeStr += "," + proportionStr[i] - } - index += 1 - } else { - if len(rangeStr) <= 0 { - rangeStr += proportionStr[i] - } else { - rangeStr += "," + proportionStr[i] - } - } - } - - return addRangeStr, rangeStr -} diff --git a/tg-service/logic/workflowadmin/add_workflow.go b/tg-service/logic/workflowadmin/add_workflow.go deleted file mode 100644 index 4d1ed31..0000000 --- a/tg-service/logic/workflowadmin/add_workflow.go +++ /dev/null @@ -1,126 +0,0 @@ -package workflowadmin - -import ( - "github.com/didi/tg-flow/tg-core/common/mysql" - "strconv" - "strings" - "tg-service/common/template" - "tg-service/idl" - "tg-service/logic" - "tg-service/logic/sceneadmin" - "time" -) - -func AddWorkFlow(addData *idl.WorkFlowConfig, responseInfo *idl.ResponseInfo) { - var flowType int64 - flowType = -1 - if addData.SceneId != 0 { - sceneData := &idl.SceneConfig{ - Id: addData.SceneId, - } - sceneConfigList := sceneadmin.SelectSceneConf(sceneData, -1, 0, false) - if len(sceneConfigList) != 0 { - flowType = sceneConfigList[0].FlowType - } - } - - nowTime := time.Now().Format("2006-01-02 15:04:05") - //新增流量占比 - proportion, _ := strconv.Atoi(addData.Proportion) - - //根据场景id,查询主流量占比,并更新 - var sceneId int - sceneId, ok := template.SceneNameAndIdMap[addData.SceneName] - if !ok { - sceneId = 0 - } - - //查询该场景和维度下的信息 - sql := "select id,dimension_id,experiment_id, modules,is_default,range1,range2, remark ,create_time,update_time,operator,manual_slot_ids,group_name, flow_chart from workflow where status=1 and experiment_id = ? and dimension_id = ?" - workFlowMap, err := SelectDBWorkFlow(sql, sceneId, addData.DimensionId) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = "添加数据失败:" + err.Error() - return - } - - //找出主流量 - defaultWorkFlow := new(idl.WorkFlow) - for _, sceneMap := range workFlowMap { //同一个场景和维度下,主流量只会有一条记录 - for _, workFlow := range sceneMap { - if workFlow.IsDefault == 1 { - defaultWorkFlow = workFlow - break - } - } - } - - //新增的分桶流量占比离散数字(0-99)字符串 - var addRangeStr string - //需要更新的主流量的分桶流量占比离散数字(0-99)字符串 - var defaultRangeStr string - //主流量实验编号 - var defaultWorkFlowId int64 - - defaultProportion := strings.Split(defaultWorkFlow.Range1, ",") - - defaultProportionLength := 0 - if len(defaultWorkFlow.Range1) > 0 { //排除主流量占比值为空的情况 - defaultProportionLength = len(defaultProportion) - } - - if flowType == 0 { - if defaultProportionLength < proportion { - responseInfo.Tag = false - responseInfo.ErrMsg = "新增的流量占比,超过了可分配的流量" - return - } else { - defaultWorkFlowId = defaultWorkFlow.Id - //生成新增流量和主流量占比的离散数字(0-99)字符串 - addRangeStr, defaultRangeStr = ModifyWorkFlowRange(defaultProportion, proportion) - - //校验添加操作后的流量总占比,是否为100 - err := logic.CheckAfterAddOP(strconv.FormatInt(defaultWorkFlow.ExperimentId, 10), addData.DimensionId, strconv.FormatInt(defaultWorkFlow.Id, 10), defaultRangeStr, addRangeStr) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - return - } - } - } - - /**************先入库新增流量成功了,才能去更新之前的主流量占比*********************/ - - //if len(addRangeStr) <= 0 || addRangeStr == "" { - // responseInfo.Tag = false - // responseInfo.ErrMsg = "新增分桶流量分配占比,必须大于0" - // return - //} - - var addSql string - var addErr error - addRange2 := logic.CreateRangeStr(addRangeStr) - if addData.WorkFlowId == "" { //新增时,没有填写实验编号 - addSql = "insert into workflow(dimension_id,experiment_id, modules,is_default,status,range1,range2,remark,create_time,update_time,operator,manual_slot_ids,group_name,flow_chart) values(?,?,?,0,1,?,?,?,?,?,?,?,?,?)" - _, addErr = mysql.Handler.Exec(addSql, addData.DimensionId, sceneId, addData.Modules, addRangeStr, addRange2, addData.Remark, nowTime, nowTime, addData.Operator, addData.ManualSlotIds, addData.GroupName, addData.FlowChartJson) - } else { - addSql = "insert into workflow(id,dimension_id,experiment_id, modules,is_default,status,range1,range2,remark,create_time,update_time,operator,manual_slot_ids,group_name,flow_chart) values(?,?,?,?,0,1,?,?,?,?,?,?,?,?,?)" - _, addErr = mysql.Handler.Exec(addSql, addData.WorkFlowId, addData.DimensionId, sceneId, addData.Modules, addRangeStr, addRange2, addData.Remark, nowTime, nowTime, addData.Operator, addData.ManualSlotIds, addData.GroupName, addData.FlowChartJson) - } - - if addErr != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = addErr.Error() - return - } - - //更新主流量占比,修改数据库 - range2 := logic.CreateRangeStr(defaultRangeStr) - updateSql := "update workflow set range1 = ? ,range2 = ? ,update_time = ?, operator =?, manual_slot_ids =? where id= ?" - _, updateErr := mysql.Handler.Exec(updateSql, defaultRangeStr, range2, nowTime, addData.Operator, defaultWorkFlow.ManualSlotIds, defaultWorkFlowId) - if updateErr != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = updateErr.Error() - return - } -} diff --git a/tg-service/logic/workflowadmin/delete_workflow.go b/tg-service/logic/workflowadmin/delete_workflow.go deleted file mode 100644 index 6f3d8d1..0000000 --- a/tg-service/logic/workflowadmin/delete_workflow.go +++ /dev/null @@ -1,73 +0,0 @@ -package workflowadmin - -import ( - "context" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "tg-service/common/logs" - "tg-service/idl" - "tg-service/logic" - "time" -) - -//删除数据 -func DeleteWorkflowConfig(deleteData *idl.WorkFlowConfig) *idl.ResponseInfo { - responseInfo := &idl.ResponseInfo{ - Tag: true, - ErrMsg: "", - } - - //1、查询对应主流量信息 - workFlow, _, _, _, err := SelectDBMainAndThisWorkFlow(deleteData) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = "数据删除失败:" + err.Error() - return responseInfo - } - - //2、更新主流量range1和range2区间字段值 - var tempRange string - if len(deleteData.Proportion) <= 0 || deleteData.Proportion == "" { - tempRange = workFlow.Proportion - } else { - //因为SelectDBMainAndThisWorkFlow方法中,将这条被删除记录的Proportion属性值(请求中是百分数),用数据库中原始的占比离散数字字符串替换了 - //所以,此处可以直接将deleteData.Proportion中的占比离散数字,直接追加到主流量中 - tempRange = deleteData.Proportion + "," + workFlow.Proportion - } - - //3、删除操作后,对流量占比的校验 - err = logic.CheckDeleteOP(tempRange) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - return responseInfo - } - - nowTime := time.Now().Format("2006-01-02 15:04:05") - - //4、需要对range1的值按从小到大顺序排列 - range1 := logic.SortStr(tempRange) - - //5、对range1生成对应的区间表达式 - range2 := logic.CreateRangeStr(range1) - - //6、先更新主流量,如果失败,则不需要执行后续的删除操作 - updateSql := "update workflow set range1 = ?,range2 = ?,update_time =?,operator=?,manual_slot_ids=? where id = ?" - _, updateErr := mysql.Handler.Exec(updateSql, range1, range2, nowTime, deleteData.Operator, workFlow.ManualSlotIds, workFlow.WorkFlowId) - if updateErr != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = updateErr.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=workflowadmin.DeleteWorkflowConfig||sql=%v||err=%v", updateSql, err) - return responseInfo - } - - //7、删除该条副流量实验 - deleteSql := "delete from workflow where id = ?" - _, deleteErr := mysql.Handler.Exec(deleteSql, deleteData.WorkFlowId) - if deleteErr != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = deleteErr.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=workflowadmin.DeleteWorkflowConfig||sql=%v||err=%v", deleteSql, err) - } - return responseInfo -} diff --git a/tg-service/logic/workflowadmin/export_workflow.go b/tg-service/logic/workflowadmin/export_workflow.go deleted file mode 100644 index 0d81229..0000000 --- a/tg-service/logic/workflowadmin/export_workflow.go +++ /dev/null @@ -1,97 +0,0 @@ -package workflowadmin - -import ( - "context" - "encoding/json" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/wfengine" - "strconv" - "tg-service/common/logs" - "tg-service/common/template" - "tg-service/idl" - "time" -) - -//导出数据 -func ExportWorkFlow(exportData *idl.WorkFlowConfig) *idl.ResponseInfo { - responseInfo := &idl.ResponseInfo{ - Tag: true, - ErrMsg: "", - } - workFlow, err := GetWorkFlowExport(exportData) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=workflowAdmin.getWorkFlowErr||err=%v", err) - return responseInfo - } - response, err := json.Marshal(workFlow) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=workflowAdmin.getWorkFlowErr||err=%v", err) - return responseInfo - } - responseInfo.Content = string(response) - return responseInfo -} -func GetWorkFlowExport(exportData *idl.WorkFlowConfig) (*idl.WorkflowExport, error) { - workflow, flowChartStr, err := getWorkFlow(exportData) - if err != nil { - return nil, err - } - var actionMap = new(idl.WorkflowChart) - if len(flowChartStr) == 0 { - actionMap.ActionMap = make(map[string]*idl.Action) - } else { - err = json.Unmarshal([]byte(flowChartStr), actionMap) - } - if err != nil { - return nil, err - } - var exportWorkflow = idl.WorkflowExport{ - Id: workflow.Id, - SceneId: workflow.SceneId, - FlowCharts: actionMap, - } - sceneId, err := strconv.Atoi(strconv.FormatInt(workflow.SceneId, 10)) - if err != nil { - return nil, err - } - exportWorkflow.SceneName = template.SceneIdAndNameMap[sceneId] - return &exportWorkflow, nil - -} -func getWorkFlowShow(exportData *idl.WorkFlowConfig) (*wfengine.Workflow, string, error) { - workflow, flowChartStr, err := getWorkFlow(exportData) - return workflow, flowChartStr, err -} -func getWorkFlow(workflowConfig *idl.WorkFlowConfig) (*wfengine.Workflow, string, error) { - sql := "select id,dimension_id,experiment_id,flow_chart,is_default,range1,range2,remark,group_name from workflow where id = ?" - rows, err := mysql.Handler.Query(sql, workflowConfig.WorkFlowId) - if err != nil { - return nil, "", err - } - defer rows.Close() - err = rows.Err() - if err != nil { - return nil, "", err - } - var workflow = new(wfengine.Workflow) - var flowChartStr string - for rows.Next() { - err := rows.Scan(&workflow.Id, &workflow.DimensionId, &workflow.SceneId, &flowChartStr, &workflow.IsDefault, &workflow.Range1, &workflow.Range2, &workflow.Remark, &workflow.GroupName) - if err != nil { - return nil, "", err - } - //if len(flowChartStr) > 0 { - // workflow.FlowCharts, workflow.FlowBranch, err = wfengine.NewWorkflowChart(flowChartStr) - // if err != nil { - // return nil, "", err - // } - //} - workflow.UpdateTime = time.Now() - } - return workflow, flowChartStr, nil -} diff --git a/tg-service/logic/workflowadmin/flowchecker/checker.go b/tg-service/logic/workflowadmin/flowchecker/checker.go deleted file mode 100644 index 950437b..0000000 --- a/tg-service/logic/workflowadmin/flowchecker/checker.go +++ /dev/null @@ -1,131 +0,0 @@ -package flowchecker - -import ( - "fmt" - "strconv" - "strings" - "tg-service/idl" -) - -func replaceWorkflowID(id string, workflowId int64) (string, error) { - idSeg := strings.Split(id, "-") - if len(idSeg) != 3 || idSeg[0] != "action" { - return "", fmt.Errorf("action id " + id + "is illegal") - } - - // 将 action ID 中的 Workflow ID 替换为当前的 Workflow ID - idSeg[1] = strconv.Itoa(int(workflowId)) - return strings.Join(idSeg, "-"), nil -} - -func replaceIDsForAction(action *idl.Action, workflowId int64) error { - if action == nil { - return fmt.Errorf("action is nil") - } - - // replace action id - newId, err := replaceWorkflowID(action.ActionId, workflowId) - if err != nil { - return fmt.Errorf("action id illegal: %v", err) - } - action.ActionId = newId - - if len(action.NextActionIds) == 0 { - return nil - } - - // replace next action id - newNextIds := make([]string, 0) - for _, id := range action.NextActionIds { - newId, err = replaceWorkflowID(id, workflowId) - if err != nil { - return fmt.Errorf("action id illegal: %v", err) - } - newNextIds = append(newNextIds, newId) - } - action.NextActionIds = newNextIds - return nil -} - -func isDup(strs []string) bool { - m := make(map[string]bool) - for _, s := range strs { - if m[s] { - return true - } - m[s] = true - } - return false -} - -func checkFlowChart(flowChart *idl.WorkflowChart) error { - - if flowChart == nil { - return fmt.Errorf("flow chart is nil") - } - - if len(flowChart.ActionMap) == 0 { - return fmt.Errorf("not action in flow chart") - } - - for id, action := range flowChart.ActionMap { - if id != action.ActionId { - return fmt.Errorf("action id is not match, id: %v", id) - } - - if isDup(action.NextActionIds) { - return fmt.Errorf("next action ids duplicated, action id %v", action.ActionId) - } - - for _, id := range action.NextActionIds { - if flowChart.ActionMap[id] == nil { - return fmt.Errorf("next action id is not in flow chart, id: %v", id) - } - } - - } - return nil -} - -func reaplceActionIdsAndCreateNewFlow(flowChart *idl.WorkflowChart, workflowId int64) (*idl.WorkflowChart, error) { - newActions := make([]*idl.Action, 0) - for aid, action := range flowChart.ActionMap { - err := replaceIDsForAction(action, workflowId) - if err != nil { - return nil, fmt.Errorf("replace id for action %v error: %v", aid, err) - } - newActions = append(newActions, action) - } - - newFLowChart := &idl.WorkflowChart{ - ActionMap: make(map[string]*idl.Action), - } - - for _, action := range newActions { - newFLowChart.ActionMap[action.ActionId] = action - } - - if len(newFLowChart.ActionMap) != len(newActions) { - return nil, fmt.Errorf("there are action ids duplicated") - } - return newFLowChart, nil -} - -// ReplaceActionIDsForFlow 替换流程中的 ActionID 以匹配新的 workflowId, action ID 格式必须为 action-{workflowId}-{uid} -func ReplaceActionIDsForFlow(flowChart *idl.WorkflowChart, workflowId int64) (*idl.WorkflowChart, error) { - - if err := checkFlowChart(flowChart); err != nil { - return nil, err - } - - newFlowChart, err := reaplceActionIdsAndCreateNewFlow(flowChart, workflowId) - if err != nil { - return nil, err - } - - if err := checkFlowChart(newFlowChart); err != nil { - return nil, err - } - - return newFlowChart, nil -} diff --git a/tg-service/logic/workflowadmin/flowchecker/checker_test.go b/tg-service/logic/workflowadmin/flowchecker/checker_test.go deleted file mode 100644 index 6994754..0000000 --- a/tg-service/logic/workflowadmin/flowchecker/checker_test.go +++ /dev/null @@ -1,156 +0,0 @@ -package flowchecker - -import ( - "reflect" - "testing" - "tg-service/idl" -) - -func TestCheckAndConvertID(t *testing.T) { - type args struct { - flowChart *idl.WorkflowChart - workflowId int64 - } - tests := []struct { - name string - args args - want *idl.WorkflowChart - wantErr bool - }{ - { - name: "flow char is nil", - args: args{ - flowChart: nil, - workflowId: 0, - }, - want: nil, - wantErr: true, - }, - { - name: "workflowId illegal", - args: args{ - flowChart: &idl.WorkflowChart{ActionMap: map[string]*idl.Action{"action-1-1": {}}}, - workflowId: 0, - }, - want: nil, - wantErr: true, - }, - { - name: "flow char actions emtpy", - args: args{ - flowChart: &idl.WorkflowChart{ActionMap: map[string]*idl.Action{}}, - workflowId: 1, - }, - want: nil, - wantErr: true, - }, - { - name: "flow char actions id illegal", - args: args{ - flowChart: &idl.WorkflowChart{ActionMap: map[string]*idl.Action{"foo-123": {}}}, - workflowId: 1, - }, - want: nil, - wantErr: true, - }, - { - name: "repeat action uid", - args: args{ - flowChart: &idl.WorkflowChart{ActionMap: map[string]*idl.Action{ - "action-103-1": {ActionId: "action-103-1"}, - "action-104-1": {ActionId: "action-104-1"}, - "action-103-3": {ActionId: "action-103-3"}, - "action-103-4": {ActionId: "action-103-4"}, - "action-103-5": {ActionId: "action-103-5"}, - }}, - workflowId: 100, - }, - want: nil, - wantErr: true, - }, - { - name: "flow char", - args: args{ - flowChart: &idl.WorkflowChart{ActionMap: map[string]*idl.Action{ - "action-103-1": {ActionId: "action-103-1"}, - "action-103-2": {ActionId: "action-103-2"}, - "action-103-3": {ActionId: "action-103-3"}, - "action-103-4": {ActionId: "action-103-4"}, - "action-103-5": {ActionId: "action-103-5"}, - }}, - workflowId: 100, - }, - want: &idl.WorkflowChart{ActionMap: map[string]*idl.Action{ - "action-100-1": {ActionId: "action-100-1"}, - "action-100-2": {ActionId: "action-100-2"}, - "action-100-3": {ActionId: "action-100-3"}, - "action-100-4": {ActionId: "action-100-4"}, - "action-100-5": {ActionId: "action-100-5"}, - }}, - wantErr: false, - }, - { - name: "action next ids", - args: args{ - flowChart: &idl.WorkflowChart{ActionMap: map[string]*idl.Action{ - "action-103-1": {ActionId: "action-103-1", NextActionIds: []string{"action-103-3"}}, - "action-103-2": {ActionId: "action-103-2", NextActionIds: []string{"action-103-4"}}, - "action-103-3": {ActionId: "action-103-3", NextActionIds: []string{"action-103-4", "action-103-5"}}, - "action-103-4": {ActionId: "action-103-4", NextActionIds: []string{"action-103-5"}}, - "action-103-5": {ActionId: "action-103-5"}, - }}, - workflowId: 100, - }, - want: &idl.WorkflowChart{ActionMap: map[string]*idl.Action{ - "action-100-1": {ActionId: "action-100-1", NextActionIds: []string{"action-100-3"}}, - "action-100-2": {ActionId: "action-100-2", NextActionIds: []string{"action-100-4"}}, - "action-100-3": {ActionId: "action-100-3", NextActionIds: []string{"action-100-4", "action-100-5"}}, - "action-100-4": {ActionId: "action-100-4", NextActionIds: []string{"action-100-5"}}, - "action-100-5": {ActionId: "action-100-5"}, - }}, - wantErr: false, - }, - { - name: "action next ids duplicated", - args: args{ - flowChart: &idl.WorkflowChart{ActionMap: map[string]*idl.Action{ - "action-103-1": {ActionId: "action-103-1", NextActionIds: []string{"action-103-3"}}, - "action-103-2": {ActionId: "action-103-2", NextActionIds: []string{"action-103-4"}}, - "action-103-3": {ActionId: "action-103-3", NextActionIds: []string{"action-103-4", "action-103-4"}}, - "action-103-4": {ActionId: "action-103-4", NextActionIds: []string{"action-103-5"}}, - "action-103-5": {ActionId: "action-103-5"}, - }}, - workflowId: 100, - }, - want: nil, - wantErr: true, - }, - { - name: "action next ids not exits", - args: args{ - flowChart: &idl.WorkflowChart{ActionMap: map[string]*idl.Action{ - "action-103-1": {ActionId: "action-103-1", NextActionIds: []string{"action-103-9"}}, - "action-103-2": {ActionId: "action-103-2", NextActionIds: []string{"action-103-4"}}, - "action-103-3": {ActionId: "action-103-3", NextActionIds: []string{"action-103-4", "action-103-5"}}, - "action-103-4": {ActionId: "action-103-4", NextActionIds: []string{"action-103-5"}}, - "action-103-5": {ActionId: "action-103-5"}, - }}, - workflowId: 100, - }, - want: nil, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := ReplaceActionIDsForFlow(tt.args.flowChart, tt.args.workflowId) - if (err != nil) != tt.wantErr { - t.Errorf("ReplaceActionIDsForFlow() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("ReplaceActionIDsForFlow() got = %#v, want %#v", got, tt.want) - } - }) - } -} diff --git a/tg-service/logic/workflowadmin/get_save_workflow.go b/tg-service/logic/workflowadmin/get_save_workflow.go deleted file mode 100644 index 19c2312..0000000 --- a/tg-service/logic/workflowadmin/get_save_workflow.go +++ /dev/null @@ -1,190 +0,0 @@ -package workflowadmin - -import ( - "container/list" - "context" - "encoding/json" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "strings" - "tg-service/common/logs" - "tg-service/idl" - "tg-service/logic/workflowadmin/flowchecker" - "time" -) - -//获取可以绘制的流程图信息 -func GetWorkFlowChart(exportData *idl.WorkFlowConfig) *idl.ResponseInfo { - responseInfo := &idl.ResponseInfo{ - Tag: true, - ErrMsg: "", - } - workFlow, err := GetWorkFlowExport(exportData) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=workflowAdmin.getWorkFlowErr||err=%v", err) - return responseInfo - } - - workFlowChart := formatWorkflow(workFlow) - - workFlowJson, err := json.Marshal(workFlowChart) - responseInfo.Content = string(workFlowJson) - return responseInfo -} - -func formatWorkflow(workflow *idl.WorkflowExport) idl.ChartG6 { - var nodeList []*idl.Node - var edgeList []*idl.Edge - - actions := workflow.FlowCharts.ActionMap - - typeMap := map[string]string{"task": "rect", "condition": "diamond", "flow": "circle", "timeout": "clock"} - - for _, v := range actions { - node := &idl.Node{ - Id: v.ActionId, - Label: v.ActionName, - NodeType: typeMap[v.ActionType], - Params: v.Params, - Timeout: v.Timeout, - RefWorkflowId: v.RefWorkflowId, - TimeoutAsync: v.TimeoutAsync, - TimeoutDynamic: v.TimeoutDynamic, - } - nodeList = append(nodeList, node) - if strings.EqualFold(v.ActionType, "condition") { - for i := 0; i < len(v.NextActionIds); i++ { - edge := &idl.Edge{ - Id: v.ActionId + "_" + v.NextActionIds[i], - EdgeType: "line", - Source: v.ActionId, - Target: v.NextActionIds[i], - Label: v.NextConditions[i], - } - edgeList = append(edgeList, edge) - } - node.Params = v.Params - } else { - for _, actionId := range v.NextActionIds { - edge := &idl.Edge{ - Id: v.ActionId + "_" + actionId, - EdgeType: "line", - Source: v.ActionId, - Target: actionId, - } - edgeList = append(edgeList, edge) - } - } - - } - workflowChart := idl.ChartG6{ - Nodes: nodeList, - Edges: edgeList, - } - return workflowChart -} - -// SaveImportWorkFlowChart 处理导入信息的保存 -func SaveImportWorkFlowChart(saveData *idl.WorkflowChart, workflowId int64, operator string) *idl.ResponseInfo { - responseInfo := &idl.ResponseInfo{ - Tag: true, - ErrMsg: "", - } - - flowChart, err := flowchecker.ReplaceActionIDsForFlow(saveData, workflowId) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=workflowAdmin.saveImportWorkFlowErr||err=%v", err) - return responseInfo - } - - updateJson, err := json.Marshal(flowChart) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=workflowAdmin.saveImportWorkFlowErr||err=%v", err) - return responseInfo - } - err = SaveUpdateJson(string(updateJson), workflowId) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=workflowAdmin.saveImportWorkFlowErr||err=%v", err) - return responseInfo - } - return responseInfo - -} - -func SaveWorkFlowChart(saveData *idl.ChartG6, workflowId int64, operator string) *idl.ResponseInfo { - responseInfo := &idl.ResponseInfo{ - Tag: true, - ErrMsg: "", - } - workflowChart := formatChartG6(saveData) - - updateJson, err := json.Marshal(workflowChart) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=workflowAdmin.saveWorkFlowErr||err=%v", err) - return responseInfo - } - err = SaveUpdateJson(string(updateJson), workflowId) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadDBFail, "etype=workflowAdmin.saveWorkFlowErr||err=%v", err) - return responseInfo - } - return responseInfo -} -func formatChartG6(saveData *idl.ChartG6) *idl.WorkflowChart { - nodes := saveData.Nodes - edges := saveData.Edges - workflowChart := new(idl.WorkflowChart) - actionMap := make(map[string]*idl.Action) - typeMap := map[string]string{"rect": "task", "diamond": "condition", "circle": "flow", "clock": "timeout"} - for _, node := range nodes { - action := idl.Action{ - ActionType: typeMap[node.NodeType], - ActionId: node.Id, - ActionName: node.Label, - Params: node.Params, - Timeout: node.Timeout, - RefWorkflowId: node.RefWorkflowId, - } - deleteIndex := list.New() - for edgeIndex, edge := range edges { - if edge.Source == node.Id { - action.NextActionIds = append(action.NextActionIds, edge.Target) - if action.ActionType == "condition" { - action.NextConditions = append(action.NextConditions, edge.Label) - } - deleteIndex.PushBack(edgeIndex) - } - } - for i := deleteIndex.Back(); i != nil; i = i.Prev() { - index := i.Value.(int) - edges = append(edges[:index], edges[index+1:]...) - } - actionMap[node.Id] = &action - } - workflowChart.ActionMap = actionMap - return workflowChart - -} - -//更新workflow信息 -func SaveUpdateJson(updateJson string, workflowId int64) error { - ctx := context.TODO() - updateTime := time.Now() - updateModifySql := "update workflow set flow_chart = ?, update_time = ? where id = ?" - _, updateModifyErr := mysql.Handler.Exec(updateModifySql, updateJson, updateTime, workflowId) - - tlog.Handler.Infof(ctx, logs.DLTagProcessLog, "etype=workflowadmin.UpdateDBWorkFlow||data=%v||err=本条记录修改成功", updateJson) - return updateModifyErr -} diff --git a/tg-service/logic/workflowadmin/select_workflow_config.go b/tg-service/logic/workflowadmin/select_workflow_config.go deleted file mode 100644 index 8226c05..0000000 --- a/tg-service/logic/workflowadmin/select_workflow_config.go +++ /dev/null @@ -1,163 +0,0 @@ -package workflowadmin - -import ( - "context" - "database/sql" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "sort" - "strconv" - "strings" - "tg-service/common/logs" - "tg-service/common/template" - "tg-service/idl" - "tg-service/logic" - "time" -) - -//查询workflow数据 -func SelectWorkFlowConfig(selectData *idl.WorkFlowConfig, pageLimit int64, pageNum int64, useLimit bool) ([]*idl.WorkFlowConfig, error) { - sqlStr := "select id, dimension_id, experiment_id, modules,is_default,range1,range2,remark,create_time,update_time,operator,manual_slot_ids,group_name, flow_chart from workflow where status=1" - workFlowMap := make(map[int64]map[int64]*idl.WorkFlow) - err := new(error) - //查询数据库,根据前端传回的名称,获取到场景id,再查询数据库 - if strings.HasPrefix(selectData.SceneName, "全部") && len(strings.Split(selectData.SceneName, ",")) > 1 { - sceneNameList := strings.Split(selectData.SceneName, ",") - sqlStr += " and experiment_id in (" - for _, sceneName := range sceneNameList[1:] { - sqlStr += strconv.Itoa(template.SceneNameAndIdMap[sceneName]) + "," - } - sqlStr = sqlStr[:len(sqlStr)-1] - sqlStr += ") and dimension_id = ?" - - if useLimit { - sqlStr = logic.AddSelectLimit(sqlStr, pageLimit, pageNum) - } - - workFlowMap, *err = SelectDBWorkFlow(sqlStr, -1, selectData.DimensionId) - } else { - sqlStr += " and experiment_id = ? and dimension_id = ?" - - if useLimit { - sqlStr = logic.AddSelectLimit(sqlStr, pageLimit, pageNum) - } - - workFlowMap, *err = SelectDBWorkFlow(sqlStr, template.SceneNameAndIdMap[selectData.SceneName], selectData.DimensionId) - } - - //组装返回结果 - workFlowDataList := Process(workFlowMap) - return workFlowDataList, *err -} - -//数据库查询 -func SelectDBWorkFlow(sqlStr string, sceneId int, dimensionId int64) (map[int64]map[int64]*idl.WorkFlow, error) { - workFlowMap := make(map[int64]map[int64]*idl.WorkFlow) - stmt, err := mysql.Handler.Prepare(sqlStr) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadExperimentWorkflowFail, "etype=workflowadmin.SelectWorkFlow||econtent=sql:%v||err=%v", sqlStr, err) - return workFlowMap, err - } - defer stmt.Close() - - var rows *sql.Rows - if sceneId == -1 { - rows, err = stmt.Query(dimensionId) - } else { - rows, err = stmt.Query(sceneId, dimensionId) - } - - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadExperimentWorkflowFail, "etype=workflowadmin.SelectWorkFlow||econtent=sql:%v||err=%v", sqlStr, err) - return workFlowMap, err - } - defer rows.Close() - - for rows.Next() { - var createTime time.Time - var updateTime time.Time - var workFlow = new(idl.WorkFlow) - err := rows.Scan(&workFlow.Id, &workFlow.DimensionId, &workFlow.ExperimentId, &workFlow.Modules, &workFlow.IsDefault, &workFlow.Range1, &workFlow.Range2, &workFlow.Remark, &createTime, &updateTime, &workFlow.Operator, &workFlow.ManualSlotIds, &workFlow.GroupName, &workFlow.FlowCharts) - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadExperimentWorkflowFail, "etype=workflowadmin.workflowrowsScan||econtent=rows:%v||err=%v", rows, err) - return workFlowMap, err - } - tempMap := workFlowMap[workFlow.ExperimentId] - if tempMap == nil { - tempMap = make(map[int64]*idl.WorkFlow) - workFlowMap[workFlow.ExperimentId] = tempMap - } - - //时间格式转化 - workFlow.CreateTime = createTime.Format(logic.TIME_FORMAT) - workFlow.UpdateTime = updateTime.Format(logic.TIME_FORMAT) - - tempMap[workFlow.Id] = workFlow - } - err = rows.Err() - if err != nil { - tlog.Handler.Errorf(context.TODO(), logs.DLTagLoadExperimentWorkflowFail, "etype=workflowadmin.workflowrowsErr||econtent=||err=%v", err) - return workFlowMap, err - } - return workFlowMap, nil -} - -//对查询的数据库数据整理 -func Process(workFlowMap map[int64]map[int64]*idl.WorkFlow) []*idl.WorkFlowConfig { - workFlowDataList := make([]*idl.WorkFlowConfig, 0) - if workFlowMap == nil || len(workFlowMap) <= 0 { - return workFlowDataList - } - - keys := make([]int, 0) - for key := range workFlowMap { - keys = append(keys, int(key)) - } - // 给key排序,从小到大 - sort.Sort(sort.IntSlice(keys)) - - for _, key := range keys { - tempMap := workFlowMap[int64(key)] - if len(tempMap) <= 0 { - continue - } - tempKeys := make([]int, 0) - for tempkey := range tempMap { - tempKeys = append(tempKeys, int(tempkey)) - } - // 给key排序,从小到大 - sort.Sort(sort.IntSlice(tempKeys)) - - for _, k := range tempKeys { - - value := tempMap[int64(k)] - - var data = new(idl.WorkFlowConfig) - sceneId, _ := strconv.Atoi(strconv.FormatInt(value.ExperimentId, 10)) - data.SceneName = template.SceneIdAndNameMap[sceneId] - data.SceneId = value.ExperimentId - data.DimensionId = value.DimensionId - data.WorkFlowId = strconv.FormatInt(value.Id, 10) - data.Modules = value.Modules - data.ShowModules = value.FlowCharts - data.Defult = template.DefultIdAndNameMap[value.IsDefault] - data.Remark = value.Remark - data.Range1 = value.Range1 - data.Range2 = value.Range2 - data.CreateTime = value.CreateTime - data.UpdateTime = value.UpdateTime - data.Operator = value.Operator - data.ManualSlotIds = value.ManualSlotIds - data.GroupName = value.GroupName - proportionStr := strings.Split(value.Range1, ",") - if len(proportionStr) == 1 && proportionStr[0] == "" { - data.Proportion = "0%" - } else { - data.Proportion = strconv.Itoa(len(proportionStr)) + "%" - } - workFlowDataList = append(workFlowDataList, data) - } - } - - return workFlowDataList -} diff --git a/tg-service/logic/workflowadmin/update_workflow.go b/tg-service/logic/workflowadmin/update_workflow.go deleted file mode 100644 index f98f0ae..0000000 --- a/tg-service/logic/workflowadmin/update_workflow.go +++ /dev/null @@ -1,160 +0,0 @@ -package workflowadmin - -import ( - "context" - "github.com/didi/tg-flow/tg-core/common/mysql" - "github.com/didi/tg-flow/tg-core/common/tlog" - "strconv" - "strings" - "tg-service/common/logs" - "tg-service/common/template" - "tg-service/idl" - "tg-service/logic" - "tg-service/logic/sceneadmin" - "time" -) - -//更新workflow信息 -func UpdateDBWorkFlow(modifyData *idl.WorkFlowConfig, responseInfo *idl.ResponseInfo) { - var flowType int64 - flowType = -1 - if modifyData.SceneId != 0 { - sceneData := &idl.SceneConfig{ - Id: modifyData.SceneId, - } - sceneConfigList := sceneadmin.SelectSceneConf(sceneData, -1, 0, false) - if len(sceneConfigList) != 0 { - flowType = sceneConfigList[0].FlowType - } - } - ctx := context.TODO() - //1、先将请求记录中的流量占比取出来,第2步中会用数据库中的占比离散数字字符串,替换Proportion属性值 - proportion, _ := strconv.Atoi(modifyData.Proportion) - //2、查询对应主流量、数据库中该流量的信息和不包括该非主流量的,其他非主流量的总占比 和总占比值 - workFlow, thismodifyWorkFlow, haveUseProportion, _, err := SelectDBMainAndThisWorkFlow(modifyData) - if err != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = "修改数据失败:" + err.Error() - return - } - if workFlow == nil && thismodifyWorkFlow == nil { - responseInfo.Tag = false - responseInfo.ErrMsg = "查询数据库失败,导致更新操作失败,请重新更新!" - return - } - - if proportion > (100-haveUseProportion) && flowType == 0 { - responseInfo.Tag = false - responseInfo.ErrMsg = "修改的占比,超过了可分配占比,可分配占比 = " + strconv.Itoa(100-haveUseProportion) - return - } - - nowTime := time.Now().Format("2006-01-02 15:04:05") - - //3、表示要修改的是主流量 - if modifyData.OldWorkFlowId == workFlow.WorkFlowId { - sql := "update workflow set id=?,modules =?,remark =?,update_time =?,operator=?,manual_slot_ids=?,group_name=?,flow_chart=? where id = ?" - _, updateMainErr := mysql.Handler.Exec(sql, modifyData.WorkFlowId, modifyData.Modules, modifyData.Remark, nowTime, modifyData.Operator, modifyData.ManualSlotIds, modifyData.GroupName, modifyData.FlowChartJson, modifyData.OldWorkFlowId) - if updateMainErr != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = updateMainErr.Error() - } - tlog.Handler.Infof(ctx, logs.DLTagProcessLog, "etype=workflowadmin.UpdateDBWorkFlow||data=%v||err=主流量修改成功", modifyData) - return - } - - //4、该流量对应的数据库中的数据 - dbMainWorkFlowRangeArry := strings.Split(workFlow.Proportion, ",") - dbThismodifyWorkFlowRangeArry := strings.Split(thismodifyWorkFlow.Proportion, ",") - - thisModifyWorkFlowRang1 := "" //修改后的该条非主流量占比 - MainWorkFlowRang1 := "" //修改后的主流量调整后的占比 - dbThismodifyWorkFlowRangeArryLen := 0 //该条非主流量,数据库原始占比离散数字个数 - - if thismodifyWorkFlow.Proportion != "" { - dbThismodifyWorkFlowRangeArryLen = len(dbThismodifyWorkFlowRangeArry) - } - - //5、对流量占比,未做修改 - if dbThismodifyWorkFlowRangeArryLen == proportion { - MainWorkFlowRang1 = workFlow.Proportion - thisModifyWorkFlowRang1 = thismodifyWorkFlow.Proportion - } else if dbThismodifyWorkFlowRangeArryLen < proportion { - //6、新增了流量占比,新增部分,需要从主流量占比值中随机抽取 - count := proportion - dbThismodifyWorkFlowRangeArryLen - addRangeStr, rangeStr := ModifyWorkFlowRange(dbMainWorkFlowRangeArry, count) - MainWorkFlowRang1 = rangeStr - - //将新增部分占比的离散数字,和原始的占比离散数字,按从小到大排序组合在一起 - if len(addRangeStr) <= 0 { - thisModifyWorkFlowRang1 = logic.SortStr(thismodifyWorkFlow.Proportion) - } else { - if thismodifyWorkFlow.Proportion == "" { - thisModifyWorkFlowRang1 = logic.SortStr(addRangeStr) - } else { - thisModifyWorkFlowRang1 = logic.SortStr(addRangeStr + "," + thismodifyWorkFlow.Proportion) - } - } - } else { - //7、减少了流量占比,从该流量占比值中随机抽取减少部分,且还原给主流量 - count := dbThismodifyWorkFlowRangeArryLen - proportion - addRangeStr, rangeStr := ModifyWorkFlowRange(dbThismodifyWorkFlowRangeArry, count) - thisModifyWorkFlowRang1 = rangeStr - if len(addRangeStr) <= 0 { - MainWorkFlowRang1 = logic.SortStr(workFlow.Proportion) - } else { - MainWorkFlowRang1 = logic.SortStr(addRangeStr + "," + workFlow.Proportion) - } - } - - //8、校验修改操作后的流量总占比,是否为100 - err = logic.CheckModifyOP(haveUseProportion, MainWorkFlowRang1, thisModifyWorkFlowRang1) - if err != nil && flowType == 0 { - responseInfo.Tag = false - responseInfo.ErrMsg = err.Error() - return - } - - //9、生成对应的区间表达式 - thisModifyWorkFlowRang2 := logic.CreateRangeStr(thisModifyWorkFlowRang1) - MainWorkFlowRang2 := logic.CreateRangeStr(MainWorkFlowRang1) - - //10、判断是否需要将原主流量变成副流量 - mainDefult := "1" - if thismodifyWorkFlow.Defult == "0" && modifyData.Defult == "主" { - mainDefult = "0" - } - - //11、更新需要修改的流量信息 - - //强校验,最终入库的流量比例是否和修改传入的流量比例一致 - var thisModifyWorkFlowRang1Array []string - if thisModifyWorkFlowRang1 != "" { - thisModifyWorkFlowRang1Array = strings.Split(thisModifyWorkFlowRang1, ",") - } - if len(thisModifyWorkFlowRang1Array) != proportion && flowType == 0 { - responseInfo.Tag = false - responseInfo.ErrMsg = "最终入库的流量比例 和 修改传入的流量比例 不一致,入库失败,请重新更新!" - return - } - - updateModifySql := "update workflow set id=?,range1 = ?,range2 = ?,is_default = ?,modules =?,remark =?,update_time =?,operator=?,manual_slot_ids=?,group_name=?,flow_chart=? where id = ?" - _, updateModifyErr := mysql.Handler.Exec(updateModifySql, modifyData.WorkFlowId, thisModifyWorkFlowRang1, thisModifyWorkFlowRang2, template.DefultNameAndIdMap[modifyData.Defult], modifyData.Modules, modifyData.Remark, nowTime, modifyData.Operator, modifyData.ManualSlotIds, modifyData.GroupName, modifyData.FlowChartJson, modifyData.OldWorkFlowId) - if updateModifyErr != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = updateModifyErr.Error() - return - } - - tlog.Handler.Infof(ctx, logs.DLTagProcessLog, "etype=workflowadmin.UpdateDBWorkFlow||data=%v||Rang1=%v||Rang2=%v||err=本条记录修改成功", modifyData, thisModifyWorkFlowRang1, thisModifyWorkFlowRang2) - - //12、更新主流量信息 - updateMainSql := "update workflow set range1 = ?,range2 = ?,is_default = ?,update_time =?,operator=?,manual_slot_ids=? where id = ?" - _, updateMainErr := mysql.Handler.Exec(updateMainSql, MainWorkFlowRang1, MainWorkFlowRang2, mainDefult, nowTime, modifyData.Operator, workFlow.ManualSlotIds, workFlow.WorkFlowId) - if updateMainErr != nil { - responseInfo.Tag = false - responseInfo.ErrMsg = updateMainErr.Error() - return - } - tlog.Handler.Infof(ctx, logs.DLTagProcessLog, "etype=workflowadmin.UpdateDBWorkFlow||Rang1=%v||Rang2=%v||err=主流量修改成功", MainWorkFlowRang1, MainWorkFlowRang2) -} diff --git a/tg-service/main.go b/tg-service/main.go deleted file mode 100644 index 9a8e822..0000000 --- a/tg-service/main.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "github.com/didi/tg-flow/tg-core/common/httpserv" - "log" - "tg-service/conf" - "tg-service/cron" -) - -func main() { - //初始化配置 - conf.InitConfig() - - //启动定时任务 - cron.StartCronTask() - - //启动服务监听 - httpserv.Handler.Run() - - log.Println("Finished!") -} diff --git a/tg-web/CHANGELOG.md b/tg-web/CHANGELOG.md deleted file mode 100644 index e69de29..0000000 diff --git a/tg-web/LICENSE b/tg-web/LICENSE deleted file mode 100644 index 6f6a7ea..0000000 --- a/tg-web/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 lyt-Top - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/tg-web/README.md b/tg-web/README.md deleted file mode 100644 index 58d0262..0000000 --- a/tg-web/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# tg-admin - -#### 🌈 介绍 - -tg-flow前端 - -#### 🏭 环境支持 - -| Edge | Firefox | Chrome | Safari | -| --------- | ------------ | ----------- | ----------- | -| Edge ≥ 79 | Firefox ≥ 78 | Chrome ≥ 64 | Safari ≥ 12 | - - -#### ⚡ 使用说明 - -# 安装依赖 -npm install - -# 运行项目 -npm run dev - -# 打包发布 -npm run build -``` - -## Contribution - -Node 依赖: - -* `node>=12.0.0` -* `npm>=6.0.0` diff --git a/tg-web/build-exclude-list.txt b/tg-web/build-exclude-list.txt deleted file mode 100644 index 3e4e48b..0000000 --- a/tg-web/build-exclude-list.txt +++ /dev/null @@ -1 +0,0 @@ -.gitignore \ No newline at end of file diff --git a/tg-web/env/.env b/tg-web/env/.env deleted file mode 100644 index 812bae3..0000000 --- a/tg-web/env/.env +++ /dev/null @@ -1,2 +0,0 @@ -VITE_API_BASE_URL=http://127.0.0.1:7001/api/ -VITE_TITLE=tg-flow \ No newline at end of file diff --git a/tg-web/env/.env.alpha b/tg-web/env/.env.alpha deleted file mode 100644 index 6968b60..0000000 --- a/tg-web/env/.env.alpha +++ /dev/null @@ -1,2 +0,0 @@ -VITE_API_BASE_URL=/trajectory/api/ -VITE_PUBLIC_PATH=/trajectory/ \ No newline at end of file diff --git a/tg-web/env/.env.pre b/tg-web/env/.env.pre deleted file mode 100644 index 8cbffa9..0000000 --- a/tg-web/env/.env.pre +++ /dev/null @@ -1,2 +0,0 @@ -VITE_API_BASE_URL=/api/ -VITE_TITLE=tg-flow \ No newline at end of file diff --git a/tg-web/env/.env.production b/tg-web/env/.env.production deleted file mode 100644 index 5b02e97..0000000 --- a/tg-web/env/.env.production +++ /dev/null @@ -1 +0,0 @@ -VITE_API_BASE_URL=/api/ \ No newline at end of file diff --git a/tg-web/index.html b/tg-web/index.html deleted file mode 100644 index 26e03d5..0000000 --- a/tg-web/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - tg-flow - - -
- - - diff --git a/tg-web/package-lock.json b/tg-web/package-lock.json deleted file mode 100644 index 024e000..0000000 --- a/tg-web/package-lock.json +++ /dev/null @@ -1,3943 +0,0 @@ -{ - "name": "vue-next-admin", - "version": "2.2.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@ant-design/colors": { - "version": "4.0.5", - "resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-4.0.5.tgz", - "integrity": "sha512-3mnuX2prnWOWvpFTS2WH2LoouWlOgtnIpc6IarWN6GOzzLF8dW/U8UctuvIPhoboETehZfJ61XP+CGakBEPJ3Q==", - "requires": { - "tinycolor2": "^1.4.1" - } - }, - "@antv/algorithm": { - "version": "0.1.25", - "resolved": "https://registry.npmmirror.com/@antv/algorithm/-/algorithm-0.1.25.tgz", - "integrity": "sha512-TGwPyFoAu4+iEJd0y1l0gHdBXCbUj8a4gR7P3GzZRfEfRnWfk+gswApzOSTd7c6HP402JOEF64PAJQKxQgSPSQ==", - "requires": { - "@antv/util": "^2.0.13", - "tslib": "^2.0.0" - } - }, - "@antv/dom-util": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/@antv/dom-util/-/dom-util-2.0.4.tgz", - "integrity": "sha512-2shXUl504fKwt82T3GkuT4Uoc6p9qjCKnJ8gXGLSW4T1W37dqf9AV28aCfoVPHp2BUXpSsB+PAJX2rG/jLHsLQ==", - "requires": { - "tslib": "^2.0.3" - } - }, - "@antv/event-emitter": { - "version": "0.1.3", - "resolved": "https://registry.npmmirror.com/@antv/event-emitter/-/event-emitter-0.1.3.tgz", - "integrity": "sha512-4ddpsiHN9Pd4UIlWuKVK1C4IiZIdbwQvy9i7DUSI3xNJ89FPUFt8lxDYj8GzzfdllV0NkJTRxnG+FvLk0llidg==" - }, - "@antv/g-base": { - "version": "0.5.14", - "resolved": "https://registry.npmmirror.com/@antv/g-base/-/g-base-0.5.14.tgz", - "integrity": "sha512-Wyx+ddatDdQBjidLHXmV3NgKp1oiyNZNX9gGflaBFDfGgywnvs85bXnKswayFXsFBg5TQ6Goi8SnBufEqwemgg==", - "requires": { - "@antv/event-emitter": "^0.1.1", - "@antv/g-math": "^0.1.9", - "@antv/matrix-util": "^3.1.0-beta.1", - "@antv/path-util": "~2.0.5", - "@antv/util": "~2.0.13", - "@types/d3-timer": "^2.0.0", - "d3-ease": "^1.0.5", - "d3-interpolate": "^3.0.1", - "d3-timer": "^1.0.9", - "detect-browser": "^5.1.0", - "tslib": "^2.0.3" - }, - "dependencies": { - "d3-timer": { - "version": "1.0.10", - "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-1.0.10.tgz", - "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" - } - } - }, - "@antv/g-canvas": { - "version": "0.5.13", - "resolved": "https://registry.npmmirror.com/@antv/g-canvas/-/g-canvas-0.5.13.tgz", - "integrity": "sha512-nu6wNeZhYomkEks2aniWlYML0ZGb9t5PGzjiOIp+B4z4HUEUvHOTdIPNfinzl5+4QC7fVZntsQKZK5dBFO5MDQ==", - "requires": { - "@antv/g-base": "^0.5.12", - "@antv/g-math": "^0.1.9", - "@antv/matrix-util": "^3.1.0-beta.1", - "@antv/path-util": "~2.0.5", - "@antv/util": "~2.0.0", - "gl-matrix": "^3.0.0", - "tslib": "^2.0.3" - } - }, - "@antv/g-math": { - "version": "0.1.9", - "resolved": "https://registry.npmmirror.com/@antv/g-math/-/g-math-0.1.9.tgz", - "integrity": "sha512-KHMSfPfZ5XHM1PZnG42Q2gxXfOitYveNTA7L61lR6mhZ8Y/aExsYmHqaKBsSarU0z+6WLrl9C07PQJZaw0uljQ==", - "requires": { - "@antv/util": "~2.0.0", - "gl-matrix": "^3.0.0" - } - }, - "@antv/g-svg": { - "version": "0.5.7", - "resolved": "https://registry.npmmirror.com/@antv/g-svg/-/g-svg-0.5.7.tgz", - "integrity": "sha512-jUbWoPgr4YNsOat2Y/rGAouNQYGpw4R0cvlN0YafwOyacFFYy2zC8RslNd6KkPhhR3XHNSqJOuCYZj/YmLUwYw==", - "requires": { - "@antv/g-base": "^0.5.12", - "@antv/g-math": "^0.1.9", - "@antv/util": "~2.0.0", - "detect-browser": "^5.0.0", - "tslib": "^2.0.3" - } - }, - "@antv/g-webgpu": { - "version": "0.7.2", - "resolved": "https://registry.npmmirror.com/@antv/g-webgpu/-/g-webgpu-0.7.2.tgz", - "integrity": "sha512-kw+oYGsdvj5qeUfy5DPb/jztZBV+2fmqBd3Vv8NlKatfBmv8AirYX/CCW74AUSdWm99rEiLyxFB1VdRZ6b/wnQ==", - "requires": { - "@antv/g-webgpu-core": "^0.7.2", - "@antv/g-webgpu-engine": "^0.7.2", - "gl-matrix": "^3.1.0", - "gl-vec2": "^1.3.0", - "lodash": "^4.17.15" - } - }, - "@antv/g-webgpu-core": { - "version": "0.7.2", - "resolved": "https://registry.npmmirror.com/@antv/g-webgpu-core/-/g-webgpu-core-0.7.2.tgz", - "integrity": "sha512-xUMmop7f3Rs34zFYKXLqHhDR1CQTeDl/7vI7Sn3X/73BqJc3X3HIIRvm83Fg2CjVACaOzw4WeLRXNaOCp9fz9w==", - "requires": { - "eventemitter3": "^4.0.0", - "gl-matrix": "^3.1.0", - "lodash": "^4.17.15", - "probe.gl": "^3.1.1" - } - }, - "@antv/g-webgpu-engine": { - "version": "0.7.2", - "resolved": "https://registry.npmmirror.com/@antv/g-webgpu-engine/-/g-webgpu-engine-0.7.2.tgz", - "integrity": "sha512-lx8Y93IW2cnJvdoDRKyMmTdYqSC1pOmF0nyG3PGGyA0NI9vBYVgO0KTF6hkyWjdTWVq7XDZyf/h8CJridLh3lg==", - "requires": { - "@antv/g-webgpu-core": "^0.7.2", - "gl-matrix": "^3.1.0", - "lodash": "^4.17.15", - "regl": "^1.3.11" - } - }, - "@antv/g6": { - "version": "4.8.8", - "resolved": "https://registry.npmmirror.com/@antv/g6/-/g6-4.8.8.tgz", - "integrity": "sha512-xJEOZX5YkUrHZ4b6zMAfKDEgJZ6DjNaanqMpKmzvbuQ2iUq2H3le4Y1OgrXCebusqYZC+GwZA4V26DCc3ei6zw==", - "requires": { - "@antv/g6-pc": "0.8.8" - } - }, - "@antv/g6-core": { - "version": "0.8.8", - "resolved": "https://registry.npmmirror.com/@antv/g6-core/-/g6-core-0.8.8.tgz", - "integrity": "sha512-cWFoxGIyscl0OZSCGmyILBFy6xd527LcVJMmpIKgJ0E3lT37b8NHDHRbar0GLq7sdyBj2cHVOWtCHjA99FyZ6g==", - "requires": { - "@antv/algorithm": "^0.1.8", - "@antv/dom-util": "^2.0.1", - "@antv/event-emitter": "~0.1.0", - "@antv/g-base": "^0.5.1", - "@antv/g-math": "^0.1.1", - "@antv/matrix-util": "^3.1.0-beta.3", - "@antv/path-util": "^2.0.3", - "@antv/util": "~2.0.5", - "ml-matrix": "^6.5.0", - "tslib": "^2.1.0" - } - }, - "@antv/g6-element": { - "version": "0.8.8", - "resolved": "https://registry.npmmirror.com/@antv/g6-element/-/g6-element-0.8.8.tgz", - "integrity": "sha512-0EOr2yJVXOqR4uFeY9K+m2bAuq3eNntnAWRa1GYUbtrjBh7N/wTn3l+Xl3RZC5Ch1dAFgxQMX4n1+PGvcCRcJA==", - "requires": { - "@antv/g-base": "^0.5.1", - "@antv/g6-core": "0.8.8", - "@antv/util": "~2.0.5" - } - }, - "@antv/g6-pc": { - "version": "0.8.8", - "resolved": "https://registry.npmmirror.com/@antv/g6-pc/-/g6-pc-0.8.8.tgz", - "integrity": "sha512-hemwe0cyjExdQiAOpzwmUchrp1/qVEHNHru9O9TqtnXlFM0VrRyPKCp3cL+Yv8N25JseOp1fboEmhMuPFeqN3Q==", - "requires": { - "@ant-design/colors": "^4.0.5", - "@antv/algorithm": "^0.1.8", - "@antv/dom-util": "^2.0.1", - "@antv/event-emitter": "~0.1.0", - "@antv/g-base": "^0.5.1", - "@antv/g-canvas": "^0.5.2", - "@antv/g-math": "^0.1.1", - "@antv/g-svg": "^0.5.1", - "@antv/g6-core": "0.8.8", - "@antv/g6-element": "0.8.8", - "@antv/g6-plugin": "^0.8.7", - "@antv/hierarchy": "^0.6.10", - "@antv/layout": "^0.3.0", - "@antv/matrix-util": "^3.1.0-beta.3", - "@antv/path-util": "^2.0.3", - "@antv/util": "~2.0.5", - "color": "^3.1.3", - "d3-force": "^2.0.1", - "dagre": "^0.8.5", - "insert-css": "^2.0.0", - "ml-matrix": "^6.5.0" - } - }, - "@antv/g6-plugin": { - "version": "0.8.8", - "resolved": "https://registry.npmmirror.com/@antv/g6-plugin/-/g6-plugin-0.8.8.tgz", - "integrity": "sha512-wc2oTAKUaCzpHzhDxYEdkTVeWa6D6tILWAcheW5r6EabEny9gg34yWTzf+wbRp8kbWWLRNNnRTkMV8mV5LSH5g==", - "requires": { - "@antv/dom-util": "^2.0.2", - "@antv/g-base": "^0.5.1", - "@antv/g-canvas": "^0.5.2", - "@antv/g-svg": "^0.5.2", - "@antv/g6-core": "0.8.8", - "@antv/g6-element": "0.8.8", - "@antv/matrix-util": "^3.1.0-beta.3", - "@antv/path-util": "^2.0.3", - "@antv/scale": "^0.3.4", - "@antv/util": "^2.0.9", - "insert-css": "^2.0.0" - } - }, - "@antv/graphlib": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/@antv/graphlib/-/graphlib-1.2.0.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2F%40antv%2Fgraphlib%2F-%2Fgraphlib-1.2.0.tgz", - "integrity": "sha1-yI+X1LNFbSYUgKEgf/xPvF19zw0=" - }, - "@antv/hierarchy": { - "version": "0.6.11", - "resolved": "https://registry.npmmirror.com/@antv/hierarchy/-/hierarchy-0.6.11.tgz", - "integrity": "sha512-RJVhEMCuu4vj+Dt25lXIiNdd7jaqm/fqWGYikiELha4S5tnzdJoTUaUvvpfWlxLx4B0RsS9XRwBs1bOKN71TKg==", - "requires": { - "@antv/util": "^2.0.7" - } - }, - "@antv/layout": { - "version": "0.3.17", - "resolved": "https://registry.npmmirror.com/@antv/layout/-/layout-0.3.17.tgz", - "integrity": "sha512-KYqnTkqQ1Dddv57yM39FVR//OHFituWJ6Fjhip4VqIYsD07RIvo4g6gMRRqVwEZufGJjgdYJKVn4xIEh/8Ezhw==", - "requires": { - "@antv/g-webgpu": "0.7.2", - "@antv/graphlib": "^1.0.0", - "d3-force": "^2.1.1", - "d3-quadtree": "^2.0.0", - "dagre-compound": "^0.0.11", - "ml-matrix": "^6.5.0" - } - }, - "@antv/matrix-util": { - "version": "3.1.0-beta.3", - "resolved": "https://registry.npmmirror.com/@antv/matrix-util/-/matrix-util-3.1.0-beta.3.tgz", - "integrity": "sha512-W2R6Za3A6CmG51Y/4jZUM/tFgYSq7vTqJL1VD9dKrvwxS4sE0ZcXINtkp55CdyBwJ6Cwm8pfoRpnD4FnHahN0A==", - "requires": { - "@antv/util": "^2.0.9", - "gl-matrix": "^3.4.3", - "tslib": "^2.0.3" - } - }, - "@antv/path-util": { - "version": "2.0.15", - "resolved": "https://registry.npmmirror.com/@antv/path-util/-/path-util-2.0.15.tgz", - "integrity": "sha512-R2VLZ5C8PLPtr3VciNyxtjKqJ0XlANzpFb5sE9GE61UQqSRuSVSzIakMxjEPrpqbgc+s+y8i+fmc89Snu7qbNw==", - "requires": { - "@antv/matrix-util": "^3.0.4", - "@antv/util": "^2.0.9", - "tslib": "^2.0.3" - }, - "dependencies": { - "@antv/matrix-util": { - "version": "3.0.4", - "resolved": "https://registry.npmmirror.com/@antv/matrix-util/-/matrix-util-3.0.4.tgz", - "integrity": "sha512-BAPyu6dUliHcQ7fm9hZSGKqkwcjEDVLVAstlHULLvcMZvANHeLXgHEgV7JqcAV/GIhIz8aZChIlzM1ZboiXpYQ==", - "requires": { - "@antv/util": "^2.0.9", - "gl-matrix": "^3.3.0", - "tslib": "^2.0.3" - } - } - } - }, - "@antv/scale": { - "version": "0.3.18", - "resolved": "https://registry.npmmirror.com/@antv/scale/-/scale-0.3.18.tgz", - "integrity": "sha512-GHwE6Lo7S/Q5fgaLPaCsW+CH+3zl4aXpnN1skOiEY0Ue9/u+s2EySv6aDXYkAqs//i0uilMDD/0/4n8caX9U9w==", - "requires": { - "@antv/util": "~2.0.3", - "fecha": "~4.2.0", - "tslib": "^2.0.0" - } - }, - "@antv/util": { - "version": "2.0.17", - "resolved": "https://registry.npmmirror.com/@antv/util/-/util-2.0.17.tgz", - "integrity": "sha512-o6I9hi5CIUvLGDhth0RxNSFDRwXeywmt6ExR4+RmVAzIi48ps6HUy+svxOCayvrPBN37uE6TAc2KDofRo0nK9Q==", - "requires": { - "csstype": "^3.0.8", - "tslib": "^2.0.3" - }, - "dependencies": { - "csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" - } - } - }, - "@antv/x6": { - "version": "2.9.4", - "resolved": "https://registry.npmmirror.com/@antv/x6/-/x6-2.9.4.tgz", - "integrity": "sha512-Q9AuU6gGnTF8zFFGzpH2q6kvO5giPJpXQdgDp2mQGpu2DPm8howovtXCWRKCfDx8WJ8ccNbZ3RrzusN8kk6iYA==", - "requires": { - "@antv/x6-common": "^2.0.11", - "@antv/x6-geometry": "^2.0.4", - "utility-types": "^3.10.0" - } - }, - "@antv/x6-common": { - "version": "2.0.11", - "resolved": "https://registry.npmmirror.com/@antv/x6-common/-/x6-common-2.0.11.tgz", - "integrity": "sha512-tSs1fa+xAE1Ty0ecu3GtvQh482CbeuTaHJjuXXfKHjaUoAjI7I5qOe/h4BFF0SnjQSxqHj+HyBT4aXsgNmJ8lg==", - "requires": { - "lodash-es": "^4.17.15", - "utility-types": "^3.10.0" - } - }, - "@antv/x6-geometry": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/@antv/x6-geometry/-/x6-geometry-2.0.4.tgz", - "integrity": "sha512-dlsLNRRTiCw8TsJ/03j3y5IbbbK4Npf6zBH6kAx8nnW+edNT/TAOChvPRzizwD+vA7wY9Rp2AiRnp/vCdIYC5w==" - }, - "@babel/parser": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.9.tgz", - "integrity": "sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==" - }, - "@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@codemirror/autocomplete": { - "version": "6.4.2", - "resolved": "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.4.2.tgz", - "integrity": "sha512-8WE2xp+D0MpWEv5lZ6zPW1/tf4AGb358T5GWYiKEuCP8MvFfT3tH2mIF9Y2yr2e3KbHuSvsVhosiEyqCpiJhZQ==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.6.0", - "@lezer/common": "^1.0.0" - } - }, - "@codemirror/commands": { - "version": "6.2.1", - "resolved": "https://registry.npmmirror.com/@codemirror/commands/-/commands-6.2.1.tgz", - "integrity": "sha512-FFiNKGuHA5O8uC6IJE5apI5rT9gyjlw4whqy4vlcX0wE/myxL6P1s0upwDhY4HtMWLOwzwsp0ap3bjdQhvfDOA==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.2.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" - } - }, - "@codemirror/lang-javascript": { - "version": "6.1.4", - "resolved": "https://registry.npmmirror.com/@codemirror/lang-javascript/-/lang-javascript-6.1.4.tgz", - "integrity": "sha512-OxLf7OfOZBTMRMi6BO/F72MNGmgOd9B0vetOLvHsDACFXayBzW8fm8aWnDM0yuy68wTK03MBf4HbjSBNRG5q7A==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.6.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/javascript": "^1.0.0" - } - }, - "@codemirror/language": { - "version": "6.6.0", - "resolved": "https://registry.npmmirror.com/@codemirror/language/-/language-6.6.0.tgz", - "integrity": "sha512-cwUd6lzt3MfNYOobdjf14ZkLbJcnv4WtndYaoBkbor/vF+rCNguMPK0IRtvZJG4dsWiaWPcK8x1VijhvSxnstg==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - }, - "@codemirror/lint": { - "version": "6.2.0", - "resolved": "https://registry.npmmirror.com/@codemirror/lint/-/lint-6.2.0.tgz", - "integrity": "sha512-KVCECmR2fFeYBr1ZXDVue7x3q5PMI0PzcIbA+zKufnkniMBo1325t0h1jM85AKp8l3tj67LRxVpZfgDxEXlQkg==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "crelt": "^1.0.5" - } - }, - "@codemirror/search": { - "version": "6.3.0", - "resolved": "https://registry.npmmirror.com/@codemirror/search/-/search-6.3.0.tgz", - "integrity": "sha512-rBhZxzT34CarfhgCZGhaLBScABDN3iqJxixzNuINp9lrb3lzm0nTpR77G1VrxGO3HOGK7j62jcJftQM7eCOIuw==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "crelt": "^1.0.5" - } - }, - "@codemirror/state": { - "version": "6.2.0", - "resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.2.0.tgz", - "integrity": "sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA==" - }, - "@codemirror/theme-one-dark": { - "version": "6.1.1", - "resolved": "https://registry.npmmirror.com/@codemirror/theme-one-dark/-/theme-one-dark-6.1.1.tgz", - "integrity": "sha512-+CfzmScfJuD6uDF5bHJkAjWTQ2QAAHxODCPxUEgcImDYcJLT+4l5vLnBHmDVv46kCC5uUJGMrBJct2Z6JbvqyQ==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/highlight": "^1.0.0" - } - }, - "@codemirror/view": { - "version": "6.9.1", - "resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.9.1.tgz", - "integrity": "sha512-bzfSjJn9dAADVpabLKWKNmMG4ibyTV2e3eOGowjElNPTdTkSbi6ixPYHm2u0ADcETfKsi2/R84Rkmi91dH9yEg==", - "requires": { - "@codemirror/state": "^6.1.4", - "style-mod": "^4.0.0", - "w3c-keyname": "^2.2.4" - } - }, - "@css-render/plugin-bem": { - "version": "0.15.12", - "resolved": "https://registry.npmmirror.com/@css-render/plugin-bem/-/plugin-bem-0.15.12.tgz", - "integrity": "sha512-Lq2jSOZn+wYQtsyaFj6QRz2EzAnd3iW5fZeHO1WSXQdVYwvwGX0ZiH3X2JQgtgYLT1yeGtrwrqJdNdMEUD2xTw==" - }, - "@css-render/vue3-ssr": { - "version": "0.15.12", - "resolved": "https://registry.npmmirror.com/@css-render/vue3-ssr/-/vue3-ssr-0.15.12.tgz", - "integrity": "sha512-AQLGhhaE0F+rwybRCkKUdzBdTEM/5PZBYy+fSYe1T9z9+yxMuV/k7ZRqa4M69X+EI1W8pa4kc9Iq2VjQkZx4rg==" - }, - "@ctrl/tinycolor": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz", - "integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==" - }, - "@element-plus/icons-vue": { - "version": "2.0.9", - "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.0.9.tgz", - "integrity": "sha512-okdrwiVeKBmW41Hkl0eMrXDjzJwhQMuKiBOu17rOszqM+LS/yBYpNQNV5Jvoh06Wc+89fMmb/uhzf8NZuDuUaQ==" - }, - "@emotion/hash": { - "version": "0.8.0", - "resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" - }, - "@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - } - }, - "@floating-ui/core": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz", - "integrity": "sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==" - }, - "@floating-ui/dom": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz", - "integrity": "sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==", - "requires": { - "@floating-ui/core": "^0.7.3" - } - }, - "@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@interactjs/actions": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/actions/-/actions-1.10.17.tgz", - "integrity": "sha512-wyB1ZqpaZy5gmz6VDqK9KWh98xKnFgL7VyLvxHODFi9V0IYX4HJAAOBlhtfze0D1R1f1cY+gqPDK+dLaHMlE+w==", - "requires": { - "@interactjs/interact": "1.10.17" - } - }, - "@interactjs/auto-scroll": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/auto-scroll/-/auto-scroll-1.10.17.tgz", - "integrity": "sha512-IQcW7N3xOaoL8RnAGOGMk0Y2gue7L4S3BT6Id4VBBu8so163DtLiZVW6jXu9rKVntzbluaAeqNZlfAVyu3kIWg==", - "requires": { - "@interactjs/interact": "1.10.17" - } - }, - "@interactjs/auto-start": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/auto-start/-/auto-start-1.10.17.tgz", - "integrity": "sha512-qYVxhAbYnwxjD/NLEegUoAST7WASJ4VmWNjsyWRx/js5Op+I4E2zteARIeZGgrutcGIXMCcQzhCMgE3PjOpbpw==", - "requires": { - "@interactjs/interact": "1.10.17" - } - }, - "@interactjs/core": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/core/-/core-1.10.17.tgz", - "integrity": "sha512-rL9w+83HDRuXub8Ezqs+97CYLl/ne7bLT/sAeduUWaxYhsW9iOqBoob9JnkkCZOaOsYizWI1EWy0+fNc5ibtLQ==" - }, - "@interactjs/dev-tools": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/dev-tools/-/dev-tools-1.10.17.tgz", - "integrity": "sha512-Oi9nEw3FfSwkNmW+V0WwdHqvzEkVHc24mH1v5EjRn60sqgrGLK9nTQ+NSuqcnUY8GxC3TkyuxnsOodxiadIRmA==", - "requires": { - "@interactjs/interact": "1.10.17" - } - }, - "@interactjs/inertia": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/inertia/-/inertia-1.10.17.tgz", - "integrity": "sha512-41vbYUjZIDCKt2/yhmjPrEW5+0uoL/hldFsll9pkvnLhmm12Xk0VXOlmR2zXKAmsTK3fJlKMyBYUX92qHLkyVQ==", - "requires": { - "@interactjs/interact": "1.10.17", - "@interactjs/offset": "1.10.17" - } - }, - "@interactjs/interact": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/interact/-/interact-1.10.17.tgz", - "integrity": "sha512-NyKsf8EFudvdahBjPz1Gt5QnynVwa/2LUfBc2/w8QOnOBiyzUm0HLloJSaB8a50QbQkSWN243/Lgpd8GTMQvuQ==", - "requires": { - "@interactjs/core": "1.10.17", - "@interactjs/types": "1.10.17", - "@interactjs/utils": "1.10.17" - } - }, - "@interactjs/interactjs": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/interactjs/-/interactjs-1.10.17.tgz", - "integrity": "sha512-hHmiukARbZhiM12zNKx0yQlFVl4C+NMeYNAYh6Mf9U3ZziQ47C+JEW8Gr7Zr/MxfNZyPu5nLKCpVQjh/JvBO9g==", - "requires": { - "@interactjs/actions": "1.10.17", - "@interactjs/auto-scroll": "1.10.17", - "@interactjs/auto-start": "1.10.17", - "@interactjs/core": "1.10.17", - "@interactjs/dev-tools": "1.10.17", - "@interactjs/inertia": "1.10.17", - "@interactjs/interact": "1.10.17", - "@interactjs/modifiers": "1.10.17", - "@interactjs/offset": "1.10.17", - "@interactjs/pointer-events": "1.10.17", - "@interactjs/reflow": "1.10.17", - "@interactjs/utils": "1.10.17" - } - }, - "@interactjs/modifiers": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/modifiers/-/modifiers-1.10.17.tgz", - "integrity": "sha512-Dxw8kv9VBIxnhNvQncR6CKAGMzKXczLvuAUIdSPFYtyerX/XiDulJUqhR+jVKNp/WjF1DvdBxWo0kGGLbM84LQ==", - "requires": { - "@interactjs/interact": "1.10.17", - "@interactjs/snappers": "1.10.17" - } - }, - "@interactjs/offset": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/offset/-/offset-1.10.17.tgz", - "integrity": "sha512-wWBnIQWgLrmJNTBbd/FdxHxAJjiXl/5ND8Jbw2DuP9gIGDxhFSdEt62Fgqimn9ICb8v8ycvSLObEmcvJF/8hQQ==", - "requires": { - "@interactjs/interact": "1.10.17" - } - }, - "@interactjs/pointer-events": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/pointer-events/-/pointer-events-1.10.17.tgz", - "integrity": "sha512-VsfluouEKb8QRGyH6jQATCW+QdAd/3dkENS7rj2m+EcVUhz2Ob5mpMRopjALi4pwltMowqTfuJ4LtwMSX2G29A==", - "requires": { - "@interactjs/interact": "1.10.17" - } - }, - "@interactjs/reflow": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/reflow/-/reflow-1.10.17.tgz", - "integrity": "sha512-ncpWP5k93FRQptEhjzPZsbuRRajd4rkW17lDavCrEjrDi/LHnYekWGqZTaFzfJ80n1x8xUm9ujDjxCTylNqEIA==", - "requires": { - "@interactjs/interact": "1.10.17" - } - }, - "@interactjs/snappers": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/snappers/-/snappers-1.10.17.tgz", - "integrity": "sha512-m753DGsNOts797e3zDT6wqELoc+BlpIC1w+TyMyISRxU6n1RlS8Q6LHBGgwAgV79LHLaahv/a5haFF9H1VG0FQ==", - "requires": { - "@interactjs/interact": "1.10.17" - } - }, - "@interactjs/types": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.17.tgz", - "integrity": "sha512-X2JpoM7xUw0p9Me0tMaI0HNfcF/Hd07ZZlzpnpEMpGerUZOLoyeThrV9P+CrBHxZrluWJrigJbcdqXliFd0YMA==" - }, - "@interactjs/utils": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@interactjs/utils/-/utils-1.10.17.tgz", - "integrity": "sha512-sZAW08CkqgvqRjUIaLRjScjObcCzN9D75yekLA21EClYAZIhi4A+GEt2z/WqOCOksTaEPLYmQyhkpXcboc0LhQ==" - }, - "@intlify/core-base": { - "version": "9.1.10", - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.10.tgz", - "integrity": "sha512-So9CNUavB/IsZ+zBmk2Cv6McQp6vc2wbGi1S0XQmJ8Vz+UFcNn9MFXAe9gY67PreIHrbLsLxDD0cwo1qsxM1Nw==", - "requires": { - "@intlify/devtools-if": "9.1.10", - "@intlify/message-compiler": "9.1.10", - "@intlify/message-resolver": "9.1.10", - "@intlify/runtime": "9.1.10", - "@intlify/shared": "9.1.10", - "@intlify/vue-devtools": "9.1.10" - } - }, - "@intlify/devtools-if": { - "version": "9.1.10", - "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.10.tgz", - "integrity": "sha512-SHaKoYu6sog3+Q8js1y3oXLywuogbH1sKuc7NSYkN3GElvXSBaMoCzW+we0ZSFqj/6c7vTNLg9nQ6rxhKqYwnQ==", - "requires": { - "@intlify/shared": "9.1.10" - } - }, - "@intlify/message-compiler": { - "version": "9.1.10", - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.10.tgz", - "integrity": "sha512-+JiJpXff/XTb0EadYwdxOyRTB0hXNd4n1HaJ/a4yuV960uRmPXaklJsedW0LNdcptd/hYUZtCkI7Lc9J5C1gxg==", - "requires": { - "@intlify/message-resolver": "9.1.10", - "@intlify/shared": "9.1.10", - "source-map": "0.6.1" - } - }, - "@intlify/message-resolver": { - "version": "9.1.10", - "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.10.tgz", - "integrity": "sha512-5YixMG/M05m0cn9+gOzd4EZQTFRUu8RGhzxJbR1DWN21x/Z3bJ8QpDYj6hC4FwBj5uKsRfKpJQ3Xqg98KWoA+w==" - }, - "@intlify/runtime": { - "version": "9.1.10", - "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.10.tgz", - "integrity": "sha512-7QsuByNzpe3Gfmhwq6hzgXcMPpxz8Zxb/XFI6s9lQdPLPe5Lgw4U1ovRPZTOs6Y2hwitR3j/HD8BJNGWpJnOFA==", - "requires": { - "@intlify/message-compiler": "9.1.10", - "@intlify/message-resolver": "9.1.10", - "@intlify/shared": "9.1.10" - } - }, - "@intlify/shared": { - "version": "9.1.10", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.10.tgz", - "integrity": "sha512-Om54xJeo1Vw+K1+wHYyXngE8cAbrxZHpWjYzMR9wCkqbhGtRV5VLhVc214Ze2YatPrWlS2WSMOWXR8JktX/IgA==" - }, - "@intlify/vue-devtools": { - "version": "9.1.10", - "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.10.tgz", - "integrity": "sha512-5l3qYARVbkWAkagLu1XbDUWRJSL8br1Dj60wgMaKB0+HswVsrR6LloYZTg7ozyvM621V6+zsmwzbQxbVQyrytQ==", - "requires": { - "@intlify/message-resolver": "9.1.10", - "@intlify/runtime": "9.1.10", - "@intlify/shared": "9.1.10" - } - }, - "@juggle/resize-observer": { - "version": "3.4.0", - "resolved": "https://registry.npmmirror.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", - "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" - }, - "@lezer/common": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.0.2.tgz", - "integrity": "sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng==" - }, - "@lezer/highlight": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.1.3.tgz", - "integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@lezer/javascript": { - "version": "1.4.1", - "resolved": "https://registry.npmmirror.com/@lezer/javascript/-/javascript-1.4.1.tgz", - "integrity": "sha512-Hqx36DJeYhKtdpc7wBYPR0XF56ZzIp0IkMO/zNNj80xcaFOV4Oj/P7TQc/8k2TxNhzl7tV5tXS8ZOCPbT4L3nA==", - "requires": { - "@lezer/highlight": "^1.1.3", - "@lezer/lr": "^1.3.0" - } - }, - "@lezer/lr": { - "version": "1.3.3", - "resolved": "https://registry.npmmirror.com/@lezer/lr/-/lr-1.3.3.tgz", - "integrity": "sha512-JPQe3mwJlzEVqy67iQiiGozhcngbO8QBgpqZM6oL1Wj/dXckrEexpBLeFkq0edtW5IqnPRFxA24BHJni8Js69w==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@probe.gl/env": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/@probe.gl/env/-/env-3.6.0.tgz", - "integrity": "sha512-4tTZYUg/8BICC3Yyb9rOeoKeijKbZHRXBEKObrfPmX4sQmYB15ZOUpoVBhAyJkOYVAM8EkPci6Uw5dLCwx2BEQ==", - "requires": { - "@babel/runtime": "^7.0.0" - } - }, - "@probe.gl/log": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/@probe.gl/log/-/log-3.6.0.tgz", - "integrity": "sha512-hjpyenpEvOdowgZ1qMeCJxfRD4JkKdlXz0RC14m42Un62NtOT+GpWyKA4LssT0+xyLULCByRAtG2fzZorpIAcA==", - "requires": { - "@babel/runtime": "^7.0.0", - "@probe.gl/env": "3.6.0" - } - }, - "@probe.gl/stats": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/@probe.gl/stats/-/stats-3.6.0.tgz", - "integrity": "sha512-JdALQXB44OP4kUBN/UrQgzbJe4qokbVF4Y8lkIA8iVCFnjVowWIgkD/z/0QO65yELT54tTrtepw1jScjKB+rhQ==", - "requires": { - "@babel/runtime": "^7.0.0" - } - }, - "@swc/core": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core/-/core-1.2.246.tgz", - "integrity": "sha512-wi0IZLv5GC2zjZoiEDoLZraS7/ZV2Y6vnAII//Ulobvdc4zuQoceJvYvyO3IJMB0bZVoiY/fn0Ae/iEMx9PFBQ==", - "dev": true, - "requires": { - "@swc/core-android-arm-eabi": "1.2.246", - "@swc/core-android-arm64": "1.2.246", - "@swc/core-darwin-arm64": "1.2.246", - "@swc/core-darwin-x64": "1.2.246", - "@swc/core-freebsd-x64": "1.2.246", - "@swc/core-linux-arm-gnueabihf": "1.2.246", - "@swc/core-linux-arm64-gnu": "1.2.246", - "@swc/core-linux-arm64-musl": "1.2.246", - "@swc/core-linux-x64-gnu": "1.2.246", - "@swc/core-linux-x64-musl": "1.2.246", - "@swc/core-win32-arm64-msvc": "1.2.246", - "@swc/core-win32-ia32-msvc": "1.2.246", - "@swc/core-win32-x64-msvc": "1.2.246" - } - }, - "@swc/core-android-arm-eabi": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.246.tgz", - "integrity": "sha512-3LXgOhtZnDsBacv71WRhuiuzjD0Q7m3XLzGyndtNZ+os4SOlmFiOTjZ3iMhnafKWZslmgAFrMemLPDOH+Np8OQ==", - "dev": true, - "optional": true, - "requires": { - "@swc/wasm": "1.2.122" - } - }, - "@swc/core-android-arm64": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-android-arm64/-/core-android-arm64-1.2.246.tgz", - "integrity": "sha512-m/BCkI7Wo4nMN6PLM1EnO43p7aoi9sxY3KESVCyAEZ07QlY++a0GEgVqCkKHWZ8zcfbLsesDA2E9/DYMOgPzxg==", - "dev": true, - "optional": true, - "requires": { - "@swc/wasm": "1.2.130" - }, - "dependencies": { - "@swc/wasm": { - "version": "1.2.130", - "resolved": "https://registry.npmmirror.com/@swc/wasm/-/wasm-1.2.130.tgz", - "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", - "dev": true, - "optional": true - } - } - }, - "@swc/core-darwin-arm64": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.246.tgz", - "integrity": "sha512-w3RbXiGPE1LshUS5T3McBJAxl9gCFZanaY5ALuUNERAIdjVxjGmB815O0hPDmjlJrhvwhKI9+Rx4X/M1vlZDtA==", - "dev": true, - "optional": true - }, - "@swc/core-darwin-x64": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-darwin-x64/-/core-darwin-x64-1.2.246.tgz", - "integrity": "sha512-2odv/ZlV9VsQuQDIul1jU2+u5vPCw6Xyg0BaejaA5FCcnXi6w2xf6/ryFFgQy4i+LP3oZdOtJG1dZiA8ozplKw==", - "dev": true, - "optional": true - }, - "@swc/core-freebsd-x64": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.246.tgz", - "integrity": "sha512-GvQuHKTc8TyJ3jn1e4HZasgGfBvD3nGe55syzAAaedh8tuU7VnQjxl1Dtq5DtyCBDDbulzuHcwLFQnWNWgEMyA==", - "dev": true, - "optional": true, - "requires": { - "@swc/wasm": "1.2.130" - }, - "dependencies": { - "@swc/wasm": { - "version": "1.2.130", - "resolved": "https://registry.npmmirror.com/@swc/wasm/-/wasm-1.2.130.tgz", - "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", - "dev": true, - "optional": true - } - } - }, - "@swc/core-linux-arm-gnueabihf": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.246.tgz", - "integrity": "sha512-J1g/q9S9I0uLn/erHoIEpYvzr48oTiVY2cmniw/q0jRfg+ECTI24AjWQj5sdIoB+GLqEn5+GNhUbYIVoCTubRA==", - "dev": true, - "optional": true, - "requires": { - "@swc/wasm": "1.2.130" - }, - "dependencies": { - "@swc/wasm": { - "version": "1.2.130", - "resolved": "https://registry.npmmirror.com/@swc/wasm/-/wasm-1.2.130.tgz", - "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", - "dev": true, - "optional": true - } - } - }, - "@swc/core-linux-arm64-gnu": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.246.tgz", - "integrity": "sha512-J7B6XgOiNgCyZp5WXkC0wi2ov3SpS5z3o6++n0qz8d1UqswDaOjnpGQgPITXFkKFFrs/uWdPiNbbBsMum6C5gQ==", - "dev": true, - "optional": true - }, - "@swc/core-linux-arm64-musl": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.246.tgz", - "integrity": "sha512-AFv/95BgZkdrLa5YfvP/69v8qT+zq0pRVZxv/IUW1mmM1UGVKrcU0i0y/haYivIFcLX+Ox5IYmIibO9C9P9pOA==", - "dev": true, - "optional": true - }, - "@swc/core-linux-x64-gnu": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.246.tgz", - "integrity": "sha512-egM6QX7PaLB2cQXJidfQvwT9OzUkTl1XIUVz+IOpv4om0xxtyJQjy/2ENjjtiw7C9IuV1xASOLlE1tMfLY7osw==", - "dev": true, - "optional": true - }, - "@swc/core-linux-x64-musl": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.246.tgz", - "integrity": "sha512-SKJRqgcbiZ4h2O0p+JA/NsmmV1ZwHTxdMRKkiNSvkyybGyOwPgP01CVITggVohz0j9NGwgIVAJOcn4Hx5WCcuw==", - "dev": true, - "optional": true - }, - "@swc/core-win32-arm64-msvc": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.246.tgz", - "integrity": "sha512-VISXunc1sU0jm5dC37TLXYTdmIcz4b9ItfrGpc+hIZWDwGbagSwHiThnJL3OlpZQrcrbZ0w+/zg4EYb5JenbXg==", - "dev": true, - "optional": true, - "requires": { - "@swc/wasm": "1.2.130" - }, - "dependencies": { - "@swc/wasm": { - "version": "1.2.130", - "resolved": "https://registry.npmmirror.com/@swc/wasm/-/wasm-1.2.130.tgz", - "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", - "dev": true, - "optional": true - } - } - }, - "@swc/core-win32-ia32-msvc": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.246.tgz", - "integrity": "sha512-4tp3BrFur90PB2EM5vvfsw2zyBDFhC1PRAYGXJf9oF0Fnf4kQt+cBYXGy13I/kJfhd/cKQJC8o9lBV+dtLtGDg==", - "dev": true, - "optional": true, - "requires": { - "@swc/wasm": "1.2.130" - }, - "dependencies": { - "@swc/wasm": { - "version": "1.2.130", - "resolved": "https://registry.npmmirror.com/@swc/wasm/-/wasm-1.2.130.tgz", - "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", - "dev": true, - "optional": true - } - } - }, - "@swc/core-win32-x64-msvc": { - "version": "1.2.246", - "resolved": "https://registry.npmmirror.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.246.tgz", - "integrity": "sha512-wD22xEFdiYtpq7Nwq1k8Dqwe08zrgaEhO9rn7G9DUtjHPSeIOpNQ1dQrzGBVK5Ah/3WJuciCAyUbVtaDUROhEQ==", - "dev": true, - "optional": true - }, - "@swc/wasm": { - "version": "1.2.122", - "resolved": "https://registry.npmmirror.com/@swc/wasm/-/wasm-1.2.122.tgz", - "integrity": "sha512-sM1VCWQxmNhFtdxME+8UXNyPNhxNu7zdb6ikWpz0YKAQQFRGT5ThZgJrubEpah335SUToNg8pkdDF7ibVCjxbQ==", - "dev": true, - "optional": true - }, - "@transloadit/prettier-bytes": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz", - "integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==" - }, - "@types/d3-timer": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-2.0.1.tgz", - "integrity": "sha512-TF8aoF5cHcLO7W7403blM7L1T+6NF3XMyN3fxyUolq2uOcFeicG/khQg/dGxiCJWoAcmYulYN7LYSRKO54IXaA==" - }, - "@types/event-emitter": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@types/event-emitter/-/event-emitter-0.3.3.tgz", - "integrity": "sha512-UfnOK1pIxO7P+EgPRZXD9jMpimd8QEFcEZ5R67R1UhGbv4zghU5+NE7U8M8G9H5Jc8FI51rqDWQs6FtUfq2e/Q==" - }, - "@types/insert-css": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/@types/insert-css/-/insert-css-2.0.1.tgz", - "integrity": "sha512-slUriXg8Y0JvpQoZlQ96bzzi86AU0DvtstKmGtBWpzJilFnpMUDbREi28Gpa+ZaIrTTCQUfvpDS7z7F1uwlYjQ==", - "dev": true - }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "@types/katex": { - "version": "0.14.0", - "resolved": "https://registry.npmmirror.com/@types/katex/-/katex-0.14.0.tgz", - "integrity": "sha512-+2FW2CcT0K3P+JMR8YG846bmDwplKUTsWgT2ENwdQ1UdVfRk3GQrh6Mi4sTopy30gI8Uau5CEqHTDZ6YvWIUPA==" - }, - "@types/lodash": { - "version": "4.14.182", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", - "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==" - }, - "@types/lodash-es": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.6.tgz", - "integrity": "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==", - "requires": { - "@types/lodash": "*" - } - }, - "@types/node": { - "version": "18.0.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz", - "integrity": "sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw==", - "dev": true - }, - "@types/nprogress": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@types/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==", - "dev": true - }, - "@types/sortablejs": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.13.0.tgz", - "integrity": "sha512-C3064MH72iEfeGCYEGCt7FCxXoAXaMPG0QPnstcxvPmbl54erpISu06d++FY37Smja64iWy5L8wOyHHBghWbJQ==", - "dev": true - }, - "@types/web-bluetooth": { - "version": "0.0.14", - "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.14.tgz", - "integrity": "sha512-5d2RhCard1nQUC3aHcq/gHzWYO6K0WJmAbjO7mQJgCQKtZpgXxv1rOM6O/dBDhDYYVutk1sciOgNSe+5YyfM8A==" - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.30.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.7.tgz", - "integrity": "sha512-l4L6Do+tfeM2OK0GJsU7TUcM/1oN/N25xHm3Jb4z3OiDU4Lj8dIuxX9LpVMS9riSXQs42D1ieX7b85/r16H9Fw==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.30.7", - "@typescript-eslint/type-utils": "5.30.7", - "@typescript-eslint/utils": "5.30.7", - "debug": "^4.3.4", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.2.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.30.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.7.tgz", - "integrity": "sha512-Rg5xwznHWWSy7v2o0cdho6n+xLhK2gntImp0rJroVVFkcYFYQ8C8UJTSuTw/3CnExBmPjycjmUJkxVmjXsld6A==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.30.7", - "@typescript-eslint/types": "5.30.7", - "@typescript-eslint/typescript-estree": "5.30.7", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.30.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.7.tgz", - "integrity": "sha512-7BM1bwvdF1UUvt+b9smhqdc/eniOnCKxQT/kj3oXtj3LqnTWCAM0qHRHfyzCzhEfWX0zrW7KqXXeE4DlchZBKw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.30.7", - "@typescript-eslint/visitor-keys": "5.30.7" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.30.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.7.tgz", - "integrity": "sha512-nD5qAE2aJX/YLyKMvOU5jvJyku4QN5XBVsoTynFrjQZaDgDV6i7QHFiYCx10wvn7hFvfuqIRNBtsgaLe0DbWhw==", - "dev": true, - "requires": { - "@typescript-eslint/utils": "5.30.7", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.30.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.7.tgz", - "integrity": "sha512-ocVkETUs82+U+HowkovV6uxf1AnVRKCmDRNUBUUo46/5SQv1owC/EBFkiu4MOHeZqhKz2ktZ3kvJJ1uFqQ8QPg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.30.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.7.tgz", - "integrity": "sha512-tNslqXI1ZdmXXrHER83TJ8OTYl4epUzJC0aj2i4DMDT4iU+UqLT3EJeGQvJ17BMbm31x5scSwo3hPM0nqQ1AEA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.30.7", - "@typescript-eslint/visitor-keys": "5.30.7", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.30.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.7.tgz", - "integrity": "sha512-Z3pHdbFw+ftZiGUnm1GZhkJgVqsDL5CYW2yj+TB2mfXDFOMqtbzQi2dNJIyPqPbx9mv2kUxS1gU+r2gKlKi1rQ==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.30.7", - "@typescript-eslint/types": "5.30.7", - "@typescript-eslint/typescript-estree": "5.30.7", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.30.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.7.tgz", - "integrity": "sha512-KrRXf8nnjvcpxDFOKej4xkD7657+PClJs5cJVSG7NNoCNnjEdc46juNAQt7AyuWctuCgs6mVRc1xGctEqrjxWw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.30.7", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@uppy/companion-client": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-2.2.1.tgz", - "integrity": "sha512-Y3E10NJLMfp/wjgthNhx3gJtT67fzFCPNPFwpNNRs5iJsW6PANhJ420eyMUFzfmEZ56ZzGYxr5pzJZx8YxHICQ==", - "requires": { - "@uppy/utils": "^4.1.0", - "namespace-emitter": "^2.0.1" - } - }, - "@uppy/core": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@uppy/core/-/core-2.3.1.tgz", - "integrity": "sha512-KV04X7ueYbYX1p37/i3QsoQSw8IDP8Yb+Bh9KNN0X2Vcun6K2VnNjhVtPmPXtyjDZooK7lVIqhRX8TZWcSfgSQ==", - "requires": { - "@transloadit/prettier-bytes": "0.0.7", - "@uppy/store-default": "^2.1.0", - "@uppy/utils": "^4.1.0", - "lodash.throttle": "^4.1.1", - "mime-match": "^1.0.2", - "namespace-emitter": "^2.0.1", - "nanoid": "^3.1.25", - "preact": "^10.5.13" - } - }, - "@uppy/store-default": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-2.1.0.tgz", - "integrity": "sha512-BkcR1wGw6Kwbvr8m1tKF9EDDWSTJoTGnVseBF/iW4bzR22assbtxZIE1iroo68UMqYEG4rv63SX4BUEtNvVjdA==" - }, - "@uppy/utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-4.1.0.tgz", - "integrity": "sha512-C47DUl4uLzmQZdW+VmetIgGRurXuPsvb+/pyYqh9DJn0Phep8u7AOj/tlJA5CHv4pefNHsFjXpaWfSUG3HtW3A==", - "requires": { - "lodash.throttle": "^4.1.1" - } - }, - "@uppy/xhr-upload": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@uppy/xhr-upload/-/xhr-upload-2.1.2.tgz", - "integrity": "sha512-VCsb7J5yHsof49nnUa+Y1n27UMtqHPttQmmoCa5hmjqa9R7ZISpBkXKOQmZo526eopKNuAKSAdkHWfCm8efJTA==", - "requires": { - "@uppy/companion-client": "^2.2.1", - "@uppy/utils": "^4.1.0", - "nanoid": "^3.1.25" - } - }, - "@vicons/ionicons5": { - "version": "0.12.0", - "resolved": "https://registry.npmmirror.com/@vicons/ionicons5/-/ionicons5-0.12.0.tgz", - "integrity": "sha512-Iy1EUVRpX0WWxeu1VIReR1zsZLMc4fqpt223czR+Rpnrwu7pt46nbnC2ycO7ItI/uqDLJxnbcMC7FujKs9IfFA==" - }, - "@vitejs/plugin-vue": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-2.3.3.tgz", - "integrity": "sha512-SmQLDyhz+6lGJhPELsBdzXGc+AcaT8stgkbiTFGpXPe8Tl1tJaBw1A6pxDqDuRsVkD8uscrkx3hA7QDOoKYtyw==", - "dev": true - }, - "@vue/compiler-core": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.37.tgz", - "integrity": "sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.37", - "estree-walker": "^2.0.2", - "source-map": "^0.6.1" - } - }, - "@vue/compiler-dom": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz", - "integrity": "sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==", - "requires": { - "@vue/compiler-core": "3.2.37", - "@vue/shared": "3.2.37" - } - }, - "@vue/compiler-sfc": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz", - "integrity": "sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.37", - "@vue/compiler-dom": "3.2.37", - "@vue/compiler-ssr": "3.2.37", - "@vue/reactivity-transform": "3.2.37", - "@vue/shared": "3.2.37", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7", - "postcss": "^8.1.10", - "source-map": "^0.6.1" - } - }, - "@vue/compiler-ssr": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz", - "integrity": "sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==", - "requires": { - "@vue/compiler-dom": "3.2.37", - "@vue/shared": "3.2.37" - } - }, - "@vue/devtools-api": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.2.1.tgz", - "integrity": "sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ==" - }, - "@vue/reactivity": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz", - "integrity": "sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A==", - "requires": { - "@vue/shared": "3.2.37" - } - }, - "@vue/reactivity-transform": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz", - "integrity": "sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.37", - "@vue/shared": "3.2.37", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7" - } - }, - "@vue/runtime-core": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.37.tgz", - "integrity": "sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ==", - "requires": { - "@vue/reactivity": "3.2.37", - "@vue/shared": "3.2.37" - } - }, - "@vue/runtime-dom": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz", - "integrity": "sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==", - "requires": { - "@vue/runtime-core": "3.2.37", - "@vue/shared": "3.2.37", - "csstype": "^2.6.8" - } - }, - "@vue/server-renderer": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.37.tgz", - "integrity": "sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==", - "requires": { - "@vue/compiler-ssr": "3.2.37", - "@vue/shared": "3.2.37" - } - }, - "@vue/shared": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.37.tgz", - "integrity": "sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==" - }, - "@vueuse/core": { - "version": "8.9.4", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-8.9.4.tgz", - "integrity": "sha512-B/Mdj9TK1peFyWaPof+Zf/mP9XuGAngaJZBwPaXBvU3aCTZlx3ltlrFFFyMV4iGBwsjSCeUCgZrtkEj9dS2Y3Q==", - "requires": { - "@types/web-bluetooth": "^0.0.14", - "@vueuse/metadata": "8.9.4", - "@vueuse/shared": "8.9.4", - "vue-demi": "*" - }, - "dependencies": { - "@vueuse/shared": { - "version": "8.9.4", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-8.9.4.tgz", - "integrity": "sha512-wt+T30c4K6dGRMVqPddexEVLa28YwxW5OFIPmzUHICjphfAuBFTTdDoyqREZNDOFJZ44ARH1WWQNCUK8koJ+Ag==", - "requires": { - "vue-demi": "*" - } - }, - "vue-demi": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.5.tgz", - "integrity": "sha512-tO3K2bML3AwiHmVHeKCq6HLef2st4zBXIV5aEkoJl6HZ+gJWxWv2O8wLH8qrA3SX3lDoTDHNghLX1xZg83MXvw==" - } - } - }, - "@vueuse/metadata": { - "version": "8.9.4", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-8.9.4.tgz", - "integrity": "sha512-IwSfzH80bnJMzqhaapqJl9JRIiyQU0zsRGEgnxN6jhq7992cPUJIRfV+JHRIZXjYqbwt07E1gTEp0R0zPJ1aqw==" - }, - "@wangeditor/basic-modules": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@wangeditor/basic-modules/-/basic-modules-1.1.3.tgz", - "integrity": "sha512-TGJix4UelO46yAgwI946ctx4lSIJbYBwNvjSJ9Tf8mKr0WMCeLVBV+MV85rXPsfcmWtR4wBNwSg648Z+RbqRUg==", - "requires": { - "is-url": "^1.2.4" - } - }, - "@wangeditor/code-highlight": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@wangeditor/code-highlight/-/code-highlight-1.0.2.tgz", - "integrity": "sha512-SCtOcUxjKqIso/LSxGSOaYr3G6MC2En0gNTyHIMCG928T0fo0ufaqp/vIXKQzVL2Y+X/CSAOB2EbrFlgGvr0AQ==", - "requires": { - "prismjs": "^1.23.0" - } - }, - "@wangeditor/core": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@wangeditor/core/-/core-1.1.8.tgz", - "integrity": "sha512-TfCtAXfL/bgmUKQbtLEaboK7wH49G7BQqYoQsylZ7MyD1LhYnVLXp/4RzJbuIr4cV9Bs1Ncz1JoZ9tqDe6WImw==", - "requires": { - "@types/event-emitter": "^0.3.3", - "event-emitter": "^0.3.5", - "html-void-elements": "^2.0.0", - "i18next": "^20.4.0", - "scroll-into-view-if-needed": "^2.2.28", - "slate-history": "^0.66.0" - } - }, - "@wangeditor/editor": { - "version": "5.1.11", - "resolved": "https://registry.npmjs.org/@wangeditor/editor/-/editor-5.1.11.tgz", - "integrity": "sha512-jJbY2OirSy16UY9OkOCBw0DhQpOzTIGRLYL724wYSWf9nO6DpO21bemmEoDDF5zULtabEm3w0dvSxTSVd7amLw==", - "requires": { - "@uppy/core": "^2.1.1", - "@uppy/xhr-upload": "^2.0.3", - "@wangeditor/basic-modules": "^1.1.3", - "@wangeditor/code-highlight": "^1.0.2", - "@wangeditor/core": "^1.1.8", - "@wangeditor/list-module": "^1.0.2", - "@wangeditor/table-module": "^1.1.1", - "@wangeditor/upload-image-module": "^1.0.1", - "@wangeditor/video-module": "^1.1.1", - "dom7": "^3.0.0", - "is-hotkey": "^0.2.0", - "lodash.camelcase": "^4.3.0", - "lodash.clonedeep": "^4.5.0", - "lodash.debounce": "^4.0.8", - "lodash.foreach": "^4.5.0", - "lodash.isequal": "^4.5.0", - "lodash.throttle": "^4.1.1", - "lodash.toarray": "^4.4.0", - "nanoid": "^3.2.0", - "slate": "^0.72.0", - "snabbdom": "^3.1.0" - } - }, - "@wangeditor/list-module": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@wangeditor/list-module/-/list-module-1.0.2.tgz", - "integrity": "sha512-VfENZEFvsLTiLxN/cj8cibFGy9NVV+/cfATTiLiH9ef+8lgKv8apttXYVlqIAfnlJLLuCk0cIm8c/zH+hbtrZg==" - }, - "@wangeditor/table-module": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@wangeditor/table-module/-/table-module-1.1.1.tgz", - "integrity": "sha512-VPjEWQtncS2DsXYXiHUxPSxn2Xhc8GdhG3la7N5YhvxQde1+4N0SZLXeWsYvbGzOq4um5XToq5pktLLbE8G+EA==" - }, - "@wangeditor/upload-image-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@wangeditor/upload-image-module/-/upload-image-module-1.0.1.tgz", - "integrity": "sha512-vgUV4ENttTITblqtVuzleIq732OmzmzzgrIvX6b3GRGPSw5u8glJ/87tOEhvHjHECc4oFo18B7xzJ1GpBj79/w==" - }, - "@wangeditor/video-module": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@wangeditor/video-module/-/video-module-1.1.1.tgz", - "integrity": "sha512-6gzpS5cnJihW2T0HFjqmbv6v8ouyaeMUjdM2X8BPohwD74p1ov00dCmRt5QekNTyYSmRHK0ASkUMOvRGqaDxMg==" - }, - "acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "async-validator": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", - "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "requires": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "batch-processor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz", - "integrity": "sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==" - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "claygl": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz", - "integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==" - }, - "clipboard": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", - "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==", - "requires": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" - } - }, - "codemirror": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/codemirror/-/codemirror-6.0.1.tgz", - "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - } - }, - "color": { - "version": "3.2.1", - "resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "requires": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - }, - "dependencies": { - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - } - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "compute-scroll-into-view": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz", - "integrity": "sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "copy-anything": { - "version": "2.0.6", - "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz", - "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", - "requires": { - "is-what": "^3.14.1" - } - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fcore-util-is%2F-%2Fcore-util-is-1.0.3.tgz", - "integrity": "sha1-pgQtNjTCsn6TKPg3uWX6yDgI24U=" - }, - "countup.js": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.3.2.tgz", - "integrity": "sha512-dQ7F/CmKGjaO6cDfhtEXwsKVlXIpJ89dFs8PvkaZH9jBVJ2Z8GU4iwG/qP7MgY8qwr+1skbwR6qecWWQLUzB8Q==" - }, - "crelt": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/crelt/-/crelt-1.0.5.tgz", - "integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==" - }, - "cropperjs": { - "version": "1.5.12", - "resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.5.12.tgz", - "integrity": "sha512-re7UdjE5UnwdrovyhNzZ6gathI4Rs3KGCBSc8HCIjUo5hO42CtzyblmWLj6QWVw7huHyDMfpKxhiO2II77nhDw==" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "css-render": { - "version": "0.15.12", - "resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.12.tgz", - "integrity": "sha512-eWzS66patiGkTTik+ipO9qNGZ+uNuGyTmnz6/+EJIiFg8+3yZRpnMwgFo8YdXhQRsiePzehnusrxVvugNjXzbw==", - "requires": { - "@emotion/hash": "~0.8.0", - "csstype": "~3.0.5" - }, - "dependencies": { - "csstype": { - "version": "3.0.11", - "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.0.11.tgz", - "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" - } - } - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, - "csstype": { - "version": "2.6.20", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", - "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" - }, - "d3-dispatch": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-2.0.0.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fd3-dispatch%2F-%2Fd3-dispatch-2.0.0.tgz", - "integrity": "sha1-ihjhb3bdP8rvQhY8l7kmqptV588=" - }, - "d3-ease": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-1.0.7.tgz", - "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" - }, - "d3-force": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/d3-force/-/d3-force-2.1.1.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fd3-force%2F-%2Fd3-force-2.1.1.tgz", - "integrity": "sha1-8gzL8ebJ6ArdGSbwm1H2hqi8CTc=", - "requires": { - "d3-dispatch": "1 - 2", - "d3-quadtree": "1 - 2", - "d3-timer": "1 - 2" - } - }, - "d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "requires": { - "d3-color": "1 - 3" - } - }, - "d3-quadtree": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/d3-quadtree/-/d3-quadtree-2.0.0.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fd3-quadtree%2F-%2Fd3-quadtree-2.0.0.tgz", - "integrity": "sha1-7brQRc74hwH2/uOu6Ok/szLTD50=" - }, - "d3-timer": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-2.0.0.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fd3-timer%2F-%2Fd3-timer-2.0.0.tgz", - "integrity": "sha1-BV7bHRcM/jGrLaiWje7pQLVmI+Y=" - }, - "dagre": { - "version": "0.8.5", - "resolved": "https://registry.npmmirror.com/dagre/-/dagre-0.8.5.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fdagre%2F-%2Fdagre-0.8.5.tgz", - "integrity": "sha1-ujCwBV2sErbB/MJHgXRCd30Gr+4=", - "requires": { - "graphlib": "^2.1.8", - "lodash": "^4.17.15" - } - }, - "dagre-compound": { - "version": "0.0.11", - "resolved": "https://registry.npmmirror.com/dagre-compound/-/dagre-compound-0.0.11.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fdagre-compound%2F-%2Fdagre-compound-0.0.11.tgz", - "integrity": "sha1-jT0QBNdW9CBYLSnyjJIEU3UBiYc=" - }, - "date-fns": { - "version": "2.29.3", - "resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-2.29.3.tgz", - "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==" - }, - "date-fns-tz": { - "version": "1.3.8", - "resolved": "https://registry.npmmirror.com/date-fns-tz/-/date-fns-tz-1.3.8.tgz", - "integrity": "sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ==" - }, - "dayjs": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.4.tgz", - "integrity": "sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g==" - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fdecode-uri-component%2F-%2Fdecode-uri-component-0.2.2.tgz", - "integrity": "sha1-5p2+JdN5QRcd1UDgJMREzVGI4ek=" - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" - }, - "detect-browser": { - "version": "5.3.0", - "resolved": "https://registry.npmmirror.com/detect-browser/-/detect-browser-5.3.0.tgz", - "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==" - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom7": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/dom7/-/dom7-3.0.0.tgz", - "integrity": "sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==", - "requires": { - "ssr-window": "^3.0.0-alpha.1" - } - }, - "dotenv": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", - "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==", - "dev": true - }, - "echarts": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.3.3.tgz", - "integrity": "sha512-BRw2serInRwO5SIwRviZ6Xgm5Lb7irgz+sLiFMmy/HOaf4SQ+7oYqxKzRHAKp4xHQ05AuHw1xvoQWJjDQq/FGw==", - "requires": { - "tslib": "2.3.0", - "zrender": "5.3.2" - } - }, - "echarts-gl": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz", - "integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==", - "requires": { - "claygl": "^1.2.1", - "zrender": "^5.1.1" - } - }, - "echarts-wordcloud": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/echarts-wordcloud/-/echarts-wordcloud-2.0.0.tgz", - "integrity": "sha512-K7l6pTklqdW7ZWzT/1CS0KhBSINr/cd7c5N1fVMzZMwLQHEwT7x+nivK7g5hkVh7WNcAv4Dn6/ZS5zMKRozC1g==" - }, - "element-plus": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.2.9.tgz", - "integrity": "sha512-jYbL0JkCdv95rkT6trZJjCAizLPySa0qcd2cgq+57SKQnCZAcNDDq4GbTuFRnNavdoeCJnuM3HIficTIUpsMOQ==", - "requires": { - "@ctrl/tinycolor": "^3.4.1", - "@element-plus/icons-vue": "^2.0.6", - "@floating-ui/dom": "^0.5.4", - "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", - "@types/lodash": "^4.14.182", - "@types/lodash-es": "^4.17.6", - "@vueuse/core": "^8.7.5", - "async-validator": "^4.2.5", - "dayjs": "^1.11.3", - "escape-html": "^1.0.3", - "lodash": "^4.17.21", - "lodash-es": "^4.17.21", - "lodash-unified": "^1.0.2", - "memoize-one": "^6.0.0", - "normalize-wheel-es": "^1.1.2" - }, - "dependencies": { - "@popperjs/core": { - "version": "npm:@sxzz/popperjs-es@2.11.7", - "resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", - "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==" - } - } - }, - "element-resize-detector": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.4.tgz", - "integrity": "sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==", - "requires": { - "batch-processor": "1.0.0" - } - }, - "errno": { - "version": "0.1.8", - "resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "optional": true, - "requires": { - "prr": "~1.0.1" - } - }, - "es5-ext": { - "version": "0.10.61", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", - "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==", - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "esbuild": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.49.tgz", - "integrity": "sha512-/TlVHhOaq7Yz8N1OJrjqM3Auzo5wjvHFLk+T8pIue+fhnhIMpfAzsG6PLVMbFveVxqD2WOp3QHei+52IMUNmCw==", - "dev": true, - "requires": { - "esbuild-android-64": "0.14.49", - "esbuild-android-arm64": "0.14.49", - "esbuild-darwin-64": "0.14.49", - "esbuild-darwin-arm64": "0.14.49", - "esbuild-freebsd-64": "0.14.49", - "esbuild-freebsd-arm64": "0.14.49", - "esbuild-linux-32": "0.14.49", - "esbuild-linux-64": "0.14.49", - "esbuild-linux-arm": "0.14.49", - "esbuild-linux-arm64": "0.14.49", - "esbuild-linux-mips64le": "0.14.49", - "esbuild-linux-ppc64le": "0.14.49", - "esbuild-linux-riscv64": "0.14.49", - "esbuild-linux-s390x": "0.14.49", - "esbuild-netbsd-64": "0.14.49", - "esbuild-openbsd-64": "0.14.49", - "esbuild-sunos-64": "0.14.49", - "esbuild-windows-32": "0.14.49", - "esbuild-windows-64": "0.14.49", - "esbuild-windows-arm64": "0.14.49" - } - }, - "esbuild-android-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.49.tgz", - "integrity": "sha512-vYsdOTD+yi+kquhBiFWl3tyxnj2qZJsl4tAqwhT90ktUdnyTizgle7TjNx6Ar1bN7wcwWqZ9QInfdk2WVagSww==", - "dev": true, - "optional": true - }, - "esbuild-android-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.49.tgz", - "integrity": "sha512-g2HGr/hjOXCgSsvQZ1nK4nW/ei8JUx04Li74qub9qWrStlysaVmadRyTVuW32FGIpLQyc5sUjjZopj49eGGM2g==", - "dev": true, - "optional": true - }, - "esbuild-darwin-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.49.tgz", - "integrity": "sha512-3rvqnBCtX9ywso5fCHixt2GBCUsogNp9DjGmvbBohh31Ces34BVzFltMSxJpacNki96+WIcX5s/vum+ckXiLYg==", - "dev": true, - "optional": true - }, - "esbuild-darwin-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.49.tgz", - "integrity": "sha512-XMaqDxO846srnGlUSJnwbijV29MTKUATmOLyQSfswbK/2X5Uv28M9tTLUJcKKxzoo9lnkYPsx2o8EJcTYwCs/A==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.49.tgz", - "integrity": "sha512-NJ5Q6AjV879mOHFri+5lZLTp5XsO2hQ+KSJYLbfY9DgCu8s6/Zl2prWXVANYTeCDLlrIlNNYw8y34xqyLDKOmQ==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.49.tgz", - "integrity": "sha512-lFLtgXnAc3eXYqj5koPlBZvEbBSOSUbWO3gyY/0+4lBdRqELyz4bAuamHvmvHW5swJYL7kngzIZw6kdu25KGOA==", - "dev": true, - "optional": true - }, - "esbuild-linux-32": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.49.tgz", - "integrity": "sha512-zTTH4gr2Kb8u4QcOpTDVn7Z8q7QEIvFl/+vHrI3cF6XOJS7iEI1FWslTo3uofB2+mn6sIJEQD9PrNZKoAAMDiA==", - "dev": true, - "optional": true - }, - "esbuild-linux-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.49.tgz", - "integrity": "sha512-hYmzRIDzFfLrB5c1SknkxzM8LdEUOusp6M2TnuQZJLRtxTgyPnZZVtyMeCLki0wKgYPXkFsAVhi8vzo2mBNeTg==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.49.tgz", - "integrity": "sha512-iE3e+ZVv1Qz1Sy0gifIsarJMQ89Rpm9mtLSRtG3AH0FPgAzQ5Z5oU6vYzhc/3gSPi2UxdCOfRhw2onXuFw/0lg==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.49.tgz", - "integrity": "sha512-KLQ+WpeuY+7bxukxLz5VgkAAVQxUv67Ft4DmHIPIW+2w3ObBPQhqNoeQUHxopoW/aiOn3m99NSmSV+bs4BSsdA==", - "dev": true, - "optional": true - }, - "esbuild-linux-mips64le": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.49.tgz", - "integrity": "sha512-n+rGODfm8RSum5pFIqFQVQpYBw+AztL8s6o9kfx7tjfK0yIGF6tm5HlG6aRjodiiKkH2xAiIM+U4xtQVZYU4rA==", - "dev": true, - "optional": true - }, - "esbuild-linux-ppc64le": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.49.tgz", - "integrity": "sha512-WP9zR4HX6iCBmMFH+XHHng2LmdoIeUmBpL4aL2TR8ruzXyT4dWrJ5BSbT8iNo6THN8lod6GOmYDLq/dgZLalGw==", - "dev": true, - "optional": true - }, - "esbuild-linux-riscv64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.49.tgz", - "integrity": "sha512-h66ORBz+Dg+1KgLvzTVQEA1LX4XBd1SK0Fgbhhw4akpG/YkN8pS6OzYI/7SGENiN6ao5hETRDSkVcvU9NRtkMQ==", - "dev": true, - "optional": true - }, - "esbuild-linux-s390x": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.49.tgz", - "integrity": "sha512-DhrUoFVWD+XmKO1y7e4kNCqQHPs6twz6VV6Uezl/XHYGzM60rBewBF5jlZjG0nCk5W/Xy6y1xWeopkrhFFM0sQ==", - "dev": true, - "optional": true - }, - "esbuild-netbsd-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.49.tgz", - "integrity": "sha512-BXaUwFOfCy2T+hABtiPUIpWjAeWK9P8O41gR4Pg73hpzoygVGnj0nI3YK4SJhe52ELgtdgWP/ckIkbn2XaTxjQ==", - "dev": true, - "optional": true - }, - "esbuild-openbsd-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.49.tgz", - "integrity": "sha512-lP06UQeLDGmVPw9Rg437Btu6J9/BmyhdoefnQ4gDEJTtJvKtQaUcOQrhjTq455ouZN4EHFH1h28WOJVANK41kA==", - "dev": true, - "optional": true - }, - "esbuild-sunos-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.49.tgz", - "integrity": "sha512-4c8Zowp+V3zIWje329BeLbGh6XI9c/rqARNaj5yPHdC61pHI9UNdDxT3rePPJeWcEZVKjkiAS6AP6kiITp7FSw==", - "dev": true, - "optional": true - }, - "esbuild-windows-32": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.49.tgz", - "integrity": "sha512-q7Rb+J9yHTeKr9QTPDYkqfkEj8/kcKz9lOabDuvEXpXuIcosWCJgo5Z7h/L4r7rbtTH4a8U2FGKb6s1eeOHmJA==", - "dev": true, - "optional": true - }, - "esbuild-windows-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.49.tgz", - "integrity": "sha512-+Cme7Ongv0UIUTniPqfTX6mJ8Deo7VXw9xN0yJEN1lQMHDppTNmKwAM3oGbD/Vqff+07K2gN0WfNkMohmG+dVw==", - "dev": true, - "optional": true - }, - "esbuild-windows-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.49.tgz", - "integrity": "sha512-v+HYNAXzuANrCbbLFJ5nmO3m5y2PGZWLe3uloAkLt87aXiO2mZr3BTmacZdjwNkNEHuH3bNtN8cak+mzVjVPfA==", - "dev": true, - "optional": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.20.0.tgz", - "integrity": "sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.2", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "eslint-plugin-vue": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.2.0.tgz", - "integrity": "sha512-W2hc+NUXoce8sZtWgZ45miQTy6jNyuSdub5aZ1IBune4JDeAyzucYX0TzkrQ1jMO52sNUDYlCIHDoaNePe0p5g==", - "dev": true, - "requires": { - "eslint-utils": "^3.0.0", - "natural-compare": "^1.4.0", - "nth-check": "^2.0.1", - "postcss-selector-parser": "^6.0.9", - "semver": "^7.3.5", - "vue-eslint-parser": "^9.0.1", - "xml-name-validator": "^4.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", - "dev": true, - "requires": { - "acorn": "^8.7.1", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, - "evtd": { - "version": "0.2.4", - "resolved": "https://registry.npmmirror.com/evtd/-/evtd-0.2.4.tgz", - "integrity": "sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==" - }, - "ext": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", - "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", - "requires": { - "type": "^2.5.0" - }, - "dependencies": { - "type": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", - "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==" - } - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/filter-obj/-/filter-obj-1.1.0.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Ffilter-obj%2F-%2Ffilter-obj-1.1.0.tgz", - "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=" - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", - "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "gl-matrix": { - "version": "3.4.3", - "resolved": "https://registry.npmmirror.com/gl-matrix/-/gl-matrix-3.4.3.tgz", - "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" - }, - "gl-vec2": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/gl-vec2/-/gl-vec2-1.3.0.tgz", - "integrity": "sha512-YiqaAuNsheWmUV0Sa8k94kBB0D6RWjwZztyO+trEYS8KzJ6OQB/4686gdrf59wld4hHFIvaxynO3nRxpk1Ij/A==" - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.16.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", - "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==", - "requires": { - "delegate": "^3.1.2" - } - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "optional": true - }, - "graphlib": { - "version": "2.1.8", - "resolved": "https://registry.npmmirror.com/graphlib/-/graphlib-2.1.8.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fgraphlib%2F-%2Fgraphlib-2.1.8.tgz", - "integrity": "sha1-V2HUFHN4cAhMkux7XbywWSydNdo=", - "requires": { - "lodash": "^4.17.15" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "highlight.js": { - "version": "11.7.0", - "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.7.0.tgz", - "integrity": "sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==" - }, - "html-void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz", - "integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==" - }, - "i18next": { - "version": "20.6.1", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-20.6.1.tgz", - "integrity": "sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==", - "requires": { - "@babel/runtime": "^7.12.0" - } - }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, - "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", - "optional": true - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fimmediate%2F-%2Fimmediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" - }, - "immer": { - "version": "9.0.15", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", - "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==" - }, - "immutable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", - "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "insert-css": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/insert-css/-/insert-css-2.0.0.tgz", - "integrity": "sha512-xGq5ISgcUP5cvGkS2MMFLtPDBtrtQPSFfC6gA6U8wHKqfjTIMZLZNxOItQnoSjdOzlXOLU/yD32RKC4SvjNbtA==" - }, - "is-any-array": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/is-any-array/-/is-any-array-2.0.0.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fis-any-array%2F-%2Fis-any-array-2.0.0.tgz", - "integrity": "sha1-5xvBN0FTfAavrAPAeIWWfvVth0I=" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-hotkey": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.2.0.tgz", - "integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" - }, - "is-url": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", - "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" - }, - "is-what": { - "version": "3.14.1", - "resolved": "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz", - "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fisarray%2F-%2Fisarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "js-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", - "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "jsplumb": { - "version": "2.15.6", - "resolved": "https://registry.npmjs.org/jsplumb/-/jsplumb-2.15.6.tgz", - "integrity": "sha512-sIpbpz5eMVM+vV+MQzFCidlaa1RsknrQs6LOTKYDjYUDdTAi2AN2bFi94TxB33TifcIsRNV1jebcaxg0tCoPzg==" - }, - "jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fjszip%2F-%2Fjszip-3.10.1.tgz", - "integrity": "sha1-NK7nDrGOofrsL1iSCKFX0f6wkcI=", - "requires": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - }, - "dependencies": { - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fprocess-nextick-args%2F-%2Fprocess-nextick-args-2.0.1.tgz", - "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=" - }, - "readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Freadable-stream%2F-%2Freadable-stream-2.3.8.tgz", - "integrity": "sha1-kRJegEK7obmIf0k0X2J3Anzovps=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fsafe-buffer%2F-%2Fsafe-buffer-5.1.2.tgz", - "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fstring_decoder%2F-%2Fstring_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" - }, - "less": { - "version": "4.1.3", - "resolved": "https://registry.npmmirror.com/less/-/less-4.1.3.tgz", - "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", - "requires": { - "copy-anything": "^2.0.1", - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "needle": "^3.1.0", - "parse-node-version": "^1.0.1", - "source-map": "~0.6.0", - "tslib": "^2.3.0" - } - }, - "less-loader": { - "version": "11.1.0", - "resolved": "https://registry.npmmirror.com/less-loader/-/less-loader-11.1.0.tgz", - "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==", - "requires": { - "klona": "^2.0.4" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lie": { - "version": "3.3.0", - "resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Flie%2F-%2Flie-3.3.0.tgz", - "integrity": "sha1-3Pgt7lRfRgdNryAMfBxaCOD0D2o=", - "requires": { - "immediate": "~3.0.5" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, - "lodash-unified": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.2.tgz", - "integrity": "sha512-OGbEy+1P+UT26CYi4opY4gebD8cWRDxAT6MAObIVQMiqYdxZr1g3QHWCToVsm31x2NkLS4K3+MC2qInaRMa39g==" - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==" - }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" - }, - "lodash.toarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", - "integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "optional": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "optional": true - } - } - }, - "memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "optional": true - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz", - "integrity": "sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==", - "requires": { - "wildcard": "^1.1.0" - } - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mitt": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", - "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==" - }, - "ml-array-max": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/ml-array-max/-/ml-array-max-1.2.4.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fml-array-max%2F-%2Fml-array-max-1.2.4.tgz", - "integrity": "sha1-I3Pit+UciAfkVswO82TFhjcTYjs=", - "requires": { - "is-any-array": "^2.0.0" - } - }, - "ml-array-min": { - "version": "1.2.3", - "resolved": "https://registry.npmmirror.com/ml-array-min/-/ml-array-min-1.2.3.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fml-array-min%2F-%2Fml-array-min-1.2.3.tgz", - "integrity": "sha1-Zi8CfEABBYFrhJzDzXhpFdCAFJU=", - "requires": { - "is-any-array": "^2.0.0" - } - }, - "ml-array-rescale": { - "version": "1.3.7", - "resolved": "https://registry.npmmirror.com/ml-array-rescale/-/ml-array-rescale-1.3.7.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fml-array-rescale%2F-%2Fml-array-rescale-1.3.7.tgz", - "integrity": "sha1-xNEpMg0ROnMuYt2WPcFpW7qaU0A=", - "requires": { - "is-any-array": "^2.0.0", - "ml-array-max": "^1.2.4", - "ml-array-min": "^1.2.3" - } - }, - "ml-matrix": { - "version": "6.10.4", - "resolved": "https://registry.npmmirror.com/ml-matrix/-/ml-matrix-6.10.4.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fml-matrix%2F-%2Fml-matrix-6.10.4.tgz", - "integrity": "sha1-ur7jRLIAYtnBI6qAHC5dDQx0d/Y=", - "requires": { - "is-any-array": "^2.0.0", - "ml-array-rescale": "^1.3.7" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "naive-ui": { - "version": "2.34.3", - "resolved": "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.34.3.tgz", - "integrity": "sha512-fUMr0dzb/iGsOTWgoblPVobY5X5dihQ1eam5dA+H74oyLYAvgX4pL96xQFPBLIYqvyRFBAsN85kHN5pLqdtpxA==", - "requires": { - "@css-render/plugin-bem": "^0.15.10", - "@css-render/vue3-ssr": "^0.15.10", - "@types/katex": "^0.14.0", - "@types/lodash": "^4.14.181", - "@types/lodash-es": "^4.17.6", - "async-validator": "^4.0.7", - "css-render": "^0.15.10", - "date-fns": "^2.28.0", - "date-fns-tz": "^1.3.3", - "evtd": "^0.2.4", - "highlight.js": "^11.5.0", - "lodash": "^4.17.21", - "lodash-es": "^4.17.21", - "seemly": "^0.3.6", - "treemate": "^0.3.11", - "vdirs": "^0.1.8", - "vooks": "^0.2.12", - "vueuc": "^0.4.47" - } - }, - "namespace-emitter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/namespace-emitter/-/namespace-emitter-2.0.1.tgz", - "integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==" - }, - "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "needle": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/needle/-/needle-3.2.0.tgz", - "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", - "optional": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.6.3", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "optional": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-wheel-es": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", - "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==" - }, - "normalize.css": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/normalize.css/-/normalize.css-7.0.0.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fnormalize.css%2F-%2Fnormalize.css-7.0.0.tgz", - "integrity": "sha1-q/sd2CRwZ04DIrU86xqvQSk45L8=" - }, - "nprogress": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" - }, - "nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "requires": { - "boolbase": "^1.0.0" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fpako%2F-%2Fpako-1.0.11.tgz", - "integrity": "sha1-bJWZ00DVTf05RjgCUqNXBaa5kr8=" - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "optional": true - }, - "pinia": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.16.tgz", - "integrity": "sha512-9/LMVO+/epny1NBfC77vnps4g3JRezxhhoF1xLUk8mZkUIxVnwfEAIRiAX8mYBTD/KCwZqnDMqXc8w3eU0FQGg==", - "requires": { - "@vue/devtools-api": "^6.1.4", - "vue-demi": "*" - }, - "dependencies": { - "vue-demi": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.5.tgz", - "integrity": "sha512-tO3K2bML3AwiHmVHeKCq6HLef2st4zBXIV5aEkoJl6HZ+gJWxWv2O8wLH8qrA3SX3lDoTDHNghLX1xZg83MXvw==" - } - } - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "preact": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.10.0.tgz", - "integrity": "sha512-fszkg1iJJjq68I4lI8ZsmBiaoQiQHbxf1lNq+72EmC/mZOsFF5zn3k1yv9QGoFgIXzgsdSKtYymLJsrJPoamjQ==" - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", - "dev": true - }, - "print-js": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/print-js/-/print-js-1.6.0.tgz", - "integrity": "sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg==" - }, - "prismjs": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.28.0.tgz", - "integrity": "sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw==" - }, - "probe.gl": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/probe.gl/-/probe.gl-3.6.0.tgz", - "integrity": "sha512-19JydJWI7+DtR4feV+pu4Mn1I5TAc0xojuxVgZdXIyfmTLfUaFnk4OloWK1bKbPtkgGKLr2lnbnCXmpZEcEp9g==", - "requires": { - "@babel/runtime": "^7.0.0", - "@probe.gl/env": "3.6.0", - "@probe.gl/log": "3.6.0", - "@probe.gl/stats": "3.6.0" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", - "optional": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "qrcodejs2-fixes": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/qrcodejs2-fixes/-/qrcodejs2-fixes-0.0.2.tgz", - "integrity": "sha512-wMUXYMOixAEJlLnjk5MbLiFaz0gQObWYm/TIFWB5+j7sTY5gPyr09Cx1EpcLYbsgfFdN3wHjrKAhZofTuCBGhg==" - }, - "query-string": { - "version": "7.1.3", - "resolved": "https://registry.npmmirror.com/query-string/-/query-string-7.1.3.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fquery-string%2F-%2Fquery-string-7.1.3.tgz", - "integrity": "sha1-oc+Q6ZSrsROjJYBKly2YJ2/gIyg=", - "requires": { - "decode-uri-component": "^0.2.2", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "regl": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/regl/-/regl-1.7.0.tgz", - "integrity": "sha512-bEAtp/qrtKucxXSJkD4ebopFZYP0q1+3Vb2WECWv/T8yQEgKxDxJ7ztO285tAMaYZVR6mM1GgI6CCn8FROtL1w==" - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rollup": { - "version": "2.77.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.0.tgz", - "integrity": "sha512-vL8xjY4yOQEw79DvyXLijhnhh+R/O9zpF/LEgkCebZFtb6ELeN9H3/2T0r8+mp+fFTBHZ5qGpOpW2ela2zRt3g==", - "dev": true, - "requires": { - "fsevents": "~2.3.2" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "optional": true - }, - "sass": { - "version": "1.53.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.53.0.tgz", - "integrity": "sha512-zb/oMirbKhUgRQ0/GFz8TSAwRq2IlR29vOUJZOx0l8sV+CkHUfHa4u5nqrG+1VceZp7Jfj59SVW9ogdhTvJDcQ==", - "dev": true, - "requires": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - } - }, - "sass-loader": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", - "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", - "dev": true, - "requires": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "optional": true - }, - "screenfull": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-6.0.2.tgz", - "integrity": "sha512-AQdy8s4WhNvUZ6P8F6PB21tSPIYKniic+Ogx0AacBMjKP1GUHN2E9URxQHtCusiwxudnCKkdy4GrHXPPJSkCCw==" - }, - "scroll-into-view-if-needed": { - "version": "2.2.29", - "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz", - "integrity": "sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg==", - "requires": { - "compute-scroll-into-view": "^1.0.17" - } - }, - "seemly": { - "version": "0.3.6", - "resolved": "https://registry.npmmirror.com/seemly/-/seemly-0.3.6.tgz", - "integrity": "sha512-lEV5VB8BUKTo/AfktXJcy+JeXns26ylbMkIUco8CYREsQijuz4mrXres2Q+vMLdwkuLxJdIPQ8IlCIxLYm71Yw==" - }, - "select": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==" - }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fsetimmediate%2F-%2Fsetimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slate": { - "version": "0.72.8", - "resolved": "https://registry.npmjs.org/slate/-/slate-0.72.8.tgz", - "integrity": "sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==", - "requires": { - "immer": "^9.0.6", - "is-plain-object": "^5.0.0", - "tiny-warning": "^1.0.3" - } - }, - "slate-history": { - "version": "0.66.0", - "resolved": "https://registry.npmjs.org/slate-history/-/slate-history-0.66.0.tgz", - "integrity": "sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==", - "requires": { - "is-plain-object": "^5.0.0" - } - }, - "snabbdom": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.5.1.tgz", - "integrity": "sha512-wHMNIOjkm/YNE5EM3RCbr/+DVgPg6AqQAX1eOxO46zYNvCXjKP5Y865tqQj3EXnaMBjkxmQA5jFuDpDK/dbfiA==" - }, - "sortablejs": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz", - "integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" - }, - "split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/split-on-first/-/split-on-first-1.1.0.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fsplit-on-first%2F-%2Fsplit-on-first-1.1.0.tgz", - "integrity": "sha1-9hCv7uOxK84dDDBCXnY5i3gkml8=" - }, - "splitpanes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/splitpanes/-/splitpanes-3.1.1.tgz", - "integrity": "sha512-VUkxDJfIGSvTM/fm/+OSrx8ha9URwE/9B8FPvfzoBuAxVELIHBWpsfnJXIXv77zVwuex//QQL4kTU9SDBPeHjA==" - }, - "ssr-window": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz", - "integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==" - }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Fstrict-uri-encode%2F-%2Fstrict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "style-mod": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/style-mod/-/style-mod-4.0.0.tgz", - "integrity": "sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, - "tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, - "tinycolor2": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz", - "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "treemate": { - "version": "0.3.11", - "resolved": "https://registry.npmmirror.com/treemate/-/treemate-0.3.11.tgz", - "integrity": "sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==" - }, - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typescript": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", - "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", - "dev": true - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmmirror.com/utility-types/-/utility-types-3.10.0.tgz?dl=https%3A%2F%2Fregistry.npmmirror.com%2Futility-types%2F-%2Futility-types-3.10.0.tgz", - "integrity": "sha1-6kFI+adBAV8F7XT9YV4dIOa+2Cs=" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "vdirs": { - "version": "0.1.8", - "resolved": "https://registry.npmmirror.com/vdirs/-/vdirs-0.1.8.tgz", - "integrity": "sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==", - "requires": { - "evtd": "^0.2.2" - } - }, - "vite": { - "version": "2.9.14", - "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.14.tgz", - "integrity": "sha512-P/UCjSpSMcE54r4mPak55hWAZPlyfS369svib/gpmz8/01L822lMPOJ/RYW6tLCe1RPvMvOsJ17erf55bKp4Hw==", - "dev": true, - "requires": { - "esbuild": "^0.14.27", - "fsevents": "~2.3.2", - "postcss": "^8.4.13", - "resolve": "^1.22.0", - "rollup": "^2.59.0" - } - }, - "vite-plugin-top-level-await": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.1.1.tgz", - "integrity": "sha512-il7YijMxjvQxx33I/mf5n+xUJscxcTqRSwPGck/jw92hUyGwjsJnIBYBcKRS+vhOtsJ5g+8xANoBEKx9G1NSEQ==", - "dev": true, - "requires": { - "@swc/core": "^1.2.147" - } - }, - "vooks": { - "version": "0.2.12", - "resolved": "https://registry.npmmirror.com/vooks/-/vooks-0.2.12.tgz", - "integrity": "sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==", - "requires": { - "evtd": "^0.2.2" - } - }, - "vue": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.37.tgz", - "integrity": "sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ==", - "requires": { - "@vue/compiler-dom": "3.2.37", - "@vue/compiler-sfc": "3.2.37", - "@vue/runtime-dom": "3.2.37", - "@vue/server-renderer": "3.2.37", - "@vue/shared": "3.2.37" - } - }, - "vue-clipboard3": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vue-clipboard3/-/vue-clipboard3-2.0.0.tgz", - "integrity": "sha512-Q9S7dzWGax7LN5iiSPcu/K1GGm2gcBBlYwmMsUc5/16N6w90cbKow3FnPmPs95sungns4yvd9/+JhbAznECS2A==", - "requires": { - "clipboard": "^2.0.6" - } - }, - "vue-codemirror": { - "version": "6.1.1", - "resolved": "https://registry.npmmirror.com/vue-codemirror/-/vue-codemirror-6.1.1.tgz", - "integrity": "sha512-rTAYo44owd282yVxKtJtnOi7ERAcXTeviwoPXjIc6K/IQYUsoDkzPvw/JDFtSP6T7Cz/2g3EHaEyeyaQCKoDMg==", - "requires": { - "@codemirror/commands": "6.x", - "@codemirror/language": "6.x", - "@codemirror/state": "6.x", - "@codemirror/view": "6.x" - } - }, - "vue-eslint-parser": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.0.3.tgz", - "integrity": "sha512-yL+ZDb+9T0ELG4VIFo/2anAOz8SvBdlqEnQnvJ3M7Scq56DvtjY0VY88bByRZB0D4J0u8olBcfrXTVONXsh4og==", - "dev": true, - "requires": { - "debug": "^4.3.4", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "lodash": "^4.17.21", - "semver": "^7.3.6" - }, - "dependencies": { - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "vue-grid-layout": { - "version": "3.0.0-beta1", - "resolved": "https://registry.npmjs.org/vue-grid-layout/-/vue-grid-layout-3.0.0-beta1.tgz", - "integrity": "sha512-MsW0yfYNtnAO/uDhfZvkP6effxSJxvhAFbIL37x6Rn3vW9xf0WHVefKaSbQMLpSq3mXnR6ut0pg2Cd5lqIIZzg==", - "requires": { - "@interactjs/actions": "^1.10.2", - "@interactjs/auto-start": "^1.10.2", - "@interactjs/dev-tools": "^1.10.2", - "@interactjs/interactjs": "^1.10.2", - "@interactjs/modifiers": "^1.10.2", - "element-resize-detector": "^1.2.1", - "mitt": "^2.1.0" - }, - "dependencies": { - "mitt": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz", - "integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==" - } - } - }, - "vue-i18n": { - "version": "9.1.10", - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.10.tgz", - "integrity": "sha512-jpr7gV5KPk4n+sSPdpZT8Qx3XzTcNDWffRlHV/cT2NUyEf+sEgTTmLvnBAibjOFJ0zsUyZlVTAWH5DDnYep+1g==", - "requires": { - "@intlify/core-base": "9.1.10", - "@intlify/shared": "9.1.10", - "@intlify/vue-devtools": "9.1.10", - "@vue/devtools-api": "^6.0.0-beta.7" - } - }, - "vue-router": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.2.tgz", - "integrity": "sha512-5BP1qXFncVRwgV/XnqzsKApdMjQPqWIpoUBdL1ynz8HyLxIX/UDAx7Ql2BjmA5CXT/p61JfZvkpiFWFpaqcfag==", - "requires": { - "@vue/devtools-api": "^6.1.4" - } - }, - "vueuc": { - "version": "0.4.51", - "resolved": "https://registry.npmmirror.com/vueuc/-/vueuc-0.4.51.tgz", - "integrity": "sha512-pLiMChM4f+W8czlIClGvGBYo656lc2Y0/mXFSCydcSmnCR1izlKPGMgiYBGjbY9FDkFG8a2HEVz7t0DNzBWbDw==", - "requires": { - "@css-render/vue3-ssr": "^0.15.10", - "@juggle/resize-observer": "^3.3.1", - "css-render": "^0.15.10", - "evtd": "^0.2.4", - "seemly": "^0.3.6", - "vdirs": "^0.1.4", - "vooks": "^0.2.4" - } - }, - "w3c-keyname": { - "version": "2.2.6", - "resolved": "https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.6.tgz", - "integrity": "sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg==" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wildcard": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz", - "integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==" - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "zrender": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.3.2.tgz", - "integrity": "sha512-8IiYdfwHj2rx0UeIGZGGU4WEVSDEdeVCaIg/fomejg1Xu6OifAL1GVzIPHg2D+MyUkbNgPWji90t0a8IDk+39w==", - "requires": { - "tslib": "2.3.0" - } - } - } -} diff --git a/tg-web/package.json b/tg-web/package.json deleted file mode 100644 index d2089fb..0000000 --- a/tg-web/package.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "name": "vue-next-admin", - "version": "2.2.0", - "description": "vue3 vite next admin template", - "author": "lyt_20201208", - "license": "MIT", - "scripts": { - "dev": "vite --force", - "build": "vite build", - "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/" - }, - "dependencies": { - "@antv/g6": "^4.8.8", - "@antv/layout": "^0.3.17", - "@antv/x6": "^2.9.4", - "@codemirror/lang-javascript": "^6.1.4", - "@codemirror/theme-one-dark": "^6.1.1", - "@element-plus/icons-vue": "^2.0.9", - "@vicons/ionicons5": "^0.12.0", - "@wangeditor/editor": "^5.1.11", - "axios": "^0.27.2", - "codemirror": "^6.0.1", - "countup.js": "^2.3.2", - "cropperjs": "^1.5.12", - "echarts": "^5.3.3", - "echarts-gl": "^2.0.9", - "echarts-wordcloud": "^2.0.0", - "element-plus": "^2.2.9", - "insert-css": "^2.0.0", - "js-cookie": "^3.0.1", - "jsplumb": "^2.15.6", - "jszip": "^3.10.0", - "less": "^4.1.3", - "less-loader": "^11.1.0", - "mitt": "^3.0.0", - "naive-ui": "^2.34.3", - "normalize.css": "^7.0.0", - "nprogress": "^0.2.0", - "pinia": "^2.0.16", - "print-js": "^1.6.0", - "qrcodejs2-fixes": "^0.0.2", - "query-string": "^7.1.1", - "screenfull": "^6.0.2", - "sortablejs": "^1.15.0", - "splitpanes": "^3.1.1", - "vue": "^3.2.37", - "vue-clipboard3": "^2.0.0", - "vue-codemirror": "^6.1.1", - "vue-grid-layout": "^3.0.0-beta1", - "vue-i18n": "^9.1.10", - "vue-router": "^4.1.2" - }, - "devDependencies": { - "@types/insert-css": "^2.0.1", - "@types/node": "^18.0.6", - "@types/nprogress": "^0.2.0", - "@types/sortablejs": "^1.13.0", - "@typescript-eslint/eslint-plugin": "^5.30.7", - "@typescript-eslint/parser": "^5.30.7", - "@vitejs/plugin-vue": "^2.3.3", - "@vue/compiler-sfc": "^3.2.37", - "dotenv": "^16.0.1", - "eslint": "^8.20.0", - "eslint-plugin-vue": "^9.2.0", - "prettier": "^2.7.1", - "sass": "^1.53.0", - "sass-loader": "^13.0.2", - "typescript": "^4.7.4", - "vite": "^2.9.14", - "vite-plugin-top-level-await": "^1.1.1", - "vue-eslint-parser": "^9.0.3" - }, - "browserslist": [ - "> 1%", - "last 2 versions", - "not dead" - ], - "bugs": { - "url": "https://gitee.com/lyt-top/vue-next-admin/issues" - }, - "engines": { - "node": ">=12.0.0", - "npm": ">= 6.0.0" - }, - "keywords": [ - "vue", - "vue3", - "vuejs/vue-next", - "element-ui", - "element-plus", - "vue-next-admin", - "next-admin" - ], - "repository": { - "type": "git", - "url": "https://gitee.com/lyt-top/vue-next-admin.git" - } -} diff --git a/tg-web/plugins.d.ts b/tg-web/plugins.d.ts deleted file mode 100644 index be0457a..0000000 --- a/tg-web/plugins.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module 'vue-grid-layout'; -declare module 'qrcodejs2-fixes'; -declare module 'splitpanes'; -declare module 'js-cookie'; diff --git a/tg-web/server.conf b/tg-web/server.conf deleted file mode 100644 index 45b7fcf..0000000 --- a/tg-web/server.conf +++ /dev/null @@ -1,8 +0,0 @@ -server { - listen 8080; - root /home/tg-flow/dist; - - location / { - index index.html; - } -} \ No newline at end of file diff --git a/tg-web/shim.d.ts b/tg-web/shim.d.ts deleted file mode 100644 index b7d1ffe..0000000 --- a/tg-web/shim.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* eslint-disable */ - -// 声明文件,*.vue 后缀的文件交给 vue 模块来处理 -declare module '*.vue' { - import type { DefineComponent } from 'vue'; - const component: DefineComponent<{}, {}, any>; - export default component; -} - -// 声明文件,定义全局变量。其它 app.config.globalProperties.xxx,使用 getCurrentInstance() 来获取 -interface Window { - nextLoading: boolean; - userInfo: any -} diff --git a/tg-web/source.d.ts b/tg-web/source.d.ts deleted file mode 100644 index 2f9c768..0000000 --- a/tg-web/source.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module '*.json'; -declare module '*.png'; -declare module '*.jpg'; -declare module '*.scss'; -declare module '*.ts'; -declare module '*.js'; diff --git a/tg-web/src/App.vue b/tg-web/src/App.vue deleted file mode 100644 index abee6fb..0000000 --- a/tg-web/src/App.vue +++ /dev/null @@ -1,122 +0,0 @@ - - - diff --git a/tg-web/src/api/request.ts b/tg-web/src/api/request.ts deleted file mode 100644 index e3a89f0..0000000 --- a/tg-web/src/api/request.ts +++ /dev/null @@ -1,42 +0,0 @@ -import axios from "axios"; -import qs from "query-string"; - -// axios.defaults.baseURL = "/api"; -// axios的封装 -export async function request( - path: string, - params: object, - method: string, - type?: number -) { - let content = "x-www-form-urlencoded"; - let data = qs.stringify(params); - if (type === 1) { - content = "json"; - data = JSON.stringify(params); - } - try { - const serve = await axios({ - method, - url: path, - withCredentials: true, - headers: { - "Content-Type": `application/${content}`, - }, - data, - }); - if (serve.data) { - return serve.data as T; - } else { - return null; - } - } catch (err: any) { - if (err.response.status === 302 || err.response.status === 301) { - // TODO 登录认证 - window.location.replace("/main"); - } - return null; - } -} - -export default request; diff --git a/tg-web/src/api/strategy/index.ts b/tg-web/src/api/strategy/index.ts deleted file mode 100644 index 5ed877a..0000000 --- a/tg-web/src/api/strategy/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './services' -export * from './types' -export * from './utils' \ No newline at end of file diff --git a/tg-web/src/api/strategy/services.ts b/tg-web/src/api/strategy/services.ts deleted file mode 100644 index 4fd5e25..0000000 --- a/tg-web/src/api/strategy/services.ts +++ /dev/null @@ -1,401 +0,0 @@ -import request from "@/api/request"; -import { - addOrUpdateWorkFlow, - deleteWorkflowI, - experimentConfigResI, - experimentHistoryVersionI, - experimentHistoryVersionParamsI, - experimentRollHistoryVersionParamsI, - experimentWorkflowNodeTypeResI, - FlowChartParamsI, - FlowChartResI, GetApolloResI, - getExperimentConfigDimensionResI, - getExperimentConfigParamsI, - globalConfigBaseContentNullI, - globalConfigBaseContentStringI, - ModuleBranchRequestI, ModuleBranchRespI, ModulesRequestI, ModulesResI, - operatorAndId, - OperatorI, - scenesConfigAppnameI, - ScenesConfigI, - scenesConfigNameI, - ScenesConfigUpdateI, - SystemConfigI, - SystemConfigUpdateI, -} from "./types"; -import { - experimentAppname, - filterData, - scenesSelectAppName, - scenesSelectName -} from "./utils"; - -export async function getWorkFlowChartByFlowIdV2(params: FlowChartParamsI) { - const res = await request( - "/api/exportWorkFlow", - params, - "POST" - ); - if (res) { - if (res.content) { - return JSON.parse(res.content); - } else { - return ""; - } - } else { - return ""; - } -} - -// 拿取系统管理中的数据 -export async function getSystemConfig(params: OperatorI) { - const res = await request( - "/api/searchAppConfig", - params, - "POST" - ); - if (res?.tag) { - return res?.content; - } - return res?.err_msg; -} - -// 更新系统管理中的数据 -export async function updateSystemConfig(params: SystemConfigUpdateI) { - const res = await request( - "/api/addOrUpdateAppConfig", - params, - "POST" - ); - if (res?.tag) { - return res; - } - return null; -} - -// 删除系统管理中的数据 -export async function deleteSystemConfig(params: operatorAndId) { - const res = await request( - "/api/deleteAppConfig", - params, - "POST" - ); - if (res?.tag) { - return res; - } - return res?.err_msg; -} - -// 导出系统管理中的部分数据 -export async function exportSystemConfig(params: operatorAndId) { - const res = await request( - "/api/exportAppConfig", - params, - "POST" - ); - if (res?.tag) { - return res; - } - return res?.err_msg; -} - -// 提交系统管理中的数据到 Apollo -export async function commitSystemConfig(params: operatorAndId, is_kflower: boolean) { - return await request( - "/api/submitAppConfig", - { - ...params, - is_kflower, - }, - "POST" - ) -} - -// 查询系统对应的 Apollo 的状态 -export async function getApolloInfo(params: operatorAndId, is_kflower: boolean) { - return await request( - "/api/getApolloInfo", - { - ...params, - is_kflower, - }, - "POST" - ) -} - - -// 添加系统管理中的数据 -export async function addSystemConfig(params: SystemConfigUpdateI) { - const res = await request( - "/api/addOrUpdateAppConfig", - params, - "POST" - ); - if (res?.tag) { - return res; - } - return res?.err_msg; -} - -// 拿到场景管理中的数据 -export async function getScenesConfig(params: OperatorI) { - const res = await request("/api/searchScene", params, "POST"); - if (res?.tag) { - return res["content"]; - } - return []; -} - -// 获取模块数据 -export async function getAppModules(params: ModulesRequestI) { - const res = await request("/api/getAppModules", params, "POST"); - if (res?.tag) { - return res["content"]; - } - return {}; -} - -// 查询系统对应分支信息 -export async function getGitBranches(params: ModuleBranchRequestI) { - const res = await request("/api/getGitBranches", params, "POST"); - if (res?.tag) { - return res["content"]; - } - return []; -} - -// 修改场景管理中的数据 -export async function updateScenesConfig(params: ScenesConfigUpdateI) { - const res = await request( - "/api/addOrUpdateScene", - params, - "POST" - ); - if (res?.tag) { - return true; - } - return res?.err_msg; -} - -// 删除场景管理中的数据 -export async function deleteScenesConfig(params: operatorAndId) { - const res = await request( - "/api/deleteScene", - params, - "POST" - ); - return !!res?.tag; - -} - -// 新增场景管理中的数据 -export async function addScenesConfig(params: ScenesConfigUpdateI) { - const res = await request( - "/api/addOrUpdateScene", - params, - "POST" - ); - if (res?.tag) { - return true; - } - return res?.err_msg; -} - -// 拿到场景管理中的系统名称数据 -export async function getScenesConfigAppname(params: OperatorI) { - const res = await request( - "/api/autoGetAPPNameMenu", - params, - "POST" - ); - if (res?.tag) { - return scenesSelectAppName(res["content"]); - } - return res?.err_msg; -} - -//拿到场景管理中的场景名称数据 -export async function getScenesConfigName(params: OperatorI) { - const res = await request( - "/api/autoGetSceneMenu", - params, - "POST" - ); - if (res?.tag) { - return scenesSelectName(res["content"]); - } - return res?.err_msg; -} - -// 拿到实验管理中的数据 -export async function getExperimentConfig(params: getExperimentConfigParamsI) { - const res = await request( - "/api/searchWorkFlow", - params, - "POST" - ); - if (res) { - if (res.tag) { - return res["content"]; - } else return res.err_msg; - } else { - return null; - } -} - -// 拿到实验管理中的scenes -export async function getExperimentScenes(types: number) { - const res = await request( - "/api/autoGetSceneMenu", - { - types, - }, - "POST" - ); - if (res?.tag) { - return scenesSelectName(res["content"]); - } - return res?.err_msg; -} - -// 拿到实验管理中的Appname -export async function getExperimentConfigAppname() { - const res = await request( - "/api/autoGetAPPNameMenu", - {}, - "POST" - ); - if (res?.tag) { - return experimentAppname(res["content"]); - } - return res?.err_msg; -} - -// 拿到实验管理中的DimensionMenu -export async function getExperimentConfigDimension(scene_name: string) { - const res = await request( - "/api/autoGetDimensionMenu", - { scene_name }, - "POST" - ); - if (res?.tag) { - return res["content"]; - } - return res?.err_msg; -} - -// 刷新实验管理中的NodeType -export async function refreshExperimentConfigNodeType(params:any) { - const res = await request( - "/api/setNodeTypeList", - params, - "POST" - ); - if (res?.tag) { - return true; - } - return res?.err_msg; -} - -// 复制实验管理中具体某个场景下的某一项 -export async function addOrUpdateWorkflow(params: addOrUpdateWorkFlow) { - const res = await request( - "/api/addOrUpdateWorkFlow", - params, - "POST" - ); - if (res) { - if (res.tag) { - return res.tag; - } else { - return res.err_msg; - } - } else { - return null; - } -} - -export async function updateWorkflowV2(params: addOrUpdateWorkFlow) { - const res = await request( - "/api/importWorkFlow", - params, - "POST" - ); - if (res) { - if (res.tag) { - return res.tag; - } else { - return res.err_msg; - } - } else { - return null; - } -} - -// 删除实验管理中具体某个场景下的某一项 -export async function deleteWorkflowData(params:deleteWorkflowI) { - const res = await request("/api/deleteWorkFlow",params,"POST") - if (res) { - if (res.tag) { - return res.tag; - } else { - return res.err_msg; - } - } else { - return null; - } -} - -// 拿到某个场景下的历史版本 -export async function getScenesHistoryVersion(params:experimentHistoryVersionParamsI) { - const res = await request("/api/searchWorkflowVersion",params,"POST") - if (res) { - if (res.tag) { - return filterData(res["content"]); - } else return res.err_msg; - } else { - return null; - } -} - -// 回滚到某个场景下的历史版本 -export async function backToHistoryVersion(params:experimentRollHistoryVersionParamsI) { - const res = await request("/api/rollBackWorkFlow",params,"POST") - if (res) { - if (res.tag) { - return res.tag; - } else { - return res.err_msg; - } - } else { - return null; - } -} - -// 导出实验管理中的某条数据 -export async function exportexperimentConfig(params:{operator:string,workflowid:string}){ - const res = await request("/api/exportWorkFlow",params,"POST") - if (res?.tag) { - return res['content']; - } - return res?.err_msg; -} - -// 导入实验管理workflow Json数据 -export async function requireWorkflowConfig(params:{workflowid:number,dataJson:string}){ - const res = await request('/api/importWorkFlow',params,"POST") - if (res?.tag) { - return true; - } - return res?.err_msg; -} - -//拿到实验管理中的nodetype -export async function getExperimentWorkflowNodeType(params:FlowChartParamsI){ - const res = await request("/api/getNodeTypeList",params,"POST") - if(res){ - return res['content'] - }else{ - return null - } -} diff --git a/tg-web/src/api/strategy/types.ts b/tg-web/src/api/strategy/types.ts deleted file mode 100644 index f164696..0000000 --- a/tg-web/src/api/strategy/types.ts +++ /dev/null @@ -1,221 +0,0 @@ -export interface OperatorI { - operator: string; -} -interface globalConfigBaseI { - err_msg: string; - tag: boolean; - typenum: number; -} - -export interface globalConfigBaseContentStringI extends globalConfigBaseI { - content: string; -} -interface ApolloNamespaceI { - id: number, -} - -interface ApolloServiceI { - id: number, - appId: number, -} - -interface ApolloResponseI { - id: number, - services: Array, - namespace: ApolloNamespaceI, - configStatus: string, -} - -export interface GetApolloResI extends globalConfigBaseI { - content: ApolloResponseI -} -export interface FlowChartParamsI extends OperatorI { - workflowid?: number; - scene_name?: string; -} - -export interface FlowChartResI extends globalConfigBaseI { - content: string | null -} - -// nodes: {label: string; id: string ...} -export interface SystemConfigI extends globalConfigBaseI { - content: Array -} - -export interface SystemConfigContentI extends SystemConfigUpdateI, createAndUpdate { -} - -export interface SystemConfigUpdateI extends operatorAndId { - app_name: string; - node_name: string; - old_id?: number; - machine_room: string; - git_url: string; -} - -export interface operatorAndId { - operator: string; - id: number; -} - -export interface ScenesConfigI extends globalConfigBaseI { - content: Array -} - -interface createAndUpdate { - create_time: string; - update_time: string; -} - -export interface ScenesConfigContentI extends ScenesConfigUpdateI { - appid: number; - bifrost_config: string; - createtime: string; - namezh: string; - updatetime: string; -} - -// ==================== Modules ==================== - -export interface ModulesRequestI extends OperatorI { - app_name: string, - project_branch: string, -} - -export interface ModulesContentI { - name: string, - type: string, - desc: string, - workflow_list: Array - scene_list: Array - create_time: string, -} - -export interface ModulesResI extends globalConfigBaseI { - content: Record> -} - -export interface ModuleBranchRequestI extends OperatorI { - app_name: string, -} - -export interface ModuleBranchRespI extends globalConfigBaseI { - content: Array -} - - -export interface ScenesConfigUpdateI { - oldid?: number; - operator: string; - id: number; - name: string; - appname: string; - expname: string; - buckettype: number; - flow_type: number; -} - -export interface globalConfigBaseContentNullI extends globalConfigBaseI { - content: null; -} -export interface scenesConfigAppnameI extends globalConfigBaseI { - content: Array -} - -export interface scenesConfigAppnameContentI { - AppId: string; - AppName: string; -} - -export interface scenesConfigNameContentI { - SceneId: string; - SceneName: string; -} - -export interface scenesConfigNameI extends globalConfigBaseI { - content: Array; -} -export interface getExperimentConfigParamsI extends OperatorI { - dimension_id: string; - scenename: string; -} - -export interface getExperimentConfigDimensionResI extends globalConfigBaseI { - content: Array; -} - -export interface getExperimentConfigDimensionResContentI { - DimensionId: string; - DimensionName: string; -} -export interface experimentConfigResI extends globalConfigBaseI { - content: Array -} - -export interface experimentConfigResContentI extends OperatorI { - configured: boolean; - createtime: string; - updatetime: string; - defult: string; - dimension_id: number; - groupname: string; - manual_slot_ids: string; - modules: string; - oldworkflowid: string; - proportion: string; - range1: string; - range2: string; - remark: string; - scene_id: number; - scenename: string; - showmodules: string; - workflowid: string; -} - -export interface addOrUpdateWorkFlow extends deleteWorkflowI { - oldworkflowid: string; - modules: string; - proportion: number; - defult: number; - remark: string; - dataJson?: string; -} - -export interface deleteWorkflowI extends experimentHistoryVersionParamsI { - workflowid: string; -} - -export interface experimentHistoryVersionI extends globalConfigBaseI { - content: Array; -} - -export interface experimentHistoryVersionParamsI extends OperatorI { - scenename: string; - dimension_id: number; -} - -export interface experimentHistoryVersionResI extends OperatorI, experimentHistoryVersionResFilterI { - scenename: string; -} - -export interface experimentHistoryVersionResFilterI { - dimension_id: number; - version_create_time: string; - version_id: number; -} - -export interface experimentRollHistoryVersionParamsI extends OperatorI { - version_id: number; - dimension_id: number; - scene_name: string; -} - -export interface experimentWorkflowNodeTypeResI extends globalConfigBaseI { - content: Array; -} - -export interface experimentWorkflowNodeTypeResContentI { - node_name: string[]; - node_type: string; -} diff --git a/tg-web/src/api/strategy/utils.ts b/tg-web/src/api/strategy/utils.ts deleted file mode 100644 index f56fdbf..0000000 --- a/tg-web/src/api/strategy/utils.ts +++ /dev/null @@ -1,115 +0,0 @@ -interface VauleAndLabelI { - value: string; - label: string; -} - -import { - scenesConfigAppnameContentI, - scenesConfigNameContentI, - experimentHistoryVersionResFilterI, - experimentHistoryVersionResI, -} from "./types"; -export function getTime(params: number): number { - let date = new Date(params * 1000); - return Number( - `${date.getFullYear()}${ - date.getMonth() + 1 < 10 - ? "0" + (date.getMonth() + 1) - : date.getMonth() + 1 - }${date.getDate() < 10 ? "0" + date.getDate() : date.getDate()}${ - date.getHours() < 10 ? "0" + date.getHours() : date.getHours() - }${date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()}${ - date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds() - }` - ); -} - -export function scenesSelectAppName( - params: Array -): Array { - const arr: Array = []; - params.forEach((p) => { - arr.push({ - label: p.AppName, - value: p.AppName, - }); - }); - arr.shift(); - return arr; -} - -export function scenesSelectName( - params: Array -): Array { - const arr: Array = []; - params.forEach((p) => { - arr.push({ - label: p.SceneName, - value: p.SceneName, - }); - }); - return arr; -} - -export function experimentAppname( - params: Array -): Array { - const arr: Array = []; - params.forEach((p) => { - arr.push({ - label: p.AppName, - value: p.AppId, - }); - }); - return arr; -} - -export function filterData( - params: Array -) :Array { - return params.map((p) => { - return { - dimension_id:p.dimension_id, - version_create_time:p.version_create_time, - version_id:p.version_id - } - }) -} - -export function algoName(params: any): Array{ - const arr: Array = []; - params.forEach((p : any) => { - if(p.AlgoName){ - arr.push({ - label:p.AlgoName, - value:p.AlgoName - }) - } - }); - return arr -} - -export function modelName(params:any[]): Array{ - const arr: Array = []; - params.forEach((p : any) => { - if(p.ModelName){ - arr.push({ - label:p.ModelName, - value:p.ModelName - }) - } - }); - return arr -} -export function appName(params:Object[]): Array{ - const arr: Array = []; - params.forEach((p : any) => { - if(p.AppName){ - arr.push({ - label:p.AppName, - value:p.AppId - }) - } - }); - return arr -} diff --git a/tg-web/src/components/g6/flow.vue b/tg-web/src/components/g6/flow.vue deleted file mode 100644 index d7154f0..0000000 --- a/tg-web/src/components/g6/flow.vue +++ /dev/null @@ -1,255 +0,0 @@ - - - diff --git a/tg-web/src/components/g6/g6.ts b/tg-web/src/components/g6/g6.ts deleted file mode 100644 index d465dc8..0000000 --- a/tg-web/src/components/g6/g6.ts +++ /dev/null @@ -1,1158 +0,0 @@ -import G6 from '@antv/g6'; -import insertCss from 'insert-css'; - -insertCss(` - .g6-component-toolbar li { - list-style-type: none !important; - } - .g6-minimap-container { - border: 1px solid #e2e2e2; - } - .g6-minimap-viewport { - border: 2px solid rgb(25, 128, 255); - } -`); - -const ICON_MAP = { - a: 'https://gw.alipayobjects.com/mdn/rms_8fd2eb/afts/img/A*0HC-SawWYUoAAAAAAAAAAABkARQnAQ', - b: 'https://gw.alipayobjects.com/mdn/rms_8fd2eb/afts/img/A*sxK0RJ1UhNkAAAAAAAAAAABkARQnAQ', -}; -const color = '#5B8FF9'; - -function drawRect(cfg: any, group: any) { - // console.log(cfg) - const r = 2; - const width = 150 - const shape = group.addShape('rect', { - attrs: { - x: 0, - y: 0, - width, - height: 60, - stroke: color, - radius: r, - cursor: 'move', - anchorPoints: [ - [0, 0.5], - [1, 0.5], - ], - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'main-box', - draggable: true, - }); - - group.addShape('rect', { - attrs: { - x: 0, - y: 0, - width, - height: 30, - fill: color, - radius: [r, r, 0, 0], - cursor: 'move', - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'title-box', - draggable: true, - }); - - // left icon - group.addShape('image', { - attrs: { - x: 4, - y: 7, - height: 16, - width: 16, - cursor: 'pointer', - img: ICON_MAP['a'], - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'node-icon', - }); - - // title text - group.addShape('text', { - attrs: { - textBaseline: 'top', - y: 9, - x: 24, - lineHeight: 20, - fontSize: 12, - fontWeight: 500, - text: 'ID:' + cfg.id, - fill: '#fff', - cursor: 'pointer', - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'title', - }); - - // The content list - // name text - group.addShape('text', { - attrs: { - textBaseline: 'top', - y: 40, - x: 10, - lineHeight: 20, - fontSize: 12, - text: fittingString(cfg.label, 130, 12), - fill: 'rgba(0,0,0, 0.4)', - cursor: 'pointer', - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: `desc`, - }); - return shape -} - -function drawTimeout(cfg: any, group: any) { - const r = 2; - const width = 150 - const shape = group.addShape('rect', { - attrs: { - x: 0, - y: 0, - width, - height: 60, - stroke: color, - radius: r, - anchorPoints: [ - [0, 0.5], - [1, 0.5], - ], - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'main-box', - draggable: true, - }); - - group.addShape('rect', { - attrs: { - x: 0, - y: 0, - width, - height: 30, - fill: color, - radius: [r, r, 0, 0], - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'title-box', - draggable: true, - }); - - // left icon - group.addShape('image', { - attrs: { - x: 4, - y: 7, - height: 16, - width: 16, - cursor: 'pointer', - img: ICON_MAP['a'], - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'node-icon', - }); - - // title text - group.addShape('text', { - attrs: { - textBaseline: 'top', - y: 9, - x: 24, - lineHeight: 20, - fontSize: 12, - fontWeight: 500, - text: 'ID:' + cfg.id, - fill: '#fff', - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'title', - }); - - // The content list - // name text - group.addShape('text', { - attrs: { - textBaseline: 'top', - y: 40, - x: 10, - lineHeight: 20, - fontSize: 12, - text: fittingString(cfg.label, 130, 12), - fill: '#666', - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: `desc`, - }); - return shape -} - -function drawCondition(cfg: any, group: any) { - const width = 80 - const shape = group.addShape('polygon', { - attrs: { - points: [ - [40, 0], - [0, 30], - [40, 60], - [80, 30], - ], - stroke: color, - fill: color, - cursor: 'move', - anchorPoints: [ - [0, 0.5], - [1, 0.5], - ], - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'main-box', - draggable: true, - }); - group.addShape('text', { - attrs: { - textBaseline: 'top', - y: 25, - x: 32, - lineHeight: 20, - fontSize: 12, - fontWeight: 500, - text: cfg.label, - fill: '#fff', - cursor: 'pointer', - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'title', - }); - return shape -} - -function drawFlow(cfg: any, group: any) { - const shape = group.addShape('circle', { - attrs: { - x: 0, - y: 0, - r: 50, - stroke: color, - cursor: 'move', - fill: color, - anchorPoints: [ - [0, 0.5], - [1, 0.5], - ], - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'main-box', - draggable: true, - }); - group.addShape('text', { - attrs: { - textBaseline: 'middle', - lineHeight: 20, - fontSize: 12, - fontWeight: 500, - textAlign: 'center', - text: cfg.label, - fill: '#fff', - cursor: 'pointer', - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'title', - }); - return shape -} - -G6.registerNode( - 'timeout', - { - draw: function drawShape(cfg: any, group: any) { - return drawTimeout(cfg, group) - }, - afterDraw(cfg, group) { - const bbox = group.getBBox(); - const anchorPoints = this.getAnchorPoints(cfg) - anchorPoints.forEach((anchorPos, i) => { - group.addShape('circle', { - attrs: { - r: 5, - x: bbox.x + bbox.width * anchorPos[0], - y: bbox.y + bbox.height * anchorPos[1], - fill: '#fff', - stroke: '#5F95FF' - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: `anchor-point`, // the name, for searching by group.find(ele => ele.get('name') === 'anchor-point') - anchorPointIdx: i, // flag the idx of the anchor-point circle - links: 0, // cache the number of edges connected to this shape - visible: false, // invisible by default, shows up when links > 1 or the node is in showAnchors state - draggable: true // allow to catch the drag events on this shape - }) - }) - }, - getAnchorPoints(cfg) { - return cfg.anchorPoints || [[0, 0.5], [0.33, 0], [0.66, 0], [1, 0.5], [0.33, 1], [0.66, 1]]; - }, - // update: function drawShape(cfg: any, item: any) { - // console.log('---',cfg, item) - // item.label = cfg.label - // } - setState(name, value, item) { - const group = item!.getContainer(); - if (name === 'selected') { - const box = group.find((element) => element.get('name') === 'main-box'); - const lineWidth = value ? 2 : 1 - box.attr('lineWidth', lineWidth) - } - if (name === 'showAnchors') { - const anchorPoints = item.getContainer().findAll(ele => ele.get('name') === 'anchor-point'); - anchorPoints.forEach(point => { - if (value || point.get('links') > 0) point.show() - else point.hide() - }) - } - } - }, -); - -G6.registerNode( - 'task', - { - draw: function drawShape(cfg: any, group: any) { - return drawRect(cfg, group) - }, - afterDraw(cfg, group) { - const bbox = group.getBBox(); - const anchorPoints = this.getAnchorPoints(cfg) - anchorPoints.forEach((anchorPos, i) => { - group.addShape('circle', { - attrs: { - r: 5, - x: bbox.x + bbox.width * anchorPos[0], - y: bbox.y + bbox.height * anchorPos[1], - fill: '#fff', - stroke: '#5F95FF' - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: `anchor-point`, // the name, for searching by group.find(ele => ele.get('name') === 'anchor-point') - anchorPointIdx: i, // flag the idx of the anchor-point circle - links: 0, // cache the number of edges connected to this shape - visible: false, // invisible by default, shows up when links > 1 or the node is in showAnchors state - draggable: true // allow to catch the drag events on this shape - }) - }) - }, - getAnchorPoints(cfg) { - return cfg.anchorPoints || [[0, 0.5], [0.33, 0], [0.66, 0], [1, 0.5], [0.33, 1], [0.66, 1]]; - }, - // update: function drawShape(cfg: any, item: any) { - // console.log('---',cfg, item) - // item.label = cfg.label - // } - setState(name, value, item) { - const group = item!.getContainer(); - if (name === 'selected') { - const box = group.find((element) => element.get('name') === 'main-box'); - const lineWidth = value ? 2 : 1 - box.attr('lineWidth', lineWidth) - } - if (name === 'showAnchors') { - const anchorPoints = item.getContainer().findAll(ele => ele.get('name') === 'anchor-point'); - anchorPoints.forEach(point => { - if (value || point.get('links') > 0) point.show() - else point.hide() - }) - } - } - }, -); - -G6.registerNode( - 'condition', - { - draw: function drawShape(cfg: any, group: any) { - return drawCondition(cfg, group) - }, - afterDraw(cfg, group) { - const bbox = group.getBBox(); - const anchorPoints = this.getAnchorPoints(cfg) - anchorPoints.forEach((anchorPos, i) => { - group.addShape('circle', { - attrs: { - r: 5, - x: bbox.x + bbox.width * anchorPos[0], - y: bbox.y + bbox.height * anchorPos[1], - fill: '#fff', - stroke: '#5F95FF' - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: `anchor-point`, // the name, for searching by group.find(ele => ele.get('name') === 'anchor-point') - anchorPointIdx: i, // flag the idx of the anchor-point circle - links: 0, // cache the number of edges connected to this shape - visible: false, // invisible by default, shows up when links > 1 or the node is in showAnchors state - draggable: true // allow to catch the drag events on this shape - }) - }) - }, - getAnchorPoints(cfg) { - return cfg.anchorPoints || [[0, 0.5], [0.33, 0], [0.66, 0], [1, 0.5], [0.33, 1], [0.66, 1]]; - }, - // update: function drawShape(cfg: any, item: any) { - // console.log('---',cfg, item) - // item.label = cfg.label - // } - setState(name, value, item) { - const group = item!.getContainer(); - if (name === 'selected') { - const box = group.find((element) => element.get('name') === 'main-box'); - const lineWidth = value ? 2 : 1 - box.attr('lineWidth', lineWidth) - } - if (name === 'showAnchors') { - const anchorPoints = item.getContainer().findAll(ele => ele.get('name') === 'anchor-point'); - anchorPoints.forEach(point => { - if (value || point.get('links') > 0) point.show() - else point.hide() - }) - } - } - }, -); - -G6.registerNode( - 'flow', - { - draw: function drawShape(cfg: any, group: any) { - return drawFlow(cfg, group) - }, - afterDraw(cfg, group) { - const bbox = group.getBBox(); - const anchorPoints = this.getAnchorPoints(cfg) - anchorPoints.forEach((anchorPos, i) => { - group.addShape('circle', { - attrs: { - r: 5, - x: bbox.x + bbox.width * anchorPos[0], - y: bbox.y + bbox.height * anchorPos[1], - fill: '#fff', - stroke: '#5F95FF' - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: `anchor-point`, // the name, for searching by group.find(ele => ele.get('name') === 'anchor-point') - anchorPointIdx: i, // flag the idx of the anchor-point circle - links: 0, // cache the number of edges connected to this shape - visible: false, // invisible by default, shows up when links > 1 or the node is in showAnchors state - draggable: true // allow to catch the drag events on this shape - }) - }) - }, - getAnchorPoints(cfg) { - return cfg.anchorPoints || [[0, 0.5], [0.33, 0], [0.66, 0], [1, 0.5], [0.33, 1], [0.66, 1]]; - }, - // update: function drawShape(cfg: any, item: any) { - // console.log('---',cfg, item) - // item.label = cfg.label - // } - setState(name, value, item) { - const group = item!.getContainer(); - if (name === 'selected') { - const box = group.find((element) => element.get('name') === 'main-box'); - const lineWidth = value ? 2 : 1 - box.attr('lineWidth', lineWidth) - } - if (name === 'showAnchors') { - const anchorPoints = item.getContainer().findAll(ele => ele.get('name') === 'anchor-point'); - anchorPoints.forEach(point => { - if (value || point.get('links') > 0) point.show() - else point.hide() - }) - } - } - }, -); - -G6.registerNode( - 'card-node', - { - draw: function drawShape(cfg: any, group: any) { - return drawRect(cfg, group) - }, - afterDraw(cfg, group) { - const bbox = group.getBBox(); - const anchorPoints = this.getAnchorPoints(cfg) - anchorPoints.forEach((anchorPos, i) => { - group.addShape('circle', { - attrs: { - r: 5, - x: bbox.x + bbox.width * anchorPos[0], - y: bbox.y + bbox.height * anchorPos[1], - fill: '#fff', - stroke: '#5F95FF' - }, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: `anchor-point`, // the name, for searching by group.find(ele => ele.get('name') === 'anchor-point') - anchorPointIdx: i, // flag the idx of the anchor-point circle - links: 0, // cache the number of edges connected to this shape - visible: false, // invisible by default, shows up when links > 1 or the node is in showAnchors state - draggable: true // allow to catch the drag events on this shape - }) - }) - }, - getAnchorPoints(cfg) { - return cfg.anchorPoints || [[0, 0.5], [0.33, 0], [0.66, 0], [1, 0.5], [0.33, 1], [0.66, 1]]; - }, - // update: function drawShape(cfg: any, item: any) { - // console.log('---',cfg, item) - // item.label = cfg.label - // } - setState(name, value, item) { - const group = item!.getContainer(); - if (name === 'selected') { - const box = group.find((element) => element.get('name') === 'main-box'); - const lineWidth = value ? 2 : 1 - box.attr('lineWidth', lineWidth) - } - if (name === 'showAnchors') { - const anchorPoints = item.getContainer().findAll(ele => ele.get('name') === 'anchor-point'); - anchorPoints.forEach(point => { - if (value || point.get('links') > 0) point.show() - else point.hide() - }) - } - } - }, -); - -const collapseIcon = (x, y, r) => { - return [ - ['M', x - r, y], - ['a', r, r, 0, 1, 0, r * 2, 0], - ['a', r, r, 0, 1, 0, -r * 2, 0], - ['M', x - r + 4, y], - ['L', x - r + 2 * r - 4, y], - ]; -}; - -const expandIcon = (x, y, r) => { - return [ - ['M', x - r, y], - ['a', r, r, 0, 1, 0, r * 2, 0], - ['a', r, r, 0, 1, 0, -r * 2, 0], - ['M', x - r + 4, y], - ['L', x - r + 2 * r - 4, y], - ['M', x - r + r, y - r + 4], - ['L', x, y + r - 4], - ]; -}; - -G6.registerCombo( - 'cCircle', - { - drawShape: function draw(cfg, group) { - const self = this; - // Get the shape style, where the style.r corresponds to the R in the Illustration of Built-in Rect Combo - const style = self.getShapeStyle(cfg); - const circle = group.addShape('circle', { - attrs: { - ...style, - x: 0, - y: 0, - // r: 100, - r: style.r, - fill: '#fff', - // fill: '#000', - }, - draggable: true, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'combo-keyShape', - }); - // Add the marker on the bottom - const marker = group.addShape('marker', { - attrs: { - ...style, - fill: '#fff', - // fill: '#000', - opacity: 1, - x: 0, - // y: 100, - y: style.r, - r: 10, - symbol: collapseIcon, - }, - draggable: true, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'combo-marker-shape', - }); - - return circle; - }, - // Define the updating logic for the marker - afterUpdate: function afterUpdate(cfg, combo) { - const self = this; - // Get the shape style, where the style.r corresponds to the R in the Illustration of Built-in Rect Combo - const style = self.getShapeStyle(cfg); - const group = combo.get('group'); - // Find the marker shape in the graphics group of the Combo - const marker = group.find((ele) => ele.get('name') === 'combo-marker-shape'); - // Update the marker shape - marker.attr({ - x: 0, - y: style.r, - // The property 'collapsed' in the combo data represents the collapsing state of the Combo - // Update the symbol according to 'collapsed' - symbol: cfg.collapsed ? expandIcon : collapseIcon, - }); - }, - }, - 'circle', -); - -G6.registerCombo( - 'cRect', - { - drawShape: function draw(cfg, group) { - const self = this; - cfg.padding = cfg.padding || [3, 3, 3, 3]; - // Get the shape style, where the style.r corresponds to the R in the Illustration of Built-in Rect Combo - const style = self.getShapeStyle(cfg); - const rect = group.addShape('rect', { - attrs: { - ...style, - x: - (cfg.padding[3] - cfg.padding[1]) / 2, - y: - (cfg.padding[0] - cfg.padding[2]) / 2, - width: style.width, - height: style.height, - fill: '#fff', - // fill: '#000', - }, - draggable: true, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'combo-keyShape', - }); - // Add the marker on the bottom - const marker = group.addShape('marker', { - attrs: { - ...style, - fill: '#fff', - // fill: '#000', - opacity: 1, - x: cfg.style.width / 2 + cfg.padding[1], - y: (cfg.padding[2] - cfg.padding[0]) / 2, - r: 10, - symbol: collapseIcon, - }, - draggable: true, - // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type - name: 'combo-marker-shape', - }); - - return rect; - }, - // Define the updating logic for the marker - afterUpdate: function afterUpdate(cfg, combo) { - const group = combo.get('group'); - // Find the circle shape in the graphics group of the Combo by name - const marker = group.find((ele) => ele.get('name') === 'combo-marker-shape'); - // Update the position of the right circle - marker.attr({ - // cfg.style.width and cfg.style.heigth correspond to the innerWidth and innerHeight in the figure of Illustration of Built-in Rect Combo - x: cfg.style.width / 2 + cfg.padding[1], - y: (cfg.padding[2] - cfg.padding[0]) / 2, - // The property 'collapsed' in the combo data represents the collapsing state of the Combo - // Update the symbol according to 'collapsed' - symbol: cfg.collapsed ? expandIcon : collapseIcon, - }); - }, - }, - 'rect', -); - -const fittingString = (str: string, maxWidth: number, fontSize: number) => { - if (!str) { - return '' - } - const ellipsis = "..."; - const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0]; - let currentWidth = 0; - let res = str; - const pattern = new RegExp("[\u4E00-\u9FA5]+"); // distinguish the Chinese charactors and letters - str.split("").forEach((letter, i) => { - if (currentWidth > maxWidth - ellipsisLength) return; - if (pattern.test(letter)) { - // Chinese charactors - currentWidth += fontSize; - } else { - // get the width of single letter according to the fontSize - currentWidth += G6.Util.getLetterWidth(letter, fontSize); - } - if (currentWidth > maxWidth - ellipsisLength) { - res = `${str.substr(0, i)}${ellipsis}`; - } - }); - return res; -}; - -const processParallelEdgesOnAnchorPoint = ( - edges, - offsetDiff = 15, - multiEdgeType = 'cubic-horizontal', - singleEdgeType = undefined, - loopEdgeType = undefined -) => { - const len = edges.length; - const cod = offsetDiff * 2; - const loopPosition = [ - 'top', - 'top-right', - 'right', - 'bottom-right', - 'bottom', - 'bottom-left', - 'left', - 'top-left', - ]; - const edgeMap = {}; - const tags = []; - const reverses = {}; - for (let i = 0; i < len; i++) { - const edge = edges[i]; - const { source, target, sourceAnchor, targetAnchor } = edge; - const sourceTarget = `${source}|${sourceAnchor}-${target}|${targetAnchor}`; - - if (tags[i]) continue; - if (!edgeMap[sourceTarget]) { - edgeMap[sourceTarget] = []; - } - tags[i] = true; - edgeMap[sourceTarget].push(edge); - for (let j = 0; j < len; j++) { - if (i === j) continue; - const sedge = edges[j]; - const { source: src, target: dst, sourceAnchor: srcAnchor, targetAnchor: dstAnchor } = sedge; - - // 两个节点之间共同的边 - // 第一条的source = 第二条的target - // 第一条的target = 第二条的source - if (!tags[j]) { - if (source === dst && sourceAnchor === dstAnchor - && target === src && targetAnchor === srcAnchor) { - edgeMap[sourceTarget].push(sedge); - tags[j] = true; - reverses[`${src}|${srcAnchor}|${dst}|${dstAnchor}|${edgeMap[sourceTarget].length - 1}`] = true; - } else if (source === src && sourceAnchor === srcAnchor - && target === dst && targetAnchor === dstAnchor) { - edgeMap[sourceTarget].push(sedge); - tags[j] = true; - } - } - } - } - - for (const key in edgeMap) { - const arcEdges = edgeMap[key]; - const { length } = arcEdges; - for (let k = 0; k < length; k++) { - const current = arcEdges[k]; - if (current.source === current.target) { - if (loopEdgeType) current.type = loopEdgeType; - // 超过8条自环边,则需要重新处理 - current.loopCfg = { - position: loopPosition[k % 8], - dist: Math.floor(k / 8) * 20 + 50, - }; - continue; - } - if (length === 1 && singleEdgeType && (current.source !== current.target || current.sourceAnchor !== current.targetAnchor)) { - current.type = singleEdgeType; - continue; - } - current.type = multiEdgeType; - const sign = - (k % 2 === 0 ? 1 : -1) * (reverses[`${current.source}|${current.sourceAnchor}|${current.target}|${current.targetAnchor}|${k}`] ? -1 : 1); - if (length % 2 === 1) { - current.curveOffset = sign * Math.ceil(k / 2) * cod; - } else { - current.curveOffset = sign * (Math.floor(k / 2) * cod + offsetDiff); - } - } - } - return edges; -}; - -export function renderFlow(containerId: string, nodes: any, edges: any, combos: any, events: any, editabel: boolean) { - const container = document.getElementById(containerId); - const width = container!.scrollWidth; - const height = container!.scrollHeight; - const toolbar = new G6.ToolBar({ - container: 'g6-toolbar', - position: { x: 10, y: 10 }, - }); - const minimap = new G6.Minimap({ - container: 'g6-minimap', - size: [150, 100] - }); - let sourceAnchorIdx, targetAnchorIdx - const graph = new G6.Graph({ - container: containerId, - width: width - 15, - height, - animate: true, - fitView: false, - fitCenter: false, - groupByTypes: false, - plugins: [toolbar, minimap], - // 设置为true,启用 redo & undo 栈功能 - enabledStack: true, - modes: { - default: [ - 'drag-canvas', 'zoom-canvas', 'drag-combo', - { - type: 'drag-node', - shouldBegin: e => { - if (e.target.get('name') === 'anchor-point') return false; - if(e.item?._cfg?.model?.hasOwnProperty('comboId') && e.item?._cfg?.model?.comboId !== undefined) return false; - return true; - } - }, - { - type: 'create-edge', - trigger: 'drag', // set the trigger to be drag to make the create-edge triggered by drag - shouldBegin: e => { - // avoid beginning at other shapes on the node - if (e.target && e.target.get('name') !== 'anchor-point') return false; - sourceAnchorIdx = e.target.get('anchorPointIdx'); - e.target.set('links', e.target.get('links') + 1); // cache the number of edge connected to this anchor-point circle - return true; - }, - shouldEnd: e => { - // avoid ending at other shapes on the node - if (e.target && e.target.get('name') !== 'anchor-point') return false; - if (e.target) { - targetAnchorIdx = e.target.get('anchorPointIdx'); - e.target.set('links', e.target.get('links') + 1); // cache the number of edge connected to this anchor-point circle - return true; - } - targetAnchorIdx = undefined; - return true; - }, - }, - ], - }, - // layout: { - // // type: 'dagre', - // // rankdir: 'LR', - // // align: 'UL', - // // controlPoints: false, - // // nodesepFunc: () => 20, - // // ranksepFunc: () => 50, - // }, - defaultNode: { - type: 'card-node', - anchorPoints: [[0, 0.5], [1, 0.5]] - }, - defaultEdge: { - type: 'cubic-horizontal', - size: 1, - style: { - stroke: '#AAB7C4', - endArrow: { - // path: G6.Arrow.circle(3, 2), - path: G6.Arrow.vee(6, 10, 2), - d: 2, - fill: '#AAB7C4', - }, - radius: 20, - }, - }, - defaultCombo: { - type: 'cRect', - // type: 'cCircle', - style:{ - lineWidth: 3, - stroke: color, - }, - fixCollapseSize: 5, - labelCfg: { - /* label's offset to the keyShape */ - refY: -15, - refX: -5, - // position: 'top', - style: { - fontSize: 12, - }, - }, - }, - nodeStateStyles: { - hover: { - lineWidth: 5, - stroke: '#000', - fill: '#000' - }, - }, - }); - const newNodes: any = [] - nodes.map((node: any) => { - const n: any = {} - n.id = node.id - n.label = node.label - n.data = node - n.type = node.type - n.x = node.x - n.y = node.y - n.comboId = node.comboId - newNodes.push(n) - }) - const newEdeges: any = [] - edges.map((edge: any) => { - newEdeges.push({ - source: edge.source, - target: edge.target, - data: edge, - comboId: edge.comboId, - }) - }) - const newCombos: any = [] - combos.map((combo: any) => { - newCombos.push({ - id: combo.id, - label: combo.label, - collapsed: combo.collapsed, - }) - }) - // console.log(JSON.stringify(newNodes)) - graph.data({ nodes: newNodes, edges: newEdeges, combos: newCombos}); - graph.node((node) => { - const data: any = node.data - // if (data.type === 'condition') { - if (data.type === 'task') { - node.type = 'task' - } - if (data.type === 'timeout') { - node.type = 'timeout' - } - if (data.type === 'condition') { - node.type = 'condition' - } - if (data.type === 'flow') { - node.type = 'flow' - } - return node - }); - graph.render(); - - // const temp_nodes = graph.getNodes(); - // for (let temp_k in temp_nodes) { - // const temp_node = temp_nodes[temp_k] - // console.log(temp_node) - // // afterDrawCircle(temp_node._cfg, temp_node._cfg?.group) - // } - - - graph.moveTo(0, graph.getHeight() / 2 - 75) - - graph.on('click', () => { - }); - graph.on('node:click', (evt: any) => { - const { item } = evt; - console.log(item) - graph.getNodes().forEach((node) => { - graph.clearItemStates(node); - }); - setTimeout(() => graph.setItemState(item, 'selected', true)); - if (editabel){ - events['click'](item) - } - - }); - graph.on('edge:mouseenter', (evt: any) => { - const { item } = evt; - // console.log(item) - graph.getEdges().forEach((edge) => { - graph.clearItemStates(edge); - }); - setTimeout(() => graph.setItemState(item, 'selected', true)); - // events['clickEdge'](item) - }); - - graph.on('edge:mouseleave', (evt: any) => { - const { item } = evt; - // console.log(item) - graph.getEdges().forEach((edge) => { - graph.clearItemStates(edge); - }); - setTimeout(() => graph.setItemState(item, 'selected', false)); - // events['clickEdge'](item) - }); - - // after drag from the first node, the edge is created, update the sourceAnchor - graph.on('afteradditem', e => { - if (e.item && e.item.getType() === 'edge') { - graph.updateItem(e.item, { - sourceAnchor: sourceAnchorIdx - }); - } - }) - - // if create-edge is canceled before ending, update the 'links' on the anchor-point circles - graph.on('afterremoveitem', e => { - if (e.item && e.item.source && e.item.target) { - const sourceNode = graph.findById(e.item.source); - const targetNode = graph.findById(e.item.target); - const { sourceAnchor, targetAnchor } = e.item; - if (sourceNode && !isNaN(sourceAnchor)) { - const sourceAnchorShape = sourceNode.getContainer().find(ele => (ele.get('name') === 'anchor-point' && ele.get('anchorPointIdx') === sourceAnchor)); - sourceAnchorShape.set('links', sourceAnchorShape.get('links') - 1); - } - if (targetNode && !isNaN(targetAnchor)) { - const targetAnchorShape = targetNode.getContainer().find(ele => (ele.get('name') === 'anchor-point' && ele.get('anchorPointIdx') === targetAnchor)); - targetAnchorShape.set('links', targetAnchorShape.get('links') - 1); - } - } - }) - - graph.on('aftercreateedge', (e) => { - graph.updateItem(e.edge, { - sourceAnchor: sourceAnchorIdx, - targetAnchor: targetAnchorIdx - }) - - // update the curveOffset for parallel edges - const edges = graph.save().edges; - processParallelEdgesOnAnchorPoint(edges); - graph.getEdges().forEach((edge, i) => { - graph.updateItem(edge, { - curveOffset: edges[i].curveOffset, - curvePosition: edges[i].curvePosition, - }); - }); - }); - - const dataChange = () => { - // const nodes: any = [], edges: any = [] - // graph.getNodes().map(node => { - // nodes.push(node.getModel()) - // }) - // graph.getEdges().map(edge => { - // edges.push(edge.getModel()) - // }) - // const data = { nodes, edges } - const data: any = graph.save() - const nodes: any = [] - const edges: any = [] - data.nodes.forEach((node: any) => { - nodes.push(Object.assign({}, {...node})) - }) - data.edges.forEach((edge: any) => { - edges.push(Object.assign({}, {...edge})) - }) - - const newData = {nodes, edges} - if (editabel){ - events['datachange'](newData) - } - // console.log('kkkkkkk',newData) - } - - graph.on('afterupdateitem', (e) => { - dataChange() - }); - - graph.on('stackchange', (e) => { - dataChange() - }); - - graph.on('afterrender', (e) => { - dataChange() - }); - - // some listeners to control the state of nodes to show and hide anchor-point circles - graph.on('node:mouseenter', e => { - graph.setItemState(e.item, 'showAnchors', true); - // setShowState('showAnchors', true, e.item); - }); - graph.on('node:mouseleave', e => { - graph.setItemState(e.item, 'showAnchors', false); - // setShowState('showAnchors', false, e.item); - }) - graph.on('node:dragenter', e => { - graph.setItemState(e.item, 'showAnchors', true); - // setShowState('showAnchors', true, e.item); - }); - graph.on('node:dragleave', e => { - graph.setItemState(e.item, 'showAnchors', false); - // setShowState('showAnchors', false, e.item); - }); - graph.on('node:dragstart', e => { - graph.setItemState(e.item, 'showAnchors', true); - // setShowState('showAnchors', true, e.item); - }); - graph.on('node:dragout', e => { - graph.setItemState(e.item, 'showAnchors', false); - // setShowState('showAnchors', false, e.item); - }); - - graph.on('combo:mouseenter', (evt) => { - const { item } = evt; - graph.setItemState(item, 'active', true); - }); - - graph.on('combo:mouseleave', (evt) => { - const { item } = evt; - graph.setItemState(item, 'active', false); - }); - // graph.on('combo:click', (evt) => { - // const { item } = evt; - // graph.setItemState(item, 'selected', true); - // }); - // graph.on('canvas:click', (evt) => { - // graph.getCombos().forEach((combo) => { - // graph.clearItemStates(combo); - // }); - // }); - - graph.on('combo:click', (e) => { - if (e.target.get('name') === 'combo-marker-shape') { - // graph.collapseExpandCombo(e.item.getModel().id); - graph.collapseExpandCombo(e.item); - if (graph.get('layout')) graph.layout(); - else graph.refreshPositions(); - } - }); - - - graph.on('canvas:click', (evt) => { - graph.getEdges().forEach((edge) => { - graph.clearItemStates(edge); - }); - graph.getNodes().forEach((node) => { - graph.clearItemStates(node); - }); - }); - - graph.on('keyup', (evt) => { - const srcEleTag = evt.srcElement!.tagName - console.log(srcEleTag) - if (evt.key === 'Backspace' && editabel && srcEleTag === 'BODY') { - console.log(evt) - graph.getEdges().forEach((edge) => { - edge.getStates().includes('selected') && graph.removeItem(edge); - }); - graph.getNodes().forEach((node) => { - node.getStates().includes('selected') && graph.removeItem(node); - }); - dataChange() - } - }) - - if (typeof window !== 'undefined') - window.onresize = () => { - if (!graph || graph.get('destroyed')) return; - if (!container || !container.scrollWidth || !container.scrollHeight) return; - graph.changeSize(container.scrollWidth, container.scrollHeight); - }; - return graph -} - - diff --git a/tg-web/src/components/svgIcon/index.vue b/tg-web/src/components/svgIcon/index.vue deleted file mode 100644 index f51c532..0000000 --- a/tg-web/src/components/svgIcon/index.vue +++ /dev/null @@ -1,42 +0,0 @@ - diff --git a/tg-web/src/config.ts b/tg-web/src/config.ts deleted file mode 100644 index 7c0bc8e..0000000 --- a/tg-web/src/config.ts +++ /dev/null @@ -1,5 +0,0 @@ -const baseURL = import.meta.env.VITE_API_BASE_URL - -export { - baseURL, -} \ No newline at end of file diff --git a/tg-web/src/i18n/index.ts b/tg-web/src/i18n/index.ts deleted file mode 100644 index a45e72c..0000000 --- a/tg-web/src/i18n/index.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { createI18n } from 'vue-i18n'; -import pinia from '/@/stores/index'; -import { storeToRefs } from 'pinia'; -import { useThemeConfig } from '/@/stores/themeConfig'; -import zhcnLocale from 'element-plus/lib/locale/lang/zh-cn'; -import enLocale from 'element-plus/lib/locale/lang/en'; -import zhtwLocale from 'element-plus/lib/locale/lang/zh-tw'; - -import nextZhcn from '/@/i18n/lang/zh-cn'; -import nextEn from '/@/i18n/lang/en'; -import nextZhtw from '/@/i18n/lang/zh-tw'; - -import pagesLoginZhcn from '/@/i18n/pages/login/zh-cn'; -import pagesLoginEn from '/@/i18n/pages/login/en'; -import pagesLoginZhtw from '/@/i18n/pages/login/zh-tw'; -import pagesFormI18nZhcn from '/@/i18n/pages/formI18n/zh-cn'; -import pagesFormI18nEn from '/@/i18n/pages/formI18n/en'; -import pagesFormI18nZhtw from '/@/i18n/pages/formI18n/zh-tw'; - -// 定义语言国际化内容 -/** - * 说明: - * /src/i18n/lang 下的 ts 为框架的国际化内容 - * /src/i18n/pages 下的 ts 为各界面的国际化内容 - */ -const messages = { - [zhcnLocale.name]: { - ...zhcnLocale, - message: { - ...nextZhcn, - ...pagesLoginZhcn, - ...pagesFormI18nZhcn, - }, - }, - [enLocale.name]: { - ...enLocale, - message: { - ...nextEn, - ...pagesLoginEn, - ...pagesFormI18nEn, - }, - }, - [zhtwLocale.name]: { - ...zhtwLocale, - message: { - ...nextZhtw, - ...pagesLoginZhtw, - ...pagesFormI18nZhtw, - }, - }, -}; - -// 读取 pinia 默认语言 -const stores = useThemeConfig(pinia); -const { themeConfig } = storeToRefs(stores); - -// 导出语言国际化 -// https://vue-i18n.intlify.dev/guide/essentials/fallback.html#explicit-fallback-with-one-locale -export const i18n = createI18n({ - silentTranslationWarn: true, - missingWarn: false, - silentFallbackWarn: true, - fallbackWarn: false, - locale: themeConfig.value.globalI18n, - fallbackLocale: zhcnLocale.name, - messages, -}); diff --git a/tg-web/src/i18n/lang/en.ts b/tg-web/src/i18n/lang/en.ts deleted file mode 100644 index 46ae830..0000000 --- a/tg-web/src/i18n/lang/en.ts +++ /dev/null @@ -1,180 +0,0 @@ -// 定义内容 -export default { - router: { - home: 'home', - system: 'system', - systemMenu: 'systemMenu', - systemRole: 'systemRole', - systemUser: 'systemUser', - systemDept: 'systemDept', - systemDic: 'systemDic', - limits: 'limits', - limitsFrontEnd: 'FrontEnd', - limitsFrontEndPage: 'FrontEndPage', - limitsFrontEndBtn: 'FrontEndBtn', - limitsBackEnd: 'BackEnd', - limitsBackEndEndPage: 'BackEndEndPage', - menu: 'menu', - menu1: 'menu1', - menu11: 'menu11', - menu12: 'menu12', - menu121: 'menu121', - menu122: 'menu122', - menu13: 'menu13', - menu2: 'menu2', - funIndex: 'function', - funTagsView: 'funTagsView', - funCountup: 'countup', - funWangEditor: 'wangEditor', - funCropper: 'cropper', - funQrcode: 'qrcode', - funEchartsMap: 'EchartsMap', - funPrintJs: 'PrintJs', - funClipboard: 'Copy cut', - funGridLayout: 'Drag layout', - funSplitpanes: 'Pane splitter', - funDragVerify: 'Validator', - pagesIndex: 'pages', - pagesFiltering: 'Filtering', - pagesFilteringDetails: 'FilteringDetails', - pagesFilteringDetails1: 'FilteringDetails1', - pagesIocnfont: 'iconfont icon', - pagesElement: 'element icon', - pagesAwesome: 'awesome icon', - pagesFormAdapt: 'FormAdapt', - pagesTableRules: 'pagesTableRules', - pagesFormI18n: 'FormI18n', - pagesFormRules: 'Multi form validation', - pagesDynamicForm: 'Dynamic complex form', - pagesWorkflow: 'Workflow', - pagesListAdapt: 'ListAdapt', - pagesWaterfall: 'Waterfall', - pagesSteps: 'Steps', - pagesPreview: 'Large preview', - pagesWaves: 'Wave effect', - pagesTree: 'tree alter table', - pagesDrag: 'Drag command', - pagesLazyImg: 'Image lazy loading', - makeIndex: 'makeIndex', - makeSelector: 'Icon selector', - makeNoticeBar: 'notification bar', - makeSvgDemo: 'Svgicon demo', - paramsIndex: 'Routing parameters', - paramsCommon: 'General routing', - paramsDynamic: 'Dynamic routing', - paramsCommonDetails: 'General routing details', - paramsDynamicDetails: 'Dynamic routing details', - chartIndex: 'chartIndex', - visualizingIndex: 'visualizingIndex', - visualizingLinkDemo1: 'visualizingLinkDemo1', - visualizingLinkDemo2: 'visualizingLinkDemo2', - personal: 'personal', - tools: 'tools', - layoutLinkView: 'LinkView', - layoutIfameView: 'IfameView', - }, - staticRoutes: { - signIn: 'signIn', - notFound: 'notFound', - noPower: 'noPower', - }, - user: { - title0: 'Component size', - title1: 'Language switching', - title2: 'Menu search', - title3: 'Layout configuration', - title4: 'news', - title5: 'Full screen on', - title6: 'Full screen off', - dropdownLarge: 'large', - dropdownDefault: 'default', - dropdownSmall: 'small', - dropdown1: 'home page', - dropdown2: 'Personal Center', - dropdown3: '404', - dropdown4: '401', - dropdown5: 'Log out', - dropdown6: 'Code warehouse', - searchPlaceholder: 'Menu search: support Chinese, routing path', - newTitle: 'notice', - newBtn: 'All read', - newGo: 'Go to the notification center', - newDesc: 'No notice', - logOutTitle: 'Tips', - logOutMessage: 'This operation will log out. Do you want to continue?', - logOutConfirm: 'determine', - logOutCancel: 'cancel', - logOutExit: 'Exiting', - }, - tagsView: { - refresh: 'refresh', - close: 'close', - closeOther: 'closeOther', - closeAll: 'closeAll', - fullscreen: 'fullscreen', - closeFullscreen: 'closeFullscreen', - }, - notFound: { - foundTitle: 'Wrong address input, please re-enter the address~', - foundMsg: 'You can check the web address first, and then re-enter or give us feedback.', - foundBtn: 'Back to home page', - }, - noAccess: { - accessTitle: 'You are not authorized to operate~', - accessMsg: 'Contact information: add QQ group discussion 665452019', - accessBtn: 'Reauthorization', - }, - layout: { - configTitle: 'Layout configuration', - oneTitle: 'Global Themes', - twoTopTitle: 'top bar set up', - twoMenuTitle: 'Menu set up', - twoColumnsTitle: 'Columns set up', - twoTopBar: 'Top bar background', - twoTopBarColor: 'Top bar default font color', - twoIsTopBarColorGradual: 'Top bar gradient', - twoMenuBar: 'Menu background', - twoMenuBarColor: 'Menu default font color', - twoIsMenuBarColorGradual: 'Menu gradient', - twoColumnsMenuBar: 'Column menu background', - twoColumnsMenuBarColor: 'Default font color bar menu', - twoIsColumnsMenuBarColorGradual: 'Column gradient', - threeTitle: 'Interface settings', - threeIsCollapse: 'Menu horizontal collapse', - threeIsUniqueOpened: 'Menu accordion', - threeIsFixedHeader: 'Fixed header', - threeIsClassicSplitMenu: 'Classic layout split menu', - threeIsLockScreen: 'Open the lock screen', - threeLockScreenTime: 'screen locking(s/s)', - fourTitle: 'Interface display', - fourIsShowLogo: 'Sidebar logo', - fourIsBreadcrumb: 'Open breadcrumb', - fourIsBreadcrumbIcon: 'Open breadcrumb icon', - fourIsTagsview: 'Open tagsview', - fourIsTagsviewIcon: 'Open tagsview Icon', - fourIsCacheTagsView: 'Enable tagsview cache', - fourIsSortableTagsView: 'Enable tagsview drag', - fourIsShareTagsView: 'Enable tagsview sharing', - fourIsFooter: 'Open footer', - fourIsGrayscale: 'Grey model', - fourIsInvert: 'Color weak mode', - fourIsDark: 'Dark Mode', - fourIsWartermark: 'Turn on watermark', - fourWartermarkText: 'Watermark copy', - fiveTitle: 'Other settings', - fiveTagsStyle: 'Tagsview style', - fiveAnimation: 'page animation', - fiveColumnsAsideStyle: 'Column style', - fiveColumnsAsideLayout: 'Column layout', - sixTitle: 'Layout switch', - sixDefaults: 'One', - sixClassic: 'Two', - sixTransverse: 'Three', - sixColumns: 'Four', - tipText: 'Click the button below to copy the layout configuration to `/src/stores/themeConfig.ts` It has been modified in.', - copyText: 'replication configuration', - resetText: 'restore default', - copyTextSuccess: 'Copy succeeded!', - copyTextError: 'Copy failed!', - }, -}; diff --git a/tg-web/src/i18n/lang/zh-cn.ts b/tg-web/src/i18n/lang/zh-cn.ts deleted file mode 100644 index 79ef328..0000000 --- a/tg-web/src/i18n/lang/zh-cn.ts +++ /dev/null @@ -1,180 +0,0 @@ -// 定义内容 -export default { - router: { - home: '首页', - system: '系统设置', - systemMenu: '菜单管理', - systemRole: '角色管理', - systemUser: '用户管理', - systemDept: '部门管理', - systemDic: '字典管理', - limits: '权限管理', - limitsFrontEnd: '前端控制', - limitsFrontEndPage: '页面权限', - limitsFrontEndBtn: '按钮权限', - limitsBackEnd: '后端控制', - limitsBackEndEndPage: '页面权限', - menu: '菜单嵌套', - menu1: '菜单1', - menu11: '菜单11', - menu12: '菜单12', - menu121: '菜单121', - menu122: '菜单122', - menu13: '菜单13', - menu2: '菜单2', - funIndex: '功能', - funTagsView: 'tagsView 操作', - funCountup: '数字滚动', - funWangEditor: 'Editor 编辑器', - funCropper: '图片裁剪', - funQrcode: '二维码生成', - funEchartsMap: '地理坐标/地图', - funPrintJs: '页面打印', - funClipboard: '复制剪切', - funGridLayout: '拖拽布局', - funSplitpanes: '窗格拆分器', - funDragVerify: '验证器', - pagesIndex: '页面', - pagesFiltering: '过滤筛选组件', - pagesFilteringDetails: '过滤筛选组件详情', - pagesFilteringDetails1: '过滤筛选组件详情111', - pagesIocnfont: 'ali 字体图标', - pagesElement: 'ele 字体图标', - pagesAwesome: 'awe 字体图标', - pagesFormAdapt: '表单自适应', - pagesTableRules: '表单表格验证', - pagesFormI18n: '表单国际化', - pagesFormRules: '多表单验证', - pagesDynamicForm: '动态复杂表单', - pagesWorkflow: '工作流', - pagesListAdapt: '列表自适应', - pagesWaterfall: '瀑布屏', - pagesSteps: '步骤条', - pagesPreview: '大图预览', - pagesWaves: '波浪效果', - pagesTree: '树形改表格', - pagesDrag: '拖动指令', - pagesLazyImg: '图片懒加载', - makeIndex: '组件封装', - makeSelector: '图标选择器', - makeNoticeBar: '滚动通知栏', - makeSvgDemo: 'svgIcon 演示', - paramsIndex: '路由参数', - paramsCommon: '普通路由', - paramsDynamic: '动态路由', - paramsCommonDetails: '普通路由详情', - paramsDynamicDetails: '动态路由详情', - chartIndex: '大数据图表', - visualizingIndex: '数据可视化', - visualizingLinkDemo1: '数据可视化演示1', - visualizingLinkDemo2: '数据可视化演示2', - personal: '个人中心', - tools: '工具类集合', - layoutLinkView: '外链', - layoutIfameView: '内嵌 iframe', - }, - staticRoutes: { - signIn: '登录', - notFound: '找不到此页面', - noPower: '没有权限', - }, - user: { - title0: '组件大小', - title1: '语言切换', - title2: '菜单搜索', - title3: '布局配置', - title4: '消息', - title5: '开全屏', - title6: '关全屏', - dropdownLarge: '大型', - dropdownDefault: '默认', - dropdownSmall: '小型', - dropdown1: '首页', - dropdown2: '个人中心', - dropdown3: '404', - dropdown4: '401', - dropdown5: '退出登录', - dropdown6: '代码仓库', - searchPlaceholder: '菜单搜索:支持中文、路由路径', - newTitle: '通知', - newBtn: '全部已读', - newGo: '前往通知中心', - newDesc: '暂无通知', - logOutTitle: '提示', - logOutMessage: '此操作将退出登录, 是否继续?', - logOutConfirm: '确定', - logOutCancel: '取消', - logOutExit: '退出中', - }, - tagsView: { - refresh: '刷新', - close: '关闭', - closeOther: '关闭其它', - closeAll: '全部关闭', - fullscreen: '当前页全屏', - closeFullscreen: '关闭全屏', - }, - notFound: { - foundTitle: '地址输入错误,请重新输入地址~', - foundMsg: '您可以先检查网址,然后重新输入或给我们反馈问题。', - foundBtn: '返回首页', - }, - noAccess: { - accessTitle: '您未被授权,没有操作权限~', - accessMsg: '联系方式:加QQ群探讨 665452019', - accessBtn: '重新授权', - }, - layout: { - configTitle: '布局配置', - oneTitle: '全局主题', - twoTopTitle: '顶栏设置', - twoMenuTitle: '菜单设置', - twoColumnsTitle: '分栏设置', - twoTopBar: '顶栏背景', - twoTopBarColor: '顶栏默认字体颜色', - twoIsTopBarColorGradual: '顶栏背景渐变', - twoMenuBar: '菜单背景', - twoMenuBarColor: '菜单默认字体颜色', - twoIsMenuBarColorGradual: '菜单背景渐变', - twoColumnsMenuBar: '分栏菜单背景', - twoColumnsMenuBarColor: '分栏菜单默认字体颜色', - twoIsColumnsMenuBarColorGradual: '分栏菜单背景渐变', - threeTitle: '界面设置', - threeIsCollapse: '菜单水平折叠', - threeIsUniqueOpened: '菜单手风琴', - threeIsFixedHeader: '固定 Header', - threeIsClassicSplitMenu: '经典布局分割菜单', - threeIsLockScreen: '开启锁屏', - threeLockScreenTime: '自动锁屏(s/秒)', - fourTitle: '界面显示', - fourIsShowLogo: '侧边栏 Logo', - fourIsBreadcrumb: '开启 Breadcrumb', - fourIsBreadcrumbIcon: '开启 Breadcrumb 图标', - fourIsTagsview: '开启 Tagsview', - fourIsTagsviewIcon: '开启 Tagsview 图标', - fourIsCacheTagsView: '开启 TagsView 缓存', - fourIsSortableTagsView: '开启 TagsView 拖拽', - fourIsShareTagsView: '开启 TagsView 共用', - fourIsFooter: '开启 Footer', - fourIsGrayscale: '灰色模式', - fourIsInvert: '色弱模式', - fourIsDark: '深色模式', - fourIsWartermark: '开启水印', - fourWartermarkText: '水印文案', - fiveTitle: '其它设置', - fiveTagsStyle: 'Tagsview 风格', - fiveAnimation: '主页面切换动画', - fiveColumnsAsideStyle: '分栏高亮风格', - fiveColumnsAsideLayout: '分栏布局风格', - sixTitle: '布局切换', - sixDefaults: '默认', - sixClassic: '经典', - sixTransverse: '横向', - sixColumns: '分栏', - tipText: '点击下方按钮,复制布局配置去 `src/stores/themeConfig.ts` 中修改。', - copyText: '一键复制配置', - resetText: '一键恢复默认', - copyTextSuccess: '复制成功!', - copyTextError: '复制失败!', - }, -}; diff --git a/tg-web/src/i18n/lang/zh-tw.ts b/tg-web/src/i18n/lang/zh-tw.ts deleted file mode 100644 index d900abb..0000000 --- a/tg-web/src/i18n/lang/zh-tw.ts +++ /dev/null @@ -1,180 +0,0 @@ -// 定义内容 -export default { - router: { - home: '首頁', - system: '系統設置', - systemMenu: '選單管理', - systemRole: '角色管理', - systemUser: '用戶管理', - systemDept: '部門管理', - systemDic: '字典管理', - limits: '許可權管理', - limitsFrontEnd: '前端控制', - limitsFrontEndPage: '頁面許可權', - limitsFrontEndBtn: '按鈕許可權', - limitsBackEnd: '後端控制', - limitsBackEndEndPage: '頁面許可權', - menu: '選單嵌套', - menu1: '選單1', - menu11: '選單11', - menu12: '選單12', - menu121: '選單121', - menu122: '選單122', - menu13: '選單13', - menu2: '選單2', - funIndex: '功能', - funTagsView: 'tagsView 操作', - funCountup: '數位滾動', - funWangEditor: 'Editor 編輯器', - funCropper: '圖片裁剪', - funQrcode: '二維碼生成', - funEchartsMap: '地理座標/地圖', - funPrintJs: '頁面列印', - funClipboard: '複製剪切', - funGridLayout: '拖拽佈局', - funSplitpanes: '窗格折開器', - funDragVerify: '驗證器', - pagesIndex: '頁面', - pagesFiltering: '過濾篩選組件', - pagesFilteringDetails: '過濾篩選組件詳情', - pagesFilteringDetails1: '過濾篩選組件詳情111', - pagesIocnfont: 'ali 字體圖標', - pagesElement: 'ele 字體圖標', - pagesAwesome: 'awe 字體圖標', - pagesFormAdapt: '表單自我調整', - pagesTableRules: '表單表格驗證', - pagesFormI18n: '表單國際化', - pagesFormRules: '多表單驗證', - pagesDynamicForm: '動態複雜表單', - pagesWorkflow: '工作流', - pagesListAdapt: '清單自我調整', - pagesWaterfall: '瀑布屏', - pagesSteps: '步驟條', - pagesPreview: '大圖預覽', - pagesWaves: '波浪效果', - pagesTree: '樹形改表格', - pagesDrag: '拖動指令', - pagesLazyImg: '圖片懶加載', - makeIndex: '組件封裝', - makeSelector: '圖標選擇器', - makeNoticeBar: '滾動通知欄', - makeSvgDemo: 'svgIcon 演示', - paramsIndex: '路由參數', - paramsCommon: '普通路由', - paramsDynamic: '動態路由', - paramsCommonDetails: '普通路由詳情', - paramsDynamicDetails: '動態路由詳情', - chartIndex: '大資料圖表', - visualizingIndex: '數據視覺化', - visualizingLinkDemo1: '數據視覺化演示1', - visualizingLinkDemo2: '數據視覺化演示2', - personal: '個人中心', - tools: '工具類集合', - layoutLinkView: '外鏈', - layoutIfameView: '内嵌 iframe', - }, - staticRoutes: { - signIn: '登入', - notFound: '找不到此頁面', - noPower: '沒有許可權', - }, - user: { - title0: '組件大小', - title1: '語言切換', - title2: '選單蒐索', - title3: '佈局配寘', - title4: '消息', - title5: '開全屏', - title6: '關全屏', - dropdownLarge: '大型', - dropdownDefault: '默認', - dropdownSmall: '小型', - dropdown1: '首頁', - dropdown2: '個人中心', - dropdown3: '404', - dropdown4: '401', - dropdown5: '登出', - dropdown6: '程式碼倉庫', - searchPlaceholder: '選單蒐索:支援中文、路由路徑', - newTitle: '通知', - newBtn: '全部已讀', - newGo: '前往通知中心', - newDesc: '暫無通知', - logOutTitle: '提示', - logOutMessage: '此操作將登出,是否繼續?', - logOutConfirm: '確定', - logOutCancel: '取消', - logOutExit: '退出中', - }, - tagsView: { - refresh: '重繪', - close: '關閉', - closeOther: '關閉其它', - closeAll: '全部關閉', - fullscreen: '當前頁全屏', - closeFullscreen: '關閉全屏', - }, - notFound: { - foundTitle: '地址輸入錯誤,請重新輸入地址~', - foundMsg: '您可以先檢查網址,然後重新輸入或給我們迴響問題。', - foundBtn: '返回首頁', - }, - noAccess: { - accessTitle: '您未被授權,沒有操作許可權~', - accessMsg: '聯繫方式:加QQ群探討665452019', - accessBtn: '重新授權', - }, - layout: { - configTitle: '佈局配寘', - oneTitle: '全域主題', - twoTopTitle: '頂欄設定', - twoMenuTitle: '選單設定', - twoColumnsTitle: '分欄設定', - twoTopBar: '頂欄背景', - twoTopBarColor: '頂欄默認字體顏色', - twoIsTopBarColorGradual: '頂欄背景漸變', - twoMenuBar: '選單背景', - twoMenuBarColor: '選單默認字體顏色', - twoIsMenuBarColorGradual: '選單背景漸變', - twoColumnsMenuBar: '分欄選單背景', - twoColumnsMenuBarColor: '分欄選單默認字體顏色', - twoIsColumnsMenuBarColorGradual: '分欄選單背景漸變', - threeTitle: '介面設定', - threeIsCollapse: '選單水准折疊', - threeIsUniqueOpened: '選單手風琴', - threeIsFixedHeader: '固定 Header', - threeIsClassicSplitMenu: '經典佈局分割選單', - threeIsLockScreen: '開啟鎖屏', - threeLockScreenTime: '自動鎖屏(s/秒)', - fourTitle: '介面顯示', - fourIsShowLogo: '側邊欄 Logo', - fourIsBreadcrumb: '開啟 Breadcrumb', - fourIsBreadcrumbIcon: '開啟 Breadcrumb 圖標', - fourIsTagsview: '開啟 Tagsview', - fourIsTagsviewIcon: '開啟 Tagsview 圖標', - fourIsCacheTagsView: '開啟 TagsView 緩存', - fourIsSortableTagsView: '開啟 TagsView 拖拽', - fourIsShareTagsView: '開啟 TagsView 共用', - fourIsFooter: '開啟 Footer', - fourIsGrayscale: '灰色模式', - fourIsInvert: '色弱模式', - fourIsDark: '深色模式', - fourIsWartermark: '開啟浮水印', - fourWartermarkText: '浮水印文案', - fiveTitle: '其它設定', - fiveTagsStyle: 'Tagsview 風格', - fiveAnimation: '主頁面切換動畫', - fiveColumnsAsideStyle: '分欄高亮風格', - fiveColumnsAsideLayout: '分欄佈局風格', - sixTitle: '佈局切換', - sixDefaults: '默認', - sixClassic: '經典', - sixTransverse: '橫向', - sixColumns: '分欄', - tipText: '點擊下方按鈕,複製佈局配寘去`src/stores/themeConfig.ts`中修改。', - copyText: '一鍵複製配寘', - resetText: '一鍵恢復默認', - copyTextSuccess: '複製成功!', - copyTextError: '複製失敗!', - }, -}; diff --git a/tg-web/src/i18n/pages/formI18n/en.ts b/tg-web/src/i18n/pages/formI18n/en.ts deleted file mode 100644 index b3c54d6..0000000 --- a/tg-web/src/i18n/pages/formI18n/en.ts +++ /dev/null @@ -1,13 +0,0 @@ -// 定义内容 -export default { - formI18nLabel: { - name: 'name', - email: 'email', - autograph: 'autograph', - }, - formI18nPlaceholder: { - name: 'Please enter your name', - email: 'Please enter the users Department', - autograph: 'Please enter the login account name', - }, -}; diff --git a/tg-web/src/i18n/pages/formI18n/zh-cn.ts b/tg-web/src/i18n/pages/formI18n/zh-cn.ts deleted file mode 100644 index 0bed3ec..0000000 --- a/tg-web/src/i18n/pages/formI18n/zh-cn.ts +++ /dev/null @@ -1,13 +0,0 @@ -// 定义内容 -export default { - formI18nLabel: { - name: '姓名', - email: '用户归属部门', - autograph: '登陆账户名', - }, - formI18nPlaceholder: { - name: '请输入姓名', - email: '请输入用户归属部门', - autograph: '请输入登陆账户名', - }, -}; diff --git a/tg-web/src/i18n/pages/formI18n/zh-tw.ts b/tg-web/src/i18n/pages/formI18n/zh-tw.ts deleted file mode 100644 index 393ac03..0000000 --- a/tg-web/src/i18n/pages/formI18n/zh-tw.ts +++ /dev/null @@ -1,13 +0,0 @@ -// 定义内容 -export default { - formI18nLabel: { - name: '姓名', - email: '用戶歸屬部門', - autograph: '登入帳戶名', - }, - formI18nPlaceholder: { - name: '請輸入姓名', - email: '請輸入用戶歸屬部門', - autograph: '請輸入登入帳戶名', - }, -}; diff --git a/tg-web/src/i18n/pages/login/en.ts b/tg-web/src/i18n/pages/login/en.ts deleted file mode 100644 index 2654a18..0000000 --- a/tg-web/src/i18n/pages/login/en.ts +++ /dev/null @@ -1,29 +0,0 @@ -// 定义内容 -export default { - label: { - one1: 'User name login', - two2: 'Mobile number', - }, - link: { - one3: 'Third party login', - two4: 'Links', - }, - account: { - accountPlaceholder1: 'The user name admin or not is common', - accountPlaceholder2: 'Password: 123456', - accountPlaceholder3: 'Please enter the verification code', - accountBtnText: 'Sign in', - }, - mobile: { - placeholder1: 'Please input mobile phone number', - placeholder2: 'Please enter the verification code', - codeText: 'Get code', - btnText: 'Sign in', - msgText: - 'Warm tip: it is recommended to use Google, Microsoft edge, version 79.0.1072.62 and above browsers, and 360 browser, please use speed mode', - }, - scan: { - text: 'Open the mobile phone to scan and quickly log in / register', - }, - signInText: 'welcome back!', -}; diff --git a/tg-web/src/i18n/pages/login/zh-cn.ts b/tg-web/src/i18n/pages/login/zh-cn.ts deleted file mode 100644 index 3367b53..0000000 --- a/tg-web/src/i18n/pages/login/zh-cn.ts +++ /dev/null @@ -1,28 +0,0 @@ -// 定义内容 -export default { - label: { - one1: '用户名登录', - two2: '手机号登录', - }, - link: { - one3: '第三方登录', - two4: '友情链接', - }, - account: { - accountPlaceholder1: '用户名 admin 或不输均为 common', - accountPlaceholder2: '密码:123456', - accountPlaceholder3: '请输入验证码', - accountBtnText: '登 录', - }, - mobile: { - placeholder1: '请输入手机号', - placeholder2: '请输入验证码', - codeText: '获取验证码', - btnText: '登 录', - msgText: '* 温馨提示:建议使用谷歌、Microsoft Edge,版本 79.0.1072.62 及以上浏览器,360浏览器请使用极速模式', - }, - scan: { - text: '打开手机扫一扫,快速登录/注册', - }, - signInText: '欢迎回来!', -}; diff --git a/tg-web/src/i18n/pages/login/zh-tw.ts b/tg-web/src/i18n/pages/login/zh-tw.ts deleted file mode 100644 index 138e8c8..0000000 --- a/tg-web/src/i18n/pages/login/zh-tw.ts +++ /dev/null @@ -1,28 +0,0 @@ -// 定义内容 -export default { - label: { - one1: '用戶名登入', - two2: '手機號登入', - }, - link: { - one3: '協力廠商登入', - two4: '友情連結', - }, - account: { - accountPlaceholder1: '用戶名admin或不輸均為common', - accountPlaceholder2: '密碼:123456', - accountPlaceholder3: '請輸入驗證碼', - accountBtnText: '登入', - }, - mobile: { - placeholder1: '請輸入手機號', - placeholder2: '請輸入驗證碼', - codeText: '獲取驗證碼', - btnText: '登入', - msgText: '* 溫馨提示:建議使用穀歌、Microsoft Edge,版本79.0.1072.62及以上瀏覽器,360瀏覽器請使用極速模式', - }, - scan: { - text: '打開手機掃一掃,快速登錄/注册', - }, - signInText: '歡迎回來!', -}; diff --git a/tg-web/src/layout/component/aside.vue b/tg-web/src/layout/component/aside.vue deleted file mode 100644 index d4ee363..0000000 --- a/tg-web/src/layout/component/aside.vue +++ /dev/null @@ -1,163 +0,0 @@ - - - diff --git a/tg-web/src/layout/component/columnsAside.vue b/tg-web/src/layout/component/columnsAside.vue deleted file mode 100644 index 7fd7806..0000000 --- a/tg-web/src/layout/component/columnsAside.vue +++ /dev/null @@ -1,292 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/component/header.vue b/tg-web/src/layout/component/header.vue deleted file mode 100644 index 21c9e2d..0000000 --- a/tg-web/src/layout/component/header.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - diff --git a/tg-web/src/layout/component/main.vue b/tg-web/src/layout/component/main.vue deleted file mode 100644 index 18eba60..0000000 --- a/tg-web/src/layout/component/main.vue +++ /dev/null @@ -1,101 +0,0 @@ - - - diff --git a/tg-web/src/layout/footer/index.vue b/tg-web/src/layout/footer/index.vue deleted file mode 100644 index ef00591..0000000 --- a/tg-web/src/layout/footer/index.vue +++ /dev/null @@ -1,47 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/index.vue b/tg-web/src/layout/index.vue deleted file mode 100644 index 50643a4..0000000 --- a/tg-web/src/layout/index.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - diff --git a/tg-web/src/layout/lockScreen/index.vue b/tg-web/src/layout/lockScreen/index.vue deleted file mode 100644 index b9173c2..0000000 --- a/tg-web/src/layout/lockScreen/index.vue +++ /dev/null @@ -1,372 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/logo/index.vue b/tg-web/src/layout/logo/index.vue deleted file mode 100644 index ac80862..0000000 --- a/tg-web/src/layout/logo/index.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/main/classic.vue b/tg-web/src/layout/main/classic.vue deleted file mode 100644 index b92290f..0000000 --- a/tg-web/src/layout/main/classic.vue +++ /dev/null @@ -1,35 +0,0 @@ - - - diff --git a/tg-web/src/layout/main/columns.vue b/tg-web/src/layout/main/columns.vue deleted file mode 100644 index 0aa1d31..0000000 --- a/tg-web/src/layout/main/columns.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - diff --git a/tg-web/src/layout/main/defaults.vue b/tg-web/src/layout/main/defaults.vue deleted file mode 100644 index 0cabb0c..0000000 --- a/tg-web/src/layout/main/defaults.vue +++ /dev/null @@ -1,47 +0,0 @@ - - - diff --git a/tg-web/src/layout/main/transverse.vue b/tg-web/src/layout/main/transverse.vue deleted file mode 100644 index 538f911..0000000 --- a/tg-web/src/layout/main/transverse.vue +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/tg-web/src/layout/navBars/breadcrumb/breadcrumb.vue b/tg-web/src/layout/navBars/breadcrumb/breadcrumb.vue deleted file mode 100644 index 609dee9..0000000 --- a/tg-web/src/layout/navBars/breadcrumb/breadcrumb.vue +++ /dev/null @@ -1,163 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/navBars/breadcrumb/business.vue b/tg-web/src/layout/navBars/breadcrumb/business.vue deleted file mode 100644 index 2301913..0000000 --- a/tg-web/src/layout/navBars/breadcrumb/business.vue +++ /dev/null @@ -1,55 +0,0 @@ - - - - diff --git a/tg-web/src/layout/navBars/breadcrumb/closeFull.vue b/tg-web/src/layout/navBars/breadcrumb/closeFull.vue deleted file mode 100644 index 6c786bb..0000000 --- a/tg-web/src/layout/navBars/breadcrumb/closeFull.vue +++ /dev/null @@ -1,61 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/navBars/breadcrumb/index.vue b/tg-web/src/layout/navBars/breadcrumb/index.vue deleted file mode 100644 index 2810718..0000000 --- a/tg-web/src/layout/navBars/breadcrumb/index.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/navBars/breadcrumb/search.vue b/tg-web/src/layout/navBars/breadcrumb/search.vue deleted file mode 100644 index d2a5713..0000000 --- a/tg-web/src/layout/navBars/breadcrumb/search.vue +++ /dev/null @@ -1,138 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/navBars/breadcrumb/setings.vue b/tg-web/src/layout/navBars/breadcrumb/setings.vue deleted file mode 100644 index 8b7db89..0000000 --- a/tg-web/src/layout/navBars/breadcrumb/setings.vue +++ /dev/null @@ -1,817 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/navBars/breadcrumb/user.vue b/tg-web/src/layout/navBars/breadcrumb/user.vue deleted file mode 100644 index 4f78bb5..0000000 --- a/tg-web/src/layout/navBars/breadcrumb/user.vue +++ /dev/null @@ -1,317 +0,0 @@ - - - - - \ No newline at end of file diff --git a/tg-web/src/layout/navBars/breadcrumb/userNews.vue b/tg-web/src/layout/navBars/breadcrumb/userNews.vue deleted file mode 100644 index 99bf204..0000000 --- a/tg-web/src/layout/navBars/breadcrumb/userNews.vue +++ /dev/null @@ -1,115 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/navBars/index.vue b/tg-web/src/layout/navBars/index.vue deleted file mode 100644 index f41a0cf..0000000 --- a/tg-web/src/layout/navBars/index.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/navBars/tagsView/contextmenu.vue b/tg-web/src/layout/navBars/tagsView/contextmenu.vue deleted file mode 100644 index c717aae..0000000 --- a/tg-web/src/layout/navBars/tagsView/contextmenu.vue +++ /dev/null @@ -1,138 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/navBars/tagsView/tagsView.vue b/tg-web/src/layout/navBars/tagsView/tagsView.vue deleted file mode 100644 index 83f272d..0000000 --- a/tg-web/src/layout/navBars/tagsView/tagsView.vue +++ /dev/null @@ -1,734 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/navMenu/horizontal.vue b/tg-web/src/layout/navMenu/horizontal.vue deleted file mode 100644 index 022668b..0000000 --- a/tg-web/src/layout/navMenu/horizontal.vue +++ /dev/null @@ -1,157 +0,0 @@ - - - - - diff --git a/tg-web/src/layout/navMenu/subItem.vue b/tg-web/src/layout/navMenu/subItem.vue deleted file mode 100644 index 3fcefd3..0000000 --- a/tg-web/src/layout/navMenu/subItem.vue +++ /dev/null @@ -1,48 +0,0 @@ - - - diff --git a/tg-web/src/layout/navMenu/vertical.vue b/tg-web/src/layout/navMenu/vertical.vue deleted file mode 100644 index 1bfa825..0000000 --- a/tg-web/src/layout/navMenu/vertical.vue +++ /dev/null @@ -1,101 +0,0 @@ - - - diff --git a/tg-web/src/layout/routerView/iframes.vue b/tg-web/src/layout/routerView/iframes.vue deleted file mode 100644 index 43e71c5..0000000 --- a/tg-web/src/layout/routerView/iframes.vue +++ /dev/null @@ -1,66 +0,0 @@ - - - diff --git a/tg-web/src/layout/routerView/link.vue b/tg-web/src/layout/routerView/link.vue deleted file mode 100644 index a48f002..0000000 --- a/tg-web/src/layout/routerView/link.vue +++ /dev/null @@ -1,61 +0,0 @@ - - - diff --git a/tg-web/src/layout/routerView/parent.vue b/tg-web/src/layout/routerView/parent.vue deleted file mode 100644 index db22141..0000000 --- a/tg-web/src/layout/routerView/parent.vue +++ /dev/null @@ -1,88 +0,0 @@ - - - diff --git a/tg-web/src/main.ts b/tg-web/src/main.ts deleted file mode 100644 index 987cb33..0000000 --- a/tg-web/src/main.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createApp } from 'vue'; -import pinia from '/@/stores/index'; -import App from './App.vue'; -import router from './router'; -import { directive } from '/@/utils/directive'; -import { i18n } from '@/i18n'; -import other from '/@/utils/other'; - -import ElementPlus from 'element-plus'; -import 'element-plus/dist/index.css'; -import '/@/theme/index.scss'; -import mitt from 'mitt'; -import VueGridLayout from 'vue-grid-layout'; -import naive from "@/plugins/naive.js"; - -const app = createApp(App); - -directive(app); -other.elSvg(app); - -app.use(pinia).use(router).use(ElementPlus, { i18n: i18n.global.t }).use(i18n).use(VueGridLayout).use(naive).mount('#app'); - -app.config.globalProperties.mittBus = mitt(); diff --git a/tg-web/src/plugins/naive.ts b/tg-web/src/plugins/naive.ts deleted file mode 100644 index 3325abc..0000000 --- a/tg-web/src/plugins/naive.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { - create, - NAlert, - NAvatar, - NBreadcrumb, - NBreadcrumbItem, - NButton, - NCard, - NCheckbox, - NDataTable, - NDescriptions, - NDescriptionsItem, - NDialog, - NDialogProvider, - NDrawer, - NDrawerContent, - NEllipsis, - NForm, - NFormItem, - NGrid, - NGi, - NH5, - NIcon, - NInput, - NInputNumber, - NLayout, - NLayoutContent, - NLayoutHeader, - NLayoutSider, - NList, - NListItem, - NMenu, - NMessageProvider, - NNotificationProvider, - NPagination, - NPopover, - NPopselect, - NRadio, - NRadioButton, - NRadioGroup, - NSelect, - NSkeleton, - NSlider, - NSpace, - NSpin, - NSwitch, - NTag, - NText, - NThing, - NTooltip, -} from "naive-ui"; - -const naive = create({ - components: [ - NButton, - NSlider, - NTooltip, - NTag, - NPopover, - NNotificationProvider, - NLayout, - NRadio, - NRadioGroup, - NLayoutContent, - NLayoutHeader, - NLayoutSider, - NIcon, - NMenu, - NSpace, - NBreadcrumb, - NBreadcrumbItem, - NPopselect, - NDataTable, - NMessageProvider, - NSelect, - NSpin, - NDialog, - NDialogProvider, - NSkeleton, - NForm, - NFormItem, - NInput, - NText, - NDrawer, - NDrawerContent, - NEllipsis, - NList, - NListItem, - NThing, - NInputNumber, - NPagination, - NDescriptions, - NDescriptionsItem, - NSwitch, - NH5, - NAlert, - NCard, - NRadioGroup, - NRadioButton, - NCheckbox, - NAvatar, - NGrid, - NGi, - ], -}); - -export default naive; diff --git a/tg-web/src/router/backEnd.ts b/tg-web/src/router/backEnd.ts deleted file mode 100644 index c690ee0..0000000 --- a/tg-web/src/router/backEnd.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { RouteRecordRaw } from 'vue-router'; -import pinia from '/@/stores/index'; -import { useUserInfo } from '/@/stores/userInfo'; -import { useRequestOldRoutes } from '/@/stores/requestOldRoutes'; -import { NextLoading } from '/@/utils/loading'; -import { dynamicRoutes, notFoundAndNoPower } from '/@/router/route'; -import { formatTwoStageRoutes, formatFlatteningRoutes, router } from '@/router/index'; -import { useRoutesList } from '/@/stores/routesList'; -import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'; -import { formatMenus } from '/@/utils/menu'; - -const layouModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}'); -const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}'); - -// 后端控制路由 - -/** - * 获取目录下的 .vue、.tsx 全部文件 - * @method import.meta.glob - * @link 参考:https://cn.vitejs.dev/guide/features.html#json - */ -const dynamicViewsModules: Record = Object.assign({}, { ...layouModules }, { ...viewsModules }); - -/** - * 后端控制路由:初始化方法,防止刷新时路由丢失 - * @method NextLoading 界面 loading 动画开始执行 - * @method useUserInfo().setUserInfos() 触发初始化用户信息 pinia - * @method useRequestOldRoutes().setRequestOldRoutes() 存储接口原始路由(未处理component),根据需求选择使用 - * @method setAddRoute 添加动态路由 - * @method setFilterMenuAndCacheTagsViewRoutes 设置路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组 - */ -export async function initBackEndControlRoutes() { - // 界面 loading 动画开始执行 - if (window.nextLoading === undefined) NextLoading.start(); - // 无 token 停止执行下一步 - // if (!Session.get('token')) return false; - // 触发初始化用户信息 pinia - // https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP - await useUserInfo().setUserInfos(); - // 获取路由菜单数据 - const menu = await getBackEndControlRoutes(); - // const res = await getBackEndControlRoutes(); - // 存储接口原始路由(未处理component),根据需求选择使用 - await useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(menu))); - // 处理路由(component),替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由 - // dynamicRoutes[0].children = await backEndComponent(res.data); - dynamicRoutes[0].children = [...(dynamicRoutes[0].children as Array), ...await backEndComponent(menu)]; - console.log(dynamicRoutes) - // 添加动态路由 - await setAddRoute(); - // 设置路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组 - await setFilterMenuAndCacheTagsViewRoutes(); -} - -/** - * 设置路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组 - * @description 用于左侧菜单、横向菜单的显示 - * @description 用于 tagsView、菜单搜索中:未过滤隐藏的(isHide) - */ -export function setFilterMenuAndCacheTagsViewRoutes() { - const storesRoutesList = useRoutesList(pinia); - storesRoutesList.setRoutesList(dynamicRoutes[0].children as any); - setCacheTagsViewRoutes(); -} - -/** - * 缓存多级嵌套数组处理后的一维数组 - * @description 用于 tagsView、菜单搜索中:未过滤隐藏的(isHide) - */ -export function setCacheTagsViewRoutes() { - const storesTagsView = useTagsViewRoutes(pinia); - storesTagsView.setTagsViewRoutes(formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes))[0].children); -} - -/** - * 处理路由格式及添加捕获所有路由或 404 Not found 路由 - * @description 替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由 - * @returns 返回替换后的路由数组 - */ -export function setFilterRouteEnd() { - let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes)); - filterRouteEnd[0].children = [...filterRouteEnd[0].children, ...notFoundAndNoPower]; - return filterRouteEnd; -} - -/** - * 添加动态路由 - * @method router.addRoute - * @description 此处循环为 dynamicRoutes(/@/router/route)第一个顶级 children 的路由一维数组,非多级嵌套 - * @link 参考:https://next.router.vuejs.org/zh/api/#addroute - */ -export async function setAddRoute() { - await setFilterRouteEnd().forEach((route: RouteRecordRaw) => { - router.addRoute(route); - }); -} - -/** - * 请求后端路由菜单接口 - * @description isRequestRoutes 为 true,则开启后端控制路由 - * @returns 返回后端路由菜单数据 - */ -export async function getBackEndControlRoutes() { - const menusUpm = { code: 0, data: { list: []} } - let menus = []; - if (menusUpm && menusUpm.code === 0) { - menus = formatMenus(menusUpm.data.list) - } - return menus; -} - -/** - * 后端路由 component 转换 - * @param routes 后端返回的路由表数组 - * @returns 返回处理成函数后的 component - */ -export function backEndComponent(routes: any) { - if (!routes) return; - return routes.map((item: any) => { - if (item.component) item.component = dynamicImport(dynamicViewsModules, item.component as string); - item.children && backEndComponent(item.children); - return item; - }); -} - -/** - * 后端路由 component 转换函数 - * @param dynamicViewsModules 获取目录下的 .vue、.tsx 全部文件 - * @param component 当前要处理项 component - * @returns 返回处理成函数后的 component - */ -export function dynamicImport(dynamicViewsModules: Record, component: string) { - const keys = Object.keys(dynamicViewsModules); - const matchKeys = keys.filter((key) => { - const k = key.replace(/..\/views|../, ''); - return k.startsWith(`${component}.`) || k.startsWith(`/${component}.`); - }); - if (matchKeys?.length === 1) { - const matchKey = matchKeys[0]; - return dynamicViewsModules[matchKey]; - } - if (matchKeys?.length > 1) { - return false; - } -} diff --git a/tg-web/src/router/frontEnd.ts b/tg-web/src/router/frontEnd.ts deleted file mode 100644 index 897f2e3..0000000 --- a/tg-web/src/router/frontEnd.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { RouteRecordRaw } from 'vue-router'; -import { storeToRefs } from 'pinia'; -import { formatTwoStageRoutes, formatFlatteningRoutes, router } from '@/router/index'; -import { dynamicRoutes, notFoundAndNoPower } from '/@/router/route'; -import pinia from '/@/stores/index'; -import { useUserInfo } from '/@/stores/userInfo'; -import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'; -import { useRoutesList } from '/@/stores/routesList'; -import { NextLoading } from '/@/utils/loading'; - -// 前端控制路由 - -/** - * 前端控制路由:初始化方法,防止刷新时路由丢失 - * @method NextLoading 界面 loading 动画开始执行 - * @method useUserInfo(pinia).setUserInfos() 触发初始化用户信息 pinia - * @method setAddRoute 添加动态路由 - * @method setFilterMenuAndCacheTagsViewRoutes 设置递归过滤有权限的路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组 - */ -export async function initFrontEndControlRoutes() { - // 界面 loading 动画开始执行 - if (window.nextLoading === undefined) NextLoading.start(); - // 无 token 停止执行下一步 - // if (!Session.get('token')) return false; - // // 触发初始化用户信息 pinia - // // https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP - // await useUserInfo(pinia).setUserInfos(); - // 添加动态路由 - await setAddRoute(); - // 设置递归过滤有权限的路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组 - await setFilterMenuAndCacheTagsViewRoutes(); -} - -/** - * 添加动态路由 - * @method router.addRoute - * @description 此处循环为 dynamicRoutes(/@/router/route)第一个顶级 children 的路由一维数组,非多级嵌套 - * @link 参考:https://next.router.vuejs.org/zh/api/#addroute - */ -export async function setAddRoute() { - await setFilterRouteEnd().forEach((route: RouteRecordRaw) => { - router.addRoute(route); - }); -} - -/** - * 获取有当前用户权限标识的路由数组,进行对原路由的替换 - * @description 替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由 - * @returns 返回替换后的路由数组 - */ -export function setFilterRouteEnd() { - let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes)); - filterRouteEnd[0].children = [...setFilterRoute(filterRouteEnd[0].children), ...notFoundAndNoPower]; - return filterRouteEnd; -} - -/** - * 获取当前用户权限标识去比对路由表(未处理成多级嵌套路由) - * @description 这里主要用于动态路由的添加,router.addRoute - * @link 参考:https://next.router.vuejs.org/zh/api/#addroute - * @param chil dynamicRoutes(/@/router/route)第一个顶级 children 的下路由集合 - * @returns 返回有当前用户权限标识的路由数组 - */ -export function setFilterRoute(chil: any) { - const stores = useUserInfo(pinia); - const { userInfos } = storeToRefs(stores); - let filterRoute: any = []; - chil.forEach((route: any) => { - if (route.meta.roles) { - route.meta.roles.forEach((metaRoles: any) => { - userInfos.value.roles.forEach((roles: any) => { - if (metaRoles === roles) filterRoute.push({ ...route }); - }); - }); - } - }); - return filterRoute; -} - -/** - * 缓存多级嵌套数组处理后的一维数组 - * @description 用于 tagsView、菜单搜索中:未过滤隐藏的(isHide) - */ -export function setCacheTagsViewRoutes() { - // 获取有权限的路由,否则 tagsView、菜单搜索中无权限的路由也将显示 - const stores = useUserInfo(pinia); - const storesTagsView = useTagsViewRoutes(pinia); - const { userInfos } = storeToRefs(stores); - let rolesRoutes = setFilterHasRolesMenu(dynamicRoutes, userInfos.value.roles); - // 添加到 pinia setTagsViewRoutes 中 - storesTagsView.setTagsViewRoutes(formatTwoStageRoutes(formatFlatteningRoutes(rolesRoutes))[0].children); -} - -/** - * 设置递归过滤有权限的路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组 - * @description 用于左侧菜单、横向菜单的显示 - * @description 用于 tagsView、菜单搜索中:未过滤隐藏的(isHide) - */ -export function setFilterMenuAndCacheTagsViewRoutes() { - const stores = useUserInfo(pinia); - const storesRoutesList = useRoutesList(pinia); - const { userInfos } = storeToRefs(stores); - storesRoutesList.setRoutesList(setFilterHasRolesMenu(dynamicRoutes[0].children, userInfos.value.roles)); - setCacheTagsViewRoutes(); -} - -/** - * 判断路由 `meta.roles` 中是否包含当前登录用户权限字段 - * @param roles 用户权限标识,在 userInfos(用户信息)的 roles(登录页登录时缓存到浏览器)数组 - * @param route 当前循环时的路由项 - * @returns 返回对比后有权限的路由项 - */ -export function hasRoles(roles: any, route: any) { - if (route.meta && route.meta.roles) return roles.some((role: any) => route.meta.roles.includes(role)); - else return true; -} - -/** - * 获取当前用户权限标识去比对路由表,设置递归过滤有权限的路由 - * @param routes 当前路由 children - * @param roles 用户权限标识,在 userInfos(用户信息)的 roles(登录页登录时缓存到浏览器)数组 - * @returns 返回有权限的路由数组 `meta.roles` 中控制 - */ -export function setFilterHasRolesMenu(routes: any, roles: any) { - const menu: any = []; - routes.forEach((route: any) => { - const item = { ...route }; - if (hasRoles(roles, item)) { - if (item.children) item.children = setFilterHasRolesMenu(item.children, roles); - menu.push(item); - } - }); - return menu; -} diff --git a/tg-web/src/router/index.ts b/tg-web/src/router/index.ts deleted file mode 100644 index 3a51876..0000000 --- a/tg-web/src/router/index.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { createRouter, createWebHashHistory } from 'vue-router'; -import NProgress from 'nprogress'; -import 'nprogress/nprogress.css'; -import pinia from '/@/stores/index'; -import { storeToRefs } from 'pinia'; -import { useKeepALiveNames } from '/@/stores/keepAliveNames'; -import { useRoutesList } from '/@/stores/routesList'; -import { useThemeConfig } from '/@/stores/themeConfig'; -import { staticRoutes } from '/@/router/route'; -import { initFrontEndControlRoutes } from '/@/router/frontEnd'; -import { initBackEndControlRoutes } from '/@/router/backEnd'; -import { useUserInfo } from '/@/stores/userInfo'; -import { Session } from '/@/utils/storage'; - -/** - * 1、前端控制路由时:isRequestRoutes 为 false,需要写 roles,需要走 setFilterRoute 方法。 - * 2、后端控制路由时:isRequestRoutes 为 true,不需要写 roles,不需要走 setFilterRoute 方法), - * 相关方法已拆解到对应的 `backEnd.ts` 与 `frontEnd.ts`(他们互不影响,不需要同时改 2 个文件)。 - * 特别说明: - * 1、前端控制:路由菜单由前端去写(无菜单管理界面,有角色管理界面),角色管理中有 roles 属性,需返回到 userInfo 中。 - * 2、后端控制:路由菜单由后端返回(有菜单管理界面、有角色管理界面) - */ - -// 读取 `/src/stores/themeConfig.ts` 是否开启后端控制路由配置 -const storesThemeConfig = useThemeConfig(pinia); -const { themeConfig } = storeToRefs(storesThemeConfig); -const { isRequestRoutes } = themeConfig.value; - -/** - * 创建一个可以被 Vue 应用程序使用的路由实例 - * @method createRouter(options: RouterOptions): Router - * @link 参考:https://next.router.vuejs.org/zh/api/#createrouter - */ -export const router = createRouter({ - history: createWebHashHistory(), - routes: staticRoutes, -}); - -/** - * 路由多级嵌套数组处理成一维数组 - * @param arr 传入路由菜单数据数组 - * @returns 返回处理后的一维路由菜单数组 - */ -export function formatFlatteningRoutes(arr: any) { - if (arr.length <= 0) return false; - for (let i = 0; i < arr.length; i++) { - if (arr[i].children) { - arr = arr.slice(0, i + 1).concat(arr[i].children, arr.slice(i + 1)); - } - } - return arr; -} - -/** - * 一维数组处理成多级嵌套数组(只保留二级:也就是二级以上全部处理成只有二级,keep-alive 支持二级缓存) - * @description isKeepAlive 处理 `name` 值,进行缓存。顶级关闭,全部不缓存 - * @link 参考:https://v3.cn.vuejs.org/api/built-in-components.html#keep-alive - * @param arr 处理后的一维路由菜单数组 - * @returns 返回将一维数组重新处理成 `定义动态路由(dynamicRoutes)` 的格式 - */ -export function formatTwoStageRoutes(arr: any) { - if (arr.length <= 0) return false; - const newArr: any = []; - const cacheList: Array = []; - arr.forEach((v: any) => { - if (v.path === '/') { - newArr.push({ component: v.component, name: v.name, path: v.path, redirect: v.redirect, meta: v.meta, children: [] }); - } else { - // 判断是否是动态路由(xx/:id/:name),用于 tagsView 等中使用 - // 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G - if (v.path.indexOf('/:') > -1) { - v.meta['isDynamic'] = true; - v.meta['isDynamicPath'] = v.path; - } - newArr[0].children.push({ ...v }); - // 存 name 值,keep-alive 中 include 使用,实现路由的缓存 - // 路径:/@/layout/routerView/parent.vue - if (newArr[0].meta.isKeepAlive && v.meta.isKeepAlive) { - cacheList.push(v.name); - const stores = useKeepALiveNames(pinia); - stores.setCacheKeepAlive(cacheList); - } - } - }); - return newArr; -} - -const initUserInfo = async () => { - const user: any = { code: 0 } - if (user && user.code === 0) { - const roles = ['common', 'admin']; - const userInfo = { - userName: 'test', - username: 'test', - photo: '', - time: new Date().getTime(), - roles, - authBtnList: [], - }; - Session.set('token', 'test'); - Session.set('userName', 'test'); - Session.set('userInfo', userInfo); - window.userInfo = userInfo - // 触发初始化用户信息 - useUserInfo().setUserInfos(); - } -} - -await initUserInfo(); - -// 路由加载前 -router.beforeEach(async (to, from, next) => { - NProgress.configure({ showSpinner: false }); - if (to.meta.title) NProgress.start(); - const storesRoutesList = useRoutesList(pinia); - const { routesList } = storeToRefs(storesRoutesList); - if (routesList.value.length === 0) { - if (isRequestRoutes) { - // 后端控制路由:路由数据初始化,防止刷新时丢失 - await initBackEndControlRoutes(); - // 动态添加路由:防止非首页刷新时跳转回首页的问题 - // 确保 addRoute() 时动态添加的路由已经被完全加载上去 - next({ ...to, replace: true }); - } else { - // https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP - await initFrontEndControlRoutes(); - next({ ...to, replace: true }); - } - } else { - next(); - } -}); - -// 路由加载后 -router.afterEach(() => { - NProgress.done(); -}); - -// 导出路由 -export default router; diff --git a/tg-web/src/router/route.ts b/tg-web/src/router/route.ts deleted file mode 100644 index 92b54e7..0000000 --- a/tg-web/src/router/route.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { RouteRecordRaw } from 'vue-router'; - -/** - * 路由meta对象参数说明 - * meta: { - * title: 菜单栏及 tagsView 栏、菜单搜索名称(国际化) - * isLink: 是否超链接菜单,开启外链条件,`1、isLink: 链接地址不为空` - * isHide: 是否隐藏此路由 - * isKeepAlive: 是否缓存组件状态 - * isAffix: 是否固定在 tagsView 栏上 - * isIframe: 是否内嵌窗口,开启条件,`1、isIframe:true 2、isLink:链接地址不为空` - * roles: 当前路由权限标识,取角色管理。控制路由显示、隐藏。超级管理员:admin 普通角色:common - * icon: 菜单、tagsView 图标,阿里:加 `iconfont xxx`,fontawesome:加 `fa xxx` - * } - */ - -/** - * 定义动态路由 - * 前端添加路由,请在顶级节点的 `children 数组` 里添加 - * @description 未开启 isRequestRoutes 为 true 时使用(前端控制路由),开启时第一个顶级 children 的路由将被替换成接口请求回来的路由数据 - * @description 各字段请查看 `/@/views/system/menu/component/addMenu.vue 下的 ruleForm` - * @returns 返回路由菜单数据 - */ -export const dynamicRoutes: Array = [ - { - path: '/', - name: '/', - component: () => import('/@/layout/index.vue'), - redirect: '/home', - meta: { - isKeepAlive: true, - }, - children: [ - { - path: '/home', - name: 'home', - component: () => import('/@/views/home/index.vue'), - meta: { - title: 'message.router.home', - isLink: '', - isHide: true, - isKeepAlive: true, - isAffix: true, - isIframe: false, - roles: ['admin', 'common'], - icon: '', - }, - }, - { - path: '/strategy', - name: 'strategy', - component: () => import("/@/views/strategy/system/system.vue"), - meta: { - title: '策略管理', - isLink: '', - isHide: false, - isKeepAlive: true, - isAffix: true, - isIframe: false, - roles: ['admin', 'common'], - icon: 'ele-Operation', - }, - children: [ - { - path: '/strategy-system', - name: 'strategy-system', - component: () => import("/@/views/strategy/system/system.vue"), - meta: { - title: '系统管理', - isLink: '', - isHide: false, - isKeepAlive: true, - isAffix: true, - isIframe: false, - roles: ['admin', 'common'], - icon: '', - }, - }, - { - path: '/strategy-scenes', - name: 'strategy-scenes', - component: () => import("/@/views/strategy/scenes/scenes.vue"), - meta: { - title: '场景管理', - isLink: '', - isHide: false, - isKeepAlive: true, - isAffix: true, - isIframe: false, - roles: ['admin', 'common'], - icon: '', - }, - }, - { - path: '/strategy-experiment', - name: 'strategy-experiment', - component: () => import("/@/views/strategy/experiment/experiment.vue"), - meta: { - title: '流程管理', - isLink: '', - isHide: false, - isKeepAlive: true, - isAffix: true, - isIframe: false, - roles: ['admin', 'common'], - icon: '', - }, - }, - { - path: '/strategy-module', - name: 'strategy-module', - component: () => import("/@/views/strategy/module/module.vue"), - meta: { - title: '模块管理', - isLink: '', - isHide: false, - isKeepAlive: true, - isAffix: true, - isIframe: false, - roles: ['admin', 'common'], - icon: '', - }, - }, - ] - }, - ], - }, -]; - -/** - * 定义404、401界面 - * @link 参考:https://next.router.vuejs.org/zh/guide/essentials/history-mode.html#netlify - */ -export const notFoundAndNoPower = [ - { - path: '/:path(.*)*', - name: 'notFound', - component: () => import('/@/views/error/404.vue'), - meta: { - title: 'message.staticRoutes.notFound', - isHide: true, - }, - }, - { - path: '/401', - name: 'noPower', - component: () => import('/@/views/error/401.vue'), - meta: { - title: 'message.staticRoutes.noPower', - isHide: true, - }, - }, -]; - -/** - * 定义静态路由(默认路由) - * 此路由不要动,前端添加路由的话,请在 `dynamicRoutes 数组` 中添加 - * @description 前端控制直接改 dynamicRoutes 中的路由,后端控制不需要修改,请求接口路由数据时,会覆盖 dynamicRoutes 第一个顶级 children 的内容(全屏,不包含 layout 中的路由出口) - * @returns 返回路由菜单数据 - */ -export const staticRoutes: Array = [ - // { - // path: '/login', - // name: 'login', - // component: () => import('/@/views/login/index.vue'), - // meta: { - // title: '登录', - // }, - // }, - // /** - // * 提示:写在这里的为全屏界面,不建议写在这里 - // * 请写在 `dynamicRoutes` 路由数组中 - // */ - // { - // path: '/visualizingDemo1', - // name: 'visualizingDemo1', - // component: () => import('/@/views/visualizing/demo1.vue'), - // meta: { - // title: 'message.router.visualizingLinkDemo1', - // }, - // }, - // { - // path: '/visualizingDemo2', - // name: 'visualizingDemo2', - // component: () => import('/@/views/visualizing/demo2.vue'), - // meta: { - // title: 'message.router.visualizingLinkDemo2', - // }, - // }, -]; diff --git a/tg-web/src/stores/index.ts b/tg-web/src/stores/index.ts deleted file mode 100644 index 27c377e..0000000 --- a/tg-web/src/stores/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -// https://pinia.vuejs.org/ -import { createPinia } from 'pinia'; - -// 创建 -const pinia = createPinia(); - -// 导出 -export default pinia; diff --git a/tg-web/src/stores/interface/index.ts b/tg-web/src/stores/interface/index.ts deleted file mode 100644 index cd14a37..0000000 --- a/tg-web/src/stores/interface/index.ts +++ /dev/null @@ -1,91 +0,0 @@ -/** - * 定义接口来定义对象的类型 - * `stores` 全部类型定义在这里 - */ - -// 用户信息 -export interface UserInfosState { - authBtnList: string[]; - photo: string; - roles: string[]; - time: number; - userName: string; - businessId?: number; -} -export interface UserInfosStates { - userInfos: UserInfosState; -} - -// 路由缓存列表 -export interface KeepAliveNamesState { - keepAliveNames: string[]; - cachedViews: string[]; -} - -// 后端返回原始路由(未处理时) -export interface RequestOldRoutesState { - requestOldRoutes: string[]; -} - -// TagsView 路由列表 -export interface TagsViewRoutesState { - tagsViewRoutes: string[]; - isTagsViewCurrenFull: Boolean; -} - -// 路由列表 -export interface RoutesListState { - routesList: string[]; - isColumnsMenuHover: Boolean; - isColumnsNavHover: Boolean; -} - -// 布局配置 -export interface ThemeConfigState { - isDrawer: boolean; - primary: string; - topBar: string; - topBarColor: string; - isTopBarColorGradual: boolean; - menuBar: string; - menuBarColor: string; - isMenuBarColorGradual: boolean; - columnsMenuBar: string; - columnsMenuBarColor: string; - isColumnsMenuBarColorGradual: boolean; - isCollapse: boolean; - isUniqueOpened: boolean; - isFixedHeader: boolean; - isFixedHeaderChange: boolean; - isClassicSplitMenu: boolean; - isLockScreen: boolean; - lockScreenTime: number; - isShowLogo: boolean; - isShowLogoChange: boolean; - isBreadcrumb: boolean; - isTagsview: boolean; - isBreadcrumbIcon: boolean; - isTagsviewIcon: boolean; - isCacheTagsView: boolean; - isSortableTagsView: boolean; - isShareTagsView: boolean; - isFooter: boolean; - isGrayscale: boolean; - isInvert: boolean; - isIsDark: boolean; - isWartermark: boolean; - wartermarkText: string; - tagsStyle: string; - animation: string; - columnsAsideStyle: string; - columnsAsideLayout: string; - layout: string; - isRequestRoutes: boolean; - globalTitle: string; - globalViceTitle: string; - globalI18n: string; - globalComponentSize: string; -} -export interface ThemeConfigStates { - themeConfig: ThemeConfigState; -} diff --git a/tg-web/src/stores/keepAliveNames.ts b/tg-web/src/stores/keepAliveNames.ts deleted file mode 100644 index 32e0389..0000000 --- a/tg-web/src/stores/keepAliveNames.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { defineStore } from 'pinia'; -import { KeepAliveNamesState } from './interface'; - -/** - * 路由缓存列表 - * @methods setCacheKeepAlive 设置要缓存的路由 names(开启 Tagsview) - * @methods addCachedView 添加要缓存的路由 names(关闭 Tagsview) - * @methods delCachedView 删除要缓存的路由 names(关闭 Tagsview) - * @methods delOthersCachedViews 右键菜单`关闭其它`,删除要缓存的路由 names(关闭 Tagsview) - * @methods delAllCachedViews 右键菜单`全部关闭`,删除要缓存的路由 names(关闭 Tagsview) - */ -export const useKeepALiveNames = defineStore('keepALiveNames', { - state: (): KeepAliveNamesState => ({ - keepAliveNames: [], - cachedViews: [], - }), - actions: { - async setCacheKeepAlive(data: Array) { - this.keepAliveNames = data; - }, - async addCachedView(view: any) { - if (this.cachedViews.includes(view.name)) return; - if (view.meta.isKeepAlive) this.cachedViews.push(view.name); - }, - async delCachedView(view: any) { - const index = this.cachedViews.indexOf(view.name); - index > -1 && this.cachedViews.splice(index, 1); - }, - async delOthersCachedViews(view: any) { - if (view.meta.isKeepAlive) this.cachedViews = [view.name]; - else this.cachedViews = []; - }, - async delAllCachedViews() { - this.cachedViews = []; - }, - }, -}); diff --git a/tg-web/src/stores/requestOldRoutes.ts b/tg-web/src/stores/requestOldRoutes.ts deleted file mode 100644 index be9b5cd..0000000 --- a/tg-web/src/stores/requestOldRoutes.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { defineStore } from 'pinia'; -import { RequestOldRoutesState } from './interface'; - -/** - * 后端返回原始路由(未处理时) - * @methods setCacheKeepAlive 设置接口原始路由数据 - */ -export const useRequestOldRoutes = defineStore('requestOldRoutes', { - state: (): RequestOldRoutesState => ({ - requestOldRoutes: [], - }), - actions: { - async setRequestOldRoutes(routes: Array) { - this.requestOldRoutes = routes; - }, - }, -}); diff --git a/tg-web/src/stores/routesList.ts b/tg-web/src/stores/routesList.ts deleted file mode 100644 index 7dd2b28..0000000 --- a/tg-web/src/stores/routesList.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { defineStore } from 'pinia'; -import { RoutesListState } from './interface'; - -/** - * 路由列表 - * @methods setRoutesList 设置路由数据 - * @methods setColumnsMenuHover 设置分栏布局菜单鼠标移入 boolean - * @methods setColumnsNavHover 设置分栏布局最左侧导航鼠标移入 boolean - */ -export const useRoutesList = defineStore('routesList', { - state: (): RoutesListState => ({ - routesList: [], - isColumnsMenuHover: false, - isColumnsNavHover: false, - }), - actions: { - async setRoutesList(data: Array) { - this.routesList = data; - }, - async setColumnsMenuHover(bool: Boolean) { - this.isColumnsMenuHover = bool; - }, - async setColumnsNavHover(bool: Boolean) { - this.isColumnsNavHover = bool; - }, - }, -}); diff --git a/tg-web/src/stores/tagsViewRoutes.ts b/tg-web/src/stores/tagsViewRoutes.ts deleted file mode 100644 index 7a5e6f4..0000000 --- a/tg-web/src/stores/tagsViewRoutes.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { defineStore } from 'pinia'; -import { TagsViewRoutesState } from './interface'; -import { Session } from '/@/utils/storage'; - -/** - * TagsView 路由列表 - * @methods setTagsViewRoutes 设置 TagsView 路由列表 - * @methods setCurrenFullscreen 设置开启/关闭全屏时的 boolean 状态 - */ -export const useTagsViewRoutes = defineStore('tagsViewRoutes', { - state: (): TagsViewRoutesState => ({ - tagsViewRoutes: [], - isTagsViewCurrenFull: false, - }), - actions: { - async setTagsViewRoutes(data: Array) { - this.tagsViewRoutes = data; - }, - setCurrenFullscreen(bool: Boolean) { - Session.set('isTagsViewCurrenFull', bool); - this.isTagsViewCurrenFull = bool; - }, - }, -}); diff --git a/tg-web/src/stores/themeConfig.ts b/tg-web/src/stores/themeConfig.ts deleted file mode 100644 index 006caaa..0000000 --- a/tg-web/src/stores/themeConfig.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { defineStore } from 'pinia'; -import { ThemeConfigStates, ThemeConfigState } from './interface'; - -/** - * 布局配置 - * 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I567R1,感谢@lanbao123 - * 2020.05.28 by lyt 优化。开发时配置不生效问题 - * 修改配置时: - * 1、需要每次都清理 `window.localStorage` 浏览器永久缓存 - * 2、或者点击布局配置最底部 `一键恢复默认` 按钮即可看到效果 - */ -export const useThemeConfig = defineStore('themeConfig', { - state: (): ThemeConfigStates => ({ - themeConfig: { - // 是否开启布局配置抽屉 - isDrawer: false, - - /** - * 全局主题 - */ - // 默认 primary 主题颜色 - primary: '#409eff', - - /** - * 菜单 / 顶栏 - * 注意:v1.0.17 版本去除设置布局切换,重置主题样式(initSetLayoutChange), - * 切换布局需手动设置样式,设置的样式自动同步各布局, - * 代码位置:/@/layout/navBars/breadcrumb/setings.vue - */ - // 默认顶栏导航背景颜色 - topBar: '#1d2128', - // 默认顶栏导航字体颜色 - topBarColor: '#ffffff', - // 是否开启顶栏背景颜色渐变 - isTopBarColorGradual: false, - // 默认菜单导航背景颜色 - menuBar: '#ffffff', - // 默认菜单导航字体颜色 - menuBarColor: '#000000', - // 是否开启菜单背景颜色渐变 - isMenuBarColorGradual: false, - // 默认分栏菜单背景颜色 - columnsMenuBar: '#545c64', - // 默认分栏菜单字体颜色 - columnsMenuBarColor: '#e6e6e6', - // 是否开启分栏菜单背景颜色渐变 - isColumnsMenuBarColorGradual: false, - - /** - * 界面设置 - */ - // 是否开启菜单水平折叠效果 - isCollapse: false, - // 是否开启菜单手风琴效果 - isUniqueOpened: false, - // 是否开启固定 Header - isFixedHeader: false, - // 初始化变量,用于更新菜单 el-scrollbar 的高度,请勿删除 - isFixedHeaderChange: false, - // 是否开启经典布局分割菜单(仅经典布局生效) - isClassicSplitMenu: false, - // 是否开启自动锁屏 - isLockScreen: false, - // 开启自动锁屏倒计时(s/秒) - lockScreenTime: 30, - - /** - * 界面显示 - */ - // 是否开启侧边栏 Logo - isShowLogo: true, - // 初始化变量,用于 el-scrollbar 的高度更新,请勿删除 - isShowLogoChange: false, - // 是否开启 Breadcrumb,强制经典、横向布局不显示 - isBreadcrumb: true, - // 是否开启 Tagsview - isTagsview: false, - // 是否开启 Breadcrumb 图标 - isBreadcrumbIcon: false, - // 是否开启 Tagsview 图标 - isTagsviewIcon: false, - // 是否开启 TagsView 缓存 - isCacheTagsView: false, - // 是否开启 TagsView 拖拽 - isSortableTagsView: true, - // 是否开启 TagsView 共用 - isShareTagsView: false, - // 是否开启 Footer 底部版权信息 - isFooter: false, - // 是否开启灰色模式 - isGrayscale: false, - // 是否开启色弱模式 - isInvert: false, - // 是否开启深色模式 - isIsDark: false, - // 是否开启水印 - isWartermark: false, - // 水印文案 - wartermarkText: 'small@小柒', - - /** - * 其它设置 - */ - // Tagsview 风格:可选值"",默认 tags-style-five - // 定义的值与 `/src/layout/navBars/tagsView/tagsView.vue` 中的 class 同名 - tagsStyle: 'tags-style-five', - // 主页面切换动画:可选值"",默认 slide-right - animation: 'slide-right', - // 分栏高亮风格:可选值"",默认 columns-round - columnsAsideStyle: 'columns-round', - // 分栏布局风格:可选值"",默认 columns-horizontal - columnsAsideLayout: 'columns-vertical', - - /** - * 布局切换 - * 注意:为了演示,切换布局时,颜色会被还原成默认,代码位置:/@/layout/navBars/breadcrumb/setings.vue - * 中的 `initSetLayoutChange(设置布局切换,重置主题样式)` 方法 - */ - // 布局切换:可选值"",默认 defaults - layout: 'classic', - - /** - * 后端控制路由 - */ - // 是否开启后端控制路由 - isRequestRoutes: false, - - /** - * 全局网站标题 / 副标题 - */ - // 网站主标题(菜单导航、浏览器当前网页标题) - globalTitle: import.meta.env.VITE_TITLE, - // 网站副标题(登录页顶部文字) - globalViceTitle: import.meta.env.VITE_TITLE, - // 默认初始语言,可选值"",默认 zh-cn - globalI18n: 'zh-cn', - // 默认全局组件大小,可选值"",默认 'large' - globalComponentSize: 'large', - }, - }), - actions: { - setThemeConfig(data: ThemeConfigState) { - this.themeConfig = data; - }, - }, -}); diff --git a/tg-web/src/stores/userInfo.ts b/tg-web/src/stores/userInfo.ts deleted file mode 100644 index 6ae3b6c..0000000 --- a/tg-web/src/stores/userInfo.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { defineStore } from 'pinia'; -import Cookies from 'js-cookie'; -import { UserInfosStates } from './interface'; -import { Session } from '/@/utils/storage'; - -/** - * 用户信息 - * @methods setUserInfos 设置用户信息 - */ -export const useUserInfo = defineStore('userInfo', { - state: (): UserInfosStates => ({ - userInfos: { - userName: '', - photo: '', - time: 0, - roles: [], - authBtnList: [], - }, - }), - actions: { - async setUserInfos() { - // 存储用户信息到浏览器缓存 - if (Session.get('userInfo')) { - this.userInfos = Session.get('userInfo'); - } else { - const userInfos: any = await this.getApiUserInfo(); - this.userInfos = userInfos; - } - }, - // 模拟接口数据 - // https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP - async getApiUserInfo() { - return new Promise((resolve) => { - setTimeout(() => { - // 模拟数据,请求接口时,记得删除多余代码及对应依赖的引入 - const userName = Cookies.get('userName'); - // 模拟数据 - let defaultRoles: Array = []; - let defaultAuthBtnList: Array = []; - // admin 页面权限标识,对应路由 meta.roles,用于控制路由的显示/隐藏 - let adminRoles: Array = ['admin']; - // admin 按钮权限标识 - let adminAuthBtnList: Array = ['btn.add', 'btn.del', 'btn.edit', 'btn.link']; - // test 页面权限标识,对应路由 meta.roles,用于控制路由的显示/隐藏 - let testRoles: Array = ['common']; - // test 按钮权限标识 - let testAuthBtnList: Array = ['btn.add', 'btn.link']; - // 不同用户模拟不同的用户权限 - if (userName === 'admin') { - defaultRoles = adminRoles; - defaultAuthBtnList = adminAuthBtnList; - } else { - defaultRoles = testRoles; - defaultAuthBtnList = testAuthBtnList; - } - // 用户信息模拟数据 - const userInfos = { - userName: userName, - photo: - userName === 'admin' - ? 'https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500' - : 'https://img2.baidu.com/it/u=2370931438,70387529&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500', - time: new Date().getTime(), - roles: defaultRoles, - authBtnList: defaultAuthBtnList, - }; - resolve(userInfos); - }, 3000); - }); - }, - }, -}); diff --git a/tg-web/src/theme/app.scss b/tg-web/src/theme/app.scss deleted file mode 100644 index 8dc1baf..0000000 --- a/tg-web/src/theme/app.scss +++ /dev/null @@ -1,286 +0,0 @@ -/* 初始化样式 -------------------------------- */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; - outline: none !important; -} - -:root { - --next-color-white: #ffffff; - --next-bg-main-color: #f8f8f8; - --next-bg-color: #f5f5ff; - --next-border-color-light: #f1f2f3; - --next-color-primary-lighter: #ecf5ff; - --next-color-success-lighter: #f0f9eb; - --next-color-warning-lighter: #fdf6ec; - --next-color-danger-lighter: #fef0f0; - --next-color-dark-hover: #0000001a; - --next-color-menu-hover: rgba(0, 0, 0, 0.2); - --next-color-user-hover: rgba(0, 0, 0, 0.04); - --next-color-seting-main: #e9eef3; - --next-color-seting-aside: #d3dce6; - --next-color-seting-header: #b3c0d1; -} - -html, -body, -#app { - margin: 0; - padding: 0; - width: 100%; - height: 100%; - font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif; - font-weight: 400; - -webkit-font-smoothing: antialiased; - -webkit-tap-highlight-color: transparent; - background-color: var(--next-bg-main-color); - font-size: 14px; - overflow: hidden; - position: relative; -} - -.n-data-table { - font-size: 12px; -} - -/* 主布局样式 -------------------------------- */ -.layout-container { - width: 100%; - height: 100%; - .layout-aside { - background: var(--next-bg-menuBar); - box-shadow: 2px 0 6px rgb(0 21 41 / 1%); - height: inherit; - position: relative; - z-index: 1; - display: flex; - flex-direction: column; - overflow-x: hidden !important; - .el-scrollbar__view { - height: 100%; - overflow: hidden; - } - } - .layout-header { - padding: 0 !important; - } - .layout-main { - padding: 0 !important; - overflow: hidden; - width: 100%; - background-color: var(--next-bg-main-color); - } - .el-scrollbar { - width: 100%; - } - // 此字段多次用到,建议不删除,如需修改,请重写覆盖样式 - .layout-view-bg-white { - background: var(--el-color-white); - width: 100%; - height: 100%; - border-radius: 4px; - border: 1px solid var(--el-border-color-light, #ebeef5); - } - .layout-el-aside-br-color { - border-right: 1px solid var(--el-border-color-light, #ebeef5); - } - // pc端左侧导航样式 - .layout-aside-pc-220 { - width: 220px !important; - transition: width 0.3s ease; - } - .layout-aside-pc-64 { - width: 64px !important; - transition: width 0.3s ease; - } - .layout-aside-pc-1 { - width: 1px !important; - transition: width 0.3s ease; - } - // 手机端左侧导航样式 - .layout-aside-mobile { - position: fixed; - top: 0; - left: -220px; - width: 220px; - z-index: 9999999; - } - .layout-aside-mobile-close { - left: -220px; - transition: all 0.3s cubic-bezier(0.39, 0.58, 0.57, 1); - } - .layout-aside-mobile-open { - left: 0; - transition: all 0.3s cubic-bezier(0.22, 0.61, 0.36, 1); - } - .layout-aside-mobile-mode { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - height: 100%; - background-color: rgba(0, 0, 0, 0.5); - z-index: 9999998; - animation: error-img 0.3s; - } - .layout-scrollbar { - @extend .el-scrollbar; - padding: 15px; - } - .layout-mian-height-50 { - height: calc(100vh - 50px); - } - .layout-columns-warp { - flex: 1; - display: flex; - overflow: hidden; - } - .layout-hide { - display: none; - } -} - -/* element plus 全局样式 -------------------------------- */ -.layout-breadcrumb-seting { - .el-divider { - background-color: rgb(230, 230, 230); - } -} - -/* nprogress 进度条跟随主题颜色 -------------------------------- */ -#nprogress { - .bar { - background: var(--el-color-primary) !important; - z-index: 9999999 !important; - } -} - -/* flex 弹性布局 -------------------------------- */ -.flex { - display: flex; -} -.flex-auto { - flex: 1; - overflow: hidden; -} -.flex-center { - @extend .flex; - flex-direction: column; - width: 100%; - overflow: hidden; -} -.flex-margin { - margin: auto; -} -.flex-warp { - display: flex; - flex-wrap: wrap; - align-content: flex-start; - margin: 0 -5px; - .flex-warp-item { - padding: 5px; - .flex-warp-item-box { - width: 100%; - height: 100%; - } - } -} - -/* cursor 鼠标形状 -------------------------------- */ -// 默认 -.cursor-default { - cursor: default !important; -} -// 帮助 -.cursor-help { - cursor: help !important; -} -// 手指 -.cursor-pointer { - cursor: pointer !important; -} -// 移动 -.cursor-move { - cursor: move !important; -} - -/* 宽高 100% -------------------------------- */ -.w100 { - width: 100% !important; -} -.h100 { - height: 100% !important; -} -.vh100 { - height: 100vh !important; -} -.max100vh { - max-height: 100vh !important; -} -.min100vh { - min-height: 100vh !important; -} - -/* 颜色值 -------------------------------- */ -.color-primary { - color: var(--el-color-primary); -} -.color-success { - color: var(--el-color-success); -} -.color-warning { - color: var(--el-color-warning); -} -.color-danger { - color: var(--el-color-danger); -} -.color-info { - color: var(--el-color-info); -} - -/* 字体大小全局样式 -------------------------------- */ -@for $i from 10 through 32 { - .font#{$i} { - font-size: #{$i}px !important; - } -} - -/* 外边距、内边距全局样式 -------------------------------- */ -@for $i from 1 through 35 { - .mt#{$i} { - margin-top: #{$i}px !important; - } - .mr#{$i} { - margin-right: #{$i}px !important; - } - .mb#{$i} { - margin-bottom: #{$i}px !important; - } - .ml#{$i} { - margin-left: #{$i}px !important; - } - .pt#{$i} { - padding-top: #{$i}px !important; - } - .pr#{$i} { - padding-right: #{$i}px !important; - } - .pb#{$i} { - padding-bottom: #{$i}px !important; - } - .pl#{$i} { - padding-left: #{$i}px !important; - } -} diff --git a/tg-web/src/theme/common/transition.scss b/tg-web/src/theme/common/transition.scss deleted file mode 100644 index a03a7bb..0000000 --- a/tg-web/src/theme/common/transition.scss +++ /dev/null @@ -1,94 +0,0 @@ -/* 页面切换动画 -------------------------------- */ -.slide-right-enter-active, -.slide-right-leave-active, -.slide-left-enter-active, -.slide-left-leave-active { - will-change: transform; - transition: all 0.3s ease; -} -// slide-right -.slide-right-enter-from { - opacity: 0; - transform: translateX(-20px); -} -.slide-right-leave-to { - opacity: 0; - transform: translateX(20px); -} -// slide-left -.slide-left-enter-from { - @extend .slide-right-leave-to; -} -.slide-left-leave-to { - @extend .slide-right-enter-from; -} -// opacitys -.opacitys-enter-active, -.opacitys-leave-active { - will-change: transform; - transition: all 0.3s ease; -} -.opacitys-enter-from, -.opacitys-leave-to { - opacity: 0; -} - -/* Breadcrumb 面包屑过渡动画 -------------------------------- */ -.breadcrumb-enter-active, -.breadcrumb-leave-active { - transition: all 0.5s ease; -} -.breadcrumb-enter-from, -.breadcrumb-leave-active { - opacity: 0; - transform: translateX(20px); -} -.breadcrumb-leave-active { - position: absolute; - z-index: -1; -} - -/* logo 过渡动画 -------------------------------- */ -@keyframes logoAnimation { - 0% { - transform: scale(0); - } - 80% { - transform: scale(1.2); - } - 100% { - transform: scale(1); - } -} - -/* 404、401 过渡动画 -------------------------------- */ -@keyframes error-num { - 0% { - transform: translateY(60px); - opacity: 0; - } - 100% { - transform: translateY(0); - opacity: 1; - } -} -@keyframes error-img { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} -@keyframes error-img-two { - 0% { - opacity: 1; - } - 100% { - opacity: 0; - } -} diff --git a/tg-web/src/theme/dark.scss b/tg-web/src/theme/dark.scss deleted file mode 100644 index c922da1..0000000 --- a/tg-web/src/theme/dark.scss +++ /dev/null @@ -1,236 +0,0 @@ -/* 深色模式样式 -------------------------------- */ -[data-theme='dark'] { - // 变量(自定义时,只需修改这里的值) - --next-bg-main: #1f1f1f; - --next-color-white: #ffffff; - --next-color-disabled: #191919; - --next-color-bar: #dadada; - --next-color-primary: #303030; - --next-border-color: #424242; - --next-border-black: #333333; - --next-border-columns: #2a2a2a; - --next-color-seting: #505050; - --next-text-color-regular: #9b9da1; - --next-text-color-placeholder: #7a7a7a; - --next-color-hover: #3c3c3c; - --next-color-hover-rgba: rgba(0, 0, 0, 0.3); - - // root - --next-bg-main-color: var(--next-bg-main) !important; - --next-bg-topBar: var(--next-color-disabled) !important; - --next-bg-topBarColor: var(--next-color-bar) !important; - --next-bg-menuBar: var(--next-color-disabled) !important; - --next-bg-menuBarColor: var(--next-color-bar) !important; - --next-bg-columnsMenuBar: var(--next-color-disabled) !important; - --next-bg-columnsMenuBarColor: var(--next-color-bar) !important; - --next-border-color-light: var(--next-border-black) !important; - --next-color-primary-lighter: var(--next-color-primary) !important; - --next-color-success-lighter: var(--next-color-primary) !important; - --next-color-warning-lighter: var(--next-color-primary) !important; - --next-color-danger-lighter: var(--next-color-primary) !important; - --next-bg-color: var(--next-color-primary) !important; - --next-color-dark-hover: var(--next-color-hover) !important; - --next-color-menu-hover: var(--next-color-hover-rgba) !important; - --next-color-user-hover: var(--next-color-hover-rgba) !important; - --next-color-seting-main: var(--next-color-seting) !important; - --next-color-seting-aside: var(--next-color-hover) !important; - --next-color-seting-header: var(--next-color-primary) !important; - - // element plus - --el-color-white: var(--next-color-disabled) !important; - --el-text-color-primary: var(--next-color-bar) !important; - --el-border-color: var(--next-border-black) !important; - --el-border-color-light: var(--next-border-black) !important; - --el-border-color-lighter: var(--next-border-black) !important; - --el-border-color-extra-light: var(--el-color-primary-light-8) !important; - --el-text-color-regular: var(--next-text-color-regular) !important; - --el-bg-color: var(--next-color-disabled) !important; - --el-color-primary-light-9: var(--next-color-hover) !important; - --el-text-color-disabled: var(--next-text-color-placeholder) !important; - --el-text-color-disabled-base: var(--el-color-primary) !important; - --el-text-color-placeholder: var(--next-text-color-placeholder) !important; - --el-disabled-bg-color: var(--next-color-disabled) !important; - --el-fill-base: var(--next-color-white) !important; - --el-fill-colo: var(--next-color-hover-rgba) !important; - --el-fill-color: var(--next-color-hover-rgba) !important; - --el-fill-color-blank: var(--next-color-disabled) !important; - --el-fill-color-light: var(--next-color-hover-rgba) !important; - --el-bg-color-overlay: var(--el-color-primary-light-9) !important; - --el-mask-color: rgb(42 42 42 / 80%); - - // button - .el-button { - &:hover { - border-color: var(--next-border-color) !important; - } - } - .el-button--primary, - .el-button--info, - .el-button--danger, - .el-button--success, - .el-button--warning { - --el-button-text-color: var(--next-color-white) !important; - --el-button-hover-text-color: var(--next-color-white) !important; - --el-button-disabled-text-color: var(--next-color-white) !important; - &:hover { - border-color: var(--el-button-hover-border-color, var(--el-button-hover-bg-color)) !important; - } - } - - // drawer - .el-divider__text { - background-color: var(--el-color-white) !important; - } - .el-drawer { - border-left: 1px solid var(--next-border-color-light) !important; - } - - // tabs - .el-tabs--border-card { - background-color: var(--el-color-white) !important; - } - .el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active { - background: var(--next-color-primary-lighter); - } - - // alert / notice-bar - .home-card-item { - border: 1px solid var(--next-border-color-light) !important; - } - .el-alert, - .notice-bar { - border: 1px solid var(--next-border-color) !important; - background-color: var(--next-color-disabled) !important; - } - - // menu - .layout-aside { - border-right: 1px solid var(--next-border-color-light) !important; - } - - // colorPicker - .el-color-picker__mask { - background: unset !important; - } - .el-color-picker__trigger { - border: 1px solid var(--next-border-color-light) !important; - } - - // popper / dropdown - .el-popper { - border: 1px solid var(--next-border-color) !important; - color: var(--el-text-color-primary) !important; - .el-popper__arrow:before { - background: var(--el-color-white) !important; - border: 1px solid var(--next-border-color); - } - a { - color: var(--el-text-color-primary) !important; - } - } - .el-popper, - .el-dropdown-menu { - background: var(--el-color-white) !important; - } - .el-dropdown-menu__item:hover:not(.is-disabled) { - background: var(--el-bg-color) !important; - } - .el-dropdown-menu__item.is-disabled { - font-weight: 700 !important; - } - - // input - .el-input-group__append, - .el-input-group__prepend { - border: var(--el-input-border) !important; - border-right: none !important; - background: var(--next-color-disabled) !important; - border-left: 0 !important; - } - .el-input-number__decrease, - .el-input-number__increase { - background: var(--next-color-disabled) !important; - } - - // tag - .el-select .el-select__tags .el-tag { - background-color: var(--next-bg-color) !important; - } - - // pagination - .el-pagination.is-background .el-pager li:not(.disabled).active { - color: var(--next-color-white) !important; - } - .el-pagination.is-background .btn-next, - .el-pagination.is-background .btn-prev, - .el-pagination.is-background .el-pager li { - background-color: var(--next-bg-color); - } - - // radio - .el-radio-button:not(.is-active) .el-radio-button__inner { - border: 1px solid var(--next-border-color-light) !important; - border-left: 0 !important; - } - .el-radio-button.is-active .el-radio-button__inner { - color: var(--next-color-white) !important; - } - - // countup - .countup-card-item-flex { - color: var(--el-text-color-primary) !important; - } - - // editor - .editor-container { - .w-e-toolbar { - background: var(--el-color-white) !important; - border: 1px solid var(--next-border-color-light) !important; - .w-e-menu:hover { - background: var(--next-color-user-hover) !important; - i { - color: var(--el-text-color-primary) !important; - } - } - } - .w-e-text-container { - border: 1px solid var(--next-border-color-light) !important; - border-top: none !important; - .w-e-text { - background: var(--el-color-white) !important; - } - } - } - - // date-picker - .el-picker-panel { - background: var(--el-color-white) !important; - } - - // dialog - .el-dialog { - border: 1px solid var(--el-border-color-lighter); - .el-dialog__header { - color: var(--el-text-color-primary) !important; - } - } - - // columns - .layout-columns-aside ul .layout-columns-active { - color: var(--next-color-white) !important; - } - .layout-columns-aside { - border-right: 1px solid var(--next-border-columns); - } - - // tagsView - .tags-style-one { - .is-active { - color: var(--el-text-color-primary) !important; - } - .layout-navbars-tagsview-ul-li:hover { - border-color: var(--el-border-color-lighter) !important; - } - } -} diff --git a/tg-web/src/theme/element.scss b/tg-web/src/theme/element.scss deleted file mode 100644 index 11c720b..0000000 --- a/tg-web/src/theme/element.scss +++ /dev/null @@ -1,293 +0,0 @@ -@import 'mixins/index.scss'; - -/* Button 按钮 -------------------------------- */ -// 第三方字体图标大小 -.el-button i.el-icon, -.el-button i.iconfont, -.el-button i.fa, -.el-button--default i.iconfont, -.el-button--default i.fa { - font-size: 14px !important; - margin-right: 5px; -} -.el-button--small i.iconfont, -.el-button--small i.fa { - font-size: 12px !important; - margin-right: 5px; -} - -/* Input 输入框、InputNumber 计数器 -------------------------------- */ -.el-input { - height: 100%; -} -// 菜单搜索 -.el-autocomplete-suggestion__wrap { - max-height: 280px !important; -} - -/* Form 表单 -------------------------------- */ -.el-form { - // 用于修改弹窗时表单内容间隔太大问题,如系统设置的新增菜单弹窗里的表单内容 - .el-form-item:last-of-type { - margin-bottom: 0 !important; - } - // 修复行内表单最后一个 el-form-item 位置下移问题 - &.el-form--inline { - .el-form-item--large.el-form-item:last-of-type { - margin-bottom: 22px !important; - } - .el-form-item--default.el-form-item:last-of-type, - .el-form-item--small.el-form-item:last-of-type { - margin-bottom: 18px !important; - } - } -} - -/* Alert 警告 -------------------------------- */ -.el-alert { - border: 1px solid; -} -.el-alert__title { - word-break: break-all; -} - -/* Message 消息提示 -------------------------------- */ -.el-message { - min-width: unset !important; - padding: 15px !important; - box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.02); -} - -/* NavMenu 导航菜单 -------------------------------- */ -// 鼠标 hover 时颜色 -.el-menu-hover-bg-color { - background-color: var(--next-color-menu-hover) !important; -} -// 默认样式修改 -.el-menu { - border-right: none !important; - width: 220px; -} -.el-menu-item { - height: 56px !important; - line-height: 56px !important; -} -.el-menu-item, -.el-sub-menu__title { - color: var(--next-bg-menuBarColor); -} -// 修复点击左侧菜单折叠再展开时,宽度不跟随问题 -.el-menu--collapse { - width: 64px !important; -} -// 外部链接时 -.el-menu-item a, -.el-menu-item a:hover, -.el-menu-item i, -.el-sub-menu__title i { - color: inherit; - text-decoration: none; -} -// 第三方图标字体间距/大小设置 -.el-menu-item .iconfont, -.el-sub-menu .iconfont, -.el-menu-item .fa, -.el-sub-menu .fa { - @include generalIcon; -} -// 水平菜单、横向菜单高亮 背景色,鼠标 hover 时,有子级菜单的背景色 -.el-menu-item.is-active, -.el-sub-menu.is-active .el-sub-menu__title, -.el-sub-menu:not(.is-opened):hover .el-sub-menu__title { - @extend .el-menu-hover-bg-color; -} -.el-sub-menu.is-active.is-opened .el-sub-menu__title { - background-color: unset !important; -} -// 子级菜单背景颜色 -// .el-menu--inline { -// background: var(--next-bg-menuBar-light-1); -// } -// 水平菜单、横向菜单折叠 a 标签 -.el-popper.is-dark a { - color: var(--el-color-white) !important; - text-decoration: none; -} -// 水平菜单、横向菜单折叠背景色 -.el-popper.is-pure.is-light { - // 水平菜单 - .el-menu--vertical { - background: var(--next-bg-menuBar); - .el-sub-menu.is-active .el-sub-menu__title { - color: var(--el-menu-active-color); - } - .el-popper.is-pure.is-light { - .el-menu--vertical { - .el-sub-menu .el-sub-menu__title { - background-color: unset !important; - color: var(--next-bg-menuBarColor); - } - .el-sub-menu.is-active .el-sub-menu__title { - color: var(--el-menu-active-color); - } - } - } - } - // 横向菜单 - .el-menu--horizontal { - background: var(--next-bg-topBar); - .el-menu-item, - .el-sub-menu { - height: 50px !important; - line-height: 50px !important; - color: var(--next-bg-topBarColor); - .el-sub-menu__title { - height: 50px !important; - line-height: 50px !important; - color: var(--next-bg-topBarColor); - } - .el-popper.is-pure.is-light { - .el-menu--horizontal { - .el-sub-menu .el-sub-menu__title { - background-color: unset !important; - color: var(--next-bg-topBarColor); - } - .el-sub-menu.is-active .el-sub-menu__title { - color: var(--el-menu-active-color); - } - } - } - } - .el-menu-item.is-active, - .el-sub-menu.is-active .el-sub-menu__title { - color: var(--el-menu-active-color); - } - } -} -// 横向菜单(经典、横向)布局 -.el-menu.el-menu--horizontal { - border-bottom: none !important; - width: 100% !important; - .el-menu-item, - .el-sub-menu__title { - height: 50px !important; - color: var(--next-bg-topBarColor); - } - .el-menu-item:not(.is-active):hover, - .el-sub-menu:not(.is-active):hover .el-sub-menu__title { - color: var(--next-bg-topBarColor); - } -} - -/* Tabs 标签页 -------------------------------- */ -.el-tabs__nav-wrap::after { - height: 1px !important; -} - -/* Dropdown 下拉菜单 -------------------------------- */ -.el-dropdown-menu { - list-style: none !important; /*修复 Dropdown 下拉菜单样式问题 2022.03.04*/ -} -.el-dropdown-menu .el-dropdown-menu__item { - white-space: nowrap; - &:not(.is-disabled):hover { - background-color: var(--el-dropdown-menuItem-hover-fill); - color: var(--el-dropdown-menuItem-hover-color); - } -} - -/* Steps 步骤条 -------------------------------- */ -.el-step__icon-inner { - font-size: 30px !important; - font-weight: 400 !important; -} -.el-step__title { - font-size: 14px; -} - -/* Dialog 对话框 -------------------------------- */ -.el-overlay { - overflow: hidden; - .el-overlay-dialog { - display: flex; - align-items: center; - justify-content: center; - position: unset !important; - width: 100%; - height: 100%; - .el-dialog { - margin: 0 auto !important; - position: absolute; - .el-dialog__body { - padding: 20px !important; - } - } - } -} -.el-dialog__body { - max-height: calc(90vh - 111px) !important; - overflow-y: auto; - overflow-x: hidden; -} - -/* Card 卡片 -------------------------------- */ -.el-card__header { - padding: 15px 20px; -} - -/* Table 表格 element plus 2.2.0 版本 -------------------------------- */ -.el-table { - .el-button.is-text { - padding: 0; - } -} - -/* scrollbar -------------------------------- */ -.el-scrollbar__bar { - z-index: 4; -} -.el-scrollbar__wrap { - max-height: 100%; /*防止页面切换时,滚动条高度不变的问题(滚动条高度非滚动条滚动高度)*/ -} -.el-select-dropdown .el-scrollbar__wrap { - overflow-x: scroll !important; -} -.el-select-dropdown__wrap { - max-height: 274px !important; /*修复Select 选择器高度问题*/ -} -.el-cascader-menu__wrap.el-scrollbar__wrap { - height: 204px !important; /*修复Cascader 级联选择器高度问题*/ -} - -/* Drawer 抽屉 -------------------------------- */ -.el-drawer { - --el-drawer-padding-primary: unset !important; - .el-drawer__header { - padding: 0 15px !important; - height: 50px; - display: flex; - align-items: center; - margin-bottom: 0 !important; - border-bottom: 1px solid var(--el-border-color); - color: var(--el-text-color-primary); - } - .el-drawer__body { - width: 100%; - height: 100%; - overflow: auto; - } -} diff --git a/tg-web/src/theme/iconSelector.scss b/tg-web/src/theme/iconSelector.scss deleted file mode 100644 index 970201e..0000000 --- a/tg-web/src/theme/iconSelector.scss +++ /dev/null @@ -1,70 +0,0 @@ -/* Popover 弹出框(图标选择器) -------------------------------- */ -.icon-selector-popper { - padding: 0 !important; - .icon-selector-warp { - height: 260px; - overflow: hidden; - .icon-selector-warp-title { - height: 40px; - line-height: 40px; - padding: 0 15px; - .icon-selector-warp-title-tab { - span { - cursor: pointer; - &:hover { - color: var(--el-color-primary); - text-decoration: underline; - } - } - .span-active { - color: var(--el-color-primary); - text-decoration: underline; - } - } - } - .icon-selector-warp-row { - height: 230px; - overflow: hidden; - border-top: 1px solid var(--el-border-color); - .el-row { - padding: 15px; - } - .el-scrollbar__bar.is-horizontal { - display: none; - } - .icon-selector-warp-item { - display: flex; - border: 1px solid var(--el-border-color); - padding: 5px; - border-radius: 5px; - margin-bottom: 10px; - .icon-selector-warp-item-value { - i { - font-size: 20px; - color: var(--el-text-color-regular); - } - } - &:hover { - cursor: pointer; - background-color: var(--el-color-primary-light-9); - border: 1px solid var(--el-color-primary-light-5); - .icon-selector-warp-item-value { - i { - color: var(--el-color-primary); - } - } - } - } - .icon-selector-active { - background-color: var(--el-color-primary-light-9); - border: 1px solid var(--el-color-primary-light-5); - .icon-selector-warp-item-value { - i { - color: var(--el-color-primary); - } - } - } - } - } -} diff --git a/tg-web/src/theme/index.scss b/tg-web/src/theme/index.scss deleted file mode 100644 index c574e00..0000000 --- a/tg-web/src/theme/index.scss +++ /dev/null @@ -1,8 +0,0 @@ -@import './app.scss'; -@import 'common/transition.scss'; -@import './other.scss'; -@import './element.scss'; -@import './iconSelector.scss'; -@import './media/media.scss'; -@import './waves.scss'; -@import './dark.scss'; diff --git a/tg-web/src/theme/loading.scss b/tg-web/src/theme/loading.scss deleted file mode 100644 index c28c7b9..0000000 --- a/tg-web/src/theme/loading.scss +++ /dev/null @@ -1,51 +0,0 @@ -.loading-next { - width: 100%; - height: 100%; -} -.loading-next .loading-next-box { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} -.loading-next .loading-next-box-warp { - width: 80px; - height: 80px; -} -.loading-next .loading-next-box-warp .loading-next-box-item { - width: 33.333333%; - height: 33.333333%; - background: var(--el-color-primary); - float: left; - animation: loading-next-animation 1.2s infinite ease; - border-radius: 1px; -} -.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(7) { - animation-delay: 0s; -} -.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(4), -.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(8) { - animation-delay: 0.1s; -} -.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(1), -.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(5), -.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(9) { - animation-delay: 0.2s; -} -.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(2), -.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(6) { - animation-delay: 0.3s; -} -.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(3) { - animation-delay: 0.4s; -} -@keyframes loading-next-animation { - 0%, - 70%, - 100% { - transform: scale3D(1, 1, 1); - } - 35% { - transform: scale3D(0, 0, 1); - } -} diff --git a/tg-web/src/theme/media/chart.scss b/tg-web/src/theme/media/chart.scss deleted file mode 100644 index 8485e39..0000000 --- a/tg-web/src/theme/media/chart.scss +++ /dev/null @@ -1,94 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于768px -------------------------------- */ -@media screen and (max-width: $sm) { - .big-data-down-left { - width: 100% !important; - flex-direction: unset !important; - flex-wrap: wrap; - .flex-warp-item { - min-height: 196.24px; - padding: 0 7.5px 15px 15px !important; - .flex-warp-item-box { - border: none !important; - border-bottom: 1px solid #ebeef5 !important; - } - } - } - .big-data-down-center { - width: 100% !important; - .big-data-down-center-one, - .big-data-down-center-two { - min-height: 196.24px; - padding-left: 15px !important; - .big-data-down-center-one-content { - border: none !important; - border-bottom: 1px solid #ebeef5 !important; - } - .flex-warp-item-box { - @extend .big-data-down-center-one-content; - } - } - } - .big-data-down-right { - .flex-warp-item { - .flex-warp-item-box { - border: none !important; - border-bottom: 1px solid #ebeef5 !important; - } - &:nth-of-type(2) { - padding-left: 15px !important; - } - &:last-of-type { - .flex-warp-item-box { - border: none !important; - } - } - } - } -} - -/* 页面宽度大于768px小于1200px -------------------------------- */ -@media screen and (min-width: $sm) and (max-width: $lg) { - .chart-warp-bottom { - .big-data-down-left { - width: 50% !important; - } - .big-data-down-center { - width: 50% !important; - } - .big-data-down-right { - .flex-warp-item { - width: 50% !important; - &:nth-of-type(2) { - padding-left: 7.5px !important; - } - } - } - } -} - -/* 页面宽度小于1200px -------------------------------- */ -@media screen and (max-width: $lg) { - .chart-warp-top { - .up-left { - display: none; - } - } - .chart-warp-bottom { - overflow-y: auto !important; - flex-wrap: wrap; - .big-data-down-right { - width: 100% !important; - flex-direction: unset !important; - flex-wrap: wrap; - .flex-warp-item { - min-height: 196.24px; - padding: 0 7.5px 15px 15px !important; - } - } - } -} diff --git a/tg-web/src/theme/media/cityLinkage.scss b/tg-web/src/theme/media/cityLinkage.scss deleted file mode 100644 index 1394156..0000000 --- a/tg-web/src/theme/media/cityLinkage.scss +++ /dev/null @@ -1,10 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于576px -------------------------------- */ -@media screen and (max-width: $xs) { - .el-cascader__dropdown.el-popper { - overflow: auto; - max-width: 100%; - } -} diff --git a/tg-web/src/theme/media/date.scss b/tg-web/src/theme/media/date.scss deleted file mode 100644 index 1a50397..0000000 --- a/tg-web/src/theme/media/date.scss +++ /dev/null @@ -1,25 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于768px -------------------------------- */ -@media screen and (max-width: $sm) { - // 时间选择器适配 - .el-date-range-picker { - width: 100vw; - .el-picker-panel__body { - min-width: 100%; - .el-date-range-picker__content { - .el-date-range-picker__header div { - margin-left: 22px; - margin-right: 0px; - } - & + .el-date-range-picker__content { - .el-date-range-picker__header div { - margin-left: 0px; - margin-right: 22px; - } - } - } - } - } -} diff --git a/tg-web/src/theme/media/dialog.scss b/tg-web/src/theme/media/dialog.scss deleted file mode 100644 index 023ccae..0000000 --- a/tg-web/src/theme/media/dialog.scss +++ /dev/null @@ -1,12 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于800px -------------------------------- */ -@media screen and (max-width: 800px) { - .el-dialog { - width: 90% !important; - } - .el-dialog.is-fullscreen { - width: 100% !important; - } -} diff --git a/tg-web/src/theme/media/error.scss b/tg-web/src/theme/media/error.scss deleted file mode 100644 index f35015f..0000000 --- a/tg-web/src/theme/media/error.scss +++ /dev/null @@ -1,45 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于768px -------------------------------- */ -@media screen and (max-width: $sm) { - .error { - .error-flex { - flex-direction: column-reverse !important; - height: auto !important; - width: 100% !important; - } - .right, - .left { - flex: unset !important; - display: flex !important; - } - .left-item { - margin: auto !important; - } - .right img { - max-width: 450px !important; - @extend .left-item; - } - } -} - -/* 页面宽度大于768px小于992px -------------------------------- */ -@media screen and (min-width: $sm) and (max-width: $md) { - .error { - .error-flex { - padding-left: 30px !important; - } - } -} - -/* 页面宽度小于1200px -------------------------------- */ -@media screen and (max-width: $lg) { - .error { - .error-flex { - padding: 0 30px; - } - } -} diff --git a/tg-web/src/theme/media/form.scss b/tg-web/src/theme/media/form.scss deleted file mode 100644 index 6fe0a8c..0000000 --- a/tg-web/src/theme/media/form.scss +++ /dev/null @@ -1,18 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于576px -------------------------------- */ -@media screen and (max-width: $xs) { - .el-form-item__label { - width: 100% !important; - text-align: left !important; - // 移动端 label 右对齐问题 - justify-content: flex-start !important; - } - .el-form-item__content { - margin-left: 0 !important; - } - .el-form-item { - display: unset !important; - } -} diff --git a/tg-web/src/theme/media/home.scss b/tg-web/src/theme/media/home.scss deleted file mode 100644 index 5a2417e..0000000 --- a/tg-web/src/theme/media/home.scss +++ /dev/null @@ -1,23 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于768px -------------------------------- */ -@media screen and (max-width: $sm) { - .home-media, - .home-media-sm { - margin-top: 15px; - } -} - -/* 页面宽度小于1200px -------------------------------- */ -@media screen and (max-width: $lg) { - .home-media-lg { - margin-top: 15px; - } - .home-monitor { - .flex-warp-item { - width: 33.33% !important; - } - } -} diff --git a/tg-web/src/theme/media/index.scss b/tg-web/src/theme/media/index.scss deleted file mode 100644 index 4761c0c..0000000 --- a/tg-web/src/theme/media/index.scss +++ /dev/null @@ -1,15 +0,0 @@ -/* 栅格布局(媒体查询变量) -* https://developer.mozilla.org/zh-CN/docs/Learn/CSS/CSS_layout/Media_queries -* $us ≥376px 响应式栅格 -* $xs ≥576px 响应式栅格 -* $sm ≥768px 响应式栅格 -* $md ≥992px 响应式栅格 -* $lg ≥1200px 响应式栅格 -* $xl ≥1920px 响应式栅格 -------------------------------- */ -$us: 376px; -$xs: 576px; -$sm: 768px; -$md: 992px; -$lg: 1200px; -$xl: 1920px; diff --git a/tg-web/src/theme/media/layout.scss b/tg-web/src/theme/media/layout.scss deleted file mode 100644 index 77cbec0..0000000 --- a/tg-web/src/theme/media/layout.scss +++ /dev/null @@ -1,55 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于576px -------------------------------- */ -@media screen and (max-width: $xs) { - // MessageBox 弹框 - .el-message-box { - width: 80% !important; - } -} - -/* 页面宽度小于768px -------------------------------- */ -@media screen and (max-width: $sm) { - // Breadcrumb 面包屑 - .layout-navbars-breadcrumb-hide { - display: none; - } - // 外链视图 - .layout-view-link { - a { - max-width: 80%; - text-align: center; - } - } - // 菜单搜索 - .layout-search-dialog { - .el-autocomplete { - width: 80% !important; - } - } -} - -/* 页面宽度小于1000px -------------------------------- */ -@media screen and (max-width: 1000px) { - // 布局配置 - .layout-drawer-content-flex { - position: relative; - &::after { - content: '手机版不支持切换布局'; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1; - text-align: center; - height: 140px; - line-height: 140px; - background: rgba(255, 255, 255, 0.9); - color: #666666; - } - } -} diff --git a/tg-web/src/theme/media/login.scss b/tg-web/src/theme/media/login.scss deleted file mode 100644 index 41a0159..0000000 --- a/tg-web/src/theme/media/login.scss +++ /dev/null @@ -1,63 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于992px -------------------------------- */ -@media screen and (max-width: $lg) { - .login-container { - .login-icon-group { - &::before { - content: ''; - height: 70% !important; - transition: all 0.3s ease; - } - &::after { - content: ''; - width: 100px !important; - height: 200px !important; - transition: all 0.3s ease; - } - } - } -} - -/* 页面宽度小于992px -------------------------------- */ -@media screen and (max-width: $md) { - .login-content { - right: unset !important; - left: 50% !important; - transform: translate(-50%, -50%) translate3d(0, 0, 0) !important; - } -} - -/* 页面宽度小于576px -------------------------------- */ -@media screen and (max-width: $xs) { - .login-container { - .login-icon-group { - display: none !important; - } - .login-content { - width: 100% !important; - height: 100% !important; - padding: 20px 0 !important; - border-radius: 0 !important; - box-shadow: unset !important; - border: none !important; - } - .el-form-item { - display: flex !important; - } - } -} - -/* 页面宽度小于375px -------------------------------- */ -@media screen and (max-width: $us) { - .login-container { - .login-content-title { - font-size: 18px !important; - transition: all 0.3s ease; - } - } -} diff --git a/tg-web/src/theme/media/media.scss b/tg-web/src/theme/media/media.scss deleted file mode 100644 index bed1c35..0000000 --- a/tg-web/src/theme/media/media.scss +++ /dev/null @@ -1,13 +0,0 @@ -@import './login.scss'; -@import './error.scss'; -@import './layout.scss'; -@import './personal.scss'; -@import './tagsView.scss'; -@import './home.scss'; -@import './chart.scss'; -@import './form.scss'; -@import './scrollbar.scss'; -@import './pagination.scss'; -@import './dialog.scss'; -@import './cityLinkage.scss'; -@import './date.scss'; diff --git a/tg-web/src/theme/media/pagination.scss b/tg-web/src/theme/media/pagination.scss deleted file mode 100644 index 400ebaa..0000000 --- a/tg-web/src/theme/media/pagination.scss +++ /dev/null @@ -1,15 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于576px -------------------------------- */ -@media screen and (max-width: $xs) { - .el-pager, - .el-pagination__jump { - display: none !important; - } -} - -// 默认居中对齐 -.el-pagination { - text-align: center !important; -} diff --git a/tg-web/src/theme/media/personal.scss b/tg-web/src/theme/media/personal.scss deleted file mode 100644 index 7ec0d4a..0000000 --- a/tg-web/src/theme/media/personal.scss +++ /dev/null @@ -1,16 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于768px -------------------------------- */ -@media screen and (max-width: $sm) { - .personal-info { - padding-left: 0 !important; - margin-top: 15px; - } - .personal-recommend-col { - margin-bottom: 15px; - &:last-of-type { - margin-bottom: 0; - } - } -} diff --git a/tg-web/src/theme/media/scrollbar.scss b/tg-web/src/theme/media/scrollbar.scss deleted file mode 100644 index 968a79d..0000000 --- a/tg-web/src/theme/media/scrollbar.scss +++ /dev/null @@ -1,56 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于768px -------------------------------- */ -@media screen and (max-width: $sm) { - // 滚动条的宽度 - ::-webkit-scrollbar { - width: 3px !important; - height: 3px !important; - } - ::-webkit-scrollbar-track-piece { - background-color: var(--next-bg-main-color); - } - // 滚动条的设置 - ::-webkit-scrollbar-thumb { - background-color: rgba(144, 147, 153, 0.3); - background-clip: padding-box; - min-height: 28px; - border-radius: 5px; - transition: 0.3s background-color; - } - ::-webkit-scrollbar-thumb:hover { - background-color: rgba(144, 147, 153, 0.5); - } - // element plus scrollbar - .el-scrollbar__bar.is-vertical { - width: 2px !important; - } - .el-scrollbar__bar.is-horizontal { - height: 2px !important; - } -} - -/* 页面宽度大于768px -------------------------------- */ -@media screen and (min-width: 769px) { - // 滚动条的宽度 - ::-webkit-scrollbar { - width: 7px; - height: 7px; - } - ::-webkit-scrollbar-track-piece { - background-color: var(--next-bg-main-color); - } - // 滚动条的设置 - ::-webkit-scrollbar-thumb { - background-color: rgba(144, 147, 153, 0.3); - background-clip: padding-box; - min-height: 28px; - border-radius: 5px; - transition: 0.3s background-color; - } - ::-webkit-scrollbar-thumb:hover { - background-color: rgba(144, 147, 153, 0.5); - } -} diff --git a/tg-web/src/theme/media/tagsView.scss b/tg-web/src/theme/media/tagsView.scss deleted file mode 100644 index b71674e..0000000 --- a/tg-web/src/theme/media/tagsView.scss +++ /dev/null @@ -1,11 +0,0 @@ -@import './index.scss'; - -/* 页面宽度小于768px -------------------------------- */ -@media screen and (max-width: $sm) { - .tags-view-form { - .tags-view-form-col { - margin-bottom: 20px; - } - } -} diff --git a/tg-web/src/theme/mixins/index.scss b/tg-web/src/theme/mixins/index.scss deleted file mode 100644 index 61f3c6b..0000000 --- a/tg-web/src/theme/mixins/index.scss +++ /dev/null @@ -1,56 +0,0 @@ -/* 第三方图标字体间距/大小设置 -------------------------------- */ -@mixin generalIcon { - font-size: 14px !important; - display: inline-block; - vertical-align: middle; - margin-right: 5px; - width: 24px; - text-align: center; - justify-content: center; -} - -/* 文本不换行 -------------------------------- */ -@mixin text-no-wrap() { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; -} - -/* 多行文本溢出 - ------------------------------- */ -@mixin text-ellipsis($line: 2) { - overflow: hidden; - word-break: break-all; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: $line; - -webkit-box-orient: vertical; -} - -/* 滚动条(页面未使用) div 中使用: - ------------------------------- */ -// .test { -// @include scrollBar; -// } -@mixin scrollBar { - // 滚动条凹槽的颜色,还可以设置边框属性 - &::-webkit-scrollbar-track-piece { - background-color: #f8f8f8; - } - // 滚动条的宽度 - &::-webkit-scrollbar { - width: 9px; - height: 9px; - } - // 滚动条的设置 - &::-webkit-scrollbar-thumb { - background-color: #dddddd; - background-clip: padding-box; - min-height: 28px; - } - &::-webkit-scrollbar-thumb:hover { - background-color: #bbb; - } -} diff --git a/tg-web/src/theme/other.scss b/tg-web/src/theme/other.scss deleted file mode 100644 index a0451b8..0000000 --- a/tg-web/src/theme/other.scss +++ /dev/null @@ -1,36 +0,0 @@ -/* wangeditor富文本编辑器 -------------------------------- */ -.editor-container { - z-index: 9999; - .w-e-toolbar { - border: 1px solid var(--el-border-color-light, #ebeef5) !important; - border-bottom: 1px solid var(--el-border-color-light, #ebeef5) !important; - border-top-left-radius: 3px; - border-top-right-radius: 3px; - z-index: 2 !important; - } - .w-e-text-container { - border: 1px solid var(--el-border-color-light, #ebeef5) !important; - border-top: none !important; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - z-index: 1 !important; - } -} - -[data-theme='dark'] { - // textarea - css vars - --w-e-textarea-bg-color: var(--el-color-white) !important; - --w-e-textarea-color: var(--el-text-color-primary) !important; - - // toolbar - css vars - --w-e-toolbar-color: var(--el-text-color-primary) !important; - --w-e-toolbar-bg-color: var(--el-color-white) !important; - --w-e-toolbar-active-color: var(--el-text-color-primary) !important; - --w-e-toolbar-active-bg-color: var(--next-color-menu-hover) !important; - --w-e-toolbar-border-color: var(--el-border-color-light, #ebeef5) !important; - - // modal - css vars - --w-e-modal-button-bg-color: var(--el-color-primary) !important; - --w-e-modal-button-border-color: var(--el-color-primary) !important; -} diff --git a/tg-web/src/theme/waves.scss b/tg-web/src/theme/waves.scss deleted file mode 100644 index 23add2c..0000000 --- a/tg-web/src/theme/waves.scss +++ /dev/null @@ -1,101 +0,0 @@ -/* Waves v0.6.0 -* http://fian.my.id/Waves -* -* Copyright 2014 Alfiana E. Sibuea and other contributors -* Released under the MIT license -* https://github.com/fians/Waves/blob/master/LICENSE -*/ -.waves-effect { - position: relative; - cursor: pointer; - display: inline-block; - overflow: hidden; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -webkit-tap-highlight-color: transparent; - vertical-align: middle; - z-index: 1; - will-change: opacity, transform; - transition: all 0.3s ease-out; -} -.waves-effect .waves-ripple { - position: absolute; - border-radius: 50%; - width: 20px; - height: 20px; - margin-top: -10px; - margin-left: -10px; - opacity: 0; - background: rgba(0, 0, 0, 0.2); - transition: all 0.7s ease-out; - transition-property: opacity, -webkit-transform; - transition-property: transform, opacity; - transition-property: transform, opacity, -webkit-transform; - -webkit-transform: scale(0); - transform: scale(0); - pointer-events: none; -} -.waves-effect.waves-light .waves-ripple { - background-color: rgba(255, 255, 255, 0.45); -} -.waves-effect.waves-red .waves-ripple { - background-color: rgba(244, 67, 54, 0.7); -} -.waves-effect.waves-yellow .waves-ripple { - background-color: rgba(255, 235, 59, 0.7); -} -.waves-effect.waves-orange .waves-ripple { - background-color: rgba(255, 152, 0, 0.7); -} -.waves-effect.waves-purple .waves-ripple { - background-color: rgba(156, 39, 176, 0.7); -} -.waves-effect.waves-green .waves-ripple { - background-color: rgba(76, 175, 80, 0.7); -} -.waves-effect.waves-teal .waves-ripple { - background-color: rgba(0, 150, 136, 0.7); -} -.waves-effect input[type='button'], -.waves-effect input[type='reset'], -.waves-effect input[type='submit'] { - border: 0; - font-style: normal; - font-size: inherit; - text-transform: inherit; - background: none; -} -.waves-notransition { - transition: none !important; -} -.waves-circle { - -webkit-transform: translateZ(0); - transform: translateZ(0); - -webkit-mask-image: -webkit-radial-gradient(circle, #fff 100%, #000 100%); -} -.waves-input-wrapper { - border-radius: 0.2em; - vertical-align: bottom; -} -.waves-input-wrapper .waves-button-input { - position: relative; - top: 0; - left: 0; - z-index: 1; -} -.waves-circle { - text-align: center; - width: 2.5em; - height: 2.5em; - line-height: 2.5em; - border-radius: 50%; - -webkit-mask-image: none; -} -.waves-block { - display: block; -} -a.waves-effect .waves-ripple { - z-index: -1; -} diff --git a/tg-web/src/types/globalInterface.ts b/tg-web/src/types/globalInterface.ts deleted file mode 100644 index b7ad617..0000000 --- a/tg-web/src/types/globalInterface.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface valueAndLabelI { - value: string; - label: string; -} \ No newline at end of file diff --git a/tg-web/src/utils/arrayOperation.ts b/tg-web/src/utils/arrayOperation.ts deleted file mode 100644 index 1fe0359..0000000 --- a/tg-web/src/utils/arrayOperation.ts +++ /dev/null @@ -1,67 +0,0 @@ -/** - * 判断两数组是否相同 - * @param news 新数据 - * @param old 源数据 - * @returns 两数组相同返回 `true`,反之则反 - */ -export function judementSameArr(news: unknown[] | string[], old: string[]): boolean { - let count = 0; - const leng = old.length; - for (let i in old) { - for (let j in news) { - if (old[i] === news[j]) count++; - } - } - return count === leng ? true : false; -} - -/** - * 判断两个对象是否相同 - * @param a 要比较的对象一 - * @param b 要比较的对象二 - * @returns 相同返回 true,反之则反 - */ -export function isObjectValueEqual(a: { [key: string]: any }, b: { [key: string]: any }) { - if (!a || !b) return false; - let aProps = Object.getOwnPropertyNames(a); - let bProps = Object.getOwnPropertyNames(b); - if (aProps.length != bProps.length) return false; - for (let i = 0; i < aProps.length; i++) { - let propName = aProps[i]; - let propA = a[propName]; - let propB = b[propName]; - if (!b.hasOwnProperty(propName)) return false; - if (propA instanceof Object) { - if (!isObjectValueEqual(propA, propB)) return false; - } else if (propA !== propB) { - return false; - } - } - return true; -} - -export function arr2Obj(arr: any[], key: string) { - const obj: any = {}; - arr.map(item => { - if (item.hasOwnProperty(key)) { - obj[key] = item[key]; - } - }) - return obj; -} - -export function sortByKey(arr: any[], key: any, desc = false) { - const compare = function(key: string, desc: boolean) { - return function(a: any, b: any) { - const aVal = a[key]; - const bVal = b[key]; - if (desc) { - return bVal - aVal; - } else { - return aVal - bVal; - } - } - } - const newArr = arr.sort(compare(key, desc)); - return newArr; -} diff --git a/tg-web/src/utils/authDirective.ts b/tg-web/src/utils/authDirective.ts deleted file mode 100644 index 5971e64..0000000 --- a/tg-web/src/utils/authDirective.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { App } from 'vue'; -import { useUserInfo } from '/@/stores/userInfo'; -import { judementSameArr } from '/@/utils/arrayOperation'; - -/** - * 用户权限指令 - * @directive 单个权限验证(v-auth="xxx") - * @directive 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]") - * @directive 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]") - */ -export function authDirective(app: App) { - // 单个权限验证(v-auth="xxx") - app.directive('auth', { - mounted(el, binding) { - const stores = useUserInfo(); - if (!stores.userInfos.authBtnList.some((v: string) => v === binding.value)) el.parentNode.removeChild(el); - }, - }); - // 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]") - app.directive('auths', { - mounted(el, binding) { - let flag = false; - const stores = useUserInfo(); - stores.userInfos.authBtnList.map((val: string) => { - binding.value.map((v: string) => { - if (val === v) flag = true; - }); - }); - if (!flag) el.parentNode.removeChild(el); - }, - }); - // 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]") - app.directive('auth-all', { - mounted(el, binding) { - const stores = useUserInfo(); - const flag = judementSameArr(binding.value, stores.userInfos.authBtnList); - if (!flag) el.parentNode.removeChild(el); - }, - }); -} diff --git a/tg-web/src/utils/authFunction.ts b/tg-web/src/utils/authFunction.ts deleted file mode 100644 index 84c0ab4..0000000 --- a/tg-web/src/utils/authFunction.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { useUserInfo } from '/@/stores/userInfo'; -import { judementSameArr } from '/@/utils/arrayOperation'; - -/** - * 单个权限验证 - * @param value 权限值 - * @returns 有权限,返回 `true`,反之则反 - */ -export function auth(value: string): boolean { - const stores = useUserInfo(); - return stores.userInfos.authBtnList.some((v: string) => v === value); -} - -/** - * 多个权限验证,满足一个则为 true - * @param value 权限值 - * @returns 有权限,返回 `true`,反之则反 - */ -export function auths(value: Array): boolean { - let flag = false; - const stores = useUserInfo(); - stores.userInfos.authBtnList.map((val: string) => { - value.map((v: string) => { - if (val === v) flag = true; - }); - }); - return flag; -} - -/** - * 多个权限验证,全部满足则为 true - * @param value 权限值 - * @returns 有权限,返回 `true`,反之则反 - */ -export function authAll(value: Array): boolean { - const stores = useUserInfo(); - return judementSameArr(value, stores.userInfos.authBtnList); -} diff --git a/tg-web/src/utils/commonFunction.ts b/tg-web/src/utils/commonFunction.ts deleted file mode 100644 index 1c069e6..0000000 --- a/tg-web/src/utils/commonFunction.ts +++ /dev/null @@ -1,65 +0,0 @@ -// 通用函数 -import useClipboard from 'vue-clipboard3'; -import { ElMessage } from 'element-plus'; -import { formatDate } from '/@/utils/formatTime'; -import { useI18n } from 'vue-i18n'; - -export default function () { - const { t } = useI18n(); - const { toClipboard } = useClipboard(); - //百分比格式化 - const percentFormat = (row: any, column: number, cellValue: any) => { - return cellValue ? `${cellValue}%` : '-'; - }; - //列表日期时间格式化 - const dateFormatYMD = (row: any, column: number, cellValue: any) => { - if (!cellValue) return '-'; - return formatDate(new Date(cellValue), 'YYYY-mm-dd'); - }; - //列表日期时间格式化 - const dateFormatYMDHMS = (row: any, column: number, cellValue: any) => { - if (!cellValue) return '-'; - return formatDate(new Date(cellValue), 'YYYY-mm-dd HH:MM:SS'); - }; - //列表日期时间格式化 - const dateFormatHMS = (row: any, column: number, cellValue: any) => { - if (!cellValue) return '-'; - let time = 0; - if (typeof row === 'number') time = row; - if (typeof cellValue === 'number') time = cellValue; - return formatDate(new Date(time * 1000), 'HH:MM:SS'); - }; - // 小数格式化 - const scaleFormat = (value: any = 0, scale: number = 4) => { - return Number.parseFloat(value).toFixed(scale); - }; - // 小数格式化 - const scale2Format = (value: any = 0) => { - return Number.parseFloat(value).toFixed(2); - }; - // 点击复制文本 - const copyText = (text: string) => { - return new Promise((resolve, reject) => { - try { - //复制 - toClipboard(text); - //下面可以设置复制成功的提示框等操作 - ElMessage.success(t('message.layout.copyTextSuccess')); - resolve(text); - } catch (e) { - //复制失败 - ElMessage.error(t('message.layout.copyTextError')); - reject(e); - } - }); - }; - return { - percentFormat, - dateFormatYMD, - dateFormatYMDHMS, - dateFormatHMS, - scaleFormat, - scale2Format, - copyText, - }; -} diff --git a/tg-web/src/utils/copy.ts b/tg-web/src/utils/copy.ts deleted file mode 100644 index fd0d06e..0000000 --- a/tg-web/src/utils/copy.ts +++ /dev/null @@ -1,47 +0,0 @@ -export function deepcopy(o :any) { - return JSON.parse(JSON.stringify(o)) -} - -export function copyToClipboard(value: any, successfully: any, failure: any) { - const clipboard = navigator.clipboard - if (clipboard !== undefined) { - navigator.clipboard.writeText(value).then(successfully, failure) - } else { - // fallback to execCommand - let isSuccess = false - - if (document.queryCommandSupported && document.queryCommandSupported('copy')) { - const el = document.createElement('textarea') - el.value = value - el.style.top = '0' - el.style.left = '0' - el.style.position = 'fixed' // Prevent scrolling to bottom of page in Microsoft Edge. - document.body.appendChild(el) - - el.focus() - el.select() - - // Security exception may be thrown by some browsers. - try { - if (document.execCommand('copy')) { - isSuccess = true - } - } catch (ex) { - console.warn('Copy to clipboard failed.', ex) - } finally { - document.body.removeChild(el) - } - } - - // callback - if (isSuccess) { - if (successfully !== undefined) { - successfully() - } - } else { - if (failure !== undefined) { - failure() - } - } - } -} \ No newline at end of file diff --git a/tg-web/src/utils/customDirective.ts b/tg-web/src/utils/customDirective.ts deleted file mode 100644 index c67350f..0000000 --- a/tg-web/src/utils/customDirective.ts +++ /dev/null @@ -1,178 +0,0 @@ -import type { App } from 'vue'; - -/** - * 按钮波浪指令 - * @directive 默认方式:v-waves,如 `
` - * @directive 参数方式:v-waves=" |light|red|orange|purple|green|teal",如 `
` - */ -export function wavesDirective(app: App) { - app.directive('waves', { - mounted(el, binding) { - el.classList.add('waves-effect'); - binding.value && el.classList.add(`waves-${binding.value}`); - function setConvertStyle(obj: { [key: string]: unknown }) { - let style: string = ''; - for (let i in obj) { - if (obj.hasOwnProperty(i)) style += `${i}:${obj[i]};`; - } - return style; - } - function onCurrentClick(e: { [key: string]: unknown }) { - let elDiv = document.createElement('div'); - elDiv.classList.add('waves-ripple'); - el.appendChild(elDiv); - let styles = { - left: `${e.layerX}px`, - top: `${e.layerY}px`, - opacity: 1, - transform: `scale(${(el.clientWidth / 100) * 10})`, - 'transition-duration': `750ms`, - 'transition-timing-function': `cubic-bezier(0.250, 0.460, 0.450, 0.940)`, - }; - elDiv.setAttribute('style', setConvertStyle(styles)); - setTimeout(() => { - elDiv.setAttribute( - 'style', - setConvertStyle({ - opacity: 0, - transform: styles.transform, - left: styles.left, - top: styles.top, - }) - ); - setTimeout(() => { - elDiv && el.removeChild(elDiv); - }, 750); - }, 450); - } - el.addEventListener('mousedown', onCurrentClick, false); - }, - unmounted(el) { - el.addEventListener('mousedown', () => {}); - }, - }); -} - -/** - * 自定义拖动指令 - * @description 使用方式:v-drag="[dragDom,dragHeader]",如 `
` - * @description dragDom 要拖动的元素,dragHeader 要拖动的 Header 位置 - * @link 注意:https://github.com/element-plus/element-plus/issues/522 - * @lick 参考:https://blog.csdn.net/weixin_46391323/article/details/105228020?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-10&spm=1001.2101.3001.4242 - */ -export function dragDirective(app: App) { - app.directive('drag', { - mounted(el, binding) { - if (!binding.value) return false; - - const dragDom = document.querySelector(binding.value[0]) as HTMLElement; - const dragHeader = document.querySelector(binding.value[1]) as HTMLElement; - - dragHeader.onmouseover = () => (dragHeader.style.cursor = `move`); - - function down(e: any, type: string) { - // 鼠标按下,计算当前元素距离可视区的距离 - const disX = type === 'pc' ? e.clientX - dragHeader.offsetLeft : e.touches[0].clientX - dragHeader.offsetLeft; - const disY = type === 'pc' ? e.clientY - dragHeader.offsetTop : e.touches[0].clientY - dragHeader.offsetTop; - - // body当前宽度 - const screenWidth = document.body.clientWidth; - // 可见区域高度(应为body高度,可某些环境下无法获取) - const screenHeight = document.documentElement.clientHeight; - - // 对话框宽度 - const dragDomWidth = dragDom.offsetWidth; - // 对话框高度 - const dragDomheight = dragDom.offsetHeight; - - const minDragDomLeft = dragDom.offsetLeft; - const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth; - - const minDragDomTop = dragDom.offsetTop; - const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight; - - // 获取到的值带px 正则匹配替换 - let styL: any = getComputedStyle(dragDom).left; - let styT: any = getComputedStyle(dragDom).top; - - // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px - if (styL.includes('%')) { - styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100); - styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100); - } else { - styL = +styL.replace(/\px/g, ''); - styT = +styT.replace(/\px/g, ''); - } - - return { - disX, - disY, - minDragDomLeft, - maxDragDomLeft, - minDragDomTop, - maxDragDomTop, - styL, - styT, - }; - } - - function move(e: any, type: string, obj: any) { - let { disX, disY, minDragDomLeft, maxDragDomLeft, minDragDomTop, maxDragDomTop, styL, styT } = obj; - - // 通过事件委托,计算移动的距离 - let left = type === 'pc' ? e.clientX - disX : e.touches[0].clientX - disX; - let top = type === 'pc' ? e.clientY - disY : e.touches[0].clientY - disY; - - // 边界处理 - if (-left > minDragDomLeft) { - left = -minDragDomLeft; - } else if (left > maxDragDomLeft) { - left = maxDragDomLeft; - } - - if (-top > minDragDomTop) { - top = -minDragDomTop; - } else if (top > maxDragDomTop) { - top = maxDragDomTop; - } - - // 移动当前元素 - dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`; - } - - /** - * pc端 - * onmousedown 鼠标按下触发事件 - * onmousemove 鼠标按下时持续触发事件 - * onmouseup 鼠标抬起触发事件 - */ - dragHeader.onmousedown = (e) => { - const obj = down(e, 'pc'); - document.onmousemove = (e) => { - move(e, 'pc', obj); - }; - document.onmouseup = () => { - document.onmousemove = null; - document.onmouseup = null; - }; - }; - - /** - * 移动端 - * ontouchstart 当按下手指时,触发ontouchstart - * ontouchmove 当移动手指时,触发ontouchmove - * ontouchend 当移走手指时,触发ontouchend - */ - dragHeader.ontouchstart = (e) => { - const obj = down(e, 'app'); - document.ontouchmove = (e) => { - move(e, 'app', obj); - }; - document.ontouchend = () => { - document.ontouchmove = null; - document.ontouchend = null; - }; - }; - }, - }); -} diff --git a/tg-web/src/utils/debounce.ts b/tg-web/src/utils/debounce.ts deleted file mode 100644 index 4a82b50..0000000 --- a/tg-web/src/utils/debounce.ts +++ /dev/null @@ -1,11 +0,0 @@ -export function debounce(fn : Function, delay : number) { - let timer = null as any; - return function(...arg : any){ - if(timer) clearTimeout(timer) - timer && clearTimeout(timer) - timer = setTimeout(() => { - //@ts-ignore - fn.call(this,arg) - },delay) - } -} \ No newline at end of file diff --git a/tg-web/src/utils/directive.ts b/tg-web/src/utils/directive.ts deleted file mode 100644 index a75b187..0000000 --- a/tg-web/src/utils/directive.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { App } from 'vue'; -import { authDirective } from '/@/utils/authDirective'; -import { wavesDirective, dragDirective } from '/@/utils/customDirective'; - -/** - * 导出指令方法:v-xxx - * @methods authDirective 用户权限指令,用法:v-auth - * @methods wavesDirective 按钮波浪指令,用法:v-waves - * @methods dragDirective 自定义拖动指令,用法:v-drag - */ -export function directive(app: App) { - // 用户权限指令 - authDirective(app); - // 按钮波浪指令 - wavesDirective(app); - // 自定义拖动指令 - dragDirective(app); -} diff --git a/tg-web/src/utils/download.ts b/tg-web/src/utils/download.ts deleted file mode 100644 index 0085db3..0000000 --- a/tg-web/src/utils/download.ts +++ /dev/null @@ -1,17 +0,0 @@ -function downloadData(data: string, filename: string, type: string) { - const file = new Blob([data], { type: type }); - const a = document.createElement("a"); - const url = URL.createObjectURL(file); - a.href = url; - a.download = filename; - document.body.appendChild(a); - a.click(); - setTimeout(function () { - document.body.removeChild(a); - window.URL.revokeObjectURL(url); - }, 0); -} - -export { - downloadData -} \ No newline at end of file diff --git a/tg-web/src/utils/filter.ts b/tg-web/src/utils/filter.ts deleted file mode 100644 index 95271c8..0000000 --- a/tg-web/src/utils/filter.ts +++ /dev/null @@ -1,45 +0,0 @@ - -/** - * Formatting time - */ -const formatDate = (value: any, fmt: any) => { - fmt = fmt || 'YYYY-MM-DD HH:mm:ss' - if (value === null) { - return '-' - } else { - // return dayjs(formatISODate(value)).format(fmt) - } -} -/** - * Formatting iso date - */ -const formatISODate =( date: any) => { - const [datetime, timezone] = date.split('+') - if (!timezone || timezone.indexOf(':') >= 0) return date - const hourOfTz = timezone.substring(0, 2) || '00' - const secondOfTz = timezone.substring(2, 4) || '00' - return `${datetime}+${hourOfTz}:${secondOfTz}` -} -/** - * filter null - */ -const filterNull = (value: any) => { - if (value === null || value === '') { - return '-' - } else { - return value - } -} - -function dateFilter(date: any) { - if (!date) { - return '-' - } - - const d = new Date(date) - return d.toLocaleString() -} - -export { - formatDate, filterNull, dateFilter -} \ No newline at end of file diff --git a/tg-web/src/utils/formatTime.ts b/tg-web/src/utils/formatTime.ts deleted file mode 100644 index e0205ff..0000000 --- a/tg-web/src/utils/formatTime.ts +++ /dev/null @@ -1,148 +0,0 @@ -/** - * 时间日期转换 - * @param date 当前时间,new Date() 格式 - * @param format 需要转换的时间格式字符串 - * @description format 字符串随意,如 `YYYY-mm、YYYY-mm-dd` - * @description format 季度:"YYYY-mm-dd HH:MM:SS QQQQ" - * @description format 星期:"YYYY-mm-dd HH:MM:SS WWW" - * @description format 几周:"YYYY-mm-dd HH:MM:SS ZZZ" - * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ" - * @returns 返回拼接后的时间字符串 - */ -export function formatDate(date: Date, format: string): string { - let we = date.getDay(); // 星期 - let z = getWeek(date); // 周 - let qut = Math.floor((date.getMonth() + 3) / 3).toString(); // 季度 - const opt: { [key: string]: string } = { - 'Y+': date.getFullYear().toString(), // 年 - 'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1) - 'd+': date.getDate().toString(), // 日 - 'H+': date.getHours().toString(), // 时 - 'M+': date.getMinutes().toString(), // 分 - 'S+': date.getSeconds().toString(), // 秒 - 'q+': qut, // 季度 - }; - // 中文数字 (星期) - const week: { [key: string]: string } = { - '0': '日', - '1': '一', - '2': '二', - '3': '三', - '4': '四', - '5': '五', - '6': '六', - }; - // 中文数字(季度) - const quarter: { [key: string]: string } = { - '1': '一', - '2': '二', - '3': '三', - '4': '四', - }; - if (/(W+)/.test(format)) - format = format.replace(RegExp.$1, RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '周' + week[we]) : week[we]); - if (/(Q+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut]); - if (/(Z+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 3 ? '第' + z + '周' : z + ''); - for (let k in opt) { - let r = new RegExp('(' + k + ')').exec(format); - // 若输入的长度不为1,则前面补零 - if (r) format = format.replace(r[1], RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0')); - } - return format; -} - -/** - * 获取当前日期是第几周 - * @param dateTime 当前传入的日期值 - * @returns 返回第几周数字值 - */ -export function getWeek(dateTime: Date): number { - let temptTime = new Date(dateTime.getTime()); - // 周几 - let weekday = temptTime.getDay() || 7; - // 周1+5天=周六 - temptTime.setDate(temptTime.getDate() - weekday + 1 + 5); - let firstDay = new Date(temptTime.getFullYear(), 0, 1); - let dayOfWeek = firstDay.getDay(); - let spendDay = 1; - if (dayOfWeek != 0) spendDay = 7 - dayOfWeek + 1; - firstDay = new Date(temptTime.getFullYear(), 0, 1 + spendDay); - let d = Math.ceil((temptTime.valueOf() - firstDay.valueOf()) / 86400000); - let result = Math.ceil(d / 7); - return result; -} - -/** - * 将时间转换为 `几秒前`、`几分钟前`、`几小时前`、`几天前` - * @param param 当前时间,new Date() 格式或者字符串时间格式 - * @param format 需要转换的时间格式字符串 - * @description param 10秒: 10 * 1000 - * @description param 1分: 60 * 1000 - * @description param 1小时: 60 * 60 * 1000 - * @description param 24小时:60 * 60 * 24 * 1000 - * @description param 3天: 60 * 60* 24 * 1000 * 3 - * @returns 返回拼接后的时间字符串 - */ -export function formatPast(param: string | Date, format: string = 'YYYY-mm-dd'): string { - // 传入格式处理、存储转换值 - let t: any, s: number; - // 获取js 时间戳 - let time: number = new Date().getTime(); - // 是否是对象 - typeof param === 'string' || 'object' ? (t = new Date(param).getTime()) : (t = param); - // 当前时间戳 - 传入时间戳 - time = Number.parseInt(`${time - t}`); - if (time < 10000) { - // 10秒内 - return '刚刚'; - } else if (time < 60000 && time >= 10000) { - // 超过10秒少于1分钟内 - s = Math.floor(time / 1000); - return `${s}秒前`; - } else if (time < 3600000 && time >= 60000) { - // 超过1分钟少于1小时 - s = Math.floor(time / 60000); - return `${s}分钟前`; - } else if (time < 86400000 && time >= 3600000) { - // 超过1小时少于24小时 - s = Math.floor(time / 3600000); - return `${s}小时前`; - } else if (time < 259200000 && time >= 86400000) { - // 超过1天少于3天内 - s = Math.floor(time / 86400000); - return `${s}天前`; - } else { - // 超过3天 - let date = typeof param === 'string' || 'object' ? new Date(param) : param; - return formatDate(date, format); - } -} - -/** - * 时间问候语 - * @param param 当前时间,new Date() 格式 - * @description param 调用 `formatAxis(new Date())` 输出 `上午好` - * @returns 返回拼接后的时间字符串 - */ -export function formatAxis(param: Date): string { - let hour: number = new Date(param).getHours(); - if (hour < 6) return '凌晨好'; - else if (hour < 9) return '早上好'; - else if (hour < 12) return '上午好'; - else if (hour < 14) return '中午好'; - else if (hour < 17) return '下午好'; - else if (hour < 19) return '傍晚好'; - else if (hour < 22) return '晚上好'; - else return '夜里好'; -} - - //格林尼治2019-03-19T16:00:00.000Z ==>> 2019-03-20 00:00:00 与北京时间8小时时差 -export function ISOtoBeijing(param: string, len = 10): string { - if (param === "") { - return ""; - } else { - var dateee = new Date(param).toJSON(); - var date = new Date(+new Date(dateee) + 8 * 3600 * 1000).toISOString().replace(/T/g, ' ').replace(/\.[\d]{3}Z/, ''); - return date.slice(0, len); - } -} \ No newline at end of file diff --git a/tg-web/src/utils/getStyleSheets.ts b/tg-web/src/utils/getStyleSheets.ts deleted file mode 100644 index 90252c3..0000000 --- a/tg-web/src/utils/getStyleSheets.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { nextTick } from 'vue'; -import * as svg from '@element-plus/icons-vue'; - -// 获取阿里字体图标 -const getAlicdnIconfont = () => { - return new Promise((resolve, reject) => { - nextTick(() => { - const styles: any = document.styleSheets; - let sheetsList = []; - let sheetsIconList = []; - for (let i = 0; i < styles.length; i++) { - if (styles[i].href && styles[i].href.indexOf('at.alicdn.com') > -1) { - sheetsList.push(styles[i]); - } - } - for (let i = 0; i < sheetsList.length; i++) { - for (let j = 0; j < sheetsList[i].cssRules.length; j++) { - if (sheetsList[i].cssRules[j].selectorText && sheetsList[i].cssRules[j].selectorText.indexOf('.icon-') > -1) { - sheetsIconList.push( - `${sheetsList[i].cssRules[j].selectorText.substring(1, sheetsList[i].cssRules[j].selectorText.length).replace(/\:\:before/gi, '')}` - ); - } - } - } - if (sheetsIconList.length > 0) resolve(sheetsIconList); - else reject('未获取到值,请刷新重试'); - }); - }); -}; - -// 初始化获取 css 样式,获取 element plus 自带 svg 图标,增加了 ele- 前缀,使用时:ele-Aim -const getElementPlusIconfont = () => { - return new Promise((resolve, reject) => { - nextTick(() => { - const icons = svg as any; - const sheetsIconList = []; - for (const i in icons) { - sheetsIconList.push(`ele-${icons[i].name}`); - } - if (sheetsIconList.length > 0) resolve(sheetsIconList); - else reject('未获取到值,请刷新重试'); - }); - }); -}; - -// 初始化获取 css 样式,这里使用 fontawesome 的图标 -const getAwesomeIconfont = () => { - return new Promise((resolve, reject) => { - nextTick(() => { - const styles: any = document.styleSheets; - let sheetsList = []; - let sheetsIconList = []; - for (let i = 0; i < styles.length; i++) { - if (styles[i].href && styles[i].href.indexOf('netdna.bootstrapcdn.com') > -1) { - sheetsList.push(styles[i]); - } - } - for (let i = 0; i < sheetsList.length; i++) { - for (let j = 0; j < sheetsList[i].cssRules.length; j++) { - if ( - sheetsList[i].cssRules[j].selectorText && - sheetsList[i].cssRules[j].selectorText.indexOf('.fa-') === 0 && - sheetsList[i].cssRules[j].selectorText.indexOf(',') === -1 - ) { - if (/::before/.test(sheetsList[i].cssRules[j].selectorText)) { - sheetsIconList.push( - `${sheetsList[i].cssRules[j].selectorText.substring(1, sheetsList[i].cssRules[j].selectorText.length).replace(/\:\:before/gi, '')}` - ); - } - } - } - } - if (sheetsIconList.length > 0) resolve(sheetsIconList.reverse()); - else reject('未获取到值,请刷新重试'); - }); - }); -}; - -/** - * 获取字体图标 `document.styleSheets` - * @method ali 获取阿里字体图标 `` - * @method ele 获取 element plus 自带图标 `` - * @method ali 获取 fontawesome 的图标 `` - */ -const initIconfont = { - // iconfont - ali: () => { - return getAlicdnIconfont(); - }, - // element plus - ele: () => { - return getElementPlusIconfont(); - }, - // fontawesome - awe: () => { - return getAwesomeIconfont(); - }, -}; - -// 导出方法 -export default initIconfont; diff --git a/tg-web/src/utils/index.ts b/tg-web/src/utils/index.ts deleted file mode 100644 index f832053..0000000 --- a/tg-web/src/utils/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -export const typeOptions = [ - { - value: "string", - label: "string", - }, - { - value: "int", - label: "int", - }, - { - value: "float", - label: "float", - }, - { - value: "bool", - label: "bool", - }, -] as any[] diff --git a/tg-web/src/utils/loading.ts b/tg-web/src/utils/loading.ts deleted file mode 100644 index c23fb77..0000000 --- a/tg-web/src/utils/loading.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { nextTick } from 'vue'; -import '/@/theme/loading.scss'; - -/** - * 页面全局 Loading - * @method start 创建 loading - * @method done 移除 loading - */ -export const NextLoading = { - // 创建 loading - start: () => { - const bodys: Element = document.body; - const div = document.createElement('div'); - div.setAttribute('class', 'loading-next'); - const htmls = ` -
-
-
-
-
-
-
-
-
-
-
-
-
- `; - div.innerHTML = htmls; - bodys.insertBefore(div, bodys.childNodes[0]); - window.nextLoading = true; - }, - // 移除 loading - done: () => { - nextTick(() => { - window.nextLoading = false; - const el = document.querySelector('.loading-next'); - el?.parentNode?.removeChild(el); - }); - }, -}; diff --git a/tg-web/src/utils/menu.ts b/tg-web/src/utils/menu.ts deleted file mode 100644 index eb3cc7f..0000000 --- a/tg-web/src/utils/menu.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { sortByKey } from '../utils/arrayOperation'; -// 只处理到二级菜单 -export function formatMenus(menusUpm: Array) { - menusUpm = sortByKey(menusUpm, 'sortVal'); - const rootMenu: any[] = []; - menusUpm.map((item: any) => { - if (item.pid === 0) { - rootMenu.push(makeMenu(item)); - } - }) - for (let i = 0; i < rootMenu.length; i++) { - const rootMenuItem = rootMenu[i]; - const children: any[] = []; - for (let i = 0; i < menusUpm.length; i++) { - const subMenu = menusUpm[i]; - const isMenu = subMenu.isMenu; - if (!isMenu || subMenu.pid !== rootMenuItem.id) { - continue; - } - children.push(makeMenu(subMenu)); - } - rootMenuItem.children = sortByKey(children, 'sortVal'); - } - return rootMenu; -} - -function makeMenu(menuUpm: any) { - const childMenu = { - id: menuUpm.id, - path: menuUpm.url, - name: menuUpm.name, - component: menuUpm.featureKey, - meta: { - title: menuUpm.name, - isLink: '', - isHide: false, - isKeepAlive: true, - isAffix: true, - isIframe: false, - roles: ['common'], - icon: menuUpm.icon, - }, - } - return childMenu; -} \ No newline at end of file diff --git a/tg-web/src/utils/normalRules.ts b/tg-web/src/utils/normalRules.ts deleted file mode 100644 index 0959d7d..0000000 --- a/tg-web/src/utils/normalRules.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const normal = (title: string) => ([{ - required: true, - message: `请输入${title}`, - trigger: "blur", -}]) diff --git a/tg-web/src/utils/other.ts b/tg-web/src/utils/other.ts deleted file mode 100644 index 6bb4fbd..0000000 --- a/tg-web/src/utils/other.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { nextTick } from 'vue'; -import type { App } from 'vue'; -import * as svg from '@element-plus/icons-vue'; -import router from '/@/router/index'; -import pinia from '/@/stores/index'; -import { storeToRefs } from 'pinia'; -import { useThemeConfig } from '/@/stores/themeConfig'; -import { i18n } from '/@/i18n/index'; -import { Local } from '/@/utils/storage'; -import SvgIcon from '/@/components/svgIcon/index.vue'; - -/** - * 导出全局注册 element plus svg 图标 - * @param app vue 实例 - * @description 使用:https://element-plus.gitee.io/zh-CN/component/icon.html - */ -export function elSvg(app: App) { - const icons = svg as any; - for (const i in icons) { - app.component(`ele-${icons[i].name}`, icons[i]); - } - app.component('SvgIcon', SvgIcon); -} - -/** - * 设置浏览器标题国际化 - * @method const title = useTitle(); ==> title() - */ -export function useTitle() { - const stores = useThemeConfig(pinia); - const { themeConfig } = storeToRefs(stores); - nextTick(() => { - let webTitle = ''; - let globalTitle: string = themeConfig.value.globalTitle; - const { path, meta } = router.currentRoute.value; - if (path === '/login') { - webTitle = meta.title; - } else { - webTitle = setTagsViewNameI18n(router.currentRoute.value); - } - document.title = `${webTitle} - ${globalTitle}` || globalTitle; - }); -} - -/** - * 设置 自定义 tagsView 名称、 自定义 tagsView 名称国际化 - * @param params 路由 query、params 中的 tagsViewName - * @returns 返回当前 tagsViewName 名称 - */ -export function setTagsViewNameI18n(item: any) { - let tagsViewName: any = ''; - const { query, params, meta } = item; - if (query?.tagsViewName || params?.tagsViewName) { - if (/\/zh-cn|en|zh-tw\//.test(query?.tagsViewName) || /\/(zh-cn|en|zh-tw)\//.test(params?.tagsViewName)) { - // 国际化 - const urlTagsParams = (query?.tagsViewName && JSON.parse(query?.tagsViewName)) || (params?.tagsViewName && JSON.parse(params?.tagsViewName)); - tagsViewName = urlTagsParams[i18n.global.locale]; - } else { - // 非国际化 - tagsViewName = query?.tagsViewName || params?.tagsViewName; - } - } else { - // 非自定义 tagsView 名称 - tagsViewName = i18n.global.t(meta.title); - } - return tagsViewName; -} - -/** - * 图片懒加载 - * @param el dom 目标元素 - * @param arr 列表数据 - * @description data-xxx 属性用于存储页面或应用程序的私有自定义数据 - */ -export const lazyImg = (el: any, arr: any) => { - const io = new IntersectionObserver((res) => { - res.forEach((v: any) => { - if (v.isIntersecting) { - const { img, key } = v.target.dataset; - v.target.src = img; - v.target.onload = () => { - io.unobserve(v.target); - arr[key]['loading'] = false; - }; - } - }); - }); - nextTick(() => { - document.querySelectorAll(el).forEach((img) => io.observe(img)); - }); -}; - -/** - * 全局组件大小 - * @returns 返回 `window.localStorage` 中读取的缓存值 `globalComponentSize` - */ -export const globalComponentSize = (): string => { - const stores = useThemeConfig(pinia); - const { themeConfig } = storeToRefs(stores); - return Local.get('themeConfig')?.globalComponentSize || themeConfig.value?.globalComponentSize; -}; - -/** - * 对象深克隆 - * @param obj 源对象 - * @returns 克隆后的对象 - */ -export function deepClone(obj: any) { - let newObj: any; - try { - newObj = obj.push ? [] : {}; - } catch (error) { - newObj = {}; - } - for (let attr in obj) { - if (obj[attr] && typeof obj[attr] === 'object') { - newObj[attr] = deepClone(obj[attr]); - } else { - newObj[attr] = obj[attr]; - } - } - return newObj; -} - -/** - * 判断是否是移动端 - */ -export function isMobile() { - if ( - navigator.userAgent.match( - /('phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone')/i - ) - ) { - return true; - } else { - return false; - } -} - -/** - * 判断数组对象中所有属性是否为空,为空则删除当前行对象 - * @description @感谢大黄 - * @param list 数组对象 - * @returns 删除空值后的数组对象 - */ -export function handleEmpty(list: any) { - const arr = []; - for (const i in list) { - const d = []; - for (const j in list[i]) { - d.push(list[i][j]); - } - const leng = d.filter((item) => item === '').length; - if (leng !== d.length) { - arr.push(list[i]); - } - } - return arr; -} - -/** - * 统一批量导出 - * @method elSvg 导出全局注册 element plus svg 图标 - * @method useTitle 设置浏览器标题国际化 - * @method setTagsViewNameI18n 设置 自定义 tagsView 名称、 自定义 tagsView 名称国际化 - * @method lazyImg 图片懒加载 - * @method globalComponentSize() element plus 全局组件大小 - * @method deepClone 对象深克隆 - * @method isMobile 判断是否是移动端 - * @method handleEmpty 判断数组对象中所有属性是否为空,为空则删除当前行对象 - */ -const other = { - elSvg: (app: App) => { - elSvg(app); - }, - useTitle: () => { - useTitle(); - }, - setTagsViewNameI18n(route: any) { - return setTagsViewNameI18n(route); - }, - lazyImg: (el: any, arr: any) => { - lazyImg(el, arr); - }, - globalComponentSize: () => { - return globalComponentSize(); - }, - deepClone: (obj: any) => { - return deepClone(obj); - }, - isMobile: () => { - return isMobile(); - }, - handleEmpty: (list: any) => { - return handleEmpty(list); - }, -}; - -// 统一批量导出 -export default other; diff --git a/tg-web/src/utils/param.ts b/tg-web/src/utils/param.ts deleted file mode 100644 index b6d6ba7..0000000 --- a/tg-web/src/utils/param.ts +++ /dev/null @@ -1,15 +0,0 @@ - -const REF_KEY = '$ref' - -export function fromRef(o: any) { - if (o[REF_KEY] === undefined) { - o[REF_KEY] = '' - } - return o[REF_KEY] -} - -export function toRef(s: any) { - return { - [REF_KEY]: s - } -} diff --git a/tg-web/src/utils/random.ts b/tg-web/src/utils/random.ts deleted file mode 100644 index 44169c4..0000000 --- a/tg-web/src/utils/random.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function Random(min: number, max: number) { - return Math.round(Math.random() * (max - min)) + min -} - -export function getRandomCode() { - return Random(0, 9999999) -} diff --git a/tg-web/src/utils/renderComponents.ts b/tg-web/src/utils/renderComponents.ts deleted file mode 100644 index 8cb7499..0000000 --- a/tg-web/src/utils/renderComponents.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { h ,withModifiers} from "vue"; -import { NTooltip, NIcon, NIconWrapper } from "naive-ui"; -import { - SyncSharp, - ArrowDownCircleOutline, - AddCircleOutline, - RemoveCircleOutline, - EllipsisHorizontalCircleOutline, - PauseCircleOutline, - TimeOutline, - ReloadCircleOutline, - NavigateCircleOutline, -} from "@vicons/ionicons5"; - -export const renderTooltip = ( - text: string, - click: () => void, - size?: number -) => { - return h(NTooltip, null, { - default: () => text, - trigger: () => { - return h( - "p", - { - onClick: withModifiers(click, ['stop']), - - style: { - color: colorAndType[text].color, - margin: "0", - cursor:"pointer" - }, - }, - { default: () => text } - // NIconWrapper, - // { - // style: { - // margin: "0 5px", - // }, - // color: colorAndType[text].color, - // }, - // [ - // h( - // NIcon, - // { - // size: size ? size : 24, - // onClick: click, - // }, - // { - // default: () => h(colorAndType[text].icon), - // } - // ), - // ] - ); - }, - }); -}; - -const colorAndType = { - ["修改"]: { - icon: SyncSharp, - color: "#2080f0", - }, - ["提交"]: { - icon: SyncSharp, - color: "#2080f0", - }, - ["导出"]: { - icon: ArrowDownCircleOutline, - color: "#2080f0", - }, - ["导入"]: { - icon: ArrowDownCircleOutline, - color:"#2080f0", - }, - ["复制"]: { - icon: AddCircleOutline, - color: "#2080f0", - }, - ["删除"]: { - icon: RemoveCircleOutline, - color: "#d03050", - }, - ["详情"]: { - icon: EllipsisHorizontalCircleOutline, - color: "#2080f0", - }, - ["更新"]: { - icon: ReloadCircleOutline, - color: "#2080f0", - }, - ["部署"]: { - icon: PauseCircleOutline, - color: "#2080f0", - }, - ["下线"]: { - icon: ArrowDownCircleOutline, - color: "#2080f0", - }, - ["运行历史"]: { - icon: TimeOutline, - color: "#2080f0", - }, - ["例行"]: { - icon: NavigateCircleOutline, - color: "#2080f0", - }, -} as any; -// 操作数据配置 diff --git a/tg-web/src/utils/setIconfont.ts b/tg-web/src/utils/setIconfont.ts deleted file mode 100644 index a6acf68..0000000 --- a/tg-web/src/utils/setIconfont.ts +++ /dev/null @@ -1,48 +0,0 @@ -// 字体图标 url -const cssCdnUrlList: Array = [ - '//at.alicdn.com/t/font_2298093_y6u00apwst.css', - '//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css', -]; -// 第三方 js url -const jsCdnUrlList: Array = []; - -// 动态批量设置字体图标 -export function setCssCdn() { - if (cssCdnUrlList.length <= 0) return false; - cssCdnUrlList.map((v) => { - let link = document.createElement('link'); - link.rel = 'stylesheet'; - link.href = v; - link.crossOrigin = 'anonymous'; - document.getElementsByTagName('head')[0].appendChild(link); - }); -} - -// 动态批量设置第三方js -export function setJsCdn() { - if (jsCdnUrlList.length <= 0) return false; - jsCdnUrlList.map((v) => { - let link = document.createElement('script'); - link.src = v; - document.body.appendChild(link); - }); -} - -/** - * 批量设置字体图标、动态js - * @method cssCdn 动态批量设置字体图标 - * @method jsCdn 动态批量设置第三方js - */ -const setIntroduction = { - // 设置css - cssCdn: () => { - setCssCdn(); - }, - // 设置js - jsCdn: () => { - setJsCdn(); - }, -}; - -// 导出函数方法 -export default setIntroduction; diff --git a/tg-web/src/utils/storage.ts b/tg-web/src/utils/storage.ts deleted file mode 100644 index a983f80..0000000 --- a/tg-web/src/utils/storage.ts +++ /dev/null @@ -1,59 +0,0 @@ -import Cookies from 'js-cookie'; - -/** - * window.localStorage 浏览器永久缓存 - * @method set 设置永久缓存 - * @method get 获取永久缓存 - * @method remove 移除永久缓存 - * @method clear 移除全部永久缓存 - */ -export const Local = { - // 设置永久缓存 - set(key: string, val: any) { - window.localStorage.setItem(key, JSON.stringify(val)); - }, - // 获取永久缓存 - get(key: string) { - let json: any = window.localStorage.getItem(key); - return JSON.parse(json); - }, - // 移除永久缓存 - remove(key: string) { - window.localStorage.removeItem(key); - }, - // 移除全部永久缓存 - clear() { - window.localStorage.clear(); - }, -}; - -/** - * window.sessionStorage 浏览器临时缓存 - * @method set 设置临时缓存 - * @method get 获取临时缓存 - * @method remove 移除临时缓存 - * @method clear 移除全部临时缓存 - */ -export const Session = { - // 设置临时缓存 - set(key: string, val: any) { - if (key === 'token') return Cookies.set(key, val); - window.sessionStorage.setItem(key, JSON.stringify(val)); - }, - // 获取临时缓存 - get(key: string) { - if (key === 'token') return Cookies.get(key); - let json: any = window.sessionStorage.getItem(key); - return JSON.parse(json); - }, - // 移除临时缓存 - remove(key: string) { - if (key === 'token') return Cookies.remove(key); - window.sessionStorage.removeItem(key); - }, - // 移除全部临时缓存 - clear() { - Cookies.remove('token'); - window.sessionStorage.clear(); - }, -}; diff --git a/tg-web/src/utils/theme.ts b/tg-web/src/utils/theme.ts deleted file mode 100644 index 5561e64..0000000 --- a/tg-web/src/utils/theme.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ElMessage } from 'element-plus'; - -/** - * hex颜色转rgb颜色 - * @param str 颜色值字符串 - * @returns 返回处理后的颜色值 - */ -export function hexToRgb(str: any) { - let hexs: any = ''; - let reg = /^\#?[0-9A-Fa-f]{6}$/; - if (!reg.test(str)) return ElMessage.warning('输入错误的hex'); - str = str.replace('#', ''); - hexs = str.match(/../g); - for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16); - return hexs; -} - -/** - * rgb颜色转Hex颜色 - * @param r 代表红色 - * @param g 代表绿色 - * @param b 代表蓝色 - * @returns 返回处理后的颜色值 - */ -export function rgbToHex(r: any, g: any, b: any) { - let reg = /^\d{1,3}$/; - if (!reg.test(r) || !reg.test(g) || !reg.test(b)) return ElMessage.warning('输入错误的rgb颜色值'); - let hexs = [r.toString(16), g.toString(16), b.toString(16)]; - for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`; - return `#${hexs.join('')}`; -} - -/** - * 加深颜色值 - * @param color 颜色值字符串 - * @param level 加深的程度,限0-1之间 - * @returns 返回处理后的颜色值 - */ -export function getDarkColor(color: string, level: number) { - let reg = /^\#?[0-9A-Fa-f]{6}$/; - if (!reg.test(color)) return ElMessage.warning('输入错误的hex颜色值'); - let rgb = hexToRgb(color); - for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level)); - return rgbToHex(rgb[0], rgb[1], rgb[2]); -} - -/** - * 变浅颜色值 - * @param color 颜色值字符串 - * @param level 加深的程度,限0-1之间 - * @returns 返回处理后的颜色值 - */ -export function getLightColor(color: string, level: number) { - let reg = /^\#?[0-9A-Fa-f]{6}$/; - if (!reg.test(color)) return ElMessage.warning('输入错误的hex颜色值'); - let rgb = hexToRgb(color); - for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]); - return rgbToHex(rgb[0], rgb[1], rgb[2]); -} diff --git a/tg-web/src/utils/toolsValidate.ts b/tg-web/src/utils/toolsValidate.ts deleted file mode 100644 index 164bb65..0000000 --- a/tg-web/src/utils/toolsValidate.ts +++ /dev/null @@ -1,378 +0,0 @@ -/** - * 2020.11.29 lyt 整理 - * 工具类集合,适用于平时开发 - * 新增多行注释信息,鼠标放到方法名即可查看 - */ - -/** - * 解决ts中, string类型不能应用于对象的索引 - */ - - export function isValidKey(key: string | number | symbol, object: object): key is keyof typeof object { - return key in object; -} - -/** - * 验证百分比(不可以小数) - * @param val 当前值字符串 - * @returns 返回处理后的字符串 - */ -export function verifyNumberPercentage(val: string): string { - // 匹配空格 - let v = val.replace(/(^\s*)|(\s*$)/g, ''); - // 只能是数字和小数点,不能是其他输入 - v = v.replace(/[^\d]/g, ''); - // 不能以0开始 - v = v.replace(/^0/g, ''); - // 数字超过100,赋值成最大值100 - v = v.replace(/^[1-9]\d\d{1,3}$/, '100'); - // 返回结果 - return v; -} - -/** - * 验证百分比(可以小数) - * @param val 当前值字符串 - * @returns 返回处理后的字符串 - */ -export function verifyNumberPercentageFloat(val: string): string { - let v = verifyNumberIntegerAndFloat(val); - // 数字超过100,赋值成最大值100 - v = v.replace(/^[1-9]\d\d{1,3}$/, '100'); - // 超过100之后不给再输入值 - v = v.replace(/^100\.$/, '100'); - // 返回结果 - return v; -} - -/** - * 小数或整数(不可以负数) - * @param val 当前值字符串 - * @returns 返回处理后的字符串 - */ -export function verifyNumberIntegerAndFloat(val: string) { - // 匹配空格 - let v = val.replace(/(^\s*)|(\s*$)/g, ''); - // 只能是数字和小数点,不能是其他输入 - v = v.replace(/[^\d.]/g, ''); - // 以0开始只能输入一个 - v = v.replace(/^0{2}$/g, '0'); - // 保证第一位只能是数字,不能是点 - v = v.replace(/^\./g, ''); - // 小数只能出现1位 - v = v.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.'); - // 小数点后面保留2位 - v = v.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3'); - // 返回结果 - return v; -} - -/** - * 正整数验证 - * @param val 当前值字符串 - * @returns 返回处理后的字符串 - */ -export function verifiyNumberInteger(val: string) { - // 匹配空格 - let v = val.replace(/(^\s*)|(\s*$)/g, ''); - // 去掉 '.' , 防止贴贴的时候出现问题 如 0.1.12.12 - v = v.replace(/[\.]*/g, ''); - // 去掉以 0 开始后面的数, 防止贴贴的时候出现问题 如 00121323 - v = v.replace(/(^0[\d]*)$/g, '0'); - // 首位是0,只能出现一次 - v = v.replace(/^0\d$/g, '0'); - // 只匹配数字 - v = v.replace(/[^\d]/g, ''); - // 返回结果 - return v; -} - -/** - * 去掉中文及空格 - * @param val 当前值字符串 - * @returns 返回处理后的字符串 - */ -export function verifyCnAndSpace(val: string) { - // 匹配中文与空格 - let v = val.replace(/[\u4e00-\u9fa5\s]+/g, ''); - // 匹配空格 - v = v.replace(/(^\s*)|(\s*$)/g, ''); - // 返回结果 - return v; -} - -/** - * 去掉英文及空格 - * @param val 当前值字符串 - * @returns 返回处理后的字符串 - */ -export function verifyEnAndSpace(val: string) { - // 匹配英文与空格 - let v = val.replace(/[a-zA-Z]+/g, ''); - // 匹配空格 - v = v.replace(/(^\s*)|(\s*$)/g, ''); - // 返回结果 - return v; -} - -/** - * 禁止输入空格 - * @param val 当前值字符串 - * @returns 返回处理后的字符串 - */ -export function verifyAndSpace(val: string) { - // 匹配空格 - let v = val.replace(/(^\s*)|(\s*$)/g, ''); - // 返回结果 - return v; -} - -/** - * 金额用 `,` 区分开 - * @param val 当前值字符串 - * @returns 返回处理后的字符串 - */ -export function verifyNumberComma(val: string) { - // 调用小数或整数(不可以负数)方法 - let v: any = verifyNumberIntegerAndFloat(val); - // 字符串转成数组 - v = v.toString().split('.'); - // \B 匹配非单词边界,两边都是单词字符或者两边都是非单词字符 - v[0] = v[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); - // 数组转字符串 - v = v.join('.'); - // 返回结果 - return v; -} - -/** - * 匹配文字变色(搜索时) - * @param val 当前值字符串 - * @param text 要处理的字符串值 - * @param color 搜索到时字体高亮颜色 - * @returns 返回处理后的字符串 - */ -export function verifyTextColor(val: string, text = '', color = 'red') { - // 返回内容,添加颜色 - let v = text.replace(new RegExp(val, 'gi'), `${val}`); - // 返回结果 - return v; -} - -/** - * 数字转中文大写 - * @param val 当前值字符串 - * @param unit 默认:仟佰拾亿仟佰拾万仟佰拾元角分 - * @returns 返回处理后的字符串 - */ -export function verifyNumberCnUppercase(val: any, unit = '仟佰拾亿仟佰拾万仟佰拾元角分', v = '') { - // 当前内容字符串添加 2个0,为什么?? - val += '00'; - // 返回某个指定的字符串值在字符串中首次出现的位置,没有出现,则该方法返回 -1 - let lookup = val.indexOf('.'); - // substring:不包含结束下标内容,substr:包含结束下标内容 - if (lookup >= 0) val = val.substring(0, lookup) + val.substr(lookup + 1, 2); - // 根据内容 val 的长度,截取返回对应大写 - unit = unit.substr(unit.length - val.length); - // 循环截取拼接大写 - for (let i = 0; i < val.length; i++) { - v += '零壹贰叁肆伍陆柒捌玖'.substr(val.substr(i, 1), 1) + unit.substr(i, 1); - } - // 正则处理 - v = v - .replace(/零角零分$/, '整') - .replace(/零[仟佰拾]/g, '零') - .replace(/零{2,}/g, '零') - .replace(/零([亿|万])/g, '$1') - .replace(/零+元/, '元') - .replace(/亿零{0,3}万/, '亿') - .replace(/^元/, '零元'); - // 返回结果 - return v; -} - -/** - * 手机号码 - * @param val 当前值字符串 - * @returns 返回 true: 手机号码正确 - */ -export function verifyPhone(val: string) { - // false: 手机号码不正确 - if (!/^((12[0-9])|(13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0|1,5-9]))\d{8}$/.test(val)) return false; - // true: 手机号码正确 - else return true; -} - -/** - * 国内电话号码 - * @param val 当前值字符串 - * @returns 返回 true: 国内电话号码正确 - */ -export function verifyTelPhone(val: string) { - // false: 国内电话号码不正确 - if (!/\d{3}-\d{8}|\d{4}-\d{7}/.test(val)) return false; - // true: 国内电话号码正确 - else return true; -} - -/** - * 登录账号 (字母开头,允许5-16字节,允许字母数字下划线) - * @param val 当前值字符串 - * @returns 返回 true: 登录账号正确 - */ -export function verifyAccount(val: string) { - // false: 登录账号不正确 - if (!/^[a-zA-Z][a-zA-Z0-9_]{4,15}$/.test(val)) return false; - // true: 登录账号正确 - else return true; -} - -/** - * 密码 (以字母开头,长度在6~16之间,只能包含字母、数字和下划线) - * @param val 当前值字符串 - * @returns 返回 true: 密码正确 - */ -export function verifyPassword(val: string) { - // false: 密码不正确 - if (!/^[a-zA-Z]\w{5,15}$/.test(val)) return false; - // true: 密码正确 - else return true; -} - -/** - * 强密码 (字母+数字+特殊字符,长度在6-16之间) - * @param val 当前值字符串 - * @returns 返回 true: 强密码正确 - */ -export function verifyPasswordPowerful(val: string) { - // false: 强密码不正确 - if (!/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&\.*]+$)(?![\d!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,16}$/.test(val)) - return false; - // true: 强密码正确 - else return true; -} - -/** - * 密码强度 - * @param val 当前值字符串 - * @description 弱:纯数字,纯字母,纯特殊字符 - * @description 中:字母+数字,字母+特殊字符,数字+特殊字符 - * @description 强:字母+数字+特殊字符 - * @returns 返回处理后的字符串:弱、中、强 - */ -export function verifyPasswordStrength(val: string) { - let v = ''; - // 弱:纯数字,纯字母,纯特殊字符 - if (/^(?:\d+|[a-zA-Z]+|[!@#$%^&\.*]+){6,16}$/.test(val)) v = '弱'; - // 中:字母+数字,字母+特殊字符,数字+特殊字符 - if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,16}$/.test(val)) v = '中'; - // 强:字母+数字+特殊字符 - if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&\.*]+$)(?![\d!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,16}$/.test(val)) - v = '强'; - // 返回结果 - return v; -} - -/** - * IP地址 - * @param val 当前值字符串 - * @returns 返回 true: IP地址正确 - */ -export function verifyIPAddress(val: string) { - // false: IP地址不正确 - if ( - !/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/.test( - val - ) - ) - return false; - // true: IP地址正确 - else return true; -} - -/** - * 邮箱 - * @param val 当前值字符串 - * @returns 返回 true: 邮箱正确 - */ -export function verifyEmail(val: string) { - // false: 邮箱不正确 - if ( - !/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test( - val - ) - ) - return false; - // true: 邮箱正确 - else return true; -} - -/** - * 身份证 - * @param val 当前值字符串 - * @returns 返回 true: 身份证正确 - */ -export function verifyIdCard(val: string) { - // false: 身份证不正确 - if (!/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(val)) return false; - // true: 身份证正确 - else return true; -} - -/** - * 姓名 - * @param val 当前值字符串 - * @returns 返回 true: 姓名正确 - */ -export function verifyFullName(val: string) { - // false: 姓名不正确 - if (!/^[\u4e00-\u9fa5]{1,6}(·[\u4e00-\u9fa5]{1,6}){0,2}$/.test(val)) return false; - // true: 姓名正确 - else return true; -} - -/** - * 邮政编码 - * @param val 当前值字符串 - * @returns 返回 true: 邮政编码正确 - */ -export function verifyPostalCode(val: string) { - // false: 邮政编码不正确 - if (!/^[1-9][0-9]{5}$/.test(val)) return false; - // true: 邮政编码正确 - else return true; -} - -/** - * url 处理 - * @param val 当前值字符串 - * @returns 返回 true: url 正确 - */ -export function verifyUrl(val: string) { - // false: url不正确 - if ( - !/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test( - val - ) - ) - return false; - // true: url正确 - else return true; -} - -/** - * 车牌号 - * @param val 当前值字符串 - * @returns 返回 true:车牌号正确 - */ -export function verifyCarNum(val: string) { - // false: 车牌号不正确 - if ( - !/^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z](([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))$/.test( - val - ) - ) - return false; - // true:车牌号正确 - else return true; -} diff --git a/tg-web/src/utils/wartermark.ts b/tg-web/src/utils/wartermark.ts deleted file mode 100644 index b897ed1..0000000 --- a/tg-web/src/utils/wartermark.ts +++ /dev/null @@ -1,47 +0,0 @@ -// 页面添加水印效果 -const setWatermark = (str: string) => { - const id = '1.23452384164.123412416'; - if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id)); - const can = document.createElement('canvas'); - can.width = 200; - can.height = 130; - const cans: any = can.getContext('2d'); - cans.rotate((-20 * Math.PI) / 180); - cans.font = '12px Vedana'; - cans.fillStyle = 'rgba(200, 200, 200, 0.30)'; - cans.textBaseline = 'Middle'; - cans.fillText(str, can.width / 10, can.height / 2); - const div = document.createElement('div'); - div.id = id; - div.style.pointerEvents = 'none'; - div.style.top = '15px'; - div.style.left = '0px'; - div.style.position = 'fixed'; - div.style.zIndex = '10000000'; - div.style.width = `${document.documentElement.clientWidth}px`; - div.style.height = `${document.documentElement.clientHeight}px`; - div.style.background = `url(${can.toDataURL('image/png')}) left top repeat`; - document.body.appendChild(div); - return id; -}; - -/** - * 页面添加水印效果 - * @method set 设置水印 - * @method del 删除水印 - */ -const watermark = { - // 设置水印 - set: (str: string) => { - let id = setWatermark(str); - if (document.getElementById(id) === null) id = setWatermark(str); - }, - // 删除水印 - del: () => { - let id = '1.23452384164.123412416'; - if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id)); - }, -}; - -// 导出方法 -export default watermark; diff --git a/tg-web/src/views/error/401.vue b/tg-web/src/views/error/401.vue deleted file mode 100644 index b031696..0000000 --- a/tg-web/src/views/error/401.vue +++ /dev/null @@ -1,90 +0,0 @@ - - - - - diff --git a/tg-web/src/views/error/404.vue b/tg-web/src/views/error/404.vue deleted file mode 100644 index 33afc3a..0000000 --- a/tg-web/src/views/error/404.vue +++ /dev/null @@ -1,91 +0,0 @@ - - - - - diff --git a/tg-web/src/views/home/index.vue b/tg-web/src/views/home/index.vue deleted file mode 100644 index 10191f7..0000000 --- a/tg-web/src/views/home/index.vue +++ /dev/null @@ -1,24 +0,0 @@ - - - - - diff --git a/tg-web/src/views/strategy/experiment/experiment.vue b/tg-web/src/views/strategy/experiment/experiment.vue deleted file mode 100644 index ee7c375..0000000 --- a/tg-web/src/views/strategy/experiment/experiment.vue +++ /dev/null @@ -1,934 +0,0 @@ - - - - - diff --git a/tg-web/src/views/strategy/experiment/historyVersion.vue b/tg-web/src/views/strategy/experiment/historyVersion.vue deleted file mode 100644 index 9155382..0000000 --- a/tg-web/src/views/strategy/experiment/historyVersion.vue +++ /dev/null @@ -1,109 +0,0 @@ - - - - - \ No newline at end of file diff --git a/tg-web/src/views/strategy/experiment/requireConfig.vue b/tg-web/src/views/strategy/experiment/requireConfig.vue deleted file mode 100644 index a5dc6f2..0000000 --- a/tg-web/src/views/strategy/experiment/requireConfig.vue +++ /dev/null @@ -1,51 +0,0 @@ - - - - - \ No newline at end of file diff --git a/tg-web/src/views/strategy/experiment/showX6params.vue b/tg-web/src/views/strategy/experiment/showX6params.vue deleted file mode 100644 index 106ca71..0000000 --- a/tg-web/src/views/strategy/experiment/showX6params.vue +++ /dev/null @@ -1,131 +0,0 @@ - - - - - \ No newline at end of file diff --git a/tg-web/src/views/strategy/experiment/updateForm.vue b/tg-web/src/views/strategy/experiment/updateForm.vue deleted file mode 100644 index 8f30187..0000000 --- a/tg-web/src/views/strategy/experiment/updateForm.vue +++ /dev/null @@ -1,62 +0,0 @@ - - - - - \ No newline at end of file diff --git a/tg-web/src/views/strategy/experiment/vis.vue b/tg-web/src/views/strategy/experiment/vis.vue deleted file mode 100644 index d9ab1a3..0000000 --- a/tg-web/src/views/strategy/experiment/vis.vue +++ /dev/null @@ -1,680 +0,0 @@ - - - - - diff --git a/tg-web/src/views/strategy/module/ModuleBox.vue b/tg-web/src/views/strategy/module/ModuleBox.vue deleted file mode 100644 index 6410c5e..0000000 --- a/tg-web/src/views/strategy/module/ModuleBox.vue +++ /dev/null @@ -1,201 +0,0 @@ - - - - - diff --git a/tg-web/src/views/strategy/module/module.vue b/tg-web/src/views/strategy/module/module.vue deleted file mode 100644 index 289e3b0..0000000 --- a/tg-web/src/views/strategy/module/module.vue +++ /dev/null @@ -1,126 +0,0 @@ - - - - - diff --git a/tg-web/src/views/strategy/scenes/formData.vue b/tg-web/src/views/strategy/scenes/formData.vue deleted file mode 100644 index b7d2c3e..0000000 --- a/tg-web/src/views/strategy/scenes/formData.vue +++ /dev/null @@ -1,245 +0,0 @@ - - - - - diff --git a/tg-web/src/views/strategy/scenes/scenes.vue b/tg-web/src/views/strategy/scenes/scenes.vue deleted file mode 100644 index 0b17721..0000000 --- a/tg-web/src/views/strategy/scenes/scenes.vue +++ /dev/null @@ -1,347 +0,0 @@ - - - - - diff --git a/tg-web/src/views/strategy/system/formData.vue b/tg-web/src/views/strategy/system/formData.vue deleted file mode 100644 index e69b9f8..0000000 --- a/tg-web/src/views/strategy/system/formData.vue +++ /dev/null @@ -1,187 +0,0 @@ - - - - - \ No newline at end of file diff --git a/tg-web/src/views/strategy/system/system.vue b/tg-web/src/views/strategy/system/system.vue deleted file mode 100644 index a1f46a4..0000000 --- a/tg-web/src/views/strategy/system/system.vue +++ /dev/null @@ -1,333 +0,0 @@ - - - - - diff --git a/tg-web/tsconfig.json b/tg-web/tsconfig.json deleted file mode 100644 index 577bad2..0000000 --- a/tg-web/tsconfig.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, - "module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, - "lib": ["esnext", "dom", "dom.iterable", "scripthost"] /* Specify library files to be included in the compilation. */, - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - "jsx": "preserve" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */, - // "declaration": true /* Generates corresponding '.d.ts' file. */, - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true /* Import emit helpers from 'tslib'. */, - // "downlevelIteration": true /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */, - // "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, - - /* Strict Type-Checking Options */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - - /* Module Resolution Options */ - "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, - "baseUrl": "." /* Base directory to resolve non-absolute module names. */, - "paths": { - "/@/*": ["src/*"], - "@/*":["src/*"] - } /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */, - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - "types": ["vite/client"] /* Type declaration files to be included in compilation. */, - "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - - /* Advanced Options */ - "skipLibCheck": true /* Skip type checking of declaration files. */, - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ - } -} diff --git a/tg-web/vite.config.ts b/tg-web/vite.config.ts deleted file mode 100644 index 1ced882..0000000 --- a/tg-web/vite.config.ts +++ /dev/null @@ -1,71 +0,0 @@ -import vue from '@vitejs/plugin-vue'; -import { resolve } from 'path'; -import { defineConfig, loadEnv, ConfigEnv } from 'vite'; -import topLevelAwait from 'vite-plugin-top-level-await'; - -const pathResolve = (dir: string): any => { - return resolve(__dirname, '.', dir); -}; - -const alias: Record = { - '@': pathResolve('./src/'), - '/@': pathResolve('./src/'), - 'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js', -}; - -const viteConfig = defineConfig((mode: ConfigEnv) => { - const env = loadEnv(mode.mode, process.cwd()); - return { - plugins: [ - vue(), - topLevelAwait({promiseExportName: '__tla', promiseImportName: i => `__tla_${i}`}) - ], - root: process.cwd(), - resolve: { alias }, - base: mode.command === 'serve' ? './' : env.VITE_PUBLIC_PATH, - hmr: true, - optimizeDeps: { - include: ['element-plus/lib/locale/lang/zh-cn', 'element-plus/lib/locale/lang/en', 'element-plus/lib/locale/lang/zh-tw'], - }, - server: { - host: '0.0.0.0', - port: 9990, - open: env.VITE_OPEN, - proxy:{ - // TODO develop api address - '/api': { - target: 'http://10.193.196.45:8052/point_arch/point-admin', - ws: true, - changeOrigin: false, - rewrite: (path) => path.replace('/api','') - }, - } - }, - build: { - outDir: 'dist', - sourcemap: false, - chunkSizeWarningLimit: 1500, - rollupOptions: { - output: { - entryFileNames: `assets/[name].${new Date().getTime()}.js`, - chunkFileNames: `assets/[name].${new Date().getTime()}.js`, - assetFileNames: `assets/[name].${new Date().getTime()}.[ext]`, - compact: true, - manualChunks: { - vue: ['vue', 'vue-router', 'pinia'], - echarts: ['echarts'], - }, - }, - }, - }, - css: { preprocessorOptions: { css: { charset: false } } }, - define: { - __VUE_I18N_LEGACY_API__: JSON.stringify(false), - __VUE_I18N_FULL_INSTALL__: JSON.stringify(false), - __INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false), - }, - envDir: './env', - }; -}); - -export default viteConfig; diff --git a/tg-core/wfengine/condition.go b/wfengine/condition.go similarity index 100% rename from tg-core/wfengine/condition.go rename to wfengine/condition.go diff --git a/tg-core/wfengine/engine.go b/wfengine/engine.go similarity index 81% rename from tg-core/wfengine/engine.go rename to wfengine/engine.go index bec0623..522a1f7 100644 --- a/tg-core/wfengine/engine.go +++ b/wfengine/engine.go @@ -10,9 +10,9 @@ import ( "context" "errors" "fmt" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/consts" - "github.com/didi/tg-flow/tg-core/model" + "github.com/didi/tg-flow/common/tlog" + "github.com/didi/tg-flow/consts" + "github.com/didi/tg-flow/model" "strings" "sync" "time" @@ -29,6 +29,56 @@ type WorkflowEngine struct { modelBaseMap map[string]IModelBase `json:"modules"` updateTime string `json:"update_time"` condExecutors *CondExecutors `json:"cond_executors"` + FlowSelectors map[int]FlowSelector `json:"flow_selectors"` +} + +func (w *WorkflowEngine) SetCustomFlowSelector(flowSelector FlowSelector) { + w.FlowSelectors[consts.FLOW_BY_CUSTOM] = flowSelector +} + +func (w *WorkflowEngine) GetWorkflow(workflowId int64) (*Workflow, bool) { + workflow, ok := w.workflowMap[workflowId] + return workflow, ok +} + +func (w *WorkflowEngine) SelectWorkflow(ctx context.Context, sc *model.StrategyContext) (*Workflow, error) { + sceneModule, okE := w.sceneModuleMap[sc.SceneId] + if !okE { + return nil, fmt.Errorf("no sceneModule found for scene_id:%v", sc.SceneId) + } + + var flowId int64 + var groupName string + var err error + if sc.FlowId > 0 { //测试 + flowId = sc.FlowId + } else { + //根据该场景设置的分流方式,获取workflowId + fs, ok := w.FlowSelectors[sceneModule.FlowType] + if !ok { + return nil, fmt.Errorf("no flow selector found for your flow type:%v", sceneModule.FlowType) + } + + flowId, groupName, err = fs.SelectWorkflowId(sc, sceneModule) + // use default workflow if no flowId found + if err != nil || flowId <= 0 { + flowId = sceneModule.DefaultWorkflowId + tlog.Handler.ErrorCount(ctx, "select_workflow_err", fmt.Sprintf("select workflow error:%v, use default workflow:%v", err, flowId)) + err = nil + } + + sc.FlowId = flowId + } + //根据分流选出的workflow_id,取得对应的实验策略配置 + flow, okW := w.GetWorkflow(sc.FlowId) + if !okW { + return nil, fmt.Errorf("no workflow found, flowId=%v||groupId=%v||err=%v", sc.FlowId, groupName, err) + } + + sc.Set("flow", flow) + sc.Set("groupId", groupName) + sc.Set("scene", sceneModule) + return flow, nil } func resetWorkflows(wfMap map[int64]*Workflow) { @@ -40,7 +90,7 @@ func resetWorkflows(wfMap map[int64]*Workflow) { for _, workflow := range wfMap { err := resetWorkflow(wfMap, workflow) if err != nil { - tlog.ErrorCount(context.TODO(), "resetWorkflow err", fmt.Sprintf("workflow=%+v", workflow)) + tlog.Handler.ErrorCount(context.TODO(), "resetWorkflow err", fmt.Sprintf("workflow=%+v", workflow)) continue } } @@ -57,7 +107,7 @@ func resetWorkflow(wfMap map[int64]*Workflow, workflow *Workflow) error { for _, action := range workflow.FlowCharts.ActionMap { if action.ActionType == ActionTypeFlow { if action.RefWorkflowId <= 0 { - tlog.ErrorCount(context.TODO(), "resetWorkflow_err", fmt.Sprintf("ref_workflow_id must >0, flowAction.Action=%+v", action)) + tlog.Handler.ErrorCount(context.TODO(), "resetWorkflow_err", fmt.Sprintf("ref_workflow_id must >0, flowAction.Action=%+v", action)) continue } flowActionMap[action.ActionId] = action @@ -72,7 +122,7 @@ func resetWorkflow(wfMap map[int64]*Workflow, workflow *Workflow) error { //get workflowid refWf := wfMap[flowAction.RefWorkflowId] if refWf == nil { - tlog.ErrorCount(context.TODO(), "resetWorkflow_err", fmt.Sprintf("reference workflow not exist, flowAction.Action=%+v", flowAction)) + tlog.Handler.ErrorCount(context.TODO(), "resetWorkflow_err", fmt.Sprintf("reference workflow not exist, flowAction.Action=%+v", flowAction)) continue } @@ -143,18 +193,29 @@ func resetWorkflow(wfMap map[int64]*Workflow, workflow *Workflow) error { return nil } -func newWorkflowEngine(sceneModuleMap map[int64]*SceneModule, workflowMap map[int64]*Workflow, modelBaseMap map[string]IModelBase, version string) *WorkflowEngine { +func NewWorkflowEngine(sceneModuleMap map[int64]*SceneModule, workflowMap map[int64]*Workflow, version string, moduleObj ModuleObjBase) (*WorkflowEngine, error) { + //TODO ZYF err + resetWorkflows(workflowMap) + + modelBaseMap, err := createModelMap(moduleObj, workflowMap) + if err != nil { + return nil, err + } //ut := fmt.Sprintf("%v", time.Now().Format("2006-01-02 15:04:05")) cExecutors := GetCondExecutors() + flowSelectors := make(map[int]FlowSelector) + flowSelectors[consts.FLOW_BY_ONLINE_RANDOM] = &RandomSelector{} + flowSelectors[consts.FLOW_BY_APOLLO] = &GroupSelector{} wfe := &WorkflowEngine{ sceneModuleMap: sceneModuleMap, workflowMap: workflowMap, modelBaseMap: modelBaseMap, updateTime: version, condExecutors: cExecutors, + FlowSelectors: flowSelectors, } - return wfe + return wfe, nil } func createModelMap(moduleObj ModuleObjBase, wfMap map[int64]*Workflow) (map[string]IModelBase, error) { @@ -166,7 +227,7 @@ func createModelMap(moduleObj ModuleObjBase, wfMap map[int64]*Workflow) (map[str modelBaseMap := make(map[string]IModelBase) for _, wf := range wfMap { if wf.FlowCharts == nil { - tlog.ErrorCount(ctx, "create_modelbase_err", fmt.Sprintf("flow_charts is nil, workflow=%v", wf)) + tlog.Handler.ErrorCount(ctx, "create_modelbase_err", fmt.Sprintf("flow_charts is nil, workflow=%v", wf)) continue } @@ -177,7 +238,7 @@ func createModelMap(moduleObj ModuleObjBase, wfMap map[int64]*Workflow) (map[str mb, err := createModelBase(moduleObj, action) if mb == nil || err != nil { - tlog.ErrorCount(ctx, "create_modelbase_err", fmt.Sprintf("workflow=%v, action=%v, mb=%v, err=%v", wf, action, mb, err)) + tlog.Handler.ErrorCount(ctx, "create_modelbase_err", fmt.Sprintf("workflow=%v, action=%v, mb=%v, err=%v", wf, action, mb, err)) continue } modelBaseMap[actionId] = mb @@ -193,22 +254,22 @@ func createModelBase(moduleObj ModuleObjBase, action *Action) (IModelBase, error return nil, fmt.Errorf("action or moduleObj empty, action:%v,moduleObj:%v", action, moduleObj) } + mb := moduleObj.NewObj(action.ActionName) + if mb == nil { + return mb, fmt.Errorf("create ModelBase instance error, action:%v", action) + } + + mb.SetName(action.ActionName) + vMap := make(map[string]string) if len(action.Params) > 0 { for _, param := range action.Params { vMap[param.Name] = param.Value } } - //vMap["Name"] = action.ActionName - mb := moduleObj.NewObj(action.ActionName, vMap) - if mb == nil { - return mb, fmt.Errorf("create ModelBase instance error, action:%v, vMap:%v", action, vMap) - } - - mb.SetName(action.ActionName) err := reflectModuleField(mb, vMap) if err != nil { - tlog.ErrorCount(context.TODO(), "createModelBase_err", fmt.Sprintf("set module field fail, actionName:%v, vMap:%v, error:%v", action.ActionName, vMap, err)) + tlog.Handler.ErrorCount(context.TODO(), "createModelBase_err", fmt.Sprintf("set module field fail, actionName:%v, vMap:%v, error:%v", action.ActionName, vMap, err)) } return mb, nil @@ -229,23 +290,20 @@ func (w *WorkflowEngine) Run(ctx context.Context, sc *model.StrategyContext) { sc.SetError(action0, err) } }() - //选择一个workflow策略 - flow, err := w.selectWorkFlow(ctx, sc) + + flow, err := w.SelectWorkflow(ctx, sc) if err != nil { - sc.SetError(action0, fmt.Errorf("flow:%v, err:%v", flow, err)) + sc.SetError(action0, fmt.Errorf("no workflow found, flowId=%v, err=%v", sc.FlowId, err)) return } - //fmt.Println(fmt.Sprintf("准备Run,先获取图,flow=%+v", flow.FlowCharts)) flowChart := flow.GetWorkflowChart() - //fmt.Println(fmt.Sprintf("准备Run,先获取图,flowChart=%+v", flowChart)) if flowChart == nil { sc.SetError(action0, errors.New("flowChart is nil")) return } wgMap, tsMap := flowChart.CreateWaitMap() - waitedMap := &sync.Map{} wgn := &sync.WaitGroup{} wgn.Add(1) @@ -255,47 +313,6 @@ func (w *WorkflowEngine) Run(ctx context.Context, sc *model.StrategyContext) { wgn.Wait() } -func (w *WorkflowEngine) selectWorkFlow(ctx context.Context, sc *model.StrategyContext) (*Workflow, error) { - sceneModule, okE := w.sceneModuleMap[sc.SceneId] - if !okE { - return nil, fmt.Errorf("no sceneModule found for scene_id:%v", sc.SceneId) - } - - var flowId int64 - var slotId int - var groupName string - var err error - if sc.FlowId > 0 { //测试 - flowId = sc.FlowId - } else { - //根据该场景设置的分流方式,获取workflowId - if sceneModule.FlowType == consts.FLOW_BY_ONLINE_RANDOM { - flowId, slotId = FlowByOnlineRandom(sc, sceneModule) - } else if sceneModule.FlowType == consts.FLOW_BY_APOLLO { - flowId, groupName, err = FlowByApollo(sc, sceneModule) - } - - // 如果没有找到 flowId,采用默认的 Workflow - if err != nil || flowId == 0 { - tlog.ErrorCount(ctx, "select_workflow_err", fmt.Sprintf("select workflow by apollo error, err=%v, use default workflow", err)) - flowId = sceneModule.DefaultWorkflowId - err = nil - } - - sc.FlowId = flowId - } - //根据分流选出的workflow_id,取得对应的实验策略配置 - flow, okW := w.workflowMap[flowId] - if !okW { - err := fmt.Errorf("no workflow found, slotId=%v||flowId=%v||groupId=%v||err=%v", slotId, flowId, groupName, err) - return nil, err - } - sc.Set("flow", flow) - sc.Set("groupId", slotId) - sc.Set("scene", sceneModule) - return flow, err -} - /* * @@ -305,12 +322,10 @@ func (w *WorkflowEngine) selectWorkFlow(ctx context.Context, sc *model.StrategyC */ func (w *WorkflowEngine) doExecuteModule(ctx context.Context, sc *model.StrategyContext, flowChart *WorkflowChart, skipedActionIdPairs *sync.Map, wgMap map[string]*sync.WaitGroup, tsMap map[string]*TimeWaiter, waitedMap *sync.Map, actionId string, wgn *sync.WaitGroup) { action, ok := flowChart.ActionMap[actionId] - //fmt.Println("开始执行actionId===>", fmt.Sprintf("%+v",action), ok) if !ok { return } - //fmt.Println("action.PrevActionIds===>", strings.Join(action.PrevActionIds, ",")) //is merge if len(action.PrevActionIds) > 1 { _, ok := waitedMap.LoadOrStore(action.ActionId, true) @@ -400,7 +415,7 @@ func (w *WorkflowEngine) executeModule(ctx context.Context, sc *model.StrategyCo defer func() { if err := recover(); err != nil { - tlog.ErrorCount(ctx, "executeModule_err", fmt.Sprintf("actionId=%+v,err=%+v", action.ActionId, err)) + tlog.Handler.ErrorCount(ctx, "executeModule_err", fmt.Sprintf("actionId=%+v,err=%+v", action.ActionId, err)) sc.SetError(action.ActionId, fmt.Errorf("%v", err)) sc.Skip(ErrNoUnknown, fmt.Sprintf("executeModule_err, actionId=%+v, err=%+v", action.ActionId, err)) } @@ -442,7 +457,7 @@ func (w *WorkflowEngine) executeModule(ctx context.Context, sc *model.StrategyCo var err error toExeActionId, err = action.executeCond(sc.ContextMap) if err != nil { - tlog.ErrorCount(ctx, "executeCond_err", fmt.Sprintf("execute condition error, actionId=%+v, toExeActionId=%+v, err=%+v", action.ActionId, toExeActionId, err)) + tlog.Handler.ErrorCount(ctx, "executeCond_err", fmt.Sprintf("execute condition error, actionId=%+v, toExeActionId=%+v, err=%+v", action.ActionId, toExeActionId, err)) sc.SetError(action.ActionId, fmt.Errorf("action.executeCond error, ActionName:%v, err:%v", action.ActionName, err)) } return toExeActionId @@ -451,6 +466,7 @@ func (w *WorkflowEngine) executeModule(ctx context.Context, sc *model.StrategyCo if moduleBase, ok := w.modelBaseMap[action.ActionId]; ok { if !sc.IsSkip() { startTime := time.Now().UnixNano() / 1e6 + sc.TC.StartSectionCount(action.ActionName) if action.Timeout > 0 && action.ActionType != ActionTypeTimeout { go func() { moduleBase.DoAction(ctx, sc) @@ -479,9 +495,7 @@ func (w *WorkflowEngine) executeModule(ctx context.Context, sc *model.StrategyCo moduleBase.DoAction(ctx, sc) } - endTime := time.Now().UnixNano() / 1e6 - sc.SetModuleResult(action.ActionId, action.ActionName, endTime-startTime) - + sc.TC.StopSectionCount(action.ActionName) } } else { sc.SetError(action.ActionId, fmt.Errorf("module not found in map, moduleMap:%v, moduleName:%v", w.modelBaseMap, action.ActionName)) diff --git a/tg-core/wfengine/engine_from_apollo.go b/wfengine/engine_from_apollo.go similarity index 61% rename from tg-core/wfengine/engine_from_apollo.go rename to wfengine/engine_from_apollo.go index a24ba1e..c37c8c0 100644 --- a/tg-core/wfengine/engine_from_apollo.go +++ b/wfengine/engine_from_apollo.go @@ -10,8 +10,7 @@ import ( "context" "encoding/json" "fmt" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/wfengine/apollo" + "github.com/didi/tg-flow/common/tlog" "strconv" "strings" ) @@ -22,50 +21,36 @@ const ( versionKey = "version" ) -func GetLatestVersionFromApollo(namespace, configName string) (string, error) { - apolloConfig, err := apollo.NewApolloConfig(namespace, configName) - if err != nil || apolloConfig == nil { - return "", fmt.Errorf("NewApolloConfig error, namespace=%v, configName=%v, err=%v", namespace, configName, err) - } - - configParams := apolloConfig.GetConfigs() - if configParams == nil || configParams[versionKey] == "" { - return "", fmt.Errorf("GetConfigs fail, configParam is nil or version empty, namespace=%v, configName=%v, configParams.lenth=%v", namespace, configName, len(configParams)) - } - - return configParams[versionKey], nil -} - -func NewWorkflowEngineFromApollo(moduleObj ModuleObjBase, namespace, configName string) (*WorkflowEngine, error) { - smMap, wfMap, version, err := loadSceneModuleWorkflowFromApollo(namespace, configName) +//func GetLatestVersionFromApollo(namespace, configName string) (string, error) { +// apolloConfig, err := apollo.NewApolloConfig(namespace, configName) +// if err != nil || apolloConfig == nil { +// return "", fmt.Errorf("NewApolloConfig error, namespace=%v, configName=%v, err=%v", namespace, configName, err) +// } +// +// configParams := apolloConfig.GetConfigs() +// if configParams == nil || configParams[versionKey] == "" { +// return "", fmt.Errorf("GetConfigs fail, configParam is nil or version empty, namespace=%v, configName=%v, configParams.lenth=%v", namespace, configName, len(configParams)) +// } +// +// return configParams[versionKey], nil +//} + +func NewWorkflowEngineFromConfig(moduleObj ModuleObjBase, configParams map[string]string) (*WorkflowEngine, error) { + smMap, wfMap, version, err := loadSceneModuleWorkflowFromApollo(configParams) if err != nil { return nil, err } - resetWorkflows(wfMap) - - mbMap, err := createModelMap(moduleObj, wfMap) - if err != nil { - return nil, err - } - - workfowEngine := newWorkflowEngine(smMap, wfMap, mbMap, version) - return workfowEngine, nil + return NewWorkflowEngine(smMap, wfMap, version, moduleObj) } -func loadSceneModuleWorkflowFromApollo(namespace, configName string) (map[int64]*SceneModule, map[int64]*Workflow, string, error) { - apolloConfig, err := apollo.NewApolloConfig(namespace, configName) - if err != nil || apolloConfig == nil { - return nil, nil, "", fmt.Errorf("NewApolloConfig error, namespace=%v, configName=%v, err=%v", namespace, configName, err) - } - - configParams := apolloConfig.GetConfigs() +func loadSceneModuleWorkflowFromApollo(configParams map[string]string) (map[int64]*SceneModule, map[int64]*Workflow, string, error) { if configParams == nil { - return nil, nil, "", fmt.Errorf("GetConfigs error, configParam is nil, namespace=%v, configName=%v", namespace, configName) + return nil, nil, "", fmt.Errorf("GetConfigs error, configParams is nil") } if configParams[sceneKey] == "" { - return nil, nil, "", fmt.Errorf("configParams error, sceneKey is empty, namespace=%v, configName=%v, sceneKey=%v", namespace, configName, sceneKey) + return nil, nil, "", fmt.Errorf("configParams error, sceneKey[%v] is empty", sceneKey) } smMap, err := loadSceneModuleString(configParams[sceneKey]) @@ -109,12 +94,12 @@ func LoadWorkflowFromApollo(configParams map[string]string) (map[int64]*Workflow } wf, err := createWorkflowFromKV(key, val) if wf == nil || err != nil { - tlog.ErrorCount(context.TODO(), "loadWorkflowFromKV_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) + tlog.Handler.ErrorCount(context.TODO(), "loadWorkflowFromKV_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) continue } wf.FlowCharts, err = NewWorkflowChart(wf.FlowChart) if err != nil { - tlog.ErrorCount(context.TODO(), "NewWorkflowChart_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) + tlog.Handler.ErrorCount(context.TODO(), "NewWorkflowChart_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) continue } wfMap[wf.Id] = wf diff --git a/tg-core/wfengine/engine_from_file.go b/wfengine/engine_from_file.go similarity index 88% rename from tg-core/wfengine/engine_from_file.go rename to wfengine/engine_from_file.go index 85da373..1b7fdc3 100644 --- a/tg-core/wfengine/engine_from_file.go +++ b/wfengine/engine_from_file.go @@ -10,7 +10,7 @@ import ( "context" "encoding/json" "fmt" - "github.com/didi/tg-flow/tg-core/common/tlog" + "github.com/didi/tg-flow/common/tlog" "io/ioutil" "os" "strconv" @@ -51,18 +51,10 @@ func NewWorkflowEngineFromFile(moduleObj ModuleObjBase, configPath string) (*Wor version, err1 := GetLatestVersionFromFile(configPath) if err1 != nil { - tlog.ErrorCount(context.TODO(), "GetLatestVersionFromRedis_err", fmt.Sprintf("configPath=%v, err=%v", configPath, err1)) + tlog.Handler.ErrorCount(context.TODO(), "GetLatestVersionFromRedis_err", fmt.Sprintf("configPath=%v, err=%v", configPath, err1)) } - resetWorkflows(wfMap) - - mbMap, err := createModelMap(moduleObj, wfMap) - if err != nil { - return nil, err - } - - workfowEngine := newWorkflowEngine(smMap, wfMap, mbMap, version) - return workfowEngine, nil + return NewWorkflowEngine(smMap, wfMap, version, moduleObj) } func LoadSceneModuleMapFromFile(path string) (map[int64]*SceneModule, error) { @@ -99,12 +91,12 @@ func LoadWorkflowFromFile(dirPath string, smMap map[int64]*SceneModule) (map[int for filePath, _ := range filePaths { wf, err := createWorkflowFromFile(filePath) if wf == nil || err != nil { - tlog.ErrorCount(context.TODO(), "loadWorkflowFromFile_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) + tlog.Handler.ErrorCount(context.TODO(), "loadWorkflowFromFile_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) continue } wf.FlowCharts, err = NewWorkflowChart(wf.FlowChart) if err != nil { - tlog.ErrorCount(context.TODO(), "NewWorkflowChart_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) + tlog.Handler.ErrorCount(context.TODO(), "NewWorkflowChart_err", fmt.Sprintf("wf:%v,err:%v", wf, err)) continue } wfMap[wf.Id] = wf diff --git a/tg-core/wfengine/engine_from_redis.go b/wfengine/engine_from_redis.go similarity index 31% rename from tg-core/wfengine/engine_from_redis.go rename to wfengine/engine_from_redis.go index 5734732..4762f89 100644 --- a/tg-core/wfengine/engine_from_redis.go +++ b/wfengine/engine_from_redis.go @@ -10,8 +10,7 @@ import ( "context" "encoding/json" "fmt" - "github.com/didi/tg-flow/tg-core/common/redis" - "github.com/didi/tg-flow/tg-core/common/tlog" + "github.com/didi/tg-flow/common/tlog" ) const ( @@ -20,79 +19,21 @@ const ( RedisKeyVersion = "version_app_" ) -func GetLatestVersionFromRedis(appId int64) (string, error) { - return redis.Handler.Get(context.TODO(), fmt.Sprintf("%v%v", RedisKeyVersion, appId)) -} - -func NewWorkflowEngine(moduleObj ModuleObjBase, appId int64) (*WorkflowEngine, error) { - //更新系统下全部场景的节点对象 - smMap, err := LoadSceneModuleMap(appId) - if err != nil { - return nil, err - } - - //load workflow - wfMap, err := LoadWorkflow(appId, smMap) - if err != nil { - return nil, err - } - - //版本号不是必须,兼容吧 - version, err1 := GetLatestVersionFromRedis(appId) - if err1 != nil { - tlog.ErrorCount(context.TODO(), "GetLatestVersionFromRedis_err", fmt.Sprintf("appId=%v, err=%v", appId, err1)) - } +//func GetLatestVersionFromRedis(appId int64) (string, error) { +// return redis.Handler.Get(context.TODO(), fmt.Sprintf("%v%v", RedisKeyVersion, appId)) +//} - //TODO ZYF err - resetWorkflows(wfMap) - - mbMap, err := createModelMap(moduleObj, wfMap) - if err != nil { - return nil, err - } - - workfowEngine := newWorkflowEngine(smMap, wfMap, mbMap, version) - return workfowEngine, nil -} - -func LoadSceneModuleMap(appId int64) (map[int64]*SceneModule, error) { - //加载redis中的数据到内存 - sceneModuleMap, err := loadSceneModules(appId) +func NewWorkflowEngineFromKV(moduleObj ModuleObjBase, sceneModuleMapString, workflowMapStr, version string) (*WorkflowEngine, error) { + var sceneModuleMap map[int64]*SceneModule + err := json.Unmarshal([]byte(sceneModuleMapString), &sceneModuleMap) if err != nil { - return sceneModuleMap, err + return nil, fmt.Errorf("err:%v, sceneModule:%v", err, sceneModuleMapString) } - - //根据app_id,获取对应的场景id - for sceneId, sceneModule := range sceneModuleMap { + /*for sceneId, sceneModule := range sceneModuleMap { if sceneModule.AppId != appId { delete(sceneModuleMap, sceneId) } - } - - return sceneModuleMap, nil -} - -// 获取redis中的数据 -func loadSceneModules(appId int64) (map[int64]*SceneModule, error) { - sceneModuleMapString, err := redis.Handler.Get(context.TODO(), fmt.Sprintf("%v%v", RedisKeySceneModule, appId)) - if err != nil && err.Error() != redis.ErrNil { - return nil, err - } - - var sceneModuleMap map[int64]*SceneModule - err = json.Unmarshal([]byte(sceneModuleMapString), &sceneModuleMap) - if err != nil { - return nil, fmt.Errorf("err:%v, sceneModule:%v", err, sceneModuleMapString) - } - - return sceneModuleMap, nil -} - -func LoadWorkflow(appId int64, smMap map[int64]*SceneModule) (map[int64]*Workflow, error) { - workflowMapStr, err := redis.Handler.Get(context.TODO(), fmt.Sprintf("%v%v", RedisKeyWorkflow, appId)) - if err != nil { - return nil, err - } + }*/ var workflowMap map[int64]*Workflow err = json.Unmarshal([]byte(workflowMapStr), &workflowMap) @@ -102,15 +43,15 @@ func LoadWorkflow(appId int64, smMap map[int64]*SceneModule) (map[int64]*Workflo wfMap := make(map[int64]*Workflow) for workflowId, workflow := range workflowMap { - if _, ok := smMap[workflow.SceneId]; ok { + if _, ok := sceneModuleMap[workflow.SceneId]; ok { workflow.FlowCharts, err = NewWorkflowChart(workflow.FlowChart) if err != nil { - tlog.ErrorCount(context.TODO(), "NewWorkflowChart_err", fmt.Sprintf("wf:%v,err:%v", workflow, err)) + tlog.Handler.ErrorCount(context.TODO(), "NewWorkflowChart_err", fmt.Sprintf("wf:%v,err:%v", workflow, err)) continue } wfMap[workflowId] = workflow } } - return wfMap, nil + return NewWorkflowEngine(sceneModuleMap, wfMap, version, moduleObj) } diff --git a/wfengine/flow_selector.go b/wfengine/flow_selector.go new file mode 100644 index 0000000..e29f977 --- /dev/null +++ b/wfengine/flow_selector.go @@ -0,0 +1,15 @@ +package wfengine + +import "github.com/didi/tg-flow/model" + +/* +* +there are 3 flow selector: + + 1: random + 2: custom + 3: apollo (apollo platform in didi) +*/ +type FlowSelector interface { + SelectWorkflowId(sc *model.StrategyContext, sceneModule *SceneModule) (int64, string, error) +} diff --git a/wfengine/group_selector.go b/wfengine/group_selector.go new file mode 100644 index 0000000..8b4498b --- /dev/null +++ b/wfengine/group_selector.go @@ -0,0 +1,39 @@ +package wfengine + +import ( + "fmt" + "github.com/didi/tg-flow/model" +) + +type GroupSelector struct { + FlowSelector +} + +/* +* +apollo分流, +如果出现error,就取缺省分桶,同时返回error信息 +*/ +func (a *GroupSelector) SelectWorkflowId(sc *model.StrategyContext, sceneModule *SceneModule) (int64, string, error) { + //var err error + //if a.ApolloInfo == nil { + // return -1, "", fmt.Errorf("apollo info not initialized") + //} + // + //// 优先采用 ApolloInfo 中设置的分流实验名称,没有的话采用场景中配置的分流实验名称 + //if a.ApolloInfo.GetDispatchExperimentName() == "" { + // a.ApolloInfo.SetDispatchExperimentName(sceneModule.DispatchExperimentName) + //} + // + //groupName, err := a.ApolloInfo.GetDispatchGroupName() + //if err != nil { + // return -1, groupName, fmt.Errorf("get dispatch groupName fail,groupName=%v, err=%v", groupName, err) + //} + + workflowId, err := sceneModule.GetWorkflowId(sc.GroupName) + if err == nil { + return workflowId, sc.GroupName, nil + } + + return -1, sc.GroupName, fmt.Errorf("select workflowId error, groupName=%v,workflowId=%v,err=%v", sc.GroupName, workflowId, err) +} diff --git a/tg-core/wfengine/inneraction/timeout_action.go b/wfengine/inneraction/timeout_action.go similarity index 84% rename from tg-core/wfengine/inneraction/timeout_action.go rename to wfengine/inneraction/timeout_action.go index ad34ae2..87e4aa1 100644 --- a/tg-core/wfengine/inneraction/timeout_action.go +++ b/wfengine/inneraction/timeout_action.go @@ -2,8 +2,8 @@ package inneraction import ( "context" - "github.com/didi/tg-flow/tg-core/model" - "github.com/didi/tg-flow/tg-core/wfengine" + "github.com/didi/tg-flow/model" + "github.com/didi/tg-flow/wfengine" ) type TimeoutAction struct { @@ -19,4 +19,4 @@ func (t TimeoutAction) DoAction(ctx context.Context, sc *model.StrategyContext) func (m TimeoutAction) OnTimeout(context.Context, *model.StrategyContext) { //this is a sample, you can do sth when timeout happen //fmt.Println("default OnTimeout function, nothing to do !!!") -} \ No newline at end of file +} diff --git a/tg-core/wfengine/module_base.go b/wfengine/module_base.go similarity index 82% rename from tg-core/wfengine/module_base.go rename to wfengine/module_base.go index 6b5e9a4..409a141 100644 --- a/tg-core/wfengine/module_base.go +++ b/wfengine/module_base.go @@ -9,8 +9,8 @@ package wfengine import ( "context" "fmt" - "github.com/didi/tg-flow/tg-core/common/tlog" - "github.com/didi/tg-flow/tg-core/model" + "github.com/didi/tg-flow/common/tlog" + "github.com/didi/tg-flow/model" "reflect" "strconv" ) @@ -44,7 +44,7 @@ func (m *ModelBase) GetName() string { } type ModuleObjBase interface { - NewObj(moduleName string, vMap map[string]string) IModelBase + NewObj(moduleName string) IModelBase } func reflectModuleField(obj interface{}, vMap map[string]string) error { @@ -55,7 +55,7 @@ func reflectModuleField(obj interface{}, vMap map[string]string) error { v := reflect.ValueOf(obj) if v.Kind() == reflect.Ptr && !v.Elem().CanSet() { err := fmt.Errorf("this obj is not match reflect") - tlog.ErrorCount(context.TODO(), "ReflectModuleField_err", fmt.Sprintf("obj:%v, err:%v", obj, err)) + tlog.Handler.ErrorCount(context.TODO(), "ReflectModuleField_err", fmt.Sprintf("obj:%v, err:%v", obj, err)) return err } @@ -66,7 +66,7 @@ func reflectModuleField(obj interface{}, vMap map[string]string) error { fieldValue := v.FieldByName(field.Name) if !fieldValue.IsValid() { err := fmt.Errorf("this obj(" + fmt.Sprintf("%v", obj) + ") field(" + field.Name + ")") - tlog.ErrorCount(context.TODO(), "ReflectModuleField_err", fmt.Sprintf("%v", err)) + tlog.Handler.ErrorCount(context.TODO(), "ReflectModuleField_err", fmt.Sprintf("%v", err)) continue } @@ -81,7 +81,7 @@ func reflectModuleField(obj interface{}, vMap map[string]string) error { tempInt, err := strconv.ParseInt(vMap[field.Name], 10, 64) if err != nil { err := fmt.Errorf("obj(" + fmt.Sprintf("%v", obj) + ") field(" + field.Name + ")'s value(" + vMap[field.Name] + ")") - tlog.ErrorCount(context.TODO(), "ReflectModuleField_err", fmt.Sprintf("%v", err)) + tlog.Handler.ErrorCount(context.TODO(), "ReflectModuleField_err", fmt.Sprintf("%v", err)) continue } fieldValue.SetInt(tempInt) @@ -90,7 +90,7 @@ func reflectModuleField(obj interface{}, vMap map[string]string) error { tempFolat, err := strconv.ParseFloat(vMap[field.Name], 64) if err != nil { err := fmt.Errorf("obj(" + fmt.Sprintf("%v", obj) + ") field(" + field.Name + ")'s value(" + vMap[field.Name] + ")") - tlog.ErrorCount(context.TODO(), "ReflectModuleField_err", fmt.Sprintf("%v", err)) + tlog.Handler.ErrorCount(context.TODO(), "ReflectModuleField_err", fmt.Sprintf("%v", err)) continue } fieldValue.SetFloat(tempFolat) diff --git a/tg-core/wfengine/flow_splitter.go b/wfengine/random_selector.go similarity index 30% rename from tg-core/wfengine/flow_splitter.go rename to wfengine/random_selector.go index 01e5339..c7850c0 100644 --- a/tg-core/wfengine/flow_splitter.go +++ b/wfengine/random_selector.go @@ -1,24 +1,28 @@ -/** -Description : flow splitter -Author : dayunzhangyunfeng@didiglobal.com -Date : 2021-05-14 -*/ package wfengine import ( "fmt" - "github.com/didi/tg-flow/tg-core/model" + "github.com/didi/tg-flow/model" "hash/crc32" + "strconv" "time" ) -//在线随机分流 -func FlowByOnlineRandom(sc *model.StrategyContext, sceneModule *SceneModule) (int64, int) { +type RandomSelector struct { + FlowSelector +} + +// 在线随机分流 +func (r *RandomSelector) SelectWorkflowId(sc *model.StrategyContext, sceneModule *SceneModule) (int64, string, error) { //1、根据用户id,算出一个0-99的数字 slotId := getSlotId(sc.UserId, sceneModule.BucketType) - //2、在对应的维度id内,根据slotId,选择对应的workflow - return sceneModule.SlotMap[slotId], slotId + workflowId, ok := sceneModule.SlotMap[slotId] + if !ok { + return -1, "", fmt.Errorf("no workflowId found, UserId=%v, BucketType=%v", sc.UserId, sceneModule.BucketType) + } + + return workflowId, strconv.Itoa(slotId), nil } func getSlotId(str string, bucketType int) int { @@ -32,28 +36,3 @@ func getSlotId(str string, bucketType int) int { } return v % 100 } - -// FlowByApollo apollo分流 -func FlowByApollo(sc *model.StrategyContext, sceneModule *SceneModule) (int64, string, error) { - var err error - if sc.ApolloInfo == nil { - return 0, "", fmt.Errorf("apollo info not initialized") - } - - // 优先采用 ApolloInfo 中设置的分流实验名称,没有的话采用场景中配置的分流实验名称 - if sc.ApolloInfo.GetDispatchExperimentName() == "" { - sc.ApolloInfo.SetDispatchExperimentName(sceneModule.DispatchExperimentName) - } - - groupName, err := sc.ApolloInfo.GetDispatchGroupName() - if err != nil { - return 0, "", err - } - - workflowId, err := sceneModule.GetWorkflowId(groupName) - if err == nil { - return workflowId, groupName, nil - } else { - return workflowId, groupName, err - } -} diff --git a/tg-core/wfengine/scene_module.go b/wfengine/scene_module.go similarity index 100% rename from tg-core/wfengine/scene_module.go rename to wfengine/scene_module.go diff --git a/tg-core/wfengine/test/condition_external.go b/wfengine/test/condition_external.go similarity index 100% rename from tg-core/wfengine/test/condition_external.go rename to wfengine/test/condition_external.go diff --git a/tg-core/wfengine/time_waiter.go b/wfengine/time_waiter.go similarity index 100% rename from tg-core/wfengine/time_waiter.go rename to wfengine/time_waiter.go diff --git a/tg-core/wfengine/workflow.go b/wfengine/workflow.go similarity index 65% rename from tg-core/wfengine/workflow.go rename to wfengine/workflow.go index 07b857c..150ee53 100644 --- a/tg-core/wfengine/workflow.go +++ b/wfengine/workflow.go @@ -1,9 +1,8 @@ -/* -* -Description : workflow v3.0 with branch -Author : dayunzhangyunfeng@didiglobal.com -Date : 2021-05-14 -*/ +/** + Description : workflow v3.0 with branch + Author : dayunzhangyunfeng@didiglobal.com + Date : 2021-05-14 + */ package wfengine import ( @@ -19,56 +18,56 @@ import ( ) const ( - ActionTypeTask = "task" - ActionTypeCond = "condition" - ActionTypeFlow = "flow" - ActionTypeTimeout = "timeout" - BranchKeyDefault = "default" - BranchKeyJoiner = "_" - defaultBranch = "*" + ActionTypeTask = "task" + ActionTypeCond = "condition" + ActionTypeFlow = "flow" + ActionTypeTimeout= "timeout" + BranchKeyDefault = "default" + BranchKeyJoiner = "_" + defaultBranch = "*" ) type Workflow struct { - Id int64 `json:"id"` - DimensionId int64 `json:"dimension_id"` - SceneId int64 `json:"scene_id"` - FlowChart string `json:"flow_chart"` - FlowCharts *WorkflowChart `json:"flow_charts"` - FlowBranch *WorkflowBranch `json:"flow_branch"` - IsDefault int `json:"is_default"` - Range1 string `json:"range1"` - Range2 string `json:"range2"` - Remark string `json:"remark"` - UpdateTime time.Time `json:"update_time"` - GroupName string `json:"group_name"` + Id int64 `json:"id"` + DimensionId int64 `json:"dimension_id"` + SceneId int64 `json:"scene_id"` + FlowChart string `json:"flow_chart"` + FlowCharts *WorkflowChart `json:"flow_charts"` + FlowBranch *WorkflowBranch `json:"flow_branch"` + IsDefault int `json:"is_default"` + Range1 string `json:"range1"` + Range2 string `json:"range2"` + Remark string `json:"remark"` + UpdateTime time.Time `json:"update_time"` + GroupName string `json:"group_name"` } type WorkflowChart struct { - FirstActionId string `json:"first_action_id"` - LastActionId string `json:"last_action_id"` - HashCondition bool `json:"has_condition"` - ActionMap map[string]*Action `json:"actions"` + FirstActionId string `json:"first_action_id"` + LastActionId string `json:"last_action_id"` + HashCondition bool `json:"has_condition"` + ActionMap map[string]*Action `json:"actions"` } type Action struct { - ActionType string `json:"action_type"` - ActionId string `json:"action_id"` - ActionName string `json:"action_name"` - Params []*Param `json:"params"` - NextActionIds []string `json:"next_action_ids"` - NextConditions []string `json:"next_conditions"` - PrevActionIds []string `json:"prev_action_ids"` - Timeout int64 `json:"timeout"` - TimeoutAsync bool `json:"timeout_async"` - TimeoutDynamic bool `json:"timeout_dynamic"` - RefWorkflowId int64 `json:"ref_workflow_id"` - Description string `json:"description"` + ActionType string `json:"action_type"` + ActionId string `json:"action_id"` + ActionName string `json:"action_name"` + Params []*Param `json:"params"` + NextActionIds []string `json:"next_action_ids"` + NextConditions []string `json:"next_conditions"` + PrevActionIds []string `json:"prev_action_ids"` + Timeout int64 `json:"timeout"` + TimeoutAsync bool `json:"timeout_async"` + TimeoutDynamic bool `json:"timeout_dynamic"` + RefWorkflowId int64 `json:"ref_workflow_id"` + Description string `json:"description"` } type Param struct { - Name string `json:"name"` - Value string `json:"value"` - Type string `json:"type"` + Name string `json:"name"` + Value string `json:"value"` + Type string `json:"type"` } type WorkflowBranch struct { @@ -90,23 +89,23 @@ func NewWorkflowChart(flowChartStr string) (*WorkflowChart, error) { } flowChart := &WorkflowChart{} - //读取的数据为json格式,需要进行解码 - err := json.Unmarshal([]byte(flowChartStr), flowChart) - if err != nil { - return nil, fmt.Errorf("create WorkflowChart fail, invalid json:%v, err:%v", flowChartStr, err) - } + //读取的数据为json格式,需要进行解码 + err := json.Unmarshal([]byte(flowChartStr), flowChart) + if err != nil { + return nil, fmt.Errorf("create WorkflowChart fail, invalid json:%v, err:%v", flowChartStr, err) + } - err = flowChart.setFirstActionId() - if err != nil { - return nil, fmt.Errorf("create WorkflowChart fail, err:%v", err) - } + err = flowChart.setFirstActionId() + if err != nil { + return nil, fmt.Errorf("create WorkflowChart fail, err:%v", err) + } flowChart.setPrevActionIds(flowChart.FirstActionId) - return flowChart, nil + return flowChart, nil } -func (w *WorkflowChart) setPrevActionIds(actionId string) { +func (w *WorkflowChart) setPrevActionIds(actionId string){ action, ok := w.ActionMap[actionId] if !ok { return @@ -126,7 +125,7 @@ func (w *WorkflowChart) setPrevActionIds(actionId string) { if nextAction.PrevActionIds == nil || len(nextAction.PrevActionIds) == 0 { nextAction.PrevActionIds = []string{actionId} - } else { + }else{ isAlreadyAdd := false var prevActionId string for _, prevActionId = range nextAction.PrevActionIds { @@ -145,7 +144,7 @@ func (w *WorkflowChart) setPrevActionIds(actionId string) { } /* -耗时搜索 + 耗时搜索 */ func (w *WorkflowChart) setFirstActionId() error { nextActionIds := make(map[string]bool) @@ -160,7 +159,7 @@ func (w *WorkflowChart) setFirstActionId() error { } } - for actionId, _ := range w.ActionMap { + for actionId,_ := range w.ActionMap { if _, ok := nextActionIds[actionId]; !ok { w.FirstActionId = actionId return nil @@ -171,14 +170,14 @@ func (w *WorkflowChart) setFirstActionId() error { } func (w *WorkflowChart) CreateWaitMap() (map[string]*sync.WaitGroup, map[string]*TimeWaiter) { - wgMap := make(map[string]*sync.WaitGroup) - tsMap := make(map[string]*TimeWaiter) + wgMap:= make(map[string]*sync.WaitGroup) + tsMap:= make(map[string]*TimeWaiter) for actionId, action := range w.ActionMap { prevCount := len(action.PrevActionIds) - if prevCount > 1 { + if prevCount >1 { wg := &sync.WaitGroup{} wg.Add(prevCount) - wgMap[actionId] = wg + wgMap[actionId]= wg } if action.Timeout > 0 { @@ -186,14 +185,14 @@ func (w *WorkflowChart) CreateWaitMap() (map[string]*sync.WaitGroup, map[string] } } - return wgMap, tsMap + return wgMap,tsMap } func (p *Param) clone() *Param { return &Param{ - Name: p.Name, - Value: p.Value, - Type: p.Type, + Name: p.Name, + Value: p.Value, + Type: p.Type, } } @@ -207,7 +206,7 @@ func (a *Action) createParamSlice(paramValues *sync.Map) ([]interface{}, error) for idx, param := range a.Params { //获取实际值 str := param.Value - if strings.HasPrefix(param.Value, "$") { + if strings.HasPrefix(param.Value,"$") { paramValue, _ := paramValues.Load(str[1:]) str = fmt.Sprintf("%v", paramValue) } @@ -215,33 +214,33 @@ func (a *Action) createParamSlice(paramValues *sync.Map) ([]interface{}, error) //获取参数 if param.Type == "string" { p[idx] = str - } else if param.Type == "int" { + }else if param.Type == "int" { val, err := strconv.ParseInt(str, 10, 64) if err != nil { return nil, err } p[idx] = val - } else if param.Type == "float" { + }else if param.Type == "float" { val, err := strconv.ParseFloat(str, 64) if err != nil { return nil, err } p[idx] = val - } else if param.Type == "bool" { + }else if param.Type == "bool" { var val bool strl := strings.ToLower(str) if strl == "true" { val = true - } else if strl == "false" { + }else if strl == "false" { val = false - } else { + }else { return nil, fmt.Errorf("invalid bool value, it must be: true or false") } p[idx] = val - } else if param.Type == "interface" { + }else if param.Type == "interface" { var val interface{} = str p[idx] = val - } else { + }else{ //TODO ZYF 先暂时支持4种最常见的类型 return nil, fmt.Errorf("unknown param type:%v", param.Type) } @@ -251,8 +250,7 @@ func (a *Action) createParamSlice(paramValues *sync.Map) ([]interface{}, error) } func (a *Action) Detach(prevAction *Action) { - //fmt.Println("\nstart detach, prevActionId, actionId:", prevAction.ActionId, a.ActionId) - if prevAction == nil || len(a.PrevActionIds) == 0 { + if prevAction == nil || len(a.PrevActionIds)==0 { //todo error return } @@ -264,7 +262,7 @@ func (a *Action) Detach(prevAction *Action) { break } } - if prevId > -1 { + if prevId > -1{ a.PrevActionIds = append(a.PrevActionIds[:prevId], a.PrevActionIds[prevId+1:]...) } @@ -277,10 +275,10 @@ func (a *Action) Detach(prevAction *Action) { } //fmt.Println("nextId============>", nextId) //fmt.Println("before set prevAction.NextActionIds===>", strings.Join(prevAction.NextActionIds,",")) - if nextId > -1 { + if nextId>-1 { prevAction.NextActionIds = append(prevAction.NextActionIds[:nextId], prevAction.NextActionIds[nextId+1:]...) //fmt.Println("after set prevAction.NextActionIds===>", strings.Join(prevAction.NextActionIds,",")) - if len(prevAction.NextConditions) > nextId { + if len(prevAction.NextConditions)> nextId { prevAction.NextConditions = append(prevAction.NextConditions[:nextId], prevAction.NextConditions[nextId+1:]...) } //fmt.Println("after set prevAction.NextConditions===>", strings.Join(prevAction.NextConditions,",")) @@ -289,7 +287,7 @@ func (a *Action) Detach(prevAction *Action) { } func (a *Action) toString() string { - return fmt.Sprintf("actionId:%+v, nextActionIds:%+v, nextConditions:%+v, prevActionIds:%+v", a.ActionId, strings.Join(a.NextActionIds, ","), strings.Join(a.NextConditions, ",")) + return fmt.Sprintf("actionId:%+v, nextActionIds:%+v, nextConditions:%+v, prevActionIds:%+v",a.ActionId, strings.Join(a.NextActionIds,","),strings.Join(a.NextConditions,",")) } func (a *Action) clone() *Action { @@ -301,19 +299,19 @@ func (a *Action) clone() *Action { } } - act := &Action{ - ActionType: a.ActionType, - ActionId: a.ActionId, - ActionName: a.ActionName, - Params: params, - NextActionIds: copyStringArray(a.NextActionIds), - NextConditions: copyStringArray(a.NextConditions), - PrevActionIds: copyStringArray(a.PrevActionIds), - Timeout: a.Timeout, + act:= &Action{ + ActionType: a.ActionType, + ActionId: a.ActionId, + ActionName: a.ActionName, + Params: params, + NextActionIds: copyStringArray(a.NextActionIds), + NextConditions: copyStringArray(a.NextConditions), + PrevActionIds: copyStringArray(a.PrevActionIds), + Timeout: a.Timeout, TimeoutAsync: a.TimeoutAsync, TimeoutDynamic: a.TimeoutDynamic, RefWorkflowId: a.RefWorkflowId, - Description: a.Description, + Description: a.Description, } return act } @@ -321,7 +319,7 @@ func (a *Action) clone() *Action { func (w *WorkflowChart) clone() *WorkflowChart { chart := &WorkflowChart{} chart.FirstActionId = w.FirstActionId - chart.LastActionId = w.LastActionId + chart.LastActionId = w.LastActionId chart.HashCondition = w.HashCondition actionMap := make(map[string]*Action) @@ -338,7 +336,7 @@ func copyStringArray(sources []string) []string { return nil } - dests := make([]string, len(sources)) + dests:=make([]string, len(sources)) for i, source := range sources { dests[i] = source } @@ -364,13 +362,10 @@ func (a *Action) executeCond(paramValues *sync.Map) (retActionId string, err err err = fmt.Errorf("executeCond error, default value: %v used, a.Params:%v, paramValues:%v, err0:%v", a.NextActionIds[defaultIndex], a.Params, paramValues, err0) } }() - //fmt.Println("\nexecuteCond start:", a.ActionId, a.ActionName, "\n") - //fmt.Println(fmt.Sprintf("\nparamValues:%+v", paramValues)) + params, err := a.createParamSlice(paramValues) - //fmt.Println(fmt.Sprintf("\n获取到的参数值params:%+v", params), "err===>", err) if err != nil { err = fmt.Errorf("createParamSlice error, default value: %v used, a.Params:%v, paramValues:%v, err:%v", a.NextActionIds[defaultIndex], a.Params, paramValues, err) - //fmt.Println("\nerr==========>", err) return } //fmt.Println("prepare to exe:", a.ActionName, params) @@ -381,20 +376,15 @@ func (a *Action) executeCond(paramValues *sync.Map) (retActionId string, err err return } - //fmt.Println("遍历比较,a和val ===>a=", fmt.Sprintf("%+v", a), "val=", val) for idx, cdt := range a.NextConditions { - //fmt.Println("cdt===",cdt, " val===", val) if cdt == val { retActionId = a.NextActionIds[idx] err = nil - //fmt.Println("retActionId===>", retActionId) return } } err = fmt.Errorf("no matched value error, default value:%v used, a.ActionId:%v, execute result:%v, nextActionIds:%v", a.NextActionIds[defaultIndex], a.ActionId, val, a.NextConditions) - //fmt.Println("eeeeeeeeeeeeeerrrrrrrrrrrrr====>", err) - //fmt.Println("\n\n\nexecuteCond end\n\n", a.ActionId, a.ActionName, "\n\n") return } @@ -418,31 +408,12 @@ func (w *WorkflowChart) newWorkflowBranch() *WorkflowBranch { } } -/*func (w *WorkflowBranch) selectBranchKey(params map[string]interface{}) (string, error) { - if !w.hasBranch() { - return BranchKeyDefault, nil - } - - branchMap := make(map[string]int) - for _, actionId := range w.SortedBranch { - idx, err := w.ActionMap[actionId].executeCond(params) - if err != nil { - return "", fmt.Errorf("execute cond error:%v", err) - } - branchMap[actionId] = idx - } - - branchKey := w.getBranchKey(branchMap) - - return branchKey, nil -}*/ - func (w *WorkflowBranch) getCurrentBranchKey() string { return w.getBranchKey(w.CurrentBranch) } func (w *WorkflowBranch) getBranchKey(branchMap map[string]int) string { - if len(w.SortedBranch) == 0 || len(branchMap) == 0 { + if len(w.SortedBranch) ==0 || len(branchMap) == 0 { return BranchKeyDefault } @@ -466,7 +437,7 @@ func (w *WorkflowBranch) currentIndex(actionId string) int { } func (w *WorkflowBranch) hasBranch() bool { - if w.SortedBranch == nil || len(w.SortedBranch) == 0 { + if w.SortedBranch == nil || len(w.SortedBranch) ==0 { return false } @@ -480,7 +451,7 @@ func (w *WorkflowBranch) nextStep() bool { return true } - for i := 0; i <= idx; i++ { + for i:=0;i<=idx;i++ { branchId := w.SortedBranch[i] w.CurrentBranch[branchId] = 0 } diff --git a/tg-core/wfengine/workflow.json b/wfengine/workflow.json similarity index 100% rename from tg-core/wfengine/workflow.json rename to wfengine/workflow.json -- Gitee From f84fb7a3dcb9a1956ae51bc5f23780a73ad9bc28 Mon Sep 17 00:00:00 2001 From: dayunzhangyunfeng Date: Sun, 15 Sep 2024 10:59:36 +0800 Subject: [PATCH 04/12] update README.MD --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1d8a194..dc1c01d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # tg-flow简介 tg-flow是一个专注于在线高并发场景的无状态的工作流引擎,它可以在保证高性能的前提下,为在线高并发系统提供复杂的的工作流调度能力,目前tg-flow已经广泛运用于滴滴内部多个日流量数十亿的核心在线系统。 -tg-flow包括三个主要模块: -* tg-core: tg-flow的工作流引擎核心模块,提供了对工作流的的调度执行能力。 -* tg-web: tg-flow的工作流配置管理前端页面,为用户提供工作流的创建、编辑、保存、删除、导入、导出等功能。 -* tg-service:tg-flow的工作流配置管理后台服务,为tg-web的后台API接口,同时也提供工作流配置分发的能力,可将配置分发到redis、zookeeper、文件系统(导出为文件)中。 +tg-flow包括两个主要模块: +* 流程调度模块: tg-flow的工作流引擎核心模块,提供了对工作流的的调度执行能力。 +* 配置管理模块: 它为用户提供工作流的创建、编辑、保存、删除、导入、导出等功能,同时也提供工作流配置在线自动分发的能力,可将配置分发到redis、zookeeper、文件系统(导出为文件)中,再由流程调度模块自动更新在线配置。 + +目前仅开源出流程调度模块,配置管理模块尚需进一步完善后开源。同时,为了便于用户快速上手,我们在github上提供了一个基于tg-flow创建的示例应用程序,下载即可运行。地址:[tg-example](https://github.com/didi/tg-example) # 我们的目标 受限于在线高并发场景的高性能要求,传统工作流引擎很难支持在线高并发系统。 我们的目标是实现一个既能够满足在线高并发场景的性能要求,又具备传统工作流引擎各种复杂功能的工作流引擎。 @@ -28,4 +29,4 @@ tg-flow包括三个主要模块: 参见: [user_manual](user_manual.html) # 欢迎加入 - 请联系:[张云锋](https://github.com/dayunzhangyunfeng), [周子纯](https://github.com/zhouzichun0315), [唐桂尧](https://github.com/tgy931) + 请联系:[张云锋](https://github.com/dayunzhangyunfeng), [周子纯](https://github.com/zhouzichun0315), [唐桂尧](https://github.com/tgy931) -- Gitee From b2cdb6546136b3b19831c81a2be3d9fbfd61677c Mon Sep 17 00:00:00 2001 From: dayunzhangyunfeng Date: Sun, 15 Sep 2024 11:08:34 +0800 Subject: [PATCH 05/12] add user_manual.md --- user_manual.md | 314 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 user_manual.md diff --git a/user_manual.md b/user_manual.md new file mode 100644 index 0000000..3c64203 --- /dev/null +++ b/user_manual.md @@ -0,0 +1,314 @@ + + + + + tg-flow 使用手册 + + + +
+

tg-flow 使用手册

+
+
+

1. 编写目的

+

通过对本公司的 tg-flow 软件产品操作过程的描述,使用户可以自主对软件进行使用和操作。

+ +

2. 下载与安装

+ +

2.1 下载安装包

+

git clone https://github.com/didi/tg-flow

+ +

2.2 安装和启动

+ +

tg-service 的安装和启动:

+
    +
  1. 将 tg-service 软件包拷贝到要安装的目录;
  2. +
  3. 进入 tg-service 目录,执行 ./build.sh,会生成 output 文件夹;
  4. +
  5. 执行:cd output 进入 output 目录,执行:nohup ./control.sh start > nohup.log 2>&1 &,启动服务。
  6. +
+ +

tg-web 的安装步骤:

+
    +
  1. 将 tg-web 软件包拷贝到要安装的目录;
  2. +
  3. 通过命令行方式,进入到 tg-web 所在目录,执行如下命令:npm install,即可完成 tg-web 部分的安装;
  4. +
  5. 通过命令行方式,进入到 tg-web 所在目录,执行如下命令:npm start,即可完成 tg-web 的启动;
  6. +
  7. 打开浏览器地址栏,输入 http://localhost:8888/,可以打开登录页,说明安装成功。
  8. +
+ +

3. tg-core 是应用系统使用时需要导入的组件,不需要单独安装。

+ +

3. 登录

+ +

3.1 账号分配

+

初次安装时,系统会默认生成一个管理员账号 admin,密码 admin,可以使用该账号进行后续的登录和分配其他子账号。

+ +

3.2 登录系统

+

第一次打开系统的时候,系统会自动跳转至登录页面,这个时候可以使用管理员账号或者子管理员账号,输入账号、密码、验证码进行登录。

+
+ 登录 +
+ +

3.3 注销登录

+

进入系统后,可以通过点击系统的右上角“退出登录”按钮注销登录。

+ +

4. 系统管理

+

tg-flow 支持同时接入并管理多个系统和应用,每个系统都有独立的系统ID和系统名称,通过系统管理页面可以查看当前接入的所有业务系统。

+
+ 登录 +
+

图4.1 系统管理界面

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
属性名称描述
id系统 ID
app_name系统名称
machine_room系统所部署的集群
node_name系统所在机房
operator系统创建和修改人员
create_time系统创建时间
update_time系统更新时间
git_url系统代码所属的代码仓库地址
+ +

在策略管理 -> 系统管理页面可以进行新增,修改,导出和删除系统等相关操作。

+
+ 登录 +
+

图4.2 新增系统对话框

+

其中系统编号,系统名称,部署机房和节点名称都为必填项,填写完成后点击保存按钮即可完成系统新增。

+
+ 登录 +
+

图4.3 修改系统对话框

+

系统条目右侧的导出按钮并点击确认导出可以导出对应系统所有场景下的流程信息,并作为 zip 压缩包的形式下载到本地并用于对应的系统中。

+ +

5. 场景管理

+ +

5.1 场景列表

+

该列表页展示在当前平台注册的所有的场景信息,系统与场景属于一对多的关系。场景信息所含字段如下图所示。搜索栏中可通过输入“场景名称”或者“系统名称”来获取相应的场景信息。

+
+ 登录 +
+

5.2 场景配置

+

场景列表页存在四个可操作按钮:

+
    +
  • 新增:可手动添加某个系统下的场景信息;
  • +
  • 刷新:手动触发获取最新的场景列表;
  • +
  • 修改:针对单一场景修改其描述内容,除了场景编号之外其他内容均可修改;
  • +
  • 删除:删除某个场景;
  • +
+ +

6 工作流引擎代码接入

+

6.1 代码框架

+

如下图为一个待接入工作流引擎的应用系统的项目代码,其中logic目录下为业务层代码,接入工作流引擎时,可以在业务层添加相关的接入代码,主要有:

+
    +
  1. 工作流引擎初始化代码,类似下图中的workflow_task.go文件;
  2. +
  3. 工作流引擎调度执行代码,类似下图中的dispatcher_genrec.go;
  4. +
  5. 工作流节点对应的算子实现代码,算子有多个,可以放到不同子目录分组存放,类似下图中的module为上述子目录的父目录;
  6. +
  7. 算子注册代码,用于将算子注册到工作流引擎,类似下图中的module_init.go文件.
  8. +
+
+ 登录 +
+

6.2 工作流引擎初始化

+

我们提供了三种工作流引擎初始化的方式:从本地工作流配置文件初始化、从全局配置中心提取配置信息初始化,以及从redis提取配置信息初始化。下面介绍的是第一种,从本地文件初始化,可以在自己的系统初始化模块中调用(其中workflowPath为工作流配置文件根目录):

+
+
+func LoadWorkflowData(ctx context.Context) {
+   moduleObj := module.ModuleObject{}
+   workflowEngine, err := wfengine.NewWorkflowEngineFromFile(moduleObj, workflowPath)
+   if err != nil {
+      tlog.ErrorCount(ctx, "cron_task_workflow_err", fmt.Sprintf("workflow engine init fail, AppId=%v, error=%v", constants.CurrentAppId, err))
+      return
+   }
+
+   global.WorkflowEngine = workflowEngine
+   tlog.Handler.Infof(ctx, consts.DLTagCronTask, "workflow engine update successful, appId=%v, workflowEngine=%v", constants.CurrentAppId, global.WorkflowEngine)
+}
+
+
+ +

6.3 工作流引擎调度执行

+

使用工作流引擎调度执行工作流,只需要在调度相关模块中添加下面的代码即可:

+
+
+global.WorkflowEngine.Run(ctx, sc)
+errMap := sc.GetErrorMap()
+errMap.Range(func(key, val interface{}) bool {
+   log.Println(fmt.Sprintf("workflowengine error, key=%v, val=%v", key, val))
+)
+   return true
+})
+
+
+ +

6.4 工作流算子开发

+

通常一个应用系统中可能会有多个业务场景,每个场景下可能需要配置多个不同的工作流,应用系统对外提供服务时,需要根据实际业务场景或实验分组情况选择使用不同的工作流来执行,工作流则由若干个执行节点组成,每个执行节点对应的是一个算子,在golang语言中这些算子对应的是struct。我们需要在要接入tg-flow的应用系统中定义这些算子,下面为一个名为RecallSampAction的算子定义示例:

+
+
+package recall
+
+import (
+   "context"
+   "fmt"
+   "github.com/didi/tg-flow/tg-core/model"
+   "github.com/didi/tg-flow/tg-core/wfengine"
+   "time"
+)
+
+type RecallSampAction struct {
+   wfengine.ModelBase
+}
+
+func (r RecallSampAction) DoAction(ctx context.Context, sc *model.StrategyContext) interface{} {
+   //此处添加相关业务逻辑
+   return nil
+}
+
+func (r RecallSampAction) OnTimeout(context.Context, *model.StrategyContext){
+   //此处添加超时处理逻辑
+}
+
+
+ +

6.5 工作流算子注册

+

为了将开发好的算子注册给工作流引擎,需要进行算子注册,如下图中的case语句部分,即为工作流算子注册函数,我们只需要自己实现一个下面的函数,并在函数中的case语句中添加自己的实际开发的算子相关注册代码即可:

+
+
+func (moduleObj ModuleObject) NewObj(moduleName string, vMap map[string]string) wfengine.IModelBase {
+   switch moduleName {
+       case “data.DataPrepareSamp1”: return &data.DataPrepareSamp1{}
+       case “recall.RecallSamp1”: return &recall.RecallSamp1{}
+       case “recall.RecallSamp2”: return &recall.RecallSamp2{}
+   }
+   return nil
+}
+
+
+ +

6.6 代码仓库注册

+

在应用系统中完成上述代码编写后,可以将代码提交到git代码仓库,并将git仓库地址拷贝出来,然后进入系统管理页面,如下图所示,点击“修改”按钮,将该代码仓库的地址填写到弹出层中的“git仓库”文本框,如下图红色矩形框所示,然后点击“保存“按钮即可。

+
+ 登录 +
+

7 工作流管理

+

7.1 工作流列表

+

点击左侧的“流程管理”目录,可以打开流程列表的管理页面,如下图所示。

+ +
+ 登录 +
+
    +

    选择列表上方的“系统”下拉框,点击查询,可以查到对应的应用系统已经添加的所有流程。

    +

    选择“场景名称”,点击查询,可以列出指定系统及场景下的所有工作流。

    +

    点击“新建”按钮,可以打开工作流编辑页面,开始一个新工作流的创建,下节会详细介绍新工作流的编辑功能。

    +

    工作流列表中有:修改、导出、复制、导入、删除等5个按钮。

    +

    点击“修改“按钮,可以打开工作流编辑页,后面的工作流编辑部分会详细介绍。

    +

    点击“导出”按钮,可以将当前工作流配置信息导出为一个json文件,点击确认后可以下载到本地,后面可以拷贝到待接入的应用系统工程目录下,应用系统在启动时,会初始化工作流引擎,工作流引擎初始化时可以通过本地方式加载上述配置。注意:此处是对单个工作流配置进行导出,如果要将某个系统下所有工作流打包导出,则需要进入到系统管理页操作。

    +

    点击“复制”按钮,可以新建一个同样的工作流配置,然后后面就可以在这个工作流上进行相关修改并保存。

    +

    点击“导入”按钮,会弹出一个对话框,这时可以将在外部编写的工作流配置信息复制并粘贴到这个对话框中的“导入内容(Json)”文本框中,点击“确认”按钮,即可完成工作流配置信息的导入,注意:工作流节点中的action_id的取值格式为:action-{场景ID}-{工作流ID},导入工作流时如果相关actionId中的场景ID和工作流ID与当前要导入的场景ID和工作流ID不一致,系统会自动帮忙将actionId中的后面两部分的值调整为当前的场景ID和工作流ID。

    +
    + 工作流编辑页面 +
    + +

    7.2 工作流编辑

    +

    打开工作流编辑页,系统会自动读取前面配置好的git仓库中的代码模块,获取代码中的算子列表,并在工作流编辑页面的左侧算子列表中展示,供后续的工作流编辑用。(注意:工作流中的条件节点已经在工作流引擎中做了内置的模块实现,所以无需单独在应用层再做实现,当然如果有需要,也可以自定义条件节点后,注册到工作流引擎)。如下图,为工作流编辑页面。工作流编辑页面包括三个区域:左侧为工作流节点列表,中间为流程构建区域,右侧为工作流及节点属性编辑区域。

    +
    + 工作流编辑页面 +
    +

    其中:

    +

    在后台管理系统中打开工作流编辑页面时,系统会从后台管理系统中配置的代码仓库中拉取代码模块名称,并以组件的方式显示在左侧组件栏。左侧的组件有两种类型:一是应用层节点,用户打开工作流图编辑界面后,会自动从对应的应用代码的git仓库中拉取对应的算子列表,展示在左侧的节点列表中。二是工作流引擎内置的条件节点和缺省节点(当git仓库未完成代码提交时,左侧无法拉取到代码模块名称,这时可以手动添加缺省节点,然后给缺省节点重新命名)。

    +

    从左侧组件栏中依次拖拽节点、添加节点之间的连线,可以逐步构建出类似下图中的主流程图和子流程图。

    +

    针对图中的每个节点,可以依次点击节点,然后在右侧的属性编辑区域编辑该节点的属性,如:节点名称、节点参数、节点描述信息等,完成所有节点属性的编辑后点击“保存”按钮,相关的工作流配置信息便会以json字符串格式存入数据库。

    + +
    + 工作流编辑页面2 +
    + +

    7.3 工作流配置发布

    +

    上一节中已存入数据库的工作流配置信息并不能自动发布到在线应用系统,需要在系统管理页面中点击“提交”按钮,将上述配置发布到一个具有存储全局配置功能的存储服务中,一般可基于zookeeper或redis开发,以供应用系统从配置中心读取上述配置信息。相应地,我们在工作流引擎也提供了从redis、zookeeper及本地文件中加载工作流配置的方法,前面工作流引擎接入部分已有介绍不作详述。

    + + +

    7.4 工作流导出与导入

    +

    然而,由于部分应用系统出于安全或者降低外部依赖的角度考虑,并不希望通过与后台管理系统进行联动的方式,动态更新工作流配置信息,因此我们在系统管理页面提供了工作流配置信息导出功能,用户可以直接将工作流配置信息导出为zip包,然后将上述zip包解压后直接添加到其应用系统的项目的本地目录下,应用系统在初始化工作流引擎时,可以直接使用工作流引擎提供的相关方法从本地目录加载工作流配置。

    + +
    + + \ No newline at end of file -- Gitee From bae54e41689ed7fe62b3f21bcccd4e96814b8eea Mon Sep 17 00:00:00 2001 From: dayunzhangyunfeng Date: Sun, 15 Sep 2024 11:23:39 +0800 Subject: [PATCH 06/12] update user_manual.md --- user_manual.md | 548 +++++++++++++++++++++---------------------------- 1 file changed, 234 insertions(+), 314 deletions(-) diff --git a/user_manual.md b/user_manual.md index 3c64203..c09d5c8 100644 --- a/user_manual.md +++ b/user_manual.md @@ -1,314 +1,234 @@ - - - - - tg-flow 使用手册 - - - -
    -

    tg-flow 使用手册

    -
    -
    -

    1. 编写目的

    -

    通过对本公司的 tg-flow 软件产品操作过程的描述,使用户可以自主对软件进行使用和操作。

    - -

    2. 下载与安装

    - -

    2.1 下载安装包

    -

    git clone https://github.com/didi/tg-flow

    - -

    2.2 安装和启动

    - -

    tg-service 的安装和启动:

    -
      -
    1. 将 tg-service 软件包拷贝到要安装的目录;
    2. -
    3. 进入 tg-service 目录,执行 ./build.sh,会生成 output 文件夹;
    4. -
    5. 执行:cd output 进入 output 目录,执行:nohup ./control.sh start > nohup.log 2>&1 &,启动服务。
    6. -
    - -

    tg-web 的安装步骤:

    -
      -
    1. 将 tg-web 软件包拷贝到要安装的目录;
    2. -
    3. 通过命令行方式,进入到 tg-web 所在目录,执行如下命令:npm install,即可完成 tg-web 部分的安装;
    4. -
    5. 通过命令行方式,进入到 tg-web 所在目录,执行如下命令:npm start,即可完成 tg-web 的启动;
    6. -
    7. 打开浏览器地址栏,输入 http://localhost:8888/,可以打开登录页,说明安装成功。
    8. -
    - -

    3. tg-core 是应用系统使用时需要导入的组件,不需要单独安装。

    - -

    3. 登录

    - -

    3.1 账号分配

    -

    初次安装时,系统会默认生成一个管理员账号 admin,密码 admin,可以使用该账号进行后续的登录和分配其他子账号。

    - -

    3.2 登录系统

    -

    第一次打开系统的时候,系统会自动跳转至登录页面,这个时候可以使用管理员账号或者子管理员账号,输入账号、密码、验证码进行登录。

    -
    - 登录 -
    - -

    3.3 注销登录

    -

    进入系统后,可以通过点击系统的右上角“退出登录”按钮注销登录。

    - -

    4. 系统管理

    -

    tg-flow 支持同时接入并管理多个系统和应用,每个系统都有独立的系统ID和系统名称,通过系统管理页面可以查看当前接入的所有业务系统。

    -
    - 登录 -
    -

    图4.1 系统管理界面

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    属性名称描述
    id系统 ID
    app_name系统名称
    machine_room系统所部署的集群
    node_name系统所在机房
    operator系统创建和修改人员
    create_time系统创建时间
    update_time系统更新时间
    git_url系统代码所属的代码仓库地址
    - -

    在策略管理 -> 系统管理页面可以进行新增,修改,导出和删除系统等相关操作。

    -
    - 登录 -
    -

    图4.2 新增系统对话框

    -

    其中系统编号,系统名称,部署机房和节点名称都为必填项,填写完成后点击保存按钮即可完成系统新增。

    -
    - 登录 -
    -

    图4.3 修改系统对话框

    -

    系统条目右侧的导出按钮并点击确认导出可以导出对应系统所有场景下的流程信息,并作为 zip 压缩包的形式下载到本地并用于对应的系统中。

    - -

    5. 场景管理

    - -

    5.1 场景列表

    -

    该列表页展示在当前平台注册的所有的场景信息,系统与场景属于一对多的关系。场景信息所含字段如下图所示。搜索栏中可通过输入“场景名称”或者“系统名称”来获取相应的场景信息。

    -
    - 登录 -
    -

    5.2 场景配置

    -

    场景列表页存在四个可操作按钮:

    -
      -
    • 新增:可手动添加某个系统下的场景信息;
    • -
    • 刷新:手动触发获取最新的场景列表;
    • -
    • 修改:针对单一场景修改其描述内容,除了场景编号之外其他内容均可修改;
    • -
    • 删除:删除某个场景;
    • -
    - -

    6 工作流引擎代码接入

    -

    6.1 代码框架

    -

    如下图为一个待接入工作流引擎的应用系统的项目代码,其中logic目录下为业务层代码,接入工作流引擎时,可以在业务层添加相关的接入代码,主要有:

    -
      -
    1. 工作流引擎初始化代码,类似下图中的workflow_task.go文件;
    2. -
    3. 工作流引擎调度执行代码,类似下图中的dispatcher_genrec.go;
    4. -
    5. 工作流节点对应的算子实现代码,算子有多个,可以放到不同子目录分组存放,类似下图中的module为上述子目录的父目录;
    6. -
    7. 算子注册代码,用于将算子注册到工作流引擎,类似下图中的module_init.go文件.
    8. -
    -
    - 登录 -
    -

    6.2 工作流引擎初始化

    -

    我们提供了三种工作流引擎初始化的方式:从本地工作流配置文件初始化、从全局配置中心提取配置信息初始化,以及从redis提取配置信息初始化。下面介绍的是第一种,从本地文件初始化,可以在自己的系统初始化模块中调用(其中workflowPath为工作流配置文件根目录):

    -
    -
    -func LoadWorkflowData(ctx context.Context) {
    -   moduleObj := module.ModuleObject{}
    -   workflowEngine, err := wfengine.NewWorkflowEngineFromFile(moduleObj, workflowPath)
    -   if err != nil {
    -      tlog.ErrorCount(ctx, "cron_task_workflow_err", fmt.Sprintf("workflow engine init fail, AppId=%v, error=%v", constants.CurrentAppId, err))
    -      return
    -   }
    -
    -   global.WorkflowEngine = workflowEngine
    -   tlog.Handler.Infof(ctx, consts.DLTagCronTask, "workflow engine update successful, appId=%v, workflowEngine=%v", constants.CurrentAppId, global.WorkflowEngine)
    -}
    -
    -
    - -

    6.3 工作流引擎调度执行

    -

    使用工作流引擎调度执行工作流,只需要在调度相关模块中添加下面的代码即可:

    -
    -
    -global.WorkflowEngine.Run(ctx, sc)
    -errMap := sc.GetErrorMap()
    -errMap.Range(func(key, val interface{}) bool {
    -   log.Println(fmt.Sprintf("workflowengine error, key=%v, val=%v", key, val))
    -)
    -   return true
    -})
    -
    -
    - -

    6.4 工作流算子开发

    -

    通常一个应用系统中可能会有多个业务场景,每个场景下可能需要配置多个不同的工作流,应用系统对外提供服务时,需要根据实际业务场景或实验分组情况选择使用不同的工作流来执行,工作流则由若干个执行节点组成,每个执行节点对应的是一个算子,在golang语言中这些算子对应的是struct。我们需要在要接入tg-flow的应用系统中定义这些算子,下面为一个名为RecallSampAction的算子定义示例:

    -
    -
    -package recall
    -
    -import (
    -   "context"
    -   "fmt"
    -   "github.com/didi/tg-flow/tg-core/model"
    -   "github.com/didi/tg-flow/tg-core/wfengine"
    -   "time"
    -)
    -
    -type RecallSampAction struct {
    -   wfengine.ModelBase
    -}
    -
    -func (r RecallSampAction) DoAction(ctx context.Context, sc *model.StrategyContext) interface{} {
    -   //此处添加相关业务逻辑
    -   return nil
    -}
    -
    -func (r RecallSampAction) OnTimeout(context.Context, *model.StrategyContext){
    -   //此处添加超时处理逻辑
    -}
    -
    -
    - -

    6.5 工作流算子注册

    -

    为了将开发好的算子注册给工作流引擎,需要进行算子注册,如下图中的case语句部分,即为工作流算子注册函数,我们只需要自己实现一个下面的函数,并在函数中的case语句中添加自己的实际开发的算子相关注册代码即可:

    -
    -
    -func (moduleObj ModuleObject) NewObj(moduleName string, vMap map[string]string) wfengine.IModelBase {
    -   switch moduleName {
    -       case “data.DataPrepareSamp1”: return &data.DataPrepareSamp1{}
    -       case “recall.RecallSamp1”: return &recall.RecallSamp1{}
    -       case “recall.RecallSamp2”: return &recall.RecallSamp2{}
    -   }
    -   return nil
    -}
    -
    -
    - -

    6.6 代码仓库注册

    -

    在应用系统中完成上述代码编写后,可以将代码提交到git代码仓库,并将git仓库地址拷贝出来,然后进入系统管理页面,如下图所示,点击“修改”按钮,将该代码仓库的地址填写到弹出层中的“git仓库”文本框,如下图红色矩形框所示,然后点击“保存“按钮即可。

    -
    - 登录 -
    -

    7 工作流管理

    -

    7.1 工作流列表

    -

    点击左侧的“流程管理”目录,可以打开流程列表的管理页面,如下图所示。

    - -
    - 登录 -
    -
      -

      选择列表上方的“系统”下拉框,点击查询,可以查到对应的应用系统已经添加的所有流程。

      -

      选择“场景名称”,点击查询,可以列出指定系统及场景下的所有工作流。

      -

      点击“新建”按钮,可以打开工作流编辑页面,开始一个新工作流的创建,下节会详细介绍新工作流的编辑功能。

      -

      工作流列表中有:修改、导出、复制、导入、删除等5个按钮。

      -

      点击“修改“按钮,可以打开工作流编辑页,后面的工作流编辑部分会详细介绍。

      -

      点击“导出”按钮,可以将当前工作流配置信息导出为一个json文件,点击确认后可以下载到本地,后面可以拷贝到待接入的应用系统工程目录下,应用系统在启动时,会初始化工作流引擎,工作流引擎初始化时可以通过本地方式加载上述配置。注意:此处是对单个工作流配置进行导出,如果要将某个系统下所有工作流打包导出,则需要进入到系统管理页操作。

      -

      点击“复制”按钮,可以新建一个同样的工作流配置,然后后面就可以在这个工作流上进行相关修改并保存。

      -

      点击“导入”按钮,会弹出一个对话框,这时可以将在外部编写的工作流配置信息复制并粘贴到这个对话框中的“导入内容(Json)”文本框中,点击“确认”按钮,即可完成工作流配置信息的导入,注意:工作流节点中的action_id的取值格式为:action-{场景ID}-{工作流ID},导入工作流时如果相关actionId中的场景ID和工作流ID与当前要导入的场景ID和工作流ID不一致,系统会自动帮忙将actionId中的后面两部分的值调整为当前的场景ID和工作流ID。

      -
      - 工作流编辑页面 -
      - -

      7.2 工作流编辑

      -

      打开工作流编辑页,系统会自动读取前面配置好的git仓库中的代码模块,获取代码中的算子列表,并在工作流编辑页面的左侧算子列表中展示,供后续的工作流编辑用。(注意:工作流中的条件节点已经在工作流引擎中做了内置的模块实现,所以无需单独在应用层再做实现,当然如果有需要,也可以自定义条件节点后,注册到工作流引擎)。如下图,为工作流编辑页面。工作流编辑页面包括三个区域:左侧为工作流节点列表,中间为流程构建区域,右侧为工作流及节点属性编辑区域。

      -
      - 工作流编辑页面 -
      -

      其中:

      -

      在后台管理系统中打开工作流编辑页面时,系统会从后台管理系统中配置的代码仓库中拉取代码模块名称,并以组件的方式显示在左侧组件栏。左侧的组件有两种类型:一是应用层节点,用户打开工作流图编辑界面后,会自动从对应的应用代码的git仓库中拉取对应的算子列表,展示在左侧的节点列表中。二是工作流引擎内置的条件节点和缺省节点(当git仓库未完成代码提交时,左侧无法拉取到代码模块名称,这时可以手动添加缺省节点,然后给缺省节点重新命名)。

      -

      从左侧组件栏中依次拖拽节点、添加节点之间的连线,可以逐步构建出类似下图中的主流程图和子流程图。

      -

      针对图中的每个节点,可以依次点击节点,然后在右侧的属性编辑区域编辑该节点的属性,如:节点名称、节点参数、节点描述信息等,完成所有节点属性的编辑后点击“保存”按钮,相关的工作流配置信息便会以json字符串格式存入数据库。

      - -
      - 工作流编辑页面2 -
      - -

      7.3 工作流配置发布

      -

      上一节中已存入数据库的工作流配置信息并不能自动发布到在线应用系统,需要在系统管理页面中点击“提交”按钮,将上述配置发布到一个具有存储全局配置功能的存储服务中,一般可基于zookeeper或redis开发,以供应用系统从配置中心读取上述配置信息。相应地,我们在工作流引擎也提供了从redis、zookeeper及本地文件中加载工作流配置的方法,前面工作流引擎接入部分已有介绍不作详述。

      - - -

      7.4 工作流导出与导入

      -

      然而,由于部分应用系统出于安全或者降低外部依赖的角度考虑,并不希望通过与后台管理系统进行联动的方式,动态更新工作流配置信息,因此我们在系统管理页面提供了工作流配置信息导出功能,用户可以直接将工作流配置信息导出为zip包,然后将上述zip包解压后直接添加到其应用系统的项目的本地目录下,应用系统在初始化工作流引擎时,可以直接使用工作流引擎提供的相关方法从本地目录加载工作流配置。

      - -
      - - \ No newline at end of file +## 1. 编写目的 + +通过对本公司的 tg-flow 软件产品操作过程的描述,使用户可以自主对软件进行使用和操作。 + +## 2. 下载与安装 + +### 2.1 下载地址 +tg-flow:git clone https://github.com/didi/tg-flow + +tg-example:git clone https://github.com/didi/tg-example + +tg-service:(敬请期待) + +### 2.2 安装和启动 + +#### tg-service 的安装和启动: + +1. 将 tg-service 软件包拷贝到要安装的目录; +2. 进入 tg-service 目录,执行 `./build.sh`,会生成 output 文件夹; +3. 执行:`cd output` 进入 output + 目录,执行:`nohup ./control.sh start > nohup.log 2>&1 &`,启动服务。 +4. 打开浏览器地址栏,输入 ,可以打开登录页,说明安装成功。 + +5. 注:tg-flow 是应用系统使用时需要导入的组件,可下载源码学习,但不需要单独安装。 + +## 3. 登录 + +### 3.1 账号分配 + +初次安装时,系统会默认生成一个管理员账号 admin,密码 +admin,可以使用该账号进行后续的登录和分配其他子账号。 + +### 3.2 登录系统 + +第一次打开系统的时候,系统会自动跳转至登录页面,这个时候可以使用管理员账号或者子管理员账号,输入账号、密码、验证码进行登录。 + +![登录](docs/login.png) + +### 3.3 注销登录 + +进入系统后,可以通过点击系统的右上角"退出登录"按钮注销登录。 + +## 4. 系统管理 + +tg-flow +支持同时接入并管理多个系统和应用,每个系统都有独立的系统ID和系统名称,通过系统管理页面可以查看当前接入的所有业务系统。 +![登录](docs/app_manage.png) + +### 图4.1 系统管理界面 + +属性名称 描述 + -------------- ---------------------------- +id 系统 ID +app_name 系统名称 +machine_room 系统所部署的集群 +node_name 系统所在机房 +operator 系统创建和修改人员 +create_time 系统创建时间 +update_time 系统更新时间 +git_url 系统代码所属的代码仓库地址 + +在策略管理 +系统管理页面可以进行新增,修改,导出和删除系统等相关操作。 +![登录](docs/app_add.png) + +### 图4.2 新增系统对话框 + +其中系统编号,系统名称,部署机房和节点名称都为必填项,填写完成后点击保存按钮即可完成系统新增。 + +![登录](docs/app_update.png) + +### 图4.3 修改系统对话框 + +系统条目右侧的导出按钮并点击确认导出可以导出对应系统所有场景下的流程信息,并作为 +zip 压缩包的形式下载到本地并用于对应的系统中。 + +## 5. 场景管理 + +### 5.1 场景列表 + +该列表页展示在当前平台注册的所有的场景信息,系统与场景属于一对多的关系。场景信息所含字段如下图所示。搜索栏中可通过输入"场景名称"或者"系统名称"来获取相应的场景信息。 + +![登录](docs/scene.png) + +### 5.2 场景配置 + +场景列表页存在四个可操作按钮: + +- 新增:可手动添加某个系统下的场景信息; +- 刷新:手动触发获取最新的场景列表; +- 修改:针对单一场景修改其描述内容,除了场景编号之外其他内容均可修改; +- 删除:删除某个场景; + +## 6 工作流引擎代码接入 + +### 6.1 代码框架 + +如下图为一个待接入工作流引擎的应用系统的项目代码,其中logic目录下为业务层代码,接入工作流引擎时,可以在业务层添加相关的接入代码,主要有: + +1. 工作流引擎初始化代码,类似下图中的workflow_task.go文件; +2. 工作流引擎调度执行代码,类似下图中的dispatcher_genrec.go; +3. 工作流节点对应的算子实现代码,算子有多个,可以放到不同子目录分组存放,类似下图中的module为上述子目录的父目录; +4. 算子注册代码,用于将算子注册到工作流引擎,类似下图中的module_init.go文件. + +![登录](docs/code_dir.png) + +### 6.2 工作流引擎初始化 + +我们提供了三种工作流引擎初始化的方式:从本地工作流配置文件初始化、从全局配置中心提取配置信息初始化,以及从redis提取配置信息初始化。下面介绍的是第一种,从本地文件初始化,可以在自己的系统初始化模块中调用(其中workflowPath为工作流配置文件根目录): + + + func LoadWorkflowData(ctx context.Context) { + moduleObj := module.ModuleObject{} + workflowEngine, err := wfengine.NewWorkflowEngineFromFile(moduleObj, workflowPath) + if err != nil { + tlog.ErrorCount(ctx, "cron_task_workflow_err", fmt.Sprintf("workflow engine init fail, AppId=%v, error=%v", constants.CurrentAppId, err)) + return + } + + global.WorkflowEngine = workflowEngine + tlog.Handler.Infof(ctx, consts.DLTagCronTask, "workflow engine update successful, appId=%v, workflowEngine=%v", constants.CurrentAppId, global.WorkflowEngine) + } + +### 6.3 工作流引擎调度执行 + +使用工作流引擎调度执行工作流,只需要在调度相关模块中添加下面的代码即可: + + + global.WorkflowEngine.Run(ctx, sc) + errMap := sc.GetErrorMap() + errMap.Range(func(key, val interface{}) bool { + log.Println(fmt.Sprintf("workflowengine error, key=%v, val=%v", key, val)) + ) + return true + }) + +### 6.4 工作流算子开发 + +通常一个应用系统中可能会有多个业务场景,每个场景下可能需要配置多个不同的工作流,应用系统对外提供服务时,需要根据实际业务场景或实验分组情况选择使用不同的工作流来执行,工作流则由若干个执行节点组成,每个执行节点对应的是一个算子,在golang语言中这些算子对应的是struct。我们需要在要接入tg-flow的应用系统中定义这些算子,下面为一个名为RecallSampAction的算子定义示例: + + + package recall + + import ( + "context" + "fmt" + "github.com/didi/tg-flow/tg-core/model" + "github.com/didi/tg-flow/tg-core/wfengine" + "time" + ) + + type RecallSampAction struct { + wfengine.ModelBase + } + + func (r RecallSampAction) DoAction(ctx context.Context, sc *model.StrategyContext) interface{} { + //此处添加相关业务逻辑 + return nil + } + + func (r RecallSampAction) OnTimeout(context.Context, *model.StrategyContext){ + //此处添加超时处理逻辑 + } + +### 6.5 工作流算子注册 + +为了将开发好的算子注册给工作流引擎,需要进行算子注册,如下图中的case语句部分,即为工作流算子注册函数,我们只需要自己实现一个下面的函数,并在函数中的case语句中添加自己的实际开发的算子相关注册代码即可: + + + func (moduleObj ModuleObject) NewObj(moduleName string, vMap map[string]string) wfengine.IModelBase { + switch moduleName { + case “data.DataPrepareSamp1”: return &data.DataPrepareSamp1{} + case “recall.RecallSamp1”: return &recall.RecallSamp1{} + case “recall.RecallSamp2”: return &recall.RecallSamp2{} + } + return nil + } + +### 6.6 代码仓库注册 + +在应用系统中完成上述代码编写后,可以将代码提交到git代码仓库,并将git仓库地址拷贝出来,然后进入系统管理页面,如下图所示,点击"修改"按钮,将该代码仓库的地址填写到弹出层中的"git仓库"文本框,如下图红色矩形框所示,然后点击"保存"按钮即可。 + +![登录](docs/git_reg.png) + +## 7 工作流管理 + +### 7.1 工作流列表 + +点击左侧的"流程管理"目录,可以打开流程列表的管理页面,如下图所示。 + +![登录](docs/flow_manage.png) + +选择列表上方的"系统"下拉框,点击查询,可以查到对应的应用系统已经添加的所有流程。 + +选择"场景名称",点击查询,可以列出指定系统及场景下的所有工作流。 + +点击"新建"按钮,可以打开工作流编辑页面,开始一个新工作流的创建,下节会详细介绍新工作流的编辑功能。 + +工作流列表中有:修改、导出、复制、导入、删除等5个按钮。 + +点击"修改"按钮,可以打开工作流编辑页,后面的工作流编辑部分会详细介绍。 + +点击"导出"按钮,可以将当前工作流配置信息导出为一个json文件,点击确认后可以下载到本地,后面可以拷贝到待接入的应用系统工程目录下,应用系统在启动时,会初始化工作流引擎,工作流引擎初始化时可以通过本地方式加载上述配置。注意:此处是对单个工作流配置进行导出,如果要将某个系统下所有工作流打包导出,则需要进入到系统管理页操作。 + +点击"复制"按钮,可以新建一个同样的工作流配置,然后后面就可以在这个工作流上进行相关修改并保存。 + +点击"导入"按钮,会弹出一个对话框,这时可以将在外部编写的工作流配置信息复制并粘贴到这个对话框中的"导入内容(Json)"文本框中,点击"确认"按钮,即可完成工作流配置信息的导入,注意:工作流节点中的action_id的取值格式为:action-{场景ID}-{工作流ID},导入工作流时如果相关actionId中的场景ID和工作流ID与当前要导入的场景ID和工作流ID不一致,系统会自动帮忙将actionId中的后面两部分的值调整为当前的场景ID和工作流ID。 + +![工作流编辑页面](docs/flow_import.png) + +### 7.2 工作流编辑 + +打开工作流编辑页,系统会自动读取前面配置好的git仓库中的代码模块,获取代码中的算子列表,并在工作流编辑页面的左侧算子列表中展示,供后续的工作流编辑用。(注意:工作流中的条件节点已经在工作流引擎中做了内置的模块实现,所以无需单独在应用层再做实现,当然如果有需要,也可以自定义条件节点后,注册到工作流引擎)。如下图,为工作流编辑页面。工作流编辑页面包括三个区域:左侧为工作流节点列表,中间为流程构建区域,右侧为工作流及节点属性编辑区域。 + +![工作流编辑页面](docs/flow_edit.png) + + +其中: + +在后台管理系统中打开工作流编辑页面时,系统会从后台管理系统中配置的代码仓库中拉取代码模块名称,并以组件的方式显示在左侧组件栏。左侧的组件有两种类型:一是应用层节点,用户打开工作流图编辑界面后,会自动从对应的应用代码的git仓库中拉取对应的算子列表,展示在左侧的节点列表中。二是工作流引擎内置的条件节点和缺省节点(当git仓库未完成代码提交时,左侧无法拉取到代码模块名称,这时可以手动添加缺省节点,然后给缺省节点重新命名)。 + +从左侧组件栏中依次拖拽节点、添加节点之间的连线,可以逐步构建出类似下图中的主流程图和子流程图。 + +针对图中的每个节点,可以依次点击节点,然后在右侧的属性编辑区域编辑该节点的属性,如:节点名称、节点参数、节点描述信息等,完成所有节点属性的编辑后点击"保存"按钮,相关的工作流配置信息便会以json字符串格式存入数据库。 + +![工作流编辑页面2](docs/flow_edit2.png) + +### 7.3 工作流配置发布 + +上一节中已存入数据库的工作流配置信息并不能自动发布到在线应用系统,需要在系统管理页面中点击"提交"按钮,将上述配置发布到一个具有存储全局配置功能的存储服务中,一般可基于zookeeper或redis开发,以供应用系统从配置中心读取上述配置信息。相应地,我们在工作流引擎也提供了从redis、zookeeper及本地文件中加载工作流配置的方法,前面工作流引擎接入部分已有介绍不作详述。 + +### 7.4 工作流导出与导入 + +然而,由于部分应用系统出于安全或者降低外部依赖的角度考虑,并不希望通过与后台管理系统进行联动的方式,动态更新工作流配置信息,因此我们在系统管理页面提供了工作流配置信息导出功能,用户可以直接将工作流配置信息导出为zip包,然后将上述zip包解压后直接添加到其应用系统的项目的本地目录下,应用系统在初始化工作流引擎时,可以直接使用工作流引擎提供的相关方法从本地目录加载工作流配置。 \ No newline at end of file -- Gitee From 185459be008f1727f32b8e6ad64c800f4ed898d8 Mon Sep 17 00:00:00 2001 From: dayunzhangyunfeng Date: Sun, 15 Sep 2024 15:30:13 +0800 Subject: [PATCH 07/12] =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=89=8B=E5=86=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 ++++++------ user_manual.md | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index dc1c01d..8a75908 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # tg-flow简介 -tg-flow是一个专注于在线高并发场景的无状态的工作流引擎,它可以在保证高性能的前提下,为在线高并发系统提供复杂的的工作流调度能力,目前tg-flow已经广泛运用于滴滴内部多个日流量数十亿的核心在线系统。 -tg-flow包括两个主要模块: -* 流程调度模块: tg-flow的工作流引擎核心模块,提供了对工作流的的调度执行能力。 -* 配置管理模块: 它为用户提供工作流的创建、编辑、保存、删除、导入、导出等功能,同时也提供工作流配置在线自动分发的能力,可将配置分发到redis、zookeeper、文件系统(导出为文件)中,再由流程调度模块自动更新在线配置。 - -目前仅开源出流程调度模块,配置管理模块尚需进一步完善后开源。同时,为了便于用户快速上手,我们在github上提供了一个基于tg-flow创建的示例应用程序,下载即可运行。地址:[tg-example](https://github.com/didi/tg-example) +tg-flow是一个专注于在线高并发场景的无状态的工作流引擎,它可以在保证高性能的前提下,为在线高并发系统提供复杂的的工作流调度能力,目前tg-flow已经广泛运用于滴滴内部多个核心在线高并发系统。 +tg-flow 相关的开源仓库总共将包括3个部分: +* tg-flow: tg-flow的工作流引擎,[github仓库](https://github.com/didi/tg-flow)提供了对工作流的的调度执行能力。 +* tg-example: 为了便于用户快速上手使用tg-flow,我们提供了一个示例应用程序:[tg-example](https://github.com/didi/tg-example),如果您本地拥有golang环境,则下载后直接go run main.go即可运行。 +* tg-service: tg-flow的后台管理系统,它为用户提供工作流的创建、编辑、保存、删除、导入、导出等功能,同时也提供工作流配置在线自动分发的能力,可将配置分发到redis、文件系统(导出为文件)中,再由流程调度模块自动更新在线配置。 +* 注:用户可以不必依赖tg-service来管理工作流配置,可以直接编辑工作流配置文件,并保存到自己项目指定的目录下,供引擎初始化时加载。 目前后台管理系统尚需进一步完善后再开源。 # 我们的目标 受限于在线高并发场景的高性能要求,传统工作流引擎很难支持在线高并发系统。 我们的目标是实现一个既能够满足在线高并发场景的性能要求,又具备传统工作流引擎各种复杂功能的工作流引擎。 diff --git a/user_manual.md b/user_manual.md index c09d5c8..2447527 100644 --- a/user_manual.md +++ b/user_manual.md @@ -2,12 +2,12 @@ 通过对本公司的 tg-flow 软件产品操作过程的描述,使用户可以自主对软件进行使用和操作。 +本手册第6节之前均为tg-service后台管理系统的安装部署,由于目前该系统尚在完善中,暂未开源,所以可以暂时不用过多关注。 +目前,可以先直接手工完成工作流配置文件的编写,将来tg-service开源后再通过该系统进行工作流的配置和管理。 + ## 2. 下载与安装 ### 2.1 下载地址 -tg-flow:git clone https://github.com/didi/tg-flow - -tg-example:git clone https://github.com/didi/tg-example tg-service:(敬请期待) -- Gitee From 790914c879d439349802da96b9ca2230555e145d Mon Sep 17 00:00:00 2001 From: dayunzhangyunfeng Date: Tue, 17 Sep 2024 19:23:29 +0800 Subject: [PATCH 08/12] move user_manual.md to tg-example --- README.md | 2 +- docs/app_add.png | Bin 28001 -> 0 bytes docs/app_manage.png | Bin 45989 -> 0 bytes docs/app_update.png | Bin 32281 -> 0 bytes docs/code_dir.png | Bin 103637 -> 0 bytes docs/flow_edit.png | Bin 73607 -> 0 bytes docs/flow_edit2.png | Bin 90576 -> 0 bytes docs/flow_import.png | Bin 37291 -> 0 bytes docs/flow_manage.png | Bin 36484 -> 0 bytes docs/git_reg.png | Bin 71345 -> 0 bytes docs/login.png | Bin 33635 -> 0 bytes docs/scene.png | Bin 35855 -> 0 bytes user_manual.html | 314 ------------------------------------------- user_manual.md | 234 -------------------------------- 14 files changed, 1 insertion(+), 549 deletions(-) delete mode 100644 docs/app_add.png delete mode 100644 docs/app_manage.png delete mode 100644 docs/app_update.png delete mode 100644 docs/code_dir.png delete mode 100644 docs/flow_edit.png delete mode 100644 docs/flow_edit2.png delete mode 100644 docs/flow_import.png delete mode 100644 docs/flow_manage.png delete mode 100644 docs/git_reg.png delete mode 100644 docs/login.png delete mode 100644 docs/scene.png delete mode 100644 user_manual.html delete mode 100644 user_manual.md diff --git a/README.md b/README.md index 8a75908..a9209a3 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ tg-flow 相关的开源仓库总共将包括3个部分: 3. 再进一步,如果需要使用tg-flow所有功能,可以在第二步的基础上,自行部署redis或zookeeper,然后在线系统中可以使用tg-core中的核心模块定期从redis或zookeeper中检测工作流新版本,并及时更新到在线系统。 # 用户手册 - 参见: [user_manual](user_manual.html) + 参见: [user_manual](https://github.com/didi/tg-example/blob/main/user_manual.md) # 欢迎加入 请联系:[张云锋](https://github.com/dayunzhangyunfeng), [周子纯](https://github.com/zhouzichun0315), [唐桂尧](https://github.com/tgy931) diff --git a/docs/app_add.png b/docs/app_add.png deleted file mode 100644 index e3757cbbb1461105425d04b7962f93ace43a0cc8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28001 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 Z6R1wOc=HiU-Y)R(s_uyH{=iO- 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 diff --git a/docs/app_update.png b/docs/app_update.png deleted file mode 100644 index ce98a196a5ac232b79e7299864f696204b460161..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32281 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 diff --git a/docs/flow_import.png b/docs/flow_import.png deleted file mode 100644 index ad9f14e9c2048886f0a333afb84cdd9ac78e02fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37291 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 diff --git a/docs/git_reg.png b/docs/git_reg.png deleted file mode 100644 index 7cd0d6b5bce5661b9c87ad26bf3ee45b3eae2c2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71345 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%0DK5|cz z%n6E4zGVuW25)0(aQv@2zvIz3Zxk#+txQ@XtvpXF<9=+>4hs_Hm$S7K4+EHx#IGqD zT=en&#YnZZ&fwr+O*KiVJvAmZt#NTY#>OA3#mDs%@8LQh zH$Cv`m5#Y@v%m1!NM8WC5B5c-yn{{N_7-mov}i{8F>Y(0@-^daxsPtS?GCiD>!#)| z@R+wJ1-~Yb?wy?6 zFp8CzksYvLc+M3!c>SD@+r5HQ{|RpilwCP7Rl9dw=(^4_xqtqJcINf#&=|1%^YQJV zJ@T0@guLQyUt{o%%e%|x>umB)Ai^k(c`_3}4~o`VucsoE^I=qBDS)N62%UPQ!%|#kke@WiccUwtlDr^egbi>lMM)C-BFc+@}T6X7noR ze-#{Hq||s{{^GmnrsgF_DhGW2T&A$Y)%!{f3bq3$j?GGnm5yM!3NN7$ zw^7Pa#Eg+;a?ryqZmm`wxnX9xp6tYRpHGu(=e8gStY-!^fRJ<>j`#D}XCN4cKoyS- zSu>6+9)0%>(^!1VLzInfCy+Z9)V0sY*yqqj_ZzV1{Z>~rG<0o-O>3GQt-tBHsgrf+ zLjjp6FjijD9amd{IyM%5?+%H0=4qKnnL&5TwN89_KL5YhY+_qB6b=KSGs|EmlVP?1 z3bf*z&58TFO%MLSAyJs?^GnZ|*Iq08qPs84fwrnII?S@&)JFH2CX|l*F`1_42-)O# zr^}Xh$7|MQ53xG`m%-%&sM1kftA}lzrz7`?_|AczO9KIYvd*1PRLNV;B}PMzBg^ew zc{An$>0e`s=qH2{i%89~Q(xm%&vvNPv)sn(q1OJT6j>QqX%Kw*Qlfpko2DL8nR5ma z1IjghHk?q75}R1sPo=2l$)y&D;g)pRwP`GtIbgEF{#Hjb9l(k*Mx|A6JPD&~E1B4=X$gMZ}Y zzshDosMMYjvKmi|L?>@@5&0s4yW#jW6?saB-*%5_oY$7bF@~O(db8PJ^w_=+JED4)u5Im~&?JU`C;_4Z{-}^ht3!tmxI?4*d;Bo5s{PO$L)4 z{a||Gf+A;sVnEDZ4ZPCwCXBQj6q5_F90!7t4DSR}$1HsJtRWR0jvc(53TgqqW zdo3f-PQs;^KgYy92BhWNDBJwEi`30VVNehN_9YY(+daCt5H>Pe=`hwu{)2233FB;| z0761;DvioG3`NGhR~S|vi>D<+Jtdu;3LOniT&*>G7?xmQ`DY-WHMJIB3M8jAmxDb`AsPZyfDz6n#>PA3 zla*{?{B6AdX2ajjOwT&ScExwk4DzAA9m3$?34f;Ugs?QL;e--es9jYX==L=?ZR~+` zaH6{eIST>?hIkM{a^B)mNBmvuYu3eTa#YeTr@iM7`T( zA~<=Ci;moGn(o>D8~KN1!1dj#H#7|CPq2e?%l4#Uqr~C4_P>27dw7Rums?nTvBL|7 z%7E}cM4_vj$!Aot`X2HnEj7LK?l4`9q)D=2e_H@ar0PW#>|FyvEE}U$YGr|%&8I+M zqHX^0t?Fet1*aTv)guV0*EjN}deIkoz|lyM<{aCF!ohbTzF{dX3o{w2Dw=WRTP}ojDI~@;F8cv=PkCPl3GKk zD!bA2*VvgMv4!cSU_k&;!OJTW4Xt2HYnV{Z;@HQ4qnACH?AxSV%T`)?+8+8(S>hJbK61jjpugknOZi^_lP1rSOX=0Bq(W7Y znNI@g>~~z32CK~_i|ovz(rls$JUn3#Gxc+~cR?Ohpr~{;dDx#>G;;gJP;XCLncI?I zI5yj4KviJ_%~igvi;HS#*-ip}!5npPPsG#jj8hz%fgCD^q}CDpiy?eyR=yTWu|%e$CUycUS0n9@iv` z{wh-_eH#_*;a53Wd;Q&s)w+`KF>}l_1BKW{*54hWeKgoYQd+SfQGF53d@h875vuX% z&7|4uqL>8S@SKxKw%5KJ80%@%k|9Gy!p7)+UF33fd@LyL5%T(Ns%pZ# z$p8Rf!S(6+@%zqGH{09juIWG1e>)PHL9pjD$ylHVYnGVgeoOb}JUwXme( z*FrHCFXNvcY%>}fgxu65Y-$kecNOQk?C6^h^*=8~;fUyrPs6#{sMgwO_I})!Q7cJ~ zZu@m~Hp~pr{Pp^kCG{Vv!}e<=kt=&)1cr&$VSnj!*cc2QytL-VaHq$`PLXjaocwUf zs6>5)cvKe=IA$e+Et!1D$;QW0ghYosi(4RwkRkmgWo6F#W9jTf=&TO(KBPz=i0&R9 zTGd3>k*0K!4*|}vLd$JZ|K0+D12Nvf#?splsW7}vI5DRa!M^9m35)sN9D)xA@mc)#2@_54`AlLAI(&&v0t*byr$+jt3D{^E?2Dt$0**cu%|k)!}2l1 zQs;kz9^Y?wS22-cTev9&tC)iB`I(KM>Px7}vr2V9&d5njj&3V~DuP6f^$k=7-YGLP z884Px?K0NB8_@rVXnDW4qWu27uda~xZMX{ZRXo6rg;tw5BDIU)54&iX{ zd_X?ynR^I0rUVHhj%6o0)SMLSZk>XtUB|(}h+%E{ ztw)4?f-~enRm?R7x>3?o&i0nn(ud=(??8G`PD2*Wm6F z+zAjI0t_%faCdiim*6D0yK8WF_u%gKO`dz7H+k=0^;dOKQ^iR4>C@-zz1G@mvq_U+ z0>G*?!`F_>G8>vKoi6vqn}({7_MVo`mX~hE$9uUiU)q5s0(#Ldw&;bYbtKp<7+Q+_ z7f>KD`X~z2qS^%{@~kqOrqZ$P!&3O%cHWK9xR++GPq6>HGj`)y`xENsmXy8ba=Ry= z7pUiVUO@pgC=ho6iD4^UE&%izm0$#%7Cx)XYim&)=5vj=CrIf}>F+_Gvzi~9vDPm5 z&+z|#VFQFsnI!Fds9vrfSRuu09|Ra7Be^k+NdboHoNd3*h?<+_;?Q)gAJ`%#AWU*Kc4U&X2$oHv!uc(j| z7l;0hi_AyCabQ@5V>8%^jP`GMy8`8;vHlaL{N`ZvvEde=R(liLL{e=%eZ-_6a{RW`ID3zSY3{Xq8nq3po6MoB~bkr+%)>d!IJ-+ z$}xJIUhV$7kuxG9B6rts=`>(GOF*O1CTE$X5yY_U?(UA2O%jJk{r8yHqcO{-V1O*I z`k@l#H`w&l`j6loTOSQ`M{rDB`L(_&KsDPgx=29l6Ft%F+>bjfMmd_q8jcAX0{|X2+w9Kugf3P(BL6F5y5NIxE+HNs@LhIV*xU$lg$BMvP4Q zf)C;42f`oplDh}W;*kwqk&KY%<;2ONTd)Crb3}x9xov%AUj~y6&tz>bML~&CdsK4@C2+i)k+# zZw=1*z2|lK*~GlGR0*PYuuuf9V=xSX3|Ha_aIEfl=b%P08mp=$YE8mK*k+Ruo(s(^ z7#4d;rfQruowTFq;KeT*N+}h;=pDaX{mllBVMB15+yzcr;ES+63219%3~__bf)&Q# zsE6L-#ro~mns3uM7Lx&f>ttP}|PMkP#nu~o-;-QQgG`CLQ$Y!hQzHmkZg1|X^Q zeNEgVezmq$Q=ew%?+TCK>Tr9~ONRBsH0?a-cNcv+doB)HCJ?8-0Rh%oKZ<$&9Tadh zZfe+jvXOV`KnKYNE6RbN0@{EmKSIsL+j1S`XvU>{VsTbU0*vYm=us_$;XUoIgTyh3 zk-Nqj?sf35T*4ReM=zOb15|z^Hxs=|^w{#k7f+)eQ2O!J<6b;IXJ!9Pjad) zkf|RPN1RJ7PxJ?gAVjXK1YMTFS z9-w>H1NDU`qqF;x1o=VwJVH9~n!eDcn(u=_On1ApPBuNtAU%#|Aj_0Nd{ZhN%xz zGEVkM?ctG9gXa|U;PfmqB`}&*|3vz~9{`eZsAJ9NoKuQh6`l9_KY2R@1EOBpad|s6 zzaPr@Yk+*=;L2nx2o*NtC`ps`M12!g0wRH0G>ytyGatesX!a+U>IC1J>B>$v*A|h7 z<_UOAesHQCSpK!t&zfo_RkXm|wm$`D-nlHcoKtxM$sISd0oI*}^+g9ktGC;jQtAdN zZIETW*N#gaHC*GgJb~`tnCXA1*K!Qz{Z5*@q2g@)+-U-FHJ`a+=EGsofZaJ3+GGIC zUdWa_AV`nD%IA8gFh<1xN@quy!j!q$6^7Bph`xr31@x@eR;SA5onV8a{Fu2a%;tAx zh^7E9@exO^A0-`rXIXuIE`GV2X% z%Af;kptM&13*70{A<r0GGJLv# zD$|Z1L{@C2Q~~Gj5ZZ*&AnEk{It^~Ylje|ATMW>5H-}G8>%qm%F%I|SNPs>sQ=&0AoxZSZV=)<-D=q~GPQJTj}u1iLrd z&KxUbTv0P|G*CS2Yt`R(G!@}(EE@8rD`jrFm(nlj$k86YQS*vr^-ke*byt% z_4)Wk*sIm(C5*peRS-%|2SsCNrG+IP=eIw!7lR=SG_J1Gb*wegtUgMMW1jaes45LB z591MCm`;q98Pcq$sqFr&i_oN2TF&0hiD&B17_L?y=gX%ffKbyFwCWu>*0>9m9Lh3n zpGsS83QmS_)Fc<_H&&|O%2i)F7`WU}r70sB_US9Y;>J#$NNSm=KQFLcV)Ioh1S_gc z3;XYr^W~BCdF+SFC0T`Q+8NTU%V=5sPGjeuq(qns#+qZKgl11U%t4pT*9@fj>0&^@ zmF`i!Gf0Er#l3_!T&71?mGom?!D6+pOUQ~}ZNAt|62r1SZPbm!*F^ne(!)zrGxY1W z238{q2NMLv#!EO|sGG1J(CrWj;2V0n~DiRkL+O*=M zY+FNHGo?dzk#?W0em)&8C8wG2-|Nxyxd}n=U36?*-`%{oM@ml?xFzu~__Al(u6mKB z;8M0Ujqv6*ZG|_hFCs{(Bc z4R1aO;Ey>dFYv`B#WvT!l#Z9$VA45trSY(Og*;BL?jxgL(l9=^!!2ZqgG8ox+YHvO z2x<5PO?O1js!U7+8=WLsYpr+{t&i~K-aR+mb8d)7jLopJNj6ox-?uIUQd0{B3_!57 zf>$KF>7OSrg$pEX-W!$FUEoUFlkf_TBJ8^o@<#q`E{Pf`VRNIJ{z$|^g=w-dVoa3 z)6q9#8t6IwRrk-#Qi%6y_(E$x251-Dp2w(A09DSluHK39_ANUuQ-H343Ow(45N;uQ zvH|c=SN!$s#f$fw;D--%=b;_<6kb>JLSZ0*fywMrlRZwB7mzJ08|-No#KqLtJGEZD zw;tGUKNq*q@Xj$WscRdB`>o-yZhTBDx%e4P)V1z$>!sB@1Y+(u`5$J04o;5M0vqqG zy$NoQ*MmzqtsKX0%tvhhFneFZbBi zT=!xI_IUDA6>=XKKc-@txMZz)o~u=t&6Y@iQ=5FFCS>=%?e4 zoJuQnX=MSiFp4DeD2?TX_{4Ab)>|+_E>Lf~cV0MfucmMF{wP~LchB%ijGBIY>vw`C zvmjYUPolb2ej=Uk)9Dkl)-vzpOfA%W#|6@_!xM2ycNn6_xdxhrQ*m+eKm?^`;ON)Z z3#9q2O-Z6IT?>=tql&zmJjj;2HgC*D-AJ$qflWGM}> zrqEc#>Qt!^V~oigS~QoNI)?i_QIt);S@UzD*H?R&?Yemz(RSD`Ub|SOg)xuwFdcqa z45JGUyNL@m&M4Rn8a|v_UFGd=7!`M0z7-xw!N^3y!`0Ze^wwR9UYFd+M1le7R*k+Q zWuauJDfS;dwtf^)u^=CUMAZq*wOD9p^yHnZ?e3mAT1!e{^MdF4)rGlKs%*l(6g ztKuWC#zg$k1eLbyTH$fcEVN|vjgBD+xSPQ!9U?C&waZ3UdD(h3vCs(Xy!Iei>sV_5 zrwcWFP}t}+ihq53x9Q(;?M1KCI3e(YxA9mZMSs|y&D3_yI`?S1`p|2Y4~^jn$5 z&alUMAjecW!g;c+fUtlx-x2$fSF6f=o>6P2yz`2wRZ3PHqy1|2Y;!0wxdrLI)mvCD zAQNeA=9J-A^uMTy59|&_=MT3r+F>~;t+97k31%-(gJUnr--@R0CFN0 zUz0v$g8Odfkb3OBj&EhTpGt78A_NC-YdCKP-VdpFNR(*6;(Uq_xVZ+V)%ja_E}U3< z(}_F-i3IEjncAPxUpG*^P^yGSn&H|XigL9O`0YpDpHt5ehq5S-Gi=`y&=3so zH|xnzP|DEl5ipS;Cv_V0_-wK+d;%Bx!VHNRchS;urEMiYPcHPzvUWSSyrRGEJ)ijt z;LsBn2YB8a@LGL;XQQhQYK&duaC3XJc}d6U7>jZeG|bq7*+)q^wmVVIx3sLJscBsF zy`-#f31Kc9l<#XdSQdqEtI{yh4WzGMQ>hDXDn@zFP$eAwy~&Y8Lmi_vH*LFq6r`e& zyYpEY0_{GIhskbNvg192<5y=@x1-uaqe7Y~PU%ASTLaI79^9)bjTfaz{&X5K)rWX_ zaFrzQD{pj?_*$ezESy}&f2bw~bV*mK! zvgwkIG_2kBHSvAFvZeg!LX)^*ZE^ue3H2O7Nj}D(B^}jQlQf1lZUkxkOHMYEsl{k} zvK*MwJJR0a&k8Jib(lNv&E_hN1~9F!{X`!Hs<2d)BA{acVv<+o)%GQpneKi=hd`>M z)Ip?vsQF*CXDAvpR_dmCc2T+fX|Y|GHOqq*Y~K6mfCs}LNm88+aglG2DF^M(c`nE9 zqvq=4)e?OfnNh3eIDaAUBoHPH;OT28rn0%!G$CWJnDy@!bE;TF5^p|UBNa`|p&$-S zYbd|(RhZ-JAl*5{mZWN@G^xU$6&P?|1e&9ZD=D(vAQjkNY@rs1`$Ddov8&}*RgfR- zMbs6L_QqeQeYkO${=S26ogq*01T%S8GmrX3BRsYZ235NqGY3Sbr4WaRGut&{FZ80^zC?Vty<4YPZx~K&5vf=%pfN-T?zJT*{w~1YBe)*cZ5*;Opq33nUVDWn6 zFfig?R8nI1_Ygm$RvzeNxJR_P(S)ks(`Xf&(@M?1*5;kq}qT1-&HFGV(kl2Rvp6swRB*)-Dv1_hZOn z{F@7W?4ezLlcjP@74(*_0Eh|X;S-L7jN;@ZNtDaETTK4cT8#&(?QH0B*98~kcYLvT zrG;JPF_lqaxoE8~r!PNqi%~%Z!K=B+abcx6V!VH#^=B^?%dUj7I5bJ3`-=N1n&)U9 zAHE`s6uzbhj(rmTc4sUALzOBGpAM3SX^W1N*C2zLUt1l>0;3$FoT`zoHvFZpb*+`p zcbO?Hm;RlHx49q0s68dUfwp>GN>AQAu-w%+w}ROJ33<|=MT|~E$S*=0)ynEp2Tetq zg11F!Wh!8+P~y6+7J65`Tr$LwI~$IiekL$LBj@K>o>Qh!Sw-*OMR4=O^K?75bD5VpOEmE57TyqG&^2^e z9t_`vDI$LQ@p6uFm^g?-Rag$;>&Ha1`;UraPZ(cppyeNIr~Cr$oOw#3np=r294>uA z$e<#2_2pB$+UEs-TQ(CSI+N~gdW~ZD`ZHTnvV2C}pIs-e>-GUi87UX= z{=_&v59QVJG_^2&o3U9$;YL&Ag{Cj53N$u6u*n{!;!vi8n$JgG`Sl|#xeiX!#!;XsoI?#0BW2!lUMPzq zV%nz~r|N<)UtJcoiVy%y0NEp5qrOk#Jm@PTg8@252O?0`u3d|Z7~g@L$cM`ES6 zz69|tO+MZfz3{Nkr<^CAqob6Qw|3u4DzaLvRnPt^TwsOm-G;lqzRoKy<|lW4&0Mmu z*=B8iopi%04AEN06ownsql!|SFR}#>On|F{-X%?tfG{nF2gHC2RL66bT)$THCaDEh zen;n?N$kGa56noU7b{sR#15kjW7$_TjjTLHSNO<-( z@;VZTt3ct9Vv-Z!+tu!S#Y;h2H*6N(+yX5G(($}_rfccz$Uv%z?oxmjZTFQ_6357? z<3Cgu{8e3PzY{irWxOPYhZ5Jx^#^S|%9X;oWH}766!Qdc(*t<*B-g^~$*`^)5wR;LhP zOsfUyy{oIcBPAIA2yQyBp=w{ouOs%{kl?DCUg2;aU*v0o7o=av;}1IA(YLb}23*LK zgsP4y@Kyi96KF_$2Z^wcKH7ojjEhW9FVIj$2zT*PZA^%h!meCxM%z?WfqOiveCw?) zki9rZA?^anI4OfX%X&MWuafm+aopV-zd?31z1;^gZ5@oJD=F*vK_lQd+Pk5@omihq z&X6sq^aHXph=m?8JdXI*MQv@-7#Ay}G|gJ?r({D0w(n=3ys&UezxaddEkW|R%KD!B zqmE~LvBvmEoc3#D2dCPr*~qam#+lifjd<@DoD~>~5R|nS((BvX&3NyNB8`??KN0;; z>3`rGW{FNXmgO7n(O*7zDD`aPy1SDP;|q~Bcyr^9L#cE3_NNyHzv!vKpJ)VP1q9gP zsxn{`Qxzc~F%5331qSFTB_t*WBCp9}e`;{8#769cBO)Oyquw+(WBISxFd1myMp7|p z(cZ;KRknF~4XWVch4PVdFtbN+k6YRc%Fd~KzjX!(1_cK(Gyg>T?g~P%DSPVC>XIKH z-E?2_yC)x|9R4HVqe1nh;OfQS6IAmRhW(fhX&beT!2I^h*|WP2%LtYWM2SYtjkAl4 z-GYl1h2cZcK7OTD20`gWxNU}ES|2`egDdiU^WbVa zt!W+ccykLNKz)4Ty%$&o)xwRD?dp9SnOr(KIhAC`{#eSLwjFG#R|WNyTHa2UOC(?Qsco9tD)RFBT-{wzsc64fuPeIkniFXob~*Ih`%}gF41|RXOt2 zTdoIHSKGBd>7Lb+ZgsvwDzv6@lvewtKhXY>y9pyh+*UTUl;F&AtT^6dPS2HTUtCWs z+MkM=YuxUP06%m38nB}6Wkv&C78$*-uk%qByBS+RHP>d67=YVPE3L^H&dN^4x{4$l zP^Izu**@}@_Gvo6RglONT(g&IW4dn;{a3*(oM&gZxZ`uI!n0&IEg`4NqH%IF(0NLC zLI;8XAHNbfil1!qhPsHd8p`-;SnD?X;ckY~o=`HH(D7}fwxPi=Wv({Kcz9x^HDaz3 zt(jKvuvd*^6;Zx5wSb}T_G@uTmBCB%2(Z#aJiLX|TtnNPK$jqVa$59=;7G6OA`H&a zk@OFA8%VJMrF1(J`u>s3qVBYmfr2Dn6&6nHsgy8^5(; zWk-+SJq{qGkD*;pG@MnufPsynPZdP3nU%OJ6KmSLs;8->5UvJMg*zY4vWW2mRPHo} zqr8%eTJodpqf#mCuJOv2XWVC*BW+PpfiRvd<FLxhmh>Hkg-a3Fm3hgn)S^@?_`iZu zwwf!sR_)jT*c01OSFajuYj8JP#fkd{yJ5ShykhFova7lRm|^PT_Fl96TB&8E1w|Zh3R#UAh|<_k zw=FSx>sD86)E&B%e^!LwHGnt}zX39CZ-R^3;TibCYP&6fnmzTkF1%upPJM4R=bHDx z`=*Q^)RuEgu+vC!g-vjd)dSo)pK$*E8Wr@Ph@WG1FO0IBO)6ryubl1{`0p&H?P^Ry zG~2ZO%VQ@EfmM1y*VO$~M|c-TQZ?bmtDFN|a;l&vPV>UKvR1ai*SftnNDi7mcby1) znRh^TTJCipx{^JpX*gyS{N%!ESr~nm@?$_KRhKb2TyC|nRsH%A>`|hm>s~Ib!Wd&L ze71c|yDVODNi(|-?2{BLQ|}XuMC;ZTtUzIkNo-YHf^Gb_9&RvciAF?LM&UT{JrS$| z*mDDqG;J6g2KYF{OQ3OA99i!&FKXE?Yl>!uwoFK**C*Z#d|yyXm~cK^^4H#KpXoC? zIog;j2BC9yv9w8nU*fkgnIBsesl>-xF;W{cQX~$#BN9p5mQFPXNW+Dx(eM6h z6Sh9sPuS7crA0KoxLj34|QDA#Et$1%jJBASRhS`Wy>N#lB(-zc4oEV$; zg^uo>46QKzg-rgVjuzAJhg#tj!K4?@VFzjXxOMCkbIppQlUV7F@MN|JGvS<%@$A0% z&S-<|lbQ|5pVyO&byqM&h;>o4zl!n~1xEPt&T&bYVnL?JH;BJ`&6do8hPW7$SH5ss z`&P0$Gr>F`kjTnCzzpHQhxo}zvlBUOW(60f;W)blfPT?V6hKE8Lx`K-K*pC+KtakW zn>`!(*O28iml^Y6u-VGTuF{V&jOo1$E36cL*$Z5&%fwum zt1?wlC!3=Lopx?bc!l5x0SRY%y1t)XhON=t8{WI>y4^SW@($KUKCy!XjT52#9InFzfXWg5oZ>DfbHV0dl6<2WJbkT+KW2AyJQyXU@gwW$$ghEkc_9|eeSI+a^B~`|SuH|k4PqE+3h0aK3bc z_$7?j>+v^qa&~{~)-Bf7m$HT?;tYFGwDhp3A)uJPjScGCeG?Oa zLwvsD`OLd-J6Z_2M$7Y<{4R%E?RY69D6jE2PL^hBsM4kjqExdEepoxnz{Lgj{rmSW z*Hr`OBYP&c_dn6*Xa}rcA0=&mP5820-5XligH&C=P7e!(TL^XePZ!!DLEaMt1cuc` zC@_LA#@B*(#hy2$%jYx5($X0=PHLM2U5nEX-f9)w2PWdHxq2~DpH`Mhh! z0Tq}dS-U_!g4`9BHx6g_^XkfwTAa@Mi%#F@m$bBPj~7_9+K>%FUZrDcH+@W=i*4IQ zS!>5)vnq;$^5Fdy#!F6mxO!~Zk>XWetGpmnAAr8i&%~L}^Ptl7vIrT`%|6U*pAxt2 z#P5xO_h-(UkL3UL$eK|89-&J^o`!eENtMKcm*o6!EB5z;ie7$QT?SBi6<0w=nh;8K>F4LB{mI$I zNSeS|tYp0)$S0Jo+vWABbnA9BIkW?>9D?9%OfXx&8400f;8jf z3c)KpL=>d$`wKO8My-(BTLnVCwV+WQMUdOleuuuk{`t(i(w4uaa0;nDCsuO;2@=Hq zKosWe8w8l`U-)t?Bm_FIu~`%#7055*&C582cPfU8hGf42zLd?F@9*|Vw>r@HCZk;Z zyxI)7`{c#lMRdS2JNHdQTMs`;AP%valTBP7IBkXuv>8^2AT%H7q+5>pbqGFRPvX^@ zx!?B7`@}H<6c1mGz2Y>G^np2;g>^2buFdA)DQ_?@eOmIN8I&nuKUd? z5ad7y*+9hazrZG8l52d1vRT6`rzxU2>f9)Y44G5QKAJSav(|?cF(%xh2W8QkKK#VZ z_uE2`kfi|OsH`lahWPui-BFDer;`A^rB!Edup`r#OIkpaHEtj#IXw-WkQv+N~qbm1H+X(FH<@F2X8IGB8m{zywKgpxb z94hmueF&MEw+RIK4^&1|*v(i=xS6B^B~1A2l<}p!P1O4L(wn>F(ko)N_2DALg}nf< z9huq`r3j^{9EuDP&4?ONHJwG&-mH3x3R=yDgS9BksN1QX{wtb5HAlSt*w$#S{jW37 zHnM!-TIk?J(n)MnqUu_Zk{ei-f zc*@FCkuXLoZ6=^u7la;3D$nATkLMLbLnG_kc)C^7qL8C|ID@W8ve@2v!=|#ac%ZZL zp+KUB9NHeWz2ivf(f-k287lxnLOv?lx_o&~f) zgkinZ_`DaevZOKRqT{k1HXW`!{s&+D?dQLN27Nb(`d(05WGMr20&G3ouvE0-JjhjW zMuD7Jx=ay6RWR9NkyG#|knS==Z@&q34>@GB?xOpZV&2^L3vT{Rn25zPC@1-@R%|A{ z?8Y8ik{ry;jh?M;5Cg(eJgySaTAf8_;A%3iMBpppW*MYz+)qQP=8nY|B^e~ZHFYD= z7z1ewsnuf~4H1se$_q*LlWy~F_B*Vk{?9rNnoHAYHuX&KNrAgM6^YD6-C*ywsWNb- z{O1ZhPt5q+=%hjEH`Z{}wc7y3ashj8SlEP)(`Xp8ALbX?648a3?i(X4a3b>W*(GGj z{yByuqrxP@C=7;{{E7v3LlqZ{xaSNSpCc#&HL~+V_LamAKa4)_K{z0u@pU$XQ@n#g z-C3*0E+t3sml|eL^76$HNt2)!LPuDI`Y+%5rO%Q5d@+&bDiuF?7}jJ8 z5R=3KFvz0P8XU2M><%nX9}|nf&+R6ZpQ`E#kr#IwDa?g-{}ejNW+j9Zkd7Ha?3CtKv_dn?8Ml z$Q+giF$~*9tLVx8cVfS+aP^uGBVf0j-;5`93I{08;T*f>IevwpQBfGRO%1l~~ukW(~BwY#^8ile@4E)*%p7mGC zKH>_&K0tvRn%mGotg4FrPFEUAA=4-S`A1Fqo%57=@}{^keEOXp6!`DF<9C+#Agn*b z#c1lT=!%fM*V8iRPEP`gLLj;`kK|j76Q>O9UkvA-{zG*zLvZfHxqKbrWKl!+ou(uq zb!vuke2ajM5EM&OT1{0QGG*Cz0NxcR{TZtacyK+R%-5ZsBo~@d0fH>!(v%DSCk-Dvk=F zo##Jc$u=tyaMGO167Jq99xxnI{X5Jc8JjDF=GUKMiaymD)?vXEKl>KSK(QnCw0b_E$ol^17k%;!z~}Z|7iS3 zyrC>!V|n%mv!XRpt>K0lYm|0|(hq(Ww#?rQ_fMInbGiC19%og#&dBkl72QN6vd9(0 zP5&mPK%h-rnf>@3Hbx{ z)L0z)_INqk^XV?{`*&CZrVfi^qIsX!KjPs|dQ=;~;CLtR(=F!%#hsTUHrJ6XJ>%#; zpk7mE0u|GsRZq+9QM8cUoqcQYi5Js$6UN!_>fy+doa5VIqNbSq8{5tRpK)5hz3q^@ zDh$+jz{Vy*RqV}F!p?(q2C8)?nA?`{3@ zcXY5(ulUCsLz2s>8qUv*f6DwrgmYl;uaZRS^nN*wn#a`>p9ziqI9}TOTpzK1n|c%1 z(H`kolwwmgr|~iwKS|g!NN4YDR$%XWZ_my39>#p7H;;PQ}2NjYtb0b*6S%cTtd9=YD!rfN(KY9R}k}OYZY(Zd>|o47CY@V z{`G$#v48&VL4qsVfa`qT+5=<-MxU*~;aS6lssmlAeC!b7!3bDwci3mY!`fX&?Z1jT zY#;Pys<^i+ZwYu5T08oeBb7J;s%?3wWsk^>2~f_xFzk+F>gm*h_Oe`FEdqDUiWb}` zJRs=a=R1`hM&pe<7dbYai0ead=04pJoj#K+BqSu}C+V5F&(m&6o##@70s;bKVZeve zL8v2FA*-`wgCsV(n&e$5+&G}Ve#{nK7ys+ek%}}*FIPuZtZqd7srB{YdiN@$3jll@ z;I6l@t<66&ol@a1Lq;+ELa67aRIcEyk#LcLP|9iUkMKl9Wh1@lScxs?XM@K*q0GSh zC}nK338<9O3uUSen(E_>lJW(rarvqy^ViKECHt9LPHF(p+mlC*K@Q&j>~90;bh>5C`Bw8Qsmv-mYbizUVc}y z2gcBO^rdj;(cl9E$uqZQ)fb?#)!%WOKyHoH-!=#HH+{yw35ZXu(i%%p$Y-{@<`&t_ zAaJ@N)6yFS4@BFCd1$7>Xgm(;Q6A8Y$kJ=o$4u-RO7lI zZb;b^nTma*_Jk$un2ho9KL&-e$V5 zWddqr3ZHF%_W_VwAd1q4u#rjLe5UxdoBr;tC-#+%u zCcE!0o?vZg0eya7a3HC^&}a9>c#OX^lpsG|(N20*N%!dmQSkck3RR?!g!_){DH7uJ zK7p7DB{a<=TA)Tx6pVWKxu0eDd>yywqso12i=i#N2HxbAQ}g0N6R5&kMFpg($42BY zKB+@kVggQHq-$>{G;JLexT*H4ZBknD>e7K5++5K{mE88$wF+`$1@D4rM`S=n3EdmF zZMAV&lQMICb_C*^@$`T|lTxBfu}(iHnm@EG2pZCaDD+nT0RoVO<0NYUy44gr<)e-^ zjVKGNS>4r#Bmf81mHF7-Pu;gBUeP*ulH<9@-@y7-{_3Ak&}LLMW-b zFo{`?O*3=1?&Ck9Jcv)FVlo}#T#ELYQCzXc=qiWWI zNx$<1S;CR)C|g+5m^%bEjr@>Bm`>FCk8tRJ;-nM|C|vlp9T{<@pPJw0)+xTnC}V!! zV_g`!`69HMnmwG`a9ZLXGUn5MKnDYcaA<2R8)6AYcQEH7ZjXCY@X?vRs3mWAnYZ@i zJ8-=Gi5mMO^WucUdw3RQr>`-LA^f0XzfY76f_4p6nJ0X%?)bJ6v&cyaBrNPcm<=jv zWfb8H7O`3JFX!QW-M<2219Fka2FKN{W&Vwp^&!BEBT38Y!m!-C`l%8|YY?Vc4T5|- z9Hdbdj}?5hLa|Fw=R+1B>EPLiH{M`clp9}indjpu%`Rm0SGQ~w{NsrQY`3dpv|(-yQ>f$Di)EL^g-s^%2oX9kk z{G^mvf}G!rfy~_0ka9e>-{m(6TKw8jieUaOc@wGLd}HK=5hy4Vf|YhoXtjQO_JM92 zC(YgaFA+hf2Pjb}(HY4-9lbKTptsTzHOFNzOgC-I4JPYDy0a}H`MR;hu*^FWpd{LK zrR$(pP7xS;ke6Y`E-aB?K7?N)@|{+m92@9fYAs);xltdympX`JP7z`h552YVqo&q4 zgc+zA#`yZS%!dKS6Lq=fZGY-np?1fh8ON{=qffAnV1ZcQgjf$r8U({FEOi-|9J9ki z8bHSc$3p6B({|oy*@?_U$b1im0a;(gAX{&-de=l(rnY1rm38-4Tm*Cjx;vRTn@I50 zyj44c>HrQ)iuDwu57+fnZ*)RO(ji9!u`o$Dt{ug1=&-{BM1BTGd^cjAB8xo2FSr;< z{JGh_v1nA@2@|LRO)?KebA!~kU{2~WLsYj?HnglZDv|Pm`k$jX*e49m)AwQ_%KN4^ zhn*wx`_dE^ilbES5apGSppC_s)6)G3;{Y|bk9ujx#HbZv@cWkVp2VrCDtl4n?28dh zau}VW*q!OQH+>`JG&M~gr9CBuSO~=?NTkqcpo~@>dD1-eCBbc}pT&?h@!sFQR|e7H zHl=3x=km$18IQIM1cmTF2XihM0>x34XAb2%rGdC;+ zHH`<3cM^W(!c#RLQWI>397~$eLX-&R@_+$ZRC5XC{j8E}C}q*eS`O-}4j2cvYN%ui zHKMdyXuu5fFXYB6MUtfvHs-6fp6Esmb!%VhqyKjdKqFDRJ+>j*X~)t`!{F9>D3ND@ z-*F6#$Z_wcLI8Oe23&|e?l2f}B{HtB8kH6XrsCUeLr8cwJxwfPAzX*3B61**fuocB z=kR3*$D)#`Xw2=ivu)$LH3eFj05XB^QV(YRL@eB%Q_}zZFzC0bu%wmQWX?UZy05t!+-rY=nOuQL0|$XA*9*iRhVO# zTy*TgEhv!O4g`JP^PpN^5DT%DWjk}c70}C)g0H%?$;?!{c;VtwcpRIIyn8ni9H)>J z)vshFHfC@-n#YyKRi$85H@E6?rs*D}{rA*CN1GT+IIr4t(!B+BHL;1rJlB!P^h8XrKCt&7JmwbeQ1wg^3y0FcTY)Fi~42V zHUbH4MqxOR!F~-}ZI3}+o}LY~EC>=sZ)Qyt@RGB#vr~ZLO?23Iqd55M?ihsBv@G=g zT6)3u*il~FH&oqkp#FRsnbb6sQkaU&>}york)Z_2@sVWSuzY9<{xsTMgGn5n z0)Jn@6soX^xw@V*ejYeTA$NJ*o}a%i6)GSY#l_sL8i z2g8@AcdhDPx6=bV+Uw6r7OTzj??`XA;IO>i^0u-2-;fV;x!558n%cq-u)R5IoX3ho zSHW?1n7MRGus;!C;?R*`jI8WG&5a-poq9>u6FcS$qQ~f2Dh_=r{p7i_wroRB*<*jqDY-VeN<(91V$kF@5UhvQRM3rmUSX| zuKYEmLQGizK&!ce*9m!@V8N3S<7)Zw;pPi$tqN^E8jrUU7z7a=-mM(%AMYrEc<-$0USo;qBivpqt{IHUUoK8fT!q? z=7l&&>OQlt38pHdyk+E}uNcZ5M!sV1&AT%-=>-qZj_4Ma(%(SXy01v-h>wZ?!(I~h zA|xrNA6KtX!vQqU+&uN_>gwgN-m8q))7TwuZ0%-KWIx8->})7$cx1IfkA&-O6VfHh zw+DCsU&l8`yP#5`1Z$6@<@31A0DXc4MXzT=Wf%kJou6nAH#Or!IsU;$ug|WRr_QUS zAb(l5CXhi#K|{m*f;xH59@Rnfffk(Dd6FAqsL`Jt3LKqA0Q^Rj;SBy~Jvl9TR+4k~ zljBP4ZGU!YI&nhgfO2&%AB}n$a+k{9PYm}spq6yG+~phsQ?!$VmzoTAeGkT7<4a?v zCoPZ3M^vUHg^w5K}u5CmJ;V$biQwIv`UncD0My3c=Uv?nqtdL#9sG$`hV@c^LJfs7dG11wj0~F z&BnITxUrqaHXGZGZQDs>voX)^`#f*k_xr{;|G+u>hdtI9$-4Ji^Pax1IpL(5fRQc5|V45a#iA-EJ;lN;F*>^;L&=7z=yKv^r3^sJ>`AytLB~iO?G-NrfOy zugt)eA4ragcr%IY3ZaR~7v(jdLny(8+?i7bk(qoS^^j;cc($ZXI2 z0x3)^zt(e^$?Yv@oXK`W8^O$I-EmCke-u25?}Jd&iHUS?%Jbe~JvGe`nS+?T02`jf z@XwHV2c%$~2=sTQP6=j0VCI*UCcTx8cMTfh#80!GD zv$lT1F_d2a&zIpgJP8cFcnxK4$S{G$2Qoi}MpEUiW7K)jVSf>`gdW1B^4S-=`A|=Ij zz8nsIyLhlaI`#FzGQjCuPhLi1iFhiBR)>n;`*JW&g2WimklSG~`z7{QOW0b| zajaU;N2WD$O01so{HCOh95rqG=F^o{3}Ni-M6J(mbv)OX#Cg5A1TR+s*HjrWA!lkD zw&LJ_6UzY^5)_xyLr?&F3hboUGQ^6Jq^3^r*@kLBs1|-3MpJTy(YCyd(RUs-fXNri zX#5jT{bX4Rz7>Mp-duPkLohj4?Q;roG7VI&JSGFtnaOAm<~~Jjv20Mf`?TUrRI{;W z7XHEN910RFuS^DR!oY2rsJI;1Nq*cRFQGHcK`4kbMj)bTv# zD+Vgsybj>t{j-1qHQvK@@m|>e68+uZE<8}1Frb00 z`XQKl;<(1i3uOQ5Su#V0K+eMvleNIjX^0T@6PXu^s;+e~oLaDjH1JH2L39dZ@On9z zElxn>?Q%b-0{u9irNx(~v?*n9$9}Gr%c6;Za%Wzha{)rt&pmZxZ<#I~YMRxq;tRs0 z>a6xzo`88cF^H<=&0E1ICeuAZGjnr+i3lDH7^h^82!|C{X;xLS3yo}TMq@OI#DL5r zGVHM|g=uV*YPsR;{Cv4xf?p^zag7CKH0TgwDHtO6|yO{q3bpL+mq(L3l62g)wZ~?p8 zrmz=x)pobq?wl^r-5Er`Hty`*OHXqA#il^WDJ1}J+VnWFWWn9UIoZOGx|&6C^C!cC zGY^)1Vzv*HTNxVpiR>SvOQMhtxWV#gi)y2a?$zEikqMgqn_h#bJ!@*XJo$z3h=Jyx z^(k2Wgb)&7`jcB;hL_UAoYQ1psZ$qfuQdkkWmsQ4294qv{XwBohAPdJIN&D##5n>s zh?jhRS#n>USOstLcxE}~()i395z=on?p?VG(9##VeTNr%KYwjtOrHhw%0zw>`K}(q zk1-%8A>b4E<4Ee=clSM1CM6T<4{dUwp}zN+yQ9f(C-T-zbBLhpO5n~l>02lOC7~Ho zGGFjfgbB=qGL|jMXzr$3QjnC+<;A8vOog0M4kEqKU%6_>xu!ad(7f}_E1IeCnH>Gt znXlup$+>Bpr07@+d3 zfkJ{MyL8Y98SB2ctM5$>(*IXT5;{N5| z{wHot@cR&?j+HY*@_+szLflu#num+$FOTk5Fs8r=%yrcNmiTST&}*4x*Cj6i}QK**Ik)LZEWcBiLvuhVXgntAh;eI02=6 z$`Mog`y`?AwLr6)SI~}Kcj-POd(Um~@+u6k;mmn&ugFMWyU50eZmWC{Q7n6X0tyu2-ToY^AC)&1tsE(VxT=eL!W# z6}*LCmsGy8F#feKF#>|XF`~6s0*daMAhl)mlO%C@@sr0op#}~BqTTWr8IGX{rjKyE=v;10fn`#oD}IE@kj3NVEt_+Tj4KfG0&jffuF*{;sco4EWUzjrICEOtQKi$A&HppsHx1QzKP*a43+$d z9P8@S`Gv1wFVFGg0~ND*jL?{4`*$r(;P=NapW)yGwvmCzbYnYniMOLVm(gpJ(Otjy zr(Dmc^GPW)@et4ZEL&_wXXMeYW1CwSADtC8cc##DPf>`kRMs1kxGqmdOnv~N z#xz!3#geU`@3A$cR52$h%nH$USJ-zR9|qC`I>_OujfCQZvBWrH#v5tL!1AjnWAXN0XtYRj0oI|WPKEybXhzI??9$5#q` zoY+bFAlo;j^GIhP!N#ule312I_F`ftPLqNqdL0tX9K4jS5j`i)>&{iN zuMqGlUecmL@)W3Sz%sTZD0cV7(we-ccNy-jrFWN+}3UaXILB#U@ul zjzaPn(Bn!;h${(rH_XN|w5Z{YSEeYEE44>y)q8cy5L&`rA@W7Ko^Jq3J`Rg>B+&u} zrNEZ2&tq=7CT2bGjzSn5FZIlfBqsDCJT1Tqg|U7H1VFs@a^LrOA_}91kj^bpHlivzXb59HUPJL0Tnsk82fdjm;w;SyzQZ&bEMe0Ga_7dGtK09N3dWxq=NEkKB$H z*>AXd;C>~aF>>5S z)mWtCC9x{!&=&y5EoRq9qR}Oyt*Dp@$FaP{D!bbGk>g#7;E5{FsXWH}I#zc*RyT=V zwQ7>AV;am@soRXZXDXVm=Yjc`{^NSx5_y+wn`*WhVPLb}paG0o{C;%dTBKJjcu{li z5pCn=ZGX2WvKn^1!uRcSd({5V?m?YBD6H{xk=?#QR;9i3y`2ifo(c3(sVHJ0%bY{c=J zT_x2U350<&w1MpJx(Wl^&QGg{^Iy9%d_!F?>|rH&{2vYMCE*aHr3lt~f1+6z#|x~* zD!7Dj8`rd}1UH>01gnga&di!j94w{v2=YXguu%d0dhYY@0Y=RHnsuLw4p><9Csdh560&T~%^8TK`V#_*(9`IG`ELU) zxH32Qen9ltBa>CVRUaY*Pw+cl?o9hOo2Uj___sW9G3(KUL!}-I&OWmOd6?2^Z9Vrb z?L+)!dVE7ZJZUOmr(|cxCApUa`5K*lz3nzC*r))ga5o1R#o%fhK$ktKp5wM_c| zu*&utcR300>cc^yv1d(odJ0_lanAp)@I?mR67)qnXGWxN&iu6^Kt?h z053KB(n(Prd z=AD}qBD=P<(Ag0~jeW1TM zLLjZHY*B2?a*n#y8J;hxOuQ|e8ISFI8;n2HdYK!o!A?Vqv4+Br9PBb%<-jd|7i(qc zuU>)U3WGbBdF%D~x(pf9A-5RxLDruFQeX>(-|7im-iShJMX=!Z(F~5B=IOKOC1ES9 zxg60{#rxuJ52*_oVrdnkD^$T>yBPLE3y>vYrM3ONNuGjMNXva8XQ>oJ3+PeV3&<~5 zPO8}PD+zhR?09u5C2F(i*U>ScpKDiFRw9yR&JN*D;W_%`2xmhVpGRvt{=0FkBvkb4S<3 z9I{|uG`*+nqsFc86MVjt*f@1XU+b`faYpg#!6*{+L(q-_jLLM3dN+t?$a}~d* zaAf=b}-pNSmZ_gGG6)E!rk?%5T;x2-=-yCK03zB*a{)G0ZvC>sw#(G3b2Zi=Dy% zCH*8{$!XIi^RPw5Xu>{il82a%NQUw!(gOI;0!NU=n9v2AI(yLhr%cD1;bFo)x?stG zbGJ||`B9c6^`<-M1~TG4U_|{WEqSlo=x4blp=Qhr(wxO1zZmEF`g@skz94lv1pL_l zdgH&=7tnwz#;o!djx9VH9PvzO5?dh8PiD%Y~|Qu&#~G(mf^mn%ceCY%Uz_hnANGv z>9#J9v8k|;xyVn-Cy=oZIz?N1oqgf)KsDT%-s1e9r71xCgtiM|MHBnKh0|nqn!fdZ zmn3evjP-A+rf-wmTB=7mCv@x(kYzgJ%^SWQEci*BeZ^2>4@&Kzh+VR6yY_=O`pJzd z&#Y~+dnvQ<^XSUYq!T?sXht8p2-4pn-+wMB1iCmnKWh3fL4o^=36;<>U4!(Q4wooO8UY^w z2ggkcVx>UZv6yn@s|hy!dz1{AY!|jfk`qjBf_jt`fK0FkofdnW#uJ~bJfL=DNCY3 zcMmDmwi?)r6<{RrCeUhA!^w>g3o?;YX9;Tp+7&yUIzW%}kO??QyPvqQ)*Tgy*71;2 zS4oSqymhfBb!ofJGgottj|z zNRrUfZ$rU4P#Q3V&URg~cQ;XT zV4-r(Jp5W;Czc;oaylkNI0&2$8+nL4Mnsvi-bApyU8X?39b>3>X1eL&BpUwqZ1&>fkC8SM#aF z)=E$jkTLcFo<8W?-p1wkyWSB?DM(VCkQXIPde`~E_(uW%M*#@}ANwj^BuSwk(u4`8 z_A`c9y@!3+w{JFja@P!~t)?y36wl&EOlWW>#~J541udLgytL{* z*Ig|##%%>Tvfmq}-9bb{F6MLvN6Sxf&xaG=lo<^S?b_#md#l)F+WbupPSCDD;v;jU zKakD~@mg@R(KEc%`iKd#xn-QWe3Qtk7B!oAxkFslkAmH>Ww{@K2)WPB@w>t4xZBHv zJr*8b=2^;;_a9Z2!UYxR=B;=y}+$=XD6M3hzw6^U++2m9&Ue{Y;X5aeMSUFK*5 zd{$-lXL$+g7CQ`jC@5TYL~}$Xyh;XcS0YmO3^X>!mMs357)K0)?|ISqv$idmk$~T=gdpmCl##)nvbEdta52?=Cd8tH9&(kPJRO?Rwn5mc4WnyzmVm?wt1- zA$bv9|IB`8?^}g`{M!1~bd!CU-1)ZJx_8#JK{TpHliV8uKPpXclL(kmt0tGpLyDo} zNw_xm-JTJ=xdaI$6zfke(-%|@2k2I$-O_m}v}lyUc&d#8E4H-8$-?c{pH(!#bR)R~ z!P>w|0KNY4E5mH)&7}~Uj?OqyGD{%iri^IjYTsT8N}-suz&Ej@EM~n;m_mrp5$R0N zSeYTY!Mr-AFW=nMFs~QsgCKiVqn)iyJ}Q#KVRkHiHx05@@5>P%?4v zx9rZ)XTp!Nb{PB*Q^F3P-h*b==P5HY@oH@^fH<8Vig7ueM2>6L%t3|9E8#C(^#9JO zb5wx(A9j}9DEcIziJO9npmCZ4!Dj?Z%s5tt&SU}${_M)q?2AG&;Cve4-C7ZX{^@U~ z)G@5^1B7iBrKY}8Gg_G|`i@rzsi^NGLX zg56g%RZ8~U=c^l}5QK>u+y`X1XW?gq~mSE}U zGGBgBu6m5zdTYd*qm-c5-AW8%3GVMJZ?V1VpGDh-30Zsc@o0hi)lKT!4s2}ryd&%) z?tk$*lGckwA;0y4O?ihjK#LUn*3_gHT>U}Z8`7{icj7f%gy=df+FPXKVb8~pot6v& z0WSe01b#Rg|0N7ijQZ)z#X5O9d#C<>BJWin%A={P%pWT@FDmeST)3FMYN1xs9WpWR zOj*~^uvp2#I>@N2;joqt?iao0oXm4GL@dV9ew~;+448;bN=qy2uvex_$?NF83&*}m zA|8#OSRkEjDg_lnd<`f?9fRebV{8`g6-FNPWYXcZf4e4Xx5eWdw%{zF|E}{Hygc)I~?Z|ee5v0zCy2h^8|#RpQ({9xZ<7M7q$B& zojXz7z&D|LAGf-dp-@{&=5^2I#Xm!}0Ow7>jmo1mvU@yPy+ z{7J;eZ``%s7Kq}6svltUrA72#F0v{6=!fTgbfuJS0Yf|gYgGN1kQAoT2B@Z}!pj*( zHZ7+&eioF5(-u|p+D=+OuHLxUeQQGa62e{?ZL-vi#? zis{$2A2iAj+!|<*s)lVvIjK0gnKse(g_VK3m@Z?e!y8lP+70eD?^>~xtp6;gKY%p! z(vfe*z=s3B-~*luYrBp3Y;aet6J~^l;}G z@*jw3-n48Jo-*krDR2kWnucuMuSgk4p_CE)ZWj4-T!cu7w&*v={g@Y&RhW&6|C4p_&)QfzJ8K_ig(?7e!&H4S=C^xac&y4+jnb8(;q5NdaAd&kv)Vtx{~>+ zF%*F=S?wQ?*boVlk|#BY{q!|i(CkxL_E5x|hM3z|_tq-+IA&x*`%O!o=Pm|^hA39p z-=wQ4`+U<3s6cP5U@*?HtVo?U9+eooct{i+K^4it`@-htwOf}y{==Y%QGy_gWS#@)$Zgt@GA+_*8uT82!BY_TIwDER)W}c|qX>Y#Rt~s=uITO6wm6JfoG(aZwdscwM4ai9~?Y45i-#GU)-Y zG1xer-sQv39-VZtK*+wqZ|iNn-x}xBgDbzrwrPFdM0GItu(wOH_dEY5zzHRDo9!Z> zDboq_zG1c5EC0I0T^Qx3&z}&7MrLadBDLg@GMM#KgqM zIXB6n9@7MDbLthAYE8;uv#^=Yx6QIWvC_Pyj{H+jj~qwxLbWnMcrre*0l=wjV1L{M49r*bTs6@FK=NACeUvu3tJXSf?4F>qRVhN|m5VolL@g40HA{ARDUTo5V3tMsbN^1lOdKM~ zBXYRt;;r2C$He_mWxI#TV3f}FK)P*$D-T^jfN@?9oFR$qp=khQY53~YBN23>cg zw6MKkU=ug84;6y2i4+dBOc!ZsJxFmR<3C31X%#=E;^Mkfw>34{IeV~5gH+x8xpC-T zew3&N%2B`u^Dy*0&P`Q)#6erbJ1YEaL{L58kTkt{FQ-;33#KkhZ;VV#ksWz$$B`lP04SI zVdJK$#MU@CII>tPrVRx>Tcd3VkHewcac`~Tfn4Zl;8-?4K~Kdx zWT|inydWg#9)Md%cr>64OJ+YD=hG^CcAuo^6LEnDevrk(cX%aXK z=!@w8SjcBXYg23$P|^g=2S-~mZd%r(oNb5~puXAp?kjSs<9>F#xb=*j(D~ZhYGQ7# zP}y>=;xHRo#eVeo8)2g04ZPwbhyh7W6t#W(0fec@EDA8iweX#39y<_(kP}6E_5$xT zy)=yRvA%O=d7jei@T-%vb1Q=X;I}jmKv4w1tzi@e=CV10L)1ISa%}Y6%_m4sq*fcE zdeLz|qBg$D6TK$l+Fr@kt#z$=*tW$c(FkZ!F(jlH*D2a; z2CNeAiY>U{&YP=ArpVX}a^x;~h}u^^36?uI68aYjeS)0PZ=q>G6vdf~*=Jr;z`8BQ zT9qIOhC}mP!JpO5Q225??Y>mSJiB?LZ15c*@(Qf&Wb3CEl zR5Pm3rRn^3hCaCvAzx=D_@k?3%6W7rV-Lb_*BJ`RTu=1fhR^Nz8y2OJ8s1#R5xFE5L%4Rx+LnSk)%>4#82mzDWimrcIM`*+tarw$*CEJ`$>d`!zFX4@z%zNVv~ zgS{0iz08uY8^)H@tIHu?klgJhtTZ}pkS;D$ZQY*6t|D(&*?b7{D=vH7n@?#+3H&dI z`C_nu{xt;-w<>BQ6FRMgE8Y;9*VQkxX8cAXW3){h;Pe|n&Bsexq_t3Z=m!U(+9BI6^8)P%79*J=ds30HO&9Z1zvTUp2RpJHn)e!?L4av%O7+ypPS9P)?job>ne=898`+gh5m$e5`QU_^i&# z#+Guogn&jy@6$ipg?#bcg+gWGezLj&?!i?*{bHEsQ~?BBx%vLMt5;B$h6#BD?)IK} z;kbz0-LC*o@YMcJDbIIXuYI3pwW~z%UW_!M*73vz3l%vEQfy^r#vPp1f#zeYls|As zvC)6^#(goTXYTfpE53`_rb#<4I|Bv|1-Y+t$!6JekhMx7iR<#E-U!k-0BhkP&8kwc zF{kZdVlxR7bd&$(wa8>T5A3qAodo9xqsph}@e9vYPxANEULW&d%tJ&K7mfAuAsO}v zoZqa)gf!Km?V-h8D0-9Hk-#T2|J?%Y%qNH6PrQ1?ky2wSqStYDc_oD^75s#gnQ%VCfsji2giyLjL`1dT{CV6>J|mn0j&V+>6M_-eh6TtO z%kKkgTg^pEwp5eK_we9#3J&qP*kI+t`@dHKx!%p{j zcHglW+Q@1(!H{{YlEPk)XYs0GW^e`XGBK(3aVYACsZNJ=>diJ(IJ{2=zik$lw=39w zNXe3wB>hdUvm4$%U1s}R`Iry}c9G+_a$peSi!86(R}9(&=EX`@!wH%Xek{l1pX|+o z_XSZ8L1zFR&|Ak40?*5g&E;J-4#rcd7qy+yW*`cnzABlkHy8#X!Ew1r3N!Qgv9H+$ zEF<*p6!61ymCO4OsaC2-kqfSi;U4DUXr4oC`j7_(K#S?Hh=j<9eS^WLzdiIkqJCeT z6ZR=*X**U?OCikUnv)ONvT(h4cTGl8##2`pK8T|YF|!^&Rw>khA#}Kf5ZP%L46!)o zVR`NTbdklEstAwGBKOX+#54xPw@rmf=$>GL5}e$^xduo*bwTXnU2>E($pyfJyo?pS zFH~EAca~>3Cf`r7aJ5aZa9p%GM&^iY?YHI$T}_M2rxA2FwLQw3e*flvJ$9*Jjgvrd z?75iz3RTNN(WE77C~=BBM(&As$DoWf&_Ma6_$V*vGUv~MRzjxl7wVbyFc}T5Do?p& zx-?c2;D8J+#lgZCjq)KB6F7`Ob@cKa_!kmXwPb2 zP1FyMsrvZR_g@ZP`By4h1hlIjBh49Pw|p3Z45lmfRRb#P8Ku{@dvoKQrbWDdI)DpW z*^%w_`mHCAP=@%HZkrM_ZL>UdpW)sFO;k-~j9iHiDBB1_h zfmzjYOjw&$Pl?7hEc5=ngBlc^IJkfmE?+ zE?Y?UY`MNUg{LSdsq?3J{)wf9%&X&aq4Eh>W)=;qdjoG@DXldTnpcj)ZJH;wvFIsf zt1^7i6O)(7&F|~1@ma*|D(PvHRlKt`gKfVLi2=^e$z}!UTP<7Z}1D9b7t9o61F2A98Q_N7oO^?1zIP1)>BTInQ=$F+d6z!z;y zDPc<~{uMC7e!%=ctmv02(Pi$N*WW8+-91*U#DdNE)kC)B!oR~%$BV>F!U)-GhV|_0 z?$O5C^-!*?m_q7jlUP|DaHKDSi&Ev~#bva@Y0Vf|dm=T84V5uE-w4xT$?}|=tw5HP zHUN*A41N!oIcTIe*(Q$}8O(~fJ4s|$NKW2MVdm9J4HHdK`+DqJmZB3gl z@+)s$7wh|$)!LDc>Ai`QUiVdETnxLwysokjH)x7dWeTDO!>NM7-}0L6N|ON10Mc?0 z{5D*4Tp0#OX@xdO-NJ1G-%4GM=cU;;jT{u|HsWe{>a_XJ`bZskUt9}f(9p9btKX1x zKWj=Yf$^UM`#DrUNFGiqB1}_!AT=SApt-R!gU^u^eGYDy#p=KhuTY@Q{aoWYLSx** zW$MH-;=m1p0-N%M^5e?EN74;NY*m$P>eXn!7*OFEv_8Me2i2&-r($$f2v9$KU7>Tr ziGV}VV|g!BXVQ;2B{_(#9C)?4XBf%|_v?IHnHdPW+Oor)x#S$xkom|`SxM&sM_E?w zt8)b^&;~{#(YV9E2ZcGuP*aJYZj)o+{*N4WF&m&xdj3nly54`4%RiFpS4s{@bpQW1 z{6nt)&rpdXO<<&O>~sn8^KTU-k&_1~@}UZE9e^IaF=6IrT@YX^YJ~jZfn7~I@_+qj z4;pP`&^)AH0!Lh&{zr{FmJ$wM4FR(<)}1|3d8N zg#bu8&C0omQZoVn!(lSK*!xXpn%3vI)5VG6liraBBuD|zbq51w?~4)l&e@3H2IZq) zI5OZQp5lCkYRu1UOTb5wD0jtD%|weC>OR8V{?Ik!dQZ}CJ=ja~qcQ@Wj-e9xaKvwY zPfFOuX-L}5;SpKQ&Ao2L59N<)FzataQq|21wLeQ0oB9_O7yx|(daPZ8K}X~T)>+7w zZ2X9nP!Yg}7}EY61p$zHA!JF9wFOLgXV!v1GO9IKEw;km8N7`LHk!6==N>y}9s9$a z$$Vmlo3*U%I~>TcEi2mhHVWywt!!DVM<^0NE?wr;r}rN>OTB}>V@mO%;m7|5(g`@D zZ@sWH7nBb9H*TwhKvP4mEOx zq!w{h>dobB!BLsVK^pdro7CDuO(81F$(W+BQKbQlgI@jXEpohU`QkHM!`#>_O9oVeby}N^0=)lMI3@A;UxL z9O0LD{Am8__Vsow2`FPN7a=EKMuMvlf@{TyTY>psJvT-qyD|}# zCy>osPqzo4OOA3>4GOzIw>MHuw6RFl5X8uZR=8+uL(nG^M_2evT*A=T)Z+Gk9A(z{lx!Ms7Yj zE!J)#BZAQaahN}@Zrh3DN}a_{*laG<-5&OpY_;&|`L^!BLdm}x7h@-3)4b6zyx@QI zhiw7y5Pl$dS(=}$)&2vY8#t;ir@i=djdo!6>aGCiw|t$Z&3sj-SXv0J4Q8hq3|heh z2Sqg&&6%)39$>&s03q&fNg&BB>bU%g)O_1h?8Fje{NXCw$69&t>qw_9Zlcwq$lics zUM(!lmoZzYA;}EQ4Xn5-UPh`A`NOvM;e^Th-@*pCU=9M*f2CH55CZK)E_zYBU+=t@ z9>^*kwk>8MS*0oUJ6R-OI=zV}`pfmxkh=x)`hJ%{=tfCil7zN-2=0{cY@uApvwKhulA= z{}$s~_Lh{{fv!JAj(&#dX6>P!)jtwue_ED&LOF(1Ce2TT)t`sk;yv=KYNFw!N4*C> zOxa}*w58v{?((k|Dy6<3cnwwTV%7Ush;uf|jf!yHb_>2|U^`#xR{MHh9WdG}0=bzY zpr3SUWr3)3U8T#zrW=IQ*wI!G^A8TACVghaNp2E5?{^F+Gd3s^gDbLNc-Yf%Ze(KtzAJN`JOIrv=i^+|XI4 z3QJk5I8^4x)OXl$i?fJCRU@N9l`fh|nY6kWn6CYQ^-5O+kpLlEKAkwL%E0ry0Yp<; z<=SaQ86Sfkgwr(+w?`+KHRr!rx-nGXjg1?b5|ooqq@aqJVnukGPraqP{xxYw7zSFL zVjur(wcQh>UDVQ%k=`w72;t789(J7B7jnkQlH>GV@Hq}39mGA^1F_2=U9Yv*)LJ_j z8RP%zb3ZIO075!gFDF%-SI2n!oSijQl+@ILSA!i{0a(nnU><|F`9oN}T^VmH)1x&<`+#r4QRR$G;c-=VD$GV7$1h zaIF5Wjs4}){d)tNmU9$8T@vc-|AL}TAOR{bm|6M{Ea6|@3l?B%V4=@{zvjOe0{`zf zgfBPb(>t5~&GQ_*pi3CsylZBPCI2{x)pXij`To@!sDI#*yGR^(UNy6yOm*wr4haPa z_x(PNwjRIFBy+76(YGO4JzCb;{C;2NOy9Hk@f-JA?>%Awlkzn(kJKU=6vv|Jw>_H8 z?HQQ-At)BmDtVc4oxFHzqP1Q_q9~!NjT%i=NB@SxBXdvqc(NKU!XKXX1uaeZK+rc? z4kJ#;R#z!5X_-&-Rj7=F!XogGbsZ7vl1Ht1S4E#=UZ4nCifCsk$NcjiF#;9Hm*I35 z-W83>XV?H>zBLWX!2d-;4S<wM!lN#y`@8Y1I%=&3qGn5Y4?muA7gM zHvU^Ei#32xJLgXeSmE*kWBL}G=Ga#-J&sIJ+OIZVvHbn!|0enepu1VicPjvS@)wsp ze(rk=)(?BzvxfWsS?;4BmT1?ieBJXG?tH3|k%UBmTGY5gxAo*=&(_neni}D+8oaA| zz-A)->-~O#f=d7_lNi3sa^@N|%Oab4`nW9(tWnlj&S+69`TeirF%ktb;HaI1VWx3a z6hAvwmBxjW(w?F4Z;B1`RQOy79lwe8t=UjOnfCp~4t+#O~1 zgRzZ1Al;Guk5HhXh>CB=x*ZhA9&KZ)yX_;R|C)ik*FryQconvfx@dfM@> zZJjDvpMjft2q$~8e5$$RroAwZypxZI4gnxafJRL9D(zcRUinMuODk}o4G}devp+Yk zf8MtbE+lEz+zo0P!~S{yF?isOw-^^atqb0}a0YrTTkLdl{)u$&(r3%r)#&t6ynSjs zz^XGf8Oup_KbL>BH@83EK?t=QV_&oz*l@KrG@dr5;IyW&Ea3k|4?8YM)Z-I#odNEH ziZb@h^8n|%G5xed{F~?gC5cC&yDNeZl}$P)~`=?1=Z3x zN&r!1WBsfWPwf$Jl&bvl(Y)HeWgbT{08MZIkwsuE?DoqCw0CV3=LDN(eyXZw%(5|m| zMJ~on^&89pXiq>3YOOcY$o{@GuxgY0Gicj}@3U3(hoB|c)~aYxkI5 z#{edmjk#*H-m-xG6!X(=loak_<+xeX4%r zi#}V=z4N=`(q-M~cR*q;N-e#ERx?Eq6nfq1&NTbblQV7Gt2BQPqg0r4q)_(H8kfk$ zB6@vV!RH6}K`-diKNn)38)N}$3tV?o5h_9G%P(<3QkPGAV>oo{WZAfhUvIi|wz?;` z3i!*v({BMCna8MkC4auuIw`T&%vxV*S>^D^=%jV#r`hC-mZt}JJv{9%)VeKb{NC)1 zyhVxE=g$QduKgMx|JoiTAX~kWa}Qqq{+lZ6juN)k6sAJU^v6PZ!1Fx8maX`=3*L&| zpQ}0NtGoSqgA^c8>&+1@v`&r-xARGS;CF&Zk5$!z5q8TDfRCi8oJggRzW@IN+$ax` diff --git a/docs/login.png b/docs/login.png deleted file mode 100644 index 20d5caf9e4d4ee4f7d7053327496efe0029dae93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33635 zcmeFZXH-*b)GjJbR76k|1f_@qo30=r9Vya_AT>Z#IwABT(nUZ;L3(c@ozMv-Gz-1A zgccAf0Rn{H0^}@I+~1G;2WJFiaker+X z{69m)aE9#H{+Tl>L^uBKYZLwR=N#fQXF_bwkp4Nx2>3qv7YlrVtN(nHq!ayn$8_S; zvx(!=N&fD;oLu()vb-1Yb>30Q5co}Z<>W(Tl{Nr$LFSCI{KF?Nh?ddhPw!alx2|*4 zVA{M7w1t;iGu162v<`C3@$Mzy~9b<=c(R++S;Mliea!Ar(`q$uFbeut&rJENFe*pb92!zPLK?=lkqGvQ)2YV`C$Iy4y%dP(a|$ty^F6R3hOl8cD+HJr8DFJrCb< zun7w4_Sir-U)6qXvxXN71_iLrR74wKpcL@os?x(y-ayU zMUfCcKbZGs($vRF2&%)Q_o%@oc_l6}afwuy+iYv$u``CTN;RBGGFi36F+MVKVty6M z?XnrmtDSMH8kRIX{fdG=vaC!1BS!GWc02D)hg4NQ2s;rNRz4XD%kRpZ)I_A8*h~WnT>gDY;1atrw?0+?HU_=zKMQr zZ-|MBfnKUszc6#MQqoBPnP>D@i2w#>pExKeXya&;|D!Iqe*5xhTF1sU?pcmFFdy2> z)q`tu3{u<~r71c0T9qP+Ru;cX`I7d4ukMF;f}mAVSDN(Hc?a6N1lJ?C4`1hc{f<=) z3t;Jf1Uyt*+n7fme3T>%UdUEYO19sCUwuk6SQJ2cB5At7+!s#LF9D+V?EJ`HJADWv zJ9lhxUuiV`Mxn-tVS`=G;dZe|$Cwagb{BR0<-7%L7rRFCKhk})N(5lF*gJQ2i?pK} zoocT_UR11vNY@&#B6h*r+33;6z2;)`{f#+|0&9Zr(QL(_#~s>1z!Lr)ywqQ`P&DLGv#YBadD`-?1K$e>oE@X z`?a&Pv-i(i(T>F0{3AKjRi3c6*@E~k53)l|jx(^SwX!)); zC%eZyBL_XTyl9D_=RBEw9$@lcKjgdwOg{HhGfspDZdGl5wB%Xf@?`z^`CvV63Nv8J zqd}FSh#Ef2?q~t;k-LU9wlOZo%F1sCKiNDh2RAlsgg}a+9@Q1K@2}sJU9ufVcAMyf zIVkA(kdWNUPa}XL3y7y-dD=3;n;{GI$&@H*Kc$#A4vQ-GXwgC1bXvrOfhySE(efGp zEw_y4P@i|wmZT1Z5~3Q50R3- z6YCuBg22Kjs)?3tj39ia^o8!wc_PxpSABLvIBslF0^FoG6?^7G#(F z5Q~rU3UgiAhYjK(-Fp1n&T9|JZMYZC)@F5;T#J1k^4uJSY{uQOJ#9l z4FUi=C3Cq!@k~JVm6GN`zwAzO-o7G-D)Dhzma2PUbjg0H$-W07V?L6?1&7&54o=Aa ziXXPPH8bAiOvUinaR&n8;=)v8vkQ+2TrCX62gQC0ADVAGn^WKD`6GS+jo57q-i9Q9 zWXyBXzI39(i~`yYMbTsBr6&rF`WaDD-(b0wrt+Y@8TJ_d!)<$5UzElNS!l@Zfj%l0 z4tJ}_H%jOE=bw*hgykIP4P}mY=3~c{LZf=4$V%*C6Uh!8sO+EFZ0_^J!^0xy19%y& zcwrj)dy`@#)vAQ<$xDOiLW|*pF09)|IAY&#MXJT>M01prT7OyQvFQOUJR^IFJ^H1n zCY_=5R-ar7OMFxmFX0el(qp&2wYBA*f-*WwPQ}C%euTf=K3tl-!zb-my&7I-hQCyU zU-g6M+hM;Fo+8PAhQ(9}_{_)zYThq%+~3Efb&^mWwD|9=qCvg|p|zj_n-Z8oC;xD` zl+VF^2`DU)BlU&sw_Wc5KrURf&+x{lQ& ztwf&?>`A>KQ7iPJn%d#g1FvOG6rptS7OzBPN(!vJAsjL?Gl^gIm>fU6Z=#)7(`{r1 zf#5u9XUfaV=1RV0lcR^(@0hsnho_W&@;ex0w%GWr{BU>X;mKeK0d*2F|IKVyCtlGavQPWs+P_=P@b+GnVkKLX0 z0vFZH+nQ)|HYz93-fSF8^s7qcF51hhBOI%7yh-(#B-@xJFvU%O3-NRN2Ylj{Jf;Rb z7@`y%R(~1Hc>}D4uWy4IYIW057<(an%Mz9cUqcKlW6rlDa1GcljmFKeaaY>C^hY`6 zKIm@b`bD^j-%;A^w{R)vmXxvY*`0j&%hLkauZG|9m&u{<-li!W)a*Wg8BilV*GkTd z!}d#zMg176($8RX7C2=Q-U>mm>54CNw-r za+KFrB8;?>Bch@->iZF<&{V?O5OlINbZ*=Fr7x*6`N~FIo2lD!oS$}yy z<)zqtStk7&F>N2R(3IP6zmu|uXH)Oy&-dfT1vDYcbS8>+(#X|=^E0iT4#oTRS64=7 zNZY8S50xG*jvMqO3#r;4VyDcv^M^H>DmKTfd&O~wAq|!0Fu~~s@a_b6C>_7M5g%;) zXu_qDTmNvjwa|0)vPkD`3$Y9+j*H|#m_$9cc*9R>h>e5&j&L3FFq}fqUe&_T-<9xj zlYcO*v=vqtHgVX?yd+_N^i{N_vFr2qY%H$UA_zMVw&(FvZ+bt6kV6AffhI-{*SU@B zc`i$bWBHrc-fiNtFX7tyyqKN7Pj5K*Rkbxo?9%aSDY%Dr-e^-* zaKhyxy@34WORQTd-})whwbInP4Vs(=T0ZDXH(+DMFqM^3D~ICl%Z^gxsF{`zG2*yt zv>{G#VzbN-MV&iHtGXLbgLn?%gsA6IAjgnKrH;gwkj~|Wqpvm(FCHl71P_fJ8093| zt6JrXkxzyoYlFB%VpBPuwhW-t`GAJq3&;!;Q+X72^Oo?wMgOPfo-&zl!IUmWLG$d#3q$)--%V89j`9=l3+rtV& zC$3{pV%!a3J6Fpzf<~*O6u!%416$zBCKqz?yr;=>-!>^jWv-jHhhu#MMEBLySgHEZ z(bDwULUw$`n!lj?y@$Pz8$EdPVll6+j&`5Vkzt{0R4B_UE8UdQNY*9e@U!;#n-xL# z$6}x}``Tf-Y*6r~YS!lP;Q>^wG}RZK%y`vn9o985yxxq1_RK12r~->?=%~ zv8MGeh0vuR!lPqjmyF^Yqc(dRwGiK}LhI);kDezujXc}El{#hFFI(W?z5AS zW3e%-+;gu|s8WM_zN+!aWPjI;V6%NEyJ=&8(uqi z^qn?Rs^1xtUNFxXoNcB9M}GY(>)|2g@ho88RE|;Mr=OIFJXva|Z1j!Khr3i0{3E-R z)7ohl?Si%llSW!cB30W1J#?$0N1bnnP{-TndCEnrnvQ->) z+j3JzWh!A0HqPxUwJPc9lJnFSH8yGsEp*2_#rs|E_JyL+RZ~tmNGBN01!$p#f`OEEppK_eyeKJZuheH+($Futa@{@vV_s<%%$KfWMbv` zm^kpo1MeS!f>u4z6t1r{^enU8`%x-sQuT8O(xwkvMUbM(zH(t<-6^B}fT+p{!1Wan`>kV^A(DqR;=0Rin3Z0(O%UZ#LiJrj$uTU#n>-x2?6tfGN;jvi4mS5S6 zepXIC&IoQdYkz$sStD$hZ^Y$u7lWo_ZClA9AIedch|2k!dcl6x6>GG%E^=8e%aewi zFj2lIH4!fw`nMs}4(@{4w5j$x)z`gV`O}+;yk27Qm)Rqh3%L|wGk6IpbM1gmYcGpj z{FAA(i%X#7<*j?5!Hu7xe38dT_{D>mnOCc3om_@Z6{{}e%O+m=+edvc#PPC2%C)WQ z1oDuJ^gO$hE>+%J{jFtMa`O%NRV?8?_;#OdKohcCkGa}I5&x~_Ycx&n;j73SC13Zy zwZ3{@o#f@aTdjqmuv2#p58bjNKuU{E!kacnW-^v;zEI=w)ui*}nY?x)XwS|YaHmy1 zcb~U>Cd|z(_&moLhEw(pY)Hu>d|mUwZ^xO1iyCEXq+BW8)Y3v-a|DqDl5tT8+0%KJ ztj^949foh>m@Y}^=N=t~Z%?6mu<$oyC|X~+O5d|gQxn_OCbM58mW^TP!(NOZV?Zl{ zJs_MM-%uk)J}#oieEVt3Qc8yDZ^TMotBLZ#7vdN{r3)Mrd(0T-qC@7YjfuNfaWmis zP)=-ErP;*Mt6_BN?$m@+*s57K;|wyd27{iF^Vw)bG(Rjleja2dC;~JmzUSv)T9KJoY`6cY_aBKW5O@T-7%io|I zH5pOWwUQbg#54@(N-I=4XWJP^Vn5=NMzvechzr)`P#YUps8#o6_%Wd;J>J^`P1^Ky zs4mAuT`{T}kgs-w*!U5C+^{8%F++H$t0AZ|b{rP#`hCM_yR4^(dcuD-SLWjR2PPdF z;?LHKjX=60v!lnfq(_Y&oohHTcV@_9IpGd7fyEev19=UGQA&jSm26Yh`PL(OQ_9f2 zH5j{d-!fDt#08rA!<|vSx%u!d3Ep_##kHHiM}q0HW1zmv!&V=utANgT?LcnU&XJ0TucYM?DBPL21xN0!));M$HZ~l)K_2Ll zWI~443@E@4+=h{cISpV+Iq3A85$|_~v!$Nk+-rZPkcGO6*fpYc1(x~q_nZQ2%?BSoQa&EZ=$0NVA6bKtTyHLacMTU!=M674jhpOoL@r zv3ng=t?scwQu-$KxDf`)1|eqm)R&UMC%Ep3GOOeMxQtfaG#PD?WW`AHmt{)4C{b2g zTcP9G8qsk(a6%eHfHikl=$0DM)tnWn-#n@~v<_ivj++w`XIl~xdKo~zO+$~8n@wZ58x<+9F|IhvT&|FMtUY@? zDF0~Dr5oD2Af?~P8=2=uQJLVDa26G!d2Q5BCb!;`=}#+v>Naf>4a%zP)yE4IL3KbaVz9thl}Y}V2X+FId2)2Uj_ztwPyKDM6`d2L``v&`QB_B{GRCL!ScSz(qmQLlxi!pI)vV~O z21Oi|PZ4^KL5yN(WvG8_EYD=?IEj)szQzc|?t#B~l(tEz)hqT$2{sa^I!(pC0HtF| z)hK0k#rm72CTP`?_MUgYrHx!(w>>KHw@M09y^%IDVA;AoV2=^kT(2~MR}|xu83p(4 zcM&aVbhhWbWxh>>-0(J&ZpDtY;vm@PvBBa=jyma>3lsJSGPAstaWrmIf=TByav*Oi z><>aPZzqaiWtxlC?YwghrJrajhTQ{$H+%$0r^sh4&53$DU3Z7AmYE!{t*!d;@a5M+J*xOS!E=RX*yi%0~VThQ))mtFTD~ zw}O42zyt)|TF8bpwsiw1<(gl@SUXC}Ai4sfw8s3CnzJBU`y|9&pAO4#(2LZp>4d*p zhqupyI6J7#VoV5ns{_qLRl{q`4ZGIs^BiZ@ZKSd5+4N1l0eVY6s1gT&?W;N6hS&997pkKA0XH8 z*3ZgmpL?8pmHO(my%IbhwThEMiZz98AZQDQ7M04F^=va&c=g#^IibeoebQp zOfv3u8&__i)!nU$m1;z2&+?BkHmDZgmc#17wv#_w7<+u?e=qE@ENDakNz>*HJwbsG zBRMt&3*p;F0@{hgGj3Q_7BvVz+Rk5O-Gsl9_>?hXS&t}h0~B{cUNIA@ zk6ofBsnWIxHTtvDW0CY9%B{eyZ^_VdO{8vG_Orp-pS+F;SXW>%A~Ccu^9ZRsddE*yTLo?cwY@s?&qb1rDfRVZ#9xam&_BY@!OFrVxv|c zdM<$rXuP?$7++Kj_ZpEXS{sOiIDU>i==K~d%cX}sE06tp)5qJmI5=eX(bBoJw`BSdn~J%lY^uu%1HE*XCNx&E5kGw@da7Ip+ReNJ`XzW> zkM%jO8txus1iFcSRtArbyTnNqqcXniDjLCK;&OL?WqN;E;oB$cp)y+Xv&m!)!*Vho zh*Frgj7Q#3b`W1_l{qjYEO;^Z?S^+u3F&$!fZF@hpnp)F|WOAi)Mb z)xu!ZL;;5lJDO*-vlmrQbB_4JeC*24wo2RVl{s3$qGsF%!rU^8k?o3jTaPXOiQ=yg zB?2PXAfef3f5sXqtUN%>i>W8jT9bfWk2>92l@6q7_uQnR>POgnwOjpmjGFn(dMHTv z(CDsFT)4==p_BoK^VVGx9i4A@$+X3BO+K2%%#6dT!SitesouY%Tz`F13Ff;LyJyvs zDqK(~ADB1oPaf;h4Ot|EHPKdF$lHsKgU2Y;9QeA@C`QSi8rymYqe+1DdJ(H!m&#{TPUBi-7Aec=;SlD2E2CU zlmk^ix@h)CAxk-F(&Ong0a82lX5k6rT}M-Oi;!uP&nV{wnTWKTpJxQzer1w>Ez)d% z49WZ@N}ai}D-Sfj%}6m!9Ex}qC}9UG`J6%W5^MXZ3>%4^`jGoi#~2c5ize)!5z59l z+J_;h&vQ+CToJjBmmHc^h1gUEP4j+73fUGx%L-;~b-5ZPu-hqM7`u)A8HM1>#25p z#gy5!L0Mtrhw`n;^EUc`m*%!-*$uk|N4gG<8?@my2Q4BS`jo{P4#RF*Fnj25&?ZvTei%G2 z;yXz|1dYk=H!6rl8MlJ$2U#3&^Dfg*RwhyRV$-l@a9!)`RSG{Gq)flZD!z1n+jdiO z)d1Rk*kUl%&>*?b{|8EVq0lr)OFy64;ZfMQLq-Y&x^EgQc35@lK3!;|y zJyX%P@55HR$-;*lU)vjmI9IelX`B6I4GN>JHYF8~4_rPJYavWQ`E8S71C~>SBRO!W z#oI8mm7;L8TbX+Z#K5iB439|a?yA~;P%N#&!NWV`!E|6uSAu$a@IJVk=Fx(-5_y#b z(-3b+@1IGhW+r9;*C8bFS-3IZLX(Pa``C`d$ON}--&{08*lU74&sBBUe+l)jN^L65 zE6?|{jTRpH-BR_#2#uZ)1vJAlL_98?u#UKzbE&{%@nux8t@RGYSNQ9XLG2%!#n?9_ z(NNsOo%R+hqjM<*=*hGLb<(n3I|6R8Hak05`G#2@{op9>`?G^0sgS)%`nIpTDGWyV zh4?O1Wzf*q(rzXX1a76;<$fQNwDwetbu3?Nh2a#P2{tFk?39y6b$+4P8)q*q{Uor* z*+;e6vIRW$qREAQ&*(M`gn~P0Zq(0&lXVgMkSFJjZFr``_i8}k6UI z^{_p=3<2FiR5L@zCHhaIckfEBO6HMO8$w*^S)hv*TFZ^2@>vd8AtOh-UQ39zk%4Pf zc}w0GYev?-ai8xZ4I6=!4OKB+TH{A}>(DG;QhPF6pBX2y9+x18^Vv<9{lC!Y^}{`5Lx>zobIzLRkRQBC2QD1)aciCyDu+5;{cU}pYqFb z5{(*&;)h>40k!lKE`K1sOsGt0Mb1n8THfk|rJ@~U=+jxj$Vr}4MX$x5(r4o+43- zLvQ{E1M(}i(j~L0>QSLv4`=rj6*>xLvO7#_KUE%(f2oO*=Sv;SqOX4Od(b3q|7iEO z@lX`ZHs~`C^&vv2S5~@dgLJrs+)BY<+c&?l)$gPg5o~O1hF56nuNg_)xEop^-vK1~ zN3e~zDe?#Xj1lj+&9OV$d9Y2SbV-C>3Pk+DTN3+rM@Q#$;o#T=xqjIpxDn@+17nq zmIG>iv68*QE4un9rRkoN6lGlU^2PQpwYblB9+Al(r(I-Y{hgJNQ6lvmO_Tew>z~W0*C{n43BQEV(H$j9p6X&3Wd(U>U zuFz^KE5}YOs}-XKi~dG+#w?FpP2F*++`76#+B!NnF#3ZP2dR|t$mQht_=Stv(3hhU zuTJ%lynYiq-T<4WuCy~YKk9E-EYg{@Sys1mW%lK78C*ytK0ii1-&MKu!y5uTv}j*@ zF8>dHbtYheg8FWzA5|{VY1e*m5)o(T*mKbRCFje7fjrKUmki~pOcQCIr@kxcCSG&> zR6VjB0NQb3ip2K!m;!Bkq2lkqm^a?u{CE2M=>R$!fk}(x^r9vmVp26tvd5M1wk2QK7$w8aV!mU#4mK zhnE%@JoUvY{f|Kch^TPVNV5X5$y{}1n*_UYxXHUv)vVfDBWLF_wqj7x)+&1N23?!( z-)HjzHUQLQ)2V|+IYifN(y0QIJByi9F*3?81T`AkLJBPUnvR`+5b2C*+vVIWhoI~| ze|!nCz!aG{AGtM#FNkt;am^L^p>JogSvYSy_|o^6{jFzst_lq57yM&wZLUo^p8hK| zv$?mkmBX3PDFd*i!%hDA&W&pYSzf!u^!L1#NP8J-KG{Mb=2$qi%m*LVcCEJR^TB~w z&cvrrKi|Zu2b`KD5HJnr(BZYUVusZ(@}8iE+H7)eqH>2`za|}ZSh}OEq7kbpoy3i% z*tjDtUED8aN?QMRziLh;97e|v`Moq$x_-nvraU*m>#rx$U5QqC5}D5xFF zXL2h^*kQ$)NRW+9Ax!k3%@X?Jbk)`Y6&&yT>I(}B;@-XE;*{A%?N|!`>RUt;|IPv^HkfT9q53#9bTuz8FXn{i zH{X@Qv3;I3L_^#F1Z`w+u;oPntd0TNE-umOJ+qWefI|(k^jV9DMXO#K@yqvKZ@!rz z=+JPLb~U;`Q?cWGD`SC0&RG}@)v111J}DG&btt?cAu8IQAT+5RVPRo0P>|zbXQ_%0 z+MF1x#LkBKP0YP_swncnhE0CH<$@$?X0&E$)|>m*3~%iy>!z`2-}^ z`0;kQgzUl2B5W{KURyhI`*v1_9mO0{w#Igti<6%}F3tE@ZV^?S79Abk=E7fh$7Q}N zMiBt44(Cyg)(?XD|7bCm9Wn!JN!i^t+0opHZx$=Hl-jwxE&)ky%KWBf#sY$Z=6fYy z0stm1ey&qr>=tzFA$j}#l~{Pu`uq16DQkRY#m8P+-|9im78Vvd_LN$#7?}v$q^OO0 zlzFer(qyHlH!Z@eR&j$GqNAhI1{Oed&iy7+7Cc1Xotg4I^2m3&Ur{v5y>@qj&nm^K z7T{PKSoQ3FTnu6Q=?%>&byR#-@{>U_HaVHq%6s#MfYVP>tNHpEmjjfh)7J%!6~3vg zPWID~MCR~Z-z)krUP9(=GC-L?RNX#OjsAXa<3*$!KkqVhqD}!>gihym$#U*!Z&(kB z5L;2AtgM>w&eTcf4KaTR*FzLzw;&WR=;;>^d_Ii0?37w`>T&XO$I)n7OAMkfTrcmn zUhD^u%1haPN6Xcz46M9Q4jk$$v*2)e&*ImfE<1^y!4|h7)B0@P@|M9$kk3fptz!jM z2gvr2qaAO#uI})M)R`N9^!^(t5Im9M!re$TrMG0DwdrMalmJlyXqP+uMw2tLa+OS z6({&vbs9hnX@6{Qx(jJy_WK^uVoXn&%^)o&l5fqH(y^PIead3oJmL80VCi$h4_MIj zkGk~o@6v~BX7p0t@e^wpX)!adl^0c)XvMsis+Gp=1bY36j#AEx0QD}AGM>i??s9qI|Su&kIEgrwn0=j5NPenJ`h?mk2uJv;wS# z5q4o(FdI9rC2V?Zy&k8UtYtHE9d>z8*2*s>DQ0D?vcPYj4>0Et4d(U8fSj@)a_*L`9WLNmyo>{oHzH0UdaK&3; zI6R90w?BGWLkI{-IT+boebAC2M-C~u$>Y$7PhBqWy*>(1=E`7&6;D!qR>@3c>c1}z z?DB^e|8-s*fv9`hnrs<@)6g-f?li=dDu%xT5?2IaUDT_a_pSX4QEvA&(yOjl?c+%E&Nq9gI@{%t%7;2$6>%PUHkh(m zI7mOgDl9Wak4c;|Wg|J*MR}}`W)rzqZf;+bK90CJyuo9B;6SV5;^M+~FJry4nf-zr z%I!W$+&^Vz&_}hry5`Tugt4hr0?Q`;6Wx>hZ9NtckF(<&n;TgmQxWAMJ)_fH#~bDV z(iO4o*_)w4kg5l&#FPunzI%|BmGa$T-_4bTvflX1wP79$fRsFa>Xq&4#pf$u-5wIJgo&(jwF;C08b5_MO)LWgYPd!(F!v2ALxY=1f?{rNA zQNJ-DnKM6yA1j}Foj|4Jx%hRL;#|(Z*Uyl3M#Im_^%v~eqqs^Jr=sFqW{?^GD{`4gR?8uCL9JfND=63;^O^Y* zYsj#Kk#%r$S)f+6+uM|eKQ7k$C#0j>QdboD1{yD3@Ss%&oab4Lh3cc1MmHO;=8658 zgYO6#n0qK)Tr{MbCn7rf&LUb6EX~o1w-Cx}%pu9PutK~Qzp$$$EBorsQpgL3o6xAF zj?-FQ>LlV=bz8S{U`xwd*WTWUp`qxJLThoz+@k;D*b{x!gNK8OnJd}N)7I;GEdQVIG?&%H?mWWaVS`TFUB zHGuLqn6w*H@R#!Ti5(CD21$-br&3Y}Fy6N2edhB2s>)m-cFKIz`}R~wh#Ua_vfdM1 z!}V7}Z2@`A?RR;8dY}oQ->ErF>KFY>zx%H#GXH0dO8aD8VYa?57|eg*cdBG%SZs;U zpKs9Ns0Pc!*crW^1Hkgwm>2*?L`4ob=j9g0iXEk%<_)PA?1(60+S`>Qfi;MBF2BRe zt5C;qpQgg1w|wO#P?@4#U9Lo8Su;CpuB2yKdxvc-*T3FR%5{f3bn}lHl0jYVTX+^x zke_d~b2Ub+K7ZfSkl}v0B)xznPtEudbh9ckmoNo+c^@_lY>a$*&#FP( zHHI3g?e6ma)!Wn%V&TzNBYs%t1SAPiUp^+1I-YeqwsU{<%-TnpSHiDFU0{+ArpC&N z2IY#S`N&R}*s}?Nza6&&l-BJR z(ibQ^+6$>HRW&riZhCZGHtx>J%NbSn?t-$qMr>iBT6-s!9dMUoUcHiN z@9KKfrOcTX0l4#*6~`O%V6Z6Ifo$pnU$rR5#aLUD+4`4manV;tOlYKjqlnbXL`fv2 zFV+P(Cu&qd{0e6;E^zyJ?M8MLUd%&ILz>yLG!~3YZ9mLH`ff_(Hoj*TVUN^J=efqR zD8FFwG|Kaxs>&U1!Sc~7FheO3k(^PC=P4vI#&_4Mhu9GQymGKl&|-s>UcfxvJ%~dT z{nI`VIb%JpL75KjGFi9?9xr)Tu9gH(n_Z$7fwuuJ%k3Z5{CY{E_Gz7!=Nqc4?^x&U zL~8WU?~|%00yW-ak)zc&Bo`jSUX;l8*4T|+;N;?qycI|eDfjccVL4C&9W7IcEjGw2 zA1!zL)bGf6I}yE>JnS{RG+YK97nXE?bCKb`4B?({<;&q!NiXx^{NbMM57(vCKu^7d zZ$iL@h6oL0F_<4saiL=QS!8T1a?RVuCIJbliH?bD@5_+;44!JB>|H1OEmIL!{^911 z+qLw)qoBa15juN}S4U2jD^oU)Z|dsm_TZ}QOh%i1d);uREUh=)0G6HZMpjS6iEOv6zM2S%B*HiFlm)SnKY|$W?L5+%i&9*yZXj$jJ z?+H)@6peheM?0tZ*@a85f37=j0llXGU@@~D-%u_lLIP;yiF7s zH(5WIqkn8bSij_&>W77S%$5?QZqeR>mQelWLd#jC=r%)wP z84~NSlgd(=k1j?v_`K}QN$ZZ;wen$&a_#vpW%fvzE70L90H6jtARS-3g^q(IkpiL* zTTNFd>s!z3Q;Of;3<64->FqY>`Q4GN6>b9KwYtNq~-O%Duv9femWf&SqMvLW%PP&H(A$gD12A7 z)$AY~Q8D#RFq?U~kkqU(2^QqCl{>v^vo~=T(Cat)Cgk!9AoripwoBpP_vzIW^@QM! zb9<4nddGfktLIu?#MvM47wzsVtb?LLddv?cJuBVN$}@q!@^jj;=DZQ)m+%}$$L5WfBt-qDJS@GZ5!`=^Z#wB z1C-7I#qr2RHgUCY&z@FSY^-=B2B~2$;QBLVrq(_;8794~53AXtiia0dnj|@mJlsHv zmw#_Dzr-kzn?cl)YDJ;>qu`7CG-K;zk^fFGsN@2Cg_A4x8aIm6D}VYr1$1SUT{HC` zNw1y0A|mjMa>trpsmvSfG>pPW)EM;ZLbm~_ms<@BU55jA6?MbgQTDJ9zp$1|yDM zqcw+BdQ6OzY82#`lMsGG=;~5>i#!t9n9iAGO&v;AIDvrl2T-=@smwcr0;CLkX zOE-X1ew?h8kb@luGKf0APIlQz5U@^nxM%@H67Pra+9tULGW!fgIR8A~DoVJ%Qp$uz zU=vd^KWhhC6{kwh6_z(}k~5VwO3vBMRB}%suasN%C^NYB9`%T7zypQ4Q-XoD4U+0nvYc+)J$MWUA zBgH;&g=maV?Mu!z;%c{V%*~9tB%e(u+1;fS-1KX>-2k7@OF_JI+j*IdiM^}VhphV1 z?Z9|z^sPmaAwnTgJF+L&UIHr`she9^7+E!S)MWSooj5=u0;#Wf_{}QK&FF`Yb`g4r zzkUPWP|*8=Udqlf3gdYiTgXxvyx%vD<1osa?yvnq)&Ev%bOljU0OozgjO#T z+U<>jrqw@*FdrLOl(0m1?Euu~i~5*FgUzNMWwwp;8@@6r>!*HJ)+QWJDpiwT`Hu!v zjkZ|N&xtT5jRxd=w*IYWY71pNOkDQ0OnA_q2qnh28tZj!!&%eRBY99|7r^ouQgJs%(854B{khGh&diy3v_qkWVz>#x&`mWCgr)SK)=3Ss;H{cuz2$^0Tn z3uaWKhZ1)#yGTkRDzXWbGdT*KG!@fSk*$!%#3ZMOs zTrF$v7(IcJoc$S2!caYQ>7tBLC)Y4bltiwB2!hG4HqT zD{JhSHmUX5oA>Upj)TtziSH`e+U8%KwNMR)+y0A3^j`s567#&91F;*eVyhClwUmIX zc>btBiqXwflU%K{hmCEnOinHvdj7od8>APBO-ks@AMb*NdJ_OYmMW}-xCKovY`9ch z=3$%1PQ+e#DfYCMMFvght7$-Y_$ISx5-p{ySP zg-qpexW>EC6HP&jOr!rrfq(4C4i6n zV>+)_Ds-Trs3?Ll$m_nbo|3&ru(_qBlJk{J;@ahc6bj%iOYY}K_(SvaZGm^T#V{Pr zNH&6EXXoaq1;(>FzMOlZb3#AG?wDxjPnUTVd$)I}?anjKo}hC98nn4U>KZEUev;?k zT8`il9i>mvCV@}^c+FtAbKVdWr!eWy$1J?O&HExzQ7May(meoWO?8`mYMwV}pq@p4 zW1HXZ7xoj-c77q(-LT3xX_KPNZ)!C%I{IqO*TCo}Iy&4*RjaN>S~@y9cR2f7`~@!E zKX;9)xk9FXXo=YxN%Ju=k;li&BhH-B{bzG`x!GOlgoTPq*>TLr#b$hq(8&^he624} zY9Q0A$PNt$`57XW9zAM3imaH{pyh+jRp@JCBUGZk%Lj`qY?C#$kMlfrdDzjxo#kj1 zVK-r%drJ`f%~B<)Gjy0l63<_uO%N+#mN7l*WYGg_4N(BE=L(Z~^fGZoUIo99+g;o2 zvC8ExYd0lDrh#IeF3YUotP&($-cCx1xwZ2WE}go(z0$mR7htArAH?ycaHOnj2Dt#3 zyZ*Hja5Pfao`B7T)z<0(fH0f1G-yC#q+{^0ee?}HaULh9w$?}9Yu9u&`raQggy_ow zyp*-;BR5X83h=%#+e-0%S@v%!&(~MAa&|7vnp>z;hVieqnfmV_OJS`vcgML}e}fZ@ z{OKmMNT0SVE{vMp^kU#9_SR8s65mEo73Te^{`MCklaPoFr&V)kF#o?CWn~o-5-Q8d zH2^>$@m$(%#wUEue*vs+iaR2NtG%w#GC)wuV^AS~;%)<0%egN==QE2m|MQ&jN=Vg& zZ>#i4*d3V}Cc$c5BV-iP%1lV(0l{TeI#t*>I2766GjrsmI+y9>Gz9{{;OssI43J;u zRXsgxE;2pP0%DWTm564Cj3PJTr9wbUp|jfhsKt!mGWLa9Z|{v#Bmvq$M@_X=M;$vQ;Zi;jTMO{_sZkO?<_#; zHXFx%@`+=iR$^qAmR##WliXCX5gk!mL+b?0DTL%l6RZ zGUp*~JTk{%eZ98h$1&;?5&<+bzNHw^pAp33aNAzB0?7QYi8X9|tfy?&MISI*RD%kx!M;*I*6nC7NeUh z63`xnQgG+i1<6N2Coaq=a$x7#F3NPyp^Y=Es|}v%Ji8_+qaVc1cO7qN_#*n1ys;^^ zz*I0$o;B-9u$Y0B7AJY9oJM_H{rGH0HajMSVabP zPbmj4vA?Uh*JXRne);s>bC`z?w#BOiCrPD$5QV?|wzBmf^xv4A6NuC*=Z zo>KxBZojN{PfeYnohk_^5Fj^`8wI({o#Wk+VzDhyHJn!Bh*k=<6_CZe-#b*gx*^sO zchz*>$ywj;fE!d=W-^1#y1{f1F{6Cb^GUvX!weXBjsl&gDnSllPwInAQws&8Kp@j2 zC(rXkQ+fHr_B^!jX96pgdC+2T~|K zy^gk!M62JE45w{|ghhoSg85+GHXrI~WvT-DbR*Pi7@cdQ6VP*I0^*r_b-gRWS~2^9 zcHl8-2Lh2vjrwA}9c(g9vp7au#%*$hdXtcsOaGtNA9{iveUcd88tK%igotpj$&!BlAT@DC#~2PA?V-1Yg2sf`cs-(bO>)l|rOrPehQjH4+TyWs3jciXbh{d@&j9o}Ispu(y`(TLe0 zoSP@6OGEXQ_qF}K;pv`Ud+G&vCr%^cLa;g)RhgdR&DW=8u;A*3`1)!-?C#4)KQY6k z0K<_pwl9W^ZZ#rSJuIs?EkU~r2r1VdPU<*RB#XLaK#xM*leSEU%O8M(8ow(z^*l~w zPhfl&84k#3$6tCp`gc7DFL`30T>*K|biJQa&UBhk9LR@majI6}r(xsHZdcUZKT#_- zJ4}+Vxh}Gc+ODjkS7p36p(ACrPlHbIUfbKA4vzg@q%y;F8wn(xhy!qXl28>f< zz9%sUq(A#i+w5{QM|4C4k6Ba+c0?@pmRp<0$Q>k1hreDwP=;;9NB6^rr_ZugHwOow zFPnb|d{*sJb)=XQk&6hovgUf=s1f5X3$^ulkd0AaO|h;B?M;$^`6SjVjY(YD!$ZD+ zzajQU%p<@%YgTAZdRwAXhh4F8msJG0#Wpu|tNerN{;2aF!)7DQNvVa38mK0uTONbve=YkxWlo;Thb zt``6Ri}pPtK0~Pi-nrHT9{Kg_%*_FIs(nEE7V&03-zE7>llJ(}yrBB2TlxCME{xXq z?#*CP_BZN7%!i*T1J=MH+@6s$5vrnRmVY@p%3IL)tVYTMTpvwo!1QpfKHK34`N+3` zFD|vmd{Ua1e0t@6>6&t^$=JT$(A?7Z>4SJyfBdfUm-33WB$tM)kWX0Jay$3p@X@n8G4VCDHGSROgCl9k*m&TR8+6ryIbklj#pW1Z?x35kAl_DVN z%2}g62Q!4;8}aNi-=}pxQa%(B5t(#4?t^P^`9fDDa9Z7%HYX6^c=zA;%uQdjNc}=> ztmnVXe>*H~zi=wqvo?(ER!xVQv=5G|@JnA9yq^=&aY@qaxj}3F&fKV65m~wPHZ_(u zdEX)G$t#+b!P~V8Nd{y9Nk3F{9_j0y?!rm6Uw*HCp3i4MjYU7}>6-NM-&wKd7(ULY z>*Q3kbdkcTy^<2W&a+`)4aZuFbbO;l`e$z0`vu3xc@ayC&}6qezF;x8Y#1y)t~I|H zCCcZ!@!|6&S(6^@)29c(4UpDacg@7TE-*t(i$JR*_S7oFij#f5o?UIe*)oo+B?q76 z`L#TNE81*N^?atHqI$qX)YO%4!-l@y=^-&2pCJMMjc*9DMMlsnBuwAW5C45jT3%S0 ztG*C$DAp@7S}x#|O+)-XCdFeC{iv0`oI*{GX$K7SI&~X&Yb0Z``%ii|mbQ9v~e5`fh+}M1796q8abcj$S^Ivw>A5xyUBm^ zm;q@pfE{#&bHQGI{ddcT!w2Klblb(xGW)ZdJs4rv`h9fO*mdJd$7uQ<$;N?$=UXG6 z(SGt=eRzAM=F>pY34N`!$WW;Tqe(Zo4O7w}T;HIW-C|&t$U@Y-^x1^TjH=*sqe)-oWqn=KSt+$) zHP`%%!l9k_Mf3K;rI(6c4D(PggsbR0dCs&gGVz@#-O|H2SL=3hgHQjhD1%g5K<}{g z+NP)?sh-*(d3iqbtmbQ@OTKo`;ciK-Ei{@>PKDcGZn%Ua&A_k#e2Dov}zLB zE>%@!UCJ6C$ATP<9YaN*p0h+g#4j%|UsG-;mAiAk`ts8A51^68>@VY*G>zD&zQb@h zq8|Mb&AMJByn%Mb6w1}x)fI)7sZJ0)>yez<-bTjkE*%52FJCP=n9ge@il8h>cVn}u zPFR_>VZbGtbc!uTTCIsOdoRW3VX&5Zd|9ztD*Pye89CfyM-I5`_9?w4%0?FoQut^04^?rt@lYLa8lKk z${1BU4igk4qye|p$W2g2w{^1fDrgB(lg_PM>THhV49UZtrJ^3*vDLm5(@A^IZEg`9 zZoF~9qY>;Tz0yfx10%E$Xz69~0}V^td)QqX=kV?w&5 zv~kd{%~3~3XCZ3-T7-1Nv}QvQ%zc(4cCqfk;9bcn2R11x17-wWJ3^}8i@~n;!2f*J z7e|NXr*9cIjKN;9%XimiIkcXnquXR-qM|B=EJArf&omUKJYl=>^GPv}m)FWBZP_>) z4kg|IYB^^lX?G4mm}{u4O3d`gmrdSAB>CZwZM~-NZJ>;Fh{;aBQb%jW?S1|lYbaSV ze-p~jEH59tprV^$ikY$p$itN3GH_Ow6`B{4?=vjlTj4IhcQMEIxD_94V}d>u)2||U z!svBH)tQFh&1B2W&{m+`bE?c_!g*lH{(CgqnDLlbfN#Kby_;#RS`X*m&>+wR~66mEN^{X#jx>B-O1ZaT+Y^-!O#8f^Z4`Bo}3GH7`5t~ zyvJ6U*;j6!rg3%n9};6}L>85_QtY;@@}7RZ0&6Ph{jVbCBD0q(Np$mjkdhs~a6S^D!L%4cwt;%-W($=WI(k+uaK?hSRx>k|l;l`pvQ>S8{bA_7zw; zqtQjzrQy88BdnbdGZ}>6rj1?mzjp23{WH$JPc@t)R!H>KhG3{n9ds*+Oq8wbrTPY0 z^{zIM$ciNB1aW^8Mo_)S9Kjan>+7p08e3@6ClfPt0PM+$ytcOd0f|z<7h<};!htka z#NYDp+-(;K{^!(HX`e|8C)CyQg9#H0r~Sw4f(sfOdv{rOy)s*+0NyBDg&*%H>$IBG z{hHbXa^Lnd?kRPM~KR3hlF1$pugpr;$}nqK%`ULC8@PtBLN ze~tAMF_J1?XU3;jkd_m}DG{qYBNOQYS|W@X!4jthze>^ttjtx^T`WE|8MBpQrJ5q| zQ3>aWbMUsRLS=qgq_Xb3V*Qi70&T8#bzWz~hLi9C?!;d(9ukVE5B+|N8NV{R6&I$K zwf#_`)Z=Z9mub^ueRF(+F?bL%($u~2%it+X^EL3QxPLaRp|)k$?f2Y3)+c)-{FRHB+nP1LTy6nO6P;K2wX3Ry?_pYrwKpZ`bT_=zwaK!AUk z1O5YiUq~?ef6nJAas2-_faU7}iQxR~Kax#9;Btq5IR_x9{P*(zF5MrM;(xczzo+8= zIx!(}rjDP!5oLDdQ$2JINwAk*4Uxi$)Me~Up?{FhkH6`k_{9W)S$QstK zQF8zlI4A9uWe zEotM{>m?X_;&Dhqp~1_|?6YXXn;`s4iuZ))Z{b$CuO zS#JSLiUZ|kG*hWj{R%g*vA5{VmrJzMK5&R%95S6qp}pZPp}o0le*V|GMQra`vZ4t* z-wVZKMiV4>s#ZJh)Kdu4$L3%>WB-UPwVNJ;K##Zg<7M&%$!#veb)yeW9`ruX@}K+_ zGjDdAiu88dy`22kx{0jW@;Y81e(f@hKf!vfwL;Q6qf)~){`Q=mNAgAWdH=g&A!fbi z$4MydqBFC@wFv}g;j z1bd{qjx`JSdC@v;O2bglR9U~&D^M&HQ;=e-yW_IbErXTPSvMm5zG-;=_+R~%#TX+& zWcG2?L`4sg48F8Q1c8;O7_*^O;98wn_c0s2D;5{y$&ytRvqv;MXW-kZ{hV@A=-w#nQf!esbVL-?0j+deIKKv z-VrpN-@C0)NVJ87()i6iilyy5wCJuZ6KOYTAO5b-^)ym;z^)le9Tf*Z9s4!vK6AG> zWqGP+KELj+yTmnEpABG29yq(g8u0wCgZleU!IOg4>s2@|YG^-rt3V9ZVpT`=d( zPns{A%6tE^VKhUzW8@?EUUG0%P*=B3Bun#=v!1?&9XWR5Xl+fH={+mTltilHSnr^> zh;K~)PI-Mle{AomF{-P&(|6a%ePk`~9fxDLzKLmi-G2T@_YTws{ufyJM%^l!dgNAc18!#-X`pHHgl zw>(o+FgeRP3fQ(QCbhTxGMEzf2BoIeuHh7LZ4&agpPqLfZ)1P{tub&}c233ptN$Q= zg>kCr=Qu%cB=``c&G8et7s=ks-IZ)vbtz9-y2s^=mixK9H_*os!!uJ7#?ik6-xQ2m zTMQ|@VvEKx)mRu#k*5vpH=ZJ!#xk+u7IPPBIx-cKWBh{KPg9Oc)y{r*?(ZI%(7PHe zOh5Nh)$`)Xkm;BzF?ab~c7Tl6Z)!B%^O~&!<53Pt_FdUaC9xfqF89e!1mjA<%4qwZ zrgxCk{dfiOAtrgyNr{PS?l^SL#95dDdX;jwK#Z16R!Tr+(`OxiQ^kLTh3xfcGWPXN}FRNT-y<=OkgOadEvEIXNeB zMf-4S-w`H+I`B3trVhKNYR+7?9r#Gn$^<=Qr49| z4R5Dw^$%y$&n6dNUR&;ws#}(oB;DNJqsk77{n%$wD)xtX^Q9 zpQH6o&WWF$zJ@K)+=oQm)vkdbc6@xeqAm}4c#!9yZlMD5xJadGE8_xp6NP44by#* zh>Moz(hZgz+|fK?uqq+h?h?*S;%iFSGM=nK`ypm7YViH=~cH@)G%dd zt}F2u`H^nWfY``y{<-2STTO#_wH3&n@Vzeo_2^8ml7^E6CmZORUI7 z%n%B0syufbnhzy=Cuo3DB1LRJvH=*g&bP)PN|3LXMU9uZlfL}Tqx@QG{$+|{mCKJw zqI-wT()#0~RPqWU23Tj8*Nejv_At}Y>7_K;)zeD+>J0Q1q%kO7UrdIYdw(w5bSZpr z5$t1Kzkj#~y!L1?A2w3T-LoHvk41c4yh0!3wVrN|+e(2yK^1uqaut8hR-*m-4|L$QYS!dER~2W3N=H-vmSQUG$CY{rZhF-kwT?y?za$o&vO~UEmdO0 zyvGQcZ;33$oJpyc%`LMxxvzk8Q@YKu_a5!+|LR?CU*l%Bg!A*YcAq_ZO;Br~xGO!p zlhUDMTm#@gCs%qWYswV79;nzl@hk@@l`_}kNS-U;9e)=$)~{@Gx$}=9^Fo$p9(uDD z$=79_&N&1(&j_Kn(&fA1x(pNwDLW_y($4;{5hyn#(?SlG|xA7j_K4B zohIc35_G2~SU5I-V%DW<_DWhc)t22p_gw|VZk4=ytUnd=yE-*{p{Rm2@gn-On=m3{tUL+jgKlDVx?|9JCi!FDqP_hte2No9KHfYgLapu;pcuU-rqu&~9|V@u zF`TvZ7U#+r{H~dsi`9q$t>ZM}CME-) zD$atwz~AlWG!*+PyP5T1Tr+o%Bugw}&A*w@eJZ^4COkreFsZnk(HcXq8%C&Ma_R#{ zJcH9OqJZ0KJ#)jxa}3Pl1126k#e<>|N=Z)+QO(*cDpfqn|2oQV>yX0wr_L`sCOr2f zu_P7Jtd9i>{>~QBG2GgcZ*IOVT>J4$>cpxlZutp?>K0Y!CA>$A+bY0KSH+FzQ0sul zrQ4jPedoE84mjjyPj>Ani7dYGA(W2b)u1QqU;1*mQSpDaPojw1rt3 zCfZXoun~4}MD&}2>sm&uaL zC`5s;$ONJYFOzzk3Qex|oqs*;d(Za?je}CMzt>W0wtHMoeBlg|Jq0RVAim^~4uHD% zm^Nz4pUjgf#}fDB(*EwwKk1v zK3>&vc(PS6p^yM^51%^*=A>0ra2FB2_Hiku;_yp^0HQvkm6*;3G2bp-%GLXI8B5>T zWx2!T{sfc+TeG2!#leSCG{%LAf8g!l51|D zDVXSUdl_Qrb=j5q4K~WP?eMIFMbT^mqa16Ml>7Fb6t=IC!R!n5F-YIW>`54@b>kUA zD}+56ocns)WraOHxTP~{Wp9G3R1Iv46V8Nm4Zb3MP%x*OrWB?;pJ*R=tlLoVVolxq zB5qL-W-gaIB{44jE#*Ub#v8N(fEA1jO{P9ftr@YOJvKhl{Gm#C(Qjv1)v9i1RPMfN z%get(ENu9s^2|HzGt>uoNw1|B8z?h~?7dC#6%NCM&+u}3EGebNx}Nc5&p?0doh}Zh zNF~P11EUqUm}%!8PV9-$^^4g*{7@-Vp4nGSu1~Xsis(7{DGn~OJ+beyayymF>e@Br zw4|JlnA;8~qC+808%K5(7JboDMt}Th>2G{mF06i;YxHvK$u{P+&Rt~W>5$b6yH{)1 zoA56^Q^mUdzLm{s0Sjn?OrNr-O;k;FMxy7gdBXEL8YzQ+Ypp|!n9Gx>SSKtzHfJGe zJTE5maCW0jY?47`bpyD&9VKcKjBp(mvbGou`xG zZ9adn_=Cz++>}x7d!Q*i0>A#yCO85RmLp|s<7m#wbVq|624MgFDPRA5ERQLCX1lIe z1F?m=&!jf--qAM_zb0q(^TkAd%zrG{)Y6p)ZmKnHfOv|GwRdJMcA3A|e3C=n(-!_m zfj`K)J(*|z8psxmUWo1kZ@TDIVcxu=Su8I9^l>!xjvtW0{8NtRFw^dzX!Iz>XL0k2 zsoZ?tv?1e`Uc}`yDvzGKX}_B_e&c^$%A{ah?@)vHHk-&3aD}1*kH_sxUjplM#WuFt z`xj|zv=MvQBK7`0qP}HDLb%)h8KkC}jUGZ4Z)RSlw<2cQT~ zjnused~T#(^v8O)LHzH3ondX6X%d5QNCJD3(`FH}dyXC3U^e)esrv}2-ou6N8R_0_ zYE=nqG(<3X^f)v%>-_T!v^ahS{fOowyh#v!To@1p{igHH`tirGirc5e+Xu8+wmkG> ziJMTg^df&+GK&x>fspsKcP{SrKLDqa93yMQ&M_z?4`buslZSH7^AZsgmw)p@F8_jy z`q4NC>s|OcO$TXe$6OhxBFi%0{I<8Yze|vG88I72^>I3$rSTSeF-`67_Epknjj9l~ z>hQ>s3h{w*S;`iGAgxkn3n!FR>#QouV^5R-&rB=#6&I4R(3La@ zB=dK(!?F;;8%im)KZ$Njc?vpH&V+(aw;yeaJ+pxKs4mdq9v#{F&)xvA@1t?{|C)sWFVhAL1hr-J{?jU? o1Y9ot-^&kT-v8^Ro1`;nD41Mbd{}RM2>5&YP~$<-eT%pM3quSzE&u=k diff --git a/docs/scene.png b/docs/scene.png deleted file mode 100644 index a7d41baef67c0404d648a6214b6f664694de2097..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35855 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 diff --git a/user_manual.html b/user_manual.html deleted file mode 100644 index 3c64203..0000000 --- a/user_manual.html +++ /dev/null @@ -1,314 +0,0 @@ - - - - - tg-flow 使用手册 - - - -

      -

      tg-flow 使用手册

      -
      -
      -

      1. 编写目的

      -

      通过对本公司的 tg-flow 软件产品操作过程的描述,使用户可以自主对软件进行使用和操作。

      - -

      2. 下载与安装

      - -

      2.1 下载安装包

      -

      git clone https://github.com/didi/tg-flow

      - -

      2.2 安装和启动

      - -

      tg-service 的安装和启动:

      -
        -
      1. 将 tg-service 软件包拷贝到要安装的目录;
      2. -
      3. 进入 tg-service 目录,执行 ./build.sh,会生成 output 文件夹;
      4. -
      5. 执行:cd output 进入 output 目录,执行:nohup ./control.sh start > nohup.log 2>&1 &,启动服务。
      6. -
      - -

      tg-web 的安装步骤:

      -
        -
      1. 将 tg-web 软件包拷贝到要安装的目录;
      2. -
      3. 通过命令行方式,进入到 tg-web 所在目录,执行如下命令:npm install,即可完成 tg-web 部分的安装;
      4. -
      5. 通过命令行方式,进入到 tg-web 所在目录,执行如下命令:npm start,即可完成 tg-web 的启动;
      6. -
      7. 打开浏览器地址栏,输入 http://localhost:8888/,可以打开登录页,说明安装成功。
      8. -
      - -

      3. tg-core 是应用系统使用时需要导入的组件,不需要单独安装。

      - -

      3. 登录

      - -

      3.1 账号分配

      -

      初次安装时,系统会默认生成一个管理员账号 admin,密码 admin,可以使用该账号进行后续的登录和分配其他子账号。

      - -

      3.2 登录系统

      -

      第一次打开系统的时候,系统会自动跳转至登录页面,这个时候可以使用管理员账号或者子管理员账号,输入账号、密码、验证码进行登录。

      -
      - 登录 -
      - -

      3.3 注销登录

      -

      进入系统后,可以通过点击系统的右上角“退出登录”按钮注销登录。

      - -

      4. 系统管理

      -

      tg-flow 支持同时接入并管理多个系统和应用,每个系统都有独立的系统ID和系统名称,通过系统管理页面可以查看当前接入的所有业务系统。

      -
      - 登录 -
      -

      图4.1 系统管理界面

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      属性名称描述
      id系统 ID
      app_name系统名称
      machine_room系统所部署的集群
      node_name系统所在机房
      operator系统创建和修改人员
      create_time系统创建时间
      update_time系统更新时间
      git_url系统代码所属的代码仓库地址
      - -

      在策略管理 -> 系统管理页面可以进行新增,修改,导出和删除系统等相关操作。

      -
      - 登录 -
      -

      图4.2 新增系统对话框

      -

      其中系统编号,系统名称,部署机房和节点名称都为必填项,填写完成后点击保存按钮即可完成系统新增。

      -
      - 登录 -
      -

      图4.3 修改系统对话框

      -

      系统条目右侧的导出按钮并点击确认导出可以导出对应系统所有场景下的流程信息,并作为 zip 压缩包的形式下载到本地并用于对应的系统中。

      - -

      5. 场景管理

      - -

      5.1 场景列表

      -

      该列表页展示在当前平台注册的所有的场景信息,系统与场景属于一对多的关系。场景信息所含字段如下图所示。搜索栏中可通过输入“场景名称”或者“系统名称”来获取相应的场景信息。

      -
      - 登录 -
      -

      5.2 场景配置

      -

      场景列表页存在四个可操作按钮:

      -
        -
      • 新增:可手动添加某个系统下的场景信息;
      • -
      • 刷新:手动触发获取最新的场景列表;
      • -
      • 修改:针对单一场景修改其描述内容,除了场景编号之外其他内容均可修改;
      • -
      • 删除:删除某个场景;
      • -
      - -

      6 工作流引擎代码接入

      -

      6.1 代码框架

      -

      如下图为一个待接入工作流引擎的应用系统的项目代码,其中logic目录下为业务层代码,接入工作流引擎时,可以在业务层添加相关的接入代码,主要有:

      -
        -
      1. 工作流引擎初始化代码,类似下图中的workflow_task.go文件;
      2. -
      3. 工作流引擎调度执行代码,类似下图中的dispatcher_genrec.go;
      4. -
      5. 工作流节点对应的算子实现代码,算子有多个,可以放到不同子目录分组存放,类似下图中的module为上述子目录的父目录;
      6. -
      7. 算子注册代码,用于将算子注册到工作流引擎,类似下图中的module_init.go文件.
      8. -
      -
      - 登录 -
      -

      6.2 工作流引擎初始化

      -

      我们提供了三种工作流引擎初始化的方式:从本地工作流配置文件初始化、从全局配置中心提取配置信息初始化,以及从redis提取配置信息初始化。下面介绍的是第一种,从本地文件初始化,可以在自己的系统初始化模块中调用(其中workflowPath为工作流配置文件根目录):

      -
      -
      -func LoadWorkflowData(ctx context.Context) {
      -   moduleObj := module.ModuleObject{}
      -   workflowEngine, err := wfengine.NewWorkflowEngineFromFile(moduleObj, workflowPath)
      -   if err != nil {
      -      tlog.ErrorCount(ctx, "cron_task_workflow_err", fmt.Sprintf("workflow engine init fail, AppId=%v, error=%v", constants.CurrentAppId, err))
      -      return
      -   }
      -
      -   global.WorkflowEngine = workflowEngine
      -   tlog.Handler.Infof(ctx, consts.DLTagCronTask, "workflow engine update successful, appId=%v, workflowEngine=%v", constants.CurrentAppId, global.WorkflowEngine)
      -}
      -
      -
      - -

      6.3 工作流引擎调度执行

      -

      使用工作流引擎调度执行工作流,只需要在调度相关模块中添加下面的代码即可:

      -
      -
      -global.WorkflowEngine.Run(ctx, sc)
      -errMap := sc.GetErrorMap()
      -errMap.Range(func(key, val interface{}) bool {
      -   log.Println(fmt.Sprintf("workflowengine error, key=%v, val=%v", key, val))
      -)
      -   return true
      -})
      -
      -
      - -

      6.4 工作流算子开发

      -

      通常一个应用系统中可能会有多个业务场景,每个场景下可能需要配置多个不同的工作流,应用系统对外提供服务时,需要根据实际业务场景或实验分组情况选择使用不同的工作流来执行,工作流则由若干个执行节点组成,每个执行节点对应的是一个算子,在golang语言中这些算子对应的是struct。我们需要在要接入tg-flow的应用系统中定义这些算子,下面为一个名为RecallSampAction的算子定义示例:

      -
      -
      -package recall
      -
      -import (
      -   "context"
      -   "fmt"
      -   "github.com/didi/tg-flow/tg-core/model"
      -   "github.com/didi/tg-flow/tg-core/wfengine"
      -   "time"
      -)
      -
      -type RecallSampAction struct {
      -   wfengine.ModelBase
      -}
      -
      -func (r RecallSampAction) DoAction(ctx context.Context, sc *model.StrategyContext) interface{} {
      -   //此处添加相关业务逻辑
      -   return nil
      -}
      -
      -func (r RecallSampAction) OnTimeout(context.Context, *model.StrategyContext){
      -   //此处添加超时处理逻辑
      -}
      -
      -
      - -

      6.5 工作流算子注册

      -

      为了将开发好的算子注册给工作流引擎,需要进行算子注册,如下图中的case语句部分,即为工作流算子注册函数,我们只需要自己实现一个下面的函数,并在函数中的case语句中添加自己的实际开发的算子相关注册代码即可:

      -
      -
      -func (moduleObj ModuleObject) NewObj(moduleName string, vMap map[string]string) wfengine.IModelBase {
      -   switch moduleName {
      -       case “data.DataPrepareSamp1”: return &data.DataPrepareSamp1{}
      -       case “recall.RecallSamp1”: return &recall.RecallSamp1{}
      -       case “recall.RecallSamp2”: return &recall.RecallSamp2{}
      -   }
      -   return nil
      -}
      -
      -
      - -

      6.6 代码仓库注册

      -

      在应用系统中完成上述代码编写后,可以将代码提交到git代码仓库,并将git仓库地址拷贝出来,然后进入系统管理页面,如下图所示,点击“修改”按钮,将该代码仓库的地址填写到弹出层中的“git仓库”文本框,如下图红色矩形框所示,然后点击“保存“按钮即可。

      -
      - 登录 -
      -

      7 工作流管理

      -

      7.1 工作流列表

      -

      点击左侧的“流程管理”目录,可以打开流程列表的管理页面,如下图所示。

      - -
      - 登录 -
      -
        -

        选择列表上方的“系统”下拉框,点击查询,可以查到对应的应用系统已经添加的所有流程。

        -

        选择“场景名称”,点击查询,可以列出指定系统及场景下的所有工作流。

        -

        点击“新建”按钮,可以打开工作流编辑页面,开始一个新工作流的创建,下节会详细介绍新工作流的编辑功能。

        -

        工作流列表中有:修改、导出、复制、导入、删除等5个按钮。

        -

        点击“修改“按钮,可以打开工作流编辑页,后面的工作流编辑部分会详细介绍。

        -

        点击“导出”按钮,可以将当前工作流配置信息导出为一个json文件,点击确认后可以下载到本地,后面可以拷贝到待接入的应用系统工程目录下,应用系统在启动时,会初始化工作流引擎,工作流引擎初始化时可以通过本地方式加载上述配置。注意:此处是对单个工作流配置进行导出,如果要将某个系统下所有工作流打包导出,则需要进入到系统管理页操作。

        -

        点击“复制”按钮,可以新建一个同样的工作流配置,然后后面就可以在这个工作流上进行相关修改并保存。

        -

        点击“导入”按钮,会弹出一个对话框,这时可以将在外部编写的工作流配置信息复制并粘贴到这个对话框中的“导入内容(Json)”文本框中,点击“确认”按钮,即可完成工作流配置信息的导入,注意:工作流节点中的action_id的取值格式为:action-{场景ID}-{工作流ID},导入工作流时如果相关actionId中的场景ID和工作流ID与当前要导入的场景ID和工作流ID不一致,系统会自动帮忙将actionId中的后面两部分的值调整为当前的场景ID和工作流ID。

        -
        - 工作流编辑页面 -
        - -

        7.2 工作流编辑

        -

        打开工作流编辑页,系统会自动读取前面配置好的git仓库中的代码模块,获取代码中的算子列表,并在工作流编辑页面的左侧算子列表中展示,供后续的工作流编辑用。(注意:工作流中的条件节点已经在工作流引擎中做了内置的模块实现,所以无需单独在应用层再做实现,当然如果有需要,也可以自定义条件节点后,注册到工作流引擎)。如下图,为工作流编辑页面。工作流编辑页面包括三个区域:左侧为工作流节点列表,中间为流程构建区域,右侧为工作流及节点属性编辑区域。

        -
        - 工作流编辑页面 -
        -

        其中:

        -

        在后台管理系统中打开工作流编辑页面时,系统会从后台管理系统中配置的代码仓库中拉取代码模块名称,并以组件的方式显示在左侧组件栏。左侧的组件有两种类型:一是应用层节点,用户打开工作流图编辑界面后,会自动从对应的应用代码的git仓库中拉取对应的算子列表,展示在左侧的节点列表中。二是工作流引擎内置的条件节点和缺省节点(当git仓库未完成代码提交时,左侧无法拉取到代码模块名称,这时可以手动添加缺省节点,然后给缺省节点重新命名)。

        -

        从左侧组件栏中依次拖拽节点、添加节点之间的连线,可以逐步构建出类似下图中的主流程图和子流程图。

        -

        针对图中的每个节点,可以依次点击节点,然后在右侧的属性编辑区域编辑该节点的属性,如:节点名称、节点参数、节点描述信息等,完成所有节点属性的编辑后点击“保存”按钮,相关的工作流配置信息便会以json字符串格式存入数据库。

        - -
        - 工作流编辑页面2 -
        - -

        7.3 工作流配置发布

        -

        上一节中已存入数据库的工作流配置信息并不能自动发布到在线应用系统,需要在系统管理页面中点击“提交”按钮,将上述配置发布到一个具有存储全局配置功能的存储服务中,一般可基于zookeeper或redis开发,以供应用系统从配置中心读取上述配置信息。相应地,我们在工作流引擎也提供了从redis、zookeeper及本地文件中加载工作流配置的方法,前面工作流引擎接入部分已有介绍不作详述。

        - - -

        7.4 工作流导出与导入

        -

        然而,由于部分应用系统出于安全或者降低外部依赖的角度考虑,并不希望通过与后台管理系统进行联动的方式,动态更新工作流配置信息,因此我们在系统管理页面提供了工作流配置信息导出功能,用户可以直接将工作流配置信息导出为zip包,然后将上述zip包解压后直接添加到其应用系统的项目的本地目录下,应用系统在初始化工作流引擎时,可以直接使用工作流引擎提供的相关方法从本地目录加载工作流配置。

        - -
        - - \ No newline at end of file diff --git a/user_manual.md b/user_manual.md deleted file mode 100644 index 2447527..0000000 --- a/user_manual.md +++ /dev/null @@ -1,234 +0,0 @@ -## 1. 编写目的 - -通过对本公司的 tg-flow 软件产品操作过程的描述,使用户可以自主对软件进行使用和操作。 - -本手册第6节之前均为tg-service后台管理系统的安装部署,由于目前该系统尚在完善中,暂未开源,所以可以暂时不用过多关注。 -目前,可以先直接手工完成工作流配置文件的编写,将来tg-service开源后再通过该系统进行工作流的配置和管理。 - -## 2. 下载与安装 - -### 2.1 下载地址 - -tg-service:(敬请期待) - -### 2.2 安装和启动 - -#### tg-service 的安装和启动: - -1. 将 tg-service 软件包拷贝到要安装的目录; -2. 进入 tg-service 目录,执行 `./build.sh`,会生成 output 文件夹; -3. 执行:`cd output` 进入 output - 目录,执行:`nohup ./control.sh start > nohup.log 2>&1 &`,启动服务。 -4. 打开浏览器地址栏,输入 ,可以打开登录页,说明安装成功。 - -5. 注:tg-flow 是应用系统使用时需要导入的组件,可下载源码学习,但不需要单独安装。 - -## 3. 登录 - -### 3.1 账号分配 - -初次安装时,系统会默认生成一个管理员账号 admin,密码 -admin,可以使用该账号进行后续的登录和分配其他子账号。 - -### 3.2 登录系统 - -第一次打开系统的时候,系统会自动跳转至登录页面,这个时候可以使用管理员账号或者子管理员账号,输入账号、密码、验证码进行登录。 - -![登录](docs/login.png) - -### 3.3 注销登录 - -进入系统后,可以通过点击系统的右上角"退出登录"按钮注销登录。 - -## 4. 系统管理 - -tg-flow -支持同时接入并管理多个系统和应用,每个系统都有独立的系统ID和系统名称,通过系统管理页面可以查看当前接入的所有业务系统。 -![登录](docs/app_manage.png) - -### 图4.1 系统管理界面 - -属性名称 描述 - -------------- ---------------------------- -id 系统 ID -app_name 系统名称 -machine_room 系统所部署的集群 -node_name 系统所在机房 -operator 系统创建和修改人员 -create_time 系统创建时间 -update_time 系统更新时间 -git_url 系统代码所属的代码仓库地址 - -在策略管理 -系统管理页面可以进行新增,修改,导出和删除系统等相关操作。 -![登录](docs/app_add.png) - -### 图4.2 新增系统对话框 - -其中系统编号,系统名称,部署机房和节点名称都为必填项,填写完成后点击保存按钮即可完成系统新增。 - -![登录](docs/app_update.png) - -### 图4.3 修改系统对话框 - -系统条目右侧的导出按钮并点击确认导出可以导出对应系统所有场景下的流程信息,并作为 -zip 压缩包的形式下载到本地并用于对应的系统中。 - -## 5. 场景管理 - -### 5.1 场景列表 - -该列表页展示在当前平台注册的所有的场景信息,系统与场景属于一对多的关系。场景信息所含字段如下图所示。搜索栏中可通过输入"场景名称"或者"系统名称"来获取相应的场景信息。 - -![登录](docs/scene.png) - -### 5.2 场景配置 - -场景列表页存在四个可操作按钮: - -- 新增:可手动添加某个系统下的场景信息; -- 刷新:手动触发获取最新的场景列表; -- 修改:针对单一场景修改其描述内容,除了场景编号之外其他内容均可修改; -- 删除:删除某个场景; - -## 6 工作流引擎代码接入 - -### 6.1 代码框架 - -如下图为一个待接入工作流引擎的应用系统的项目代码,其中logic目录下为业务层代码,接入工作流引擎时,可以在业务层添加相关的接入代码,主要有: - -1. 工作流引擎初始化代码,类似下图中的workflow_task.go文件; -2. 工作流引擎调度执行代码,类似下图中的dispatcher_genrec.go; -3. 工作流节点对应的算子实现代码,算子有多个,可以放到不同子目录分组存放,类似下图中的module为上述子目录的父目录; -4. 算子注册代码,用于将算子注册到工作流引擎,类似下图中的module_init.go文件. - -![登录](docs/code_dir.png) - -### 6.2 工作流引擎初始化 - -我们提供了三种工作流引擎初始化的方式:从本地工作流配置文件初始化、从全局配置中心提取配置信息初始化,以及从redis提取配置信息初始化。下面介绍的是第一种,从本地文件初始化,可以在自己的系统初始化模块中调用(其中workflowPath为工作流配置文件根目录): - - - func LoadWorkflowData(ctx context.Context) { - moduleObj := module.ModuleObject{} - workflowEngine, err := wfengine.NewWorkflowEngineFromFile(moduleObj, workflowPath) - if err != nil { - tlog.ErrorCount(ctx, "cron_task_workflow_err", fmt.Sprintf("workflow engine init fail, AppId=%v, error=%v", constants.CurrentAppId, err)) - return - } - - global.WorkflowEngine = workflowEngine - tlog.Handler.Infof(ctx, consts.DLTagCronTask, "workflow engine update successful, appId=%v, workflowEngine=%v", constants.CurrentAppId, global.WorkflowEngine) - } - -### 6.3 工作流引擎调度执行 - -使用工作流引擎调度执行工作流,只需要在调度相关模块中添加下面的代码即可: - - - global.WorkflowEngine.Run(ctx, sc) - errMap := sc.GetErrorMap() - errMap.Range(func(key, val interface{}) bool { - log.Println(fmt.Sprintf("workflowengine error, key=%v, val=%v", key, val)) - ) - return true - }) - -### 6.4 工作流算子开发 - -通常一个应用系统中可能会有多个业务场景,每个场景下可能需要配置多个不同的工作流,应用系统对外提供服务时,需要根据实际业务场景或实验分组情况选择使用不同的工作流来执行,工作流则由若干个执行节点组成,每个执行节点对应的是一个算子,在golang语言中这些算子对应的是struct。我们需要在要接入tg-flow的应用系统中定义这些算子,下面为一个名为RecallSampAction的算子定义示例: - - - package recall - - import ( - "context" - "fmt" - "github.com/didi/tg-flow/tg-core/model" - "github.com/didi/tg-flow/tg-core/wfengine" - "time" - ) - - type RecallSampAction struct { - wfengine.ModelBase - } - - func (r RecallSampAction) DoAction(ctx context.Context, sc *model.StrategyContext) interface{} { - //此处添加相关业务逻辑 - return nil - } - - func (r RecallSampAction) OnTimeout(context.Context, *model.StrategyContext){ - //此处添加超时处理逻辑 - } - -### 6.5 工作流算子注册 - -为了将开发好的算子注册给工作流引擎,需要进行算子注册,如下图中的case语句部分,即为工作流算子注册函数,我们只需要自己实现一个下面的函数,并在函数中的case语句中添加自己的实际开发的算子相关注册代码即可: - - - func (moduleObj ModuleObject) NewObj(moduleName string, vMap map[string]string) wfengine.IModelBase { - switch moduleName { - case “data.DataPrepareSamp1”: return &data.DataPrepareSamp1{} - case “recall.RecallSamp1”: return &recall.RecallSamp1{} - case “recall.RecallSamp2”: return &recall.RecallSamp2{} - } - return nil - } - -### 6.6 代码仓库注册 - -在应用系统中完成上述代码编写后,可以将代码提交到git代码仓库,并将git仓库地址拷贝出来,然后进入系统管理页面,如下图所示,点击"修改"按钮,将该代码仓库的地址填写到弹出层中的"git仓库"文本框,如下图红色矩形框所示,然后点击"保存"按钮即可。 - -![登录](docs/git_reg.png) - -## 7 工作流管理 - -### 7.1 工作流列表 - -点击左侧的"流程管理"目录,可以打开流程列表的管理页面,如下图所示。 - -![登录](docs/flow_manage.png) - -选择列表上方的"系统"下拉框,点击查询,可以查到对应的应用系统已经添加的所有流程。 - -选择"场景名称",点击查询,可以列出指定系统及场景下的所有工作流。 - -点击"新建"按钮,可以打开工作流编辑页面,开始一个新工作流的创建,下节会详细介绍新工作流的编辑功能。 - -工作流列表中有:修改、导出、复制、导入、删除等5个按钮。 - -点击"修改"按钮,可以打开工作流编辑页,后面的工作流编辑部分会详细介绍。 - -点击"导出"按钮,可以将当前工作流配置信息导出为一个json文件,点击确认后可以下载到本地,后面可以拷贝到待接入的应用系统工程目录下,应用系统在启动时,会初始化工作流引擎,工作流引擎初始化时可以通过本地方式加载上述配置。注意:此处是对单个工作流配置进行导出,如果要将某个系统下所有工作流打包导出,则需要进入到系统管理页操作。 - -点击"复制"按钮,可以新建一个同样的工作流配置,然后后面就可以在这个工作流上进行相关修改并保存。 - -点击"导入"按钮,会弹出一个对话框,这时可以将在外部编写的工作流配置信息复制并粘贴到这个对话框中的"导入内容(Json)"文本框中,点击"确认"按钮,即可完成工作流配置信息的导入,注意:工作流节点中的action_id的取值格式为:action-{场景ID}-{工作流ID},导入工作流时如果相关actionId中的场景ID和工作流ID与当前要导入的场景ID和工作流ID不一致,系统会自动帮忙将actionId中的后面两部分的值调整为当前的场景ID和工作流ID。 - -![工作流编辑页面](docs/flow_import.png) - -### 7.2 工作流编辑 - -打开工作流编辑页,系统会自动读取前面配置好的git仓库中的代码模块,获取代码中的算子列表,并在工作流编辑页面的左侧算子列表中展示,供后续的工作流编辑用。(注意:工作流中的条件节点已经在工作流引擎中做了内置的模块实现,所以无需单独在应用层再做实现,当然如果有需要,也可以自定义条件节点后,注册到工作流引擎)。如下图,为工作流编辑页面。工作流编辑页面包括三个区域:左侧为工作流节点列表,中间为流程构建区域,右侧为工作流及节点属性编辑区域。 - -![工作流编辑页面](docs/flow_edit.png) - - -其中: - -在后台管理系统中打开工作流编辑页面时,系统会从后台管理系统中配置的代码仓库中拉取代码模块名称,并以组件的方式显示在左侧组件栏。左侧的组件有两种类型:一是应用层节点,用户打开工作流图编辑界面后,会自动从对应的应用代码的git仓库中拉取对应的算子列表,展示在左侧的节点列表中。二是工作流引擎内置的条件节点和缺省节点(当git仓库未完成代码提交时,左侧无法拉取到代码模块名称,这时可以手动添加缺省节点,然后给缺省节点重新命名)。 - -从左侧组件栏中依次拖拽节点、添加节点之间的连线,可以逐步构建出类似下图中的主流程图和子流程图。 - -针对图中的每个节点,可以依次点击节点,然后在右侧的属性编辑区域编辑该节点的属性,如:节点名称、节点参数、节点描述信息等,完成所有节点属性的编辑后点击"保存"按钮,相关的工作流配置信息便会以json字符串格式存入数据库。 - -![工作流编辑页面2](docs/flow_edit2.png) - -### 7.3 工作流配置发布 - -上一节中已存入数据库的工作流配置信息并不能自动发布到在线应用系统,需要在系统管理页面中点击"提交"按钮,将上述配置发布到一个具有存储全局配置功能的存储服务中,一般可基于zookeeper或redis开发,以供应用系统从配置中心读取上述配置信息。相应地,我们在工作流引擎也提供了从redis、zookeeper及本地文件中加载工作流配置的方法,前面工作流引擎接入部分已有介绍不作详述。 - -### 7.4 工作流导出与导入 - -然而,由于部分应用系统出于安全或者降低外部依赖的角度考虑,并不希望通过与后台管理系统进行联动的方式,动态更新工作流配置信息,因此我们在系统管理页面提供了工作流配置信息导出功能,用户可以直接将工作流配置信息导出为zip包,然后将上述zip包解压后直接添加到其应用系统的项目的本地目录下,应用系统在初始化工作流引擎时,可以直接使用工作流引擎提供的相关方法从本地目录加载工作流配置。 \ No newline at end of file -- Gitee From 8cd0b67ff75f3acbaf9d40c4ea1e207737a99581 Mon Sep 17 00:00:00 2001 From: dayunzhangyunfeng Date: Tue, 17 Sep 2024 19:52:29 +0800 Subject: [PATCH 09/12] update README.md --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a9209a3..da1f41e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # tg-flow简介 -tg-flow是一个专注于在线高并发场景的无状态的工作流引擎,它可以在保证高性能的前提下,为在线高并发系统提供复杂的的工作流调度能力,目前tg-flow已经广泛运用于滴滴内部多个核心在线高并发系统。 +tg-flow是一个专注于在线高并发场景的工作流引擎,它综合运用了多种优化算法,可以在高并发场景下轻松地支持由100多个节点组成的复杂工作流的调度,除去业务逻辑之外引擎本身的耗时仅0.1ms左右。 +目前tg-flow已经广泛运用于滴滴内部多个日流量数十亿的核心在线高并发系统。 tg-flow 相关的开源仓库总共将包括3个部分: -* tg-flow: tg-flow的工作流引擎,[github仓库](https://github.com/didi/tg-flow)提供了对工作流的的调度执行能力。 -* tg-example: 为了便于用户快速上手使用tg-flow,我们提供了一个示例应用程序:[tg-example](https://github.com/didi/tg-example),如果您本地拥有golang环境,则下载后直接go run main.go即可运行。 -* tg-service: tg-flow的后台管理系统,它为用户提供工作流的创建、编辑、保存、删除、导入、导出等功能,同时也提供工作流配置在线自动分发的能力,可将配置分发到redis、文件系统(导出为文件)中,再由流程调度模块自动更新在线配置。 -* 注:用户可以不必依赖tg-service来管理工作流配置,可以直接编辑工作流配置文件,并保存到自己项目指定的目录下,供引擎初始化时加载。 目前后台管理系统尚需进一步完善后再开源。 +* tg-flow: 工作流引擎核心模块,[github仓库](https://github.com/didi/tg-flow),提供对工作流的的调度执行能力。 +* tg-example: 为了便于新用户快速上手而提供的一个示例项目:[tg-example](https://github.com/didi/tg-example),如果您本地拥有golang环境,下载后直接go run main.go即可运行。 +* tg-service: tg-flow的后台管理系统,为用户提供工作流的创建、编辑、保存、删除、导入、导出等功能,同时也提供工作流配置自动分发到在线服务的能力。 +* 注:用户可以不必依赖tg-service来管理工作流,可人工编辑工作流配置文件,并存入自己项目目录下,供引擎使用。后台管理系统尚需进一步完善后再开源。 # 我们的目标 受限于在线高并发场景的高性能要求,传统工作流引擎很难支持在线高并发系统。 我们的目标是实现一个既能够满足在线高并发场景的性能要求,又具备传统工作流引擎各种复杂功能的工作流引擎。 @@ -26,7 +27,7 @@ tg-flow 相关的开源仓库总共将包括3个部分: 3. 再进一步,如果需要使用tg-flow所有功能,可以在第二步的基础上,自行部署redis或zookeeper,然后在线系统中可以使用tg-core中的核心模块定期从redis或zookeeper中检测工作流新版本,并及时更新到在线系统。 # 用户手册 - 参见: [user_manual](https://github.com/didi/tg-example/blob/main/user_manual.md) + [用户手册](https://github.com/didi/tg-example/blob/main/user_manual.md) # 欢迎加入 请联系:[张云锋](https://github.com/dayunzhangyunfeng), [周子纯](https://github.com/zhouzichun0315), [唐桂尧](https://github.com/tgy931) -- Gitee From 01ea702ffd487ed928e5d1989a662b5de920dd9c Mon Sep 17 00:00:00 2001 From: dayunzhangyunfeng Date: Tue, 17 Sep 2024 19:55:56 +0800 Subject: [PATCH 10/12] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index da1f41e..08f7be8 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ tg-flow是一个专注于在线高并发场景的工作流引擎,它综合运用了多种优化算法,可以在高并发场景下轻松地支持由100多个节点组成的复杂工作流的调度,除去业务逻辑之外引擎本身的耗时仅0.1ms左右。 目前tg-flow已经广泛运用于滴滴内部多个日流量数十亿的核心在线高并发系统。 tg-flow 相关的开源仓库总共将包括3个部分: -* tg-flow: 工作流引擎核心模块,[github仓库](https://github.com/didi/tg-flow),提供对工作流的的调度执行能力。 -* tg-example: 为了便于新用户快速上手而提供的一个示例项目:[tg-example](https://github.com/didi/tg-example),如果您本地拥有golang环境,下载后直接go run main.go即可运行。 +* tg-flow: 工作流引擎核心模块,提供对工作流的的调度执行能力。 +* tg-example: 为了便于新用户快速上手而提供的一个示例项目,如果用户本地有安装golang环境,下载后直接go run main.go即可运行。[点此了解 tg-example](https://github.com/didi/tg-example) * tg-service: tg-flow的后台管理系统,为用户提供工作流的创建、编辑、保存、删除、导入、导出等功能,同时也提供工作流配置自动分发到在线服务的能力。 * 注:用户可以不必依赖tg-service来管理工作流,可人工编辑工作流配置文件,并存入自己项目目录下,供引擎使用。后台管理系统尚需进一步完善后再开源。 -- Gitee From f2ba3fe4d50dd68f1720625c1c3eac492a4a9fad Mon Sep 17 00:00:00 2001 From: dayunzhangyunfeng Date: Tue, 17 Sep 2024 20:12:00 +0800 Subject: [PATCH 11/12] update README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 08f7be8..2e5489d 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,9 @@ tg-flow是一个专注于在线高并发场景的工作流引擎,它综合运用了多种优化算法,可以在高并发场景下轻松地支持由100多个节点组成的复杂工作流的调度,除去业务逻辑之外引擎本身的耗时仅0.1ms左右。 目前tg-flow已经广泛运用于滴滴内部多个日流量数十亿的核心在线高并发系统。 tg-flow 相关的开源仓库总共将包括3个部分: -* tg-flow: 工作流引擎核心模块,提供对工作流的的调度执行能力。 -* tg-example: 为了便于新用户快速上手而提供的一个示例项目,如果用户本地有安装golang环境,下载后直接go run main.go即可运行。[点此了解 tg-example](https://github.com/didi/tg-example) -* tg-service: tg-flow的后台管理系统,为用户提供工作流的创建、编辑、保存、删除、导入、导出等功能,同时也提供工作流配置自动分发到在线服务的能力。 -* 注:用户可以不必依赖tg-service来管理工作流,可人工编辑工作流配置文件,并存入自己项目目录下,供引擎使用。后台管理系统尚需进一步完善后再开源。 +* tg-flow: 工作流引擎核心模块,提供高并发场景下的复杂工作流的调度执行能力。 +* tg-example: 为便于用户快速上手而提供的一个示例项目,如果用户本地有安装golang环境,下载后直接go run main.go即可运行。[点此了解 tg-example](https://github.com/didi/tg-example) +* tg-service: 后台管理系统,辅助用户对工作流进行配置管理及发布上线,此部分非tg-flow的强依赖模块,尚在整理中,待完善后再提交。 # 我们的目标 受限于在线高并发场景的高性能要求,传统工作流引擎很难支持在线高并发系统。 我们的目标是实现一个既能够满足在线高并发场景的性能要求,又具备传统工作流引擎各种复杂功能的工作流引擎。 -- Gitee From b2fba4d8b1859cc4301d27012a1644dc2cae9679 Mon Sep 17 00:00:00 2001 From: dayunzhangyunfeng Date: Tue, 17 Sep 2024 20:28:37 +0800 Subject: [PATCH 12/12] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2e5489d..47280e1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # tg-flow简介 -tg-flow是一个专注于在线高并发场景的工作流引擎,它综合运用了多种优化算法,可以在高并发场景下轻松地支持由100多个节点组成的复杂工作流的调度,除去业务逻辑之外引擎本身的耗时仅0.1ms左右。 -目前tg-flow已经广泛运用于滴滴内部多个日流量数十亿的核心在线高并发系统。 +tg-flow是一个专注于在线高并发场景的工作流引擎,它综合运用了多种优化算法,可以在高并发场景下轻松地支持由100多个节点组成的复杂工作流的调度,目前已经广泛运用于滴滴内部多个日流量数十亿的核心在线高并发系统。 + tg-flow 相关的开源仓库总共将包括3个部分: * tg-flow: 工作流引擎核心模块,提供高并发场景下的复杂工作流的调度执行能力。 * tg-example: 为便于用户快速上手而提供的一个示例项目,如果用户本地有安装golang环境,下载后直接go run main.go即可运行。[点此了解 tg-example](https://github.com/didi/tg-example) -- Gitee

        S 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≻zL!&O2{I?KolKmwasZb8cTasBy$I_J(;z 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�hVZU+E_Ywov2RSJ2_+|#wHd!wCd z4^V7(sZ?YLB7inSmLq;Ep|($7sCm|ADKD9~4OZk?sCLE%!9B)x`^RroBT%dQEI2tb;DP{W5nfe+3DkmpwLfB*m(yB5b z#rWr<+vF0^b0(TgY?`6VRH-}y;M3!6nJAhWs9q>iG+u7Oq zMe6=T_0~cRpCVu7RMMw;fNATQ+5r^iU7F(+Et78fU9)4SLQcpIk(tyj_Ucu=zQT{? zKTnEUOob3V^T=8C)O%yHJl3$xALR0&^{YM<9X55?_t7%AQiiQ`Vxp)HFFQoRXDs1{ zhnNduw@>&J5lz1SL- zwfQ&O)V+gZ-TfSjl%l?Bw9G*LR@pr2rcWtwzDrv7s{Y1Box^3H=(l%o)4uxdg4v9= z26bPqXp(Iw%!$gKL)Vmhj^N-zt>n$H+vHn z2Ls1D`cZ=ImE};*`Q-e$h|KxnvDe)LjQ)E^yySl9(Un0Kt@>Qx_6FtErcomtqPJXf zg!fL>{IaAd(093h;&|`hcxxnyhoEJtApR8!pW^ydE6;kA+xuzD5Nknm^NIg#Og`5i zz*kU2!_w!ndbDolo&AgWs;H)+%Nq}zh)xwpcx}yHnk0TNFvoi;Rp0grAaZ8hVZRY^ zHe4hV_V=&;j4NCTLNp5rZ8)2` z3X~JxOGCkj!j)d()%p#~t*i`!#|rZB5NARd z#B>mo&T;o9-&OD5zNQjKsvJ(a@4XL$CX}+GT`W2@tt6Y!r+*%SOA4T@T{M_3K3{dIe5=Sw;&KyYx%1_0Wp}%?B;du4VULXBG!$=^szZyY;tbmGtNVCm{E3;oGg78 zTXev_)p%1djC~EfnwJppN{iQ7tmegueVGY;!>SsaGz~3cs-o3IxlC(P*XEol&3H*u zUORBR3~zL;>PzcUxBX8|HW1ai>lpj>{!y%c5bXTwFm35J&Z}E(AywW9P=}5)n^mno zKXYPf?THJ{54-92ZhT@ULGbPkGV;+4nk6Ay>nDkvnHGflK6IR^rdJD9e zTfX4NjavH@w;<3Qq2zXC>@94@n1s4@pOJ`4N#2)9Bj~Z#HD~!Zy_iXJ^sHTcQ+%!& z#L+4gp3RkS=x{IsFCB1LQJomfo0hg+;`Y#vqoO(JS~N%I)QDwF z=Pc&i1>H^Z_(*F8OUMweZu&U*H8Qp>MubOy6BoVk3DkAy#OHmk7f`p$JhQ=j)s0S% zb1wLWl%Hfy2eWvf6&}42Cd>k&iG1Pnm(yjdMMyzYrFIi zW5^S-pg4}-V|?CdwnMm1uBiXF%ZG}TH{v$h4Op;Xl{fs3m>Dohq+SN?AN1pdXEgqB z*U^m)d1TGBi{2u3ifQx0_`F3oPS^=(3djGQt0pk7jL$m-z-k}iHE9-_+j5a~&>Vk6 zLZ2Wq%I?J%qO)(rQMktSx?0T^`MabX3FRVG>U<{-v=}x1fo4}+Yi%0p55=qgbt-?7 z$9`I2KI*X{4gpF}`?f>_?CBD^PnJP?GetYpyR_rF^Sic`o+3%&mHnGCns{On&F}Vs zXJj#^C?(^MX(y|_f$oH^Tl&I9+oG9oy~A3uV95rX6h?*6h0zJHrl%e8ZDFmQIy15M zRkFn%ZifawG=tZwQD2EP)!09Rml8|0&-d_ zY1$A-B#crk-xdpVvItZPytRvOS=>+yhgE^>CBd>c*eXV~4xTM%uja`&CY?8perxi7 zjg<}~AY*J-pZMqKkUmyh{vwjmDeG5LMZO;pTN_(j--z}q6;W_%6m&b>47>gq-&E7| zOQ9z7vN0E;J*_qxruPMb=Gq!6saseUn@Pli9~|1Rw-`y{)pqHOP3_QqmuF)%o~`WE*((dKQ^BYP?Ui7uvV*(S&Rkzf!zBmHimRp|M{q-vmS) zCnoPsc>#`{Ai)!pJ)(=FJh~jGD=pnZZ<>{Pc^_21A_Xo~UdE7b1ENSj|C39H+r7?h&bha$PSxJKI`o5r2N)RqmxR!Ov1L?a@aN5l^2yNHvIJ`TpDF; zlSAi*FS7POlB&LcFHQQcmKFwae@ibYe}kOtfl$ z$Wr%s=?VMJUX!}sgH~K{kOll|fN z1!wg=dfzZJ_#I1EUrG{Z^_xWjhb7Q^BjI2<8=T|Oclu4aEqhi~oTE;9bK9%NQha1} zu3$cAr!}^N4W_r}AAp9Dg#8!tnLFNi2UjC}z1sU4tVE&Glrfl@tfC~N`Ch$lx{3Mg zCg>bdf&<1fBH0zLE!K;DTG!D{%Mm@=!I3@d*b%+m$77g^W(4Job+1L!8PNVv8uu+1k z14q@Bl(P3ioSd9w=?QS@o;IT8sA;12l44`A9m{e+AP{9wPY;NLgZA-NfU=5C1bUj} z`f2^eHHH)uS(gQgMAoN|@hjt%M#QsA-Fw~aQ&ADrVzY&pS!0Rqcdiqf8FORlX=pN- zhl2wnG0V%k4q5*GaE>>g+Yg*JwP6cZkBH2E&O2LM*-Jfq`3xf?%*v4*Cax_7RDGlC z-QD$@J3GW=mFj3{VF$nKTB@T+#G4k)sTQp2z^@87ew_4Jfl+y@JY!sdxUmzl${#D? z0W)>AwKC%3fpeuzO_JZgPmj#Ik%kvQ$_JSWFM^e~wGjyGP+`-MubGV@ohK(1+~~LU zi`JnV^AbUl-4UbC(srRWKYkcA4W+SShxXGSnJGBeLb|Z_4)+tB5)xu#44m@ZG6OoK z7pV}nA}#4(J3L?HpYeP-x*9AX=FF?EmPJ89HMyT=mZfA_B*UM_|DkBG%n(L9&QUkS z92Xb2{Ztt+dfDRjxc&9UGZ^q51b(%hlEt;!((2qIeRc}n+}ctQEEE+L-R253ziwZR zb`rGZ<0Pn}wkuuqDL`7iy&=`--+z$yY)dmG6;xTRK?rQ%H9+Jz{5c=RCznuRg-_R<25ci=?+r(SV`dB|h{1?rw8pi!nCb z=M@nVQFqj%tM>g<#hc9`qm%S|b0s*-7~k8-Rc`rJLE5|IvL4<%D`hwtA!H_IKMO!F z`o4|NwFi~*tkUgNxewKb5zg81bDl`{KzoLQ6X=$YZMZnqxbwVU#kqAZtFl_6 z6MuWC(uhtjxQ?iEYpUGq%`;p0(DPl}BVtieDI29N%=%h;-2K+V%2VQAd?l98GlpQy zn>WOAl1Ze?a*j+@Cc`TRf^W3&$c|)@Ws(%>3EBBlb*F|^xzJ8@Z%UkMmo8&`-PT#Yf zp{rdd8%9At?WZK}*5G+D-UssYc;4}OGCS*PyVY`tc)rtnLbb58Nt!epO&&eEEFhj( z9tV8dpB`xSiW=Yu`9}V*T23Jp%Ow$*#^a^gdfvB^=oNLy{xBD6cFmtqfIK(%bZ~;@ z;N^Df24*#p7R=;!P%UF;9{?)~(g@xT2oCO-x4oPC^l4&glKJ^&d(8O0*2;dpn*mv} zGd3=c`T1<@xnZx~2MMpub0^byKam+z%#M!{(YWi*{Z{a4?Ag3~zP0bFpVj+R@vIYd zOqj%E0Ch}|bIIDF<;GYKRDON22L^F*nYRk!B5ot1KDdsfcMPG=94$5FH8sgjm+P@U zWCwIMm$&KV%(|>B^KajupX)s+lOZ~Utxhpqy$x+hd=BwJoS)}0etz50GN*aCtoTe!wvG*yxR@GC8M3qFhjF()o3I@3DQr?U1JFDd}=F>qo9~ zw@L1Oro!#IDvfjnXVF=$rlJ!-u35G9t7uYLRh2fED%bw<_ov%l&Zqtdn2Sjt&mp7| zIZwgr>Z*|_!HpZX!0R+YBsV_Yb1Hc+uk^KMtA*();Ucq7e|Zi)EKL@Cyb#fg|46`! z1AdUDfB*g4$re@Qtox}iLO*+>TklZ^lL}Dc(qUkTgttWR>`pa~)IDTZR9qYyJm zGny{i*P1oB2U!;l4?$uI}T;AVOC)wM*6CV1NswD zMuY;VkDXaCCmmfM2^E}VIgXXM;wTrLH&Xo76*{-)CK5?z^!v4$XS-i3jTUixz^KRw z8U8W{uGrX>A3Xpv(zDc5yKEfsQ*~_HevdgaTiT=T^3-PqVDh|@(bget%eiKQu!cppkWJ5wfttdiYY;TK2H zAj4bun*mWP^^CO(LICAJhlvi?^U}JJmPo%bEkO@0`!ytX&bt27>^SAf(c|+me#Sn< zwD(^a6#AF89ABkw@rhB~(BGAb!dQKo4cXbavW)7=&Vt$W-Wk)2mf*p+K%XCWX1xNF z=B+$EU1PG|=V1yGeV~3gTzOJocHM~D>W|6oz08$yTyLG4nG!*|Ojc2%R(2J<4m67( zcEf}78zm5j&*T7Eb~#j%%`AtRxnN(L?nEAL6EZL}i@w=Ud5_pdE+A?)mXTDtP}@Hc zS2%nwMOTH~*;Hwli?&L5^XPG$vQvo-cm4Z^rD~X*F8b_%cUrAduvNp!>!0p;?^r?;n(y$t&yuLgCoB!^&a$1my?c~2Eyw) zK^(`-Tj(j_dK3e$;ls7>C{^9#Royg(Xxrn`ab9Bqiht?k!@@?dgQQx50wHSKj7fez z2*g9Hwq_pLnXT7H9;A@wyO9!l97Q~~<#cYNNKb2Z zxy;sPjKfmScUT93-cAKnXNqliZ$y0V%)uq!v!m#rUw?Qm3qiYHLyaV3+mgY*-bH?p2?I{&s;H}}P^EIP6Jgo!t(!Qw2f=}uPJdil z7YUi`y8sblLTuSFNrP=k#A_DmNh;CnJ;1WBx;x6!dGqo5n8gueNzZkfoQZ*P4-$CNfU*YNE1*!6@metUk9GP}z15cK>@B zfOtm-=C9(JvH%rwh(scCU+%rMd`BdZ_ZRM1>8>`pfcq~b^&lnPUN16YV$SPR+FB+3 zMRN_-oR*V2U-J@^@%V>s7(zdOlMcX>|9&bV_I<^s&(&~efk zc&LnaOX83hB!tuCXrROrE+=!M!J95^K6>7FQ8w$6ftnjI%G4JaX#JO#YJrr=fO?G- z4g1}n;Gn(0aQs4V%+;yI#u+r5kk-ub)>zGv<3(#bYCc&(X6zMoaR z4xh5Qtah|s3MURnqDFc7RG`#(Es8Ae;b^s~LuKElEV<39bnv)R7h}XJd6jA;dO}-@@NGd zqqgB}4&QI}w0&ag=@H*1Kqd(g)CS7cqOX_?bp+v{B}H5xZ5F9iy}A|(|0{|9cPO5Q z^RtkIwu#s*H4?VDnkDPSFx2x|m+u*~)kL>3ISh|Ta1r7nJg_&Gl;CCyY218j9@}*wy(dwA#U^23)cE-Zrwb%bNJ@SI zOY`|nI@^Zw7h7CIzU8+e$dT9CD&3=tqldgdQy7E0ep-vr5DN4~vp(@C`Uw?ojBu{R zGKECh`0S^01> zRxn~&XX^9uvqK9JV^@7`Wg+hp;gw_e?4-TtXPG054f|3_l|Y2#naM=Y>xtWm#kOCy z&saLeUco-sd~^N1Dh$;c0%soX+hj@&64V?V&trr9T{4L`MpvOGZq`n&gp@89yE_um zKj$`Yi1B}|(?c*3kVWghk<-`r0&aGdKYf}aGK-AF?pyiLZ7=tU#3=Dbo3GZU9GNqJ zmY(I^dudXxf{4f&a<50HjSujmN=V2c*DIm&*SG9 z35Yvr)8-Y=78>Wl(NQ2+T|Yw-?2P&*OG_kM)GVk>-Oer>OA(i_&HGx%p^aO&f}*V* zGCrPe=p}eGvJrI*W}#(qlz}d-w_58YV3h=8kh5=0O^ZHk8=y@lGN-N$ld=t?^Pi)U zRMBppUYlD0adAM?e75hTHZ`b^}+RR_~u=Au_QvnW)}Ljg3tvUXSj4 zBIpogmrxABb~y#PuP4G`PfJaow`BD^5d?=1x8qBN4)WI-7+gy( z-eWg5zIZehs>s6+-X&AcpPT*_Z}lyZCb%C)5!x41+K2G-bH#gOS9$g{m(~qYs&)pD45ORlL z>utea&Ca$asXYoaRkMEHfiLgXvksK7@?55r-SmKURFa0=zZ8kJsiI4wZhLbePB~0( z_%;P@2cjscgq4)_tPI5wXh3q7D z!i>yz2h;p!i6K;1V|DJE$7MZv95W2ux-ny`6s;d4HZfHS^BkmwL19MVhwyaws;qzk z1t9M?InVY@Qh`kJGQHwoQ`0hdJgv=9j+iOQG+|=Mj*0aBT&f2@W?E@=q?$V_`t+Xf zbO!obuHC_#9+*Nt{v7vSDZL4fLXaxs)!BQ1_HLCDQZ>u*%p)GHK0va`_7;TA`BC+w z|CF%X{$ZvxZHAeEk9EbW6DMa~@;{%SIGDOA^iy zjlPCOGO%G70aDycamG~^c1V$VXv!CNX#|hZBl9-$*tm)eaoz$yvLVM6V?$%rp~~`F zflCSD=7?+S+^CNlzwGR#RzU89nW$WMs>@lX3&t2yYdd>ee<+vJkw-ZFm{WgeruW>4 zEMJo$n2FV^C{Vc)!dFEtC<}F$gCivi5X?H^J?sPbbXGc}#r=qd*H{~nJ1`J2uq)$% z3T~XMYNQ_C@NT))#z0WXS4!K&RqUM_>T*ly7 z9Y=ZEe3M~@eE>)OD9aa49U5oMIpo}ALvy08jn{}ldCarc+OH9(!RHh@iB*9n3 ztKca~vCYkps6vl=WwgFp`yow9r~sdqxxs#`iaMKbFXZtzXB%6}a;?<@?f(AnO_**x zpYi8)#&QaS?zn<;8KrJ&oZ9eNWX^+PE*~rO`qfL3gdu3YrDTX9mqPF;4_$R%I-lD- z1of%O=iWw6@-6^F@cs2gKE57sBO}=>(kSC5N9EVtoCUnIXqWrqjLhU-{(@_6Lk|+N zE^3^lEz@KZX81ueeus3zSD|ISGB0V;J|L!}9=uU5Y$}bxE^{R_3 zH3o$Sn#e)TSD3xLRmNso12VLPO z@7r9$oH0P3=L17^t5zkHK3Ml&2DD=7A3#T17 zltb>X67j?q+g8XiIdiUXdH7S+I*1qIC`M0mwrV)HU!QS*)?@qz7ql3) z7AnWr(K&kba}_8i1k+dDiKVgf+xY9dg5psct*X^N*DrMf8@mUEUq9}t9%}jAqI#h4 z^EHt!b~=CH`aqFKK-Isos{fFXd3*o_DFq@%CKC=pyhC^*wfB&TkHS z40i%Q(2^MFG~zBsT^@4SyU~Ps31kel>S>#rlLRt~@@e#K_dDmGb6(?F(b0j{oospg zY}hVF6jDd`H|xvb>f5h=obhcx)PEEx-0NxdOhr8IKU|pHFYDQIEc-$}r>qTzOlUK+ zM2}&$fjw2#`$t-j7gxNu4~*s(R%(iLl zwyk`_5ES;10Big<`>UA$8q{_f@5hnY3V_cSlZp{7&UK`Yseh#5 zf8Rt54p@)xlnS!>$iMKNSqCB~uY@F0lK2D1S|U5|n0TEJqA&MnCWWheL+qBBO&_yj zhXw}5i9rE*0$E_+UCBS!;D;Cs69_L)Fp8k1p<%Hd=0(A}3H$lIIKd8u<>$|_IXRS6 z-LnsSY=$JpwQlF82AUGzHzXTuJJ;b&jkZaCkkz5G7H)3s2o6eJ%qRS3b$>ktGB`N0 zD9K22K^AhKhf2>qS-sfK&d%V~@6%T_A3}Nz*>0b%+k8^j*qOU$Jr@jfl{pZq#R^Mk zsv|hjJ+v&LRpFu~dEKEV=7~iv!%PudxYG*?*ioq0Z!DECe)sCykeVMpM)Y9E%4MgU zYl4XI+XT(V&tH|a6i?zY{AmSzgYdJ3b$2@o*|6Crx5yu!qutyaiC1+{?(?WjE(-{5Adw0?$NMkQFI2$Z-cjO^TKPahZ^XpnBIVT7C*=rXOAQST ziDA#DTebmFQ!EAN#MD%yPVyaM#gygQ1Wko}>`C`Hf4M(PruQfy+ zjuLu#Yrl4RT9`TT0+vopPEs8nl1?m5F*guc_S*KXtzjAY^j0XEQmAC`UqCmhhHaQp z*?3$|E+)A5s{Uy|uJlMiR`@=qmfP*rf;MZ^>frVD=~eNc$W$909m2Vp2eb;PY-L%P zx#yqI=!Q{;s!D1;F4ilhr>83`DY5hrXiu-Kuu2E3Q~_$LszCW!Ss#Ay|E5lnqD*6@ zX>vY#Pgz`FFQ$;mPxXS^#7$HwpWZ*%zp^IW_%Kt8R6V}i^gpw?LHdCE^TS+|OL?e% zDk13lMCbIk9#-J7B)OO40%qv~B>B)@r)8R&eTp3f1S0hf0R+?&-)?QiCJ_3Dyz{Mo z8(n#d%)rXnH9VowyS08W;e}~|nCU6veSLo&@y(%WQTd}f^`}q!-N;Erul!j8KM1zW z6+Z*bQU4K-$mHSPi_adfRGYC?BRSROTC(QHg*y`W(l+i3DGOpULYIlCA)J$r_cUQkZi?tEgS{+#jT zO+a(Z9w^3X<=2$pe@g2AOrV4hw$U&~YBudgL`%8MsjwTIP;RrOb#9x1)qqMPI0T5Y zvZegwwHf)XeSw6HvVUwSm{`^17aQV6oX3Lt1WzW)m&nnp?K3vdt3 z+>4)DJAW;wEYZ>PL41^N#F(5axi|~c09tBw_)X>Bc4E5tZ;s+$Nb6s|i$UNQHzIvI zSD9yQnzi3~i;m{-Tjq2A>$N0lf zI^jZB&u8>J-f|S6W(P{`N8X|F=ix}yLE)g%{AXwO5nH{Fbp{HcFpeOqUM&2*>sx9l z6reqw^XI;BqK~aF82u(oX z|5jS6WC>=<|5U-a{1ka!uYWPy$F_|T#GTTKF%5@qQR>jh_bvR?+C0i4Qz>>bBKdop zyA8+WUcs38pp^*JFf1FAM#a?Pn_f@enXL5vbOq^8K!SQQRRFp^EMf_=lpVsM zfqgWuGM5N7K-|A5*9L^CW-0o*4@Y8!ynl!Vw5*?+$TGcuw>%7I*NAm*UR***mp>aW zNoQib+H2(mI=B4x0KG9J4Xc2b7#^g@p0--9Uu{Vl8w!Cy;oZdj;KNkAUG{rB&5%>t zc?}%1(TXLG=vXkb@8GL{p*TZ9)E28t83IJ z6oFLpRyuOMNB48r!Y$_q+i{<;4L!bXKrggC^d9C+K-?4W0@?3PmJ-#d38VT~M~px} zowe;{Ii+uVcTR2{LQ2`}T$^J<0x4R79~rRXjx5{olNMMzPRRz!X8~Jp#==lLtvQ6( zAUJqL`!UP=rT^pUEAO2qZEor%#OL>vqKGoR_u7zX1sN+ zD_i_oq&jEy{?`XED`2~Kwv8zSh{Co=e7-)FY|;#VvMVSy)+;Tw`$9FcxLOpkw?DC; z&$`fOpd%3Jx#PCOld1D;eSFm_P)9^Al{ zAC@!FKU~v88+PD)3 z=sy~Gs2cTjwOv2~vtB6;5!~M3vi-3x_4?IyL?5GeU0(%SHuJ{g3}B*GR_V|i8QrO$ zON5eJ6>2mQO}23rPT68izj<~ZcvPi_x`?GhIMLT3J*l&U+x z)9cgH_D00T#8q>_vIz)8N(WQ*h7Jx6Htw+3*3=}|+`Rx=HOR6LBR1YtP0oj)BW!WJ zB?cZ@hd}yT!)5ItAcr9j9gmjW_}H%#Tz$d2@hGU&yLa!tR+Bn&0Yq`#1b9BXxw*Y? z5CCqwb%KQB%2Zaoy*6#v9+^&-~9f1}7woLv2!(?mJjXpyFHA+X*OeUR1VE++P zQY0a19AU71KT3qv7#bn9;5Da8b=*f0NU8Afm*wWUX0Yt_ zQa4f$z7BcVyU5cjo(JTjqN13WvMP1V!Eft^2_;h>XbwS52RaXPON7G(9p+fFFWfTl z(UUQB)BC;y(o8YD72%V-Rq$NE0G zypEEPqMSF0V1zoRx-Z4Zgu~1WU$|@!2=PoU$Y%_AN}6R(g`svO1YK_&FqivH@W|(d zvwJ$v?L7dUIEN0YKQJks!@N}NBAzvpF}r$fVMuh|fzT*PD$TmF6&*`JnBPN&GPj2+ z#09(YMF}|Lxht8@hBlBH2JMoibKTxI>NKRF-;NYPNX#8W$5La_L%`&}j}}(_@8s7# z(Kqr=wF^_@s$5!dN`L+%!K;`?`uQdYDciTGtc+u1|KK1#S2BtS>oSnl(?AtqtcNIi zx-0h<7ngu8ybK!wGmsPk1;=K1%iNB5WbNjm#1>Po7i5;F@+7`$E+?=Z#(s~wk!K-rn- z`UV_UebKn{U}{KN!ua}I_Bt;emnOnLC6&4X2mJ#HdT^e}`yR9s0?}`$C#?qpyKZjT zO+PtG@eS-M%0iV~6FP70^*>uQYGg~vpntLNxb+X|s;`>I4u*CjY}{0NVX7gC#aB|) zrFhWiB7HKRe_;NEKH&GN?`K~rJCa3r<#uy0#%mX={6A_aG8s5rd0bMG4Y9Ru5&WQYH|HXu7I_uJQf0+NN7cV7tdP(qc;`5O#!d@yElV(UbF^gw^pm`ebg(%O-z_iX^kg^NN_4H-ituj%!-3$HXk zv3|p_?nZy`jb@nq$cFVC>7oD8jRdKos7rAwJ9n=lGE?`|SJM9PsOD5@z*i zOe!=1#X>D6@yvi7U$}6ihF+kzrTK)vq2d!C)G1TPZ)8{e={Z@H7SJfeR#sw05U_6> z5-AMD51=q&@ROY$V>3`63vLf|=&2+LtZ1E1h%c}?4`UFa8H^q3Q>t zLlv}{kNO$~6j_}^%3rRS#v&qs$)AX)H&uE!z!?d_Nk z%gX)owVV_n5Vy}mX*D7uLRwtBXIMqSXLNKFF9^>2>2~LA#^-TH;sGcZdsql6n^?(>&Ldj@V$Wj?XTBe2r585b%Mx2l!r`)5bG{$+DpEf)W!m`r)2j< zv+wVm*_;?SZTap!P1}kbh%;1%xO|I@4qIxU&SLq5*qkg}-dn3Ky8Qa8DZEP1X_EG? zEa#_Tih`a^b**Y#xFfn|T|2!|jlbc@FG*6`Lts`VA3RoXDtEOluPBAv5gYBfP~hJA zNJ&ZNKW}9MOyIO!rMOZe%ka^3d&BQ#MyF;kxudZI2pZeL@82M!MtZm?QeIk{mdnKE z(_UhUthr0GRtG3v55eTBLkWR64Vd+5OYbTFp-1l_5KF3dlG8yC1((Ohv(S8#H;|;S z~en*wd;_OLyK$7H!0tOg9`07gxx({sha*e&|$2 zIK+N`asE+72&pL;Zb9GJmKaoIvD=d9UuNRfown-h-8n26(`zoyH@HpYd ze=T~C5T)($&}K-{=T3#HhAZext;kGRB%Ub!B&sN`%fYPOm|K56hR%E1ovh?7Ctv_o zH7ASNfNc;P8EWXKlE^30!YcTwco>oAIdv*r;cpgVF$s!Y2QISIl^Ha%&|yPFl4nI1 zZidzRQ6#43b3V`UPifBBg`-K{+SPoyPoxpLsEj|I=^*u!SXrjcxx=y{c758=@w-et z$t&!1*12^MBjR^@e;-RZE@TR^7reCwy00V==u}s4l zSjdP5nY7es#4{1noexjGN+$tQZ@;D_SXcwAJ2p4BiMfo7s6c+W`Ov-1oyX9r-iId)-aqA9?8V=o z%e0wl(s^&ktH*TZJ-PFBKGD1EqVY6?%}C;H&iyVc4}|f*^U^ty{-o& z>CH=W5`x6mCjj^r_l}%C$v#2%C-m-5c6P|{T2hVfcXD5tH@?_00tW2^1F$qQ!67@F z@G;B3f@ckkpVYHKCcjI6&Rx9!137TzLaG4E)pa^clSLN0+m`#Mdo81CwKGj6YRjPVYe6AH@Bg zi=Che;v7!Ts#kgL9DhL6e5>#TJbm6|_g>C)x3Fz_h>f?nQq36Q$aQJ^oqM1;hr&QA zr57N#m&Ed*Nw3A3)R2Jz;TAz6&1A<|_6QrIsJv=N#6$<80ZmeXzZvluc-AKN#rUOX zIskWo)#ZX{UEz2v-L`Wdmmc8eQPLbpKYWJ`qEhE^ZpP1-ZcNfDkm+P3Rs8tAwyP%_yr0lWG3GoXtBM^d7t& zYXZq#%A^!=NOc;Ukqi<5>g=3+@tA0NE8J!`)FU2M{NK6zd0^=m`V@(-9XTm1wSwSX zZRQkB-)p_inLCSK0ALDD3zV2V^9!<$HQS#EZ~^HD#2#9Sd(XL7A#WtJdnqc)pbf?p z2l1)GNn(I*&=Ny)?8IAp>FgPRBA%!q-w6nf5QHx>in8ONpLh;pU}R1t=uVzVHCSZB zx0bV0$~HDbps27_M=D2~O=9An(~#<2>+AMZ?Tv=V-ZQ&nEUad&ibOSl$`)<94L7~r z$7<4g^Cu+mH_gBghkS8rG)_P{FJj+PB+bEY{r=H0h#HhG;2`v(HlaX-%!suzBLKo; zV})c;R-J!zy^(Ey45JqNy%y=T>Oj30pNL#&yq({@3RQLH?|D=|y){p<YE%Vp(FGF)Bk!GMiW#zY)1+SK_L7!g&90f# zcI1U1_k*F8zUZ3!&pXOTe5`Ic+QTuWD{!~pBI6aJ+Jn=ubyT77FAgeTrq^QK_A4e%_2kYncut9*MPJWw*uw{;WO~3 zH7(BOTy};OFFUvH`PYbMeRM@i8v7Zh#4WRa%)|c?FaBP3Vmb`Y`CQ=K#I2#USgDK0 zf^w^hvw?9O!?Z>LNnDZBq7d6vMZ6nwrM{T4F}6|DZU{fMngTD_cXo17?%P4|=GP0* zA0GZfUbc%M|3s+PLtRqaACq6zmD{1|W~rswq6@}Zmu&1077qzDqP*t_X?}tiVR8C4 zyQmm#{m;Gb8?r6RyU{mwv4q3qpR-i(B>}w+aXafgtN@M|$(EsqGRO-f*rmHT*>^}y z>Er}yNOH|!+VbQ77vQ~2N8KIHPe|y&d-QFA2{)4M6MI-yiixfW+PzE=A_I+5YOwxq z7c$1kRe5h`qb9S@#EL+bveRm>&mJkK>CPUmo?n%nNy(^MY#+$$#v4F8J!Tl7ZPwa~ zH@Y=T5pty9g~nN<-`@YU-Bn>~O!VNGNP+i-4(AlQlbRObrQU;s1C6;+*^pBaddST> zS7j_8qUkgqLzjnw(59*0U^A3JH2*@_5!cewtq?+lT`UBHyvW{cq|?p_dXFCb?n@{) z3e~ikrVLpW4eJoL2Y(kP+ZfiIm=ojy@CXNUKEomt-#*h`@~1 zXGhK{sI`5|Me2BJkElk6;x2=_ygx$7B?5j8y}^|xsMv;!eq{s*gyNWB25)C>1_1~& z$49QsfOBasJxpz`cjCzy>qR-33xjF4qsp-e_$5Aad97&Hcw)&oh((N8NQ{WI>IwsX z5_F5eU#MKslyPR&(@9&x-`?^@#PxE&qF=lz` zMBPhKW{=e=1?yDB08m@?zl`8Ac7=L0t}i_gFy+4`GC66RtW)xWegbzkrq4k3p;-Y@8? zkUnuaiWAVvW3nz_ek}_QEc(37pJprN(oaLhrhx2KZ0lWBIXf@&3(=y+*S~nPCrU1l z8d4JQNgUnHIv90`p~2s_n?x{;N!f##c=q$x1CDDzme~cs%co&S$&rCx@Geq0JiyqNKI)NjCjzYlRpi2N2 zF1$%&*hA+vAzAniCq3YtZwxo|);mNTsagrCOipa`(>mB>`j)Luo7iROsJVXAtpOdO zG0&ot#w=**%>7fh`CEzOQs);)M$|IpOTb3Ux0=!AnqH_9b>)S4C7D{dF=R43oLpDz z$3YAsmZQCcCb#Er^^g0ox}c8#CnCIsaO@fQ6Ku=Zi3pp~^z( zJnDUFXRGEv!&8CAzPirEv<}ya%0_ zw82=aIJOmVm>OM5g&#~mn8}Heu8S{4V#=jeyb{eL@S?M?;9{}+R*ZQWgi875xMgo`;KlWIgZWUjF%F9s3*rKq>{Gd4tJb+9kVx-?6o5Yxo| z;fiLeP2zjsbH{{hpxp<8TwYcEctvlZ)?XS!Gklz^)9I3{z(u`mlpF&hJ7$cB3{cp19 zM5Hjs5zU}rb$%s+IE*{~*!JF?!JIROMBdem@vV0dRMjA(G(tP#kq(PGe@s0pOj{)` zi`*A5=aW%u=oHHfO5d9@heQ<;l2AJ_n(}n@0nbw?* z_G!~2EDRkx+^HTT%I3}Mn%`Py;X~7vZVw*#O|6dt3uMzAg;X@hX%Id!aJ|I;Xb3*Q zsb(&2IZC@}q@+Cmez-;5EvFLuxs&?4oRypgrHBhui0`gUdvUU!EPIyCZ_h7H82t15 zh@1?jNEjgKG6mV__fJyBQyLRd)y{e|wMs=|h&IBs)u1#52!n;_sz_wlaHwAP0m(@l zl@f}(L)ZNwrgjCtmRBALa5$(AJq<(;)raFkgy2~X(Z6=Jn*h}jqrAnQN)*dpg}Aop zhKf;|PFvR@5^{f758;m3P$!E*>_m@;_OhoY9!6n>bKQj!8)}5-B*b3ps(%O`X^GU zm3B#~76`$b=en{j(0!>i_S(A{eSz|$67;Br7 zvspSIimU?>vmq95C~-9jc?es{urI*|FG3>5Juz(1n0BAwHfgwZ2+$)XRkevpE=2wZ z2U(P;tN5xXLWKOl7)z3tgoH#^)~ncXG)C`cGh<_d;NkrdUt+vK%S3*sQ*;aKRs%7T z{c&k9TUfEY=veuG^ZS2s4t8B%(5^fUuXeWFGwYZiQ06r8+lBH-+%O zDOY^^I~;Wgrs$Ek-0$8~UB|WuB)UARUOS^ShZnt;e{owIxy*bRl@mZaCa1KnZl>NC zR|nU=Hl(_)9E8f>@;^qgiByrP;Lt5od)=?|RHX}KAJf*xr|y;u>M|Aq0sc}wK*QPj z_#k!$)-Sz6G2g#`FM__4jw=bEGqr2dS??LyN(O~-qHv>d;nSjJ#o={r8jqWIhtxC> zHI_gwR{yz)uVT@(f8)3sS8F35g(o4H{4s5agny&`0eor#3S4CkJo>Vhj*4q2C41~B zD{WEaMiWCgZt%K~Exkq7-rimw95+z^>!VsY>Ku+nRZI;PF*4szv~zAcI#efziZ5om zY|K4o_e&qOrUmiRO!x!IwEvkCm$<<}KO3#LOyLI^GRXDFNuy46J?EDcr4+W) z4!4%HP0T%{%va@eGQ4@K?%MwL8duIs<+9IsI6{# zfxXa0=Q1$OSIQ}dJ^SK2rv&l;2>w(~+3m(_J6m9G3wmrhlN zCUCU>`7QvnYnC{M<(D4i#MET4KJ0HT+8iKN)l%3amP`e zE>leGm2gZL-*8xI=dril@27v7y}mGfNkw~nO>OPq&(*9=$_`x@m#Fx7Tv_0Ih8YUS z@5QBZdoNAd*e8h8#2WZJ8!*h@0N>;)FSoU9Qc4aG5D@IoO^d&*w7!vkuG;E~5=i6% zmA5^d$=YuZ6A}`UB?evY%c~oyxzv69sG7{71k`PGoD2D0KrH_kiGzQ7`_dgqtFkG9 zRNHp9Pd=F0d`c?U^ENR_&%J}BxTR$&1cbtyaUc44zq!-qiijpsbzIr<(!9U<&~RR5 zbdSEbe;oIsE84(gp)RqPN2;`CqM`e$e71ZGA8VeM$Fu@qJ#rk}AWn-tRHL)b#weXu z`yb<{5mNf3smavh<#qNVs}puTVDpDYVisnfbtdGG!5jYSeCD&@V*@<%eno5n9rbWpU-xhH-(b8&a zzF$}oQAZEO2Ly9|IGz(7>wjrfr{W-AXi|Hr`1?JMy(k24_fjQ>*{v7plUViGLOIHG znrNAsnX4>RaG~ zi!7%D9H+|;IxM7D6)rQl!S)uhT-i-L{%^XMi~);MHl$WI&~UzEveMc@OL8(f7AaIx z5)4+QeCa*IG~QSjqYPV%Q39)-ct$q;s#ZbjF9XT;Cg8bJcW)GB{h5q} z8_2{w;O$DRPd%E>Gty&6kh~P^gXXe2Zx%sygizxuN_rYibn}$Ek)rB z5bi4{0?w8i6j&)TA)Pq6&LS5)vTAvjNgz;8lU2`=`1kK8W9Ivvg73`$NIHc@yD2XG zL*Lytqcf}tEi4D|WV97kw;E;i9*}CK-dlGE#;q3EEFc6#>qyZu{1eNnY|6F^s$RB%%<>R z@ViL=iJk{wSu`VU9}9?Qb_UCon?uqqr=eqd3Z}yGy8OwdjlF-MtUD4#%*ntlWFOCN zqc}5!2VKlKLb%!3q>jRsGm!Fq6sut~yMCQ^TdhOltG7o_u9nY|ko67%;#h{gZVKXw z`O@-l__vk-TF5AfOJUpr-!mi*Zq{_{X^=I~rp>@J^oxqr2CX|={`0AeayBroysTX9 z;1XXsZaFPYEQ)fBD(%>!YcO@Frd{S>fIeF}N{c(eZ8T+;few+KS{lt07D9B8`eE#k z6NPk=pX*u!5a5TNrotIZZ@gQ?>7bQaUrzZdLCO_nq1p#Ha6~EO@xliP+-&U@n`;c& z{>qgiZEH+UvaPT>T8+Sgz9;dlFdMaUke!=53xkr_!S@bg#A2tSRW z;23KG zi)4BGc|T4h;W3;m^62wi_G=jcpTsrf;l)W*gwPt`m%>yX>!c4D`>n5 zCVe~5jN|&To@K^E@mCIA*$*sR>nGK2Xy-3zq|1G7Z31zCV2sphZZ??(}=K=I8Ib(hGe0*&RGG%Aj0(+p<0IldTB;+ znV-RCamNs(qX^jH`R)_(k2Q{WU=(+&>8L*Az*^V}$vkRbGL@p!=qXp>m}q2``*hAWObXOs6T`Mg zlnI~HMOvJwN=9z^NQ+C&hR9Aj3~&w~_1yh6Rxmw>v!<~xWmlrbr&A4MI3r#5{2Q+) z@*d76hFMd+EOxD&V@I5h7A>4->}O9M&H1mK8gDog)S1%2{16Q()|VXKiKxdhKu1p} z4V`?0A=9BS;N4fJCUtGe&r^a?WCpnWl~{ZeS7yt6^ygG=HeyzyrNhN}2D{1SjS=TW z(Wahi3rMVrY=|lGceTpnNqZ^LZzcHKP9|MMi!{=RI$u!JaQ5NQ9O^c;n-KeQ*_zyqJ}L$}5(7$-2UQgR$9x)q<&q z0DapwZhU=9kex59=wH#$zg-{`BOOIjf|KqW1(;o>M`1}|r(gMuQf(qUT@oxpQ{Ra0 z*^wLESyVNLvYN7&WC1#9*-c)`6tY(9h2^NsIIM!Aq5nt5#jF^Pv0g|>xV)yuP@wo~ zK%(DI#028RQ`?vMAay_)5;hV-LPF&g6$S~H{`HsL(S^djVzN8OEw<$y9ct|^+i1q0 zGb%_w;KT@QbUm@I#9YAV2?93q&da<3&kNhlIaW{oRgH}^rQ%2}49C-dC0i|g-6+zZM2C?~c&T|%6TO;N^8sJq8u9T2d?m6O54ujNs~6#g_&t1d-9 z54Gq|C}gILG88f^cn0PVNpLl zHMFVc7KIbvnUR?RG;yg%#y7%{pGo3;536?0ufXvp&yWgE{$P^fp2nOqzp3Krx&I?P zS@_Vch|JgfvyFqt_;g6X?<45_y)}yKLZ1^aW2hv$J7SuC}fSYQ#; zmX){ZxNJLR!jwgsPU6H0zt+%a&m-YieC?ZE+t83cKd-R{L3FK~J@!$%1h``H?m9QD z)9fRsKI(2<$D3vk1oCYgVR>cBC^_ono1Y|j$qGUsbyH{%R?Zlzn!?=7z!`3Ni2&t`H z_~uOB!UL;Mbs&7b-I-;xiQ(X;v}m|MX<}Qn+qI!*rih5*cvL_TBYkSX$aeX$G2{03 zo&I#?ocXl0rX(f@l@WKMx>&HLPg9g{f^#@6>6E005g^xEw$mo2 z=XUQVEu1ykH0TgNiay5L%?_xDRiUu^9^AYqHRD1ii~ohtGG{m0A&ONh?&YVGltF+N z{mJf)u(1u9UX1)w_6OyEyy<`kuA9_BBjUMFHysa)=XWnWGatq02$&cIZjieJMb)u^ zQnv(j4#kho1(=yWl8u7>FEh%C$WQ7)TrCF{Q`N{#w_}dk64ec2P-7hulBh(N)GMRy<5(k1$pZIWrRmnn^ON_xZ;Qjr=&lMv9S zYZ|9tXDuwE+R;f4B(bp16=tU~_M_8Oh_{G1mYMP>I9mUb9Lc(fg4UGBj@PMt#6`M+ zPiXO4SnGN?1xL<|Ycc^91?{Z67pgPW@NxJANlr#}8>r}Ls%R2SO~-Mnx8{}E!LKU* z4;vmE3&le@X3?ZSI;3b?j*2Uz71fsfptxa$iT36l9a05+F5RF7mI=0#@Mi6-amTjZyaMh`SFnQk;?{SL{&-S(G|xh0(+6g&S%m_9gfXpa zeoFe!g@OlNXWu~j+P4kA@&iYe6g{~dRtV`ZGbxE7;u*_P;}?%$)xIh z3(Fjv_T*7vI`GNregRJ7wOEdvFpI3X%<3yRYgo2n5~} z@B;IZ#ZL=#pSEmx4S94SjCGXO^=rzJaz{9>^h6PsXhVUlG|Ett>}u+cR~a=#d1n>h zgnp-s-NfM-drC9}RNbb@uz>j!s|337G7HL=r;>VxWOsVm$#0AWuv?h0Op*0;uLQRE z7;yCbK5(4|9AD(KffR)LLSM-JEAMC&B*WY(r!g#55(FK><3xJTxt`7~cfc_b%%_K7 zbrI%%*C(Tp^;>AN=8RJomkhl(VM^k21_mZ&v<6@4$_}!}afcdZE<}tPyUDlCJgBBi zCU!#7z`w4%5tI4Pw6>l3{~1kY42U4P87+<~Wgpr&);zw15R($8vKe+v{M915u)u7& zZ@J*nqO-iRqO|m0QPA_84Cm}=Bpe`*Cp8b$Zg*K@RpEbQZD%(T*NP;wH5iFl*4gML zWW~|TM9iMjv*W24vf?g7Ms#8w&$-%e7L#yt>n*_!BDyr52Xsk-wm*uqZ+N~EXL02Pnmge}|iQO9Q*+i#9&=%tnyPB?3taRuozBc{kIEHvrVbZem+ zKmfcKh+;(X1$hteHX>t_Q4o)M$l$oYH)?NuA07TIlg1^u;0vAn8j1A0RyzF4zz+*k z2R}K*W)zn*Cl2%Mah7%q64vZikndaoTK6{te^Y9EuZL+te;R=FK5!s)?2}1;^3Dj% zqiX+*)t1PqSG3`DPpI;(`(raoQ+N7G-S3fxnK>L$vYv@90xmoZ@_rLNLu6V$iaaYT zbB2U&#le@uyi1wVnoCkPSlUgt{zaC%F8sK-JkfUz0^pP=@{rs(svwu-{Y|00ZyX;&@Re5CXHk9H zP$O@s-Z=rX2(ggO(rl_tt2chmXTbGfY=+^{yV~Gj#$vl*$Sa~W*9np3kLQg)jR_zU zZ?!G|%Ef9}I{4(Yt!=CoTuqCxZJ2JCT3TAl zD&EVzacu`erAzSe2CBLC>+kHWa0m(t%5_>7Q;a9UM*`ddHzpzk{Bl!Hk2ztH7BlRL`#4i%$TrHf57O${z`RQXqu%?<(n7Q#=4+oxJ59K`50&;_OV45*L~5du>!Fu zUOAWk-SP~qGYnu!3mLeiye8qIudQ!K*o~sTYxCZdL)=chgN=k8yQ;1?u-z_gdUtSm z6i0|GYUHLgHa@N~7YRoxM)T?V=H~F*l4DNu6z|C+CkYR+ON!G>RVpr$S}PBG#i9qb zyF4d$*4+B+1dn$h-6HCPja-+|M|zq;1tvC*ku2fpboVskfT@$0BdqM7`pBnsgq?yk&A7X&wQR1@9n)lKzed$QB~Im zVb$JT$`Xz_<6HD^_H61$=P3AL7GDO9nmqOViZCMSaT&s7>Z`SoUGNum@xM62UNh)1 zG1>Y~rkrnxVX$b{-4lEqTs6&{{pM7&)%^_9+`tDg)?^f)k$G1%l+hf|A8($3#)T%2 zokr9#gif3$KwO9-d>ZM9aD_kGj&ysJLcgI_z`TT0ns z-j1&mB13{;fd|4Z)@>LE16t+Oqrx(60tg`!jGN^jO2Yg#4f%)~>&o(5xcBO|@SBVi zB2&JwkE7Q?XnVs{lF7W5!3kWdx2Jh;Dn2it&C~tvU*5u<$4aCXoL5O<+hpHb2O593 zn!6Y3t6V9lQ4M$z7+JmVF?hQ04U$jBG|oJsaprUyy7XWr9BV}i-9zpa{D?*u%p6=xFYbiMs0K761ihI?Pw6P-5MY;%io9KxE z;QZ2NAgTh~-iZuusgl-2$=-m|CN(5ad7D?vJ;dmBU9Nv1gTY3uo zAZk%k%bDr~J*7YLYKedWEGdRGC-skZ`a&>x9r7Lg@MqtsT$MZ@e;0Q}j_h1Xk`zf38qE%rnFveCSq}Nt|Xjd$iRY zcvRc$K+5Ry^v2m~GXbed)LZq**j&@#`{%H=NwsrW#DW$mDZHY!1WB9robHtU_>mTK}+Vk1xl37h=>UNwfZ=$>1nTt zMB#$#$Oqi9$&%r=RjCQG*Y~G<*-kYEFghKpF<<{#{ssU@K(`P`;Ti+==g<-g(qR`! zDTX?Ey?)r@weu!V7BCGkt_M-{*G{vhYEq^t6KVKh!t?L@P2Z)S_2h9HVFShz18owO z>U}E@dOa0DsNJHU+yI1>lFZL4CuG;_xXeQYLn{9eIthQmakDT2!L4a1eQMV8I5DPm* z=yK}lLz2hgIxq28%8{}URPfPs~-uDeVawiYFa`+F;t#r*)<5w&{Uhp>#{KjSNNiQ-rk($~}bdBP$ zfmFlhn{^*f@*(NEjFvhDp1IYJok-(hfUUj*n}k{xuL(#!o>Pqv3G|n~o5EV}zICzR zE&8PdwpoQZ|0Wi{QQD6mMo9J43Ij_Pe&+fLRoYpviRKRn?)6w5vS34sAn{yWBklKV z_Q~nFbV#}VDIq^G@zYw0Pz!#g09_i$ymlpy*p|<*fOZInhias{IbohXhILyXC1@=z zNqtnE)$hv~Z>Nn;6pm<;X;ULS3)^8XPqCN99PWH2(3c{`zI>scU=ElnM5|9?I0}k8KcuUk~ljwvq zs0pn&mj8__(1&1*q8L-9m=A5m3CD8gePF=hBm)IuKL=HTae=4Bp&omJP|-A|g3aBq zt*%W;G7&c;Ml3yO)wB5L3^y^Vbkr!-rwSQ3#O%6VZnhDm0@!OgdHE!A!93?qqZP?w zYjCBz8k7+dgONK|)L7K;1Y^XIPu=6whupiA|2r%{enI98IF;Im(BJO^;)Q?-W#`Y; z(%!FM?}!G^%zLi!P^6r-wIuuUOEeBzU{e9ZsD2$3{UD@*j`)F13ytI2XcCyT*3`SW zPptHlVXLl5wYBMS%%os>( zg>$6t2KB^iWr*@M#|LnfcWZ~zDC%5kGQ#o6Nwzl5LKZrXC|TApeJ%e-03ky?aZ0Ry z>p(`y%BY`%YRJtcs(})yU2QpdJq18Kl>M?R0E^fV$^qez# zV$0{z%w?0Gd*2G~<*?P^@StTS{{3Us1y0}>2MbGPI)f@nyo|3c1FaRZpvPmQp9qD0 zHsA5_G1o{n3I%pyX&fZ;8XOtt^dI=p+quhTu3zTNTt6O^31AT>pk*C8?Tsal zcfvk$$B*mJ&JIWVpg{}ILQP-x6r6I;B_K6l%vC#+!oKMKworArLU@>b#Krr>_!|$* z%~_r`&qu}1ijJd`x57F6i(QCM()#+jljGMjN_VmqP^ZJEmPZ3Zp#*PzRzD+8^b38r zd2jZmp{7up6>$cY{XX z&J#1OimHA^uU2X$L)_|*A3ZYE*j;|dsF1$)&d0YGg4sFQ65egM;z4$x+vK}@5;aB_ zaJ%9N0o8;jqx@yB*%%Lx#MVlTr zKC-`bb}gR&gXigN#QQjpTu#C^aYja;12p*wSH$0X6%2U9nWs9cLyx+vtn3p9XqF}u|*$8r1pj&ux*g9vR35AB}5 zvQnaO>X3bQZkT84SgY~?2WSY7q>tjlpjIHnR1_LwB2|nPr(NY9sL-nspbsg);j^l1 zD~JCQ>9M;`QgRL*-r!xPs#qGqR& z9LQDO9Dx<^Jr|50wUiQ7Q|uNPFzj%doJ?m(+E7p&novNDK`<{P1kX2VJhgQE`ULKr zlNbViGyoi0H1f-B+I;38BervP$wc6m9G&{|tvr78Aw%LDFC z_rN_94LxU-$%UplH`;LDdi{^%iWfeF{nm%%tc#*@38!&<+}Ad-POC)rcxB(pbt8EA z=M>ysl}i_7H_zN#SoZ{KUo96sj6u{<4Uqw2jOlQBCU|XkB`$6oBJII zwMOABPwe=1#2AK2pI4;$6BUAFJRsarxbFF1_U=C)8}eIYFA~u)`q5q0ZF>D~M@M<( ziro9rx%5dJQ-PMWL812MXRBgkF{vVAHCK=5IE|GSYThsI7#Deey!`vE@$h_ats-a;u95l>H}j>RLZ^ek&>7y&Hg z`f)5;wA95#LQZp#tK~FSQl(>c04WfCT7Cl^@5Ijj8nbz6|! zFx0MfBK!jtj!|S+*W@1T&qi=&rrk(BmZL8+XXrB`PZDuR3GLq-@M-S-nTU^Z z_mgvy8^7VF_J0}?+oZ7Bh>kOq1E@o0YB_1{_vyZpLz!wGHgStKXNPGFMSQ3ql#aLM zUdl;>6`x)){hv?#&u2y?4u@3Zz^V8U-IUp}I<&J)v6Qvf695`)K%8S255!t@D4I^= zcxYOg^Zu|983x#h;;NjlRlyDc1`vrh5L65X8$CU}S^WWzdF$6=QH*}PPtA#PrDK+X z_V)2IX5U7di{Fq8>rv90FHsI(}i-SBLUHxbRZX! z7wV8UItIq1tJqIHMeM1Bn`2s|ss6hoQqFK8^F5SBz-LIeG{47z8)p6;@AQ1B{6WhN zA*es$=J9^ecVyyg&X0M@pQ|INN>ny_&MI}6WXLN@9DR&;ciz|2zB|(7v44$Qu#qHE z1Jo{GS@vF!W@jIMPa>5YuAkAOZ=-S)|1o$$u*C~G!FhM5-%DG6oWew~E!G@{P%%)M zAj6@GFCK#_MJ@u;q;1B45fxTWEyB`szwhFyEt~))hSY+L#ix%S?RxSTGwg{+SmC?N znSfP4ulOk?ziB)4yxr;bHGMB6404NIsF_Mo+%Ib&&}2mk*$({$OC#!N?-D^Zj=ae5 zSP)iuOGInhf)u!R)8yFKQ33?sPx#q z-G2Dqt@Gk4fiUD5RV+^}g~)finy%bwf?38KGjOp<6Uz?w4VkL0Pf020o1%c_u^5s@ zSWN``+ zv5{kz`201#0PcKuI2I>DJCPRT0Ak&b?eM9bX3Esb)Qs-gybq{(OL8nz#EL&YF;lCd zgGS}bRgoV@q&!~MtXu{|snlbK&F$C5|IE&iQ=5pvR|~MI`MyRLTiM)^w2w($Q`tWZ93#vL04s4c+Q%Y8ES1%`H$bIY z!f#JNRZ3e|*XDGHfDl`Y$j8`g;C006NKzb=vbXGa;~X_sqKj`CCG+i02sRS5hKa2w zF!LqGglEH2;GWc=Oo{R)JRClTGP)1o{9ugCk>zC+L4-DG!Z;#2D{Y0Ipu+@_pxX)H zo(NJPe$0n;Ms21om+U5VrEr;705ZZ!Q?%fkh>Y4CO^o8P+VeEL)K{q^pPtDVh}5^7s}vh|nTc@KwU)H1Hq?{{H}Y6)$|F<3g`#bZQ#S-(z@Jj@w?L z{bzl0(zhPH^{g}l7!w8DDL?L>M3_EV}+5Zl?EBCJLFSY+jre_ zvMEi+DG|865dpNWY=!x-kVDd33`w*f&htW8{$ZOF2JWdpZUn+HhnnCfCmd9jwk!gb zG}V(Vyc33F+V{>)iXw$KV8<++G*uGp0ZCKbVVnn(H`_a%@v!`*cn@HYa zw(-NsVC?4i4G*> zIEBxDizNwvOFy)>bHI z;OSKy?v0ix54UY9-}$0&iN@N2ds>$={HifP|DNxLjrteNx4m|zY0tMU=t{X+gJ9bj2~`RD6f zD1)EmtO)vkD4DnFn3{5A;%BN88@? zzM;ne+u4Qfp9j6exXZmE(2iCh|GvjcF_Mi-H5^rP+y;wHm>XBpC>-A2&Q1c(r9Xbc zIDyL9KBJ|~iYmx%B|Zl>ue@$@k*`MMAK}Np&JE5MNwW}x&#$f?ag04J7paGibin1efp5Q==}T~pF5ir?~T$Q_Y5z5 zOY-w^K(ym`=x8xuUOxRhLwsrj4mP$rFuP*EgM&lVxa5X1oD)u2RTUF*?66dcEcNJT z$G?UJ{(wv}HP zUIP`CB}By+QAxRJCCDlemYdMp^G)Uz74hTnrSTozJ-<|tXPHO2;s_jBcRS6&>&S$> zTbK}A^Y$nTe!EViuoZyanRqp1SMv&79L5= zEfY`Bp|GCAn%Cd}tY-cowuIM$d*bTo$P?SSwY1dc5#Vf6XjYhzNtPQ7Tsj@O@iKQU z;3EAl|37xwSeA-`T5j)Eti%-Ic}3X*4NNHcMiv={Hj;zPMO+uS)Mzisd09}HVC}IM z=xEoHAE5d_o>CwQdCC!+FA(Y{|8^G?;g#G`Ye7NNftGGk#_O}7l7fw)W?<4zZfbI! zJx{liqv~<}|FBij)QxZAto^W@yH4R2nFZI*!M}dWySSGDx|-EaBVAnuhcu+OxE(v? zf)^+Z?%khr{%~~=tEy(4l9J^XJb%=#IRZbOr>Qxs5z{<^m zK24p@Qf;qP{*Uc`%LxbJd};vDiNo+OI3iTs#FIohNKY%4QbVGKz8ryhboVCIYX*6m zDTv+o8Rvh*8<02PgsF)mGG1hml-g_R6v)Te|6b74rKqTc##H+3n?2pThp@fdxF>nR z-*MRrX!&Y8_~-!tUw`o*{4NqcfP!uLqwcB8A0lP;D?N&4aba{qd##Vq2}Ec$4>zw1#5-!rJ|w&GI(zIdTN@33G_df z%LwC7)hIQhOXfKs2U_4-k6Rlja%LeYio~0tX(j#(Ty6NKuD>CH^p)~{u(UFTA z(s6RH;0A?1Pc^hG6+aw=w@n$R8Bd|imc^Av?Sma`U_5qkbWOM7w(nu|@$t@e6*_s_ zAro?+_sU5;T50->@1xNli)+k7s>NzzAdQB7K%)W7dknUT_A4c^1j0!pKwGZ+Ew=VnTM{ zPSKLmQg!Nl-f%F8td(NvjBzw2nk(cnS1*YFx7@yGh%!Gha3ZoVe#8KrYfuSy^!+_SvaLP4#sh>3#!??4R_aC91Styl+LQ=(+WSE{I#X4n2HYN6SMyX69O#LBz>R?r?vfSccjoU=; ze8$J=Ub@1_Thx*F)idU0W|bQSs{~J<9>M%Dd;lU+(#Q@s<*&SFj4-VLNCGGg zGGbci>_BA%G=sXwUM;AZa96j;ZvT$J|3VQ34z)Ul%HABz@C`ky#7D8&$jqjkN|(!z z+3B4RiVj9q-zw6bjEg+ru3B~tu6K;1!kQKh2C<4zVU07+1b2d@mrPS--f-4zp=Ru@ zaKb>JeAq5Q%n(yB^9KrMBUgW%BttRCZW}`yg~}|zY%R7jf4d_%J}17 zvd}}XoeK@;2oj*;ISPuE)zli&5)NfY7>`TXR_Y|D5m?o8v$%mZ6cUQO1Pam#@M%$n z3(27Bcz3MRlN3!&gl^9DxK!n(@8-A&OCzJ@uV{@hc$&O~7ByitSHx4(4y{NW;u}SW ztsKn-;l1U-?N6<@DZ(SD9&!;rp?GH}WVIgyrKT?ca4}fZ7rz30^(#uXkJ~1Q6ecB= z?1`|1sT4wQI<-O`BS;snBV)pnU;o>0SVU%fGvWQkO$W#reNQz2Gm)>`R9d}M{MLQ;S9_XCIp|xd|o+Q9b{-AefriQ?v zfyuAjoZLc&Oss={93CYDj=#fG9MOxVmo?*{8VJnPJnE)dZW*;b`&`k=YKC1IDuRyu zzPvrit?6Uxe>}ILbl3uMTI6snCe0-$8Nc1SI~;IC?CAiPhPZ`vIyY`!0Z}L1iQDS6 zgjo8n{C%-X|ILSfduv0HNv@BJG_F!A2?Hh)VI9oQ%*UT&60w4g-XWktH)||Sp8z&d zVYkatZilAIQW~Nof9m`@$ZJmsb7pB@x;9|s9xTa_!T1!KS?B$c^pr)F#no;bL2s=a1_IkTp&MX(mWP{p#jSd+4-Svg?N) zN(#BwwUSJW*9#?`o4QMAR(#?`oF-C4jI+|O6LQwBdKRC`NXL^98n?!)uHPwOuR0=u z@#c|dv7P;ZejD270e)HEDmI_12=C#uZia=q&j#E3;cZbm@iX$DG+vmK0NggGIbtp@Z0vy>5E}I&0&?CRtw2qZERivo zwM=rm#P!>Sde@_x)0-73FbJ16qLw>c3tYQlmy+OIKhecD-B=Z6eFjyMhHsmSA7$vT zwgd+t&iWl`5RyWhTRtMkG@AAP@{^{8X{h3hw1HcQg<8b`eEqWTRtn~1MPE7 zvL4s_eBNE7Tnf3LKH^H)lY*(hiBF$0M!&gnJwHD~nz@t^5D;d#xW)JsS8|j=%PJA0 z+*Y4yjc0~$Xl?NE@sEUw&=hraGI6*_ogn#ZZX~dKt&SVnARE;B23qCFC^L8h#vo1OZ~vF zW$~T#f=!Y+ef?c!IcnLNRi2g{_5w9riF`w)SF*I`SYPH_aoWt&l)rJtetbw^B0J76 zTf^H&5^HQ0iW#QV$0fH3W$}b58+guXMk>n6L^!&T2~_v~pfWZ*6k>dtEb|b>K@n>T zEr{F&aq}2ADk`$p>k!d(gL9A#ge7-mlKo6fC9xMFKLsc4S|=HGZ=EMF)#^J+vvvXQ(vMb`3?rop7Xv394-jBUa=MkmjS%RLQp3 z$it1~d++8gp;H{y&PbHS57{kVnlL&@llDRhKM|a~g%L#F z;KC8F`LZTD@BH_`1B2i%9&SBdfO8rpnri6P4U6i#uM69Na0l^-R~+GH`e__#oFTvk z0r%1kX9!q9u>7(oLtNS(cR?rcz{tkLd)F!8xEUQ)Ol6Ms1~0rf3W}l>(K72c{@w&F zN=zs{jpJF?qIGg;p9j^$Yabh?Ryq@`%1vO7MUWinW}fdWUu$+6MDirUBOpZX$f-&> zEgJEGxWE%^l}mN@&XQte@*Rn8Sdio$*gDxQdR^Mzqwf{m z-i+GPWGd$;It@bD-u{SyOF5Kuwo`4Zq3FdD-UEa$2gZ7#^oM}0lh{? z)4NAs6iQ+15~L5k*<`+wBps7~m8Z$KNXtW=T01|FxG{4c>8<ny`^2 zO)kXkPd#q^krFB^%&t6K>%S56l=uQi=jY{i`Tc5!wP{tj_C=Tz>7$DWa&@=$Ep$<4 z^M!UA@%i{*veWT`frsTal~zos^-^m<`qR5vyUNqE+TjY(la!_Qg3vQxKBj50j`o66 zPs6p@^9|jp-&w~(UKl~_CX>~-Kc+7-h%W5s-Vbjqi4~x>t_bR?NWK2HtiO$d_r9qc ziFGh@H*@1feMLplc!B|)7n(`_RJ1-=y;>$E(otdM{Qh#SLylBH!1Go0%v>M;I_%%i zVS5Y&)|Y0)@fo*%DM6GD*{Fi2__S^cCF~zGA=c*GFG%*)ZN92hRNG>}s(W*%J94q` zLI-XN>$az|S3~UmW9BVpk~H^G*6fkcV~lI2rzrcrjacbf_v=_a_5PRQg`IVk}qlAduR22PU$@?$W!kRf&d zL&OAfXSDd&_ zHc^VoPfkK)S?ZioW*^Fr1g71*-@qkpXus@>^J~Y{?{qTN)qS9cYBiRJe%anF36u%w z7-qD)>yCsfB2MG1NX`nNX_|D&<={~{p)w0wJ2*^!$Ux*!2q{}nd>}Ep9sfeBYC%vm zj`|5*^C%qMq{?5uZZU%5Kktjd3iIhzZf-8Gdx)c>W0R<5s~Svrm`o`bD;5VSKv~v5 z8j=~EI5?m^dJ=0XP?a`>m)knD+4T%%3-I{fgx0Z$sy@#YE+_*K@r;g3HKM~o?t;u> z=N92+<}H4kFXArcvh|*=6w*?)yub`mLh?9U=e>eHfUMrM(@Wtmx(Fv_6eRq({sxSO zo&w~xNVtkX$HY{ERJ!hu2OOsF{M6kJ+IRHmZ%`9jUymZxaBjjlZK;Euqmp2{mhmG< zB!`H3VTwu);k|tthT^Gi#!poD@{fPo0e zT^}B@aP#vsjB{pFj**4JSk<)jZUym#xS_z1%sx$vQV1a6=t5j^XwJsoEVD6 z#Oa5W?9j~H^l82mU*O9a?{Ze^^G|+adOAWOB6O&(sR6vmeQvHSBO{Vv}v+#>W00Of@53zIu|a4G?1v1rJ_ zvPWl3maS21qwN1Nb&k<-w(H++(y+1Z2^*V@lZlc;*y{Uf?KprUT+_}xHF50v4pXYrRE7Y!Ci%5FN=(`H~j4WeFHS(&i z`&2TMk9(%~jFi(@6J2g!r8VxwfRWAHAGq-iIlsej)i9-I4pFwxPENo&%eF zphSMcA4e|kj~~s}e(DB?o!Y{>4}Z>H$8R!@Z6(|Vd(F>Q*LCbz zjSoL!QoY8oP6t~egaJa!#JrATgswCxSaxp)lc;b+f$hP;!Q(PvPmhMV`PN=uYk!?p zVAGXZGbu*M{dYtVvJB&DZH(rl)tIGQ!LecUPy*e-I>v)M0EnsM)U}bh+*~;*Xk1^- zkpJtXh%^r~DU}bHz37b_^LnV#R-p>5m^$ zNWQ_O;o#sJ+uHJ*KF!PevQm0Sc>Es_nv&6DqykH_Lu|g9iUOqO=PTo%--~|i^o{7q z$i(9TnQ^NE+eXRv?0-iap#%xDXE!pEcC2X8?DeII(0vOx{gFv*^M@J ze#kw|zmN=bb-pM5aB(s4cX#4z(@@j}8yE3xg84d<(S#gd*NOYrwpru{o3$pxuml?# zIABaN^O~pR`G>qz?Q5wG@42XLW!IQOl-fINId7xMtNY+U!L!#mB$WHTx~Qn_BsS=S zW>1}4;DG`~nUz>GPSCrL@~~BxZ-t_?lQO$>b$&%rkq;oHWsDB!?(7#ZiZ!Kh5m9AMD;pxkx7NMkJ6NP?Q*dixc=Z_FDLbd^Uscr>wwz z-DImx8*^g=4#toiEhYGi$W0`7ul&fbRU6sY>lb<{|Hr>57vHkPv~H8aT`hu95=e)B z|0^lTjxano4CaiqUa7IvFdH5a+A`QWJxmJF<4)mL5L>~7T)>yU=kav7&pz{VCC>Eqnw)&I>11J- zd%^V|^QL|J_$E8rZSn=<=uV%EUu(x{_)q=t$NPiS(_7N)bp{(F6r`S@FPQ^_F+^be ziy2V!3kRmHm6g`fXpQmExU;m8#Jor+qErwt-;Mrd-$Gz2dT0TpM!}~fJ$OSedW0LV z+nATvcN!nMY!cy~oMhU}-FNmD2LuvWe@2&xIqR90Dv@+JqXRzu?>vWhpObDm?KU-+ zOvlhR0-Xj*_!@q27kv>46(dvN8ODA>Fy|^bSEeA`sSz4n%dM=;9Ee1<#BTuC&4RV~}^X$0cOx`qAw`@;~ki zm1h5@int`ZStOy}CnNND?fJ@R32$3P5zSKE=ALsu=-0cNQd^*~Y%-iEVcuklQFdy4 z@9HW*uwN&IJGOMk#uPSYS9q7Xo?#)lRo1&i$jXZs+bXou+_DLmmSLfPqWX{ehfD_v zQo3*Jx_SG(?R@o!;`2&j-IIdPpVA^J>fHgKG~h0KuTxl;x1R9!{;-{oHRih5vY|xS=Ga zn8l-_3f?Bcv~M?o^d=9!hOhgnu+p~(Mw>4%tdvuJbGqUeRhKSPbd16nnX|6*f$ zhhqD^PR)50*ahNss}Q99Se@F|oT&3P3#bv(>hvC@4Z)XzQl3%^%TEZyV4yTE%ceah za#Q&o@t|@&Igr1Gm+L_^ce9|gssqz~B9GLjUA+78G+zr&->7WxLR5KRX?K0>-XL5) zvoSRYlY|=jWEhqGM$HBj{y$&d4?7ai^;_3fn=pKFZ#=MZ8vCOZE~Pe5GlFM<1Sp`| zT6NOZQv7G)S-OD3J*%rw$Oy;JJktVAgoQdnw?f6QDDP1fbMoX=5YQrwTQ}D8+AQAG z_h{6^*_trIuNLeXm4%)CPL<-#Be7UPfg2w$*#G`r<@$U-UpBD%kDx5sVxlQpZSUM& z{AQt$pP0{Kx(e^fHHA{YD;oMj*b&}UkAR2ipr+in=8xPcmE)h{|VC|6DPuDd4yXEtg8EbLf&k z)+{A?>mz^9jHGa2eYFw!k7C8Rg>#v|s1|&Slp0b5`&*oF) zmk#6%AKOh*Eo)hXYSW&hcgq)H3;MV>LAfwi%`Yxi$XBYwxO4g2LM zP|n6+_HZm!Qageq;xFKUVnw01U-u9x(D%h%Ik?EN$S<#4jctOM{GwnV@S&XhK6nt*>){W6i>%-^q3_BD_FjL;vxa;_gH+%#L`v1!h|Jl{}?JHr`j^Kg{$HE;;kju z_1)#H`^@G?q$@!v`rk^<7*LP@(4#7@f6H&s**(Xf`~S-E;RF!qEQ6#6UKU-H@<)xv zdPz8FyP)4l-+%MrV31B-oJIA}Nh-p0dRi`Iw=6UI{i#AV0UcQ_2wu9ND1e~znm zb_C)y7Uf!&NWig5x1VRr{tOhwhjbu8e!=l*=9}4JbtUbBYDx3E5nT1AyISLr>!mAfIlIb3LCd+6e%uY^C!8CWDSugci9>UO zaT5ZSP6`Rw0sCNxn4e|kbJtuYy=IAr6c}TAesMQ?(`n9?dt>=DfMba#@h<2yL21iz zvKa*5>_&ozVvN>{6c)BG@6+LNx3nSd*ONuCHRFS66&}Bhw*=CxnZG{|tv~$7?(M!O z6FU5ffFSdd5X!T~*=6!OWUzu1Y#i2U507im%7ylDhU|qYGnL|k>t1Gd(yKL4RO4`9 z24`CO;K4yrmu|4CZ2sp)PGnRTbY_y!^(iLCu|*GEGb1lgnqRo`mhLC{2>xa)WJ6+U zVm5Hf(GSd5KC~$F%xz=O9ZjfPbbWz2U<)84@!**hfb6qd5@lr=*o^YNUd5m$wjtUA zlyDzgP>*=sa&Nkg6rFHjWe~VSUJFr;cJb3F*zY?bee|>Rwldw86dB}C+QKw}dV8GG z`#E~E&R8c@1LbYl>0p-Y7&b+nkBxET*JZWpcX*T7P2R7<*J8_nZ77cW4c<{x%5mvu z9B3Z8%+$gLyvyV0Snq!`T^5>#j!v9pG%?wRg(9T?9fyc=%&>#JSV^)cn@fSf7t9P- zN+BmYw7BA!1TM5X>Jq?rGH#@o1IwgaKa;oH+5Gwi4HFw)J2^A&-^dg!TYIrHa4T*Z z=T50%Yyw^LdHe#|48jvv8ec>4K+gy0$nnvQ=Biw6U)r)LCa^Wj0swiiGj%i6(12eC z*`^Vg{&^V;tej}EGK&|Y;cDcze`G4E?3214n`J8nOfI)3q-IwPW9yZa34+BE`$03+ z`udW7`*^Xw??@ivvQoGR2s%P;TDj~=2%mkU45sU*H7(1cq%cwTc6w{CKugoA=6c^0 z%BYiW{+5zO`94k8%1-6_wtu)^exgE7OA3i#*_8Nm_N5&o(}k`t8nM6-BPQIjt+%d& zWhe9QvEKXt-dp~G5#5nhopIRx=U`}9?;HgKm)%;o4+3x@00aUJPjBR*XG9v=?IbWU z>BmMzA@evtV}7cmqSLAVu~%!J^N^`C8g(S5HHw!nd*!Dj*EE1{z z^B0zZ^y;*dz|^EJkv(%ov^2krMJTHJbV?=4Sd4W`S+^=dX}qS*I>n>#{_NnTzwF1M z5o4CJIvLd{eXN4YD5}^~n^HMGu!O!ERwf7*UwN5kxDpd3F{*{joPbC+<96n%q;kS< zbxdemkuO`*G{C1?!efh|xgPtqU(Wm6uuL&?Mf>j-5&^$=bqM)4+p*n-uI{>DS_obB z%z2^i^{}PJ3g|XgWg9erkMGWm?k$T5p$0^BE*sM(mYgq!a(3CEh8i8 z^!7xOx8*K#f8hJU%n)Si*XLZFc1^#g%){1fd@XIl^*goSyX(-ogkj-)djUwcdQ}TM zxIyxe_6COu50YTO=xvONjOGl6wrUwtEsyzw z!%8O|q&)C-!mp+H*WMU_u&EfUV0N1YJjGor4@&BdYH95SAUcLnfe{}g7KV5Ye(t$FA$lI(jGT2`Q6BqD8YSb* zyqKAHaI6^PbeT~JSqV$(dqWetf4&MUR@CU_Bjy}Nhw@n&`&SCr4X;l3DrNDc^V_-$ zPvS%4S`?gA&Jb7}zYDldjPkM?_xp8Tf_|4m{6Q19g{7!z{(!zt6l^kQ$ zrC(ZR7;%jlzj*Td{oUtjd1@op_pR!^OwKco4Q*Gfsl(OQPiH)s8)t>aaj=!sbj`j> zz{ae}RoAse&HXG;AwkF6Z~4?iJ5i$b8Y+n?yW`fW1( zkJk9mx1>!c8EYEai=My(xO8(xQ~SWv!W$5i>$}}dab2vUu18E_VazbaQbY~b#%{f{ z4%3yqzr!SVRE`#*6mEyRUBaVNZjl%hfO6MCzYFflWdb{ZC+&F}q?z6w2r=Zod3(zh5bvN)H9As9P%3n@{(vtkGu^%64Li z7+G{~Qm;w&Z7gBpF3Es?O#3cZ#BHqzf_|y|su)K!KU~MTHq|mqT{z(siS`i}b!4>sBNf);Xr&q+oD_<-H zFmoquqi9B*qAdan@T|;1UMzea`Cna%iS@GGnSYX=gtAbN+8@s+ z`fP!|+fUBPNkx~dT?a#gpXEIHaQ;*{->3UX;4I5KP!D1rwA8*mpby zLy@MWrKa|_A4>7Dwt-)cpm-X2yR-m@c{uyvIyOUah}mIPgYoVUk_-1@84QMwPI*d7 zD9es<(Q(Y8?sYzyCtZsP=za7neY9~rhVi8sE0d3JgWKnp2%MxOY(+*gwOiH0C9hhU zv}~-afXHAUK^3S2bcDfOQr&<l*msh!Dc-KAg2s!=bz<%GRyJy>ylV392pVT1FF))=k*}IkQeW_sUMpF7D0x zcETDVD$mBCcT&4A1FcHT^DhILWn}vpj!StfsmK{8Yq;ag(7wgM-DuQLX#s*!K~tPK zn%wdp+yzcdHkT4BhCG5J&LuTiH!|!z)N*M_a)8!H|vjoA|?JhU}bi} z!<~z-N$>rPA54l2%325b5!`K_BTR*eD0Zv>Du%KbCMMwtsI_UEWzu`0`py_*9iZ44 zP>I9or2MqBwN1ec<4}fphkZG3IaYK0>*==9Jby9@MnKpuOJYh^)=+-Jd6)1tGloeg zn7^umlR*qnK}!jKw?s8k3>P(K#-;}cMNJ9FzLfV}_K+5bF5Ne1@WS~?lK4zcqkBo@kS6wv5PoA20r#g~wIhEdDj>4AsH(9q zqN_b1EwVeX=9#fOcyeEQti`S<%-0N{R~#56Q!Fw3>4aKn4_MB)t}s?6iIza*nuXzz z>)bd-`{t0)=XWv@y158v^O7^89o)FG4G7#&5^PTmm?erxfL6pQ_)!-+=~IZ9Ugr{}H@VQ$pF8CM!QsOCpaNT1pyz| zV$APLQi+2(7}A51Q=|Z8O5E4Cmj_DXXo6aC_3@i&3(P@p(O_enA`}y*H9ot$V+S zOUbdLu-20~7c{M`KPP85`GfY;xQT-Un}2N9e$ShYc@Pf9EsBn#OIuZXi$hma$@IM`;2DH7J6`Sm^Buv>+@BhwY1h&j5 z9zl@>Z9O&Q(VI)n(LlUlo!)`lW`kiPO2AoMHfCOh+$2tj^$Y{QeNyjB%bJUL#|>fD zU|eH{yR@p%Gf(Cpe*aD?$mEJJZS^>MZW|79L^futWsMvmPL;`V2zh336!jR?@^G;7 zPGTsO0E+}=aG_vFY+hWt3fvKh42qbcN2gBIAB6gKs2L>&0$4jbPN$nYG;eI^QLKZ5 z8SMVk`5ma3BJ-`UmrCPjdAII$>{94s@A(Bqp zU)bl<`S96zPig(C>q)n%-R`uf5l3dFEA|I+r2M`vOM!j@aN6ZtpD?0nsf8a*)fr8o z35aDD7$Yx2bZOiFOn{t6s?1eQ#Keub!+Rm z4AI6ULp0+LQZ>#GH$snN3odxfWuja}Ei{6F#wX*U5H{AgBV&F^;{It4pcREnmNUY= z=9SYu9qUx+ZXvd6ynV;Z{Pn2#?<*IK9D#$BsAKHobItcBDrNT#!!>jN%`C*kRK;8# zuO|$y*Bj)k0t{6t@`FHth<;Fhgh|*-0!S$`_Hg|VfwO2=70|)oF_dhxIHjXZg1{K+v9?E!oAU(5}~y~uz`f#lByvL zj>v>G9zAp6umAYx59>#MiR<4Un%ppR9xzvCSpb7xzB?3UC@%0z%?-z<%c@$g>W9LL z1<^Mz+4=a->UKA@%)t(iZtUZ!pstH`tZm%l2g1k!f4&qz`d_P*SC zVuFFcz%rUts4BT0nm**G@bSH!`3~SQjhb*qN0;39ZaQv_sAoN7)uf`7+%15N77_bB z5J%^l2sq=)pJQK-;R%H`AW3R6f#k3y+|gGA*w|-_k;v#uf<(*xOjM+Et^hBRXjs#W zPe5cd-qSKjnkgz>sKHQRVLgHp9WOTR4nt{F&-PMFLXdLy7jw^biPKH^`5yJ&NL7h_ z2=xz9p|J$kOcWUqYnVwfbIO;tUAhtxHiJ=I`&Ns`-{-pduKt^8^~>H5TF`xfK?CAg zOa#{-Z&n+k;Q!O%y8R{noWFQomf3gSV-SD+OlKnzPk{D0INd}}VwN=;>+*NwcL-@C zQ~$^kbu&-3;Bq29`-Pl_G~^iOVgb#ON&5?PWIldnrF9r{NGQXJ7TLtRPFz50*LmQjQBBJkEQ0PYrVnGXzG;2Tm0D%B))BezH^iQeb)TzymTk#7Nl~GyL>-6!W4B}a zzkxm-ve?0yqH7SjD*raiq6{8&GKNlfP7_gHxV7E2qFKvrs{6PhNn{4g=;|^!P%&Ay zYfmndsP!QZ+i$FpH`t+@ybUKgI%wH@CdlQXoPL=NKKsrUfFUoRC0-@9nmWC`3#LNU zPxx_~4nLV}ayI_DjG&-p^T4UKugkaNXp zbKj5}$;c#kpVo$9}FhoCiMamO<-;GY>q_k zg={SQpX0ht(_3}nAXQ~Ry)`o3u!1LTF~uSe_q0juk3`ou;4SU21k}8>*`iT*J5gsM zN$RP^NRNQ1`KS^gX3b#l_52$IZOEBPx4tE$NYU)KlHhd$-F}k2{%PvO=Fvn>1)C#qS4KTQ3--z5c{)*W^e#*!QV6OhSXOYglSJ(-P zs_D;FY#NvEC#(5n{~5TnCWOS+7G!6Szm+xs+TN96%sE}G8J>SSNGz30uP zz1dtb>NI8zm|v8j?hEFoXZkJt1tv}U_KE{l0)2!s{2JMUeA%eH9^t2Q_b+9+m7Zx> zQy7d>Z3KyhI+@f#6|YM7oOMzY#A;m_a@AmaUWk>6y{giFK3B9LPi(qy2%SapmM2#X zDBJ&*6%D4ygf8}+HTWOY_Y1%EZ|(q2Q|Y$u##%DxjHk$|Tl>Zor?xM6())1Oqrqf| z@=QGhYAUKsQlql8jStU+Ig1r{sK2!v+BJ90hQDsBiIr=NWb2cR=^K(1Nc=sF3JPka zj*#0?XH32M@|)fEB5F?B7N|X(+ByEBcU4J?_=EB@D|Tx*5_yOM;^?!riZLbx2#LF~uY>@{t^5`M) z$?7}zWiYTL9d~LhYQ%KcY}rtAF9>P&7k>x9N%^Lk!u7o4V*)^l&KZ1zjI2<#7 z0&EsuP;%~{;K>CE$surWN#1Uw)wWkdjZA00fk1ym@TE?WBXfc$v)5uYKJ>CFEvFJp zXU%LG>51B8HLXpNR{n(aT_>HJ)FvskH{BpD4e0lge8M$c9; zMk2=$%LeIu18+Peip3%qjg~EkL4BO+3Iowu1*``8JZ9bqGc+Qj)A7n$S{HLz)&3}) z=3);21Bo$M12eMfN{xPwn*I|Jv_~@M+4hoq_iwB~LDY}MUoyK2%nf(2YH(06z2X6Gl_mJpp@@CR5kB`q!*JEE5 z2qfeW7vIlUS2%dECfR03mFcNzFxh-Ax2sISP>lfpPcR<(si~>?4}X=Ou1B#jR?8X+ z2C6rJ+~j?Iec;7x3_S*aia+fyH<}4{!v&e|9`o2m>-cv1-R}lOAPEiye`shr%Y1S* z{y|)asQYMPqgS{!;Y2v8^2&{U>)E6xjVyu%|RsWS%L?Rz@CiEui9Vr#}#lkRPNDLuV;<{l|v zP*u^j$cp}99wHeah9;1u>)8-IN&OgCKm5yh-;0}&lQVmK^Uogx^|&RzDjdf7p+VG8 zqOTgoA^XW-G5D33h=^n6inm?k&8N#UtN%7R?CNj7!6sTZT7!%TQ)9Qu?YXT?=!V8;x&hr z&n^nlu|EcKQu;TSm8D-bQ`8ECTmaB2yWvV$zXt}hEf~7xbZ|@SkSKbM(&hDiGM$GZ zXX}3aYe_IqG$$CbJkcIv6~ni`X8kJ^jebS0zOi1~{z9q6<1UrOL4k3$ZauEPn>l*O zezS$5MuRX@0$O-{q2ng^BJ4@=v{mG!Hn=eNXF5F<38HgT)Vqi8k5e;-S;Z@06l2g_yts4USFv=+Vf+F?{M zh&Q5b_R^()J^l7<_|Sb9%UI;4d%0nEa_{5|un1VtX_14 zn1=9y6)mZvPGSM#(Z&2(37|%`cjvxz_qC z5lE5pSgKRVDEejp643esTOe?3QXW??47@UQWdRZyL}!vx%icggMfH4^oH_VF}e%T2K)rzFjT|uBP8v(cRD=9?M8&{MLeY}eOs*^0dg*{ z1{-4QVo{Qoxp2()dZq{$Pzw)^^2044+DBwEz__y0#b{jOXMn&(q0E0ziV&I`I)v34 zA>TsVasTq+Wa=AnOkGDJIAX~s-v;N2egKwCNiF2yxO;X&FnGJBB5Y+I;^|{6H+bAv z6-Jb6*K~usvMyI`=Ufuikd-r-=Dp!y+}wEgv@SEQQuCP9c1=uv zdlPMd<6Sn;sl@}jAN|NfzZwRX>U48t^;;`rT#gL8Y4p~ya3hW|7?^#twb1)F_rQfl zs^2}e+9|urQ8^FRa}nhGxobw{)7dT*ff=V}uHFPz1%GJ0YnP$a!OI5^viNf(zexC1 z_E-mcE{2Kre6lfy1_-<6+9qdQ&rdgosogs(uqI}lHa_}P$9scbmWR%6VE5CMnS%|J zW$2}G(OFkw=3&d^{Qi0|)BGA5RP2sOHjAnDe`W2i|M%xMs3PBT$TvkpuKGJ?+(nsZ z8zk&V)(`_uXVP5l&~bjBT9j_aP7*hQZ*^-l5&y-mL2o)TlOqRUp3?XYN2A92JG3Ie zRNM70Z7lknQ#2*Va-uW}kkWxPx|#6h@agMbEuNv3;YEi?s(uswCIRB)DFj(W@9rt{ z=}_qwbPbt8)2$j7F@<3oNUWe_u7YKML0lW>{gp8k5_Z=-~(to!%mmgT5xz*mJE z#~ZdhSo8;377rU=`JZ3m3>KO4qQ~Pyr7awqMsP)7tNdR$$E^@9L1;G6hx*>S)?I~c zS6jTFv(y3ty{5!Mi;m@KnTfXZ6D`TNR96&#ZbG3ssMoJSz7JX(s|>MTx9q(8o4)A@ zA5Zp)pt|%x;DF6@riQc(*nGDlv6jMKhi2?SQIHXh93MO|`BWM{4+ocF{l^TwiWfLT zj2uajbpHb~_g{%_DFV(0R&zOGcd}uM2aQEtf-IO}xVpgTp39r(@Wiu67H~BrTX~AN z0MpS(m7zN6R1Vxb?X*!eneDTQ+P^uwDg1prB8OJ3j>D0N;S#d#{vDe{IK0y1i zj*@{Mh7;l@mEW$smrD6`tBwbT(8JytiG4p$`IvTHQK+x@3HOymWNN%rwi?EXT>zNYs2{ z;q;n*m*Hn}do1^9#2>xLO|rmvZbNge)tGY}B1ps?*~8D)^Li1LdE#qif>0FiU%m;Y z-lId_RxX7t8SkpFu`=`0x;UtdYF8IkpsSPWz)=p<&u*xOY)!j&RX(80u6+OJ5N~yp z#Vj)MNuhnuQK;{k5I7yM5)OSHRB>wUBfHQyCH+2zuuTyIK&PLnO~fx=enhiy1r5#2 zsGlSpX=ovH@yKPqU)4WmlWXW6#nb@t0gMbt^YU(NvSVjC^?*d+qgW}w1~e>$O_eiH z<`3D&!J^bO?a@ZFemwJx4p$hMm&%f5Uf&pC8t<716_)_2^M=Z~4&(jyw(re|A@jg*QA{*ZHm{5UK*|4ere^WG7>{9jL zq~^qXcINQZL+0=~vZq2yWd{zFoA!%ZzEu9kN z)5(A2-uQKdV8|E_Mpq3os$U0Q`ut zoJ`{X;J=~!S8Q!9W5MhBY?wrt;}V6kwF3P8Ztw17z((WT!b0lqSaP!}seFQS{ASbH z-^03M3iz~Achrd)<;UvmezOSLs+1ZAJp5F7yb_=&2SD!**&Xh#_a%>1R`GlO-f*G@ zL(haOeQa>T7{ncZ>!v)xg?*ojURvT2&F-y`2h8=)pUHt6JE1r9>M%i!qT<)~lVI*z%-kg@b*pSj`b0*kQiT;B z&b+o}gY}|~pk9^~JxseZnI$$ka&0``H*Zx>pQCCFlBC$~i-D7ghwqKG>1;z4xye#$bFq9EUKR>x>peQ<+j)Y0|cEk=v|BdOugOuv##_4JX=X&U44@Fo}=*uOA z5}cH~+2^T(ZCi43_qVtk$t1Ce{D`VQe0>Y~q335M;mtx)xN#{osei}t1LDu ztzsdBrUkT`IzeKHPZHUM!^X0vl%buGB$UKl#-cv8He6q#gHyOn8}ZT!YPQQcWk(oZ zw}qT!Psd)#hXK!F8Wliw!>AAvi?y@)Ur&T+8W{|~5ZGaJ$6rX3ppm8f{$}U33r(LL z>k*ap+6ZicoGY1+Oprdvz4re<(1R zGUOqs!#{{?yp6Ypf?;S`49}0@vKir4^sz4~W!%-)G_CJxzTY+Vu!W-kW0a&L?nQz2 zEK{&OGeKWxr%LzWf$^j8K^#7n#{uF&-?vIkKHyL4<~sujR0CbbFFV$c*gjhO3emyx zq^%S%;9sXJZ`mvIq<*#5OZkWV1=C*~*e=3B25g|)yU*a$pfB&`?^FfXEemQ&gu!eR zmLV|2fuG^$#YX)3xn13({N|_ow~*D^Y{@ts11l?OayB;f;>tut8dcO4jQIFVGaV{5 zH3xnrDB2=@l+M{lKW9jJp%zW$1o)-VwpsSj z@#47+cJtWHO6$D)Q`f9}B&RHABa+%x3cQ?D%5@8-0L&TdVd+ihN|y^WcDKDh-jz|~ z!Zz9v>&kbD$)635)zM15%dzX8MI^P<8|l=O01X%RuVQU515o)qb$E9-fwvplCMtmPj=7iQ`FG7unJPHb0mSgI$!Q)c##KeTCX_r{;G>u1m;&Ur+*uXA&pAX0I zD%2be*hL4`p9||}fzjPOp7$X3-K9Et@Fhk5-XiaAYSRg?jR`%Lfs#CV(|#c_b-5hX z9$VhzUb^30}atof>o#h}D==fPxOGN_TC%9QR9B~ajpCwhk_iF;JM ztaSR4r1W}bqFdyaOg}&3c%~eoCJtpHI6R+5>#rY#xfFDdSjiNk5Suvp%>++yL`(bM#X7UM8O%~$>r%2-o{fOxR?R1%R@J%Zqa6(4?jCe zEAgxm(ODQVt!vZ6e{H9%teJlaC08Vcw6(Pj{%Z=7ulR=raLIFaew4^#VeeqL9tjV3 z_D;)Kv%bW`tQk3E99++$U^Ql9=u)JTyjFWYORX?kvHEjwGQ-p9WWhEi$fPVUk7(~J z6_}|!j4+kOb|?i&%RvCvGD_nBPHN22CD6iN?`u3fh}up6!8cJ2Qs1MZeWoH-Y+Urz zbto71W9un`b6!o({7O==4Ix`r5AFq8%tQo8Pc)OB2Zm9INtINwkLgty>PIxh< zZQ=T2PPuemt}sSjY)@R8{6o>H5aZamIH>Y|h@AGRHoDOX+dnO;Qc%fIpId~y9~DB^ zr9R#n_?a*VY{lRm(wyS7jgwfRe|@)LyV5hK@n7_07?f~P9CTs3BioEMi_L_=QC(Gz z0p!cMm+eiy$>pl3HkJBKL_|F2r#2!%=-KnO^B_s`xXhC`8T1V+qUe0u>gefz>Gv1Z zfim!U5icUh&=#pY*pe`Y}_~Z*m}fUMQn+CbJf$H~VPwrtZi+OtM>1<(q z^hta)b+MkOda?hsi{sBnb3__>m#s5v&OO<%BCE%wcbf)~9%8=qn+ZQRo?6%! z7RO#leEfd;DudZ`O>Je`rA*XexYh>1T3Tj0cvV$kK2AoI7>AX ze(;KfMm?X}i!U{NeFbg$uqVJZ(lUCmxaaNfo+-%qnzNms@p69;Mh=3x|6s6uzX{an z-f9Akz$n+1WH}y&KFq>CuTV95y#Bb4Cw{uq6NDc3y`=gE)GIT~r?IGiTsWaO%k>-fJHTH>KA}Oe@Q0)sYPI~zM0Y&A{9Xzz)HERg-(#zTo zdt_3CM@H2dJn(5lFKe@hEt(aaO5JLaFS@GsAc-HB`HOHSsaaKcheq=&TpV^MSZ>Oo z07aRpt4Ty8CaNM*6TQ4Lz$#Y;Mv^M1zZ_qgy_D%(V&|nA|3V~~`f9r)q1*FxKvbSv zqZT{BVhq7t6I}=dl(xqU1`vjL6JUoGXG-P@lh@N-&Hp-gM)Q9_THWHq5@mldvkb#J z?vKGdsKFRBX>?C^WRyZKndD<8Pt5$s@&mZFv@H9 z+b7s}nky>hBBRj*1mUX8RRhH8@g6b}#D~Ft9oc9avfKB1O>$q%WZP%cJay(g;BU7} zktJ)JkafHF7ZK?p zgM`TO+kSEWnX5GlOyO*Uj02$4g}(tC zxk-`TU?hG3WM0jPkK?JXz7F#f@{0$~O#ymxxxw>LH!62##DLLPsB&bF43j#5%bFRH zP2lQkO5=6!E<%FGTY}GY_oGIc=(iT}sqdKMT2;#L1#^~Yf?9QtS4E%Wv@Fmlw=M$( z?_Jwo4^TAVhn0w^s%;w>xdr_(;RWN;^Vfz@^Z}WPNz5ZxfhI`uJ0Z|eAjd_90foj!`II5}KPAxzt;H;t0E8$|)lMQ5@^T-7 zp7z*)?@k3r?qcUJon-!EMzB_72x|1TrNdOqHk?KCr|M(%$;2jX9(b06x8wqU)h;|@ zWl`eBZ^_vhIL(x&hD~PKZ0;mDN6cc&=@#^sVu65|m(D7*oDV)Oel$1}Dsnao3JSK@ zPqB`>dR;#2A18|!Pj7^RnIupa+BCQs%8ZJg%zo)unp%1?(kT&XxiajWg@`T@AQpqm zu~k|$$HR>_?4Q0@O3LaH&1rvvToZ)Wsc1*Sh1{TV0ux>oW!SxB!r86wrqm5W8J}S+w)t;_ucbN8dPYRNTJS7ZvneA7fst-IY6;mzmZ0) z^GO+&<98FH0un>iPuKnENb#wn&^3#gDvRZf&TpWVTo~eFbrA^bNf&M;3EJd%iBwz% z-1!ELyts0jMV^e8_3uBT;3l}wbYCo7V_F|niTQDeEiO-F)8Jw%hj_PlpRYb}7;671c=1{ z#OKd3+KGqL)innu;@)hySE{Tb>1fbdNc%fliynDB{?t|<;~4pZ)JQU)VmCoBup03k zw8}o73HxAx&293_PSAy)&W? z(rMy;Sx1HLG~vlQkLI+-NrVvjIM(V+==DpN&t`}FO`)~Dy`U@q!$;!*bgmxsHMCS| zS%ovf^H9)LSQtEoVmXPBDVd^1O{GptZWOH@AX4!_ZEGt4;(-uYK0t0>VpHm|Lps1{(fTO~*BDUoqz9=MW& zvAkLC<>qL26cjmOVcSU8L8xTmW|%#f+m8M_u3^qWz*vq=f<#FoNCO^GT2LdHUk#b5)dYzZk2jnq+2N4IVnbHR=~If&99dP zNq@w|o*U|eKrBwtLCxYy;v-L)^j-NwrZ}dklpa!xMe8$BOfor{m`Z`nFwhiDjs94t zpa3VKTC)S3^_l+C=XI+W)w0b36eFMI0cQ_*IokTmJ8-uj z?8A`@89KJJE&klGrCM9!wU<2L%U8jL3dYz3rrpsj&T`k_YtVJYVt6bx9O09JY47}t zS=WNoZ1zG3N}Sd%tJ4{=>M{bYPUOB4@n4=ms1E8yUx!DH&$j05)gsI^s5?C3FC^-> zTUjH=XL=EB5gtvi*IzSB%%{)}^A0|((dvVp@PJR)b_^_fH0faR-6tjBpvpv>R z2Z|*ZcX%!=WJh|58RY8dy~41^xei4#-MJj6isnN2QT>hoILJ;to_Ny{i!(rO6G@TG zS9(74-F?61ljER0oAp$yd~bGmILSbOStqx-b^KjQfcq!PGj-PvXq_tJ=SLLq#eIjE zncD*|F8_W!TexeO;d4g(5$7mhN`X6YEF0@A4Q@6@e`ELi@2NKOrf4e5G_BGQAC>)m zv<@FCr!#MMV%?j7;fHXEayv^>;b_+wKOrd!#R?(y&G&oHRa^tO=d0+CjI&LjJT>*2 zvK@lufLJ*9i}jz4C_S{V1_$?(#w$yLXe!i!4G0KW{qU><0d^bL!l7eem#)rXI}vC~ zW*fr=TpS(_wVfRio}|NqiGMX{&vY;p=&O~`%k;xSFo+>t;CN}(+XEFh%oVW5rPkwM zhkv#sjA0n;Z53;G_83nVDGEEpMz2e|F;uxx_}0l9X_r%a!$h0)CZIwTY&LkYA2F{K8cc z&SCQuD^pkRx91i^QnO4&*)0&C{;jk{vZSJ0LjLJ+LKu<_POW+~BNx7wsS|SS%J)I8 zWkxW$U9&_9I991utCUFd<5RQ2nxW*L5%tbDZQ1;bh@tSMu|Hvj%(y3x?ik& zH(nW=GB4G5eb*TN&jLSvnYO0uE9^wYs*(Uzs1yy|*;LqY)`P3$zWzJLP|(Jd8in|> z%3oGj$sxqH?~%$yFVz>)-PS)bc*o$QY4M<>A>S$OFwTTRVA{#Po>d z)=OBll8QA~EtO&qQ0uwp4xKV3j7Doiqfl#W3=;r z!}iY7UxE!4lx>o|uwxelc#U45S1Nlorn5RLFp?SCfPNzo2{nDoV{#RE1w}ZwG~gy# zZ)Y7RFsKT3t#_?7vcZD~Y7O9$FbHb^k2m1JHgl^))5h(`C_N8!`n}xF{mJjK>X)+5 z(*Ok@TsJpHntpAg+@ZOqx(zEyFDm zISHO5ko(RHGA0OFfY1@QWP2p{DLk;7_3ujQ>x5rn{zx{tG}uf%F#sky?59$bA9h3g zmZ4_%)RW2=kwR|nmBlHQ+?WhT_4=)?Z?ZqFijlG4I%LNJ&=$!DtvA`}oz>Kq&Fw?S zjOa~-2x=4dckBLm2pvT@`RS9xV@j$zDeCGc=!YhZ7R9H;XDj5*Yf7&FbJBo`#ZTGN zRT7B(%~|#IU{=0%z}aqIMxrLB3JmXe#8 zHKRQCep`;=wHblpp#L`1S&2&5v1O=-1er4J0D$^8SIy6{QD^fT1cYbb)eTMh}Pn+KLZ)gDp0^sD}=2+!Ao5; z3j8gD;#w2}DD69)1dP<~%5ENSatYAp3`x_IwRmZoaG56ZMB@FqoEQp+9^Vp&1L;R8 z?x0VL1mJp34Xj2}%})QAn(YZr`hegtT-h%nLr^qljk*p57<+Rbs;IDUg(*bjk58r*{gFuA?EF8 z6%JoF&s{U5wECM9uhr!hYJieMI{C9ypii}~vWoJMr?N1vkv9_n0RUAXU%)0WOP?sz zz?>uXDiU>j5!#%{%+!)2PuNHrhgc9PWa>e2&36^~fQ=Sk9dy$18*ri2fNthFXAh@){F-NueDr`|1~F2QwV+)2lXdr6WrM@n zJiNEJ>JOCE5!v-#x{%Yd<;$k@;PV$3pwLMnQQ`WI*)bqYAa~q@0XzGpSIyE>)xM9-KA7uOt|aZcM?ud)oaV$DFeu zVXvEre$}v%csUE?wghK34^$|`@xfFs_oV0QBpq&OwR@*T&(ld?9^kISk~+CYBDXYw z+kWo2uGa84d-@6mDhp;mjD|Xp;m&RV?dI$?Z|D*bq7I0vcDfCm(=R=u}H}Ax-=(}vIQbxK_PUcq^{%@=+NOO#unKb%Y z28Ui9_KO3~h`KQPyy<6J^K$w>G4i5X+X7TFu`oe=?X#If_A0&1jD(rO8_U|)53#Z^ zk4KeJjTC~6D&7wZr7n}IBht1PZyimAWc(#f_b1K0^mjPs(c4#j-x8iLHfzUkjFpyr zc@-CVo{CgTC51ExvAo%)yGWQaJA3 zE?i`)(4#p{lzST_*E%wzkm}`?p99gA9lb__b1|7nb_fQW6bg7y(GvT(Z@MICZ86#EQ=fRT-5c2Xa_;D^JkTzep_l{f;PExiZhY@oY#W=CqJ+sQ4B(n2M(EM>G|3o zSaT&Ts^(o=F3`1{v4T#|+Y@$^Ji;8T{O|tz8U^cX<6_awsaXY@x9(b|4pc3N?G=qW zR)M@}Ihz%?_ld}_(K3zZU(eBp6--tNh9!k7oIp^jS=>a3m9Fh7Fxvn3G5O=hmbP>H ze4EB2Zx+@F=P6x@`*n6Qb!)!xiC}A{P(Mj5+DW@Tp1pV4*1~C6d3@kAZd&$iqDzb_ zcd-nrz?W~U9BehIRSv58mJ-6&3F^z`a39Jy+W_r4U)&g-$OQ?V?{>K_M(l%FBk(HW z%agi;B~B)oe~jfFX$JqMPpXD({7pg7isR#AvUqUT+3)_bJKRCODhVP+glkM*qbd=m zyUTN2zspEk`H|!o{44Em3e>8T&?#zfmYrwaTO|< z{Z1v^r7NlbMP|@kWM_y%IlE3R`9^8kT`^vujFMOlNin+lRyO{EaF>iE=>2^s58x?PEY!Kq$Df-g zr?dFus-D}7-w*7$#oxO6)TlRgu}CrQjLK2x?B)Oo3(m zLLFn_`cue?u`!Lkty;H_Li=0v^>w^2g*vzlw_zZ`DHHBT zomrvuyT=}ooA}Kzy#oZdUmjFXdKrthcKV9_Z}v8zvyfHI{LB=)K$j-VD=clM>I`l% z@Uyg6I62ZapRJvjf*KniL})3Dwhj9lyQr9ea*=vzU?G01RAX*=N4tXZ+`M+ zQJ45nV$e{MKpAoFx0}k2Tn50mtg6riUsuhFg}5n$7*5S5I=G{|Y1&TjdlP<#Mg>kWeJof9_ z6l-1ktGLPKFZEk_>Y1*9t87Y}tUqcCluX!B?b%qFe&4^-4w=PiWbniNHnvKuvC<0X zwdohk%~;71ta)~S%vrgycKICWI%wVDyC_u+^SA$jt%Q$f4*EkOxE7ADkBXvNPEyId zK>B5>>FCFaaL=7IW*U!f1RmSfN+a4uXo4%#9){7hM+K6ag>P93JKeHRK+1!p!``#W zb$mq1wl5qUsg_btUY zpPccfiu0=0%07y2QLE|jUmCeAzK-2>=xTh{i&K%pMI}!8bDLL<=DM+0DCyOluk78= zrvT=ujo%Och5blRoS!1CQUFGNau#@~rtgZCslD7SN+*24%4ReJr?dvc60skwU9hneTprjdPJ)0S>42~HqgNJL%gs`h4?zR)WyPAlxAEil+@4|LqO6Uy z<8cK}Fkh_}fNY|FdgjddG`Y#IF)`J{-)U&|`940{a%r3&to$tX%}??8&l?tW`b(!? znQj~fI*|;Z8;8HQpuDWTJH1n2j<|ODf!jmbv-~Xm_c12qHzPj8JUpi4Ag*-LXvw7Y zn{nQb)mlr;yDzH&tv~JBM&JKUBk63+3B#2{GG1e*&an76#9Bd`3H&m*usBFfacl$QA9|acmrs44dV2cr*)O77wD4*tw`vhZ zW1~LRB0i00qvU+^yIM0bB^{@ylq8;I&H_?Ho=%!L5(g8)P!`veipMlF-B2QcBuPph zX8A0A-R60l^1q!m|WJxFE@`}>&v5NdHIi(JdL{hb>2zs`YKT* z$2i^d`ePu_F(SX{+{Vec50(|we^_X*LFq^KFAjN=N59ARvf$&p(d5a~_jHV-5|*7n zJZ#j{U^s6CyN6-rZ%pGTJEFY=<WtbpAiaew%j#s!+rr0xi*@x1DFRrKfdUl$ zP0#aEeaS+lzm+I)a!GRScjl=)P6lBC`+6w=pLtYa5+{$#O_z5+zsr@2jmry%ZZkO* zWf(3QIxSI{S)sJ9TjCOO{LNiTMh@%ui|uz+f~AhqaLFfy5#xpR@q8-d48jO0Ni$p> z4i&KL>ILvXY=B`!TrnK>F__<@Rij7^k%wF8(0euq{4=0s>E$dUA`-Xl*Da6D^{uU6 z&URB@OV=SRKHv;+sDCswnZ9!j4B23xr?zHHbv`PI0w0uW$DUTZ)OxCg_(>u>jUIMG zh{)&h(@mMP^)xx~B`GhLY^IAhgJI88j-s~OY#hSO(Dy?;Vcf$eXz3ap#MbJ@QDHQ8 zn6;W56GPXqZ9{_M-i5?bdW~S4p7T%doT%cdoBvFew%`+LVl~ka9!_oP4EA{?x)7Th z2i`jyB%mG^5L0qHR-(ktt>-O#G?hL~*mnEYg=+yZ!hl!UL1(MA?dU@PMAU1qy1Je& zz94SDKhWe+E2vQ@QkHe-Awt%IA0Xd$c#RqOL zEExBBIrYyx``_-5a~J&mPKBJvJOe4bqHQt+IVkB5hW5=@Lf4b?Ty5*>>U5}%yltEE z?=ysX(Z)@LU&pquCK=DY`^k-?_OmeW8fEY3d6#H1_|*+`OvsR#$Qjq8-yCSw52yW- z95?7vL?06%W;yKU+LWDt+g%s@`5kvojlwxs`yC(q)&7KIcJo9#bM%k<2gc4pQ~3@H ziXoc_oL6PaaFwXgvj8KCE3stzr)HIt5mQO;J6pzqvps>t7;Fjt;?P0Y7(eAtr1CQ} zeQsNL7+JWWmlwDvc;SCR`~khQ_ga+Z2H>kYa4BY`gPLQxZF#u3#-+bqW@Tr|f8K{j zi$SzhTXB&2UKjX0ENht75G4xfoxQxiB0HUs)CFz1K1|>~fwe+wkJKRc0(X0CcC6ni zm=XnLWuty?<7JnHQ}Gy~3I0zFL2qRNx1*Prn?7)y6CcG0Qa(C9_k;LOG5Sj-^UQyL zA<`c#uCH5(gcyJga53p{)Ddubpam#QGJeeF3p=PcG&HcwO$z-{5mo8z?EJB7W>AFJ zeb^=Xbvi7}s|y{2<(9%A#kg1I54Qv=ncgL&>^F4TrbrH#kHl|{`oCBV2K7eVLLG4W z-{V{Rs)-Z3OT(S%Q@C!uwmAXGB2c>U=_%(*8?2L8oTNuWi@oHaW?03vwTL9}uh0%0hSBp(#L@ zq(K8HLq`3HU>KHXpqbWUEf3A!Y0*f?GcMIG`!pCFynpgbLWx`1+`WQ17(a;ofUs&)eO%Mf^MNSsN*-2wY51T3VXz zT*480U24yZ$Ms(=xCq;vu^5Xqi_BMX{cfAaFrlE=k#{643ktoOtlqL*Y}Zi!WnZ)mhkg9VcX|w({3}Mt`U4lIHFc14ldIe2R+0b zE4n#<5zibXe*SRq-z)uEhVx6qsOTYh1m3xzaA6n#wQ_)m-LSnVymDj^+`Y7R7*c_^ z5IqRoVsU0Tz_Ze+H|i<#D?YtJ z!+YR0hZJJLIPC7gIG|2fLismuLOMhAKYiBI9>Q_^Q3AiClHL+$(o_-4Obb!Agz!#z zoy5oSwG^AVa!V=}@BRI8Wr4aZ)|NZbx!A_f`O%-0yi%*w1IHQlif z{rEw=P@mwS@B04!?w(`wd%rOu9eShNwgc^`usjic&_SQTcTJJ%nl<}vH>40}T9l#? zceVIKil&|U#fwFrgBLsQd|*$xW_q!iMC-b|R$L3=$T_Y@iJ_ zMFWQwh-x>~_7nK!BBWLFb)UdXFg1Zoc6GhUVmcRZIF7TKA_dUAia8!oWwb?ok$3dFtVnG;iQo034(Fg;3K;Ci25(qrk!?W+zy+iUMYum!*XYDkPb|>{glLxqvN@zbVM| z8qRRB{D$ETL>0@^uMm$C4HC^Poi41OueF@@O`LB~?igMMKBW4|JV3&UKz=aQRCmme zYw4)I*=qQB^F~7HYWlr)9$H?uIM%YlUPK5*Jr2Utq_>`=8u~AObT@)mPH>Pnq;bqS z{)*Nrq1(c8vbgN?Z?=nNg#H)v>>!92aRkJpWb_Msvn&D2Ga=TQBTaTBFuEUnJELp7 zg`7z4A42f}f$n&ieUCu!Xj6|iRW3YBJ4&E=<0rJeGw7AaB=Ys2o}OCGZu9XbD&jt$ zr-tK|Cw>tA_vF!~`;?jrggjs0eC1eAm9H<{6Ee>gtXFc8xOf4r6~aSl+!934R6s1N z{-}?Hr#do-->Av8EzI8dcPxJiUguNSN^^g~-&uSor6$L~phJPWmlA#pA#1<#eHDBN zU@wwLhbJnZVy%XE*{0JMnvJHA8DN!wNz@{Y9P;+Vo zJM6G+OItvktdfz?8 zB#CaJ5vHf-iF0YRtd(PxU6oap;FQ_MV$KY$_6X?+oB3}go(;3_2v8-1iUeOEL*G_A zU7byX4sJ4eeR@0jv!+Sf5?dlP>#Pz>#PN>P5ubA1ga1zYgm6m;OY{G(nfE`=xBRI; z@L)H`_@Ws|j?v7UVpQnp=I*}M4;6yc%mZXvEttN`KKuRV?cDFMkcj?D&b|*{5u|DQ zG{9P=Q4qg4`s#t8`3%WFG3jt!!g`b$?y5poU*ex5M|k?<-3yO5znPJ7^0JpQDu~_7 zOH5LDz(E>j;~p7=etWO!Oq|n^Ah0+cH(lG?wuON54sFI@Uck~jJe9v z(;vz`=~Nw&r~@Q(D~?ObbD#9T9yWonA-ITl83^LMkIC7)qqYNI_lx{?M7Z`{OvvT* zijSP%U07eFNUap1#@~nHq%y+;3?aY9$a@CiX$MkZBL$nY73AYTJISOUMsuyGcga`y2pf6uW{-OI(xld^SU45QX8Wfz(ue!ivQDiwJpC zxJF6WNjE@T6z9|U>x3}kb_(7stI-t6j2BTXw?Ud>M>HTSs-TW%)ahypFaDJ?9fuES zGT=8p!+6)Gcm$SzikTAzpcuRV3cVFmvcmTD5kpiiWQ8^;H5REPP%50s+qpx{LxJ!N zmLt7)Y6bwQVPe6@jQ$7l7`p_zV6+V^A_5Ey6qFA|{ifU;BKv~$Xws&aK(6~`vJ}$3 zGE10G9Y|r09reDmH@^KfYs)WlD40Q2RRTM8y|gzg+Q=rQ+J3C%tQkV&0F9i`lzTFL z)x+`|J-vwLQ2bBLy0Xt7>Dd{1E>zggo3_7<3nOEunE9EVqbr@ub)eA7j%tNM#PsSr zHfC7H4!)=TdbR6%$-7{A(HaC3(CzKl#fH}@uDuqXtYb1^jQar7LilFsUWwTaU-f}ZpzEw?eX1*qvZ_FvJ_&_)Z zB3q!L>Glu;eDBsx(FmV|AOd2+2sDf^QB1CY;&<9_V%22L#sT2kv7*9|k8-?x7HwP% zM11&y@pCajy5d^qBLJu+EmW zq`b}(IX#_f0f;B9$r4pqCD(ul@hvIfbPXDXAsFP#2s>1E4V^EW{f+Itu$C%CN2AP6 zsU=Q@%?bsqHq-as4ES!f)z1U%3US$m;X>mpHRP_o>jfyoU_>Ega$#xLFN#y zW1M7SL+Yqyo1xa@wQShJ*3YF5bNzDsw?0T^AU*I`X-cJ4QNZ%giG5Q>m0Epc5P;!~ zr28sH*vIVZb*V?;`=y0rUE_s&>|<)d;CUL;IXVI{FO-1r0pSnbRV;~Pi!%8gq*N7f z+#VVgSQUYjXHCDg>`k_?UEp#ThZzb_dXR(u+Blp(OSac$cQ*SdbaDUc=Y4xK>Q`o* zx~x!p#>}z4I(fjex+aTtlVCN`g}nMxL}YcdaQV-fiTy!lf$dYW!ZYTfF!w5A8>eGz z`E4L(eUWCdvWs>k6OUk<iHTriGQ+(ANgR7yePQaJ6amV$;)qPmywL8KX=6gl zj)P!y?(03qk)F0rrZcojQu_E~OzqH45xxnAs}bNbU2D z^I0pwEl2tR9Q+cIC@cI1>YM0Pc5_TPo-+okDV&(*a^gqlHGUt&)sU{u7!+RT?b_3r zU*ARf2`8+`80-D_$>p;?S5>EpxQr?} zD^_wwO6i})@qxzZG*|0&m~5C>w8iFY^)I;!PHtv?CPHtj3{68c~LC z-vUwb8YBtz({#_HyDEl@bv6**De{Uu5zeJNj~+ZmKvRV--RN?9Gd8j|U1L3Dpbb{i zW1{J-UT%S+BP;zZfOWu=N#82t;r*>vV~{n_w3JkR$_&knzXze%lxu)c66Vh)=(nQt zMY6X?UDVr>h%6e#E2VLHAWCAdzrIb!v6*uz*H@^t#`@iW_wh8FkjunEowpo=G_pi* z1L2~7qUo*k^HRkl^Yv3mhBzhSf{5jm#MA@k??P-zkHs_SLby;r;3Wpm;;7jqw-;qVY* zf*|i6{4>QUz*9f@{>@NcTKjb@TdqNuDXFF+hwxG$D1>U^0&ZPs3f( z0ShZ!x1Syc)Sytw_AJ1{%$Q)kvU)}Q*9Q(`oBe=6_#s6VpN*#K4D&C75fWsssxGt0 z#J7fAXYp>QiRh6_RWJ(LC=P%Y?#H)0{k9EQz=WV}|sTP|rH5-b_ zhvJ``8Z9hw1o$jbo73!1t_%l4WIx(HYQqXh0~n)siOlRWl_4{{2hLMot9X$W+qi7x ziJ9+tN{5}Yo3}K5CSz<`FIZO-`N$Lbp|X)IyS&+>c)Bxq8%*neIqQ4Y z0fV6H7c^YAda`hcT9mqY-1;7%Jk($O3!pk#0Ha)9rWjOkJG4%h~N! z<49+Up(7Fx@deH?0e|(&m@}Pu-+C^sj$Xf_sRc_^!_}&X*z^?~QhoJ?HdesU56G*j z9`*`D!URCK(3ixPiyRP9RdZ{sE}p?v!eOe{&Y!0nDy3TZfk98_#TZQ`hBMrx+Qz~^ zsER+b&v0WzetsmApWMVHD=if<7s0J6Ec_$1N0wu>vDZywI}CDTgHVbs-HYuC59=a+ z3J23|zzaYQ?xZoGm}phh)GojoM1TFC#%Wt0jT9Bz@X{QXgAc3sIx8U*Q?>K2e%17O zsPZ90he1Op#GcpK$O}trZFjg_mI=T5tgg?W^xow@q51XwC{ttcwbFD%zADv?3xeFxVTE zR?%1gr}Y98N5pyePyAD>`9c?iE}`kYN?Un&N?nLJ(Z2A-wF zP$CzQ;bNLJ+^;|LKJA~>`|t^z6E^>QsnPKO#o}4aYN%S_*%|2Tx59!tw*LMtv?Jrk zT5CnX3l%ZoB)dASmed_td|YP{#XOgE~`m8cuzl(jI=M#27Lf>U@2 zTZtFX`*KmYA@doaHEOOaXOdfu>xQ>kwic}jk<<5&?&M>+?jbgXqC|Kw>u4-m7Zn>o2~L{jCN4cdr9?0hMz5wmDfq0D!v7e^oa)X&+h*g8XY2x zwT%rZ|NSJt{icC^m4XN0@a%n*z3qH);p~*G+vV=&qr~r~dNwJGscE32VH@oBv@)IT zKAr!z&$?+-*LSL|Q{+ti7;gBkV_@L%^XEJtGb$?T0{W9#*F*@?> z`$n_l4Uc>KwS4UJ+M$px0Dku5H_dmyBd34{jywsgN=EVOUh8D45s zZd}fy0#)Ua?M^uo6!p?v_VA)m|MPwYvs8mb%);9wL?bmuTGmZv3Rm^L)V$}?s;ZWE zI=VBr{9lPA(@GEW#zc&5c}nOQ>w5fEc$kc&n0foGDzI5Zxl-wQgn9N3ThbtsZ-E=b z(YR6etm`Syj&5#%iJu@!r|Fp)&85`ZTKk~XY#(LWYC9t%eVf>EziQ*FvkNu6fY=sC zce8jQ<$p6IkX)L=9qdoZ(X=$^x>b0~HOhZ7bab7HoHcB%%Uh14hks6t5yn*+cfEvb z%vfKRREv9r31aX6*nY^LTyUTQH#Jtonbmq(gC$IZ{r_&JZ( z*4S9MLxd=Y>}0|+UkY-&?g=%UvC%6F3xnm?*U>rqp1_%75-N8nB4*Os+`Pt%4iC2( zhKzszdM$wx#xDJV5vb%~l*hZPQk0a&AkSfzTc$<7imdIK4(m}B*FolRK+c)E5N9A* zru3$*Fw|%?9kzb`g95TF#jrq)c4yL4aTlUr%Xr13XBylj= zSL153@|-qw6#KRUu2qQWH0+va9#lb+vAADL^_|@hA-U$|nyM=^=SbfTMw49)E zadT;t?uT)qQjVDNib=m9<(FoAW}T|gpB-dTPUUyM3 znTs+wntc)2>C^XhHG}a6z*uM07(O0`)B(MQ?jHjp&7=^Rvj4u??)L|-P%HJm9TH063f)Dy2a~ERCupv(s7-mQ7W1 ztN%7I4Ue}@l~aW#5Fu#<1P3UuPs^PzJnieu#nD=1Pkm|WrFF`}yE0090z<^xMt{j_XU#=C*oH=J4 zlZ2m_3HE6nGm<5Ca|juY-j(E%=w>}3DPVO@fbRDt)j)A1w?I$%nVHGpy0I+q(Xz2@ z|Gq-e#4@pn$fxpU1%q+I2OA0ZzpqgREGQ?fK?HqV@VE%x`9H=Nb;&`e8*)?~92tlfounz&3-0(c?L@W?H?coMg79 zUrTtM=$~He(mM)HQha_td}f%844(D%q;ukrXm?P3DAKdmCh^4;YR9W3>fY35JZb?k{@WV8Q|yf70fKX=QBdmC2U{J7hd7J@a>+;9 zglBp-9udnYBAI!K$&T{XCbvfazcYb>iN^^7MP{Vf6y`NRG{J@2v5d!*+!jYuVX$&K zvu19b-_5soujKjVaB4SIf5htiTIeVifqcf24Tn{blhpbh;(MTT`YbYpg-1BMy|@^I zFd#4!ooHuCnZk@v!)uxvO+Mjm%B5oHbOx31=LOY*3ZcRXwI%`XSFJ%>wS#^tasiYH z+mW>~+Ed?g>?pQO615$p-~|aX&S+$Pq)6}@I)(RVE!ru35?+#yBDTF;TVk1)t%8K? zwuE7K*;p({OK6JX=Yzyt^9s(q)Z%SNwUL1k*weGV{rTTlqRyGwzG#;I;0ul=+}s_w z>09J><=QCAI!pG$)CbgEb@P>3$*+B>k+xBhYU&ap1~e)MO4I+ZYa>PmXR)&%+R4#6 zecl;ZW3}P1>HbEdq_dTBWX92Ef{0zfFHq3Lu)Oob)Ky@|HI$)al$7V+##Q%EjmF>J zoHxG1$2Zf~Ug{osE-`~0q+6v7(Y95>DWz)sjd}>g{GR?y0b2x@kdTUoYKZn(|}fiqgfFtJ^AZ&lmUm@6g+Ck8s_a zr>p0<$e0zQL_N6Uu49*cR-VMD6mDxJdbAuy%aWE725Yvb32P=LtK}6^WyfdoGBg>T zytwh0MOpe@CTxfnEjjGwqR^S4QZ9SnGHRg2qUgODU-c$Aw%b>aR;f|K?WV z(|o8|1GP#GiVj{fRMXN-PxuWp-23AkE?B*`gGX}8RE1K7Zj{4J>!^{OO_{K2@$l=m zeuMV9?2!PW019a(vHqJNU(wcohdlPFAjnAP+_yEY-rpR_%jWxPNZH|0a6hllzzE6zG+-rB z{ZqigR|BD*3xBTW0|sMfKQDbXdI37`Oq;PKzq-DO_d6c@_ELtL`Q)^Q0!2{YKgS`v zq0?gQi-N#^*zx~- z>tXf*{giNY$&jU$7r6$m3}$Ob;Zf0JlW_93$)X~5vL0oM){uQfNUL$Weo;yoa*e4k zRrBIe1fu;MCK43#1oacwIxtX;Bi^>5-hPFKDnjoe}xP5nDE7*|Ll!!S`0>imy`D3HCHtZXbsCA~_C=?Pwhk5}g!pi&Gezh#=hE7V9& z*ErRyJ8+6+Gf!jSe^CDX>YG)1OkGv%u^&Db!v!peB@_P9`MGz5h-{Lp)5>qEV%nv? zzw%%Q%l}WYH>RXu49P<*CL}C^mq*lHvL8Dr_8(r_oUVThtQJ9ThEtq2VeoORo%}=e zgeDbK!SIw>*yp30TO_VC<`(bkueF$6Ymp#nJaucmuIhX9G2o?3gXh5b!uA_NBP$H| zL8A;m>i;X;7?Jx&iH-h%Bd^^yG8fu*{5~&yjlT^=c1!)|9f$jg?kWvWoF~`zyBkLU z!rHDoM!H*S%ITOwkKWQ@i4La}U8A3M2&yko}9)l%*Li7qA$J*<<<$D zJj(Xz2>A;?Ggl~pO)!56!#LJ#=r^+ltmnPtOqsFhP0l=;ug(KgTDK&a^N+(DxwNAr zEpxo3AFB-|njH^NTm+tAP9fm2or^ZDhHfCV!hZg5jqapS1YYVfA1cwVq+)!tO4s<} zJzhz$9+}o(fl*;q8Pk;n>s4Ev>LL7fg0S3e*$F@$U#&AqB%_1hQVKVVd%|0K_B)%l zEo#PSC&bc-`OI4V<3+jdY;s^Wt}>@OPP@W{M7z|&oeNAoGJVZ*>}>Uh&Ht%QS?y46 ze@ybA{j)_Szj!8$srJ-I=B$R)eSJD7Qd70ll_D&nJ$RRJ}U{2T(o(e%MF@!3Re-9ZQI!;(xDaoWb;qYq(D@zOd{>*(V;y6{JO83>zMO^vpZvlqvujY~MrO8NK< z9ye{q3n2FnI*#2?EfxjHUH$AI9oR+gw6N=G7>T5~E9W@}2qwDv*|9&> z^m05&$hmXbye}1G7QZ@ko@{+5@~ES$+eoHNtb||b2m8HQN-WaQ9f?kMUh%LLSM(?= z?-aAPR)|eC5&k5wV=~Ws&w6Hw%b``s_=xqa*a1lZBGeP1GC3Z!bmQw_BpnB{Y*dKv`RvcZE`7_uqUpcTYO>wx+0I$Oi_9!r)o6l610_g&jx4TNXoy4U;0Uv$pSv^}a;;-BySJdI0`7f|#zV(Ic< zm~Pc5o&LAI0*^*>T}Kikc&93t#Ka=%)$AQFdMh~>)+ZnQ=Ro?RL=F{JRaZvGuk;_mDzOR}4a)$vL z?Pl|-Qc2l*~H*K~S6`{0BHlv%HVbjkr7WyPgV+oG^x9HpA zi>syS@VQh*hcR;Mgd5)skHo*CC5M(uMN%UA!QWBKy(0c+7I7{f>+J%yefGF0V9xRk4-Bc5s z`M;pbFU)!`jfK6amO$E9nk3pWn90$5?je9TId{^NP8?M1u4n|VwXAyi*OMnZF8Z+F ziX9aPmi8-dHxR)lZ>}PIebO7zo`I_G;)rih9%DwBfG{n0y+*?to8Jxk2S#IF+6Q0o zdArE%=V{M0VyZ11{|{N;99`Gey&c;&PTbfwn>4mc~8T+rZ_u6Z&sppwq{Hr?Zw2YQ?=ymcbw&nOd?L0Ku7$;%-y)x&)_$6J z4#RS>t+49FAf?M(q0?1qED_wnp5wILD&gsHFy`SuP zZPNHQZ~TE|dP*1~Rw(rH_Ho3$oQS*8=mx}a+f1X^+>j>qGn0Uyhx)kN(B6JRrjbY! ziL=DyI0fhdb&G7F2AV4>CM40#840`y##c<6L-s0U;PqQ?VOV!YNI*s%DJj!g8Zj-~zR8o7pn0 zL$_aZ;y&y}SGsS0N2I){l-ZbH+d)DDVL57WOXE4Uvg&2h7F~o#X7~#i&tFED$k>UO z*m_}w95QbtxvTdih=T<8l6k1Xs1MCpRV7K|$E8)5>gYF}+jz?+0nKCpSY;o!<5cE! zw%*7W!4Ao%@Kl8iI2ALv!Me?pVV3&%dD8t~Xua}ih+2k*`5mCtopx0#!HOw)$X7;Y z=4@MX38ZE5_#G*@j{`-+T#uJK+!`Ek*vBjPH^-{3mZ46fC$+K0xBEO^VjE<3`%`aZ zG=$*iytcKX!@+;sSu#TKEtFPte=Y#ASg3g$DBcE`+0Or-d+wQaE4I;2IgjitQE|pM zB@=N`OqJ!Ks+do1c77b_Ld_5{;3gH4*CI&+7@<)|stOs1f2A65$sS~M);dp~xcW%6 z>^Lq~fIbSMooPLW@6Z7vzgF*>p9cjeOit8~Y_J@rE!z061YWgPwb>qgCv|`AfJILl z+_l^}M}0Wi01ev?cVZnyr|ROyV3iY7cAzqK(1Dh!1UwmFOuU}Gmb76xM5S4G=}g^5 zJ0LXSrMpjvx!QJUoMvRhgBi3|wUV1a?}5tQQnq^UYJS*Mg~-d4ebuL z3yQ|0fx{^|I@X|^T)M< z1lej-!_+hdy?S^U#^de9%QYZ$-&m56i0DVdm-<&{X6m zE*P@48>9{%_0c`!m?LD0Ud4d<4{TJHr21vAW^SK~JK)2K6p)#q(NvmfwUDW3v6C6w ziI+VXRb6SI$Y7EldWa6L%n;RYmQ++M%qNmu4d0TvE!P;fsIDmb3%ze%!9T~42FbTk z*W(y{Ce?ePLZw3bE5|BE9%yhL1^d)?ymy*f($jqFTT_vOLY#L^CX*#bkk91$F4{~U zfllxB`Sa(kmofo3b1}A<{d=!^5SBF_FFKkJytnk1ZmypcWZs^Rid6mzGj;SdvTcci zd)@ohLTyt}F4gbVO`aE5G-y4c;PmW1=k&!IYXAEEO~XR9Zu~WkjKAfQA9Nt>lXsHH z9wm)nWx8&o9XDV=X*01G=N{qbb<*t^3DIAjB5$2$=l9PDPhF5C&|PnIop>?4n(9%L z`Tgx-AE5toSkK`KjxfUwx~=%WiEWQO&&PR+n!UImQ^X`4nVsAO!md01}`?0R=m;cWLoKd|61xieG~CdU870)UTv88-H)eMn}h@U z18&++k$?qruXnDu3fOV-oJLp(XJm4_lR{4^#5)-xl!NfVHHPr$)48k-1tGt0m(Lc; zqkk+)+#m0@X{NILaY}F>=dXyL20EyHoDX_l%Q-uRv|}ZmgO5?zy%M0(nx&&&kch0Q z`AssGy`=NoOqc52y@PRuDln1t8TbHI-H;XAF`=HYVX-NXiyU*IpO+Jbw@S&08Y}W1 zk`O^;T!&9ilT1)pmF2U$nd%f_(zw~dz&Od=3L0+gn$#~J%^8QM+ylz+`lWc3HN{-N zSar?VdfTi4n$hvVGAg^Ieve(4V^oE_)~oG2_@f&|1<6ytL5~qaWX?rN-K?5IkIkK2 zmkI=m(2RIf=IW5Kt#6V!24)yZarMje5U%*cZ@nrV{OMU;3P1?DGoJT6-VvT30~O^j z63{bu@Thh%x4e zN8<5PnVlJ#bfX5y-!y`TYqr$Z8bL+1J43xP2qT0_S0K35lqbO+HZx{p4LtDDnZ1nZE`@u96p&FYBtVLxp59XOxv6; zQss_#ukFk#HhprGu$P=dUSjEIR4*f<1Vdx^pNsi}ny}e4bw~G^DFE{v57Nf7i3ib@Wg8Xd{;)ptN6)EGU);+OC4%7}Rx z9EcP2MdS^ipqjPBKfU)O#ASUAJ^Smu;NXZDAwZ~zRwJ2WA{8744t+4BQDHyAA#U=f z*7oe(S~DW~2yX_o3Av-z)XK0VdhoDwrGeJ?Xu>N4&qTx?n(&>gL*lL^*p)oq=^spt z-Z4=lL`5j=fnT=&iebo<@Ss3z_0(=@YKoy%$8+uvOAHf%&yQ2LGdCX^RdtnXsHmz) zyS0`RoZC9rkZjDnQK6PY@G?FNb~1h>z<%ui0Q300qGH{^x0z)& zw1Nl;}_a=7XxFVjEu>|rPC0J zu1`)MZ{}&Q(&>a4xI}qRY{&Jzuz%lQkpkrTq!TGb$f}(_hYB{;a*CGoLxhT_g9p7( z^b*y{B8L;-hn~JHGwkF2bs#|;3P~~=z%XE%U2gjILU@^v9bo!vl^IU9Wjnv^RZQ&c z5`pjd{u3~ypgctQH0yIY;#sYtS8v}rQuzk`(Pti#&%_8pE^%KQgx1r8+fyete@sLN ze`2VvC2Mu0e3P;EV@EC5-osnOek1qvdhLrnQe0rX?M!9zC)1$X_;SLLLFe&6Br0H; zp%qZ#R(D-hS8RnD#+t>ttG10)Yu zpPzrpwM?B}dC;k`epfO~aS>!Rw()*TjB^po8tCFE^fJkdoMI>gWe+15J%I4mt&pV^ zXbn(<^nR@O`R~S(YyCt!&CA<{8?x=f?`cb24HBg>#EK3nPet#So2@>;dG_QU+|N!m z0gu^?_iy%!gR;27wO00#ZST7Ydu}e~aioaZpa=%6i7$ z`JrT(J->PXS8pff$#;44Jost_^)J$qlcNN`MJWAcJFWcc|W`HmG(h8(d(a*VIWS>-Fvc80n*p~4AQ9Xrc+Y0^^EMrX6f}`jII{E zVWcfU4`hF72J!B~m+D_L@$+*@n*XK(prP@Q{gG0~%0y5#LGerj2*`U&(^Z&BdrN=E z`t9Opx&Rv7cMXkizYXtrM^U9cR=gO8Hz19+fdjP`>udd#;J;nTHPlJ$S>SD=Uk+`h!%@S)227E857l1hs1(ok;n zBS3$O)uSmQacW3^DMh3GWZb2td!%TZHbiT94XeRi^m;gbnbXlR!bi!!Va_*ReR*6PT-li9`AZ*FVBiRtb#;)R{!{&?t>eK&v3W2@K zaPxx$2UE$@4{}Phtm0t4hhGTB z%xrf>_z8`o=qdbbVAD?3cE$0v;O89J;jGX?DV}9@N%Jp;AHUlvS0Bj!3bAFFAa;$g zD9k~iNIdn1{@yx^?_sHf^&){yLgaCN{Aqp-S4$1i@I*ru9~w$qIm^BvFqcnZv&*bm z@b1y{2mUu@{=@E-%+Q~g56(!ZfY65ny{h>o#Gc!#uV;^U%|680j{ zA>8f80XWbnqn6VVbm~0Ze-kuHgG3Z0kV>1S5D>;8#$gcPf&rt{ zaZ0^5P<2W+fR6C2H0|juew2}|d56Z%^B8{}aZJardzsoSIoaLd*IdLI**hPI_&Bw` zaAE5*hiTgJL%*y|?IU`A=Cd!0HI)8VvxC9|*^ep%NY$U*<-CdeiXA4iKhs>Dxog_& z?BiJP7gI_uap*Ki`KsKY2>S>=`IHI*8gDOp2wE)nnhzo84VKk#p1P_njZr<%hZ z%ji^6u@Ou_YH!?RP{H7=lH z?slt{MYdHcslkNmlI)vNFjedu4B)mUU;3bXgV^I9%Z|DbDo3&F z>j0e=6wlGJ=}LRozEaW`)i|uASrLSz`v~4C^q=F zv^GRpxC@}I<*rn?w@zeyL@@52lV&RwB(YB-yxq)IqE(3f*RxGiXyLave>F5xF zECe%={QC$8MCOsMtvJ;@c8#_r_y=(Unae|Mp*Kv8Y&rZYrMby0D~Xhcd8z6jfs<(G ztC*5tom6stq`t_uP)nhsvbX)OYHMswvtJn{5l*i7)WqI@FgdOEo1>|`N%d5+0ub`Y zyupJA(hn?MgpR9k)3vi7l{Cn)EK_%#wY{B|J7)1+H*E&gZFDIB+^8eT7F0g>Hd$+R zdNk&9BIF}itYo-c(--z}F|Cww4`yn>QEuq4w#nb0HZ*yeJ*3AUA(N&w=zbZEun*8A z|H2?6wjXvFKr>CwqW}^yZey$ zZCItOqv7y8g0cK@p413!Lb4WLcQKh{y)%otD<#wZ=knwNO6|S>9M(O{y0N_Dg=!pcis7Wxv5Wy4jAz1Wor`ubK zZ5}m48_02~#aKlW1q=nW#iw1=6N6q;W3BWVgoiC|AHn;DPTI-C^7u@V_p*e|3`$q#k2wWyl?UeE=- zlGmu)w5m6<{arCRHU*F|vB`FbF|wwMd78Nv?$d<*@s^l!`;vkoiC|7FxB>?ei*?%P zz7qs8eSBkeBY+o`4;gHU4SpT+oX04LfOC|)Y|Kb%~rqX&2Hp#M?WM@S) zH+c-RecByQN%PJ3r{M}0$FSL8@JHI(uTMhuw9?gmf?Z#GMROqEZ2RMyw;1IdQdo|3 z7$AeNISe7@ba4r*)Wo9?Shs!CnD;UN_WKv&d*326XGOq=)1bp(N_W0tt*UF&WqwVF z217T6;DML}5a3o@EKf^ZJH71|GsZ!s(5leZIvIa_u296}GK366my92K-);*s2O#|D zg<~sT?&->(B@i=P zM(ckqvZRoEbC5#&06B=X%y2FoJRR8t4Y+j_rmn)$q?4pHR3dC6xSZT@9MXN z8596Aik3b`hI9(6|KgxHq8{!e?~(6Sh}!H=XXhM^$4u8DBJwr<G`&6fd_UNxN=m ze3N4i4+RNTcX1hZhKcde4McKfYzTN0(b_oL2boY-YjQ}f=5rKjJP`>m6mkWDW#MO8 z*9Zx;+A?EfO5sZ;+|L^^`P=cejh6bj;i)?=?bP++PKcE`aONhPHROJ+!Xs3AgfC7h zos|6{zz_Ei;!T`-P;o@x;H z;IoVNmP)@PpWoHNrAz{=I+_PtLzN@JlA> z?R#ZwiGS&%wiZ9RqR<1(Vu5cWJCs;f^;-2o_>YO+CdQmS=$H!J>OS=R{B6gF(ttp8 zd!(_!wCybWobAVCPFB%KH{$M}|8ToOMc6zLAV`1~j@Bd})dpPYwYRF$;NmjDtkfj= zJyk4`B6jUdKO=qr2xgi82O05wk6b4~6w zZZT3sWWL33;1rRo0rR>;m%Vfi{N}Up>$K#}#!dp{8%p!8% zVo#A~r#5l{o@-{MN$LG@D@1WyLXSqPeSv6$aC-bB$sN3v-n8h;wY4RK(L6d`SxY9q zW1zE}a6e}-5i-O*jP$2I;F-$~-4*;{vaqd6+9 zYe3l{&c@FL_~jo;M|`@3`ug%sYdr)Vq1XGr?>ZBguYDA5nSq+k^f{lI<(D%py-h5( zK>S&EOTP8V&1z9Z(s^O<_`C)0iidFFw^Bl^@ zXT46Yn{uL=IF%FMD;dKdun?Ccch~Yb47E?fZ)OvbD{|4@`IX{27r*^hU z>P6G;P2BBmO>FlG4pQjwech?6P~`jXhu2mp#4#n}gH|a5yh8wATg zw90>ZW8?VeH~k-nJ_5tCjA|W2Z0Nt0ZCYrfAZ$P@*b=QR z@oRVmJ!k}9Mkz;n$M7&Uoy1yO1SMuRLP{r7SPG{L)|kDQa9C@ zS{L;pi|Vu-J7Ep_)ykAbq7su4SP3H-IH%#GVcH94vM91o`8r|FcJ>wy3)c{1J!{*0 zYr23Ig?J3GVm)BDXunoP?T@gaz%>@;${*RHUjzQyH!OGvUQAY)JopTK^>inSVoZXp z<=~p=*4pYzBM{2%;N`X2)Y7tk52~B8O$T&$Q{R)94%l#UbJJkpEc3!QNGvWdN5!hQ zE-tFa{Yd`<4F_ekYjeW|R%g1Op1i~6x9SCY&*n?z($87&x>>dlS+c@hThMR2u8+O(-igqGujjcN66- zEG+if)QktMy{uPdaG;O?*yy#Xw$|3pDJdxyD4Mb(~p-OZEdS?cwS<| zpoPZmJNsIHted$e6Ga|_j3kc<6bV9yo3Rf9N6kR|3wHt~27ivCt=vZg5|1mdbydPyh7$82pat<0)-cnImNY1a22P6c7NbF+_riq+)#`4$e zn3$NK1@A6&a2+uW~QeJ3P+y4&^6W_A6qx%CEt;Wt?zm7yAuqWj`YF? z-+t*sgHfo@r^@GQ*dj>?+0DvY{pT`9Svy?rwI_GcgDl=xRps<0cartjas$NCno zAfceF@g_1%)5x2vo1xoUS*@h3EcJJXQP7Z5ERt|;mT8O@&8&hBo>67CYv z{&*9(vdn%>4dEIWTH~e+W2@D-6MLm)CmoMi#6SH?e_%m6gB2Qh)9HoPuW<=t8WN)2n&sVZ^CDhj>77nGMiSAMjr`_Ujq^C zQTq>`xmkR#_M@6;E^OqVF*cU&#wX}28Ixg0{0tA)-BP*gfzHU|y|=o$x>fqF#B0R& z%>T|;&E?Xf}6n7_#4Ou@qM{1ErF@q}e>-iM*#IHOv7Uuc z#g|1nMYPwV=u#htUa?Eln|BimYo$hP;woK*WZCCI{nU<W9#9Un?iGqD4#0y5o3nYdZKBagCwb?_5 zo!;RIjcqiN%tM14WnHG6vPL`czdzS+=vFmc14!hxt9ZnRKx@rYTeEkcDF@8Y-Srah zP6!6dpnrt{Na-*<KjBv>?)#s>yi+CJJQ z+G;Ip(EJ_~9HLZ>)cHp*29y~FsGJxVBvz56@Jb3aBt}d~MuX@l{w$hDf7Gb+{mb zK*C2ONeW`mB-Dpo>Yz}u&tI7{AGl=5$Z88Wl^yR~iFUG4?Wgxf{4Ptz_awUa13)D!l+cABOL*H2YsO$jH@+7a4ZE7i(Zko_>D06=GPnr1-MqD zk_$OTBEX_3#p5XaOP|UUuKL%igLbQeTD&P^P=0QEc#|UQo> zn%yqm^R{Hg%E|5(^T*8=;6UV#nXfdav31yO2Ub7=&%qA?iEdTW&)jNrfUJkF(3k2-Wx&b01dU zMn)3L-#s#%e49ixzdc=TDEpbm?=c+-K3+=$OIwUak*bEJ2UUul9Oy(Oi6^<5A5j!z z5-SLW*%X&;sKs2HfbT#E4i5pxL#|afi0K{9SvIjM08WP0opWWVegz8@=mLxSN zDfCZNI)NZTV?B}AQT(tHo+^qniYtJA1;(6!euPU;qVd!31w7%sr^_`e?o{^T*~76b z=7PH;d$a0%sTkA)H_0yUbaI8nldXLo-{lkTZ})|x|l3g<=-&o(5+nw0J>kl=TC^^tlHf%c8@)gemfDG-phNg zkUM_4xX-<~7Y^%-baTM~|@3qYEvWv(WjkW(h;QFcYCYd{c zLddZan6LWAooZh|ci-vtTXM-RqbDjEE;8`Xt9O+qQ6^HK5b<D4p=9s*u@S21ue6HZSKxl*e8@kOt1yTqL#qC1Xl?2rvoe3|n>lMbIzLTe-+>75qzu^ee| ziJ+l;{DwtZChl~vumzch^`HL+N56!^fUoOmTW)-R5*@g@)Uh#z+plrnZ6=CVBV!7< zwK3{>=bX%+BRf+HC>GvwPvmg#bMhc`B|vQ3b3#^F zn;2jo=~*`)L>jaFNu71c`=QrKMzi<1A*o_OyF52@lq?s)t_(}t&@K=tfz)pK*E)HW zp)inPk1mBT5-zx^2$v|{P&c!NUzWC~mE#@n9#t}2*=6x&pIfUJ~W9RVYyh%K3Dgs zo}p)-K6K7?O?yS!=WV$=*77$Rd@9`A7A>c7vAuT;sAO(5W(Ybo0J!srym4>5dvp#c z|GJYDfsz1SKUd%>F-)qu_C;LKLI_~|kA9XRI_8j0nt-b^ST_F7Ym(el;vj!m8-5{+ z=0<_4cf#KdjII`!!)D0SkpzQ4VEb{vXQ(FQZ}15frqJ4Kwu(B2uQ)`j7pK}mMY*bH za%}4ejKDUC{wztPW7jkxgEwVyqaotmfi~(trvvoc`w=j^G@I#|%AT43J8c4PJ=Tlf z)Xy!;W^G+_;qFLS)YGE1>jzm;caGSK1(L6pzX@3_dg`+YU$9clDZ)m59z#}_Kb@1g zR(g@$*9dz_g~qrdf$g_#F_JL70L`C=VNTVP*b+{iNXsSf4+B!V8eu6=`hMvDmCpYh zfnRnI_V9Ih*pM(HYJFmyU3jYj&%m#W7KXdr-uh_-M(GRrJ5CF3OxymC% zbj4zKzs94@=5LfwMxSD84gT_P1v6Hw5*Hz-6u`y`aaO8jtd_WYD^F_hoI6NF6h{^D zol4t%fvwF^cB^BUQ8k)-8UnI#IwQjzwZ2o<`)El=iN$KPVn?#U+!~o|HmCH6tYfQ- z{we(bIRbzF-YJsRp%wqsF170C%wA!e8h9Eq4NpVG|6&&*F*nF-j-~K2DQ?HAC%9k& z{R~|6%sW#GbKbkfzUB>x9g(A*@vqH&4K=cO26g;umi<4=0yzBuX+YNKd?tpHC)kTJ z2G|O<%4DvCWL$ZE_qQUVk1Q=4skMkQGRO)oZY z;LQec>$kSH-0#KLD)JO2CMKF|YXiSVkqcR8XL$v44$j~8r_o2|z^Sf#N9*H}|IVS` zTd)`Oe_1(*i;ovW63V?;|Jb>Z<+ZAP;-F~J^MsHo{ak-&yl z<;rd%nw~RHT>7BXXT`V|0b*+R?WUiH;W8Vz;na*~0CL@Qpe#D9yMgmj@ zQ|X;sTe7opSswFA{Xy}+-!!NMYRy$%qT(Bic}NJP0tYWR%x?)qd`VnJol=#tUnVm5 z;yQ!0-)U>>SCJS@>eTJLy~hzAgY`Q;PiZK1YcsUhhlzpPBLUbEW3tK;U-kHb6qn_5_r~5ZrJMFfuaQ z6M)vVEy6P`3aiRg)UNA*m3HQ)`=t}&(;Dpaas|vdt84%Mk45-P73Te6nvkK6wP>?n z6A}oRV*M6I3b(J#G1c}tgS&5o25sTZ^ui`~uoJG@(bZD?zTz|1 zQ$3EASHM5lKaAXavj%Aagmf_t;U(vHt@iGJy{^bJR3{ic&ec-m?AzIM(v^?-ish6d|g)Q=(&It+~XxJbp)vW2@K#T>V5cKKimf9 zEfNW`o4({f`Yk>PEDTRI5Y!+{%Bac0DB#M+q9&I=e41#FiuX_-GCJVa5JL9_i#;&D zj=8-NRr-MlgVb-eQTX*b+}*f(Mf+nZXMdshrEy~?Cl0W$)oz_U9uyOmp ztWJ&y#52BJUhlefMcs8OEf^Ttw~rE{YI3>07xqDX`~6Yk>Pj!3=);#6FG`u%u0iYN zwAYVSeurM|;7>1cuebd|T2er-$?^?0uX%k`!GloQX2{o)|6fYeMF25cz)fWyeSAbC zc{YA-G~HBL+aN!;ieo)ox}w%Yydlgc4V?5w2H$rL4by|gZR`~t#`?Oq*0{wF)N<)2 z3r?j3hGV*0ADl?dszgJlk8UmGl8`;J<&h%pQkcI;J4D$Y6!`s1D*n`~EA&Clp_vU* zqLK1Q;Y<1~G@U{(oz@qfcsk!OR&F57Ft2iN5ycpwX?_54#X2}J)+|x8(<5j*Sg_Uv zpcV(wsJ|ATR(-tZWJJRH`~X*l0ue4*Ozdi$eSY3?aHxNugDJd8X@#feG*mfwo- zCd?p!j7B?VkzY@TW~0%Rgj?row@i#C%xO7a_=WIRz$(oy0@iA|nahnHqjMzVH0nHR zBBUzts+@3@9Ka#YW2BS1xPqepe;X(ZFS6U1K^vrU$-jTM5xBbTL(@pV2M(ozx$|?% zo2+w#h`E*av+>)Cm7x*xS2mql+G!?~fubdk`eKHo!~?R^6$2FD7rOsXi&#_5rc1`!qW(a)zZ`xoL(F6iZPSHrC43ti z2p81(DZsoo@zW*pQ4dQ~?qJkmBL*~kgB8~f-gRWUuipI~9twBKu|P7;A(~tdkM2b1 zMWEkHiUa9qGhW+hxMI5JR9QpwT=Ej^@4Lr=71_@^g%#WX$I#@;7ci1bIYWn}fp9;> zccIJ>n_&4Eh`2sp0yOKYDDquLp;$%(hLC`>KBQo~S{8zW(Or`JiZQYs1OO{uqq?``e^+wNIK5~rF?7FVpOlK(DAK}D*FTT(~L)hg_ zrtO;+B!;dLycStNEhb1A$6_A?c!ER-kwgt^s&ty#qj`<#CXNbcAwD6jGfKKwn7U;7 zph1R~88UXXw~67}P{rV4Z~`?3I8~34gxWT%(`joEFBS|<5pJ8`KX&uEm)SI&y))?rHAw?TAY`Sr7Cn4i#7^wAXf?xr>5QA z-JN$QZOlw>OV(>BJtMse-@ug2$JOwf)*rD;jVR!-s?RK5l(Y$H$0vY z_f(}&6)9Ns5%yL_qgZstcs?T=v#wt}+;Xt0f>2+wEW@8DXG(Tn^h@L8(U@8w_X55Q zH1nHgsWZP4R}o9IlAD7A4yZX)R#wK4tI$!8We+OORB)RcABIiejag!*M(6L zc@U@Pn;i;HtodG8x>)v~7+P9ee{I3dB1qj!rW*?hZwhW!JJe~pokxOD4bX6uqv^RT zDIkI5s8dabE@f2%->RjN!G!BY*?%V~LA zgF8ez376D<(EM_@MS~uy6UmlzP#=^%jM=Bstmd#rHK_4Wy!~m_%JAoKJC@0GMJ(d^ zhbS<(MykPw*)Zd^sf8;@F9fzPmf234pDgRSczEE36qkoMny1`V?OItcEeY!$WOo=v z5m4ESaK*}%VwH`Q14}RO$g%~G*3N*y2(&%US_S-2eT1TO4tQ4PqSjl8NWB-qhwIJ& z!*s0H(NfZu2T>|kRCARuGvzla3O(R zo2{N2z8rn%UwKyVjX%jdj+mI~qv}q4*i|;`c^e-#TL?24Pm0~y_O5A>O z-Lh%w8aunyz1Cencsv`aGH+u3$FItjGQu`Ks=(9nzeAH_1C8um-l!8{1JW6dl@*TG zD=EJX2@Xw^!No*{@%*-;t8KDDj{%V%b{a`1IOnubK1bn7?Q*GD#QL`F6cB#w6zrxok#Qd4lb=h`R&S8AB^kC2l(N~Bvoc%of#CSXN zJiHSpl)^gJ<$NpFNWeh~{PfDns^?j~bzp~1FMuPyz&64s3Na+8>oYngE$U-aAgcVy!uIl8y*ZRHR>X~l(Jz#42*4($lAGm(=dU?=@{r5=^QlPAb_^sY|)Iqvb1=~** zGvs1>NmscD&<&jRIvVPId&sV}yLDQNOp#(Ovo{?kFj;*6Ww9XC9U}HM!P5;*tL{>V zLHcgKt@mxNWdUvtU9;qH_lEF$35FI9bYQTf>0txf%LNq#&@w5p3 zd4}_hqapGcY5*cSam%J0jSwu&P9De=2jW6>_>(Kor8p_ld`GPk`>M|2oG;YBg}rd( z)wd7)ZJ{&scqO37D#fjQ*phCk`@)hR^(?fVe=RnRJ)7}Q4B!SAL?rQvnt0W*$K-Qc zKEe1*sUrEQuhkI44@I?zUU*ha2B&m-?sOe)>DlJc!nCy7vh#@}mCx0rx4leZPPcmH zTwz=q%DAG~`3GUZ!N{24{bLyQ^KFB$>=YiA{zn& zT)uGtreKdT#1xl{g8y0}8t`PZI(ibF}Bwnd_O(b?c0x ztqrzu)nj+g_7mUw#C7L|qHP{?P1Qe(&vA!vo7vl)aO^@`t%+SGw=yvva@LMi>=I2; z8`rMU6XAoSj;~We@+R^w3ju+D=AaC<4K@Ja0^U|4sNvQb3loFJ;^}DOJ-VPZ{?XJdR zu_UkBaXh@03-WuGXKM9m^=Q;5vln*gdQ$NEIDMvesC_0>V`nRP7V~$s)2j^-VH_$T zP)-bs?7NZ$hUi)m(&YTY$rQUp?nbq&MTy767p)Ujq}2JUlwnGj{ytVUdnn6mY3^dL zVzuL2wH2s@c&b_B!dRC{!l*S(0bdlAH1O()b1kNZB@sD&~q;r&z$ph-Nv_)>3;_X zIh+sBY09!V=7>yfghg1~+^hSCw9$R0H&#{;0^;^W8zm@6h|ukS8Xfa7I0qo-Y+{|^scLioMD1;aVrb~9PhPvra^o{pF&nX<3bb< z*;(+$e;=){iq7kaV9 z=_srx$+A%{dZp6zYx%p&?$r~{-wG3S6V!$FlP8_|jhAHecqoA3D+KU-d{s_z@;X?x zkTjKV58{MfNQ&5G@*@MsuV#KXYM~416@IMQZy2Z2(Lg~P93h}`#?G6y zzNNbCwCBAiT)ALzr~K)F<5MNtCx!t3Sv$NP;htVzJ}!d}qLma$@n4>u>LOX|KyyQ0 zR8j7Tk8z&q($Bm*EYo|%o0_3*xQ@ipwd4B4h-hEfYu|5#04PGhZ$b;gSq}drIF+?7JfBHp4h`fVP5Z)@bfvW`*SY03K!Vi?ZSG} zZGk)!UrRVA>IctESm(F5lu-wH&NE=mFpzVw3aI<9!z}`Z5-x&PX5T8DFuIoc8FP4N zcdwV2MZ#$D5bKB96yqq-DxxXT(^3waY3LA9F^fvtj$w4O4l*KR!Y&#xS=o243SB#W z>Yz5PHso0g#?-oWnsT-qEd*v@&DFQa4uUDT=bfqFWf#2t@fvgtV5jrbnIh{X8EU~> z=|QFh`rJzNxvNEGI#^O$};a44JfdMOp!0vR+>PUiu&R69aD^Wvij@i1G2(jSO& z-#_v>Ye(!caab5;+1bc!fIm}uxXlddRCyA*ITYx`wr_y>4wm{q!evAQK@@$+j%PRD zzxYK5Hik=@4_IMdgQ8 zSJt)vGx@*&FmGzihNh&Dw8$ZHDv{=xbCS)FHA3&9CFIPILqftR%CVBem^qi6az2F| zm*Zl}sVqaIKI{F%7k%$P;C{UB*WtRZ=i$Cykq!DiT0zO@-83{%T8FoqxSr}lt!COH zyRO5_bPh<*^`Tn4H$JSY1;t%~a+wxR+ul;XK8*Kb&>1011C4*7*XaE?aJA9u3^wKX z5$iiYnq#$>JjL*itnhrFP<;CxXcF-;GvYg5?b-J=-)tvpR_wj48*f3{@UKMvIvj~+ zw{p(J0{m-`X5#EL@mgvkam8jKEtaNK;JOQn{ukp6bXYQU4stqw>i5*`7lm9|pY1~7 z(~aJqlAi=m%TzzFlN&}3Sy!W*$H3+|UB_I$AxgCzR>7@;Hio?Yboi6&LuR``T4wM{ z&L*m}ymF{F_Z;NxegBvK zf_Io{JS}Cxf1QB#v5}D~uX_@?H)32K-0lV{l2(7OSY5nNSKys+3|Rlq%(_sT0Q z&3k9Tr^*DTxDzi;spvm_wH^0({URTgdx^)e)r_^c+V-#YSzWJ@C@GuUlOaV#MPoBF zkC`&^@)TCb#ana{veWGgZ}3%$(|R>6+{!{tyAkRjQNmH#lF%31+u$3Ak1Vu+$vo+El--jX281 zd&}vpen9HtYwfU%KV4F4H)di?efrq-NVClc7wlR8!Y@zMV%L|)!<5~48l@e8K%f;$ zZghSm4;$mw)6-MqN2OAUMSnDq2_V_TNZ3XrU1wU#VPv}GTEPy_h7$o)snLgtdfrrV zg5lG}3u(y2f%{-W2Ak+>qvW{wKa2|JWdZsd4`lfAAj1B>u4}h7$))-$i}(r9TldL+ECux>4dWIs4FE{z8=OB(@R`L%dQRI!RsqEPps9epde!s!V$rXU*;v51u zTWyBdy{B`4Sb!*2L+BCBdl;+LZwvIXLgSEI+u7x@&{CEiva;9v$qI8=PHuwlsu={E zd9#E=cTV?YFF9V_ZTxcJ?kXsXC2^SFZUGu)tWwn0#wWoTK`}wqGOa%T+4~@`c8WZJ zG3HzN>!?+Nkec`${T3%Znx{d?LAZh}D&q+S~K`p%E zbL!gP8_cjB68XjmHoS@$a)YdUXUhli9w=k~d^wVN`JINlROl(5dxjKd)zv5CJ!?W# z+jo!=&8wtjHd!BL?U4Jn`STrM5mi6U|II-?_6tk1##QxFOp8|D2gHEy@=0B5S4l9I z{lTMAHsA$XOc%PzHH0?OUVFCo>~zDzDiz;%lzxWU-!Hqg!yvE}r88_7iF6VdsA}`- zV7lm}N2eN}p}uxVp@x63u6_m|?d#Y4@v1vb-J@g0*E&^$P?g8{D|<`?fa=br>E?UF zDsGG$SGdreDoT4+;jfnWtDpK%;m{%EO6~f@UAA>lzFRq6iB0pKCiiqA%&fwPc}~1# z)12jObsv}4le8Q&+}HE;48QwX0e9K#-C>P2o5}z+hBNv?P*Pk(JJ)`Q>l)e<_k=E?ez;ATE#mRv4 z?x$6z;mb#RSEA<^!12YiDH1??#1&I${Y(VQ-9g)l?dibJzPv{aZ=CER@oJ20=!A^J(% z=rI_J&)Utq{;A_rwe}rO41E;kS|odpL;C|E{DrS7KOGgXs`#ATmKMACqX`2pE@gTc{E5o03_J;Ao^WYcq%Mw7n=U#JgWx*PMK7+ zJwDp;{*|qi;Hwh+F%QZ~hOhB@qvxR&)e^HSi46*!l9!1~08WQ1$t73HTD=1)UO#}R z{i`)^x`f;GqOs!VQLXzQ44i1MtRDS2gyP58_|6;bDHe6m@qkmIL2k!6`}l1L9_F(4 zT`Gm_@r`}NdSZOIw=?P1i)$T8^)C@6@o#f6uJPl3PUXTi-x)qG0)gT>gi#CS(tebq zQ52mNzGMzjWq(rfP?I6Ze!5tBns|8Bl@nSM9=d2PlmhmL9{$?`VPG6$MMUUXF#2&W zz=Ktg>^YofN%CO5X4toy4?-ie`1+r#Qq-ig51#+N3kxmh;bICZN2^yCiG^Jny0dw= z7B4%DCWW0B#-r0^^;PT|6$&v@c^$=ZIjle!Pv{QvIg$*@?ERec|8zwEllx>Hq@oDL zw%l%