This commit is contained in:
fuenmao
2026-09-23 18:19:40 +08:00
parent db7bd2231a
commit 6a622c9bd5
8 changed files with 534 additions and 247 deletions
@@ -24,6 +24,7 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
private readonly SqlSugarRepository<HealthProfile> _profileRepository;
private readonly SqlSugarRepository<HealthModuleSelection> _moduleRepository;
private readonly SqlSugarRepository<HealthPlan> _planRepository;
private readonly MattressComfortService _mattressComfort;
private readonly IHttpContextAccessor _httpContextAccessor;
public HealthAppService(
@@ -31,12 +32,14 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
SqlSugarRepository<HealthProfile> profileRepository,
SqlSugarRepository<HealthModuleSelection> moduleRepository,
SqlSugarRepository<HealthPlan> planRepository,
MattressComfortService mattressComfort,
IHttpContextAccessor httpContextAccessor)
{
_userRepository = userRepository;
_profileRepository = profileRepository;
_moduleRepository = moduleRepository;
_planRepository = planRepository;
_mattressComfort = mattressComfort;
_httpContextAccessor = httpContextAccessor;
}
@@ -114,21 +117,12 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
bmiStatus = bmi < 18.5m ? "偏瘦" : bmi <= 23.9m ? "正常" : bmi <= 27.9m ? "超重" : "肥胖";
}
PersonalizedSceneConfig? sceneConfig = null;
if (!string.IsNullOrWhiteSpace(profile?.CustomSceneConfigJson))
var tcmResult = TcmConstitutionRules.Read(profile);
MattressComfortOutput? mattressComfort = null;
if (tcmResult.HasCompleted)
{
try
{
sceneConfig = JsonSerializer.Deserialize<PersonalizedSceneConfig>(profile.CustomSceneConfigJson);
}
catch
{
}
}
if (sceneConfig == null && hasCompletedAssessment)
{
var answers = JsonSerializer.Deserialize<HealthAssessmentAnswers>(profile!.AssessmentJson!) ?? new HealthAssessmentAnswers();
sceneConfig = CalculatePersonalizedSceneConfig(answers, MapProfile(profile));
mattressComfort = await _mattressComfort.GetAsync(profile);
MattressComfortService.ApplyTo(tcmResult.SceneConfig, mattressComfort);
}
return new HealthDashboardOutput
@@ -140,7 +134,9 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
HasCompletedAssessment = hasCompletedAssessment,
Bmi = bmi,
BmiStatus = bmiStatus,
SceneConfig = sceneConfig
SceneConfig = tcmResult.HasCompleted ? tcmResult.SceneConfig : null,
TcmResult = tcmResult,
MattressComfort = mattressComfort
};
}
@@ -156,8 +152,7 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
{
HasCompletedAssessment = false,
BasicInfo = new SaveHealthProfileInput(),
Answers = new HealthAssessmentAnswers(),
SceneConfig = new PersonalizedSceneConfig()
Answers = new HealthAssessmentAnswers()
};
}
@@ -166,9 +161,6 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
: JsonSerializer.Deserialize<HealthAssessmentAnswers>(profile.AssessmentJson) ?? new HealthAssessmentAnswers();
var hasCompleted = !string.IsNullOrWhiteSpace(profile.AssessmentJson);
var sceneConfig = string.IsNullOrWhiteSpace(profile.CustomSceneConfigJson)
? CalculatePersonalizedSceneConfig(answers, MapProfile(profile))
: JsonSerializer.Deserialize<PersonalizedSceneConfig>(profile.CustomSceneConfigJson) ?? new PersonalizedSceneConfig();
decimal? bmi = null;
var bmiStatus = "正常";
@@ -184,7 +176,6 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
HasCompletedAssessment = hasCompleted,
BasicInfo = MapProfile(profile),
Answers = answers,
SceneConfig = sceneConfig,
Bmi = bmi,
BmiStatus = bmiStatus
};
@@ -198,21 +189,18 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
ValidateAssessment(input);
var userId = GetCurrentUserId();
var profile = await _profileRepository.GetFirstAsync(p => p.UserId == userId);
var calculatedConfig = CalculatePersonalizedSceneConfig(input.Answers, input.BasicInfo);
if (profile == null)
{
profile = new HealthProfile { UserId = userId };
ApplyProfile(profile, input.BasicInfo);
profile.AssessmentJson = JsonSerializer.Serialize(input.Answers);
profile.CustomSceneConfigJson = JsonSerializer.Serialize(calculatedConfig);
await _profileRepository.InsertAsync(profile);
}
else
{
ApplyProfile(profile, input.BasicInfo);
profile.AssessmentJson = JsonSerializer.Serialize(input.Answers);
profile.CustomSceneConfigJson = JsonSerializer.Serialize(calculatedConfig);
await _profileRepository.UpdateAsync(profile);
}
@@ -240,7 +228,7 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
UserId = userId,
Version = maxVersion + 1,
Title = $"个性化健康方案 v{maxVersion + 1}.0",
Summary = BuildAssessmentPlanSummary(profile, input.Answers),
Summary = BuildAssessmentPlanSummary(input.Answers),
ParametersJson = JsonSerializer.Serialize(planItems),
IsCurrent = true
};
@@ -393,7 +381,7 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
ModuleCode = module,
Title = "睡眠质量",
Target = profile?.AverageSleepHours is < 7 ? "逐步达到每晚 7–8 小时" : "保持每晚 7–8 小时",
Advice = "固定入睡时间,睡前一小时降低灯光亮度,并使用卧室舒睡场景。"
Advice = "固定入睡时间,睡前一小时减少屏幕使用,保持卧室安静。"
});
break;
case "exercise":
@@ -431,7 +419,7 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
bmiText = $" 当前 BMI 约为 {bmi}。";
}
return $"方案依据您选择的 {modules.Count} 个健康板块生成。{bmiText}第一版参数用于健康管理演示,不替代专业医疗诊断。";
return $"方案依据您选择的 {modules.Count} 个健康板块生成。{bmiText}建议仅用于日常健康管理演示,不替代专业医疗诊断。";
}
private static HealthPlanOutput MapPlan(HealthPlan plan) => new()
@@ -439,7 +427,9 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
Id = plan.Id,
Version = plan.Version,
Title = plan.Title,
Summary = plan.Summary,
Summary = plan.Summary.Contains("智能家具启动参数", StringComparison.Ordinal)
? "已完成健康评估,可查看睡眠、运动与饮食建议。设备启动参数仅由中医四诊体质结果生成。"
: plan.Summary,
CreateTime = plan.CreateTime,
Items = JsonSerializer.Deserialize<List<HealthPlanItemOutput>>(plan.ParametersJson) ?? []
};
@@ -498,166 +488,11 @@ public sealed class HealthAppService : IDynamicApiController, ITransient
throw Oops.Oh($"请完成“{label}”问题");
}
private static PersonalizedSceneConfig CalculatePersonalizedSceneConfig(HealthAssessmentAnswers answers, SaveHealthProfileInput basicInfo)
private static string BuildAssessmentPlanSummary(HealthAssessmentAnswers answers)
{
var config = new PersonalizedSceneConfig();
switch (answers.TemperaturePreference)
{
case "heat_fear":
config.TargetHumidity = 48;
config.FanLevel = "2挡";
config.MattressTargetTemp = 32m;
config.MattressHeatingLevel = 1;
break;
case "cold_fear":
config.TargetHumidity = 58;
config.FanLevel = "1挡";
config.MattressTargetTemp = 37.5m;
config.MattressHeatingLevel = 3;
break;
default:
config.TargetHumidity = 52;
config.FanLevel = "1挡";
config.MattressTargetTemp = 35.5m;
config.MattressHeatingLevel = 2;
break;
}
switch (answers.FallingAsleepSpeed)
{
case "hard":
config.LightBrightnessPercent = 8;
config.LightColorTemperatureKelvin = 2200;
config.FragranceLevel = 2;
config.EnableSpeakerSleepMode = true;
config.SpeakerAudioMode = "助眠白噪音";
config.SpeakerVolumePercent = 12;
break;
case "fast":
config.LightBrightnessPercent = 25;
config.LightColorTemperatureKelvin = 3000;
config.EnableSpeakerSleepMode = false;
config.SpeakerAudioMode = "关闭助眠音频";
config.SpeakerVolumePercent = 10;
break;
default:
config.LightBrightnessPercent = 18;
config.LightColorTemperatureKelvin = 2700;
config.FragranceLevel = 1;
config.EnableSpeakerSleepMode = true;
config.SpeakerAudioMode = "舒缓轻音乐";
config.SpeakerVolumePercent = 15;
break;
}
switch (answers.SleepDuration)
{
case "<6":
config.LightBrightnessPercent -= 3;
config.SpeakerVolumePercent += 3;
config.FragranceLevel++;
break;
case "6-7":
config.LightBrightnessPercent -= 1;
config.SpeakerVolumePercent += 1;
break;
case ">8":
config.LightBrightnessPercent += 2;
config.SpeakerVolumePercent -= 2;
break;
}
if (answers.MuscleFatigueArea is "waist_back" or "back_neck" || answers.MattressPreference == "firm")
{
config.MattressLeftFirmness = 7;
config.MattressRightFirmness = 7;
config.MattressMode = "智能腰托";
}
else if (answers.MattressPreference == "soft")
{
config.MattressLeftFirmness = 4;
config.MattressRightFirmness = 4;
config.MattressMode = "瑜伽助眠";
}
else
{
config.MattressLeftFirmness = answers.MuscleFatigueArea == "legs" ? 6 : 5;
config.MattressRightFirmness = answers.MuscleFatigueArea == "legs" ? 6 : 5;
config.MattressMode = "智能适配";
}
config.DehumidifierMode = answers.NightAwakening == "none" ? "智能" : "睡眠";
if (answers.NightAwakening == "frequent")
{
config.FanLevel = "1挡";
config.LightBrightnessPercent -= 2;
}
switch (answers.ExerciseFrequency)
{
case "none": config.MattressHeatingLevel++; break;
case "daily": config.MattressHeatingLevel--; break;
}
switch (answers.ActiveTimeOfDay)
{
case "morning": config.LightColorTemperatureKelvin += 150; break;
case "night":
config.LightColorTemperatureKelvin -= 250;
config.LightBrightnessPercent -= 2;
break;
case "irregular":
config.EnableSpeakerSleepMode = true;
config.SpeakerVolumePercent += 2;
break;
}
config.EnableUv = answers.LateNightEating is "often" or "sometimes" || answers.FlavorPreference == "heavy";
if (answers.MealRegularity == "irregular")
{
config.EnableUv = true;
config.FragranceLevel++;
}
else if (answers.MealRegularity == "skip_breakfast")
{
config.FragranceLevel++;
}
if (answers.DailyWaterIntake == "scarce") config.TargetHumidity += 3;
if (answers.DailyWaterIntake == "abundant") config.TargetHumidity -= 2;
if (basicInfo.HeightCm > 0 && basicInfo.WeightKg > 0)
{
var height = basicInfo.HeightCm.Value / 100m;
var bmi = basicInfo.WeightKg.Value / (height * height);
if (bmi < 18.5m) config.MattressHeatingLevel++;
if (bmi >= 28m) config.TargetHumidity -= 2;
}
if (basicInfo.Birthday.HasValue)
{
var age = DateTime.Today.Year - basicInfo.Birthday.Value.Year;
if (age >= 60)
{
config.LightBrightnessPercent += 3;
config.SpeakerVolumePercent = Math.Max(8, config.SpeakerVolumePercent - 2);
}
}
config.TargetHumidity = Math.Clamp(config.TargetHumidity, 40, 70);
config.LightBrightnessPercent = Math.Clamp(config.LightBrightnessPercent, 1, 100);
config.LightColorTemperatureKelvin = Math.Clamp(config.LightColorTemperatureKelvin, 2200, 5000);
config.SpeakerVolumePercent = Math.Clamp(config.SpeakerVolumePercent, 1, 50);
config.FragranceLevel = Math.Clamp(config.FragranceLevel, 1, 3);
config.MattressHeatingLevel = Math.Clamp(config.MattressHeatingLevel, 0, 4);
return config;
}
private static string BuildAssessmentPlanSummary(HealthProfile profile, HealthAssessmentAnswers answers)
{
var sleepNote = answers.FallingAsleepSpeed == "hard" ? "针对入睡耗时较长启用了音箱睡眠模式与极暗暖光;" : "";
var tempNote = answers.TemperaturePreference == "heat_fear" ? "依据体感偏热降低了床垫加热档位与室内湿度;" : answers.TemperaturePreference == "cold_fear" ? "依据体感偏冷提高了床垫保温档位;" : "";
var supportNote = answers.MuscleFatigueArea is "waist_back" or "back_neck" ? "针对腰背与颈部疲劳启用了床垫智能腰托模式;" : "";
return $"已完成全面健康评估,{sleepNote}{tempNote}{supportNote}全套智能家具启动参数已完成个性化配置。";
var sleepNote = answers.FallingAsleepSpeed == "hard" ? "入睡较慢,建议保持规律的睡前作息;" : "";
var activityNote = answers.ExerciseFrequency == "none" ? "可从轻度日常活动开始;" : "";
var dietNote = answers.MealRegularity == "irregular" ? "建议逐步建立规律三餐;" : "";
return $"已完成健康评估。{sleepNote}{activityNote}{dietNote}结果用于了解身体基本情况,不参与智能家居启动参数计算。";
}
}