VedAstro架构解析:深入理解核心公式 event = (data + logic) × time 完整指南

原创2026-11-05 23:59:381,676 阅读

VedAstro架构解析:深入理解核心公式 event = (data + logic) × time 完整指南

VedAstro 是一个非营利开源吠陀占星(Vedic Astrology)项目,把上千条古老的占星规则变成了可计算的人生预测引擎。它的心脏只有一条公式:

event = (data + logic) × time

用大白话翻译:事件 = (数据 + 逻辑)× 时间。

  • data(数据):写在 XML 里的事件规则——这是什么事件、吉凶如何、描述是什么
  • logic(逻辑):C# 里的计算方法——这个事件在什么天文条件下发生
  • time(时间):运行时输入的时间范围——把"规则"变成"某年某月某日真的会发生"

这篇文章不要求你会编程,带你 5 分钟看懂 VedAstro 是如何用一条公式生成人生预测图的。🔭


公式三要素拆解:data、logic 和时间各是什么?

data:写在 XML 里的"事件规则表"

所有事件的"档案"都存放在 EventDataList.xml 中,这个文件超过 8900 行,每条 <Event> 节点就是一条占星规则。比如"适合出行的月亮日"这条规则长这样:

<Event>
    <Name>GoodLunarDayForTravel</Name>
    <Nature>Good</Nature>
    <Description>
        For journeys/travel the best lunar days are the 2nd, 3rd, 5th...
    </Description>
    <Tag>Travel</Tag>
</Event>

注意这里只有"它是什么",没有"怎么算":名称、吉凶(Nature)、描述文字、分类标签(Tag)。数据与计算被刻意分开了,这是整个架构的关键。

VedAstro 占星计算的数据基础:星座与行星

logic:用"特性"一对一绑定的计算方法

那"怎么算"呢?答案在 Muhurtha.cs 这个近 1 万行的文件里,每个静态方法就是一个事件的判断逻辑。以"适合出行的月亮日"为例:

[EventCalculator(EventName.GoodLunarDayForTravel)]
public static CalculatorResult GoodLunarDayForTravel(Time time, Person person)
{
    var lunarDayNumber = Calculate.LunarDay(time).GetLunarDayNumber();
    var rightLunarDay = lunarDayNumber == 2 || lunarDayNumber == 3
                      || lunarDayNumber == 5 || lunarDayNumber == 7
                      || lunarDayNumber == 10 || lunarDayNumber == 11
                      || lunarDayNumber == 13;
    return new() { Occuring = rightLunarDay };
}

三个角色把数据和逻辑"焊"在一起:

  1. EventName 枚举(EventName.cs):每个事件名称在代码里都有对应的枚举值,相当于事件的"身份证号"
  2. EventCalculatorAttribute 特性(EventNameAttribute.cs):像标签一样贴在计算方法上,声明"我是为哪个事件服务的"
  3. EventCalculatorDelegate 委托(CalculatorDelegates.cs):统一签名 CalculatorResult (Time, Person),让上千个不同算法能被同一种方式调用

启动时,程序会读取 XML 里的每个事件名,用反射找到挂着同名特性的方法并绑定——数据找到了它的逻辑,这一步就完成了。

time:把"规则"变成"发生"的扳机

Time 结构体同时保存了标准时间和地理位置(占星计算必须基于出生地的本地平太阳时)。当你在页面上选择"从 1982 年到 2015 年"并点击 Calculate 时,GetTimeListFromRange 会把这个范围按设定精度切成一片一片的时间片——每一片都是逻辑要检查一次的时刻。

这就是公式里为什么是 × time 而不是 + time:同一套(数据 + 逻辑)要在每一个时间片上重复判断,同一个规则在不同时刻得出不同结果,时间的"乘法"把静态规则放大成贯穿一生的事件序列。


公式如何运转:两步生成一个 Event

整个流程在 EventManager.cs 中编排,官方架构图在 README.md:

第 1 步:data + logic 绑定

XML 数据经 EventData.FromXml 解析后,生成 EventData 结构——它同时持有"事件档案"(名称、吉凶、描述、标签)和"计算方法"(EventCalculator 委托)。此时还没有做任何占星计算,只是完成了链接。

第 2 步:× time 扫描

GetEventsInTimePeriod 拿到时间片列表后,用并行线程对每个时间片调用 eventData.EventCalculator(time, person):

  • 若返回"正在发生" → 记下一个 EventSlice(临时事件切片)
  • 连续为真的时间片被合并成一个完整区间

最后 EventSlicesToEvents 把每个"持续区间"封装成最终的 Event 对象——带开始时间、结束时间、吉凶、描述。这就是你在界面上看到的一条条事件。

💡 性能小彩蛋:并行扫描让一次全人生计算从 143 秒缩短到 40 秒(见 EventManager.cs 注释)。


全局视角:公式在 VedAstro 四大组件中的位置

VedAstro 项目结构:CORE LIBRARY、API、API LIBRARY 与 CLIENT 四大组件

这条公式运行在 CORE LIBRARY(即 Library/ 目录)中,向外辐射出三种形态:

形态 目录 说明
Web 网站 Website/ Blazor 前端,普通用户直接使用
REST API API/ 200+ 端点,供开发者集成
.NET 库 Library/ 可直接引用进你自己的项目

为什么计算放在服务器而不是浏览器?因为一次完整的人生事件计算约需 1GB 内存、30% CPU(README.md 设计笔记),手机浏览器扛不住,所以 API 路线成为定案。

VedAstro API 服务架构示意


公式的产物:一张人生预测图

下面这张图就是 event = (data + logic) × time 跑完全程的输出——Steve Jobs 1982 至 2015 年的人生事件图:

VedAstro 生成的人生事件预测图:绿色与红色条形代表吉凶事件及其发生时间

横轴是时间(time 维度的展开),每一根绿/红竖条都是一个由公式生成的 Event:绿色为吉事、红色为凶事,标注了"离开苹果""回归苹果""癌症手术"等真实人生节点。你可以看到成千上万个事件如何从一条公式里"长"出来。📊


快速定位:公式每个部分的源码在哪?

想动手验证?按下表直接找到对应文件:

公式部分 文件路径
公式原文与流程图 README.md
data:事件规则 XML Library/XMLData/EventDataList.xml
事件名枚举(ID 卡) Library/Data/Enum/EventName.cs
logic:计算方法集合 Library/Logic/Calculate/Muhurtha.cs
绑定特性 Library/Data/EventNameAttribute.cs
统一委托签名 Library/Data/Delegate/CalculatorDelegates.cs
data+logic 绑定 Library/Data/EventData.cs
× time 扫描编排 Library/Logic/EventManager.cs
时间片切分 Library/Data/Time.cs
最终事件对象 Library/Data/Event.cs

想新增一条预测? 按官方 Code Edit Guide 三步走:写一个计算方法 → 在 EventName 枚举加名字 → 在 XML 里补上规则数据,公式就会自动把它纳入计算。


一句话总结

data + logic 回答了"世界上有哪些可能发生的事",× time 回答了"它在你的哪一年真正发生"。

VedAstro 的架构智慧不在于算法有多神秘,而在于把占星知识拆成了"数据、逻辑、时间"三个正交的维度——任何一类规则都可以独立增改。这正是它能容纳数千种占星计算、并持续进化十年的原因。🌟

登录后查看全文
VedAstro