"""
命理系统抽象基类
定义统一的接口,支持不同命理系统(紫微、八字、占星等)
"""
from abc import ABC, abstractmethod
from datetime import datetime
from typing import Any, Dict, Optional
from .exceptions import ValidationError
class BaseFortuneSystem(ABC):
"""命理系统抽象基类"""
@abstractmethod
def get_system_name(self) -> str:
"""
返回系统名称
Returns:
系统名称,如 "紫微斗数"、"八字"、"西方占星"
"""
pass
@abstractmethod
def get_system_version(self) -> str:
"""
返回系统版本
Returns:
版本号字符串
"""
pass
@abstractmethod
def get_chart(self, birth_info: Dict[str, Any], language: str = "zh-CN") -> Dict[str, Any]:
"""
获取排盘信息
Args:
birth_info: 生辰信息字典,包含以下字段:
- date: 日期 (str格式 YYYY-MM-DD 或 datetime对象)
- time_index: 时辰序号 (int, 0-12)
- gender: 性别 (str, "男"/"女")
- calendar: 历法 (str, "solar"/"lunar", 默认"solar")
- is_leap_month: 是否闰月 (bool, 默认False, 仅农历有效)
language: 输出语言 (str, 默认"zh-CN")
支持: zh-CN, zh-TW, en-US, ja-JP, ko-KR, vi-VN
Returns:
排盘详细信息字典,包含:
- system: 系统名称
- basic_info: 基本信息(日期、时辰、星座等)
- palaces/houses: 宫位信息
- stars: 星曜信息
- elements: 五行信息
- ... (各系统特有数据)
Raises:
ValueError: 参数格式错误
RuntimeError: 排盘计算错误
"""
pass
@abstractmethod
def get_fortune(
self,
birth_info: Dict[str, Any],
query_date: Optional[datetime] = None,
language: str = "zh-CN",
) -> Dict[str, Any]:
"""
获取运势信息
Args:
birth_info: 生辰信息字典(同get_chart)
query_date: 查询日期,默认为当前时间
language: 输出语言 (str, 默认"zh-CN")
支持: zh-CN, zh-TW, en-US, ja-JP, ko-KR, vi-VN
Returns:
运势信息字典,可能包含:
- decadal: 大限(紫微)
- yearly: 流年
- monthly: 流月
- daily: 流日
- ... (各系统特有运势)
Raises:
ValueError: 参数错误
RuntimeError: 运势计算错误
"""
pass
@abstractmethod
def analyze_palace(
self, birth_info: Dict[str, Any], palace_name: str, language: str = "zh-CN"
) -> Dict[str, Any]:
"""
分析特定宫位的详细信息
Args:
birth_info: 生辰信息字典
palace_name: 宫位名称(各系统定义不同)
language: 输出语言 (str, 默认"zh-CN")
支持: zh-CN, zh-TW, en-US, ja-JP, ko-KR, vi-VN
Returns:
宫位详细分析字典,包含:
- palace_name: 宫位名称
- basic_info: 基本信息
- stars: 该宫位的星曜
- interpretation: 解读信息(可选)
Raises:
ValueError: 宫位名称无效
"""
pass
def validate_birth_info(self, birth_info: Dict[str, Any]) -> None:
"""
验证生辰信息的有效性
Args:
birth_info: 生辰信息字典
Raises:
ValidationError: 参数无效或缺失
DateRangeError: 日期超出支持范围
"""
# 延迟导入避免循环依赖
from utils.validators import validate_date_range
required_fields = ["date", "time_index", "gender"]
for field in required_fields:
if field not in birth_info:
raise ValidationError(f"缺少必需字段: {field}")
# 验证日期格式和范围
validate_date_range(birth_info["date"])
# 验证时辰
if not 0 <= birth_info["time_index"] <= 12:
raise ValidationError("时辰序号必须在0-12之间")
# 验证性别
if birth_info["gender"] not in ["男", "女"]:
raise ValidationError("性别必须是'男'或'女'")
def validate_language(self, language: str) -> None:
"""
验证语言是否支持
Args:
language: 语言代码
Raises:
LanguageNotSupportedError: 语言不支持
"""
# 延迟导入避免循环依赖
from utils.validators import validate_language as _validate_language
_validate_language(language)
def get_supported_palaces(self) -> list:
"""
返回该系统支持的宫位列表
Returns:
宫位名称列表
"""
return []
def get_capabilities(self) -> Dict[str, bool]:
"""
返回系统支持的功能
Returns:
功能字典,例如:
{
'chart': True,
'fortune': True,
'palace_analysis': True,
'compatibility': False,
...
}
"""
return {
"chart": True,
"fortune": False,
"palace_analysis": False,
"compatibility": False,
}
def apply_solar_time_correction(self, birth_info: Dict[str, Any]) -> int:
"""
应用真太阳时修正,返回修正后的时辰序号
如果birth_info中指定了use_solar_time=True且提供了经度,
则计算真太阳时修正后的时辰序号;否则返回原始time_index。
Args:
birth_info: 生辰信息字典
Returns:
修正后的时辰序号 (0-12)
Examples:
>>> birth_info = {
... "date": "2000-08-16",
... "time_index": 6,
... "gender": "女",
... "use_solar_time": True,
... "longitude": 87.6, # 乌鲁木齐
... "birth_hour": 12,
... "birth_minute": 0
... }
>>> system.apply_solar_time_correction(birth_info)
5 # 真太阳时修正后变为巳时
"""
# 检查是否启用真太阳时
if not birth_info.get("use_solar_time", False):
return birth_info["time_index"]
# 检查是否提供了经度
longitude = birth_info.get("longitude")
if longitude is None:
return birth_info["time_index"]
# 导入真太阳时计算函数
from utils.solar_time import adjust_time_index_for_solar_time
# 获取出生时刻
birth_hour = birth_info.get("birth_hour")
birth_minute = birth_info.get("birth_minute")
# 如果没有提供具体时刻,使用时辰的中点时间
if birth_hour is None or birth_minute is None:
birth_hour, birth_minute = self._get_time_index_midpoint(birth_info["time_index"])
# 计算真太阳时修正后的时辰
adjusted_index, _, _ = adjust_time_index_for_solar_time(birth_hour, birth_minute, longitude)
return adjusted_index
def _get_time_index_midpoint(self, time_index: int) -> tuple[int, int]:
"""
获取时辰的中点时间(小时,分钟)
Args:
time_index: 时辰序号 (0-12)
Returns:
(小时, 分钟) 元组
"""
midpoints = [
(0, 0), # 0 早子时
(2, 0), # 1 丑时
(4, 0), # 2 寅时
(6, 0), # 3 卯时
(8, 0), # 4 辰时
(10, 0), # 5 巳时
(12, 0), # 6 午时
(14, 0), # 7 未时
(16, 0), # 8 申时
(18, 0), # 9 酉时
(20, 0), # 10 戌时
(22, 0), # 11 亥时
(0, 0), # 12 晚子时
]
return midpoints[time_index]