消息通
Change-Id: Ibf0f82b7d2843d0a67935ceb986047934f0642dd
diff --git a/src/main/java/com/ptp/ptplatform/controller/HelpPostController.java b/src/main/java/com/ptp/ptplatform/controller/HelpPostController.java
index 9d18320..457823a 100644
--- a/src/main/java/com/ptp/ptplatform/controller/HelpPostController.java
+++ b/src/main/java/com/ptp/ptplatform/controller/HelpPostController.java
@@ -5,6 +5,7 @@
import com.ptp.ptplatform.entity.HelpPost;
import com.ptp.ptplatform.service.HelpCommentService;
import com.ptp.ptplatform.service.HelpPostService;
+import com.ptp.ptplatform.service.NotificationService;
import com.ptp.ptplatform.utils.Result;
import lombok.AllArgsConstructor;
import org.springframework.http.MediaType;
@@ -26,6 +27,7 @@
public class HelpPostController {
private final HelpPostService postService;
private final HelpCommentService commentService;
+ private final NotificationService notificationService;
// 修改创建帖子的方法,支持图片上传
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@@ -149,7 +151,7 @@
);
// 获取实时评论总数(包括所有层级)
- long totalComments = allComments.size(); // 直接使用列表大小
+ int totalComments = allComments.size(); // 直接使用列表大小
post.setReplyCount(totalComments); // 更新计数
// 构建评论树形结构
@@ -181,33 +183,43 @@
// 点赞帖子
@PostMapping("/{Id}/like")
- public Result likePost(@PathVariable int Id) {
- postService.incrementLike(Id);
- return Result.ok();
+ public Result likePost(
+ @PathVariable("Id") int Id,
+ @RequestParam("likerId") String likerId
+ ) {
+ // 调用 ServiceImpl.likePost(...),自动更新 like_count 并插入通知
+ postService.likePost(Id, likerId);
+ return Result.ok().message("点赞成功");
}
+ // 评论帖子
@PostMapping(value = "/{Id}/comments", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
- public Result comment(@PathVariable int Id,
- @RequestParam("authorId") String authorId,
- @RequestParam("content") String content,
- @RequestParam(value = "image", required = false) MultipartFile image) {
-
+ public Result commentOnPost(
+ @PathVariable("Id") int Id,
+ @RequestParam("authorId") String authorId,
+ @RequestParam("content") String content,
+ @RequestParam(value = "image", required = false) MultipartFile image
+ ) {
+ // 1. 构造 HelpComment 对象
HelpComment comment = new HelpComment();
comment.setPostId(Id);
- comment.setAuthorId(authorId); // 类型为 String
+ comment.setAuthorId(authorId);
comment.setContent(content);
comment.setCreateTime(LocalDateTime.now());
comment.setLikeCount(0);
- comment.setParentId(0);
- comment.setReplyTo(null);
+ comment.setParentId(0); // 这里是对帖子的一级评论
+ comment.setImageUrl(null); // 默认无图片
+ // 2. 处理可选的图片上传逻辑
if (image != null && !image.isEmpty()) {
try {
String fileExt = image.getOriginalFilename()
- .substring(image.getOriginalFilename().lastIndexOf(".") + 1);
- String fileName = UUID.randomUUID() + "." + fileExt;
+ .substring(image.getOriginalFilename().lastIndexOf('.') + 1);
+ String fileName = UUID.randomUUID().toString() + "." + fileExt;
- String uploadDir = System.getProperty("user.dir") + File.separator + "uploads";
+ // 上传到项目根目录的 uploads 文件夹
+ String uploadDir = System.getProperty("user.dir")
+ + File.separator + "uploads";
File dir = new File(uploadDir);
if (!dir.exists()) dir.mkdirs();
@@ -216,20 +228,21 @@
comment.setImageUrl("/uploads/" + fileName);
} catch (IOException e) {
- return Result.error(404).setMessage("图片上传失败:" + e.getMessage());
+ return Result.error(500).message("图片上传失败:" + e.getMessage());
}
}
- // 保存评论
- boolean saved = commentService.save(comment);
- if (!saved) return Result.error(500).setMessage("评论保存失败");
+ // 3. 通过 ServiceImpl 完成“保存评论 + 更新 reply_count + 发通知给帖子作者”
+ commentService.commentOnHelpPost(comment);
- postService.incrementReplyCount(Id);
+ // 4. 构造返回值:把刚刚保存的评论(从数据库中查出来的完整对象)和帖子最新的 replyCount 都返回
+ // 注意:comment.insert(...) 后,comment.getId() 将有 DB 自动生成的主键
HelpComment newComment = commentService.getById(comment.getId());
+ int updatedReplyCount = postService.getById(Id).getReplyCount();
return Result.ok()
.data("comment", newComment)
- .data("newReplyCount", postService.getById(Id).getReplyCount());
+ .data("newReplyCount", updatedReplyCount);
}
// 删除帖子
diff --git a/src/main/java/com/ptp/ptplatform/controller/NotificationController.java b/src/main/java/com/ptp/ptplatform/controller/NotificationController.java
new file mode 100644
index 0000000..61a0f12
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/controller/NotificationController.java
@@ -0,0 +1,29 @@
+package com.ptp.ptplatform.controller;
+
+import com.ptp.ptplatform.entity.Notification;
+import com.ptp.ptplatform.service.NotificationService;
+import com.ptp.ptplatform.utils.Result;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/notifications")
+public class NotificationController {
+
+ @Autowired
+ private NotificationService notificationService;
+
+ @GetMapping("")
+ public Result listNotifications(@RequestParam("userId") String userId) {
+ List<Notification> list = notificationService.listByUser(userId);
+ return Result.ok().data("notifications", list);
+ }
+
+ @PostMapping("/{id}/read")
+ public Result markAsRead(@PathVariable("id") Integer id) {
+ notificationService.markAsRead(id);
+ return Result.ok().message("通知已标记为已读");
+ }
+}
diff --git a/src/main/java/com/ptp/ptplatform/controller/SystemNotificationController.java b/src/main/java/com/ptp/ptplatform/controller/SystemNotificationController.java
new file mode 100644
index 0000000..81b111a
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/controller/SystemNotificationController.java
@@ -0,0 +1,92 @@
+package com.ptp.ptplatform.controller;
+
+import com.ptp.ptplatform.entity.Notification;
+import com.ptp.ptplatform.entity.USER;
+import com.ptp.ptplatform.mapper.UserMapper;
+import com.ptp.ptplatform.service.NotificationService;
+import com.ptp.ptplatform.utils.Result;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 调用示例:
+ * POST /api/system/announce
+ * Content-Type: application/json
+ * {
+ * "title": "系统维护提醒",
+ * "content": "本周六(6月5日)凌晨2:00-4:00将进行全站维护,届时功能暂不可用。"
+ * }
+ */
+@RestController
+@RequestMapping("/system")
+@AllArgsConstructor
+public class SystemNotificationController {
+
+ private final UserMapper userMapper; // 用于查询所有用户
+ private final NotificationService notificationService; // 用于保存通知
+
+
+ //发布一条系统公告,系统会给当前所有注册用户发一条通知。
+
+ @PostMapping("/announce")
+ public Result announce(@RequestBody AnnounceRequest body) {
+ // 1. 参数校验
+ if (body.getTitle() == null || body.getTitle().isBlank()
+ || body.getContent() == null || body.getContent().isBlank()) {
+ return Result.error(400).message("请求参数 title/content 不能为空");
+ }
+
+ // 2. 查询所有用户
+ List<USER> allUsers = userMapper.selectAllUsers();
+ if (allUsers == null || allUsers.isEmpty()) {
+ return Result.error(500).message("当前系统中没有可发送通知的用户");
+ }
+
+ // 3. 遍历所有用户,为每个用户创建并保存一条 Notification
+ int sentCount = 0;
+ LocalDateTime now = LocalDateTime.now();
+ for (USER u : allUsers) {
+ String uid = u.getUsername(); // 获取用户名(用户主键)
+
+ Notification n = new Notification();
+ n.setUserId(uid); // 通知接收者 = 当前用户名
+ n.setType("SYSTEM"); // 通知类型标记为系统消息
+ n.setTitle(body.getTitle()); // 通知标题
+ n.setContent(body.getContent()); // 通知内容
+ n.setTargetId(null); // 系统公告通常无需跳转,设为 null
+ n.setTargetType(null);
+ n.setIsRead(false);
+ n.setCreateTime(now);
+
+ notificationService.saveNotification(n);
+ sentCount++;
+ }
+
+ // 4. 返回发送成功及发送数量
+ return Result.ok()
+ .message("公告已发送,通知条数:" + sentCount)
+ .data("sentCount", sentCount);
+ }
+
+ public static class AnnounceRequest {
+ private String title;
+ private String content;
+
+ // Getter & Setter
+ public String getTitle() {
+ return title;
+ }
+ public void setTitle(String title) {
+ this.title = title;
+ }
+ public String getContent() {
+ return content;
+ }
+ public void setContent(String content) {
+ this.content = content;
+ }
+ }
+}
diff --git a/src/main/java/com/ptp/ptplatform/controller/TorrentController.java b/src/main/java/com/ptp/ptplatform/controller/TorrentController.java
index 2c6031c..e69c70e 100644
--- a/src/main/java/com/ptp/ptplatform/controller/TorrentController.java
+++ b/src/main/java/com/ptp/ptplatform/controller/TorrentController.java
@@ -7,11 +7,8 @@
import com.ptp.ptplatform.mapper.TorrentMapper;
import com.ptp.ptplatform.mapper.UserMapper;
import com.ptp.ptplatform.mapper.DownloadTorrentMapper;
-import com.ptp.ptplatform.service.ClientService;
-import com.ptp.ptplatform.service.TrackerService;
+import com.ptp.ptplatform.service.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.ptp.ptplatform.service.TorrentCommentService;
-import com.ptp.ptplatform.service.TorrentService;
import com.ptp.ptplatform.utils.Result;
import com.ptp.ptplatform.utils.SizeCalculation;
import com.turn.ttorrent.bcodec.BDecoder;
@@ -54,6 +51,7 @@
public class TorrentController {
private final TorrentService postService;
private final TorrentCommentService commentService;
+ private final NotificationService notificationService;
@Resource
private TorrentMapper torrentMapper;
@@ -188,36 +186,55 @@
// 点赞帖子
@PostMapping("/{Id}/like")
- public Result likePost(@PathVariable int Id) {
+ public Result likePost(
+ @PathVariable int Id,
+ @RequestParam("likerId") String likerId
+ ) {
postService.incrementLike(Id);
+ // 通知发帖人
+ TORRENT post = postService.getById(Id);
+ if (post != null) {
+ String authorId = post.getUsername(); // TORRENT 实体中发帖人字段是 username
+ if (!authorId.equals(likerId)) {
+ Notification n = new Notification();
+ n.setUserId(authorId); // 通知接收者 = 帖子作者
+ n.setType("TORRENT_LIKE");
+ n.setTitle("您的 Torrent 帖子被点赞");
+ n.setContent("用户 "
+ + likerId
+ + " 点赞了您的 Torrent 帖子: \""
+ + post.getTorrentName()
+ + "\"");
+ n.setTargetId(Id);
+ n.setTargetType("TORRENT");
+ n.setIsRead(false);
+ n.setCreateTime(LocalDateTime.now());
+ notificationService.saveNotification(n);
+ }
+ }
+
return Result.ok();
}
@PostMapping("/{Id}/comments")
- public Result comment(@PathVariable int Id,
- @RequestBody TorrentComment comment) {
- // 设置评论信息
+ public Result comment(
+ @PathVariable int Id,
+ @RequestBody TorrentComment comment
+ ) {
comment.setPostId(Id);
comment.setCreateTime(LocalDateTime.now());
- comment.setLikeCount(0); // 初始化点赞数
- comment.setParentId(0); // 默认父评论ID
- comment.setReplyTo(null); // 主评论 replyTo=null
-
- // 保存评论
- boolean saved = commentService.save(comment);
- if (!saved) {
- return Result.error(404).setMessage("评论保存失败");
+ comment.setLikeCount(0);
+ if (comment.getParentId() == null) {
+ comment.setParentId(0); // 默认一级评论
}
- // 更新回复数
- postService.incrementReplyCount(Id);
-
- // 获取更新后的完整评论(包含数据库生成的ID和时间)
+ commentService.commentOnTorrentPost(comment); //通知作者
TorrentComment newComment = commentService.getById(comment.getId());
+ int updatedReplyCount = postService.getById(Id).getReply_count();
return Result.ok()
- .data("comment", newComment) // 返回完整评论数据
- .data("newReplyCount", postService.getById(Id).getReply_count());
+ .data("comment", newComment)
+ .data("newReplyCount", updatedReplyCount);
}
@PostConstruct //启动项目时候自动启动tracker服务器
@@ -372,6 +389,20 @@
trackerservice.serTracker();
+ // 通知下载完成
+ Notification downloadCompleteNotice = new Notification();
+ downloadCompleteNotice.setUserId(userDownload.getUsername());
+ downloadCompleteNotice.setType("DOWNLOAD_COMPLETE");
+ downloadCompleteNotice.setTitle("下载完成提醒");
+ downloadCompleteNotice.setContent(
+ "您下载的种子 \"" + torrent.getTorrentName() + "\" 已经成功完成。"
+ );
+ downloadCompleteNotice.setTargetId(id);
+ downloadCompleteNotice.setTargetType("TORRENT");
+ downloadCompleteNotice.setIsRead(false);
+ downloadCompleteNotice.setCreateTime(LocalDateTime.now());
+ notificationService.saveNotification(downloadCompleteNotice);
+
//更新上传量和下载量
userDownload.updateDownload(SizeCalculation.getDownload(torrent.getSize(), discountMapper, torrent.getCreateTime()));
userUpload.updateUpload(SizeCalculation.getUpload(torrent.getSize(), discountMapper, torrent.getCreateTime()));
diff --git a/src/main/java/com/ptp/ptplatform/entity/HelpPost.java b/src/main/java/com/ptp/ptplatform/entity/HelpPost.java
index 4a2cfe6..a7cfd11 100644
--- a/src/main/java/com/ptp/ptplatform/entity/HelpPost.java
+++ b/src/main/java/com/ptp/ptplatform/entity/HelpPost.java
@@ -16,6 +16,6 @@
private String content;
private String imageUrl;
private Integer likeCount;
- private long replyCount;
+ private Integer replyCount;
private LocalDateTime createTime;
}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/entity/Notification.java b/src/main/java/com/ptp/ptplatform/entity/Notification.java
new file mode 100644
index 0000000..af1677b
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/entity/Notification.java
@@ -0,0 +1,23 @@
+package com.ptp.ptplatform.entity;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("notification")
+public class Notification {
+ @TableId(type = IdType.AUTO)
+ private Integer id;
+ private String userId;
+ private String type;
+ private String title;
+ private String content;
+ private Integer targetId;
+ private String targetType;
+ private Boolean isRead;
+ private LocalDateTime createTime;
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/entity/USER.java b/src/main/java/com/ptp/ptplatform/entity/USER.java
index 9762d88..3798696 100644
--- a/src/main/java/com/ptp/ptplatform/entity/USER.java
+++ b/src/main/java/com/ptp/ptplatform/entity/USER.java
@@ -3,6 +3,8 @@
package com.ptp.ptplatform.entity;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
import com.ptp.ptplatform.utils.SizeCalculation;
import jakarta.persistence.*;
import lombok.Data;
@@ -11,6 +13,7 @@
@Data
@Table(name = "user")
+@TableName("user")
public class USER {
@jakarta.persistence.Id
@@ -22,15 +25,19 @@
private int level; // 用户等级0-6
+ @TableField("registTime")
@Temporal(TemporalType.DATE)
private Date registTime = new Date();
+ @TableField("lastLogin")
@Temporal(TemporalType.DATE)
private Date lastLogin;
private long upload;
private long download;
+ @TableField("shareRate")
private double shareRate;//分享率 前端展示数据应该为 90.23%这种
+ @TableField("magicPoints")
private long magicPoints;// 魔力值
public enum Authority {
diff --git a/src/main/java/com/ptp/ptplatform/mapper/NotificationMapper.java b/src/main/java/com/ptp/ptplatform/mapper/NotificationMapper.java
new file mode 100644
index 0000000..76a5d83
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/mapper/NotificationMapper.java
@@ -0,0 +1,8 @@
+package com.ptp.ptplatform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ptp.ptplatform.entity.Notification;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface NotificationMapper extends BaseMapper<Notification> {}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/HelpCommentService.java b/src/main/java/com/ptp/ptplatform/service/HelpCommentService.java
index 8236fa7..4cead77 100644
--- a/src/main/java/com/ptp/ptplatform/service/HelpCommentService.java
+++ b/src/main/java/com/ptp/ptplatform/service/HelpCommentService.java
@@ -10,4 +10,6 @@
List<HelpComment> getReplies(int parentId);
// 新增:获取帖子直接评论数
long countByPostId(Integer postId);// 新增方法
-}
+ void commentOnHelpPost(HelpComment comment);
+ void replyComment(HelpComment reply);
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/HelpPostService.java b/src/main/java/com/ptp/ptplatform/service/HelpPostService.java
index 541f927..f005189 100644
--- a/src/main/java/com/ptp/ptplatform/service/HelpPostService.java
+++ b/src/main/java/com/ptp/ptplatform/service/HelpPostService.java
@@ -1,9 +1,12 @@
package com.ptp.ptplatform.service;
import com.baomidou.mybatisplus.extension.service.IService;
+import com.ptp.ptplatform.entity.HelpComment;
import com.ptp.ptplatform.entity.HelpPost;
public interface HelpPostService extends IService<HelpPost> {
- void incrementLike(int postId);
- void incrementReplyCount(int postId);
+ void incrementLike(Integer postId);
+ void incrementReplyCount(Integer postId);
+ void likePost(Integer postId, String likerId);//点赞,并通知该帖子的作者。
+ void commentOnHelpPost(HelpComment comment); //在帖子下发表一条新评论,并通知该帖子的作者。
}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/NotificationService.java b/src/main/java/com/ptp/ptplatform/service/NotificationService.java
new file mode 100644
index 0000000..d213484
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/service/NotificationService.java
@@ -0,0 +1,10 @@
+package com.ptp.ptplatform.service;
+
+import com.ptp.ptplatform.entity.Notification;
+import java.util.List;
+
+public interface NotificationService {
+ void saveNotification(Notification notification);
+ List<Notification> listByUser(String userId);
+ void markAsRead(Integer id);
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/TorrentCommentService.java b/src/main/java/com/ptp/ptplatform/service/TorrentCommentService.java
index 3b60257..648405f 100644
--- a/src/main/java/com/ptp/ptplatform/service/TorrentCommentService.java
+++ b/src/main/java/com/ptp/ptplatform/service/TorrentCommentService.java
@@ -8,4 +8,5 @@
public interface TorrentCommentService extends IService<TorrentComment> {
void incrementLike(int commentId);
List<TorrentComment> getReplies(int parentId);
+ void commentOnTorrentPost(TorrentComment comment);
}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/UserLevelService.java b/src/main/java/com/ptp/ptplatform/service/UserLevelService.java
index a95dec4..1e77b3f 100644
--- a/src/main/java/com/ptp/ptplatform/service/UserLevelService.java
+++ b/src/main/java/com/ptp/ptplatform/service/UserLevelService.java
@@ -6,6 +6,7 @@
import java.util.Date;
import java.util.List;
+import com.ptp.ptplatform.entity.Notification;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@@ -22,6 +23,8 @@
@Autowired
private UserMapper userMapper;
+ @Autowired
+ private NotificationService notificationService; // 新注入,用于发送通知
private static final double BYTES_PER_GB = 1024d * 1024 * 1024;
// 每周一凌晨 2 点触发
@@ -31,24 +34,56 @@
log.info("===== 开始刷新所有用户等级 =====");
LocalDate today = LocalDate.now();
+ // 1. 查询数据库里所有用户
List<USER> users = userMapper.selectList(null);
log.info(">> 从数据库查到 {} 个用户", users.size());
for (USER u : users) {
- double uploadGb = u.getUpload() / BYTES_PER_GB;
- long magicPoints = u.getMagicPoints();
- log.info("[LevelCheck] 用户={} shareRate={} uploadGB={} magicPoints={}",
- u.getUsername(),
- u.getShareRate(),
- uploadGb,
- magicPoints);
- int newLevel = calcLevel(u, today);
- if (newLevel != u.getLevel()) {
- log.info("[LevelUpdate] 用户={} level: {} -> {}", u.getUsername(), u.getLevel(), newLevel);
+ double shareRate = u.getShareRate(); // 当前分享率
+ double uploadGb = u.getUpload() / BYTES_PER_GB; // 上传大小 (GB)
+ long magicPoints = u.getMagicPoints(); // 魔力值
+ Date regTime = u.getRegistTime(); // 注册时间
+ LocalDate regDate = regTime.toInstant()
+ .atZone(ZoneId.systemDefault())
+ .toLocalDate();
+ long monthsSince = ChronoUnit.MONTHS.between(regDate, today);
+
+ log.info("[LevelCheck] 用户={} shareRate={} uploadGB={} magicPoints={} months={}",
+ u.getUsername(), shareRate, uploadGb, magicPoints, monthsSince);
+
+ // 2. 先记录旧等级
+ int oldLevel = u.getLevel();
+
+ // 3. 根据业务规则计算新等级
+ int newLevel = calcLevel(u, uploadGb, shareRate, magicPoints, monthsSince);
+
+ if (newLevel != oldLevel) {
+ log.info("[LevelUpdate] 用户={} 等级: {} -> {}", u.getUsername(), oldLevel, newLevel);
+
+ // 4. 更新用户实体的等级并写库
u.setLevel(newLevel);
- userMapper.updateById(u);
+// userMapper.updateById(u);
+ userMapper.updateUser(u);
+
+ // 5. 构造并保存“等级提升”通知
+ Notification n = new Notification();
+ n.setUserId(u.getUsername()); // 通知接收者 = 用户自己
+ n.setType("LEVEL_UP"); // 通知类型:LEVEL_UP
+ n.setTitle("恭喜您,用户等级已提升"); // 通知标题
+ n.setContent("您的用户等级已从 "
+ + oldLevel
+ + " 级提升至 "
+ + newLevel
+ + " 级!"); // 通知内容
+ n.setTargetId(null); // 等级通知无需跳转
+ n.setTargetType(null);
+ n.setIsRead(false);
+ n.setCreateTime(java.time.LocalDateTime.now());
+
+ notificationService.saveNotification(n);
}
}
+
log.info("===== 刷新完毕 =====");
}
@@ -56,46 +91,40 @@
/**
* 根据各项指标和注册时长计算用户等级
*/
- private int calcLevel(USER u, LocalDate today) {
- double shareRate = u.getShareRate(); // 0.9023 表示 90.23%
- double uploadGb = u.getUpload() / BYTES_PER_GB;
- long magicPoints = u.getMagicPoints();
-
- // 注册日期转 LocalDate
- Date regTime = u.getRegistTime();
- LocalDate regDate = regTime.toInstant()
- .atZone(ZoneId.systemDefault())
- .toLocalDate();
-
- long months = ChronoUnit.MONTHS.between(regDate, today);
-
- if (shareRate >= 1.2 &&
+ private int calcLevel(
+ USER u,
+ double uploadGb,
+ double shareRate,
+ long magicPoints,
+ long monthsSince
+ ) {
+ if (shareRate >= 1.2 &&
magicPoints >= 100_000 &&
uploadGb >= 10_240 &&
- months >= 12) {
+ monthsSince >= 12) {
return 5;
}
- if (shareRate >= 1.0 &&
+ if (shareRate >= 1.0 &&
magicPoints >= 50_000 &&
uploadGb >= 5_120 &&
- months >= 6) {
+ monthsSince >= 6) {
return 4;
}
- if (shareRate >= 0.8 &&
+ if (shareRate >= 0.8 &&
magicPoints >= 20_000 &&
uploadGb >= 1_024 &&
- months >= 3) {
+ monthsSince >= 3) {
return 3;
}
- if (shareRate >= 0.6 &&
+ if (shareRate >= 0.6 &&
magicPoints >= 5_000 &&
- uploadGb >= 200 &&
- months >= 1) {
+ uploadGb >= 200 &&
+ monthsSince >= 1) {
return 2;
}
- if (shareRate >= 0.4 &&
+ if (shareRate >= 0.4 &&
magicPoints >= 1_000 &&
- uploadGb >= 50) {
+ uploadGb >= 50) {
return 1;
}
return 0;
diff --git a/src/main/java/com/ptp/ptplatform/service/impl/HelpCommentServiceImpl.java b/src/main/java/com/ptp/ptplatform/service/impl/HelpCommentServiceImpl.java
index f15f618..7ba564b 100644
--- a/src/main/java/com/ptp/ptplatform/service/impl/HelpCommentServiceImpl.java
+++ b/src/main/java/com/ptp/ptplatform/service/impl/HelpCommentServiceImpl.java
@@ -3,8 +3,13 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ptp.ptplatform.entity.HelpComment;
+import com.ptp.ptplatform.entity.HelpPost;
+import com.ptp.ptplatform.entity.Notification;
import com.ptp.ptplatform.mapper.HelpCommentMapper;
+import com.ptp.ptplatform.mapper.HelpPostMapper;
import com.ptp.ptplatform.service.HelpCommentService;
+import com.ptp.ptplatform.service.NotificationService;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@@ -13,6 +18,14 @@
public class HelpCommentServiceImpl
extends ServiceImpl<HelpCommentMapper, HelpComment>
implements HelpCommentService {
+ @Autowired
+ private HelpCommentMapper helpCommentMapper;
+
+ @Autowired
+ private HelpPostMapper helpPostMapper;
+
+ @Autowired
+ private NotificationService notificationService;
@Override
@Transactional
@@ -38,4 +51,55 @@
return count(new QueryWrapper<HelpComment>()
.eq("post_id", postId)); // 只按post_id统计
}
+
+ @Override
+ public void commentOnHelpPost(HelpComment comment) {
+ // 1. 保存评论
+ helpCommentMapper.insert(comment);
+
+ // 2. 通知帖子作者
+ HelpPost post = helpPostMapper.selectById(comment.getPostId());
+ if (post != null
+ && !post.getAuthorId().equals(comment.getAuthorId())) {
+ Notification n = new Notification();
+ n.setUserId(post.getAuthorId());
+ n.setType("POST_REPLY");
+ n.setTitle("您的帖子有了新回复");
+ n.setContent("用户 "
+ + comment.getAuthorId()
+ + " 评论了您的帖子: \""
+ + comment.getContent()
+ + "\"");
+ n.setTargetId(post.getId());
+ n.setTargetType("HelpPost");
+ notificationService.saveNotification(n);
+ }
+ }
+
+ /**
+ * 用户回复已有评论
+ */
+ @Override
+ public void replyComment(HelpComment reply) {
+ // 1. 保存子评论
+ helpCommentMapper.insert(reply);
+
+ // 2. 通知父评论作者
+ HelpComment parent = helpCommentMapper.selectById(reply.getParentId());
+ if (parent != null
+ && !parent.getAuthorId().equals(reply.getAuthorId())) {
+ Notification n = new Notification();
+ n.setUserId(parent.getAuthorId());
+ n.setType("COMMENT_REPLY");
+ n.setTitle("您的评论有了新回复");
+ n.setContent("用户 "
+ + reply.getAuthorId()
+ + " 回复了您的评论: \""
+ + reply.getContent()
+ + "\"");
+ n.setTargetId(parent.getId());
+ n.setTargetType("HelpComment");
+ notificationService.saveNotification(n);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/impl/HelpPostServiceImpl.java b/src/main/java/com/ptp/ptplatform/service/impl/HelpPostServiceImpl.java
index 5e8d5d0..748eec5 100644
--- a/src/main/java/com/ptp/ptplatform/service/impl/HelpPostServiceImpl.java
+++ b/src/main/java/com/ptp/ptplatform/service/impl/HelpPostServiceImpl.java
@@ -1,19 +1,68 @@
package com.ptp.ptplatform.service.impl;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ptp.ptplatform.entity.HelpComment;
import com.ptp.ptplatform.entity.HelpPost;
+import com.ptp.ptplatform.entity.Notification;
+import com.ptp.ptplatform.mapper.HelpCommentMapper;
import com.ptp.ptplatform.mapper.HelpPostMapper;
import com.ptp.ptplatform.service.HelpPostService;
+import com.ptp.ptplatform.service.NotificationService;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import java.time.LocalDateTime;
+
@Service
public class HelpPostServiceImpl extends ServiceImpl<HelpPostMapper, HelpPost> implements HelpPostService {
+ @Autowired
+ private HelpPostMapper helpPostMapper;
+ @Autowired
+ private HelpCommentMapper helpCommentMapper;
+ @Autowired
+ private NotificationService notificationService;
@Override
@Transactional
- public void incrementLike(int postId) {
- this.update(null,
- new com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper<HelpPost>()
+ public void likePost(Integer postId, String likerId) {
+ // 1. 查询帖子本身,获取 authorId 和 title
+ HelpPost post = helpPostMapper.selectById(postId);
+ if (post == null) {
+ throw new RuntimeException("帖子不存在,ID=" + postId);
+ }
+
+ // 2. 更新 like_count 字段
+ helpPostMapper.update(
+ null,
+ new UpdateWrapper<HelpPost>()
+ .eq("id", postId)
+ .setSql("like_count = like_count + 1")
+ );
+
+ // 3. 给帖子作者发通知(排除自己给自己点赞的情况)
+ String authorId = post.getAuthorId();
+ if (!authorId.equals(likerId)) {
+ Notification n = new Notification();
+ n.setUserId(authorId); // 通知接收方 = 帖子作者
+ n.setType("POST_LIKE"); // 通知类型,可前端约定
+ n.setTitle("您的帖子被点赞"); // 通知标题
+ n.setContent("用户 "
+ + likerId
+ + " 点赞了您的帖子: \""
+ + post.getTitle()
+ + "\"");
+ n.setTargetId(postId); // 通知跳转时可指向该帖子
+ n.setTargetType("HelpPost"); // 前端可根据此值决定跳转逻辑
+ notificationService.saveNotification(n);
+ }
+ }
+ @Override
+ @Transactional
+ public void incrementLike(Integer postId) {
+ this.update(
+ null,
+ new UpdateWrapper<HelpPost>()
.eq("id", postId)
.setSql("like_count = like_count + 1")
);
@@ -21,11 +70,53 @@
@Override
@Transactional
- public void incrementReplyCount(int postId) {
- this.update(null,
- new com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper<HelpPost>()
+ public void incrementReplyCount(Integer postId) {
+ this.update(
+ null,
+ new UpdateWrapper<HelpPost>()
.eq("id", postId)
.setSql("reply_count = reply_count + 1")
);
}
+
+ @Override
+ @Transactional
+ public void commentOnHelpPost(HelpComment comment) {
+ // 1. 将新的 HelpComment 插入数据库
+ // 如果 parentId != 0,则说明这是一条对子评论的回复;如果 parentId = 0,则这就是对帖子的一级评论。
+ helpCommentMapper.insert(comment);
+
+ // 2. 帖子回复数 +1(reply_count 字段)
+ Integer postId = comment.getPostId();
+ helpPostMapper.update(
+ null,
+ new UpdateWrapper<HelpPost>()
+ .eq("id", postId)
+ .setSql("reply_count = reply_count + 1")
+ );
+
+ // 3. 给帖子作者发一条通知(如果评论人不是作者自己)
+ HelpPost post = helpPostMapper.selectById(postId);
+ if (post != null) {
+ String authorId = post.getAuthorId(); // 帖子作者的 ID
+ String commenterId = comment.getAuthorId(); // 当前发表评论的用户 ID
+ if (!authorId.equals(commenterId)) {
+ Notification n = new Notification();
+ n.setUserId(authorId); // 通知接收人 = 帖子作者
+ n.setType("POST_REPLY"); // 通知类型,可与前端约定
+ n.setTitle("您的帖子有了新回复"); // 通知标题
+ // 通知内容示例: 用户 <commenterId> 评论了您的帖子: "评论内容"
+ n.setContent("用户 "
+ + commenterId
+ + " 评论了您的帖子: \""
+ + comment.getContent()
+ + "\"");
+ n.setTargetId(postId); // targetId 可指向该帖子
+ n.setTargetType("HelpPost"); // targetType = "HelpPost"
+ n.setIsRead(false);
+ n.setCreateTime(LocalDateTime.now());
+ notificationService.saveNotification(n);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/impl/NotificationServiceImpl.java b/src/main/java/com/ptp/ptplatform/service/impl/NotificationServiceImpl.java
new file mode 100644
index 0000000..faeae12
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/service/impl/NotificationServiceImpl.java
@@ -0,0 +1,41 @@
+package com.ptp.ptplatform.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.ptp.ptplatform.entity.Notification;
+import com.ptp.ptplatform.mapper.NotificationMapper;
+import com.ptp.ptplatform.service.NotificationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Service
+public class NotificationServiceImpl implements NotificationService {
+ @Autowired
+ private NotificationMapper notificationMapper;
+
+ @Override
+ public void saveNotification(Notification n) {
+ n.setCreateTime(LocalDateTime.now());
+ n.setIsRead(false);
+ notificationMapper.insert(n);
+ }
+
+ @Override
+ public List<Notification> listByUser(String userId) {
+ return notificationMapper.selectList(
+ new QueryWrapper<Notification>()
+ .eq("user_id", userId)
+ .orderByDesc("create_time")
+ );
+ }
+
+ @Override
+ public void markAsRead(Integer id) {
+ Notification n = new Notification();
+ n.setId(id);
+ n.setIsRead(true);
+ notificationMapper.updateById(n);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/impl/TorrentCommentServiceImpl.java b/src/main/java/com/ptp/ptplatform/service/impl/TorrentCommentServiceImpl.java
index 540df71..95b2a7d 100644
--- a/src/main/java/com/ptp/ptplatform/service/impl/TorrentCommentServiceImpl.java
+++ b/src/main/java/com/ptp/ptplatform/service/impl/TorrentCommentServiceImpl.java
@@ -1,17 +1,33 @@
package com.ptp.ptplatform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ptp.ptplatform.entity.Notification;
+import com.ptp.ptplatform.entity.TORRENT;
import com.ptp.ptplatform.entity.TorrentComment;
import com.ptp.ptplatform.mapper.TorrentCommentMapper;
+import com.ptp.ptplatform.mapper.TorrentMapper;
+import com.ptp.ptplatform.service.NotificationService;
import com.ptp.ptplatform.service.TorrentCommentService;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import java.time.LocalDateTime;
import java.util.List;
@Service
public class TorrentCommentServiceImpl extends ServiceImpl<TorrentCommentMapper, TorrentComment> implements TorrentCommentService {
+ @Autowired
+ private TorrentCommentMapper torrentCommentMapper;
+
+ @Autowired
+ private TorrentMapper torrentMapper;
+
+ @Autowired
+ private NotificationService notificationService;
+
@Override
@Transactional
public void incrementLike(int commentId) {
@@ -31,4 +47,76 @@
.orderByAsc("create_time")
);
}
+
+ @Override
+ @Transactional
+ public void commentOnTorrentPost(TorrentComment comment) {
+ // ————————————————
+ // 1. 插入新评论到 torrent_comments 表
+ // 插入后,MyBatis-Plus 会自动将自增主键 id 回写到 comment.getId()
+ // ————————————————
+ comment.setCreateTime(LocalDateTime.now());
+ comment.setLikeCount(0);
+ torrentCommentMapper.insert(comment);
+
+ // ————————————————
+ // 2. 更新对应 TORRENT 帖子的 reply_count 字段 +1
+ // ————————————————
+ Integer postId = comment.getPostId();
+ torrentMapper.update(
+ null,
+ new UpdateWrapper<TORRENT>()
+ .eq("id", postId)
+ .setSql("reply_count = reply_count + 1")
+ );
+
+ // ————————————————
+ // 3. 给该帖子的作者发通知(如果评论人 != 作者)
+ // ————————————————
+ TORRENT post = torrentMapper.selectById(postId);
+ if (post != null) {
+ String postAuthorId = post.getUsername();
+ String commenterId = comment.getAuthorId();
+
+ if (!postAuthorId.equals(commenterId)) {
+ Notification n = new Notification();
+ n.setUserId(postAuthorId); // 通知接收人 = 帖子作者
+ n.setType("TORRENT_POST_REPLY"); // 通知类型,可自定义
+ n.setTitle("您的 Torrent 帖子有新回复"); // 通知标题
+ n.setContent(
+ "用户 " + commenterId + " 评论了您的 Torrent 帖子: \"" +
+ comment.getContent() + "\""
+ );
+ n.setTargetId(postId); // 目标 ID = 帖子 ID
+ n.setTargetType("TORRENT"); // 目标类型 = TORRENT
+ n.setIsRead(false); // 默认未读
+ n.setCreateTime(LocalDateTime.now());
+ notificationService.saveNotification(n);
+ }
+
+ if (comment.getParentId() != null && comment.getParentId() > 0) {
+ Integer parentCommentId = comment.getParentId();
+ TorrentComment parentComment = torrentCommentMapper.selectById(parentCommentId);
+ if (parentComment != null) {
+ String parentAuthorId = parentComment.getAuthorId();
+ // 排除顶层评论 + 排除作者自己给自己回复
+ if (!parentAuthorId.equals(commenterId)) {
+ Notification n2 = new Notification();
+ n2.setUserId(parentAuthorId);
+ n2.setType("TORRENT_COMMENT_REPLY");
+ n2.setTitle("您的评论有新回复");
+ n2.setContent(
+ "用户 " + commenterId + " 回复了您的评论: \"" +
+ comment.getContent() + "\""
+ );
+ n2.setTargetId(parentCommentId);
+ n2.setTargetType("TorrentComment");
+ n2.setIsRead(false);
+ n2.setCreateTime(LocalDateTime.now());
+ notificationService.saveNotification(n2);
+ }
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/test/java/com/ptp/ptplatform/controller/HelpPostControllerTest.java b/src/test/java/com/ptp/ptplatform/controller/HelpPostControllerTest.java
index 2176e5b..07d89d3 100644
--- a/src/test/java/com/ptp/ptplatform/controller/HelpPostControllerTest.java
+++ b/src/test/java/com/ptp/ptplatform/controller/HelpPostControllerTest.java
@@ -4,8 +4,11 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ptp.ptplatform.entity.HelpComment;
import com.ptp.ptplatform.entity.HelpPost;
+import com.ptp.ptplatform.entity.Notification;
+import com.ptp.ptplatform.mapper.HelpPostMapper;
import com.ptp.ptplatform.service.HelpCommentService;
import com.ptp.ptplatform.service.HelpPostService;
+import com.ptp.ptplatform.service.NotificationService;
import com.ptp.ptplatform.utils.Result;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -33,6 +36,9 @@
@Mock
private HelpCommentService commentService;
+ @Mock
+ private NotificationService notificationService;
+
@InjectMocks
private HelpPostController postController;
@@ -142,12 +148,15 @@
@Test
void likePost_ShouldReturnSuccess() {
int postId = 1;
- doNothing().when(postService).incrementLike(postId);
+ String likerId = "alice123";
+ // 对应 ServiceImpl 中的 likePost(postId, likerId)
+ doNothing().when(postService).likePost(postId, likerId);
- Result result = postController.likePost(postId);
+ Result result = postController.likePost(postId, likerId);
assertEquals(200, result.getCode());
- verify(postService, times(1)).incrementLike(postId);
+ assertEquals("点赞成功", result.getMessage());
+ verify(postService, times(1)).likePost(postId, likerId);
}
@Test
@@ -173,7 +182,7 @@
String author = "user2";
String content= "Hello";
- // 这是我们希望最后返回的那个对象
+ // 构造“保存后”返回给前端的 HelpComment 对象
HelpComment saved = new HelpComment();
saved.setId(99);
saved.setPostId(postId);
@@ -181,46 +190,49 @@
saved.setContent(content);
saved.setImageUrl(null);
- // 1) stub save(...):拦截到 comment,将它的 id 设为 99
+ // 1) 当 commentService.commentOnHelpPost(...) 被调用时,
+ // 我们在 doAnswer 中给它传入的 HelpComment 对象设置 ID = 99
doAnswer(invocation -> {
HelpComment arg = invocation.getArgument(0);
arg.setId(saved.getId());
- return true;
- }).when(commentService).save(any(HelpComment.class));
+ return null; // commentOnHelpPost 返回 void
+ }).when(commentService).commentOnHelpPost(any(HelpComment.class));
- // 2) stub getById(99) -> saved
+ // 2) 后面 commentService.getById(99) 返回我们上面构造的 saved
when(commentService.getById(saved.getId())).thenReturn(saved);
- // 回复数的模拟
- doNothing().when(postService).incrementReplyCount(postId);
+ // 模拟:commentOnHelpPost() 内部会调用 postService.incrementReplyCount(postId)
+ // 但控制器本身并不直接调用 incrementReplyCount,所以测试里不再 verify 这一行。
+ // 只要保证 postService.getById(postId) 能返回一个带有 replyCount 的 HelpPost 即可:
HelpPost stubPost = new HelpPost();
stubPost.setReplyCount(5);
when(postService.getById(postId)).thenReturn(stubPost);
- // 调用接口
- Result result = postController.comment(postId, author, content, null);
+ // 调用控制器的 commentOnPost(...) 方法,第四个参数传 null(不带图片)
+ Result result = postController.commentOnPost(postId, author, content, null);
- // 断言
assertEquals(200, result.getCode());
assertEquals(saved, result.getData().get("comment"));
- assertEquals(5L, result.getData().get("newReplyCount"));
+ assertEquals(5, result.getData().get("newReplyCount"));
- verify(commentService, times(1)).save(any(HelpComment.class));
- verify(postService, times(1)).incrementReplyCount(postId);
+ // 确保 commentService.commentOnHelpPost(...) 被调用一次
+ verify(commentService, times(1)).commentOnHelpPost(any(HelpComment.class));
+ // postService.incrementReplyCount(...) 由 commentOnHelpPost 内部调用,因此测试里无需 verify
}
+
@Test
void addCommentWithImage_ShouldReturnSuccess() throws Exception {
int postId = 8;
String author = "user3";
String content= "With Image";
- // 准备一个小文件
+ // 准备一个 “模拟上传文件”
MockMultipartFile mockFile = new MockMultipartFile(
"image", "test.jpg", "image/jpeg", new byte[]{1,2,3}
);
- // 我们要“拿回”给前端的对象
+ // 构造“保存后”返回给前端的 HelpComment 对象
HelpComment saved = new HelpComment();
saved.setId(100);
saved.setPostId(postId);
@@ -228,39 +240,36 @@
saved.setContent(content);
saved.setImageUrl("/uploads/whatever.jpg");
- // 1) save(...) 时给 comment 赋 id=100
+ // 当 commentService.commentOnHelpPost(...) 被调用时,给传入的对象设置 ID 与 imageUrl
doAnswer(invocation -> {
HelpComment arg = invocation.getArgument(0);
arg.setId(saved.getId());
- // 同时把 imageUrl 塞一下,模拟上传后持久化时已经写入
arg.setImageUrl(saved.getImageUrl());
- return true;
- }).when(commentService).save(any(HelpComment.class));
+ return null; // commentOnHelpPost 返回 void
+ }).when(commentService).commentOnHelpPost(any(HelpComment.class));
- // 2) getById(100) -> saved
+ // 后面 commentService.getById(100) 返回我们构造的 saved
when(commentService.getById(saved.getId())).thenReturn(saved);
- // 回复数
- doNothing().when(postService).incrementReplyCount(postId);
+ // 同样,postService.getById(postId) 返回一个带 replyCount=10 的 HelpPost
HelpPost stubPost = new HelpPost();
stubPost.setReplyCount(10);
when(postService.getById(postId)).thenReturn(stubPost);
- // 调用
- Result result = postController.comment(postId, author, content, mockFile);
+ // 调用 commentOnPost,第四个参数传 mockFile(模拟有图片上传)
+ Result result = postController.commentOnPost(postId, author, content, mockFile);
assertEquals(200, result.getCode());
assertEquals(saved, result.getData().get("comment"));
- assertEquals(10L, result.getData().get("newReplyCount"));
+ assertEquals(10, result.getData().get("newReplyCount"));
- // 并且真正保存的对象也带上了 imageUrl
+ // 验证真正传给 commentService.commentOnHelpPost(...) 的 HelpComment 对象中,
+ // imageUrl 已经被设置成 "/uploads/whatever.jpg"
ArgumentCaptor<HelpComment> captor = ArgumentCaptor.forClass(HelpComment.class);
- verify(commentService).save(captor.capture());
+ verify(commentService).commentOnHelpPost(captor.capture());
HelpComment toSave = captor.getValue();
assertEquals(saved.getImageUrl(), toSave.getImageUrl());
- verify(postService, times(1)).incrementReplyCount(postId);
+ // 同样无需 verify postService.incrementReplyCount(...),因为该调用在 commentOnHelpPost 内部完成
}
-
-
}
diff --git a/uploads/15b11ff9-2344-4d3f-b4bc-49734a1e7156.png b/uploads/15b11ff9-2344-4d3f-b4bc-49734a1e7156.png
new file mode 100644
index 0000000..71bd63e
--- /dev/null
+++ b/uploads/15b11ff9-2344-4d3f-b4bc-49734a1e7156.png
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/uploads/47983664-980d-4831-979d-22eee853c22b.jpg b/uploads/47983664-980d-4831-979d-22eee853c22b.jpg
new file mode 100644
index 0000000..a6d7f38
--- /dev/null
+++ b/uploads/47983664-980d-4831-979d-22eee853c22b.jpg
@@ -0,0 +1 @@
+test image
\ No newline at end of file
diff --git a/uploads/49d1a2db-5482-4d21-9a22-0071bd4cf5e5.jpg b/uploads/49d1a2db-5482-4d21-9a22-0071bd4cf5e5.jpg
new file mode 100644
index 0000000..a6d7f38
--- /dev/null
+++ b/uploads/49d1a2db-5482-4d21-9a22-0071bd4cf5e5.jpg
@@ -0,0 +1 @@
+test image
\ No newline at end of file
diff --git a/uploads/4ed56b76-c367-45db-98e1-d0f36508d8f1.jpg b/uploads/4ed56b76-c367-45db-98e1-d0f36508d8f1.jpg
new file mode 100644
index 0000000..aed2973
--- /dev/null
+++ b/uploads/4ed56b76-c367-45db-98e1-d0f36508d8f1.jpg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/uploads/f2e35f6d-2d7b-456a-8bf4-27526e20c5b5.jpg b/uploads/f2e35f6d-2d7b-456a-8bf4-27526e20c5b5.jpg
new file mode 100644
index 0000000..aed2973
--- /dev/null
+++ b/uploads/f2e35f6d-2d7b-456a-8bf4-27526e20c5b5.jpg
@@ -0,0 +1 @@
+
\ No newline at end of file