概览

包结构、类型体系与阅读本参考的方式。

Go 包内嵌一份 WebAssembly 编译的核心,经纯 Go 的 wazero 运行时调用—— 不需要 cgo,交叉编译与静态链接都不受影响。

这一栏是 Go 侧的完整 API 参考——每个导出函数、类型与方法都有独立条目。

安装

go get github.com/x-haose/x-iztro/go/iztro
import "github.com/x-haose/x-iztro/go/iztro"

第一张盘

chart, err := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil)
if err != nil {
    log.Fatal(err)
}

fmt.Println(chart.SolarDate, chart.LunarDate)
// 2000-8-16 二〇〇〇年七月十七

soul := chart.Palace(iztro.PalaceSoul)
if len(soul.MajorStars) > 0 {
    fmt.Println(soul.MajorStars[0].Name)
    // 紫微
} else {
    fmt.Println("命宫是空宫,借对宫看:", soul.OppositePalace().MajorStars[0].Name)
}

主星列表可能为空

一张盘上通常有两宫无主星(空宫),命宫也可能是其中之一。 直接写 soul.MajorStars[0] 在那种盘上会 panic——先判长度,或用 IsEmpty 分支到借对宫的写法。

包结构

包是扁平的,全部导出项都在 iztro 下。按主题划分:

主题主要导出本参考对应页
排盘BySolarByLunarRearranged排盘入口
数据类型AstrolabePalaceStarHoroscopeConfig星盘对象 起的四页
标识常量PalaceSoulStarZiweiMajMutagenLu数据表
轻量查询GetZodiacBySolarDate轻量查询
工具函数FixIndexGetBrightness工具函数
安星GetMajorStarGetHoroscopeStar安星模块
数据表StarsInfoHeavenlyStems数据表
翻译TranslateKeyOfKeyOfIn翻译
错误*ErrorErr* 哨兵、Code* 常量错误处理
运行时WarmupCloseCompilationCacheDir本页下方

常量即标识

包里的标识常量取值就是语言无关标识,可直接与数据对象的 *Key 字段比较:

soul := chart.Palace(iztro.PalaceSoul)
fmt.Println(soul.MajorStars[0].Key == iztro.StarZiweiMaj)
// true

它们都是无类型字符串常量,字符串字面量同样有效—— chart.Palace("soulPalace")chart.Palace(iztro.PalaceSoul) 等价。 常量的价值在于 IDE 补全与拼写检查。

判断用 Key,不要用 Name

star.Name 随排盘语言变化(中文盘是「紫微」,英文盘是 emperor); star.Key 在任何语言下都是 ziweiMaj。所有判断都应基于 *Key 字段或内置判断方法。

可变参数

需要传星耀列表或四化列表的方法一律用可变参数,调用处不必构造切片:

soul := chart.Palace(iztro.PalaceSoul)
target := chart.Palace(iztro.PalaceWealth)

fmt.Println(soul.Has(iztro.StarZiweiMaj, iztro.StarTianxiangMaj))
fmt.Println(soul.FliesTo(target, iztro.MutagenLu, iztro.MutagenJi))

输出

false
false

已有切片时用 ... 展开:

soul := chart.Palace(iztro.PalaceSoul)
stars := []string{iztro.StarZiweiMaj, iztro.StarTianxiangMaj}

fmt.Println(soul.Has(stars...))

输出

false

运限的流耀查询是例外

HasHoroscopeStars 一族的星耀参数是 []string 而非可变参数, 因为它前面已有宫名与层级两个字符串参数,可变参数会让调用处产生歧义。

错误处理

需要计算的入口都返回 (值, error);纯查询方法(PalaceStarHas 等)不返回错误, 查不到时返回 nil 或零值。

_, err := iztro.BySolar("2000-13-1", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil)

fmt.Println(err)
fmt.Println(errors.Is(err, iztro.ErrInvalidDate))

var e *iztro.Error
if errors.As(err, &e) {
    fmt.Println(e.Code)
}

输出

iztro: invalid solar date '2000-13-1': month must be within 1-12
true
invalid_date

错误一律是 *iztro.Error,带机器可读的 Code,并可用 errors.Is 对四个哨兵匹配。 详见错误处理

