dsh-metaphysics 0.67.0 → 0.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2158 -2072
- package/README.md +16 -9
- package/client.almanac.js +73 -67
- package/client.chart.js +152 -146
- package/client.chenggu.js +73 -67
- package/client.constitution.js +154 -0
- package/client.convert.js +67 -61
- package/client.daliuren.js +141 -135
- package/client.fengshui.js +66 -60
- package/client.js +457 -310
- package/client.liuyao.js +141 -135
- package/client.mbti.js +74 -68
- package/client.name.js +78 -72
- package/client.numerology.js +74 -68
- package/client.qimen.js +130 -124
- package/client.star.js +73 -67
- package/client.taiyi.js +154 -0
- package/client.tarot.js +135 -129
- package/client.wuyunliuqi.js +154 -0
- package/client.xiaoliuren.js +71 -65
- package/client.zeri.js +60 -54
- package/engine/bridge.js +3 -0
- package/engine/constitution.js +324 -0
- package/engine/index.js +109 -8
- package/engine/liuyao.js +1 -1
- package/engine/plain-summary.js +101 -0
- package/engine/random.js +32 -7
- package/engine/taiyi.js +216 -0
- package/engine/tarot.js +77 -8
- package/engine/wuyunliuqi.js +426 -0
- package/index.js +25 -13
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +2 -2
- package//345/221/275/347/220/206/346/216/222/347/233/230.html +1791 -42
package/engine/bridge.js
CHANGED
|
@@ -26,6 +26,9 @@ const SYSTEM_LABEL = {
|
|
|
26
26
|
qimen: '奇门', hepan: '合盘', tarot: '塔罗', almanac: '黄历', zeri: '择日',
|
|
27
27
|
chenggu: '称骨', xiaoliuren: '小六壬', star: '星座', numerology: '生命数字',
|
|
28
28
|
mbti: '人格', trend: '人生K线', fengshui: '风水', name: '起名', convert: '农历',
|
|
29
|
+
constitution: '体质',
|
|
30
|
+
wuyunliuqi: '运气',
|
|
31
|
+
taiyi: '太乙',
|
|
29
32
|
};
|
|
30
33
|
|
|
31
34
|
const labelOf = (system) => SYSTEM_LABEL[system] || system || '未知体系';
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
// 中医体质(九种体质):按中华中医药学会《中医体质分类与判定》的条目结构做的简化问卷——
|
|
2
|
+
// 九个体质各 5 条自评题,1–5 计分,用「转化分」判主次体质。
|
|
3
|
+
//
|
|
4
|
+
// 口径(沿用通行标准,条目数是压缩过的):
|
|
5
|
+
// 转化分 = [(原始分 − 条目数) / (条目数 × 4)] × 100
|
|
6
|
+
// 偏颇体质:≥40 为「是」,30–39 为「有倾向」,<30 为「否」
|
|
7
|
+
// 平和质:≥60 且其余八种都 <30 为「是」;≥60 且其余都 <40 为「基本是」
|
|
8
|
+
// 题目与阈值取自公开标准的结构,文案为白话重写;判定只作自我观察的参照,不是医学诊断。
|
|
9
|
+
import { fail } from './fail.js';
|
|
10
|
+
|
|
11
|
+
const ITEMS = 5;
|
|
12
|
+
|
|
13
|
+
/** 九种体质:判定题 + 表现 + 调养方向。 */
|
|
14
|
+
export const CONSTITUTIONS = [
|
|
15
|
+
{
|
|
16
|
+
key: 'pinghe',
|
|
17
|
+
name: '平和质',
|
|
18
|
+
oneLine: '阴阳气血相对匀,吃得下、睡得着、恢复快。',
|
|
19
|
+
trait: '精力够用,耐受冷热,情绪起伏不大,睡醒后能缓过来。',
|
|
20
|
+
tendency: '这种底子最省心,重点是别自己把它折腾坏。',
|
|
21
|
+
care: [
|
|
22
|
+
'食:什么都能吃一点,别追一个味道到底;七分饱比补品有用。',
|
|
23
|
+
'起居:睡点固定比睡够时长更关键,熬夜是最快的消耗方式。',
|
|
24
|
+
'动:每周三四次微汗即可,不必练到力竭。',
|
|
25
|
+
'情志:留一点「什么都不干」的时间,别把日程排满。',
|
|
26
|
+
],
|
|
27
|
+
questions: [
|
|
28
|
+
'你精力充沛,不太容易觉得累',
|
|
29
|
+
'你能耐受冷也能耐受热,换季不太难受',
|
|
30
|
+
'你胃口稳定,吃完不胀不反酸',
|
|
31
|
+
'你睡得踏实,醒来精神能回来',
|
|
32
|
+
'你情绪起伏不大,遇事能较快平复',
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
key: 'qixu',
|
|
37
|
+
name: '气虚质',
|
|
38
|
+
oneLine: '气不够用:容易累、说话没劲、稍动就出汗。',
|
|
39
|
+
trait: '容易疲乏、气短、说话声音低,活动后出汗多,容易感冒。',
|
|
40
|
+
tendency: '不是懒,是电量掉得快;硬撑只会更空。',
|
|
41
|
+
care: [
|
|
42
|
+
'食:主食别减太狠,山药、小米、黄豆这类平补的东西比猛补更合适。',
|
|
43
|
+
'起居:宁可早睡半小时,也别靠咖啡顶着;午间闭眼十分钟有用。',
|
|
44
|
+
'动:散步、八段锦这类「不喘」的运动优先,别一上来就高强度。',
|
|
45
|
+
'情志:少做长时间的强撑式社交,累了就直说。',
|
|
46
|
+
],
|
|
47
|
+
questions: [
|
|
48
|
+
'你容易疲乏,休息后也不太容易缓过来',
|
|
49
|
+
'你说话声音偏低,说久了觉得没气',
|
|
50
|
+
'你稍一活动就比别人容易出汗',
|
|
51
|
+
'你容易感冒,或者感冒后拖得久',
|
|
52
|
+
'你吃完饭容易腹胀、消化慢',
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
key: 'yangxu',
|
|
57
|
+
name: '阳虚质',
|
|
58
|
+
oneLine: '火力不足:怕冷、手脚凉、喜欢热的。',
|
|
59
|
+
trait: '畏寒怕冷、手脚发凉,吃凉的容易不舒服,大便偏稀,精神偏静。',
|
|
60
|
+
tendency: '不是矫情,是产热不够;冷饮和空调直吹对这个体质最伤。',
|
|
61
|
+
care: [
|
|
62
|
+
'食:热的、熟的优先;姜、羊肉、桂圆可以吃,但别一顿猛补。',
|
|
63
|
+
'起居:腰腹和脚要保暖,睡前泡脚比吃补药直接。',
|
|
64
|
+
'动:白天动起来产热,晒背十分钟比闷在屋里强。',
|
|
65
|
+
'情志:冬天与阴雨天情绪容易低,把户外时间挪到中午。',
|
|
66
|
+
],
|
|
67
|
+
questions: [
|
|
68
|
+
'你比一般人怕冷,别人不觉得冷时你已经要加衣服',
|
|
69
|
+
'你的手脚常年偏凉',
|
|
70
|
+
'你喝冷饮或吃凉的东西后容易不舒服',
|
|
71
|
+
'你大便偏稀,或者一受凉就腹泻',
|
|
72
|
+
'你精神偏静,不太想动',
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: 'yinxu',
|
|
77
|
+
name: '阴虚质',
|
|
78
|
+
oneLine: '水不够:口干、手心热、睡不沉。',
|
|
79
|
+
trait: '口燥咽干、手足心热、容易上火、睡浅多梦、大便偏干。',
|
|
80
|
+
tendency: '不是火气大,是水少;越熬夜越明显。',
|
|
81
|
+
care: [
|
|
82
|
+
'食:银耳、百合、梨、黑芝麻这类润的;辛辣烧烤少碰。',
|
|
83
|
+
'起居:熬夜对这个体质伤害最直接,十一点前躺下比什么方子都强。',
|
|
84
|
+
'动:别在正午暴晒下猛练,选傍晚、选温和的。',
|
|
85
|
+
'情志:容易急、容易烦,发现自己在赶时间就先停三分钟。',
|
|
86
|
+
],
|
|
87
|
+
questions: [
|
|
88
|
+
'你常觉得口干、咽干,想喝水',
|
|
89
|
+
'你手足心发热,或者下午容易烘热',
|
|
90
|
+
'你容易上火,口腔溃疡或脸上起东西',
|
|
91
|
+
'你睡得不沉,多梦,容易醒',
|
|
92
|
+
'你容易大便干结,排便不太顺畅',
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
key: 'tanshi',
|
|
97
|
+
name: '痰湿质',
|
|
98
|
+
oneLine: '水湿停着走不动:身重、腹胀、容易困。',
|
|
99
|
+
trait: '身体沉重、腹部松软肥满、容易困倦、口中黏腻、痰多。',
|
|
100
|
+
tendency: '不是吃太多,是运化不动;久坐加甜腻会加重。',
|
|
101
|
+
care: [
|
|
102
|
+
'食:少甜少油少夜宵;薏米、赤小豆、冬瓜这类利湿的可以常吃。',
|
|
103
|
+
'起居:别吃完就躺;房间保持干燥通风。',
|
|
104
|
+
'动:出汗是重点,快走、跳绳这类能真出汗的优先。',
|
|
105
|
+
'情志:困倦时容易拖,把要紧事放在上午做。',
|
|
106
|
+
],
|
|
107
|
+
questions: [
|
|
108
|
+
'你常觉得身体沉重、不清爽',
|
|
109
|
+
'你腹部松软,容易胀',
|
|
110
|
+
'你容易困倦,睡够了也昏沉',
|
|
111
|
+
'你口中黏腻,或者痰偏多',
|
|
112
|
+
'你脸上或头发容易出油',
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
key: 'shire',
|
|
117
|
+
name: '湿热质',
|
|
118
|
+
oneLine: '又湿又热:脸油、口苦、容易长东西。',
|
|
119
|
+
trait: '面垢油光、容易生痤疮、口苦口干、大便黏滞、小便偏黄。',
|
|
120
|
+
tendency: '不是「火大」这么简单,湿裹着热,清得太猛反而伤脾。',
|
|
121
|
+
care: [
|
|
122
|
+
'食:少酒少辣少油炸;绿豆、冬瓜、苦瓜可以吃,但别长期只吃寒凉。',
|
|
123
|
+
'起居:别熬夜加夜宵,这两样凑一起最容易出湿热。',
|
|
124
|
+
'动:规律出汗,运动后及时擦干换衣。',
|
|
125
|
+
'情志:急躁时先降一件事的优先级,别同时开五个头。',
|
|
126
|
+
],
|
|
127
|
+
questions: [
|
|
128
|
+
'你脸上或头皮容易出油',
|
|
129
|
+
'你容易长痘、长疖子或起湿疹',
|
|
130
|
+
'你常觉得口苦或口干',
|
|
131
|
+
'你大便黏滞,不容易冲干净',
|
|
132
|
+
'你的小便颜色偏黄、量偏少',
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
key: 'xueyu',
|
|
137
|
+
name: '血瘀质',
|
|
138
|
+
oneLine: '血行不畅:肤色暗、易有瘀青、痛处固定。',
|
|
139
|
+
trait: '面色偏暗、口唇偏紫、容易出现瘀斑、身体某处固定刺痛。',
|
|
140
|
+
tendency: '久坐不动和长期受寒是两大来源。',
|
|
141
|
+
care: [
|
|
142
|
+
'食:山楂、黑木耳、玫瑰花这类活血的可以常吃;经期量多时别过量。',
|
|
143
|
+
'起居:别久坐,每小时起来走两分钟。',
|
|
144
|
+
'动:有氧为主,练到微微出汗;拉伸比猛练更对症。',
|
|
145
|
+
'情志:闷着不说话最容易瘀,找个人把话说完。',
|
|
146
|
+
],
|
|
147
|
+
questions: [
|
|
148
|
+
'你面色或口唇偏暗、偏紫',
|
|
149
|
+
'你身上容易出现瘀青,磕一下就好久不退',
|
|
150
|
+
'你身体某处有固定的刺痛或胀痛',
|
|
151
|
+
'你皮肤偏干,或者容易起红血丝',
|
|
152
|
+
'你记性下降,或者常常觉得「脑子转不动」',
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
key: 'qiyu',
|
|
157
|
+
name: '气郁质',
|
|
158
|
+
oneLine: '气堵着不顺:胸闷、爱叹气、情绪闷。',
|
|
159
|
+
trait: '情绪低沉、容易紧张焦虑、胸闷叹气、咽部有异物感、睡不安稳。',
|
|
160
|
+
tendency: '不是想太多,是气机不通;长期憋着会连带影响睡眠和胃口。',
|
|
161
|
+
care: [
|
|
162
|
+
'食:少食多餐,别用吃东西压情绪;玫瑰花、陈皮、佛手可以泡水。',
|
|
163
|
+
'起居:把「必须做」的事减到三件以内,留出空档。',
|
|
164
|
+
'动:唱歌、快走、拉伸这类能「把气呼出去」的最有用。',
|
|
165
|
+
'情志:写下来比在脑子里绕有效,哪怕只写三行。',
|
|
166
|
+
],
|
|
167
|
+
questions: [
|
|
168
|
+
'你常觉得闷闷不乐,情绪提不起来',
|
|
169
|
+
'你容易紧张、焦虑,或者心里放不下事',
|
|
170
|
+
'你常叹气,觉得胸口不畅',
|
|
171
|
+
'你咽喉像有东西堵着,吞不下也吐不出',
|
|
172
|
+
'你睡不安稳,容易醒或者多梦',
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
key: 'tebing',
|
|
177
|
+
name: '特禀质',
|
|
178
|
+
oneLine: '对外界刺激反应大:过敏、易起反应。',
|
|
179
|
+
trait: '容易过敏(鼻炎、皮肤、食物)、对季节与气味敏感、易起荨麻疹。',
|
|
180
|
+
tendency: '不是体质弱,是反应阈值低;避开诱因比「增强体质」更直接。',
|
|
181
|
+
care: [
|
|
182
|
+
'食:把自己的过敏原记下来,比记一堆「该吃什么」有用。',
|
|
183
|
+
'起居:换季前把床品洗晒一遍,花粉季回家先洗脸。',
|
|
184
|
+
'动:室内温和运动为主,避开花粉浓度高的时段户外跑。',
|
|
185
|
+
'情志:反复发作容易烦,把「避免诱因」当成日常动作而不是任务。',
|
|
186
|
+
],
|
|
187
|
+
questions: [
|
|
188
|
+
'你容易打喷嚏、流清涕,或者鼻子痒',
|
|
189
|
+
'你皮肤容易起荨麻疹、湿疹或红疹',
|
|
190
|
+
'你对某些食物或药物有过敏反应',
|
|
191
|
+
'你对季节变化、气味或灰尘特别敏感',
|
|
192
|
+
'你换环境后容易不舒服(比如新房间、新城市)',
|
|
193
|
+
],
|
|
194
|
+
},
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
/** 题目扁平成一张卷:每题带所属体质,顺序按体质分组。 */
|
|
198
|
+
export const QUESTIONS = CONSTITUTIONS.flatMap((c) => c.questions.map((q, i) => ({ i: c.key + '-' + (i + 1), key: c.key, name: c.name, text: q })));
|
|
199
|
+
|
|
200
|
+
export const SCALE_OPTIONS = [
|
|
201
|
+
{ v: 1, label: '没有' },
|
|
202
|
+
{ v: 2, label: '很少' },
|
|
203
|
+
{ v: 3, label: '有时' },
|
|
204
|
+
{ v: 4, label: '经常' },
|
|
205
|
+
{ v: 5, label: '总是' },
|
|
206
|
+
];
|
|
207
|
+
|
|
208
|
+
/** 转化分:0–100,保留一位小数。 */
|
|
209
|
+
function converted(raw, n) {
|
|
210
|
+
return Math.round(((raw - n) / (n * 4)) * 1000) / 10;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function levelOf(conv, isPinghe) {
|
|
214
|
+
if (isPinghe) return conv >= 60 ? '是' : conv >= 50 ? '倾向是' : '否';
|
|
215
|
+
return conv >= 40 ? '是' : conv >= 30 ? '倾向是' : '否';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** 卷子:不带 answers 时先给题,别让模型替人打分。 */
|
|
219
|
+
export function constitutionPaper() {
|
|
220
|
+
return {
|
|
221
|
+
system: 'constitution',
|
|
222
|
+
paper: true,
|
|
223
|
+
total: QUESTIONS.length,
|
|
224
|
+
scaleCount: CONSTITUTIONS.length,
|
|
225
|
+
itemsPerScale: ITEMS,
|
|
226
|
+
scale: SCALE_OPTIONS,
|
|
227
|
+
scales: CONSTITUTIONS.map((c) => ({ key: c.key, name: c.name, oneLine: c.oneLine, from: QUESTIONS.findIndex((q) => q.key === c.key) })),
|
|
228
|
+
questions: QUESTIONS.map((q, i) => ({ no: i + 1, key: q.key, name: q.name, text: q.text })),
|
|
229
|
+
howto: '按最近一年的真实感受给每题打 1–5 分(1 没有 / 2 很少 / 3 有时 / 4 经常 / 5 总是),'
|
|
230
|
+
+ '拿不准的题传 null 跳过;把分数按题目顺序填成 answers 数组再调一次。',
|
|
231
|
+
note: '九种体质各 5 题,共 45 题。这是按通行标准结构做的简化问卷,不是量表原件;结果只作自我观察的参照。',
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 体质判定。
|
|
237
|
+
* @param {{answers?: Array<number|null>}} input answers 按题目顺序的 1–5 分,null 表示跳过
|
|
238
|
+
*/
|
|
239
|
+
export function buildConstitution(input) {
|
|
240
|
+
const answers = input && Array.isArray(input.answers) ? input.answers : null;
|
|
241
|
+
if (!answers) throw fail('E_INPUT', '体质判定需要 answers(按题目顺序的 1–5 分);先不带 answers 调一次拿问卷');
|
|
242
|
+
const total = QUESTIONS.length;
|
|
243
|
+
// 长度不匹配是最常见的错:短一位就会整体错位,静默按「第 N 题」算下去更糟
|
|
244
|
+
if (answers.length !== total) {
|
|
245
|
+
throw fail('E_INPUT', `answers 要 ${total} 个(九种体质各 5 题),收到 ${answers.length} 个;跳过的题传 null,不要缩短数组`);
|
|
246
|
+
}
|
|
247
|
+
const seen = answers.map((v, i) => {
|
|
248
|
+
if (v === null || v === undefined) return null;
|
|
249
|
+
const n = Number(v);
|
|
250
|
+
if (!Number.isInteger(n) || n < 1 || n > 5) {
|
|
251
|
+
throw fail('E_INPUT', `第 ${i + 1} 题的分数要是 1–5 的整数或 null,收到「${String(v).slice(0, 16)}」`);
|
|
252
|
+
}
|
|
253
|
+
return n;
|
|
254
|
+
});
|
|
255
|
+
const answered = seen.filter((x) => x !== null).length;
|
|
256
|
+
if (!answered) throw fail('E_INPUT', 'answers 全为 null:至少答几题才能判体质,否则每种都是同一个分数');
|
|
257
|
+
|
|
258
|
+
const scales = CONSTITUTIONS.map((c) => {
|
|
259
|
+
const idx = [];
|
|
260
|
+
QUESTIONS.forEach((q, i) => { if (q.key === c.key) idx.push(i); });
|
|
261
|
+
const vals = idx.map((i) => seen[i]).filter((x) => x !== null);
|
|
262
|
+
// 未答的题按「3(有时)」补齐:直接丢题会抬高转化分,把没答的人判成更偏
|
|
263
|
+
const raw = idx.reduce((sum, i) => sum + (seen[i] === null ? 3 : seen[i]), 0);
|
|
264
|
+
const conv = converted(raw, idx.length);
|
|
265
|
+
return {
|
|
266
|
+
key: c.key,
|
|
267
|
+
name: c.name,
|
|
268
|
+
oneLine: c.oneLine,
|
|
269
|
+
trait: c.trait,
|
|
270
|
+
tendency: c.tendency,
|
|
271
|
+
care: c.care.slice(),
|
|
272
|
+
answered: vals.length,
|
|
273
|
+
skipped: idx.length - vals.length,
|
|
274
|
+
raw,
|
|
275
|
+
max: idx.length * 5,
|
|
276
|
+
converted: conv,
|
|
277
|
+
level: levelOf(conv, c.key === 'pinghe'),
|
|
278
|
+
};
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
const pinghe = scales.find((s) => s.key === 'pinghe');
|
|
282
|
+
const biased = scales.filter((s) => s.key !== 'pinghe').slice().sort((a, b) => b.converted - a.converted);
|
|
283
|
+
const others = biased.map((s) => s.converted);
|
|
284
|
+
const othersMax = others.length ? Math.max(...others) : 0;
|
|
285
|
+
// 平和质按标准要「其余八种都很低」才算成立:光自己分高不算
|
|
286
|
+
let pingheLevel = pinghe.level;
|
|
287
|
+
if (pinghe.converted >= 60) {
|
|
288
|
+
if (othersMax < 30) pingheLevel = '是';
|
|
289
|
+
else if (othersMax < 40) pingheLevel = '基本是';
|
|
290
|
+
else pingheLevel = '否';
|
|
291
|
+
}
|
|
292
|
+
const pingheFinal = { ...pinghe, level: pingheLevel };
|
|
293
|
+
|
|
294
|
+
const primaryBiased = biased.find((s) => s.level === '是') || biased[0];
|
|
295
|
+
const hasBiased = biased.some((s) => s.level === '是' || s.level === '倾向是');
|
|
296
|
+
const isPinghe = pingheLevel === '是' || pingheLevel === '基本是';
|
|
297
|
+
const primary = isPinghe && !biased.some((s) => s.level === '是') ? pingheFinal : primaryBiased;
|
|
298
|
+
const secondary = biased.find((s) => s.key !== primary.key && s.converted >= 30) || null;
|
|
299
|
+
const mingled = biased.filter((s) => s.level === '是' && s.key !== primary.key && (!secondary || s.key !== secondary.key)).map((s) => s.name);
|
|
300
|
+
|
|
301
|
+
// 全选同一项等于没区分:转化分会整齐地落在 50 上下,把人判成「九种都偏」。
|
|
302
|
+
// 这种卷子只提示、不改判定门槛——门槛一改就和公开标准的口径分叉了。
|
|
303
|
+
const vals = seen.filter((x) => x !== null);
|
|
304
|
+
const flat = vals.length >= 10 && new Set(vals).size === 1;
|
|
305
|
+
const main = primary.key === 'pinghe' ? pingheFinal : primary;
|
|
306
|
+
return {
|
|
307
|
+
system: 'constitution',
|
|
308
|
+
meta: { answered, total, scaleCount: CONSTITUTIONS.length, itemsPerScale: ITEMS, scale: SCALE_OPTIONS },
|
|
309
|
+
scales: scales.map((s) => (s.key === 'pinghe' ? { ...s, level: pingheLevel } : s)),
|
|
310
|
+
primary: { key: main.key, name: main.name, converted: main.converted, level: main.level, oneLine: main.oneLine, trait: main.trait, tendency: main.tendency },
|
|
311
|
+
secondary: secondary ? { key: secondary.key, name: secondary.name, converted: secondary.converted, level: secondary.level } : null,
|
|
312
|
+
mingled,
|
|
313
|
+
pinghe: { converted: pinghe.converted, level: pingheLevel },
|
|
314
|
+
balanced: isPinghe && !biased.some((s) => s.level === '是'),
|
|
315
|
+
hasTendency: hasBiased,
|
|
316
|
+
flat,
|
|
317
|
+
care: main.care.slice(),
|
|
318
|
+
top: biased.slice(0, 3).map((s) => ({ name: s.name, converted: s.converted, level: s.level })),
|
|
319
|
+
note: '九种体质各 5 题的简化问卷,转化分按 [(原始分 − 题数) / (题数 × 4)] × 100 换算;'
|
|
320
|
+
+ '偏颇质 ≥40 判「是」、30–39 判「有倾向」,平和质要 ≥60 且其余八种都低才成立。'
|
|
321
|
+
+ '体质是长期倾向,不是当下病名;有具体不适请就医,这份结果不与八字、星座互证。'
|
|
322
|
+
+ (flat ? ' 本次每题都选了同一个选项,问卷对你的区分度不足,主次体质仅供参考。' : ''),
|
|
323
|
+
};
|
|
324
|
+
}
|
package/engine/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { buildBazi } from './bazi.js';
|
|
3
3
|
import { buildLiuyao, tossLines, bodyLabels, yongshenOf, yongshenStrength, fushenAccess, hexagramStrength, jinTuiPower, yingqiWindow } from './liuyao.js';
|
|
4
4
|
import { buildZiwei, BRIGHTNESS, flowOf, asOfParts, ZIWEI_SCHOOL_DEFAULTS } from './ziwei.js';
|
|
5
|
-
import { drawTarot } from './tarot.js';
|
|
5
|
+
import { drawTarot, SPREAD_KEYS } from './tarot.js';
|
|
6
6
|
import { buildMeihua } from './meihua.js';
|
|
7
7
|
import { buildDaliuren } from './daliuren.js';
|
|
8
8
|
import { buildHepan, tripleOf } from './hepan.js';
|
|
@@ -27,6 +27,9 @@ import { buildXiaoliuren } from './xiaoliuren.js';
|
|
|
27
27
|
import { buildStar } from './star.js';
|
|
28
28
|
import { buildNumerology } from './numerology.js';
|
|
29
29
|
import { buildMbti, mbtiPaper } from './mbti.js';
|
|
30
|
+
import { buildConstitution, constitutionPaper } from './constitution.js';
|
|
31
|
+
import { buildWuyunliuqi } from './wuyunliuqi.js';
|
|
32
|
+
import { buildTaiyi } from './taiyi.js';
|
|
30
33
|
import { buildConvert } from './convert.js';
|
|
31
34
|
import { classicsFor, classicsText, suiyunClassicOf, classicOf, CLASSIC_BOOKS } from './classics.js';
|
|
32
35
|
import { diffObjects, summarize, sortDiffs, labelOf, rootOf, categoryOf, formatValue, CALIBER_SWITCHES, viewRows } from './diff.js';
|
|
@@ -632,7 +635,7 @@ function shadowParams(args, result) {
|
|
|
632
635
|
else put(key, value);
|
|
633
636
|
}
|
|
634
637
|
if (system === 'daliuren' && params.lateZi === undefined) params.lateZi = true;
|
|
635
|
-
if (system === 'tarot' && params.count === undefined) params.count = 3;
|
|
638
|
+
if (system === 'tarot' && params.spread === undefined && params.count === undefined) params.count = 3;
|
|
636
639
|
if (system === 'meihua' && params.mode === undefined) params.mode = 'time';
|
|
637
640
|
if (system === 'xiaoliuren' && params.mode === undefined) params.mode = 'time';
|
|
638
641
|
if (system === 'convert' && params.mode === undefined) params.mode = 'solar2lunar';
|
|
@@ -1345,14 +1348,24 @@ function computeRaw(args = {}) {
|
|
|
1345
1348
|
return { ...result, ...(asOf ? { asOf } : {}), precision: dt.precision, summary: ziweiText(result) };
|
|
1346
1349
|
}
|
|
1347
1350
|
if (system === 'tarot') {
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
+
// 给了牌阵名就由牌阵定张数,count 只在没给名字时兜底(1/3/5)
|
|
1352
|
+
const hasSpread = args.spread !== undefined && args.spread !== null && String(args.spread).trim() !== '';
|
|
1353
|
+
let count;
|
|
1354
|
+
if (hasSpread) {
|
|
1355
|
+
if (SPREAD_KEYS.indexOf(String(args.spread).trim().toLowerCase()) < 0) {
|
|
1356
|
+
throw fail('E_SPREAD', `塔罗牌阵只能是 ${SPREAD_KEYS.join(' / ')},收到「${args.spread}」`);
|
|
1357
|
+
}
|
|
1358
|
+
count = undefined;
|
|
1359
|
+
} else {
|
|
1360
|
+
count = args.count === undefined || args.count === null || args.count === '' ? 3 : Number(args.count);
|
|
1361
|
+
if (!TAROT_COUNTS.includes(count)) {
|
|
1362
|
+
throw fail('E_COUNT', `塔罗张数只能是 1 / 3 / 5,收到「${args.count}」`);
|
|
1363
|
+
}
|
|
1351
1364
|
}
|
|
1352
1365
|
if (!isSeed(args.seed)) throw fail('E_INPUT', `seed 必须是数字,收到「${args.seed}」`);
|
|
1353
1366
|
const asOf = checkAsOf(args.asOf);
|
|
1354
1367
|
// 抽牌不看时间:castAt 只记录此刻(或 asOf),避免误用出生时间;给了 seed 就能复现同一次抽牌
|
|
1355
|
-
const result = drawTarot({ count, question: args.question, at: args.asOf, random: randomOf(args.seed) });
|
|
1368
|
+
const result = drawTarot({ spread: hasSpread ? args.spread : undefined, count, question: args.question, at: args.asOf, random: randomOf(args.seed) });
|
|
1356
1369
|
if (args.seed !== undefined && args.seed !== null && args.seed !== '') result.seed = Number(args.seed);
|
|
1357
1370
|
return { ...result, ...(asOf ? { asOf } : {}), summary: tarotText(result) };
|
|
1358
1371
|
}
|
|
@@ -1613,6 +1626,31 @@ function computeRaw(args = {}) {
|
|
|
1613
1626
|
const result = buildNumerology({ year: dt.year, month: dt.month, day: dt.day });
|
|
1614
1627
|
return { ...result, summary: numerologyText(result) };
|
|
1615
1628
|
}
|
|
1629
|
+
if (system === 'wuyunliuqi') {
|
|
1630
|
+
const dt = parseDateTime(args.datetime);
|
|
1631
|
+
const result = buildWuyunliuqi({
|
|
1632
|
+
year: dt.year,
|
|
1633
|
+
month: dt.month,
|
|
1634
|
+
day: dt.day,
|
|
1635
|
+
hour: dt.hour,
|
|
1636
|
+
minute: dt.minute,
|
|
1637
|
+
});
|
|
1638
|
+
return { ...result, summary: wuyunliuqiText(result) };
|
|
1639
|
+
}
|
|
1640
|
+
if (system === 'taiyi') {
|
|
1641
|
+
const dt = parseDateTime(args.datetime);
|
|
1642
|
+
const result = buildTaiyi({ year: dt.year, month: dt.month, day: dt.day });
|
|
1643
|
+
return { ...result, summary: taiyiText(result) };
|
|
1644
|
+
}
|
|
1645
|
+
if (system === 'constitution') {
|
|
1646
|
+
// 与 mbti 同一套路:没给答案先发卷,别让模型替人打分(体质题答歪了,判定会整体偏掉)
|
|
1647
|
+
if (args.answers === undefined || args.answers === null) {
|
|
1648
|
+
const paper = constitutionPaper();
|
|
1649
|
+
return { ...paper, summary: constitutionPaperText(paper) };
|
|
1650
|
+
}
|
|
1651
|
+
const result = buildConstitution({ answers: args.answers });
|
|
1652
|
+
return { ...result, summary: constitutionText(result) };
|
|
1653
|
+
}
|
|
1616
1654
|
if (system === 'mbti') {
|
|
1617
1655
|
// 没给答案就先发问卷:让模型把 24 题交给用户逐题打分,而不是自己编一个类型
|
|
1618
1656
|
if (args.answers === undefined || args.answers === null) {
|
|
@@ -1727,9 +1765,9 @@ function computeRaw(args = {}) {
|
|
|
1727
1765
|
return { ...result, summary: nameText(result) };
|
|
1728
1766
|
}
|
|
1729
1767
|
if (!system) {
|
|
1730
|
-
throw fail('E_INPUT', '缺少 system,可选:bazi(四柱八字)、ziwei(紫微斗数)、liuyao(六爻)、meihua(梅花易数)、daliuren(大六壬)、qimen(奇门遁甲)、hepan(合盘)、tarot(塔罗)、almanac(黄历)、zeri(择日)、chenggu(称骨)、xiaoliuren(小六壬)、star(星座)、numerology(生命数字)、mbti(十六型人格问卷)、trend(人生 K 线)、fengshui(风水)、name(起名)、convert(公农历互转)。');
|
|
1768
|
+
throw fail('E_INPUT', '缺少 system,可选:bazi(四柱八字)、ziwei(紫微斗数)、liuyao(六爻)、meihua(梅花易数)、daliuren(大六壬)、qimen(奇门遁甲)、hepan(合盘)、tarot(塔罗)、almanac(黄历)、zeri(择日)、chenggu(称骨)、xiaoliuren(小六壬)、star(星座)、numerology(生命数字)、mbti(十六型人格问卷)、trend(人生 K 线)、fengshui(风水)、name(起名)、constitution(中医体质)、wuyunliuqi(五运六气)、taiyi(太乙神数)、convert(公农历互转)。');
|
|
1731
1769
|
}
|
|
1732
|
-
throw fail('E_SYSTEM', `未知体系「${system}」,可选:bazi(四柱八字)、ziwei(紫微斗数)、liuyao(六爻)、meihua(梅花易数)、daliuren(大六壬)、qimen(奇门遁甲)、hepan(合盘)、tarot(塔罗)、almanac(黄历)、zeri(择日)、chenggu(称骨)、xiaoliuren(小六壬)、star(星座)、numerology(生命数字)、mbti(十六型人格问卷)、trend(人生 K 线)、fengshui(风水)、name(起名)、convert(公农历互转)。`);
|
|
1770
|
+
throw fail('E_SYSTEM', `未知体系「${system}」,可选:bazi(四柱八字)、ziwei(紫微斗数)、liuyao(六爻)、meihua(梅花易数)、daliuren(大六壬)、qimen(奇门遁甲)、hepan(合盘)、tarot(塔罗)、almanac(黄历)、zeri(择日)、chenggu(称骨)、xiaoliuren(小六壬)、star(星座)、numerology(生命数字)、mbti(十六型人格问卷)、trend(人生 K 线)、fengshui(风水)、name(起名)、constitution(中医体质)、wuyunliuqi(五运六气)、taiyi(太乙神数)、convert(公农历互转)。`);
|
|
1733
1771
|
}
|
|
1734
1772
|
|
|
1735
1773
|
/** 黄历文本 */
|
|
@@ -1856,6 +1894,66 @@ function numerologyText(r) {
|
|
|
1856
1894
|
}
|
|
1857
1895
|
|
|
1858
1896
|
/** 十六型人格文本 */
|
|
1897
|
+
/** 中医体质文本 */
|
|
1898
|
+
/** 五运六气的纯文本版(对话里直接读)。 */
|
|
1899
|
+
function taiyiText(r) {
|
|
1900
|
+
const L = [];
|
|
1901
|
+
L.push(`太乙积年 ${r.meta.jinian},残年 ${r.meta.rest360},${r.meta.dunText}${r.meta.ju}局。`);
|
|
1902
|
+
L.push(`${r.year.jiName}${r.year.jiYear}年,${r.year.yuanName}第${r.year.yuanYear}年。`);
|
|
1903
|
+
L.push(`太乙在${r.taiyi.palaceName}宫(第${r.taiyi.palace}宫,${r.taiyi.dir}),第${r.taiyi.yearInPalace}年,${r.taiyi.li}。`);
|
|
1904
|
+
L.push(`八门:${r.gate.gate}。`);
|
|
1905
|
+
L.push(`文昌在${r.wenchang.pos}(${r.wenchang.god},${r.wenchang.palaceName}宫)。`);
|
|
1906
|
+
L.push(`这一层只算到太乙、八门、文昌三个落点;${r.scope.notCovered.join('、')}。`);
|
|
1907
|
+
return L.join('\n');
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
function wuyunliuqiText(r) {
|
|
1911
|
+
const L = [];
|
|
1912
|
+
L.push(`【运气年】${r.year.ganzhi} ${r.year.nian} 年(大寒界:${r.year.startTime} — ${r.year.endTime})`);
|
|
1913
|
+
L.push(`【中运】${r.zhongyun.text}`);
|
|
1914
|
+
L.push(`【司天在泉】${r.sitian.qi}司天 ${r.zaiquan.qi}在泉`);
|
|
1915
|
+
L.push(`【当前】${r.current.text}`);
|
|
1916
|
+
L.push('【客运五步】');
|
|
1917
|
+
for (const s of r.keYun) L.push(` ${s.no} ${s.name}(${s.taiShao}) ${s.startTime} — ${s.endTime}`);
|
|
1918
|
+
L.push('【客气六步】');
|
|
1919
|
+
for (const s of r.keQi) {
|
|
1920
|
+
const tag = s.sitian ? '(司天)' : s.zaiquan ? '(在泉)' : '';
|
|
1921
|
+
L.push(` ${s.no} ${s.qi}${tag} ${s.startTime} — ${s.endTime}`);
|
|
1922
|
+
}
|
|
1923
|
+
const sp = [];
|
|
1924
|
+
if (r.special.taiYi) sp.push('太乙天符');
|
|
1925
|
+
if (r.special.tianFu && !r.special.taiYi) sp.push('天符');
|
|
1926
|
+
if (r.special.suiHui && !r.special.taiYi) sp.push('岁会');
|
|
1927
|
+
if (r.special.tongTianFu) sp.push('同天符');
|
|
1928
|
+
if (r.special.tongSuiHui) sp.push('同岁会');
|
|
1929
|
+
if (sp.length) L.push(`【特殊】${sp.join('、')}`);
|
|
1930
|
+
L.push(`【组合】${r.combo.text}`);
|
|
1931
|
+
L.push(`【边界】${r.scope.note}`);
|
|
1932
|
+
return L.join('\n');
|
|
1933
|
+
}
|
|
1934
|
+
function constitutionText(r) {
|
|
1935
|
+
return [
|
|
1936
|
+
`【中医体质】${r.primary.name}(主)${r.primary.level === '是' ? '' : `·${r.primary.level}`} 转化分 ${r.primary.converted}`,
|
|
1937
|
+
r.secondary ? `兼夹:${r.secondary.name}(${r.secondary.converted})${r.mingled.length ? ';同判为「是」:' + r.mingled.join('、') : ''}` : '',
|
|
1938
|
+
'九种体质转化分(偏颇质 ≥40 判是、30–39 有倾向):',
|
|
1939
|
+
r.scales.map((x) => `· ${x.name}:${x.converted}${x.level === '否' ? '' : `(${x.level})`}`).join('\n'),
|
|
1940
|
+
r.primary.oneLine,
|
|
1941
|
+
r.primary.tendency,
|
|
1942
|
+
'调养方向:',
|
|
1943
|
+
r.care.map((c) => '· ' + c).join('\n'),
|
|
1944
|
+
r.note,
|
|
1945
|
+
].filter(Boolean).join('\n');
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
function constitutionPaperText(p) {
|
|
1949
|
+
return [
|
|
1950
|
+
`【中医体质·问卷】${p.scaleCount} 种体质 × ${p.itemsPerScale} 题,共 ${p.total} 题`,
|
|
1951
|
+
'打分:' + p.scale.map((x) => `${x.v}=${x.label}`).join(' '),
|
|
1952
|
+
p.howto,
|
|
1953
|
+
p.note,
|
|
1954
|
+
].join('\n');
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1859
1957
|
function mbtiText(r) {
|
|
1860
1958
|
const dims = r.dims.map((d) => {
|
|
1861
1959
|
const side = d.picked === d.left ? d.leftSay : d.rightSay;
|
|
@@ -2093,6 +2191,9 @@ export { pastSignals, verifyFit, rectifyBirth };
|
|
|
2093
2191
|
export { crossCheck, crossText, crossSection };
|
|
2094
2192
|
export { buildAlmanac, buildZeri, buildChenggu, buildXiaoliuren, buildStar, buildNumerology, buildConvert };
|
|
2095
2193
|
export { buildTrend, buildFengshui, buildName };
|
|
2194
|
+
export { buildConstitution };
|
|
2195
|
+
export { buildWuyunliuqi };
|
|
2196
|
+
export { buildTaiyi };
|
|
2096
2197
|
export { buildTrace, traceMarkdown };
|
|
2097
2198
|
export { encodeHandle, decodeHandle, resolveHandle, pruneForeignKeys, ownedKeys };
|
|
2098
2199
|
// C3 对话桥替代:深链编解码、表单还原、三档复制包(面板与独立页共用一份)
|
package/engine/liuyao.js
CHANGED
|
@@ -465,7 +465,7 @@ export function yongshenOf(result, opts = {}) {
|
|
|
465
465
|
}
|
|
466
466
|
|
|
467
467
|
/** 随机摇卦:三枚铜钱,背为阳(3),字为阴(2),合计 6/7/8/9 */
|
|
468
|
-
export function tossLines(random =
|
|
468
|
+
export function tossLines(random = randomOf()) {
|
|
469
469
|
const lines = [];
|
|
470
470
|
for (let i = 0; i < 6; i++) {
|
|
471
471
|
let sum = 0;
|
package/engine/plain-summary.js
CHANGED
|
@@ -615,6 +615,104 @@ function trendSummary(r) {
|
|
|
615
615
|
* @param {object} r compute() 的结果
|
|
616
616
|
* @returns {null|{system,title,chips,lead,sections,advice,caveat,footer}}
|
|
617
617
|
*/
|
|
618
|
+
/**
|
|
619
|
+
* 中医体质:主质一条、次高一条、调养三条。
|
|
620
|
+
* 判定线是公开标准的口径,这里不改;问卷区分度不足时照实说(flat),别硬给一个主质。
|
|
621
|
+
*/
|
|
622
|
+
/**
|
|
623
|
+
* 五运六气:一句总纲、四段(气候底色 / 当前运与气 / 被强调的脏腑 / 常见不适)。
|
|
624
|
+
* 特殊格局(天符岁会那一类)只放在 caveat 里点一句,不喧宾夺主。
|
|
625
|
+
*/
|
|
626
|
+
function taiyiSummary(r) {
|
|
627
|
+
const t = r.taiyi;
|
|
628
|
+
const sec = [
|
|
629
|
+
{
|
|
630
|
+
h: '太乙落在哪一宫',
|
|
631
|
+
t: `${t.palaceName}宫(${t.dir}),这是三年里的第 ${t.yearInPalace} 年,对应「${t.li}」。太乙三年换一宫,走的是八宫、不入中五,所以在盘面上永远看不到它落在中间。`,
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
h: '八门这三十年',
|
|
635
|
+
t: `${r.gate.gate}。八门三十年一转,1954 年从休门起,顺序是休、生、伤、杜、景、死、惊、开,所以一个门要值三十年,不是每年都换。`,
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
h: '文昌在哪',
|
|
639
|
+
t: `文昌在${r.wenchang.pos}(${r.wenchang.god},${r.wenchang.palaceName}宫)。文昌十八年一周,阳局从申起、阴局从寅起,路上遇乾、坤各数两次,所以要数满十八步才回到原点。`,
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
h: '这一盘算到哪一层',
|
|
643
|
+
t: `积年 ${r.meta.jinian},残年 ${r.meta.rest360},${r.meta.dunText}${r.meta.ju}局。太乙、八门、文昌三个落点是齐的;${r.scope.notCovered.slice(0, 2).join('、')}这几项没有收录,盘面上也不显示。`,
|
|
644
|
+
},
|
|
645
|
+
];
|
|
646
|
+
return {
|
|
647
|
+
system: 'taiyi',
|
|
648
|
+
title: '先看这一张',
|
|
649
|
+
chips: [`${r.meta.year}年`, `${r.meta.dunText}${r.meta.ju}局`, `${t.palaceName}宫`].filter(Boolean),
|
|
650
|
+
lead: `${r.meta.year} 年太乙:${r.meta.dunText}${r.meta.ju}局,太乙临 ${t.palaceName} 宫(${t.dir}),八门值 ${r.gate.gate}。`,
|
|
651
|
+
sections: sec,
|
|
652
|
+
advice: [
|
|
653
|
+
`先看太乙落在哪:${t.palaceName}宫${t.dir},这三年的大方向就在这一格,别拿它当每年的细节来读。`,
|
|
654
|
+
`八门十年一变的说法常被混用,这里按三十年一转的口径——${r.gate.gate}值这三十年,不是今年才轮到。`,
|
|
655
|
+
'太乙是象数盘,给的是位置的对应关系,不是「今年会发生什么」的判决。',
|
|
656
|
+
],
|
|
657
|
+
caveat: `这一层只到太乙、八门、文昌三个落点;${r.scope.notCovered.join(';')}都未收录,别按它们解读。`,
|
|
658
|
+
footer: '太乙年家用积年推局,与八字按立春换年、运气按大寒分年都不同口径,三套不要互相印证。',
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function wuyunliuqiSummary(r) {
|
|
662
|
+
const sp = [];
|
|
663
|
+
if (r.special.taiYi) sp.push('太乙天符');
|
|
664
|
+
if (r.special.tianFu && !r.special.taiYi) sp.push('天符');
|
|
665
|
+
if (r.special.suiHui && !r.special.taiYi) sp.push('岁会');
|
|
666
|
+
if (r.special.tongTianFu) sp.push('同天符');
|
|
667
|
+
if (r.special.tongSuiHui) sp.push('同岁会');
|
|
668
|
+
const sec = [
|
|
669
|
+
{ h: '这一年的气候底色', t: r.combo.text },
|
|
670
|
+
{ h: '此刻在哪个运、哪口气', t: r.current.text },
|
|
671
|
+
{ h: '被强调的脏腑', t: `中运主${r.zhongyun.organ}:传统上这样描述——${r.zhongyun.effect.organ}。${r.zhongyun.effect.sign}` },
|
|
672
|
+
{ h: '今年常被说到的身体反应', t: `上半年(${r.sitian.qi}司天):${r.sitian.climate.sign};下半年(${r.zaiquan.qi}在泉):${r.zaiquan.climate.sign}` },
|
|
673
|
+
];
|
|
674
|
+
return {
|
|
675
|
+
system: 'wuyunliuqi',
|
|
676
|
+
title: '先看这一张',
|
|
677
|
+
chips: [`${r.year.ganzhi}年`, r.zhongyun.name, `${r.sitian.qi}司天`].filter(Boolean),
|
|
678
|
+
lead: `${r.year.ganzhi} 运气年:中运${r.zhongyun.name},${r.sitian.qi}司天、${r.zaiquan.qi}在泉。`,
|
|
679
|
+
sections: sec,
|
|
680
|
+
advice: [
|
|
681
|
+
`顺着「${r.zhongyun.nature}」来调:${r.zhongyun.tai ? '本气偏胜,别再加码,先把被克的那一头护住' : '本气不足,别硬扛,先补本气再谈其他'}。`,
|
|
682
|
+
`这一年偏「${r.sitian.climate.climate}」,起居上按这个方向做减法,比按季节硬套更贴。`,
|
|
683
|
+
'运气盘给的是「今年偏什么」,不是「你会得什么病」;有具体不适看医生。',
|
|
684
|
+
],
|
|
685
|
+
caveat: sp.length
|
|
686
|
+
? `特殊格局:${sp.join('、')}。${r.special.notes.join('')}`
|
|
687
|
+
: '这一年在格局上没有天符、岁会那一类标记,按中运与司天在泉的常例读就好。',
|
|
688
|
+
footer: '运气年以大寒为界,与八字按立春定年柱不同;两套口径不要互相印证。',
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
function constitutionSummary(r) {
|
|
692
|
+
const p = r.primary;
|
|
693
|
+
const sec = [];
|
|
694
|
+
if (p) {
|
|
695
|
+
sec.push({ h: p.name + '是什么', t: (p.oneLine || '') + (p.trait ? ' ' + p.trait : '') });
|
|
696
|
+
if (p.tendency) sec.push({ h: '容易被影响的地方', t: p.tendency });
|
|
697
|
+
}
|
|
698
|
+
const second = (r.top || []).find((x) => p && x.name !== p.name);
|
|
699
|
+
if (second) sec.push({ h: '次高的一项', t: `${second.name} ${second.converted} 分(${second.level})——它常和主质一起出现,调的时候顺手带上。` });
|
|
700
|
+
return {
|
|
701
|
+
system: 'constitution',
|
|
702
|
+
title: '先看这一张',
|
|
703
|
+
chips: [p ? p.name : '未判定', r.pinghe ? `平和 ${r.pinghe.converted}` : '', r.meta ? `${r.meta.answered}/${r.meta.total} 题` : ''].filter(Boolean),
|
|
704
|
+
lead: p
|
|
705
|
+
? `主体质是「${p.name}」${p.level === '是' ? '' : `(${p.level},未完全达判定线)`},转化分 ${p.converted};平和质 ${r.pinghe ? r.pinghe.converted : '—'} 分。`
|
|
706
|
+
: '这份问卷还没答够,先别急着对号入座。',
|
|
707
|
+
sections: sec,
|
|
708
|
+
advice: (r.care || []).slice(0, 3),
|
|
709
|
+
caveat: r.flat
|
|
710
|
+
? '每题都选了同一个选项,问卷对你的区分度不足——主质仅供参考,建议按最近一年的真实感受重答一遍。'
|
|
711
|
+
: '体质是长期倾向,不是病名,也不与八字、星座互证;有具体不适请就医。',
|
|
712
|
+
footer: '打分为自评,同一个人隔一个月再答可能差一档,看趋势比看单次结果有用。',
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
|
|
618
716
|
export function plainSummary(r) {
|
|
619
717
|
if (!r || typeof r !== 'object') return null;
|
|
620
718
|
if (r.system === 'bazi') return baziSummary(r);
|
|
@@ -626,5 +724,8 @@ export function plainSummary(r) {
|
|
|
626
724
|
if (r.system === 'tarot') return tarotSummary(r);
|
|
627
725
|
if (r.system === 'hepan') return hepanSummary(r);
|
|
628
726
|
if (r.system === 'trend') return trendSummary(r);
|
|
727
|
+
if (r.system === 'constitution') return constitutionSummary(r);
|
|
728
|
+
if (r.system === 'wuyunliuqi') return wuyunliuqiSummary(r);
|
|
729
|
+
if (r.system === 'taiyi') return taiyiSummary(r);
|
|
629
730
|
return null;
|
|
630
731
|
}
|