* refactor: rename queryannouncementhandler.go to queryAnnouncementLogic.go for clarity

* feat(panDomain): update subscription logic to use V2 handler for improved functionality

* refactor(subscribe): replace V2 handler with a unified Handler method for subscription logic

* feat(subscribe): implement user agent limit feature with configurable list

* fix(subscribe): improve error handling and logging for subscription requests

* feat(subscribe): add user agent limit configuration to system settings

* refactor(api): remove deprecated application-related endpoints and types

* refactor(swagger): remove deprecated app.json generation from swagger configuration

* refactor(swagger): remove deprecated app.json check from swagger configuration

* fix(subscribe): update delete method to use Where clause for improved query accuracy

* fix(subscribe): update Id field tag to use primaryKey and improve save method query

* fix(subscribe): update Id field tag to use primaryKey and improve model queries

* fix(subscribe): rename variable for clarity and add special handling for Stash user agent

* fix(email): convert RegisterStartTime and RegisterEndTime to time.Time for accurate query filtering

* refactor(log): consolidate logging models and update related logic for improved clarity and functionality

* fix(types): change Content field type in MessageLog to interface{} for improved flexibility

* fix(log): change MessageLog list to use value type for improved performance and memory efficiency

* fix(email): set EmailTypeVerify in task payload and update content type conversion for verification email

* fix(log): remove unused Id field from SystemLog during login log insertion

* fix(login): remove debug logs and error logging during user login process

* fix(log): add traffic reset logging for subscription resets

* fix(log): insert reset traffic log during subscription activation

* feat(log): add endpoints for retrieving and resetting subscribe traffic logs

* refactor(log): remove Reset Subscribe Traffic Log endpoint and related types

* feat(traffic): add traffic statistics logging and scheduling

* fix(subscribe): ensure active status and reset timestamps during traffic resets

* feat(api): enhance server and node management with new request/response structures

* refactor(api): rename OnlineUser to ServerOnlineUser for clarity

* feat(api): define OnlineUser type with SID and IP fields

* feat(server): implement server management handlers and database schema

* feat(api): add traffic log details filtering and enhance traffic log structures

* feat(api): migrate server and node data handling, update related structures and logic

* feat(server): implement server deletion logic with error handling

* feat(api): update log filtering to use ResetSubscribe type for subscription logs

* feat(api): standardize timestamp field across log structures

* feat(api): refactor cache key handling for server and user lists

* feat(api): enhance server status handling with protocol support and refactor related logic

* fix(traffic): adjust start date for traffic statistics and improve log deletion comment

* feat(api): implement daily traffic ranking for users and servers with error handling

* feat(api): update server total data response to use 'OnlineUsers' and implement daily traffic statistics logging

* feat(api): add log settings management with auto-clear and clear days configuration

* fix(log): correct category in log settings update query

* feat(routes): add handler for scheduled traffic statistics

* feat(model): add user counts struct and update queries for new and renewal users

* feat(api): add referral percentage and only first purchase fields to user model and requests

* feat(database): update user table to add referral percentage and only first purchase fields

* feat(api): add reset sort endpoints for server and node

* feat(api): add sort field to server model

* feat(api): implement sorting functionality for nodes and servers

* fix(database): add sort column to nodes table

* fix(model): enhance user statistics queries with new order and renewal order counts

* fix(log): update timestamp handling in login and registration logs

* fix(log): update sorting logic for server and user subscribe traffic logs

* fix(server): add server status handling based on last reported time

* fix(model): correct filter condition to use 'date' instead of 'data'

* fix(migration): add index for traffic log on timestamp, user_id, and subscribe_id

* fix(log): optimize user traffic rank data handling by using append instead of index assignment

* fix(filter): refactor node list creation to use append and remove duplicates from tags

* fix(node): add ServerId and Enabled fields to node update logic

* feat(tags): add endpoint to query all node tags

* fix(preview): add Preload parameter to FilterNodeList for improved data retrieval

* fix(log): date is empty

* feat(subscribe): add Language field to subscription models and update query logic

* feat(subscription): add Language parameter to GetSubscription request and update query logic

* fix(server): encode ServerKey in base64 and update last reported time for nodes

* feat: delete common GetSubscription

* feat(subscription): implement FilterList method for subscription queries and update related logic

* fix(subscribe): remove duplicate user agents in SubscribeHandler

* fix(push): initialize onlineUsers as a map in pushOnlineUsersLogic

* fix(reset): initialize subs as a map in clearCache method

