diff --git a/go.mod b/go.mod index 04daadd..3e110be 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( go.opentelemetry.io/otel/sdk v1.29.0 go.opentelemetry.io/otel/trace v1.29.0 go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.32.0 + golang.org/x/crypto v0.35.0 golang.org/x/oauth2 v0.25.0 golang.org/x/time v0.6.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df @@ -138,8 +138,8 @@ require ( golang.org/x/arch v0.13.0 // indirect golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d // indirect golang.org/x/net v0.34.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect diff --git a/go.sum b/go.sum index 9e9f2c5..d4ae3d4 100644 --- a/go.sum +++ b/go.sum @@ -404,6 +404,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d h1:N0hmiNbwsSNwHBAvR3QB5w25pUwH4tK0Y/RltD1j1h4= golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= @@ -465,6 +467,7 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -480,6 +483,7 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= diff --git a/internal/logic/public/portal/purchaseCheckoutLogic.go b/internal/logic/public/portal/purchaseCheckoutLogic.go index f72ed4f..0a002f7 100644 --- a/internal/logic/public/portal/purchaseCheckoutLogic.go +++ b/internal/logic/public/portal/purchaseCheckoutLogic.go @@ -347,6 +347,11 @@ func (l *PurchaseCheckoutLogic) queryExchangeRate(to string, src int64) (amount // Convert cents to decimal amount amount = float64(src) / float64(100) + if l.svcCtx.ExchangeRate != 0 && to == "CNY" { + amount = amount * l.svcCtx.ExchangeRate + return amount, nil + } + // Retrieve system currency configuration currency, err := l.svcCtx.SystemModel.GetCurrencyConfig(l.ctx) if err != nil { diff --git a/internal/svc/serviceContext.go b/internal/svc/serviceContext.go index 184dc69..be05079 100644 --- a/internal/svc/serviceContext.go +++ b/internal/svc/serviceContext.go @@ -32,10 +32,12 @@ import ( ) type ServiceContext struct { - DB *gorm.DB - Redis *redis.Client - Config config.Config - Queue *asynq.Client + DB *gorm.DB + Redis *redis.Client + Config config.Config + Queue *asynq.Client + ExchangeRate float64 + //NodeCache *cache.NodeCacheClient AuthModel auth.Model AdsModel ads.Model @@ -82,10 +84,11 @@ func NewServiceContext(c config.Config) *ServiceContext { } authLimiter := limit.NewPeriodLimit(86400, 15, rds, config.SendCountLimitKeyPrefix, limit.Align()) srv := &ServiceContext{ - DB: db, - Redis: rds, - Config: c, - Queue: NewAsynqClient(c), + DB: db, + Redis: rds, + Config: c, + Queue: NewAsynqClient(c), + ExchangeRate: 1.0, //NodeCache: cache.NewNodeCacheClient(rds), AuthLimiter: authLimiter, AdsModel: ads.NewModel(db, rds), diff --git a/queue/handler/routes.go b/queue/handler/routes.go index edf2293..2e96219 100644 --- a/queue/handler/routes.go +++ b/queue/handler/routes.go @@ -3,7 +3,6 @@ package handler import ( "github.com/hibiken/asynq" "github.com/perfect-panel/server/internal/svc" - countrylogic "github.com/perfect-panel/server/queue/logic/country" orderLogic "github.com/perfect-panel/server/queue/logic/order" smslogic "github.com/perfect-panel/server/queue/logic/sms" "github.com/perfect-panel/server/queue/logic/subscription" @@ -15,8 +14,6 @@ import ( ) func RegisterHandlers(mux *asynq.ServeMux, serverCtx *svc.ServiceContext) { - // get country task - mux.Handle(types.ForthwithGetCountry, countrylogic.NewGetNodeCountryLogic(serverCtx)) // Send email task mux.Handle(types.ForthwithSendEmail, emailLogic.NewSendEmailLogic(serverCtx)) // Send sms task diff --git a/queue/logic/country/getCountryLogic.go b/queue/logic/country/getCountryLogic.go deleted file mode 100644 index 75e0f6f..0000000 --- a/queue/logic/country/getCountryLogic.go +++ /dev/null @@ -1,22 +0,0 @@ -package countrylogic - -import ( - "context" - - "github.com/hibiken/asynq" - "github.com/perfect-panel/server/internal/svc" -) - -type GetNodeCountryLogic struct { - svcCtx *svc.ServiceContext -} - -func NewGetNodeCountryLogic(svcCtx *svc.ServiceContext) *GetNodeCountryLogic { - return &GetNodeCountryLogic{ - svcCtx: svcCtx, - } -} -func (l *GetNodeCountryLogic) ProcessTask(ctx context.Context, task *asynq.Task) error { - - return nil -} diff --git a/queue/logic/task/rateLogic.go b/queue/logic/task/rateLogic.go new file mode 100644 index 0000000..2e33fae --- /dev/null +++ b/queue/logic/task/rateLogic.go @@ -0,0 +1,52 @@ +package task + +import ( + "context" + + "github.com/hibiken/asynq" + "github.com/perfect-panel/server/internal/svc" + "github.com/perfect-panel/server/pkg/exchangeRate" + "github.com/perfect-panel/server/pkg/logger" + "github.com/perfect-panel/server/pkg/tool" +) + +type RateLogic struct { + svcCtx *svc.ServiceContext +} + +func NewRateLogic(svcCtx *svc.ServiceContext) *RateLogic { + return &RateLogic{ + svcCtx: svcCtx, + } +} + +func (l *RateLogic) ProcessTask(ctx context.Context, _ *asynq.Task) error { + // Retrieve system currency configuration + currency, err := l.svcCtx.SystemModel.GetCurrencyConfig(ctx) + if err != nil { + logger.Errorw("[PurchaseCheckout] GetCurrencyConfig error", logger.Field("error", err.Error())) + return err + } + // Parse currency configuration + configs := struct { + CurrencyUnit string + CurrencySymbol string + AccessKey string + }{} + tool.SystemConfigSliceReflectToStruct(currency, &configs) + + // Skip conversion if no exchange rate API key configured + if configs.AccessKey == "" { + logger.Debugf("[RateLogic] skip exchange rate, no access key configured") + return nil + } + // Update exchange rates + result, err := exchangeRate.GetExchangeRete(configs.CurrencyUnit, "CNY", configs.AccessKey, 1) + if err != nil { + logger.Errorw("[RateLogic] GetExchangeRete error", logger.Field("error", err.Error())) + return err + } + l.svcCtx.ExchangeRate = result + logger.WithContext(ctx).Infof("[RateLogic] GetExchangeRete success, result: %+v", result) + return nil +} diff --git a/queue/types/country.go b/queue/types/country.go deleted file mode 100644 index 13b9e0c..0000000 --- a/queue/types/country.go +++ /dev/null @@ -1,11 +0,0 @@ -package types - -const ( - // ForthwithGetCountry forthwith country get - ForthwithGetCountry = "forthwith:country:get" -) - -type GetNodeCountry struct { - Protocol string `json:"protocol"` - ServerAddr string `json:"server_addr"` -} diff --git a/queue/types/task.go b/queue/types/task.go index 0115f28..85da331 100644 --- a/queue/types/task.go +++ b/queue/types/task.go @@ -6,4 +6,7 @@ const ( // ForthwithQuotaTask create quota task immediately ForthwithQuotaTask = "forthwith:quota:task" + + // SchedulerExchangeRate fetch exchange rate task + SchedulerExchangeRate = "scheduler:exchange:rate" ) diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 377dca3..bf131e8 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -46,6 +46,12 @@ func (m *Service) Start() { logger.Errorf("register traffic stat task failed: %s", err.Error()) } + // schedule update exchange rate task: every day at 01:00 + rateTask := asynq.NewTask(types.ForthwithQuotaTask, nil) + if _, err := m.server.Register("0 1 * * *", rateTask, asynq.MaxRetry(3)); err != nil { + logger.Errorf("register update exchange rate task failed: %s", err.Error()) + } + if err := m.server.Run(); err != nil { logger.Errorf("run scheduler failed: %s", err.Error()) }