feat(quota): add quota task creation and querying endpoints with updated data structures
This commit is contained in:
parent
f4c6bd919b
commit
d1be5febc3
@ -12,7 +12,7 @@ type (
|
|||||||
CreateBatchSendEmailTaskRequest {
|
CreateBatchSendEmailTaskRequest {
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Scope string `json:"scope"`
|
Scope int8 `json:"scope"`
|
||||||
RegisterStartTime int64 `json:"register_start_time,omitempty"`
|
RegisterStartTime int64 `json:"register_start_time,omitempty"`
|
||||||
RegisterEndTime int64 `json:"register_end_time,omitempty"`
|
RegisterEndTime int64 `json:"register_end_time,omitempty"`
|
||||||
Additional string `json:"additional,omitempty"`
|
Additional string `json:"additional,omitempty"`
|
||||||
@ -25,7 +25,7 @@ type (
|
|||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Recipients string `json:"recipients"`
|
Recipients string `json:"recipients"`
|
||||||
Scope string `json:"scope"`
|
Scope int8 `json:"scope"`
|
||||||
RegisterStartTime int64 `json:"register_start_time"`
|
RegisterStartTime int64 `json:"register_start_time"`
|
||||||
RegisterEndTime int64 `json:"register_end_time"`
|
RegisterEndTime int64 `json:"register_end_time"`
|
||||||
Additional string `json:"additional"`
|
Additional string `json:"additional"`
|
||||||
@ -42,7 +42,7 @@ type (
|
|||||||
GetBatchSendEmailTaskListRequest {
|
GetBatchSendEmailTaskListRequest {
|
||||||
Page int `form:"page"`
|
Page int `form:"page"`
|
||||||
Size int `form:"size"`
|
Size int `form:"size"`
|
||||||
Scope string `form:"scope,omitempty"`
|
Scope *int8 `form:"scope,omitempty"`
|
||||||
Status *uint8 `form:"status,omitempty"`
|
Status *uint8 `form:"status,omitempty"`
|
||||||
}
|
}
|
||||||
GetBatchSendEmailTaskListResponse {
|
GetBatchSendEmailTaskListResponse {
|
||||||
@ -69,6 +69,57 @@ type (
|
|||||||
Total int64 `json:"total"`
|
Total int64 `json:"total"`
|
||||||
Errors string `json:"errors"`
|
Errors string `json:"errors"`
|
||||||
}
|
}
|
||||||
|
CreateQuotaTaskRequest {
|
||||||
|
Scope int8 `json:"scope"`
|
||||||
|
RegisterStartTime int64 `json:"register_start_time"`
|
||||||
|
RegisterEndTime int64 `json:"register_end_time"`
|
||||||
|
QuotaType uint8 `json:"quota_type"`
|
||||||
|
Days uint64 `json:"days"` // Number of days for the quota
|
||||||
|
Gift uint8 `json:"gift"` // Invoice amount ratio(%) to gift amount for quota
|
||||||
|
}
|
||||||
|
QuotaTask {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Scope int8 `json:"scope"`
|
||||||
|
RegisterStartTime int64 `json:"register_start_time"`
|
||||||
|
RegisterEndTime int64 `json:"register_end_time"`
|
||||||
|
QuotaType uint8 `json:"quota_type"`
|
||||||
|
Days uint64 `json:"days"` // Number of days for the quota
|
||||||
|
Gift uint8 `json:"gift"` // Invoice amount ratio(%) to gift
|
||||||
|
Recipients []int64 `json:"recipients"` // UserSubscribe IDs of recipients
|
||||||
|
Status uint8 `json:"status"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Current int64 `json:"current"`
|
||||||
|
Errors string `json:"errors"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
}
|
||||||
|
QueryQuotaTaskPreCountRequest {
|
||||||
|
Scope uint8 `json:"scope"`
|
||||||
|
RegisterStartTime int64 `json:"register_start_time"`
|
||||||
|
RegisterEndTime int64 `json:"register_end_time"`
|
||||||
|
}
|
||||||
|
QueryQuotaTaskPreCountResponse {
|
||||||
|
Count int64 `json:"count"`
|
||||||
|
}
|
||||||
|
QueryQuotaTaskListRequest {
|
||||||
|
Page int `form:"page"`
|
||||||
|
Size int `form:"size"`
|
||||||
|
Scope *uint8 `form:"scope,omitempty"`
|
||||||
|
Status *uint8 `form:"status,omitempty"`
|
||||||
|
}
|
||||||
|
QueryQuotaTaskListResponse {
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
List []QuotaTask `json:"list"`
|
||||||
|
}
|
||||||
|
QueryQuotaTaskStatusRequest {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
}
|
||||||
|
QueryQuotaTaskStatusResponse {
|
||||||
|
Status uint8 `json:"status"`
|
||||||
|
Current int64 `json:"current"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Errors string `json:"errors"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@server (
|
@server (
|
||||||
@ -96,5 +147,21 @@ service ppanel {
|
|||||||
@doc "Get batch send email task status"
|
@doc "Get batch send email task status"
|
||||||
@handler GetBatchSendEmailTaskStatus
|
@handler GetBatchSendEmailTaskStatus
|
||||||
post /email/batch/status (GetBatchSendEmailTaskStatusRequest) returns (GetBatchSendEmailTaskStatusResponse)
|
post /email/batch/status (GetBatchSendEmailTaskStatusRequest) returns (GetBatchSendEmailTaskStatusResponse)
|
||||||
|
|
||||||
|
@doc "Create a quota task"
|
||||||
|
@handler CreateQuotaTask
|
||||||
|
post /quota/create (CreateQuotaTaskRequest)
|
||||||
|
|
||||||
|
@doc "Query quota task pre-count"
|
||||||
|
@handler QueryQuotaTaskPreCount
|
||||||
|
post /quota/pre-count (QueryQuotaTaskPreCountRequest) returns (QueryQuotaTaskPreCountResponse)
|
||||||
|
|
||||||
|
@doc "Query quota task list"
|
||||||
|
@handler QueryQuotaTaskList
|
||||||
|
get /quota/list (QueryQuotaTaskListRequest) returns (QueryQuotaTaskListResponse)
|
||||||
|
|
||||||
|
@doc "Query quota task status"
|
||||||
|
@handler QueryQuotaTaskStatus
|
||||||
|
post /quota/status (QueryQuotaTaskStatusRequest) returns (QueryQuotaTaskStatusResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
0
initialize/migrate/database/02113_task.down.sql
Normal file
0
initialize/migrate/database/02113_task.down.sql
Normal file
14
initialize/migrate/database/02113_task.up.sql
Normal file
14
initialize/migrate/database/02113_task.up.sql
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
DROP TABLE IF EXISTS `email_task`;
|
||||||
|
CREATE TABLE `task` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||||
|
`type` tinyint NOT NULL COMMENT 'Task Type',
|
||||||
|
`scope` text COLLATE utf8mb4_general_ci COMMENT 'Task Scope',
|
||||||
|
`content` text COLLATE utf8mb4_general_ci COMMENT 'Task Content',
|
||||||
|
`status` tinyint NOT NULL DEFAULT '0' COMMENT 'Task Status: 0: Pending, 1: In Progress, 2: Completed, 3: Failed',
|
||||||
|
`errors` text COLLATE utf8mb4_general_ci COMMENT 'Task Errors',
|
||||||
|
`total` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'Total Number',
|
||||||
|
`current` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'Current Number',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
26
internal/handler/admin/marketing/createQuotaTaskHandler.go
Normal file
26
internal/handler/admin/marketing/createQuotaTaskHandler.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package marketing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/perfect-panel/server/internal/logic/admin/marketing"
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a quota task
|
||||||
|
func CreateQuotaTaskHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
var req types.CreateQuotaTaskRequest
|
||||||
|
_ = c.ShouldBind(&req)
|
||||||
|
validateErr := svcCtx.Validate(&req)
|
||||||
|
if validateErr != nil {
|
||||||
|
result.ParamErrorResult(c, validateErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := marketing.NewCreateQuotaTaskLogic(c.Request.Context(), svcCtx)
|
||||||
|
err := l.CreateQuotaTask(&req)
|
||||||
|
result.HttpResult(c, nil, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package marketing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/perfect-panel/server/internal/logic/admin/marketing"
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Query quota task list
|
||||||
|
func QueryQuotaTaskListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
var req types.QueryQuotaTaskListRequest
|
||||||
|
_ = c.ShouldBind(&req)
|
||||||
|
validateErr := svcCtx.Validate(&req)
|
||||||
|
if validateErr != nil {
|
||||||
|
result.ParamErrorResult(c, validateErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := marketing.NewQueryQuotaTaskListLogic(c.Request.Context(), svcCtx)
|
||||||
|
resp, err := l.QueryQuotaTaskList(&req)
|
||||||
|
result.HttpResult(c, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package marketing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/perfect-panel/server/internal/logic/admin/marketing"
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Query quota task pre-count
|
||||||
|
func QueryQuotaTaskPreCountHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
var req types.QueryQuotaTaskPreCountRequest
|
||||||
|
_ = c.ShouldBind(&req)
|
||||||
|
validateErr := svcCtx.Validate(&req)
|
||||||
|
if validateErr != nil {
|
||||||
|
result.ParamErrorResult(c, validateErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := marketing.NewQueryQuotaTaskPreCountLogic(c.Request.Context(), svcCtx)
|
||||||
|
resp, err := l.QueryQuotaTaskPreCount(&req)
|
||||||
|
result.HttpResult(c, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package marketing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/perfect-panel/server/internal/logic/admin/marketing"
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Query quota task status
|
||||||
|
func QueryQuotaTaskStatusHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
var req types.QueryQuotaTaskStatusRequest
|
||||||
|
_ = c.ShouldBind(&req)
|
||||||
|
validateErr := svcCtx.Validate(&req)
|
||||||
|
if validateErr != nil {
|
||||||
|
result.ParamErrorResult(c, validateErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := marketing.NewQueryQuotaTaskStatusLogic(c.Request.Context(), svcCtx)
|
||||||
|
resp, err := l.QueryQuotaTaskStatus(&req)
|
||||||
|
result.HttpResult(c, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -253,6 +253,18 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
|
|||||||
|
|
||||||
// Stop a batch send email task
|
// Stop a batch send email task
|
||||||
adminMarketingGroupRouter.POST("/email/batch/stop", adminMarketing.StopBatchSendEmailTaskHandler(serverCtx))
|
adminMarketingGroupRouter.POST("/email/batch/stop", adminMarketing.StopBatchSendEmailTaskHandler(serverCtx))
|
||||||
|
|
||||||
|
// Create a quota task
|
||||||
|
adminMarketingGroupRouter.POST("/quota/create", adminMarketing.CreateQuotaTaskHandler(serverCtx))
|
||||||
|
|
||||||
|
// Query quota task list
|
||||||
|
adminMarketingGroupRouter.GET("/quota/list", adminMarketing.QueryQuotaTaskListHandler(serverCtx))
|
||||||
|
|
||||||
|
// Query quota task pre-count
|
||||||
|
adminMarketingGroupRouter.POST("/quota/pre-count", adminMarketing.QueryQuotaTaskPreCountHandler(serverCtx))
|
||||||
|
|
||||||
|
// Query quota task status
|
||||||
|
adminMarketingGroupRouter.POST("/quota/status", adminMarketing.QueryQuotaTaskStatusHandler(serverCtx))
|
||||||
}
|
}
|
||||||
|
|
||||||
adminOrderGroupRouter := router.Group("/v1/admin/order")
|
adminOrderGroupRouter := router.Group("/v1/admin/order")
|
||||||
|
|||||||
@ -24,7 +24,7 @@ type CreateBatchSendEmailTaskLogic struct {
|
|||||||
svcCtx *svc.ServiceContext
|
svcCtx *svc.ServiceContext
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a batch send email task
|
// NewCreateBatchSendEmailTaskLogic Create a batch send email task
|
||||||
func NewCreateBatchSendEmailTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateBatchSendEmailTaskLogic {
|
func NewCreateBatchSendEmailTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateBatchSendEmailTaskLogic {
|
||||||
return &CreateBatchSendEmailTaskLogic{
|
return &CreateBatchSendEmailTaskLogic{
|
||||||
Logger: logger.WithContext(ctx),
|
Logger: logger.WithContext(ctx),
|
||||||
@ -55,24 +55,27 @@ func (l *CreateBatchSendEmailTaskLogic) CreateBatchSendEmailTask(req *types.Crea
|
|||||||
|
|
||||||
var query *gorm.DB
|
var query *gorm.DB
|
||||||
|
|
||||||
switch req.Scope {
|
scope := task.ParseScopeType(req.Scope)
|
||||||
case "all":
|
|
||||||
|
switch scope {
|
||||||
|
case task.ScopeAll:
|
||||||
query = baseQuery()
|
query = baseQuery()
|
||||||
|
|
||||||
case "active":
|
case task.ScopeActive:
|
||||||
query = baseQuery().
|
query = baseQuery().
|
||||||
Joins("JOIN user_subscribe ON user.id = user_subscribe.user_id").
|
Joins("JOIN user_subscribe ON user.id = user_subscribe.user_id").
|
||||||
Where("user_subscribe.status IN ?", []int64{1, 2})
|
Where("user_subscribe.status IN ?", []int64{1, 2})
|
||||||
|
|
||||||
case "expired":
|
case task.ScopeExpired:
|
||||||
query = baseQuery().
|
query = baseQuery().
|
||||||
Joins("JOIN user_subscribe ON user.id = user_subscribe.user_id").
|
Joins("JOIN user_subscribe ON user.id = user_subscribe.user_id").
|
||||||
Where("user_subscribe.status = ?", 3)
|
Where("user_subscribe.status = ?", 3)
|
||||||
|
|
||||||
case "none":
|
case task.ScopeNone:
|
||||||
query = baseQuery().
|
query = baseQuery().
|
||||||
Joins("LEFT JOIN user_subscribe ON user.id = user_subscribe.user_id").
|
Joins("LEFT JOIN user_subscribe ON user.id = user_subscribe.user_id").
|
||||||
Where("user_subscribe.user_id IS NULL")
|
Where("user_subscribe.user_id IS NULL")
|
||||||
|
default:
|
||||||
|
|
||||||
}
|
}
|
||||||
if query != nil {
|
if query != nil {
|
||||||
@ -85,7 +88,7 @@ func (l *CreateBatchSendEmailTaskLogic) CreateBatchSendEmailTask(req *types.Crea
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 邮箱列表为空,返回错误
|
// 邮箱列表为空,返回错误
|
||||||
if len(emails) == 0 && req.Scope != "skip" {
|
if len(emails) == 0 && scope != task.ScopeSkip {
|
||||||
l.Errorf("[CreateBatchSendEmailTask] No email addresses found for the specified scope")
|
l.Errorf("[CreateBatchSendEmailTask] No email addresses found for the specified scope")
|
||||||
return xerr.NewErrMsg("No email addresses found for the specified scope")
|
return xerr.NewErrMsg("No email addresses found for the specified scope")
|
||||||
}
|
}
|
||||||
@ -96,41 +99,59 @@ func (l *CreateBatchSendEmailTaskLogic) CreateBatchSendEmailTask(req *types.Crea
|
|||||||
var additionalEmails []string
|
var additionalEmails []string
|
||||||
// 追加额外的邮箱地址(不覆盖)
|
// 追加额外的邮箱地址(不覆盖)
|
||||||
if req.Additional != "" {
|
if req.Additional != "" {
|
||||||
additionalEmails = strings.Split(req.Additional, "\n")
|
additionalEmails = tool.RemoveDuplicateElements(strings.Split(req.Additional, "\n")...)
|
||||||
}
|
}
|
||||||
if len(additionalEmails) == 0 && req.Scope == "skip" {
|
if len(additionalEmails) == 0 && scope == task.ScopeSkip {
|
||||||
l.Errorf("[CreateBatchSendEmailTask] No additional email addresses provided for skip scope")
|
l.Errorf("[CreateBatchSendEmailTask] No additional email addresses provided for skip scope")
|
||||||
return xerr.NewErrMsg("No additional email addresses provided for skip scope")
|
return xerr.NewErrMsg("No additional email addresses provided for skip scope")
|
||||||
}
|
}
|
||||||
|
|
||||||
var scheduledAt time.Time
|
scheduledAt := time.Now().Add(10 * time.Second) // 默认延迟10秒执行,防止任务创建和执行时间过于接近
|
||||||
if req.Scheduled == 0 {
|
if req.Scheduled != 0 {
|
||||||
scheduledAt = time.Now()
|
|
||||||
} else {
|
|
||||||
scheduledAt = time.Unix(req.Scheduled, 0)
|
scheduledAt = time.Unix(req.Scheduled, 0)
|
||||||
if scheduledAt.Before(time.Now()) {
|
if scheduledAt.Before(time.Now()) {
|
||||||
scheduledAt = time.Now()
|
scheduledAt = time.Now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
taskInfo := &task.EmailTask{
|
scopeInfo := task.EmailScope{
|
||||||
Subject: req.Subject,
|
Type: scope.Int8(),
|
||||||
Content: req.Content,
|
RegisterStartTime: req.RegisterStartTime,
|
||||||
Recipients: strings.Join(emails, "\n"),
|
RegisterEndTime: req.RegisterEndTime,
|
||||||
Scope: req.Scope,
|
Recipients: emails,
|
||||||
RegisterStartTime: time.Unix(req.RegisterStartTime, 0),
|
Additional: additionalEmails,
|
||||||
RegisterEndTime: time.Unix(req.RegisterEndTime, 0),
|
Scheduled: req.Scheduled,
|
||||||
Additional: req.Additional,
|
|
||||||
Scheduled: scheduledAt,
|
|
||||||
Interval: req.Interval,
|
Interval: req.Interval,
|
||||||
Limit: req.Limit,
|
Limit: req.Limit,
|
||||||
Status: 0,
|
}
|
||||||
Errors: "",
|
scopeBytes, _ := scopeInfo.Marshal()
|
||||||
Total: uint64(len(emails) + len(additionalEmails)),
|
|
||||||
Current: 0,
|
taskContent := task.EmailContent{
|
||||||
|
Subject: req.Subject,
|
||||||
|
Content: req.Content,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = l.svcCtx.DB.Model(&task.EmailTask{}).Create(taskInfo).Error; err != nil {
|
contentBytes, _ := taskContent.Marshal()
|
||||||
|
|
||||||
|
var total uint64
|
||||||
|
if additionalEmails != nil {
|
||||||
|
list := append(emails, additionalEmails...)
|
||||||
|
total = uint64(len(tool.RemoveDuplicateElements(list...)))
|
||||||
|
} else {
|
||||||
|
total = uint64(len(emails))
|
||||||
|
}
|
||||||
|
|
||||||
|
taskInfo := &task.Task{
|
||||||
|
Type: task.TypeEmail,
|
||||||
|
Scope: string(scopeBytes),
|
||||||
|
Content: string(contentBytes),
|
||||||
|
Status: 0,
|
||||||
|
Errors: "",
|
||||||
|
Total: total,
|
||||||
|
Current: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = l.svcCtx.DB.Model(&task.Task{}).Create(taskInfo).Error; err != nil {
|
||||||
l.Errorf("[CreateBatchSendEmailTask] Failed to create email task: %v", err.Error())
|
l.Errorf("[CreateBatchSendEmailTask] Failed to create email task: %v", err.Error())
|
||||||
return xerr.NewErrCode(xerr.DatabaseInsertError)
|
return xerr.NewErrCode(xerr.DatabaseInsertError)
|
||||||
}
|
}
|
||||||
@ -138,12 +159,12 @@ func (l *CreateBatchSendEmailTaskLogic) CreateBatchSendEmailTask(req *types.Crea
|
|||||||
l.Infof("[CreateBatchSendEmailTask] Successfully created email task with ID: %d", taskInfo.Id)
|
l.Infof("[CreateBatchSendEmailTask] Successfully created email task with ID: %d", taskInfo.Id)
|
||||||
|
|
||||||
t := asynq.NewTask(types2.ScheduledBatchSendEmail, []byte(strconv.FormatInt(taskInfo.Id, 10)))
|
t := asynq.NewTask(types2.ScheduledBatchSendEmail, []byte(strconv.FormatInt(taskInfo.Id, 10)))
|
||||||
info, err := l.svcCtx.Queue.EnqueueContext(l.ctx, t, asynq.ProcessAt(taskInfo.Scheduled))
|
info, err := l.svcCtx.Queue.EnqueueContext(l.ctx, t, asynq.ProcessAt(scheduledAt))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Errorf("[CreateBatchSendEmailTask] Failed to enqueue email task: %v", err.Error())
|
l.Errorf("[CreateBatchSendEmailTask] Failed to enqueue email task: %v", err.Error())
|
||||||
return xerr.NewErrCode(xerr.QueueEnqueueError)
|
return xerr.NewErrCode(xerr.QueueEnqueueError)
|
||||||
}
|
}
|
||||||
l.Infof("[CreateBatchSendEmailTask] Successfully enqueued email task with ID: %s, scheduled at: %s", info.ID, taskInfo.Scheduled)
|
l.Infof("[CreateBatchSendEmailTask] Successfully enqueued email task with ID: %s, scheduled at: %s", info.ID, scheduledAt.Format(time.DateTime))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
30
internal/logic/admin/marketing/createQuotaTaskLogic.go
Normal file
30
internal/logic/admin/marketing/createQuotaTaskLogic.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package marketing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateQuotaTaskLogic struct {
|
||||||
|
logger.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a quota task
|
||||||
|
func NewCreateQuotaTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateQuotaTaskLogic {
|
||||||
|
return &CreateQuotaTaskLogic{
|
||||||
|
Logger: logger.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *CreateQuotaTaskLogic) CreateQuotaTask(req *types.CreateQuotaTaskRequest) error {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -2,12 +2,12 @@ package marketing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/model/task"
|
"github.com/perfect-panel/server/internal/model/task"
|
||||||
"github.com/perfect-panel/server/internal/svc"
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
"github.com/perfect-panel/server/internal/types"
|
"github.com/perfect-panel/server/internal/types"
|
||||||
"github.com/perfect-panel/server/pkg/logger"
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
"github.com/perfect-panel/server/pkg/tool"
|
|
||||||
"github.com/perfect-panel/server/pkg/xerr"
|
"github.com/perfect-panel/server/pkg/xerr"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,12 +28,12 @@ func NewGetBatchSendEmailTaskListLogic(ctx context.Context, svcCtx *svc.ServiceC
|
|||||||
|
|
||||||
func (l *GetBatchSendEmailTaskListLogic) GetBatchSendEmailTaskList(req *types.GetBatchSendEmailTaskListRequest) (resp *types.GetBatchSendEmailTaskListResponse, err error) {
|
func (l *GetBatchSendEmailTaskListLogic) GetBatchSendEmailTaskList(req *types.GetBatchSendEmailTaskListRequest) (resp *types.GetBatchSendEmailTaskListResponse, err error) {
|
||||||
|
|
||||||
var tasks []*task.EmailTask
|
var tasks []*task.Task
|
||||||
tx := l.svcCtx.DB.Model(&task.EmailTask{})
|
tx := l.svcCtx.DB.Model(&task.Task{}).Where("`type` = ?", task.TypeEmail)
|
||||||
if req.Status != nil {
|
if req.Status != nil {
|
||||||
tx = tx.Where("status = ?", *req.Status)
|
tx = tx.Where("status = ?", *req.Status)
|
||||||
}
|
}
|
||||||
if req.Scope != "" {
|
if req.Scope != nil {
|
||||||
tx = tx.Where("scope = ?", req.Scope)
|
tx = tx.Where("scope = ?", req.Scope)
|
||||||
}
|
}
|
||||||
if req.Page == 0 {
|
if req.Page == 0 {
|
||||||
@ -49,7 +49,40 @@ func (l *GetBatchSendEmailTaskListLogic) GetBatchSendEmailTaskList(req *types.Ge
|
|||||||
}
|
}
|
||||||
|
|
||||||
list := make([]types.BatchSendEmailTask, 0)
|
list := make([]types.BatchSendEmailTask, 0)
|
||||||
tool.DeepCopy(&list, tasks)
|
|
||||||
|
for _, t := range tasks {
|
||||||
|
var scopeInfo task.EmailScope
|
||||||
|
if err = scopeInfo.Unmarshal([]byte(t.Scope)); err != nil {
|
||||||
|
l.Errorf("[GetBatchSendEmailTaskList] failed to unmarshal email task scope: %v", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var contentInfo task.EmailContent
|
||||||
|
if err = contentInfo.Unmarshal([]byte(t.Content)); err != nil {
|
||||||
|
l.Errorf("[GetBatchSendEmailTaskList] failed to unmarshal email task content: %v", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
list = append(list, types.BatchSendEmailTask{
|
||||||
|
Id: t.Id,
|
||||||
|
Subject: contentInfo.Subject,
|
||||||
|
Content: contentInfo.Content,
|
||||||
|
Recipients: strings.Join(scopeInfo.Recipients, "\n"),
|
||||||
|
Scope: scopeInfo.Type,
|
||||||
|
RegisterStartTime: scopeInfo.RegisterStartTime,
|
||||||
|
RegisterEndTime: scopeInfo.RegisterEndTime,
|
||||||
|
Additional: strings.Join(scopeInfo.Additional, "\n"),
|
||||||
|
Scheduled: scopeInfo.Scheduled,
|
||||||
|
Interval: scopeInfo.Interval,
|
||||||
|
Limit: scopeInfo.Limit,
|
||||||
|
Status: uint8(t.Status),
|
||||||
|
Errors: t.Errors,
|
||||||
|
Total: t.Total,
|
||||||
|
Current: t.Current,
|
||||||
|
CreatedAt: t.CreatedAt.UnixMilli(),
|
||||||
|
UpdatedAt: t.UpdatedAt.UnixMilli(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return &types.GetBatchSendEmailTaskListResponse{
|
return &types.GetBatchSendEmailTaskListResponse{
|
||||||
List: list,
|
List: list,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
30
internal/logic/admin/marketing/queryQuotaTaskListLogic.go
Normal file
30
internal/logic/admin/marketing/queryQuotaTaskListLogic.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package marketing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QueryQuotaTaskListLogic struct {
|
||||||
|
logger.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query quota task list
|
||||||
|
func NewQueryQuotaTaskListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryQuotaTaskListLogic {
|
||||||
|
return &QueryQuotaTaskListLogic{
|
||||||
|
Logger: logger.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *QueryQuotaTaskListLogic) QueryQuotaTaskList(req *types.QueryQuotaTaskListRequest) (resp *types.QueryQuotaTaskListResponse, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package marketing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QueryQuotaTaskPreCountLogic struct {
|
||||||
|
logger.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query quota task pre-count
|
||||||
|
func NewQueryQuotaTaskPreCountLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryQuotaTaskPreCountLogic {
|
||||||
|
return &QueryQuotaTaskPreCountLogic{
|
||||||
|
Logger: logger.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *QueryQuotaTaskPreCountLogic) QueryQuotaTaskPreCount(req *types.QueryQuotaTaskPreCountRequest) (resp *types.QueryQuotaTaskPreCountResponse, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
30
internal/logic/admin/marketing/queryQuotaTaskStatusLogic.go
Normal file
30
internal/logic/admin/marketing/queryQuotaTaskStatusLogic.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package marketing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QueryQuotaTaskStatusLogic struct {
|
||||||
|
logger.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query quota task status
|
||||||
|
func NewQueryQuotaTaskStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryQuotaTaskStatusLogic {
|
||||||
|
return &QueryQuotaTaskStatusLogic{
|
||||||
|
Logger: logger.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *QueryQuotaTaskStatusLogic) QueryQuotaTaskStatus(req *types.QueryQuotaTaskStatusRequest) (resp *types.QueryQuotaTaskStatusResponse, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@ -1,27 +1,142 @@
|
|||||||
package task
|
package task
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type EmailTask struct {
|
type Type int8
|
||||||
Id int64 `gorm:"column:id;primaryKey;autoIncrement;comment:ID"`
|
|
||||||
Subject string `gorm:"column:subject;type:varchar(255);not null;comment:Email Subject"`
|
const (
|
||||||
Content string `gorm:"column:content;type:text;not null;comment:Email Content"`
|
Undefined Type = -1
|
||||||
Recipients string `gorm:"column:recipient;type:text;not null;comment:Email Recipient"`
|
TypeEmail = iota
|
||||||
Scope string `gorm:"column:scope;type:varchar(50);not null;comment:Email Scope"`
|
TypeQuota
|
||||||
RegisterStartTime time.Time `gorm:"column:register_start_time;default:null;comment:Register Start Time"`
|
)
|
||||||
RegisterEndTime time.Time `gorm:"column:register_end_time;default:null;comment:Register End Time"`
|
|
||||||
Additional string `gorm:"column:additional;type:text;default:null;comment:Additional Information"`
|
type Task struct {
|
||||||
Scheduled time.Time `gorm:"column:scheduled;not null;comment:Scheduled Time"`
|
Id int64 `gorm:"primaryKey;autoIncrement;comment:ID"`
|
||||||
Interval uint8 `gorm:"column:interval;not null;comment:Interval in Seconds"`
|
Type int8 `gorm:"not null;comment:Task Type"`
|
||||||
Limit uint64 `gorm:"column:limit;not null;comment:Daily send limit"`
|
Scope string `gorm:"type:text;comment:Task Scope"`
|
||||||
Status uint8 `gorm:"column:status;not null;comment:Daily Status"`
|
Content string `gorm:"type:text;comment:Task Content"`
|
||||||
Errors string `gorm:"column:errors;type:text;not null;comment:Errors"`
|
Status int8 `gorm:"not null;default:0;comment:Task Status: 0: Pending, 1: In Progress, 2: Completed, 3: Failed"`
|
||||||
Total uint64 `gorm:"column:total;not null;default:0;comment:Total Number"`
|
Errors string `gorm:"type:text;comment:Task Errors"`
|
||||||
Current uint64 `gorm:"column:current;not null;default:0;comment:Current Number"`
|
Total uint64 `gorm:"column:total;not null;default:0;comment:Total Number"`
|
||||||
CreatedAt time.Time `gorm:"<-:create;comment:Creation Time"`
|
Current uint64 `gorm:"column:current;not null;default:0;comment:Current Number"`
|
||||||
UpdatedAt time.Time `gorm:"comment:Update Time"`
|
CreatedAt time.Time `gorm:"<-:create;comment:Creation Time"`
|
||||||
|
UpdatedAt time.Time `gorm:"comment:Update Time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (EmailTask) TableName() string {
|
func (Task) TableName() string {
|
||||||
return "email_task"
|
return "task"
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScopeType int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ScopeAll ScopeType = iota + 1 // All users
|
||||||
|
ScopeActive // Active users
|
||||||
|
ScopeExpired // Expired users
|
||||||
|
ScopeNone // No Subscribe
|
||||||
|
ScopeSkip // Skip user filtering
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t ScopeType) Int8() int8 {
|
||||||
|
return int8(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmailScope struct {
|
||||||
|
Type int8 `gorm:"not null;comment:Scope Type"`
|
||||||
|
RegisterStartTime int64 `json:"register_start_time"`
|
||||||
|
RegisterEndTime int64 `json:"register_end_time"`
|
||||||
|
Recipients []string `json:"recipients"` // list of email addresses
|
||||||
|
Additional []string `json:"additional"` // additional email addresses
|
||||||
|
Scheduled int64 `json:"scheduled"` // scheduled time (unix timestamp)
|
||||||
|
Interval uint8 `json:"interval"` // interval in seconds
|
||||||
|
Limit uint64 `json:"limit"` // daily send limit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EmailScope) Marshal() ([]byte, error) {
|
||||||
|
type Alias EmailScope
|
||||||
|
return json.Marshal(&struct {
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Alias: (*Alias)(s),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EmailScope) Unmarshal(data []byte) error {
|
||||||
|
type Alias EmailScope
|
||||||
|
aux := (*Alias)(s)
|
||||||
|
return json.Unmarshal(data, &aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmailContent struct {
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *EmailContent) Marshal() ([]byte, error) {
|
||||||
|
type Alias EmailContent
|
||||||
|
return json.Marshal(&struct {
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Alias: (*Alias)(c),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *EmailContent) Unmarshal(data []byte) error {
|
||||||
|
type Alias EmailContent
|
||||||
|
aux := (*Alias)(c)
|
||||||
|
return json.Unmarshal(data, &aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuotaScope struct {
|
||||||
|
Type int8 `gorm:"not null;comment:Scope Type"`
|
||||||
|
RegisterStartTime int64 `json:"register_start_time"`
|
||||||
|
RegisterEndTime int64 `json:"register_end_time"`
|
||||||
|
Recipients []int64 `json:"recipients"` // list of user subs IDs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *QuotaScope) Marshal() ([]byte, error) {
|
||||||
|
type Alias QuotaScope
|
||||||
|
return json.Marshal(&struct {
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Alias: (*Alias)(s),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *QuotaScope) Unmarshal(data []byte) error {
|
||||||
|
type Alias QuotaScope
|
||||||
|
aux := (*Alias)(s)
|
||||||
|
return json.Unmarshal(data, &aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuotaType int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
QuotaTypeReset QuotaType = iota + 1 // Reset Subscribe Quota
|
||||||
|
QuotaTypeDays // Add Subscribe Days
|
||||||
|
QuotaTypeGift // Add Gift Amount
|
||||||
|
)
|
||||||
|
|
||||||
|
type QuotaContent struct {
|
||||||
|
Type int8 `json:"type"`
|
||||||
|
Days uint64 `json:"days,omitempty"` // days to add
|
||||||
|
Gift uint8 `json:"gift,omitempty"` // Invoice amount ratio(%) to gift amount
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseScopeType(t int8) ScopeType {
|
||||||
|
switch t {
|
||||||
|
case 1:
|
||||||
|
return ScopeAll
|
||||||
|
case 2:
|
||||||
|
return ScopeActive
|
||||||
|
case 3:
|
||||||
|
return ScopeExpired
|
||||||
|
case 4:
|
||||||
|
return ScopeNone
|
||||||
|
default:
|
||||||
|
return ScopeSkip
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -162,7 +162,7 @@ type BatchSendEmailTask struct {
|
|||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Recipients string `json:"recipients"`
|
Recipients string `json:"recipients"`
|
||||||
Scope string `json:"scope"`
|
Scope int8 `json:"scope"`
|
||||||
RegisterStartTime int64 `json:"register_start_time"`
|
RegisterStartTime int64 `json:"register_start_time"`
|
||||||
RegisterEndTime int64 `json:"register_end_time"`
|
RegisterEndTime int64 `json:"register_end_time"`
|
||||||
Additional string `json:"additional"`
|
Additional string `json:"additional"`
|
||||||
@ -274,7 +274,7 @@ type CreateAnnouncementRequest struct {
|
|||||||
type CreateBatchSendEmailTaskRequest struct {
|
type CreateBatchSendEmailTaskRequest struct {
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Scope string `json:"scope"`
|
Scope int8 `json:"scope"`
|
||||||
RegisterStartTime int64 `json:"register_start_time,omitempty"`
|
RegisterStartTime int64 `json:"register_start_time,omitempty"`
|
||||||
RegisterEndTime int64 `json:"register_end_time,omitempty"`
|
RegisterEndTime int64 `json:"register_end_time,omitempty"`
|
||||||
Additional string `json:"additional,omitempty"`
|
Additional string `json:"additional,omitempty"`
|
||||||
@ -344,6 +344,15 @@ type CreatePaymentMethodRequest struct {
|
|||||||
Enable *bool `json:"enable" validate:"required"`
|
Enable *bool `json:"enable" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateQuotaTaskRequest struct {
|
||||||
|
Scope int8 `json:"scope"`
|
||||||
|
RegisterStartTime int64 `json:"register_start_time"`
|
||||||
|
RegisterEndTime int64 `json:"register_end_time"`
|
||||||
|
QuotaType uint8 `json:"quota_type"`
|
||||||
|
Days uint64 `json:"days"` // Number of days for the quota
|
||||||
|
Gift uint8 `json:"gift"` // Invoice amount ratio(%) to gift amount for quota
|
||||||
|
}
|
||||||
|
|
||||||
type CreateServerRequest struct {
|
type CreateServerRequest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Country string `json:"country,omitempty"`
|
Country string `json:"country,omitempty"`
|
||||||
@ -756,7 +765,7 @@ type GetAvailablePaymentMethodsResponse struct {
|
|||||||
type GetBatchSendEmailTaskListRequest struct {
|
type GetBatchSendEmailTaskListRequest struct {
|
||||||
Page int `form:"page"`
|
Page int `form:"page"`
|
||||||
Size int `form:"size"`
|
Size int `form:"size"`
|
||||||
Scope string `form:"scope,omitempty"`
|
Scope *int8 `form:"scope,omitempty"`
|
||||||
Status *uint8 `form:"status,omitempty"`
|
Status *uint8 `form:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1522,6 +1531,39 @@ type QueryPurchaseOrderResponse struct {
|
|||||||
Token string `json:"token,omitempty"`
|
Token string `json:"token,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueryQuotaTaskListRequest struct {
|
||||||
|
Page int `form:"page"`
|
||||||
|
Size int `form:"size"`
|
||||||
|
Scope *uint8 `form:"scope,omitempty"`
|
||||||
|
Status *uint8 `form:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryQuotaTaskListResponse struct {
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
List []QuotaTask `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryQuotaTaskPreCountRequest struct {
|
||||||
|
Scope uint8 `json:"scope"`
|
||||||
|
RegisterStartTime int64 `json:"register_start_time"`
|
||||||
|
RegisterEndTime int64 `json:"register_end_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryQuotaTaskPreCountResponse struct {
|
||||||
|
Count int64 `json:"count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryQuotaTaskStatusRequest struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryQuotaTaskStatusResponse struct {
|
||||||
|
Status uint8 `json:"status"`
|
||||||
|
Current int64 `json:"current"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Errors string `json:"errors"`
|
||||||
|
}
|
||||||
|
|
||||||
type QuerySubscribeGroupListResponse struct {
|
type QuerySubscribeGroupListResponse struct {
|
||||||
List []SubscribeGroup `json:"list"`
|
List []SubscribeGroup `json:"list"`
|
||||||
Total int64 `json:"total"`
|
Total int64 `json:"total"`
|
||||||
@ -1571,6 +1613,23 @@ type QueryUserSubscribeListResponse struct {
|
|||||||
Total int64 `json:"total"`
|
Total int64 `json:"total"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QuotaTask struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Scope int8 `json:"scope"`
|
||||||
|
RegisterStartTime int64 `json:"register_start_time"`
|
||||||
|
RegisterEndTime int64 `json:"register_end_time"`
|
||||||
|
QuotaType uint8 `json:"quota_type"`
|
||||||
|
Days uint64 `json:"days"` // Number of days for the quota
|
||||||
|
Gift uint8 `json:"gift"` // Invoice amount ratio(%) to gift
|
||||||
|
Recipients []int64 `json:"recipients"` // UserSubscribe IDs of recipients
|
||||||
|
Status uint8 `json:"status"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Current int64 `json:"current"`
|
||||||
|
Errors string `json:"errors"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type RechargeOrderRequest struct {
|
type RechargeOrderRequest struct {
|
||||||
Amount int64 `json:"amount"`
|
Amount int64 `json:"amount"`
|
||||||
Payment int64 `json:"payment"`
|
Payment int64 `json:"payment"`
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package orm
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/model/node"
|
"github.com/perfect-panel/server/internal/model/task"
|
||||||
|
|
||||||
"gorm.io/driver/mysql"
|
"gorm.io/driver/mysql"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@ -31,7 +31,7 @@ func TestMysql(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to connect to MySQL: %v", err)
|
t.Fatalf("Failed to connect to MySQL: %v", err)
|
||||||
}
|
}
|
||||||
err = db.Migrator().AutoMigrate(&node.Server{})
|
err = db.Migrator().AutoMigrate(&task.Task{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to auto migrate: %v", err)
|
t.Fatalf("Failed to auto migrate: %v", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@ -44,8 +44,8 @@ func (l *BatchEmailLogic) ProcessTask(ctx context.Context, task *asynq.Task) err
|
|||||||
return asynq.SkipRetry
|
return asynq.SkipRetry
|
||||||
}
|
}
|
||||||
tx := l.svcCtx.DB.WithContext(ctx)
|
tx := l.svcCtx.DB.WithContext(ctx)
|
||||||
var taskInfo taskModel.EmailTask
|
var taskInfo taskModel.Task
|
||||||
if err = tx.Model(&taskModel.EmailTask{}).Where("id = ?", taskID).First(&taskInfo).Error; err != nil {
|
if err = tx.Model(&taskModel.Task{}).Where("id = ?", taskID).First(&taskInfo).Error; err != nil {
|
||||||
logger.WithContext(ctx).Error("[BatchEmailLogic] ProcessTask failed",
|
logger.WithContext(ctx).Error("[BatchEmailLogic] ProcessTask failed",
|
||||||
logger.Field("error", err.Error()),
|
logger.Field("error", err.Error()),
|
||||||
logger.Field("taskID", taskID),
|
logger.Field("taskID", taskID),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user