From a23fa40c825abad7eb5ea2d3b6cc8017ffda6920 Mon Sep 17 00:00:00 2001 From: superlishunqin <852326703@qq.com> Date: Sat, 17 May 2025 15:59:13 +0800 Subject: [PATCH] user_profile_fix --- app/controllers/user.py | 116 ++++++++++++++++- app/static/js/user-profile.js | 231 ++++++++++++++++++++++++---------- app/templates/base.html | 1 - 3 files changed, 279 insertions(+), 69 deletions(-) diff --git a/app/controllers/user.py b/app/controllers/user.py index d672bb3..6250610 100644 --- a/app/controllers/user.py +++ b/app/controllers/user.py @@ -10,7 +10,8 @@ from datetime import datetime, timedelta from app.services.user_service import UserService from flask_login import login_user, logout_user, current_user, login_required from app.models.user import User - +from app.models.log import Log # 导入日志模型 +from app.models.borrow import BorrowRecord # 导入借阅记录模型 # 创建蓝图 user_bp = Blueprint('user', __name__) @@ -755,3 +756,116 @@ def add_user(): # GET请求,显示添加用户表单 return render_template('user/add.html', roles=roles) + + +@user_bp.route('/api/activities') +@login_required +def user_activities(): + """获取当前用户的活动记录""" + activity_type = request.args.get('type', 'all') + page = request.args.get('page', 1, type=int) + per_page = request.args.get('per_page', 10, type=int) + + # 从日志表中查询该用户的日志 + query = Log.query.filter(Log.user_id == current_user.id).order_by(Log.created_at.desc()) + + # 根据活动类型筛选 + if activity_type == 'login': + query = query.filter(Log.action.in_(['登录', '登出', '登录失败'])) + elif activity_type == 'borrow': + query = query.filter(Log.action.in_(['借书', '预约'])) + elif activity_type == 'return': + query = query.filter(Log.action.in_(['还书', '续借'])) + + # 分页 + pagination = query.paginate(page=page, per_page=per_page, error_out=False) + activities = pagination.items + + # 格式化活动数据 + result = [] + for log in activities: + activity_type = determine_activity_type(log.action) + + activity = { + 'id': log.id, + 'type': activity_type, + 'title': get_activity_title(log.action), + 'details': log.description or '无详细信息', + 'time': log.created_at.strftime('%Y-%m-%d %H:%M:%S'), + 'ip': log.ip_address + } + + # 如果存在目标ID和类型,添加到结果中 + if log.target_id and log.target_type: + activity['target_id'] = log.target_id + activity['target_type'] = log.target_type + + result.append(activity) + + return jsonify({ + 'activities': result, + 'total': pagination.total, + 'pages': pagination.pages, + 'current_page': pagination.page + }) + + +def determine_activity_type(action): + """根据日志动作确定活动类型""" + login_actions = ['登录', '登出', '登录失败'] + borrow_actions = ['借书', '预约'] + return_actions = ['还书', '续借'] + + if action in login_actions: + return 'login' + elif action in borrow_actions: + return 'borrow' + elif action in return_actions: + return 'return' + else: + return 'other' + + +def get_activity_title(action): + """根据动作返回活动标题""" + action_titles = { + '登录': '系统登录', + '登出': '退出登录', + '登录失败': '登录失败', + '借书': '借阅图书', + '还书': '归还图书', + '预约': '预约图书', + '续借': '续借图书' + } + + return action_titles.get(action, action) + + +@user_bp.route('/api/stats') +@login_required +def user_stats(): + """获取用户统计数据""" + # 查询用户的借阅统计 + borrowed = db.session.query(db.func.count(BorrowRecord.id)) \ + .filter(BorrowRecord.user_id == current_user.id, + BorrowRecord.status == 1, # 借阅中状态 + BorrowRecord.return_date == None) \ + .scalar() or 0 + + returned = db.session.query(db.func.count(BorrowRecord.id)) \ + .filter(BorrowRecord.user_id == current_user.id, + BorrowRecord.return_date != None) \ + .scalar() or 0 + + overdue = db.session.query(db.func.count(BorrowRecord.id)) \ + .filter(BorrowRecord.user_id == current_user.id, + BorrowRecord.status == 1, # 借阅中状态 + BorrowRecord.return_date == None, + BorrowRecord.due_date < datetime.now()) \ + .scalar() or 0 + + return jsonify({ + 'borrow': borrowed, + 'returned': returned, + 'overdue': overdue + }) diff --git a/app/static/js/user-profile.js b/app/static/js/user-profile.js index e2c8ccf..b2d1230 100644 --- a/app/static/js/user-profile.js +++ b/app/static/js/user-profile.js @@ -131,22 +131,21 @@ document.addEventListener('DOMContentLoaded', function() { // 获取用户统计数据 function fetchUserStats() { - // 这里使用虚拟数据,实际应用中应当从后端获取 - // fetch('/api/user/stats') - // .then(response => response.json()) - // .then(data => { - // updateUserStats(data); - // }); - - // 模拟数据 - setTimeout(() => { - const mockData = { - borrow: 2, - returned: 15, - overdue: 0 - }; - updateUserStats(mockData); - }, 500); + fetch('/user/api/stats') + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + updateUserStats(data); + }) + .catch(error => { + console.error('Error fetching user stats:', error); + // 出错时使用默认值 + updateUserStats({borrow: 0, returned: 0, overdue: 0}); + }); } // 更新用户统计显示 @@ -175,58 +174,20 @@ document.addEventListener('DOMContentLoaded', function() { `; - // 实际应用中应当从后端获取 - // fetch(`/api/user/activities?type=${type}`) - // .then(response => response.json()) - // .then(data => { - // renderActivityTimeline(data, timelineContainer); - // }); - - // 模拟数据 - setTimeout(() => { - const mockActivities = [ - { - id: 1, - type: 'login', - title: '系统登录', - details: '成功登录系统', - time: '2023-04-28 15:30:22', - ip: '192.168.1.1' - }, - { - id: 2, - type: 'borrow', - title: '借阅图书', - details: '借阅《JavaScript高级编程》', - time: '2023-04-27 11:45:10', - book_id: 101 - }, - { - id: 3, - type: 'return', - title: '归还图书', - details: '归还《Python数据分析》', - time: '2023-04-26 09:15:33', - book_id: 95 - }, - { - id: 4, - type: 'login', - title: '系统登录', - details: '成功登录系统', - time: '2023-04-25 08:22:15', - ip: '192.168.1.1' - } - ]; - - // 根据筛选条件过滤活动 - let filteredActivities = mockActivities; - if (type !== 'all') { - filteredActivities = mockActivities.filter(activity => activity.type === type); + fetch(`/user/api/activities?type=${type}`) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); } - - renderActivityTimeline(filteredActivities, timelineContainer); - }, 800); + return response.json(); + }) + .then(data => { + renderActivityTimeline(data.activities, timelineContainer); + }) + .catch(error => { + console.error('Error fetching activities:', error); + timelineContainer.innerHTML = '