节假日
节假日子域提供中国法定节假日 / 工作日的数据同步与本地查询能力。它有两个面向:一端对接外部数据源 api.apihubs.cn(拉取与代理),另一端把数据落成本地 Holiday / EnumGroup 表,供业务稳定、可离线地查询。
节假日同步是手动触发的(启动时自动同步的宿主服务
SampleHostedService目前已被注释掉)。首次使用必须先触发一次同步把数据灌入本地库,本地查询才有数据。
业务能力
- 远端代理查询:直接转发
api.apihubs.cn的holiday/get与enum/get,透传原始响应。适合需要实时、未被本地覆盖的查询。 - 本地同步:把指定年份区间的节假日数据落库,并同步枚举基础数据(如「工作日 / 周末 / 法定假日」等
EnumGroup)。 - 本地查询:从数据库按日期 / 年份 / 月份 / 是否工作日等条件分页查询,并支持下拉用枚举列表。这是业务代码最常走的路径。
- CRUD:节假日记录的新增 / 更新 / 删除(受
plat.Holiday权限保护)。
:::caution 当前授权边界
HolidayController 直接继承 ControllerBase。当前只有新增、更新、删除显式标注 [Authorize("plat.Holiday")];远端代理、同步和本地查询 Action 没有显式授权特性。若这些端点不应公开,需要在代码或网关层补齐策略,不能仅凭文档假设它们已受保护。
:::
date 参数格式为 yyyyMMdd(如 20250101 = 2025-01-01)。HolidayQuery 支持 year / month / date / workday / weekend / holiday_legal / holiday_recess / lunar / cn 等过滤条件,这些枚举分组都来自 api.apihubs.cn 的 enum/get。
概念与关系
两个服务职责清晰分离:
IHolidayService:本地数据的读写与查询。GetHolidayByDateAsync(int date)/GetHolidayList(HolidayQuery)/GetEnumList(string enum)等。IHolidayManager:与外部 API 对接的同步逻辑。SyncHolidayAsync(beginYear, endYear)按年份区间拉取并落库;SyncEnum()同步枚举基础数据。它实现IDomainService,是「外部数据源 → 本地表」的桥。
为什么需要本地同步与缓存
节假日数据变化极低频(一年调一两次),但查询极高频(判断“今天是否工作日”之类的调用可能每天发生无数次)。如果不落地,每次查询都打 api.apihubs.cn 会带来几个问题:
- 外部依赖与限流:远端接口带
api_key并有调用限制,高频透传既不经济也不稳定。 - 可离线、可组合:本地表能按
workday/holiday_legal等条件做任意组合过滤和分页,而代理接口做不到。 - 审计与修正:业务可能需要对个别日期人工
CRUD覆盖(如特殊调休),只有落库后这些修正才有存放之处。
因此设计成"低频同步 + 高频本地查":同步是手动/低频动作,查询走本地 FreeSql,外部 API 只在代理与同步两种场景出现。
配置(HolidayAppOption)
绑定自配置节 HolidayAppOption,决定同步覆盖的年份区间与远端凭据:
| 键 | 业务含义 |
|---|---|
BeginYear | 同步起始年份 |
EndYear | 同步结束年份 |
ApiKey | api.apihubs.cn 的密钥(代理 /hubs/* 与同步时按需透传) |
{
"HolidayAppOption": {
"BeginYear": 2024,
"EndYear": 2026,
"ApiKey": "your-apihubs-key"
}
}
开发示例
// 注入应用服务做本地查询
private readonly IHolidayService _holidayService;
// 查某天是否为工作日
var day = await _holidayService.GetHolidayByDateAsync(20250101);
bool isWorkday = day?.workday == 1;
// 按条件分页拉取
var list = await _holidayService.GetHolidayList(new HolidayQuery { year = "2025", size = 31 });
// 手动触发一次同步(年份区间来自 HolidayAppOption)
private readonly IHolidayManager _holidayManager;
await _holidayManager.SyncHolidayAsync(2024, 2026);
await _holidayManager.SyncEnum();