diff --git a/backend/cmd/server/wire_gen.go b/backend/cmd/server/wire_gen.go
index 9b059c5d823..78b6b3cc520 100644
--- a/backend/cmd/server/wire_gen.go
+++ b/backend/cmd/server/wire_gen.go
@@ -176,6 +176,9 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
crsSyncService := service.NewCRSSyncService(accountRepository, proxyRepository, oAuthService, openAIOAuthService, geminiOAuthService, configConfig)
accountHandler := admin.NewAccountHandler(adminService, oAuthService, openAIOAuthService, geminiOAuthService, antigravityOAuthService, rateLimitService, accountUsageService, accountTestService, concurrencyService, crsSyncService, sessionLimitCache, rpmCache, compositeTokenCacheInvalidator)
adminAnnouncementHandler := admin.NewAnnouncementHandler(announcementService)
+ emailBroadcastRepository := repository.NewEmailBroadcastRepository(client)
+ emailBroadcastService := service.NewEmailBroadcastService(emailBroadcastRepository, userRepository, emailService, settingRepository)
+ emailBroadcastHandler := admin.NewEmailBroadcastHandler(emailBroadcastService, adminService)
dataManagementService := service.NewDataManagementService()
dataManagementHandler := admin.NewDataManagementHandler(dataManagementService)
backupObjectStoreFactory := repository.NewS3BackupStoreFactory()
@@ -239,7 +242,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
contentModerationHandler := admin.NewContentModerationHandler(contentModerationService)
paymentHandler := admin.NewPaymentHandler(paymentService, paymentConfigService)
affiliateHandler := admin.NewAffiliateHandler(affiliateService, adminService)
- adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, adminAnnouncementHandler, dataManagementHandler, backupHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, promoHandler, settingHandler, opsHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler, userAttributeHandler, errorPassthroughHandler, tlsFingerprintProfileHandler, adminAPIKeyHandler, scheduledTestHandler, channelHandler, channelMonitorHandler, channelMonitorRequestTemplateHandler, contentModerationHandler, paymentHandler, affiliateHandler)
+ adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, adminAnnouncementHandler, emailBroadcastHandler, dataManagementHandler, backupHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, promoHandler, settingHandler, opsHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler, userAttributeHandler, errorPassthroughHandler, tlsFingerprintProfileHandler, adminAPIKeyHandler, scheduledTestHandler, channelHandler, channelMonitorHandler, channelMonitorRequestTemplateHandler, contentModerationHandler, paymentHandler, affiliateHandler)
usageRecordWorkerPool := service.NewUsageRecordWorkerPool(configConfig)
userMsgQueueCache := repository.NewUserMsgQueueCache(redisClient)
userMessageQueueService := service.ProvideUserMessageQueueService(userMsgQueueCache, rpmCache, configConfig)
diff --git a/backend/ent/client.go b/backend/ent/client.go
index 06b53c781a9..3d6d80a0598 100644
--- a/backend/ent/client.go
+++ b/backend/ent/client.go
@@ -26,6 +26,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
+ "github.com/Wei-Shaw/sub2api/ent/emailbroadcast"
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
@@ -81,6 +82,8 @@ type Client struct {
ChannelMonitorHistory *ChannelMonitorHistoryClient
// ChannelMonitorRequestTemplate is the client for interacting with the ChannelMonitorRequestTemplate builders.
ChannelMonitorRequestTemplate *ChannelMonitorRequestTemplateClient
+ // EmailBroadcast is the client for interacting with the EmailBroadcast builders.
+ EmailBroadcast *EmailBroadcastClient
// ErrorPassthroughRule is the client for interacting with the ErrorPassthroughRule builders.
ErrorPassthroughRule *ErrorPassthroughRuleClient
// Group is the client for interacting with the Group builders.
@@ -151,6 +154,7 @@ func (c *Client) init() {
c.ChannelMonitorDailyRollup = NewChannelMonitorDailyRollupClient(c.config)
c.ChannelMonitorHistory = NewChannelMonitorHistoryClient(c.config)
c.ChannelMonitorRequestTemplate = NewChannelMonitorRequestTemplateClient(c.config)
+ c.EmailBroadcast = NewEmailBroadcastClient(c.config)
c.ErrorPassthroughRule = NewErrorPassthroughRuleClient(c.config)
c.Group = NewGroupClient(c.config)
c.IdempotencyRecord = NewIdempotencyRecordClient(c.config)
@@ -278,6 +282,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
ChannelMonitorDailyRollup: NewChannelMonitorDailyRollupClient(cfg),
ChannelMonitorHistory: NewChannelMonitorHistoryClient(cfg),
ChannelMonitorRequestTemplate: NewChannelMonitorRequestTemplateClient(cfg),
+ EmailBroadcast: NewEmailBroadcastClient(cfg),
ErrorPassthroughRule: NewErrorPassthroughRuleClient(cfg),
Group: NewGroupClient(cfg),
IdempotencyRecord: NewIdempotencyRecordClient(cfg),
@@ -332,6 +337,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
ChannelMonitorDailyRollup: NewChannelMonitorDailyRollupClient(cfg),
ChannelMonitorHistory: NewChannelMonitorHistoryClient(cfg),
ChannelMonitorRequestTemplate: NewChannelMonitorRequestTemplateClient(cfg),
+ EmailBroadcast: NewEmailBroadcastClient(cfg),
ErrorPassthroughRule: NewErrorPassthroughRuleClient(cfg),
Group: NewGroupClient(cfg),
IdempotencyRecord: NewIdempotencyRecordClient(cfg),
@@ -388,8 +394,8 @@ func (c *Client) Use(hooks ...Hook) {
c.APIKey, c.Account, c.AccountGroup, c.Announcement, c.AnnouncementRead,
c.AuthIdentity, c.AuthIdentityChannel, c.ChannelMonitor,
c.ChannelMonitorDailyRollup, c.ChannelMonitorHistory,
- c.ChannelMonitorRequestTemplate, c.ErrorPassthroughRule, c.Group,
- c.IdempotencyRecord, c.IdentityAdoptionDecision, c.PaymentAuditLog,
+ c.ChannelMonitorRequestTemplate, c.EmailBroadcast, c.ErrorPassthroughRule,
+ c.Group, c.IdempotencyRecord, c.IdentityAdoptionDecision, c.PaymentAuditLog,
c.PaymentOrder, c.PaymentProviderInstance, c.PendingAuthSession, c.PromoCode,
c.PromoCodeUsage, c.Proxy, c.RedeemCode, c.SecuritySecret, c.Setting,
c.SubscriptionPlan, c.TLSFingerprintProfile, c.UsageCleanupTask, c.UsageLog,
@@ -407,8 +413,8 @@ func (c *Client) Intercept(interceptors ...Interceptor) {
c.APIKey, c.Account, c.AccountGroup, c.Announcement, c.AnnouncementRead,
c.AuthIdentity, c.AuthIdentityChannel, c.ChannelMonitor,
c.ChannelMonitorDailyRollup, c.ChannelMonitorHistory,
- c.ChannelMonitorRequestTemplate, c.ErrorPassthroughRule, c.Group,
- c.IdempotencyRecord, c.IdentityAdoptionDecision, c.PaymentAuditLog,
+ c.ChannelMonitorRequestTemplate, c.EmailBroadcast, c.ErrorPassthroughRule,
+ c.Group, c.IdempotencyRecord, c.IdentityAdoptionDecision, c.PaymentAuditLog,
c.PaymentOrder, c.PaymentProviderInstance, c.PendingAuthSession, c.PromoCode,
c.PromoCodeUsage, c.Proxy, c.RedeemCode, c.SecuritySecret, c.Setting,
c.SubscriptionPlan, c.TLSFingerprintProfile, c.UsageCleanupTask, c.UsageLog,
@@ -444,6 +450,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
return c.ChannelMonitorHistory.mutate(ctx, m)
case *ChannelMonitorRequestTemplateMutation:
return c.ChannelMonitorRequestTemplate.mutate(ctx, m)
+ case *EmailBroadcastMutation:
+ return c.EmailBroadcast.mutate(ctx, m)
case *ErrorPassthroughRuleMutation:
return c.ErrorPassthroughRule.mutate(ctx, m)
case *GroupMutation:
@@ -2267,6 +2275,139 @@ func (c *ChannelMonitorRequestTemplateClient) mutate(ctx context.Context, m *Cha
}
}
+// EmailBroadcastClient is a client for the EmailBroadcast schema.
+type EmailBroadcastClient struct {
+ config
+}
+
+// NewEmailBroadcastClient returns a client for the EmailBroadcast from the given config.
+func NewEmailBroadcastClient(c config) *EmailBroadcastClient {
+ return &EmailBroadcastClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `emailbroadcast.Hooks(f(g(h())))`.
+func (c *EmailBroadcastClient) Use(hooks ...Hook) {
+ c.hooks.EmailBroadcast = append(c.hooks.EmailBroadcast, hooks...)
+}
+
+// Intercept adds a list of query interceptors to the interceptors stack.
+// A call to `Intercept(f, g, h)` equals to `emailbroadcast.Intercept(f(g(h())))`.
+func (c *EmailBroadcastClient) Intercept(interceptors ...Interceptor) {
+ c.inters.EmailBroadcast = append(c.inters.EmailBroadcast, interceptors...)
+}
+
+// Create returns a builder for creating a EmailBroadcast entity.
+func (c *EmailBroadcastClient) Create() *EmailBroadcastCreate {
+ mutation := newEmailBroadcastMutation(c.config, OpCreate)
+ return &EmailBroadcastCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// CreateBulk returns a builder for creating a bulk of EmailBroadcast entities.
+func (c *EmailBroadcastClient) CreateBulk(builders ...*EmailBroadcastCreate) *EmailBroadcastCreateBulk {
+ return &EmailBroadcastCreateBulk{config: c.config, builders: builders}
+}
+
+// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
+// a builder and applies setFunc on it.
+func (c *EmailBroadcastClient) MapCreateBulk(slice any, setFunc func(*EmailBroadcastCreate, int)) *EmailBroadcastCreateBulk {
+ rv := reflect.ValueOf(slice)
+ if rv.Kind() != reflect.Slice {
+ return &EmailBroadcastCreateBulk{err: fmt.Errorf("calling to EmailBroadcastClient.MapCreateBulk with wrong type %T, need slice", slice)}
+ }
+ builders := make([]*EmailBroadcastCreate, rv.Len())
+ for i := 0; i < rv.Len(); i++ {
+ builders[i] = c.Create()
+ setFunc(builders[i], i)
+ }
+ return &EmailBroadcastCreateBulk{config: c.config, builders: builders}
+}
+
+// Update returns an update builder for EmailBroadcast.
+func (c *EmailBroadcastClient) Update() *EmailBroadcastUpdate {
+ mutation := newEmailBroadcastMutation(c.config, OpUpdate)
+ return &EmailBroadcastUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *EmailBroadcastClient) UpdateOne(_m *EmailBroadcast) *EmailBroadcastUpdateOne {
+ mutation := newEmailBroadcastMutation(c.config, OpUpdateOne, withEmailBroadcast(_m))
+ return &EmailBroadcastUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *EmailBroadcastClient) UpdateOneID(id int64) *EmailBroadcastUpdateOne {
+ mutation := newEmailBroadcastMutation(c.config, OpUpdateOne, withEmailBroadcastID(id))
+ return &EmailBroadcastUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for EmailBroadcast.
+func (c *EmailBroadcastClient) Delete() *EmailBroadcastDelete {
+ mutation := newEmailBroadcastMutation(c.config, OpDelete)
+ return &EmailBroadcastDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a builder for deleting the given entity.
+func (c *EmailBroadcastClient) DeleteOne(_m *EmailBroadcast) *EmailBroadcastDeleteOne {
+ return c.DeleteOneID(_m.ID)
+}
+
+// DeleteOneID returns a builder for deleting the given entity by its id.
+func (c *EmailBroadcastClient) DeleteOneID(id int64) *EmailBroadcastDeleteOne {
+ builder := c.Delete().Where(emailbroadcast.ID(id))
+ builder.mutation.id = &id
+ builder.mutation.op = OpDeleteOne
+ return &EmailBroadcastDeleteOne{builder}
+}
+
+// Query returns a query builder for EmailBroadcast.
+func (c *EmailBroadcastClient) Query() *EmailBroadcastQuery {
+ return &EmailBroadcastQuery{
+ config: c.config,
+ ctx: &QueryContext{Type: TypeEmailBroadcast},
+ inters: c.Interceptors(),
+ }
+}
+
+// Get returns a EmailBroadcast entity by its id.
+func (c *EmailBroadcastClient) Get(ctx context.Context, id int64) (*EmailBroadcast, error) {
+ return c.Query().Where(emailbroadcast.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *EmailBroadcastClient) GetX(ctx context.Context, id int64) *EmailBroadcast {
+ obj, err := c.Get(ctx, id)
+ if err != nil {
+ panic(err)
+ }
+ return obj
+}
+
+// Hooks returns the client hooks.
+func (c *EmailBroadcastClient) Hooks() []Hook {
+ return c.hooks.EmailBroadcast
+}
+
+// Interceptors returns the client interceptors.
+func (c *EmailBroadcastClient) Interceptors() []Interceptor {
+ return c.inters.EmailBroadcast
+}
+
+func (c *EmailBroadcastClient) mutate(ctx context.Context, m *EmailBroadcastMutation) (Value, error) {
+ switch m.Op() {
+ case OpCreate:
+ return (&EmailBroadcastCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+ case OpUpdate:
+ return (&EmailBroadcastUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+ case OpUpdateOne:
+ return (&EmailBroadcastUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+ case OpDelete, OpDeleteOne:
+ return (&EmailBroadcastDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
+ default:
+ return nil, fmt.Errorf("ent: unknown EmailBroadcast mutation op: %q", m.Op())
+ }
+}
+
// ErrorPassthroughRuleClient is a client for the ErrorPassthroughRule schema.
type ErrorPassthroughRuleClient struct {
config
@@ -6195,24 +6336,24 @@ type (
hooks struct {
APIKey, Account, AccountGroup, Announcement, AnnouncementRead, AuthIdentity,
AuthIdentityChannel, ChannelMonitor, ChannelMonitorDailyRollup,
- ChannelMonitorHistory, ChannelMonitorRequestTemplate, ErrorPassthroughRule,
- Group, IdempotencyRecord, IdentityAdoptionDecision, PaymentAuditLog,
- PaymentOrder, PaymentProviderInstance, PendingAuthSession, PromoCode,
- PromoCodeUsage, Proxy, RedeemCode, SecuritySecret, Setting, SubscriptionPlan,
- TLSFingerprintProfile, UsageCleanupTask, UsageLog, User, UserAllowedGroup,
- UserAttributeDefinition, UserAttributeValue, UserPlatformQuota,
- UserSubscription []ent.Hook
+ ChannelMonitorHistory, ChannelMonitorRequestTemplate, EmailBroadcast,
+ ErrorPassthroughRule, Group, IdempotencyRecord, IdentityAdoptionDecision,
+ PaymentAuditLog, PaymentOrder, PaymentProviderInstance, PendingAuthSession,
+ PromoCode, PromoCodeUsage, Proxy, RedeemCode, SecuritySecret, Setting,
+ SubscriptionPlan, TLSFingerprintProfile, UsageCleanupTask, UsageLog, User,
+ UserAllowedGroup, UserAttributeDefinition, UserAttributeValue,
+ UserPlatformQuota, UserSubscription []ent.Hook
}
inters struct {
APIKey, Account, AccountGroup, Announcement, AnnouncementRead, AuthIdentity,
AuthIdentityChannel, ChannelMonitor, ChannelMonitorDailyRollup,
- ChannelMonitorHistory, ChannelMonitorRequestTemplate, ErrorPassthroughRule,
- Group, IdempotencyRecord, IdentityAdoptionDecision, PaymentAuditLog,
- PaymentOrder, PaymentProviderInstance, PendingAuthSession, PromoCode,
- PromoCodeUsage, Proxy, RedeemCode, SecuritySecret, Setting, SubscriptionPlan,
- TLSFingerprintProfile, UsageCleanupTask, UsageLog, User, UserAllowedGroup,
- UserAttributeDefinition, UserAttributeValue, UserPlatformQuota,
- UserSubscription []ent.Interceptor
+ ChannelMonitorHistory, ChannelMonitorRequestTemplate, EmailBroadcast,
+ ErrorPassthroughRule, Group, IdempotencyRecord, IdentityAdoptionDecision,
+ PaymentAuditLog, PaymentOrder, PaymentProviderInstance, PendingAuthSession,
+ PromoCode, PromoCodeUsage, Proxy, RedeemCode, SecuritySecret, Setting,
+ SubscriptionPlan, TLSFingerprintProfile, UsageCleanupTask, UsageLog, User,
+ UserAllowedGroup, UserAttributeDefinition, UserAttributeValue,
+ UserPlatformQuota, UserSubscription []ent.Interceptor
}
)
diff --git a/backend/ent/emailbroadcast.go b/backend/ent/emailbroadcast.go
new file mode 100644
index 00000000000..df0235d4eb0
--- /dev/null
+++ b/backend/ent/emailbroadcast.go
@@ -0,0 +1,277 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+ "time"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect/sql"
+ "github.com/Wei-Shaw/sub2api/ent/emailbroadcast"
+)
+
+// EmailBroadcast is the model entity for the EmailBroadcast schema.
+type EmailBroadcast struct {
+ config `json:"-"`
+ // ID of the ent.
+ ID int64 `json:"id,omitempty"`
+ // 邮件主题
+ Subject string `json:"subject,omitempty"`
+ // 邮件正文 (HTML 或纯文本)
+ Body string `json:"body,omitempty"`
+ // 正文格式: html, text
+ BodyFormat string `json:"body_format,omitempty"`
+ // 收件人模式: all, selected
+ RecipientsMode string `json:"recipients_mode,omitempty"`
+ // 收件人用户 ID 列表 (selected 模式时使用)
+ RecipientUserIds []int64 `json:"recipient_user_ids,omitempty"`
+ // 状态: pending, sending, completed, failed
+ Status string `json:"status,omitempty"`
+ // 收件人总数
+ TotalCount int `json:"total_count,omitempty"`
+ // 发送成功数
+ SuccessCount int `json:"success_count,omitempty"`
+ // 发送失败数
+ FailedCount int `json:"failed_count,omitempty"`
+ // 整批失败原因 (status=failed 时填充)
+ ErrorMessage *string `json:"error_message,omitempty"`
+ // 创建管理员用户 ID
+ CreatedBy *int64 `json:"created_by,omitempty"`
+ // 开始发送时间
+ StartedAt *time.Time `json:"started_at,omitempty"`
+ // 发送结束时间
+ FinishedAt *time.Time `json:"finished_at,omitempty"`
+ // CreatedAt holds the value of the "created_at" field.
+ CreatedAt time.Time `json:"created_at,omitempty"`
+ // UpdatedAt holds the value of the "updated_at" field.
+ UpdatedAt time.Time `json:"updated_at,omitempty"`
+ selectValues sql.SelectValues
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*EmailBroadcast) scanValues(columns []string) ([]any, error) {
+ values := make([]any, len(columns))
+ for i := range columns {
+ switch columns[i] {
+ case emailbroadcast.FieldRecipientUserIds:
+ values[i] = new([]byte)
+ case emailbroadcast.FieldID, emailbroadcast.FieldTotalCount, emailbroadcast.FieldSuccessCount, emailbroadcast.FieldFailedCount, emailbroadcast.FieldCreatedBy:
+ values[i] = new(sql.NullInt64)
+ case emailbroadcast.FieldSubject, emailbroadcast.FieldBody, emailbroadcast.FieldBodyFormat, emailbroadcast.FieldRecipientsMode, emailbroadcast.FieldStatus, emailbroadcast.FieldErrorMessage:
+ values[i] = new(sql.NullString)
+ case emailbroadcast.FieldStartedAt, emailbroadcast.FieldFinishedAt, emailbroadcast.FieldCreatedAt, emailbroadcast.FieldUpdatedAt:
+ values[i] = new(sql.NullTime)
+ default:
+ values[i] = new(sql.UnknownType)
+ }
+ }
+ return values, nil
+}
+
+// assignValues assigns the values that were returned from sql.Rows (after scanning)
+// to the EmailBroadcast fields.
+func (_m *EmailBroadcast) assignValues(columns []string, values []any) error {
+ if m, n := len(values), len(columns); m < n {
+ return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
+ }
+ for i := range columns {
+ switch columns[i] {
+ case emailbroadcast.FieldID:
+ value, ok := values[i].(*sql.NullInt64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field id", value)
+ }
+ _m.ID = int64(value.Int64)
+ case emailbroadcast.FieldSubject:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field subject", values[i])
+ } else if value.Valid {
+ _m.Subject = value.String
+ }
+ case emailbroadcast.FieldBody:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field body", values[i])
+ } else if value.Valid {
+ _m.Body = value.String
+ }
+ case emailbroadcast.FieldBodyFormat:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field body_format", values[i])
+ } else if value.Valid {
+ _m.BodyFormat = value.String
+ }
+ case emailbroadcast.FieldRecipientsMode:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field recipients_mode", values[i])
+ } else if value.Valid {
+ _m.RecipientsMode = value.String
+ }
+ case emailbroadcast.FieldRecipientUserIds:
+ if value, ok := values[i].(*[]byte); !ok {
+ return fmt.Errorf("unexpected type %T for field recipient_user_ids", values[i])
+ } else if value != nil && len(*value) > 0 {
+ if err := json.Unmarshal(*value, &_m.RecipientUserIds); err != nil {
+ return fmt.Errorf("unmarshal field recipient_user_ids: %w", err)
+ }
+ }
+ case emailbroadcast.FieldStatus:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field status", values[i])
+ } else if value.Valid {
+ _m.Status = value.String
+ }
+ case emailbroadcast.FieldTotalCount:
+ if value, ok := values[i].(*sql.NullInt64); !ok {
+ return fmt.Errorf("unexpected type %T for field total_count", values[i])
+ } else if value.Valid {
+ _m.TotalCount = int(value.Int64)
+ }
+ case emailbroadcast.FieldSuccessCount:
+ if value, ok := values[i].(*sql.NullInt64); !ok {
+ return fmt.Errorf("unexpected type %T for field success_count", values[i])
+ } else if value.Valid {
+ _m.SuccessCount = int(value.Int64)
+ }
+ case emailbroadcast.FieldFailedCount:
+ if value, ok := values[i].(*sql.NullInt64); !ok {
+ return fmt.Errorf("unexpected type %T for field failed_count", values[i])
+ } else if value.Valid {
+ _m.FailedCount = int(value.Int64)
+ }
+ case emailbroadcast.FieldErrorMessage:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field error_message", values[i])
+ } else if value.Valid {
+ _m.ErrorMessage = new(string)
+ *_m.ErrorMessage = value.String
+ }
+ case emailbroadcast.FieldCreatedBy:
+ if value, ok := values[i].(*sql.NullInt64); !ok {
+ return fmt.Errorf("unexpected type %T for field created_by", values[i])
+ } else if value.Valid {
+ _m.CreatedBy = new(int64)
+ *_m.CreatedBy = value.Int64
+ }
+ case emailbroadcast.FieldStartedAt:
+ if value, ok := values[i].(*sql.NullTime); !ok {
+ return fmt.Errorf("unexpected type %T for field started_at", values[i])
+ } else if value.Valid {
+ _m.StartedAt = new(time.Time)
+ *_m.StartedAt = value.Time
+ }
+ case emailbroadcast.FieldFinishedAt:
+ if value, ok := values[i].(*sql.NullTime); !ok {
+ return fmt.Errorf("unexpected type %T for field finished_at", values[i])
+ } else if value.Valid {
+ _m.FinishedAt = new(time.Time)
+ *_m.FinishedAt = value.Time
+ }
+ case emailbroadcast.FieldCreatedAt:
+ if value, ok := values[i].(*sql.NullTime); !ok {
+ return fmt.Errorf("unexpected type %T for field created_at", values[i])
+ } else if value.Valid {
+ _m.CreatedAt = value.Time
+ }
+ case emailbroadcast.FieldUpdatedAt:
+ if value, ok := values[i].(*sql.NullTime); !ok {
+ return fmt.Errorf("unexpected type %T for field updated_at", values[i])
+ } else if value.Valid {
+ _m.UpdatedAt = value.Time
+ }
+ default:
+ _m.selectValues.Set(columns[i], values[i])
+ }
+ }
+ return nil
+}
+
+// Value returns the ent.Value that was dynamically selected and assigned to the EmailBroadcast.
+// This includes values selected through modifiers, order, etc.
+func (_m *EmailBroadcast) Value(name string) (ent.Value, error) {
+ return _m.selectValues.Get(name)
+}
+
+// Update returns a builder for updating this EmailBroadcast.
+// Note that you need to call EmailBroadcast.Unwrap() before calling this method if this EmailBroadcast
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (_m *EmailBroadcast) Update() *EmailBroadcastUpdateOne {
+ return NewEmailBroadcastClient(_m.config).UpdateOne(_m)
+}
+
+// Unwrap unwraps the EmailBroadcast entity that was returned from a transaction after it was closed,
+// so that all future queries will be executed through the driver which created the transaction.
+func (_m *EmailBroadcast) Unwrap() *EmailBroadcast {
+ _tx, ok := _m.config.driver.(*txDriver)
+ if !ok {
+ panic("ent: EmailBroadcast is not a transactional entity")
+ }
+ _m.config.driver = _tx.drv
+ return _m
+}
+
+// String implements the fmt.Stringer.
+func (_m *EmailBroadcast) String() string {
+ var builder strings.Builder
+ builder.WriteString("EmailBroadcast(")
+ builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
+ builder.WriteString("subject=")
+ builder.WriteString(_m.Subject)
+ builder.WriteString(", ")
+ builder.WriteString("body=")
+ builder.WriteString(_m.Body)
+ builder.WriteString(", ")
+ builder.WriteString("body_format=")
+ builder.WriteString(_m.BodyFormat)
+ builder.WriteString(", ")
+ builder.WriteString("recipients_mode=")
+ builder.WriteString(_m.RecipientsMode)
+ builder.WriteString(", ")
+ builder.WriteString("recipient_user_ids=")
+ builder.WriteString(fmt.Sprintf("%v", _m.RecipientUserIds))
+ builder.WriteString(", ")
+ builder.WriteString("status=")
+ builder.WriteString(_m.Status)
+ builder.WriteString(", ")
+ builder.WriteString("total_count=")
+ builder.WriteString(fmt.Sprintf("%v", _m.TotalCount))
+ builder.WriteString(", ")
+ builder.WriteString("success_count=")
+ builder.WriteString(fmt.Sprintf("%v", _m.SuccessCount))
+ builder.WriteString(", ")
+ builder.WriteString("failed_count=")
+ builder.WriteString(fmt.Sprintf("%v", _m.FailedCount))
+ builder.WriteString(", ")
+ if v := _m.ErrorMessage; v != nil {
+ builder.WriteString("error_message=")
+ builder.WriteString(*v)
+ }
+ builder.WriteString(", ")
+ if v := _m.CreatedBy; v != nil {
+ builder.WriteString("created_by=")
+ builder.WriteString(fmt.Sprintf("%v", *v))
+ }
+ builder.WriteString(", ")
+ if v := _m.StartedAt; v != nil {
+ builder.WriteString("started_at=")
+ builder.WriteString(v.Format(time.ANSIC))
+ }
+ builder.WriteString(", ")
+ if v := _m.FinishedAt; v != nil {
+ builder.WriteString("finished_at=")
+ builder.WriteString(v.Format(time.ANSIC))
+ }
+ builder.WriteString(", ")
+ builder.WriteString("created_at=")
+ builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
+ builder.WriteString(", ")
+ builder.WriteString("updated_at=")
+ builder.WriteString(_m.UpdatedAt.Format(time.ANSIC))
+ builder.WriteByte(')')
+ return builder.String()
+}
+
+// EmailBroadcasts is a parsable slice of EmailBroadcast.
+type EmailBroadcasts []*EmailBroadcast
diff --git a/backend/ent/emailbroadcast/emailbroadcast.go b/backend/ent/emailbroadcast/emailbroadcast.go
new file mode 100644
index 00000000000..38ce4f05a16
--- /dev/null
+++ b/backend/ent/emailbroadcast/emailbroadcast.go
@@ -0,0 +1,193 @@
+// Code generated by ent, DO NOT EDIT.
+
+package emailbroadcast
+
+import (
+ "time"
+
+ "entgo.io/ent/dialect/sql"
+)
+
+const (
+ // Label holds the string label denoting the emailbroadcast type in the database.
+ Label = "email_broadcast"
+ // FieldID holds the string denoting the id field in the database.
+ FieldID = "id"
+ // FieldSubject holds the string denoting the subject field in the database.
+ FieldSubject = "subject"
+ // FieldBody holds the string denoting the body field in the database.
+ FieldBody = "body"
+ // FieldBodyFormat holds the string denoting the body_format field in the database.
+ FieldBodyFormat = "body_format"
+ // FieldRecipientsMode holds the string denoting the recipients_mode field in the database.
+ FieldRecipientsMode = "recipients_mode"
+ // FieldRecipientUserIds holds the string denoting the recipient_user_ids field in the database.
+ FieldRecipientUserIds = "recipient_user_ids"
+ // FieldStatus holds the string denoting the status field in the database.
+ FieldStatus = "status"
+ // FieldTotalCount holds the string denoting the total_count field in the database.
+ FieldTotalCount = "total_count"
+ // FieldSuccessCount holds the string denoting the success_count field in the database.
+ FieldSuccessCount = "success_count"
+ // FieldFailedCount holds the string denoting the failed_count field in the database.
+ FieldFailedCount = "failed_count"
+ // FieldErrorMessage holds the string denoting the error_message field in the database.
+ FieldErrorMessage = "error_message"
+ // FieldCreatedBy holds the string denoting the created_by field in the database.
+ FieldCreatedBy = "created_by"
+ // FieldStartedAt holds the string denoting the started_at field in the database.
+ FieldStartedAt = "started_at"
+ // FieldFinishedAt holds the string denoting the finished_at field in the database.
+ FieldFinishedAt = "finished_at"
+ // FieldCreatedAt holds the string denoting the created_at field in the database.
+ FieldCreatedAt = "created_at"
+ // FieldUpdatedAt holds the string denoting the updated_at field in the database.
+ FieldUpdatedAt = "updated_at"
+ // Table holds the table name of the emailbroadcast in the database.
+ Table = "email_broadcasts"
+)
+
+// Columns holds all SQL columns for emailbroadcast fields.
+var Columns = []string{
+ FieldID,
+ FieldSubject,
+ FieldBody,
+ FieldBodyFormat,
+ FieldRecipientsMode,
+ FieldRecipientUserIds,
+ FieldStatus,
+ FieldTotalCount,
+ FieldSuccessCount,
+ FieldFailedCount,
+ FieldErrorMessage,
+ FieldCreatedBy,
+ FieldStartedAt,
+ FieldFinishedAt,
+ FieldCreatedAt,
+ FieldUpdatedAt,
+}
+
+// ValidColumn reports if the column name is valid (part of the table columns).
+func ValidColumn(column string) bool {
+ for i := range Columns {
+ if column == Columns[i] {
+ return true
+ }
+ }
+ return false
+}
+
+var (
+ // SubjectValidator is a validator for the "subject" field. It is called by the builders before save.
+ SubjectValidator func(string) error
+ // BodyValidator is a validator for the "body" field. It is called by the builders before save.
+ BodyValidator func(string) error
+ // DefaultBodyFormat holds the default value on creation for the "body_format" field.
+ DefaultBodyFormat string
+ // BodyFormatValidator is a validator for the "body_format" field. It is called by the builders before save.
+ BodyFormatValidator func(string) error
+ // DefaultRecipientsMode holds the default value on creation for the "recipients_mode" field.
+ DefaultRecipientsMode string
+ // RecipientsModeValidator is a validator for the "recipients_mode" field. It is called by the builders before save.
+ RecipientsModeValidator func(string) error
+ // DefaultStatus holds the default value on creation for the "status" field.
+ DefaultStatus string
+ // StatusValidator is a validator for the "status" field. It is called by the builders before save.
+ StatusValidator func(string) error
+ // DefaultTotalCount holds the default value on creation for the "total_count" field.
+ DefaultTotalCount int
+ // TotalCountValidator is a validator for the "total_count" field. It is called by the builders before save.
+ TotalCountValidator func(int) error
+ // DefaultSuccessCount holds the default value on creation for the "success_count" field.
+ DefaultSuccessCount int
+ // SuccessCountValidator is a validator for the "success_count" field. It is called by the builders before save.
+ SuccessCountValidator func(int) error
+ // DefaultFailedCount holds the default value on creation for the "failed_count" field.
+ DefaultFailedCount int
+ // FailedCountValidator is a validator for the "failed_count" field. It is called by the builders before save.
+ FailedCountValidator func(int) error
+ // DefaultCreatedAt holds the default value on creation for the "created_at" field.
+ DefaultCreatedAt func() time.Time
+ // DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
+ DefaultUpdatedAt func() time.Time
+ // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
+ UpdateDefaultUpdatedAt func() time.Time
+)
+
+// OrderOption defines the ordering options for the EmailBroadcast queries.
+type OrderOption func(*sql.Selector)
+
+// ByID orders the results by the id field.
+func ByID(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldID, opts...).ToFunc()
+}
+
+// BySubject orders the results by the subject field.
+func BySubject(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldSubject, opts...).ToFunc()
+}
+
+// ByBody orders the results by the body field.
+func ByBody(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldBody, opts...).ToFunc()
+}
+
+// ByBodyFormat orders the results by the body_format field.
+func ByBodyFormat(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldBodyFormat, opts...).ToFunc()
+}
+
+// ByRecipientsMode orders the results by the recipients_mode field.
+func ByRecipientsMode(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldRecipientsMode, opts...).ToFunc()
+}
+
+// ByStatus orders the results by the status field.
+func ByStatus(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldStatus, opts...).ToFunc()
+}
+
+// ByTotalCount orders the results by the total_count field.
+func ByTotalCount(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldTotalCount, opts...).ToFunc()
+}
+
+// BySuccessCount orders the results by the success_count field.
+func BySuccessCount(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldSuccessCount, opts...).ToFunc()
+}
+
+// ByFailedCount orders the results by the failed_count field.
+func ByFailedCount(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldFailedCount, opts...).ToFunc()
+}
+
+// ByErrorMessage orders the results by the error_message field.
+func ByErrorMessage(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldErrorMessage, opts...).ToFunc()
+}
+
+// ByCreatedBy orders the results by the created_by field.
+func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldCreatedBy, opts...).ToFunc()
+}
+
+// ByStartedAt orders the results by the started_at field.
+func ByStartedAt(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldStartedAt, opts...).ToFunc()
+}
+
+// ByFinishedAt orders the results by the finished_at field.
+func ByFinishedAt(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldFinishedAt, opts...).ToFunc()
+}
+
+// ByCreatedAt orders the results by the created_at field.
+func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
+}
+
+// ByUpdatedAt orders the results by the updated_at field.
+func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
+}
diff --git a/backend/ent/emailbroadcast/where.go b/backend/ent/emailbroadcast/where.go
new file mode 100644
index 00000000000..56ffa1a0bf7
--- /dev/null
+++ b/backend/ent/emailbroadcast/where.go
@@ -0,0 +1,900 @@
+// Code generated by ent, DO NOT EDIT.
+
+package emailbroadcast
+
+import (
+ "time"
+
+ "entgo.io/ent/dialect/sql"
+ "github.com/Wei-Shaw/sub2api/ent/predicate"
+)
+
+// ID filters vertices based on their ID field.
+func ID(id int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldID, id))
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldID, id))
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldID, id))
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldID, ids...))
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldID, ids...))
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldID, id))
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldID, id))
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldID, id))
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldID, id))
+}
+
+// Subject applies equality check predicate on the "subject" field. It's identical to SubjectEQ.
+func Subject(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldSubject, v))
+}
+
+// Body applies equality check predicate on the "body" field. It's identical to BodyEQ.
+func Body(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldBody, v))
+}
+
+// BodyFormat applies equality check predicate on the "body_format" field. It's identical to BodyFormatEQ.
+func BodyFormat(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldBodyFormat, v))
+}
+
+// RecipientsMode applies equality check predicate on the "recipients_mode" field. It's identical to RecipientsModeEQ.
+func RecipientsMode(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldRecipientsMode, v))
+}
+
+// Status applies equality check predicate on the "status" field. It's identical to StatusEQ.
+func Status(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldStatus, v))
+}
+
+// TotalCount applies equality check predicate on the "total_count" field. It's identical to TotalCountEQ.
+func TotalCount(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldTotalCount, v))
+}
+
+// SuccessCount applies equality check predicate on the "success_count" field. It's identical to SuccessCountEQ.
+func SuccessCount(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldSuccessCount, v))
+}
+
+// FailedCount applies equality check predicate on the "failed_count" field. It's identical to FailedCountEQ.
+func FailedCount(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldFailedCount, v))
+}
+
+// ErrorMessage applies equality check predicate on the "error_message" field. It's identical to ErrorMessageEQ.
+func ErrorMessage(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldErrorMessage, v))
+}
+
+// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ.
+func CreatedBy(v int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldCreatedBy, v))
+}
+
+// StartedAt applies equality check predicate on the "started_at" field. It's identical to StartedAtEQ.
+func StartedAt(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldStartedAt, v))
+}
+
+// FinishedAt applies equality check predicate on the "finished_at" field. It's identical to FinishedAtEQ.
+func FinishedAt(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldFinishedAt, v))
+}
+
+// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
+func CreatedAt(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
+func UpdatedAt(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// SubjectEQ applies the EQ predicate on the "subject" field.
+func SubjectEQ(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldSubject, v))
+}
+
+// SubjectNEQ applies the NEQ predicate on the "subject" field.
+func SubjectNEQ(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldSubject, v))
+}
+
+// SubjectIn applies the In predicate on the "subject" field.
+func SubjectIn(vs ...string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldSubject, vs...))
+}
+
+// SubjectNotIn applies the NotIn predicate on the "subject" field.
+func SubjectNotIn(vs ...string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldSubject, vs...))
+}
+
+// SubjectGT applies the GT predicate on the "subject" field.
+func SubjectGT(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldSubject, v))
+}
+
+// SubjectGTE applies the GTE predicate on the "subject" field.
+func SubjectGTE(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldSubject, v))
+}
+
+// SubjectLT applies the LT predicate on the "subject" field.
+func SubjectLT(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldSubject, v))
+}
+
+// SubjectLTE applies the LTE predicate on the "subject" field.
+func SubjectLTE(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldSubject, v))
+}
+
+// SubjectContains applies the Contains predicate on the "subject" field.
+func SubjectContains(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldContains(FieldSubject, v))
+}
+
+// SubjectHasPrefix applies the HasPrefix predicate on the "subject" field.
+func SubjectHasPrefix(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldHasPrefix(FieldSubject, v))
+}
+
+// SubjectHasSuffix applies the HasSuffix predicate on the "subject" field.
+func SubjectHasSuffix(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldHasSuffix(FieldSubject, v))
+}
+
+// SubjectEqualFold applies the EqualFold predicate on the "subject" field.
+func SubjectEqualFold(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEqualFold(FieldSubject, v))
+}
+
+// SubjectContainsFold applies the ContainsFold predicate on the "subject" field.
+func SubjectContainsFold(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldContainsFold(FieldSubject, v))
+}
+
+// BodyEQ applies the EQ predicate on the "body" field.
+func BodyEQ(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldBody, v))
+}
+
+// BodyNEQ applies the NEQ predicate on the "body" field.
+func BodyNEQ(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldBody, v))
+}
+
+// BodyIn applies the In predicate on the "body" field.
+func BodyIn(vs ...string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldBody, vs...))
+}
+
+// BodyNotIn applies the NotIn predicate on the "body" field.
+func BodyNotIn(vs ...string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldBody, vs...))
+}
+
+// BodyGT applies the GT predicate on the "body" field.
+func BodyGT(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldBody, v))
+}
+
+// BodyGTE applies the GTE predicate on the "body" field.
+func BodyGTE(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldBody, v))
+}
+
+// BodyLT applies the LT predicate on the "body" field.
+func BodyLT(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldBody, v))
+}
+
+// BodyLTE applies the LTE predicate on the "body" field.
+func BodyLTE(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldBody, v))
+}
+
+// BodyContains applies the Contains predicate on the "body" field.
+func BodyContains(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldContains(FieldBody, v))
+}
+
+// BodyHasPrefix applies the HasPrefix predicate on the "body" field.
+func BodyHasPrefix(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldHasPrefix(FieldBody, v))
+}
+
+// BodyHasSuffix applies the HasSuffix predicate on the "body" field.
+func BodyHasSuffix(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldHasSuffix(FieldBody, v))
+}
+
+// BodyEqualFold applies the EqualFold predicate on the "body" field.
+func BodyEqualFold(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEqualFold(FieldBody, v))
+}
+
+// BodyContainsFold applies the ContainsFold predicate on the "body" field.
+func BodyContainsFold(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldContainsFold(FieldBody, v))
+}
+
+// BodyFormatEQ applies the EQ predicate on the "body_format" field.
+func BodyFormatEQ(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldBodyFormat, v))
+}
+
+// BodyFormatNEQ applies the NEQ predicate on the "body_format" field.
+func BodyFormatNEQ(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldBodyFormat, v))
+}
+
+// BodyFormatIn applies the In predicate on the "body_format" field.
+func BodyFormatIn(vs ...string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldBodyFormat, vs...))
+}
+
+// BodyFormatNotIn applies the NotIn predicate on the "body_format" field.
+func BodyFormatNotIn(vs ...string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldBodyFormat, vs...))
+}
+
+// BodyFormatGT applies the GT predicate on the "body_format" field.
+func BodyFormatGT(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldBodyFormat, v))
+}
+
+// BodyFormatGTE applies the GTE predicate on the "body_format" field.
+func BodyFormatGTE(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldBodyFormat, v))
+}
+
+// BodyFormatLT applies the LT predicate on the "body_format" field.
+func BodyFormatLT(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldBodyFormat, v))
+}
+
+// BodyFormatLTE applies the LTE predicate on the "body_format" field.
+func BodyFormatLTE(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldBodyFormat, v))
+}
+
+// BodyFormatContains applies the Contains predicate on the "body_format" field.
+func BodyFormatContains(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldContains(FieldBodyFormat, v))
+}
+
+// BodyFormatHasPrefix applies the HasPrefix predicate on the "body_format" field.
+func BodyFormatHasPrefix(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldHasPrefix(FieldBodyFormat, v))
+}
+
+// BodyFormatHasSuffix applies the HasSuffix predicate on the "body_format" field.
+func BodyFormatHasSuffix(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldHasSuffix(FieldBodyFormat, v))
+}
+
+// BodyFormatEqualFold applies the EqualFold predicate on the "body_format" field.
+func BodyFormatEqualFold(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEqualFold(FieldBodyFormat, v))
+}
+
+// BodyFormatContainsFold applies the ContainsFold predicate on the "body_format" field.
+func BodyFormatContainsFold(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldContainsFold(FieldBodyFormat, v))
+}
+
+// RecipientsModeEQ applies the EQ predicate on the "recipients_mode" field.
+func RecipientsModeEQ(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldRecipientsMode, v))
+}
+
+// RecipientsModeNEQ applies the NEQ predicate on the "recipients_mode" field.
+func RecipientsModeNEQ(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldRecipientsMode, v))
+}
+
+// RecipientsModeIn applies the In predicate on the "recipients_mode" field.
+func RecipientsModeIn(vs ...string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldRecipientsMode, vs...))
+}
+
+// RecipientsModeNotIn applies the NotIn predicate on the "recipients_mode" field.
+func RecipientsModeNotIn(vs ...string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldRecipientsMode, vs...))
+}
+
+// RecipientsModeGT applies the GT predicate on the "recipients_mode" field.
+func RecipientsModeGT(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldRecipientsMode, v))
+}
+
+// RecipientsModeGTE applies the GTE predicate on the "recipients_mode" field.
+func RecipientsModeGTE(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldRecipientsMode, v))
+}
+
+// RecipientsModeLT applies the LT predicate on the "recipients_mode" field.
+func RecipientsModeLT(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldRecipientsMode, v))
+}
+
+// RecipientsModeLTE applies the LTE predicate on the "recipients_mode" field.
+func RecipientsModeLTE(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldRecipientsMode, v))
+}
+
+// RecipientsModeContains applies the Contains predicate on the "recipients_mode" field.
+func RecipientsModeContains(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldContains(FieldRecipientsMode, v))
+}
+
+// RecipientsModeHasPrefix applies the HasPrefix predicate on the "recipients_mode" field.
+func RecipientsModeHasPrefix(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldHasPrefix(FieldRecipientsMode, v))
+}
+
+// RecipientsModeHasSuffix applies the HasSuffix predicate on the "recipients_mode" field.
+func RecipientsModeHasSuffix(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldHasSuffix(FieldRecipientsMode, v))
+}
+
+// RecipientsModeEqualFold applies the EqualFold predicate on the "recipients_mode" field.
+func RecipientsModeEqualFold(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEqualFold(FieldRecipientsMode, v))
+}
+
+// RecipientsModeContainsFold applies the ContainsFold predicate on the "recipients_mode" field.
+func RecipientsModeContainsFold(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldContainsFold(FieldRecipientsMode, v))
+}
+
+// RecipientUserIdsIsNil applies the IsNil predicate on the "recipient_user_ids" field.
+func RecipientUserIdsIsNil() predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIsNull(FieldRecipientUserIds))
+}
+
+// RecipientUserIdsNotNil applies the NotNil predicate on the "recipient_user_ids" field.
+func RecipientUserIdsNotNil() predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotNull(FieldRecipientUserIds))
+}
+
+// StatusEQ applies the EQ predicate on the "status" field.
+func StatusEQ(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldStatus, v))
+}
+
+// StatusNEQ applies the NEQ predicate on the "status" field.
+func StatusNEQ(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldStatus, v))
+}
+
+// StatusIn applies the In predicate on the "status" field.
+func StatusIn(vs ...string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldStatus, vs...))
+}
+
+// StatusNotIn applies the NotIn predicate on the "status" field.
+func StatusNotIn(vs ...string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldStatus, vs...))
+}
+
+// StatusGT applies the GT predicate on the "status" field.
+func StatusGT(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldStatus, v))
+}
+
+// StatusGTE applies the GTE predicate on the "status" field.
+func StatusGTE(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldStatus, v))
+}
+
+// StatusLT applies the LT predicate on the "status" field.
+func StatusLT(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldStatus, v))
+}
+
+// StatusLTE applies the LTE predicate on the "status" field.
+func StatusLTE(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldStatus, v))
+}
+
+// StatusContains applies the Contains predicate on the "status" field.
+func StatusContains(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldContains(FieldStatus, v))
+}
+
+// StatusHasPrefix applies the HasPrefix predicate on the "status" field.
+func StatusHasPrefix(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldHasPrefix(FieldStatus, v))
+}
+
+// StatusHasSuffix applies the HasSuffix predicate on the "status" field.
+func StatusHasSuffix(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldHasSuffix(FieldStatus, v))
+}
+
+// StatusEqualFold applies the EqualFold predicate on the "status" field.
+func StatusEqualFold(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEqualFold(FieldStatus, v))
+}
+
+// StatusContainsFold applies the ContainsFold predicate on the "status" field.
+func StatusContainsFold(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldContainsFold(FieldStatus, v))
+}
+
+// TotalCountEQ applies the EQ predicate on the "total_count" field.
+func TotalCountEQ(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldTotalCount, v))
+}
+
+// TotalCountNEQ applies the NEQ predicate on the "total_count" field.
+func TotalCountNEQ(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldTotalCount, v))
+}
+
+// TotalCountIn applies the In predicate on the "total_count" field.
+func TotalCountIn(vs ...int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldTotalCount, vs...))
+}
+
+// TotalCountNotIn applies the NotIn predicate on the "total_count" field.
+func TotalCountNotIn(vs ...int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldTotalCount, vs...))
+}
+
+// TotalCountGT applies the GT predicate on the "total_count" field.
+func TotalCountGT(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldTotalCount, v))
+}
+
+// TotalCountGTE applies the GTE predicate on the "total_count" field.
+func TotalCountGTE(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldTotalCount, v))
+}
+
+// TotalCountLT applies the LT predicate on the "total_count" field.
+func TotalCountLT(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldTotalCount, v))
+}
+
+// TotalCountLTE applies the LTE predicate on the "total_count" field.
+func TotalCountLTE(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldTotalCount, v))
+}
+
+// SuccessCountEQ applies the EQ predicate on the "success_count" field.
+func SuccessCountEQ(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldSuccessCount, v))
+}
+
+// SuccessCountNEQ applies the NEQ predicate on the "success_count" field.
+func SuccessCountNEQ(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldSuccessCount, v))
+}
+
+// SuccessCountIn applies the In predicate on the "success_count" field.
+func SuccessCountIn(vs ...int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldSuccessCount, vs...))
+}
+
+// SuccessCountNotIn applies the NotIn predicate on the "success_count" field.
+func SuccessCountNotIn(vs ...int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldSuccessCount, vs...))
+}
+
+// SuccessCountGT applies the GT predicate on the "success_count" field.
+func SuccessCountGT(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldSuccessCount, v))
+}
+
+// SuccessCountGTE applies the GTE predicate on the "success_count" field.
+func SuccessCountGTE(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldSuccessCount, v))
+}
+
+// SuccessCountLT applies the LT predicate on the "success_count" field.
+func SuccessCountLT(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldSuccessCount, v))
+}
+
+// SuccessCountLTE applies the LTE predicate on the "success_count" field.
+func SuccessCountLTE(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldSuccessCount, v))
+}
+
+// FailedCountEQ applies the EQ predicate on the "failed_count" field.
+func FailedCountEQ(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldFailedCount, v))
+}
+
+// FailedCountNEQ applies the NEQ predicate on the "failed_count" field.
+func FailedCountNEQ(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldFailedCount, v))
+}
+
+// FailedCountIn applies the In predicate on the "failed_count" field.
+func FailedCountIn(vs ...int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldFailedCount, vs...))
+}
+
+// FailedCountNotIn applies the NotIn predicate on the "failed_count" field.
+func FailedCountNotIn(vs ...int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldFailedCount, vs...))
+}
+
+// FailedCountGT applies the GT predicate on the "failed_count" field.
+func FailedCountGT(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldFailedCount, v))
+}
+
+// FailedCountGTE applies the GTE predicate on the "failed_count" field.
+func FailedCountGTE(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldFailedCount, v))
+}
+
+// FailedCountLT applies the LT predicate on the "failed_count" field.
+func FailedCountLT(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldFailedCount, v))
+}
+
+// FailedCountLTE applies the LTE predicate on the "failed_count" field.
+func FailedCountLTE(v int) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldFailedCount, v))
+}
+
+// ErrorMessageEQ applies the EQ predicate on the "error_message" field.
+func ErrorMessageEQ(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldErrorMessage, v))
+}
+
+// ErrorMessageNEQ applies the NEQ predicate on the "error_message" field.
+func ErrorMessageNEQ(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldErrorMessage, v))
+}
+
+// ErrorMessageIn applies the In predicate on the "error_message" field.
+func ErrorMessageIn(vs ...string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldErrorMessage, vs...))
+}
+
+// ErrorMessageNotIn applies the NotIn predicate on the "error_message" field.
+func ErrorMessageNotIn(vs ...string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldErrorMessage, vs...))
+}
+
+// ErrorMessageGT applies the GT predicate on the "error_message" field.
+func ErrorMessageGT(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldErrorMessage, v))
+}
+
+// ErrorMessageGTE applies the GTE predicate on the "error_message" field.
+func ErrorMessageGTE(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldErrorMessage, v))
+}
+
+// ErrorMessageLT applies the LT predicate on the "error_message" field.
+func ErrorMessageLT(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldErrorMessage, v))
+}
+
+// ErrorMessageLTE applies the LTE predicate on the "error_message" field.
+func ErrorMessageLTE(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldErrorMessage, v))
+}
+
+// ErrorMessageContains applies the Contains predicate on the "error_message" field.
+func ErrorMessageContains(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldContains(FieldErrorMessage, v))
+}
+
+// ErrorMessageHasPrefix applies the HasPrefix predicate on the "error_message" field.
+func ErrorMessageHasPrefix(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldHasPrefix(FieldErrorMessage, v))
+}
+
+// ErrorMessageHasSuffix applies the HasSuffix predicate on the "error_message" field.
+func ErrorMessageHasSuffix(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldHasSuffix(FieldErrorMessage, v))
+}
+
+// ErrorMessageIsNil applies the IsNil predicate on the "error_message" field.
+func ErrorMessageIsNil() predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIsNull(FieldErrorMessage))
+}
+
+// ErrorMessageNotNil applies the NotNil predicate on the "error_message" field.
+func ErrorMessageNotNil() predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotNull(FieldErrorMessage))
+}
+
+// ErrorMessageEqualFold applies the EqualFold predicate on the "error_message" field.
+func ErrorMessageEqualFold(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEqualFold(FieldErrorMessage, v))
+}
+
+// ErrorMessageContainsFold applies the ContainsFold predicate on the "error_message" field.
+func ErrorMessageContainsFold(v string) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldContainsFold(FieldErrorMessage, v))
+}
+
+// CreatedByEQ applies the EQ predicate on the "created_by" field.
+func CreatedByEQ(v int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldCreatedBy, v))
+}
+
+// CreatedByNEQ applies the NEQ predicate on the "created_by" field.
+func CreatedByNEQ(v int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldCreatedBy, v))
+}
+
+// CreatedByIn applies the In predicate on the "created_by" field.
+func CreatedByIn(vs ...int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldCreatedBy, vs...))
+}
+
+// CreatedByNotIn applies the NotIn predicate on the "created_by" field.
+func CreatedByNotIn(vs ...int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldCreatedBy, vs...))
+}
+
+// CreatedByGT applies the GT predicate on the "created_by" field.
+func CreatedByGT(v int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldCreatedBy, v))
+}
+
+// CreatedByGTE applies the GTE predicate on the "created_by" field.
+func CreatedByGTE(v int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldCreatedBy, v))
+}
+
+// CreatedByLT applies the LT predicate on the "created_by" field.
+func CreatedByLT(v int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldCreatedBy, v))
+}
+
+// CreatedByLTE applies the LTE predicate on the "created_by" field.
+func CreatedByLTE(v int64) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldCreatedBy, v))
+}
+
+// CreatedByIsNil applies the IsNil predicate on the "created_by" field.
+func CreatedByIsNil() predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIsNull(FieldCreatedBy))
+}
+
+// CreatedByNotNil applies the NotNil predicate on the "created_by" field.
+func CreatedByNotNil() predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotNull(FieldCreatedBy))
+}
+
+// StartedAtEQ applies the EQ predicate on the "started_at" field.
+func StartedAtEQ(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldStartedAt, v))
+}
+
+// StartedAtNEQ applies the NEQ predicate on the "started_at" field.
+func StartedAtNEQ(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldStartedAt, v))
+}
+
+// StartedAtIn applies the In predicate on the "started_at" field.
+func StartedAtIn(vs ...time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldStartedAt, vs...))
+}
+
+// StartedAtNotIn applies the NotIn predicate on the "started_at" field.
+func StartedAtNotIn(vs ...time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldStartedAt, vs...))
+}
+
+// StartedAtGT applies the GT predicate on the "started_at" field.
+func StartedAtGT(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldStartedAt, v))
+}
+
+// StartedAtGTE applies the GTE predicate on the "started_at" field.
+func StartedAtGTE(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldStartedAt, v))
+}
+
+// StartedAtLT applies the LT predicate on the "started_at" field.
+func StartedAtLT(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldStartedAt, v))
+}
+
+// StartedAtLTE applies the LTE predicate on the "started_at" field.
+func StartedAtLTE(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldStartedAt, v))
+}
+
+// StartedAtIsNil applies the IsNil predicate on the "started_at" field.
+func StartedAtIsNil() predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIsNull(FieldStartedAt))
+}
+
+// StartedAtNotNil applies the NotNil predicate on the "started_at" field.
+func StartedAtNotNil() predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotNull(FieldStartedAt))
+}
+
+// FinishedAtEQ applies the EQ predicate on the "finished_at" field.
+func FinishedAtEQ(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldFinishedAt, v))
+}
+
+// FinishedAtNEQ applies the NEQ predicate on the "finished_at" field.
+func FinishedAtNEQ(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldFinishedAt, v))
+}
+
+// FinishedAtIn applies the In predicate on the "finished_at" field.
+func FinishedAtIn(vs ...time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldFinishedAt, vs...))
+}
+
+// FinishedAtNotIn applies the NotIn predicate on the "finished_at" field.
+func FinishedAtNotIn(vs ...time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldFinishedAt, vs...))
+}
+
+// FinishedAtGT applies the GT predicate on the "finished_at" field.
+func FinishedAtGT(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldFinishedAt, v))
+}
+
+// FinishedAtGTE applies the GTE predicate on the "finished_at" field.
+func FinishedAtGTE(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldFinishedAt, v))
+}
+
+// FinishedAtLT applies the LT predicate on the "finished_at" field.
+func FinishedAtLT(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldFinishedAt, v))
+}
+
+// FinishedAtLTE applies the LTE predicate on the "finished_at" field.
+func FinishedAtLTE(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldFinishedAt, v))
+}
+
+// FinishedAtIsNil applies the IsNil predicate on the "finished_at" field.
+func FinishedAtIsNil() predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIsNull(FieldFinishedAt))
+}
+
+// FinishedAtNotNil applies the NotNil predicate on the "finished_at" field.
+func FinishedAtNotNil() predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotNull(FieldFinishedAt))
+}
+
+// CreatedAtEQ applies the EQ predicate on the "created_at" field.
+func CreatedAtEQ(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
+func CreatedAtNEQ(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtIn applies the In predicate on the "created_at" field.
+func CreatedAtIn(vs ...time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
+func CreatedAtNotIn(vs ...time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtGT applies the GT predicate on the "created_at" field.
+func CreatedAtGT(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldCreatedAt, v))
+}
+
+// CreatedAtGTE applies the GTE predicate on the "created_at" field.
+func CreatedAtGTE(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldCreatedAt, v))
+}
+
+// CreatedAtLT applies the LT predicate on the "created_at" field.
+func CreatedAtLT(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldCreatedAt, v))
+}
+
+// CreatedAtLTE applies the LTE predicate on the "created_at" field.
+func CreatedAtLTE(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldCreatedAt, v))
+}
+
+// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
+func UpdatedAtEQ(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
+func UpdatedAtNEQ(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtIn applies the In predicate on the "updated_at" field.
+func UpdatedAtIn(vs ...time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
+func UpdatedAtNotIn(vs ...time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldNotIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtGT applies the GT predicate on the "updated_at" field.
+func UpdatedAtGT(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
+func UpdatedAtGTE(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldGTE(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLT applies the LT predicate on the "updated_at" field.
+func UpdatedAtLT(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
+func UpdatedAtLTE(v time.Time) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.FieldLTE(FieldUpdatedAt, v))
+}
+
+// And groups predicates with the AND operator between them.
+func And(predicates ...predicate.EmailBroadcast) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.AndPredicates(predicates...))
+}
+
+// Or groups predicates with the OR operator between them.
+func Or(predicates ...predicate.EmailBroadcast) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.OrPredicates(predicates...))
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.EmailBroadcast) predicate.EmailBroadcast {
+ return predicate.EmailBroadcast(sql.NotPredicates(p))
+}
diff --git a/backend/ent/emailbroadcast_create.go b/backend/ent/emailbroadcast_create.go
new file mode 100644
index 00000000000..c755561cf94
--- /dev/null
+++ b/backend/ent/emailbroadcast_create.go
@@ -0,0 +1,1508 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "time"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+ "github.com/Wei-Shaw/sub2api/ent/emailbroadcast"
+)
+
+// EmailBroadcastCreate is the builder for creating a EmailBroadcast entity.
+type EmailBroadcastCreate struct {
+ config
+ mutation *EmailBroadcastMutation
+ hooks []Hook
+ conflict []sql.ConflictOption
+}
+
+// SetSubject sets the "subject" field.
+func (_c *EmailBroadcastCreate) SetSubject(v string) *EmailBroadcastCreate {
+ _c.mutation.SetSubject(v)
+ return _c
+}
+
+// SetBody sets the "body" field.
+func (_c *EmailBroadcastCreate) SetBody(v string) *EmailBroadcastCreate {
+ _c.mutation.SetBody(v)
+ return _c
+}
+
+// SetBodyFormat sets the "body_format" field.
+func (_c *EmailBroadcastCreate) SetBodyFormat(v string) *EmailBroadcastCreate {
+ _c.mutation.SetBodyFormat(v)
+ return _c
+}
+
+// SetNillableBodyFormat sets the "body_format" field if the given value is not nil.
+func (_c *EmailBroadcastCreate) SetNillableBodyFormat(v *string) *EmailBroadcastCreate {
+ if v != nil {
+ _c.SetBodyFormat(*v)
+ }
+ return _c
+}
+
+// SetRecipientsMode sets the "recipients_mode" field.
+func (_c *EmailBroadcastCreate) SetRecipientsMode(v string) *EmailBroadcastCreate {
+ _c.mutation.SetRecipientsMode(v)
+ return _c
+}
+
+// SetNillableRecipientsMode sets the "recipients_mode" field if the given value is not nil.
+func (_c *EmailBroadcastCreate) SetNillableRecipientsMode(v *string) *EmailBroadcastCreate {
+ if v != nil {
+ _c.SetRecipientsMode(*v)
+ }
+ return _c
+}
+
+// SetRecipientUserIds sets the "recipient_user_ids" field.
+func (_c *EmailBroadcastCreate) SetRecipientUserIds(v []int64) *EmailBroadcastCreate {
+ _c.mutation.SetRecipientUserIds(v)
+ return _c
+}
+
+// SetStatus sets the "status" field.
+func (_c *EmailBroadcastCreate) SetStatus(v string) *EmailBroadcastCreate {
+ _c.mutation.SetStatus(v)
+ return _c
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (_c *EmailBroadcastCreate) SetNillableStatus(v *string) *EmailBroadcastCreate {
+ if v != nil {
+ _c.SetStatus(*v)
+ }
+ return _c
+}
+
+// SetTotalCount sets the "total_count" field.
+func (_c *EmailBroadcastCreate) SetTotalCount(v int) *EmailBroadcastCreate {
+ _c.mutation.SetTotalCount(v)
+ return _c
+}
+
+// SetNillableTotalCount sets the "total_count" field if the given value is not nil.
+func (_c *EmailBroadcastCreate) SetNillableTotalCount(v *int) *EmailBroadcastCreate {
+ if v != nil {
+ _c.SetTotalCount(*v)
+ }
+ return _c
+}
+
+// SetSuccessCount sets the "success_count" field.
+func (_c *EmailBroadcastCreate) SetSuccessCount(v int) *EmailBroadcastCreate {
+ _c.mutation.SetSuccessCount(v)
+ return _c
+}
+
+// SetNillableSuccessCount sets the "success_count" field if the given value is not nil.
+func (_c *EmailBroadcastCreate) SetNillableSuccessCount(v *int) *EmailBroadcastCreate {
+ if v != nil {
+ _c.SetSuccessCount(*v)
+ }
+ return _c
+}
+
+// SetFailedCount sets the "failed_count" field.
+func (_c *EmailBroadcastCreate) SetFailedCount(v int) *EmailBroadcastCreate {
+ _c.mutation.SetFailedCount(v)
+ return _c
+}
+
+// SetNillableFailedCount sets the "failed_count" field if the given value is not nil.
+func (_c *EmailBroadcastCreate) SetNillableFailedCount(v *int) *EmailBroadcastCreate {
+ if v != nil {
+ _c.SetFailedCount(*v)
+ }
+ return _c
+}
+
+// SetErrorMessage sets the "error_message" field.
+func (_c *EmailBroadcastCreate) SetErrorMessage(v string) *EmailBroadcastCreate {
+ _c.mutation.SetErrorMessage(v)
+ return _c
+}
+
+// SetNillableErrorMessage sets the "error_message" field if the given value is not nil.
+func (_c *EmailBroadcastCreate) SetNillableErrorMessage(v *string) *EmailBroadcastCreate {
+ if v != nil {
+ _c.SetErrorMessage(*v)
+ }
+ return _c
+}
+
+// SetCreatedBy sets the "created_by" field.
+func (_c *EmailBroadcastCreate) SetCreatedBy(v int64) *EmailBroadcastCreate {
+ _c.mutation.SetCreatedBy(v)
+ return _c
+}
+
+// SetNillableCreatedBy sets the "created_by" field if the given value is not nil.
+func (_c *EmailBroadcastCreate) SetNillableCreatedBy(v *int64) *EmailBroadcastCreate {
+ if v != nil {
+ _c.SetCreatedBy(*v)
+ }
+ return _c
+}
+
+// SetStartedAt sets the "started_at" field.
+func (_c *EmailBroadcastCreate) SetStartedAt(v time.Time) *EmailBroadcastCreate {
+ _c.mutation.SetStartedAt(v)
+ return _c
+}
+
+// SetNillableStartedAt sets the "started_at" field if the given value is not nil.
+func (_c *EmailBroadcastCreate) SetNillableStartedAt(v *time.Time) *EmailBroadcastCreate {
+ if v != nil {
+ _c.SetStartedAt(*v)
+ }
+ return _c
+}
+
+// SetFinishedAt sets the "finished_at" field.
+func (_c *EmailBroadcastCreate) SetFinishedAt(v time.Time) *EmailBroadcastCreate {
+ _c.mutation.SetFinishedAt(v)
+ return _c
+}
+
+// SetNillableFinishedAt sets the "finished_at" field if the given value is not nil.
+func (_c *EmailBroadcastCreate) SetNillableFinishedAt(v *time.Time) *EmailBroadcastCreate {
+ if v != nil {
+ _c.SetFinishedAt(*v)
+ }
+ return _c
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (_c *EmailBroadcastCreate) SetCreatedAt(v time.Time) *EmailBroadcastCreate {
+ _c.mutation.SetCreatedAt(v)
+ return _c
+}
+
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
+func (_c *EmailBroadcastCreate) SetNillableCreatedAt(v *time.Time) *EmailBroadcastCreate {
+ if v != nil {
+ _c.SetCreatedAt(*v)
+ }
+ return _c
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (_c *EmailBroadcastCreate) SetUpdatedAt(v time.Time) *EmailBroadcastCreate {
+ _c.mutation.SetUpdatedAt(v)
+ return _c
+}
+
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
+func (_c *EmailBroadcastCreate) SetNillableUpdatedAt(v *time.Time) *EmailBroadcastCreate {
+ if v != nil {
+ _c.SetUpdatedAt(*v)
+ }
+ return _c
+}
+
+// Mutation returns the EmailBroadcastMutation object of the builder.
+func (_c *EmailBroadcastCreate) Mutation() *EmailBroadcastMutation {
+ return _c.mutation
+}
+
+// Save creates the EmailBroadcast in the database.
+func (_c *EmailBroadcastCreate) Save(ctx context.Context) (*EmailBroadcast, error) {
+ _c.defaults()
+ return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (_c *EmailBroadcastCreate) SaveX(ctx context.Context) *EmailBroadcast {
+ v, err := _c.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Exec executes the query.
+func (_c *EmailBroadcastCreate) Exec(ctx context.Context) error {
+ _, err := _c.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_c *EmailBroadcastCreate) ExecX(ctx context.Context) {
+ if err := _c.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// defaults sets the default values of the builder before save.
+func (_c *EmailBroadcastCreate) defaults() {
+ if _, ok := _c.mutation.BodyFormat(); !ok {
+ v := emailbroadcast.DefaultBodyFormat
+ _c.mutation.SetBodyFormat(v)
+ }
+ if _, ok := _c.mutation.RecipientsMode(); !ok {
+ v := emailbroadcast.DefaultRecipientsMode
+ _c.mutation.SetRecipientsMode(v)
+ }
+ if _, ok := _c.mutation.Status(); !ok {
+ v := emailbroadcast.DefaultStatus
+ _c.mutation.SetStatus(v)
+ }
+ if _, ok := _c.mutation.TotalCount(); !ok {
+ v := emailbroadcast.DefaultTotalCount
+ _c.mutation.SetTotalCount(v)
+ }
+ if _, ok := _c.mutation.SuccessCount(); !ok {
+ v := emailbroadcast.DefaultSuccessCount
+ _c.mutation.SetSuccessCount(v)
+ }
+ if _, ok := _c.mutation.FailedCount(); !ok {
+ v := emailbroadcast.DefaultFailedCount
+ _c.mutation.SetFailedCount(v)
+ }
+ if _, ok := _c.mutation.CreatedAt(); !ok {
+ v := emailbroadcast.DefaultCreatedAt()
+ _c.mutation.SetCreatedAt(v)
+ }
+ if _, ok := _c.mutation.UpdatedAt(); !ok {
+ v := emailbroadcast.DefaultUpdatedAt()
+ _c.mutation.SetUpdatedAt(v)
+ }
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (_c *EmailBroadcastCreate) check() error {
+ if _, ok := _c.mutation.Subject(); !ok {
+ return &ValidationError{Name: "subject", err: errors.New(`ent: missing required field "EmailBroadcast.subject"`)}
+ }
+ if v, ok := _c.mutation.Subject(); ok {
+ if err := emailbroadcast.SubjectValidator(v); err != nil {
+ return &ValidationError{Name: "subject", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.subject": %w`, err)}
+ }
+ }
+ if _, ok := _c.mutation.Body(); !ok {
+ return &ValidationError{Name: "body", err: errors.New(`ent: missing required field "EmailBroadcast.body"`)}
+ }
+ if v, ok := _c.mutation.Body(); ok {
+ if err := emailbroadcast.BodyValidator(v); err != nil {
+ return &ValidationError{Name: "body", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.body": %w`, err)}
+ }
+ }
+ if _, ok := _c.mutation.BodyFormat(); !ok {
+ return &ValidationError{Name: "body_format", err: errors.New(`ent: missing required field "EmailBroadcast.body_format"`)}
+ }
+ if v, ok := _c.mutation.BodyFormat(); ok {
+ if err := emailbroadcast.BodyFormatValidator(v); err != nil {
+ return &ValidationError{Name: "body_format", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.body_format": %w`, err)}
+ }
+ }
+ if _, ok := _c.mutation.RecipientsMode(); !ok {
+ return &ValidationError{Name: "recipients_mode", err: errors.New(`ent: missing required field "EmailBroadcast.recipients_mode"`)}
+ }
+ if v, ok := _c.mutation.RecipientsMode(); ok {
+ if err := emailbroadcast.RecipientsModeValidator(v); err != nil {
+ return &ValidationError{Name: "recipients_mode", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.recipients_mode": %w`, err)}
+ }
+ }
+ if _, ok := _c.mutation.Status(); !ok {
+ return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "EmailBroadcast.status"`)}
+ }
+ if v, ok := _c.mutation.Status(); ok {
+ if err := emailbroadcast.StatusValidator(v); err != nil {
+ return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.status": %w`, err)}
+ }
+ }
+ if _, ok := _c.mutation.TotalCount(); !ok {
+ return &ValidationError{Name: "total_count", err: errors.New(`ent: missing required field "EmailBroadcast.total_count"`)}
+ }
+ if v, ok := _c.mutation.TotalCount(); ok {
+ if err := emailbroadcast.TotalCountValidator(v); err != nil {
+ return &ValidationError{Name: "total_count", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.total_count": %w`, err)}
+ }
+ }
+ if _, ok := _c.mutation.SuccessCount(); !ok {
+ return &ValidationError{Name: "success_count", err: errors.New(`ent: missing required field "EmailBroadcast.success_count"`)}
+ }
+ if v, ok := _c.mutation.SuccessCount(); ok {
+ if err := emailbroadcast.SuccessCountValidator(v); err != nil {
+ return &ValidationError{Name: "success_count", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.success_count": %w`, err)}
+ }
+ }
+ if _, ok := _c.mutation.FailedCount(); !ok {
+ return &ValidationError{Name: "failed_count", err: errors.New(`ent: missing required field "EmailBroadcast.failed_count"`)}
+ }
+ if v, ok := _c.mutation.FailedCount(); ok {
+ if err := emailbroadcast.FailedCountValidator(v); err != nil {
+ return &ValidationError{Name: "failed_count", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.failed_count": %w`, err)}
+ }
+ }
+ if _, ok := _c.mutation.CreatedAt(); !ok {
+ return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "EmailBroadcast.created_at"`)}
+ }
+ if _, ok := _c.mutation.UpdatedAt(); !ok {
+ return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "EmailBroadcast.updated_at"`)}
+ }
+ return nil
+}
+
+func (_c *EmailBroadcastCreate) sqlSave(ctx context.Context) (*EmailBroadcast, error) {
+ if err := _c.check(); err != nil {
+ return nil, err
+ }
+ _node, _spec := _c.createSpec()
+ if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil {
+ if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ return nil, err
+ }
+ id := _spec.ID.Value.(int64)
+ _node.ID = int64(id)
+ _c.mutation.id = &_node.ID
+ _c.mutation.done = true
+ return _node, nil
+}
+
+func (_c *EmailBroadcastCreate) createSpec() (*EmailBroadcast, *sqlgraph.CreateSpec) {
+ var (
+ _node = &EmailBroadcast{config: _c.config}
+ _spec = sqlgraph.NewCreateSpec(emailbroadcast.Table, sqlgraph.NewFieldSpec(emailbroadcast.FieldID, field.TypeInt64))
+ )
+ _spec.OnConflict = _c.conflict
+ if value, ok := _c.mutation.Subject(); ok {
+ _spec.SetField(emailbroadcast.FieldSubject, field.TypeString, value)
+ _node.Subject = value
+ }
+ if value, ok := _c.mutation.Body(); ok {
+ _spec.SetField(emailbroadcast.FieldBody, field.TypeString, value)
+ _node.Body = value
+ }
+ if value, ok := _c.mutation.BodyFormat(); ok {
+ _spec.SetField(emailbroadcast.FieldBodyFormat, field.TypeString, value)
+ _node.BodyFormat = value
+ }
+ if value, ok := _c.mutation.RecipientsMode(); ok {
+ _spec.SetField(emailbroadcast.FieldRecipientsMode, field.TypeString, value)
+ _node.RecipientsMode = value
+ }
+ if value, ok := _c.mutation.RecipientUserIds(); ok {
+ _spec.SetField(emailbroadcast.FieldRecipientUserIds, field.TypeJSON, value)
+ _node.RecipientUserIds = value
+ }
+ if value, ok := _c.mutation.Status(); ok {
+ _spec.SetField(emailbroadcast.FieldStatus, field.TypeString, value)
+ _node.Status = value
+ }
+ if value, ok := _c.mutation.TotalCount(); ok {
+ _spec.SetField(emailbroadcast.FieldTotalCount, field.TypeInt, value)
+ _node.TotalCount = value
+ }
+ if value, ok := _c.mutation.SuccessCount(); ok {
+ _spec.SetField(emailbroadcast.FieldSuccessCount, field.TypeInt, value)
+ _node.SuccessCount = value
+ }
+ if value, ok := _c.mutation.FailedCount(); ok {
+ _spec.SetField(emailbroadcast.FieldFailedCount, field.TypeInt, value)
+ _node.FailedCount = value
+ }
+ if value, ok := _c.mutation.ErrorMessage(); ok {
+ _spec.SetField(emailbroadcast.FieldErrorMessage, field.TypeString, value)
+ _node.ErrorMessage = &value
+ }
+ if value, ok := _c.mutation.CreatedBy(); ok {
+ _spec.SetField(emailbroadcast.FieldCreatedBy, field.TypeInt64, value)
+ _node.CreatedBy = &value
+ }
+ if value, ok := _c.mutation.StartedAt(); ok {
+ _spec.SetField(emailbroadcast.FieldStartedAt, field.TypeTime, value)
+ _node.StartedAt = &value
+ }
+ if value, ok := _c.mutation.FinishedAt(); ok {
+ _spec.SetField(emailbroadcast.FieldFinishedAt, field.TypeTime, value)
+ _node.FinishedAt = &value
+ }
+ if value, ok := _c.mutation.CreatedAt(); ok {
+ _spec.SetField(emailbroadcast.FieldCreatedAt, field.TypeTime, value)
+ _node.CreatedAt = value
+ }
+ if value, ok := _c.mutation.UpdatedAt(); ok {
+ _spec.SetField(emailbroadcast.FieldUpdatedAt, field.TypeTime, value)
+ _node.UpdatedAt = value
+ }
+ return _node, _spec
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+// client.EmailBroadcast.Create().
+// SetSubject(v).
+// OnConflict(
+// // Update the row with the new values
+// // the was proposed for insertion.
+// sql.ResolveWithNewValues(),
+// ).
+// // Override some of the fields with custom
+// // update values.
+// Update(func(u *ent.EmailBroadcastUpsert) {
+// SetSubject(v+v).
+// }).
+// Exec(ctx)
+func (_c *EmailBroadcastCreate) OnConflict(opts ...sql.ConflictOption) *EmailBroadcastUpsertOne {
+ _c.conflict = opts
+ return &EmailBroadcastUpsertOne{
+ create: _c,
+ }
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+// client.EmailBroadcast.Create().
+// OnConflict(sql.ConflictColumns(columns...)).
+// Exec(ctx)
+func (_c *EmailBroadcastCreate) OnConflictColumns(columns ...string) *EmailBroadcastUpsertOne {
+ _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...))
+ return &EmailBroadcastUpsertOne{
+ create: _c,
+ }
+}
+
+type (
+ // EmailBroadcastUpsertOne is the builder for "upsert"-ing
+ // one EmailBroadcast node.
+ EmailBroadcastUpsertOne struct {
+ create *EmailBroadcastCreate
+ }
+
+ // EmailBroadcastUpsert is the "OnConflict" setter.
+ EmailBroadcastUpsert struct {
+ *sql.UpdateSet
+ }
+)
+
+// SetSubject sets the "subject" field.
+func (u *EmailBroadcastUpsert) SetSubject(v string) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldSubject, v)
+ return u
+}
+
+// UpdateSubject sets the "subject" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateSubject() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldSubject)
+ return u
+}
+
+// SetBody sets the "body" field.
+func (u *EmailBroadcastUpsert) SetBody(v string) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldBody, v)
+ return u
+}
+
+// UpdateBody sets the "body" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateBody() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldBody)
+ return u
+}
+
+// SetBodyFormat sets the "body_format" field.
+func (u *EmailBroadcastUpsert) SetBodyFormat(v string) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldBodyFormat, v)
+ return u
+}
+
+// UpdateBodyFormat sets the "body_format" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateBodyFormat() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldBodyFormat)
+ return u
+}
+
+// SetRecipientsMode sets the "recipients_mode" field.
+func (u *EmailBroadcastUpsert) SetRecipientsMode(v string) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldRecipientsMode, v)
+ return u
+}
+
+// UpdateRecipientsMode sets the "recipients_mode" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateRecipientsMode() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldRecipientsMode)
+ return u
+}
+
+// SetRecipientUserIds sets the "recipient_user_ids" field.
+func (u *EmailBroadcastUpsert) SetRecipientUserIds(v []int64) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldRecipientUserIds, v)
+ return u
+}
+
+// UpdateRecipientUserIds sets the "recipient_user_ids" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateRecipientUserIds() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldRecipientUserIds)
+ return u
+}
+
+// ClearRecipientUserIds clears the value of the "recipient_user_ids" field.
+func (u *EmailBroadcastUpsert) ClearRecipientUserIds() *EmailBroadcastUpsert {
+ u.SetNull(emailbroadcast.FieldRecipientUserIds)
+ return u
+}
+
+// SetStatus sets the "status" field.
+func (u *EmailBroadcastUpsert) SetStatus(v string) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldStatus, v)
+ return u
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateStatus() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldStatus)
+ return u
+}
+
+// SetTotalCount sets the "total_count" field.
+func (u *EmailBroadcastUpsert) SetTotalCount(v int) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldTotalCount, v)
+ return u
+}
+
+// UpdateTotalCount sets the "total_count" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateTotalCount() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldTotalCount)
+ return u
+}
+
+// AddTotalCount adds v to the "total_count" field.
+func (u *EmailBroadcastUpsert) AddTotalCount(v int) *EmailBroadcastUpsert {
+ u.Add(emailbroadcast.FieldTotalCount, v)
+ return u
+}
+
+// SetSuccessCount sets the "success_count" field.
+func (u *EmailBroadcastUpsert) SetSuccessCount(v int) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldSuccessCount, v)
+ return u
+}
+
+// UpdateSuccessCount sets the "success_count" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateSuccessCount() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldSuccessCount)
+ return u
+}
+
+// AddSuccessCount adds v to the "success_count" field.
+func (u *EmailBroadcastUpsert) AddSuccessCount(v int) *EmailBroadcastUpsert {
+ u.Add(emailbroadcast.FieldSuccessCount, v)
+ return u
+}
+
+// SetFailedCount sets the "failed_count" field.
+func (u *EmailBroadcastUpsert) SetFailedCount(v int) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldFailedCount, v)
+ return u
+}
+
+// UpdateFailedCount sets the "failed_count" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateFailedCount() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldFailedCount)
+ return u
+}
+
+// AddFailedCount adds v to the "failed_count" field.
+func (u *EmailBroadcastUpsert) AddFailedCount(v int) *EmailBroadcastUpsert {
+ u.Add(emailbroadcast.FieldFailedCount, v)
+ return u
+}
+
+// SetErrorMessage sets the "error_message" field.
+func (u *EmailBroadcastUpsert) SetErrorMessage(v string) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldErrorMessage, v)
+ return u
+}
+
+// UpdateErrorMessage sets the "error_message" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateErrorMessage() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldErrorMessage)
+ return u
+}
+
+// ClearErrorMessage clears the value of the "error_message" field.
+func (u *EmailBroadcastUpsert) ClearErrorMessage() *EmailBroadcastUpsert {
+ u.SetNull(emailbroadcast.FieldErrorMessage)
+ return u
+}
+
+// SetCreatedBy sets the "created_by" field.
+func (u *EmailBroadcastUpsert) SetCreatedBy(v int64) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldCreatedBy, v)
+ return u
+}
+
+// UpdateCreatedBy sets the "created_by" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateCreatedBy() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldCreatedBy)
+ return u
+}
+
+// AddCreatedBy adds v to the "created_by" field.
+func (u *EmailBroadcastUpsert) AddCreatedBy(v int64) *EmailBroadcastUpsert {
+ u.Add(emailbroadcast.FieldCreatedBy, v)
+ return u
+}
+
+// ClearCreatedBy clears the value of the "created_by" field.
+func (u *EmailBroadcastUpsert) ClearCreatedBy() *EmailBroadcastUpsert {
+ u.SetNull(emailbroadcast.FieldCreatedBy)
+ return u
+}
+
+// SetStartedAt sets the "started_at" field.
+func (u *EmailBroadcastUpsert) SetStartedAt(v time.Time) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldStartedAt, v)
+ return u
+}
+
+// UpdateStartedAt sets the "started_at" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateStartedAt() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldStartedAt)
+ return u
+}
+
+// ClearStartedAt clears the value of the "started_at" field.
+func (u *EmailBroadcastUpsert) ClearStartedAt() *EmailBroadcastUpsert {
+ u.SetNull(emailbroadcast.FieldStartedAt)
+ return u
+}
+
+// SetFinishedAt sets the "finished_at" field.
+func (u *EmailBroadcastUpsert) SetFinishedAt(v time.Time) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldFinishedAt, v)
+ return u
+}
+
+// UpdateFinishedAt sets the "finished_at" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateFinishedAt() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldFinishedAt)
+ return u
+}
+
+// ClearFinishedAt clears the value of the "finished_at" field.
+func (u *EmailBroadcastUpsert) ClearFinishedAt() *EmailBroadcastUpsert {
+ u.SetNull(emailbroadcast.FieldFinishedAt)
+ return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *EmailBroadcastUpsert) SetUpdatedAt(v time.Time) *EmailBroadcastUpsert {
+ u.Set(emailbroadcast.FieldUpdatedAt, v)
+ return u
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *EmailBroadcastUpsert) UpdateUpdatedAt() *EmailBroadcastUpsert {
+ u.SetExcluded(emailbroadcast.FieldUpdatedAt)
+ return u
+}
+
+// UpdateNewValues updates the mutable fields using the new values that were set on create.
+// Using this option is equivalent to using:
+//
+// client.EmailBroadcast.Create().
+// OnConflict(
+// sql.ResolveWithNewValues(),
+// ).
+// Exec(ctx)
+func (u *EmailBroadcastUpsertOne) UpdateNewValues() *EmailBroadcastUpsertOne {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
+ u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
+ if _, exists := u.create.mutation.CreatedAt(); exists {
+ s.SetIgnore(emailbroadcast.FieldCreatedAt)
+ }
+ }))
+ return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+// client.EmailBroadcast.Create().
+// OnConflict(sql.ResolveWithIgnore()).
+// Exec(ctx)
+func (u *EmailBroadcastUpsertOne) Ignore() *EmailBroadcastUpsertOne {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
+ return u
+}
+
+// DoNothing configures the conflict_action to `DO NOTHING`.
+// Supported only by SQLite and PostgreSQL.
+func (u *EmailBroadcastUpsertOne) DoNothing() *EmailBroadcastUpsertOne {
+ u.create.conflict = append(u.create.conflict, sql.DoNothing())
+ return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the EmailBroadcastCreate.OnConflict
+// documentation for more info.
+func (u *EmailBroadcastUpsertOne) Update(set func(*EmailBroadcastUpsert)) *EmailBroadcastUpsertOne {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+ set(&EmailBroadcastUpsert{UpdateSet: update})
+ }))
+ return u
+}
+
+// SetSubject sets the "subject" field.
+func (u *EmailBroadcastUpsertOne) SetSubject(v string) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetSubject(v)
+ })
+}
+
+// UpdateSubject sets the "subject" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateSubject() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateSubject()
+ })
+}
+
+// SetBody sets the "body" field.
+func (u *EmailBroadcastUpsertOne) SetBody(v string) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetBody(v)
+ })
+}
+
+// UpdateBody sets the "body" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateBody() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateBody()
+ })
+}
+
+// SetBodyFormat sets the "body_format" field.
+func (u *EmailBroadcastUpsertOne) SetBodyFormat(v string) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetBodyFormat(v)
+ })
+}
+
+// UpdateBodyFormat sets the "body_format" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateBodyFormat() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateBodyFormat()
+ })
+}
+
+// SetRecipientsMode sets the "recipients_mode" field.
+func (u *EmailBroadcastUpsertOne) SetRecipientsMode(v string) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetRecipientsMode(v)
+ })
+}
+
+// UpdateRecipientsMode sets the "recipients_mode" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateRecipientsMode() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateRecipientsMode()
+ })
+}
+
+// SetRecipientUserIds sets the "recipient_user_ids" field.
+func (u *EmailBroadcastUpsertOne) SetRecipientUserIds(v []int64) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetRecipientUserIds(v)
+ })
+}
+
+// UpdateRecipientUserIds sets the "recipient_user_ids" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateRecipientUserIds() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateRecipientUserIds()
+ })
+}
+
+// ClearRecipientUserIds clears the value of the "recipient_user_ids" field.
+func (u *EmailBroadcastUpsertOne) ClearRecipientUserIds() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.ClearRecipientUserIds()
+ })
+}
+
+// SetStatus sets the "status" field.
+func (u *EmailBroadcastUpsertOne) SetStatus(v string) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetStatus(v)
+ })
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateStatus() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateStatus()
+ })
+}
+
+// SetTotalCount sets the "total_count" field.
+func (u *EmailBroadcastUpsertOne) SetTotalCount(v int) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetTotalCount(v)
+ })
+}
+
+// AddTotalCount adds v to the "total_count" field.
+func (u *EmailBroadcastUpsertOne) AddTotalCount(v int) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.AddTotalCount(v)
+ })
+}
+
+// UpdateTotalCount sets the "total_count" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateTotalCount() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateTotalCount()
+ })
+}
+
+// SetSuccessCount sets the "success_count" field.
+func (u *EmailBroadcastUpsertOne) SetSuccessCount(v int) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetSuccessCount(v)
+ })
+}
+
+// AddSuccessCount adds v to the "success_count" field.
+func (u *EmailBroadcastUpsertOne) AddSuccessCount(v int) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.AddSuccessCount(v)
+ })
+}
+
+// UpdateSuccessCount sets the "success_count" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateSuccessCount() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateSuccessCount()
+ })
+}
+
+// SetFailedCount sets the "failed_count" field.
+func (u *EmailBroadcastUpsertOne) SetFailedCount(v int) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetFailedCount(v)
+ })
+}
+
+// AddFailedCount adds v to the "failed_count" field.
+func (u *EmailBroadcastUpsertOne) AddFailedCount(v int) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.AddFailedCount(v)
+ })
+}
+
+// UpdateFailedCount sets the "failed_count" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateFailedCount() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateFailedCount()
+ })
+}
+
+// SetErrorMessage sets the "error_message" field.
+func (u *EmailBroadcastUpsertOne) SetErrorMessage(v string) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetErrorMessage(v)
+ })
+}
+
+// UpdateErrorMessage sets the "error_message" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateErrorMessage() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateErrorMessage()
+ })
+}
+
+// ClearErrorMessage clears the value of the "error_message" field.
+func (u *EmailBroadcastUpsertOne) ClearErrorMessage() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.ClearErrorMessage()
+ })
+}
+
+// SetCreatedBy sets the "created_by" field.
+func (u *EmailBroadcastUpsertOne) SetCreatedBy(v int64) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetCreatedBy(v)
+ })
+}
+
+// AddCreatedBy adds v to the "created_by" field.
+func (u *EmailBroadcastUpsertOne) AddCreatedBy(v int64) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.AddCreatedBy(v)
+ })
+}
+
+// UpdateCreatedBy sets the "created_by" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateCreatedBy() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateCreatedBy()
+ })
+}
+
+// ClearCreatedBy clears the value of the "created_by" field.
+func (u *EmailBroadcastUpsertOne) ClearCreatedBy() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.ClearCreatedBy()
+ })
+}
+
+// SetStartedAt sets the "started_at" field.
+func (u *EmailBroadcastUpsertOne) SetStartedAt(v time.Time) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetStartedAt(v)
+ })
+}
+
+// UpdateStartedAt sets the "started_at" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateStartedAt() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateStartedAt()
+ })
+}
+
+// ClearStartedAt clears the value of the "started_at" field.
+func (u *EmailBroadcastUpsertOne) ClearStartedAt() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.ClearStartedAt()
+ })
+}
+
+// SetFinishedAt sets the "finished_at" field.
+func (u *EmailBroadcastUpsertOne) SetFinishedAt(v time.Time) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetFinishedAt(v)
+ })
+}
+
+// UpdateFinishedAt sets the "finished_at" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateFinishedAt() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateFinishedAt()
+ })
+}
+
+// ClearFinishedAt clears the value of the "finished_at" field.
+func (u *EmailBroadcastUpsertOne) ClearFinishedAt() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.ClearFinishedAt()
+ })
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *EmailBroadcastUpsertOne) SetUpdatedAt(v time.Time) *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetUpdatedAt(v)
+ })
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertOne) UpdateUpdatedAt() *EmailBroadcastUpsertOne {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateUpdatedAt()
+ })
+}
+
+// Exec executes the query.
+func (u *EmailBroadcastUpsertOne) Exec(ctx context.Context) error {
+ if len(u.create.conflict) == 0 {
+ return errors.New("ent: missing options for EmailBroadcastCreate.OnConflict")
+ }
+ return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *EmailBroadcastUpsertOne) ExecX(ctx context.Context) {
+ if err := u.create.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// Exec executes the UPSERT query and returns the inserted/updated ID.
+func (u *EmailBroadcastUpsertOne) ID(ctx context.Context) (id int64, err error) {
+ node, err := u.create.Save(ctx)
+ if err != nil {
+ return id, err
+ }
+ return node.ID, nil
+}
+
+// IDX is like ID, but panics if an error occurs.
+func (u *EmailBroadcastUpsertOne) IDX(ctx context.Context) int64 {
+ id, err := u.ID(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return id
+}
+
+// EmailBroadcastCreateBulk is the builder for creating many EmailBroadcast entities in bulk.
+type EmailBroadcastCreateBulk struct {
+ config
+ err error
+ builders []*EmailBroadcastCreate
+ conflict []sql.ConflictOption
+}
+
+// Save creates the EmailBroadcast entities in the database.
+func (_c *EmailBroadcastCreateBulk) Save(ctx context.Context) ([]*EmailBroadcast, error) {
+ if _c.err != nil {
+ return nil, _c.err
+ }
+ specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
+ nodes := make([]*EmailBroadcast, len(_c.builders))
+ mutators := make([]Mutator, len(_c.builders))
+ for i := range _c.builders {
+ func(i int, root context.Context) {
+ builder := _c.builders[i]
+ builder.defaults()
+ var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+ mutation, ok := m.(*EmailBroadcastMutation)
+ if !ok {
+ return nil, fmt.Errorf("unexpected mutation type %T", m)
+ }
+ if err := builder.check(); err != nil {
+ return nil, err
+ }
+ builder.mutation = mutation
+ var err error
+ nodes[i], specs[i] = builder.createSpec()
+ if i < len(mutators)-1 {
+ _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
+ } else {
+ spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
+ spec.OnConflict = _c.conflict
+ // Invoke the actual operation on the latest mutation in the chain.
+ if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
+ if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ mutation.id = &nodes[i].ID
+ if specs[i].ID.Value != nil {
+ id := specs[i].ID.Value.(int64)
+ nodes[i].ID = int64(id)
+ }
+ mutation.done = true
+ return nodes[i], nil
+ })
+ for i := len(builder.hooks) - 1; i >= 0; i-- {
+ mut = builder.hooks[i](mut)
+ }
+ mutators[i] = mut
+ }(i, ctx)
+ }
+ if len(mutators) > 0 {
+ if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
+ return nil, err
+ }
+ }
+ return nodes, nil
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (_c *EmailBroadcastCreateBulk) SaveX(ctx context.Context) []*EmailBroadcast {
+ v, err := _c.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Exec executes the query.
+func (_c *EmailBroadcastCreateBulk) Exec(ctx context.Context) error {
+ _, err := _c.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_c *EmailBroadcastCreateBulk) ExecX(ctx context.Context) {
+ if err := _c.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+// client.EmailBroadcast.CreateBulk(builders...).
+// OnConflict(
+// // Update the row with the new values
+// // the was proposed for insertion.
+// sql.ResolveWithNewValues(),
+// ).
+// // Override some of the fields with custom
+// // update values.
+// Update(func(u *ent.EmailBroadcastUpsert) {
+// SetSubject(v+v).
+// }).
+// Exec(ctx)
+func (_c *EmailBroadcastCreateBulk) OnConflict(opts ...sql.ConflictOption) *EmailBroadcastUpsertBulk {
+ _c.conflict = opts
+ return &EmailBroadcastUpsertBulk{
+ create: _c,
+ }
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+// client.EmailBroadcast.Create().
+// OnConflict(sql.ConflictColumns(columns...)).
+// Exec(ctx)
+func (_c *EmailBroadcastCreateBulk) OnConflictColumns(columns ...string) *EmailBroadcastUpsertBulk {
+ _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...))
+ return &EmailBroadcastUpsertBulk{
+ create: _c,
+ }
+}
+
+// EmailBroadcastUpsertBulk is the builder for "upsert"-ing
+// a bulk of EmailBroadcast nodes.
+type EmailBroadcastUpsertBulk struct {
+ create *EmailBroadcastCreateBulk
+}
+
+// UpdateNewValues updates the mutable fields using the new values that
+// were set on create. Using this option is equivalent to using:
+//
+// client.EmailBroadcast.Create().
+// OnConflict(
+// sql.ResolveWithNewValues(),
+// ).
+// Exec(ctx)
+func (u *EmailBroadcastUpsertBulk) UpdateNewValues() *EmailBroadcastUpsertBulk {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
+ u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
+ for _, b := range u.create.builders {
+ if _, exists := b.mutation.CreatedAt(); exists {
+ s.SetIgnore(emailbroadcast.FieldCreatedAt)
+ }
+ }
+ }))
+ return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+// client.EmailBroadcast.Create().
+// OnConflict(sql.ResolveWithIgnore()).
+// Exec(ctx)
+func (u *EmailBroadcastUpsertBulk) Ignore() *EmailBroadcastUpsertBulk {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
+ return u
+}
+
+// DoNothing configures the conflict_action to `DO NOTHING`.
+// Supported only by SQLite and PostgreSQL.
+func (u *EmailBroadcastUpsertBulk) DoNothing() *EmailBroadcastUpsertBulk {
+ u.create.conflict = append(u.create.conflict, sql.DoNothing())
+ return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the EmailBroadcastCreateBulk.OnConflict
+// documentation for more info.
+func (u *EmailBroadcastUpsertBulk) Update(set func(*EmailBroadcastUpsert)) *EmailBroadcastUpsertBulk {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+ set(&EmailBroadcastUpsert{UpdateSet: update})
+ }))
+ return u
+}
+
+// SetSubject sets the "subject" field.
+func (u *EmailBroadcastUpsertBulk) SetSubject(v string) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetSubject(v)
+ })
+}
+
+// UpdateSubject sets the "subject" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateSubject() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateSubject()
+ })
+}
+
+// SetBody sets the "body" field.
+func (u *EmailBroadcastUpsertBulk) SetBody(v string) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetBody(v)
+ })
+}
+
+// UpdateBody sets the "body" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateBody() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateBody()
+ })
+}
+
+// SetBodyFormat sets the "body_format" field.
+func (u *EmailBroadcastUpsertBulk) SetBodyFormat(v string) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetBodyFormat(v)
+ })
+}
+
+// UpdateBodyFormat sets the "body_format" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateBodyFormat() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateBodyFormat()
+ })
+}
+
+// SetRecipientsMode sets the "recipients_mode" field.
+func (u *EmailBroadcastUpsertBulk) SetRecipientsMode(v string) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetRecipientsMode(v)
+ })
+}
+
+// UpdateRecipientsMode sets the "recipients_mode" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateRecipientsMode() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateRecipientsMode()
+ })
+}
+
+// SetRecipientUserIds sets the "recipient_user_ids" field.
+func (u *EmailBroadcastUpsertBulk) SetRecipientUserIds(v []int64) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetRecipientUserIds(v)
+ })
+}
+
+// UpdateRecipientUserIds sets the "recipient_user_ids" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateRecipientUserIds() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateRecipientUserIds()
+ })
+}
+
+// ClearRecipientUserIds clears the value of the "recipient_user_ids" field.
+func (u *EmailBroadcastUpsertBulk) ClearRecipientUserIds() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.ClearRecipientUserIds()
+ })
+}
+
+// SetStatus sets the "status" field.
+func (u *EmailBroadcastUpsertBulk) SetStatus(v string) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetStatus(v)
+ })
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateStatus() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateStatus()
+ })
+}
+
+// SetTotalCount sets the "total_count" field.
+func (u *EmailBroadcastUpsertBulk) SetTotalCount(v int) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetTotalCount(v)
+ })
+}
+
+// AddTotalCount adds v to the "total_count" field.
+func (u *EmailBroadcastUpsertBulk) AddTotalCount(v int) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.AddTotalCount(v)
+ })
+}
+
+// UpdateTotalCount sets the "total_count" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateTotalCount() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateTotalCount()
+ })
+}
+
+// SetSuccessCount sets the "success_count" field.
+func (u *EmailBroadcastUpsertBulk) SetSuccessCount(v int) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetSuccessCount(v)
+ })
+}
+
+// AddSuccessCount adds v to the "success_count" field.
+func (u *EmailBroadcastUpsertBulk) AddSuccessCount(v int) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.AddSuccessCount(v)
+ })
+}
+
+// UpdateSuccessCount sets the "success_count" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateSuccessCount() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateSuccessCount()
+ })
+}
+
+// SetFailedCount sets the "failed_count" field.
+func (u *EmailBroadcastUpsertBulk) SetFailedCount(v int) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetFailedCount(v)
+ })
+}
+
+// AddFailedCount adds v to the "failed_count" field.
+func (u *EmailBroadcastUpsertBulk) AddFailedCount(v int) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.AddFailedCount(v)
+ })
+}
+
+// UpdateFailedCount sets the "failed_count" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateFailedCount() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateFailedCount()
+ })
+}
+
+// SetErrorMessage sets the "error_message" field.
+func (u *EmailBroadcastUpsertBulk) SetErrorMessage(v string) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetErrorMessage(v)
+ })
+}
+
+// UpdateErrorMessage sets the "error_message" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateErrorMessage() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateErrorMessage()
+ })
+}
+
+// ClearErrorMessage clears the value of the "error_message" field.
+func (u *EmailBroadcastUpsertBulk) ClearErrorMessage() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.ClearErrorMessage()
+ })
+}
+
+// SetCreatedBy sets the "created_by" field.
+func (u *EmailBroadcastUpsertBulk) SetCreatedBy(v int64) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetCreatedBy(v)
+ })
+}
+
+// AddCreatedBy adds v to the "created_by" field.
+func (u *EmailBroadcastUpsertBulk) AddCreatedBy(v int64) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.AddCreatedBy(v)
+ })
+}
+
+// UpdateCreatedBy sets the "created_by" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateCreatedBy() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateCreatedBy()
+ })
+}
+
+// ClearCreatedBy clears the value of the "created_by" field.
+func (u *EmailBroadcastUpsertBulk) ClearCreatedBy() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.ClearCreatedBy()
+ })
+}
+
+// SetStartedAt sets the "started_at" field.
+func (u *EmailBroadcastUpsertBulk) SetStartedAt(v time.Time) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetStartedAt(v)
+ })
+}
+
+// UpdateStartedAt sets the "started_at" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateStartedAt() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateStartedAt()
+ })
+}
+
+// ClearStartedAt clears the value of the "started_at" field.
+func (u *EmailBroadcastUpsertBulk) ClearStartedAt() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.ClearStartedAt()
+ })
+}
+
+// SetFinishedAt sets the "finished_at" field.
+func (u *EmailBroadcastUpsertBulk) SetFinishedAt(v time.Time) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetFinishedAt(v)
+ })
+}
+
+// UpdateFinishedAt sets the "finished_at" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateFinishedAt() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateFinishedAt()
+ })
+}
+
+// ClearFinishedAt clears the value of the "finished_at" field.
+func (u *EmailBroadcastUpsertBulk) ClearFinishedAt() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.ClearFinishedAt()
+ })
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *EmailBroadcastUpsertBulk) SetUpdatedAt(v time.Time) *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.SetUpdatedAt(v)
+ })
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *EmailBroadcastUpsertBulk) UpdateUpdatedAt() *EmailBroadcastUpsertBulk {
+ return u.Update(func(s *EmailBroadcastUpsert) {
+ s.UpdateUpdatedAt()
+ })
+}
+
+// Exec executes the query.
+func (u *EmailBroadcastUpsertBulk) Exec(ctx context.Context) error {
+ if u.create.err != nil {
+ return u.create.err
+ }
+ for i, b := range u.create.builders {
+ if len(b.conflict) != 0 {
+ return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the EmailBroadcastCreateBulk instead", i)
+ }
+ }
+ if len(u.create.conflict) == 0 {
+ return errors.New("ent: missing options for EmailBroadcastCreateBulk.OnConflict")
+ }
+ return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *EmailBroadcastUpsertBulk) ExecX(ctx context.Context) {
+ if err := u.create.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
diff --git a/backend/ent/emailbroadcast_delete.go b/backend/ent/emailbroadcast_delete.go
new file mode 100644
index 00000000000..f9ecc53c375
--- /dev/null
+++ b/backend/ent/emailbroadcast_delete.go
@@ -0,0 +1,88 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "context"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+ "github.com/Wei-Shaw/sub2api/ent/emailbroadcast"
+ "github.com/Wei-Shaw/sub2api/ent/predicate"
+)
+
+// EmailBroadcastDelete is the builder for deleting a EmailBroadcast entity.
+type EmailBroadcastDelete struct {
+ config
+ hooks []Hook
+ mutation *EmailBroadcastMutation
+}
+
+// Where appends a list predicates to the EmailBroadcastDelete builder.
+func (_d *EmailBroadcastDelete) Where(ps ...predicate.EmailBroadcast) *EmailBroadcastDelete {
+ _d.mutation.Where(ps...)
+ return _d
+}
+
+// Exec executes the deletion query and returns how many vertices were deleted.
+func (_d *EmailBroadcastDelete) Exec(ctx context.Context) (int, error) {
+ return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_d *EmailBroadcastDelete) ExecX(ctx context.Context) int {
+ n, err := _d.Exec(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return n
+}
+
+func (_d *EmailBroadcastDelete) sqlExec(ctx context.Context) (int, error) {
+ _spec := sqlgraph.NewDeleteSpec(emailbroadcast.Table, sqlgraph.NewFieldSpec(emailbroadcast.FieldID, field.TypeInt64))
+ if ps := _d.mutation.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
+ if err != nil && sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ _d.mutation.done = true
+ return affected, err
+}
+
+// EmailBroadcastDeleteOne is the builder for deleting a single EmailBroadcast entity.
+type EmailBroadcastDeleteOne struct {
+ _d *EmailBroadcastDelete
+}
+
+// Where appends a list predicates to the EmailBroadcastDelete builder.
+func (_d *EmailBroadcastDeleteOne) Where(ps ...predicate.EmailBroadcast) *EmailBroadcastDeleteOne {
+ _d._d.mutation.Where(ps...)
+ return _d
+}
+
+// Exec executes the deletion query.
+func (_d *EmailBroadcastDeleteOne) Exec(ctx context.Context) error {
+ n, err := _d._d.Exec(ctx)
+ switch {
+ case err != nil:
+ return err
+ case n == 0:
+ return &NotFoundError{emailbroadcast.Label}
+ default:
+ return nil
+ }
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_d *EmailBroadcastDeleteOne) ExecX(ctx context.Context) {
+ if err := _d.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
diff --git a/backend/ent/emailbroadcast_query.go b/backend/ent/emailbroadcast_query.go
new file mode 100644
index 00000000000..97b42dc671b
--- /dev/null
+++ b/backend/ent/emailbroadcast_query.go
@@ -0,0 +1,564 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "context"
+ "fmt"
+ "math"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect"
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+ "github.com/Wei-Shaw/sub2api/ent/emailbroadcast"
+ "github.com/Wei-Shaw/sub2api/ent/predicate"
+)
+
+// EmailBroadcastQuery is the builder for querying EmailBroadcast entities.
+type EmailBroadcastQuery struct {
+ config
+ ctx *QueryContext
+ order []emailbroadcast.OrderOption
+ inters []Interceptor
+ predicates []predicate.EmailBroadcast
+ modifiers []func(*sql.Selector)
+ // intermediate query (i.e. traversal path).
+ sql *sql.Selector
+ path func(context.Context) (*sql.Selector, error)
+}
+
+// Where adds a new predicate for the EmailBroadcastQuery builder.
+func (_q *EmailBroadcastQuery) Where(ps ...predicate.EmailBroadcast) *EmailBroadcastQuery {
+ _q.predicates = append(_q.predicates, ps...)
+ return _q
+}
+
+// Limit the number of records to be returned by this query.
+func (_q *EmailBroadcastQuery) Limit(limit int) *EmailBroadcastQuery {
+ _q.ctx.Limit = &limit
+ return _q
+}
+
+// Offset to start from.
+func (_q *EmailBroadcastQuery) Offset(offset int) *EmailBroadcastQuery {
+ _q.ctx.Offset = &offset
+ return _q
+}
+
+// Unique configures the query builder to filter duplicate records on query.
+// By default, unique is set to true, and can be disabled using this method.
+func (_q *EmailBroadcastQuery) Unique(unique bool) *EmailBroadcastQuery {
+ _q.ctx.Unique = &unique
+ return _q
+}
+
+// Order specifies how the records should be ordered.
+func (_q *EmailBroadcastQuery) Order(o ...emailbroadcast.OrderOption) *EmailBroadcastQuery {
+ _q.order = append(_q.order, o...)
+ return _q
+}
+
+// First returns the first EmailBroadcast entity from the query.
+// Returns a *NotFoundError when no EmailBroadcast was found.
+func (_q *EmailBroadcastQuery) First(ctx context.Context) (*EmailBroadcast, error) {
+ nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
+ if err != nil {
+ return nil, err
+ }
+ if len(nodes) == 0 {
+ return nil, &NotFoundError{emailbroadcast.Label}
+ }
+ return nodes[0], nil
+}
+
+// FirstX is like First, but panics if an error occurs.
+func (_q *EmailBroadcastQuery) FirstX(ctx context.Context) *EmailBroadcast {
+ node, err := _q.First(ctx)
+ if err != nil && !IsNotFound(err) {
+ panic(err)
+ }
+ return node
+}
+
+// FirstID returns the first EmailBroadcast ID from the query.
+// Returns a *NotFoundError when no EmailBroadcast ID was found.
+func (_q *EmailBroadcastQuery) FirstID(ctx context.Context) (id int64, err error) {
+ var ids []int64
+ if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
+ return
+ }
+ if len(ids) == 0 {
+ err = &NotFoundError{emailbroadcast.Label}
+ return
+ }
+ return ids[0], nil
+}
+
+// FirstIDX is like FirstID, but panics if an error occurs.
+func (_q *EmailBroadcastQuery) FirstIDX(ctx context.Context) int64 {
+ id, err := _q.FirstID(ctx)
+ if err != nil && !IsNotFound(err) {
+ panic(err)
+ }
+ return id
+}
+
+// Only returns a single EmailBroadcast entity found by the query, ensuring it only returns one.
+// Returns a *NotSingularError when more than one EmailBroadcast entity is found.
+// Returns a *NotFoundError when no EmailBroadcast entities are found.
+func (_q *EmailBroadcastQuery) Only(ctx context.Context) (*EmailBroadcast, error) {
+ nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
+ if err != nil {
+ return nil, err
+ }
+ switch len(nodes) {
+ case 1:
+ return nodes[0], nil
+ case 0:
+ return nil, &NotFoundError{emailbroadcast.Label}
+ default:
+ return nil, &NotSingularError{emailbroadcast.Label}
+ }
+}
+
+// OnlyX is like Only, but panics if an error occurs.
+func (_q *EmailBroadcastQuery) OnlyX(ctx context.Context) *EmailBroadcast {
+ node, err := _q.Only(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return node
+}
+
+// OnlyID is like Only, but returns the only EmailBroadcast ID in the query.
+// Returns a *NotSingularError when more than one EmailBroadcast ID is found.
+// Returns a *NotFoundError when no entities are found.
+func (_q *EmailBroadcastQuery) OnlyID(ctx context.Context) (id int64, err error) {
+ var ids []int64
+ if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
+ return
+ }
+ switch len(ids) {
+ case 1:
+ id = ids[0]
+ case 0:
+ err = &NotFoundError{emailbroadcast.Label}
+ default:
+ err = &NotSingularError{emailbroadcast.Label}
+ }
+ return
+}
+
+// OnlyIDX is like OnlyID, but panics if an error occurs.
+func (_q *EmailBroadcastQuery) OnlyIDX(ctx context.Context) int64 {
+ id, err := _q.OnlyID(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return id
+}
+
+// All executes the query and returns a list of EmailBroadcasts.
+func (_q *EmailBroadcastQuery) All(ctx context.Context) ([]*EmailBroadcast, error) {
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
+ if err := _q.prepareQuery(ctx); err != nil {
+ return nil, err
+ }
+ qr := querierAll[[]*EmailBroadcast, *EmailBroadcastQuery]()
+ return withInterceptors[[]*EmailBroadcast](ctx, _q, qr, _q.inters)
+}
+
+// AllX is like All, but panics if an error occurs.
+func (_q *EmailBroadcastQuery) AllX(ctx context.Context) []*EmailBroadcast {
+ nodes, err := _q.All(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return nodes
+}
+
+// IDs executes the query and returns a list of EmailBroadcast IDs.
+func (_q *EmailBroadcastQuery) IDs(ctx context.Context) (ids []int64, err error) {
+ if _q.ctx.Unique == nil && _q.path != nil {
+ _q.Unique(true)
+ }
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
+ if err = _q.Select(emailbroadcast.FieldID).Scan(ctx, &ids); err != nil {
+ return nil, err
+ }
+ return ids, nil
+}
+
+// IDsX is like IDs, but panics if an error occurs.
+func (_q *EmailBroadcastQuery) IDsX(ctx context.Context) []int64 {
+ ids, err := _q.IDs(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return ids
+}
+
+// Count returns the count of the given query.
+func (_q *EmailBroadcastQuery) Count(ctx context.Context) (int, error) {
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
+ if err := _q.prepareQuery(ctx); err != nil {
+ return 0, err
+ }
+ return withInterceptors[int](ctx, _q, querierCount[*EmailBroadcastQuery](), _q.inters)
+}
+
+// CountX is like Count, but panics if an error occurs.
+func (_q *EmailBroadcastQuery) CountX(ctx context.Context) int {
+ count, err := _q.Count(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return count
+}
+
+// Exist returns true if the query has elements in the graph.
+func (_q *EmailBroadcastQuery) Exist(ctx context.Context) (bool, error) {
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
+ switch _, err := _q.FirstID(ctx); {
+ case IsNotFound(err):
+ return false, nil
+ case err != nil:
+ return false, fmt.Errorf("ent: check existence: %w", err)
+ default:
+ return true, nil
+ }
+}
+
+// ExistX is like Exist, but panics if an error occurs.
+func (_q *EmailBroadcastQuery) ExistX(ctx context.Context) bool {
+ exist, err := _q.Exist(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return exist
+}
+
+// Clone returns a duplicate of the EmailBroadcastQuery builder, including all associated steps. It can be
+// used to prepare common query builders and use them differently after the clone is made.
+func (_q *EmailBroadcastQuery) Clone() *EmailBroadcastQuery {
+ if _q == nil {
+ return nil
+ }
+ return &EmailBroadcastQuery{
+ config: _q.config,
+ ctx: _q.ctx.Clone(),
+ order: append([]emailbroadcast.OrderOption{}, _q.order...),
+ inters: append([]Interceptor{}, _q.inters...),
+ predicates: append([]predicate.EmailBroadcast{}, _q.predicates...),
+ // clone intermediate query.
+ sql: _q.sql.Clone(),
+ path: _q.path,
+ }
+}
+
+// GroupBy is used to group vertices by one or more fields/columns.
+// It is often used with aggregate functions, like: count, max, mean, min, sum.
+//
+// Example:
+//
+// var v []struct {
+// Subject string `json:"subject,omitempty"`
+// Count int `json:"count,omitempty"`
+// }
+//
+// client.EmailBroadcast.Query().
+// GroupBy(emailbroadcast.FieldSubject).
+// Aggregate(ent.Count()).
+// Scan(ctx, &v)
+func (_q *EmailBroadcastQuery) GroupBy(field string, fields ...string) *EmailBroadcastGroupBy {
+ _q.ctx.Fields = append([]string{field}, fields...)
+ grbuild := &EmailBroadcastGroupBy{build: _q}
+ grbuild.flds = &_q.ctx.Fields
+ grbuild.label = emailbroadcast.Label
+ grbuild.scan = grbuild.Scan
+ return grbuild
+}
+
+// Select allows the selection one or more fields/columns for the given query,
+// instead of selecting all fields in the entity.
+//
+// Example:
+//
+// var v []struct {
+// Subject string `json:"subject,omitempty"`
+// }
+//
+// client.EmailBroadcast.Query().
+// Select(emailbroadcast.FieldSubject).
+// Scan(ctx, &v)
+func (_q *EmailBroadcastQuery) Select(fields ...string) *EmailBroadcastSelect {
+ _q.ctx.Fields = append(_q.ctx.Fields, fields...)
+ sbuild := &EmailBroadcastSelect{EmailBroadcastQuery: _q}
+ sbuild.label = emailbroadcast.Label
+ sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
+ return sbuild
+}
+
+// Aggregate returns a EmailBroadcastSelect configured with the given aggregations.
+func (_q *EmailBroadcastQuery) Aggregate(fns ...AggregateFunc) *EmailBroadcastSelect {
+ return _q.Select().Aggregate(fns...)
+}
+
+func (_q *EmailBroadcastQuery) prepareQuery(ctx context.Context) error {
+ for _, inter := range _q.inters {
+ if inter == nil {
+ return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
+ }
+ if trv, ok := inter.(Traverser); ok {
+ if err := trv.Traverse(ctx, _q); err != nil {
+ return err
+ }
+ }
+ }
+ for _, f := range _q.ctx.Fields {
+ if !emailbroadcast.ValidColumn(f) {
+ return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+ }
+ }
+ if _q.path != nil {
+ prev, err := _q.path(ctx)
+ if err != nil {
+ return err
+ }
+ _q.sql = prev
+ }
+ return nil
+}
+
+func (_q *EmailBroadcastQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*EmailBroadcast, error) {
+ var (
+ nodes = []*EmailBroadcast{}
+ _spec = _q.querySpec()
+ )
+ _spec.ScanValues = func(columns []string) ([]any, error) {
+ return (*EmailBroadcast).scanValues(nil, columns)
+ }
+ _spec.Assign = func(columns []string, values []any) error {
+ node := &EmailBroadcast{config: _q.config}
+ nodes = append(nodes, node)
+ return node.assignValues(columns, values)
+ }
+ if len(_q.modifiers) > 0 {
+ _spec.Modifiers = _q.modifiers
+ }
+ for i := range hooks {
+ hooks[i](ctx, _spec)
+ }
+ if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
+ return nil, err
+ }
+ if len(nodes) == 0 {
+ return nodes, nil
+ }
+ return nodes, nil
+}
+
+func (_q *EmailBroadcastQuery) sqlCount(ctx context.Context) (int, error) {
+ _spec := _q.querySpec()
+ if len(_q.modifiers) > 0 {
+ _spec.Modifiers = _q.modifiers
+ }
+ _spec.Node.Columns = _q.ctx.Fields
+ if len(_q.ctx.Fields) > 0 {
+ _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
+ }
+ return sqlgraph.CountNodes(ctx, _q.driver, _spec)
+}
+
+func (_q *EmailBroadcastQuery) querySpec() *sqlgraph.QuerySpec {
+ _spec := sqlgraph.NewQuerySpec(emailbroadcast.Table, emailbroadcast.Columns, sqlgraph.NewFieldSpec(emailbroadcast.FieldID, field.TypeInt64))
+ _spec.From = _q.sql
+ if unique := _q.ctx.Unique; unique != nil {
+ _spec.Unique = *unique
+ } else if _q.path != nil {
+ _spec.Unique = true
+ }
+ if fields := _q.ctx.Fields; len(fields) > 0 {
+ _spec.Node.Columns = make([]string, 0, len(fields))
+ _spec.Node.Columns = append(_spec.Node.Columns, emailbroadcast.FieldID)
+ for i := range fields {
+ if fields[i] != emailbroadcast.FieldID {
+ _spec.Node.Columns = append(_spec.Node.Columns, fields[i])
+ }
+ }
+ }
+ if ps := _q.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ if limit := _q.ctx.Limit; limit != nil {
+ _spec.Limit = *limit
+ }
+ if offset := _q.ctx.Offset; offset != nil {
+ _spec.Offset = *offset
+ }
+ if ps := _q.order; len(ps) > 0 {
+ _spec.Order = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ return _spec
+}
+
+func (_q *EmailBroadcastQuery) sqlQuery(ctx context.Context) *sql.Selector {
+ builder := sql.Dialect(_q.driver.Dialect())
+ t1 := builder.Table(emailbroadcast.Table)
+ columns := _q.ctx.Fields
+ if len(columns) == 0 {
+ columns = emailbroadcast.Columns
+ }
+ selector := builder.Select(t1.Columns(columns...)...).From(t1)
+ if _q.sql != nil {
+ selector = _q.sql
+ selector.Select(selector.Columns(columns...)...)
+ }
+ if _q.ctx.Unique != nil && *_q.ctx.Unique {
+ selector.Distinct()
+ }
+ for _, m := range _q.modifiers {
+ m(selector)
+ }
+ for _, p := range _q.predicates {
+ p(selector)
+ }
+ for _, p := range _q.order {
+ p(selector)
+ }
+ if offset := _q.ctx.Offset; offset != nil {
+ // limit is mandatory for offset clause. We start
+ // with default value, and override it below if needed.
+ selector.Offset(*offset).Limit(math.MaxInt32)
+ }
+ if limit := _q.ctx.Limit; limit != nil {
+ selector.Limit(*limit)
+ }
+ return selector
+}
+
+// ForUpdate locks the selected rows against concurrent updates, and prevent them from being
+// updated, deleted or "selected ... for update" by other sessions, until the transaction is
+// either committed or rolled-back.
+func (_q *EmailBroadcastQuery) ForUpdate(opts ...sql.LockOption) *EmailBroadcastQuery {
+ if _q.driver.Dialect() == dialect.Postgres {
+ _q.Unique(false)
+ }
+ _q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
+ s.ForUpdate(opts...)
+ })
+ return _q
+}
+
+// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock
+// on any rows that are read. Other sessions can read the rows, but cannot modify them
+// until your transaction commits.
+func (_q *EmailBroadcastQuery) ForShare(opts ...sql.LockOption) *EmailBroadcastQuery {
+ if _q.driver.Dialect() == dialect.Postgres {
+ _q.Unique(false)
+ }
+ _q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
+ s.ForShare(opts...)
+ })
+ return _q
+}
+
+// EmailBroadcastGroupBy is the group-by builder for EmailBroadcast entities.
+type EmailBroadcastGroupBy struct {
+ selector
+ build *EmailBroadcastQuery
+}
+
+// Aggregate adds the given aggregation functions to the group-by query.
+func (_g *EmailBroadcastGroupBy) Aggregate(fns ...AggregateFunc) *EmailBroadcastGroupBy {
+ _g.fns = append(_g.fns, fns...)
+ return _g
+}
+
+// Scan applies the selector query and scans the result into the given value.
+func (_g *EmailBroadcastGroupBy) Scan(ctx context.Context, v any) error {
+ ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
+ if err := _g.build.prepareQuery(ctx); err != nil {
+ return err
+ }
+ return scanWithInterceptors[*EmailBroadcastQuery, *EmailBroadcastGroupBy](ctx, _g.build, _g, _g.build.inters, v)
+}
+
+func (_g *EmailBroadcastGroupBy) sqlScan(ctx context.Context, root *EmailBroadcastQuery, v any) error {
+ selector := root.sqlQuery(ctx).Select()
+ aggregation := make([]string, 0, len(_g.fns))
+ for _, fn := range _g.fns {
+ aggregation = append(aggregation, fn(selector))
+ }
+ if len(selector.SelectedColumns()) == 0 {
+ columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
+ for _, f := range *_g.flds {
+ columns = append(columns, selector.C(f))
+ }
+ columns = append(columns, aggregation...)
+ selector.Select(columns...)
+ }
+ selector.GroupBy(selector.Columns(*_g.flds...)...)
+ if err := selector.Err(); err != nil {
+ return err
+ }
+ rows := &sql.Rows{}
+ query, args := selector.Query()
+ if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
+ return err
+ }
+ defer rows.Close()
+ return sql.ScanSlice(rows, v)
+}
+
+// EmailBroadcastSelect is the builder for selecting fields of EmailBroadcast entities.
+type EmailBroadcastSelect struct {
+ *EmailBroadcastQuery
+ selector
+}
+
+// Aggregate adds the given aggregation functions to the selector query.
+func (_s *EmailBroadcastSelect) Aggregate(fns ...AggregateFunc) *EmailBroadcastSelect {
+ _s.fns = append(_s.fns, fns...)
+ return _s
+}
+
+// Scan applies the selector query and scans the result into the given value.
+func (_s *EmailBroadcastSelect) Scan(ctx context.Context, v any) error {
+ ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
+ if err := _s.prepareQuery(ctx); err != nil {
+ return err
+ }
+ return scanWithInterceptors[*EmailBroadcastQuery, *EmailBroadcastSelect](ctx, _s.EmailBroadcastQuery, _s, _s.inters, v)
+}
+
+func (_s *EmailBroadcastSelect) sqlScan(ctx context.Context, root *EmailBroadcastQuery, v any) error {
+ selector := root.sqlQuery(ctx)
+ aggregation := make([]string, 0, len(_s.fns))
+ for _, fn := range _s.fns {
+ aggregation = append(aggregation, fn(selector))
+ }
+ switch n := len(*_s.selector.flds); {
+ case n == 0 && len(aggregation) > 0:
+ selector.Select(aggregation...)
+ case n != 0 && len(aggregation) > 0:
+ selector.AppendSelect(aggregation...)
+ }
+ rows := &sql.Rows{}
+ query, args := selector.Query()
+ if err := _s.driver.Query(ctx, query, args, rows); err != nil {
+ return err
+ }
+ defer rows.Close()
+ return sql.ScanSlice(rows, v)
+}
diff --git a/backend/ent/emailbroadcast_update.go b/backend/ent/emailbroadcast_update.go
new file mode 100644
index 00000000000..86670313260
--- /dev/null
+++ b/backend/ent/emailbroadcast_update.go
@@ -0,0 +1,927 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "time"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/dialect/sql/sqljson"
+ "entgo.io/ent/schema/field"
+ "github.com/Wei-Shaw/sub2api/ent/emailbroadcast"
+ "github.com/Wei-Shaw/sub2api/ent/predicate"
+)
+
+// EmailBroadcastUpdate is the builder for updating EmailBroadcast entities.
+type EmailBroadcastUpdate struct {
+ config
+ hooks []Hook
+ mutation *EmailBroadcastMutation
+}
+
+// Where appends a list predicates to the EmailBroadcastUpdate builder.
+func (_u *EmailBroadcastUpdate) Where(ps ...predicate.EmailBroadcast) *EmailBroadcastUpdate {
+ _u.mutation.Where(ps...)
+ return _u
+}
+
+// SetSubject sets the "subject" field.
+func (_u *EmailBroadcastUpdate) SetSubject(v string) *EmailBroadcastUpdate {
+ _u.mutation.SetSubject(v)
+ return _u
+}
+
+// SetNillableSubject sets the "subject" field if the given value is not nil.
+func (_u *EmailBroadcastUpdate) SetNillableSubject(v *string) *EmailBroadcastUpdate {
+ if v != nil {
+ _u.SetSubject(*v)
+ }
+ return _u
+}
+
+// SetBody sets the "body" field.
+func (_u *EmailBroadcastUpdate) SetBody(v string) *EmailBroadcastUpdate {
+ _u.mutation.SetBody(v)
+ return _u
+}
+
+// SetNillableBody sets the "body" field if the given value is not nil.
+func (_u *EmailBroadcastUpdate) SetNillableBody(v *string) *EmailBroadcastUpdate {
+ if v != nil {
+ _u.SetBody(*v)
+ }
+ return _u
+}
+
+// SetBodyFormat sets the "body_format" field.
+func (_u *EmailBroadcastUpdate) SetBodyFormat(v string) *EmailBroadcastUpdate {
+ _u.mutation.SetBodyFormat(v)
+ return _u
+}
+
+// SetNillableBodyFormat sets the "body_format" field if the given value is not nil.
+func (_u *EmailBroadcastUpdate) SetNillableBodyFormat(v *string) *EmailBroadcastUpdate {
+ if v != nil {
+ _u.SetBodyFormat(*v)
+ }
+ return _u
+}
+
+// SetRecipientsMode sets the "recipients_mode" field.
+func (_u *EmailBroadcastUpdate) SetRecipientsMode(v string) *EmailBroadcastUpdate {
+ _u.mutation.SetRecipientsMode(v)
+ return _u
+}
+
+// SetNillableRecipientsMode sets the "recipients_mode" field if the given value is not nil.
+func (_u *EmailBroadcastUpdate) SetNillableRecipientsMode(v *string) *EmailBroadcastUpdate {
+ if v != nil {
+ _u.SetRecipientsMode(*v)
+ }
+ return _u
+}
+
+// SetRecipientUserIds sets the "recipient_user_ids" field.
+func (_u *EmailBroadcastUpdate) SetRecipientUserIds(v []int64) *EmailBroadcastUpdate {
+ _u.mutation.SetRecipientUserIds(v)
+ return _u
+}
+
+// AppendRecipientUserIds appends value to the "recipient_user_ids" field.
+func (_u *EmailBroadcastUpdate) AppendRecipientUserIds(v []int64) *EmailBroadcastUpdate {
+ _u.mutation.AppendRecipientUserIds(v)
+ return _u
+}
+
+// ClearRecipientUserIds clears the value of the "recipient_user_ids" field.
+func (_u *EmailBroadcastUpdate) ClearRecipientUserIds() *EmailBroadcastUpdate {
+ _u.mutation.ClearRecipientUserIds()
+ return _u
+}
+
+// SetStatus sets the "status" field.
+func (_u *EmailBroadcastUpdate) SetStatus(v string) *EmailBroadcastUpdate {
+ _u.mutation.SetStatus(v)
+ return _u
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (_u *EmailBroadcastUpdate) SetNillableStatus(v *string) *EmailBroadcastUpdate {
+ if v != nil {
+ _u.SetStatus(*v)
+ }
+ return _u
+}
+
+// SetTotalCount sets the "total_count" field.
+func (_u *EmailBroadcastUpdate) SetTotalCount(v int) *EmailBroadcastUpdate {
+ _u.mutation.ResetTotalCount()
+ _u.mutation.SetTotalCount(v)
+ return _u
+}
+
+// SetNillableTotalCount sets the "total_count" field if the given value is not nil.
+func (_u *EmailBroadcastUpdate) SetNillableTotalCount(v *int) *EmailBroadcastUpdate {
+ if v != nil {
+ _u.SetTotalCount(*v)
+ }
+ return _u
+}
+
+// AddTotalCount adds value to the "total_count" field.
+func (_u *EmailBroadcastUpdate) AddTotalCount(v int) *EmailBroadcastUpdate {
+ _u.mutation.AddTotalCount(v)
+ return _u
+}
+
+// SetSuccessCount sets the "success_count" field.
+func (_u *EmailBroadcastUpdate) SetSuccessCount(v int) *EmailBroadcastUpdate {
+ _u.mutation.ResetSuccessCount()
+ _u.mutation.SetSuccessCount(v)
+ return _u
+}
+
+// SetNillableSuccessCount sets the "success_count" field if the given value is not nil.
+func (_u *EmailBroadcastUpdate) SetNillableSuccessCount(v *int) *EmailBroadcastUpdate {
+ if v != nil {
+ _u.SetSuccessCount(*v)
+ }
+ return _u
+}
+
+// AddSuccessCount adds value to the "success_count" field.
+func (_u *EmailBroadcastUpdate) AddSuccessCount(v int) *EmailBroadcastUpdate {
+ _u.mutation.AddSuccessCount(v)
+ return _u
+}
+
+// SetFailedCount sets the "failed_count" field.
+func (_u *EmailBroadcastUpdate) SetFailedCount(v int) *EmailBroadcastUpdate {
+ _u.mutation.ResetFailedCount()
+ _u.mutation.SetFailedCount(v)
+ return _u
+}
+
+// SetNillableFailedCount sets the "failed_count" field if the given value is not nil.
+func (_u *EmailBroadcastUpdate) SetNillableFailedCount(v *int) *EmailBroadcastUpdate {
+ if v != nil {
+ _u.SetFailedCount(*v)
+ }
+ return _u
+}
+
+// AddFailedCount adds value to the "failed_count" field.
+func (_u *EmailBroadcastUpdate) AddFailedCount(v int) *EmailBroadcastUpdate {
+ _u.mutation.AddFailedCount(v)
+ return _u
+}
+
+// SetErrorMessage sets the "error_message" field.
+func (_u *EmailBroadcastUpdate) SetErrorMessage(v string) *EmailBroadcastUpdate {
+ _u.mutation.SetErrorMessage(v)
+ return _u
+}
+
+// SetNillableErrorMessage sets the "error_message" field if the given value is not nil.
+func (_u *EmailBroadcastUpdate) SetNillableErrorMessage(v *string) *EmailBroadcastUpdate {
+ if v != nil {
+ _u.SetErrorMessage(*v)
+ }
+ return _u
+}
+
+// ClearErrorMessage clears the value of the "error_message" field.
+func (_u *EmailBroadcastUpdate) ClearErrorMessage() *EmailBroadcastUpdate {
+ _u.mutation.ClearErrorMessage()
+ return _u
+}
+
+// SetCreatedBy sets the "created_by" field.
+func (_u *EmailBroadcastUpdate) SetCreatedBy(v int64) *EmailBroadcastUpdate {
+ _u.mutation.ResetCreatedBy()
+ _u.mutation.SetCreatedBy(v)
+ return _u
+}
+
+// SetNillableCreatedBy sets the "created_by" field if the given value is not nil.
+func (_u *EmailBroadcastUpdate) SetNillableCreatedBy(v *int64) *EmailBroadcastUpdate {
+ if v != nil {
+ _u.SetCreatedBy(*v)
+ }
+ return _u
+}
+
+// AddCreatedBy adds value to the "created_by" field.
+func (_u *EmailBroadcastUpdate) AddCreatedBy(v int64) *EmailBroadcastUpdate {
+ _u.mutation.AddCreatedBy(v)
+ return _u
+}
+
+// ClearCreatedBy clears the value of the "created_by" field.
+func (_u *EmailBroadcastUpdate) ClearCreatedBy() *EmailBroadcastUpdate {
+ _u.mutation.ClearCreatedBy()
+ return _u
+}
+
+// SetStartedAt sets the "started_at" field.
+func (_u *EmailBroadcastUpdate) SetStartedAt(v time.Time) *EmailBroadcastUpdate {
+ _u.mutation.SetStartedAt(v)
+ return _u
+}
+
+// SetNillableStartedAt sets the "started_at" field if the given value is not nil.
+func (_u *EmailBroadcastUpdate) SetNillableStartedAt(v *time.Time) *EmailBroadcastUpdate {
+ if v != nil {
+ _u.SetStartedAt(*v)
+ }
+ return _u
+}
+
+// ClearStartedAt clears the value of the "started_at" field.
+func (_u *EmailBroadcastUpdate) ClearStartedAt() *EmailBroadcastUpdate {
+ _u.mutation.ClearStartedAt()
+ return _u
+}
+
+// SetFinishedAt sets the "finished_at" field.
+func (_u *EmailBroadcastUpdate) SetFinishedAt(v time.Time) *EmailBroadcastUpdate {
+ _u.mutation.SetFinishedAt(v)
+ return _u
+}
+
+// SetNillableFinishedAt sets the "finished_at" field if the given value is not nil.
+func (_u *EmailBroadcastUpdate) SetNillableFinishedAt(v *time.Time) *EmailBroadcastUpdate {
+ if v != nil {
+ _u.SetFinishedAt(*v)
+ }
+ return _u
+}
+
+// ClearFinishedAt clears the value of the "finished_at" field.
+func (_u *EmailBroadcastUpdate) ClearFinishedAt() *EmailBroadcastUpdate {
+ _u.mutation.ClearFinishedAt()
+ return _u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (_u *EmailBroadcastUpdate) SetUpdatedAt(v time.Time) *EmailBroadcastUpdate {
+ _u.mutation.SetUpdatedAt(v)
+ return _u
+}
+
+// Mutation returns the EmailBroadcastMutation object of the builder.
+func (_u *EmailBroadcastUpdate) Mutation() *EmailBroadcastMutation {
+ return _u.mutation
+}
+
+// Save executes the query and returns the number of nodes affected by the update operation.
+func (_u *EmailBroadcastUpdate) Save(ctx context.Context) (int, error) {
+ _u.defaults()
+ return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (_u *EmailBroadcastUpdate) SaveX(ctx context.Context) int {
+ affected, err := _u.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return affected
+}
+
+// Exec executes the query.
+func (_u *EmailBroadcastUpdate) Exec(ctx context.Context) error {
+ _, err := _u.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_u *EmailBroadcastUpdate) ExecX(ctx context.Context) {
+ if err := _u.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// defaults sets the default values of the builder before save.
+func (_u *EmailBroadcastUpdate) defaults() {
+ if _, ok := _u.mutation.UpdatedAt(); !ok {
+ v := emailbroadcast.UpdateDefaultUpdatedAt()
+ _u.mutation.SetUpdatedAt(v)
+ }
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (_u *EmailBroadcastUpdate) check() error {
+ if v, ok := _u.mutation.Subject(); ok {
+ if err := emailbroadcast.SubjectValidator(v); err != nil {
+ return &ValidationError{Name: "subject", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.subject": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.Body(); ok {
+ if err := emailbroadcast.BodyValidator(v); err != nil {
+ return &ValidationError{Name: "body", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.body": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.BodyFormat(); ok {
+ if err := emailbroadcast.BodyFormatValidator(v); err != nil {
+ return &ValidationError{Name: "body_format", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.body_format": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.RecipientsMode(); ok {
+ if err := emailbroadcast.RecipientsModeValidator(v); err != nil {
+ return &ValidationError{Name: "recipients_mode", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.recipients_mode": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.Status(); ok {
+ if err := emailbroadcast.StatusValidator(v); err != nil {
+ return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.status": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.TotalCount(); ok {
+ if err := emailbroadcast.TotalCountValidator(v); err != nil {
+ return &ValidationError{Name: "total_count", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.total_count": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.SuccessCount(); ok {
+ if err := emailbroadcast.SuccessCountValidator(v); err != nil {
+ return &ValidationError{Name: "success_count", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.success_count": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.FailedCount(); ok {
+ if err := emailbroadcast.FailedCountValidator(v); err != nil {
+ return &ValidationError{Name: "failed_count", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.failed_count": %w`, err)}
+ }
+ }
+ return nil
+}
+
+func (_u *EmailBroadcastUpdate) sqlSave(ctx context.Context) (_node int, err error) {
+ if err := _u.check(); err != nil {
+ return _node, err
+ }
+ _spec := sqlgraph.NewUpdateSpec(emailbroadcast.Table, emailbroadcast.Columns, sqlgraph.NewFieldSpec(emailbroadcast.FieldID, field.TypeInt64))
+ if ps := _u.mutation.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ if value, ok := _u.mutation.Subject(); ok {
+ _spec.SetField(emailbroadcast.FieldSubject, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.Body(); ok {
+ _spec.SetField(emailbroadcast.FieldBody, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.BodyFormat(); ok {
+ _spec.SetField(emailbroadcast.FieldBodyFormat, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.RecipientsMode(); ok {
+ _spec.SetField(emailbroadcast.FieldRecipientsMode, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.RecipientUserIds(); ok {
+ _spec.SetField(emailbroadcast.FieldRecipientUserIds, field.TypeJSON, value)
+ }
+ if value, ok := _u.mutation.AppendedRecipientUserIds(); ok {
+ _spec.AddModifier(func(u *sql.UpdateBuilder) {
+ sqljson.Append(u, emailbroadcast.FieldRecipientUserIds, value)
+ })
+ }
+ if _u.mutation.RecipientUserIdsCleared() {
+ _spec.ClearField(emailbroadcast.FieldRecipientUserIds, field.TypeJSON)
+ }
+ if value, ok := _u.mutation.Status(); ok {
+ _spec.SetField(emailbroadcast.FieldStatus, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.TotalCount(); ok {
+ _spec.SetField(emailbroadcast.FieldTotalCount, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.AddedTotalCount(); ok {
+ _spec.AddField(emailbroadcast.FieldTotalCount, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.SuccessCount(); ok {
+ _spec.SetField(emailbroadcast.FieldSuccessCount, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.AddedSuccessCount(); ok {
+ _spec.AddField(emailbroadcast.FieldSuccessCount, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.FailedCount(); ok {
+ _spec.SetField(emailbroadcast.FieldFailedCount, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.AddedFailedCount(); ok {
+ _spec.AddField(emailbroadcast.FieldFailedCount, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.ErrorMessage(); ok {
+ _spec.SetField(emailbroadcast.FieldErrorMessage, field.TypeString, value)
+ }
+ if _u.mutation.ErrorMessageCleared() {
+ _spec.ClearField(emailbroadcast.FieldErrorMessage, field.TypeString)
+ }
+ if value, ok := _u.mutation.CreatedBy(); ok {
+ _spec.SetField(emailbroadcast.FieldCreatedBy, field.TypeInt64, value)
+ }
+ if value, ok := _u.mutation.AddedCreatedBy(); ok {
+ _spec.AddField(emailbroadcast.FieldCreatedBy, field.TypeInt64, value)
+ }
+ if _u.mutation.CreatedByCleared() {
+ _spec.ClearField(emailbroadcast.FieldCreatedBy, field.TypeInt64)
+ }
+ if value, ok := _u.mutation.StartedAt(); ok {
+ _spec.SetField(emailbroadcast.FieldStartedAt, field.TypeTime, value)
+ }
+ if _u.mutation.StartedAtCleared() {
+ _spec.ClearField(emailbroadcast.FieldStartedAt, field.TypeTime)
+ }
+ if value, ok := _u.mutation.FinishedAt(); ok {
+ _spec.SetField(emailbroadcast.FieldFinishedAt, field.TypeTime, value)
+ }
+ if _u.mutation.FinishedAtCleared() {
+ _spec.ClearField(emailbroadcast.FieldFinishedAt, field.TypeTime)
+ }
+ if value, ok := _u.mutation.UpdatedAt(); ok {
+ _spec.SetField(emailbroadcast.FieldUpdatedAt, field.TypeTime, value)
+ }
+ if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
+ if _, ok := err.(*sqlgraph.NotFoundError); ok {
+ err = &NotFoundError{emailbroadcast.Label}
+ } else if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ return 0, err
+ }
+ _u.mutation.done = true
+ return _node, nil
+}
+
+// EmailBroadcastUpdateOne is the builder for updating a single EmailBroadcast entity.
+type EmailBroadcastUpdateOne struct {
+ config
+ fields []string
+ hooks []Hook
+ mutation *EmailBroadcastMutation
+}
+
+// SetSubject sets the "subject" field.
+func (_u *EmailBroadcastUpdateOne) SetSubject(v string) *EmailBroadcastUpdateOne {
+ _u.mutation.SetSubject(v)
+ return _u
+}
+
+// SetNillableSubject sets the "subject" field if the given value is not nil.
+func (_u *EmailBroadcastUpdateOne) SetNillableSubject(v *string) *EmailBroadcastUpdateOne {
+ if v != nil {
+ _u.SetSubject(*v)
+ }
+ return _u
+}
+
+// SetBody sets the "body" field.
+func (_u *EmailBroadcastUpdateOne) SetBody(v string) *EmailBroadcastUpdateOne {
+ _u.mutation.SetBody(v)
+ return _u
+}
+
+// SetNillableBody sets the "body" field if the given value is not nil.
+func (_u *EmailBroadcastUpdateOne) SetNillableBody(v *string) *EmailBroadcastUpdateOne {
+ if v != nil {
+ _u.SetBody(*v)
+ }
+ return _u
+}
+
+// SetBodyFormat sets the "body_format" field.
+func (_u *EmailBroadcastUpdateOne) SetBodyFormat(v string) *EmailBroadcastUpdateOne {
+ _u.mutation.SetBodyFormat(v)
+ return _u
+}
+
+// SetNillableBodyFormat sets the "body_format" field if the given value is not nil.
+func (_u *EmailBroadcastUpdateOne) SetNillableBodyFormat(v *string) *EmailBroadcastUpdateOne {
+ if v != nil {
+ _u.SetBodyFormat(*v)
+ }
+ return _u
+}
+
+// SetRecipientsMode sets the "recipients_mode" field.
+func (_u *EmailBroadcastUpdateOne) SetRecipientsMode(v string) *EmailBroadcastUpdateOne {
+ _u.mutation.SetRecipientsMode(v)
+ return _u
+}
+
+// SetNillableRecipientsMode sets the "recipients_mode" field if the given value is not nil.
+func (_u *EmailBroadcastUpdateOne) SetNillableRecipientsMode(v *string) *EmailBroadcastUpdateOne {
+ if v != nil {
+ _u.SetRecipientsMode(*v)
+ }
+ return _u
+}
+
+// SetRecipientUserIds sets the "recipient_user_ids" field.
+func (_u *EmailBroadcastUpdateOne) SetRecipientUserIds(v []int64) *EmailBroadcastUpdateOne {
+ _u.mutation.SetRecipientUserIds(v)
+ return _u
+}
+
+// AppendRecipientUserIds appends value to the "recipient_user_ids" field.
+func (_u *EmailBroadcastUpdateOne) AppendRecipientUserIds(v []int64) *EmailBroadcastUpdateOne {
+ _u.mutation.AppendRecipientUserIds(v)
+ return _u
+}
+
+// ClearRecipientUserIds clears the value of the "recipient_user_ids" field.
+func (_u *EmailBroadcastUpdateOne) ClearRecipientUserIds() *EmailBroadcastUpdateOne {
+ _u.mutation.ClearRecipientUserIds()
+ return _u
+}
+
+// SetStatus sets the "status" field.
+func (_u *EmailBroadcastUpdateOne) SetStatus(v string) *EmailBroadcastUpdateOne {
+ _u.mutation.SetStatus(v)
+ return _u
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (_u *EmailBroadcastUpdateOne) SetNillableStatus(v *string) *EmailBroadcastUpdateOne {
+ if v != nil {
+ _u.SetStatus(*v)
+ }
+ return _u
+}
+
+// SetTotalCount sets the "total_count" field.
+func (_u *EmailBroadcastUpdateOne) SetTotalCount(v int) *EmailBroadcastUpdateOne {
+ _u.mutation.ResetTotalCount()
+ _u.mutation.SetTotalCount(v)
+ return _u
+}
+
+// SetNillableTotalCount sets the "total_count" field if the given value is not nil.
+func (_u *EmailBroadcastUpdateOne) SetNillableTotalCount(v *int) *EmailBroadcastUpdateOne {
+ if v != nil {
+ _u.SetTotalCount(*v)
+ }
+ return _u
+}
+
+// AddTotalCount adds value to the "total_count" field.
+func (_u *EmailBroadcastUpdateOne) AddTotalCount(v int) *EmailBroadcastUpdateOne {
+ _u.mutation.AddTotalCount(v)
+ return _u
+}
+
+// SetSuccessCount sets the "success_count" field.
+func (_u *EmailBroadcastUpdateOne) SetSuccessCount(v int) *EmailBroadcastUpdateOne {
+ _u.mutation.ResetSuccessCount()
+ _u.mutation.SetSuccessCount(v)
+ return _u
+}
+
+// SetNillableSuccessCount sets the "success_count" field if the given value is not nil.
+func (_u *EmailBroadcastUpdateOne) SetNillableSuccessCount(v *int) *EmailBroadcastUpdateOne {
+ if v != nil {
+ _u.SetSuccessCount(*v)
+ }
+ return _u
+}
+
+// AddSuccessCount adds value to the "success_count" field.
+func (_u *EmailBroadcastUpdateOne) AddSuccessCount(v int) *EmailBroadcastUpdateOne {
+ _u.mutation.AddSuccessCount(v)
+ return _u
+}
+
+// SetFailedCount sets the "failed_count" field.
+func (_u *EmailBroadcastUpdateOne) SetFailedCount(v int) *EmailBroadcastUpdateOne {
+ _u.mutation.ResetFailedCount()
+ _u.mutation.SetFailedCount(v)
+ return _u
+}
+
+// SetNillableFailedCount sets the "failed_count" field if the given value is not nil.
+func (_u *EmailBroadcastUpdateOne) SetNillableFailedCount(v *int) *EmailBroadcastUpdateOne {
+ if v != nil {
+ _u.SetFailedCount(*v)
+ }
+ return _u
+}
+
+// AddFailedCount adds value to the "failed_count" field.
+func (_u *EmailBroadcastUpdateOne) AddFailedCount(v int) *EmailBroadcastUpdateOne {
+ _u.mutation.AddFailedCount(v)
+ return _u
+}
+
+// SetErrorMessage sets the "error_message" field.
+func (_u *EmailBroadcastUpdateOne) SetErrorMessage(v string) *EmailBroadcastUpdateOne {
+ _u.mutation.SetErrorMessage(v)
+ return _u
+}
+
+// SetNillableErrorMessage sets the "error_message" field if the given value is not nil.
+func (_u *EmailBroadcastUpdateOne) SetNillableErrorMessage(v *string) *EmailBroadcastUpdateOne {
+ if v != nil {
+ _u.SetErrorMessage(*v)
+ }
+ return _u
+}
+
+// ClearErrorMessage clears the value of the "error_message" field.
+func (_u *EmailBroadcastUpdateOne) ClearErrorMessage() *EmailBroadcastUpdateOne {
+ _u.mutation.ClearErrorMessage()
+ return _u
+}
+
+// SetCreatedBy sets the "created_by" field.
+func (_u *EmailBroadcastUpdateOne) SetCreatedBy(v int64) *EmailBroadcastUpdateOne {
+ _u.mutation.ResetCreatedBy()
+ _u.mutation.SetCreatedBy(v)
+ return _u
+}
+
+// SetNillableCreatedBy sets the "created_by" field if the given value is not nil.
+func (_u *EmailBroadcastUpdateOne) SetNillableCreatedBy(v *int64) *EmailBroadcastUpdateOne {
+ if v != nil {
+ _u.SetCreatedBy(*v)
+ }
+ return _u
+}
+
+// AddCreatedBy adds value to the "created_by" field.
+func (_u *EmailBroadcastUpdateOne) AddCreatedBy(v int64) *EmailBroadcastUpdateOne {
+ _u.mutation.AddCreatedBy(v)
+ return _u
+}
+
+// ClearCreatedBy clears the value of the "created_by" field.
+func (_u *EmailBroadcastUpdateOne) ClearCreatedBy() *EmailBroadcastUpdateOne {
+ _u.mutation.ClearCreatedBy()
+ return _u
+}
+
+// SetStartedAt sets the "started_at" field.
+func (_u *EmailBroadcastUpdateOne) SetStartedAt(v time.Time) *EmailBroadcastUpdateOne {
+ _u.mutation.SetStartedAt(v)
+ return _u
+}
+
+// SetNillableStartedAt sets the "started_at" field if the given value is not nil.
+func (_u *EmailBroadcastUpdateOne) SetNillableStartedAt(v *time.Time) *EmailBroadcastUpdateOne {
+ if v != nil {
+ _u.SetStartedAt(*v)
+ }
+ return _u
+}
+
+// ClearStartedAt clears the value of the "started_at" field.
+func (_u *EmailBroadcastUpdateOne) ClearStartedAt() *EmailBroadcastUpdateOne {
+ _u.mutation.ClearStartedAt()
+ return _u
+}
+
+// SetFinishedAt sets the "finished_at" field.
+func (_u *EmailBroadcastUpdateOne) SetFinishedAt(v time.Time) *EmailBroadcastUpdateOne {
+ _u.mutation.SetFinishedAt(v)
+ return _u
+}
+
+// SetNillableFinishedAt sets the "finished_at" field if the given value is not nil.
+func (_u *EmailBroadcastUpdateOne) SetNillableFinishedAt(v *time.Time) *EmailBroadcastUpdateOne {
+ if v != nil {
+ _u.SetFinishedAt(*v)
+ }
+ return _u
+}
+
+// ClearFinishedAt clears the value of the "finished_at" field.
+func (_u *EmailBroadcastUpdateOne) ClearFinishedAt() *EmailBroadcastUpdateOne {
+ _u.mutation.ClearFinishedAt()
+ return _u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (_u *EmailBroadcastUpdateOne) SetUpdatedAt(v time.Time) *EmailBroadcastUpdateOne {
+ _u.mutation.SetUpdatedAt(v)
+ return _u
+}
+
+// Mutation returns the EmailBroadcastMutation object of the builder.
+func (_u *EmailBroadcastUpdateOne) Mutation() *EmailBroadcastMutation {
+ return _u.mutation
+}
+
+// Where appends a list predicates to the EmailBroadcastUpdate builder.
+func (_u *EmailBroadcastUpdateOne) Where(ps ...predicate.EmailBroadcast) *EmailBroadcastUpdateOne {
+ _u.mutation.Where(ps...)
+ return _u
+}
+
+// Select allows selecting one or more fields (columns) of the returned entity.
+// The default is selecting all fields defined in the entity schema.
+func (_u *EmailBroadcastUpdateOne) Select(field string, fields ...string) *EmailBroadcastUpdateOne {
+ _u.fields = append([]string{field}, fields...)
+ return _u
+}
+
+// Save executes the query and returns the updated EmailBroadcast entity.
+func (_u *EmailBroadcastUpdateOne) Save(ctx context.Context) (*EmailBroadcast, error) {
+ _u.defaults()
+ return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (_u *EmailBroadcastUpdateOne) SaveX(ctx context.Context) *EmailBroadcast {
+ node, err := _u.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return node
+}
+
+// Exec executes the query on the entity.
+func (_u *EmailBroadcastUpdateOne) Exec(ctx context.Context) error {
+ _, err := _u.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_u *EmailBroadcastUpdateOne) ExecX(ctx context.Context) {
+ if err := _u.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// defaults sets the default values of the builder before save.
+func (_u *EmailBroadcastUpdateOne) defaults() {
+ if _, ok := _u.mutation.UpdatedAt(); !ok {
+ v := emailbroadcast.UpdateDefaultUpdatedAt()
+ _u.mutation.SetUpdatedAt(v)
+ }
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (_u *EmailBroadcastUpdateOne) check() error {
+ if v, ok := _u.mutation.Subject(); ok {
+ if err := emailbroadcast.SubjectValidator(v); err != nil {
+ return &ValidationError{Name: "subject", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.subject": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.Body(); ok {
+ if err := emailbroadcast.BodyValidator(v); err != nil {
+ return &ValidationError{Name: "body", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.body": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.BodyFormat(); ok {
+ if err := emailbroadcast.BodyFormatValidator(v); err != nil {
+ return &ValidationError{Name: "body_format", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.body_format": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.RecipientsMode(); ok {
+ if err := emailbroadcast.RecipientsModeValidator(v); err != nil {
+ return &ValidationError{Name: "recipients_mode", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.recipients_mode": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.Status(); ok {
+ if err := emailbroadcast.StatusValidator(v); err != nil {
+ return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.status": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.TotalCount(); ok {
+ if err := emailbroadcast.TotalCountValidator(v); err != nil {
+ return &ValidationError{Name: "total_count", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.total_count": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.SuccessCount(); ok {
+ if err := emailbroadcast.SuccessCountValidator(v); err != nil {
+ return &ValidationError{Name: "success_count", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.success_count": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.FailedCount(); ok {
+ if err := emailbroadcast.FailedCountValidator(v); err != nil {
+ return &ValidationError{Name: "failed_count", err: fmt.Errorf(`ent: validator failed for field "EmailBroadcast.failed_count": %w`, err)}
+ }
+ }
+ return nil
+}
+
+func (_u *EmailBroadcastUpdateOne) sqlSave(ctx context.Context) (_node *EmailBroadcast, err error) {
+ if err := _u.check(); err != nil {
+ return _node, err
+ }
+ _spec := sqlgraph.NewUpdateSpec(emailbroadcast.Table, emailbroadcast.Columns, sqlgraph.NewFieldSpec(emailbroadcast.FieldID, field.TypeInt64))
+ id, ok := _u.mutation.ID()
+ if !ok {
+ return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "EmailBroadcast.id" for update`)}
+ }
+ _spec.Node.ID.Value = id
+ if fields := _u.fields; len(fields) > 0 {
+ _spec.Node.Columns = make([]string, 0, len(fields))
+ _spec.Node.Columns = append(_spec.Node.Columns, emailbroadcast.FieldID)
+ for _, f := range fields {
+ if !emailbroadcast.ValidColumn(f) {
+ return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+ }
+ if f != emailbroadcast.FieldID {
+ _spec.Node.Columns = append(_spec.Node.Columns, f)
+ }
+ }
+ }
+ if ps := _u.mutation.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ if value, ok := _u.mutation.Subject(); ok {
+ _spec.SetField(emailbroadcast.FieldSubject, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.Body(); ok {
+ _spec.SetField(emailbroadcast.FieldBody, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.BodyFormat(); ok {
+ _spec.SetField(emailbroadcast.FieldBodyFormat, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.RecipientsMode(); ok {
+ _spec.SetField(emailbroadcast.FieldRecipientsMode, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.RecipientUserIds(); ok {
+ _spec.SetField(emailbroadcast.FieldRecipientUserIds, field.TypeJSON, value)
+ }
+ if value, ok := _u.mutation.AppendedRecipientUserIds(); ok {
+ _spec.AddModifier(func(u *sql.UpdateBuilder) {
+ sqljson.Append(u, emailbroadcast.FieldRecipientUserIds, value)
+ })
+ }
+ if _u.mutation.RecipientUserIdsCleared() {
+ _spec.ClearField(emailbroadcast.FieldRecipientUserIds, field.TypeJSON)
+ }
+ if value, ok := _u.mutation.Status(); ok {
+ _spec.SetField(emailbroadcast.FieldStatus, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.TotalCount(); ok {
+ _spec.SetField(emailbroadcast.FieldTotalCount, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.AddedTotalCount(); ok {
+ _spec.AddField(emailbroadcast.FieldTotalCount, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.SuccessCount(); ok {
+ _spec.SetField(emailbroadcast.FieldSuccessCount, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.AddedSuccessCount(); ok {
+ _spec.AddField(emailbroadcast.FieldSuccessCount, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.FailedCount(); ok {
+ _spec.SetField(emailbroadcast.FieldFailedCount, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.AddedFailedCount(); ok {
+ _spec.AddField(emailbroadcast.FieldFailedCount, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.ErrorMessage(); ok {
+ _spec.SetField(emailbroadcast.FieldErrorMessage, field.TypeString, value)
+ }
+ if _u.mutation.ErrorMessageCleared() {
+ _spec.ClearField(emailbroadcast.FieldErrorMessage, field.TypeString)
+ }
+ if value, ok := _u.mutation.CreatedBy(); ok {
+ _spec.SetField(emailbroadcast.FieldCreatedBy, field.TypeInt64, value)
+ }
+ if value, ok := _u.mutation.AddedCreatedBy(); ok {
+ _spec.AddField(emailbroadcast.FieldCreatedBy, field.TypeInt64, value)
+ }
+ if _u.mutation.CreatedByCleared() {
+ _spec.ClearField(emailbroadcast.FieldCreatedBy, field.TypeInt64)
+ }
+ if value, ok := _u.mutation.StartedAt(); ok {
+ _spec.SetField(emailbroadcast.FieldStartedAt, field.TypeTime, value)
+ }
+ if _u.mutation.StartedAtCleared() {
+ _spec.ClearField(emailbroadcast.FieldStartedAt, field.TypeTime)
+ }
+ if value, ok := _u.mutation.FinishedAt(); ok {
+ _spec.SetField(emailbroadcast.FieldFinishedAt, field.TypeTime, value)
+ }
+ if _u.mutation.FinishedAtCleared() {
+ _spec.ClearField(emailbroadcast.FieldFinishedAt, field.TypeTime)
+ }
+ if value, ok := _u.mutation.UpdatedAt(); ok {
+ _spec.SetField(emailbroadcast.FieldUpdatedAt, field.TypeTime, value)
+ }
+ _node = &EmailBroadcast{config: _u.config}
+ _spec.Assign = _node.assignValues
+ _spec.ScanValues = _node.scanValues
+ if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
+ if _, ok := err.(*sqlgraph.NotFoundError); ok {
+ err = &NotFoundError{emailbroadcast.Label}
+ } else if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ return nil, err
+ }
+ _u.mutation.done = true
+ return _node, nil
+}
diff --git a/backend/ent/ent.go b/backend/ent/ent.go
index 33d36e70ee8..f1208e43277 100644
--- a/backend/ent/ent.go
+++ b/backend/ent/ent.go
@@ -23,6 +23,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
+ "github.com/Wei-Shaw/sub2api/ent/emailbroadcast"
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
@@ -118,6 +119,7 @@ func checkColumn(t, c string) error {
channelmonitordailyrollup.Table: channelmonitordailyrollup.ValidColumn,
channelmonitorhistory.Table: channelmonitorhistory.ValidColumn,
channelmonitorrequesttemplate.Table: channelmonitorrequesttemplate.ValidColumn,
+ emailbroadcast.Table: emailbroadcast.ValidColumn,
errorpassthroughrule.Table: errorpassthroughrule.ValidColumn,
group.Table: group.ValidColumn,
idempotencyrecord.Table: idempotencyrecord.ValidColumn,
diff --git a/backend/ent/hook/hook.go b/backend/ent/hook/hook.go
index 71bfd3b88eb..2781b91754c 100644
--- a/backend/ent/hook/hook.go
+++ b/backend/ent/hook/hook.go
@@ -141,6 +141,18 @@ func (f ChannelMonitorRequestTemplateFunc) Mutate(ctx context.Context, m ent.Mut
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ChannelMonitorRequestTemplateMutation", m)
}
+// The EmailBroadcastFunc type is an adapter to allow the use of ordinary
+// function as EmailBroadcast mutator.
+type EmailBroadcastFunc func(context.Context, *ent.EmailBroadcastMutation) (ent.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f EmailBroadcastFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+ if mv, ok := m.(*ent.EmailBroadcastMutation); ok {
+ return f(ctx, mv)
+ }
+ return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.EmailBroadcastMutation", m)
+}
+
// The ErrorPassthroughRuleFunc type is an adapter to allow the use of ordinary
// function as ErrorPassthroughRule mutator.
type ErrorPassthroughRuleFunc func(context.Context, *ent.ErrorPassthroughRuleMutation) (ent.Value, error)
diff --git a/backend/ent/intercept/intercept.go b/backend/ent/intercept/intercept.go
index 5d86e25bd55..c38a6122709 100644
--- a/backend/ent/intercept/intercept.go
+++ b/backend/ent/intercept/intercept.go
@@ -19,6 +19,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
+ "github.com/Wei-Shaw/sub2api/ent/emailbroadcast"
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
@@ -399,6 +400,33 @@ func (f TraverseChannelMonitorRequestTemplate) Traverse(ctx context.Context, q e
return fmt.Errorf("unexpected query type %T. expect *ent.ChannelMonitorRequestTemplateQuery", q)
}
+// The EmailBroadcastFunc type is an adapter to allow the use of ordinary function as a Querier.
+type EmailBroadcastFunc func(context.Context, *ent.EmailBroadcastQuery) (ent.Value, error)
+
+// Query calls f(ctx, q).
+func (f EmailBroadcastFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
+ if q, ok := q.(*ent.EmailBroadcastQuery); ok {
+ return f(ctx, q)
+ }
+ return nil, fmt.Errorf("unexpected query type %T. expect *ent.EmailBroadcastQuery", q)
+}
+
+// The TraverseEmailBroadcast type is an adapter to allow the use of ordinary function as Traverser.
+type TraverseEmailBroadcast func(context.Context, *ent.EmailBroadcastQuery) error
+
+// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
+func (f TraverseEmailBroadcast) Intercept(next ent.Querier) ent.Querier {
+ return next
+}
+
+// Traverse calls f(ctx, q).
+func (f TraverseEmailBroadcast) Traverse(ctx context.Context, q ent.Query) error {
+ if q, ok := q.(*ent.EmailBroadcastQuery); ok {
+ return f(ctx, q)
+ }
+ return fmt.Errorf("unexpected query type %T. expect *ent.EmailBroadcastQuery", q)
+}
+
// The ErrorPassthroughRuleFunc type is an adapter to allow the use of ordinary function as a Querier.
type ErrorPassthroughRuleFunc func(context.Context, *ent.ErrorPassthroughRuleQuery) (ent.Value, error)
@@ -1072,6 +1100,8 @@ func NewQuery(q ent.Query) (Query, error) {
return &query[*ent.ChannelMonitorHistoryQuery, predicate.ChannelMonitorHistory, channelmonitorhistory.OrderOption]{typ: ent.TypeChannelMonitorHistory, tq: q}, nil
case *ent.ChannelMonitorRequestTemplateQuery:
return &query[*ent.ChannelMonitorRequestTemplateQuery, predicate.ChannelMonitorRequestTemplate, channelmonitorrequesttemplate.OrderOption]{typ: ent.TypeChannelMonitorRequestTemplate, tq: q}, nil
+ case *ent.EmailBroadcastQuery:
+ return &query[*ent.EmailBroadcastQuery, predicate.EmailBroadcast, emailbroadcast.OrderOption]{typ: ent.TypeEmailBroadcast, tq: q}, nil
case *ent.ErrorPassthroughRuleQuery:
return &query[*ent.ErrorPassthroughRuleQuery, predicate.ErrorPassthroughRule, errorpassthroughrule.OrderOption]{typ: ent.TypeErrorPassthroughRule, tq: q}, nil
case *ent.GroupQuery:
diff --git a/backend/ent/migrate/schema.go b/backend/ent/migrate/schema.go
index 7abe4c601e3..4a20674d44a 100644
--- a/backend/ent/migrate/schema.go
+++ b/backend/ent/migrate/schema.go
@@ -596,6 +596,48 @@ var (
},
},
}
+ // EmailBroadcastsColumns holds the columns for the "email_broadcasts" table.
+ EmailBroadcastsColumns = []*schema.Column{
+ {Name: "id", Type: field.TypeInt64, Increment: true},
+ {Name: "subject", Type: field.TypeString, Size: 200},
+ {Name: "body", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}},
+ {Name: "body_format", Type: field.TypeString, Size: 10, Default: "html"},
+ {Name: "recipients_mode", Type: field.TypeString, Size: 20, Default: "selected"},
+ {Name: "recipient_user_ids", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}},
+ {Name: "status", Type: field.TypeString, Size: 20, Default: "pending"},
+ {Name: "total_count", Type: field.TypeInt, Default: 0},
+ {Name: "success_count", Type: field.TypeInt, Default: 0},
+ {Name: "failed_count", Type: field.TypeInt, Default: 0},
+ {Name: "error_message", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
+ {Name: "created_by", Type: field.TypeInt64, Nullable: true},
+ {Name: "started_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
+ {Name: "finished_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
+ {Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
+ {Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
+ }
+ // EmailBroadcastsTable holds the schema information for the "email_broadcasts" table.
+ EmailBroadcastsTable = &schema.Table{
+ Name: "email_broadcasts",
+ Columns: EmailBroadcastsColumns,
+ PrimaryKey: []*schema.Column{EmailBroadcastsColumns[0]},
+ Indexes: []*schema.Index{
+ {
+ Name: "emailbroadcast_status",
+ Unique: false,
+ Columns: []*schema.Column{EmailBroadcastsColumns[6]},
+ },
+ {
+ Name: "emailbroadcast_created_at",
+ Unique: false,
+ Columns: []*schema.Column{EmailBroadcastsColumns[14]},
+ },
+ {
+ Name: "emailbroadcast_created_by",
+ Unique: false,
+ Columns: []*schema.Column{EmailBroadcastsColumns[11]},
+ },
+ },
+ }
// ErrorPassthroughRulesColumns holds the columns for the "error_passthrough_rules" table.
ErrorPassthroughRulesColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true},
@@ -1762,6 +1804,7 @@ var (
ChannelMonitorDailyRollupsTable,
ChannelMonitorHistoriesTable,
ChannelMonitorRequestTemplatesTable,
+ EmailBroadcastsTable,
ErrorPassthroughRulesTable,
GroupsTable,
IdempotencyRecordsTable,
@@ -1835,6 +1878,9 @@ func init() {
ChannelMonitorRequestTemplatesTable.Annotation = &entsql.Annotation{
Table: "channel_monitor_request_templates",
}
+ EmailBroadcastsTable.Annotation = &entsql.Annotation{
+ Table: "email_broadcasts",
+ }
ErrorPassthroughRulesTable.Annotation = &entsql.Annotation{
Table: "error_passthrough_rules",
}
diff --git a/backend/ent/mutation.go b/backend/ent/mutation.go
index 003e25d5015..cb693cfcba1 100644
--- a/backend/ent/mutation.go
+++ b/backend/ent/mutation.go
@@ -23,6 +23,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
+ "github.com/Wei-Shaw/sub2api/ent/emailbroadcast"
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
@@ -71,6 +72,7 @@ const (
TypeChannelMonitorDailyRollup = "ChannelMonitorDailyRollup"
TypeChannelMonitorHistory = "ChannelMonitorHistory"
TypeChannelMonitorRequestTemplate = "ChannelMonitorRequestTemplate"
+ TypeEmailBroadcast = "EmailBroadcast"
TypeErrorPassthroughRule = "ErrorPassthroughRule"
TypeGroup = "Group"
TypeIdempotencyRecord = "IdempotencyRecord"
@@ -13529,6 +13531,1339 @@ func (m *ChannelMonitorRequestTemplateMutation) ResetEdge(name string) error {
return fmt.Errorf("unknown ChannelMonitorRequestTemplate edge %s", name)
}
+// EmailBroadcastMutation represents an operation that mutates the EmailBroadcast nodes in the graph.
+type EmailBroadcastMutation struct {
+ config
+ op Op
+ typ string
+ id *int64
+ subject *string
+ body *string
+ body_format *string
+ recipients_mode *string
+ recipient_user_ids *[]int64
+ appendrecipient_user_ids []int64
+ status *string
+ total_count *int
+ addtotal_count *int
+ success_count *int
+ addsuccess_count *int
+ failed_count *int
+ addfailed_count *int
+ error_message *string
+ created_by *int64
+ addcreated_by *int64
+ started_at *time.Time
+ finished_at *time.Time
+ created_at *time.Time
+ updated_at *time.Time
+ clearedFields map[string]struct{}
+ done bool
+ oldValue func(context.Context) (*EmailBroadcast, error)
+ predicates []predicate.EmailBroadcast
+}
+
+var _ ent.Mutation = (*EmailBroadcastMutation)(nil)
+
+// emailbroadcastOption allows management of the mutation configuration using functional options.
+type emailbroadcastOption func(*EmailBroadcastMutation)
+
+// newEmailBroadcastMutation creates new mutation for the EmailBroadcast entity.
+func newEmailBroadcastMutation(c config, op Op, opts ...emailbroadcastOption) *EmailBroadcastMutation {
+ m := &EmailBroadcastMutation{
+ config: c,
+ op: op,
+ typ: TypeEmailBroadcast,
+ clearedFields: make(map[string]struct{}),
+ }
+ for _, opt := range opts {
+ opt(m)
+ }
+ return m
+}
+
+// withEmailBroadcastID sets the ID field of the mutation.
+func withEmailBroadcastID(id int64) emailbroadcastOption {
+ return func(m *EmailBroadcastMutation) {
+ var (
+ err error
+ once sync.Once
+ value *EmailBroadcast
+ )
+ m.oldValue = func(ctx context.Context) (*EmailBroadcast, error) {
+ once.Do(func() {
+ if m.done {
+ err = errors.New("querying old values post mutation is not allowed")
+ } else {
+ value, err = m.Client().EmailBroadcast.Get(ctx, id)
+ }
+ })
+ return value, err
+ }
+ m.id = &id
+ }
+}
+
+// withEmailBroadcast sets the old EmailBroadcast of the mutation.
+func withEmailBroadcast(node *EmailBroadcast) emailbroadcastOption {
+ return func(m *EmailBroadcastMutation) {
+ m.oldValue = func(context.Context) (*EmailBroadcast, error) {
+ return node, nil
+ }
+ m.id = &node.ID
+ }
+}
+
+// Client returns a new `ent.Client` from the mutation. If the mutation was
+// executed in a transaction (ent.Tx), a transactional client is returned.
+func (m EmailBroadcastMutation) Client() *Client {
+ client := &Client{config: m.config}
+ client.init()
+ return client
+}
+
+// Tx returns an `ent.Tx` for mutations that were executed in transactions;
+// it returns an error otherwise.
+func (m EmailBroadcastMutation) Tx() (*Tx, error) {
+ if _, ok := m.driver.(*txDriver); !ok {
+ return nil, errors.New("ent: mutation is not running in a transaction")
+ }
+ tx := &Tx{config: m.config}
+ tx.init()
+ return tx, nil
+}
+
+// ID returns the ID value in the mutation. Note that the ID is only available
+// if it was provided to the builder or after it was returned from the database.
+func (m *EmailBroadcastMutation) ID() (id int64, exists bool) {
+ if m.id == nil {
+ return
+ }
+ return *m.id, true
+}
+
+// IDs queries the database and returns the entity ids that match the mutation's predicate.
+// That means, if the mutation is applied within a transaction with an isolation level such
+// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated
+// or updated by the mutation.
+func (m *EmailBroadcastMutation) IDs(ctx context.Context) ([]int64, error) {
+ switch {
+ case m.op.Is(OpUpdateOne | OpDeleteOne):
+ id, exists := m.ID()
+ if exists {
+ return []int64{id}, nil
+ }
+ fallthrough
+ case m.op.Is(OpUpdate | OpDelete):
+ return m.Client().EmailBroadcast.Query().Where(m.predicates...).IDs(ctx)
+ default:
+ return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
+ }
+}
+
+// SetSubject sets the "subject" field.
+func (m *EmailBroadcastMutation) SetSubject(s string) {
+ m.subject = &s
+}
+
+// Subject returns the value of the "subject" field in the mutation.
+func (m *EmailBroadcastMutation) Subject() (r string, exists bool) {
+ v := m.subject
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldSubject returns the old "subject" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldSubject(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldSubject is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldSubject requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldSubject: %w", err)
+ }
+ return oldValue.Subject, nil
+}
+
+// ResetSubject resets all changes to the "subject" field.
+func (m *EmailBroadcastMutation) ResetSubject() {
+ m.subject = nil
+}
+
+// SetBody sets the "body" field.
+func (m *EmailBroadcastMutation) SetBody(s string) {
+ m.body = &s
+}
+
+// Body returns the value of the "body" field in the mutation.
+func (m *EmailBroadcastMutation) Body() (r string, exists bool) {
+ v := m.body
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldBody returns the old "body" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldBody(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldBody is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldBody requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldBody: %w", err)
+ }
+ return oldValue.Body, nil
+}
+
+// ResetBody resets all changes to the "body" field.
+func (m *EmailBroadcastMutation) ResetBody() {
+ m.body = nil
+}
+
+// SetBodyFormat sets the "body_format" field.
+func (m *EmailBroadcastMutation) SetBodyFormat(s string) {
+ m.body_format = &s
+}
+
+// BodyFormat returns the value of the "body_format" field in the mutation.
+func (m *EmailBroadcastMutation) BodyFormat() (r string, exists bool) {
+ v := m.body_format
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldBodyFormat returns the old "body_format" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldBodyFormat(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldBodyFormat is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldBodyFormat requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldBodyFormat: %w", err)
+ }
+ return oldValue.BodyFormat, nil
+}
+
+// ResetBodyFormat resets all changes to the "body_format" field.
+func (m *EmailBroadcastMutation) ResetBodyFormat() {
+ m.body_format = nil
+}
+
+// SetRecipientsMode sets the "recipients_mode" field.
+func (m *EmailBroadcastMutation) SetRecipientsMode(s string) {
+ m.recipients_mode = &s
+}
+
+// RecipientsMode returns the value of the "recipients_mode" field in the mutation.
+func (m *EmailBroadcastMutation) RecipientsMode() (r string, exists bool) {
+ v := m.recipients_mode
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldRecipientsMode returns the old "recipients_mode" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldRecipientsMode(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldRecipientsMode is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldRecipientsMode requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldRecipientsMode: %w", err)
+ }
+ return oldValue.RecipientsMode, nil
+}
+
+// ResetRecipientsMode resets all changes to the "recipients_mode" field.
+func (m *EmailBroadcastMutation) ResetRecipientsMode() {
+ m.recipients_mode = nil
+}
+
+// SetRecipientUserIds sets the "recipient_user_ids" field.
+func (m *EmailBroadcastMutation) SetRecipientUserIds(i []int64) {
+ m.recipient_user_ids = &i
+ m.appendrecipient_user_ids = nil
+}
+
+// RecipientUserIds returns the value of the "recipient_user_ids" field in the mutation.
+func (m *EmailBroadcastMutation) RecipientUserIds() (r []int64, exists bool) {
+ v := m.recipient_user_ids
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldRecipientUserIds returns the old "recipient_user_ids" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldRecipientUserIds(ctx context.Context) (v []int64, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldRecipientUserIds is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldRecipientUserIds requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldRecipientUserIds: %w", err)
+ }
+ return oldValue.RecipientUserIds, nil
+}
+
+// AppendRecipientUserIds adds i to the "recipient_user_ids" field.
+func (m *EmailBroadcastMutation) AppendRecipientUserIds(i []int64) {
+ m.appendrecipient_user_ids = append(m.appendrecipient_user_ids, i...)
+}
+
+// AppendedRecipientUserIds returns the list of values that were appended to the "recipient_user_ids" field in this mutation.
+func (m *EmailBroadcastMutation) AppendedRecipientUserIds() ([]int64, bool) {
+ if len(m.appendrecipient_user_ids) == 0 {
+ return nil, false
+ }
+ return m.appendrecipient_user_ids, true
+}
+
+// ClearRecipientUserIds clears the value of the "recipient_user_ids" field.
+func (m *EmailBroadcastMutation) ClearRecipientUserIds() {
+ m.recipient_user_ids = nil
+ m.appendrecipient_user_ids = nil
+ m.clearedFields[emailbroadcast.FieldRecipientUserIds] = struct{}{}
+}
+
+// RecipientUserIdsCleared returns if the "recipient_user_ids" field was cleared in this mutation.
+func (m *EmailBroadcastMutation) RecipientUserIdsCleared() bool {
+ _, ok := m.clearedFields[emailbroadcast.FieldRecipientUserIds]
+ return ok
+}
+
+// ResetRecipientUserIds resets all changes to the "recipient_user_ids" field.
+func (m *EmailBroadcastMutation) ResetRecipientUserIds() {
+ m.recipient_user_ids = nil
+ m.appendrecipient_user_ids = nil
+ delete(m.clearedFields, emailbroadcast.FieldRecipientUserIds)
+}
+
+// SetStatus sets the "status" field.
+func (m *EmailBroadcastMutation) SetStatus(s string) {
+ m.status = &s
+}
+
+// Status returns the value of the "status" field in the mutation.
+func (m *EmailBroadcastMutation) Status() (r string, exists bool) {
+ v := m.status
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldStatus returns the old "status" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldStatus(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldStatus is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldStatus requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldStatus: %w", err)
+ }
+ return oldValue.Status, nil
+}
+
+// ResetStatus resets all changes to the "status" field.
+func (m *EmailBroadcastMutation) ResetStatus() {
+ m.status = nil
+}
+
+// SetTotalCount sets the "total_count" field.
+func (m *EmailBroadcastMutation) SetTotalCount(i int) {
+ m.total_count = &i
+ m.addtotal_count = nil
+}
+
+// TotalCount returns the value of the "total_count" field in the mutation.
+func (m *EmailBroadcastMutation) TotalCount() (r int, exists bool) {
+ v := m.total_count
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldTotalCount returns the old "total_count" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldTotalCount(ctx context.Context) (v int, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldTotalCount is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldTotalCount requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldTotalCount: %w", err)
+ }
+ return oldValue.TotalCount, nil
+}
+
+// AddTotalCount adds i to the "total_count" field.
+func (m *EmailBroadcastMutation) AddTotalCount(i int) {
+ if m.addtotal_count != nil {
+ *m.addtotal_count += i
+ } else {
+ m.addtotal_count = &i
+ }
+}
+
+// AddedTotalCount returns the value that was added to the "total_count" field in this mutation.
+func (m *EmailBroadcastMutation) AddedTotalCount() (r int, exists bool) {
+ v := m.addtotal_count
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// ResetTotalCount resets all changes to the "total_count" field.
+func (m *EmailBroadcastMutation) ResetTotalCount() {
+ m.total_count = nil
+ m.addtotal_count = nil
+}
+
+// SetSuccessCount sets the "success_count" field.
+func (m *EmailBroadcastMutation) SetSuccessCount(i int) {
+ m.success_count = &i
+ m.addsuccess_count = nil
+}
+
+// SuccessCount returns the value of the "success_count" field in the mutation.
+func (m *EmailBroadcastMutation) SuccessCount() (r int, exists bool) {
+ v := m.success_count
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldSuccessCount returns the old "success_count" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldSuccessCount(ctx context.Context) (v int, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldSuccessCount is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldSuccessCount requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldSuccessCount: %w", err)
+ }
+ return oldValue.SuccessCount, nil
+}
+
+// AddSuccessCount adds i to the "success_count" field.
+func (m *EmailBroadcastMutation) AddSuccessCount(i int) {
+ if m.addsuccess_count != nil {
+ *m.addsuccess_count += i
+ } else {
+ m.addsuccess_count = &i
+ }
+}
+
+// AddedSuccessCount returns the value that was added to the "success_count" field in this mutation.
+func (m *EmailBroadcastMutation) AddedSuccessCount() (r int, exists bool) {
+ v := m.addsuccess_count
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// ResetSuccessCount resets all changes to the "success_count" field.
+func (m *EmailBroadcastMutation) ResetSuccessCount() {
+ m.success_count = nil
+ m.addsuccess_count = nil
+}
+
+// SetFailedCount sets the "failed_count" field.
+func (m *EmailBroadcastMutation) SetFailedCount(i int) {
+ m.failed_count = &i
+ m.addfailed_count = nil
+}
+
+// FailedCount returns the value of the "failed_count" field in the mutation.
+func (m *EmailBroadcastMutation) FailedCount() (r int, exists bool) {
+ v := m.failed_count
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldFailedCount returns the old "failed_count" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldFailedCount(ctx context.Context) (v int, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldFailedCount is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldFailedCount requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldFailedCount: %w", err)
+ }
+ return oldValue.FailedCount, nil
+}
+
+// AddFailedCount adds i to the "failed_count" field.
+func (m *EmailBroadcastMutation) AddFailedCount(i int) {
+ if m.addfailed_count != nil {
+ *m.addfailed_count += i
+ } else {
+ m.addfailed_count = &i
+ }
+}
+
+// AddedFailedCount returns the value that was added to the "failed_count" field in this mutation.
+func (m *EmailBroadcastMutation) AddedFailedCount() (r int, exists bool) {
+ v := m.addfailed_count
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// ResetFailedCount resets all changes to the "failed_count" field.
+func (m *EmailBroadcastMutation) ResetFailedCount() {
+ m.failed_count = nil
+ m.addfailed_count = nil
+}
+
+// SetErrorMessage sets the "error_message" field.
+func (m *EmailBroadcastMutation) SetErrorMessage(s string) {
+ m.error_message = &s
+}
+
+// ErrorMessage returns the value of the "error_message" field in the mutation.
+func (m *EmailBroadcastMutation) ErrorMessage() (r string, exists bool) {
+ v := m.error_message
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldErrorMessage returns the old "error_message" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldErrorMessage(ctx context.Context) (v *string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldErrorMessage is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldErrorMessage requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldErrorMessage: %w", err)
+ }
+ return oldValue.ErrorMessage, nil
+}
+
+// ClearErrorMessage clears the value of the "error_message" field.
+func (m *EmailBroadcastMutation) ClearErrorMessage() {
+ m.error_message = nil
+ m.clearedFields[emailbroadcast.FieldErrorMessage] = struct{}{}
+}
+
+// ErrorMessageCleared returns if the "error_message" field was cleared in this mutation.
+func (m *EmailBroadcastMutation) ErrorMessageCleared() bool {
+ _, ok := m.clearedFields[emailbroadcast.FieldErrorMessage]
+ return ok
+}
+
+// ResetErrorMessage resets all changes to the "error_message" field.
+func (m *EmailBroadcastMutation) ResetErrorMessage() {
+ m.error_message = nil
+ delete(m.clearedFields, emailbroadcast.FieldErrorMessage)
+}
+
+// SetCreatedBy sets the "created_by" field.
+func (m *EmailBroadcastMutation) SetCreatedBy(i int64) {
+ m.created_by = &i
+ m.addcreated_by = nil
+}
+
+// CreatedBy returns the value of the "created_by" field in the mutation.
+func (m *EmailBroadcastMutation) CreatedBy() (r int64, exists bool) {
+ v := m.created_by
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldCreatedBy returns the old "created_by" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldCreatedBy(ctx context.Context) (v *int64, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldCreatedBy requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err)
+ }
+ return oldValue.CreatedBy, nil
+}
+
+// AddCreatedBy adds i to the "created_by" field.
+func (m *EmailBroadcastMutation) AddCreatedBy(i int64) {
+ if m.addcreated_by != nil {
+ *m.addcreated_by += i
+ } else {
+ m.addcreated_by = &i
+ }
+}
+
+// AddedCreatedBy returns the value that was added to the "created_by" field in this mutation.
+func (m *EmailBroadcastMutation) AddedCreatedBy() (r int64, exists bool) {
+ v := m.addcreated_by
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// ClearCreatedBy clears the value of the "created_by" field.
+func (m *EmailBroadcastMutation) ClearCreatedBy() {
+ m.created_by = nil
+ m.addcreated_by = nil
+ m.clearedFields[emailbroadcast.FieldCreatedBy] = struct{}{}
+}
+
+// CreatedByCleared returns if the "created_by" field was cleared in this mutation.
+func (m *EmailBroadcastMutation) CreatedByCleared() bool {
+ _, ok := m.clearedFields[emailbroadcast.FieldCreatedBy]
+ return ok
+}
+
+// ResetCreatedBy resets all changes to the "created_by" field.
+func (m *EmailBroadcastMutation) ResetCreatedBy() {
+ m.created_by = nil
+ m.addcreated_by = nil
+ delete(m.clearedFields, emailbroadcast.FieldCreatedBy)
+}
+
+// SetStartedAt sets the "started_at" field.
+func (m *EmailBroadcastMutation) SetStartedAt(t time.Time) {
+ m.started_at = &t
+}
+
+// StartedAt returns the value of the "started_at" field in the mutation.
+func (m *EmailBroadcastMutation) StartedAt() (r time.Time, exists bool) {
+ v := m.started_at
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldStartedAt returns the old "started_at" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldStartedAt(ctx context.Context) (v *time.Time, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldStartedAt is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldStartedAt requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldStartedAt: %w", err)
+ }
+ return oldValue.StartedAt, nil
+}
+
+// ClearStartedAt clears the value of the "started_at" field.
+func (m *EmailBroadcastMutation) ClearStartedAt() {
+ m.started_at = nil
+ m.clearedFields[emailbroadcast.FieldStartedAt] = struct{}{}
+}
+
+// StartedAtCleared returns if the "started_at" field was cleared in this mutation.
+func (m *EmailBroadcastMutation) StartedAtCleared() bool {
+ _, ok := m.clearedFields[emailbroadcast.FieldStartedAt]
+ return ok
+}
+
+// ResetStartedAt resets all changes to the "started_at" field.
+func (m *EmailBroadcastMutation) ResetStartedAt() {
+ m.started_at = nil
+ delete(m.clearedFields, emailbroadcast.FieldStartedAt)
+}
+
+// SetFinishedAt sets the "finished_at" field.
+func (m *EmailBroadcastMutation) SetFinishedAt(t time.Time) {
+ m.finished_at = &t
+}
+
+// FinishedAt returns the value of the "finished_at" field in the mutation.
+func (m *EmailBroadcastMutation) FinishedAt() (r time.Time, exists bool) {
+ v := m.finished_at
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldFinishedAt returns the old "finished_at" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldFinishedAt(ctx context.Context) (v *time.Time, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldFinishedAt is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldFinishedAt requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldFinishedAt: %w", err)
+ }
+ return oldValue.FinishedAt, nil
+}
+
+// ClearFinishedAt clears the value of the "finished_at" field.
+func (m *EmailBroadcastMutation) ClearFinishedAt() {
+ m.finished_at = nil
+ m.clearedFields[emailbroadcast.FieldFinishedAt] = struct{}{}
+}
+
+// FinishedAtCleared returns if the "finished_at" field was cleared in this mutation.
+func (m *EmailBroadcastMutation) FinishedAtCleared() bool {
+ _, ok := m.clearedFields[emailbroadcast.FieldFinishedAt]
+ return ok
+}
+
+// ResetFinishedAt resets all changes to the "finished_at" field.
+func (m *EmailBroadcastMutation) ResetFinishedAt() {
+ m.finished_at = nil
+ delete(m.clearedFields, emailbroadcast.FieldFinishedAt)
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (m *EmailBroadcastMutation) SetCreatedAt(t time.Time) {
+ m.created_at = &t
+}
+
+// CreatedAt returns the value of the "created_at" field in the mutation.
+func (m *EmailBroadcastMutation) CreatedAt() (r time.Time, exists bool) {
+ v := m.created_at
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldCreatedAt returns the old "created_at" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldCreatedAt requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err)
+ }
+ return oldValue.CreatedAt, nil
+}
+
+// ResetCreatedAt resets all changes to the "created_at" field.
+func (m *EmailBroadcastMutation) ResetCreatedAt() {
+ m.created_at = nil
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (m *EmailBroadcastMutation) SetUpdatedAt(t time.Time) {
+ m.updated_at = &t
+}
+
+// UpdatedAt returns the value of the "updated_at" field in the mutation.
+func (m *EmailBroadcastMutation) UpdatedAt() (r time.Time, exists bool) {
+ v := m.updated_at
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldUpdatedAt returns the old "updated_at" field's value of the EmailBroadcast entity.
+// If the EmailBroadcast object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *EmailBroadcastMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldUpdatedAt requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err)
+ }
+ return oldValue.UpdatedAt, nil
+}
+
+// ResetUpdatedAt resets all changes to the "updated_at" field.
+func (m *EmailBroadcastMutation) ResetUpdatedAt() {
+ m.updated_at = nil
+}
+
+// Where appends a list predicates to the EmailBroadcastMutation builder.
+func (m *EmailBroadcastMutation) Where(ps ...predicate.EmailBroadcast) {
+ m.predicates = append(m.predicates, ps...)
+}
+
+// WhereP appends storage-level predicates to the EmailBroadcastMutation builder. Using this method,
+// users can use type-assertion to append predicates that do not depend on any generated package.
+func (m *EmailBroadcastMutation) WhereP(ps ...func(*sql.Selector)) {
+ p := make([]predicate.EmailBroadcast, len(ps))
+ for i := range ps {
+ p[i] = ps[i]
+ }
+ m.Where(p...)
+}
+
+// Op returns the operation name.
+func (m *EmailBroadcastMutation) Op() Op {
+ return m.op
+}
+
+// SetOp allows setting the mutation operation.
+func (m *EmailBroadcastMutation) SetOp(op Op) {
+ m.op = op
+}
+
+// Type returns the node type of this mutation (EmailBroadcast).
+func (m *EmailBroadcastMutation) Type() string {
+ return m.typ
+}
+
+// Fields returns all fields that were changed during this mutation. Note that in
+// order to get all numeric fields that were incremented/decremented, call
+// AddedFields().
+func (m *EmailBroadcastMutation) Fields() []string {
+ fields := make([]string, 0, 15)
+ if m.subject != nil {
+ fields = append(fields, emailbroadcast.FieldSubject)
+ }
+ if m.body != nil {
+ fields = append(fields, emailbroadcast.FieldBody)
+ }
+ if m.body_format != nil {
+ fields = append(fields, emailbroadcast.FieldBodyFormat)
+ }
+ if m.recipients_mode != nil {
+ fields = append(fields, emailbroadcast.FieldRecipientsMode)
+ }
+ if m.recipient_user_ids != nil {
+ fields = append(fields, emailbroadcast.FieldRecipientUserIds)
+ }
+ if m.status != nil {
+ fields = append(fields, emailbroadcast.FieldStatus)
+ }
+ if m.total_count != nil {
+ fields = append(fields, emailbroadcast.FieldTotalCount)
+ }
+ if m.success_count != nil {
+ fields = append(fields, emailbroadcast.FieldSuccessCount)
+ }
+ if m.failed_count != nil {
+ fields = append(fields, emailbroadcast.FieldFailedCount)
+ }
+ if m.error_message != nil {
+ fields = append(fields, emailbroadcast.FieldErrorMessage)
+ }
+ if m.created_by != nil {
+ fields = append(fields, emailbroadcast.FieldCreatedBy)
+ }
+ if m.started_at != nil {
+ fields = append(fields, emailbroadcast.FieldStartedAt)
+ }
+ if m.finished_at != nil {
+ fields = append(fields, emailbroadcast.FieldFinishedAt)
+ }
+ if m.created_at != nil {
+ fields = append(fields, emailbroadcast.FieldCreatedAt)
+ }
+ if m.updated_at != nil {
+ fields = append(fields, emailbroadcast.FieldUpdatedAt)
+ }
+ return fields
+}
+
+// Field returns the value of a field with the given name. The second boolean
+// return value indicates that this field was not set, or was not defined in the
+// schema.
+func (m *EmailBroadcastMutation) Field(name string) (ent.Value, bool) {
+ switch name {
+ case emailbroadcast.FieldSubject:
+ return m.Subject()
+ case emailbroadcast.FieldBody:
+ return m.Body()
+ case emailbroadcast.FieldBodyFormat:
+ return m.BodyFormat()
+ case emailbroadcast.FieldRecipientsMode:
+ return m.RecipientsMode()
+ case emailbroadcast.FieldRecipientUserIds:
+ return m.RecipientUserIds()
+ case emailbroadcast.FieldStatus:
+ return m.Status()
+ case emailbroadcast.FieldTotalCount:
+ return m.TotalCount()
+ case emailbroadcast.FieldSuccessCount:
+ return m.SuccessCount()
+ case emailbroadcast.FieldFailedCount:
+ return m.FailedCount()
+ case emailbroadcast.FieldErrorMessage:
+ return m.ErrorMessage()
+ case emailbroadcast.FieldCreatedBy:
+ return m.CreatedBy()
+ case emailbroadcast.FieldStartedAt:
+ return m.StartedAt()
+ case emailbroadcast.FieldFinishedAt:
+ return m.FinishedAt()
+ case emailbroadcast.FieldCreatedAt:
+ return m.CreatedAt()
+ case emailbroadcast.FieldUpdatedAt:
+ return m.UpdatedAt()
+ }
+ return nil, false
+}
+
+// OldField returns the old value of the field from the database. An error is
+// returned if the mutation operation is not UpdateOne, or the query to the
+// database failed.
+func (m *EmailBroadcastMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
+ switch name {
+ case emailbroadcast.FieldSubject:
+ return m.OldSubject(ctx)
+ case emailbroadcast.FieldBody:
+ return m.OldBody(ctx)
+ case emailbroadcast.FieldBodyFormat:
+ return m.OldBodyFormat(ctx)
+ case emailbroadcast.FieldRecipientsMode:
+ return m.OldRecipientsMode(ctx)
+ case emailbroadcast.FieldRecipientUserIds:
+ return m.OldRecipientUserIds(ctx)
+ case emailbroadcast.FieldStatus:
+ return m.OldStatus(ctx)
+ case emailbroadcast.FieldTotalCount:
+ return m.OldTotalCount(ctx)
+ case emailbroadcast.FieldSuccessCount:
+ return m.OldSuccessCount(ctx)
+ case emailbroadcast.FieldFailedCount:
+ return m.OldFailedCount(ctx)
+ case emailbroadcast.FieldErrorMessage:
+ return m.OldErrorMessage(ctx)
+ case emailbroadcast.FieldCreatedBy:
+ return m.OldCreatedBy(ctx)
+ case emailbroadcast.FieldStartedAt:
+ return m.OldStartedAt(ctx)
+ case emailbroadcast.FieldFinishedAt:
+ return m.OldFinishedAt(ctx)
+ case emailbroadcast.FieldCreatedAt:
+ return m.OldCreatedAt(ctx)
+ case emailbroadcast.FieldUpdatedAt:
+ return m.OldUpdatedAt(ctx)
+ }
+ return nil, fmt.Errorf("unknown EmailBroadcast field %s", name)
+}
+
+// SetField sets the value of a field with the given name. It returns an error if
+// the field is not defined in the schema, or if the type mismatched the field
+// type.
+func (m *EmailBroadcastMutation) SetField(name string, value ent.Value) error {
+ switch name {
+ case emailbroadcast.FieldSubject:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetSubject(v)
+ return nil
+ case emailbroadcast.FieldBody:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetBody(v)
+ return nil
+ case emailbroadcast.FieldBodyFormat:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetBodyFormat(v)
+ return nil
+ case emailbroadcast.FieldRecipientsMode:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetRecipientsMode(v)
+ return nil
+ case emailbroadcast.FieldRecipientUserIds:
+ v, ok := value.([]int64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetRecipientUserIds(v)
+ return nil
+ case emailbroadcast.FieldStatus:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetStatus(v)
+ return nil
+ case emailbroadcast.FieldTotalCount:
+ v, ok := value.(int)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetTotalCount(v)
+ return nil
+ case emailbroadcast.FieldSuccessCount:
+ v, ok := value.(int)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetSuccessCount(v)
+ return nil
+ case emailbroadcast.FieldFailedCount:
+ v, ok := value.(int)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetFailedCount(v)
+ return nil
+ case emailbroadcast.FieldErrorMessage:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetErrorMessage(v)
+ return nil
+ case emailbroadcast.FieldCreatedBy:
+ v, ok := value.(int64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetCreatedBy(v)
+ return nil
+ case emailbroadcast.FieldStartedAt:
+ v, ok := value.(time.Time)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetStartedAt(v)
+ return nil
+ case emailbroadcast.FieldFinishedAt:
+ v, ok := value.(time.Time)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetFinishedAt(v)
+ return nil
+ case emailbroadcast.FieldCreatedAt:
+ v, ok := value.(time.Time)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetCreatedAt(v)
+ return nil
+ case emailbroadcast.FieldUpdatedAt:
+ v, ok := value.(time.Time)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetUpdatedAt(v)
+ return nil
+ }
+ return fmt.Errorf("unknown EmailBroadcast field %s", name)
+}
+
+// AddedFields returns all numeric fields that were incremented/decremented during
+// this mutation.
+func (m *EmailBroadcastMutation) AddedFields() []string {
+ var fields []string
+ if m.addtotal_count != nil {
+ fields = append(fields, emailbroadcast.FieldTotalCount)
+ }
+ if m.addsuccess_count != nil {
+ fields = append(fields, emailbroadcast.FieldSuccessCount)
+ }
+ if m.addfailed_count != nil {
+ fields = append(fields, emailbroadcast.FieldFailedCount)
+ }
+ if m.addcreated_by != nil {
+ fields = append(fields, emailbroadcast.FieldCreatedBy)
+ }
+ return fields
+}
+
+// AddedField returns the numeric value that was incremented/decremented on a field
+// with the given name. The second boolean return value indicates that this field
+// was not set, or was not defined in the schema.
+func (m *EmailBroadcastMutation) AddedField(name string) (ent.Value, bool) {
+ switch name {
+ case emailbroadcast.FieldTotalCount:
+ return m.AddedTotalCount()
+ case emailbroadcast.FieldSuccessCount:
+ return m.AddedSuccessCount()
+ case emailbroadcast.FieldFailedCount:
+ return m.AddedFailedCount()
+ case emailbroadcast.FieldCreatedBy:
+ return m.AddedCreatedBy()
+ }
+ return nil, false
+}
+
+// AddField adds the value to the field with the given name. It returns an error if
+// the field is not defined in the schema, or if the type mismatched the field
+// type.
+func (m *EmailBroadcastMutation) AddField(name string, value ent.Value) error {
+ switch name {
+ case emailbroadcast.FieldTotalCount:
+ v, ok := value.(int)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.AddTotalCount(v)
+ return nil
+ case emailbroadcast.FieldSuccessCount:
+ v, ok := value.(int)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.AddSuccessCount(v)
+ return nil
+ case emailbroadcast.FieldFailedCount:
+ v, ok := value.(int)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.AddFailedCount(v)
+ return nil
+ case emailbroadcast.FieldCreatedBy:
+ v, ok := value.(int64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.AddCreatedBy(v)
+ return nil
+ }
+ return fmt.Errorf("unknown EmailBroadcast numeric field %s", name)
+}
+
+// ClearedFields returns all nullable fields that were cleared during this
+// mutation.
+func (m *EmailBroadcastMutation) ClearedFields() []string {
+ var fields []string
+ if m.FieldCleared(emailbroadcast.FieldRecipientUserIds) {
+ fields = append(fields, emailbroadcast.FieldRecipientUserIds)
+ }
+ if m.FieldCleared(emailbroadcast.FieldErrorMessage) {
+ fields = append(fields, emailbroadcast.FieldErrorMessage)
+ }
+ if m.FieldCleared(emailbroadcast.FieldCreatedBy) {
+ fields = append(fields, emailbroadcast.FieldCreatedBy)
+ }
+ if m.FieldCleared(emailbroadcast.FieldStartedAt) {
+ fields = append(fields, emailbroadcast.FieldStartedAt)
+ }
+ if m.FieldCleared(emailbroadcast.FieldFinishedAt) {
+ fields = append(fields, emailbroadcast.FieldFinishedAt)
+ }
+ return fields
+}
+
+// FieldCleared returns a boolean indicating if a field with the given name was
+// cleared in this mutation.
+func (m *EmailBroadcastMutation) FieldCleared(name string) bool {
+ _, ok := m.clearedFields[name]
+ return ok
+}
+
+// ClearField clears the value of the field with the given name. It returns an
+// error if the field is not defined in the schema.
+func (m *EmailBroadcastMutation) ClearField(name string) error {
+ switch name {
+ case emailbroadcast.FieldRecipientUserIds:
+ m.ClearRecipientUserIds()
+ return nil
+ case emailbroadcast.FieldErrorMessage:
+ m.ClearErrorMessage()
+ return nil
+ case emailbroadcast.FieldCreatedBy:
+ m.ClearCreatedBy()
+ return nil
+ case emailbroadcast.FieldStartedAt:
+ m.ClearStartedAt()
+ return nil
+ case emailbroadcast.FieldFinishedAt:
+ m.ClearFinishedAt()
+ return nil
+ }
+ return fmt.Errorf("unknown EmailBroadcast nullable field %s", name)
+}
+
+// ResetField resets all changes in the mutation for the field with the given name.
+// It returns an error if the field is not defined in the schema.
+func (m *EmailBroadcastMutation) ResetField(name string) error {
+ switch name {
+ case emailbroadcast.FieldSubject:
+ m.ResetSubject()
+ return nil
+ case emailbroadcast.FieldBody:
+ m.ResetBody()
+ return nil
+ case emailbroadcast.FieldBodyFormat:
+ m.ResetBodyFormat()
+ return nil
+ case emailbroadcast.FieldRecipientsMode:
+ m.ResetRecipientsMode()
+ return nil
+ case emailbroadcast.FieldRecipientUserIds:
+ m.ResetRecipientUserIds()
+ return nil
+ case emailbroadcast.FieldStatus:
+ m.ResetStatus()
+ return nil
+ case emailbroadcast.FieldTotalCount:
+ m.ResetTotalCount()
+ return nil
+ case emailbroadcast.FieldSuccessCount:
+ m.ResetSuccessCount()
+ return nil
+ case emailbroadcast.FieldFailedCount:
+ m.ResetFailedCount()
+ return nil
+ case emailbroadcast.FieldErrorMessage:
+ m.ResetErrorMessage()
+ return nil
+ case emailbroadcast.FieldCreatedBy:
+ m.ResetCreatedBy()
+ return nil
+ case emailbroadcast.FieldStartedAt:
+ m.ResetStartedAt()
+ return nil
+ case emailbroadcast.FieldFinishedAt:
+ m.ResetFinishedAt()
+ return nil
+ case emailbroadcast.FieldCreatedAt:
+ m.ResetCreatedAt()
+ return nil
+ case emailbroadcast.FieldUpdatedAt:
+ m.ResetUpdatedAt()
+ return nil
+ }
+ return fmt.Errorf("unknown EmailBroadcast field %s", name)
+}
+
+// AddedEdges returns all edge names that were set/added in this mutation.
+func (m *EmailBroadcastMutation) AddedEdges() []string {
+ edges := make([]string, 0, 0)
+ return edges
+}
+
+// AddedIDs returns all IDs (to other nodes) that were added for the given edge
+// name in this mutation.
+func (m *EmailBroadcastMutation) AddedIDs(name string) []ent.Value {
+ return nil
+}
+
+// RemovedEdges returns all edge names that were removed in this mutation.
+func (m *EmailBroadcastMutation) RemovedEdges() []string {
+ edges := make([]string, 0, 0)
+ return edges
+}
+
+// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
+// the given name in this mutation.
+func (m *EmailBroadcastMutation) RemovedIDs(name string) []ent.Value {
+ return nil
+}
+
+// ClearedEdges returns all edge names that were cleared in this mutation.
+func (m *EmailBroadcastMutation) ClearedEdges() []string {
+ edges := make([]string, 0, 0)
+ return edges
+}
+
+// EdgeCleared returns a boolean which indicates if the edge with the given name
+// was cleared in this mutation.
+func (m *EmailBroadcastMutation) EdgeCleared(name string) bool {
+ return false
+}
+
+// ClearEdge clears the value of the edge with the given name. It returns an error
+// if that edge is not defined in the schema.
+func (m *EmailBroadcastMutation) ClearEdge(name string) error {
+ return fmt.Errorf("unknown EmailBroadcast unique edge %s", name)
+}
+
+// ResetEdge resets all changes to the edge with the given name in this mutation.
+// It returns an error if the edge is not defined in the schema.
+func (m *EmailBroadcastMutation) ResetEdge(name string) error {
+ return fmt.Errorf("unknown EmailBroadcast edge %s", name)
+}
+
// ErrorPassthroughRuleMutation represents an operation that mutates the ErrorPassthroughRule nodes in the graph.
type ErrorPassthroughRuleMutation struct {
config
diff --git a/backend/ent/predicate/predicate.go b/backend/ent/predicate/predicate.go
index ab4d7d1827d..95207037980 100644
--- a/backend/ent/predicate/predicate.go
+++ b/backend/ent/predicate/predicate.go
@@ -39,6 +39,9 @@ type ChannelMonitorHistory func(*sql.Selector)
// ChannelMonitorRequestTemplate is the predicate function for channelmonitorrequesttemplate builders.
type ChannelMonitorRequestTemplate func(*sql.Selector)
+// EmailBroadcast is the predicate function for emailbroadcast builders.
+type EmailBroadcast func(*sql.Selector)
+
// ErrorPassthroughRule is the predicate function for errorpassthroughrule builders.
type ErrorPassthroughRule func(*sql.Selector)
diff --git a/backend/ent/runtime/runtime.go b/backend/ent/runtime/runtime.go
index fdb837e805f..f05bbeb7a9e 100644
--- a/backend/ent/runtime/runtime.go
+++ b/backend/ent/runtime/runtime.go
@@ -16,6 +16,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
+ "github.com/Wei-Shaw/sub2api/ent/emailbroadcast"
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
@@ -690,6 +691,76 @@ func init() {
channelmonitorrequesttemplate.DefaultBodyOverrideMode = channelmonitorrequesttemplateDescBodyOverrideMode.Default.(string)
// channelmonitorrequesttemplate.BodyOverrideModeValidator is a validator for the "body_override_mode" field. It is called by the builders before save.
channelmonitorrequesttemplate.BodyOverrideModeValidator = channelmonitorrequesttemplateDescBodyOverrideMode.Validators[0].(func(string) error)
+ emailbroadcastFields := schema.EmailBroadcast{}.Fields()
+ _ = emailbroadcastFields
+ // emailbroadcastDescSubject is the schema descriptor for subject field.
+ emailbroadcastDescSubject := emailbroadcastFields[0].Descriptor()
+ // emailbroadcast.SubjectValidator is a validator for the "subject" field. It is called by the builders before save.
+ emailbroadcast.SubjectValidator = func() func(string) error {
+ validators := emailbroadcastDescSubject.Validators
+ fns := [...]func(string) error{
+ validators[0].(func(string) error),
+ validators[1].(func(string) error),
+ }
+ return func(subject string) error {
+ for _, fn := range fns {
+ if err := fn(subject); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+ }()
+ // emailbroadcastDescBody is the schema descriptor for body field.
+ emailbroadcastDescBody := emailbroadcastFields[1].Descriptor()
+ // emailbroadcast.BodyValidator is a validator for the "body" field. It is called by the builders before save.
+ emailbroadcast.BodyValidator = emailbroadcastDescBody.Validators[0].(func(string) error)
+ // emailbroadcastDescBodyFormat is the schema descriptor for body_format field.
+ emailbroadcastDescBodyFormat := emailbroadcastFields[2].Descriptor()
+ // emailbroadcast.DefaultBodyFormat holds the default value on creation for the body_format field.
+ emailbroadcast.DefaultBodyFormat = emailbroadcastDescBodyFormat.Default.(string)
+ // emailbroadcast.BodyFormatValidator is a validator for the "body_format" field. It is called by the builders before save.
+ emailbroadcast.BodyFormatValidator = emailbroadcastDescBodyFormat.Validators[0].(func(string) error)
+ // emailbroadcastDescRecipientsMode is the schema descriptor for recipients_mode field.
+ emailbroadcastDescRecipientsMode := emailbroadcastFields[3].Descriptor()
+ // emailbroadcast.DefaultRecipientsMode holds the default value on creation for the recipients_mode field.
+ emailbroadcast.DefaultRecipientsMode = emailbroadcastDescRecipientsMode.Default.(string)
+ // emailbroadcast.RecipientsModeValidator is a validator for the "recipients_mode" field. It is called by the builders before save.
+ emailbroadcast.RecipientsModeValidator = emailbroadcastDescRecipientsMode.Validators[0].(func(string) error)
+ // emailbroadcastDescStatus is the schema descriptor for status field.
+ emailbroadcastDescStatus := emailbroadcastFields[5].Descriptor()
+ // emailbroadcast.DefaultStatus holds the default value on creation for the status field.
+ emailbroadcast.DefaultStatus = emailbroadcastDescStatus.Default.(string)
+ // emailbroadcast.StatusValidator is a validator for the "status" field. It is called by the builders before save.
+ emailbroadcast.StatusValidator = emailbroadcastDescStatus.Validators[0].(func(string) error)
+ // emailbroadcastDescTotalCount is the schema descriptor for total_count field.
+ emailbroadcastDescTotalCount := emailbroadcastFields[6].Descriptor()
+ // emailbroadcast.DefaultTotalCount holds the default value on creation for the total_count field.
+ emailbroadcast.DefaultTotalCount = emailbroadcastDescTotalCount.Default.(int)
+ // emailbroadcast.TotalCountValidator is a validator for the "total_count" field. It is called by the builders before save.
+ emailbroadcast.TotalCountValidator = emailbroadcastDescTotalCount.Validators[0].(func(int) error)
+ // emailbroadcastDescSuccessCount is the schema descriptor for success_count field.
+ emailbroadcastDescSuccessCount := emailbroadcastFields[7].Descriptor()
+ // emailbroadcast.DefaultSuccessCount holds the default value on creation for the success_count field.
+ emailbroadcast.DefaultSuccessCount = emailbroadcastDescSuccessCount.Default.(int)
+ // emailbroadcast.SuccessCountValidator is a validator for the "success_count" field. It is called by the builders before save.
+ emailbroadcast.SuccessCountValidator = emailbroadcastDescSuccessCount.Validators[0].(func(int) error)
+ // emailbroadcastDescFailedCount is the schema descriptor for failed_count field.
+ emailbroadcastDescFailedCount := emailbroadcastFields[8].Descriptor()
+ // emailbroadcast.DefaultFailedCount holds the default value on creation for the failed_count field.
+ emailbroadcast.DefaultFailedCount = emailbroadcastDescFailedCount.Default.(int)
+ // emailbroadcast.FailedCountValidator is a validator for the "failed_count" field. It is called by the builders before save.
+ emailbroadcast.FailedCountValidator = emailbroadcastDescFailedCount.Validators[0].(func(int) error)
+ // emailbroadcastDescCreatedAt is the schema descriptor for created_at field.
+ emailbroadcastDescCreatedAt := emailbroadcastFields[13].Descriptor()
+ // emailbroadcast.DefaultCreatedAt holds the default value on creation for the created_at field.
+ emailbroadcast.DefaultCreatedAt = emailbroadcastDescCreatedAt.Default.(func() time.Time)
+ // emailbroadcastDescUpdatedAt is the schema descriptor for updated_at field.
+ emailbroadcastDescUpdatedAt := emailbroadcastFields[14].Descriptor()
+ // emailbroadcast.DefaultUpdatedAt holds the default value on creation for the updated_at field.
+ emailbroadcast.DefaultUpdatedAt = emailbroadcastDescUpdatedAt.Default.(func() time.Time)
+ // emailbroadcast.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
+ emailbroadcast.UpdateDefaultUpdatedAt = emailbroadcastDescUpdatedAt.UpdateDefault.(func() time.Time)
errorpassthroughruleMixin := schema.ErrorPassthroughRule{}.Mixin()
errorpassthroughruleMixinFields0 := errorpassthroughruleMixin[0].Fields()
_ = errorpassthroughruleMixinFields0
diff --git a/backend/ent/schema/email_broadcast.go b/backend/ent/schema/email_broadcast.go
new file mode 100644
index 00000000000..236a06e0514
--- /dev/null
+++ b/backend/ent/schema/email_broadcast.go
@@ -0,0 +1,102 @@
+package schema
+
+import (
+ "time"
+
+ "github.com/Wei-Shaw/sub2api/internal/domain"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect"
+ "entgo.io/ent/dialect/entsql"
+ "entgo.io/ent/schema"
+ "entgo.io/ent/schema/field"
+ "entgo.io/ent/schema/index"
+)
+
+// EmailBroadcast 管理员批量公告邮件记录。
+// EmailBroadcast records an admin-issued bulk announcement email batch.
+type EmailBroadcast struct {
+ ent.Schema
+}
+
+func (EmailBroadcast) Annotations() []schema.Annotation {
+ return []schema.Annotation{
+ entsql.Annotation{Table: "email_broadcasts"},
+ }
+}
+
+func (EmailBroadcast) Fields() []ent.Field {
+ return []ent.Field{
+ field.String("subject").
+ MaxLen(200).
+ NotEmpty().
+ Comment("邮件主题"),
+ field.String("body").
+ SchemaType(map[string]string{dialect.Postgres: "text"}).
+ NotEmpty().
+ Comment("邮件正文 (HTML 或纯文本)"),
+ field.String("body_format").
+ MaxLen(10).
+ Default(domain.EmailBroadcastBodyFormatHTML).
+ Comment("正文格式: html, text"),
+ field.String("recipients_mode").
+ MaxLen(20).
+ Default(domain.EmailBroadcastRecipientsModeSelected).
+ Comment("收件人模式: all, selected"),
+ field.JSON("recipient_user_ids", []int64{}).
+ Optional().
+ SchemaType(map[string]string{dialect.Postgres: "jsonb"}).
+ Comment("收件人用户 ID 列表 (selected 模式时使用)"),
+ field.String("status").
+ MaxLen(20).
+ Default(domain.EmailBroadcastStatusPending).
+ Comment("状态: pending, sending, completed, failed"),
+ field.Int("total_count").
+ Default(0).
+ NonNegative().
+ Comment("收件人总数"),
+ field.Int("success_count").
+ Default(0).
+ NonNegative().
+ Comment("发送成功数"),
+ field.Int("failed_count").
+ Default(0).
+ NonNegative().
+ Comment("发送失败数"),
+ field.String("error_message").
+ SchemaType(map[string]string{dialect.Postgres: "text"}).
+ Optional().
+ Nillable().
+ Comment("整批失败原因 (status=failed 时填充)"),
+ field.Int64("created_by").
+ Optional().
+ Nillable().
+ Comment("创建管理员用户 ID"),
+ field.Time("started_at").
+ Optional().
+ Nillable().
+ SchemaType(map[string]string{dialect.Postgres: "timestamptz"}).
+ Comment("开始发送时间"),
+ field.Time("finished_at").
+ Optional().
+ Nillable().
+ SchemaType(map[string]string{dialect.Postgres: "timestamptz"}).
+ Comment("发送结束时间"),
+ field.Time("created_at").
+ Immutable().
+ Default(time.Now).
+ SchemaType(map[string]string{dialect.Postgres: "timestamptz"}),
+ field.Time("updated_at").
+ Default(time.Now).
+ UpdateDefault(time.Now).
+ SchemaType(map[string]string{dialect.Postgres: "timestamptz"}),
+ }
+}
+
+func (EmailBroadcast) Indexes() []ent.Index {
+ return []ent.Index{
+ index.Fields("status"),
+ index.Fields("created_at"),
+ index.Fields("created_by"),
+ }
+}
diff --git a/backend/ent/tx.go b/backend/ent/tx.go
index 846cfcd4dac..0ba5f4481d2 100644
--- a/backend/ent/tx.go
+++ b/backend/ent/tx.go
@@ -36,6 +36,8 @@ type Tx struct {
ChannelMonitorHistory *ChannelMonitorHistoryClient
// ChannelMonitorRequestTemplate is the client for interacting with the ChannelMonitorRequestTemplate builders.
ChannelMonitorRequestTemplate *ChannelMonitorRequestTemplateClient
+ // EmailBroadcast is the client for interacting with the EmailBroadcast builders.
+ EmailBroadcast *EmailBroadcastClient
// ErrorPassthroughRule is the client for interacting with the ErrorPassthroughRule builders.
ErrorPassthroughRule *ErrorPassthroughRuleClient
// Group is the client for interacting with the Group builders.
@@ -226,6 +228,7 @@ func (tx *Tx) init() {
tx.ChannelMonitorDailyRollup = NewChannelMonitorDailyRollupClient(tx.config)
tx.ChannelMonitorHistory = NewChannelMonitorHistoryClient(tx.config)
tx.ChannelMonitorRequestTemplate = NewChannelMonitorRequestTemplateClient(tx.config)
+ tx.EmailBroadcast = NewEmailBroadcastClient(tx.config)
tx.ErrorPassthroughRule = NewErrorPassthroughRuleClient(tx.config)
tx.Group = NewGroupClient(tx.config)
tx.IdempotencyRecord = NewIdempotencyRecordClient(tx.config)
diff --git a/backend/go.mod b/backend/go.mod
index 587d53701f2..0ddc98c3ca9 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -73,6 +73,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 // indirect
+ github.com/aymerick/douceur v0.2.0 // indirect
github.com/bmatcuk/doublestar v1.3.4 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/bytedance/sonic v1.9.1 // indirect
@@ -106,6 +107,7 @@ require (
github.com/goccy/go-json v0.10.2 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
+ github.com/gorilla/css v1.0.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/hcl/v2 v2.18.1 // indirect
@@ -118,6 +120,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mdelapenya/tlscert v0.2.0 // indirect
+ github.com/microcosm-cc/bluemonday v1.0.27 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
diff --git a/backend/go.sum b/backend/go.sum
index 7735fda29e3..7a520e24a42 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -62,6 +62,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb8
github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs=
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
+github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
+github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
@@ -168,6 +170,8 @@ 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/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4=
github.com/google/wire v0.7.0/go.mod h1:n6YbUQD9cPKTnHXEBN2DXlOp/mVADhVErcMFb0v3J18=
+github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
+github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
@@ -224,6 +228,8 @@ github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI=
github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o=
+github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
+github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
diff --git a/backend/internal/domain/email_broadcast.go b/backend/internal/domain/email_broadcast.go
new file mode 100644
index 00000000000..26c57f15487
--- /dev/null
+++ b/backend/internal/domain/email_broadcast.go
@@ -0,0 +1,48 @@
+package domain
+
+import (
+ infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
+)
+
+// Email broadcast body formats.
+const (
+ EmailBroadcastBodyFormatHTML = "html"
+ EmailBroadcastBodyFormatText = "text"
+)
+
+// Email broadcast recipient modes.
+const (
+ EmailBroadcastRecipientsModeAll = "all"
+ EmailBroadcastRecipientsModeSelected = "selected"
+)
+
+// Email broadcast lifecycle statuses.
+const (
+ EmailBroadcastStatusPending = "pending"
+ EmailBroadcastStatusSending = "sending"
+ EmailBroadcastStatusCompleted = "completed"
+ EmailBroadcastStatusFailed = "failed"
+)
+
+// EmailBroadcastSubjectMaxLen 邮件主题最大字符数 (与 schema 约束保持一致)。
+const EmailBroadcastSubjectMaxLen = 200
+
+// EmailBroadcastBodyMaxLen 邮件正文最大字符数 (上限保护,防止 DoS)。
+const EmailBroadcastBodyMaxLen = 65536
+
+// EmailBroadcastMaxSelectedRecipients selected 模式下单次最大收件人数量。
+const EmailBroadcastMaxSelectedRecipients = 5000
+
+var (
+ ErrEmailBroadcastNotFound = infraerrors.NotFound("EMAIL_BROADCAST_NOT_FOUND", "email broadcast not found")
+ ErrEmailBroadcastSubjectRequired = infraerrors.BadRequest("EMAIL_BROADCAST_SUBJECT_REQUIRED", "email broadcast subject is required")
+ ErrEmailBroadcastBodyRequired = infraerrors.BadRequest("EMAIL_BROADCAST_BODY_REQUIRED", "email broadcast body is required")
+ ErrEmailBroadcastSubjectTooLong = infraerrors.BadRequest("EMAIL_BROADCAST_SUBJECT_TOO_LONG", "email broadcast subject exceeds maximum length")
+ ErrEmailBroadcastBodyTooLong = infraerrors.BadRequest("EMAIL_BROADCAST_BODY_TOO_LONG", "email broadcast body exceeds maximum length")
+ ErrEmailBroadcastInvalidBodyFormat = infraerrors.BadRequest("EMAIL_BROADCAST_INVALID_BODY_FORMAT", "email broadcast body format must be html or text")
+ ErrEmailBroadcastInvalidMode = infraerrors.BadRequest("EMAIL_BROADCAST_INVALID_MODE", "email broadcast recipients mode must be all or selected")
+ ErrEmailBroadcastNoRecipients = infraerrors.BadRequest("EMAIL_BROADCAST_NO_RECIPIENTS", "email broadcast requires at least one recipient")
+ ErrEmailBroadcastTooManyRecipients = infraerrors.BadRequest("EMAIL_BROADCAST_TOO_MANY_RECIPIENTS", "email broadcast recipient count exceeds maximum")
+ ErrEmailBroadcastEmailNotConfigured = infraerrors.BadRequest("EMAIL_BROADCAST_EMAIL_NOT_CONFIGURED", "SMTP/email service is not configured")
+ ErrEmailBroadcastDeleteInFlight = infraerrors.Conflict("EMAIL_BROADCAST_DELETE_IN_FLIGHT", "cannot delete a broadcast that is still pending or sending")
+)
diff --git a/backend/internal/handler/admin/email_broadcast_handler.go b/backend/internal/handler/admin/email_broadcast_handler.go
new file mode 100644
index 00000000000..8105dedba33
--- /dev/null
+++ b/backend/internal/handler/admin/email_broadcast_handler.go
@@ -0,0 +1,204 @@
+package admin
+
+import (
+ "strconv"
+ "strings"
+
+ "github.com/Wei-Shaw/sub2api/internal/handler/dto"
+ "github.com/Wei-Shaw/sub2api/internal/pkg/response"
+ middleware2 "github.com/Wei-Shaw/sub2api/internal/server/middleware"
+ "github.com/Wei-Shaw/sub2api/internal/service"
+
+ "github.com/gin-gonic/gin"
+)
+
+// EmailBroadcastHandler handles admin bulk announcement email management.
+type EmailBroadcastHandler struct {
+ broadcastService *service.EmailBroadcastService
+ adminService service.AdminService
+}
+
+// NewEmailBroadcastHandler wires a new EmailBroadcastHandler.
+func NewEmailBroadcastHandler(
+ broadcastService *service.EmailBroadcastService,
+ adminService service.AdminService,
+) *EmailBroadcastHandler {
+ return &EmailBroadcastHandler{
+ broadcastService: broadcastService,
+ adminService: adminService,
+ }
+}
+
+// CreateEmailBroadcastRequest is the body for POST /api/v1/admin/email-broadcasts.
+type CreateEmailBroadcastRequest struct {
+ Subject string `json:"subject" binding:"required"`
+ Body string `json:"body" binding:"required"`
+ BodyFormat string `json:"body_format" binding:"omitempty,oneof=html text"`
+ RecipientsMode string `json:"recipients_mode" binding:"required,oneof=all selected"`
+ RecipientUserIDs []int64 `json:"recipient_user_ids"`
+}
+
+// PreviewEmailBroadcastRequest is the body for POST /api/v1/admin/email-broadcasts/preview.
+type PreviewEmailBroadcastRequest struct {
+ Subject string `json:"subject"`
+ Body string `json:"body"`
+ BodyFormat string `json:"body_format" binding:"omitempty,oneof=html text"`
+}
+
+// EmailBroadcastRecipientCandidate is one searchable user shown in the recipient picker.
+type EmailBroadcastRecipientCandidate struct {
+ ID int64 `json:"id"`
+ Email string `json:"email"`
+ Username string `json:"username,omitempty"`
+}
+
+// Create handles POST /api/v1/admin/email-broadcasts.
+// 立即返回 202 + broadcast 当前状态;实际发送在后台进行。
+func (h *EmailBroadcastHandler) Create(c *gin.Context) {
+ var req CreateEmailBroadcastRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "Invalid request: "+err.Error())
+ return
+ }
+
+ subject, _ := middleware2.GetAuthSubjectFromContext(c)
+ var createdBy *int64
+ if subject.UserID > 0 {
+ uid := subject.UserID
+ createdBy = &uid
+ }
+
+ broadcast, err := h.broadcastService.Send(c.Request.Context(), service.EmailBroadcastSendInput{
+ Subject: req.Subject,
+ Body: req.Body,
+ BodyFormat: req.BodyFormat,
+ RecipientsMode: req.RecipientsMode,
+ RecipientUserIDs: req.RecipientUserIDs,
+ CreatedBy: createdBy,
+ })
+ if err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+
+ response.Accepted(c, dto.EmailBroadcastFromService(broadcast))
+}
+
+// Get returns the full broadcast (including body) for inspecting status / re-reading content.
+// GET /api/v1/admin/email-broadcasts/:id
+func (h *EmailBroadcastHandler) Get(c *gin.Context) {
+ id, err := strconv.ParseInt(c.Param("id"), 10, 64)
+ if err != nil || id <= 0 {
+ response.BadRequest(c, "Invalid broadcast id")
+ return
+ }
+
+ b, err := h.broadcastService.Get(c.Request.Context(), id)
+ if err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+ response.Success(c, dto.EmailBroadcastFromService(b))
+}
+
+// List returns broadcast history (paginated, newest first).
+// GET /api/v1/admin/email-broadcasts
+func (h *EmailBroadcastHandler) List(c *gin.Context) {
+ page, pageSize := response.ParsePagination(c)
+ status := strings.TrimSpace(c.Query("status"))
+
+ result, err := h.broadcastService.List(c.Request.Context(), service.EmailBroadcastListParams{
+ Page: page,
+ PageSize: pageSize,
+ Status: status,
+ })
+ if err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+
+ items := make([]dto.EmailBroadcastSummary, 0, len(result.Items))
+ for i := range result.Items {
+ items = append(items, dto.EmailBroadcastSummaryFromService(&result.Items[i]))
+ }
+
+ response.Success(c, gin.H{
+ "items": items,
+ "total": result.Total,
+ "page": result.Page,
+ "page_size": result.PageSize,
+ })
+}
+
+// Delete hard-deletes a broadcast record by id. Returns 409 if the broadcast is
+// still pending or sending so the user retries after it settles.
+// DELETE /api/v1/admin/email-broadcasts/:id
+func (h *EmailBroadcastHandler) Delete(c *gin.Context) {
+ id, err := strconv.ParseInt(c.Param("id"), 10, 64)
+ if err != nil || id <= 0 {
+ response.BadRequest(c, "Invalid broadcast id")
+ return
+ }
+ if err := h.broadcastService.Delete(c.Request.Context(), id); err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+ response.Success(c, gin.H{"id": id})
+}
+
+// Preview returns the fully composed HTML that would be delivered for the given
+// subject + body + format, so the admin UI can render a faithful live preview.
+// POST /api/v1/admin/email-broadcasts/preview
+func (h *EmailBroadcastHandler) Preview(c *gin.Context) {
+ var req PreviewEmailBroadcastRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "Invalid request: "+err.Error())
+ return
+ }
+ if req.BodyFormat == "" {
+ req.BodyFormat = "html"
+ }
+ htmlBody := h.broadcastService.PreviewHTML(c.Request.Context(), req.Subject, req.Body, req.BodyFormat)
+ response.Success(c, gin.H{"html": htmlBody})
+}
+
+// SearchRecipients backs the recipient multi-select picker.
+// GET /api/v1/admin/email-broadcasts/recipients/search?q=...&limit=...
+func (h *EmailBroadcastHandler) SearchRecipients(c *gin.Context) {
+ q := strings.TrimSpace(c.Query("q"))
+ if len(q) > 200 {
+ q = q[:200]
+ }
+ limit := 20
+ if v := strings.TrimSpace(c.Query("limit")); v != "" {
+ if parsed, err := strconv.Atoi(v); err == nil && parsed > 0 && parsed <= 100 {
+ limit = parsed
+ }
+ }
+
+ filters := service.UserListFilters{Search: q}
+ // 不需要订阅信息,节省查询成本
+ includeSubs := false
+ filters.IncludeSubscriptions = &includeSubs
+
+ users, _, err := h.adminService.ListUsers(c.Request.Context(), 1, limit, filters, "id", "asc")
+ if err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+
+ out := make([]EmailBroadcastRecipientCandidate, 0, len(users))
+ for i := range users {
+ u := users[i]
+ if strings.TrimSpace(u.Email) == "" {
+ continue
+ }
+ out = append(out, EmailBroadcastRecipientCandidate{
+ ID: u.ID,
+ Email: u.Email,
+ Username: u.Username,
+ })
+ }
+
+ response.Success(c, gin.H{"items": out})
+}
diff --git a/backend/internal/handler/dto/email_broadcast.go b/backend/internal/handler/dto/email_broadcast.go
new file mode 100644
index 00000000000..731c8a4a35e
--- /dev/null
+++ b/backend/internal/handler/dto/email_broadcast.go
@@ -0,0 +1,85 @@
+package dto
+
+import (
+ "time"
+
+ "github.com/Wei-Shaw/sub2api/internal/service"
+)
+
+// EmailBroadcast is the admin-facing JSON representation of a broadcast record.
+type EmailBroadcast struct {
+ ID int64 `json:"id"`
+ Subject string `json:"subject"`
+ Body string `json:"body"`
+ BodyFormat string `json:"body_format"`
+ RecipientsMode string `json:"recipients_mode"`
+ RecipientUserIDs []int64 `json:"recipient_user_ids,omitempty"`
+ Status string `json:"status"`
+ TotalCount int `json:"total_count"`
+ SuccessCount int `json:"success_count"`
+ FailedCount int `json:"failed_count"`
+ ErrorMessage *string `json:"error_message,omitempty"`
+ CreatedBy *int64 `json:"created_by,omitempty"`
+ StartedAt *time.Time `json:"started_at,omitempty"`
+ FinishedAt *time.Time `json:"finished_at,omitempty"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+// EmailBroadcastSummary is the trimmed representation returned in list responses
+// (omits the full body to keep payloads small).
+type EmailBroadcastSummary struct {
+ ID int64 `json:"id"`
+ Subject string `json:"subject"`
+ BodyFormat string `json:"body_format"`
+ RecipientsMode string `json:"recipients_mode"`
+ Status string `json:"status"`
+ TotalCount int `json:"total_count"`
+ SuccessCount int `json:"success_count"`
+ FailedCount int `json:"failed_count"`
+ CreatedBy *int64 `json:"created_by,omitempty"`
+ StartedAt *time.Time `json:"started_at,omitempty"`
+ FinishedAt *time.Time `json:"finished_at,omitempty"`
+ CreatedAt time.Time `json:"created_at"`
+}
+
+func EmailBroadcastFromService(b *service.EmailBroadcast) *EmailBroadcast {
+ if b == nil {
+ return nil
+ }
+ return &EmailBroadcast{
+ ID: b.ID,
+ Subject: b.Subject,
+ Body: b.Body,
+ BodyFormat: b.BodyFormat,
+ RecipientsMode: b.RecipientsMode,
+ RecipientUserIDs: append([]int64(nil), b.RecipientUserIDs...),
+ Status: b.Status,
+ TotalCount: b.TotalCount,
+ SuccessCount: b.SuccessCount,
+ FailedCount: b.FailedCount,
+ ErrorMessage: b.ErrorMessage,
+ CreatedBy: b.CreatedBy,
+ StartedAt: b.StartedAt,
+ FinishedAt: b.FinishedAt,
+ CreatedAt: b.CreatedAt,
+ UpdatedAt: b.UpdatedAt,
+ }
+}
+
+func EmailBroadcastSummaryFromService(b *service.EmailBroadcast) EmailBroadcastSummary {
+ return EmailBroadcastSummary{
+ ID: b.ID,
+ Subject: b.Subject,
+ BodyFormat: b.BodyFormat,
+ RecipientsMode: b.RecipientsMode,
+ Status: b.Status,
+ TotalCount: b.TotalCount,
+ SuccessCount: b.SuccessCount,
+ FailedCount: b.FailedCount,
+ CreatedBy: b.CreatedBy,
+ StartedAt: b.StartedAt,
+ FinishedAt: b.FinishedAt,
+ CreatedAt: b.CreatedAt,
+ }
+}
diff --git a/backend/internal/handler/handler.go b/backend/internal/handler/handler.go
index 308b219921e..50f0615822b 100644
--- a/backend/internal/handler/handler.go
+++ b/backend/internal/handler/handler.go
@@ -11,6 +11,7 @@ type AdminHandlers struct {
Group *admin.GroupHandler
Account *admin.AccountHandler
Announcement *admin.AnnouncementHandler
+ EmailBroadcast *admin.EmailBroadcastHandler
DataManagement *admin.DataManagementHandler
Backup *admin.BackupHandler
OAuth *admin.OAuthHandler
diff --git a/backend/internal/handler/wire.go b/backend/internal/handler/wire.go
index c8c4615702e..d12269e51c6 100644
--- a/backend/internal/handler/wire.go
+++ b/backend/internal/handler/wire.go
@@ -14,6 +14,7 @@ func ProvideAdminHandlers(
groupHandler *admin.GroupHandler,
accountHandler *admin.AccountHandler,
announcementHandler *admin.AnnouncementHandler,
+ emailBroadcastHandler *admin.EmailBroadcastHandler,
dataManagementHandler *admin.DataManagementHandler,
backupHandler *admin.BackupHandler,
oauthHandler *admin.OAuthHandler,
@@ -46,6 +47,7 @@ func ProvideAdminHandlers(
Group: groupHandler,
Account: accountHandler,
Announcement: announcementHandler,
+ EmailBroadcast: emailBroadcastHandler,
DataManagement: dataManagementHandler,
Backup: backupHandler,
OAuth: oauthHandler,
@@ -159,6 +161,7 @@ var ProviderSet = wire.NewSet(
admin.NewGroupHandler,
admin.NewAccountHandler,
admin.NewAnnouncementHandler,
+ admin.NewEmailBroadcastHandler,
admin.NewDataManagementHandler,
admin.NewBackupHandler,
admin.NewOAuthHandler,
diff --git a/backend/internal/repository/email_broadcast_repo.go b/backend/internal/repository/email_broadcast_repo.go
new file mode 100644
index 00000000000..e1e6ecb46a1
--- /dev/null
+++ b/backend/internal/repository/email_broadcast_repo.go
@@ -0,0 +1,197 @@
+package repository
+
+import (
+ "context"
+
+ dbent "github.com/Wei-Shaw/sub2api/ent"
+ "github.com/Wei-Shaw/sub2api/ent/emailbroadcast"
+ "github.com/Wei-Shaw/sub2api/internal/service"
+)
+
+type emailBroadcastRepository struct {
+ client *dbent.Client
+}
+
+// NewEmailBroadcastRepository wires an ent-backed EmailBroadcastRepository.
+func NewEmailBroadcastRepository(client *dbent.Client) service.EmailBroadcastRepository {
+ return &emailBroadcastRepository{client: client}
+}
+
+func (r *emailBroadcastRepository) Create(ctx context.Context, b *service.EmailBroadcast) error {
+ client := clientFromContext(ctx, r.client)
+ builder := client.EmailBroadcast.Create().
+ SetSubject(b.Subject).
+ SetBody(b.Body).
+ SetBodyFormat(b.BodyFormat).
+ SetRecipientsMode(b.RecipientsMode).
+ SetStatus(b.Status).
+ SetTotalCount(b.TotalCount).
+ SetSuccessCount(b.SuccessCount).
+ SetFailedCount(b.FailedCount)
+
+ if len(b.RecipientUserIDs) > 0 {
+ builder.SetRecipientUserIds(b.RecipientUserIDs)
+ }
+ if b.CreatedBy != nil {
+ builder.SetCreatedBy(*b.CreatedBy)
+ }
+ if b.ErrorMessage != nil {
+ builder.SetErrorMessage(*b.ErrorMessage)
+ }
+ if b.StartedAt != nil {
+ builder.SetStartedAt(*b.StartedAt)
+ }
+ if b.FinishedAt != nil {
+ builder.SetFinishedAt(*b.FinishedAt)
+ }
+
+ created, err := builder.Save(ctx)
+ if err != nil {
+ return err
+ }
+ applyEmailBroadcastEntityToService(b, created)
+ return nil
+}
+
+func (r *emailBroadcastRepository) GetByID(ctx context.Context, id int64) (*service.EmailBroadcast, error) {
+ m, err := r.client.EmailBroadcast.Query().
+ Where(emailbroadcast.IDEQ(id)).
+ Only(ctx)
+ if err != nil {
+ return nil, translatePersistenceError(err, service.ErrEmailBroadcastNotFound, nil)
+ }
+ return emailBroadcastEntityToService(m), nil
+}
+
+func (r *emailBroadcastRepository) UpdateStatus(ctx context.Context, id int64, patch service.EmailBroadcastStatusUpdate) error {
+ client := clientFromContext(ctx, r.client)
+ upd := client.EmailBroadcast.UpdateOneID(id)
+
+ if patch.Status != nil {
+ upd.SetStatus(*patch.Status)
+ }
+ if patch.TotalCount != nil {
+ upd.SetTotalCount(*patch.TotalCount)
+ }
+ if patch.SuccessCount != nil {
+ upd.SetSuccessCount(*patch.SuccessCount)
+ }
+ if patch.FailedCount != nil {
+ upd.SetFailedCount(*patch.FailedCount)
+ }
+ if patch.ErrorMessage != nil {
+ upd.SetErrorMessage(*patch.ErrorMessage)
+ }
+ if patch.StartedAt != nil {
+ upd.SetStartedAt(*patch.StartedAt)
+ }
+ if patch.FinishedAt != nil {
+ upd.SetFinishedAt(*patch.FinishedAt)
+ }
+
+ _, err := upd.Save(ctx)
+ if err != nil {
+ return translatePersistenceError(err, service.ErrEmailBroadcastNotFound, nil)
+ }
+ return nil
+}
+
+func (r *emailBroadcastRepository) Delete(ctx context.Context, id int64) error {
+ client := clientFromContext(ctx, r.client)
+ n, err := client.EmailBroadcast.Delete().Where(emailbroadcast.IDEQ(id)).Exec(ctx)
+ if err != nil {
+ return err
+ }
+ if n == 0 {
+ return service.ErrEmailBroadcastNotFound
+ }
+ return nil
+}
+
+func (r *emailBroadcastRepository) List(ctx context.Context, params service.EmailBroadcastListParams) (*service.EmailBroadcastListResult, error) {
+ q := r.client.EmailBroadcast.Query()
+ if params.Status != "" {
+ q = q.Where(emailbroadcast.StatusEQ(params.Status))
+ }
+
+ total, err := q.Count(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ page := params.Page
+ if page <= 0 {
+ page = 1
+ }
+ pageSize := params.PageSize
+ if pageSize <= 0 {
+ pageSize = 20
+ }
+
+ items, err := q.
+ Order(dbent.Desc(emailbroadcast.FieldCreatedAt), dbent.Desc(emailbroadcast.FieldID)).
+ Offset((page - 1) * pageSize).
+ Limit(pageSize).
+ All(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ out := make([]service.EmailBroadcast, 0, len(items))
+ for _, m := range items {
+ out = append(out, *emailBroadcastEntityToService(m))
+ }
+
+ return &service.EmailBroadcastListResult{
+ Items: out,
+ Total: int64(total),
+ Page: page,
+ PageSize: pageSize,
+ }, nil
+}
+
+func emailBroadcastEntityToService(m *dbent.EmailBroadcast) *service.EmailBroadcast {
+ if m == nil {
+ return nil
+ }
+ out := &service.EmailBroadcast{
+ ID: m.ID,
+ Subject: m.Subject,
+ Body: m.Body,
+ BodyFormat: m.BodyFormat,
+ RecipientsMode: m.RecipientsMode,
+ RecipientUserIDs: append([]int64(nil), m.RecipientUserIds...),
+ Status: m.Status,
+ TotalCount: m.TotalCount,
+ SuccessCount: m.SuccessCount,
+ FailedCount: m.FailedCount,
+ CreatedAt: m.CreatedAt,
+ UpdatedAt: m.UpdatedAt,
+ }
+ if m.ErrorMessage != nil {
+ v := *m.ErrorMessage
+ out.ErrorMessage = &v
+ }
+ if m.CreatedBy != nil {
+ v := *m.CreatedBy
+ out.CreatedBy = &v
+ }
+ if m.StartedAt != nil {
+ v := *m.StartedAt
+ out.StartedAt = &v
+ }
+ if m.FinishedAt != nil {
+ v := *m.FinishedAt
+ out.FinishedAt = &v
+ }
+ return out
+}
+
+func applyEmailBroadcastEntityToService(b *service.EmailBroadcast, m *dbent.EmailBroadcast) {
+ if b == nil || m == nil {
+ return
+ }
+ b.ID = m.ID
+ b.CreatedAt = m.CreatedAt
+ b.UpdatedAt = m.UpdatedAt
+}
diff --git a/backend/internal/repository/wire.go b/backend/internal/repository/wire.go
index 2d1b04e393b..a9e86f77d0c 100644
--- a/backend/internal/repository/wire.go
+++ b/backend/internal/repository/wire.go
@@ -75,6 +75,7 @@ var ProviderSet = wire.NewSet(
NewPromoCodeRepository,
NewAnnouncementRepository,
NewAnnouncementReadRepository,
+ NewEmailBroadcastRepository,
NewUsageLogRepository,
NewUsageBillingRepository,
NewIdempotencyRepository,
diff --git a/backend/internal/server/routes/admin.go b/backend/internal/server/routes/admin.go
index 2301adda6bf..6898cc3b0a5 100644
--- a/backend/internal/server/routes/admin.go
+++ b/backend/internal/server/routes/admin.go
@@ -32,6 +32,9 @@ func RegisterAdminRoutes(
// 公告管理
registerAnnouncementRoutes(admin, h)
+ // 公告邮件批量发送
+ registerEmailBroadcastRoutes(admin, h)
+
// OpenAI OAuth
registerOpenAIOAuthRoutes(admin, h)
@@ -338,6 +341,20 @@ func registerAnnouncementRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
}
}
+// registerEmailBroadcastRoutes 注册管理员"批量公告邮件"相关路由。
+// registerEmailBroadcastRoutes wires the admin bulk-announcement email endpoints.
+func registerEmailBroadcastRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
+ broadcasts := admin.Group("/email-broadcasts")
+ {
+ broadcasts.GET("", h.Admin.EmailBroadcast.List)
+ broadcasts.POST("", h.Admin.EmailBroadcast.Create)
+ broadcasts.POST("/preview", h.Admin.EmailBroadcast.Preview)
+ broadcasts.GET("/:id", h.Admin.EmailBroadcast.Get)
+ broadcasts.DELETE("/:id", h.Admin.EmailBroadcast.Delete)
+ broadcasts.GET("/recipients/search", h.Admin.EmailBroadcast.SearchRecipients)
+ }
+}
+
func registerOpenAIOAuthRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
openai := admin.Group("/openai")
{
diff --git a/backend/internal/service/email_broadcast.go b/backend/internal/service/email_broadcast.go
new file mode 100644
index 00000000000..1be8c53956b
--- /dev/null
+++ b/backend/internal/service/email_broadcast.go
@@ -0,0 +1,101 @@
+package service
+
+import (
+ "context"
+ "time"
+
+ "github.com/Wei-Shaw/sub2api/internal/domain"
+)
+
+// Re-exported domain constants for service-layer consumers.
+const (
+ EmailBroadcastBodyFormatHTML = domain.EmailBroadcastBodyFormatHTML
+ EmailBroadcastBodyFormatText = domain.EmailBroadcastBodyFormatText
+ EmailBroadcastRecipientsModeAll = domain.EmailBroadcastRecipientsModeAll
+ EmailBroadcastRecipientsModeSelected = domain.EmailBroadcastRecipientsModeSelected
+ EmailBroadcastStatusPending = domain.EmailBroadcastStatusPending
+ EmailBroadcastStatusSending = domain.EmailBroadcastStatusSending
+ EmailBroadcastStatusCompleted = domain.EmailBroadcastStatusCompleted
+ EmailBroadcastStatusFailed = domain.EmailBroadcastStatusFailed
+)
+
+// Re-exported domain errors.
+var (
+ ErrEmailBroadcastNotFound = domain.ErrEmailBroadcastNotFound
+ ErrEmailBroadcastSubjectRequired = domain.ErrEmailBroadcastSubjectRequired
+ ErrEmailBroadcastBodyRequired = domain.ErrEmailBroadcastBodyRequired
+ ErrEmailBroadcastSubjectTooLong = domain.ErrEmailBroadcastSubjectTooLong
+ ErrEmailBroadcastBodyTooLong = domain.ErrEmailBroadcastBodyTooLong
+ ErrEmailBroadcastInvalidBodyFormat = domain.ErrEmailBroadcastInvalidBodyFormat
+ ErrEmailBroadcastInvalidMode = domain.ErrEmailBroadcastInvalidMode
+ ErrEmailBroadcastNoRecipients = domain.ErrEmailBroadcastNoRecipients
+ ErrEmailBroadcastTooManyRecipients = domain.ErrEmailBroadcastTooManyRecipients
+ ErrEmailBroadcastEmailNotConfigured = domain.ErrEmailBroadcastEmailNotConfigured
+ ErrEmailBroadcastDeleteInFlight = domain.ErrEmailBroadcastDeleteInFlight
+)
+
+// EmailBroadcast 是 service 层暴露的批量公告邮件聚合状态。
+type EmailBroadcast struct {
+ ID int64
+ Subject string
+ Body string
+ BodyFormat string
+ RecipientsMode string
+ RecipientUserIDs []int64
+ Status string
+ TotalCount int
+ SuccessCount int
+ FailedCount int
+ ErrorMessage *string
+ CreatedBy *int64
+ StartedAt *time.Time
+ FinishedAt *time.Time
+ CreatedAt time.Time
+ UpdatedAt time.Time
+}
+
+// EmailBroadcastCreateInput 描述创建/发起一次批量邮件公告所需输入。
+type EmailBroadcastCreateInput struct {
+ Subject string
+ Body string
+ BodyFormat string
+ RecipientsMode string
+ RecipientUserIDs []int64
+ CreatedBy *int64
+}
+
+// EmailBroadcastListParams 控制 ListBroadcasts 查询的分页与过滤。
+type EmailBroadcastListParams struct {
+ Page int
+ PageSize int
+ Status string // 可空:按状态过滤
+}
+
+// EmailBroadcastListResult 列表查询返回值。
+type EmailBroadcastListResult struct {
+ Items []EmailBroadcast
+ Total int64
+ Page int
+ PageSize int
+}
+
+// EmailBroadcastStatusUpdate 用于增量更新一次广播的执行状态。
+// nil 字段表示"不变",便于把 finished_at / counts 等部分字段一次提交。
+type EmailBroadcastStatusUpdate struct {
+ Status *string
+ TotalCount *int
+ SuccessCount *int
+ FailedCount *int
+ ErrorMessage *string
+ StartedAt *time.Time
+ FinishedAt *time.Time
+}
+
+// EmailBroadcastRepository 定义批量邮件公告的持久化接口。
+type EmailBroadcastRepository interface {
+ Create(ctx context.Context, broadcast *EmailBroadcast) error
+ GetByID(ctx context.Context, id int64) (*EmailBroadcast, error)
+ UpdateStatus(ctx context.Context, id int64, patch EmailBroadcastStatusUpdate) error
+ List(ctx context.Context, params EmailBroadcastListParams) (*EmailBroadcastListResult, error)
+ Delete(ctx context.Context, id int64) error
+}
diff --git a/backend/internal/service/email_broadcast_service.go b/backend/internal/service/email_broadcast_service.go
new file mode 100644
index 00000000000..416b26707e5
--- /dev/null
+++ b/backend/internal/service/email_broadcast_service.go
@@ -0,0 +1,562 @@
+package service
+
+import (
+ "context"
+ "fmt"
+ "html"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/Wei-Shaw/sub2api/internal/domain"
+ "github.com/Wei-Shaw/sub2api/internal/pkg/logger"
+ "github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
+
+ "github.com/microcosm-cc/bluemonday"
+ "go.uber.org/zap"
+)
+
+// EmailBroadcastService 提供管理员批量发送公告邮件的能力。
+//
+// 设计要点:
+// - HTTP 入口通过 Send() 立刻创建一条 broadcast 记录后异步发送,避免长时间占用请求 goroutine。
+// - 发送过程对单封邮件失败保持容错,整批的成功 / 失败计数会增量回写到 DB。
+// - HTML 正文使用 bluemonday UGCPolicy 做 sanitize,去除潜在 XSS 风险;纯文本正文会被
+// HTML-escape 并替换换行后再生成最终邮件,保证两种格式的最终 MIME 都是合法的 HTML 邮件。
+type EmailBroadcastService struct {
+ repo EmailBroadcastRepository
+ userRepo UserRepository
+ emailService *EmailService
+ settingRepo SettingRepository
+ htmlSanitizer *bluemonday.Policy
+ sendIntervalPerEmail time.Duration
+
+ mu sync.Mutex
+ running map[int64]struct{}
+}
+
+// EmailBroadcastSendInput 发送一次广播邮件所需的参数集合 (供 handler 调用)。
+type EmailBroadcastSendInput struct {
+ Subject string
+ Body string
+ BodyFormat string
+ RecipientsMode string
+ RecipientUserIDs []int64
+ CreatedBy *int64
+}
+
+// NewEmailBroadcastService 创建广播邮件服务。
+func NewEmailBroadcastService(
+ repo EmailBroadcastRepository,
+ userRepo UserRepository,
+ emailService *EmailService,
+ settingRepo SettingRepository,
+) *EmailBroadcastService {
+ policy := bluemonday.UGCPolicy()
+ // 允许 等公告里常用的属性。
+ policy.AllowAttrs("target").OnElements("a")
+ policy.RequireNoReferrerOnLinks(true)
+ policy.RequireNoFollowOnLinks(true)
+ policy.AllowStandardURLs()
+
+ return &EmailBroadcastService{
+ repo: repo,
+ userRepo: userRepo,
+ emailService: emailService,
+ settingRepo: settingRepo,
+ htmlSanitizer: policy,
+ sendIntervalPerEmail: 200 * time.Millisecond,
+ running: make(map[int64]struct{}),
+ }
+}
+
+// Send 校验输入、解析收件人、立刻持久化 broadcast 记录,然后异步触发批量发送。
+// 返回值是已创建的 broadcast (status=pending)。
+func (s *EmailBroadcastService) Send(ctx context.Context, input EmailBroadcastSendInput) (*EmailBroadcast, error) {
+ subject := strings.TrimSpace(input.Subject)
+ body := strings.TrimSpace(input.Body)
+ bodyFormat := strings.ToLower(strings.TrimSpace(input.BodyFormat))
+ mode := strings.ToLower(strings.TrimSpace(input.RecipientsMode))
+
+ if subject == "" {
+ return nil, ErrEmailBroadcastSubjectRequired
+ }
+ if len([]rune(subject)) > domain.EmailBroadcastSubjectMaxLen {
+ return nil, ErrEmailBroadcastSubjectTooLong
+ }
+ if body == "" {
+ return nil, ErrEmailBroadcastBodyRequired
+ }
+ if len(body) > domain.EmailBroadcastBodyMaxLen {
+ return nil, ErrEmailBroadcastBodyTooLong
+ }
+ switch bodyFormat {
+ case EmailBroadcastBodyFormatHTML, EmailBroadcastBodyFormatText:
+ case "":
+ bodyFormat = EmailBroadcastBodyFormatHTML
+ default:
+ return nil, ErrEmailBroadcastInvalidBodyFormat
+ }
+ switch mode {
+ case EmailBroadcastRecipientsModeAll, EmailBroadcastRecipientsModeSelected:
+ case "":
+ mode = EmailBroadcastRecipientsModeSelected
+ default:
+ return nil, ErrEmailBroadcastInvalidMode
+ }
+
+ dedupedIDs := dedupePositiveInt64s(input.RecipientUserIDs)
+ if mode == EmailBroadcastRecipientsModeSelected {
+ if len(dedupedIDs) == 0 {
+ return nil, ErrEmailBroadcastNoRecipients
+ }
+ if len(dedupedIDs) > domain.EmailBroadcastMaxSelectedRecipients {
+ return nil, ErrEmailBroadcastTooManyRecipients
+ }
+ } else {
+ // "all" 模式忽略用户传入的 IDs,避免歧义。
+ dedupedIDs = nil
+ }
+
+ // 提前校验 SMTP 配置存在 — 没配的话直接返回,不写脏数据。
+ smtpConfig, err := s.emailService.GetSMTPConfig(ctx)
+ if err != nil {
+ return nil, ErrEmailBroadcastEmailNotConfigured
+ }
+ if smtpConfig == nil || strings.TrimSpace(smtpConfig.Host) == "" {
+ return nil, ErrEmailBroadcastEmailNotConfigured
+ }
+
+ broadcast := &EmailBroadcast{
+ Subject: subject,
+ Body: body,
+ BodyFormat: bodyFormat,
+ RecipientsMode: mode,
+ RecipientUserIDs: dedupedIDs,
+ Status: EmailBroadcastStatusPending,
+ CreatedBy: input.CreatedBy,
+ }
+ if err := s.repo.Create(ctx, broadcast); err != nil {
+ return nil, err
+ }
+
+ go s.runBroadcast(broadcast.ID)
+
+ return broadcast, nil
+}
+
+// List 查询 broadcast 历史。
+func (s *EmailBroadcastService) List(ctx context.Context, params EmailBroadcastListParams) (*EmailBroadcastListResult, error) {
+ if params.Page <= 0 {
+ params.Page = 1
+ }
+ if params.PageSize <= 0 || params.PageSize > 100 {
+ params.PageSize = 20
+ }
+ return s.repo.List(ctx, params)
+}
+
+// Get 取单条 broadcast 详情。
+func (s *EmailBroadcastService) Get(ctx context.Context, id int64) (*EmailBroadcast, error) {
+ if id <= 0 {
+ return nil, ErrEmailBroadcastNotFound
+ }
+ return s.repo.GetByID(ctx, id)
+}
+
+// Delete 物理删除一条历史 broadcast。
+// 拒绝删除正在发送中的记录,避免与后台 worker 的状态回写竞争。
+// Delete hard-deletes a broadcast record. Refuses to delete records that are
+// still being dispatched to avoid racing the background worker's status writes.
+func (s *EmailBroadcastService) Delete(ctx context.Context, id int64) error {
+ if id <= 0 {
+ return ErrEmailBroadcastNotFound
+ }
+ existing, err := s.repo.GetByID(ctx, id)
+ if err != nil {
+ return err
+ }
+ if existing == nil {
+ return ErrEmailBroadcastNotFound
+ }
+ if existing.Status == EmailBroadcastStatusPending || existing.Status == EmailBroadcastStatusSending {
+ return ErrEmailBroadcastDeleteInFlight
+ }
+ if s.isRunning(id) {
+ return ErrEmailBroadcastDeleteInFlight
+ }
+ return s.repo.Delete(ctx, id)
+}
+
+// runBroadcast 是后台 goroutine 入口,对单次 broadcast 执行解析收件人 + 实际 SMTP 投递 + 状态回写。
+// 整个过程使用独立的 context.Background,避免 HTTP 请求结束导致中断。
+func (s *EmailBroadcastService) runBroadcast(id int64) {
+ if !s.markRunning(id) {
+ return
+ }
+ defer s.unmarkRunning(id)
+
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
+ defer cancel()
+
+ defer func() {
+ if r := recover(); r != nil {
+ logger.L().Error("email_broadcast.panic", zap.Int64("broadcast_id", id), zap.Any("panic", r))
+ now := time.Now()
+ msg := fmt.Sprintf("panic: %v", r)
+ _ = s.repo.UpdateStatus(ctx, id, EmailBroadcastStatusUpdate{
+ Status: ptrStr(EmailBroadcastStatusFailed),
+ ErrorMessage: &msg,
+ FinishedAt: &now,
+ })
+ }
+ }()
+
+ broadcast, err := s.repo.GetByID(ctx, id)
+ if err != nil || broadcast == nil {
+ logger.L().Error("email_broadcast.load_failed", zap.Int64("broadcast_id", id), zap.Error(err))
+ return
+ }
+
+ smtpConfig, err := s.emailService.GetSMTPConfig(ctx)
+ if err != nil || smtpConfig == nil {
+ now := time.Now()
+ msg := "SMTP config unavailable"
+ _ = s.repo.UpdateStatus(ctx, id, EmailBroadcastStatusUpdate{
+ Status: ptrStr(EmailBroadcastStatusFailed),
+ ErrorMessage: &msg,
+ FinishedAt: &now,
+ })
+ return
+ }
+
+ emails, err := s.resolveRecipientEmails(ctx, broadcast)
+ if err != nil {
+ now := time.Now()
+ msg := err.Error()
+ _ = s.repo.UpdateStatus(ctx, id, EmailBroadcastStatusUpdate{
+ Status: ptrStr(EmailBroadcastStatusFailed),
+ ErrorMessage: &msg,
+ FinishedAt: &now,
+ })
+ return
+ }
+ if len(emails) == 0 {
+ now := time.Now()
+ msg := "no valid recipients found"
+ zero := 0
+ _ = s.repo.UpdateStatus(ctx, id, EmailBroadcastStatusUpdate{
+ Status: ptrStr(EmailBroadcastStatusFailed),
+ TotalCount: &zero,
+ ErrorMessage: &msg,
+ FinishedAt: &now,
+ })
+ return
+ }
+
+ senderName := s.resolveSenderName(ctx)
+ htmlBody := s.composeHTMLBody(broadcast.Subject, broadcast.Body, broadcast.BodyFormat, senderName)
+
+ started := time.Now()
+ total := len(emails)
+ zero := 0
+ _ = s.repo.UpdateStatus(ctx, id, EmailBroadcastStatusUpdate{
+ Status: ptrStr(EmailBroadcastStatusSending),
+ TotalCount: &total,
+ SuccessCount: &zero,
+ FailedCount: &zero,
+ StartedAt: &started,
+ })
+
+ success, failed := 0, 0
+ for idx, addr := range emails {
+ if err := s.emailService.SendEmailWithConfigAndContentType(
+ smtpConfig,
+ addr,
+ broadcast.Subject,
+ htmlBody,
+ "text/html; charset=UTF-8",
+ ); err != nil {
+ failed++
+ logger.L().Warn("email_broadcast.send_failed",
+ zap.Int64("broadcast_id", id),
+ zap.String("recipient", addr),
+ zap.Error(err))
+ } else {
+ success++
+ }
+
+ // Throttle to avoid SMTP rate limits; skip after last message.
+ if idx < total-1 && s.sendIntervalPerEmail > 0 {
+ select {
+ case <-ctx.Done():
+ failed += total - idx - 1
+ goto done
+ case <-time.After(s.sendIntervalPerEmail):
+ }
+ }
+ }
+
+done:
+ finished := time.Now()
+ finalStatus := EmailBroadcastStatusCompleted
+ if success == 0 && failed > 0 {
+ finalStatus = EmailBroadcastStatusFailed
+ }
+ _ = s.repo.UpdateStatus(ctx, id, EmailBroadcastStatusUpdate{
+ Status: ptrStr(finalStatus),
+ SuccessCount: &success,
+ FailedCount: &failed,
+ FinishedAt: &finished,
+ })
+
+ logger.L().Info("email_broadcast.completed",
+ zap.Int64("broadcast_id", id),
+ zap.Int("total", total),
+ zap.Int("success", success),
+ zap.Int("failed", failed),
+ zap.String("status", finalStatus))
+}
+
+// resolveRecipientEmails 把 broadcast 描述的"全部用户 / 指定 IDs"展开为收件人邮箱列表。
+func (s *EmailBroadcastService) resolveRecipientEmails(ctx context.Context, b *EmailBroadcast) ([]string, error) {
+ emails := make([]string, 0)
+ seen := make(map[string]struct{})
+
+ collect := func(email string) {
+ email = strings.TrimSpace(email)
+ if email == "" {
+ return
+ }
+ key := strings.ToLower(email)
+ if _, ok := seen[key]; ok {
+ return
+ }
+ seen[key] = struct{}{}
+ emails = append(emails, email)
+ }
+
+ switch b.RecipientsMode {
+ case EmailBroadcastRecipientsModeAll:
+ const pageSize = 500
+ page := 1
+ for {
+ users, pageResult, err := s.userRepo.List(ctx, paginationParams(page, pageSize))
+ if err != nil {
+ return nil, fmt.Errorf("list users: %w", err)
+ }
+ for i := range users {
+ collect(users[i].Email)
+ }
+ if pageResult == nil || len(users) < pageSize {
+ break
+ }
+ // 防御性:避免无限循环
+ if int64(page*pageSize) >= pageResult.Total {
+ break
+ }
+ page++
+ }
+ case EmailBroadcastRecipientsModeSelected:
+ for _, id := range b.RecipientUserIDs {
+ user, err := s.userRepo.GetByID(ctx, id)
+ if err != nil {
+ logger.L().Warn("email_broadcast.user_lookup_failed",
+ zap.Int64("broadcast_id", b.ID),
+ zap.Int64("user_id", id),
+ zap.Error(err))
+ continue
+ }
+ if user != nil {
+ collect(user.Email)
+ }
+ }
+ default:
+ return nil, fmt.Errorf("unknown recipients mode: %s", b.RecipientsMode)
+ }
+
+ return emails, nil
+}
+
+// PreviewHTML 生成与最终投递一致的预览 HTML。
+// 不需要落库,主要给管理后台编辑器实时预览使用。
+//
+// PreviewHTML returns the exact HTML that would be sent for the given
+// subject + body + format. It is intentionally side-effect free so it can be
+// driven by the admin composer for live previewing.
+func (s *EmailBroadcastService) PreviewHTML(ctx context.Context, subject, body, format string) string {
+ senderName := s.resolveSenderName(ctx)
+ return s.composeHTMLBody(subject, body, format, senderName)
+}
+
+// composeHTMLBody 根据 broadcast 的 body_format 生成最终的 HTML 邮件正文。
+// 纯文本会被 HTML-escape、保留段落与换行;HTML 会经过 bluemonday sanitize。
+// 两种格式最终都使用统一的卡片式邮件模板,附 senderName 头部和系统签名页脚,
+// 与 sub2api 现有其他模板风格保持一致。
+func (s *EmailBroadcastService) composeHTMLBody(subject, body, format, senderName string) string {
+ var inner string
+ switch format {
+ case EmailBroadcastBodyFormatText:
+ inner = renderPlainTextAsHTML(body)
+ case EmailBroadcastBodyFormatHTML:
+ inner = s.htmlSanitizer.Sanitize(body)
+ default:
+ inner = html.EscapeString(body)
+ }
+ return wrapBroadcastHTMLShell(subject, senderName, inner)
+}
+
+// resolveSenderName 读取邮件落款使用的"发件人名称":
+// 1. 优先使用 SMTP 配置里的 \"发件人名称\"(smtp_from_name)。这与收件人收件箱中看到的
+// 发件人显示名一致(SendEmailWithConfig 在 From 头里就用这个字段),邮件正文头
+// 与签名也跟着保持一致。
+// 2. 回退到站点名(site_name)。
+// 3. 仍然为空时回退到 \"Sub2API\"。
+//
+// resolveSenderName chooses the display name used in the email header banner and
+// system footer. It prefers the SMTP From-Name (the same name the inbox shows as
+// the sender), falling back to site_name and finally to "Sub2API".
+func (s *EmailBroadcastService) resolveSenderName(ctx context.Context) string {
+ if s.settingRepo == nil {
+ return "Sub2API"
+ }
+ for _, key := range []string{SettingKeySMTPFromName, SettingKeySiteName} {
+ name, err := s.settingRepo.GetValue(ctx, key)
+ if err != nil {
+ continue
+ }
+ if trimmed := strings.TrimSpace(name); trimmed != "" {
+ return trimmed
+ }
+ }
+ return "Sub2API"
+}
+
+// renderPlainTextAsHTML 把纯文本转成对应的 HTML 片段:
+// - HTML-escape 防 XSS
+// - 连续两个换行 -> 段落分隔 ( )
+// - 单个换行 -> 行内换行 ( "+escaped+"
)
+// renderPlainTextAsHTML converts a plain-text body into HTML by escaping it,
+// splitting on blank lines into paragraphs and turning single line breaks into
.
+func renderPlainTextAsHTML(body string) string {
+ normalized := strings.ReplaceAll(body, "\r\n", "\n")
+ paragraphs := strings.Split(normalized, "\n\n")
+ parts := make([]string, 0, len(paragraphs))
+ for _, p := range paragraphs {
+ trimmed := strings.TrimSpace(p)
+ if trimmed == "" {
+ continue
+ }
+ escaped := html.EscapeString(trimmed)
+ escaped = strings.ReplaceAll(escaped, "\n", "
")
+ parts = append(parts, "
| + + | +
+ require.GreaterOrEqual(t, strings.Count(got, "
safe
link`, EmailBroadcastBodyFormatHTML, "Site") + require.Contains(t, got, "safe
") + require.NotContains(t, got, " diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 6735029c7c3..d0de9e27988 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -4424,6 +4424,82 @@ export default { deleteConfirm: 'Are you sure you want to delete this announcement? This action cannot be undone.' }, + // Bulk announcement email broadcasts + emailBroadcast: { + title: 'Announcement Email Broadcast', + description: 'Compose and bulk-send an announcement email to selected users or all registered users, useful when users cannot see in-app popups before they log in.', + openComposer: 'Compose announcement email', + form: { + subject: 'Subject', + subjectPlaceholder: 'e.g. Scheduled maintenance this Sunday', + body: 'Body', + bodyFormat: 'Format', + bodyFormatText: 'Plain text', + bodyPlaceholderHtml: 'Basic HTML allowed:, , , /
/