落地 synastry/star/ask API 与 H5 页面,补齐 cece-frontend-re complete-design 证据文档,并加入 Android 模拟器截图抓取脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
238 lines
5.2 KiB
Go
238 lines
5.2 KiB
Go
package portrait
|
|
|
|
// Triangle is the classic life-number pyramid from js/common.js calc().
|
|
type Triangle struct {
|
|
AB, CD, EF, GH [2]int
|
|
I, J, K, L int
|
|
M, N, O int
|
|
Q, P, R int
|
|
T, S, U int
|
|
V, W, X int
|
|
}
|
|
|
|
// Calc builds the digital psychology triangle for a birth date.
|
|
func Calc(y, m, d int) Triangle {
|
|
dd := digs(d, 2)
|
|
mm := digs(m, 2)
|
|
yy := digs(y, 4)
|
|
a, b := dd[0], dd[1]
|
|
c, dd1 := mm[0], mm[1]
|
|
e, f, g, h := yy[0], yy[1], yy[2], yy[3]
|
|
v := Triangle{
|
|
AB: [2]int{a, b},
|
|
CD: [2]int{c, dd1},
|
|
EF: [2]int{e, f},
|
|
GH: [2]int{g, h},
|
|
}
|
|
v.I = s1(a + b)
|
|
v.J = s1(c + dd1)
|
|
v.K = s1(e + f)
|
|
v.L = s1(g + h)
|
|
v.M = s1(v.I + v.J)
|
|
v.N = s1(v.K + v.L)
|
|
v.O = s1(v.M + v.N)
|
|
v.Q = s1(v.N + v.O)
|
|
v.P = s1(v.M + v.O)
|
|
v.R = s1(v.Q + v.P)
|
|
v.T = s1(v.I + v.M)
|
|
v.S = s1(v.J + v.M)
|
|
v.U = s1(v.T + v.S)
|
|
v.V = s1(v.K + v.N)
|
|
v.W = s1(v.L + v.N)
|
|
v.X = s1(v.V + v.W)
|
|
return v
|
|
}
|
|
|
|
// PyramidMap returns grid values for H5 mini-triangle rendering.
|
|
func (v Triangle) PyramidMap() map[string]int {
|
|
return map[string]int{
|
|
"I": v.I, "J": v.J, "K": v.K, "L": v.L,
|
|
"M": v.M, "N": v.N, "O": v.O,
|
|
"Q": v.Q, "P": v.P, "R": v.R,
|
|
"T": v.T, "S": v.S, "U": v.U,
|
|
"V": v.V, "W": v.W, "X": v.X,
|
|
}
|
|
}
|
|
|
|
func s1(n int) int {
|
|
for n >= 10 {
|
|
sum := 0
|
|
for n > 0 {
|
|
sum += n % 10
|
|
n /= 10
|
|
}
|
|
n = sum
|
|
}
|
|
return n
|
|
}
|
|
|
|
func digs(n, l int) []int {
|
|
s := fmtPad(n, l)
|
|
out := make([]int, l)
|
|
for i := 0; i < l; i++ {
|
|
out[i] = int(s[i] - '0')
|
|
}
|
|
return out
|
|
}
|
|
|
|
func fmtPad(n, l int) string {
|
|
if n < 0 {
|
|
n = -n
|
|
}
|
|
buf := make([]byte, l)
|
|
for i := l - 1; i >= 0; i-- {
|
|
buf[i] = byte('0' + n%10)
|
|
n /= 10
|
|
}
|
|
return string(buf)
|
|
}
|
|
|
|
func cell(v Triangle, key string) int {
|
|
switch key {
|
|
case "I":
|
|
return v.I
|
|
case "J":
|
|
return v.J
|
|
case "K":
|
|
return v.K
|
|
case "L":
|
|
return v.L
|
|
case "M":
|
|
return v.M
|
|
case "N":
|
|
return v.N
|
|
case "O":
|
|
return v.O
|
|
case "Q":
|
|
return v.Q
|
|
case "P":
|
|
return v.P
|
|
case "R":
|
|
return v.R
|
|
case "T":
|
|
return v.T
|
|
case "S":
|
|
return v.S
|
|
case "U":
|
|
return v.U
|
|
case "V":
|
|
return v.V
|
|
case "W":
|
|
return v.W
|
|
case "X":
|
|
return v.X
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// jointGroups builds age-grouped joint codes (付费层).
|
|
func jointGroups(v Triangle) []map[string]any {
|
|
pairs := [][2]string{
|
|
{"I", "J"}, {"J", "M"}, {"M", "I"}, {"K", "L"},
|
|
{"L", "N"}, {"N", "K"}, {"M", "O"}, {"O", "N"},
|
|
{"M", "N"}, {"Q", "P"}, {"Q", "O"}, {"P", "O"},
|
|
}
|
|
joints := make([]map[string]any, 0, len(pairs))
|
|
for _, p := range pairs {
|
|
code := fmtInt(cell(v, p[0])) + fmtInt(cell(v, p[1]))
|
|
label := "—"
|
|
for k, desc := range jointCodes {
|
|
if len(k) >= len(code) && k[:len(code)] == code {
|
|
label = desc
|
|
break
|
|
}
|
|
}
|
|
joints = append(joints, map[string]any{"code": code, "label": label})
|
|
}
|
|
g1Desc := "由潜意识码主导,反映你在职场上升期和社交圈中的表现模式。"
|
|
if v.I+v.J+v.M >= 10 {
|
|
g1Desc += "能量较强,事业心旺盛,社交活跃。"
|
|
} else {
|
|
g1Desc += "能量内敛,更注重深度而非广度。"
|
|
}
|
|
g2Desc := "由内心码和主性格主导,体现你对下一代和下级的培养方式及中年价值观。"
|
|
if v.K+v.L+v.N >= 10 {
|
|
g2Desc += "有较强的责任感和支配欲。"
|
|
} else {
|
|
g2Desc += "倾向于给予空间,放手让他人成长。"
|
|
}
|
|
g3Desc := "由外心码主导,展现你晚年的生活智慧和家庭关系的最终呈现。"
|
|
if v.M+v.N+v.O >= 10 {
|
|
g3Desc += "晚年活跃,持续发挥影响力。"
|
|
} else {
|
|
g3Desc += "晚年宁静,享受内在平和。"
|
|
}
|
|
return []map[string]any{
|
|
{"label": "21—40岁 · 工作和朋友", "codes": joints[0:4], "desc": g1Desc},
|
|
{"label": "41—60岁 · 儿女和下属", "codes": joints[4:8], "desc": g2Desc},
|
|
{"label": "61岁至晚年 · 晚年和家庭", "codes": joints[8:12], "desc": g3Desc},
|
|
}
|
|
}
|
|
|
|
func missAndOver(v Triangle) (missing []map[string]any, over []map[string]any) {
|
|
innerSet := map[int]bool{v.O: true, v.M: true, v.N: true, v.I: true, v.J: true, v.K: true, v.L: true}
|
|
outerVals := []int{v.Q, v.P, v.R, v.T, v.S, v.U, v.V, v.W, v.X}
|
|
present := map[int]bool{}
|
|
for n := range innerSet {
|
|
present[n] = true
|
|
}
|
|
for _, n := range outerVals {
|
|
present[n] = true
|
|
}
|
|
for _, n := range []int{v.AB[0], v.AB[1], v.CD[0], v.CD[1], v.EF[0], v.EF[1], v.GH[0], v.GH[1]} {
|
|
present[n] = true
|
|
}
|
|
freq := map[int]int{}
|
|
for i := 1; i <= 9; i++ {
|
|
freq[i] = 0
|
|
}
|
|
allVals := []int{
|
|
v.O, v.M, v.N, v.I, v.J, v.K, v.L, v.Q, v.P, v.R, v.T, v.S, v.U, v.V, v.W, v.X,
|
|
v.AB[0], v.AB[1], v.CD[0], v.CD[1], v.EF[0], v.EF[1], v.GH[0], v.GH[1],
|
|
}
|
|
for _, x := range allVals {
|
|
if x >= 1 && x <= 9 {
|
|
freq[x]++
|
|
}
|
|
}
|
|
missBoth := []int{}
|
|
for n := 0; n <= 9; n++ {
|
|
if !present[n] {
|
|
missBoth = append(missBoth, n)
|
|
}
|
|
}
|
|
missSet := map[int]bool{}
|
|
for _, n := range missBoth {
|
|
missSet[n] = true
|
|
if n >= 1 && n <= 9 {
|
|
missing = append(missing, map[string]any{"n": n, "msg": missMsg[n]})
|
|
}
|
|
}
|
|
for i := 1; i <= 9; i++ {
|
|
if freq[i] >= 4 && !missSet[i] {
|
|
over = append(over, map[string]any{"n": i, "msg": missStrong[i], "count": freq[i]})
|
|
}
|
|
}
|
|
return missing, over
|
|
}
|
|
|
|
func fmtInt(n int) string {
|
|
if n < 0 {
|
|
n = -n
|
|
}
|
|
if n < 10 {
|
|
return string(rune('0' + n))
|
|
}
|
|
return fmtPad(n, 2)
|
|
}
|
|
|
|
func firstSentence(s string) string {
|
|
for i, r := range s {
|
|
if r == '。' || r == '.' {
|
|
return s[:i]
|
|
}
|
|
}
|
|
return s
|
|
}
|