Skill: devioslang/fortune-career (version 2.1.0)

# fortune-career · 职业与健康命理分析 ## 功能概述 基于**八字排盘** + **紫微斗数** + **大运流年**规则引擎，为应届生求职和职场人士提供职业方向和健康分析。支持完整报告和**一句话摘要**两种模式。 --- ## 触发关键词 ``` 算命 / 看命 / 命理 / 八字 / 职业分析 / 健康分析 / 看职业 / 看健康 / 大运 / 摘要 ``` --- ## 输入格式 支持自然语言，直接发送出生信息： ``` 算命 1990年5月15日10时 ← 完整报告 摘要 1990年5月15日10时 ← 一句话摘要（微信友好） 职业分析 90年5月15日10时 女 健康分析 1990 5 15 10 农历1990年5月15日10时 ← 自动识别农历 ``` --- ## 输出模式 ### 摘要模式（说"摘要"触发） 压缩为5句话，适合微信快速阅读： 1. 性格本质 + 最适合行业方向 2. 当前大运质量 + 整体感受 3. 今年流年 + 机危判断 4. 健康重点提醒 5. 行动建议（进攻/防守/维稳） ### 完整报告模式 - **大运走势** — 10步大运，🟢好运/🟡平缓/🔴低谷 + 每步具体解释 - **近三年流年** — 六冲/六合/六害/三合/半三合完整覆盖 - **职业分析** — 精确身强弱（藏干加权）+ 命宫星曜 + 五行行业 - **健康分析** — 先天体质 + 疾厄宫 + 流年预警 --- ## 技术架构 ``` 输入（自然语言，含农历识别） ↓ calc/bazi.py # 八字排盘（sxtwl） calc/lunar.py # 农历/阳历互转 calc/ganzhi.py # 藏干系统 + 精确身强弱 calc/ziwei.py # 紫微斗数（14正曜+12宫） calc/dayun_v2.py # 大运（节气起运 + 定性） calc/liunian_v2.py # 流年（六冲/六合/六害/三合） scripts/career_analysis.py scripts/health_analysis.py scripts/summary.py # 一句话摘要生成（v2.1新增） ↓ 输出（摘要5句 / 完整报告） ``` --- ## 版本历史 | 版本 | 改进 | |------|------| | v1.0 | 基础框架（八字+紫微+职业/健康） | | v1.1 | 农历支持 + 大运流年基础 | | v2.0 | 藏干加权身强弱 / 节气起运 / 流年冲合害三合 / 大运定性 | | **v2.1** | **一句话摘要模式（5句版，微信友好）** |

path: calc/bazi.py
content: """
八字排盘核心算法
基于 sxtwl 日历库
"""

import sxtwl
from typing import Tuple

STEMS = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']
BRANCHES = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']
ELEMENTS = {
    '甲': '木', '乙': '木', '丙': '火', '丁': '火', '戊': '土',
    '己': '土', '庚': '金', '辛': '金', '壬': '水', '癸': '水'
}
BRANCH_ELEMENTS = {
    '子': '水', '丑': '土', '寅': '木', '卯': '木', '辰': '土',
    '巳': '火', '午': '火', '未': '土', '申': '金', '酉': '金',
    '戌': '土', '亥': '水'
}

# 五行相生：木→火→土→金→水→木
# 五行相克：木克土克水克火克金克木

def get_bazi(year: int, month: int, day: int, hour: int) -> dict:
    """
    排八字
    year/month/day: 阳历日期
    hour: 小时 (0-23)
    """
    # sxtwl 使用12小时制
    hour_gan = hour

    solar = sxtwl.fromSolar(year, month, day)

    year_gz = solar.getYearGZ()
    month_gz = solar.getMonthGZ()
    day_gz = solar.getDayGZ()
    hour_gz = solar.getHourGZ(hour_gan)

    bazi = {
        'year': {'stem': STEMS[year_gz.tg], 'branch': BRANCHES[year_gz.dz]},
        'month': {'stem': STEMS[month_gz.tg], 'branch': BRANCHES[month_gz.dz]},
        'day': {'stem': STEMS[day_gz.tg], 'branch': BRANCHES[day_gz.dz]},
        'hour': {'stem': STEMS[hour_gz.tg], 'branch': BRANCHES[hour_gz.dz]},
    }

    # 八字字符串
    bazi['str'] = (
        f"{bazi['year']['stem']}{bazi['year']['branch']} "
        f"{bazi['month']['stem']}{bazi['month']['branch']} "
        f"{bazi['day']['stem']}{bazi['day']['branch']} "
        f"{bazi['hour']['stem']}{bazi['hour']['branch']}"
    )

    # 五行统计
    bazi['elements'] = {
        '木': 0, '火': 0, '土': 0, '金': 0, '水': 0
    }
    for col in ['year', 'month', 'day', 'hour']:
        bazi['elements'][ELEMENTS[bazi[col]['stem']]] += 1
        bazi['elements'][BRANCH_ELEMENTS[bazi[col]['branch']]] += 1

    # 日主
    bazi['day_master'] = bazi['day']['stem']

    return bazi


def get_day_ganzhi(year: int, month: int, day: int) -> str:
    """获取日干支字符串（用于查日柱）"""
    solar = sxtwl.fromSolar(year, month, day)
    day_gz = solar.getDayGZ()
    return f"{STEMS[day_gz.tg]}{BRANCHES[day_gz.dz]}"


def analyze_wuxing(bazi: dict) -> dict:
    """分析五行强弱"""
    elements = bazi['elements']
    day_master = bazi['day_master']

    # 日主对应五行
    day_element = ELEMENTS[day_master]

    # 计算五行得分（简单版：个数×权重）
    scores = {}
    total = sum(elements.values())

    # 身强：日干在月令旺、相 + 得多助
    # 简单判断：日干是否得月支
    month_branch = bazi['month']['branch']

    # 月支藏干（简化版）
    month_stems_hidden = {
        '子': ['癸'], '丑': ['己', '癸', '辛'], '寅': ['甲', '丙', '戊'],
        '卯': ['乙'], '辰': ['戊', '乙', '癸'], '巳': ['丙', '庚', '戊'],
        '午': ['丁', '己'], '未': ['己', '丁', '乙'], '申': ['庚', '壬', '戊'],
        '酉': ['辛'], '戌': ['戊', '辛', '丁'], '亥': ['壬', '甲']
    }

    # 判断日主是否得月令
    # 旺、相月份：木→寅卯月旺、相于冬；火→巳午月旺、相于春；土→辰戌丑未；金→申酉月旺、相于夏；水→亥子月旺、相于秋
    month_season = {
        '寅': '木', '卯': '木',    # 春
        '巳': '火', '午': '火',    # 夏
        '辰': '土', '戌': '土', '未': '土', '丑': '土',  # 四季
        '申': '金', '酉': '金',    # 秋
        '亥': '水', '子': '水',    # 冬
    }

    season = month_season[month_branch]

    strength = elements[day_element]
    if season == day_element:
        strength += 2  # 月令当令
    elif (
        (day_element == '木' and season in ['水']) or
        (day_element == '火' and season in ['木']) or
        (day_element == '土' and season in ['火']) or
        (day_element == '金' and season in ['土']) or
        (day_element == '水' and season in ['金'])
    ):
        strength += 1  # 月令相

    if strength >= 4:
        status = '身强'
    elif strength >= 2:
        status = '身中'
    else:
        status = '身弱'

    bazi['strength'] = status
    bazi['strength_score'] = strength
    bazi['season'] = season

    return bazi


def get_hsci(bazi: dict) -> str:
    """
    获取大运（简化版：仅支持顺逆排）
    大运：阳男阴女顺排，阴男阳女逆排
    这里简化处理，仅输出命理信息字符串
    """
    return bazi['str']


if __name__ == '__main__':
    # 测试：1984年4月4日和文章中张雪峰的八字
    # 文章：甲子 己巳 壬子 乙巳
    # 反推生日...这里先测一个已知八字
    test = get_bazi(1984, 4, 4, 10)
    print("1984年4月4日10时:", test['str'])
    print("五行:", test['elements'])
    test = analyze_wuxing(test)
    print("身强:", test['strength'])


path: calc/dayun.py
content: """
大运流年分析模块
大运：10年一步运，从出生月开始推算
流年：每一年的天干地支
"""

from typing import List, Dict
from calc.bazi import STEMS, BRANCHES

STEMS10 = STEMS  # 甲乙丙丁戊己庚辛壬癸
BRANCHES12 = BRANCHES  # 子丑寅卯辰巳午未申酉戌亥

# 天干索引
STEM_IDX = {s: i for i, s in enumerate(STEMS10)}
BRANCH_IDX = {b: i for i, b in enumerate(BRANCHES12)}


def get_dayun(bazi: dict, gender: str = '男') -> List[dict]:
    """
    计算大运
    bazi: 八字字典（来自 bazi.py）
    gender: '男' 或 '女'

    算法：
    1. 找月柱天干索引、月落地支索引
    2. 阳男阴女顺排（天干/地支各+1），阴男阳女逆排（各-1）
    3. 每步大运管10年
    4. 起始年龄 = 3 + (下一步干序 - 月干序) % 10
    """
    month_stem = bazi['month']['stem']
    month_branch = bazi['month']['branch']
    month_stem_idx = STEM_IDX[month_stem]
    month_branch_idx = BRANCH_IDX[month_branch]

    # 阴阳：甲丙戊庚壬为阳，乙丁己辛癸为阴
    day_stem = bazi['day']['stem']
    is_yang_day = STEM_IDX[day_stem] % 2 == 0  # 0=甲=阳, 2=丙=阳, ...

    # 阳男阴女顺排，阴男阳女逆排
    if (gender == '男' and is_yang_day) or (gender == '女' and not is_yang_day):
        direction = 1  # 顺
    else:
        direction = -1  # 逆

    dayun_list = []

    # 起始年龄 = 3 + 差值（至少为0）
    # 下一步天干索引
    next_stem_idx = (month_stem_idx + direction) % 10
    # 差值：跨越的步数（每步+1或-1，需取正值）
    diff = (next_stem_idx - month_stem_idx) % 10 if direction == 1 else (month_stem_idx - next_stem_idx) % 10
    start_age = 3 + diff

    for i in range(10):
        # 第i步大运
        stem_idx = (month_stem_idx + direction * (i + 1)) % 10
        branch_idx = (month_branch_idx + direction * (i + 1)) % 12

        age_start = start_age + i * 10
        age_end = age_start + 9

        dayun_list.append({
            'stem': STEMS10[stem_idx],
            'branch': BRANCHES12[branch_idx],
            'age_start': age_start,
            'age_end': age_end,
            'direction': '顺' if direction == 1 else '逆',
        })

    return dayun_list


