## Sprint 9 Implementation (100% Complete) ### New Features 1. **Behavioral Recommendation Engine** - Priority scoring algorithm: (Severity × 0.5) + (Feasibility × 0.3) + (User Attention × 0.2) - 4 recommendation strategies: Exercise, Sleep, Diet, Attention - Top 5 prioritized recommendations per user - Automatic behavioral profile generation (scheduled daily 03:30) 2. **Trend Analysis System** - 30-day trend analysis with linear regression - Trend classification: IMPROVING/STABLE/DECLINING - Time series data extraction for visualization - Milestone identification (best/worst dates) ### New APIs (6 endpoints) - POST /api/health/behavioral-recommendations/generate - GET /api/health/behavioral-recommendations/query - POST /api/health/behavioral-recommendations/batch-generate - GET /api/health/behavioral-trends/query ### Deliverables - 20 new files: 4 DTOs, 5 strategies, 3 services, 3 controllers, 5 test files - 3,800+ lines of production code - 62+ unit test cases, 10 integration test cases - Architecture documentation (450 lines) - Sprint 9 completion summary (700+ lines) ## Compilation Fixes (Pre-existing Code) ### Fixed Errors 1. **Java 11+ Compatibility** - Replaced deprecated @Resource with @Autowired (8 files) - Fixed javax.annotation.Resource → org.springframework.beans.factory.annotation.Autowired 2. **Import Path Corrections** - Fixed SysUserMapper path: com.ljwx.modules.system.mapper → com.ljwx.modules.system.repository.mapper - Fixed SysUser entity path: com.ljwx.modules.system.entity → com.ljwx.modules.system.domain.entity 3. **Result API Alignment** - Fixed Result.ok() method calls in RiskProfileController - Updated import: com.ljwx.common.api.Result → com.ljwx.common.api.vo.Result - Aligned Sprint 9 controllers with correct Result API 4. **User Entity Field Mapping** - Calculated age from birthday instead of using non-existent getAge() - Used position field instead of non-existent getPositionType() 5. **Dependency Injection** - Added BehavioralProfileService as @Autowired field in HealthBaselineScoreTasks - Removed manual bean lookup via non-existent ApplicationContextProvider ### Modified Files (Compilation Fixes) - RiskProfileService.java - RiskProfileController.java - RiskProfileServiceTest.java - CardiovascularRiskAssessor.java - RespiratoryRiskAssessor.java - SleepRiskAssessor.java - ExerciseRiskAssessor.java - RiskTrendAnalysisService.java - HealthBaselineScoreTasks.java - BehavioralProfileController.java - BehavioralRecommendationController.java - BehavioralTrendController.java ## Test Results - ✅ Compilation: SUCCESS (Java 21) - ✅ Application: Started successfully on port 9998 - ✅ OpenAPI: Exported (458 endpoints, 550 schemas) ## Reference - Sprint 9 Summary: docs/02-architecture/sprint9-completion-summary.md - Architecture Doc: docs/02-architecture/phase2-behavioral-recommendation-architecture.md Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
146 lines
3.3 KiB
CSS
146 lines
3.3 KiB
CSS
/* ========================================
|
|
LJWX BigScreen - 动画效果
|
|
从 main-complete.css 提取
|
|
======================================== */
|
|
|
|
/* 告警动画效果 */
|
|
@keyframes alertPulse {
|
|
0%, 100% { box-shadow: 0 20px 60px rgba(255,68,68,0.3), 0 0 30px rgba(255,68,68,0.2); }
|
|
50% { box-shadow: 0 25px 80px rgba(255,68,68,0.5), 0 0 50px rgba(255,68,68,0.4); }
|
|
}
|
|
|
|
@keyframes alertBlink {
|
|
0%, 100% { opacity: 1; transform: scale(1); }
|
|
50% { opacity: 0.6; transform: scale(1.2); }
|
|
}
|
|
|
|
@keyframes levelGlow {
|
|
0% { opacity: 0.8; }
|
|
100% { opacity: 1; box-shadow: 0 0 20px currentColor; }
|
|
}
|
|
|
|
@keyframes scanLine {
|
|
0% { left: -100%; }
|
|
100% { left: 100%; }
|
|
}
|
|
|
|
@keyframes avatarGlow {
|
|
0% { box-shadow: 0 0 20px rgba(255,68,68,0.6); }
|
|
100% { box-shadow: 0 0 30px rgba(255,68,68,0.9), 0 0 60px rgba(255,68,68,0.3); }
|
|
}
|
|
|
|
@keyframes iconPulse {
|
|
0%, 100% { transform: scale(1); }
|
|
50% { transform: scale(1.1); }
|
|
}
|
|
|
|
/* 健康点动画效果 */
|
|
@keyframes healthGlow {
|
|
0%, 100% { box-shadow: 0 25px 80px rgba(0,228,255,0.3), 0 0 40px rgba(0,228,255,0.2); }
|
|
50% { box-shadow: 0 30px 100px rgba(0,228,255,0.5), 0 0 60px rgba(0,228,255,0.4); }
|
|
}
|
|
|
|
@keyframes healthPulse {
|
|
0% { opacity: 0.8; }
|
|
100% { opacity: 1; box-shadow: 0 0 25px currentColor; }
|
|
}
|
|
|
|
@keyframes healthBeat {
|
|
0%, 100% { transform: scale(1); }
|
|
50% { transform: scale(1.1); }
|
|
}
|
|
|
|
@keyframes healthAvatarGlow {
|
|
0%, 100% { box-shadow: 0 0 25px rgba(0,228,255,0.6); }
|
|
50% { box-shadow: 0 0 35px rgba(0,228,255,0.9), 0 0 70px rgba(0,228,255,0.3); }
|
|
}
|
|
|
|
@keyframes healthIconFloat {
|
|
0%, 100% { transform: translateY(0); }
|
|
50% { transform: translateY(-8px); }
|
|
}
|
|
|
|
@keyframes heartbeatLine {
|
|
0%, 100% { transform: translateX(-10px); opacity: 0; }
|
|
50% { transform: translateX(46px); opacity: 1; }
|
|
}
|
|
|
|
@keyframes bgShift {
|
|
0%, 100% { transform: translateX(0) translateY(0); }
|
|
50% { transform: translateX(10px) translateY(-10px); }
|
|
}
|
|
|
|
/* 通用动画 */
|
|
@keyframes slideIn {
|
|
from { transform: translateY(30px) scale(0.9); opacity: 0; }
|
|
to { transform: translateY(0) scale(1); opacity: 1; }
|
|
}
|
|
|
|
@keyframes iconFloat {
|
|
0%, 100% { transform: translateY(0); }
|
|
50% { transform: translateY(-3px); }
|
|
}
|
|
|
|
@keyframes numberGlow {
|
|
0% { text-shadow: 0 0 15px rgba(0, 228, 255, 0.8); }
|
|
100% { text-shadow: 0 0 20px rgba(0, 228, 255, 1), 0 0 30px rgba(0, 228, 255, 0.5); }
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
@keyframes gradientShift {
|
|
0%, 100% { background-position: 0% 50%; }
|
|
50% { background-position: 100% 50%; }
|
|
}
|
|
|
|
@keyframes scan {
|
|
0% { left: -100%; }
|
|
100% { left: 100%; }
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
100% { opacity: 1; }
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
|
|
@keyframes modalFadeIn {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
|
|
@keyframes criticalPulse {
|
|
0%, 100% {
|
|
box-shadow: 0 0 50px rgba(255, 68, 68, 0.5), 0 0 100px rgba(255, 68, 68, 0.3);
|
|
transform: scale(1);
|
|
}
|
|
50% {
|
|
box-shadow: 0 0 70px rgba(255, 68, 68, 0.7), 0 0 120px rgba(255, 68, 68, 0.5);
|
|
transform: scale(1.02);
|
|
}
|
|
}
|
|
|
|
@keyframes alertFadeIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translate(-50%, -60%);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
}
|
|
|
|
@keyframes overlayFadeIn {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|