mirror of
https://gitee.com/houhuan/TrendRadar.git
synced 2026-05-01 01:12:42 +08:00
v3.0.0 AI 智能分析功能
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
"""
|
||||
工具类模块
|
||||
|
||||
提供参数验证、错误处理等辅助功能。
|
||||
"""
|
||||
@@ -0,0 +1,278 @@
|
||||
"""
|
||||
日期解析工具
|
||||
|
||||
支持多种自然语言日期格式解析,包括相对日期和绝对日期。
|
||||
"""
|
||||
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from .errors import InvalidParameterError
|
||||
|
||||
|
||||
class DateParser:
|
||||
"""日期解析器类"""
|
||||
|
||||
# 中文日期映射
|
||||
CN_DATE_MAPPING = {
|
||||
"今天": 0,
|
||||
"昨天": 1,
|
||||
"前天": 2,
|
||||
"大前天": 3,
|
||||
}
|
||||
|
||||
# 英文日期映射
|
||||
EN_DATE_MAPPING = {
|
||||
"today": 0,
|
||||
"yesterday": 1,
|
||||
}
|
||||
|
||||
# 星期映射
|
||||
WEEKDAY_CN = {
|
||||
"一": 0, "二": 1, "三": 2, "四": 3,
|
||||
"五": 4, "六": 5, "日": 6, "天": 6
|
||||
}
|
||||
|
||||
WEEKDAY_EN = {
|
||||
"monday": 0, "tuesday": 1, "wednesday": 2, "thursday": 3,
|
||||
"friday": 4, "saturday": 5, "sunday": 6
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def parse_date_query(date_query: str) -> datetime:
|
||||
"""
|
||||
解析日期查询字符串
|
||||
|
||||
支持的格式:
|
||||
- 相对日期(中文):今天、昨天、前天、大前天、N天前
|
||||
- 相对日期(英文):today、yesterday、N days ago
|
||||
- 星期(中文):上周一、上周二、本周三
|
||||
- 星期(英文):last monday、this friday
|
||||
- 绝对日期:2025-10-10、10月10日、2025年10月10日
|
||||
|
||||
Args:
|
||||
date_query: 日期查询字符串
|
||||
|
||||
Returns:
|
||||
datetime对象
|
||||
|
||||
Raises:
|
||||
InvalidParameterError: 日期格式无法识别
|
||||
|
||||
Examples:
|
||||
>>> DateParser.parse_date_query("今天")
|
||||
datetime(2025, 10, 11)
|
||||
>>> DateParser.parse_date_query("昨天")
|
||||
datetime(2025, 10, 10)
|
||||
>>> DateParser.parse_date_query("3天前")
|
||||
datetime(2025, 10, 8)
|
||||
>>> DateParser.parse_date_query("2025-10-10")
|
||||
datetime(2025, 10, 10)
|
||||
"""
|
||||
if not date_query or not isinstance(date_query, str):
|
||||
raise InvalidParameterError(
|
||||
"日期查询字符串不能为空",
|
||||
suggestion="请提供有效的日期查询,如:今天、昨天、2025-10-10"
|
||||
)
|
||||
|
||||
date_query = date_query.strip().lower()
|
||||
|
||||
# 1. 尝试解析中文常用相对日期
|
||||
if date_query in DateParser.CN_DATE_MAPPING:
|
||||
days_ago = DateParser.CN_DATE_MAPPING[date_query]
|
||||
return datetime.now() - timedelta(days=days_ago)
|
||||
|
||||
# 2. 尝试解析英文常用相对日期
|
||||
if date_query in DateParser.EN_DATE_MAPPING:
|
||||
days_ago = DateParser.EN_DATE_MAPPING[date_query]
|
||||
return datetime.now() - timedelta(days=days_ago)
|
||||
|
||||
# 3. 尝试解析 "N天前" 或 "N days ago"
|
||||
cn_days_ago_match = re.match(r'(\d+)\s*天前', date_query)
|
||||
if cn_days_ago_match:
|
||||
days = int(cn_days_ago_match.group(1))
|
||||
if days > 365:
|
||||
raise InvalidParameterError(
|
||||
f"天数过大: {days}天",
|
||||
suggestion="请使用小于365天的相对日期或使用绝对日期"
|
||||
)
|
||||
return datetime.now() - timedelta(days=days)
|
||||
|
||||
en_days_ago_match = re.match(r'(\d+)\s*days?\s+ago', date_query)
|
||||
if en_days_ago_match:
|
||||
days = int(en_days_ago_match.group(1))
|
||||
if days > 365:
|
||||
raise InvalidParameterError(
|
||||
f"天数过大: {days}天",
|
||||
suggestion="请使用小于365天的相对日期或使用绝对日期"
|
||||
)
|
||||
return datetime.now() - timedelta(days=days)
|
||||
|
||||
# 4. 尝试解析星期(中文):上周一、本周三
|
||||
cn_weekday_match = re.match(r'(上|本)周([一二三四五六日天])', date_query)
|
||||
if cn_weekday_match:
|
||||
week_type = cn_weekday_match.group(1) # 上 或 本
|
||||
weekday_str = cn_weekday_match.group(2)
|
||||
target_weekday = DateParser.WEEKDAY_CN[weekday_str]
|
||||
return DateParser._get_date_by_weekday(target_weekday, week_type == "上")
|
||||
|
||||
# 5. 尝试解析星期(英文):last monday、this friday
|
||||
en_weekday_match = re.match(r'(last|this)\s+(monday|tuesday|wednesday|thursday|friday|saturday|sunday)', date_query)
|
||||
if en_weekday_match:
|
||||
week_type = en_weekday_match.group(1) # last 或 this
|
||||
weekday_str = en_weekday_match.group(2)
|
||||
target_weekday = DateParser.WEEKDAY_EN[weekday_str]
|
||||
return DateParser._get_date_by_weekday(target_weekday, week_type == "last")
|
||||
|
||||
# 6. 尝试解析绝对日期:YYYY-MM-DD
|
||||
iso_date_match = re.match(r'(\d{4})-(\d{1,2})-(\d{1,2})', date_query)
|
||||
if iso_date_match:
|
||||
year = int(iso_date_match.group(1))
|
||||
month = int(iso_date_match.group(2))
|
||||
day = int(iso_date_match.group(3))
|
||||
try:
|
||||
return datetime(year, month, day)
|
||||
except ValueError as e:
|
||||
raise InvalidParameterError(
|
||||
f"无效的日期: {date_query}",
|
||||
suggestion=f"日期值错误: {str(e)}"
|
||||
)
|
||||
|
||||
# 7. 尝试解析中文日期:MM月DD日 或 YYYY年MM月DD日
|
||||
cn_date_match = re.match(r'(?:(\d{4})年)?(\d{1,2})月(\d{1,2})日', date_query)
|
||||
if cn_date_match:
|
||||
year_str = cn_date_match.group(1)
|
||||
month = int(cn_date_match.group(2))
|
||||
day = int(cn_date_match.group(3))
|
||||
|
||||
# 如果没有年份,使用当前年份
|
||||
if year_str:
|
||||
year = int(year_str)
|
||||
else:
|
||||
year = datetime.now().year
|
||||
# 如果月份大于当前月份,说明是去年
|
||||
current_month = datetime.now().month
|
||||
if month > current_month:
|
||||
year -= 1
|
||||
|
||||
try:
|
||||
return datetime(year, month, day)
|
||||
except ValueError as e:
|
||||
raise InvalidParameterError(
|
||||
f"无效的日期: {date_query}",
|
||||
suggestion=f"日期值错误: {str(e)}"
|
||||
)
|
||||
|
||||
# 8. 尝试解析斜杠格式:YYYY/MM/DD 或 MM/DD
|
||||
slash_date_match = re.match(r'(?:(\d{4})/)?(\d{1,2})/(\d{1,2})', date_query)
|
||||
if slash_date_match:
|
||||
year_str = slash_date_match.group(1)
|
||||
month = int(slash_date_match.group(2))
|
||||
day = int(slash_date_match.group(3))
|
||||
|
||||
if year_str:
|
||||
year = int(year_str)
|
||||
else:
|
||||
year = datetime.now().year
|
||||
current_month = datetime.now().month
|
||||
if month > current_month:
|
||||
year -= 1
|
||||
|
||||
try:
|
||||
return datetime(year, month, day)
|
||||
except ValueError as e:
|
||||
raise InvalidParameterError(
|
||||
f"无效的日期: {date_query}",
|
||||
suggestion=f"日期值错误: {str(e)}"
|
||||
)
|
||||
|
||||
# 如果所有格式都不匹配
|
||||
raise InvalidParameterError(
|
||||
f"无法识别的日期格式: {date_query}",
|
||||
suggestion=(
|
||||
"支持的格式:\n"
|
||||
"- 相对日期: 今天、昨天、前天、3天前、today、yesterday、3 days ago\n"
|
||||
"- 星期: 上周一、本周三、last monday、this friday\n"
|
||||
"- 绝对日期: 2025-10-10、10月10日、2025年10月10日"
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_date_by_weekday(target_weekday: int, is_last_week: bool) -> datetime:
|
||||
"""
|
||||
根据星期几获取日期
|
||||
|
||||
Args:
|
||||
target_weekday: 目标星期 (0=周一, 6=周日)
|
||||
is_last_week: 是否是上周
|
||||
|
||||
Returns:
|
||||
datetime对象
|
||||
"""
|
||||
today = datetime.now()
|
||||
current_weekday = today.weekday()
|
||||
|
||||
# 计算天数差
|
||||
if is_last_week:
|
||||
# 上周的某一天
|
||||
days_diff = current_weekday - target_weekday + 7
|
||||
else:
|
||||
# 本周的某一天
|
||||
days_diff = current_weekday - target_weekday
|
||||
if days_diff < 0:
|
||||
days_diff += 7
|
||||
|
||||
return today - timedelta(days=days_diff)
|
||||
|
||||
@staticmethod
|
||||
def format_date_folder(date: datetime) -> str:
|
||||
"""
|
||||
将日期格式化为文件夹名称
|
||||
|
||||
Args:
|
||||
date: datetime对象
|
||||
|
||||
Returns:
|
||||
文件夹名称,格式: YYYY年MM月DD日
|
||||
|
||||
Examples:
|
||||
>>> DateParser.format_date_folder(datetime(2025, 10, 11))
|
||||
'2025年10月11日'
|
||||
"""
|
||||
return date.strftime("%Y年%m月%d日")
|
||||
|
||||
@staticmethod
|
||||
def validate_date_not_future(date: datetime) -> None:
|
||||
"""
|
||||
验证日期不在未来
|
||||
|
||||
Args:
|
||||
date: 待验证的日期
|
||||
|
||||
Raises:
|
||||
InvalidParameterError: 日期在未来
|
||||
"""
|
||||
if date.date() > datetime.now().date():
|
||||
raise InvalidParameterError(
|
||||
f"不能查询未来的日期: {date.strftime('%Y-%m-%d')}",
|
||||
suggestion="请使用今天或过去的日期"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def validate_date_not_too_old(date: datetime, max_days: int = 365) -> None:
|
||||
"""
|
||||
验证日期不太久远
|
||||
|
||||
Args:
|
||||
date: 待验证的日期
|
||||
max_days: 最大天数
|
||||
|
||||
Raises:
|
||||
InvalidParameterError: 日期太久远
|
||||
"""
|
||||
days_ago = (datetime.now().date() - date.date()).days
|
||||
if days_ago > max_days:
|
||||
raise InvalidParameterError(
|
||||
f"日期太久远: {date.strftime('%Y-%m-%d')} ({days_ago}天前)",
|
||||
suggestion=f"请查询{max_days}天内的数据"
|
||||
)
|
||||
@@ -0,0 +1,93 @@
|
||||
"""
|
||||
自定义错误类
|
||||
|
||||
定义MCP Server使用的所有自定义异常类型。
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class MCPError(Exception):
|
||||
"""MCP工具错误基类"""
|
||||
|
||||
def __init__(self, message: str, code: str = "MCP_ERROR", suggestion: Optional[str] = None):
|
||||
super().__init__(message)
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.suggestion = suggestion
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""转换为字典格式"""
|
||||
error_dict = {
|
||||
"code": self.code,
|
||||
"message": self.message
|
||||
}
|
||||
if self.suggestion:
|
||||
error_dict["suggestion"] = self.suggestion
|
||||
return error_dict
|
||||
|
||||
|
||||
class DataNotFoundError(MCPError):
|
||||
"""数据不存在错误"""
|
||||
|
||||
def __init__(self, message: str, suggestion: Optional[str] = None):
|
||||
super().__init__(
|
||||
message=message,
|
||||
code="DATA_NOT_FOUND",
|
||||
suggestion=suggestion or "请检查日期范围或等待爬取任务完成"
|
||||
)
|
||||
|
||||
|
||||
class InvalidParameterError(MCPError):
|
||||
"""参数无效错误"""
|
||||
|
||||
def __init__(self, message: str, suggestion: Optional[str] = None):
|
||||
super().__init__(
|
||||
message=message,
|
||||
code="INVALID_PARAMETER",
|
||||
suggestion=suggestion or "请检查参数格式是否正确"
|
||||
)
|
||||
|
||||
|
||||
class ConfigurationError(MCPError):
|
||||
"""配置错误"""
|
||||
|
||||
def __init__(self, message: str, suggestion: Optional[str] = None):
|
||||
super().__init__(
|
||||
message=message,
|
||||
code="CONFIGURATION_ERROR",
|
||||
suggestion=suggestion or "请检查配置文件是否正确"
|
||||
)
|
||||
|
||||
|
||||
class PlatformNotSupportedError(MCPError):
|
||||
"""平台不支持错误"""
|
||||
|
||||
def __init__(self, platform: str):
|
||||
super().__init__(
|
||||
message=f"平台 '{platform}' 不受支持",
|
||||
code="PLATFORM_NOT_SUPPORTED",
|
||||
suggestion="支持的平台: zhihu, weibo, douyin, bilibili, baidu, toutiao, qq, 36kr, sspai, hellogithub, thepaper"
|
||||
)
|
||||
|
||||
|
||||
class CrawlTaskError(MCPError):
|
||||
"""爬取任务错误"""
|
||||
|
||||
def __init__(self, message: str, suggestion: Optional[str] = None):
|
||||
super().__init__(
|
||||
message=message,
|
||||
code="CRAWL_TASK_ERROR",
|
||||
suggestion=suggestion or "请稍后重试或查看日志"
|
||||
)
|
||||
|
||||
|
||||
class FileParseError(MCPError):
|
||||
"""文件解析错误"""
|
||||
|
||||
def __init__(self, file_path: str, reason: str):
|
||||
super().__init__(
|
||||
message=f"解析文件 {file_path} 失败: {reason}",
|
||||
code="FILE_PARSE_ERROR",
|
||||
suggestion="请检查文件格式是否正确"
|
||||
)
|
||||
@@ -0,0 +1,324 @@
|
||||
"""
|
||||
参数验证工具
|
||||
|
||||
提供统一的参数验证功能。
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
import os
|
||||
import yaml
|
||||
|
||||
from .errors import InvalidParameterError
|
||||
from .date_parser import DateParser
|
||||
|
||||
|
||||
def get_supported_platforms() -> List[str]:
|
||||
"""
|
||||
从 config.yaml 动态获取支持的平台列表
|
||||
|
||||
Returns:
|
||||
平台ID列表
|
||||
|
||||
Note:
|
||||
- 读取失败时返回空列表,允许所有平台通过(降级策略)
|
||||
- 平台列表来自 config/config.yaml 中的 platforms 配置
|
||||
"""
|
||||
try:
|
||||
# 获取 config.yaml 路径(相对于当前文件)
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
config_path = os.path.join(current_dir, "..", "..", "config", "config.yaml")
|
||||
config_path = os.path.normpath(config_path)
|
||||
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
config = yaml.safe_load(f)
|
||||
platforms = config.get('platforms', [])
|
||||
return [p['id'] for p in platforms if 'id' in p]
|
||||
except Exception as e:
|
||||
# 降级方案:返回空列表,允许所有平台
|
||||
print(f"警告:无法加载平台配置 ({config_path}): {e}")
|
||||
return []
|
||||
|
||||
|
||||
def validate_platforms(platforms: Optional[List[str]]) -> List[str]:
|
||||
"""
|
||||
验证平台列表
|
||||
|
||||
Args:
|
||||
platforms: 平台ID列表,None表示使用 config.yaml 中配置的所有平台
|
||||
|
||||
Returns:
|
||||
验证后的平台列表
|
||||
|
||||
Raises:
|
||||
InvalidParameterError: 平台不支持
|
||||
|
||||
Note:
|
||||
- platforms=None 时,返回 config.yaml 中配置的平台列表
|
||||
- 会验证平台ID是否在 config.yaml 的 platforms 配置中
|
||||
- 配置加载失败时,允许所有平台通过(降级策略)
|
||||
"""
|
||||
supported_platforms = get_supported_platforms()
|
||||
|
||||
if platforms is None:
|
||||
# 返回配置文件中的平台列表(用户的默认配置)
|
||||
return supported_platforms if supported_platforms else []
|
||||
|
||||
if not isinstance(platforms, list):
|
||||
raise InvalidParameterError("platforms 参数必须是列表类型")
|
||||
|
||||
if not platforms:
|
||||
# 空列表时,返回配置文件中的平台列表
|
||||
return supported_platforms if supported_platforms else []
|
||||
|
||||
# 如果配置加载失败(supported_platforms为空),允许所有平台通过
|
||||
if not supported_platforms:
|
||||
print("警告:平台配置未加载,跳过平台验证")
|
||||
return platforms
|
||||
|
||||
# 验证每个平台是否在配置中
|
||||
invalid_platforms = [p for p in platforms if p not in supported_platforms]
|
||||
if invalid_platforms:
|
||||
raise InvalidParameterError(
|
||||
f"不支持的平台: {', '.join(invalid_platforms)}",
|
||||
suggestion=f"支持的平台(来自config.yaml): {', '.join(supported_platforms)}"
|
||||
)
|
||||
|
||||
return platforms
|
||||
|
||||
|
||||
def validate_limit(limit: Optional[int], default: int = 20, max_limit: int = 1000) -> int:
|
||||
"""
|
||||
验证数量限制参数
|
||||
|
||||
Args:
|
||||
limit: 限制数量
|
||||
default: 默认值
|
||||
max_limit: 最大限制
|
||||
|
||||
Returns:
|
||||
验证后的限制值
|
||||
|
||||
Raises:
|
||||
InvalidParameterError: 参数无效
|
||||
"""
|
||||
if limit is None:
|
||||
return default
|
||||
|
||||
if not isinstance(limit, int):
|
||||
raise InvalidParameterError("limit 参数必须是整数类型")
|
||||
|
||||
if limit <= 0:
|
||||
raise InvalidParameterError("limit 必须大于0")
|
||||
|
||||
if limit > max_limit:
|
||||
raise InvalidParameterError(
|
||||
f"limit 不能超过 {max_limit}",
|
||||
suggestion=f"请使用分页或降低limit值"
|
||||
)
|
||||
|
||||
return limit
|
||||
|
||||
|
||||
def validate_date(date_str: str) -> datetime:
|
||||
"""
|
||||
验证日期格式
|
||||
|
||||
Args:
|
||||
date_str: 日期字符串 (YYYY-MM-DD)
|
||||
|
||||
Returns:
|
||||
datetime对象
|
||||
|
||||
Raises:
|
||||
InvalidParameterError: 日期格式错误
|
||||
"""
|
||||
try:
|
||||
return datetime.strptime(date_str, "%Y-%m-%d")
|
||||
except ValueError:
|
||||
raise InvalidParameterError(
|
||||
f"日期格式错误: {date_str}",
|
||||
suggestion="请使用 YYYY-MM-DD 格式,例如: 2025-10-11"
|
||||
)
|
||||
|
||||
|
||||
def validate_date_range(date_range: Optional[dict]) -> Optional[tuple]:
|
||||
"""
|
||||
验证日期范围
|
||||
|
||||
Args:
|
||||
date_range: 日期范围字典 {"start": "YYYY-MM-DD", "end": "YYYY-MM-DD"}
|
||||
|
||||
Returns:
|
||||
(start_date, end_date) 元组,或 None
|
||||
|
||||
Raises:
|
||||
InvalidParameterError: 日期范围无效
|
||||
"""
|
||||
if date_range is None:
|
||||
return None
|
||||
|
||||
if not isinstance(date_range, dict):
|
||||
raise InvalidParameterError("date_range 必须是字典类型")
|
||||
|
||||
start_str = date_range.get("start")
|
||||
end_str = date_range.get("end")
|
||||
|
||||
if not start_str or not end_str:
|
||||
raise InvalidParameterError(
|
||||
"date_range 必须包含 start 和 end 字段",
|
||||
suggestion='例如: {"start": "2025-10-01", "end": "2025-10-11"}'
|
||||
)
|
||||
|
||||
start_date = validate_date(start_str)
|
||||
end_date = validate_date(end_str)
|
||||
|
||||
if start_date > end_date:
|
||||
raise InvalidParameterError(
|
||||
"开始日期不能晚于结束日期",
|
||||
suggestion=f"start: {start_str}, end: {end_str}"
|
||||
)
|
||||
|
||||
return (start_date, end_date)
|
||||
|
||||
|
||||
def validate_keyword(keyword: str) -> str:
|
||||
"""
|
||||
验证关键词
|
||||
|
||||
Args:
|
||||
keyword: 搜索关键词
|
||||
|
||||
Returns:
|
||||
处理后的关键词
|
||||
|
||||
Raises:
|
||||
InvalidParameterError: 关键词无效
|
||||
"""
|
||||
if not keyword:
|
||||
raise InvalidParameterError("keyword 不能为空")
|
||||
|
||||
if not isinstance(keyword, str):
|
||||
raise InvalidParameterError("keyword 必须是字符串类型")
|
||||
|
||||
keyword = keyword.strip()
|
||||
|
||||
if not keyword:
|
||||
raise InvalidParameterError("keyword 不能为空白字符")
|
||||
|
||||
if len(keyword) > 100:
|
||||
raise InvalidParameterError(
|
||||
"keyword 长度不能超过100个字符",
|
||||
suggestion="请使用更简洁的关键词"
|
||||
)
|
||||
|
||||
return keyword
|
||||
|
||||
|
||||
def validate_top_n(top_n: Optional[int], default: int = 10) -> int:
|
||||
"""
|
||||
验证TOP N参数
|
||||
|
||||
Args:
|
||||
top_n: TOP N数量
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
验证后的值
|
||||
|
||||
Raises:
|
||||
InvalidParameterError: 参数无效
|
||||
"""
|
||||
return validate_limit(top_n, default=default, max_limit=100)
|
||||
|
||||
|
||||
def validate_mode(mode: Optional[str], valid_modes: List[str], default: str) -> str:
|
||||
"""
|
||||
验证模式参数
|
||||
|
||||
Args:
|
||||
mode: 模式字符串
|
||||
valid_modes: 有效模式列表
|
||||
default: 默认模式
|
||||
|
||||
Returns:
|
||||
验证后的模式
|
||||
|
||||
Raises:
|
||||
InvalidParameterError: 模式无效
|
||||
"""
|
||||
if mode is None:
|
||||
return default
|
||||
|
||||
if not isinstance(mode, str):
|
||||
raise InvalidParameterError("mode 必须是字符串类型")
|
||||
|
||||
if mode not in valid_modes:
|
||||
raise InvalidParameterError(
|
||||
f"无效的模式: {mode}",
|
||||
suggestion=f"支持的模式: {', '.join(valid_modes)}"
|
||||
)
|
||||
|
||||
return mode
|
||||
|
||||
|
||||
def validate_config_section(section: Optional[str]) -> str:
|
||||
"""
|
||||
验证配置节参数
|
||||
|
||||
Args:
|
||||
section: 配置节名称
|
||||
|
||||
Returns:
|
||||
验证后的配置节
|
||||
|
||||
Raises:
|
||||
InvalidParameterError: 配置节无效
|
||||
"""
|
||||
valid_sections = ["all", "crawler", "push", "keywords", "weights"]
|
||||
return validate_mode(section, valid_sections, "all")
|
||||
|
||||
|
||||
def validate_date_query(
|
||||
date_query: str,
|
||||
allow_future: bool = False,
|
||||
max_days_ago: int = 365
|
||||
) -> datetime:
|
||||
"""
|
||||
验证并解析日期查询字符串
|
||||
|
||||
Args:
|
||||
date_query: 日期查询字符串
|
||||
allow_future: 是否允许未来日期
|
||||
max_days_ago: 允许查询的最大天数
|
||||
|
||||
Returns:
|
||||
解析后的datetime对象
|
||||
|
||||
Raises:
|
||||
InvalidParameterError: 日期查询无效
|
||||
|
||||
Examples:
|
||||
>>> validate_date_query("昨天")
|
||||
datetime(2025, 10, 10)
|
||||
>>> validate_date_query("2025-10-10")
|
||||
datetime(2025, 10, 10)
|
||||
"""
|
||||
if not date_query:
|
||||
raise InvalidParameterError(
|
||||
"日期查询字符串不能为空",
|
||||
suggestion="请提供日期查询,如:今天、昨天、2025-10-10"
|
||||
)
|
||||
|
||||
# 使用DateParser解析日期
|
||||
parsed_date = DateParser.parse_date_query(date_query)
|
||||
|
||||
# 验证日期不在未来
|
||||
if not allow_future:
|
||||
DateParser.validate_date_not_future(parsed_date)
|
||||
|
||||
# 验证日期不太久远
|
||||
DateParser.validate_date_not_too_old(parsed_date, max_days=max_days_ago)
|
||||
|
||||
return parsed_date
|
||||
|
||||
Reference in New Issue
Block a user