feat: add notification

This commit is contained in:
王性驊 2025-10-03 00:16:33 +08:00
parent 78505faf75
commit 4d770723b3
43 changed files with 1162 additions and 286 deletions

View File

@ -1,189 +0,0 @@
mode: set
backend/pkg/library/mongo/custom_mongo_decimal.go:18.104,21.9 2 1
backend/pkg/library/mongo/custom_mongo_decimal.go:21.9,23.3 1 1
backend/pkg/library/mongo/custom_mongo_decimal.go:26.2,27.16 2 0
backend/pkg/library/mongo/custom_mongo_decimal.go:27.16,29.3 1 0
backend/pkg/library/mongo/custom_mongo_decimal.go:31.2,31.35 1 0
backend/pkg/library/mongo/custom_mongo_decimal.go:34.104,36.16 2 0
backend/pkg/library/mongo/custom_mongo_decimal.go:36.16,38.3 1 0
backend/pkg/library/mongo/custom_mongo_decimal.go:41.2,42.16 2 0
backend/pkg/library/mongo/custom_mongo_decimal.go:42.16,44.3 1 0
backend/pkg/library/mongo/custom_mongo_decimal.go:47.2,49.12 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:18.171,20.16 2 1
backend/pkg/library/mongo/doc-db-with-cache.go:20.16,22.3 1 1
backend/pkg/library/mongo/doc-db-with-cache.go:24.2,29.8 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:32.84,34.2 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:36.66,38.2 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:40.66,42.2 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:45.137,48.27 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:48.27,49.17 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:49.17,51.28 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:51.28,53.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:54.4,54.26 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:54.26,56.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:57.4,57.23 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:57.23,59.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:60.4,60.44 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:64.2,65.16 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:65.16,67.3 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:69.2,69.46 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:69.46,71.3 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:73.2,73.17 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:77.127,78.57 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:78.57,81.28 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:81.28,82.18 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:82.18,84.29 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:84.29,86.6 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:87.5,87.27 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:87.27,89.6 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:90.5,90.24 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:90.24,92.6 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:93.5,93.30 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:93.30,95.6 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:96.5,96.24 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:96.24,98.6 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:99.5,99.24 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:99.24,101.6 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:102.5,102.45 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:106.3,106.63 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:111.145,114.27 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:114.27,115.17 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:115.17,117.28 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:117.28,119.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:120.4,120.26 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:120.26,122.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:123.4,123.23 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:123.23,125.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:126.4,126.29 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:126.29,128.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:129.4,129.23 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:129.23,131.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:132.4,132.44 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:136.2,136.87 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:136.87,138.3 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:140.2,140.30 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:144.160,147.27 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:147.27,148.17 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:148.17,150.28 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:150.28,152.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:153.4,153.26 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:153.26,155.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:156.4,156.23 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:156.23,158.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:159.4,159.29 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:159.29,161.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:162.4,162.33 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:162.33,164.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:165.4,165.23 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:165.23,167.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:168.4,168.25 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:168.25,170.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:171.4,171.44 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:175.2,175.101 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:175.101,177.3 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:179.2,179.30 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:183.156,186.27 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:186.27,187.17 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:187.17,189.43 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:189.43,191.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:192.4,192.26 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:192.26,194.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:195.4,195.44 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:199.2,200.16 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:200.16,202.3 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:204.2,204.45 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:204.45,206.3 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:208.2,208.17 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:212.156,215.27 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:215.27,216.17 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:216.17,218.31 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:218.31,220.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:221.4,221.43 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:221.43,223.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:224.4,224.28 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:224.28,226.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:227.4,227.26 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:227.26,229.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:230.4,230.23 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:230.23,232.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:233.4,233.25 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:233.25,235.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:236.4,236.44 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:240.2,241.16 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:241.16,243.3 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:245.2,245.45 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:245.45,247.3 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:249.2,249.17 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:253.164,256.27 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:256.27,257.17 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:257.17,259.31 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:259.31,261.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:262.4,262.43 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:262.43,264.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:265.4,265.28 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:265.28,267.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:268.4,268.26 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:268.26,270.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:271.4,271.23 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:271.23,273.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:274.4,274.25 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:274.25,276.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:277.4,277.44 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:281.2,282.16 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:282.16,284.3 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:286.2,286.49 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:286.49,288.3 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:290.2,290.17 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:294.159,297.27 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:297.27,298.17 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:298.17,300.31 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:300.31,302.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:303.4,303.43 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:303.43,305.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:306.4,306.28 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:306.28,308.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:309.4,309.26 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:309.26,311.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:312.4,312.23 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:312.23,314.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:315.4,315.25 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:315.25,317.5 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:318.4,318.44 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:322.2,323.16 2 0
backend/pkg/library/mongo/doc-db-with-cache.go:323.16,325.3 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:327.2,327.45 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:327.45,329.3 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:331.2,331.17 1 0
backend/pkg/library/mongo/doc-db-with-cache.go:337.77,339.2 1 0
backend/pkg/library/mongo/doc-db.go:23.100,25.23 2 1
backend/pkg/library/mongo/doc-db.go:25.23,31.3 1 1
backend/pkg/library/mongo/doc-db.go:33.2,41.16 3 1
backend/pkg/library/mongo/doc-db.go:41.16,43.3 1 1
backend/pkg/library/mongo/doc-db.go:44.2,46.43 3 0
backend/pkg/library/mongo/doc-db.go:46.43,50.3 3 0
backend/pkg/library/mongo/doc-db.go:52.2,57.16 5 0
backend/pkg/library/mongo/doc-db.go:57.16,59.3 1 0
backend/pkg/library/mongo/doc-db.go:61.2,68.16 4 0
backend/pkg/library/mongo/doc-db.go:68.16,70.3 1 0
backend/pkg/library/mongo/doc-db.go:71.2,75.8 2 0
backend/pkg/library/mongo/doc-db.go:78.101,85.16 5 0
backend/pkg/library/mongo/doc-db.go:85.16,87.3 1 0
backend/pkg/library/mongo/doc-db.go:90.115,98.16 5 0
backend/pkg/library/mongo/doc-db.go:98.16,100.3 1 0
backend/pkg/library/mongo/doc-db.go:103.112,104.29 1 0
backend/pkg/library/mongo/doc-db.go:104.29,108.3 2 0
backend/pkg/library/mongo/doc-db.go:110.2,115.16 5 0
backend/pkg/library/mongo/doc-db.go:115.16,117.3 1 0
backend/pkg/library/mongo/doc-db.go:120.52,122.2 1 0
backend/pkg/library/mongo/doc-db.go:124.144,126.29 2 0
backend/pkg/library/mongo/doc-db.go:126.29,130.3 3 0
backend/pkg/library/mongo/doc-db.go:131.2,131.21 1 0
backend/pkg/library/mongo/doc-db.go:131.21,133.3 1 0
backend/pkg/library/mongo/doc-db.go:134.2,139.14 3 0
backend/pkg/library/mongo/option.go:19.56,20.40 1 1
backend/pkg/library/mongo/option.go:20.40,22.32 2 0
backend/pkg/library/mongo/option.go:22.32,25.4 2 0
backend/pkg/library/mongo/option.go:26.3,26.26 1 0
backend/pkg/library/mongo/option.go:31.40,37.2 1 1
backend/pkg/library/mongo/option.go:39.44,40.43 1 1
backend/pkg/library/mongo/option.go:40.43,45.3 4 0

