fix: 修复企业看板API 500错误
All checks were successful
continuous-integration/drone/push Build is passing

- 修复 get_realtime_activities() 中字段名错误 (exp_amount -> exp_change)
- 添加 get_enterprise_overview() 的异常处理,防止单个查询失败导致整体失败
- 满分人数查询添加 NULL 值检查
This commit is contained in:
yuliang_guo
2026-02-02 12:57:31 +08:00
parent 99c4ac5473
commit cf71fabef0

View File

@@ -42,101 +42,164 @@ class DashboardService:
Returns: Returns:
企业级数据概览 企业级数据概览
""" """
today = date.today() try:
week_ago = today - timedelta(days=7) today = date.today()
month_ago = today - timedelta(days=30) week_ago = today - timedelta(days=7)
month_ago = today - timedelta(days=30)
# 基础统计 # 基础统计
# 1. 总学员数 # 1. 总学员数
result = await self.db.execute( result = await self.db.execute(
select(func.count(User.id)) select(func.count(User.id))
.where(User.is_deleted == False, User.role == 'trainee') .where(User.is_deleted == False, User.role == 'trainee')
)
total_users = result.scalar() or 0
# 2. 今日活跃用户(有经验值记录)
result = await self.db.execute(
select(func.count(func.distinct(ExpHistory.user_id)))
.where(func.date(ExpHistory.created_at) == today)
)
today_active = result.scalar() or 0
# 3. 本周活跃用户
result = await self.db.execute(
select(func.count(func.distinct(ExpHistory.user_id)))
.where(ExpHistory.created_at >= datetime.combine(week_ago, datetime.min.time()))
)
week_active = result.scalar() or 0
# 4. 本月活跃用户
result = await self.db.execute(
select(func.count(func.distinct(ExpHistory.user_id)))
.where(ExpHistory.created_at >= datetime.combine(month_ago, datetime.min.time()))
)
month_active = result.scalar() or 0
# 5. 总学习时长(小时)
result = await self.db.execute(
select(func.coalesce(func.sum(PracticeSession.duration_seconds), 0))
.where(PracticeSession.status == 'completed')
)
practice_hours = (result.scalar() or 0) / 3600
result = await self.db.execute(
select(func.coalesce(func.sum(TrainingSession.duration_seconds), 0))
.where(TrainingSession.status == 'COMPLETED')
)
training_hours = (result.scalar() or 0) / 3600
total_hours = round(practice_hours + training_hours, 1)
# 6. 考试统计
result = await self.db.execute(
select(
func.count(Exam.id),
func.count(case((Exam.is_passed == True, 1))),
func.avg(Exam.score)
) )
.where(Exam.status == 'submitted') total_users = result.scalar() or 0
)
exam_row = result.first()
exam_count = exam_row[0] or 0
exam_passed = exam_row[1] or 0
exam_avg_score = round(exam_row[2] or 0, 1)
exam_pass_rate = round(exam_passed / exam_count * 100, 1) if exam_count > 0 else 0
# 7. 满分人数 # 2. 今日活跃用户(有经验值记录)
result = await self.db.execute( try:
select(func.count(func.distinct(Exam.user_id))) result = await self.db.execute(
.where(Exam.status == 'submitted', Exam.score >= Exam.total_score) select(func.count(func.distinct(ExpHistory.user_id)))
) .where(func.date(ExpHistory.created_at) == today)
perfect_users = result.scalar() or 0 )
today_active = result.scalar() or 0
except Exception as e:
logger.warning(f"获取今日活跃用户失败: {e}")
today_active = 0
# 8. 签到率(今日签到人数/总用户数) # 3. 本周活跃用户
result = await self.db.execute( try:
select(func.count(UserLevel.id)) result = await self.db.execute(
.where(func.date(UserLevel.last_login_date) == today) select(func.count(func.distinct(ExpHistory.user_id)))
) .where(ExpHistory.created_at >= datetime.combine(week_ago, datetime.min.time()))
today_checkin = result.scalar() or 0 )
checkin_rate = round(today_checkin / total_users * 100, 1) if total_users > 0 else 0 week_active = result.scalar() or 0
except Exception as e:
logger.warning(f"获取本周活跃用户失败: {e}")
week_active = 0
return { # 4. 本月活跃用户
"overview": { try:
"total_users": total_users, result = await self.db.execute(
"today_active": today_active, select(func.count(func.distinct(ExpHistory.user_id)))
"week_active": week_active, .where(ExpHistory.created_at >= datetime.combine(month_ago, datetime.min.time()))
"month_active": month_active, )
"total_hours": total_hours, month_active = result.scalar() or 0
"checkin_rate": checkin_rate, except Exception as e:
}, logger.warning(f"获取本月活跃用户失败: {e}")
"exam": { month_active = 0
"total_count": exam_count,
"pass_rate": exam_pass_rate, # 5. 总学习时长(小时)
"avg_score": exam_avg_score, practice_hours = 0
"perfect_users": perfect_users, training_hours = 0
}, try:
"updated_at": datetime.now().isoformat() result = await self.db.execute(
} select(func.coalesce(func.sum(PracticeSession.duration_seconds), 0))
.where(PracticeSession.status == 'completed')
)
practice_hours = (result.scalar() or 0) / 3600
except Exception as e:
logger.warning(f"获取陪练时长失败: {e}")
try:
result = await self.db.execute(
select(func.coalesce(func.sum(TrainingSession.duration_seconds), 0))
.where(TrainingSession.status == 'COMPLETED')
)
training_hours = (result.scalar() or 0) / 3600
except Exception as e:
logger.warning(f"获取培训时长失败: {e}")
total_hours = round(practice_hours + training_hours, 1)
# 6. 考试统计
exam_count = 0
exam_passed = 0
exam_avg_score = 0
try:
result = await self.db.execute(
select(
func.count(Exam.id),
func.count(case((Exam.is_passed == True, 1))),
func.avg(Exam.score)
)
.where(Exam.status == 'submitted')
)
exam_row = result.first()
if exam_row:
exam_count = exam_row[0] or 0
exam_passed = exam_row[1] or 0
exam_avg_score = round(exam_row[2] or 0, 1)
except Exception as e:
logger.warning(f"获取考试统计失败: {e}")
exam_pass_rate = round(exam_passed / exam_count * 100, 1) if exam_count > 0 else 0
# 7. 满分人数
perfect_users = 0
try:
result = await self.db.execute(
select(func.count(func.distinct(Exam.user_id)))
.where(
Exam.status == 'submitted',
Exam.score.isnot(None),
Exam.total_score.isnot(None),
Exam.score >= Exam.total_score
)
)
perfect_users = result.scalar() or 0
except Exception as e:
logger.warning(f"获取满分人数失败: {e}")
# 8. 签到率(今日签到人数/总用户数)
today_checkin = 0
try:
result = await self.db.execute(
select(func.count(UserLevel.id))
.where(UserLevel.last_login_date == today)
)
today_checkin = result.scalar() or 0
except Exception as e:
logger.warning(f"获取签到率失败: {e}")
checkin_rate = round(today_checkin / total_users * 100, 1) if total_users > 0 else 0
return {
"overview": {
"total_users": total_users,
"today_active": today_active,
"week_active": week_active,
"month_active": month_active,
"total_hours": total_hours,
"checkin_rate": checkin_rate,
},
"exam": {
"total_count": exam_count,
"pass_rate": exam_pass_rate,
"avg_score": exam_avg_score,
"perfect_users": perfect_users,
},
"updated_at": datetime.now().isoformat()
}
except Exception as e:
logger.error(f"获取企业概览失败: {e}")
# 返回默认数据而不是抛出异常
return {
"overview": {
"total_users": 0,
"today_active": 0,
"week_active": 0,
"month_active": 0,
"total_hours": 0,
"checkin_rate": 0,
},
"exam": {
"total_count": 0,
"pass_rate": 0,
"avg_score": 0,
"perfect_users": 0,
},
"updated_at": datetime.now().isoformat()
}
async def get_department_comparison(self) -> List[Dict[str, Any]]: async def get_department_comparison(self) -> List[Dict[str, Any]]:
""" """
@@ -313,35 +376,40 @@ class DashboardService:
""" """
activities = [] activities = []
# 获取最近的经验值记录 try:
result = await self.db.execute( # 获取最近的经验值记录
select(ExpHistory, User) result = await self.db.execute(
.join(User, ExpHistory.user_id == User.id) select(ExpHistory, User)
.order_by(ExpHistory.created_at.desc()) .join(User, ExpHistory.user_id == User.id)
.limit(limit) .order_by(ExpHistory.created_at.desc())
) .limit(limit)
rows = result.all() )
rows = result.all()
for exp, user in rows: for exp, user in rows:
activity_type = "学习" activity_type = "学习"
if "考试" in (exp.description or ""): description = exp.description or ""
activity_type = "考试" if "考试" in description:
elif "签到" in (exp.description or ""): activity_type = "考试"
activity_type = "签到" elif "签到" in description:
elif "陪练" in (exp.description or ""): activity_type = "签到"
activity_type = "陪练" elif "陪练" in description:
elif "奖章" in (exp.description or ""): activity_type = "陪练"
activity_type = "奖章" elif "奖章" in description:
activity_type = "奖章"
activities.append({ activities.append({
"id": exp.id, "id": exp.id,
"user_id": user.id, "user_id": user.id,
"user_name": user.full_name or user.username, "user_name": user.full_name or user.username,
"type": activity_type, "type": activity_type,
"description": exp.description, "description": description,
"exp_amount": exp.exp_amount, "exp_amount": exp.exp_change, # 修复: exp_change 而非 exp_amount
"created_at": exp.created_at.isoformat() if exp.created_at else None, "created_at": exp.created_at.isoformat() if exp.created_at else None,
}) })
except Exception as e:
logger.error(f"获取实时动态失败: {e}")
# 返回空列表而不是抛出异常
return activities return activities