Phase Schedule 按浏览器时区查询日期 (2026-09-08)

适用项目:phase-back-admin;对接页面:phase-front 的 /live-schedule。

本次扩展每日总数与 UTC 日期查询的接口契约:产品需要按用户浏览器时区划分日期,因此新增 date_zone。此前文档中“日期固定按 UTC 解释”的说明,现仅适用于未传 date_zone 的兼容调用。数据库仍以 UTC 时间查询。

接口变更

schedule

  • GET /api/schedule/index:新增 date_zone,将 date 解释为用户时区中的自然日,再转换为 UTC 查询。旧时间戳查询不重复套用时区。
  • POST /api/schedule/daily-counts:新增 date_zone,整批 dates 使用该时区统计,响应结构和输入顺序保持不变。

接口示例

schedule

GET /api/schedule/index

公开日程列表;无需登录。新增时区参数用于日期模式。

请求参数

字段类型必填说明
datestring否YYYY-MM-DD 格式的真实日期,表示用户当地日期
date_zonestring否IANA 时区名称,例如 Asia/Shanghai、America/Los_Angeles;省略时为 UTC
pageinteger否默认 1,最小 1
sizeinteger否默认 15,范围 1~50
s_time / e_timeinteger否旧秒级时间戳参数;不能与 date 混传

原 talent_id、source_type、status 筛选继续支持。date_zone 在传入时必须为非空有效时区字符串;支持 PHP 时区库中的历史别名,如浏览器可能返回的 Asia/Calcutta。不接受 +08:00、Z、日期时间字符串或非法时区名称。

请求示例

GET /api/schedule/index?date=2026-09-08&date_zone=Asia%2FShanghai&size=50&page=1

对应当地 2026-09-08 全天,UTC 查询边界是:

[2026-09-07 16:00:00 UTC, 2026-09-08 16:00:00 UTC)

与反馈中的旧时间戳请求对齐:

GET /api/schedule/index?s_time=1788796800&e_time=1788883199&size=50&page=1

响应示例

HTTP 200,以下为空列表示例;有数据时 data 仍为原日程对象数组,total 为符合筛选的全部记录数,不受当前页大小影响。

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

错误响应

HTTP 422:date_zone 为空、null、数组、数字、偏移值或非法时区名称。错误字段为 errors.date_zone。日期、分页等原有校验继续生效。

错误结构示例,提示文本随服务端语言配置变化:

{
  "message": "The date zone field must be a valid timezone.",
  "errors": {
    "date_zone": ["The date zone field must be a valid timezone."]
  }
}

POST /api/schedule/daily-counts

公开批量统计;无需登录。整批日期使用一个 date_zone。

请求参数

字段类型必填说明
datesstring[]是1~7 个不重复的真实日期,严格 YYYY-MM-DD 格式
date_zonestring否时区校验同列表接口,省略时为 UTC

日期允许乱序、不连续;不要求传满一周。当前只使用这两个参数,不支持艺人、平台或状态过滤,默认统计两个平台符合日期窗口的全部日程。

请求示例

{
  "dates": ["2026-09-09", "2026-09-08", "2026-09-07"],
  "date_zone": "Asia/Shanghai"
}

响应示例

HTTP 200,以下数量为示例:

{
  "data": [
    { "date": "2026-09-09", "total": 0 },
    { "date": "2026-09-08", "total": 12 },
    { "date": "2026-09-07", "total": 3 }
  ]
}

date 保持请求中的当地日期,顺序与输入一致;total 为整数,无记录时为 0。同一数据和当前时间下,应与相同 date、date_zone 且没有额外平台、艺人、状态筛选的列表 total 一致。

错误响应

HTTP 422:非法 date_zone(错误结构同列表),或日期缺失、超过 7 个、重复、格式错误、日期不存在等。整个请求校验失败,不返回部分日期结果;日期错误字段仍为 errors.dates 或 errors["dates.N"]。

日期边界和统计规则

后端在用户时区中分别构造当天零点和次日零点,再转换成 UTC,按左闭右开窗口查询。次日边界按日历推算,不通过固定增加 86400 秒生成。

日期与时区UTC 查询区间(不包含结束边界)时长
2026-09-08 / Asia/Shanghai09-07 16:00 ~ 09-08 16:0024 小时
2026-03-08 / America/Los_Angeles03-08 08:00 ~ 03-09 07:0023 小时
2026-11-01 / America/Los_Angeles11-01 07:00 ~ 11-02 08:0025 小时

沿用现有状态规则:UPCOMING 按预定开始时间;LIVE / COMPLETED 按实际直播区间与窗口的交集。缺少实际结束时间的 COMPLETED 仅计入实际开始所在的当地日期;未结束 LIVE 不因结束时间为空而计入尚未到达的未来日期。仅统计有对应艺人的记录。

跨天直播可以计入多个当地日期。为兼容原有列表,实际结束时间恰好等于当天零点时仍计入当天。单次批量统计共用一个当前时间;统计与列表独立请求之间若数据更新或跨日,总数可能短暂变化。

前端接入

通过浏览器获取时区名称:

const dateZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
  1. 保留按用户浏览器当地日期生成的 YYYY-MM-DD,不要先转为 UTC 日期字符串。
  2. 列表请求传 date 和 date_zone,移除 s_time、e_time;批量统计传 dates 和相同 date_zone。
  3. 页面默认日期、日期按钮和直播时间显示应使用同一用户时区。按返回的 date 回填总数,翻页无需重新请求整周统计。
  4. 请求失败显示未知或重试状态,不能当作总数 0。

兼容性说明

  • 省略 date_zone 时仍按 UTC 日期解释,后端不能从日期字符串或 Cookie 自动推断浏览器时区;新前端应明确传入。
  • s_time / e_time 已表示具体时间点,合法 date_zone 对旧时间戳模式没有影响;非法时区仍会触发校验错误。
  • 未提供 date 且未同时提供两个时间戳时,仍不做时间过滤,单独提供 date_zone 不会启用过滤。
  • 响应结构、排序、分页和旧时间戳闭区间语义保持不变,无需数据库迁移。
  • 本次仅修改后端及文档,前端接入由前端负责人完成;上线状态以实际部署为准。