Phase Schedule 每日总数与 UTC 日期查询 (2026-09-08)

适用项目:phase-back-admin;对接页面:phase-front 的 /live-schedule。本文记录已实现的后端接口契约,前端由对应负责人接入,部署状态以实际环境为准。

页面需要一次获取一周中每天的日程总数。新增批量统计接口,同时为原列表接口增加 UTC 日期查询,保证日期按钮总数与对应列表使用相同的日期范围和统计规则。

接口变更

schedule

  • POST /api/schedule/daily-counts:新增公开接口,接收 1~7 个日期,按 UTC 自然日统计,按输入顺序返回 { date, total }。
  • GET /api/schedule/index:公开日程列表新增 date=YYYY-MM-DD;后端生成 UTC 日期范围,旧 s_time / e_time 继续兼容,但不能与 date 混传。

接口示例

schedule

POST /api/schedule/daily-counts

一次查询多个日期的日程总数,不返回日程详情,不分页。无须登录。

请求参数

字段类型必填说明
datesstring[]是1~7 个不重复的真实日期,严格使用 YYYY-MM-DD 格式

其他校验规则:

  • 必须是列表数组,不接受对象、嵌套数组、空数组或时间戳数组。
  • 允许不连续、倒序的日期;不要求从周一或周日开始,也不强制必须传满 7 天。
  • 日期固定按 UTC 解释,无须传时区参数。
  • 当前接口仅使用 dates,不提供 source_type、status、talent_id 筛选。不要通过附加这些字段期待筛选统计结果。

请求示例

{
  "dates": ["2026-09-02", "2026-08-31", "2026-09-01"]
}

响应示例

HTTP 200,以下数量为示例:

{
  "data": [
    { "date": "2026-09-02", "total": 0 },
    { "date": "2026-08-31", "total": 2 },
    { "date": "2026-09-01", "total": 5 }
  ]
}
字段类型说明
dataobject[]每个请求日期对应一项,顺序与输入一致
data[].datestring对应的 YYYY-MM-DD 日期
data[].totalinteger当天日程总数;没有数据时返回 0,不会省略该日期

total 包含 YouTube(Y2B)和 Twitch(TWITCH),包括符合当天查询条件的 UPCOMING、LIVE、COMPLETED 记录,与分页大小无关。相同数据和当前时间下,该数值等于不附加平台、艺人、状态筛选的日期列表 total。

错误响应

HTTP 422:缺少 dates、数量不在 1~7 范围内、日期重复、格式错误或日期不存在,例如 2026-02-30。整个请求校验失败,不返回部分日期统计结果。

错误结构如下,提示文本随服务端语言配置变化,前端应按字段处理:

{
  "message": "The dates.0 field must match the format Y-m-d.",
  "errors": {
    "dates.0": ["The dates.0 field must match the format Y-m-d."]
  }
}

数组整体错误对应 errors.dates,单个日期错误对应 errors["dates.N"],其中 N 为从 0 开始的索引。

GET /api/schedule/index

新增 date 查询,后端统一生成 UTC 自然日范围;返回结构、分页及原有筛选保持兼容。

请求参数

字段类型必填说明
datestring否新增;严格 YYYY-MM-DD 格式的真实日期,固定按 UTC 查询
pageinteger否页码,最小为 1,默认第 1 页
sizeinteger否每页数量,1~50,默认 15
talent_idinteger否原有艺人筛选,最小为 1
statusstring否原有状态筛选:UPCOMING、LIVE、COMPLETED
source_typestring否原有平台筛选,业务值为 Y2B、TWITCH
s_timeinteger否原有开始时间,正整数秒级 Unix 时间戳,需与 e_time 配合
e_timeinteger否原有结束时间,正整数秒级 Unix 时间戳,需与 s_time 配合

其他校验规则:

  • 提供 date 时,该值不能为空,也不能同时提供 s_time 或 e_time。
  • 未提供 date 时,原时间戳查询规则保持:两个时间戳都传入才启用时间过滤,使用闭区间 [s_time, e_time]。
  • 新日期查询使用 [当天 00:00:00 UTC, 次日 00:00:00 UTC),不包含次日零点开始的记录。
  • size 的最大值校验已修正为 50,超过时返回 HTTP 422。

请求示例

GET /api/schedule/index?date=2026-09-01&page=1&size=50

对应的 UTC 查询窗口:

2026-09-01 00:00:00 <= 时间 < 2026-09-02 00:00:00

响应示例

HTTP 200。以下为空列表示例;有数据时 data 仍为原有 ScheduleResource 日程对象数组,本次未新增或移除对象字段。

{
  "data": [],
  "total": 0
}

错误响应

HTTP 422:date 为空、日期格式错误、日期不存在,或与任一时间戳参数混传。原有分页、艺人 ID、状态及时间戳校验继续生效。

混传示例:?date=2026-09-01&s_time=1788220800。对应错误结构示例(提示语言以实际服务端为准):

{
  "message": "The date field is prohibited.",
  "errors": {
    "date": ["The date field is prohibited."]
  }
}

统计口径

两个接口共用时间窗口过滤逻辑,只统计存在关联艺人的日程,每条日程在同一天最多计一次。

状态计入当天的规则
UPCOMINGscheduled_start_time 落在当天窗口内;即使有 actual_start_time,仍以预定时间为准
LIVE必须有实际开始时间且已经开播,开始时间早于窗口结束;实际结束时间不早于窗口开始,或结束时间为空且当前时间已到达该窗口
COMPLETED必须有实际开始时间,实际直播区间与当天有交集;若实际结束时间为空,仅计入实际开始时间所在日期

跨天直播可以计入多天,所以每天总数相加不是一周去重日程总数。为兼容原列表,实际结束时间恰好等于当天零点的直播仍计入当天,判断为 actual_end_time >= 当天零点。

尚未结束的 LIVE 记录不会仅因结束时间为空而计入尚未到达的未来日期。整个批量请求共用一个当前时间;统计接口与列表为独立请求,直播状态更新或时间跨日时可能短暂出现数量变化。

前端接入步骤

  1. 确定页面展示的 UTC 日期数组,通常为周日至周六的 7 天,格式为 YYYY-MM-DD。
  2. 页面初始化或切换日期组时,一次调用批量统计接口,按返回的 date 关联日期按钮的总数。
  3. 点击日期时,列表请求改为传同一个 date,并移除原 s_time、e_time。翻页只调整 page,不必重复查询整周数量。
  4. 总数读取 data[].total;不要使用列表当前页的 data.length 或前端计算的总页数替代。
  5. 加载失败应展示未知或重试状态,不能将失败当作 0;成功响应中的 0 才表示当天没有数据。

默认选中日期、周起始日期也应按 UTC 口径生成。如果具体直播时刻仍按用户本地时区显示,应明确标注,避免与 UTC 日期分组混淆。

兼容性说明

  • 旧 s_time / e_time 秒级时间戳查询继续可用;但 Unix 时间戳本身不携带时区,浏览器本地零点生成的时间戳并不等于 UTC 零点。
  • 例如北京时间 2026-09-01 00:00:00 对应 UTC 2026-08-31 16:00:00。新接口传 "2026-09-01" 查的是 UTC 9 月 1 日全天,两者范围不同。
  • 若继续使用旧时间戳查询,需传同一 UTC 日期的 00:00:00 至 23:59:59 对应时间戳,才能对齐统计结果。
  • 不新增数据库表、字段或迁移。后端每个日期执行一次计数查询,单次最多 7 次,不加载日程详情。
  • 本次仅完成后端实现;前端需按本文接入后才能显示每天总数。