package emailcli import "time" // PaginationResult 是分页接口的统一返回结构。 // // { // "list": [...], // 当前页数据 // "total": 123, // 总记录数 // "page": 1, // "page_size": 20 // } type PaginationResult[T any] struct { List []T `json:"list"` Total int64 `json:"total"` Page int `json:"page"` PageSize int `json:"page_size"` } // PaginationQuery 是所有列表接口的分页参数基类, // 业务 Query 结构体可通过嵌入来复用。 // - Page: 页码,从 1 开始;省略时后端默认 1 // - PageSize: 每页大小,省略时后端默认 20,最大 100 type PaginationQuery struct { Page int `json:"page,omitempty"` PageSize int `json:"page_size,omitempty"` } // GormModel 对应后端 gorm.Model。注意后端该部分没有自定义 json tag, // 因此字段使用 PascalCase 序列化(与数据库模型保持一致)。 type GormModel struct { ID uint `json:"ID"` CreatedAt time.Time `json:"CreatedAt"` UpdatedAt time.Time `json:"UpdatedAt"` DeletedAt *time.Time `json:"DeletedAt"` } // ============================================================ // Account 邮件账号 // ============================================================ // Account 表示一个邮件发送账号。 // AppKey 用于发件认证;AppSecret 只在创建或重置时返回一次, // 入库时以 bcrypt 保存,SDK 不会回传。 type Account struct { GormModel UserID int `json:"user_id"` // 关联主平台用户 ID Name string `json:"name"` // 账号名称 AppKey string `json:"app_key"` // 发件公钥 Status int8 `json:"status"` // 账号状态: 0=禁用 1=启用 AuditMode int8 `json:"audit_mode"` // 审核模式: 0=免审核 1=自动 2=人工 RateLimit int `json:"rate_limit"` // 频率限制(封/分钟),0 表示不限 DefaultChannelID *uint `json:"default_channel_id"` // 默认发件通道 ID AllowedChannels string `json:"allowed_channels"` // 允许的通道 ID 列表 JSON 字符串,如 "[1,2]";空串=不限 DefaultSignatureID *uint `json:"default_signature_id"` // 默认签名 ID Remark string `json:"remark"` // 备注 } // CreateAccountReq 是创建账号的请求体。 type CreateAccountReq struct { UserID int `json:"user_id"` // 必填,关联用户 ID Name string `json:"name"` // 必填,账号名称,≤100 AuditMode *int8 `json:"audit_mode,omitempty"` // 可选,默认 0 免审核 RateLimit *int `json:"rate_limit,omitempty"` // 可选,0=不限 DefaultChannelID *uint `json:"default_channel_id,omitempty"` // 可选,默认发件通道 ID AllowedChannels string `json:"allowed_channels,omitempty"` // 可选,允许通道 ID 列表的 JSON 字符串 Remark string `json:"remark,omitempty"` // 可选,备注 } // CreateAccountResp 是创建账号后的返回,AppSecret 仅此一次明文展示。 type CreateAccountResp struct { ID uint `json:"id"` AppKey string `json:"app_key"` AppSecret string `json:"app_secret"` // 明文密钥,务必妥善保存 Name string `json:"name"` } // UpdateAccountReq 所有字段均为可选;只对传入字段做局部更新。 type UpdateAccountReq struct { Name *string `json:"name,omitempty"` Status *int8 `json:"status,omitempty"` // 0=禁用 1=启用 AuditMode *int8 `json:"audit_mode,omitempty"` // 0=免审核 1=自动 2=人工 RateLimit *int `json:"rate_limit,omitempty"` // 封/分钟,0=不限 DefaultChannelID *uint `json:"default_channel_id,omitempty"` // 默认发件通道 ID AllowedChannels *string `json:"allowed_channels,omitempty"` // 允许通道 ID 列表 JSON DefaultSignatureID *uint `json:"default_signature_id,omitempty"` // 默认签名 ID Remark *string `json:"remark,omitempty"` } // AccountListQuery 是账号列表过滤参数。 type AccountListQuery struct { PaginationQuery UserID *int `json:"user_id,omitempty"` // 按用户过滤 Status *int8 `json:"status,omitempty"` // 0=禁用 1=启用 Keyword string `json:"keyword,omitempty"` // 模糊匹配 name / remark } // ResetSecretResp 是重置密钥后的返回。 type ResetSecretResp struct { AppSecret string `json:"app_secret"` // 新生成的明文密钥 } // ============================================================ // Signature 签名 // ============================================================ // Signature 表示一个邮件签名。 // EnglishName 会作为 From 地址的 local 部分组装发件人(签名@域名)。 type Signature struct { GormModel UserID int `json:"user_id"` AccountID *uint `json:"account_id"` // 为空表示用户全局签名 Title string `json:"title"` // 中文抬头 EnglishName string `json:"english_name"` // 英文标识(用于拼 From 地址) Content string `json:"content"` // HTML 签名正文 Applicant string `json:"applicant"` // 申请人 ApplicantInfo string `json:"applicant_info"` // 申请说明 Status int8 `json:"status"` // 0=待审核 1=已通过 2=已驳回 RejectReason string `json:"reject_reason"` Auditor string `json:"auditor"` AuditedAt *time.Time `json:"audited_at"` } // CreateSignatureReq 是创建签名的请求体。新建签名默认 Status=0(待审核)。 type CreateSignatureReq struct { UserID int `json:"user_id"` // 必填 AccountID *uint `json:"account_id,omitempty"` // 可选,绑定账号 Title string `json:"title"` // 必填,中文抬头 EnglishName string `json:"english_name"` // 必填,英文标识 Content string `json:"content,omitempty"` // HTML 签名内容 Applicant string `json:"applicant,omitempty"` // 申请人 ApplicantInfo string `json:"applicant_info,omitempty"` // 申请说明 } // UpdateSignatureReq 局部更新签名字段。 type UpdateSignatureReq struct { Title *string `json:"title,omitempty"` EnglishName *string `json:"english_name,omitempty"` Content *string `json:"content,omitempty"` Applicant *string `json:"applicant,omitempty"` ApplicantInfo *string `json:"applicant_info,omitempty"` } // AuditSignatureReq 是审核签名的请求体。 type AuditSignatureReq struct { Status int8 `json:"status"` // 1=通过 2=驳回 RejectReason string `json:"reject_reason,omitempty"` // 驳回时填写 } // SignatureListQuery 是签名列表过滤参数。 type SignatureListQuery struct { PaginationQuery AccountID *uint `json:"account_id,omitempty"` Status *int8 `json:"status,omitempty"` // 0=待审核 1=已通过 2=已驳回 UserID *int `json:"user_id,omitempty"` Keyword string `json:"keyword,omitempty"` // 模糊匹配 title / english_name } // ============================================================ // Mail 发送邮件 // ============================================================ // SendMailReq 是 POST /api/v1/mail/send 的请求体。 // // Channel 为空时,后端按以下顺序自动选择: // 1. Account.DefaultChannelID 对应的已启用通道; // 2. Account.AllowedChannels 列表中的首个已启用通道。 type SendMailReq struct { To []string `json:"to"` // 必填,收件人列表(至少 1 个) Cc []string `json:"cc,omitempty"` // 抄送 Bcc []string `json:"bcc,omitempty"` // 密送 Subject string `json:"subject"` // 必填,主题 Body string `json:"body"` // 必填,正文 ContentType string `json:"content_type,omitempty"` // text/plain 或 text/html,默认 text/html Channel string `json:"channel,omitempty"` // 通道 code;留空走默认/允许通道 SignatureID *uint `json:"signature_id,omitempty"` // 指定签名 ID(优先级低于 title) SignatureTitle string `json:"signature_title,omitempty"` // 按 title + user_id 查找已审核签名 Attachments []AttachmentItem `json:"attachments,omitempty"` // 附件 } // AttachmentItem 附件项,Content 为 base64 编码字符串。 type AttachmentItem struct { Filename string `json:"filename"` // 文件名(含扩展名) Content string `json:"content"` // base64 编码的文件内容 } // SendMailResp 是发送邮件的返回结果。 type SendMailResp struct { MailLogID uint `json:"mail_log_id"` // 创建的邮件日志 ID Status string `json:"status"` // queued / pending_audit / rejected } // ============================================================ // MailLog 邮件日志 // ============================================================ // MailLog 邮件日志记录。 // Status 枚举:0=待审核 1=排队中 2=发送中 3=成功 4=失败 5=放弃 6=驳回 type MailLog struct { GormModel UserID int `json:"user_id"` AccountID uint `json:"account_id"` QuotaID *uint `json:"quota_id"` // 扣减的配额记录 ID ChannelID *uint `json:"channel_id"` // 使用的发件通道 ID SenderAccountID *uint `json:"sender_account_id"` // 实际使用的发信账号 ID SignatureID *uint `json:"signature_id"` MessageID string `json:"message_id"` // SMTP 返回的 Message-ID FromAddress string `json:"from_address"` // 实际 From 地址 ToAddresses string `json:"to_addresses"` // JSON 字符串数组 CcAddresses string `json:"cc_addresses"` // JSON 字符串数组 BccAddresses string `json:"bcc_addresses"` // JSON 字符串数组 Subject string `json:"subject"` ContentType string `json:"content_type"` HasAttachment bool `json:"has_attachment"` SourceIP string `json:"source_ip"` // 请求来源 IP SourceType string `json:"source_type"` // http / smtp Status int8 `json:"status"` RetryCount int `json:"retry_count"` MaxRetry int `json:"max_retry"` ErrorMessage string `json:"error_message"` SentAt *time.Time `json:"sent_at"` } // MailLogListQuery 是邮件日志列表过滤参数。 type MailLogListQuery struct { PaginationQuery UserID *int `json:"user_id,omitempty"` AccountID *uint `json:"account_id,omitempty"` Status *int8 `json:"status,omitempty"` // 见 MailLog.Status 枚举 StartDate string `json:"start_date,omitempty"` // 格式 YYYY-MM-DD EndDate string `json:"end_date,omitempty"` // 格式 YYYY-MM-DD To string `json:"to,omitempty"` // 精确匹配收件人 Keyword string `json:"keyword,omitempty"` // 模糊匹配主题/收件人 } // MailLogDetail 邮件日志详情,包含完整正文。 type MailLogDetail struct { Log MailLog `json:"log"` Body string `json:"body"` // 邮件正文 } // MailStatItem 按状态分组的邮件计数,用于概览统计。 type MailStatItem struct { Status int8 `json:"status"` Count int64 `json:"count"` } // ============================================================ // Quota 配额 // ============================================================ // MailQuota 配额记录。 // - QuotaType=1 总量:Total 为生命周期内的总配额 // - QuotaType=2 周期:每个 CycleUnit 周期重置 Used type MailQuota struct { GormModel UserID int `json:"user_id"` AccountID uint `json:"account_id"` QuotaType int8 `json:"quota_type"` // 1=总量 2=周期 Total int `json:"total"` Used int `json:"used"` ExpireAt *time.Time `json:"expire_at"` // 到期时间,仅总量配额有意义 CycleUnit string `json:"cycle_unit"` // day/week/month/year CycleResetAt *time.Time `json:"cycle_reset_at"` // 周期起始时间 Status int8 `json:"status"` // 0=禁用 1=启用 } // CreateQuotaReq 创建配额。 type CreateQuotaReq struct { AccountID uint `json:"account_id"` // 必填 QuotaType int8 `json:"quota_type"` // 必填,1=总量 2=周期 Total int `json:"total"` // 必填,配额上限 ExpireAt string `json:"expire_at,omitempty"` // YYYY-MM-DD HH:mm:ss CycleUnit string `json:"cycle_unit,omitempty"` // day/week/month/year CycleResetAt string `json:"cycle_reset_at,omitempty"` // 周期起点 } // UpdateQuotaReq 局部更新配额。 type UpdateQuotaReq struct { Total *int `json:"total,omitempty"` Status *int8 `json:"status,omitempty"` ExpireAt *string `json:"expire_at,omitempty"` CycleUnit *string `json:"cycle_unit,omitempty"` CycleResetAt *string `json:"cycle_reset_at,omitempty"` } // QuotaListQuery 配额列表过滤参数。 type QuotaListQuery struct { PaginationQuery AccountID *uint `json:"account_id,omitempty"` UserID *int `json:"user_id,omitempty"` Status *int8 `json:"status,omitempty"` } // QuotaSummary 指定账号的配额汇总信息。 type QuotaSummary struct { AccountID uint `json:"account_id"` TotalQuota int `json:"total_quota"` // 所有配额累加总量 TotalUsed int `json:"total_used"` // 已用 TotalRemaining int `json:"total_remaining"` // 剩余 Details []MailQuota `json:"details"` // 每条配额明细 } // ============================================================ // Channel 通道 // ============================================================ // Channel 发件通道,一个通道下可挂多个 SenderAccount,由 Strategy 决定分发逻辑。 type Channel struct { GormModel Name string `json:"name"` Code string `json:"code"` // 唯一标识,发件请求中 channel 字段填这个 Description string `json:"description"` Strategy string `json:"strategy"` // round_robin / weight / least_used Status int8 `json:"status"` // 0=禁用 1=启用 } // CreateChannelReq 创建通道。 type CreateChannelReq struct { Name string `json:"name"` // 必填 Code string `json:"code"` // 必填,唯一 Description string `json:"description,omitempty"` Strategy string `json:"strategy,omitempty"` // 默认 round_robin } // UpdateChannelReq 局部更新通道。 type UpdateChannelReq struct { Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` Strategy *string `json:"strategy,omitempty"` Status *int8 `json:"status,omitempty"` } // ChannelListQuery 通道列表过滤参数。 type ChannelListQuery struct { PaginationQuery Status *int8 `json:"status,omitempty"` Keyword string `json:"keyword,omitempty"` // 模糊匹配 name / code } // ============================================================ // SenderAccount 发信账号(SMTP) // ============================================================ // SenderAccount 挂在某个 Channel 下的 SMTP 发信账号。 type SenderAccount struct { GormModel ChannelID uint `json:"channel_id"` Name string `json:"name"` SmtpHost string `json:"smtp_host"` SmtpPort int `json:"smtp_port"` SmtpUser string `json:"smtp_user"` SmtpSSL bool `json:"smtp_ssl"` FromName string `json:"from_name"` FromAddress string `json:"from_address"` DailyLimit int `json:"daily_limit"` // 每日发送上限,0=不限 DailySent int `json:"daily_sent"` // 当日已发送数 Weight int `json:"weight"` // Strategy=weight 时使用 Status int8 `json:"status"` // 0=禁用 1=启用 LastCheckAt *time.Time `json:"last_check_at"` LastCheckResult string `json:"last_check_result"` } // CreateSenderReq 创建发信账号。Password 只存密文,不会回显。 type CreateSenderReq struct { Name string `json:"name"` // 必填 SmtpHost string `json:"smtp_host"` // 必填 SmtpPort int `json:"smtp_port"` // 必填 SmtpUser string `json:"smtp_user"` // 必填 SmtpPassword string `json:"smtp_password"` // 必填 SmtpSSL *bool `json:"smtp_ssl,omitempty"` FromName string `json:"from_name,omitempty"` FromAddress string `json:"from_address"` // 必填 DailyLimit *int `json:"daily_limit,omitempty"` Weight *int `json:"weight,omitempty"` } // UpdateSenderReq 局部更新发信账号。 type UpdateSenderReq struct { Name *string `json:"name,omitempty"` SmtpHost *string `json:"smtp_host,omitempty"` SmtpPort *int `json:"smtp_port,omitempty"` SmtpUser *string `json:"smtp_user,omitempty"` SmtpPassword *string `json:"smtp_password,omitempty"` SmtpSSL *bool `json:"smtp_ssl,omitempty"` FromName *string `json:"from_name,omitempty"` FromAddress *string `json:"from_address,omitempty"` DailyLimit *int `json:"daily_limit,omitempty"` Weight *int `json:"weight,omitempty"` Status *int8 `json:"status,omitempty"` } // SenderListQuery 发信账号列表过滤参数。 type SenderListQuery struct { PaginationQuery Status *int8 `json:"status,omitempty"` Keyword string `json:"keyword,omitempty"` } // ============================================================ // Audit 审核 // ============================================================ // MailAudit 邮件审核记录。 type MailAudit struct { GormModel MailLogID uint `json:"mail_log_id"` UserID int `json:"user_id"` AccountID uint `json:"account_id"` AuditType int8 `json:"audit_type"` // 1=自动 2=人工 Action int8 `json:"action"` // 1=通过 2=驳回 RejectReason string `json:"reject_reason"` HitRules string `json:"hit_rules"` // 命中规则列表 JSON Auditor string `json:"auditor"` AuditedAt time.Time `json:"audited_at"` } // AuditPendingQuery 待审核列表过滤参数。 type AuditPendingQuery struct { PaginationQuery UserID *int `json:"user_id,omitempty"` AccountID *uint `json:"account_id,omitempty"` Keyword string `json:"keyword,omitempty"` // 模糊匹配主题/收件人 } // AuditLogQuery 审核记录列表过滤参数。 type AuditLogQuery struct { PaginationQuery AuditType *int8 `json:"audit_type,omitempty"` // 1=自动 2=人工 Action *int8 `json:"action,omitempty"` // 1=通过 2=驳回 UserID *int `json:"user_id,omitempty"` StartDate string `json:"start_date,omitempty"` // YYYY-MM-DD EndDate string `json:"end_date,omitempty"` // YYYY-MM-DD } // AuditRejectReq 驳回审核请求体。 type AuditRejectReq struct { RejectReason string `json:"reject_reason"` // 建议填写驳回原因 } // BatchAuditApproveReq 批量通过请求体。 type BatchAuditApproveReq struct { MailLogIDs []uint `json:"mail_log_ids"` } // BatchAuditRejectReq 批量驳回请求体。 type BatchAuditRejectReq struct { MailLogIDs []uint `json:"mail_log_ids"` RejectReason string `json:"reject_reason"` } // AuditStats 审核概览统计(用于仪表盘)。 type AuditStats struct { PendingCount int64 `json:"pending_count"` // 待审核数量 TodayDetails []AuditDetailCount `json:"today_details"` // 今日按 type/action 分组 } // AuditDetailCount 按 AuditType+Action 分组的计数。 type AuditDetailCount struct { AuditType int8 `json:"audit_type"` Action int8 `json:"action"` Count int64 `json:"count"` } // ============================================================ // AuditRule 审核规则 // ============================================================ // AuditRule 审核规则。自动审核会按 Priority 从高到低依次评估, // 命中即返回 Action。 type AuditRule struct { GormModel Name string `json:"name"` RuleType string `json:"rule_type"` // keyword/regex/domain Target string `json:"target"` // subject/body/to/from Condition string `json:"condition"` // 关键词或正则 Action int8 `json:"action"` // 1=自动通过 2=自动驳回 3=转人工 Priority int `json:"priority"` // 数字越大优先级越高 Status int8 `json:"status"` // 0=禁用 1=启用 Remark string `json:"remark"` } // CreateAuditRuleReq 创建规则。 type CreateAuditRuleReq struct { Name string `json:"name"` // 必填 RuleType string `json:"rule_type"` // 必填,keyword/regex/domain Target string `json:"target"` // 必填,subject/body/to/from Condition string `json:"condition"` // 必填 Action int8 `json:"action"` // 必填,1/2/3 Priority *int `json:"priority,omitempty"` Remark string `json:"remark,omitempty"` } // UpdateAuditRuleReq 局部更新规则。 type UpdateAuditRuleReq struct { Name *string `json:"name,omitempty"` RuleType *string `json:"rule_type,omitempty"` Target *string `json:"target,omitempty"` Condition *string `json:"condition,omitempty"` Action *int8 `json:"action,omitempty"` Priority *int `json:"priority,omitempty"` Status *int8 `json:"status,omitempty"` Remark *string `json:"remark,omitempty"` } // TestAuditRuleReq 在不发送邮件的前提下测试规则命中情况。 type TestAuditRuleReq struct { Subject string `json:"subject,omitempty"` Body string `json:"body,omitempty"` To []string `json:"to,omitempty"` From string `json:"from,omitempty"` AccountID uint `json:"account_id,omitempty"` } // TestAuditRuleResp 规则测试结果。 type TestAuditRuleResp struct { Action string `json:"action"` // approve/reject/to_manual/none HitRules []HitRuleEntry `json:"hit_rules"` // 命中的规则列表 } // HitRuleEntry 命中规则信息。 type HitRuleEntry struct { RuleID uint `json:"rule_id"` RuleName string `json:"rule_name"` RuleType string `json:"rule_type"` } // ============================================================ // Queue 发送队列 // ============================================================ // QueueStatusData 队列状态快照。 // - Queues: key = 通道 code, value = 该通道排队长度 // - DelayQueue: 延迟重试队列长度 type QueueStatusData struct { Queues map[string]int `json:"queues"` DelayQueue int `json:"delay_queue"` } // QueuePendingQuery 队列待发送邮件过滤参数。 type QueuePendingQuery struct { PaginationQuery ChannelID *uint `json:"channel_id,omitempty"` UserID *int `json:"user_id,omitempty"` AccountID *uint `json:"account_id,omitempty"` } // ============================================================ // Check 健康检查 // ============================================================ // CheckLog 发信账号健康检查记录。 // 通过向 verification 地址发测试邮件并等待 webhook 回调验证收信可用性。 type CheckLog struct { ID uint `json:"id"` SenderAccountID uint `json:"sender_account_id"` VerificationCode string `json:"verification_code"` SentAt time.Time `json:"sent_at"` Received bool `json:"received"` // 是否收到回执 ReceivedAt *time.Time `json:"received_at"` LatencyMs int `json:"latency_ms"` // 端到端延迟,毫秒 ErrorMessage string `json:"error_message"` CreatedAt time.Time `json:"created_at"` } // CheckLogQuery 健康检查日志过滤参数。 type CheckLogQuery struct { PaginationQuery SenderAccountID *uint `json:"sender_account_id,omitempty"` StartDate string `json:"start_date,omitempty"` EndDate string `json:"end_date,omitempty"` } // SenderHealth 发信账号健康汇总。 type SenderHealth struct { SenderAccountID uint `json:"sender_account_id"` Name string `json:"name"` FromAddress string `json:"from_address"` Status int8 `json:"status"` LastCheckResult string `json:"last_check_result"` TotalChecks int64 `json:"total_checks"` SuccessChecks int64 `json:"success_checks"` } // TriggerCheckResp 手动触发健康检查的返回。 type TriggerCheckResp struct { Result string `json:"result"` // ok / failed Error string `json:"error,omitempty"` CheckLog *CheckLog `json:"check_log,omitempty"` }