主题
OperationMail 运营邮件查询
OperationMail 是服务端查询运营后台或系统投递邮件的低层 API。它覆盖全服运营邮件和指定玩家邮件,不是玩家互发邮件系统;这个底层 API 本身不管理收件箱 UI、红点、已读、领取、删除状态。若希望直接使用官方界面与状态管理,参阅 GameSystemUI 运营邮件收件箱。
当前适合使用 OperationMail 的场景:
- 服务端需要读取运营后台配置的全服邮件。
- 服务端需要读取运营后台投递给某个玩家的个人邮件。
- 项目要自建收件箱 UI,并自行把已读、已领取、已展示等状态存入 CloudData。
不适合使用 OperationMail 的场景:
- 玩家之间互发邮件。
- 游戏脚本主动发邮件。
- 排行榜结算奖励邮件迁移。
- 客户端直接查询后台邮件。
服务端查询
OperationMail API 只在服务端可用。
csharp
#if SERVER
using GameCore.OperationMail;
public static async Task LoadOperationMailsAsync(long userId)
{
await OperationMailApi.WaitUntilReadyAsync();
var mapName = "my_map";
var env = Game.RuntimeEnvironment;
var now = DateTime.UtcNow;
var globalResult = await OperationMailApi.QueryGlobalOperationMailsAsync(mapName, env, now);
if (!globalResult.IsSuccess)
{
Game.Logger.LogWarning("全服运营邮件查询失败: {Result}", globalResult.Result);
return;
}
var userResult = await OperationMailApi.QueryUserOperationMailsAsync(userId, mapName, env, now);
if (!userResult.IsSuccess)
{
Game.Logger.LogWarning("个人运营邮件查询失败: {Result}", userResult.Result);
return;
}
foreach (var mail in globalResult.Records.Concat(userResult.Records))
{
Game.Logger.LogInformation("运营邮件: {Id} {Title}", mail.Id, mail.Title);
}
}
#endif查询语义
底层 Host 使用固定查询,不向游戏代码开放 SQL 字符串:
- 全服运营邮件来自
email.admin_email。 - 个人运营邮件来自
email.user_email。 - 查询条件包含
mapName、env、state = 0和now所在时间窗口。 - 全服邮件还限制
target_type = 0。 - 个人邮件还限制指定
userId。
推荐传 Game.RuntimeEnvironment(GameRuntimeEnvironment.Debug / Test / Formal),API 会映射为后台的 debug / test / formal。Unknown 或不认识的枚举值会抛出参数异常;原 string 重载仍可使用。这里不是部署环境:不要传 Game.DeploymentEnvironment 或 production。
now 由服务端取当前时间传入,推荐 DateTime.UtcNow。运营后台的邮件生效窗口按北京时间配置,查询层会把 DateTimeKind.Utc / Local 的时间转换为北京墙钟后交给后台比较;DateTimeKind.Unspecified 表示调用方已经提供北京墙钟,不会再转换。WASM 环境的 DateTime.Now 可能是 UTC+0,但带 Local Kind 时也会按实际偏移转换。旧项目若曾用 DateTime.Now.AddHours(8) 绕过时差,升级后必须改为 DateTime.UtcNow,否则会重复转换。不要在客户端构造时间后传给服务端决定邮件可见性。
返回字段
OperationMailRecord 包含后台读取邮件所需的原始信息:
| 字段 | 说明 |
|---|---|
Id | 查询表中的记录 id |
AdminEmailId | 来源 admin mail id;全服邮件通常等于 Id |
Uuid | 后台邮件 uuid |
Title / Content | 标题和正文 |
SenderName | 后台配置的发件人显示名 |
ContentType | 后台内容类型 |
Attachment | 后台附件原始 payload |
ReceiveType | 后台领取类型 |
StartTime / EndTime | 生效时间窗口对应的 UTC 时间(DateTimeKind.Utc);展示北京时间时需自行转换,不能再将其当作北京墙钟 |
IncludeNewPlayer | 全服邮件是否包含邮件开始后加入的新玩家;后台 include_new_player=1 表示排除新玩家,API 返回时已取反。低层 API 不替调用方过滤 |
Source | 后台来源值 |
Env | 后台环境 |
状态存储
OperationMail 只查询运营后台邮件,不保存玩家状态。项目自建 UI 时,推荐用 CloudData 保存状态,例如:
operation_mail_read_{mailId}operation_mail_claimed_{mailId}operation_mail_hidden_{mailId}
状态 key 的命名应包含邮件 id 或 uuid,避免全服邮件和个人邮件冲突。附件领取逻辑必须在服务端做幂等检查,不能只依赖客户端 UI 状态。
错误处理
常见结果:
| 结果 | 含义 | 处理 |
|---|---|---|
Success | 查询成功,结果可以为空列表 | 正常处理 |
ServiceNotInitialized | 服务或 provider 尚未 ready | 等待 OperationMailApi.WaitUntilReadyAsync() |
FailedToSend | Host 没有接受请求 | 可有限重试并记录日志 |
QueryMapNameMissing / QueryEnvironmentMissing / QueryUserIdMissing | 参数错误 | 修正调用参数 |
TooManyReadOperations / MySqlOperationLimitExceeded | 后台读限流 | 降低查询频率 |
ResultParseFailed | 后台响应无法解析 | 记录后台数据和版本信息 |
QueryConnectionTimeout | 查询超时 | 有限重试或降级为空收件箱 |
与 GameSystemUI 收件箱的关系
GameSystemUI 已基于 OperationMail 提供运营邮件收件箱、红点、CloudData 玩家状态和资源附件领取。需要开箱即用的入口与收件箱时使用 GameSystemUI 邮件系统;仅需原始邮件数据或自定义发放规则时,仍可直接调用此底层 API。不要把底层查询误认为已自动保存玩家的已读或领取状态。