Second P1 growth engine: compare two profiles, gate full tips behind deep-access mock payment, and list personal archives on /profile. Co-authored-by: Cursor <cursoragent@cursor.com>
84 lines
2.3 KiB
Go
84 lines
2.3 KiB
Go
// Package relation builds 关系理解 content from two birth-based portraits.
|
|
package relation
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/yuxingu/digital-psychology/apps/api/internal/portrait"
|
|
)
|
|
|
|
// Output is free summary + gated detail.
|
|
type Output struct {
|
|
Summary map[string]any
|
|
Detail map[string]any
|
|
}
|
|
|
|
// Build compares two profiles' exploration styles (lexicon-safe copy).
|
|
func Build(aBirth, bBirth time.Time, aName, bName string) Output {
|
|
if aName == "" {
|
|
aName = "我"
|
|
}
|
|
if bName == "" {
|
|
bName = "TA"
|
|
}
|
|
pa := portrait.Build(aBirth, aName)
|
|
pb := portrait.Build(bBirth, bName)
|
|
aLabel := str(pa.Summary["headline"])
|
|
bLabel := str(pb.Summary["headline"])
|
|
aKeys := strSlice(pa.Summary["keywords"])
|
|
bKeys := strSlice(pb.Summary["keywords"])
|
|
|
|
summary := map[string]any{
|
|
"title": "关系理解·基础",
|
|
"me_style": aLabel,
|
|
"other_style": bLabel,
|
|
"me_keywords": aKeys,
|
|
"other_keywords": bKeys,
|
|
"diff_one_liner": fmt.Sprintf("%s更偏内在节奏,%s的表达方式不同——差异可以成为互补,而不是对立。", aName, bName),
|
|
"share_hint": "了解彼此的沟通方式,查看关系理解",
|
|
}
|
|
detail := map[string]any{
|
|
"title": "相处建议·完整分析",
|
|
"communication": []string{
|
|
fmt.Sprintf("%s:先讲清楚事实与需要,再谈感受。", aName),
|
|
fmt.Sprintf("%s:先确认被听见,再进入方案讨论。", bName),
|
|
"冲突时约定「复述对方一句」再回应,降低误解。",
|
|
},
|
|
"interaction": []string{
|
|
"用「我观察到…我需要…」代替指责句式。",
|
|
"每周留一次轻松同步:最近什么在消耗/滋养这段关系。",
|
|
},
|
|
"maintenance": []string{
|
|
"差异标签不是评分,而是协作说明书。",
|
|
"重要决定前,双方各写三点顾虑再合并。",
|
|
},
|
|
"me_behavior": str(pa.Detail["behavior_pattern"]),
|
|
"other_behavior": str(pb.Detail["behavior_pattern"]),
|
|
}
|
|
return Output{Summary: summary, Detail: detail}
|
|
}
|
|
|
|
func str(v any) string {
|
|
s, _ := v.(string)
|
|
return s
|
|
}
|
|
|
|
func strSlice(v any) []string {
|
|
arr, ok := v.([]string)
|
|
if ok {
|
|
return arr
|
|
}
|
|
raw, ok := v.([]any)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
out := make([]string, 0, len(raw))
|
|
for _, x := range raw {
|
|
if s, ok := x.(string); ok {
|
|
out = append(out, s)
|
|
}
|
|
}
|
|
return out
|
|
}
|