View File

@ -9,7 +9,8 @@
},
"CredentialsPayload": {
"properties": {
"accountType": {
"account_type": {
"description": "帳號型別 email phone any",
"type": "string"
},
"password": {
@ -24,7 +25,7 @@
"required": [
"password",
"password_confirm",
"accountType"
"account_type"
],
"type": "object"
},
@ -52,6 +53,7 @@
"LoginReq": {
"properties": {
"auth_method": {
"description": "驗證類型 credentials platform",
"type": "string"
},
"credentials": {
@ -117,7 +119,7 @@
"PlatformPayload": {
"properties": {
"provider": {
"description": "平台名稱",
"description": "平台名稱,目前支援 google line apple",
"type": "string"
},
"token": {
@ -1148,7 +1150,7 @@
"url": "https://localhost:8888"
}
],
"x-date": "2025-10-02 22:08:26",
"x-date": "2025-10-02 23:03:53",
"x-description": "This is a go-doc generated swagger file.",
"x-generator": "go-doc",
"x-github": "https://github.com/danielchan-25/go-doc",

View File

@ -10,18 +10,18 @@ type (
CredentialsPayload {
Password string `json:"password" validate:"required,min=8,max=128"` // 密碼 (後端應使用 bcrypt 進行雜湊)
PasswordConfirm string `json:"password_confirm" validate:"eqfield=Password"` // 確認密碼
AccountType string `json:"accountType" validate:"required,oneof=email phone any"`
AccountType string `json:"account_type" validate:"required,oneof=email phone any"` // 帳號型別 email phone any
}
// PlatformPayload 第三方平台註冊的資料
PlatformPayload {
Provider string `json:"provider" validate:"required,oneof=google line apple"` // 平台名稱
Provider string `json:"provider" validate:"required,oneof=google line apple"` // 平台名稱,目前支援 google line apple
Token string `json:"token" validate:"required"` // 平台提供的 Access Token 或 ID Token
}
// RegisterReq 註冊請求 (整合了兩種方式)
LoginReq {
AuthMethod string `json:"auth_method" validate:"required,oneof=credentials platform"`
AuthMethod string `json:"auth_method" validate:"required,oneof=credentials platform"`// 驗證類型 credentials platform
LoginID string `json:"login_id" validate:"required,min=3,max=50"` // 信箱或手機號碼
Credentials *CredentialsPayload `json:"credentials,optional"` // AuthMethod 為 'credentials' 時使用
Platform *PlatformPayload `json:"platform,optional"` // AuthMethod 為 'platform' 時使用

71
go.mod
View File

@ -7,22 +7,38 @@ replace backend/pkg/library/errs => ./pkg/library/errs
require (
code.30cm.net/digimon/library-go/utils/invited_code v1.2.5
github.com/alicebob/miniredis/v2 v2.35.0
github.com/aws/aws-sdk-go-v2 v1.39.2
github.com/aws/aws-sdk-go-v2/credentials v1.18.16
github.com/aws/aws-sdk-go-v2/service/ses v1.34.5
github.com/go-playground/validator/v10 v10.27.0
github.com/matcornic/hermes/v2 v2.1.0
github.com/minchao/go-mitake v1.0.0
github.com/panjf2000/ants/v2 v2.11.3
github.com/shopspring/decimal v1.4.0
github.com/stretchr/testify v1.11.1
github.com/testcontainers/testcontainers-go v0.39.0
github.com/zeromicro/go-zero v1.9.0
go.mongodb.org/mongo-driver v1.17.1
github.com/zeromicro/go-zero v1.9.1
go.mongodb.org/mongo-driver v1.17.4
go.mongodb.org/mongo-driver/v2 v2.3.0
go.uber.org/mock v0.4.0
golang.org/x/crypto v0.37.0
google.golang.org/grpc v1.67.0
google.golang.org/protobuf v1.36.5
go.uber.org/mock v0.6.0
golang.org/x/crypto v0.42.0
google.golang.org/grpc v1.75.1
google.golang.org/protobuf v1.36.10
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
)
require (
dario.cat/mergo v1.0.2 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Masterminds/semver v1.4.2 // indirect
github.com/Masterminds/sprig v2.16.0+incompatible // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/PuerkitoBio/goquery v1.5.0 // indirect
github.com/andybalholm/cascadia v1.0.0 // indirect
github.com/aokoli/goutils v1.0.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 // indirect
github.com/aws/smithy-go v1.23.0 // 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
@ -40,21 +56,30 @@ require (
github.com/ebitengine/purego v0.8.4 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grafana/pyroscope-go v1.2.4 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/grafana/pyroscope-go v1.2.7 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/huandu/xstrings v1.2.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.1.0 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
@ -64,6 +89,7 @@ require (
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/olekukonko/tablewriter v0.0.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/openzipkin/zipkin-go v0.4.3 // indirect
@ -75,38 +101,45 @@ require (
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.12.1 // indirect
github.com/redis/go-redis/v9 v9.15.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 // indirect
github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe // 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.4 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // 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.38.0 // indirect
go.opentelemetry.io/otel v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/zipkin v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.24.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
golang.org/x/text v0.29.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

174
go.sum
View File

@ -6,10 +6,32 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/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/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.16.0+incompatible h1:QZbMUPxRQ50EKAq3LFMnxddMu88/EUUG3qmxwtDmPsY=
github.com/Masterminds/sprig v2.16.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI=
github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg=
github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I=
github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/credentials v1.18.16 h1:4JHirI4zp958zC026Sm+V4pSDwW4pwLefKrc0bF2lwI=
github.com/aws/aws-sdk-go-v2/credentials v1.18.16/go.mod h1:qQMtGx9OSw7ty1yLclzLxXCRbrkjWAM7JnObZjmCB7I=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI=
github.com/aws/aws-sdk-go-v2/service/ses v1.34.5 h1:NwOeuOFrWoh4xWKINrmaAK4Vh75jmmY0RAuNjQ6W5Es=
github.com/aws/aws-sdk-go-v2/service/ses v1.34.5/go.mod h1:m3BsMJZD0eqjGIniBzwrNUqG9ZUPquC4hY9FyE2qNFo=
github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
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/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
@ -51,6 +73,9 @@ 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/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@ -58,44 +83,75 @@ 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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
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 v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grafana/pyroscope-go v1.2.4 h1:B22GMXz+O0nWLatxLuaP7o7L9dvP0clLvIpmeEQQM0Q=
github.com/grafana/pyroscope-go v1.2.4/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/grafana/pyroscope-go v1.2.7 h1:VWBBlqxjyR0Cwk2W6UrE8CdcdD80GOFNutj0Kb1T8ac=
github.com/grafana/pyroscope-go v1.2.7/go.mod h1:o/bpSLiJYYP6HQtvcoVKiE9s5RiNgjYTj1DhiddP2Pc=
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og=
github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 h1:xqgexXAGQgY3HAjNPSaCqn5Aahbo5TKsmhp8VRfr1iQ=
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
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/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
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/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
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.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/matcornic/hermes/v2 v2.1.0 h1:9TDYFBPFv6mcXanaDmRDEp/RTWj0dTTi+LpFnnnfNWc=
github.com/matcornic/hermes/v2 v2.1.0/go.mod h1:2+ziJeoyRfaLiATIL8VZ7f9hpzH4oDHqTmn0bhrsgVI=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
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/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/minchao/go-mitake v1.0.0 h1:OgfCUkSRftd6sWibpJyeKU3/gPQhq1t0ttHsnoaeVgQ=
github.com/minchao/go-mitake v1.0.0/go.mod h1:RAo0TijPUqhM2ZLMqP9x76wsomL11Ud4sDSwRYwbeGU=
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/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
@ -116,12 +172,16 @@ 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/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
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.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
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/panjf2000/ants/v2 v2.11.3 h1:AfI0ngBoXJmYOpDh9m516vjqoUu2sLrIVgppI9TZVpg=
github.com/panjf2000/ants/v2 v2.11.3/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@ -140,23 +200,32 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg=
github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/redis/go-redis/v9 v9.15.0 h1:2jdes0xJxer4h3NUZrZ4OGSntGlXp4WbXju2nOTRXto=
github.com/redis/go-redis/v9 v9.15.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
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/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
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/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/stretchr/objx v0.1.0/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/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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=
@ -170,6 +239,10 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
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/vanng822/css v0.0.0-20190504095207-a21e860bcd04 h1:L0rPdfzq43+NV8rfIx2kA4iSSLRj2jN5ijYHoeXRwvQ=
github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04/go.mod h1:tcnB1voG49QhCrwq1W0w5hhGasvOg+VQp9i9H1rCM1w=
github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe h1:9YnI5plmy+ad6BM+JCLJb2ZV7/TNiE5l7SNKfumYKgc=
github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe/go.mod h1:JTFJA/t820uFDoyPpErFQ3rb3amdZoPtxcKervG0OE4=
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=
@ -185,18 +258,18 @@ 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.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zeromicro/go-zero v1.9.0 h1:hlVtQCSHPszQdcwZTawzGwTej1G2mhHybYzMRLuwCt4=
github.com/zeromicro/go-zero v1.9.0/go.mod h1:TMyCxiaOjLQ3YxyYlJrejaQZF40RlzQ3FVvFu5EbcV4=
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
github.com/zeromicro/go-zero v1.9.1 h1:GZCl4jun/ZgZHnSvX3SSNDHf+tEGmEQ8x2Z23xjHa9g=
github.com/zeromicro/go-zero v1.9.1/go.mod h1:bHOl7Xr7EV/iHZWEqsUNJwFc/9WgAMrPpPagYvOaMtY=
go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.mongodb.org/mongo-driver/v2 v2.3.0 h1:sh55yOXA2vUjW1QYw/2tRlHSQViwDyPnW61AwpZ4rtU=
go.mongodb.org/mongo-driver/v2 v2.3.0/go.mod h1:jHeEDJHJq7tm6ZF45Issun9dbogjfnPySb1vXA7EeAI=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
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.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
@ -209,46 +282,50 @@ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDO
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA=
go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY=
go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/crypto v0.0.0-20181029175232-7e6ffbd03851/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-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.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/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-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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190225065934-cc5685c2db12/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=
@ -267,14 +344,15 @@ golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
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.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -286,19 +364,27 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8=
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
gopkg.in/yaml.v2 v2.2.1/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=

View File

@ -25,15 +25,17 @@ func RegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return
}
//if err := svcCtx.Validate.ValidateAll(req); err != nil {
// e := errs.InvalidFormat(err.Error())
// httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.RespOK{
// Code: int(e.FullCode()),
// Msg: err.Error(),
// })
//
// return
//}
if err := svcCtx.Validate.ValidateAll(req); err != nil {
e := errs.InvalidFormat(err.Error())
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.RespOK{
Code: int(e.FullCode()),
Msg: err.Error(),
})
return
}
l := auth.NewRegisterLogic(r.Context(), svcCtx)
resp, err := l.Register(&req)
if err != nil {

View File

@ -1,5 +1,5 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.8.5
// goctl 1.8.1
package handler

View File

@ -3,14 +3,15 @@ package auth
import (
"backend/internal/svc"
"backend/internal/types"
mb "backend/pkg/member/domain/member"
member "backend/pkg/member/domain/usecase"
"backend/pkg/library/errs"
"backend/pkg/library/errs/code"
mb "backend/pkg/member/domain/member"
member "backend/pkg/member/domain/usecase"
"context"
"google.golang.org/protobuf/proto"
"time"
"google.golang.org/protobuf/proto"
"github.com/zeromicro/go-zero/core/logx"
)
@ -36,7 +37,7 @@ func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Register
}
func (l *RegisterLogic) Register(req *types.LoginReq) (resp *types.LoginResp, err error) {
// Step 1: 根據平台構建相關數據
// Step 1: 根據平台構建相關資料
var bd buildData
switch req.AuthMethod {
case "credentials":

View File

@ -8,6 +8,7 @@ import (
"backend/pkg/member/repository"
uc "backend/pkg/member/usecase"
"context"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/mon"
"github.com/zeromicro/go-zero/core/stores/redis"

View File

@ -3,6 +3,9 @@ package svc
import (
"backend/internal/config"
"backend/internal/middleware"
"backend/pkg/library/errs"
"backend/pkg/library/errs/code"
vi "backend/pkg/library/validator"
"backend/pkg/member/domain/usecase"
"github.com/zeromicro/go-zero/core/stores/redis"
@ -13,6 +16,7 @@ type ServiceContext struct {
Config config.Config
AuthMiddleware rest.Middleware
AccountUC usecase.AccountUseCase
Validate vi.Validate
}
func NewServiceContext(c config.Config) *ServiceContext {
@ -20,10 +24,12 @@ func NewServiceContext(c config.Config) *ServiceContext {
if err != nil {
panic(err)
}
errs.Scope = code.CloudEPPortalGW
return &ServiceContext{
Config: c,
AuthMiddleware: middleware.NewAuthMiddleware().Handle,
AccountUC: NewAccountUC(&c, rds),
Validate: vi.MustValidator(),
}
}

View File

@ -1,5 +1,5 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.8.5
// goctl 1.8.1
package types
@ -11,9 +11,9 @@ type BaseReq struct {
}
type CredentialsPayload struct {
Password string `json:"password" validate:"required,min=8,max=128"` // 密碼 (後端應使用 bcrypt 進行雜湊)
PasswordConfirm string `json:"password_confirm" validate:"eqfield=Password"` // 確認密碼
AccountType string `json:"accountType" validate:"required,oneof=email phone any"`
Password string `json:"password" validate:"required,min=8,max=128"` // 密碼 (後端應使用 bcrypt 進行雜湊)
PasswordConfirm string `json:"password_confirm" validate:"eqfield=Password"` // 確認密碼
AccountType string `json:"account_type" validate:"required,oneof=email phone any"` // 帳號型別 email phone any
}
type ErrorResp struct {
@ -24,10 +24,10 @@ type ErrorResp struct {
}
type LoginReq struct {
AuthMethod string `json:"auth_method" validate:"required,oneof=credentials platform"`
LoginID string `json:"login_id" validate:"required,min=3,max=50"` // 信箱或手機號碼
Credentials *CredentialsPayload `json:"credentials,optional"` // AuthMethod 為 'credentials' 時使用
Platform *PlatformPayload `json:"platform,optional"` // AuthMethod 為 'platform' 時使用
AuthMethod string `json:"auth_method" validate:"required,oneof=credentials platform"` // 驗證類型 credentials platform
LoginID string `json:"login_id" validate:"required,min=3,max=50"` // 信箱或手機號碼
Credentials *CredentialsPayload `json:"credentials,optional"` // AuthMethod 為 'credentials' 時使用
Platform *PlatformPayload `json:"platform,optional"` // AuthMethod 為 'platform' 時使用
}
type LoginResp struct {
@ -44,7 +44,7 @@ type PagerResp struct {
}
type PlatformPayload struct {
Provider string `json:"provider" validate:"required,oneof=google line apple"` // 平台名稱
Provider string `json:"provider" validate:"required,oneof=google line apple"` // 平台名稱,目前支援 google line apple
Token string `json:"token" validate:"required"` // 平台提供的 Access Token 或 ID Token
}

View File

@ -6,6 +6,7 @@ import (
"strings"
"backend/pkg/library/errs/code"
"github.com/zeromicro/go-zero/core/logx"
"google.golang.org/grpc/status"
)
@ -305,6 +306,12 @@ func ResourceAlreadyExist(s ...string) *LibError {
fmt.Sprintf("%s", strings.Join(s, " ")))
}
// ResourceAlreadyExistWithScope returns Err
func ResourceAlreadyExistWithScope(scope uint32, s ...string) *LibError {
return NewError(scope, code.ResourceAlreadyExist, defaultDetailCode,
fmt.Sprintf("%s", strings.Join(s, " ")))
}
// ResourceAlreadyExistL logs error message and returns Err
func ResourceAlreadyExistL(l logx.Logger, filed []logx.LogField, s ...string) *LibError {
e := ResourceAlreadyExist(s...)

View File

@ -5,6 +5,7 @@ import (
"testing"
"backend/pkg/library/errs/code"
"github.com/zeromicro/go-zero/core/logx"
)

View File

@ -5,6 +5,7 @@ import (
"strings"
"backend/pkg/library/errs/code"
"github.com/zeromicro/go-zero/core/logx"
)

View File

@ -6,6 +6,7 @@ import (
"net/http"
"backend/pkg/library/errs/code"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
@ -154,8 +155,8 @@ func (e *LibError) HTTPStatus() int {
return http.StatusOK
}
// 根據錯誤碼判斷對應的 HTTP 狀態碼
switch e.Code() % 100 {
case code.ResourceInsufficient:
switch e.Code() / 100 {
case code.ResourceInsufficient, code.InvalidFormat:
// 如果資源不足,返回 400 狀態碼
return http.StatusBadRequest
case code.Unauthorized, code.InsufficientPermission:

View File

@ -6,6 +6,7 @@ import (
"testing"
"backend/pkg/library/errs/code"
"google.golang.org/grpc/codes"
)

View File

@ -0,0 +1,50 @@
package required
import (
"fmt"
"github.com/go-playground/validator/v10"
"github.com/zeromicro/go-zero/core/logx"
)
type Validate interface {
ValidateAll(obj any) error
BindToValidator(opts ...Option) error
}
type Validator struct {
V *validator.Validate
}
func (v *Validator) ValidateAll(obj any) error {
err := v.V.Struct(obj)
if err != nil {
return err
}
return nil
}
func (v *Validator) BindToValidator(opts ...Option) error {
for _, item := range opts {
err := v.V.RegisterValidation(item.ValidatorName, item.ValidatorFunc)
if err != nil {
return fmt.Errorf("failed to register validator : %w", err)
}
}
return nil
}
func MustValidator(option ...Option) Validate {
v := &Validator{
V: validator.New(),
}
if err := v.BindToValidator(option...); err != nil {
logx.Error("failed to bind validator")
}
return v
}

View File

@ -0,0 +1,29 @@
package required
import (
"regexp"
"github.com/go-playground/validator/v10"
)
type Option struct {
ValidatorName string
ValidatorFunc func(fl validator.FieldLevel) bool
}
// WithAccount 創建一個新的 Option 結構,包含自定義的驗證函數,用於驗證 email 和台灣的手機號碼格式
func WithAccount(tagName string) Option {
return Option{
ValidatorName: tagName,
ValidatorFunc: func(fl validator.FieldLevel) bool {
value := fl.Field().String()
emailRegex := `^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`
phoneRegex := `^(\+886|0)?9\d{8}$`
emailMatch, _ := regexp.MatchString(emailRegex, value)
phoneMatch, _ := regexp.MatchString(phoneRegex, value)
return emailMatch || phoneMatch
},
}
}

View File

@ -0,0 +1,71 @@
package worker_pool
import (
"sync"
"github.com/panjf2000/ants/v2"
)
const defaultWorkerPoolSize = 2000
type WorkerPool interface {
Submit(task func()) error
SubmitAndWaitAll(tasks ...func() error) (taskErr chan error, submitErr error)
}
type workerPool struct {
p *ants.Pool
}
func NewWorkerPool(size int) WorkerPool {
if size <= 0 {
size = defaultWorkerPoolSize
}
p, err := ants.NewPool(
size,
ants.WithDisablePurge(true),
)
if err != nil {
return &workerPool{p: nil}
}
return &workerPool{p: p}
}
func (p *workerPool) Submit(task func()) error {
if p.p == nil {
return ants.Submit(task)
}
return p.p.Submit(task)
}
func (p *workerPool) SubmitAndWaitAll(tasks ...func() error) (chan error, error) {
taskErrCh := make(chan error, len(tasks))
submitErrCh := make(chan error, len(tasks))
wg := sync.WaitGroup{}
wg.Add(len(tasks))
for i := range tasks {
task := tasks[i]
err := p.Submit(func() {
defer wg.Done()
if err := task(); err != nil {
taskErrCh <- err
}
})
if err != nil {
submitErrCh <- err
wg.Done()
}
}
wg.Wait()
if len(submitErrCh) != 0 {
return nil, <-submitErrCh
}
return taskErrCh, nil
}

View File

@ -0,0 +1,89 @@
package worker_pool
import (
"errors"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestNewWorkerPool(t *testing.T) {
t.Run("default size pool", func(t *testing.T) {
pool := NewWorkerPool(0)
assert.NotNil(t, pool)
})
t.Run("custom size pool", func(t *testing.T) {
size := 100
pool := NewWorkerPool(size)
assert.NotNil(t, pool)
})
}
func TestSubmit(t *testing.T) {
t.Run("submit task to worker pool", func(t *testing.T) {
pool := NewWorkerPool(10)
var wg sync.WaitGroup
wg.Add(1)
err := pool.Submit(func() {
defer wg.Done()
time.Sleep(100 * time.Millisecond)
})
assert.NoError(t, err)
wg.Wait()
})
}
func TestSubmitAndWaitAll(t *testing.T) {
t.Run("submit and wait all tasks succeed", func(t *testing.T) {
pool := NewWorkerPool(10)
tasks := []func() error{
func() error {
time.Sleep(100 * time.Millisecond)
return nil
},
func() error {
time.Sleep(50 * time.Millisecond)
return nil
},
}
taskErrCh, submitErr := pool.SubmitAndWaitAll(tasks...)
assert.NoError(t, submitErr)
close(taskErrCh)
for err := range taskErrCh {
assert.NoError(t, err)
}
})
t.Run("submit and wait all tasks with errors", func(t *testing.T) {
pool := NewWorkerPool(10)
expectedError := errors.New("task error")
tasks := []func() error{
func() error {
time.Sleep(100 * time.Millisecond)
return nil
},
func() error {
time.Sleep(50 * time.Millisecond)
return expectedError
},
}
taskErrCh, submitErr := pool.SubmitAndWaitAll(tasks...)
assert.NoError(t, submitErr)
close(taskErrCh)
foundError := false
for err := range taskErrCh {
if err != nil {
foundError = true
assert.Equal(t, expectedError, err)
}
}
assert.True(t, foundError)
})
}

View File

@ -11,6 +11,7 @@ import (
"backend/pkg/library/errs"
"backend/pkg/library/errs/code"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/mon"
)

View File

@ -9,6 +9,7 @@ import (
"backend/pkg/library/errs"
"backend/pkg/library/errs/code"
GIDLib "code.30cm.net/digimon/library-go/utils/invited_code"
"github.com/zeromicro/go-zero/core/logx"
)

View File

@ -4,9 +4,10 @@ import (
"context"
"errors"
"fmt"
"go.mongodb.org/mongo-driver/v2/mongo"
"math"
"go.mongodb.org/mongo-driver/v2/mongo"
"backend/pkg/member/domain"
"backend/pkg/member/domain/entity"
"backend/pkg/member/domain/member"
@ -15,6 +16,7 @@ import (
"backend/pkg/library/errs"
"backend/pkg/library/errs/code"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/mon"
)
@ -86,7 +88,7 @@ func (use *MemberUseCase) insertAccount(ctx context.Context, account *entity.Acc
if err != nil {
if mongo.IsDuplicateKeyError(err) {
return errs.DBDuplicate("account duplicate").Wrap(err)
return errs.ResourceAlreadyExistWithScope(code.CloudEPMember, "account duplicate").Wrap(err)
}
return errs.DatabaseErrorWithScopeL(
code.CloudEPMember,

View File

@ -15,6 +15,7 @@ import (
"backend/pkg/library/errs"
"backend/pkg/library/errs/code"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/stores/mon"
"go.uber.org/mock/gomock"

View File

@ -10,6 +10,7 @@ import (
"backend/pkg/library/errs"
"backend/pkg/library/errs/code"
"github.com/stretchr/testify/assert"
)

View File

@ -12,6 +12,7 @@ import (
mockRepo "backend/pkg/member/mock/repository"
"backend/pkg/library/errs"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
)

View File

@ -0,0 +1,34 @@
package config
type SMTPConfig struct {
Enable bool
Sort int
GoroutinePoolNum int
Host string
Port int
Username string
Password string
}
type AmazonSesSettings struct {
Enable bool
Sort int
PoolSize int
Region string
Sender string
Charset string
AccessKey string
SecretKey string
Token string
}
type MitakeSMSSender struct {
Enable bool
Sort int
PoolSize int
User string
Password string
}

View File

@ -0,0 +1,29 @@
package domain
import (
"fmt"
"strings"
ers "backend/pkg/library/errs"
"backend/pkg/library/errs/code"
"github.com/zeromicro/go-zero/core/logx"
)
func ThirdPartyError(scope uint32, ec ers.ErrorCode, s ...string) *ers.LibError {
return ers.NewError(scope, code.ThirdParty, ec.ToUint32(), fmt.Sprintf("thirty error: %s", strings.Join(s, " ")))
}
func ThirdPartyErrorL(scope uint32, ec ers.ErrorCode,
l logx.Logger, filed []logx.LogField, s ...string) *ers.LibError {
e := ThirdPartyError(scope, ec, s...)
l.WithCallerSkip(1).WithFields(filed...).Error(e.Error())
return e
}
const (
NotificationErrorCode = 1 + iota
FailedToSendEmailErrorCode
FailedToSendSMSErrorCode
)

View File

@ -0,0 +1,9 @@
package notification
// Language 定義模板請求
type Language string
const (
LanguageZhTW Language = "zh-tw"
LanguageEnUS Language = "en-us"
)

View File

@ -0,0 +1,16 @@
package notification
import "fmt"
type TypeID int64
func (id TypeID) String() string {
return fmt.Sprintf("%4d", id)
}
// 驗證碼通知類 0 ~ 100
const (
BindingEmail TypeID = 1 // 驗證碼:綁定 Email
BindingPhone TypeID = 2 // 驗證碼:綁定 手機
ForgetPasswordVerify TypeID = 3 // 驗證碼: 忘記密碼
)

View File

@ -0,0 +1,15 @@
package repository
import "context"
type MailRepository interface {
// SendMail 送出 Email
SendMail(ctx context.Context, req MailReq) error
}
type MailReq struct {
To []string
From string
Subject string
Body string
}

View File

@ -0,0 +1,16 @@
package repository
import (
"context"
)
type SMSClientRepository interface {
SendSMS(ctx context.Context, req SMSMessageRequest) error
}
// SMSMessageRequest SMS 訊息請求結構
type SMSMessageRequest struct {
PhoneNumber string `json:"phone_number" validate:"required,e164"` // 接收者號碼 (e164 格式用於驗證國際號碼)
RecipientName string `json:"recipient_name" validate:"required"` // 接收者姓名
MessageContent string `json:"message_content" validate:"required"` // 要傳送的訊息
}

View File

@ -0,0 +1,12 @@
package template
// ==============================
// 產品資訊常數
// ==============================
const (
ProductName = "TrueHeart 團隊"
ProductLink = "https://code.30cm.net"
ProductLogo = "https://true-heart-dev.s3.ap-northeast-3.amazonaws.com/f70904eb-1a29-40f7-8940-9a124f23793a.png"
ProductCopyright = "© 2025 TrueHeart Inc. 版權所有"
)

View File

@ -0,0 +1,128 @@
package template
import (
"github.com/matcornic/hermes/v2"
)
// ==============================
// Email 結構定義
// ==============================
// EmailTemplate 代表 Email 樣板
type EmailTemplate struct {
Title string
Body string
}
// ProductInfo 包含產品相關的資訊
type ProductInfo struct {
Name string
Link string
Logo string
Copyright string
}
// EmailBodyContent 包含郵件正文的資訊
type EmailBodyContent struct {
RecipientName string
Intros []string
Actions []hermes.Action
Outros []string
Signature string
}
// EmailContentParams 組成 Email 內容的參數
type EmailContentParams struct {
Product ProductInfo
Content EmailBodyContent
}
// ==============================
// Email 內容產生器
// ==============================
// GenerateEmailBody 根據參數產生 Email HTML 內容
func GenerateEmailBody(params EmailContentParams) (string, error) {
product := hermes.Product{
Name: params.Product.Name,
Link: params.Product.Link,
Logo: params.Product.Logo,
Copyright: params.Product.Copyright,
}
body := hermes.Body{
Name: params.Content.RecipientName,
Intros: params.Content.Intros,
Actions: params.Content.Actions,
Outros: params.Content.Outros,
Signature: params.Content.Signature,
}
h := hermes.Hermes{Product: product}
email := hermes.Email{Body: body}
return h.GenerateHTML(email)
}
// GenerateEmailContent 生成 Email 內容(可用於不同驗證類型)
func GenerateEmailContent(title string, intros []string) (EmailTemplate, error) {
req := EmailContentParams{
Product: ProductInfo{
Name: ProductName,
Link: ProductLink,
Logo: ProductLogo,
Copyright: ProductCopyright,
},
Content: EmailBodyContent{
RecipientName: "{{.Username}}",
Intros: intros,
Actions: []hermes.Action{
{
Instructions: "請複製您的驗證碼,到網頁重置",
InviteCode: "{{.VerifyCode}}",
},
},
Outros: []string{
"如果您沒有請求此操作,請忽略此郵件。",
},
Signature: "",
},
}
emailBody, err := GenerateEmailBody(req)
if err != nil {
return EmailTemplate{}, err
}
return EmailTemplate{
Title: title,
Body: emailBody,
}, nil
}
// ==============================
// Email 產生函數
// ==============================
// GenerateForgetPasswordEmailZHTW 生成繁體中文的忘記密碼驗證信
func GenerateForgetPasswordEmailZHTW() (EmailTemplate, error) {
return GenerateEmailContent("TrueHeart 重設密碼驗證信",
[]string{"您收到此電子郵件是因為我們收到了針對帳戶的密碼重置請求。"})
}
// GenerateBindingEmailZHTW 生成綁定帳號驗證信
func GenerateBindingEmailZHTW() (EmailTemplate, error) {
return GenerateEmailContent("TrueHeart 綁定信箱驗證信",
[]string{"您收到此電子郵件是因為我們收到了針對帳戶的 Email 認證請求。"})
}
// ==============================
// Email 模板對應表
// ==============================
var EmailTemplateMap = map[Language]map[Type]func() (EmailTemplate, error){
LanguageZhTW: {
ForgetPasswordVerify: GenerateForgetPasswordEmailZHTW,
BindingEmail: GenerateBindingEmailZHTW,
},
}

View File

@ -0,0 +1,11 @@
package template
// ==============================
// 語言與驗證碼類型
// ==============================
type Language string
const (
LanguageZhTW Language = "zh-tw"
)

View File

@ -0,0 +1,9 @@
package template
type Type string
const (
BindingEmail Type = "binding_email" // 驗證碼:綁定 Email
BindingPhone Type = "binding_phone" // 驗證碼:綁定 手機
ForgetPasswordVerify Type = "forget_password" // 驗證碼: 忘記密碼
)

View File

@ -0,0 +1,43 @@
package usecase
import (
"backend/pkg/notification/domain/repository"
"context"
)
type DeliveryUseCase interface {
SendMessage(ctx context.Context, req SMSMessageRequest) error
SendEmail(ctx context.Context, req MailReq) error
}
type MailReq struct {
To []string
From string
Subject string
Body string
}
type SMSMessageRequest struct {
PhoneNumber string `json:"phone_number" validate:"required,e164"` // 接收者號碼 (e164 格式用於驗證國際號碼)
RecipientName string `json:"recipient_name" validate:"required"` // 接收者姓名
MessageContent string `json:"message_content" validate:"required"` // 要傳送的訊息
}
type EmailTemplateResp struct {
Subject string `json:"subject"` // 郵件主題
Body string `json:"body"` // 郵件內容
}
type SMSTemplateResp struct {
Body string `json:"body"`
}
type SMSProvider struct {
Sort int64
Repo repository.SMSClientRepository
}
type EmailProvider struct {
Sort int64
Repo repository.MailRepository
}

View File

@ -0,0 +1,10 @@
package usecase
import (
"backend/pkg/notification/domain/template"
"context"
)
type TemplateUseCase interface {
GetEmailTemplateByStatic(ctx context.Context, language template.Language, templateID template.Type) (template.EmailTemplate, error)
}

View File

@ -0,0 +1,110 @@
package repository
import (
"backend/pkg/notification/config"
"backend/pkg/notification/domain"
"backend/pkg/notification/domain/repository"
"context"
"time"
"backend/pkg/library/errs/code"
pool "backend/pkg/library/worker_pool"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/ses/types"
"github.com/zeromicro/go-zero/core/logx"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ses"
)
// AwsEmailDeliveryParam 傳送參數配置
type AwsEmailDeliveryParam struct {
Conf *config.AmazonSesSettings
}
type AwsEmailDeliveryRepository struct {
Client *ses.Client
Pool pool.WorkerPool
}
func MustAwsSesMailRepository(param AwsEmailDeliveryParam) repository.MailRepository {
// 手動指定 AWS 配置,不使用默認配置
cfg := aws.Config{
Region: param.Conf.Region, // 自定義的 AWS 區域
Credentials: credentials.NewStaticCredentialsProvider(
param.Conf.AccessKey, // AWS Access Key
param.Conf.SecretKey, // AWS Secret Key
"",
),
}
// 創建 SES 客戶端
sesClient := ses.NewFromConfig(cfg)
return &AwsEmailDeliveryRepository{
Client: sesClient,
Pool: pool.NewWorkerPool(param.Conf.PoolSize),
}
}
func (use *AwsEmailDeliveryRepository) SendMail(ctx context.Context, req repository.MailReq) error {
err := use.Pool.Submit(func() {
// 設置郵件參數
to := make([]string, 0, len(req.To))
to = append(to, req.To...)
input := &ses.SendEmailInput{
Destination: &types.Destination{
ToAddresses: to,
},
Message: &types.Message{
Body: &types.Body{
Html: &types.Content{
Charset: aws.String("UTF-8"),
Data: aws.String(req.Body),
},
},
Subject: &types.Content{
Charset: aws.String("UTF-8"),
Data: aws.String(req.Subject),
},
},
Source: aws.String(req.From),
}
// 發送郵件
// TODO 不明原因送不出去,會被 context cancel 這裡先把它手動加到100sec
newCtx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
defer cancel()
//nolint:contextcheck
if _, err := use.Client.SendEmail(newCtx, input); err != nil {
_ = domain.ThirdPartyErrorL(
code.CloudEPNotification,
domain.FailedToSendEmailErrorCode,
logx.WithContext(ctx),
[]logx.LogField{
{Key: "req", Value: req},
{Key: "func", Value: "AwsEmailDeliveryU.SendEmail"},
{Key: "err", Value: err.Error()},
},
"failed to send mail by aws ses")
}
})
if err != nil {
e := domain.ThirdPartyErrorL(
code.CloudEPNotification,
domain.FailedToSendEmailErrorCode,
logx.WithContext(ctx),
[]logx.LogField{
{Key: "req", Value: req},
{Key: "func", Value: "AwsEmailDeliveryU.SendEmail"},
{Key: "err", Value: err.Error()},
},
"failed to send mail by aws ses")
return e
}
return nil
}

View File

@ -0,0 +1,64 @@
package repository
import (
"backend/pkg/notification/config"
"backend/pkg/notification/domain"
"backend/pkg/notification/domain/repository"
"context"
"backend/pkg/library/errs/code"
pool "backend/pkg/library/worker_pool"
"github.com/minchao/go-mitake"
"github.com/zeromicro/go-zero/core/logx"
)
// MitakeSMSDeliveryParam 三竹傳送參數配置
type MitakeSMSDeliveryParam struct {
Conf *config.MitakeSMSSender
}
type MitakeSMSDeliveryRepository struct {
Client *mitake.Client
Pool pool.WorkerPool
}
func (use *MitakeSMSDeliveryRepository) SendSMS(ctx context.Context, req repository.SMSMessageRequest) error {
// 用 goroutine pool 送,否則會超時
err := use.Pool.Submit(func() {
message := mitake.Message{
Dstaddr: req.PhoneNumber,
Destname: req.RecipientName,
Smbody: req.MessageContent,
}
_, err := use.Client.Send(message)
if err != nil {
logx.Error("failed to send sms via mitake")
}
})
if err != nil {
// 錯誤代碼 20-201-04
e := domain.ThirdPartyErrorL(
code.CloudEPNotification,
domain.FailedToSendSMSErrorCode,
logx.WithContext(ctx),
[]logx.LogField{
{Key: "req", Value: req},
{Key: "func", Value: "MitakeSMSDeliveryRepository.Client.Send"},
{Key: "err", Value: err.Error()},
},
"failed to send sns by mitake").Wrap(err)
return e
}
return nil
}
func MustMitakeRepository(param MitakeSMSDeliveryParam) repository.SMSClientRepository {
return &MitakeSMSDeliveryRepository{
Client: mitake.NewClient(param.Conf.User, param.Conf.Password, nil),
Pool: pool.NewWorkerPool(param.Conf.PoolSize),
}
}

View File

@ -0,0 +1,53 @@
package repository
import (
"backend/pkg/notification/config"
"backend/pkg/notification/domain/repository"
"context"
pool "backend/pkg/library/worker_pool"
"github.com/zeromicro/go-zero/core/logx"
"gopkg.in/gomail.v2"
)
type SMTPMailUseCaseParam struct {
Conf config.SMTPConfig
}
type SMTPMailRepository struct {
Client *gomail.Dialer
Pool pool.WorkerPool
}
func MustSMTPUseCase(param SMTPMailUseCaseParam) repository.MailRepository {
return &SMTPMailRepository{
Client: gomail.NewDialer(
param.Conf.Host,
param.Conf.Port,
param.Conf.Username,
param.Conf.Password,
),
Pool: pool.NewWorkerPool(param.Conf.GoroutinePoolNum),
}
}
func (repo *SMTPMailRepository) SendMail(_ context.Context, req repository.MailReq) error {
// 用 goroutine pool 送,否則會超時
err := repo.Pool.Submit(func() {
m := gomail.NewMessage()
m.SetHeader("From", req.From)
m.SetHeader("To", req.To...)
m.SetHeader("Subject", req.Subject)
m.SetBody("text/html", req.Body)
if err := repo.Client.DialAndSend(m); err != nil {
logx.WithCallerSkip(1).WithFields(
logx.Field("func", "MailUseCase.SendMail"),
logx.Field("req", req),
logx.Field("err", err),
).Error("failed to send mail by mailgun")
}
})
return err
}

View File

@ -0,0 +1,75 @@
package usecase
import (
"backend/pkg/notification/domain/repository"
"backend/pkg/notification/domain/usecase"
"context"
"sort"
"time"
)
// DeliveryUseCaseParam 傳送參數配置
type DeliveryUseCaseParam struct {
SMSProviders []usecase.SMSProvider
EmailProviders []usecase.EmailProvider
}
// DeliveryUseCase 通知
type DeliveryUseCase struct {
param DeliveryUseCaseParam
}
func MustDeliveryUseCase(param DeliveryUseCaseParam) usecase.DeliveryUseCase {
return &DeliveryUseCase{
param: param,
}
}
func (use *DeliveryUseCase) SendMessage(ctx context.Context, req usecase.SMSMessageRequest) error {
var err error
// 根據 Sort 欄位對 SMSProviders 進行排序
sort.Slice(use.param.SMSProviders, func(i, j int) bool {
return use.param.SMSProviders[i].Sort < use.param.SMSProviders[j].Sort
})
newCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
// 依序嘗試發送
for _, provider := range use.param.SMSProviders {
if err = provider.Repo.SendSMS(newCtx, repository.SMSMessageRequest{
PhoneNumber: req.PhoneNumber,
RecipientName: req.RecipientName,
MessageContent: req.MessageContent,
}); err == nil {
return nil // 發送成功
}
}
return err
}
func (use *DeliveryUseCase) SendEmail(ctx context.Context, req usecase.MailReq) error {
var err error
// 根據 Sort 欄位對 SMSProviders 進行排序
sort.Slice(use.param.EmailProviders, func(i, j int) bool {
return use.param.EmailProviders[i].Sort < use.param.EmailProviders[j].Sort
})
newCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
// 依序嘗試發送 dreq
for _, provider := range use.param.EmailProviders {
if err = provider.Repo.SendMail(newCtx, repository.MailReq{
From: req.From,
To: req.To,
Subject: req.Subject,
Body: req.Body,
}); err == nil {
return nil // 發送成功
}
}
return err
}

View File

@ -0,0 +1,43 @@
package usecase
import (
"backend/pkg/notification/domain/template"
"backend/pkg/notification/domain/usecase"
"context"
"fmt"
)
type TemplateUseCaseParam struct{}
type TemplateUseCase struct {
TemplateUseCaseParam
}
func MustTemplateUseCase(param TemplateUseCaseParam) usecase.TemplateUseCase {
return &TemplateUseCase{
param,
}
}
func (use *TemplateUseCase) GetEmailTemplateByStatic(_ context.Context, language template.Language, templateID template.Type) (template.EmailTemplate, error) {
// 查找指定語言的模板映射
templateByLang, exists := template.EmailTemplateMap[language]
if !exists {
return template.EmailTemplate{}, fmt.Errorf("email template not found for language: %s", language)
}
// 查找指定類型的模板生成函數
templateFunc, exists := templateByLang[templateID]
if !exists {
return template.EmailTemplate{}, fmt.Errorf("email template not found for type ID: %s", templateID)
}
// 執行模板生成函數
tmp, err := templateFunc()
if err != nil {
return template.EmailTemplate{}, fmt.Errorf("error generating email template: %w", err)
}
// 返回構建好的響應
return tmp, nil
}