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:
@@ -1,5 +1,4 @@
|
||||
// Package portrait builds deterministic personal-portrait content from birth date.
|
||||
// Copy follows .ai/product/lexicon.md — exploration / analysis, not fortune-telling.
|
||||
// Package portrait builds 愈心解码 content from birth date (classic decode engine).
|
||||
package portrait
|
||||
|
||||
import (
|
||||
@@ -13,134 +12,202 @@ type Output struct {
|
||||
Detail map[string]any `json:"detail"`
|
||||
}
|
||||
|
||||
// Build generates 个人画像 content from a birth date (deterministic).
|
||||
// Build generates 愈心解码 content (deterministic for a given birth date).
|
||||
func Build(birth time.Time, displayName string) Output {
|
||||
y, m, d := birth.Date()
|
||||
num := reduce(y) + reduce(int(m)) + reduce(d)
|
||||
for num > 9 {
|
||||
num = reduce(num)
|
||||
}
|
||||
trait := traits[num%len(traits)]
|
||||
tri := Calc(y, int(m), d)
|
||||
main := tri.O
|
||||
person := numberPeople[main]
|
||||
name := displayName
|
||||
if name == "" {
|
||||
name = "你"
|
||||
}
|
||||
oneLiner := firstSentence(person.Desc)
|
||||
if oneLiner == "" {
|
||||
oneLiner = person.Title
|
||||
}
|
||||
|
||||
bz := getBazi(y, int(m), d, 12)
|
||||
pct := calc5E(bz)
|
||||
strong := strongestEl(pct)
|
||||
weak := weakestEl(pct)
|
||||
cst := constitutionByElement[strong]
|
||||
emo := emotionByElement[strong]
|
||||
|
||||
dims := []map[string]any{
|
||||
{"key": "personality", "title": "性格特点", "teaser": oneLiner, "score": dimScore(main, 0)},
|
||||
{"key": "communication", "title": "沟通方式", "teaser": teaserCut(oneLiner, 28), "score": dimScore(main, 1)},
|
||||
{"key": "relation", "title": "关系模式", "teaser": "深度关系模式见完整分析", "score": dimScore(main, 2)},
|
||||
{"key": "career", "title": "事业节奏", "teaser": "适合方向见深度版", "score": dimScore(main, 3)},
|
||||
{"key": "emotion", "title": "情绪调节", "teaser": joinCN(emo.Strength, 3), "score": dimScore(main, 4)},
|
||||
{"key": "lifestyle", "title": "生活节奏", "teaser": "核心养护:养" + cst.Strong + " · 需呵护" + cst.Weak, "score": dimScore(main, 5)},
|
||||
}
|
||||
|
||||
keywords := []string{fmt.Sprintf("%d号人", main), person.Elem + "行倾向", cst.Primary}
|
||||
if len(emo.Strength) > 0 {
|
||||
keywords = append(keywords, emo.Strength[0])
|
||||
}
|
||||
|
||||
tip := todayTip(time.Now())
|
||||
missing, over := missAndOver(tri)
|
||||
|
||||
summary := map[string]any{
|
||||
"title": "基础画像",
|
||||
"headline": fmt.Sprintf("%s更偏「%s」的互动风格", name, trait.Label),
|
||||
"keywords": trait.Keywords,
|
||||
"one_liner": trait.OneLiner,
|
||||
"life_tip": trait.LifeTip,
|
||||
"pattern_key": num,
|
||||
"title": "愈心解码·基础",
|
||||
"main_number": main,
|
||||
"person_title": person.Title,
|
||||
"style_label": person.Title,
|
||||
"style_badge": fmt.Sprintf("%d号人", main),
|
||||
"headline": fmt.Sprintf("%s是%d号人 · %s", name, main, person.Title),
|
||||
"keywords": keywords,
|
||||
"one_liner": oneLiner,
|
||||
"overview": oneLiner,
|
||||
"life_tip": emo.Advice,
|
||||
"today_practice": emo.Advice,
|
||||
// 模糊锁占位(非完整深文案,完整内容仅在 detail)
|
||||
"lock_teaser_number": "天赋优势与人生课题、适合方向与能量长文,解锁后查看。联合码按生命阶段分组解读。",
|
||||
"lock_teaser_wuxing": "体质特征、调养要点、易感倾向、食疗与作息节律,解锁后查看完整方案。",
|
||||
"pattern_key": main,
|
||||
"pyramid": tri.PyramidMap(),
|
||||
"wuxing": map[string]any{
|
||||
"bars": wuxingBars(pct),
|
||||
"primary": cst.Primary,
|
||||
"strong": cst.Strong,
|
||||
"weak": cst.Weak,
|
||||
"strong_el": strong,
|
||||
"weak_el": weak,
|
||||
"emotion": emo.Strength,
|
||||
"care_dir": "养" + cst.Strong + " · 需呵护" + cst.Weak,
|
||||
},
|
||||
"today_tip": tip,
|
||||
"dimensions": dims,
|
||||
"strengths_preview": []string{
|
||||
fmt.Sprintf("%d号人·%s", main, person.Title),
|
||||
cst.Primary,
|
||||
},
|
||||
"blind_spots_preview": []string{"完整天赋优势、人生课题、联合码与体质深析见深度版。"},
|
||||
"disclaimer_constitution": "体质与调养内容为生活方式参考,非医疗建议。",
|
||||
}
|
||||
|
||||
numberDeep := map[string]any{
|
||||
"talent": person.Pos,
|
||||
"topic": person.Neg,
|
||||
"direction": person.Job,
|
||||
"elem": person.Elem,
|
||||
"desc": person.Desc,
|
||||
"missing": missing,
|
||||
"over": over,
|
||||
"joints": jointGroups(tri),
|
||||
}
|
||||
constitutionDeep := map[string]any{
|
||||
"desc": cst.Desc,
|
||||
"body": cst.Body,
|
||||
"tips": cst.Tips,
|
||||
"risk": cst.Diseases,
|
||||
"season_risk": cst.SeasonRisk,
|
||||
"food": cst.FoodTherapy,
|
||||
"rhythm": cst.DailyRhythm,
|
||||
"primary": cst.Primary,
|
||||
"secondary": cst.Secondary,
|
||||
"emotion_risk": emo.Risk,
|
||||
"emotion_advice": emo.Advice,
|
||||
}
|
||||
|
||||
detail := map[string]any{
|
||||
"title": "完整分析",
|
||||
"behavior_pattern": trait.Behavior,
|
||||
"relation_style": trait.Relation,
|
||||
"growth_direction": trait.Growth,
|
||||
"daily_suggestions": []string{
|
||||
trait.LifeTip,
|
||||
"遇到分歧时,先复述对方观点再表达自己的需要。",
|
||||
"用一周记录情绪与精力高峰,找到更适合自己的节奏。",
|
||||
"title": "完整分析",
|
||||
"number_deep": numberDeep,
|
||||
"constitution_deep": constitutionDeep,
|
||||
"sections": []map[string]any{
|
||||
section("天赋与课题", person.Desc, []string{"天赋优势:" + person.Pos, "人生课题:" + person.Neg, "适合方向:" + person.Job}),
|
||||
section("体质特征", cst.Body, []string{cst.Tips}),
|
||||
section("易感与季节", cst.Diseases, []string{cst.SeasonRisk}),
|
||||
section("食疗方向", cst.FoodTherapy, nil),
|
||||
section("作息节律", cst.DailyRhythm, nil),
|
||||
section("情绪调养", emo.Advice, emo.Risk),
|
||||
},
|
||||
"behavior_pattern": person.Desc,
|
||||
"relation_style": person.Neg,
|
||||
"growth_direction": person.Pos,
|
||||
"strengths": splitComma(person.Pos),
|
||||
"blind_spots": splitComma(person.Neg),
|
||||
"growth_plan": []map[string]any{
|
||||
{"phase": "本周", "focus": emo.Advice},
|
||||
{"phase": "本月", "focus": cst.Tips},
|
||||
{"phase": "长期", "focus": "结合数字课题「" + person.Neg + "」与体质养护「养" + cst.Strong + "」,形成可持续的生活节奏。"},
|
||||
},
|
||||
"conversation_scripts": []string{
|
||||
"我想聊聊自己的节奏,不用急着给建议。",
|
||||
"当我压力大时,需要一点安静空间再继续聊。",
|
||||
"帮我一起看看,哪件事最值得这周聚焦。",
|
||||
},
|
||||
"daily_suggestions": []string{strAny(tip["yi"]), emo.Advice, "核心养护:养" + cst.Strong},
|
||||
"faq": []map[string]string{
|
||||
{"q": "数字性格是固定的吗?", "a": "主数提供稳定的自我探索框架,具体表现会随阶段与环境变化,可当作镜子而非标签。"},
|
||||
{"q": "五行体质能当看病依据吗?", "a": "不能。这里的体质与调养只是生活方式参考,不适请就医,勿自行当作治疗方案。"},
|
||||
},
|
||||
}
|
||||
return Output{Summary: summary, Detail: detail}
|
||||
}
|
||||
|
||||
type trait struct {
|
||||
Label string
|
||||
Keywords []string
|
||||
OneLiner string
|
||||
LifeTip string
|
||||
Behavior string
|
||||
Relation string
|
||||
Growth string
|
||||
func section(title, body string, bullets []string) map[string]any {
|
||||
return map[string]any{"title": title, "body": body, "bullets": bullets}
|
||||
}
|
||||
|
||||
var traits = []trait{
|
||||
{
|
||||
Label: "稳健探索", Keywords: []string{"条理", "耐心", "观察"},
|
||||
OneLiner: "你习惯先理解再行动,适合把复杂事情拆成小步。",
|
||||
LifeTip: "今天给自己一段不被打断的专注时间。",
|
||||
Behavior: "决策偏审慎,信息充分时执行力更强;压力下可能拖延。",
|
||||
Relation: "更愿意用行动表达关心,需要对方给予明确反馈。",
|
||||
Growth: "练习在信息不完整时做小步试验,积累行动信心。",
|
||||
},
|
||||
{
|
||||
Label: "热情连接", Keywords: []string{"表达", "共鸣", "主动"},
|
||||
OneLiner: "你容易带动气氛,也需要被真诚回应。",
|
||||
LifeTip: "把想说的话写下来,再选择合适的时机分享。",
|
||||
Behavior: "行动快、反馈敏感;情绪起伏会影响专注时长。",
|
||||
Relation: "重视即时沟通,冷处理容易让你感到不安。",
|
||||
Growth: "学会区分「被回应」与「被认同」,减少过度解读。",
|
||||
},
|
||||
{
|
||||
Label: "理性澄清", Keywords: []string{"分析", "边界", "清晰"},
|
||||
OneLiner: "你擅长把模糊感受变成可讨论的问题。",
|
||||
LifeTip: "睡前做一次简短复盘:今天最有价值的一件事是什么。",
|
||||
Behavior: "偏好逻辑框架;在情绪场域可能显得抽离。",
|
||||
Relation: "沟通时需要结构和具体例子,忌空泛安慰。",
|
||||
Growth: "在分析之外,练习先接纳情绪再谈方案。",
|
||||
},
|
||||
{
|
||||
Label: "柔韧调节", Keywords: []string{"适应", "体察", "平衡"},
|
||||
OneLiner: "你善于照顾氛围,也别忘了照顾自己的节奏。",
|
||||
LifeTip: "安排一次轻度活动,帮助身心回到平稳状态。",
|
||||
Behavior: "弹性强,容易迁就;长期可能积压需求。",
|
||||
Relation: "更在意关系和谐,冲突时倾向先安抚场面。",
|
||||
Growth: "练习用「我需要…」表达边界,而不是只做协调者。",
|
||||
},
|
||||
{
|
||||
Label: "目标推进", Keywords: []string{"决断", "效率", "成果"},
|
||||
OneLiner: "你推动事情落地的能力突出,记得留白给休息。",
|
||||
LifeTip: "把今日目标收束到一件最重要的事。",
|
||||
Behavior: "结果导向,节奏偏快;对低效协作耐心有限。",
|
||||
Relation: "欣赏直接沟通的人,含糊表态会消耗信任。",
|
||||
Growth: "把「效率」与「关系维护」列为同等优先级的周目标。",
|
||||
},
|
||||
{
|
||||
Label: "内观沉淀", Keywords: []string{"深度", "独立", "洞察"},
|
||||
OneLiner: "你习惯向内理解世界,适合深度思考类任务。",
|
||||
LifeTip: "留出安静独处时间,整理近期的想法与感受。",
|
||||
Behavior: "思考深入,对外表达可能滞后于内心结论。",
|
||||
Relation: "需要安全感和节奏感,突然施压会让你退回内在。",
|
||||
Growth: "把洞察翻译成可分享的语言,让重要的人跟上你。",
|
||||
},
|
||||
{
|
||||
Label: "创意发散", Keywords: []string{"想象", "灵感", "可能"},
|
||||
OneLiner: "你容易看到多种可能性,适合用原型验证想法。",
|
||||
LifeTip: "用纸笔画出今天最想尝试的一个小实验。",
|
||||
Behavior: "点子多、切换快;收尾与复盘是短板。",
|
||||
Relation: "喜欢有趣的对话,重复与僵化会让你疏离。",
|
||||
Growth: "为每个灵感设定「最小完成定义」,提高闭环率。",
|
||||
},
|
||||
{
|
||||
Label: "责任担当", Keywords: []string{"可靠", "承诺", "稳定"},
|
||||
OneLiner: "你重视承诺与秩序,是团队里让人安心的存在。",
|
||||
LifeTip: "检查一下是否把别人的期待误当成自己的必须。",
|
||||
Behavior: "可靠且自律;过度负责时容易耗竭。",
|
||||
Relation: "用持续在场表达在乎,需要被看见付出。",
|
||||
Growth: "练习委托与求助,让支持系统真正运转起来。",
|
||||
},
|
||||
{
|
||||
Label: "敏锐觉察", Keywords: []string{"细腻", "直觉", "体贴"},
|
||||
OneLiner: "你对情绪与细节敏感,适合需要同理的场景。",
|
||||
LifeTip: "觉察身体信号:紧张时先放慢呼吸再回应。",
|
||||
Behavior: "感知力强;信息过载时容易内耗。",
|
||||
Relation: "能很快读到对方状态,也易被情绪感染。",
|
||||
Growth: "建立「感受—事实—选择」三步,减少被情绪牵着走。",
|
||||
},
|
||||
func dimScore(main, salt int) int {
|
||||
// Stable pseudo-scores 62–92 for relation dimension compare.
|
||||
return 62 + (main*7+salt*11)%31
|
||||
}
|
||||
|
||||
func reduce(n int) int {
|
||||
if n < 0 {
|
||||
n = -n
|
||||
func teaserCut(s string, n int) string {
|
||||
r := []rune(s)
|
||||
if len(r) <= n {
|
||||
return s
|
||||
}
|
||||
sum := 0
|
||||
for n > 0 {
|
||||
sum += n % 10
|
||||
n /= 10
|
||||
}
|
||||
return sum
|
||||
return string(r[:n]) + "…"
|
||||
}
|
||||
|
||||
func joinCN(parts []string, n int) string {
|
||||
if len(parts) == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(parts) < n {
|
||||
n = len(parts)
|
||||
}
|
||||
out := parts[0]
|
||||
for i := 1; i < n; i++ {
|
||||
out += " · " + parts[i]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func splitComma(s string) []string {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
// pos/neg use顿号
|
||||
parts := []string{}
|
||||
cur := ""
|
||||
for _, r := range s {
|
||||
if r == '、' || r == ',' || r == ',' {
|
||||
if cur != "" {
|
||||
parts = append(parts, cur)
|
||||
cur = ""
|
||||
}
|
||||
continue
|
||||
}
|
||||
cur += string(r)
|
||||
}
|
||||
if cur != "" {
|
||||
parts = append(parts, cur)
|
||||
}
|
||||
if len(parts) > 3 {
|
||||
return parts[:3]
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
func strAny(v any) string {
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user