111
This commit is contained in:
@@ -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}结果用于了解身体基本情况,不参与智能家居启动参数计算。";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user