diff --git a/api-reference/on-call.openapi.en.json b/api-reference/on-call.openapi.en.json index d99d082c..3d00d7ed 100644 --- a/api-reference/on-call.openapi.en.json +++ b/api-reference/on-call.openapi.en.json @@ -2471,7 +2471,7 @@ "post": { "operationId": "calEventUpsert", "summary": "Upsert calendar event", - "description": "Create or update a calendar event (holiday or workday override). Omit event_id to create a new event.", + "description": "Create or update an event the calendar owns (a day off or a working-day override) over the days [start_at, end_at). Omit event_id to create a new event; a given event_id must be an existing event of this calendar. The write clears the calendar's other own events off the same days: an event fully inside the range is deleted, one partly inside is shortened, and one spanning the range is split in two, so the latest write decides those days. end_at must be later than start_at.", "tags": [ "On-call/Calendars" ], @@ -7843,7 +7843,7 @@ "post": { "operationId": "calEventDelete", "summary": "Delete calendar event", - "description": "Delete a calendar event by calendar ID and event ID.", + "description": "Delete one event of a personal calendar by event_id, or clear the calendar's own events off the days [start_at, end_at). Pass either event_id or both start_at and end_at. Cleared days fall back to the linked holiday calendars and the weekly workdays.", "tags": [ "On-call/Calendars" ], @@ -8132,7 +8132,7 @@ "post": { "operationId": "calEventList", "summary": "List calendar events", - "description": "Return events for a personal calendar within a year/month/day scope. When month and day are both omitted the whole year is returned.", + "description": "Return events for a personal calendar within a year/month/day scope. When month and day are both omitted the whole year is returned. The response also carries days, the resolved state of every day in the scope.", "tags": [ "On-call/Calendars" ], @@ -8194,7 +8194,27 @@ "updated_at": 0 } ], - "total": 11 + "total": 11, + "days": { + "2026-05-01": { + "is_off": true, + "event_id": "cale.KyG9XWTCU5CucbwukEVBQ4", + "layer": "custom" + }, + "2026-05-02": { + "is_off": true, + "event_id": "non_work.20260502", + "layer": "weekly" + }, + "2026-05-09": { + "is_off": false, + "event_id": "zh-cn.china.official.c234057c-fd90-380e-889c-3e3254dd5b01", + "layer": "holiday" + }, + "2026-05-11": { + "is_off": false + } + } } } } @@ -19847,10 +19867,9 @@ }, "CalEventIDRequest": { "type": "object", - "description": "Calendar event delete request.", + "description": "Calendar event delete request. Provide event_id, or both start_at and end_at.", "required": [ - "cal_id", - "event_id" + "cal_id" ], "properties": { "cal_id": { @@ -19859,7 +19878,15 @@ }, "event_id": { "type": "string", - "description": "Event ID." + "description": "ID of the event to delete. Omit when clearing a date range." + }, + "start_at": { + "type": "string", + "description": "Start date of the range to clear, in YYYY-MM-DD. Pass together with end_at instead of event_id." + }, + "end_at": { + "type": "string", + "description": "End date of the range to clear, in YYYY-MM-DD (exclusive). Must be later than start_at." } } }, @@ -19963,7 +19990,8 @@ "description": "Calendar event list response.", "required": [ "items", - "total" + "total", + "days" ], "properties": { "items": { @@ -19977,6 +20005,39 @@ "type": "integer", "description": "Total number of events returned.", "format": "int64" + }, + "days": { + "type": "object", + "description": "Resolved state of every day in the requested scope, keyed by date (YYYY-MM-DD). The highest layer holding any event decides a day: the calendar's own events (custom), then the linked holiday calendars (holiday), then the weekly workdays (weekly). Inside one layer a working-day event beats a day off, so a make-up workday from a holiday calendar wins over holiday rest days. A day no event covers is a working day.", + "additionalProperties": { + "$ref": "#/components/schemas/CalDay" + } + } + } + }, + "CalDay": { + "type": "object", + "description": "Resolved state of one calendar day.", + "required": [ + "is_off" + ], + "properties": { + "is_off": { + "type": "boolean", + "description": "Whether the day is a day off." + }, + "event_id": { + "type": "string", + "description": "ID of the event that decided the day. Absent when no event covers the day." + }, + "layer": { + "type": "string", + "enum": [ + "custom", + "holiday", + "weekly" + ], + "description": "Layer that decided the day: custom = the calendar's own events, holiday = a linked holiday calendar, weekly = the weekly workdays. Absent when no event covers the day." } } }, @@ -19985,7 +20046,6 @@ "description": "Calendar event upsert request. Provide event_id to update an existing event; omit it to create a new one.", "required": [ "cal_id", - "summary", "start_at", "end_at", "is_off" @@ -20002,8 +20062,7 @@ }, "summary": { "type": "string", - "description": "Event summary.", - "minLength": 1, + "description": "Event summary. Optional.", "maxLength": 39 }, "description": { diff --git a/api-reference/on-call.openapi.zh.json b/api-reference/on-call.openapi.zh.json index c9bc1158..cc5e23c5 100644 --- a/api-reference/on-call.openapi.zh.json +++ b/api-reference/on-call.openapi.zh.json @@ -2471,7 +2471,7 @@ "post": { "operationId": "calEventUpsert", "summary": "创建或更新日历事件", - "description": "创建或更新日历事件(节假日或工作日覆盖)。不传 event_id 时会创建新事件。", + "description": "创建或更新日历自己的事件(休息日或工作日覆盖),作用于 [start_at, end_at) 这些日期。不传 event_id 时创建新事件;传入的 event_id 必须是该日历已有的事件。写入时会把该日历自己的其他事件从这些日期上移除:完全落在范围内的事件被删除,部分重叠的被截短,跨越整个范围的被拆成两段,因此这些日期以最后一次写入为准。end_at 必须晚于 start_at。", "tags": [ "On-call/日历管理" ], @@ -7843,7 +7843,7 @@ "post": { "operationId": "calEventDelete", "summary": "删除日历事件", - "description": "根据日历 ID 与事件 ID 删除日历事件。", + "description": "按 event_id 删除个人日历的一个事件,或清除该日历自己在 [start_at, end_at) 这些日期上的事件。event_id 与 start_at/end_at 二选一,start_at 和 end_at 须同时传入。清除后这些日期回到关联节假日日历和每周工作日设置的判定。", "tags": [ "On-call/日历管理" ], @@ -8132,7 +8132,7 @@ "post": { "operationId": "calEventList", "summary": "查询日历事件列表", - "description": "返回个人日历在指定年/月/日范围内的事件列表。未同时传入 month 和 day 时返回整年数据。", + "description": "返回个人日历在指定年/月/日范围内的事件列表。未同时传入 month 和 day 时返回整年数据。响应中的 days 给出范围内每一天的判定结果。", "tags": [ "On-call/日历管理" ], @@ -8194,7 +8194,27 @@ "updated_at": 0 } ], - "total": 11 + "total": 11, + "days": { + "2026-05-01": { + "is_off": true, + "event_id": "cale.KyG9XWTCU5CucbwukEVBQ4", + "layer": "custom" + }, + "2026-05-02": { + "is_off": true, + "event_id": "non_work.20260502", + "layer": "weekly" + }, + "2026-05-09": { + "is_off": false, + "event_id": "zh-cn.china.official.c234057c-fd90-380e-889c-3e3254dd5b01", + "layer": "holiday" + }, + "2026-05-11": { + "is_off": false + } + } } } } @@ -19847,10 +19867,9 @@ }, "CalEventIDRequest": { "type": "object", - "description": "日历事件删除请求。", + "description": "日历事件删除请求。传入 event_id,或同时传入 start_at 和 end_at。", "required": [ - "cal_id", - "event_id" + "cal_id" ], "properties": { "cal_id": { @@ -19859,7 +19878,15 @@ }, "event_id": { "type": "string", - "description": "事件 ID。" + "description": "要删除的事件 ID。按日期范围清除时不传。" + }, + "start_at": { + "type": "string", + "description": "要清除的日期范围的开始日期,格式 YYYY-MM-DD。与 end_at 一起传入,代替 event_id。" + }, + "end_at": { + "type": "string", + "description": "要清除的日期范围的结束日期(不含),格式 YYYY-MM-DD。必须晚于 start_at。" } } }, @@ -19963,7 +19990,8 @@ "description": "日历事件列表响应。", "required": [ "items", - "total" + "total", + "days" ], "properties": { "items": { @@ -19977,6 +20005,39 @@ "type": "integer", "description": "返回的事件总数。", "format": "int64" + }, + "days": { + "type": "object", + "description": "请求范围内每一天的判定结果,键为日期(YYYY-MM-DD)。每天由有事件的最高一层决定:日历自己的事件(custom)优先,其次是关联的节假日日历(holiday),最后是每周工作日设置(weekly)。同一层内工作日事件优先于休息日事件,因此节假日日历中的调休补班日会覆盖节假日休息日。没有任何事件覆盖的日期为工作日。", + "additionalProperties": { + "$ref": "#/components/schemas/CalDay" + } + } + } + }, + "CalDay": { + "type": "object", + "description": "单个日期的判定结果。", + "required": [ + "is_off" + ], + "properties": { + "is_off": { + "type": "boolean", + "description": "该日期是否为休息日。" + }, + "event_id": { + "type": "string", + "description": "决定该日期的事件 ID。没有事件覆盖该日期时不返回。" + }, + "layer": { + "type": "string", + "enum": [ + "custom", + "holiday", + "weekly" + ], + "description": "决定该日期的层:custom = 日历自己的事件,holiday = 关联的节假日日历,weekly = 每周工作日设置。没有事件覆盖该日期时不返回。" } } }, @@ -19985,7 +20046,6 @@ "description": "日历事件创建/更新请求。传入 event_id 表示更新,留空表示创建。", "required": [ "cal_id", - "summary", "start_at", "end_at", "is_off" @@ -20002,8 +20062,7 @@ }, "summary": { "type": "string", - "description": "事件摘要。", - "minLength": 1, + "description": "事件摘要,选填。", "maxLength": 39 }, "description": { diff --git a/api-reference/openapi.en.json b/api-reference/openapi.en.json index 25a09cd4..83b94f76 100644 --- a/api-reference/openapi.en.json +++ b/api-reference/openapi.en.json @@ -3991,20 +3991,27 @@ "type": "object" }, "CalEventIDRequest": { - "description": "Calendar event delete request.", + "description": "Calendar event delete request. Provide event_id, or both start_at and end_at.", "properties": { "cal_id": { "description": "Calendar ID; obtain it from `POST /calendar/list`.", "type": "string" }, "event_id": { - "description": "Event ID.", + "description": "ID of the event to delete. Omit when clearing a date range.", + "type": "string" + }, + "start_at": { + "description": "Start date of the range to clear, in YYYY-MM-DD. Pass together with end_at instead of event_id.", + "type": "string" + }, + "end_at": { + "description": "End date of the range to clear, in YYYY-MM-DD (exclusive). Must be later than start_at.", "type": "string" } }, "required": [ - "cal_id", - "event_id" + "cal_id" ], "type": "object" }, @@ -4117,11 +4124,45 @@ "description": "Total number of events returned.", "format": "int64", "type": "integer" + }, + "days": { + "additionalProperties": { + "$ref": "#/components/schemas/CalDay" + }, + "description": "Resolved state of every day in the requested scope, keyed by date (YYYY-MM-DD). The highest layer holding any event decides a day: the calendar's own events (custom), then the linked holiday calendars (holiday), then the weekly workdays (weekly). Inside one layer a working-day event beats a day off, so a make-up workday from a holiday calendar wins over holiday rest days. A day no event covers is a working day.", + "type": "object" } }, "required": [ "items", - "total" + "total", + "days" + ], + "type": "object" + }, + "CalDay": { + "description": "Resolved state of one calendar day.", + "properties": { + "event_id": { + "description": "ID of the event that decided the day. Absent when no event covers the day.", + "type": "string" + }, + "is_off": { + "description": "Whether the day is a day off.", + "type": "boolean" + }, + "layer": { + "description": "Layer that decided the day: custom = the calendar's own events, holiday = a linked holiday calendar, weekly = the weekly workdays. Absent when no event covers the day.", + "enum": [ + "custom", + "holiday", + "weekly" + ], + "type": "string" + } + }, + "required": [ + "is_off" ], "type": "object" }, @@ -4155,15 +4196,13 @@ "type": "string" }, "summary": { - "description": "Event summary.", + "description": "Event summary. Optional.", "maxLength": 39, - "minLength": 1, "type": "string" } }, "required": [ "cal_id", - "summary", "start_at", "end_at", "is_off" @@ -34621,7 +34660,7 @@ }, "/calendar/event/delete": { "post": { - "description": "Delete a calendar event by calendar ID and event ID.", + "description": "Delete one event of a personal calendar by event_id, or clear the calendar's own events off the days [start_at, end_at). Pass either event_id or both start_at and end_at. Cleared days fall back to the linked holiday calendars and the weekly workdays.", "operationId": "calEventDelete", "requestBody": { "content": { @@ -34692,7 +34731,7 @@ }, "/calendar/event/list": { "post": { - "description": "Return events for a personal calendar within a year/month/day scope. When month and day are both omitted the whole year is returned.", + "description": "Return events for a personal calendar within a year/month/day scope. When month and day are both omitted the whole year is returned. The response also carries days, the resolved state of every day in the scope.", "operationId": "calEventList", "requestBody": { "content": { @@ -34743,7 +34782,27 @@ "updated_at": 0 } ], - "total": 11 + "total": 11, + "days": { + "2026-05-01": { + "is_off": true, + "event_id": "cale.KyG9XWTCU5CucbwukEVBQ4", + "layer": "custom" + }, + "2026-05-02": { + "is_off": true, + "event_id": "non_work.20260502", + "layer": "weekly" + }, + "2026-05-09": { + "is_off": false, + "event_id": "zh-cn.china.official.c234057c-fd90-380e-889c-3e3254dd5b01", + "layer": "holiday" + }, + "2026-05-11": { + "is_off": false + } + } }, "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4" }, @@ -34794,7 +34853,7 @@ }, "/calendar/event/upsert": { "post": { - "description": "Create or update a calendar event (holiday or workday override). Omit event_id to create a new event.", + "description": "Create or update an event the calendar owns (a day off or a working-day override) over the days [start_at, end_at). Omit event_id to create a new event; a given event_id must be an existing event of this calendar. The write clears the calendar's other own events off the same days: an event fully inside the range is deleted, one partly inside is shortened, and one spanning the range is split in two, so the latest write decides those days. end_at must be later than start_at.", "operationId": "calEventUpsert", "requestBody": { "content": { diff --git a/api-reference/openapi.zh.json b/api-reference/openapi.zh.json index 281ea240..06f44622 100644 --- a/api-reference/openapi.zh.json +++ b/api-reference/openapi.zh.json @@ -3991,20 +3991,27 @@ "type": "object" }, "CalEventIDRequest": { - "description": "日历事件删除请求。", + "description": "日历事件删除请求。传入 event_id,或同时传入 start_at 和 end_at。", "properties": { "cal_id": { "description": "日历 ID,可通过 `POST /calendar/list` 获取。", "type": "string" }, "event_id": { - "description": "事件 ID。", + "description": "要删除的事件 ID。按日期范围清除时不传。", + "type": "string" + }, + "start_at": { + "description": "要清除的日期范围的开始日期,格式 YYYY-MM-DD。与 end_at 一起传入,代替 event_id。", + "type": "string" + }, + "end_at": { + "description": "要清除的日期范围的结束日期(不含),格式 YYYY-MM-DD。必须晚于 start_at。", "type": "string" } }, "required": [ - "cal_id", - "event_id" + "cal_id" ], "type": "object" }, @@ -4117,11 +4124,45 @@ "description": "返回的事件总数。", "format": "int64", "type": "integer" + }, + "days": { + "additionalProperties": { + "$ref": "#/components/schemas/CalDay" + }, + "description": "请求范围内每一天的判定结果,键为日期(YYYY-MM-DD)。每天由有事件的最高一层决定:日历自己的事件(custom)优先,其次是关联的节假日日历(holiday),最后是每周工作日设置(weekly)。同一层内工作日事件优先于休息日事件,因此节假日日历中的调休补班日会覆盖节假日休息日。没有任何事件覆盖的日期为工作日。", + "type": "object" } }, "required": [ "items", - "total" + "total", + "days" + ], + "type": "object" + }, + "CalDay": { + "description": "单个日期的判定结果。", + "properties": { + "event_id": { + "description": "决定该日期的事件 ID。没有事件覆盖该日期时不返回。", + "type": "string" + }, + "is_off": { + "description": "该日期是否为休息日。", + "type": "boolean" + }, + "layer": { + "description": "决定该日期的层:custom = 日历自己的事件,holiday = 关联的节假日日历,weekly = 每周工作日设置。没有事件覆盖该日期时不返回。", + "enum": [ + "custom", + "holiday", + "weekly" + ], + "type": "string" + } + }, + "required": [ + "is_off" ], "type": "object" }, @@ -4155,15 +4196,13 @@ "type": "string" }, "summary": { - "description": "事件摘要。", + "description": "事件摘要,选填。", "maxLength": 39, - "minLength": 1, "type": "string" } }, "required": [ "cal_id", - "summary", "start_at", "end_at", "is_off" @@ -34621,7 +34660,7 @@ }, "/calendar/event/delete": { "post": { - "description": "根据日历 ID 与事件 ID 删除日历事件。", + "description": "按 event_id 删除个人日历的一个事件,或清除该日历自己在 [start_at, end_at) 这些日期上的事件。event_id 与 start_at/end_at 二选一,start_at 和 end_at 须同时传入。清除后这些日期回到关联节假日日历和每周工作日设置的判定。", "operationId": "calEventDelete", "requestBody": { "content": { @@ -34692,7 +34731,7 @@ }, "/calendar/event/list": { "post": { - "description": "返回个人日历在指定年/月/日范围内的事件列表。未同时传入 month 和 day 时返回整年数据。", + "description": "返回个人日历在指定年/月/日范围内的事件列表。未同时传入 month 和 day 时返回整年数据。响应中的 days 给出范围内每一天的判定结果。", "operationId": "calEventList", "requestBody": { "content": { @@ -34743,7 +34782,27 @@ "updated_at": 0 } ], - "total": 11 + "total": 11, + "days": { + "2026-05-01": { + "is_off": true, + "event_id": "cale.KyG9XWTCU5CucbwukEVBQ4", + "layer": "custom" + }, + "2026-05-02": { + "is_off": true, + "event_id": "non_work.20260502", + "layer": "weekly" + }, + "2026-05-09": { + "is_off": false, + "event_id": "zh-cn.china.official.c234057c-fd90-380e-889c-3e3254dd5b01", + "layer": "holiday" + }, + "2026-05-11": { + "is_off": false + } + } }, "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4" }, @@ -34794,7 +34853,7 @@ }, "/calendar/event/upsert": { "post": { - "description": "创建或更新日历事件(节假日或工作日覆盖)。不传 event_id 时会创建新事件。", + "description": "创建或更新日历自己的事件(休息日或工作日覆盖),作用于 [start_at, end_at) 这些日期。不传 event_id 时创建新事件;传入的 event_id 必须是该日历已有的事件。写入时会把该日历自己的其他事件从这些日期上移除:完全落在范围内的事件被删除,部分重叠的被截短,跨越整个范围的被拆成两段,因此这些日期以最后一次写入为准。end_at 必须晚于 start_at。", "operationId": "calEventUpsert", "requestBody": { "content": { diff --git a/en/on-call/configuration/service-calendar.mdx b/en/on-call/configuration/service-calendar.mdx index 8396752b..a445f50f 100644 --- a/en/on-call/configuration/service-calendar.mdx +++ b/en/on-call/configuration/service-calendar.mdx @@ -45,9 +45,9 @@ New calendars default to all workdays. After creation, open the calendar detail | Action | Description | | --- | --- | | **Modify Basic Info** | Calendar name, description, management team | -| **Link Holidays** | On the calendar detail page, link national holiday calendars. The system automatically marks statutory holidays as rest days; you can adjust or unlink them at any time | +| **Link Holidays** | On the calendar detail page, link national holiday calendars. The system automatically marks statutory holidays as rest days and make-up workdays as workdays; you can adjust or unlink them at any time | | **Bulk Mark Rest Days** | Quickly mark by day of week, for example, every Saturday and Sunday as rest days | -| **Single Day Adjustment** | Click on a date to toggle workday/rest day status | +| **Adjust Dates** | Click a date, or click two dates to select the days between them, then click **Set as workday**, **Set as day off**, or **Reset to default** in the action bar below. The note is optional | ![Edit Calendar](https://fcpub-1301667576.cos.ap-nanjing.myqcloud.com/flashduty/doc/rili-1.png) @@ -66,6 +66,17 @@ Deleting a calendar is irreversible. Confirm no escalation rules or silence rule Service calendars are above schedules in hierarchy—first determining if it's a workday, then deciding whether to assign to on-call personnel. + + The first item in this order that has a setting for the day decides it: + + 1. **Manual settings**: days you set as a workday or day off on the calendar + 2. **Linked holiday calendars**: a make-up workday wins over holiday rest days + 3. **Weekly rest days**: rest days marked by day of week + + A day with none of these settings is a workday. Clicking **Reset to default** on a day clears its manual setting, and the day goes back to the holiday calendars and weekly rest days. + + On the calendar, yellow marks a day off and green marks a workday set manually or by a holiday make-up day. Uncolored days follow the weekly settings and are workdays. + ## Related topics diff --git a/zh/on-call/configuration/service-calendar.mdx b/zh/on-call/configuration/service-calendar.mdx index 12829246..1b6ab4cb 100644 --- a/zh/on-call/configuration/service-calendar.mdx +++ b/zh/on-call/configuration/service-calendar.mdx @@ -46,9 +46,9 @@ keywords: ["服务日历", "工作日", "休息日", "节假日", "差异化处 | 操作 | 说明 | | --- | --- | | **修改基础信息** | 日历名称、描述、管理团队 | -| **关联节假日** | 在日历详情页关联国家节假日日历,系统自动将法定假日标记为休息日,可随时调整或取消关联 | +| **关联节假日** | 在日历详情页关联国家节假日日历,系统自动将法定假日标记为休息日、调休补班日标记为工作日,可随时调整或取消关联 | | **批量标记休息日** | 按星期几快速标记,如每周六、日为休息日 | -| **单日调整** | 点击日期切换工作日/休息日状态 | +| **调整日期** | 点击一个日期,或先后点击两个日期选中一段日期,在下方操作栏点击 **设为工作日**、**设为休息日** 或 **恢复默认**,备注选填 | ![编辑日历](https://fcpub-1301667576.cos.ap-nanjing.myqcloud.com/flashduty/doc/rili-1.png) @@ -67,6 +67,17 @@ keywords: ["服务日历", "工作日", "休息日", "节假日", "差异化处 服务日历位于值班表的上层,先判断是否在工作日,再决定是否分派给值班人员。 + + 按以下顺序,由排在最前、且对这一天有设置的一项决定: + + 1. **手动设置**:在日历上设为工作日或休息日的日期 + 2. **关联的节假日日历**:调休补班日优先于节假日休息日 + 3. **每周休息日**:按星期几批量标记的休息日 + + 以上都没有设置的日期为工作日。对日期点击 **恢复默认** 会清除手动设置,该日期回到节假日日历和每周休息日的判定。 + + 日历上黄色表示休息日,绿色表示手动设置或节假日补班的工作日,没有颜色的日期按每周设置为工作日。 + ## 延伸阅读