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