Files
Api/DouyinApi.Services/DailyFortune/DailyFortuneFallbackGenerator.cs
2025-11-09 18:41:10 +08:00

225 lines
9.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using DouyinApi.Model.DailyFortune;
using DouyinApi.Services.Naming;
namespace DouyinApi.Services.DailyFortune;
internal sealed class DailyFortuneFallbackGenerator
{
private static readonly (string Key, string Title, string Focus)[] DimensionDefinitions =
{
("career", "事业运", "职场推进"),
("wealth", "财务运", "资源调度"),
("relationship", "情感运", "情绪表达"),
("health", "健康运", "身心节奏"),
("social", "人际运", "团队协同"),
("inspiration", "灵感运", "学习洞察")
};
private static readonly Dictionary<string, string> NourishMapping = new(StringComparer.Ordinal)
{
["木"] = "水",
["火"] = "木",
["土"] = "火",
["金"] = "土",
["水"] = "金"
};
private static readonly Dictionary<string, (string[] Colors, string[] Props, string[] Directions)> ElementMappings = new(StringComparer.Ordinal)
{
["木"] = (new[] { "青绿", "墨绿" }, new[] { "绿松石", "木质手串" }, new[] { "正东", "东南" }),
["火"] = (new[] { "朱红", "橘色" }, new[] { "朱砂饰品", "暖色围巾" }, new[] { "正南", "东南" }),
["土"] = (new[] { "杏色", "暖棕" }, new[] { "陶土摆件", "黄玉戒" }, new[] { "中央", "西南" }),
["金"] = (new[] { "米金", "象牙白" }, new[] { "金属耳饰", "白水晶" }, new[] { "正西", "西北" }),
["水"] = (new[] { "深蓝", "青蓝" }, new[] { "黑曜石", "海蓝宝" }, new[] { "正北", "东北" })
};
private static readonly (string Label, string Period)[] TimeSlots =
{
("拂晓", "05:00-07:00"),
("辰时", "07:00-09:00"),
("巳时", "09:00-11:00"),
("午时", "11:00-13:00"),
("申时", "15:00-17:00"),
("酉时", "17:00-19:00"),
("夜间", "19:00-21:00")
};
public DailyFortuneInsight Generate(DailyFortuneRequest request, BaziProfile birthProfile, BaziProfile todayProfile, DateTime fortuneMoment)
{
var seedSource = $"{request.BirthCity}-{request.BirthDate}-{request.BirthTime}-{fortuneMoment:yyyyMMdd}";
var seed = Math.Abs(seedSource.GetHashCode());
var random = new Random(seed);
var weakElement = ResolveWeakElement(birthProfile, todayProfile);
var dimensions = DimensionDefinitions
.Select((definition, index) =>
{
var swing = ((seed >> (index + 1)) % 21) - 10;
var baseScore = 70 + swing;
if (!birthProfile.IsBalanced && string.Equals(definition.Key, "health", StringComparison.Ordinal))
{
baseScore -= 2;
}
var score = Math.Clamp(baseScore, 48, 96);
var trend = swing switch
{
> 3 => "up",
< -3 => "down",
_ => "steady"
};
var insight = $"{definition.Focus}受{weakElement}气场牵引,今日呈现{DescribeTrend(trend)}走势。";
var suggestion = BuildSuggestion(definition.Key, weakElement, todayProfile, trend);
return new FortuneDimension
{
Key = definition.Key,
Title = definition.Title,
Score = score,
Trend = trend,
Insight = insight,
Suggestion = suggestion
};
})
.ToList();
var luckyGuide = BuildLuckyGuide(weakElement, todayProfile, random);
var summary = BuildSummary(dimensions, luckyGuide, fortuneMoment);
var narrative = BuildNarrative(request, dimensions, luckyGuide, weakElement, todayProfile, fortuneMoment);
return new DailyFortuneInsight
{
Dimensions = dimensions,
LuckyGuide = luckyGuide,
Summary = summary,
Narrative = narrative
};
}
private static LuckyGuide BuildLuckyGuide(string weakElement, BaziProfile todayProfile, Random random)
{
var target = TargetElement(weakElement);
if (!ElementMappings.TryGetValue(target, out var mapping))
{
mapping = ElementMappings["木"];
}
var slotIndex = random.Next(TimeSlots.Length);
var slotB = (slotIndex + 3) % TimeSlots.Length;
return new LuckyGuide
{
Element = target,
Colors = mapping.Colors,
Props = mapping.Props,
Directions = mapping.Directions,
Activities = BuildActivities(target, weakElement, todayProfile),
BestTimeSlots = new[]
{
new LuckyTimeSlot
{
Label = TimeSlots[slotIndex].Label,
Period = TimeSlots[slotIndex].Period,
Reason = $"与日支{todayProfile.DayBranch}气息谐和"
},
new LuckyTimeSlot
{
Label = TimeSlots[slotB].Label,
Period = TimeSlots[slotB].Period,
Reason = "利于沉浸式处理重点事务"
}
}
};
}
private static IReadOnlyList<string> BuildActivities(string element, string weakElement, BaziProfile todayProfile)
{
var activities = new List<string>
{
$"携带{element}系小物件,稳住气场",
$"在{todayProfile.MonthBranch}月令的主题上做一点复盘"
};
if (!string.Equals(element, weakElement, StringComparison.Ordinal))
{
activities.Add($"补足{weakElement}之气,宜接触自然或深呼吸练习");
}
activities.Add("保持饮水与拉伸,照顾身体节奏");
return activities;
}
private static string BuildSummary(IEnumerable<FortuneDimension> dimensions, LuckyGuide guide, DateTime fortuneMoment)
{
var avg = (int)Math.Round(dimensions.Average(x => x.Score));
var strongest = dimensions.OrderByDescending(x => x.Score).First();
var weakest = dimensions.OrderBy(x => x.Score).First();
return $"{fortuneMoment:yyyy年M月d日}整体指数约为{avg}分,{strongest.Title}是今日亮点,{weakest.Title}需留意。以{guide.Element}元素入场,能更顺利地衔接节奏。";
}
private static string BuildNarrative(
DailyFortuneRequest request,
IEnumerable<FortuneDimension> dimensions,
LuckyGuide guide,
string weakElement,
BaziProfile todayProfile,
DateTime fortuneMoment)
{
var focus = dimensions
.Where(x => x.Trend == "up")
.Select(x => x.Title)
.DefaultIfEmpty("核心主题")
.ToArray();
return $"结合{request.BirthCity}出生的先天命盘与今日{todayProfile.DayStem}{todayProfile.DayBranch}能量,建议把握{string.Join("", focus)}。" +
$"多使用{string.Join("", guide.Colors)}等{guide.Element}系元素,可温和弥补{weakElement}不足。" +
$"若能在{string.Join("", guide.BestTimeSlots.Select(s => s.Label))}安排重要事项,将更容易与当日天象共振。";
}
private static string BuildSuggestion(string dimensionKey, string weakElement, BaziProfile todayProfile, string trend)
{
return dimensionKey switch
{
"career" => trend == "up"
? "适合推进关键节点善用会议前的5分钟整理要点。"
: "避免一次承担过多议题,可把结论拆分分批落实。",
"wealth" => trend == "up"
? "梳理现金流或理财计划,集中处理应收款。"
: "控制冲动型消费,预算留白以应对变量。",
"relationship" => trend == "up"
? "表达时保持真诚与柔软,分享情绪而非立场。"
: "先倾听再回应,避免在情绪波动时做决定。",
"health" => trend == "up"
? "保持稳定作息,适合做轻量有氧或拉伸。"
: "补水与热身不可省,防止因节奏紊乱带来疲惫感。",
"social" => trend == "up"
? "在团队中扮演连接者的角色,协调资源会有惊喜。"
: "不必事事亲自上阵,学会授权也能稳住局面。",
"inspiration" => trend == "up"
? "灵感度高,可记录随手闪现的想法并快速试错。"
: "转换学习方式,利用碎片化时间吸收新知。",
_ => $"保持呼吸节奏,善用{weakElement}相关意象维持专注度。"
};
}
private static string DescribeTrend(string trend) =>
trend switch
{
"up" => "回升",
"down" => "略有波动",
_ => "趋于平稳"
};
private static string ResolveWeakElement(BaziProfile birthProfile, BaziProfile todayProfile) =>
birthProfile.WeakElements.FirstOrDefault() ??
todayProfile.WeakElements.FirstOrDefault() ??
"木";
private static string TargetElement(string weakElement) =>
NourishMapping.TryGetValue(weakElement, out var target) ? target : weakElement;
}