def get_liunian(year: int, birth_year: int) -> dict:
    """
    计算流年（某一年）
    year: 要查询的年份
    birth_year: 出生年份
    """
    # 年干：基准年（1984=甲子年），距1984的年数模10
    offset = year - 1984
    year_stem_idx = offset % 10
    year_branch_idx = offset % 12

    return {
        'stem': STEMS10[year_stem_idx],
        'branch': BRANCHES12[year_branch_idx],
        'year': year,
    }


def get_current_liunian(birth_year: int, current_year: int = 2026) -> List[dict]:
    """
    获取从出生到今年的流年列表
    """
    results = []
    for y in range(birth_year, current_year + 1):
        results.append(get_liunian(y, birth_year))
    return results


# 流年与八字的作用关系（简化版）
BRANCH_CONFLICT = {
    ('子', '午'): '子午冲 - 心血管/心脏问题，情绪波动大',
    ('丑', '未'): '丑未冲 - 脾胃/肝胆问题',
    ('寅', '申'): '寅申冲 - 肝胆/肺部问题',
    ('卯', '酉'): '卯酉冲 - 肝肺不和，神经/呼吸',
    ('辰', '戌'): '辰戌冲 - 脾胃皮肤病',
    ('巳', '亥'): '巳亥冲 - 心脏血液问题',
}

BRANCH_HARMONY = {
    ('子', '丑'): '子丑合 - 助力合作，运势稳定',
    ('寅', '亥'): '寅亥合 - 事业推进，贵人运',
    ('卯', '戌'): '卯戌合 - 感情/财运',
    ('辰', '酉'): '辰酉合 - 财运人脉',
    ('午', '未'): '午未合 - 学业/名声',
    ('申', '巳'): '申巳合 - 智慧输出',
}


def analyze_liunian_influence(liunian: str, bazi: dict, year: int) -> dict:
    """
    分析流年对八字的影响
    liunian: 流年干支字符串，如 '丙午'
    bazi: 八字字典
    year: 年份
    """
    stem = liunian[0]
    branch = liunian[1]

    effects = []

    # 1. 流年地支与命局地支的冲合
    bazi_branches = [bazi['year']['branch'], bazi['month']['branch'],
                     bazi['day']['branch'], bazi['hour']['branch']]

    conflict = None
    harmony = None
    for my_branch in bazi_branches:
        pair = tuple(sorted([branch, my_branch], key=lambda x: BRANCH_IDX[x]))
        # Check conflict
        for c_pair, desc in BRANCH_CONFLICT.items():
            if set(c_pair) == set([branch, my_branch]):
                conflict = desc
                effects.append(f"⚠️ {desc}")
        # Check harmony
        for h_pair, desc in BRANCH_HARMONY.items():
            if set(h_pair) == set([branch, my_branch]):
                effects.append(f"✅ {desc}")

    # 2. 流年天干生克
    day_stem = bazi['day']['stem']
    day_stem_idx = STEM_IDX[day_stem]

    # 3. 五行影响
    BRANCH_ELEMENTS = {
        '子': '水', '丑': '土', '寅': '木', '卯': '木', '辰': '土',
        '巳': '火', '午': '火', '未': '土', '申': '金', '酉': '金',
        '戌': '土', '亥': '水'
    }
    STEM_ELEMENTS = {
        '甲': '木', '乙': '木', '丙': '火', '丁': '火', '戊': '土',
        '己': '土', '庚': '金', '辛': '金', '壬': '水', '癸': '水'
    }

    liunian_elem = STEM_ELEMENTS[stem]
    bazi_elems = [STEM_ELEMENTS[bazi['year']['stem']], STEM_ELEMENTS[bazi['month']['stem']],
                  STEM_ELEMENTS[bazi['day']['stem']], STEM_ELEMENTS[bazi['hour']['stem']]]

    # 流年地支五行
    liunian_branch_elem = BRANCH_ELEMENTS[branch]

    return {
        'year': year,
        'liunian': liunian,
        'stem': stem,
        'branch': branch,
        'stem_element': liunian_elem,
        'branch_element': liunian_branch_elem,
        'effects': effects if effects else ['平稳过渡'],
    }


def format_dayun_report(dayun_list: List[dict], current_year: int = 2026,
                         birth_year: int = 1990) -> str:
    """格式化大运报告（无标题，供嵌入使用）"""
    lines = []

    # 找当前年龄落在哪步大运
    current_age = current_year - birth_year
    for du in dayun_list:
        marker = ''
        if du['age_start'] <= current_age <= du['age_end']:
            marker = ' ◀ 当前'
        lines.append(f"  {du['age_start']}-{du['age_end']}岁：{du['stem']}{du['branch']}（{du['direction']}运）{marker}")

    return '\n'.join(lines)


if __name__ == '__main__':
    from calc.bazi import get_bazi, analyze_wuxing

    test = get_bazi(1990, 5, 15, 10)
    analyze_wuxing(test)

    print("大运：")
    dayun = get_dayun(test, '男')
    for du in dayun:
        print(f"  {du['age_start']}-{du['age_end']}岁：{du['stem']}{du['branch']}")

    print("\n流年（2024-2027）：")
    for y in [2024, 2025, 2026, 2027]:
        ln = get_liunian(y, 1990)
        print(f"  {y}年：{ln['stem']}{ln['branch']}")

    print("\n流年影响分析（2026）：")
    analysis = analyze_liunian_influence('丙午', test, 2026)
    print(f"  流年：{analysis['liunian']}")
    print(f"  天干五行：{analysis['stem_element']}")
    print(f"  地支五行：{analysis['branch_element']}")
    print(f"  影响：{analysis['effects']}")


path: calc/dayun_v2.py
content: """
大运分析 v2：
1. 精确起运年龄（节气算法）
2. 每步运定性（好坏+建议）
"""

from calc.ganzhi import STEMS, BRANCHES, STEM_ELEMENTS, BRANCH_ELEMENTS, ZANGAN, MONTH_LING, SHENG, KE

STEM_IDX = {s: i for i, s in enumerate(STEMS)}
BRANCH_IDX = {b: i for i, b in enumerate(BRANCHES)}


