feat(quota): add quota task creation and querying endpoints with updated data structures

This commit is contained in:
Chang lue Tsen
2025-09-09 13:39:05 -04:00
parent f4c6bd919b
commit d1be5febc3
18 changed files with 610 additions and 65 deletions
@@ -24,7 +24,7 @@ type CreateBatchSendEmailTaskLogic struct {
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 {
return &CreateBatchSendEmailTaskLogic{
Logger: logger.WithContext(ctx),
@@ -55,24 +55,27 @@ func (l *CreateBatchSendEmailTaskLogic) CreateBatchSendEmailTask(req *types.Crea
var query *gorm.DB
switch req.Scope {
case "all":
scope := task.ParseScopeType(req.Scope)
switch scope {
case task.ScopeAll:
query = baseQuery()
case "active":
case task.ScopeActive:
query = baseQuery().
Joins("JOIN user_subscribe ON user.id = user_subscribe.user_id").
Where("user_subscribe.status IN ?", []int64{1, 2})
case "expired":
case task.ScopeExpired:
query = baseQuery().
Joins("JOIN user_subscribe ON user.id = user_subscribe.user_id").
Where("user_subscribe.status = ?", 3)
case "none":
case task.ScopeNone:
query = baseQuery().
Joins("LEFT JOIN user_subscribe ON user.id = user_subscribe.user_id").
Where("user_subscribe.user_id IS NULL")
default:
}
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")
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
// 追加额外的邮箱地址(不覆盖)
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")
return xerr.NewErrMsg("No additional email addresses provided for skip scope")
}
var scheduledAt time.Time
if req.Scheduled == 0 {
scheduledAt = time.Now()
} else {
scheduledAt := time.Now().Add(10 * time.Second) // 默认延迟10秒执行,防止任务创建和执行时间过于接近
if req.Scheduled != 0 {
scheduledAt = time.Unix(req.Scheduled, 0)
if scheduledAt.Before(time.Now()) {
scheduledAt = time.Now()
}
}
taskInfo := &task.EmailTask{
Subject: req.Subject,
Content: req.Content,
Recipients: strings.Join(emails, "\n"),
Scope: req.Scope,
RegisterStartTime: time.Unix(req.RegisterStartTime, 0),
RegisterEndTime: time.Unix(req.RegisterEndTime, 0),
Additional: req.Additional,
Scheduled: scheduledAt,
scopeInfo := task.EmailScope{
Type: scope.Int8(),
RegisterStartTime: req.RegisterStartTime,
RegisterEndTime: req.RegisterEndTime,
Recipients: emails,
Additional: additionalEmails,
Scheduled: req.Scheduled,
Interval: req.Interval,
Limit: req.Limit,
Status: 0,
Errors: "",
Total: uint64(len(emails) + len(additionalEmails)),
Current: 0,
}
scopeBytes, _ := scopeInfo.Marshal()
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())
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)
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 {
l.Errorf("[CreateBatchSendEmailTask] Failed to enqueue email task: %v", err.Error())
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
}
@@ -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 (
"context"
"strings"
"github.com/perfect-panel/server/internal/model/task"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/tool"
"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) {
var tasks []*task.EmailTask
tx := l.svcCtx.DB.Model(&task.EmailTask{})
var tasks []*task.Task
tx := l.svcCtx.DB.Model(&task.Task{}).Where("`type` = ?", task.TypeEmail)
if req.Status != nil {
tx = tx.Where("status = ?", *req.Status)
}
if req.Scope != "" {
if req.Scope != nil {
tx = tx.Where("scope = ?", req.Scope)
}
if req.Page == 0 {
@@ -49,7 +49,40 @@ func (l *GetBatchSendEmailTaskListLogic) GetBatchSendEmailTaskList(req *types.Ge
}
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{
List: list,
}, nil
@@ -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
}
@@ -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
}