* refactor(query): simplify node and tag filtering using InSet function

* feat(userlist): enhance GetServerUserListLogic with improved node and tag handling

* fix(userlist): correct node ID assignment and update query logic for tag filtering

* fix(userlist): correct node ID assignment in getServerUserListLogic

* refactor(query): streamline query construction for tag filtering

* fix(statistics): optimize server ranking data handling in QueryServerTotalDataLogic

* refactor(statistics): simplify server ranking data construction in QueryServerTotalDataLogic

* fix(statistics): correct server traffic data assignment in QueryServerTotalDataLogic

* fix(statistics): optimize yesterday's top 10 server traffic data assignment in QueryServerTotalDataLogic

* fix(middleware): remove duplicate elements from user agent list in PanDomainMiddleware

* feat(middleware): enhance user agent handling by querying client list in PanDomainMiddleware

* feat(client): subscribe_template

* feat(oauth): add user agent and IP logging to registration and login processes

* fix(balance): add timestamp to balance logs for payment, refund, and recharge transactions

* fix(log): correct comment for CommissionTypeRefund to improve clarity

* fix(log): replace magic number with constant for gift type in purchase checkout logic

* fix(log): rename OrderId to OrderNo for consistency in balance logging

* feat(log): add logging for balance, gift amount, and commission adjustments

* fix(user): correct placement of DeepCopy for user info update logic

* feat(log): add UserSubscribeId to FilterSubscribeLogRequest for enhanced filtering

* fix(purchase): streamline error handling and improve JSON marshaling for temporary orders

* fix(order): simplify commission handling and improve payload parsing logic

* fix(order): update commission calculation to actual payment amount minus gateway handling fee

* feat(payment): add support for CryptoSaaS payment platform and enhance configuration handling

* fix(balance): update QueryUserBalanceLog response structure to include balance log list

* fix(email): update task progress handling to use specific task ID for updates

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

* fix(email): update task handling to use generic task model and improve error logging

* fix(order): improve error logging for database transaction and user cache updates

* feat(quota): enhance quota task management with new request structures and processing logic

* fix(quota): remove redundant quota task status endpoint from admin marketing routes

* fix(worker): update task completion status handling in worker logic

* fix(quota): update taskInfo to include current subscription count in quota logic

* doc(log): rename function for clarity and add cache cleanup comment

* fix(quota): update time handling in quota logic and correct subscriber ID query

* fix(quota): update time handling to use UnixMilli for start time in quota logic

* feat(protocol): add server protocol configuration query and enhance protocol options

* fix(quota): correct time range queries for start and expire times in quota logic

* fix(types): update plugin options to include 'none' in the plugin field

---------

