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