Files
ljwx-health/ljwx-bigscreen-bak/app/services/fetchConfig.py
T
brunoandClaude Sonnet 4.5 7ca86bce38 feat(health): Sprint 9 - Behavioral Recommendation Engine + Compilation Fixes
## 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>
2026-01-21 07:48:01 +08:00

430 lines
19 KiB
Python

from flask import Flask, jsonify, request, Blueprint
from flask_cors import CORS
import mysql.connector
from app.models import HealthDataConfig, CustomerConfig, Interface, db
from .device import fetch_customer_id_by_deviceSn, fetch_user_info_by_deviceSn
app = Flask(__name__)
CORS(app)
# Database configuration
import os
config = {
'user': os.getenv('MYSQL_USER', 'root'),
'password': os.getenv('MYSQL_PASSWORD', '123456'),
'host': os.getenv('MYSQL_HOST', 'mysql' if os.getenv('IS_DOCKER', 'false').lower() == 'true' else '127.0.0.1'),
'database': os.getenv('MYSQL_DATABASE', 'lj-06'),
'raise_on_warnings': True
}
def copy_health_data_config(old_customer_id, new_customer_id):
# Fetch all configurations with customer_id 8
health_data_config = HealthDataConfig.query.filter_by(customer_id=old_customer_id).all()
for health_data in health_data_config:
print("health_data", health_data)
new_health_data = HealthDataConfig(
customer_id=new_customer_id,
data_type=health_data.data_type,
frequency_interval=health_data.frequency_interval,
is_realtime=health_data.is_realtime,
is_enabled=health_data.is_enabled,
is_default=health_data.is_default,
warning_high=health_data.warning_high,
warning_low=health_data.warning_low,
warning_cnt=health_data.warning_cnt
)
print("new_health_data", new_health_data)
db.session.add(new_health_data) # Add the new entry to the session
db.session.commit() # Commit the changes to the database
interface_config = Interface.query.filter_by(customer_id=old_customer_id).all()
for interface in interface_config:
new_interface = Interface(
customer_id=new_customer_id,
name=interface.name,
url=interface.url,
call_interval=interface.call_interval,
method=interface.method,
description=interface.description,
is_enabled=interface.is_enabled,
api_id=interface.api_id,
api_auth=interface.api_auth,
create_user=interface.create_user,
create_user_id=interface.create_user_id,
create_time=interface.create_time,
update_user=interface.update_user,
update_user_id=interface.update_user_id,
update_time=interface.update_time
# Copy other fields from interface as needed
)
print("new_interface", new_interface)
db.session.add(new_interface) # Add the new entry to the session
db.session.commit() # Commit the changes to the database
customer_config = CustomerConfig.query.filter_by(id=old_customer_id).first()
# Check if the new_customer_id already exists
existing_customer = CustomerConfig.query.filter_by(id=new_customer_id).first()
if existing_customer:
return jsonify({"success": False, "error": "Customer ID already exists"}), 400
new_customer_config = CustomerConfig(
id=new_customer_id,
customer_name=customer_config.customer_name,
upload_method=customer_config.upload_method,
is_support_license=customer_config.is_support_license,
license_key=customer_config.license_key,
os_version=customer_config.os_version,
create_user=customer_config.create_user,
create_user_id=customer_config.create_user_id,
create_time=customer_config.create_time,
update_user=customer_config.update_user,
update_user_id=customer_config.update_user_id,
update_time=customer_config.update_time
# Copy other fields from customer_config as needed
)
print("new_customer_config", new_customer_config)
db.session.add(new_customer_config) # Add the new entry to the session
db.session.commit() # Commit the changes to the database
return jsonify({"success": True, "message": "Configuration copied successfully"})
def fetch_health_data_config(customer_id=None,deviceSn=None):
if customer_id is None:
customer_id = request.args.get('customerId') # Get customerId from request parameters
if deviceSn is None:
deviceSn = request.args.get('deviceSn') # Get deviceSn from request parameters
# 获取设备的完整客户信息
device_info = fetch_user_info_by_deviceSn(deviceSn)
customerId = device_info.get('customer_id', '0')
orgId = device_info.get('org_id')
userId = device_info.get('user_id')
print("fetchHealthDataConfig - customerId:", customerId, "orgId:", orgId, "userId:", userId)
if not customerId or customerId == '0':
return jsonify({"success": False, "error": "customerId parameter is required"}), 400
connection = mysql.connector.connect(**config)
try:
cursor = connection.cursor(dictionary=True)
query = """
SELECT
h.data_type,
h.frequency_interval,
h.is_enabled,
h.is_realtime,
h.warning_high,
h.warning_low,
h.warning_cnt,
c.customer_name,
c.upload_method,
c.is_support_license,
c.license_key,
c.enable_resume,
c.upload_retry_count,
c.cache_max_count,
c.upload_retry_interval,
i.name AS interface_name,
i.url AS interface_url,
i.call_interval AS interface_call_interval,
i.is_enabled AS interface_is_enabled,
i.api_id AS interface_api_id,
i.api_auth AS interface_api_auth
FROM t_health_data_config h
JOIN t_customer_config c ON h.customer_id = c.id
JOIN t_interface i ON h.customer_id = i.customer_id
WHERE h.customer_id = %s
"""
cursor.execute(query, (customerId,))
result = cursor.fetchall()
# Format the result into the desired JSON structure
config_data = {
"customer_name": result[0]['customer_name'] if result else None,
"customer_id": customerId,
"org_id": orgId,
"user_id": userId,
"upload_method": result[0]['upload_method'] if result else 'wifi', # Default to 'wifi' if None
"enable_resume": result[0]['enable_resume'] if result else False,
"upload_retry_count": result[0]['upload_retry_count'] if result else 3,
"cache_max_count": result[0]['cache_max_count'] if result else 100,
"upload_retry_interval": result[0]['upload_retry_interval'] if result else 5,
"health_data": {
row['data_type']: f"{row['frequency_interval']}:{row['is_enabled']}:{row['is_realtime']}:" +
f"{row['warning_high'] if row['warning_high'] is not None else -1}:" +
f"{row['warning_low'] if row['warning_low'] is not None else -1}:" +
f"{row['warning_cnt'] if row['warning_cnt'] is not None else -1}"
for row in result
},
"is_support_license": result[0]['is_support_license'] if result else False,
"license_key": result[0]['license_key'] if result else None,
"interface_data": {
row['interface_name']: f"{row['interface_url']};{row['interface_call_interval']};{row['interface_is_enabled']};" +
f"{row['interface_api_id']};{row['interface_api_auth']}"
for row in result
}
}
return jsonify(config_data)
except mysql.connector.Error as err:
print(f"Error: {err}")
return jsonify({"success": False, "error": str(err)}), 500
finally:
cursor.close()
connection.close()
def fetch_health_data_config_bak(customer_id=None,deviceSn=None):
if customer_id is None:
customer_id = request.args.get('customerId') # Get customerId from request parameters
if deviceSn is None:
deviceSn = request.args.get('deviceSn') # Get deviceSn from request parameters
# 获取设备的完整客户信息
device_info = fetch_user_info_by_deviceSn(deviceSn)
customerId = device_info.get('customer_id', '0')
orgId = device_info.get('org_id')
userId = device_info.get('user_id')
print("fetchHealthDataConfig_bak - customerId:", customerId, "orgId:", orgId, "userId:", userId)
if not customerId or customerId == '0':
return jsonify({"success": False, "error": "customerId parameter is required"}), 400
connection = mysql.connector.connect(**config)
try:
cursor = connection.cursor(dictionary=True)
query = """
SELECT h.data_type, h.frequency_interval, h.is_enabled, h.is_realtime, h.warning_high, h.warning_low, h.warning_cnt,
c.customer_name, c.upload_method, c.is_support_license,
c.license_key, i.name as interface_name, i.url as interface_url, i.call_interval as interface_call_interval,
i.is_enabled as interface_is_enabled, i.api_id as interface_api_id, i.api_auth as interface_api_auth
FROM t_health_data_config h
JOIN t_customer_config c ON h.customer_id = c.id
JOIN t_interface i ON h.customer_id = i.customer_id
WHERE h.customer_id = %s
"""
cursor.execute(query, (customerId,))
result = cursor.fetchall()
# Format the result into the desired JSON structure
config_data = {
"customer_name": result[0]['customer_name'] if result else None,
"customer_id": customerId,
"org_id": orgId,
"user_id": userId,
"upload_method": result[0]['upload_method'] if result else 'wifi', # Default to 'wifi' if None
"health_data": {
row['data_type']: f"{row['frequency_interval']}:{row['is_enabled']}:{row['is_realtime']}:" +
f"{row['warning_high'] if row['warning_high'] is not None else -1}:" +
f"{row['warning_low'] if row['warning_low'] is not None else -1}:" +
f"{row['warning_cnt'] if row['warning_cnt'] is not None else -1}"
for row in result
},
"is_support_license": result[0]['is_support_license'] if result else False,
"license_key": result[0]['license_key'] if result else None,
"interface_data": {
row['interface_name']: f"{row['interface_url']};{row['interface_call_interval']};{row['interface_is_enabled']};" +
f"{row['interface_api_id']};{row['interface_api_auth']}"
for row in result
}
}
return jsonify(config_data)
except mysql.connector.Error as err:
print(f"Error: {err}")
return jsonify({"success": False, "error": str(err)}), 500
finally:
cursor.close()
connection.close()
def main():
app.run(host='0.0.0.0', port=6063, debug=True)
if __name__ == "__main__":
main()
config_bp = Blueprint('config', __name__)
@config_bp.route('/get_interface_config', methods=['GET'])
def get_interface_config():
try:
customer_id = request.args.get('customerId')
if not customer_id:
return jsonify({'success': False, 'error': '缺少 customerId 参数'})
interfaces = Interface.query.filter_by(customer_id=customer_id).all()
config = {interface.name: interface.url for interface in interfaces}
return jsonify({'success': True, 'data': config})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
@config_bp.route('/save_interface_config', methods=['POST'])
def save_interface_config():
try:
customer_id = request.args.get('customerId')
if not customer_id:
return jsonify({'success': False, 'error': '缺少 customerId 参数'})
data = request.get_json()
for name, url in data.items():
interface = Interface.query.filter_by(customer_id=customer_id, name=name).first()
if interface:
interface.url = url
else:
interface = Interface(customer_id=customer_id, name=name, url=url)
db.session.add(interface)
db.session.commit()
return jsonify({'success': True})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'error': str(e)})
@config_bp.route('/get_health_config', methods=['GET'])
def get_health_config():
try:
customer_id = request.args.get('customerId')
if not customer_id:
return jsonify({'success': False, 'error': '缺少 customerId 参数'})
configs = HealthDataConfig.query.filter_by(customer_id=customer_id).all()
config = {}
for cfg in configs:
config[cfg.data_type] = {
'frequency_interval': cfg.frequency_interval,
'is_enabled': cfg.is_enabled,
'is_realtime': cfg.is_realtime,
'warning_cnt': cfg.warning_cnt,
'warning_high': cfg.warning_high,
'warning_low': cfg.warning_low,
'weight': cfg.weight
}
return jsonify({'success': True, 'data': config})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
@config_bp.route('/save_health_config', methods=['POST'])
def save_health_config():
try:
customer_id = request.args.get('customerId')
if not customer_id:
return jsonify({'success': False, 'error': '缺少 customerId 参数'})
data = request.get_json()
for data_type, config in data.items():
health_config = HealthDataConfig.query.filter_by(customer_id=customer_id, data_type=data_type).first()
if health_config:
health_config.frequency_interval = config.get('frequency_interval')
health_config.is_enabled = config.get('is_enabled')
health_config.is_realtime = config.get('is_realtime')
health_config.warning_cnt = config.get('warning_cnt')
health_config.warning_high = config.get('warning_high')
health_config.warning_low = config.get('warning_low')
else:
health_config = HealthDataConfig(
customer_id=customer_id,
data_type=data_type,
frequency_interval=config.get('frequency_interval'),
is_enabled=config.get('is_enabled'),
is_realtime=config.get('is_realtime'),
warning_cnt=config.get('warning_cnt'),
warning_high=config.get('warning_high'),
warning_low=config.get('warning_low')
)
db.session.add(health_config)
db.session.commit()
return jsonify({'success': True})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'error': str(e)})
@config_bp.route('/get_customer_config', methods=['GET'])
def get_customer_config():
try:
customer_id = request.args.get('customerId')
if not customer_id:
return jsonify({'success': False, 'error': '缺少 customerId 参数'})
customer = CustomerConfig.query.filter_by(id=customer_id).first()
if not customer:
return jsonify({'success': False, 'error': '客户不存在'})
config = {
customer_id: {
'customer_name': customer.customer_name,
'is_support_license': customer.is_support_license,
'license_key': customer.license_key,
'os_version': customer.os_version,
'upload_method': customer.upload_method
}
}
return jsonify({'success': True, 'data': config})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
@config_bp.route('/save_customer_config', methods=['POST'])
def save_customer_config():
try:
customer_id = request.args.get('customerId')
if not customer_id:
return jsonify({'success': False, 'error': '缺少 customerId 参数'})
data = request.get_json()
customer = CustomerConfig.query.filter_by(id=customer_id).first()
if not customer:
return jsonify({'success': False, 'error': '客户不存在'})
customer.customer_name = data.get('customer_name')
customer.is_support_license = data.get('is_support_license')
customer.license_key = data.get('license_key')
customer.os_version = data.get('os_version')
customer.upload_method = data.get('upload_method')
db.session.commit()
return jsonify({'success': True})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'error': str(e)})
@config_bp.route('/get_optimal_config', methods=['GET'])
def get_optimal_config():
try:
customer_id = request.args.get('customerId')
if not customer_id:
return jsonify({'success': False, 'error': '缺少 customerId 参数'})
# 获取所有配置
interfaces = Interface.query.filter_by(customer_id=customer_id).all()
health_configs = HealthDataConfig.query.filter_by(customer_id=customer_id).all()
customer = CustomerConfig.query.filter_by(id=customer_id).first()
if not customer:
return jsonify({'success': False, 'error': '客户不存在'})
# 构建最优配置
optimal_config = {
'interface': {interface.name: interface.url for interface in interfaces},
'health': {
config.data_type: {
'frequency_interval': config.frequency_interval,
'is_enabled': config.is_enabled,
'is_realtime': config.is_realtime,
'warning_cnt': config.warning_cnt,
'warning_high': config.warning_high,
'warning_low': config.warning_low
} for config in health_configs
},
'customer': {
customer_id: {
'customer_name': customer.customer_name,
'is_support_license': customer.is_support_license,
'license_key': customer.license_key,
'os_version': customer.os_version,
'upload_method': customer.upload_method
}
}
}
return jsonify({'success': True, 'data': optimal_config})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})