From 6f27ff3bbc9f5df3afa2b537dd69bbe43691124e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A7=E9=A9=8A?= Date: Mon, 17 Mar 2025 10:08:22 +0800 Subject: [PATCH] feat: product repo --- .golangci.yaml | 1 - client/product/product.go | 5 - gen_result/pb/product/product.pb.go | 3 +- go.mod | 49 ++ go.sum | 116 +++++ pkg/domain/const.go | 8 + pkg/domain/entity/product.go | 44 ++ pkg/domain/entity/product_item.go | 30 ++ pkg/domain/entity/product_statistics.go | 19 + pkg/domain/entity/product_tag.go | 16 + pkg/domain/entity/tags_binding.go | 15 + pkg/domain/error.go | 1 + pkg/domain/product/item_types.go | 10 + pkg/domain/product/show_type.go | 7 + pkg/domain/product/time_series.go | 10 + pkg/domain/redis.go | 23 + pkg/domain/repository/index.go | 3 + pkg/domain/repository/product.go | 51 ++ pkg/domain/repository/product_item.go | 3 + pkg/domain/repository/product_statistics.go | 3 + pkg/domain/repository/tags.go | 7 + pkg/repository/error.go | 12 + pkg/repository/mongo_init_test.go | 52 ++ pkg/repository/product.go | 258 ++++++++++ pkg/repository/product_test.go | 499 ++++++++++++++++++++ product.go | 5 +- 26 files changed, 1241 insertions(+), 9 deletions(-) create mode 100644 pkg/domain/const.go create mode 100755 pkg/domain/entity/product.go create mode 100644 pkg/domain/entity/product_item.go create mode 100644 pkg/domain/entity/product_statistics.go create mode 100644 pkg/domain/entity/product_tag.go create mode 100644 pkg/domain/entity/tags_binding.go create mode 100644 pkg/domain/error.go create mode 100644 pkg/domain/product/item_types.go create mode 100644 pkg/domain/product/show_type.go create mode 100644 pkg/domain/product/time_series.go create mode 100644 pkg/domain/redis.go create mode 100644 pkg/domain/repository/index.go create mode 100644 pkg/domain/repository/product.go create mode 100644 pkg/domain/repository/product_item.go create mode 100644 pkg/domain/repository/product_statistics.go create mode 100644 pkg/domain/repository/tags.go create mode 100755 pkg/repository/error.go create mode 100755 pkg/repository/mongo_init_test.go create mode 100644 pkg/repository/product.go create mode 100644 pkg/repository/product_test.go diff --git a/.golangci.yaml b/.golangci.yaml index a7d6a77..c623c07 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -74,7 +74,6 @@ linters: - errorlint # - execinquery - exhaustive - - exportloopref - forbidigo - forcetypeassert # - gochecknoglobals diff --git a/client/product/product.go b/client/product/product.go index ff92b57..49c06f1 100644 --- a/client/product/product.go +++ b/client/product/product.go @@ -5,12 +5,7 @@ package product import ( - "context" - - "app-cloudep-product-service/gen_result/pb/product" - "github.com/zeromicro/go-zero/zrpc" - "google.golang.org/grpc" ) type ( diff --git a/gen_result/pb/product/product.pb.go b/gen_result/pb/product/product.pb.go index 3cd2f9f..311cfa1 100644 --- a/gen_result/pb/product/product.pb.go +++ b/gen_result/pb/product/product.pb.go @@ -7,9 +7,10 @@ package product import ( + reflect "reflect" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" ) const ( diff --git a/go.mod b/go.mod index 801e60d..76f1669 100644 --- a/go.mod +++ b/go.mod @@ -3,29 +3,50 @@ module code.30cm.net/digimon/app-cloudep-product-service go 1.24.0 require ( + code.30cm.net/digimon/library-go/mongo v0.0.9 + github.com/alicebob/miniredis/v2 v2.34.0 + github.com/shopspring/decimal v1.4.0 + github.com/stretchr/testify v1.10.0 + github.com/testcontainers/testcontainers-go v0.34.0 github.com/zeromicro/go-zero v1.8.1 + go.mongodb.org/mongo-driver v1.17.3 google.golang.org/grpc v1.71.0 google.golang.org/protobuf v1.36.5 ) require ( + dario.cat/mergo v1.0.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/containerd/containerd v1.7.18 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v27.1.1+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/fatih/color v1.18.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -34,24 +55,50 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.11 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_golang v1.21.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/redis/go-redis/v9 v9.7.1 // indirect + github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + github.com/yuin/gopher-lua v1.1.1 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect go.etcd.io/etcd/api/v3 v3.5.15 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect go.etcd.io/etcd/client/v3 v3.5.15 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect go.opentelemetry.io/otel v1.34.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect @@ -67,8 +114,10 @@ require ( go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect + golang.org/x/crypto v0.33.0 // indirect golang.org/x/net v0.35.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect golang.org/x/term v0.29.0 // indirect golang.org/x/text v0.22.0 // indirect diff --git a/go.sum b/go.sum index cfd3e71..d8f6c5c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,13 @@ +code.30cm.net/digimon/library-go/mongo v0.0.9 h1:fPciIE5B85tXpLg8aeVQqKVbLnfpVAk9xbMu7pE2tVw= +code.30cm.net/digimon/library-go/mongo v0.0.9/go.mod h1:KBVKz/Ci5IheI77BgZxPUeKkaGvDy8fV8EDHSCOLIO4= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0= @@ -14,26 +24,48 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= +github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= +github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/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.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= @@ -50,8 +82,11 @@ 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.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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= @@ -83,6 +118,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -90,17 +129,35 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= @@ -109,6 +166,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= @@ -123,6 +182,16 @@ github.com/redis/go-redis/v9 v9.7.1 h1:4LhKRCIduqXqtvCUlaq9c8bdHOkICjDMrr1+Zb3os github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/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/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -133,6 +202,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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= @@ -140,11 +210,28 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= +github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zeromicro/go-zero v1.8.1 h1:iUYQEMQzS9Pb8ebzJtV3FGtv/YTjZxAh/NvLW/316wo= github.com/zeromicro/go-zero v1.8.1/go.mod h1:gc54Ad4qt7OJ0PbKajnYsSKsZBYN4JLRIXKlqDX2A2I= go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk= @@ -153,8 +240,12 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5 go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU= go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4= go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU= +go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ= +go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= @@ -192,14 +283,20 @@ go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= @@ -208,21 +305,37 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/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-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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= @@ -232,6 +345,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -259,6 +373,8 @@ 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= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= k8s.io/apimachinery v0.29.4 h1:RaFdJiDmuKs/8cm1M6Dh1Kvyh59YQFDcFuFTSmXes6Q= diff --git a/pkg/domain/const.go b/pkg/domain/const.go new file mode 100644 index 0000000..4e7624b --- /dev/null +++ b/pkg/domain/const.go @@ -0,0 +1,8 @@ +package domain + +import "time" + +const ( + DefaultSingleFlyCacheTimeout = 60 * time.Second + DefaultFindDataNotFoundTimeout = 5 * time.Second +) diff --git a/pkg/domain/entity/product.go b/pkg/domain/entity/product.go new file mode 100755 index 0000000..ba472da --- /dev/null +++ b/pkg/domain/entity/product.go @@ -0,0 +1,44 @@ +package entity + +import "go.mongodb.org/mongo-driver/bson/primitive" + +type Stage struct { + Stage uint64 `bson:"stage"` // 第幾階段 + Target uint64 `bson:"target"` // 階段目標金額 + Description string `bson:"description"` // 階段描述 +} + +// Product 專案,頻道,等共用的東西 +type Product struct { + ID primitive.ObjectID `bson:"_id,omitempty"` // 專案 ID + UID string `bson:"uid"` // 專案擁有者 UID + Title string `bson:"title"` // 專案名稱 + ShortTitle *string `bson:"short_title,omitempty"` // 計畫簡短標題 -> 不一定要有 + Details *string `bson:"details"` // 詳細內容 + ShortDescription string `bson:"short_description,omitempty"` // 簡短描述 + Media []Media `bson:"media,omitempty"` // 專案動態內容(圖片或者影片) + Slug *string `bson:"slug,omitempty"` // URL 後綴(查詢用) + IsPublished bool `bson:"is_published" ` // 是否已上架 + Amount uint64 `bson:"amount,omitempty"` // 目標金額 + StartTime *int64 `bson:"start_time,omitempty"` // 專案開始時間 + EndTime *int64 `bson:"end_time,omitempty"` // 專案結束時間 + Category string `bson:"category"` // 類別 + CustomFields []CustomFields `bson:"custom_fields,omitempty"` // 自定義屬性 + UpdatedAt int64 `bson:"updated_at,omitempty"` // 更新時間 + CreatedAt int64 `bson:"created_at,omitempty"` // 建立時間 +} + +type Media struct { + Sort uint64 `bson:"sort"` + Type string `bson:"type"` + URL string `bson:"url"` +} + +type CustomFields struct { + Key string `bson:"key"` + Value string `bson:"value"` +} + +func (p *Product) CollectionName() string { + return "product" +} diff --git a/pkg/domain/entity/product_item.go b/pkg/domain/entity/product_item.go new file mode 100644 index 0000000..b550334 --- /dev/null +++ b/pkg/domain/entity/product_item.go @@ -0,0 +1,30 @@ +package entity + +import ( + "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/product" + "github.com/shopspring/decimal" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type ProductItems struct { + ID primitive.ObjectID `bson:"_id,omitempty"` // 專案 ID + ProductID string `bson:"product_id"` // 對應的專案 ID + Name string `bson:"name" json:"name"` // 名稱 + Cover string `bson:"cover"` // 封面 + Description string `bson:"description"` // 描述 + ShortDescription string `bson:"short_description"` // 封面簡短描述 + Price decimal.Decimal `bson:"price"` // 價格 + SKU string `bson:"sku"` // 型號:對應顯示 Item 的 FK + TimeSeries product.TimeSeries `bson:"time_series"` // 時段種類 + Media []Media `bson:"medias,omitempty"` // 專案動態內容(圖片或者影片) + AverageRating float64 `bson:"average_rating"` // 綜合評價(如:4.5 顆星) + AverageRatingUpdateTime int64 `bson:"average_rating_time"` // 更新評價的時間 + Orders uint64 `bson:"total_orders"` // 總接單數 + OrdersUpdateTime int64 `bson:"total_orders_update_time"` // 更新總接單數的時間 + UpdatedAt int64 `bson:"updated_at" json:"updated_at"` // 更新時間 + CreatedAt int64 `bson:"created_at" json:"created_at"` // 創建時間 +} + +func (p *ProductItems) CollectionName() string { + return "product" +} diff --git a/pkg/domain/entity/product_statistics.go b/pkg/domain/entity/product_statistics.go new file mode 100644 index 0000000..3672d93 --- /dev/null +++ b/pkg/domain/entity/product_statistics.go @@ -0,0 +1,19 @@ +package entity + +import "go.mongodb.org/mongo-driver/bson/primitive" + +// ProductStatistics 統計的資訊表 +type ProductStatistics struct { + ID primitive.ObjectID `bson:"_id,omitempty"` // 專案 ID + ProductID string `bson:"product_id"` // 對應的專案 ID + Orders uint64 `bson:"total_orders"` // 總接單數 + OrdersUpdateTime int64 `bson:"total_orders_update_time"` // 更新總接單數的時間 + AverageRating float64 `bson:"average_rating"` // 綜合評價(如:4.5 顆星) + AverageRatingUpdateTime int64 `bson:"average_rating_time"` // 更新評價的時間 + FansCount uint64 `bson:"fans_count"` // 追蹤數量 + FansCountUpdateTime int64 `bson:"fans_count_update_time"` // 更新追蹤的時間 +} + +func (p *ProductStatistics) CollectionName() string { + return "product_statistics" +} diff --git a/pkg/domain/entity/product_tag.go b/pkg/domain/entity/product_tag.go new file mode 100644 index 0000000..93880cd --- /dev/null +++ b/pkg/domain/entity/product_tag.go @@ -0,0 +1,16 @@ +package entity + +import ( + "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/product" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type ProductTags struct { + ID primitive.ObjectID `bson:"_id,omitempty"` // 專案 ID + Types product.ItemType `bson:"types"` // Tag 類型 + Name string `bson:"name"` + ShowType product.ShowType `bson:"show_type"` // 顯示筐 + Cover *string `bson:"cover,omitempty"` // 封面圖片 + UpdatedAt int64 `bson:"updated_at" json:"updated_at"` // 更新時間 + CreatedAt int64 `bson:"created_at" json:"created_at"` // 創建時間 +} diff --git a/pkg/domain/entity/tags_binding.go b/pkg/domain/entity/tags_binding.go new file mode 100644 index 0000000..2d8c2a1 --- /dev/null +++ b/pkg/domain/entity/tags_binding.go @@ -0,0 +1,15 @@ +package entity + +import "go.mongodb.org/mongo-driver/bson/primitive" + +type TagsBindingTable struct { + ID primitive.ObjectID `bson:"_id,omitempty"` + ReferenceID string `bson:"reference_id"` // 參照 id (可能為專案或其他的東西) + TagID string `bson:"tag_id"` // tag id + UpdatedAt int64 `bson:"updated_at"` // 更新時間 + CreatedAt int64 `bson:"created_at"` // 創建時間 +} + +func (tbt *TagsBindingTable) CollectionName() string { + return "tag_binding_table" +} diff --git a/pkg/domain/error.go b/pkg/domain/error.go new file mode 100644 index 0000000..4188b5a --- /dev/null +++ b/pkg/domain/error.go @@ -0,0 +1 @@ +package domain diff --git a/pkg/domain/product/item_types.go b/pkg/domain/product/item_types.go new file mode 100644 index 0000000..e4fb565 --- /dev/null +++ b/pkg/domain/product/item_types.go @@ -0,0 +1,10 @@ +package product + +type ItemType int8 + +const ( + ItemTypeUnknown ItemType = iota // 未知 + ItemTypeNormal // 普通 tag + ItemTypeSkill // 技能專用 tag + ItemTypeProduct // 專案用 +) diff --git a/pkg/domain/product/show_type.go b/pkg/domain/product/show_type.go new file mode 100644 index 0000000..b88639a --- /dev/null +++ b/pkg/domain/product/show_type.go @@ -0,0 +1,7 @@ +package product + +type ShowType int64 + +const ( + ShowTypeNormal ShowType = iota // 沒有任何外匡 +) diff --git a/pkg/domain/product/time_series.go b/pkg/domain/product/time_series.go new file mode 100644 index 0000000..770dd9e --- /dev/null +++ b/pkg/domain/product/time_series.go @@ -0,0 +1,10 @@ +package product + +type TimeSeries int8 + +const ( + TimeSeriesUnknown TimeSeries = iota // 未知 + TimeSeriesTenMinutes // 每 10 分鐘 + TimeSeriesHalfHour // 每半小時 + TimeSeriesOneHour // 每小時 +) diff --git a/pkg/domain/redis.go b/pkg/domain/redis.go new file mode 100644 index 0000000..2eaf9d6 --- /dev/null +++ b/pkg/domain/redis.go @@ -0,0 +1,23 @@ +package domain + +import "strings" + +type RedisKey string + +func (key RedisKey) ToString() string { + return "product:" + string(key) +} + +func (key RedisKey) With(s ...string) RedisKey { + parts := append([]string{string(key)}, s...) + + return RedisKey(strings.Join(parts, ":")) +} + +const ( + GetProductRedisKey RedisKey = "get" +) + +func GetProductRK(id string) string { + return GetProductRedisKey.With(id).ToString() +} diff --git a/pkg/domain/repository/index.go b/pkg/domain/repository/index.go new file mode 100644 index 0000000..9769d87 --- /dev/null +++ b/pkg/domain/repository/index.go @@ -0,0 +1,3 @@ +package repository + +type Index interface{} diff --git a/pkg/domain/repository/product.go b/pkg/domain/repository/product.go new file mode 100644 index 0000000..ae94ce8 --- /dev/null +++ b/pkg/domain/repository/product.go @@ -0,0 +1,51 @@ +package repository + +import ( + "context" + + "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +// ProductRepository 存取 Product 的地方 +type ProductRepository interface { + Insert(ctx context.Context, data *entity.Product) error + FindOneByID(ctx context.Context, id string) (*entity.Product, error) + FindOneBySlug(ctx context.Context, slug string) (*entity.Product, error) + Update(ctx context.Context, productID string, data *ProductUpdateParams) (*mongo.UpdateResult, error) + Delete(ctx context.Context, id string) error + ListProduct(ctx context.Context, params *ProductQueryParams) ([]*entity.Product, int64, error) + Transaction(ctx context.Context, + fn func(sessCtx mongo.SessionContext) (any, error), + opts ...*options.TransactionOptions) error +} + +// ProductQueryParams 用於查詢專案的參數 +type ProductQueryParams struct { + UID *string // 專案擁有者 UID + IsPublished *bool // 是否已上架 + Category *string // 類別 + StartTime *int64 // 起始時間(Unix 時間戳) + EndTime *int64 // 結束時間(Unix 時間戳) + Slug *string // URL 後綴 + + PageIndex int64 // 頁碼 + PageSize int64 // 每頁數量 +} + +// ProductUpdateParams 用於更新專案的參數 +type ProductUpdateParams struct { + Title *string + ShortTitle *string + Details *string + ShortDescription string + Media []entity.Media + Category *string + Slug *string + IsPublished *bool + Amount *uint64 + StartTime *int64 + EndTime *int64 + CustomFields []entity.CustomFields +} diff --git a/pkg/domain/repository/product_item.go b/pkg/domain/repository/product_item.go new file mode 100644 index 0000000..fafb08b --- /dev/null +++ b/pkg/domain/repository/product_item.go @@ -0,0 +1,3 @@ +package repository + +type ProductItemRepo interface{} diff --git a/pkg/domain/repository/product_statistics.go b/pkg/domain/repository/product_statistics.go new file mode 100644 index 0000000..04d5457 --- /dev/null +++ b/pkg/domain/repository/product_statistics.go @@ -0,0 +1,3 @@ +package repository + +type ProductStatisticsRepo interface{} diff --git a/pkg/domain/repository/tags.go b/pkg/domain/repository/tags.go new file mode 100644 index 0000000..24c7a81 --- /dev/null +++ b/pkg/domain/repository/tags.go @@ -0,0 +1,7 @@ +package repository + +type TagRepo interface { +} + +type TagBindingRepo interface { +} diff --git a/pkg/repository/error.go b/pkg/repository/error.go new file mode 100755 index 0000000..c98cf79 --- /dev/null +++ b/pkg/repository/error.go @@ -0,0 +1,12 @@ +package repository + +import ( + "errors" + + "github.com/zeromicro/go-zero/core/stores/mon" +) + +var ( + ErrNotFound = mon.ErrNotFound + ErrInvalidObjectID = errors.New("invalid objectId") +) diff --git a/pkg/repository/mongo_init_test.go b/pkg/repository/mongo_init_test.go new file mode 100755 index 0000000..1bcfe1a --- /dev/null +++ b/pkg/repository/mongo_init_test.go @@ -0,0 +1,52 @@ +package repository + +import ( + "context" + "fmt" + + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" +) + +const ( + Host = "127.0.0.1" + Port = "27017" + Schema = "mongodb" +) + +func startMongoContainer() (string, string, func(), error) { + ctx := context.Background() + + req := testcontainers.ContainerRequest{ + Image: "mongo:latest", + ExposedPorts: []string{"27017/tcp"}, + WaitingFor: wait.ForListeningPort("27017/tcp"), + } + + mongoC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + if err != nil { + return "", "", nil, err + } + + port, err := mongoC.MappedPort(ctx, Port) + if err != nil { + return "", "", nil, err + } + + host, err := mongoC.Host(ctx) + if err != nil { + return "", "", nil, err + } + + uri := fmt.Sprintf("mongodb://%s:%s", host, port.Port()) + tearDown := func() { + mongoC.Terminate(ctx) + } + + fmt.Printf("Connecting to %s\n", uri) + + return host, port.Port(), tearDown, nil +} diff --git a/pkg/repository/product.go b/pkg/repository/product.go new file mode 100644 index 0000000..d496c14 --- /dev/null +++ b/pkg/repository/product.go @@ -0,0 +1,258 @@ +package repository + +import ( + "context" + "errors" + "time" + + "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain" + "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity" + "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository" + mgo "code.30cm.net/digimon/library-go/mongo" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/mon" +) + +type ProductRepositoryParam struct { + Conf *mgo.Conf + CacheConf cache.CacheConf + DBOpts []mon.Option + CacheOpts []cache.Option +} + +type ProductRepository struct { + DB mgo.DocumentDBWithCacheUseCase +} + +func NewProductRepository(param ProductRepositoryParam) repository.ProductRepository { + e := entity.Product{} + documentDB, err := mgo.MustDocumentDBWithCache( + param.Conf, + e.CollectionName(), + param.CacheConf, + param.DBOpts, + param.CacheOpts, + ) + if err != nil { + panic(err) + } + + return &ProductRepository{ + DB: documentDB, + } +} + +func (repo *ProductRepository) Insert(ctx context.Context, data *entity.Product) error { + if data.ID.IsZero() { + now := time.Now().UTC().UnixNano() + data.ID = primitive.NewObjectID() + data.CreatedAt = now + data.UpdatedAt = now + } + rk := domain.GetProductRK(data.ID.Hex()) + _, err := repo.DB.InsertOne(ctx, rk, data) + + return err +} + +func (repo *ProductRepository) FindOneByID(ctx context.Context, id string) (*entity.Product, error) { + oid, err := primitive.ObjectIDFromHex(id) + if err != nil { + return nil, err + } + + var result *entity.Product + err = repo.DB.FindOne(ctx, domain.GetProductRK(id), &result, bson.M{"_id": oid}) + switch { + case err == nil: + return result, nil + case errors.Is(err, mon.ErrNotFound): + return nil, ErrNotFound + default: + return nil, err + } +} + +func (repo *ProductRepository) FindOneBySlug(ctx context.Context, slug string) (*entity.Product, error) { + var result *entity.Product + err := repo.DB.FindOne(ctx, domain.GetProductRK(slug), &result, bson.M{"slug": slug}) + switch { + case err == nil: + return result, nil + case errors.Is(err, mon.ErrNotFound): + return nil, ErrNotFound + default: + return nil, err + } +} + +func (repo *ProductRepository) Update(ctx context.Context, productID string, data *repository.ProductUpdateParams) (*mongo.UpdateResult, error) { + item, err := repo.FindOneByID(ctx, productID) + if err != nil { + return nil, err + } + now := time.Now().UTC().UnixNano() + // 動態構建更新內容 + updateFields := bson.M{ + "updated_at": now, // 確保 `updateAt` 總是更新 + } + if data.Title != nil { + updateFields["title"] = *data.Title + } + if data.IsPublished != nil { + updateFields["is_published"] = *data.IsPublished + } + if data.StartTime != nil { + updateFields["start_time"] = *data.StartTime + } + if data.EndTime != nil { + updateFields["end_time"] = *data.EndTime + } + if data.ShortDescription != "" { + updateFields["short_description"] = data.ShortDescription + } + if data.Category != nil { + updateFields["category"] = *data.Category + } + if data.Details != nil { + updateFields["details"] = *data.Details + } + if data.ShortTitle != nil { + updateFields["short_title"] = *data.ShortTitle + } + if data.Slug != nil { + updateFields["slug"] = *data.Slug + } + if data.Amount != nil { + updateFields["amount"] = *data.Amount + } + if len(data.Media) > 0 { + updateFields["media"] = data.Media + } + if len(data.Media) > 0 { + updateFields["media"] = data.Media + } + if len(data.CustomFields) > 0 { + updateFields["custom_fields"] = data.CustomFields + } + + oid, err := primitive.ObjectIDFromHex(productID) + if err != nil { + return nil, ErrInvalidObjectID + } + + // 執行更新 + rk := domain.GetProductRK(productID) + res, err := repo.DB.UpdateOne(ctx, rk, bson.M{"_id": oid}, bson.M{"$set": updateFields}) + if err != nil { + return nil, err + } + _ = repo.DB.DelCache(ctx, domain.GetProductRK(*item.Slug)) + + return res, err +} + +func (repo *ProductRepository) Delete(ctx context.Context, id string) error { + item, err := repo.FindOneByID(ctx, id) + if err != nil { + return err + } + + _, err = repo.DB.DeleteOne(ctx, domain.GetProductRK(id), item) + if err != nil { + return err + } + + _ = repo.DB.DelCache(ctx, domain.GetProductRK(*item.Slug)) + + return nil +} + +func (repo *ProductRepository) ListProduct(ctx context.Context, params *repository.ProductQueryParams) ([]*entity.Product, int64, error) { + // 構建查詢過濾器 + filter := bson.M{} + if params.UID != nil && *params.UID != "" { + filter["uid"] = *params.UID + } + if params.IsPublished != nil { + filter["is_published"] = *params.IsPublished + } + if params.Category != nil { + filter["category"] = *params.Category + } + if params.StartTime != nil { + filter["start_time"] = bson.M{"$gte": *params.StartTime} // 篩選開始時間在指定時間之後的專案 + } + if params.EndTime != nil { + filter["end_time"] = bson.M{"$lte": *params.EndTime} // 篩選結束時間在指定時間之前的專案 + } + if params.Slug != nil && *params.Slug != "" { + filter["slug"] = *params.Slug + } + + // 設置排序選項 + opts := options.Find().SetSkip((params.PageIndex - 1) * params.PageSize).SetLimit(params.PageSize) + opts.SetSort(bson.D{{Key: "updated_at", Value: -1}}) + + // 查詢符合條件的總數 + count, err := repo.DB.GetClient().CountDocuments(ctx, filter) + if err != nil { + return nil, 0, err + } + + // 執行查詢並獲取結果 + var products []*entity.Product + err = repo.DB.GetClient().Find(ctx, &products, filter, opts) + if err != nil { + return nil, 0, err + } + + return products, count, nil +} + +func (repo *ProductRepository) Transaction( + ctx context.Context, + fn func(sessCtx mongo.SessionContext) (any, error), + opts ...*options.TransactionOptions) error { + // 獲取資料庫連接 + db := repo.DB.GetClient().Database() + + // 使用 MongoDB 的會話進行操作 + return db.Client().UseSession(ctx, func(sessionCtx mongo.SessionContext) error { + // 開始交易,支持可選的 TransactionOptions + if err := sessionCtx.StartTransaction(opts...); err != nil { + return err + } + + // 執行用戶提供的交易函數 + result, err := fn(sessionCtx) + if err != nil { + // 若發生錯誤則中止交易 + if abortErr := sessionCtx.AbortTransaction(ctx); abortErr != nil { + return abortErr + } + + return err + } + + // 提交交易 + if err := sessionCtx.CommitTransaction(ctx); err != nil { + // 若提交失敗則中止交易 + if abortErr := sessionCtx.AbortTransaction(ctx); abortErr != nil { + return abortErr + } + + return err + } + + // 返回交易執行結果 + _ = result // 結果未使用,保留擴展可能 + + return nil + }) +} diff --git a/pkg/repository/product_test.go b/pkg/repository/product_test.go new file mode 100644 index 0000000..479366c --- /dev/null +++ b/pkg/repository/product_test.go @@ -0,0 +1,499 @@ +package repository + +import ( + "context" + "errors" + "fmt" + "testing" + "time" + + "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity" + "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository" + mgo "code.30cm.net/digimon/library-go/mongo" + "github.com/alicebob/miniredis/v2" + "github.com/stretchr/testify/assert" + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/redis" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" +) + +func SetupTestProductRepository(db string) (repository.ProductRepository, func(), error) { + h, p, tearDown, err := startMongoContainer() + if err != nil { + return nil, nil, err + } + s, _ := miniredis.Run() + + conf := &mgo.Conf{ + Schema: Schema, + Host: fmt.Sprintf("%s:%s", h, p), + Database: db, + MaxStaleness: 300, + MaxPoolSize: 100, + MinPoolSize: 100, + MaxConnIdleTime: 300, + Compressors: []string{}, + EnableStandardReadWriteSplitMode: false, + ConnectTimeoutMs: 3000, + } + + cacheConf := cache.CacheConf{ + cache.NodeConf{ + RedisConf: redis.RedisConf{ + Host: s.Addr(), + Type: redis.NodeType, + }, + Weight: 100, + }, + } + + cacheOpts := []cache.Option{ + cache.WithExpiry(1000 * time.Microsecond), + cache.WithNotFoundExpiry(1000 * time.Microsecond), + } + + param := ProductRepositoryParam{ + Conf: conf, + CacheConf: cacheConf, + CacheOpts: cacheOpts, + } + + repo := NewProductRepository(param) + //_, _ = repo.Index20241226001UP(context.Background()) + + return repo, tearDown, nil +} + +func TestListProduct(t *testing.T) { + model, tearDown, err := SetupTestProductRepository("testDB") + defer tearDown() + assert.NoError(t, err) + + now := time.Now() + products := []*entity.Product{ + { + UID: "user1", + Title: "Product 1", + IsPublished: true, + Amount: 100, + StartTime: ptr(now.Add(-24 * time.Hour).Unix()), + EndTime: ptr(now.Add(24 * time.Hour).Unix()), + Category: "tech", + CreatedAt: now.Unix(), + UpdatedAt: now.Unix(), + Slug: ptr("product-1"), + Details: ptr("details..."), + }, + { + UID: "user2", + Title: "Product 2", + IsPublished: false, + Amount: 200, + StartTime: ptr(now.Add(-48 * time.Hour).Unix()), + EndTime: ptr(now.Add(48 * time.Hour).Unix()), + Category: "health", + CreatedAt: now.Unix(), + UpdatedAt: now.Unix(), + Slug: ptr("product-2"), + Details: ptr("details..."), + }, + { + UID: "user1", + Title: "Product 3", + IsPublished: true, + Amount: 300, + StartTime: ptr(now.Add(-72 * time.Hour).Unix()), + EndTime: ptr(now.Add(72 * time.Hour).Unix()), + Category: "tech", + CreatedAt: now.Unix(), + UpdatedAt: now.Unix(), + Slug: ptr("product-3"), + Details: ptr("details..."), + }, + } + + for _, p := range products { + err = model.Insert(context.Background(), p) + assert.NoError(t, err) + } + + tests := []struct { + name string + params *repository.ProductQueryParams + expectCount int64 + expectIDs []string + }{ + { + name: "Filter by UID", + params: &repository.ProductQueryParams{ + UID: ptr("user1"), + PageSize: 10, + PageIndex: 1, + }, + expectCount: 2, + expectIDs: []string{products[2].ID.Hex(), products[0].ID.Hex()}, + }, + { + name: "Filter by Published", + params: &repository.ProductQueryParams{ + IsPublished: ptr(true), + PageSize: 10, + PageIndex: 1, + }, + expectCount: 2, + expectIDs: []string{products[2].ID.Hex(), products[0].ID.Hex()}, + }, + { + name: "Filter by Category", + params: &repository.ProductQueryParams{ + Category: ptr("tech"), + PageSize: 10, + PageIndex: 1, + }, + expectCount: 2, + expectIDs: []string{products[2].ID.Hex(), products[0].ID.Hex()}, + }, + { + name: "Filter by StartTime >= now - 36h (should get P1 & P2)", + params: &repository.ProductQueryParams{ + StartTime: ptr(now.Add(-36 * time.Hour).Unix()), + PageSize: 10, + PageIndex: 1, + }, + expectCount: 1, + expectIDs: []string{products[0].ID.Hex()}, + }, + { + name: "Filter by EndTime <= now + 30h (should get P1)", + params: &repository.ProductQueryParams{ + EndTime: ptr(now.Add(30 * time.Hour).Unix()), + PageSize: 10, + PageIndex: 1, + }, + expectCount: 1, + expectIDs: []string{products[0].ID.Hex()}, + }, + { + name: "Filter by Slug = product-2", + params: &repository.ProductQueryParams{ + Slug: ptr("product-2"), + PageSize: 10, + PageIndex: 1, + }, + expectCount: 1, + expectIDs: []string{products[1].ID.Hex()}, + }, + { + name: "Filter by UID + Category + Published", + params: &repository.ProductQueryParams{ + UID: ptr("user1"), + Category: ptr("tech"), + IsPublished: ptr(true), + PageSize: 10, + PageIndex: 1, + }, + expectCount: 2, + expectIDs: []string{products[2].ID.Hex(), products[0].ID.Hex()}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, count, err := model.ListProduct(context.Background(), tt.params) + assert.NoError(t, err) + assert.Equal(t, tt.expectCount, count) + + var resultIDs []string + for _, r := range result { + resultIDs = append(resultIDs, r.ID.Hex()) + } + assert.Equal(t, tt.expectIDs, resultIDs) + }) + } +} + +func TestFindOneBySlug(t *testing.T) { + model, tearDown, err := SetupTestProductRepository("testDB") + defer tearDown() + assert.NoError(t, err) + + now := time.Now() + products := []*entity.Product{ + { + UID: "user1", + Title: "Product 1", + IsPublished: true, + Slug: ptr("product-1"), + CreatedAt: now.Unix(), + UpdatedAt: now.Unix(), + }, + { + UID: "user2", + Title: "Product 2", + IsPublished: true, + Slug: ptr("product-2"), + CreatedAt: now.Unix(), + UpdatedAt: now.Unix(), + }, + } + + // 插入測試資料 + for _, p := range products { + err = model.Insert(context.Background(), p) + assert.NoError(t, err) + } + + tests := []struct { + name string + slug string + expectFound bool + expectID string + }{ + { + name: "Found: product-1", + slug: "product-1", + expectFound: true, + expectID: products[0].ID.Hex(), + }, + { + name: "Not Found: unknown-slug", + slug: "unknown-slug", + expectFound: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res, err := model.FindOneBySlug(context.Background(), tt.slug) + + if tt.expectFound { + assert.NoError(t, err) + assert.NotNil(t, res) + assert.Equal(t, tt.expectID, res.ID.Hex()) + } else { + assert.Nil(t, res) + assert.ErrorIs(t, err, ErrNotFound) + } + }) + } +} + +func TestDeleteProduct(t *testing.T) { + model, tearDown, err := SetupTestProductRepository("testDB") + defer tearDown() + assert.NoError(t, err) + + // 建立測試資料 + now := time.Now() + product := &entity.Product{ + UID: "user1", + Title: "Deletable Product", + IsPublished: true, + Slug: ptr("delete-slug"), + CreatedAt: now.Unix(), + UpdatedAt: now.Unix(), + } + err = model.Insert(context.Background(), product) + assert.NoError(t, err) + + t.Run("Delete existing product", func(t *testing.T) { + err := model.Delete(context.Background(), product.ID.Hex()) + assert.NoError(t, err) + + // 再查詢應該會是找不到 + _, err = model.FindOneByID(context.Background(), product.ID.Hex()) + assert.ErrorIs(t, err, ErrNotFound) + }) + + t.Run("Delete non-existing product", func(t *testing.T) { + invalidID := primitive.NewObjectID().Hex() + err := model.Delete(context.Background(), invalidID) + assert.ErrorIs(t, err, ErrNotFound) + }) +} + +func TestUpdateProduct(t *testing.T) { + model, tearDown, err := SetupTestProductRepository("testDB") + defer tearDown() + assert.NoError(t, err) + + now := time.Now() + product := &entity.Product{ + UID: "user1", + Title: "Original Title", + IsPublished: false, + Slug: ptr("original-slug"), + CreatedAt: now.UnixNano(), + UpdatedAt: now.UnixNano(), + } + err = model.Insert(context.Background(), product) + assert.NoError(t, err) + + tests := []struct { + name string + update *repository.ProductUpdateParams + validate func(t *testing.T, updated *entity.Product) + }{ + { + name: "Update title", + update: &repository.ProductUpdateParams{ + Title: ptr("New Title"), + }, + validate: func(t *testing.T, updated *entity.Product) { + assert.Equal(t, "New Title", updated.Title) + }, + }, + { + name: "Update is_published", + update: &repository.ProductUpdateParams{ + IsPublished: ptr(true), + }, + validate: func(t *testing.T, updated *entity.Product) { + assert.True(t, updated.IsPublished) + }, + }, + { + name: "Update start and end time", + update: &repository.ProductUpdateParams{ + StartTime: ptr(now.Add(1 * time.Hour).UnixNano()), + EndTime: ptr(now.Add(2 * time.Hour).UnixNano()), + }, + validate: func(t *testing.T, updated *entity.Product) { + assert.Equal(t, now.Add(1*time.Hour).UnixNano(), *updated.StartTime) + assert.Equal(t, now.Add(2*time.Hour).UnixNano(), *updated.EndTime) + }, + }, + { + name: "Update short description", + update: &repository.ProductUpdateParams{ + ShortDescription: "Short desc here", + }, + validate: func(t *testing.T, updated *entity.Product) { + assert.Equal(t, "Short desc here", updated.ShortDescription) + }, + }, + { + name: "Update details, short_title, slug", + update: &repository.ProductUpdateParams{ + Details: ptr("Updated details"), + ShortTitle: ptr("ShortT"), + Slug: ptr("new-slug"), + }, + validate: func(t *testing.T, updated *entity.Product) { + assert.Equal(t, "Updated details", *updated.Details) + assert.Equal(t, "ShortT", *updated.ShortTitle) + assert.Equal(t, "new-slug", *updated.Slug) + }, + }, + { + name: "Update amount", + update: &repository.ProductUpdateParams{ + Amount: ptr(uint64(500)), + }, + validate: func(t *testing.T, updated *entity.Product) { + assert.Equal(t, uint64(500), updated.Amount) + }, + }, + { + name: "Update media", + update: &repository.ProductUpdateParams{ + Media: []entity.Media{ + {Sort: 1, Type: "image", URL: "https://example.com/1.jpg"}, + }, + }, + validate: func(t *testing.T, updated *entity.Product) { + assert.Len(t, updated.Media, 1) + assert.Equal(t, "image", updated.Media[0].Type) + }, + }, + { + name: "Update custom fields", + update: &repository.ProductUpdateParams{ + CustomFields: []entity.CustomFields{ + {Key: "color", Value: "red"}, + }, + }, + validate: func(t *testing.T, updated *entity.Product) { + assert.Len(t, updated.CustomFields, 1) + assert.Equal(t, "color", updated.CustomFields[0].Key) + assert.Equal(t, "red", updated.CustomFields[0].Value) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := model.Update(context.Background(), product.ID.Hex(), tt.update) + assert.NoError(t, err) + // 重新查詢確認資料已更新 + updated, err := model.FindOneByID(context.Background(), product.ID.Hex()) + assert.NoError(t, err) + tt.validate(t, updated) + }) + } +} + +func TestProductRepository_Transaction(t *testing.T) { + model, tearDown, err := SetupTestProductRepository("testDB") + defer tearDown() + assert.NoError(t, err) + ctx := context.Background() + + tests := []struct { + name string + transactionFunc func(sessCtx mongo.SessionContext) (any, error) + expectError bool + }{ + { + name: "Successful Transaction", + transactionFunc: func(sessCtx mongo.SessionContext) (any, error) { + product := &entity.Product{ + UID: "user123", + Title: "Test Transaction Product", + Amount: 1000, + IsPublished: true, + } + err := model.Insert(ctx, product) + return product, err + }, + expectError: false, + }, + { + name: "Transaction Rollback on Error", + transactionFunc: func(sessCtx mongo.SessionContext) (any, error) { + product := &entity.Product{ + UID: "user123", + Title: "Rollback Test Product", + Amount: 5000, + IsPublished: true, + } + _ = model.Insert(ctx, product) + // 模擬交易內錯誤 + return nil, errors.New("forced error") + }, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := model.Transaction(ctx, tt.transactionFunc) + if tt.expectError { + assert.Error(t, err, "交易應該返回錯誤") + } else { + assert.NoError(t, err, "交易應該成功完成") + product, _, err := model.ListProduct(ctx, + &repository.ProductQueryParams{ + PageSize: 20, PageIndex: 1, + UID: ptr("user123")}) + assert.NoError(t, err) + assert.Equal(t, product[0].UID, "user123") + } + }) + } +} + +func ptr[T any](v T) *T { + return &v +} diff --git a/product.go b/product.go index 902a085..b8679d6 100644 --- a/product.go +++ b/product.go @@ -2,7 +2,8 @@ package main import ( "flag" - "fmt" + + "github.com/zeromicro/go-zero/core/logx" "code.30cm.net/digimon/app-cloudep-product-service/gen_result/pb/product" "code.30cm.net/digimon/app-cloudep-product-service/internal/config" @@ -34,6 +35,6 @@ func main() { }) defer s.Stop() - fmt.Printf("Starting rpc server at %s...\n", c.ListenOn) + logx.Infof("Starting rpc server at %s...\n", c.ListenOn) s.Start() }