Co-authored-by: Chang lue Tsen <tension@ppanel.dev>
This commit is contained in:
Leif Draven
2025-09-14 22:50:22 +09:00
committed by GitHub
parent b5e244d83d
commit e895180388
359 changed files with 9213 additions and 12125 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),
@@ -45,34 +45,37 @@ func (l *CreateBatchSendEmailTaskLogic) CreateBatchSendEmailTask(req *types.Crea
Where("auth_type = ?", "email")
if req.RegisterStartTime != 0 {
query = query.Where("user.created_at >= ?", req.RegisterStartTime)
query = query.Where("user.created_at >= ?", time.UnixMilli(req.RegisterStartTime))
}
if req.RegisterEndTime != 0 {
query = query.Where("user.created_at <= ?", req.RegisterEndTime)
query = query.Where("user.created_at <= ?", time.UnixMilli(req.RegisterEndTime))
}
return query
}
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,104 @@
package marketing
import (
"context"
"strconv"
"time"
"github.com/hibiken/asynq"
"github.com/perfect-panel/server/internal/model/task"
"github.com/perfect-panel/server/internal/model/user"
"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/xerr"
queueType "github.com/perfect-panel/server/queue/types"
"github.com/pkg/errors"
)
type CreateQuotaTaskLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewCreateQuotaTaskLogic 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 {
var subs []*user.Subscribe
query := l.svcCtx.DB.WithContext(l.ctx).Model(&user.Subscribe{})
if len(req.Subscribers) > 0 {
query = query.Where("`subscribe_id` IN ?", req.Subscribers)
}
if req.IsActive != nil && *req.IsActive {
query = query.Where("`status` IN ?", []int64{0, 1, 2}) // 0: Pending 1: Active 2: Finished
}
if req.StartTime != 0 {
start := time.UnixMilli(req.StartTime)
query = query.Where("`start_time` <= ?", start)
}
if req.EndTime != 0 {
end := time.UnixMilli(req.EndTime)
query = query.Where("`expire_time` >= ?", end)
}
if err := query.Find(&subs).Error; err != nil {
l.Errorf("[CreateQuotaTask] find subscribers error: %v", err.Error())
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find subscribers error")
}
if len(subs) == 0 {
return errors.Wrapf(xerr.NewErrMsg("No subscribers found"), "no subscribers found")
}
var subIds []int64
for _, sub := range subs {
subIds = append(subIds, sub.Id)
}
scopeInfo := task.QuotaScope{
Subscribers: req.Subscribers,
IsActive: req.IsActive,
StartTime: req.StartTime,
EndTime: req.EndTime,
Objects: subIds,
}
scopeBytes, _ := scopeInfo.Marshal()
contentInfo := task.QuotaContent{
ResetTraffic: req.ResetTraffic,
Days: req.Days,
GiftType: req.GiftType,
GiftValue: req.GiftValue,
}
contentBytes, _ := contentInfo.Marshal()
// create task
newTask := &task.Task{
Type: task.TypeQuota,
Status: 0,
Scope: string(scopeBytes),
Content: string(contentBytes),
Total: uint64(len(subIds)),
Current: 0,
Errors: "",
}
if err := l.svcCtx.DB.WithContext(l.ctx).Model(&task.Task{}).Create(newTask).Error; err != nil {
l.Errorf("[CreateQuotaTask] create task error: %v", err.Error())
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create task error")
}
// enqueue task
queueTask := asynq.NewTask(queueType.ForthwithQuotaTask, []byte(strconv.FormatInt(newTask.Id, 10)))
if _, err := l.svcCtx.Queue.EnqueueContext(l.ctx, queueTask); err != nil {
l.Errorf("[CreateQuotaTask] enqueue task error: %v", err.Error())
return errors.Wrapf(xerr.NewErrCode(xerr.QueueEnqueueError), "enqueue task error")
}
logger.Infof("[CreateQuotaTask] Successfully created task with ID: %d", newTask.Id)
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
@@ -28,15 +28,15 @@ func NewGetBatchSendEmailTaskStatusLogic(ctx context.Context, svcCtx *svc.Servic
func (l *GetBatchSendEmailTaskStatusLogic) GetBatchSendEmailTaskStatus(req *types.GetBatchSendEmailTaskStatusRequest) (resp *types.GetBatchSendEmailTaskStatusResponse, err error) {
tx := l.svcCtx.DB
var taskInfo *task.EmailTask
err = tx.Model(&task.EmailTask{}).Where("id = ?", req.Id).First(&taskInfo).Error
var taskInfo *task.Task
err = tx.Model(&task.Task{}).Where("id = ?", req.Id).First(&taskInfo).Error
if err != nil {
l.Errorf("failed to get email task status, error: %v", err)
return nil, xerr.NewErrCode(xerr.DatabaseQueryError)
}
return &types.GetBatchSendEmailTaskStatusResponse{
Status: taskInfo.Status,
Status: uint8(taskInfo.Status),
Total: int64(taskInfo.Total),
Current: int64(taskInfo.Current),
Errors: taskInfo.Errors,
@@ -2,7 +2,9 @@ package marketing
import (
"context"
"time"
"github.com/perfect-panel/server/internal/model/task"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
@@ -37,36 +39,41 @@ func (l *GetPreSendEmailCountLogic) GetPreSendEmailCount(req *types.GetPreSendEm
Where("auth_type = ?", "email")
if req.RegisterStartTime != 0 {
query = query.Where("user.created_at >= ?", req.RegisterStartTime)
registerStartTime := time.UnixMilli(req.RegisterStartTime)
query = query.Where("user.created_at >= ?", registerStartTime)
}
if req.RegisterEndTime != 0 {
query = query.Where("user.created_at <= ?", req.RegisterEndTime)
registerEndTime := time.UnixMilli(req.RegisterEndTime)
query = query.Where("user.created_at <= ?", registerEndTime)
}
return query
}
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")
case "skip":
case task.ScopeSkip:
// Skip scope does not require a count
query = nil
default:
l.Errorf("[CreateBatchSendEmailTask] Invalid scope: %v", req.Scope)
return nil, xerr.NewErrMsg("Invalid email scope")
@@ -0,0 +1,83 @@
package marketing
import (
"context"
"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"
)
type QueryQuotaTaskListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewQueryQuotaTaskListLogic 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) {
var data []*task.Task
var count int64
query := l.svcCtx.DB.Model(&task.Task{}).Where("`type` = ?", task.TypeQuota)
if req.Page == 0 {
req.Page = 1
}
if req.Size == 0 {
req.Size = 20
}
if req.Status != nil {
query = query.Where("`status` = ?", *req.Status)
}
err = query.Count(&count).Offset((req.Page - 1) * req.Size).Limit(req.Size).Order("created_at DESC").Find(&data).Error
if err != nil {
l.Errorf("[QueryQuotaTaskList] failed to get quota tasks: %v", err)
return nil, err
}
var list []types.QuotaTask
for _, item := range data {
var scopeInfo task.QuotaScope
if err = scopeInfo.Unmarshal([]byte(item.Scope)); err != nil {
l.Errorf("[QueryQuotaTaskList] failed to unmarshal quota task scope: %v", err.Error())
continue
}
var contentInfo task.QuotaContent
if err = contentInfo.Unmarshal([]byte(item.Content)); err != nil {
l.Errorf("[QueryQuotaTaskList] failed to unmarshal quota task content: %v", err.Error())
continue
}
list = append(list, types.QuotaTask{
Id: item.Id,
Subscribers: scopeInfo.Subscribers,
IsActive: scopeInfo.IsActive,
StartTime: scopeInfo.StartTime,
EndTime: scopeInfo.EndTime,
ResetTraffic: contentInfo.ResetTraffic,
Days: contentInfo.Days,
GiftType: contentInfo.GiftType,
GiftValue: contentInfo.GiftValue,
Objects: scopeInfo.Objects,
Status: uint8(item.Status),
Total: int64(item.Total),
Current: int64(item.Current),
Errors: item.Errors,
CreatedAt: item.CreatedAt.UnixMilli(),
UpdatedAt: item.UpdatedAt.UnixMilli(),
})
}
return &types.QueryQuotaTaskListResponse{
Total: count,
List: list,
}, nil
}
@@ -0,0 +1,55 @@
package marketing
import (
"context"
"time"
"github.com/perfect-panel/server/internal/model/user"
"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
}
// NewQueryQuotaTaskPreCountLogic 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) {
tx := l.svcCtx.DB.WithContext(l.ctx).Model(&user.Subscribe{})
var count int64
if len(req.Subscribers) > 0 {
tx = tx.Where("`subscribe_id` IN ?", req.Subscribers)
}
if req.IsActive != nil && *req.IsActive {
tx = tx.Where("`status` IN ?", []int64{0, 1, 2}) // 0: Pending 1: Active 2: Finished
}
if req.StartTime != 0 {
start := time.UnixMilli(req.StartTime)
tx = tx.Where("`start_time` <= ?", start)
}
if req.EndTime != 0 {
end := time.UnixMilli(req.EndTime)
tx = tx.Where("`expire_time` >= ?", end)
}
if err = tx.Count(&count).Error; err != nil {
l.Errorf("[QueryQuotaTaskPreCount] count error: %v", err.Error())
return nil, err
}
return &types.QueryQuotaTaskPreCountResponse{
Count: count,
}, nil
}
@@ -0,0 +1,42 @@
package marketing
import (
"context"
"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/xerr"
"github.com/pkg/errors"
)
type QueryQuotaTaskStatusLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewQueryQuotaTaskStatusLogic 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) {
var data *task.Task
err = l.svcCtx.DB.Model(&task.Task{}).Where("id = ? AND `type` = ?", req.Id, task.TypeQuota).First(&data).Error
if err != nil {
l.Errorf("[QueryQuotaTaskStatus] failed to get quota task: %v", err.Error())
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), " failed to get quota task: %v", err.Error())
}
return &types.QueryQuotaTaskStatusResponse{
Status: uint8(data.Status),
Current: int64(data.Current),
Total: int64(data.Total),
Errors: data.Errors,
}, nil
}
@@ -32,7 +32,7 @@ func (l *StopBatchSendEmailTaskLogic) StopBatchSendEmailTask(req *types.StopBatc
} else {
logger.Error("[StopBatchSendEmailTaskLogic] email.Manager is nil, cannot stop task")
}
err = l.svcCtx.DB.Model(&task.EmailTask{}).Where("id = ?", req.Id).Update("status", 2).Error
err = l.svcCtx.DB.Model(&task.Task{}).Where("id = ?", req.Id).Update("status", 2).Error
if err != nil {
l.Errorf("failed to stop email task, error: %v", err)