blob: 6f159ca8a5cdbe1bc7070474dec8448f4ace0ce6 [file] [log] [blame]
Raverafc93da2025-06-15 18:12:49 +08001from flask import Blueprint, request, jsonify
2from .functions.FAuth import FAuth
Raver9d0ecf52025-06-25 17:35:39 +08003from app.services.recommendation_service import RecommendationService
4from app.utils.graph_build import build_user_post_graph
Raverafc93da2025-06-15 18:12:49 +08005from sqlalchemy import create_engine
6from sqlalchemy.orm import sessionmaker
7from config import Config
8from functools import wraps
9from datetime import datetime
10
11main = Blueprint('main', __name__)
12
TRM-coding3127efa2025-06-18 22:54:25 +080013# 初始化推荐服务
14recommendation_service = RecommendationService()
15
Raverafc93da2025-06-15 18:12:49 +080016def token_required(f):
17 """装饰器:需要令牌验证"""
18 @wraps(f)
19 def decorated(*args, **kwargs):
20 token = request.headers.get('Authorization')
Raver9d0ecf52025-06-25 17:35:39 +080021 print(f"收到的Authorization头: {token}")
22
Raverafc93da2025-06-15 18:12:49 +080023 if not token:
Raver9d0ecf52025-06-25 17:35:39 +080024 print("缺少Authorization头")
Raverafc93da2025-06-15 18:12:49 +080025 return jsonify({'success': False, 'message': '缺少访问令牌'}), 401
26
27 session = None
28 try:
29 # 移除Bearer前缀
30 if token.startswith('Bearer '):
31 token = token[7:]
Raver9d0ecf52025-06-25 17:35:39 +080032 print(f"提取的token: {token[:20]}...")
Raverafc93da2025-06-15 18:12:49 +080033
34 engine = create_engine(Config.SQLURL)
35 SessionLocal = sessionmaker(bind=engine)
36 session = SessionLocal()
37 f_auth = FAuth(session)
38
Raver9d0ecf52025-06-25 17:35:39 +080039 # 验证token
40 token_data = f_auth.verify_token(token)
41 print(f"Token验证结果: {token_data}")
42
43 if not token_data:
44 print("Token验证失败 - token无效")
45 return jsonify({'success': False, 'message': '无效的访问令牌'}), 401
46
Raverafc93da2025-06-15 18:12:49 +080047 user = f_auth.get_user_by_token(token)
48 if not user:
Raver9d0ecf52025-06-25 17:35:39 +080049 print("根据token获取用户失败")
Raverafc93da2025-06-15 18:12:49 +080050 return jsonify({'success': False, 'message': '无效的访问令牌'}), 401
51
Raver9d0ecf52025-06-25 17:35:39 +080052 print(f"Token验证成功,用户: {user.username}, ID: {user.id}")
53
Raverafc93da2025-06-15 18:12:49 +080054 # 将用户信息传递给路由函数
55 return f(user, *args, **kwargs)
56 except Exception as e:
Raver9d0ecf52025-06-25 17:35:39 +080057 print(f"Token验证异常: {str(e)}")
Raverafc93da2025-06-15 18:12:49 +080058 if session:
59 session.rollback()
Raver9d0ecf52025-06-25 17:35:39 +080060 return jsonify({'success': False, 'message': f'令牌验证失败: {str(e)}'}), 401
Raverafc93da2025-06-15 18:12:49 +080061 finally:
62 if session:
63 session.close()
64
65 return decorated
66
67@main.route('/login', methods=['POST'])
68def login():
69 """用户登录接口"""
70 session = None
71 try:
72 data = request.get_json()
73
74 # 验证必填字段
75 if not data or not data.get('email') or not data.get('password'):
76 return jsonify({
77 'success': False,
78 'message': '用户名和密码不能为空'
79 }), 400
80
81 email = data['email']
82 password = data['password']
83
84 # 创建数据库连接
85 engine = create_engine(Config.SQLURL)
86 SessionLocal = sessionmaker(bind=engine)
87 session = SessionLocal()
88
89 # 执行登录
90 f_auth = FAuth(session)
91 result = f_auth.login(email, password)
92
93 if result['success']:
94 session.commit()
95 return jsonify(result), 200
96 else:
97 return jsonify(result), 401
98
99 except Exception as e:
100 if session:
101 session.rollback()
102 return jsonify({
103 'success': False,
104 'message': '服务器内部错误'
105 }), 500
106 finally:
107 if session:
108 session.close()
109
110@main.route('/register', methods=['POST'])
111def register():
112 """用户注册接口"""
113
114 engine = create_engine(Config.SQLURL)
115 SessionLocal = sessionmaker(bind=engine)
116 session = SessionLocal()
117
118 try:
119 data = request.get_json()
120
121 # 验证必填字段
122 if not data or not data.get('username') or not data.get('email') or not data.get('password') or not data.get('verification_code'):
123 return jsonify({
124 'success': False,
125 'message': '用户名、邮箱和密码不能为空'
126 }), 400
127
128 username = data['username']
129 email = data['email']
130 password = data['password']
131 verification_code = data['verification_code']
132
133 # 简单的邮箱格式验证
134 if '@' not in email or '.' not in email:
135 return jsonify({
136 'success': False,
137 'message': '邮箱格式不正确'
138 }), 400
139
140 # 密码长度验证
141 if len(password) < 6:
142 return jsonify({
143 'success': False,
144 'message': '密码长度不能少于6位'
145 }), 400
146
147 # 执行注册
148 f_auth = FAuth(session)
149 result = f_auth.register(username, email, password, verification_code)
150
151 if result['success']:
152 session.commit()
153 return jsonify(result), 201
154 else:
155 return jsonify(result), 400
156
157 except Exception as e:
158 session.rollback()
159 return jsonify({
160 'success': False,
161 'message': '服务器内部错误'
162 }), 500
163 finally:
164 session.close()
165
166@main.route('/profile', methods=['GET'])
167@token_required
168def get_profile(current_user):
169 """获取用户信息接口(需要登录)"""
170 try:
171 return jsonify({
172 'success': True,
173 'user': current_user.to_dict()
174 }), 200
175 except Exception as e:
176 return jsonify({
177 'success': False,
178 'message': '获取用户信息失败'
179 }), 500
180
181@main.route('/logout', methods=['POST'])
182@token_required
183def logout(current_user):
184 """用户登出接口(需要登录)"""
185 try:
186 # 这里可以将令牌加入黑名单(如果需要的话)
187 return jsonify({
188 'success': True,
189 'message': '登出成功'
190 }), 200
191 except Exception as e:
192 return jsonify({
193 'success': False,
194 'message': '登出失败'
195 }), 500
196
197@main.route('/send-verification-code', methods=['POST'])
198def send_verification_code():
199 """发送邮箱验证码接口"""
200
201 engine = create_engine(Config.SQLURL)
202 SessionLocal = sessionmaker(bind=engine)
203 session = SessionLocal()
204
205 try:
206 data = request.get_json()
207
208 # 验证必填字段
209 if not data or not data.get('email'):
210 return jsonify({
211 'success': False,
212 'message': '邮箱地址不能为空'
213 }), 400
214
215 email = data['email']
216 verification_type = data.get('type', 'register') # 默认为注册验证码
217
218 # 简单的邮箱格式验证
219 if '@' not in email or '.' not in email:
220 return jsonify({
221 'success': False,
222 'message': '邮箱格式不正确'
223 }), 400
224
225 # 发送验证码
226 f_auth = FAuth(session)
227 result = f_auth.send_verification_email(email, verification_type)
228
229 if result['success']:
230 session.commit()
231 return jsonify(result), 200
232 else:
233 return jsonify(result), 400
234
235 except Exception as e:
236 session.rollback()
237 return jsonify({
238 'success': False,
239 'message': '服务器内部错误'
240 }), 500
241 finally:
242 session.close()
243
244@main.route('/reset-password', methods=['POST'])
245def reset_password():
246 """重置密码接口"""
247
248 engine = create_engine(Config.SQLURL)
249 SessionLocal = sessionmaker(bind=engine)
250 session = SessionLocal()
251
252 try:
253 data = request.get_json()
254
255 # 验证必填字段
256 if not data or not data.get('email') or not data.get('new_password') or not data.get('verification_code'):
257 return jsonify({
258 'success': False,
259 'message': '邮箱地址、新密码和验证码不能为空'
260 }), 400
261
262 email = data['email']
263 new_password = data['new_password']
264 verification_code = data['verification_code']
265
266 # 简单的邮箱格式验证
267 if '@' not in email or '.' not in email:
268 return jsonify({
269 'success': False,
270 'message': '邮箱格式不正确'
271 }), 400
272
273 # 密码长度验证
274 if len(new_password) < 6:
275 return jsonify({
276 'success': False,
277 'message': '密码长度不能少于6位'
278 }), 400
279
280 # 重置密码
281 f_auth = FAuth(session)
282 result = f_auth.reset_password_with_verification(email, new_password, verification_code)
283
284 if result['success']:
285 session.commit()
286 return jsonify(result), 200
287 else:
288 return jsonify(result), 400
289
290 except Exception as e:
291 session.rollback()
292 return jsonify({
293 'success': False,
294 'message': '服务器内部错误'
295 }), 500
296 finally:
297 session.close()
298
299@main.route('/test-jwt', methods=['POST'])
300@token_required
301def test_jwt(current_user):
302 """测试JWT令牌接口(需要登录)"""
303 try:
304 # 获取当前请求的token(从装饰器已验证的Authorization header)
305 auth_header = request.headers.get('Authorization')
306 current_token = auth_header[7:] if auth_header and auth_header.startswith('Bearer ') else None
307
308 print(f"当前用户: {current_user.username}")
309 print(f"当前用户ID: {current_user.id}")
310 print(current_user.role)
311 print(f"Token验证成功: {current_token[:20]}..." if current_token else "No token")
312
313 # 可选:检查请求体中是否有额外的token需要验证
314 data = request.get_json() or {}
315 additional_token = data.get('token')
316
317 response_data = {
318 'success': True,
319 'message': 'JWT令牌验证成功',
320 'user': current_user.to_dict(),
321 'token_info': {
322 'header_token_verified': True,
323 'token_preview': current_token[:20] + "..." if current_token else None
324 }
325 }
326
327 # 如果请求体中有额外的token,也验证一下
328 if additional_token:
329 try:
330 additional_result = FAuth.verify_token(additional_token)
331 response_data['additional_token_verification'] = additional_result
332 print(f"额外token验证结果: {additional_result}")
333 except Exception as e:
334 response_data['additional_token_verification'] = {
335 'success': False,
336 'message': f'额外token验证失败: {str(e)}'
337 }
338
339 return jsonify(response_data), 200
340
341 except Exception as e:
342 print(f"test_jwt 错误: {str(e)}")
343 return jsonify({
344 'success': False,
345 'message': f'JWT令牌验证失败: {str(e)}'
TRM-coding3127efa2025-06-18 22:54:25 +0800346 }), 500
347
348@main.route('/verify_user', methods=['POST'])
349@token_required
350def verify_user(current_user):
351 """测试JWT令牌接口(需要登录)"""
352 try:
353 # 获取当前请求的token(从装饰器已验证的Authorization header)
354 auth_header = request.headers.get('Authorization')
355 current_token = auth_header[7:] if auth_header and auth_header.startswith('Bearer ') else None
356
357 print(f"当前用户: {current_user.username}")
358 print(f"当前用户ID: {current_user.id}")
359 print(current_user.role)
360 print(f"Token验证成功: {current_token[:20]}..." if current_token else "No token")
361
362 # 可选:检查请求体中是否有额外的token需要验证
363 data = request.get_json() or {}
364 additional_token = data.get('token')
365
366 response_data = {
367 'success': True,
368 'userid': current_user.id,
369 'role': current_user.role,
370 }
371
372 return jsonify(response_data), 200
373
374 except Exception as e:
375 print(f"用户验证错误: {str(e)}")
376 return jsonify({
377 'success': False,
378 'message': f'JWT令牌验证失败: {str(e)}'
379 }), 500
380
381@main.route('/recommend', methods=['POST'])
382@token_required
383def get_recommendations(current_user):
384 """获取个性化推荐接口"""
385 try:
386 data = request.get_json() or {}
387 user_id = data.get('user_id') or current_user.id
TRM-coding3127efa2025-06-18 22:54:25 +0800388
Raver9d0ecf52025-06-25 17:35:39 +0800389 print(f"为用户 {user_id} 获取个性化推荐")
TRM-coding3127efa2025-06-18 22:54:25 +0800390
391 # 调用推荐系统
Raver9d0ecf52025-06-25 17:35:39 +0800392 user2idx, post2idx = build_user_post_graph(return_mapping=True)
393 recommendations = recommendation_service.get_recommendations(float(user_id), topk=10)
TRM-coding3127efa2025-06-18 22:54:25 +0800394
395 return jsonify({
396 'success': True,
397 'data': {
398 'user_id': user_id,
399 'recommendations': recommendations,
400 'count': len(recommendations),
401 'type': 'personalized'
402 },
403 'message': '个性化推荐获取成功'
404 }), 200
405
406 except Exception as e:
407 print(f"推荐系统错误: {str(e)}")
408 return jsonify({
409 'success': False,
410 'message': f'推荐获取失败: {str(e)}'
411 }), 500