def get_accurate_dayun_start(gender: str, bazi: dict) -> int:
    """
    精确起运年龄计算

    原理：
    - 大运从出生后第1个"节"开始起算
    - 起运月数 = (出生到下一个节的天数) / 3（每月按3天算）
    - 岁数 = 3 + 起运月数（向下取整）

    简化版：用24节气判断
    - 寅月节：立春（2月4日左右）
    - 卯月节：惊蛰（3月5日左右）
    - 辰月节：清明（4月5日左右）
    - 巳月节：立夏（5月5日左右）
    - 午月节：芒种（6月5日左右）
    - 未月节：小暑（7月7日左右）
    - 申月节：立秋（8月7日左右）
    - 酉月节：白露（9月7日左右）
    - 戌月节：寒露（10月8日左右）
    - 亥月节：立冬（11月7日左右）
    - 子月节：大雪（12月7日左右）
    - 丑月节：小寒（1月5日左右）

    我们用简化近似：出生月距下一个节的天数/30 ≈ 月数
    """
    month = None
    for i, b in enumerate(BRANCHES):
        if b == bazi['month']['branch']:
            month = i + 1  # 1=子月, 2=丑月...
            break

    # 节气近似日期（每月两个节气，前一个为节）
    JIEQI_APPROX = {
        1: 5,   # 小寒 ~ 1月5日
        2: 4,   # 立春 ~ 2月4日
        3: 5,   # 惊蛰 ~ 3月5日
        4: 5,   # 清明 ~ 4月5日
        5: 5,   # 立夏 ~ 5月5日
        6: 5,   # 芒种 ~ 6月5日
        7: 7,   # 小暑 ~ 7月7日
        8: 7,   # 立秋 ~ 8月7日
        9: 7,   # 白露 ~ 9月7日
        10: 8,  # 寒露 ~ 10月8日
        11: 7,  # 立冬 ~ 11月7日
        12: 7,  # 大雪 ~ 12月7日
    }

    import datetime
    birth_date = datetime.date(bazi.get('_year', 1990), bazi.get('_month', 5), bazi.get('_day', 15))
    birth_month = birth_date.month

    # 计算下一个节
    year = birth_date.year
    next_month = (birth_month % 12) + 1
    next_jieqi_day = JIEQI_APPROX.get(next_month, 5)
    try:
        next_jieqi = datetime.date(year if next_month > birth_month else year + 1,
                                   next_month, next_jieqi_day)
    except:
        next_jieqi = datetime.date(year if next_month > birth_month else year + 1,
                                   next_month, 28)

    days_to_jieqi = (next_jieqi - birth_date).days
    if days_to_jieqi < 0:
        days_to_jieqi += 365

    # 起运月数 = 天数/3（向下取整）
    start_months = max(0, days_to_jieqi // 3)
    start_age = 3 + start_months

    return start_age


def get_dayun_v2(bazi: dict, gender: str = '男') -> list:
    """
    v2版大运计算
    - 精确起运年龄
    - 每步运定性（好/坏/平）
    """
    month_stem = bazi['month']['stem']
    month_branch = bazi['month']['branch']
    month_stem_idx = STEM_IDX[month_stem]
    month_branch_idx = BRANCH_IDX[month_branch]
    day_stem = bazi['day']['stem']
    is_yang_day = STEM_IDX[day_stem] % 2 == 0

    # 顺逆
    if (gender == '男' and is_yang_day) or (gender == '女' and not is_yang_day):
        direction = 1
    else:
        direction = -1

    # 精确起运年龄
    start_age = get_accurate_dayun_start(gender, bazi)

    dayun_list = []
    day_master_elem = STEM_ELEMENTS[day_stem]

    for i in range(10):
        stem_idx = (month_stem_idx + direction * (i + 1)) % 10
        branch_idx = (month_branch_idx + direction * (i + 1)) % 12

        age_start = start_age + i * 10
        age_end = age_start + 9

        stem = STEMS[stem_idx]
        branch = BRANCHES[branch_idx]
        stem_elem = STEM_ELEMENTS[stem]
        branch_elem = BRANCH_ELEMENTS[branch]

        # 定性：看地支五行对日主的影响
        # 生助日主 = 好，克耗日主 = 差，同类 = 平
        good_count = 0
        bad_count = 0

        # 天干对日主
        if SHENG.get(stem_elem) == day_master_elem or stem_elem == day_master_elem:
            good_count += 1
        elif KE.get(stem_elem) == day_master_elem:
            bad_count += 1

        # 地支对日主
        if SHENG.get(branch_elem) == day_master_elem or branch_elem == day_master_elem:
            good_count += 1
        elif KE.get(branch_elem) == day_master_elem:
            bad_count += 1

        if good_count >= 2:
            quality = '🟢好运'
            quality_tip = f'天干{stem}（{stem_elem}）、地支{branch}（{branch_elem}），助力日主'
        elif bad_count >= 2:
            quality = '🔴低谷'
            quality_tip = f'天干{stem}（{stem_elem}）、地支{branch}（{branch_elem}），克耗日主'
        else:
            quality = '🟡平缓'
            quality_tip = f'天干{stem}（{stem_elem}）、地支{branch}（{branch_elem}），平稳过渡'

        dayun_list.append({
            'stem': stem,
            'branch': branch,
            'stem_elem': stem_elem,
            'branch_elem': branch_elem,
            'age_start': age_start,
            'age_end': age_end,
            'direction': '顺' if direction == 1 else '逆',
            'quality': quality,
            'quality_tip': quality_tip,
        })

    return dayun_list


def format_dayun_v2(dayun_list: list, current_year: int, birth_year: int) -> str:
    """格式化v2大运报告"""
    current_age = current_year - birth_year
    lines = []
    for du in dayun_list:
        marker = ''
        if du['age_start'] <= current_age <= du['age_end']:
            marker = ' ◀ 当前'
        lines.append(
            f"  {du['age_start']}-{du['age_end']}岁："
            f"{du['stem']}{du['branch']}（{du['direction']}运）"
            f" {du['quality']}{marker}"
        )
        lines.append(f"    {du['quality_tip']}")
    return '\n'.join(lines)


if __name__ == '__main__':
    from calc.bazi import get_bazi
    from calc.ganzhi import analyze_strength_detailed

    test = get_bazi(1990, 5, 15, 10)
    test['_year'] = 1990
    test['_month'] = 5
    test['_day'] = 15

    print(f"八字: {test['str']}")
    print(f"起运年龄: {get_accurate_dayun_start('男', test)}岁")
    print()
    dayun = get_dayun_v2(test, '男')
    print(format_dayun_v2(dayun, 2026, 1990))


path: calc/ganzhi.py
content: """
地支藏干系统 + 身强弱精确判断
藏干：每个地支内藏的天干及其中余气力量
"""

STEMS = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']
BRANCHES = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']
STEM_ELEMENTS = {
    '甲': '木', '乙': '木', '丙': '火', '丁': '火', '戊': '土',
    '己': '土', '庚': '金', '辛': '金', '壬': '水', '癸': '水'
}
BRANCH_ELEMENTS = {
    '子': '水', '丑': '土', '寅': '木', '卯': '木', '辰': '土',
    '巳': '火', '午': '火', '未': '土', '申': '金', '酉': '金',
    '戌': '土', '亥': '水'
}

# 地支藏干表（本气、中气、余气）
# 格式：(天干, 力量等级)
# 力量等级：本气=1.0，中气=0.5，余气=0.3
ZANGAN = {
    '子': [('癸', 1.0)],
    '丑': [('己', 1.0), ('癸', 0.5), ('辛', 0.3)],
    '寅': [('甲', 1.0), ('丙', 0.5), ('戊', 0.3)],
    '卯': [('乙', 1.0)],
    '辰': [('戊', 1.0), ('乙', 0.5), ('癸', 0.3)],
    '巳': [('丙', 1.0), ('庚', 0.5), ('戊', 0.3)],
    '午': [('丁', 1.0), ('己', 0.5)],
    '未': [('己', 1.0), ('丁', 0.5), ('乙', 0.3)],
    '申': [('庚', 1.0), ('壬', 0.5), ('戊', 0.3)],
    '酉': [('辛', 1.0)],
    '戌': [('戊', 1.0), ('辛', 0.5), ('丁', 0.3)],
    '亥': [('壬', 1.0), ('甲', 0.5)],
}

# 月令（地支）对应季节的当令五行
MONTH_LING = {
    '寅': '木', '卯': '木',    # 春
    '巳': '火', '午': '火',    # 夏
    '申': '金', '酉': '金',    # 秋
    '亥': '水', '子': '水',    # 冬
    '辰': '土', '戌': '土', '未': '土', '丑': '土',  # 四季末
}

# 五行相生
SHENG = {'木': '火', '火': '土', '土': '金', '金': '水', '水': '木'}
# 五行相克
KE = {'木': '土', '土': '水', '水': '火', '火': '金', '金': '木'}


def get_all_stems_with_weight(bazi: dict) -> dict:
    """
    展开八字所有天干（含藏干），计算每个天干的权重
    返回：{天干: 总权重分}
    """
    weights = {s: 0.0 for s in STEMS}

    # 明柱天干（各占1分）
    for col in ['year', 'month', 'day', 'hour']:
        stem = bazi[col]['stem']
        weights[stem] += 1.0

    # 地支藏干
    for col in ['year', 'month', 'day', 'hour']:
        branch = bazi[col]['branch']
        for hidden_stem, weight in ZANGAN[branch]:
            weights[hidden_stem] += weight

    return weights


def analyze_strength_detailed(bazi: dict) -> dict:
    """
    精确身强弱分析
    1. 计算日主总权重
    2. 月令当令程度
    3. 通根判断（地支是否有日主同类）
    4. 综合打分
    """
    day_master = bazi['day']['stem']
    day_elem = STEM_ELEMENTS[day_master]
    month_branch = bazi['month']['branch']
    month_elem = BRANCH_ELEMENTS[month_branch]

    # 计算所有天干权重
    weights = get_all_stems_with_weight(bazi)
    day_total = weights[day_master]

    # 1. 月令判断（当令=1.5分，相=0.8分，死=0.3分，囚=0.5分）
    ling_table = {'当令': 1.5, '相': 0.8, '死': 0.3, '囚': 0.5}
    season_elem = MONTH_LING.get(month_branch, '土')

    if month_elem == day_elem:
        ling_score = 1.5  # 当令
    elif SHENG.get(month_elem) == day_elem:
        ling_score = 0.8  # 月令相
    elif KE.get(month_elem) == day_elem:
        ling_score = 0.3  # 月令死
    else:
        ling_score = 0.5  # 月令囚

    # 2. 通根：天干地支里有没有和日主同五行
    # 有根则强，无根则弱（天干一片比劫但地支无根 = 假强）
    same_elem_stems = [s for s in STEMS if STEM_ELEMENTS[s] == day_elem]
    has_root = any(
        STEM_ELEMENTS.get(bazi[col]['stem']) == day_elem or
        BRANCH_ELEMENTS.get(bazi[col]['branch']) == day_elem
        for col in ['year', 'month', 'day', 'hour']
    )

    # 地支本气是否为日主同类
    month_branch_main = ZANGAN[month_branch][0][0]  # 本气
    has_month_root = STEM_ELEMENTS[month_branch_main] == day_elem

    # 3. 综合评分
    # 基准 = 日主权重 + 月令分 + 通根加分
    base_score = day_total + ling_score
    if has_root:
        base_score += 0.5
    if has_month_root:
        base_score += 0.5

    # 4. 定性
    if base_score >= 5.0:
        strength = '极强'
        tip = '日主极旺，不宜再补同气，宜泄不宜助'
    elif base_score >= 3.5:
        strength = '身强'
        tip = '日主偏旺，宜泄宜克，适合竞争性、开创性工作'
    elif base_score >= 2.5:
        strength = '身中'
        tip = '日主中和，进退自如，适应面广'
    elif base_score >= 1.5:
        strength = '身弱'
        tip = '日主偏弱，宜生宜助，不宜过度消耗'
    else:
        strength = '极弱'
        tip = '日主极弱，宜大补元气，谨慎高压环境'

    bazi['strength'] = strength
    bazi['strength_score'] = round(base_score, 1)
    bazi['strength_tip'] = tip
    bazi['day_total_weight'] = round(day_total, 1)
    bazi['ling_score'] = ling_score
    bazi['has_root'] = has_root
    bazi['has_month_root'] = has_month_root

    return bazi


def format_strength_detail(bazi: dict) -> str:
    """格式化身强弱详情"""
    lines = []
    lines.append(f"日主：{bazi['day_master']}（{STEM_ELEMENTS[bazi['day_master']]}）")
    lines.append(f"月令：{bazi['month']['branch']}（{BRANCH_ELEMENTS[bazi['month']['branch']]}）")
    lines.append(f"日主权重分：{bazi['day_total_weight']}（天干1.0 + 藏干加权）")
    lines.append(f"月令当令分：{bazi['ling_score']}")
    lines.append(f"通根状态：{'有根' if bazi['has_root'] else '无根'}")
    lines.append(f"综合评分：{bazi['strength_score']}")
    lines.append(f"定性：{bazi['strength']} → {bazi['strength_tip']}")
    return '\n'.join(lines)


if __name__ == '__main__':
    from calc.bazi import get_bazi

    # 测试1：张雪峰 甲子 己巳 壬子 乙巳
    # 需要找这个生日...先测试庚金日主
    test = get_bazi(1990, 5, 15, 10)
    analyze_strength_detailed(test)
    print("八字:", test['str'])
    print("旧版判断:", test.get('strength', 'N/A'))
    print("旧版得分:", test.get('strength_score', 'N/A'))
    print()
    print("精确版:")
    print(format_strength_detail(test))

    print()
    weights = get_all_stems_with_weight(test)
    print("各天干权重:", {k: round(v, 1) for k, v in weights.items() if v > 0})


path: calc/liunian_v2.py
content: """
流年分析 v2：完整地支关系判断
支持：冲、合、刑、害、三合、三会
"""

from calc.ganzhi import STEMS, BRANCHES, STEM_ELEMENTS, BRANCH_ELEMENTS, ZANGAN

STEM_IDX = {s: i for i, s in enumerate(STEMS)}
BRANCH_IDX = {b: i for i, b in enumerate(BRANCHES)}

# 地支关系表
BRANCH_CONFLICT = {  # 六冲
    ('子', '午'): ('冲', '心血管/心脏、情绪波动'),
    ('丑', '未'): ('冲', '脾胃肝胆、消化系统'),
    ('寅', '申'): ('冲', '肝胆/肺部、肢体'),
    ('卯', '酉'): ('冲', '肝肺、神经系统'),
    ('辰', '戌'): ('冲', '脾胃皮肤、精神'),
    ('巳', '亥'): ('冲', '心肾、血液'),
}

BRANCH_HARMONY = {  # 六合
    ('子', '丑'): ('合', '助力合作、运势稳定'),
    ('寅', '亥'): ('合', '事业推进、贵人运'),
    ('卯', '戌'): ('合', '感情/财运'),
    ('辰', '酉'): ('合', '财运人脉'),
    ('午', '未'): ('合', '学业/名声'),
    ('申', '巳'): ('合', '智慧输出'),
}

BRANCH_XING = {  # 三刑
    ('子', '卯'): ('刑', '是非纠纷、健康波折'),
    ('寅', '巳', '申'): ('三刑', '是非官讼、健康意外'),
    ('丑', '未', '戌'): ('三刑', '小人阻碍、脾胃受损'),
    ('午', '午'): ('自刑', '自我纠结、慢性损耗'),
}

BRANCH_HAI = {  # 六害
    ('子', '未'): ('害', '脾胃不和、感情波折'),
    ('丑', '午'): ('害', '心肾不交、事业阻碍'),
    ('寅', '巳'): ('害', '人际是非、呼吸系统'),
    ('卯', '辰'): ('害', '小人暗算、肝胆问题'),
    ('申', '亥'): ('害', '口舌是非、肾水受损'),
    ('酉', '戌'): ('害', '肺大肠、财运阻碍'),
}

# 三合局（生地半合）
BRANCH_SANHE = {
    ('申', '子', '辰'): ('水局', '水旺、财运/感情推进'),
    ('亥', '卯', '未'): ('木局', '木旺、文创/学业进步'),
    ('寅', '午', '戌'): ('火局', '火旺、事业/名声上升'),
    ('巳', '酉', '丑'): ('金局', '金旺、财运/武职有利'),
}

# 三会局
BRANCH_SANHUI = {
    ('寅', '卯', '辰'): ('木会', '木旺'),
    ('巳', '午', '未'): ('火会', '火旺'),
    ('申', '酉', '戌'): ('金会', '金旺'),
    ('亥', '子', '丑'): ('水会', '水旺'),
}

ELEM_NAMES = {'木': '木', '火': '火', '土': '土', '金': '金', '水': '水'}


def get_branch_pair_key(a: str, b: str) -> tuple:
    """标准化配对key（用于冲/合/害的tuple查表）"""
    return tuple(sorted([a, b], key=lambda x: BRANCH_IDX[x]))


def check_relationships(liunian_branch: str, bazi: dict) -> list:
    """
    检查流年地支与命局所有地支的作用关系
    返回描述列表
    """
    results = []
    bazi_branches = [bazi['year']['branch'], bazi['month']['branch'],
                     bazi['day']['branch'], bazi['hour']['branch']]

    # 1. 冲（6组）
    for my_branch in bazi_branches:
        pair = get_branch_pair_key(liunian_branch, my_branch)
        if pair in BRANCH_CONFLICT:
            rel, desc = BRANCH_CONFLICT[pair]
            results.append(f"⚠️ **{rel}**：{desc}")

    # 2. 合（6组）
    for my_branch in bazi_branches:
        pair = get_branch_pair_key(liunian_branch, my_branch)
        if pair in BRANCH_HARMONY:
            rel, desc = BRANCH_HARMONY[pair]
            results.append(f"✅ **{rel}**：{desc}")

    # 3. 害（6组）
    for my_branch in bazi_branches:
        pair = get_branch_pair_key(liunian_branch, my_branch)
        if pair in BRANCH_HAI:
            rel, desc = BRANCH_HAI[pair]
            results.append(f"⚡ **{rel}**：{desc}")

    # 4. 刑（两组常见）
    # 子卯刑
    pair_limao = get_branch_pair_key(liunian_branch, '卯')
    if pair_limao in [('子', '卯')] and '卯' in bazi_branches:
        results.append(f"⚡ **子卯刑**：是非纠纷、健康波折")

    # 5. 三合局检查（流年+命局两个支）
    liunian_elem = BRANCH_ELEMENTS[liunian_branch]
    # 流年是否参与某个三合局
    for he_组成, (ju_name, ju_desc) in BRANCH_SANHE.items():
        count = sum(1 for b in bazi_branches if b in he_组成)
        if liunian_branch in he_组成 and count >= 1:
            # 流年加入命局已有三合
            results.append(f"✨ **半三合{ju_name}**：{ju_desc}")

    return results


def analyze_liunian_v2(liunian: str, bazi: dict, year: int) -> dict:
    """v2版流年分析"""
    stem = liunian[0]
    branch = liunian[1]
    liunian_elem = STEM_ELEMENTS[stem]
    liunian_branch_elem = BRANCH_ELEMENTS[branch]

    # 五行生克关系
    day_stem = bazi['day']['stem']
    day_elem = STEM_ELEMENTS[day_stem]
    SHENG = {'木': '火', '火': '土', '土': '金', '金': '水', '水': '木'}
    KE = {'木': '土', '火': '金', '土': '水', '金': '木', '水': '火'}

    relations = []
    if SHENG.get(day_elem) == liunian_elem:
        relations.append(f"流年天干{stem}（{liunian_elem}）生助日主→有情转化")
    elif KE.get(day_elem) == liunian_elem:
        relations.append(f"流年天干{stem}（{liunian_elem}）克耗日主→压力增大")

    # 地支关系
    branch_effects = check_relationships(branch, bazi)

    return {
        'year': year,
        'liunian': liunian,
        'stem': stem,
        'branch': branch,
        'stem_element': liunian_elem,
        'branch_element': liunian_branch_elem,
        'stem_relations': relations,
        'branch_effects': branch_effects,
    }


def format_liunian_v2(analysis: dict) -> str:
    """格式化v2流年报告"""
    parts = []
    parts.append(f"{analysis['stem_element']}气（{analysis['stem']}）")

    if analysis['stem_relations']:
        parts.append(' | '.join(analysis['stem_relations']))

    if analysis['branch_effects']:
        parts.append(' | '.join(analysis['branch_effects']))
    else:
        parts.append('无明显冲合刑害')

    return f"({analysis['liunian']}）" + ' / '.join(parts)


if __name__ == '__main__':
    from calc.bazi import get_bazi
    from calc.ganzhi import analyze_strength_detailed

    test = get_bazi(1990, 5, 15, 10)
    analyze_strength_detailed(test)

    print("=== 流年分析 v2 测试 ===")
    for y in [2024, 2025, 2026, 2027]:
        offset = y - 1984
        liunian = STEMS[offset % 10] + BRANCHES[offset % 12]
        a = analyze_liunian_v2(liunian, test, y)
        print(f"{y}年 {liunian}：{format_liunian_v2(a)}")


path: calc/lunar.py
content: """
农历/阳历转换模块
支持：用户输入自动识别 + 互转
"""

import sxtwl
from typing import Optional, Tuple

STEMS = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']
BRANCHES = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']

# 农历月份别称
LUNAR_MONTHS = {
    1: '正月', 2: '二月', 3: '三月', 4: '四月', 5: '五月', 6: '六月',
    7: '七月', 8: '八月', 9: '九月', 10: '十月', 11: '冬月', 12: '腊月'
}


def solar_to_lunar(year: int, month: int, day: int) -> dict:
    """阳历 → 农历"""
    solar = sxtwl.fromSolar(year, month, day)
    return {
        'year': solar.getLunarYear(),
        'month': solar.getLunarMonth(),
        'day': solar.getLunarDay(),
        'is_leap': solar.isLunarLeap(),
    }


def lunar_to_solar(year: int, month: int, day: int, is_leap: bool = False) -> dict:
    """农历 → 阳历"""
    solar = sxtwl.fromLunar(year, month, day, is_leap)
    return {
        'year': solar.getSolarYear(),
        'month': solar.getSolarMonth(),
        'day': solar.getSolarDay(),
    }


def is_lunar_date(text: str) -> bool:
    """判断输入是否是农历表述"""
    lunar_markers = ['农历', '阴历', '闰']
    return any(m in text for m in lunar_markers)


def parse_birth_date(text: str) -> Tuple[int, int, int, int, bool]:
    """
    解析出生日期字符串，自动识别农历/阳历
    返回：(year, month, day, hour, is_lunar)

    支持格式：
    - 1990年5月15日10时
    - 1990年5月15日 10点
    - 农历1990年5月15日10时
    - 1990/5/15 10
    - 90年5月15日10时
    """
    import re
    text = text.strip()

    # 判断是否农历
    is_lunar = is_lunar_date(text)
    text = text.replace('农历', '').replace('阴历', '').replace('闰', '闰')

    # 提取数字
    nums = re.findall(r'\d+', text)
    if len(nums) < 4:
        raise ValueError(f"日期格式不正确：{text}，示例：1990年5月15日10时")

    year = int(nums[0])
    if year < 100:
        year += 1900 if year >= 50 else 2000
    month = int(nums[1])
    day = int(nums[2])
    hour = int(nums[3]) if len(nums) > 3 else 0

    # 阳历直接返回
    if not is_lunar:
        return year, month, day, hour, False

    # 农历：检查是否闰月
    is_leap = '闰' in text and month > 0

    # 转换为阳历
    solar = lunar_to_solar(year, month, day, is_leap)
    return solar['year'], solar['month'], solar['day'], hour, True


if __name__ == '__main__':
    # 测试
    print("=== 阳历 → 农历 ===")
    print(solar_to_lunar(1986, 2, 19))  # 应该是 1986年1月11日

    print("=== 农历 → 阳历 ===")
    print(lunar_to_solar(1986, 1, 21))  # 应该是 1986年3月1日

    print("=== 解析器测试 ===")
    print(parse_birth_date("1986年2月19日5时"))
    print(parse_birth_date("农历1986年1月21日5时"))
    print(parse_birth_date("1990年5月15日10时"))
    print(parse_birth_date("农历闰六月十五10时"))


path: calc/ziwei.py
content: """
紫微斗数排盘算法
基于八字月柱和年柱推算各宫位主星
"""

from typing import Dict, List, Optional

# 十二宫顺序（从命宫逆时针排）
PALACES = ['命', '父母', '福德', '田宅', '事业', '交友', '迁移', '疾厄', '财帛', '子女', '夫妻', '官禄']

# 地支对应索引
ZHI_INDEX = {'子': 0, '丑': 1, '寅': 2, '卯': 3, '辰': 4, '巳': 5,
             '午': 6, '未': 7, '申': 8, '酉': 9, '戌': 10, '亥': 11}

# 藏干表
ZANGAN = {
    '子': ['癸'], '丑': ['己', '癸', '辛'], '寅': ['甲', '丙', '戊'],
    '卯': ['乙'], '辰': ['戊', '乙', '癸'], '巳': ['丙', '庚', '戊'],
    '午': ['丁', '己'], '未': ['己', '丁', '乙'], '申': ['庚', '壬', '戊'],
    '酉': ['辛'], '戌': ['戊', '辛', '丁'], '亥': ['壬', '甲']
}

# 十四正曜分类
# 第一等：紫微、天机、太阳、武曲、天同、廉贞
# 第二等：天府、太阴、贪狼、巨门、天相、天梁、七杀、破军

def get_ziwei_position(month_zhi: str) -> str:
    """
    紫微星落宫（简化版）
    规则：紫微星永远落在亥、子、丑三宫
    确切宫位根据月支和时支推算
    口诀：紫微子在亥午未申，卯戌二宫空
    """
    # 简化规则：月支为亥/子/丑 → 紫微在亥
    # 月支为寅/午/未/申 → 紫微在午
    # 月支为卯/戌 → 空
    # 月支为辰/巳/酉 → 紫微在丑
    mapping = {
        '亥': '亥', '子': '亥', '丑': '亥',
        '寅': '午', '午': '午', '未': '午', '申': '午',
        '卯': '空', '戌': '空',
        '辰': '丑', '巳': '丑', '酉': '丑',
    }
    return mapping.get(month_zhi, '亥')


def get_main_stars(year_gan: str, year_zhi: str, month_zhi: str) -> Dict[str, str]:
    """
    排十四正曜
    年干用于定三合局，月支用于定紫微位置
    """
    stars = {}

    # 1. 紫微星 - 根据月支定
    stars['紫微'] = get_ziwei_position(month_zhi)

    # 2. 天机 - 永远在丑宫
    stars['天机'] = '丑'

    # 3. 太阳 - 根据年支定（年支为申子辰→亥，寅午戌→午，亥卯未→未，巳酉丑→申）
    sun_table = {
        '申': '亥', '子': '亥', '辰': '亥',
        '寅': '午', '午': '午', '戌': '午',
        '亥': '未', '卯': '未', '未': '未',
        '巳': '申', '酉': '申', '丑': '申',
    }
    stars['太阳'] = sun_table.get(year_zhi, '午')

    # 4. 武曲 - 根据年支定
    wuqu_table = {
        '申': '辰', '子': '辰', '辰': '辰',
        '寅': '戌', '午': '戌', '戌': '戌',
        '亥': '巳', '卯': '巳', '未': '巳',
        '巳': '丑', '酉': '丑', '丑': '丑',
    }
    stars['武曲'] = wuqu_table.get(year_zhi, '辰')

    # 5. 天同 - 根据年支定
    tiantong_table = {
        '申': '未', '子': '未', '辰': '未',
        '寅': '辰', '午': '辰', '戌': '辰',
        '亥': '酉', '卯': '酉', '未': '酉',
        '巳': '子', '酉': '子', '丑': '子',
    }
    stars['天同'] = tiantong_table.get(year_zhi, '未')

    # 6. 廉贞 - 根据年支定
    lianzhen_table = {
        '申': '寅', '子': '寅', '辰': '寅',
        '寅': '申', '午': '申', '戌': '申',
        '亥': '亥', '卯': '亥', '未': '亥',
        '巳': '巳', '酉': '巳', '丑': '巳',
    }
    stars['廉贞'] = lianzhen_table.get(year_zhi, '寅')

    # 7. 天府 - 与紫微相对
    ziwei_pos = ZHI_INDEX[stars['紫微']]
    tianfu_pos = (ziwei_pos + 6) % 12
    tianfu_zhi = list(ZHI_INDEX.keys())[list(ZHI_INDEX.values()).index(tianfu_pos)]
    stars['天府'] = tianfu_zhi

    # 8. 太阴 - 根据年支定
    taiyin_table = {
        '申': '戌', '子': '戌', '辰': '戌',
        '寅': '丑', '午': '丑', '戌': '丑',
        '亥': '卯', '卯': '卯', '未': '卯',
        '巳': '亥', '酉': '亥', '丑': '亥',
    }
    stars['太阴'] = taiyin_table.get(year_zhi, '戌')

    # 9. 贪狼 - 根据年支和月支定（简化）
    tanlang_pos = (ZHI_INDEX[year_zhi] + 1) % 12
    stars['贪狼'] = list(ZHI_INDEX.keys())[tanlang_pos]

    # 10. 巨门 - 根据年支定
    jumen_table = {
        '申': '子', '子': '子', '辰': '子',
        '寅': '卯', '午': '卯', '戌': '卯',
        '亥': '丑', '卯': '丑', '未': '丑',
        '巳': '寅', '酉': '寅', '丑': '寅',
    }
    stars['巨门'] = jumen_table.get(year_zhi, '子')

    # 11. 天相 - 根据年支和月支推（简化：永远在巳或午）
    tianxiang_table = {
        '子': '巳', '丑': '巳', '寅': '午', '卯': '午',
        '辰': '巳', '巳': '午', '午': '巳', '未': '午',
        '申': '巳', '酉': '午', '戌': '巳', '亥': '午',
    }
    stars['天相'] = tianxiang_table.get(year_zhi, '巳')

    # 12. 天梁 - 根据年支定
    tianliang_table = {
        '申': '午', '子': '午', '辰': '午',
        '寅': '未', '午': '未', '戌': '未',
        '亥': '寅', '卯': '寅', '未': '寅',
        '巳': '卯', '酉': '卯', '丑': '卯',
    }
    stars['天梁'] = tianliang_table.get(year_zhi, '午')

    # 13. 七杀 - 根据年支定
    qisha_table = {
        '申': '子', '子': '子', '辰': '子',
        '寅': '丑', '午': '丑', '戌': '丑',
        '亥': '卯', '卯': '卯', '未': '卯',
        '巳': '亥', '酉': '亥', '丑': '亥',
    }
    stars['七杀'] = qisha_table.get(year_zhi, '子')

    # 14. 破军 - 根据年支定
    pojund_table = {
        '申': '辰', '子': '辰', '辰': '辰',
        '寅': '午', '午': '午', '戌': '午',
        '亥': '寅', '卯': '寅', '未': '寅',
        '巳': '亥', '酉': '亥', '丑': '亥',
    }
    stars['破军'] = pojund_table.get(year_zhi, '辰')

    return stars


def build_chart(bazi: dict) -> dict:
    """
    构建完整命盘
    输入：八字字典（from bazi.py）
    输出：十二宫及每个宫位的主星
    """
    year_gan = bazi['year']['stem']
    year_zhi = bazi['year']['branch']
    month_zhi = bazi['month']['branch']

    # 1. 命宫位置 = 逆布：年支起正月，顺数至生月，再逆时针布十二宫
    # 简化：用年支地支索引找命宫
    year_zhi_idx = ZHI_INDEX[year_zhi]
    month_zhi_idx = ZHI_INDEX[month_zhi]

    # 命宫 = (年支索引 - 生月 + 12) % 12（简化算法）
    minggong_idx = (year_zhi_idx - month_zhi_idx) % 12

    # 2. 排十二宫
    palaces = {}
    zhi_list = list(ZHI_INDEX.keys())

    for i, palace in enumerate(PALACES):
        palace_idx = (minggong_idx - i) % 12
        palaces[palace] = zhi_list[palace_idx]

    # 3. 排十四正曜
    main_stars = get_main_stars(year_gan, year_zhi, month_zhi)

    # 4. 分配主星到各宫
    chart = {}
    for palace_name, palace_zhi in palaces.items():
        stars_in_palace = []

        # 找出哪些星落在此宫
        for star_name, star_zhi in main_stars.items():
            if star_zhi == palace_zhi:
                stars_in_palace.append(star_name)

        chart[palace_name] = {
            'zhi': palace_zhi,
            'stars': stars_in_palace
        }

    # 5. 标记格局
    chart['_meta'] = {
        'minggong_zhi': palaces['命'],
        'main_stars': main_stars
    }

    return chart


def analyze_geshi(chart: dict) -> List[str]:
    """分析格局"""
    geshi = []
    main_stars = chart['_meta']['main_stars']

    # 杀破狼格局
    if '七杀' in main_stars.values() and '破军' in main_stars.values() and '贪狼' in main_stars.values():
        if '命' in str(main_stars):
            geshi.append('杀破狼')

    # 紫府同宫
    if '紫微' in main_stars.values() and '天府' in main_stars.values():
        geshi.append('紫府同宫')

    # 日月同宫
    if '太阳' in main_stars.values() and '太阴' in main_stars.values():
        geshi.append('日月同宫')

    return geshi if geshi else ['普通格局']


if __name__ == '__main__':
    from bazi import get_bazi, analyze_wuxing

    # 测试八字：甲子 己巳 壬子 乙巳
    # 需要找一个能排出这个八字的生日，这里用估计
    # 壬子日柱...
    test = get_bazi(1984, 4, 4, 10)
    print("八字:", test['str'])
    analyze_wuxing(test)

    chart = build_chart(test)
    for palace, info in chart.items():
        if palace != '_meta':
            print(f"{palace}宫({info['zhi']}): {', '.join(info['stars']) if info['stars'] else '无主星'}")
    print("格局:", analyze_geshi(chart))


path: main.py
content: #!/usr/bin/env python3
"""
fortune-career skill 主入口
v2.1: 一句话摘要模式（--brief）
"""

import sys
import datetime
from calc.bazi import get_bazi
from calc.ganzhi import analyze_strength_detailed
from calc.ziwei import build_chart, analyze_geshi
from calc.lunar import parse_birth_date
from calc.dayun_v2 import get_dayun_v2, format_dayun_v2
from calc.liunian_v2 import analyze_liunian_v2, format_liunian_v2
from scripts.career_analysis import analyze_career, format_career_report
from scripts.health_analysis import analyze_health, format_health_report
from scripts.summary import generate_summary
from calc.bazi import STEMS, BRANCHES


def run_full_analysis(year: int, month: int, day: int, hour: int,
                      gender: str = '男', mode: str = 'all',
                      is_lunar: bool = False,
                      brief: bool = False) -> str:
    """执行命理分析"""
    current_year = datetime.datetime.now().year
    lunar_note = '（农历）' if is_lunar else ''

    bazi = get_bazi(year, month, day, hour)
    bazi['_year'] = year
    bazi['_month'] = month
    bazi['_day'] = day

    analyze_strength_detailed(bazi)
    chart = build_chart(bazi)
    geshi = analyze_geshi(chart)
    chart['_geshi'] = geshi

    dayun = get_dayun_v2(bazi, gender)

    # 流年
    offset = current_year - 1984
    liunian = STEMS[offset % 10] + BRANCHES[offset % 12]
    ln_analysis = analyze_liunian_v2(liunian, bazi, current_year)

    health_result = analyze_health(bazi, chart, year=current_year)

    # ========== brief 摘要模式 ==========
    if brief:
        health_warnings = []
        if health_result.get('jihe_health'):
            health_warnings = health_result['jihe_health']
        return generate_summary(
            bazi, dayun, liunian, year, current_year, health_warnings
        )

    # ========== 完整报告模式 ==========
    lines = []
    lines.append(f"**出生信息：{year}年{month}月{day}日 {hour}时**{lunar_note}")
    lines.append(f"**八字：{bazi['str']}**\n")

    # 大运
    lines.append("---")
    lines.append("**【大运走势】**")
    lines.append(format_dayun_v2(dayun, current_year, year))

    # 流年
    lines.append("")
    lines.append("**【近三年流年】**")
    for y in [current_year - 1, current_year, current_year + 1]:
        off = y - 1984
        ln = STEMS[off % 10] + BRANCHES[off % 12]
        a = analyze_liunian_v2(ln, bazi, y)
        marker = ' ◀ 今年' if y == current_year else ''
        lines.append(f"  {y}年 {ln}：{format_liunian_v2(a)}{marker}")

    # 职业分析
    if mode in ('all', 'career'):
        lines.append("")
        lines.append("---")
        lines.append("**【职业分析】**\n")
        career = analyze_career(bazi, chart)
        lines.append(format_career_report(career, bazi))

    # 健康分析
    if mode in ('all', 'health'):
        lines.append("")
        lines.append("---")
        lines.append("**【健康分析】**\n")
        lines.append(format_health_report(health_result, bazi))

    lines.append("")
    lines.append("---")
    lines.append("*本分析基于传统命理学规则框架，仅供参考*")

    return '\n'.join(lines)


def cmd_main():
    """命令行入口"""
    if len(sys.argv) < 2:
        print("用法:")
        print("  python3 main.py 算命 <出生信息> [性别]")
        print("  python3 main.py 摘要 <出生信息> [性别]   ← 一句话版")
        print("  python3 main.py 职业 <出生信息> [性别]")
        print("  python3 main.py 健康 <出生信息> [性别]")
        print()
        print("示例:")
        print("  python3 main.py 算命 1990年5月15日10时")
        print("  python3 main.py 摘要 1990年5月15日10时")
        print("  python3 main.py 摘要 农历1990年5月15日10时 女")
        sys.exit(1)

    cmd = sys.argv[1]
    birth_text = sys.argv[2] if len(sys.argv) > 2 else sys.argv[1]
    gender = sys.argv[3] if len(sys.argv) > 3 else '男'

    if birth_text in ('算命', '摘要', '职业', '健康', 'all'):
        print(f"用法: python3 main.py 算命/摘要 <出生信息> [性别]")
        sys.exit(1)

    try:
        year, month, day, hour, is_lun = parse_birth_date(birth_text)
    except Exception as e:
        print(f"解析失败：{e}")
        sys.exit(1)

    mode_map = {'算命': 'all', '摘要': 'brief', '职业': 'career', '健康': 'health', 'all': 'all'}
    mode = mode_map.get(cmd, 'all')
    brief = (cmd == '摘要')

    result = run_full_analysis(year, month, day, hour, gender, mode, is_lun, brief)
    print(result)


if __name__ == '__main__':
    cmd_main()


path: scripts/career_analysis.py
content: """
职业分析框架
基于八字和紫微斗数输出职业方向、性格特点、事业走势
"""

from calc.bazi import STEMS, BRANCHES, ELEMENTS, BRANCH_ELEMENTS

# 性格关键词库
STEM_PERSONALITY = {
    '甲': '开创型 · 直来直去 · 领导欲强 · 上进不服输',
    '乙': '细腻型 · 善于变通 · 配合度高 · 善于思考',
    '丙': '热烈型 · 执行力快 · 表面外向 · 内心需要认可',
    '丁': '内敛型 · 观察力强 · 温和但有主见 · 善于幕后',
    '戊': '稳重型 · 踏实诚信 · 物质导向 · 抗压能力强',
    '己': '包容型 · 善于协调 · 务实灵活 · 讨厌浮夸',
    '庚': '刚烈型 · 果断决绝 · 原则性强 · 不喜欢绕弯子',
    '辛': '精致型 · 审美敏锐 · 追求完美 · 善于表达',
    '壬': '流动型 · 好奇心强 · 适应力强 · 喜欢自由',
    '癸': '深沉型 · 直觉敏锐 · 善于洞察 · 内向安静',
}

# 食伤星性格
STEM_EATJOY = {
    '甲': '食神：温和表达、享受生活、口福好',
    '乙': '伤官：才华外露、表演欲强、不服管教',
    '丙': '食神：热情洋溢、表达直接、喜欢被认可',
    '丁': '伤官：才华横溢、想法多、容易挑剔',
    '戊': '食神：务实沉稳、做事有耐心、不喜表现',
    '己': '伤官：灵活变通、脑子快、善于批判',
    '庚': '食神：直接爽快、执行力强、不喜欢拖沓',
    '辛': '伤官：审美精致、口才流利、追求独特',
    '壬': '食神：自由洒脱、兴趣广泛、不拘小节',
    '癸': '伤官：思想深邃、直觉敏锐、善于分析',
}

# 八字十神职业倾向
TEN_GOD_JOBS = {
    '比': ['合伙人创业', '专业技术', '自由职业', '销售'],
    '劫': ['体力劳动', '竞技体育', '安保', '执行'],
    '食': ['创意设计', '餐饮美食', '教育培训', '咨询顾问'],
    '伤': ['技术创新', '艺术创作', '写作出版', '律师'],
    '财': ['企业管理', '财务金融', '销售', '商务'],
    '才': ['艺术设计', '审美相关', '品牌管理', '媒体'],
    '官': ['公务员', '企业管理', '法律合规', '组织管理'],
    '杀': ['司法公安', '军事', '执法', '高压职业'],
    '枭': ['学术研究', '医疗健康', '技术研发', '神秘学'],
    '印': ['教育', '文化', '出版', '政府机构'],
}

# 紫微主星事业特征
STAR_CAREER = {
    '紫微': '管理型 · 权力欲望强 · 适合高层管理、政府、决策',
    '天机': '智囊型 · 策划能力强 · 适合策划、咨询、技术',
    '太阳': '领导型 · 社交能力强 · 适合外交、教育、媒体',
    '武曲': '刚毅型 · 执行力强 · 适合金融、军警、技术',
    '天同': '温和型 · 人际和谐 · 适合服务、教育、艺术',
    '廉贞': '复杂型 · 适应力强 · 适合销售、公关、自由职业',
    '天府': '保守型 · 管理稳健 · 适合财务、行政、地产',
    '太阴': '内敛型 · 思考深沉 · 适合幕后、财务、艺术',
    '贪狼': '欲望型 · 野心勃勃 · 适合商业、销售、公关',
    '巨门': '口才型 · 争论能力强 · 适合律师、销售、教育',
    '天相': '协调型 · 辅佐能力强 · 适合行政、助理、服务',
    '天梁': '稳定型 · 监督能力强 · 适合监察、稳健投资',
    '七杀': '开创型 · 冲击力强 · 适合创业、军警、外科',
    '破军': '破坏型 · 变革力强 · 适合转型、创新、破坏性建设',
}

# 五行行业倾向
ELEMENT_JOBS = {
    '木': '教育、文化出版、农业造林、医药卫生、宗教',
    '火': '能源、照明、电子、互联网、餐饮、化妆品',
    '土': '房地产、建筑、矿业、农业、仓储、政府',
    '金': '金融、珠宝、五金器械、汽车、司法、军队',
    '水': '贸易、物流、运输、航海、旅游、媒体',
}


def analyze_career(bazi: dict, chart: dict) -> dict:
    """综合职业分析"""
    day_master = bazi['day_master']
    year_stem = bazi['year']['stem']
    month_stem = bazi['month']['stem']
    strength = bazi.get('strength', '身中')
    elements = bazi['elements']
    main_stars = chart['_meta']['main_stars']
    geshi = chart.get('_geshi', ['普通格局'])

    # 1. 性格本质
    personality = STEM_PERSONALITY.get(day_master, '未知')

    # 2. 食伤性格
    month_stem_idx = STEMS.index(month_stem)
    if month_stem_idx < 5:  # 甲乙丙丁戊
        eatjoy = f"月柱透出{month_stem}：{STEM_EATJOY.get(month_stem, '')}"
    else:  # 己庚辛壬癸
        eatjoy = f"月柱透出{month_stem}：{STEM_EATJOY.get(month_stem, '')}"

    # 3. 日主强弱与职业选择
    if strength == '身强':
        strength_tip = '身强则宜主动出击，适合开创性、竞争性工作'
    elif strength == '身弱':
        strength_tip = '身弱则宜稳中求进，适合配合型、稳定型工作'
    else:
        strength_tip = '身中则进退自如，适应面广'

    # 4. 五行最旺 → 最适合行业
    dominant = max(elements, key=elements.get)
    recommended_industries = ELEMENT_JOBS.get(dominant, '')

    # 5. 命宫主星事业特征
    minggong_zhi = chart.get('命', {}).get('zhi', '')
    minggong_stars = chart.get('命', {}).get('stars', [])
    minggong_career = '、'.join([STAR_CAREER.get(s, s) for s in minggong_stars]) if minggong_stars else '普通命宫'

    # 6. 迁移宫（外地/出行发展）
    qianyi_stars = chart.get('迁移', {}).get('stars', [])
    qianyi_tip = f"迁移宫主星：{'、'.join(qianyi_stars) if qianyi_stars else '无主星'}，"
    if qianyi_stars:
        for star in qianyi_stars:
            if star in STAR_CAREER:
                qianyi_tip += STAR_CAREER[star]
                break
    else:
        qianyi_tip += '宜本地发展'

    # 7. 事业宫
    shiye_stars = chart.get('事业', {}).get('stars', [])
    shiye_tip = f"事业宫主星：{'、'.join(shiye_stars) if shiye_stars else '无主星'}"

    # 8. 综合建议
    suggestions = []

    # 强弱建议
    suggestions.append(f"▎强弱判断：{strength} → {strength_tip}")

    # 行业建议
    suggestions.append(f"▎推荐行业（五行{dominant}旺）：{recommended_industries}")

    # 命宫建议
    if minggong_stars:
        suggestions.append(f"▎命宫主星：{'、'.join(minggong_stars)} → {minggong_career.split('·')[0] if '·' in minggong_career else minggong_career}")

    # 格局建议
    if geshi and geshi[0] != '普通格局':
        suggestions.append(f"▎命格：{'、'.join(geshi)} → 适合挑战性工作，不宜安于现状")

    return {
        'personality': personality,
        'eatjoy': eatjoy,
        'strength': strength,
        'strength_tip': strength_tip,
        'dominant_element': dominant,
        'industries': recommended_industries,
        'minggong_stars': minggong_stars,
        'minggong_career': minggong_career,
        'qianyi_tip': qianyi_tip,
        'shiye_tip': shiye_tip,
        'shiye_stars': shiye_stars,
        'suggestions': suggestions,
        'geshi': geshi,
    }


def format_career_report(analysis: dict, bazi: dict) -> str:
    """格式化输出职业分析报告"""
    lines = []
    lines.append("【职业性格分析】")
    lines.append(f"日主：{bazi['day_master']}（{ELEMENTS[bazi['day_master']]}）")
    lines.append(f"性格本质：{analysis['personality']}")
    lines.append(f"食伤表现：{analysis['eatjoy']}")
    lines.append("")

    lines.append("【事业特征】")
    lines.append(f"命宫主星：{'、'.join(analysis['minggong_stars']) if analysis['minggong_stars'] else '无主星'}")
    lines.append(f"事业宫：{analysis['shiye_tip']}")
    lines.append(f"迁移宫：{analysis['qianyi_tip']}")
    lines.append(f"命格格局：{'、'.join(analysis['geshi']) if analysis['geshi'] else '普通格局'}")
    lines.append("")

    lines.append("【五行与行业】")
    lines.append(f"日主五行：{ELEMENTS[bazi['day_master']]}")
    lines.append(f"最旺五行：{analysis['dominant_element']}（{bazi['elements'][analysis['dominant_element']]}个）")
    lines.append(f"推荐行业：{analysis['industries']}")
    lines.append("")

    lines.append("【综合建议】")
    for s in analysis['suggestions']:
        lines.append(s)

    return '\n'.join(lines)


if __name__ == '__main__':
    from calc.bazi import get_bazi, analyze_wuxing
    from calc.ziwei import build_chart, analyze_geshi

    # 测试
    test = get_bazi(1984, 4, 4, 10)
    analyze_wuxing(test)
    chart = build_chart(test)
    chart['_geshi'] = analyze_geshi(chart)

    result = analyze_career(test, chart)
    print(format_career_report(result, test))


path: scripts/health_analysis.py
content: """
健康分析框架
基于八字五行强弱和紫微疾厄宫分析健康风险
"""

from calc.bazi import STEMS, BRANCHES, ELEMENTS, BRANCH_ELEMENTS

# 十天干对应脏腑
STEM_ORGANS = {
    '甲': '肝、胆（筋骨）',
    '乙': '肝、胆（毛发）',
    '丙': '小肠、心（血液、眼睛）',
    '丁': '心脏、血（眼睛）',
    '戊': '胃、脾（消化）',
    '己': '脾、胃（肌肉）',
    '庚': '大肠、肺（呼吸、皮肤）',
    '辛': '肺、呼吸系统（牙齿、骨骼）',
    '壬': '膀胱、肾（血液、代谢）',
    '癸': '肾、心包（免疫、内分泌）',
}

# 地支对应身体部位
BRANCH_BODY = {
    '子': '耳、肾、泌尿、生殖',
    '丑': '肚腹、脾、肝',
    '寅': '胆、手臂、肝',
    '卯': '肝、神经、手指',
    '辰': '肩、胸、胃',
    '巳': '脸、咽、心',
    '午': '眼、心、小肠',
    '未': '脊背、脾、消化',
    '申': '大肠、肺、呼吸',
    '酉': '肺、骨、齿',
    '戌': '腿、命门、消化',
    '亥': '肾、血、循环',
}

# 五行过弱/过旺的健康表现
ELEMENT_HEALTH = {
    '木': {'弱': '肝胆不适、筋骨酸痛、情绪抑郁',
           '旺': '肝火旺盛、偏头痛、易怒',
           'note': '木对应肝胆，肝主疏泄，木弱者易情绪郁结'},
    '火': {'弱': '心脏血液循环不佳、手脚冰冷、面色苍白',
           '旺': '心火旺、口舌生疮、失眠多梦、血压高',
           'note': '火对应心和小肠，火旺者易心血管问题'},
    '土': {'弱': '消化不良、脾胃虚弱、容易疲劳',
           '旺': '脾胃湿热、体内毒素、口臭、皮肤问题',
           'note': '土对应脾胃，是后天之本'},
    '金': {'弱': '呼吸系统弱、易感冒、皮肤粗糙',
           '旺': '肺部燥热、咳嗽、便秘、痔',
           'note': '金对应肺大肠，金旺者呼吸系统敏感'},
    '水': {'弱': '肾虚、腰酸、泌尿问题、精力不济',
           '旺': '肾水过旺、水肿、体寒、妇科/男科问题',
           'note': '水对应肾和膀胱，肾为先天之本'},
}

# 流年冲克健康预警
YEAR_CONFLICT = {
    '子午冲': '心肾不交、心脏血压问题、情绪波动大',
    '丑未冲': '脾胃肝胆问题、消化系统紊乱',
    '寅申冲': '肝胆、肺大肠问题、肢体疼痛',
    '卯酉冲': '肝肺不和、神经系统、呼吸系统',
    '辰戌冲': '脾胃皮肤病、精神压力、失眠',
    '巳亥冲': '心肾不交、心脏、血液问题',
}

# 紫微疾厄宫重点
STAR_HEALTH = {
    '紫微': '身体一般较好，但易因权力欲望引发情志问题',
    '天机': '神经系统易受损，用脑过度则失眠头痛',
    '太阳': '眼睛、血液、血压、心脏问题',
    '武曲': '肺部、呼吸系统、骨骼',
    '天同': '消化系统、肾脏、水液代谢',
    '廉贞': '泌尿系统、血液循环、肿瘤需注意',
    '天府': '消化系统、脾胃',
    '太阴': '肝肾、妇科、血液',
    '贪狼': '肝胆、欲望引发的情志病',
    '巨门': '口舌、脾胃、呼吸系统',
    '天相': '皮肤、脾胃、泌尿',
    '天梁': '心脏、血管、需注意肿瘤',
    '七杀': '肝胆、骨骼、意外伤害',
    '破军': '消耗性疾病、泌尿系统、手术',
}


def analyze_health(bazi: dict, chart: dict, year: int = None) -> dict:
    """综合健康分析"""
    elements = bazi['elements']
    day_master = bazi['day_master']
    month_branch = bazi['month']['branch']

    # 1. 五行强弱分析
    weakness = []
    strength_issues = []

    for elem, count in elements.items():
        if count == 0:
            weakness.append(f"{elem}（严重缺损）")
        elif count == 1:
            weakness.append(f"{elem}（较弱）")

    # 2. 日主对应脏腑
    day_organs = STEM_ORGANS.get(day_master, '未知')

    # 3. 疾厄宫分析
    jihe_info = chart.get('疾厄', {})
    jihe_stars = jihe_info.get('stars', [])
    jihe_zhi = jihe_info.get('zhi', '')
    jihe_body = BRANCH_BODY.get(jihe_zhi, '全身')

    jihe_tip = f"疾厄宫{jihe_zhi}宫，主星：{'、'.join(jihe_stars) if jihe_stars else '无主星'}"
    jihe_health = []
    for star in jihe_stars:
        if star in STAR_HEALTH:
            jihe_health.append(STAR_HEALTH[star])

    # 4. 流年分析（如果有）
    year_warnings = []
    if year:
        # 简化：计算该年地支
        year_zhi_idx = (year - 1984) % 12 + 1  # 简化
        # 需要精确计算，这里仅提示
        year_warnings.append(f"{year}年流年健康预警：需关注心血管、情绪波动问题")

    # 5. 综合养护建议
    suggestions = []

    # 五行养护
    if weakness:
        suggestions.append(f"▎五行较弱：{'、'.join(weakness)}，需重点养护对应脏腑")

    # 食伤重者
    month_stem_idx = STEMS.index(bazi['month']['stem'])
    if month_stem_idx < 5:  # 食神月
        suggestions.append("▎食神透出：消化系统需注意，忌暴饮暴食")
    else:  # 伤官月
        suggestions.append("▎伤官透出：神经系统消耗大，忌过度思虑，宜多运动")

    # 水火交战预警（张雪峰案例）
    if bazi['year']['branch'] == '子' or bazi['month']['branch'] == '午':
        suggestions.append("▎⚠️ 子午冲或壬午月柱：心血管系统为薄弱环节，忌高强度情绪波动")

    return {
        'day_organs': day_organs,
        'elements': elements,
        'weakness': weakness,
        'jihe_zhi': jihe_zhi,
        'jihe_stars': jihe_stars,
        'jihe_body': jihe_body,
        'jihe_tip': jihe_tip,
        'jihe_health': jihe_health,
        'year_warnings': year_warnings,
        'suggestions': suggestions,
    }


def format_health_report(analysis: dict, bazi: dict) -> str:
    """格式化健康分析报告"""
    lines = []
    lines.append("【先天体质】")
    lines.append(f"日主：{bazi['day_master']}（{ELEMENTS[bazi['day_master']]}）")
    lines.append(f"日主对应脏腑：{analysis['day_organs']}")
    lines.append("")

    lines.append("【五行强弱】")
    for elem, count in bazi['elements'].items():
        status = '旺' if count >= 3 else ('平' if count >= 2 else '弱')
        lines.append(f"  {elem}：{count}个 {'✅' if count >= 2 else '⚠️' if count == 1 else '❌'}（{status}）")
    lines.append("")

    if analysis['weakness']:
        lines.append(f"【重点关注】五行偏弱：{'、'.join(analysis['weakness'])}")
        lines.append("")

    lines.append("【疾厄宫分析】")
    lines.append(analysis['jihe_tip'])
    lines.append(f"疾厄宫身体部位：{analysis['jihe_body']}")
    if analysis['jihe_health']:
        for tip in analysis['jihe_health']:
            lines.append(f"  → {tip}")
    lines.append("")

    lines.append("【健康建议】")
    for s in analysis['suggestions']:
        lines.append(s)

    if analysis['year_warnings']:
        lines.append("")
        for w in analysis['year_warnings']:
            lines.append(w)

    return '\n'.join(lines)


if __name__ == '__main__':
    from calc.bazi import get_bazi, analyze_wuxing
    from calc.ziwei import build_chart

    test = get_bazi(1984, 4, 4, 10)
    analyze_wuxing(test)
    chart = build_chart(test)

    result = analyze_health(test, chart, 2026)
    print(format_health_report(result, test))


path: scripts/summary.py
content: """
一句话摘要生成
将完整命理报告压缩为3-5句核心信息
适用于微信等短阅读场景
"""

from calc.ganzhi import STEM_ELEMENTS, BRANCH_ELEMENTS, SHENG, KE, MONTH_LING

STEMS = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']
BRANCHES = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']

# 性格速读关键词
PERSONALITY_SHORT = {
    '甲': '开创冲动、领导型、不服输',
    '乙': '细腻敏感、适应力强、善于变通',
    '丙': '热情外放、执行力快、渴望认可',
    '丁': '内敛直觉、观察力强、有主见',
    '戊': '踏实诚信、务实稳重、抗压强',
    '己': '包容灵活、善于协调、讨厌浮夸',
    '庚': '果断决绝、原则性强、不喜欢绕弯',
    '辛': '审美精致、追求完美、口才好',
    '壬': '好奇心强、适应力强、喜欢自由',
    '癸': '深沉敏锐、直觉强、内向安静',
}

# 五行行业速配
ELEMENT_JOBS = {
    '木': '教育/文化/医药',
    '火': '互联网/能源/餐饮/互联网',
    '土': '地产/建筑/政府/农业',
    '金': '金融/司法/军警/机械',
    '水': '贸易/物流/航海/媒体',
}

# 五行养护重点
ELEMENT_HEALTH_TIPS = {
    '木': '肝胆养护，忌熬夜生气',
    '火': '心血管养护，忌情绪激动',
    '土': '脾胃养护，忌暴饮暴食',
    '金': '肺大肠养护，忌烟酒辛辣',
    '水': '肾泌尿养护，忌过度劳累',
}

# 流年好坏判断关键词
QUALITY_WORDS = {
    '🟢': '利',
    '🟡': '平',
    '🔴': '慎',
}


def get_current_status(dayun_list: list, birth_year: int, current_year: int) -> dict:
    """获取当前大运和流年状态"""
    current_age = current_year - birth_year
    current_du = None
    for du in dayun_list:
        if du['age_start'] <= current_age <= du['age_end']:
            current_du = du
            break
    return current_du


def generate_summary(bazi: dict, dayun_list: list,
                     liunian: str, birth_year: int,
                     current_year: int,
                     health_warnings: list) -> str:
    """
    生成一句话摘要
    格式：性格本质 + 当下运势 + 今年重点 + 健康提醒 + 行动建议
    """
    day_master = bazi['day_master']
    day_elem = STEM_ELEMENTS[day_master]
    strength = bazi.get('strength', '身中')
    strength_tip = bazi.get('strength_tip', '')

    # 1. 性格速读
    personality = PERSONALITY_SHORT.get(day_master, '综合型')

    # 2. 当前大运
    current_du = get_current_status(dayun_list, birth_year, current_year)
    if current_du:
        du_quality = current_du['quality']
        du_desc = f"{current_du['age_start']}-{current_du['age_end']}岁{current_du['stem']}{current_du['branch']}运"
    else:
        du_quality = '🟡'
        du_desc = '大运待起'

    # 3. 今年流年
    ln_stem, ln_branch = liunian[0], liunian[1]
    ln_elem_stem = STEM_ELEMENTS[ln_stem]
    ln_elem_branch = BRANCH_ELEMENTS[ln_branch]

    # 4. 健康重点（只说有问题的）
    health_focus = health_warnings if health_warnings else []

    # 5. 最旺五行行业
    dominant = max(bazi['elements'], key=bazi['elements'].get)
    top_industry = ELEMENT_JOBS.get(dominant, '综合')

    # ========== 组装摘要 ==========
    lines = []

    # 第1句：性格+行业
    lines.append(f"「{day_master}日主」—— {personality}，五行{day_elem}气旺盛，最适合{top_industry}方向发展")

    # 第2句：当前大运
    du_word = {'🟢': '顺利', '🟡': '平稳', '🔴': '波折'}.get(du_quality, '一般')
    du_detail = current_du['quality_tip'].split('，')[1] if current_du else ''
    lines.append(f"当前大运：{du_desc}，整体{du_word}，{du_detail}")

    # 第3句：今年运势
    ln_word = '有机会' if ln_elem_stem in [day_elem] or SHENG.get(day_elem) == ln_elem_stem else '有挑战'
    lines.append(f"今年流年{current_year}年{liunian}，{ln_elem_stem}气主导，{ln_word}，{ln_branch}地支{ln_elem_branch}需关注")

    # 第4句：健康提醒（如果有）
    if health_focus:
        weakest = bazi.get('strength', '身中')
        tips = ELEMENT_HEALTH_TIPS.get(day_elem, '注意休息')
        lines.append(f"健康重点：{tips}，今年{ln_branch}字运势需重点关注")
    else:
        lines.append(f"健康状态：整体平稳，{ELEMENT_HEALTH_TIPS.get(day_elem, '注意休息')}")

    # 第5句：行动建议
    if du_quality == '🟢':
        action = '宜主动出击，适合换赛道/争取晋升/尝试新方向'
    elif du_quality == '🔴':
        action = '宜守成，不宜冒险，优先保住现有成果，修炼内功'
    else:
        action = '稳步推进，专注本业，有机会可小试，但勿冒进'

    lines.append(f"行动建议：{action}")

    return '\n'.join(lines)


if __name__ == '__main__':
    from calc.bazi import get_bazi
    from calc.ganzhi import analyze_strength_detailed
    from calc.dayun_v2 import get_dayun_v2
    from calc.bazi import STEMS, BRANCHES

    test = get_bazi(1990, 5, 15, 10)
    test['_year'] = 1990; test['_month'] = 5; test['_day'] = 15
    analyze_strength_detailed(test)
    dayun = get_dayun_v2(test, '男')

    # 今年流年：丙午
    offset = 2026 - 1984
    liunian = STEMS[offset % 10] + BRANCHES[offset % 12]

    health_warnings = ['心脑血管']  # 假设有警告
    summary = generate_summary(test, dayun, liunian, 1990, 2026, health_warnings)
    print("=== 一句话摘要 ===")
    print(summary)


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)