运行时与性能

wasm 模块编译一次、实例按需铺开:实例各持有自己的线性内存, 因此并发调用各占一个实例而不是共享一个加锁,实例数上限为 GOMAXPROCS。 排盘的热路径上没有全局互斥锁——取实例走 channel,只有运行时首次初始化与 Close 才加锁。多 goroutine 同时排盘因此能真正并行:同一台十核机器上, 8 个 goroutine 跑 800 次排盘比单 goroutine 快四倍出头。

编译产物落盘缓存在用户缓存目录下(CompilationCacheDir 可查), 按 wasm 内容哈希分桶,换了 wasm 自然换桶。

实测量级(Apple M 系列,10 核;具体数字随机器与 wasm 体积浮动):

阶段耗时
首次调用,编译缓存未命中(要现编译 wasm)一两百毫秒
首次调用,编译缓存命中二三十毫秒
稳态单次排盘半毫秒上下

主要成本是 JSON 编解码不是计算

稳态那半毫秒里,大头是 wasm 侧把整张盘序列化成 JSON、Go 侧再反序列化成结构体, 而不是斗数推算本身。只需要个别字段时,用 轻量查询GetMajorStarBySolarDate 一类)比排整盘划算得多。

Warmup / Close / CompilationCacheDir

func Warmup(ctx context.Context) error
func Close(ctx context.Context) error
func CompilationCacheDir(ctx context.Context) (string, error)
函数说明
Warmup预先完成编译并把实例池铺满,把冷启动开销提前到启动阶段。不调用也能正常工作——编译与实例化本来就是惰性的
Close关闭运行时、归还全部实例内存。通常不必调用;关闭后再调用本包任何函数会自动重新初始化
CompilationCacheDir返回编译缓存目录;未启用落盘缓存时返回空串
ctx := context.Background()

if err := iztro.Warmup(ctx); err != nil {
    log.Fatal(err)
}

dir, err := iztro.CompilationCacheDir(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Println(dir != "")

chart, err := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil)
if err != nil {
    log.Fatal(err)
}
fmt.Println(chart.SolarDate)

输出

true
2000-8-16

服务进程希望第一个请求就走热路径时,在启动阶段调一次 Warmup 即可。

Context 变体

排盘、运限、重排、Prompt 这些要进 wasm 的入口都有一个 *Context 版本, 多收一个 context.Context

无 ctx带 ctx
BySolar / ByLunarBySolarContext / ByLunarContext
Astrolabe.Horoscope / HoroscopeNowHoroscopeContext / HoroscopeNowContext
Astrolabe.RearrangedRearrangedContext
Astrolabe.AstrolabeToPrompt / HoroscopeToPromptAstrolabeToPromptContext / HoroscopeToPromptContext
Warmup / Close / CompilationCacheDir 只有 ctx 版本
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

chart, err := iztro.BySolarContext(ctx, "2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil)
if err != nil {
    log.Fatal(err)
}
fmt.Println(chart.SolarDate)

输出

2000-8-16

ctx 管的是排队,不是计算

ctx 用于取消等待空闲 wasm 实例的排队。实例一旦拿到,wasm 侧的计算不可中断—— 单次排盘本来就是亚毫秒级,没有需要中途打断的长任务。

wazero 编译器只覆盖 amd64 与 arm64

这两个架构上 wazero 走优化编译器(编译成机器码)。其余架构回落到解释器, 仍然能跑出正确结果,但速度会低一个量级以上。生产环境请部署在 amd64 或 arm64 上。

条目怎么读

每个 API 条目按固定八段组织:

用途 —— 一句话说清它做什么
斗数含义 —— 它在紫微斗数里对应什么概念(纯工程性的函数省略此段)
签名 —— 从源码原样摘出
参数 —— 名、类型、是否必填、默认值、说明
返回值 —— 类型与结构
示例 —— 可直接运行的片段
输出 —— 该示例的真实运行结果
边界与陷阱 —— 空值、越界、配置影响、与其他 API 的相互作用

示例统一用同一张盘:2000 年 8 月 16 日寅时女命, 方便跨页对照。这张盘的完整数据见数据结构

本页目录