dsh-metaphysics 0.38.2 → 0.46.5
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 +215 -8
- package/README.md +348 -342
- package/client.js +2134 -129
- package/engine/bazi-words.js +174 -0
- package/engine/classics.js +136 -7
- package/engine/fengshui.js +197 -11
- package/engine/followup.js +29 -5
- package/engine/geju.js +41 -0
- package/engine/index.js +96 -8
- package/engine/interpret-knowledge.js +31 -4
- package/engine/interpret-plain.js +367 -322
- package/engine/interpret.js +288 -32
- package/engine/liuyao.js +85 -0
- package/engine/name-data.js +229 -0
- package/engine/name.js +234 -9
- package/engine/ziwei-words.js +245 -0
- package/engine/ziwei.js +254 -26
- package/index.js +65 -9
- package/package.json +2 -2
- package//345/221/275/347/220/206/346/216/222/347/233/230.html +2933 -487
package/engine/fengshui.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// 参照公开通行表实现(与常见八宅/玄空入门资料一致),年紫白用纯公式,不依赖任何第三方历法库。
|
|
4
4
|
import { partsToJd, termInstantJd } from './calendar.js';
|
|
5
5
|
import { applyDst } from './dst.js';
|
|
6
|
+
import { fail } from './fail.js';
|
|
6
7
|
|
|
7
8
|
/** 九星名称表:数 → {name, color, element, star(北斗名), lucky}。 */
|
|
8
9
|
export const NINE_STARS = {
|
|
@@ -56,7 +57,7 @@ const MOUNTAIN_INDEX = new Map(MOUNTAINS_24.map((x, i) => [x.m, i]));
|
|
|
56
57
|
export function mountainInfo(name) {
|
|
57
58
|
const i = MOUNTAIN_INDEX.get(name);
|
|
58
59
|
if (i === undefined) {
|
|
59
|
-
throw
|
|
60
|
+
throw fail('E_INPUT', `坐向必须是 24 山之一(${MOUNTAINS_24.map((x) => x.m).join('')}),收到「${name}」——请按罗盘 24 山报坐或报向,不要自造方位词。`);
|
|
60
61
|
}
|
|
61
62
|
return { ...MOUNTAINS_24[i], index: i };
|
|
62
63
|
}
|
|
@@ -143,6 +144,127 @@ export function yearlyFlying(year) {
|
|
|
143
144
|
|
|
144
145
|
const pad2 = (n) => String(n).padStart(2, '0');
|
|
145
146
|
|
|
147
|
+
// ── 玄空飞星(下卦) ─────────────────────────────────────────────
|
|
148
|
+
// 口径:三元九运每运 20 年,1864 年起一运;运盘当运星入中顺飞。
|
|
149
|
+
// 山盘/向盘:取坐宫/向宫的运星入中,按「入中运星本宫同元龙」的阴阳定顺逆(五黄入中按本山阴阳)。
|
|
150
|
+
// 这一步是玄空挨星的关键:不是按坐山本身的阴阳,而是按入中运星原宫对应元龙的阴阳;
|
|
151
|
+
// 按本山阴阳会把旺山旺向算成每运固定的六局,与通行二十四山旺山旺向表对不上。
|
|
152
|
+
|
|
153
|
+
/** 三元九运:1864 起每 20 年一运,九运一轮(180 年)。 */
|
|
154
|
+
export function periodOfYear(year) {
|
|
155
|
+
const k = Math.floor((year - 1864) / 20);
|
|
156
|
+
const period = ((k % 9) + 9) % 9 + 1;
|
|
157
|
+
const start = 1864 + 20 * k;
|
|
158
|
+
return { period, start, end: start + 19, label: `${period}运(${start}–${start + 19})` };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** 三元龙阴阳:天元/人元四隅为阳、四正为阴;地元四正为阳、四隅为阴。 */
|
|
162
|
+
const YANG_MOUNTAINS = new Set(['乾', '巽', '艮', '坤', '甲', '庚', '壬', '丙', '寅', '申', '巳', '亥']);
|
|
163
|
+
const YUAN_NAME = ['地元', '天元', '人元'];
|
|
164
|
+
|
|
165
|
+
/** 24 山 → 三元龙 + 阴阳(MOUNTAINS_24 每卦三山依次为 地元/天元/人元)。 */
|
|
166
|
+
export const MOUNTAIN_META = {};
|
|
167
|
+
MOUNTAINS_24.forEach((x, i) => {
|
|
168
|
+
MOUNTAIN_META[x.m] = { ...x, yuan: YUAN_NAME[i % 3], yinYang: YANG_MOUNTAINS.has(x.m) ? '阳' : '阴' };
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
/** 飞星轨迹:中 → 乾 → 兑 → 艮 → 离 → 坎 → 坤 → 震 → 巽(即洛书 5→6→7→8→9→1→2→3→4)。 */
|
|
172
|
+
const FLY_SEQ = ['中', '乾', '兑', '艮', '离', '坎', '坤', '震', '巽'];
|
|
173
|
+
const NUM_TO_GUA = ['坎', '坤', '震', '巽', '中', '乾', '兑', '艮', '离']; // 洛书数 1–9 本位卦
|
|
174
|
+
const YUAN_INDEX = { 地元: 0, 天元: 1, 人元: 2 };
|
|
175
|
+
|
|
176
|
+
const mountainsOfGua = (gua) => MOUNTAINS_24.filter((x) => x.gua === gua).map((x) => MOUNTAIN_META[x.m]);
|
|
177
|
+
|
|
178
|
+
/** 入中星按轨迹飞布:forward=true 顺飞,false 逆飞。返回 宫 → 星数。 */
|
|
179
|
+
function fly(center, forward) {
|
|
180
|
+
const out = {};
|
|
181
|
+
for (let i = 0; i < 9; i++) {
|
|
182
|
+
const v = ((center - 1 + (forward ? i : -i)) % 9 + 9) % 9 + 1;
|
|
183
|
+
out[FLY_SEQ[i]] = v;
|
|
184
|
+
}
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** 入中运星定顺逆:取本宫(运星本位卦)同元龙之山的阴阳;五黄入中按本山阴阳。 */
|
|
189
|
+
function flightOf(mount, center) {
|
|
190
|
+
if (center === 5) {
|
|
191
|
+
return { forward: mount.yinYang === '阳', basis: `五黄入中,按${mount.m}本山${mount.yuan}${mount.yinYang}论` };
|
|
192
|
+
}
|
|
193
|
+
const home = NUM_TO_GUA[center - 1];
|
|
194
|
+
const hm = mountainsOfGua(home)[YUAN_INDEX[mount.yuan]];
|
|
195
|
+
return { forward: hm.yinYang === '阳', basis: `${center}${home}宫取${mount.yuan}${hm.m}(${hm.yinYang})` };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** 通行二十四山旺山旺向表(每项含正反两向;一运、九运无局)。 */
|
|
199
|
+
export const WANG_SHAN_WANG_XIANG = {
|
|
200
|
+
1: [],
|
|
201
|
+
2: [['乾', '巽'], ['亥', '巳'], ['丑', '未']],
|
|
202
|
+
3: [['卯', '酉'], ['乙', '辛'], ['辰', '戌']],
|
|
203
|
+
4: [['艮', '坤'], ['寅', '申'], ['甲', '庚']],
|
|
204
|
+
5: [['子', '午'], ['癸', '丁'], ['卯', '酉'], ['乙', '辛'], ['丑', '未'], ['辰', '戌']],
|
|
205
|
+
6: [['艮', '坤'], ['寅', '申'], ['甲', '庚']],
|
|
206
|
+
7: [['卯', '酉'], ['乙', '辛'], ['辰', '戌']],
|
|
207
|
+
8: [['乾', '巽'], ['亥', '巳'], ['丑', '未']],
|
|
208
|
+
9: [],
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export const XUANKONG_SOURCES = [
|
|
212
|
+
'三元九运(1864 年起一运、每运 20 年,九运 180 年一轮)',
|
|
213
|
+
'玄空下卦挨星(运盘顺飞;山盘/向盘以入中运星本宫同元龙阴阳定顺逆,五黄入中按本山阴阳)',
|
|
214
|
+
'二十四山旺山旺向通行表(每项含正反两向;一运、九运无局)',
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
export const XUANKONG_DISCLAIMER = '玄空下卦为传统通行口径的本地排盘,属民俗参考,不构成现场勘舆结论,仅供娱乐;替卦/兼向、年月日时飞星与形峦不在本工具范围内。';
|
|
218
|
+
|
|
219
|
+
/** 玄空下卦排盘:运盘 + 山盘 + 向盘 + 当运星 + 合十 + 表法旺山旺向。 */
|
|
220
|
+
export function xuankongChart(period, sitting, facing) {
|
|
221
|
+
const sit = MOUNTAIN_META[sitting];
|
|
222
|
+
const face = MOUNTAIN_META[facing];
|
|
223
|
+
const yun = fly(period, true);
|
|
224
|
+
const sCenter = yun[sit.gua];
|
|
225
|
+
const fCenter = yun[face.gua];
|
|
226
|
+
const sFly = flightOf(sit, sCenter);
|
|
227
|
+
const fFly = flightOf(face, fCenter);
|
|
228
|
+
const shan = fly(sCenter, sFly.forward);
|
|
229
|
+
const xiang = fly(fCenter, fFly.forward);
|
|
230
|
+
const palaces = PALACE_ORDER.map((palace, i) => ({
|
|
231
|
+
palace,
|
|
232
|
+
direction: palace === '中' ? '中宫' : PALACE_DIRECTION[palace],
|
|
233
|
+
luoshu: i + 1,
|
|
234
|
+
yun: yun[palace],
|
|
235
|
+
shan: shan[palace],
|
|
236
|
+
xiang: xiang[palace],
|
|
237
|
+
}));
|
|
238
|
+
const wheres = (map) => PALACE_ORDER.filter((p) => map[p] === period);
|
|
239
|
+
const pairs = WANG_SHAN_WANG_XIANG[period];
|
|
240
|
+
const hit = pairs.find(([a, b]) => (sit.m === a && face.m === b) || (sit.m === b && face.m === a));
|
|
241
|
+
const byChart = shan[sit.gua] === period && xiang[face.gua] === period;
|
|
242
|
+
const note = byChart && hit
|
|
243
|
+
? '盘面与表法一致:本局到山到向。'
|
|
244
|
+
: byChart
|
|
245
|
+
? '盘面到山到向,但不在通行表法所列之局(表法与盘面口径不同,并列不合并)。'
|
|
246
|
+
: hit
|
|
247
|
+
? '通行表法列为旺山旺向,但本盘当运山星/向星未同时落坐向(表法与盘面口径不同,并列不合并)。'
|
|
248
|
+
: '本局既未到山到向,也不在表法旺山旺向之列。';
|
|
249
|
+
return {
|
|
250
|
+
palaces,
|
|
251
|
+
sitting: sit,
|
|
252
|
+
facing: face,
|
|
253
|
+
yun,
|
|
254
|
+
shan,
|
|
255
|
+
xiang,
|
|
256
|
+
yunCenter: period,
|
|
257
|
+
shanCenter: { star: sCenter, ...sFly },
|
|
258
|
+
xiangCenter: { star: fCenter, ...fFly },
|
|
259
|
+
dangYun: { shan: wheres(shan), xiang: wheres(xiang) },
|
|
260
|
+
heshi: {
|
|
261
|
+
shan: PALACE_ORDER.filter((p) => shan[p] + yun[p] === 10),
|
|
262
|
+
xiang: PALACE_ORDER.filter((p) => xiang[p] + yun[p] === 10),
|
|
263
|
+
},
|
|
264
|
+
wangShanWangXiang: { byChart, byTable: Boolean(hit), table: pairs.map(([a, b]) => `${a}${b}`), note },
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
146
268
|
/** DST 校正出生日期(1986–1991),返回校正后的年月日。 */
|
|
147
269
|
function civilDate(input) {
|
|
148
270
|
const dst = input.dst === false
|
|
@@ -161,19 +283,21 @@ const DISCLAIMER = '八宅与飞星为传统通行口径的本地计算,属民
|
|
|
161
283
|
|
|
162
284
|
/**
|
|
163
285
|
* 风水主函数。
|
|
164
|
-
* @param {{action?:'natal'|'advise', year?:number, month?:number, day?:number, gender?:'male'|'female',
|
|
165
|
-
* fortuneYear?:number, sitting?:string, facing?:string, dst?:boolean}} input
|
|
286
|
+
* @param {{action?:'natal'|'advise'|'xuankong', year?:number, month?:number, day?:number, gender?:'male'|'female',
|
|
287
|
+
* fortuneYear?:number, sitting?:string, facing?:string, period?:number, dst?:boolean}} input
|
|
166
288
|
* natal(默认):要生日 + 性别 + 流年(缺流年则只算命卦与八星)。
|
|
167
289
|
* advise:要坐山或向山(24 山,给一即可);出生信息可选,给了才做命宅配。
|
|
290
|
+
* xuankong:要坐山或向山;运由 period(1–9)或 fortuneYear 定,缺省按九运并注明。
|
|
168
291
|
*/
|
|
169
292
|
export function buildFengshui(input) {
|
|
170
293
|
if (input.action === 'advise') return buildAdvise(input);
|
|
294
|
+
if (input.action === 'xuankong') return buildXuankong(input);
|
|
171
295
|
const gender = input.gender;
|
|
172
296
|
if (gender !== 'male' && gender !== 'female') {
|
|
173
|
-
throw
|
|
297
|
+
throw fail('E_INPUT', '风水命卦需要性别:男女命卦算法不同,不可缺省');
|
|
174
298
|
}
|
|
175
299
|
if (!Number.isInteger(input.year) || !Number.isInteger(input.month) || !Number.isInteger(input.day)) {
|
|
176
|
-
throw
|
|
300
|
+
throw fail('E_INPUT', '风水命卦需要完整出生年月日(公历)——只知道年份不够:命卦按立春界划分出生年');
|
|
177
301
|
}
|
|
178
302
|
const civil = civilDate(input);
|
|
179
303
|
const guaYear = guaYearOf(civil.year, civil.month, civil.day);
|
|
@@ -191,7 +315,7 @@ export function buildFengshui(input) {
|
|
|
191
315
|
fortuneYear,
|
|
192
316
|
...(civil.dst.applied ? { dst: civil.dst.note } : {}),
|
|
193
317
|
...(fortuneYear === null ? { note: '未给流年:未算年紫白飞星(需要时补 fortuneYear)。' } : {}),
|
|
194
|
-
scope: '八宅命卦 + 大游年八星 +
|
|
318
|
+
scope: '八宅命卦 + 大游年八星 + 年紫白;玄空飞星见 action=xuankong;不含形峦与现场勘舆。',
|
|
195
319
|
},
|
|
196
320
|
mingGua: {
|
|
197
321
|
num,
|
|
@@ -208,22 +332,28 @@ export function buildFengshui(input) {
|
|
|
208
332
|
};
|
|
209
333
|
}
|
|
210
334
|
|
|
211
|
-
/**
|
|
212
|
-
function
|
|
335
|
+
/** 坐向解析:至少给坐或向;两者都给则必须对宫。 */
|
|
336
|
+
function resolveSittingFacing(input) {
|
|
213
337
|
const hasSitting = typeof input.sitting === 'string' && input.sitting.trim() !== '';
|
|
214
338
|
const hasFacing = typeof input.facing === 'string' && input.facing.trim() !== '';
|
|
215
339
|
if (!hasSitting && !hasFacing) {
|
|
216
|
-
throw
|
|
340
|
+
throw fail('E_INPUT', '缺少坐向:sitting(坐山)或 facing(向山)至少给一个——按罗盘 24 山报坐或报向,例如「坐北朝南」对应坐子向午');
|
|
217
341
|
}
|
|
218
342
|
if (hasSitting && hasFacing) {
|
|
219
343
|
const s = mountainInfo(input.sitting.trim());
|
|
220
344
|
const f = mountainInfo(input.facing.trim());
|
|
221
345
|
if ((s.index + 12) % 24 !== f.index) {
|
|
222
|
-
throw
|
|
346
|
+
throw fail('E_INPUT', `坐向不自洽:坐${s.m}(${s.deg}°)与向${f.m}(${f.deg}°)不是对宫(相差应 180°,实测 ${((f.deg - s.deg) + 360) % 360}°)——请核对罗盘读数。`);
|
|
223
347
|
}
|
|
224
348
|
}
|
|
225
349
|
const sitting = hasSitting ? mountainInfo(input.sitting.trim()) : sittingFromFacing(input.facing.trim());
|
|
226
350
|
const facing = MOUNTAINS_24[(sitting.index + 12) % 24];
|
|
351
|
+
return { sitting, facing };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/** 宅卦坐向 + 门主灶厕布局倾向 + 命宅配。 */
|
|
355
|
+
function buildAdvise(input) {
|
|
356
|
+
const { sitting, facing } = resolveSittingFacing(input);
|
|
227
357
|
const houseGua = sitting.gua;
|
|
228
358
|
const stars = eightStars(houseGua);
|
|
229
359
|
const lucky = LUCKY_STARS.map((s) => stars[s]);
|
|
@@ -277,7 +407,7 @@ function buildAdvise(input) {
|
|
|
277
407
|
facing: `${facing.m}山(罗盘 ${facing.deg}°)`,
|
|
278
408
|
houseGua,
|
|
279
409
|
houseGroup: `${guaGroup(houseGua)}宅`,
|
|
280
|
-
scope: '
|
|
410
|
+
scope: '门主灶厕走向按八宅通行口径给倾向,玄空飞星见 action=xuankong;不含形峦、不是现场勘舆结论。',
|
|
281
411
|
},
|
|
282
412
|
houseStars: starChart,
|
|
283
413
|
layout,
|
|
@@ -286,3 +416,59 @@ function buildAdvise(input) {
|
|
|
286
416
|
disclaimer: DISCLAIMER,
|
|
287
417
|
};
|
|
288
418
|
}
|
|
419
|
+
|
|
420
|
+
/** 玄空下卦:坐向 + 运 → 九宫盘、当运星、合十、表法旺山旺向。 */
|
|
421
|
+
function buildXuankong(input) {
|
|
422
|
+
const { sitting, facing } = resolveSittingFacing(input);
|
|
423
|
+
let period;
|
|
424
|
+
let yearInfo = null;
|
|
425
|
+
let note;
|
|
426
|
+
if (input.period !== undefined && input.period !== null && !Number.isInteger(input.period)) {
|
|
427
|
+
throw fail('E_INPUT', `运必须是 1–9 的整数(三元九运),收到「${input.period}」——一运 1864 起、九运 2024–2043。`);
|
|
428
|
+
}
|
|
429
|
+
if (Number.isInteger(input.period)) {
|
|
430
|
+
if (input.period < 1 || input.period > 9) {
|
|
431
|
+
throw fail('E_INPUT', `运必须是 1–9 的整数(三元九运),收到「${input.period}」——一运 1864 起、九运 2024–2043。`);
|
|
432
|
+
}
|
|
433
|
+
period = input.period;
|
|
434
|
+
} else if (Number.isInteger(input.fortuneYear)) {
|
|
435
|
+
yearInfo = periodOfYear(input.fortuneYear);
|
|
436
|
+
period = yearInfo.period;
|
|
437
|
+
} else {
|
|
438
|
+
period = 9;
|
|
439
|
+
note = '未给运:默认按九运(2024–2043)排盘;可传 period(1–9)或 fortuneYear 指定。';
|
|
440
|
+
}
|
|
441
|
+
const chart = xuankongChart(period, sitting.m, facing.m);
|
|
442
|
+
const sitMeta = MOUNTAIN_META[sitting.m];
|
|
443
|
+
const faceMeta = MOUNTAIN_META[facing.m];
|
|
444
|
+
const center = (c) => ({ star: c.star, name: NINE_STARS[c.star].name, fly: c.forward ? '顺飞' : '逆飞', basis: c.basis });
|
|
445
|
+
const where = (list) => list.map((p) => `${p}宫${PALACE_DIRECTION[p]}`).join('、') || '无';
|
|
446
|
+
return {
|
|
447
|
+
system: 'fengshui',
|
|
448
|
+
meta: {
|
|
449
|
+
action: 'xuankong',
|
|
450
|
+
period,
|
|
451
|
+
periodLabel: yearInfo ? yearInfo.label : `${period}运`,
|
|
452
|
+
...(yearInfo ? { year: input.fortuneYear, periodStart: yearInfo.start, periodEnd: yearInfo.end } : {}),
|
|
453
|
+
sitting: `${sitting.m}山(罗盘 ${sitting.deg}°)`,
|
|
454
|
+
facing: `${facing.m}山(罗盘 ${facing.deg}°)`,
|
|
455
|
+
sittingGua: sitting.gua,
|
|
456
|
+
facingGua: facing.gua,
|
|
457
|
+
sittingYuan: `${sitMeta.yuan}${sitMeta.yinYang}`,
|
|
458
|
+
facingYuan: `${faceMeta.yuan}${faceMeta.yinYang}`,
|
|
459
|
+
shanCenter: center(chart.shanCenter),
|
|
460
|
+
xiangCenter: center(chart.xiangCenter),
|
|
461
|
+
...(note ? { note } : {}),
|
|
462
|
+
scope: '玄空下卦:运盘 + 山盘 + 向盘 + 当运星 + 合十 + 表法旺山旺向;不含替卦/兼向、不含年月日时飞星、不含形峦与现场勘舆。',
|
|
463
|
+
},
|
|
464
|
+
palaces: chart.palaces,
|
|
465
|
+
dangYun: {
|
|
466
|
+
shan: { palaces: chart.dangYun.shan, text: where(chart.dangYun.shan) },
|
|
467
|
+
xiang: { palaces: chart.dangYun.xiang, text: where(chart.dangYun.xiang) },
|
|
468
|
+
},
|
|
469
|
+
heshi: { shan: chart.heshi.shan, xiang: chart.heshi.xiang },
|
|
470
|
+
wangShanWangXiang: chart.wangShanWangXiang,
|
|
471
|
+
sources: XUANKONG_SOURCES,
|
|
472
|
+
disclaimer: XUANKONG_DISCLAIMER,
|
|
473
|
+
};
|
|
474
|
+
}
|
package/engine/followup.js
CHANGED
|
@@ -199,30 +199,54 @@ const SYSTEM_LABEL = {
|
|
|
199
199
|
* 客户端把这段文字写进对话输入框并提交,由对话里的模型基于盘面回答。
|
|
200
200
|
*
|
|
201
201
|
* @param {object} r compute 的结果(至少要带 system 与 summary)
|
|
202
|
-
* @param {{question?:string, maxChars?:number}} [opts]
|
|
202
|
+
* @param {{question?:string, maxChars?:number, interpret?:object}} [opts]
|
|
203
|
+
* interpret 传面板已算好的解析对象(it)时,把其中的白话结论一并交给模型,
|
|
204
|
+
* 让对话开场与面板里的「先答所问 / 说人话」是同一个口径;不传则不加这一段。
|
|
203
205
|
* @returns {{text:string, question:string, system:string, label:string, chars:number, truncated:boolean}}
|
|
204
206
|
*/
|
|
205
207
|
export function askPrompt(r, opts = {}) {
|
|
206
208
|
const label = SYSTEM_LABEL[r && r.system] || (r && r.system) || '未知体系';
|
|
207
209
|
const asked = String((opts && opts.question) || '').trim();
|
|
208
|
-
const question = asked || '
|
|
210
|
+
const question = asked || '请先用一句白话讲清这张盘的主线,再指出最值得注意的一两处,并说明每处的具体依据。';
|
|
209
211
|
const limit = Number.isFinite(opts && opts.maxChars) && opts.maxChars >= 400 ? opts.maxChars : 3000;
|
|
210
212
|
const raw = String((r && r.summary) || '').trim();
|
|
211
213
|
const truncated = raw.length > limit;
|
|
212
214
|
const summary = truncated ? `${raw.slice(0, limit)}…(摘要已截断)` : raw;
|
|
213
215
|
|
|
216
|
+
// 面板里已经算过一遍解析:直接取「直答 / 说人话」,避免模型开场另起一套说法
|
|
217
|
+
const it = (opts && typeof opts.interpret === 'object' && opts.interpret)
|
|
218
|
+
|| (r && typeof r.interpret === 'object' && r.interpret) || null;
|
|
219
|
+
const answer = String((it && it.answer) || '').trim();
|
|
220
|
+
const plain = String((it && it.plain) || '').trim();
|
|
221
|
+
const draft = [];
|
|
222
|
+
if (answer) draft.push(`直答:${answer}`);
|
|
223
|
+
if (plain) draft.push(`说人话:${plain}`);
|
|
224
|
+
|
|
214
225
|
const lines = [
|
|
215
226
|
'【命理追问】下面这张盘是命理插件刚排好的,请就着它回答我的问题;不用重新排盘,盘面字段已经够用。',
|
|
216
227
|
`体系:${label}`,
|
|
217
228
|
`所问:${question}`,
|
|
218
229
|
'',
|
|
230
|
+
...(draft.length ? [
|
|
231
|
+
'——开场结论(插件已按同一套规则算好,开场先沿用这句;措辞可改,口径别换)——',
|
|
232
|
+
...draft,
|
|
233
|
+
'',
|
|
234
|
+
] : []),
|
|
219
235
|
'——盘面摘要——',
|
|
220
236
|
summary || '(这张盘没有摘要字段,请先让我重新排一次。)',
|
|
221
237
|
'',
|
|
222
238
|
'——回答要求——',
|
|
223
|
-
'
|
|
224
|
-
|
|
225
|
-
|
|
239
|
+
'读者不懂命理。按下面的骨架答;任何一句他看不懂,就算没写完:',
|
|
240
|
+
draft.length
|
|
241
|
+
? '1) 开头一句结论:先沿用上面的「开场结论」,再用一句不用术语的话说清这张盘的主线——只读这一句也该有收获;'
|
|
242
|
+
: '1) 开头一句结论:不用术语,一句话说清这张盘的主线,只读这一句也该有收获;',
|
|
243
|
+
'2) 判断最多两条,按重要性排序,每条分四行写:',
|
|
244
|
+
' 盘面|只引盘面已有的字(具体的柱、宫或爻、字段名),不掺解释;',
|
|
245
|
+
' 白话|把上一行的术语当场翻成日常话——调候 / 化气 / 通关 / 空亡 / 争合 这类词必须落地,一个词只翻一次,不连续堆三个以上;',
|
|
246
|
+
' 倾向|说「更可能……」,讲清它在现实里大概意味着什么,用概率语气;',
|
|
247
|
+
' 边界|这条在什么情况下不成立;盘面没给的,直接写「这盘里看不出来」;',
|
|
248
|
+
'3) 收尾一句「如果只记一件事」:给一条能核对、能动手的观察点,并说明它出自上面哪条盘面依据;',
|
|
249
|
+
'4) 别把传统说法当成已证实的事实:不下绝对断言,也不用「肯定」「绝对」这类词,更不写「格局很好」「运程不顺」这种没有信息的空话。',
|
|
226
250
|
];
|
|
227
251
|
const text = lines.join('\n');
|
|
228
252
|
return { text, question, system: (r && r.system) || '', label, chars: text.length, truncated };
|
package/engine/geju.js
CHANGED
|
@@ -198,6 +198,36 @@ function specialPatterns(pillars, dayStem, monthBranch, strength, wuxing, gods)
|
|
|
198
198
|
return out;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
// ── 通关 ────────────────────────────────────────────────────
|
|
202
|
+
// 滴天髓「通关」只给取象(织女、牛郎),这里把结构说清:
|
|
203
|
+
// 用神与忌神两行在局中都真有分量、且相克时,五行里恰有一行能接上——克方所生、且能生被克方。
|
|
204
|
+
// 只报这一行在局中的分量,不判「关通了就吉」。
|
|
205
|
+
const TONGGUAN_MIN = 1.5;
|
|
206
|
+
export function tongguanOf(favor = [], avoid = [], wuxing = {}) {
|
|
207
|
+
const w = (el) => Math.round((Number(wuxing[el]) || 0) * 10) / 10;
|
|
208
|
+
for (const use of favor) {
|
|
209
|
+
if (w(use) < TONGGUAN_MIN) continue;
|
|
210
|
+
for (const foe of avoid) {
|
|
211
|
+
if (OVERCOMES[foe] !== use || w(foe) < TONGGUAN_MIN) continue;
|
|
212
|
+
const bridge = GENERATES[foe];
|
|
213
|
+
if (!bridge || GENERATES[bridge] !== use) continue;
|
|
214
|
+
const weight = w(bridge);
|
|
215
|
+
const bridgeIsAvoid = avoid.includes(bridge) && !favor.includes(bridge);
|
|
216
|
+
const level = bridgeIsAvoid ? '为忌' : weight >= TONGGUAN_MIN ? '可通' : weight > 0 ? '关弱' : '未通';
|
|
217
|
+
return {
|
|
218
|
+
use, foe, bridge,
|
|
219
|
+
weight,
|
|
220
|
+
present: weight > 0,
|
|
221
|
+
bridgeIsAvoid,
|
|
222
|
+
level,
|
|
223
|
+
usable: level === '可通',
|
|
224
|
+
note: '相战=用神与忌神两行各在局中 ≥1.5 分且相克;通关一行=克方所生、又能生用神的那一行(五行里唯一),只报局中分量,不据此断吉凶',
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
201
231
|
// ── 用神 / 喜忌 ──────────────────────────────────────────────
|
|
202
232
|
|
|
203
233
|
|
|
@@ -232,6 +262,7 @@ function yongshenOf(dayStem, monthBranch, strength, wuxing, branches = []) {
|
|
|
232
262
|
};
|
|
233
263
|
const favorTenGods = Array.from(new Set(favor.flatMap((el) => expand[roleName(el)] || [])));
|
|
234
264
|
const avoidTenGods = Array.from(new Set(avoid.flatMap((el) => expand[roleName(el)] || [])));
|
|
265
|
+
const tongguan = tongguanOf(favor, avoid, wuxing);
|
|
235
266
|
|
|
236
267
|
const detail = [
|
|
237
268
|
`日主${dayStem}${me},${strength.level}(${strength.score} 分):`,
|
|
@@ -242,6 +273,15 @@ function yongshenOf(dayStem, monthBranch, strength, wuxing, branches = []) {
|
|
|
242
273
|
: '身中和,先取调候与通关,喜忌随大运流年动态变化。',
|
|
243
274
|
`调候:${climate.label}(${(climate.evidence || []).join('、')})${climate.need ? `,取${climate.need.stem}${climate.need.element},忌${climate.avoidElement}` : ',气候平和'}。`,
|
|
244
275
|
qt ? `穷通宝鉴:${qt.month}${dayStem}${me},用神${qt.yong.join('、')}(首取${qt.primary})。` : '',
|
|
276
|
+
tongguan
|
|
277
|
+
? `通关:忌${tongguan.foe}克用${tongguan.use},五行以${tongguan.bridge}为关;局中${tongguan.level === '可通'
|
|
278
|
+
? `${tongguan.bridge} ${tongguan.weight} 分,关可通`
|
|
279
|
+
: tongguan.level === '关弱'
|
|
280
|
+
? `仅 ${tongguan.bridge} ${tongguan.weight} 分,关弱`
|
|
281
|
+
: tongguan.level === '为忌'
|
|
282
|
+
? `${tongguan.bridge} ${tongguan.weight} 分但此行为忌,通关力打折`
|
|
283
|
+
: `未见${tongguan.bridge},关未通`}(只作结构提示,不据此断吉凶)。`
|
|
284
|
+
: '',
|
|
245
285
|
arb.text,
|
|
246
286
|
arb.clash ? '取舍:本盘按「调候优先、扶抑次之」取用;若你更认同扶抑派,可把调候当环境偏好,强弱判断不受影响。' : '',
|
|
247
287
|
].filter(Boolean).join('');
|
|
@@ -258,6 +298,7 @@ function yongshenOf(dayStem, monthBranch, strength, wuxing, branches = []) {
|
|
|
258
298
|
tiaohou: { ...climate, ...arb, element: climate.need ? climate.need.element : null },
|
|
259
299
|
climate,
|
|
260
300
|
qt,
|
|
301
|
+
tongguan,
|
|
261
302
|
detail,
|
|
262
303
|
};
|
|
263
304
|
}
|
package/engine/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 统一入口:解析参数、分派到各体系、生成可读摘要
|
|
2
2
|
import { buildBazi } from './bazi.js';
|
|
3
|
-
import { buildLiuyao, tossLines, bodyLabels, yongshenOf, yongshenStrength, hexagramStrength, jinTuiPower } from './liuyao.js';
|
|
4
|
-
import { buildZiwei, BRIGHTNESS, flowOf, asOfParts } from './ziwei.js';
|
|
3
|
+
import { buildLiuyao, tossLines, bodyLabels, yongshenOf, yongshenStrength, fushenAccess, hexagramStrength, jinTuiPower } from './liuyao.js';
|
|
4
|
+
import { buildZiwei, BRIGHTNESS, flowOf, asOfParts, ZIWEI_SCHOOL_DEFAULTS } from './ziwei.js';
|
|
5
5
|
import { drawTarot } from './tarot.js';
|
|
6
6
|
import { buildMeihua } from './meihua.js';
|
|
7
7
|
import { buildDaliuren } from './daliuren.js';
|
|
@@ -55,6 +55,21 @@ function requireGender(value, who = '') {
|
|
|
55
55
|
throw fail('E_INPUT', `${who ? who + '的' : ''}gender 必须是 male 或 female${got};性别决定大运/大限顺逆,不能靠默认值`);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
const ZIWEI_SCHOOL_KEYS = [
|
|
59
|
+
'brightnessSchool', 'gengYearSihua', 'tianmaSchool', 'tiankongSchool', 'jiekongSchool',
|
|
60
|
+
'kuiyueSchool', 'tianshiTianshang', 'changshengSchool', 'lateZishi',
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
/** 紫微流派参数原样透传;非法值交给引擎按默认口径回退 */
|
|
64
|
+
function ziweiSchoolArgs(args) {
|
|
65
|
+
const out = {};
|
|
66
|
+
for (const key of ZIWEI_SCHOOL_KEYS) {
|
|
67
|
+
if (args[key] !== undefined) out[key] = args[key];
|
|
68
|
+
}
|
|
69
|
+
if (args.schools && typeof args.schools === 'object') out.schools = args.schools;
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
/** 解析 'YYYY-MM-DDTHH:mm' / 'YYYY-MM-DD HH:mm' / 'YYYY-MM-DD' */
|
|
59
74
|
export function parseDateTime(text) {
|
|
60
75
|
if (typeof text !== 'string' || !text.trim()) {
|
|
@@ -148,7 +163,23 @@ function baziText(r) {
|
|
|
148
163
|
}
|
|
149
164
|
|
|
150
165
|
function ziweiText(r) {
|
|
151
|
-
|
|
166
|
+
// 闰月:日期按「闰X月」原样写;换算过起盘月时再附一句,免得把 21 日写成「闰5月21日」
|
|
167
|
+
const rawMonth = r.lunar.monthRaw || r.lunar.month;
|
|
168
|
+
const lunarText = `${r.lunar.year}年${r.lunar.isLeap ? `闰${rawMonth}` : r.lunar.month}月${r.lunar.day}日`
|
|
169
|
+
+ (r.lunar.isLeap && r.lunar.month !== rawMonth ? `(按${r.lunar.month}月起盘)` : '');
|
|
170
|
+
// 只在显式改动流派时补一行,默认盘不增加噪音
|
|
171
|
+
const schoolLine = (() => {
|
|
172
|
+
if (!r.schools) return '';
|
|
173
|
+
const labels = {
|
|
174
|
+
brightnessSchool: '亮度', gengYearSihua: '庚干四化', tianmaSchool: '天马', tiankongSchool: '天空',
|
|
175
|
+
jiekongSchool: '截空', kuiyueSchool: '魁钺', tianshiTianshang: '天伤天使',
|
|
176
|
+
changshengSchool: '长生', lateZishi: '晚子时',
|
|
177
|
+
};
|
|
178
|
+
const diff = Object.entries(labels)
|
|
179
|
+
.filter(([key]) => r.schools[key] && r.schools[key] !== ZIWEI_SCHOOL_DEFAULTS[key])
|
|
180
|
+
.map(([key, label]) => `${label} ${r.schools[key]}`);
|
|
181
|
+
return diff.length ? `【流派】${diff.join(' ')}` : '';
|
|
182
|
+
})();
|
|
152
183
|
const starText = (stars) => (stars.length
|
|
153
184
|
? stars.map((s) => `${s.name}${s.mutagen ? `化${s.mutagen}` : ''}`).join(' ')
|
|
154
185
|
: '—');
|
|
@@ -199,6 +230,7 @@ function ziweiText(r) {
|
|
|
199
230
|
`【命宫】${r.soul.branch}(${r.soul.ganzhi}·${r.soul.nayin}) 【身宫】${r.body.branch}(落${r.body.lands}) 【五行局】${r.bureau.name}`,
|
|
200
231
|
`【紫微】${r.ziwei} 【天府】${r.tianfu} 【命主】${r.lifeMaster} 【身主】${r.bodyMaster}`,
|
|
201
232
|
`【大限】${r.daxian.rule},${r.daxian.startAge}岁起 【小限】${r.xiaoxianRule.direction}`,
|
|
233
|
+
schoolLine,
|
|
202
234
|
`【昌曲】文昌${r.changqu.wenchang.branch}(${r.changqu.wenchang.palace}${r.changqu.wenchang.mutagen ? '·化' + r.changqu.wenchang.mutagen : ''}) 文曲${r.changqu.wenqu.branch}(${r.changqu.wenqu.palace}${r.changqu.wenqu.mutagen ? '·化' + r.changqu.wenqu.mutagen : ''})`,
|
|
203
235
|
` ${r.changqu.relation} 入命三方:${r.changqu.inSoulSanfang ? '是' : '否'} 吸收面:${r.changqu.absorb.label}(${r.changqu.absorb.reason})`,
|
|
204
236
|
flyLine,
|
|
@@ -355,6 +387,11 @@ function liuyaoText(r) {
|
|
|
355
387
|
const m = strength.month && strength.month.branch ? `月建 ${strength.month.branch}(${strength.month.element}) ` : '';
|
|
356
388
|
out.push(`用神旺衰:${m}${strength.labels.join(' · ')}`);
|
|
357
389
|
}
|
|
390
|
+
// 伏神出伏条件:只在用神不上卦时出现;只归条件档,不给应期
|
|
391
|
+
const chufu = r.fushenAccess;
|
|
392
|
+
if (chufu && Array.isArray(chufu.labels) && chufu.labels.length) {
|
|
393
|
+
out.push(`伏神出伏:${chufu.labels.join(' · ')}`);
|
|
394
|
+
}
|
|
358
395
|
} else {
|
|
359
396
|
out.push('用神:未取(未指明所问之事;用「询问方向」指定后即按六亲取用)');
|
|
360
397
|
}
|
|
@@ -421,7 +458,7 @@ export function compute(args = {}) {
|
|
|
421
458
|
};
|
|
422
459
|
const asOf = checkAsOf(args.asOf);
|
|
423
460
|
const other = r.system === 'bazi'
|
|
424
|
-
? buildZiwei({ ...common, leapAsNext: args.leapAsNext === true, asOf })
|
|
461
|
+
? buildZiwei({ ...common, leapMonth: args.leapMonth, leapAsNext: args.leapAsNext === true, ...ziweiSchoolArgs(args), asOf })
|
|
425
462
|
: buildBazi({ ...common, lateZi: args.lateZi !== false, nowYear: asOf ? asOf.year : undefined });
|
|
426
463
|
r.cross = crossCheck(r.system === 'bazi' ? r : other, r.system === 'ziwei' ? r : other);
|
|
427
464
|
if (r.interpret) {
|
|
@@ -474,8 +511,9 @@ export function compute(args = {}) {
|
|
|
474
511
|
export function interpretText(i) {
|
|
475
512
|
if (!i) return '';
|
|
476
513
|
const out = [`【${i.title || '解析与详解'}】${i.ask && i.ask.echo ? i.ask.echo : ''}`];
|
|
477
|
-
//
|
|
478
|
-
|
|
514
|
+
// 直答放在最前:先回答「你问的事」,再说依据,避免读者读到最后才知道结论;
|
|
515
|
+
// 没填问题时就不该喊「答所问」——改为「先看结论」,否则是一句假话
|
|
516
|
+
if (i.answer) out.push(`▶ ${i.ask && i.ask.question ? '先答所问' : '先看结论'}:${i.answer}`);
|
|
479
517
|
if (i.plain) out.push(`▶ 说人话:${i.plain}`);
|
|
480
518
|
if (i.lead) out.push(i.lead);
|
|
481
519
|
// 一页读懂:报告顶部的五条结论,先给读的人一个「整体形状」
|
|
@@ -551,7 +589,9 @@ function computeRaw(args = {}) {
|
|
|
551
589
|
const result = buildZiwei({
|
|
552
590
|
...dt,
|
|
553
591
|
gender: requireGender(args.gender),
|
|
592
|
+
leapMonth: args.leapMonth,
|
|
554
593
|
leapAsNext: args.leapAsNext === true,
|
|
594
|
+
...ziweiSchoolArgs(args),
|
|
555
595
|
longitude: typeof args.longitude === 'number' ? args.longitude : undefined,
|
|
556
596
|
address: typeof args.address === 'string' ? args.address : undefined,
|
|
557
597
|
geo: args.geo && typeof args.geo === 'object' ? args.geo : undefined,
|
|
@@ -702,6 +742,9 @@ function computeRaw(args = {}) {
|
|
|
702
742
|
if (ug.liuqin) {
|
|
703
743
|
const strength = yongshenStrength(result, ug, { monthBranch, dayBranch: result.dayGanZhi.branch });
|
|
704
744
|
if (strength) result.useGodStrength = strength;
|
|
745
|
+
// 伏神出伏条件:飞伏生克叠月建日辰,只判条件、不出应期
|
|
746
|
+
const chufu = fushenAccess(result, ug, { monthBranch, dayBranch: result.dayGanZhi.branch });
|
|
747
|
+
if (chufu) result.fushenAccess = chufu;
|
|
705
748
|
}
|
|
706
749
|
if (args.seed !== undefined && args.seed !== null && args.seed !== '') result.seed = Number(args.seed);
|
|
707
750
|
return { ...result, ...(asOf ? { asOf } : {}), summary: liuyaoText(result) };
|
|
@@ -802,7 +845,17 @@ function computeRaw(args = {}) {
|
|
|
802
845
|
return { ...result, precision: dt.precision, summary: trendText(result) };
|
|
803
846
|
}
|
|
804
847
|
if (system === 'fengshui') {
|
|
805
|
-
const action = args.action === 'advise' ? 'advise' : 'natal';
|
|
848
|
+
const action = args.action === 'advise' ? 'advise' : args.action === 'xuankong' ? 'xuankong' : 'natal';
|
|
849
|
+
if (action === 'xuankong') {
|
|
850
|
+
const result = buildFengshui({
|
|
851
|
+
action,
|
|
852
|
+
sitting: args.sitting,
|
|
853
|
+
facing: args.facing,
|
|
854
|
+
...(args.period !== undefined ? { period: args.period } : {}),
|
|
855
|
+
...(Number.isInteger(args.fortuneYear) ? { fortuneYear: args.fortuneYear } : {}),
|
|
856
|
+
});
|
|
857
|
+
return { ...result, summary: fengshuiText(result) };
|
|
858
|
+
}
|
|
806
859
|
if (action === 'advise') {
|
|
807
860
|
const result = buildFengshui({
|
|
808
861
|
action,
|
|
@@ -841,6 +894,8 @@ function computeRaw(args = {}) {
|
|
|
841
894
|
givenName: args.givenName,
|
|
842
895
|
limit: args.limit,
|
|
843
896
|
charElements: args.charElements && typeof args.charElements === 'object' ? args.charElements : undefined,
|
|
897
|
+
strokeOverrides: args.strokeOverrides && typeof args.strokeOverrides === 'object' ? args.strokeOverrides : undefined,
|
|
898
|
+
surnameStrokes: args.surnameStrokes && typeof args.surnameStrokes === 'object' ? args.surnameStrokes : undefined,
|
|
844
899
|
...birth,
|
|
845
900
|
...(args.gender === 'male' || args.gender === 'female' ? { gender: args.gender } : {}),
|
|
846
901
|
...(args.datetime ? {
|
|
@@ -1019,6 +1074,25 @@ function trendText(r) {
|
|
|
1019
1074
|
|
|
1020
1075
|
/** 风水文本 */
|
|
1021
1076
|
function fengshuiText(r) {
|
|
1077
|
+
if (r.meta.action === 'xuankong') {
|
|
1078
|
+
const p = r.meta.period;
|
|
1079
|
+
const cell = (palace) => {
|
|
1080
|
+
const x = r.palaces.find((y) => y.palace === palace);
|
|
1081
|
+
return `${palace}${x.yun}/${x.shan}/${x.xiang}`;
|
|
1082
|
+
};
|
|
1083
|
+
const row = (names) => names.map(cell).join(' ');
|
|
1084
|
+
const w = r.wangShanWangXiang;
|
|
1085
|
+
return [
|
|
1086
|
+
`【玄空】${r.meta.periodLabel} 坐 ${r.meta.sitting} 向 ${r.meta.facing}`,
|
|
1087
|
+
`【入中】山盘 ${r.meta.shanCenter.star}${r.meta.shanCenter.name}(${r.meta.shanCenter.fly},${r.meta.shanCenter.basis});向盘 ${r.meta.xiangCenter.star}${r.meta.xiangCenter.name}(${r.meta.xiangCenter.fly},${r.meta.xiangCenter.basis})`,
|
|
1088
|
+
`【九宫】${row(['巽', '离', '坤'])}|${row(['震', '中', '兑'])}|${row(['艮', '坎', '乾'])}`,
|
|
1089
|
+
`【当运】山星${p}到${r.dangYun.shan.text};向星${p}到${r.dangYun.xiang.text}`,
|
|
1090
|
+
`【表法】${w.table.length ? w.table.join('、') : '无'};${w.note}`,
|
|
1091
|
+
`【合十】山星合十 ${r.heshi.shan.join('、') || '无'};向星合十 ${r.heshi.xiang.join('、') || '无'}`,
|
|
1092
|
+
...(r.meta.note ? [`※ ${r.meta.note}`] : []),
|
|
1093
|
+
`※ ${r.disclaimer}`,
|
|
1094
|
+
].join('\n');
|
|
1095
|
+
}
|
|
1022
1096
|
if (r.meta.action === 'advise') {
|
|
1023
1097
|
const match = r.mingMatch
|
|
1024
1098
|
? [`【命宅配】${r.mingMatch.text}`]
|
|
@@ -1051,7 +1125,11 @@ function nameText(r) {
|
|
|
1051
1125
|
if (r.action === 'generate') {
|
|
1052
1126
|
return [
|
|
1053
1127
|
`【候选名】姓 ${r.surname}(${r.gender},喜用 ${r.bazi.favorable.join('、')})${r.bazi.basis}`,
|
|
1054
|
-
...r.candidates.map((c) =>
|
|
1128
|
+
...r.candidates.map((c) => {
|
|
1129
|
+
const wu = c.wuge && c.wuge.available ? ` 五格 ${c.wuge.summary}` : '';
|
|
1130
|
+
const ph = c.phonetics && c.phonetics.available ? ` 音韵 ${c.phonetics.level}(${c.phonetics.tonePattern})` : '';
|
|
1131
|
+
return `${c.rank}. ${c.name} ${c.chars.map((x) => `${x.char}(${x.element})${x.meaning}`).join(' ')}${wu}${ph}`;
|
|
1132
|
+
}),
|
|
1055
1133
|
...r.notes.map((n) => `※ ${n}`),
|
|
1056
1134
|
].join('\n');
|
|
1057
1135
|
}
|
|
@@ -1064,10 +1142,20 @@ function nameText(r) {
|
|
|
1064
1142
|
: '';
|
|
1065
1143
|
return `· ${c.char}:${el}|${fit}|${c.meaning}${sug}`;
|
|
1066
1144
|
};
|
|
1145
|
+
const wu = r.wuge;
|
|
1146
|
+
const wugeLine = wu && wu.available
|
|
1147
|
+
? `【五格】${wu.strokes.map((x) => `${x.char}${x.strokes}画`).join('+')}|${wu.summary}|三才 ${wu.sancai.text}`
|
|
1148
|
+
: `【五格】${wu ? wu.reason : '未算'}`;
|
|
1149
|
+
const ph = r.phonetics;
|
|
1150
|
+
const phLine = ph && ph.available
|
|
1151
|
+
? `【音韵】${ph.chars.map((x) => `${x.char}${x.py}${x.tone}`).join(' ')}|声调 ${ph.tonePattern}|${ph.level}${ph.issues.length ? `:${ph.issues.map((i) => i.text).join(';')}` : ''}`
|
|
1152
|
+
: `【音韵】${ph ? ph.reason : '未算'}`;
|
|
1067
1153
|
return [
|
|
1068
1154
|
`【名字评估】${r.fullName}`,
|
|
1069
1155
|
...r.chars.map(charLine),
|
|
1070
1156
|
r.bazi ? `【八字】日主 ${r.bazi.dayMaster && r.bazi.dayMaster.stem ? `${r.bazi.dayMaster.stem}(${r.bazi.dayMaster.element})` : r.bazi.dayMaster},旺衰 ${r.bazi.strength.level}(${r.bazi.strength.score}),${r.bazi.basis}` : '',
|
|
1157
|
+
wugeLine,
|
|
1158
|
+
phLine,
|
|
1071
1159
|
`【结论】${r.verdict}`,
|
|
1072
1160
|
...r.notes.map((n) => `※ ${n}`),
|
|
1073
1161
|
].filter(Boolean).join('\n');
|