docs(README): 更新文档为中文并完善API参考

- 将README从英文翻译为中文
- 添加详细的API参考文档,包括所有管理接口和枚举值说明
- 补充安装、快速开始、认证方式等使用指南

refactor(client): 优化客户端代码结构并添加详细注释

- 为所有API方法添加中文注释和使用说明
- 改进Client结构体和Option配置的设计
- 统一错误处理和响应结构的文档说明
This commit is contained in:
shiran
2026-04-18 15:54:19 +08:00
parent fe19922eff
commit fe43b9bdce
15 changed files with 1013 additions and 291 deletions
+394 -135
View File
@@ -1,46 +1,38 @@
# email-serverr-cli
Go client library for the Email Server API.
Email Server API 的 Go 客户端库。提供管理端(ServiceAuth)与发件端(AppAuth)两种客户端,
覆盖邮件发送、账号、签名、配额、通道/发信、审核、队列、健康检查等全部后端能力。
## Install
- 模块路径: `gitea.s1f.ren/shiran/email-serverr-cli`
- 依赖: 仅使用标准库 `net/http``encoding/json`,无第三方依赖
- 风格: 对每一类资源一个文件,请求/响应类型在 `types.go`,底层请求在 `client.go`
## 安装
```bash
go get gitea.s1f.ren/shiran/email-serverr-cli
```
## Quick Start
### Management Client (ServiceAuth)
```go
package main
import (
"context"
"fmt"
emailcli "gitea.s1f.ren/shiran/email-serverr-cli"
)
func main() {
client := emailcli.NewServiceClient(
"https://your-server.com",
"your-service-token",
)
// List accounts
accounts, err := client.ListAccounts(context.Background(), emailcli.AccountListQuery{
PaginationQuery: emailcli.PaginationQuery{Page: 1, PageSize: 20},
})
if err != nil {
panic(err)
}
for _, a := range accounts.List {
fmt.Printf("Account: %s (ID: %d)\n", a.Name, a.ID)
}
}
import emailcli "gitea.s1f.ren/shiran/email-serverr-cli"
```
### Mail Sending Client (AppAuth)
## 快速开始
### 管理客户端(ServiceAuth
```go
client := emailcli.NewServiceClient(
"https://your-server.com",
"your-service-token",
)
accounts, err := client.ListAccounts(context.Background(), emailcli.AccountListQuery{
PaginationQuery: emailcli.PaginationQuery{Page: 1, PageSize: 20},
})
```
### 发件客户端(AppAuth
```go
client := emailcli.NewAppClient(
@@ -53,130 +45,393 @@ resp, err := client.SendMail(context.Background(), emailcli.SendMailReq{
To: []string{"recipient@example.com"},
Subject: "Hello",
Body: "<h1>Hello World</h1>",
Channel: "default",
// Channel 可选:不传时优先使用账号默认通道,其次使用允许通道列表首个可用项
})
if err != nil {
panic(err)
}
fmt.Printf("Mail sent: log_id=%d status=%s\n", resp.MailLogID, resp.Status)
```
## Authentication
### 客户端选项
| Mode | Constructor | Header | Use Case |
|------|-------------|--------|----------|
| ServiceAuth | `NewServiceClient` | `Authorization: Bearer <token>` | Management APIs |
| AppAuth | `NewAppClient` | `X-App-Key` + `X-App-Secret` | Mail sending |
```go
client := emailcli.NewServiceClient(baseURL, token,
emailcli.WithTimeout(60*time.Second),
emailcli.WithHTTPClient(customClient),
)
```
## API Reference
## 认证方式
### Mail (AppAuth)
| 模式 | 构造函数 | 请求头 | 适用范围 |
|------|----------|--------|----------|
| ServiceAuth | `NewServiceClient` | `Authorization: Bearer <token>` | 所有 `/api/v1` 管理接口 |
| AppAuth | `NewAppClient` | `X-App-Key` + `X-App-Secret` | 仅 `POST /api/v1/mail/send` |
| Method | Description |
|--------|-------------|
| `SendMail` | Send an email |
---
### Accounts (ServiceAuth)
## 枚举值说明
| Method | Description |
|--------|-------------|
| `CreateAccount` | Create mail account |
| `ListAccounts` | List accounts with filters |
| `GetAccount` | Get account details |
| `UpdateAccount` | Update account |
| `DeleteAccount` | Delete account |
| `ResetAccountSecret` | Reset app secret |
### Account.Status(账号状态)
| 值 | 含义 |
|----|------|
| 0 | 禁用 |
| 1 | 启用 |
### Signatures (ServiceAuth)
### Account.AuditMode(审核模式)
| 值 | 含义 |
|----|------|
| 0 | 免审核(直接入队) |
| 1 | 自动(按规则判定) |
| 2 | 人工(待审核) |
| Method | Description |
|--------|-------------|
| `CreateSignature` | Create signature |
| `ListSignatures` | List signatures with filters |
| `GetSignature` | Get signature details |
| `UpdateSignature` | Update signature |
| `DeleteSignature` | Delete signature |
| `AuditSignature` | Approve or reject signature |
### Signature.Status(签名状态)
| 值 | 含义 |
|----|------|
| 0 | 待审核 |
| 1 | 已通过 |
| 2 | 已驳回 |
### Mail Logs (ServiceAuth)
### MailLog.Status(邮件状态)
| 值 | 含义 |
|----|------|
| 0 | 待审核 |
| 1 | 排队中 |
| 2 | 发送中 |
| 3 | 成功 |
| 4 | 失败 |
| 5 | 放弃 |
| 6 | 驳回 |
| Method | Description |
|--------|-------------|
| `ListMailLogs` | List mail logs with filters |
| `GetMailLog` | Get mail log detail with body |
| `GetMailStats` | Get status statistics |
### MailQuota.QuotaType(配额类型)
| 值 | 含义 |
|----|------|
| 1 | 总量配额(`total` 为总发送上限) |
| 2 | 周期配额(到期自动重置) |
### Quotas (ServiceAuth)
### MailQuota.CycleUnit(配额周期单位)
`quota_type=2` 时生效,取值:`day``week``month``year`
| Method | Description |
|--------|-------------|
| `CreateQuota` | Create quota |
| `ListQuotas` | List quotas with filters |
| `GetQuotaSummary` | Get quota summary for account |
| `UpdateQuota` | Update quota |
| `DeleteQuota` | Delete quota |
### MailQuota.Status
| 值 | 含义 |
|----|------|
| 0 | 禁用 |
| 1 | 启用 |
### Channels (ServiceAuth)
### Channel.Status / SenderAccount.Status
| 值 | 含义 |
|----|------|
| 0 | 禁用 |
| 1 | 启用 |
| Method | Description |
|--------|-------------|
| `CreateChannel` | Create channel |
| `ListChannels` | List channels with filters |
| `UpdateChannel` | Update channel |
| `DeleteChannel` | Delete channel |
### Channel.Strategy(发信挑选策略)
| 值 | 含义 |
|------|------|
| `round_robin` | 轮询(默认) |
| `weight` | 按 `weight` 加权随机 |
| `least_used` | 今日发送数最少优先 |
### Sender Accounts (ServiceAuth)
### AuditRule.Action(规则动作)
| 值 | 含义 |
|----|------|
| 1 | 自动通过 |
| 2 | 自动驳回 |
| 3 | 转人工 |
| Method | Description |
|--------|-------------|
| `CreateSender` | Create sender under channel |
| `ListSendersByChannel` | List senders for channel |
| `UpdateSender` | Update sender |
| `DeleteSender` | Delete sender |
### AuditRule.RuleType / Target(规则类型与目标)
- `RuleType` 取值: `keyword``regex``domain`
- `Target` 取值: `subject``body``to``from`
### Audits (ServiceAuth)
### MailAudit.AuditType(审核来源)
| 值 | 含义 |
|----|------|
| 1 | 自动 |
| 2 | 人工 |
| Method | Description |
|--------|-------------|
| `ListAuditPending` | List pending audit items |
| `GetAuditPendingDetail` | Get pending item detail |
| `ApproveAudit` | Approve single item |
| `RejectAudit` | Reject single item |
| `BatchApproveAudit` | Batch approve |
| `BatchRejectAudit` | Batch reject |
| `ListAuditLogs` | List audit history |
| `GetAuditStats` | Get audit statistics |
### MailAudit.Action(审核动作)
| 值 | 含义 |
|----|------|
| 1 | 通过 |
| 2 | 驳回 |
### Audit Rules (ServiceAuth)
### SendMailReq.ContentType
取值:`text/plain`(默认)、`text/html`
| Method | Description |
|--------|-------------|
| `CreateAuditRule` | Create rule |
| `ListAuditRules` | List all rules |
| `GetAuditRule` | Get rule details |
| `UpdateAuditRule` | Update rule |
| `DeleteAuditRule` | Delete rule |
| `UpdateAuditRuleStatus` | Toggle rule status |
| `TestAuditRule` | Test rules against sample |
---
### Queue (ServiceAuth)
## API 参考
| Method | Description |
|--------|-------------|
| `GetQueueStatus` | Get queue lengths |
| `ListQueuePending` | List pending queue items |
| `CancelQueueItem` | Cancel queued mail |
| `RetryQueueItem` | Retry failed mail |
所有管理接口挂载在 `/api/v1` 下。以下按功能模块分组,并给出方法签名、HTTP 路径与关键参数说明。
### Health Checks (ServiceAuth)
### 一、发送邮件(AppAuth
| Method | Description |
|--------|-------------|
| `ListCheckLogs` | List check logs |
| `GetCheckSummary` | Get sender health summary |
| `TriggerCheck` | Trigger health check |
#### `SendMail(ctx, req SendMailReq) -> *SendMailResp`
## Error Handling
`POST /api/v1/mail/send`
`SendMailReq` 字段:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `To` | `[]string` | 是 | 收件人列表,至少 1 个 |
| `Cc` | `[]string` | 否 | 抄送 |
| `Bcc` | `[]string` | 否 | 密送 |
| `Subject` | `string` | 是 | 主题 |
| `Body` | `string` | 是 | 正文 |
| `ContentType` | `string` | 否 | 默认 `text/html` |
| `Channel` | `string` | 否 | 通道 `code`。为空时按 账号默认通道 → 账号允许通道首个可用 顺序自动解析 |
| `SignatureID` | `*uint` | 否 | 指定签名 ID(用户必须拥有且已审核) |
| `SignatureTitle` | `string` | 否 | 按 title + user_id 选择签名;未传则使用默认签名 |
| `Attachments` | `[]AttachmentItem` | 否 | 附件(`filename` + base64 `content` |
`SendMailResp` 字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `MailLogID` | `uint` | 创建的邮件日志 ID |
| `Status` | `string` | `queued` / `pending_audit` / `rejected` |
---
### 二、账号管理(ServiceAuth
#### `CreateAccount(ctx, req CreateAccountReq) -> *CreateAccountResp`
`POST /api/v1/accounts`
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `UserID` | `int` | 是 | 关联用户 ID |
| `Name` | `string` | 是 | 账号名称(≤100) |
| `AuditMode` | `*int8` | 否 | 审核模式枚举,默认 `0` |
| `RateLimit` | `*int` | 否 | 频率限制(封/分钟),`0` 表示不限 |
| `DefaultChannelID` | `*uint` | 否 | 默认发件通道 ID |
| `AllowedChannels` | `string` | 否 | 允许发件通道 ID 列表的 JSON 字符串,例如 `"[1,2]"`。为空时视作不限制 |
| `Remark` | `string` | 否 | 备注 |
返回 `CreateAccountResp`(首次创建返回明文 `AppSecret`):
```json
{ "id": 1, "app_key": "...", "app_secret": "只在此次展示", "name": "..." }
```
#### `ListAccounts(ctx, q AccountListQuery) -> *PaginationResult[Account]`
`GET /api/v1/accounts?page=&page_size=&user_id=&status=&keyword=`
| 参数 | 类型 | 说明 |
|------|------|------|
| `Page`/`PageSize` | `int` | 分页,`PageSize` 默认 20 |
| `UserID` | `*int` | 按用户筛选 |
| `Status` | `*int8` | 账号状态(0/1 |
| `Keyword` | `string` | 模糊匹配 `name``remark` |
#### `GetAccount(ctx, id uint) -> *Account`
`GET /api/v1/accounts/{id}`
#### `UpdateAccount(ctx, id uint, req UpdateAccountReq) -> *Account`
`PUT /api/v1/accounts/{id}`
所有字段均为可选指针,只更新已传字段。
| 字段 | 类型 | 说明 |
|------|------|------|
| `Name` | `*string` | |
| `Status` | `*int8` | 启用/禁用 |
| `AuditMode` | `*int8` | |
| `RateLimit` | `*int` | |
| `DefaultChannelID` | `*uint` | 默认发件通道 |
| `AllowedChannels` | `*string` | 允许发件通道 ID 列表 JSON |
| `DefaultSignatureID` | `*uint` | 默认签名 ID |
| `Remark` | `*string` | |
#### `DeleteAccount(ctx, id uint) error`
`DELETE /api/v1/accounts/{id}`
#### `ResetAccountSecret(ctx, id uint) -> *ResetSecretResp`
`POST /api/v1/accounts/{id}/reset-secret`
返回新生成的明文 `AppSecret`
---
### 三、签名管理(ServiceAuth
#### `CreateSignature(ctx, req CreateSignatureReq) -> *Signature`
`POST /api/v1/signatures`
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `UserID` | `int` | 是 | |
| `AccountID` | `*uint` | 否 | 绑定账号,为空表示用户全局签名 |
| `Title` | `string` | 是 | 中文抬头 |
| `EnglishName` | `string` | 是 | 英文标识(用于组装 From 地址) |
| `Content` | `string` | 否 | HTML 签名内容 |
| `Applicant` | `string` | 否 | 申请人 |
| `ApplicantInfo` | `string` | 否 | 申请说明 |
新建默认为 `Status=0`(待审核)。
#### `ListSignatures(ctx, q SignatureListQuery) -> *PaginationResult[Signature]`
`GET /api/v1/signatures?page=&page_size=&user_id=&account_id=&status=&keyword=`
#### `GetSignature(ctx, id uint)` / `UpdateSignature` / `DeleteSignature`
`GET|PUT|DELETE /api/v1/signatures/{id}`
#### `AuditSignature(ctx, id uint, req AuditSignatureReq)`
`POST /api/v1/signatures/{id}/audit`
| 字段 | 类型 | 说明 |
|------|------|------|
| `Action` | `int8` | `1`=通过,`2`=驳回 |
| `RejectReason` | `string` | 驳回时建议填写 |
| `Auditor` | `string` | 审核人标识 |
---
### 四、邮件日志(ServiceAuth
#### `ListMailLogs(ctx, q MailLogListQuery) -> *PaginationResult[MailLog]`
`GET /api/v1/mail-logs`
| 参数 | 类型 | 说明 |
|------|------|------|
| `UserID` | `*int` | |
| `AccountID` | `*uint` | |
| `Status` | `*int8` | 见 `MailLog.Status` 枚举 |
| `StartDate`/`EndDate` | `string` | `YYYY-MM-DD` |
| `To` | `string` | 收件人精确匹配 |
| `Keyword` | `string` | 模糊匹配主题或收件人 |
#### `GetMailLog(ctx, id uint) -> *MailLogDetail`
`GET /api/v1/mail-logs/{id}` 返回 `MailLog` + 正文 `Body`
#### `GetMailStats(ctx) -> []MailStatItem`
`GET /api/v1/mail-logs/stats``Status` 分组的邮件数统计。
---
### 五、配额管理(ServiceAuth
#### `CreateQuota(ctx, req CreateQuotaReq) -> *MailQuota`
`POST /api/v1/quotas`
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `UserID` | `int` | 是 | |
| `AccountID` | `uint` | 是 | |
| `QuotaType` | `int8` | 是 | `1`=总量,`2`=周期 |
| `Total` | `int` | 是 | 额度上限 |
| `ExpireAt` | `string` | 否 | `YYYY-MM-DD HH:mm:ss` |
| `CycleUnit` | `string` | 否 | `day` / `week` / `month` / `year` |
| `CycleResetAt` | `string` | 否 | 周期起始时间 |
#### `ListQuotas(ctx, q QuotaListQuery)` · `GetQuotaSummary(ctx, accountID)` · `UpdateQuota(ctx, id, req)` · `DeleteQuota(ctx, id)`
`UpdateQuotaReq` 支持局部更新 `Total``Status``ExpireAt``CycleUnit``CycleResetAt`
`QuotaListQuery` 过滤参数:`UserID``AccountID``QuotaType``Status`
---
### 六、通道与发信账号(ServiceAuth
#### 通道 `Channel`
- `CreateChannel(ctx, req CreateChannelReq) -> *Channel``POST /api/v1/channels`
- `ListChannels(ctx, q ChannelListQuery) -> *PaginationResult[Channel]``GET /api/v1/channels`
- `UpdateChannel(ctx, id, req UpdateChannelReq) -> *Channel``PUT /api/v1/channels/{id}`
- `DeleteChannel(ctx, id uint) error``DELETE /api/v1/channels/{id}`
`CreateChannelReq` 字段:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `Code` | `string` | 是 | 唯一标识(发送邮件用) |
| `Name` | `string` | 是 | |
| `Description` | `string` | 否 | |
| `Strategy` | `string` | 否 | 见 `Channel.Strategy` 枚举 |
#### 发信账号 `SenderAccount`
- `CreateSender(ctx, channelID uint, req CreateSenderReq) -> *SenderAccount``POST /api/v1/channels/{channelID}/senders`
- `ListSendersByChannel(ctx, channelID, q SenderListQuery) -> *PaginationResult[SenderAccount]``GET /api/v1/channels/{channelID}/senders`
- `UpdateSender(ctx, id uint, req UpdateSenderReq) -> *SenderAccount``PUT /api/v1/senders/{id}`
- `DeleteSender(ctx, id uint) error``DELETE /api/v1/senders/{id}`
`CreateSenderReq` 关键字段:`Name``SmtpHost``SmtpPort``SmtpUser``SmtpPassword``SmtpSSL``FromName``FromAddress``DailyLimit``Weight`
---
### 七、审核(ServiceAuth
- `ListAuditPending(ctx, q AuditPendingQuery) -> *PaginationResult[MailLog]``GET /api/v1/audits/pending`
- `GetAuditPendingDetail(ctx, id uint) -> *MailLogDetail``GET /api/v1/audits/pending/{id}`
- `ApproveAudit(ctx, id uint) error``POST /api/v1/audits/{id}/approve`
- `RejectAudit(ctx, id uint, req AuditRejectReq) error``POST /api/v1/audits/{id}/reject`
- `BatchApproveAudit(ctx, req BatchAuditApproveReq) error``POST /api/v1/audits/batch/approve`
- `BatchRejectAudit(ctx, req BatchAuditRejectReq) error``POST /api/v1/audits/batch/reject`
- `ListAuditLogs(ctx, q AuditLogQuery) -> *PaginationResult[MailAudit]``GET /api/v1/audits/logs`
- `GetAuditStats(ctx) -> *AuditStats``GET /api/v1/audits/stats`
`AuditPendingQuery` 过滤:`AccountID``UserID``Keyword`
`AuditLogQuery` 过滤:`AccountID``UserID``Action``AuditType``StartDate``EndDate`
---
### 八、审核规则(ServiceAuth
- `CreateAuditRule(ctx, req CreateAuditRuleReq) -> *AuditRule``POST /api/v1/audit-rules`
- `ListAuditRules(ctx) -> []AuditRule``GET /api/v1/audit-rules`
- `GetAuditRule(ctx, id uint) -> *AuditRule``GET /api/v1/audit-rules/{id}`
- `UpdateAuditRule(ctx, id uint, req UpdateAuditRuleReq) -> *AuditRule``PUT /api/v1/audit-rules/{id}`
- `DeleteAuditRule(ctx, id uint) error``DELETE /api/v1/audit-rules/{id}`
- `UpdateAuditRuleStatus(ctx, id uint, status int8) -> *AuditRule``PUT /api/v1/audit-rules/{id}/status`
- `TestAuditRule(ctx, req TestAuditRuleReq) -> *TestAuditRuleResp``POST /api/v1/audit-rules/test`
`CreateAuditRuleReq` 字段:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `Name` | `string` | 是 | |
| `RuleType` | `string` | 是 | 见 `AuditRule.RuleType` 枚举 |
| `Target` | `string` | 是 | 见 `AuditRule.Target` 枚举 |
| `Condition` | `string` | 是 | 关键词或正则表达式 |
| `Action` | `int8` | 是 | 见 `AuditRule.Action` 枚举 |
| `Priority` | `int` | 否 | 数字越大优先级越高 |
| `Remark` | `string` | 否 | |
---
### 九、队列(ServiceAuth
- `GetQueueStatus(ctx) -> *QueueStatusData``GET /api/v1/queue/status`
- `ListQueuePending(ctx, q QueuePendingQuery) -> *PaginationResult[MailLog]``GET /api/v1/queue/pending`
- `CancelQueueItem(ctx, mailLogID uint) error``POST /api/v1/queue/{mailLogID}/cancel`
- `RetryQueueItem(ctx, mailLogID uint) error``POST /api/v1/queue/{mailLogID}/retry`
`QueueStatusData` 返回各通道队列长度与延迟队列长度:
```go
type QueueStatusData struct {
Queues map[string]int64 `json:"queues"` // key = channel code
DelayQueue int64 `json:"delay_queue"`
}
```
---
### 十、健康检查(ServiceAuth
- `ListCheckLogs(ctx, q CheckLogQuery) -> *PaginationResult[CheckLog]``GET /api/v1/check-logs`
- `GetCheckSummary(ctx) -> []SenderHealth``GET /api/v1/check-logs/summary`
- `TriggerCheck(ctx, senderAccountID uint) -> *TriggerCheckResp``POST /api/v1/check-logs/trigger/{senderAccountID}`
`CheckLogQuery` 过滤:`SenderAccountID``Received``StartDate``EndDate`
---
## 统一返回结构
后端所有接口统一使用:
```json
{ "code": 200, "message": "ok", "data": { /* */ } }
```
SDK 内部会解包 `data` 并在非 200 时返回 `*APIError`
```go
resp, err := client.SendMail(ctx, req)
@@ -188,13 +443,17 @@ if err != nil {
}
```
## Options
分页接口统一包装:
```go
client := emailcli.NewServiceClient(
"https://your-server.com",
"token",
emailcli.WithTimeout(60 * time.Second),
emailcli.WithHTTPClient(customClient),
)
type PaginationResult[T any] struct {
List []T `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
```
## License
MIT