feat: P1 合盘/星座/问答与测测完整设计包及模拟器取证工具
落地 synastry/star/ask API 与 H5 页面,补齐 cece-frontend-re complete-design 证据文档,并加入 Android 模拟器截图抓取脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
package portrait
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/companion"
|
||||
)
|
||||
|
||||
var (
|
||||
tg = []string{"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"}
|
||||
dz = []string{"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"}
|
||||
tge = map[string]string{"甲": "木", "乙": "木", "丙": "火", "丁": "火", "戊": "土", "己": "土", "庚": "金", "辛": "金", "壬": "水", "癸": "水"}
|
||||
dze = map[string]string{"子": "水", "丑": "土", "寅": "木", "卯": "木", "辰": "土", "巳": "火", "午": "火", "未": "土", "申": "金", "酉": "金", "戌": "土", "亥": "水"}
|
||||
eo = []string{"木", "火", "土", "金", "水"}
|
||||
ec = map[string]string{"木": "#5CB85C", "火": "#E54D42", "土": "#E8985A", "金": "#C8923A", "水": "#4A90E2"}
|
||||
)
|
||||
|
||||
var yiJi = map[string]struct{ Yi, Ji string }{
|
||||
"木": {"早睡养肝血 · 舒展拉伸 · 绿色蔬菜", "久坐 · 生闷气 · 熬夜"},
|
||||
"火": {"午休15分钟 · 菊花/莲子 · 平心静气", "暴晒 · 辛辣油腻 · 过度兴奋"},
|
||||
"土": {"规律三餐 · 健脾山药 · 饭后散步", "思虑过度 · 生冷 · 暴饮暴食"},
|
||||
"金": {"滋润养肺 · 白色食物 · 深呼吸", "干燥受凉 · 悲忧 · 剧烈耗气"},
|
||||
"水": {"早卧晚起 · 温补黑色食物 · 护腰腹", "受寒 · 过劳 · 惊恐紧张"},
|
||||
}
|
||||
|
||||
// seasonEl maps month index (0=Jan) → 时令五行 (decode.js SEASON_EL).
|
||||
var seasonEl = []string{"水", "水", "木", "木", "木", "火", "火", "火", "土", "金", "金", "水"}
|
||||
|
||||
type pillar struct{ tg, dz string }
|
||||
|
||||
type bazi struct {
|
||||
year, month, day, hour pillar
|
||||
}
|
||||
|
||||
// getBazi mirrors js/common.js getBazi (hour default 12 = 午时).
|
||||
// Day-pillar index uses civil days since 1900-01-01 minus 1, matching legacy
|
||||
// browser Date local-midnight flooring under historical CN TZ/DST (decode.js).
|
||||
func getBazi(y, m, d, h int) bazi {
|
||||
gzY := pillar{tg: tg[(y-4+10000)%10], dz: dz[(y-4+12000)%12]}
|
||||
birth := time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC)
|
||||
dayOfYear := birth.YearDay()
|
||||
lcDay := 35
|
||||
mi := int(float64((dayOfYear-lcDay+365)%365) / 30.44)
|
||||
if mi < 0 {
|
||||
mi += 12
|
||||
}
|
||||
if mi >= 12 {
|
||||
mi = 11
|
||||
}
|
||||
mTg := tg[(((y-4+10000)%10)*2+mi)%10]
|
||||
mDz := dz[(mi+2)%12]
|
||||
|
||||
base := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
dd := int(birth.Sub(base).Hours()/24) - 1
|
||||
dTg := tg[((dd%10)+10)%10]
|
||||
dDz := dz[((dd%12)+12)%12]
|
||||
hDz := dz[((h+1)/2)%12]
|
||||
hTg := tg[(indexOf(tg, dTg)*2+indexOf(dz, hDz))%10]
|
||||
return bazi{
|
||||
year: gzY,
|
||||
month: pillar{tg: mTg, dz: mDz},
|
||||
day: pillar{tg: dTg, dz: dDz},
|
||||
hour: pillar{tg: hTg, dz: hDz},
|
||||
}
|
||||
}
|
||||
|
||||
func calc5E(bz bazi) map[string]int {
|
||||
c := map[string]int{"木": 0, "火": 0, "土": 0, "金": 0, "水": 0}
|
||||
for _, p := range []pillar{bz.year, bz.month, bz.day, bz.hour} {
|
||||
c[tge[p.tg]]++
|
||||
c[dze[p.dz]]++
|
||||
}
|
||||
t := 0
|
||||
for _, e := range eo {
|
||||
t += c[e]
|
||||
}
|
||||
r := map[string]int{}
|
||||
for _, e := range eo {
|
||||
if t == 0 {
|
||||
r[e] = 0
|
||||
} else {
|
||||
r[e] = int(float64(c[e])/float64(t)*100 + 0.5) // Math.round
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func strongestEl(p map[string]int) string {
|
||||
best := eo[0]
|
||||
for _, e := range eo[1:] {
|
||||
if p[e] > p[best] {
|
||||
best = e
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
func weakestEl(p map[string]int) string {
|
||||
worst := eo[0]
|
||||
for _, e := range eo[1:] {
|
||||
if p[e] < p[worst] {
|
||||
worst = e
|
||||
}
|
||||
}
|
||||
return worst
|
||||
}
|
||||
|
||||
func wuxingBars(p map[string]int) []map[string]any {
|
||||
bars := make([]map[string]any, 0, 5)
|
||||
for _, e := range eo {
|
||||
bars = append(bars, map[string]any{
|
||||
"el": e, "pct": p[e], "color": ec[e],
|
||||
})
|
||||
}
|
||||
return bars
|
||||
}
|
||||
|
||||
func todayTip(now time.Time) map[string]any {
|
||||
st := companion.TodaySolar(now)
|
||||
el := seasonEl[int(now.Month())-1]
|
||||
yj := yiJi[el]
|
||||
return map[string]any{
|
||||
"solar_term": st.Name,
|
||||
"season_el": el,
|
||||
"yi": yj.Yi,
|
||||
"ji": yj.Ji,
|
||||
"tip": st.Tip,
|
||||
}
|
||||
}
|
||||
|
||||
func indexOf(arr []string, s string) int {
|
||||
for i, v := range arr {
|
||||
if v == s {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user