# -*- coding: utf-8 -*- import json from datetime import datetime from typing import Optional, List from functools import lru_cache from openai import OpenAI from cnlunar import Lunar from app.config.setting import settings class CalendarService: """万年历服务 - 使用 cnlunar 获取农历信息,DeepSeek 生成宜忌""" # 内存缓存,存储日期对应的宜忌数据 _yi_ji_cache: dict = {} @classmethod def get_calendar_info(cls, year: Optional[int] = None, month: Optional[int] = None, day: Optional[int] = None) -> dict: """ 获取万年历信息 参数: - year: 年份,默认今年 - month: 月份,默认本月 - day: 日期,默认今天 返回: - dict: 万年历信息 """ # 获取日期 if year and month and day: solar_date = datetime(year, month, day) else: solar_date = datetime.now() # 使用 cnlunar 获取农历信息 lunar = Lunar(solar_date) # 获取农历月份和日期 lunar_month = lunar.lunarMonthCn # 如:腊月 lunar_day = lunar.lunarDayCn # 如:十二 # 获取天干地支年份 gan_zhi_year = lunar.year8Char # 如:甲辰 # 使用 DeepSeek 生成宜忌(带缓存) yi_list, ji_list = cls._get_yi_ji_cached(solar_date, lunar) return { "lunar": f"{lunar_month} {lunar_day}", "year": f"{gan_zhi_year}年", "solar": solar_date.strftime("%Y.%m.%d"), "yi": yi_list, "ji": ji_list, } @classmethod def _get_yi_ji_cached(cls, solar_date: datetime, lunar: Lunar) -> tuple[List[str], List[str]]: """ 获取宜忌数据(带缓存) 同一天的数据只调用一次 API,后续直接返回缓存 """ # 用日期字符串作为缓存 key cache_key = solar_date.strftime("%Y-%m-%d") # 如果缓存中有数据,直接返回 if cache_key in cls._yi_ji_cache: return cls._yi_ji_cache[cache_key] # 缓存中没有,调用 API 获取 yi_list, ji_list = cls._get_yi_ji_from_deepseek(solar_date, lunar) # 存入缓存 cls._yi_ji_cache[cache_key] = (yi_list, ji_list) # 限制缓存大小,最多保留 30 天的数据 if len(cls._yi_ji_cache) > 30: # 删除最早的一条 oldest_key = next(iter(cls._yi_ji_cache)) del cls._yi_ji_cache[oldest_key] return yi_list, ji_list @classmethod def _get_yi_ji_from_deepseek(cls, solar_date: datetime, lunar: Lunar) -> tuple[List[str], List[str]]: """ 调用 DeepSeek API 生成宜忌数据 参数: - solar_date: 公历日期 - lunar: 农历对象 返回: - tuple: (宜列表, 忌列表) """ try: # 从配置文件读取 API 配置 client = OpenAI( api_key=settings.DEEPSEEK_API_KEY, base_url=settings.DEEPSEEK_BASE_URL ) # 构建提示词 prompt = f"""请根据以下日期信息,生成中国传统黄历的宜忌数据。 日期信息: - 公历:{solar_date.strftime("%Y年%m月%d日")} - 农历:{lunar.lunarMonthCn}{lunar.lunarDayCn} - 天干地支:{lunar.year8Char}年 {lunar.month8Char}月 {lunar.day8Char}日 请严格按照以下 JSON 格式返回,不要有其他内容: {{"yi": ["宜做的事1", "宜做的事2", "宜做的事3", "宜做的事4"], "ji": ["忌做的事1", "忌做的事2", "忌做的事3", "忌做的事4"]}} 要求: 1. 宜和忌各返回4-6个项目 2. 使用传统黄历术语,如:祭祀、祈福、嫁娶、出行、开市、动土、安葬等 3. 只返回 JSON,不要有任何解释""" response = client.chat.completions.create( model=settings.DEEPSEEK_MODEL, messages=[ {"role": "system", "content": "你是一个精通中国传统黄历的专家,请根据日期生成准确的宜忌信息。"}, {"role": "user", "content": prompt} ], temperature=0.7, max_tokens=200 ) # 解析返回的 JSON result_text = response.choices[0].message.content.strip() result = json.loads(result_text) return result.get("yi", []), result.get("ji", []) except Exception as e: # 如果 API 调用失败,返回默认值 print(f"DeepSeek API 调用失败: {e}") return ["祭祀", "祈福", "出行", "开市"], ["动土", "安葬", "破土", "作灶"]