feat: 集成 apisbo.com 中国节假日 API 自动获取+缓存

- 新增 holidays.py 服务:按年获取节假日数据,缓存到 system_config
- 区分 holiday(工作日假日)和 workday(调休上班日)两种类型
- is_working_day() 支持调休判断:周末在 workdays 集合中视为工作日
- 启动时自动从 API 刷新当年+明年数据,失败则使用缓存
- 系统设置新增「从 API 刷新」按钮,支局长可手动触发
- 支持 2010-2026 年数据,API 免费免鉴权

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-12 14:17:31 +08:00
parent a63bd6874c
commit 7dcb22f573
7 changed files with 199 additions and 33 deletions
+4 -13
View File
@@ -11,7 +11,7 @@ from app.models.mini_business import MiniBusiness
from app.models.key_visit import KeyVisit
from app.models.daily_note import DailyNote
from app.models.leave import Leave
from app.models.system_config import SystemConfig
from app.services.holidays import load_holiday_sets
from app.utils.timezone import today_cst, is_working_day
@@ -79,21 +79,12 @@ async def get_reporting_progress(db: AsyncSession, reference_date: date | None =
)
visit_map = {str(uid): cnt for uid, cnt in visits_result.all()}
# Load holiday config
holiday_row = await db.get(SystemConfig, "holidays")
holidays: set[date] = set()
if holiday_row and holiday_row.value:
for s in holiday_row.value.split(","):
s = s.strip()
if s:
try:
holidays.add(date.fromisoformat(s))
except ValueError:
pass
# Load holiday data (auto-refreshes from API if not cached)
holidays, workdays = await load_holiday_sets(db)
# Query leave records for today — build a set of managers on leave
today = today_cst()
is_rest = not is_working_day(today, holidays)
is_rest = not is_working_day(today, holidays, workdays)
leaves_result = await db.execute(
select(Leave).where(and_(