blob: f672fc3568f409a4b7fadc3147da53ef180c9dd2 [file] [log] [blame]
TRM-coding29174c22025-06-18 23:56:51 +08001from flask import Flask, jsonify, request, session
2from flask_sqlalchemy import SQLAlchemy
3from flask_cors import CORS
4
5app = Flask(__name__)
6app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@10.126.59.25/redbook'
7app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
8app.secret_key = 'your_secret_key'
9CORS(app, supports_credentials=True)
10
11db = SQLAlchemy(app)
12
13# 模型定义
14# 用户表
15class User(db.Model):
16 __tablename__ = 'users'
17 id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='用户ID')
18 username = db.Column(db.String(50), unique=True, nullable=False, comment='用户名')
19 password = db.Column(db.String(255), nullable=False, comment='加密密码')
20 email = db.Column(db.String(100), unique=True, nullable=False, comment='邮箱')
21 avatar = db.Column(db.String(255), comment='头像URL')
22 role = db.Column(db.Enum('superadmin', 'user', 'admin'), default='user', comment='角色')
23 bio = db.Column(db.String(255), comment='个人简介')
24 status = db.Column(db.Enum('active', 'banned', 'muted'), default='active', comment='账号状态')
25 created_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), comment='创建时间')
26 updated_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(),
27 onupdate=db.func.current_timestamp(), comment='更新时间')
28 birthday = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(),comment='生日')
29 gender = db.Column(db.String(10),comment='性别')
30 location = db.Column(db.String(100),comment='所在地')
31
32 # 关系定义
33 posts = db.relationship('Post', backref='author', lazy=True)
34 behaviors = db.relationship('Behavior', backref='user', lazy=True)
35 comments = db.relationship('Comment', backref='commenter', lazy=True)
36 notifications = db.relationship('Notification', backref='recipient', lazy=True)
37 audits = db.relationship('Audit', backref='admin', lazy=True)
38 logs = db.relationship('Log', backref='logger', lazy=True)
39 user_tags = db.relationship('UserTag', backref='user', lazy=True)
40
41# 标签表
42class Tag(db.Model):
43 __tablename__ = 'tags'
44 id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='标签ID')
45 name = db.Column(db.String(50), unique=True, nullable=False, comment='标签名称')
46 description = db.Column(db.String(255), comment='标签描述')
47 created_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), comment='创建时间')
48
49 # 关系定义
50 post_tags = db.relationship('PostTag', backref='tag', lazy=True)
51 user_tags = db.relationship('UserTag', backref='tag', lazy=True)
52
53# 话题/超话表
54class Topic(db.Model):
55 __tablename__ = 'topics'
56 id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='话题ID')
57 name = db.Column(db.String(100), unique=True, nullable=False, comment='话题名称')
58 description = db.Column(db.Text, comment='话题描述')
59 status = db.Column(db.Enum('active', 'archived'), default='active', comment='状态')
60 created_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), comment='创建时间')
61
62 # 关系定义
63 posts = db.relationship('Post', backref='topic', lazy=True)
64
65# 内容帖子表
66class Post(db.Model):
67 __tablename__ = 'posts'
68 id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='帖子ID')
69 user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False, comment='作者ID')
70 topic_id = db.Column(db.Integer, db.ForeignKey('topics.id', ondelete='SET NULL'), comment='所属话题ID')
71 type = db.Column(db.Enum('text', 'image', 'video', 'document'), default='text', comment='内容类型')
72 title = db.Column(db.String(255), nullable=False, comment='标题')
73 content = db.Column(db.Text, nullable=False, comment='正文内容')
74 media_urls = db.Column(db.JSON, comment='媒体资源URL数组')
75 status = db.Column(db.Enum('draft', 'pending', 'published', 'deleted', 'rejected'), default='draft', comment='状态')
76 heat = db.Column(db.Integer, default=0, comment='热度值')
77 created_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), comment='创建时间')
78 updated_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(),
79 onupdate=db.func.current_timestamp(), comment='更新时间')
80
81 # 关系定义
82 behaviors = db.relationship('Behavior', backref='post', lazy=True)
83 comments = db.relationship('Comment', backref='post', lazy=True)
84 post_tags = db.relationship('PostTag', backref='post', lazy=True)
85 audits = db.relationship('Audit', backref='post', lazy=True)
86
87# 帖子标签关联表
88class PostTag(db.Model):
89 __tablename__ = 'post_tags'
90 post_id = db.Column(db.Integer, db.ForeignKey('posts.id', ondelete='CASCADE'), primary_key=True, comment='帖子ID')
91 tag_id = db.Column(db.Integer, db.ForeignKey('tags.id', ondelete='CASCADE'), primary_key=True, comment='标签ID')
92
93# 用户行为表
94class Behavior(db.Model):
95 __tablename__ = 'behaviors'
96 id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, comment='行为ID')
97 user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False, comment='用户ID')
98 post_id = db.Column(db.Integer, db.ForeignKey('posts.id', ondelete='CASCADE'), nullable=False, comment='帖子ID')
99 type = db.Column(db.Enum('like', 'comment', 'favorite', 'view', 'share'), nullable=False, comment='行为类型')
100 value = db.Column(db.Integer, default=1, comment='行为值')
101 created_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), comment='行为时间')
102
103# 评论表
104class Comment(db.Model):
105 __tablename__ = 'comments'
106 id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='评论ID')
107 post_id = db.Column(db.Integer, db.ForeignKey('posts.id', ondelete='CASCADE'), nullable=False, comment='帖子ID')
108 user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False, comment='用户ID')
109 parent_id = db.Column(db.Integer, db.ForeignKey('comments.id', ondelete='CASCADE'), comment='父评论ID')
110 content = db.Column(db.Text, nullable=False, comment='评论内容')
111 status = db.Column(db.Enum('active', 'deleted'), default='active', comment='状态')
112 created_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), comment='创建时间')
113 updated_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(),
114 onupdate=db.func.current_timestamp(), comment='更新时间')
115
116 # 关系定义
117 replies = db.relationship('Comment', backref=db.backref('parent', remote_side=[id]), lazy=True)
118
119# 用户关注关系表
120class Follow(db.Model):
121 __tablename__ = 'follows'
122 follower_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), primary_key=True, comment='关注者ID')
123 followee_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), primary_key=True, comment='被关注者ID')
124 created_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), comment='关注时间')
125
126 # 关系定义
127 follower = db.relationship('User', foreign_keys=[follower_id], backref='following')
128 followee = db.relationship('User', foreign_keys=[followee_id], backref='followers')
129
130# 通知表
131class Notification(db.Model):
132 __tablename__ = 'notifications'
133 id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='通知ID')
134 user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False, comment='接收用户ID')
135 type = db.Column(db.Enum('like', 'comment', 'follow', 'system', 'audit'), nullable=False, comment='通知类型')
136 content = db.Column(db.JSON, nullable=False, comment='通知内容')
137 is_read = db.Column(db.Boolean, default=False, comment='是否已读')
138 created_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), comment='创建时间')
139
140# 审核记录表
141class Audit(db.Model):
142 __tablename__ = 'audits'
143 id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='审核ID')
144 post_id = db.Column(db.Integer, db.ForeignKey('posts.id', ondelete='CASCADE'), nullable=False, comment='帖子ID')
145 admin_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False, comment='管理员ID')
146 result = db.Column(db.Enum('approved', 'rejected'), nullable=False, comment='审核结果')
147 reason = db.Column(db.String(255), comment='审核原因')
148 created_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), comment='审核时间')
149
150# 日志表
151class Log(db.Model):
152 __tablename__ = 'logs'
153 id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, comment='日志ID')
154 user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='SET NULL'), comment='用户ID')
155 type = db.Column(db.Enum('access', 'error', 'behavior', 'system'), nullable=False, comment='日志类型')
156 content = db.Column(db.Text, nullable=False, comment='日志内容')
157 ip = db.Column(db.String(45), comment='IP地址')
158 created_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), comment='记录时间')
159
160# 用户兴趣标签表
161class UserTag(db.Model):
162 __tablename__ = 'user_tags'
163 user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), primary_key=True, comment='用户ID')
164 tag_id = db.Column(db.Integer, db.ForeignKey('tags.id', ondelete='CASCADE'), primary_key=True, comment='标签ID')
165 weight = db.Column(db.Float, default=1.0, comment='兴趣权重')
166 created_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), comment='创建时间')
167 updated_at = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(),
168 onupdate=db.func.current_timestamp(), comment='更新时间')
169
170
171# 自动登录用户11
172@app.before_request
173def auto_login():
174 # 如果用户未登录,自动设置为用户11
175 if 'user_id' not in session:
176 session['user_id'] = 11
177
178# 获取当前用户信息
179@app.route('/api/current-user')
180def current_user():
181 user_id = session.get('user_id', 1)
182 user = User.query.get(user_id)
183 if not user:
184 return jsonify({'error': 'User not found'}), 404
185
186 following_count = Follow.query.filter_by(follower_id=user_id).count()
187 followers_count = Follow.query.filter_by(followee_id=user_id).count()
188
189 return jsonify({
190 'id': user.id,
191 'username': user.username,
192 'email': user.email,
193 'avatar': user.avatar,
194 'bio': user.bio,
195 'following_count': following_count,
196 'followers_count': followers_count
197 })
198
199# 获取指定用户信息
200@app.route('/api/user/<int:user_id>')
201def get_user(user_id):
202 current_user_id = session.get('user_id', 1)
203 user = User.query.get(user_id)
204 if not user:
205 return jsonify({'error': 'User not found'}), 404
206
207 following_count = Follow.query.filter_by(follower_id=user_id).count()
208 followers_count = Follow.query.filter_by(followee_id=user_id).count()
209
210 is_following = False
211 if current_user_id:
212 is_following = Follow.query.filter_by(
213 follower_id=current_user_id,
214 followee_id=user_id
215 ).first() is not None
216
217 return jsonify({
218 'id': user.id,
219 'username': user.username,
220 'avatar': user.avatar,
221 'bio': user.bio,
222 'following_count': following_count,
223 'followers_count': followers_count,
224 'is_following': is_following,
225 'gender': user.gender,
226 'location': user.location,
227 'birthday': user.birthday
228 })
229
230# 更新用户信息
231@app.route('/api/user/<int:user_id>', methods=['PUT'])
232def update_user(user_id):
956303669f3699a12025-06-21 16:40:05 +0800233 current_user_id = session.get('user_id', 1)
234 if current_user_id != user_id:
235 return jsonify({'error': 'Unauthorized'}), 403
TRM-coding29174c22025-06-18 23:56:51 +0800236
237 user = User.query.get(user_id)
238 if not user:
239 return jsonify({'error': 'User not found'}), 404
240
241 data = request.json
242 print(data)
243 if 'avatar' in data:
244 user.avatar = data['avatar']
245 if 'bio' in data:
246 user.bio = data['bio']
247 if 'gender' in data:
248 user.gender = data['gender']
249 if 'location' in data:
250 user.location = data['location']
251 if 'birthday' in data:
252 user.birthday = data['birthday']
253
254 db.session.commit()
255 return jsonify({
256 'id': user.id,
257 'avatar': user.avatar,
258 'bio': user.bio
259 })
260
261# 获取用户收藏
262@app.route('/api/user/<int:user_id>/favorites', methods=['GET'])
263def get_user_favorites(user_id):
264 # 检查用户是否登录
265 if 'user_id' not in session:
266 return jsonify({'error': '未登录'}), 401
267
956303669f3699a12025-06-21 16:40:05 +0800268 # 验证请求的用户ID与登录用户ID是否一致
269 if session['user_id'] != user_id:
270 return jsonify({'error': '无权访问其他用户的收藏'}), 403
TRM-coding29174c22025-06-18 23:56:51 +0800271
272 try:
273 # 获取收藏行为及其关联的帖子
274 favorites = db.session.query(Behavior, Post).join(
275 Post, Behavior.post_id == Post.id
276 ).filter(
277 Behavior.user_id == user_id,
278 Behavior.type == 'favorite'
279 ).all()
280
281 # 构建响应数据
282 result = []
283 for behavior, post in favorites:
284 # 获取帖子作者信息
285 author = User.query.get(post.user_id)
286
287 # 构建响应对象
288 result.append({
289 'behavior_id': behavior.id,
290 'post': {
291 'id': post.id,
292 'title': post.title,
293 'type': post.type,
294 'content_preview': post.content[:100] + '...' if len(post.content) > 100 else post.content,
295 'media_urls': post.media_urls,
296 'created_at': post.created_at.strftime('%Y-%m-%d %H:%M:%S'),
297 'author': {
298 'id': author.id,
299 'username': author.username,
300 'avatar': author.avatar
301 }
302 },
303 'favorited_at': behavior.created_at.strftime('%Y-%m-%d %H:%M:%S')
304 })
305
306 return jsonify(result)
307
308 except Exception as e:
309 app.logger.error(f"获取收藏时出错: {str(e)}")
310 return jsonify({'error': '获取收藏失败'}), 500
311
312
313# 获取用户发布的帖子
314@app.route('/api/user/<int:user_id>/posts')
315def get_user_posts(user_id):
316 # 允许任何人查看用户发布的帖子
317 posts = Post.query.filter_by(
318 user_id=user_id,
319 status='published'
320 ).all()
321
322 return jsonify([{
323 'id': post.id,
324 'title': post.title,
325 'content': post.content[:100] + '...' if len(post.content) > 100 else post.content,
326 'type': post.type,
327 'heat': post.heat,
328 'created_at': post.created_at.strftime('%Y-%m-%d %H:%M')
329 } for post in posts])
330
331# 获取用户关注列表
332@app.route('/api/user/<int:user_id>/following')
333def get_user_following(user_id):
334 # 允许任何人查看用户的关注列表
335 following = Follow.query.filter_by(follower_id=user_id).all()
336
337 # 获取被关注用户的详细信息
338 following_list = []
339 for follow in following:
340 user = User.query.get(follow.followee_id)
341 if user:
342 followers_count = Follow.query.filter_by(followee_id=user.id).count()
343
344 following_list.append({
345 'id': user.id,
346 'username': user.username,
347 'avatar': user.avatar,
348 'followers_count': followers_count
349 })
350
351 return jsonify(following_list)
352
353# 关注/取消关注用户
956303669fe89ae12025-06-21 16:43:56 +0800354@app.route('/api/follow/<int:followee_id>', methods=['POST', 'DELETE'])
355def follow_user(followee_id):
356 follower_id = session.get('user_id', 1)
TRM-coding29174c22025-06-18 23:56:51 +0800357 if follower_id == followee_id:
358 return jsonify({'error': 'Cannot follow yourself'}), 400
359
360 if request.method == 'POST':
361 existing = Follow.query.filter_by(
362 follower_id=follower_id,
363 followee_id=followee_id
364 ).first()
365
366 if not existing:
367 follow = Follow(
368 follower_id=follower_id,
369 followee_id=followee_id
370 )
371 db.session.add(follow)
372 db.session.commit()
373 return jsonify({'message': 'Followed successfully'})
374
375 elif request.method == 'DELETE':
376 follow = Follow.query.filter_by(
377 follower_id=follower_id,
378 followee_id=followee_id
379 ).first()
380
381 if follow:
382 db.session.delete(follow)
383 db.session.commit()
384 return jsonify({'message': 'Unfollowed successfully'})
385
386
387# 新增获取粉丝列表的API
388@app.route('/api/user/<int:user_id>/followers')
389def get_user_followers(user_id):
390 try:
391 # 查询关注该用户的用户列表
392 followers = db.session.query(User).join(
393 Follow, Follow.follower_id == User.id
394 ).filter(
395 Follow.followee_id == user_id
396 ).all()
397
398 # 获取当前登录用户ID(如果有)
399 current_user_id = session.get('user_id') if 'user_id' in session else None
400
401 # 构建响应数据
402 result = []
403 for user in followers:
404 # 检查当前用户是否关注了这个粉丝
405 is_following = False
406 if current_user_id:
407 follow_relation = Follow.query.filter_by(
408 follower_id=current_user_id,
409 followee_id=user.id
410 ).first()
411 is_following = follow_relation is not None
412
413 # 计算该粉丝的粉丝数
414 followers_count = Follow.query.filter_by(followee_id=user.id).count()
415
416 result.append({
417 'id': user.id,
418 'username': user.username,
419 'avatar': user.avatar,
420 'bio': user.bio,
421 'followers_count': followers_count,
422 'is_following': is_following
423 })
424
425
426 return jsonify({
427 'success': True,
428 'data': result
429 })
430
431 except Exception as e:
432 app.logger.error(f"获取粉丝列表失败: {str(e)}")
433 return jsonify({
434 'success': False,
435 'error': '获取粉丝列表失败'
436 }), 500
437
438# 辅助函数:检查当前用户是否关注了目标用户
439def check_following_status(follower_id, followee_id):
440 return Follow.query.filter_by(
441 follower_id=follower_id,
442 followee_id=followee_id
443 ).first() is not None
444
445
446# 记录用户点赞收藏总数
447@app.route('/api/user/<int:user_id>/interactions', methods=['GET'])
448def get_user_interactions(user_id):
449 try:
450 # 计算用户的获赞总数(所有帖子的点赞数)
451 like_count = db.session.query(db.func.sum(Behavior.value)).filter(
956303669f3699a12025-06-21 16:40:05 +0800452 Behavior.post.has(user_id=user_id),
TRM-coding29174c22025-06-18 23:56:51 +0800453 Behavior.type == 'like'
454 ).scalar() or 0
455
456 # 计算用户的收藏总数(所有帖子的收藏数)
457 favorite_count = db.session.query(db.func.sum(Behavior.value)).filter(
956303669f3699a12025-06-21 16:40:05 +0800458 Behavior.post.has(user_id=user_id),
TRM-coding29174c22025-06-18 23:56:51 +0800459 Behavior.type == 'favorite'
460 ).scalar() or 0
461
462 return jsonify({
463 'likes_count': like_count,
464 'favorites_count': favorite_count
465 })
466
467 except Exception as e:
468 app.logger.error(f"获取用户互动数据失败: {str(e)}")
469 return jsonify({'error': '获取互动数据失败'}), 500
470
TRM-coding29174c22025-06-18 23:56:51 +0800471
472if __name__ == '__main__':
473 app.run(debug=True,port='5715',host='0.0.0.0')