Bladeren bron

feature:add kafka mail send module

ggbocoder 2 jaren geleden
bovenliggende
commit
9668f3b718
27 gewijzigde bestanden met toevoegingen van 347 en 78 verwijderingen
  1. 16 0
      pom.xml
  2. 32 6
      src/main/java/cn/seecoder/fdroidrepository/Controller/CommentController.java
  3. 16 1
      src/main/java/cn/seecoder/fdroidrepository/Controller/FollowController.java
  4. 6 7
      src/main/java/cn/seecoder/fdroidrepository/Controller/ForumPostController.java
  5. 35 3
      src/main/java/cn/seecoder/fdroidrepository/Controller/LikeController.java
  6. 5 7
      src/main/java/cn/seecoder/fdroidrepository/Controller/UserController.java
  7. 47 0
      src/main/java/cn/seecoder/fdroidrepository/DataObject/Event.java
  8. 1 6
      src/main/java/cn/seecoder/fdroidrepository/Mapper/CommentMapper.java
  9. 0 4
      src/main/java/cn/seecoder/fdroidrepository/Service/ApkService.java
  10. 2 2
      src/main/java/cn/seecoder/fdroidrepository/Service/ICommentService.java
  11. 1 1
      src/main/java/cn/seecoder/fdroidrepository/Service/IForumPostService.java
  12. 1 1
      src/main/java/cn/seecoder/fdroidrepository/Service/ILikeService.java
  13. 1 2
      src/main/java/cn/seecoder/fdroidrepository/Service/IUserService.java
  14. 4 0
      src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/ApkService.java
  15. 4 4
      src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/CommentService.java
  16. 1 1
      src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/FollowService.java
  17. 2 3
      src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/ForumPostService.java
  18. 43 0
      src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/MailService.java
  19. 2 2
      src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/RedisILikeService.java
  20. 5 23
      src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/UserService.java
  21. 7 2
      src/main/java/cn/seecoder/fdroidrepository/aspect/ServiceLogAspect.java
  22. 9 0
      src/main/java/cn/seecoder/fdroidrepository/common/constants.java
  23. 2 3
      src/main/java/cn/seecoder/fdroidrepository/config/WebSecurityConfig.java
  24. 51 0
      src/main/java/cn/seecoder/fdroidrepository/event/EventConsumer.java
  25. 19 0
      src/main/java/cn/seecoder/fdroidrepository/event/EventProducer.java
  26. 5 0
      src/main/resources/application.properties
  27. 30 0
      src/main/resources/templates/mail/follow.html

+ 16 - 0
pom.xml

@@ -89,6 +89,12 @@
             <scope>runtime</scope>
         </dependency>
 
+        <dependency>
+            <groupId>org.springframework.kafka</groupId>
+            <artifactId>spring-kafka</artifactId>
+        </dependency>
+
+
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-thymeleaf</artifactId>
@@ -107,6 +113,16 @@
             <artifactId>aspectjweaver</artifactId>
             <version>1.9.19</version>
         </dependency>
+        <dependency>
+            <groupId>com.alibaba.fastjson2</groupId>
+            <artifactId>fastjson2</artifactId>
+            <version>2.0.35</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.83</version>
+        </dependency>
 
 
     </dependencies>

+ 32 - 6
src/main/java/cn/seecoder/fdroidrepository/Controller/CommentController.java

@@ -1,7 +1,12 @@
 package cn.seecoder.fdroidrepository.Controller;
 
 import cn.seecoder.fdroidrepository.DataObject.Comment;
-import cn.seecoder.fdroidrepository.Service.CommentService;
+import cn.seecoder.fdroidrepository.DataObject.Event;
+import cn.seecoder.fdroidrepository.DataObject.ForumPost;
+import cn.seecoder.fdroidrepository.Service.ICommentService;
+import cn.seecoder.fdroidrepository.Service.IForumPostService;
+import cn.seecoder.fdroidrepository.event.EventProducer;
+import cn.seecoder.fdroidrepository.result.SingleResult;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
@@ -10,24 +15,45 @@ import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
 
+import static cn.seecoder.fdroidrepository.common.constants.TOPIC_COMMENT;
+
 @PreAuthorize("hasRole('ROLE_ADMIN')")
 @RestController
 @RequestMapping("/api/comments")
 public class CommentController {
+
+    @Autowired
+    private ICommentService ICommentService;
+
     @Autowired
-    private CommentService commentService;
+    private IForumPostService IForumPostService;
 
+    @Autowired
+    private EventProducer eventProducer;
     @GetMapping("/post/{postId}")
     public ResponseEntity<List<Comment>> getCommentsByPostId(@PathVariable Long postId) {
-        List<Comment> comments = commentService.getCommentsByPostId(postId);
+        List<Comment> comments = ICommentService.getCommentsByPostId(postId);
         return new ResponseEntity<>(comments, HttpStatus.OK);
     }
 
     @PostMapping("/post/{postId}")
-    public ResponseEntity<String> saveComment(@PathVariable Long postId, @RequestBody Comment comment) {
+    public SingleResult<?> saveComment(@PathVariable Long postId, @RequestBody Comment comment) {
         comment.setPostId(postId);
-        commentService.saveComment(comment);
-        return new ResponseEntity<>("Comment saved successfully", HttpStatus.OK);
+        int rows = ICommentService.saveComment(comment);
+        if(rows==1){
+            Event event = new Event()
+                .setTopic(TOPIC_COMMENT)
+                .setUserId(comment.getUserId())
+                .setEntityId(comment.getCommentId())
+                .setData("postId",postId);
+            ForumPost forumPost = IForumPostService.getPostById(postId);
+            event.setEntityUserId(forumPost.getUserId());
+            eventProducer.fireEvent(event);
+            return SingleResult.success("comment successfully!");
+        }else {
+            return SingleResult.failure("400","comment failed!");
+        }
+
     }
 }
 

+ 16 - 1
src/main/java/cn/seecoder/fdroidrepository/Controller/FollowController.java

@@ -1,7 +1,9 @@
 package cn.seecoder.fdroidrepository.Controller;
 
 
+import cn.seecoder.fdroidrepository.DataObject.Event;
 import cn.seecoder.fdroidrepository.Service.ServiceImpl.FollowService;
+import cn.seecoder.fdroidrepository.event.EventProducer;
 import cn.seecoder.fdroidrepository.result.SingleResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -10,17 +12,30 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import static cn.seecoder.fdroidrepository.common.constants.TOPIC_FOLLOW;
+
 @RestController
 @RequestMapping("/api/fans")
 public class FollowController {
     private Logger logger = LoggerFactory.getLogger(FollowController.class);
+
+
+    @Autowired
+    private EventProducer eventProducer;
+
     @Autowired
-    FollowService followService;
+    private FollowService followService;
 
     @PostMapping("/follow")
     public SingleResult<?> follow(int followerId,int followeeId){
         try {
             followService.follow(followerId,followeeId);
+            Event event = new Event()
+                .setTopic(TOPIC_FOLLOW)
+                .setUserId(followeeId)
+                .setEntityId(followerId)
+                .setEntityUserId(followerId);
+            eventProducer.fireEvent(event);
             return SingleResult.success("ok!");
         } catch (InterruptedException e) {
             logger.error("关注失败:" + e.getMessage());

+ 6 - 7
src/main/java/cn/seecoder/fdroidrepository/Controller/ForumPostController.java

@@ -1,11 +1,10 @@
 package cn.seecoder.fdroidrepository.Controller;
 
 import cn.seecoder.fdroidrepository.DataObject.ForumPost;
-import cn.seecoder.fdroidrepository.Service.ForumPostService;
+import cn.seecoder.fdroidrepository.Service.IForumPostService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.web.bind.annotation.*;
@@ -18,23 +17,23 @@ import java.util.List;
 @RequestMapping("/api/forum")
 public class ForumPostController {
     @Autowired
-    private ForumPostService forumPostService;
+    private IForumPostService IForumPostService;
 
     @GetMapping("/posts")
     public ResponseEntity<List<ForumPost>> getAllPosts() {
-        List<ForumPost> posts = forumPostService.getAllPosts();
+        List<ForumPost> posts = IForumPostService.getAllPosts();
         return new ResponseEntity<>(posts, HttpStatus.OK);
     }
 
     @GetMapping("/posts/page/{page}")
     public ResponseEntity<List<ForumPost>> getPostsWithPagination(@PathVariable int page) {
-        List<ForumPost> posts = forumPostService.getPostsWithPagination(page);
+        List<ForumPost> posts = IForumPostService.getPostsWithPagination(page);
         return new ResponseEntity<>(posts, HttpStatus.OK);
     }
 
     @GetMapping("/posts/{postId}")
     public ResponseEntity<ForumPost> getPostById(@PathVariable Long postId) {
-        ForumPost post = forumPostService.getPostById(postId);
+        ForumPost post = IForumPostService.getPostById(postId);
         return new ResponseEntity<>(post, HttpStatus.OK);
     }
 
@@ -45,7 +44,7 @@ public class ForumPostController {
             post.setUsername(authentication.getName());
         }
         post.setCreatedAt(new Date());
-        forumPostService.savePost(post);
+        IForumPostService.savePost(post);
         return new ResponseEntity<>("Post saved successfully", HttpStatus.OK);
     }
 }

+ 35 - 3
src/main/java/cn/seecoder/fdroidrepository/Controller/LikeController.java

@@ -1,16 +1,33 @@
 package cn.seecoder.fdroidrepository.Controller;
 
 
-import cn.seecoder.fdroidrepository.Service.ServiceImpl.RedisLikeService;
+import cn.seecoder.fdroidrepository.DataObject.Event;
+import cn.seecoder.fdroidrepository.DataObject.ForumPost;
+import cn.seecoder.fdroidrepository.Service.ServiceImpl.ForumPostService;
+import cn.seecoder.fdroidrepository.Service.ServiceImpl.RedisILikeService;
+import cn.seecoder.fdroidrepository.event.EventProducer;
+import cn.seecoder.fdroidrepository.result.SingleResult;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.HashMap;
+import java.util.Map;
+
+import static cn.seecoder.fdroidrepository.common.constants.TOPIC_LIKE;
+
 @RestController
 @RequestMapping("/api/like")
 public class LikeController {
 
+
+    @Autowired
+    private RedisILikeService likeService;
+
+    @Autowired
+    private EventProducer eventProducer;
+
     @Autowired
-    private RedisLikeService likeService;
+    private ForumPostService forumPostService;
 
     /**
      *
@@ -18,8 +35,23 @@ public class LikeController {
      * @param userId userId
      */
     @GetMapping("/")
-    public void like(@RequestParam String postId,@RequestParam String userId){
+    public SingleResult<Map> like(@RequestParam String postId, @RequestParam String userId){
         likeService.operateLike2Redis(userId,postId);
+        boolean isLike= likeService.userPostStatus(postId, userId);
+        if(isLike){
+            ForumPost forumPost=forumPostService.getPostById(Long.valueOf(postId));
+            Event event = new Event()
+                .setTopic(TOPIC_LIKE)
+                .setUserId(Long.parseLong(userId))
+                .setEntityId(Long.parseLong(postId))
+                .setUserId(forumPost.getUserId())
+                .setData("postId",postId);
+            eventProducer.fireEvent(event);
+        }
+        Map<String,Object> map = new HashMap();
+        map.put("likeCount",likeService.getPostLikeSum(postId));
+        map.put("likeStatus",isLike);
+        return SingleResult.success(map);
     }
 
 }

+ 5 - 7
src/main/java/cn/seecoder/fdroidrepository/Controller/UserController.java

@@ -4,15 +4,13 @@ package cn.seecoder.fdroidrepository.Controller;
 
 import cn.seecoder.fdroidrepository.DataObject.User;
 import cn.seecoder.fdroidrepository.Service.ServiceImpl.USER_STATUS;
-import cn.seecoder.fdroidrepository.Service.UserService;
+import cn.seecoder.fdroidrepository.Service.IUserService;
 import cn.seecoder.fdroidrepository.config.WebSecurityConfig;
 import cn.seecoder.fdroidrepository.result.Code;
 import cn.seecoder.fdroidrepository.result.SingleResult;
 import cn.seecoder.fdroidrepository.utils.JwtUtil;
 import cn.seecoder.fdroidrepository.result.Result;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -27,7 +25,7 @@ import javax.servlet.http.HttpServletResponse;
 @RequestMapping("/api/users")
 public class UserController {
     @Autowired
-    private UserService userService;
+    private IUserService IUserService;
 
     @Autowired
     private JwtUtil jwtTokenUtils;
@@ -37,16 +35,16 @@ public class UserController {
 
     @PostMapping("/register")
     public SingleResult<?> register(@RequestBody User user) {
-        if (!userService.isUsernameUnique(user.getUsername())) {
+        if (!IUserService.isUsernameUnique(user.getUsername())) {
             return SingleResult.failure("400","username has been taken!");
         }
-        userService.save(user);
+        IUserService.save(user);
         return SingleResult.success("register successfully!");
     }
 
     @GetMapping("/activation/{userId}/{code}")
     private Result<?> activate(@PathVariable int userId,@PathVariable String code){
-        USER_STATUS result=userService.activate(userId,code);
+        USER_STATUS result= IUserService.activate(userId,code);
         if(result==USER_STATUS.START_ACTIVATE){
             return new Result<>("200","activate success");
         }else if(result==USER_STATUS.REPEAT_ACTIVATE){

+ 47 - 0
src/main/java/cn/seecoder/fdroidrepository/DataObject/Event.java

@@ -0,0 +1,47 @@
+package cn.seecoder.fdroidrepository.DataObject;
+
+
+import lombok.Data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Data
+public class Event {
+    private String topic;
+    private long userId;
+    private int entityType;
+    private long entityId;
+    private long entityUserId;
+    private Map<String, Object> data =new HashMap<>();
+
+    public Event setTopic(String topic) {
+        this.topic = topic;
+        return this;
+    }
+
+    public Event setUserId(long userId) {
+        this.userId = userId;
+        return this;
+    }
+
+    public Event setEntityType(int entityType) {
+        this.entityType = entityType;
+        return this;
+    }
+
+    public Event setEntityId(long entityId) {
+        this.entityId = entityId;
+        return this;
+    }
+
+    public Event setEntityUserId(long entityUserId) {
+        this.entityUserId = entityUserId;
+        return this;
+    }
+
+    public Event setData(String key, Object value) {
+        this.data.put(key, value);
+        return this;
+    }
+}

+ 1 - 6
src/main/java/cn/seecoder/fdroidrepository/Mapper/CommentMapper.java

@@ -1,12 +1,7 @@
 package cn.seecoder.fdroidrepository.Mapper;
 
 import cn.seecoder.fdroidrepository.DataObject.Comment;
-import cn.seecoder.fdroidrepository.Service.CommentService;
 import org.apache.ibatis.annotations.*;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
 
@@ -26,7 +21,7 @@ public interface CommentMapper {
 
     @Insert("INSERT INTO comments (post_id, user_id, content, created_at) " +
         "VALUES (#{postId}, #{userId}, #{content}, #{createdAt})")
-    void save(Comment comment);
+    int save(Comment comment);
 }
 
 

+ 0 - 4
src/main/java/cn/seecoder/fdroidrepository/Service/ApkService.java

@@ -1,4 +0,0 @@
-package cn.seecoder.fdroidrepository.Service;
-
-public class ApkService {
-}

+ 2 - 2
src/main/java/cn/seecoder/fdroidrepository/Service/CommentService.java → src/main/java/cn/seecoder/fdroidrepository/Service/ICommentService.java

@@ -5,9 +5,9 @@ import cn.seecoder.fdroidrepository.DataObject.Comment;
 import java.util.List;
 
 // CommentService.java
-public interface CommentService {
+public interface ICommentService {
     List<Comment> getCommentsByPostId(Long postId);
 
-    void saveComment(Comment comment);
+    int saveComment(Comment comment);
 }
 

+ 1 - 1
src/main/java/cn/seecoder/fdroidrepository/Service/ForumPostService.java → src/main/java/cn/seecoder/fdroidrepository/Service/IForumPostService.java

@@ -5,7 +5,7 @@ import cn.seecoder.fdroidrepository.DataObject.ForumPost;
 import java.util.List;
 
 // ForumPostService.java
-public interface ForumPostService {
+public interface IForumPostService {
     List<ForumPost> getAllPosts();
 
     List<ForumPost> getPostsWithPagination(int page);

+ 1 - 1
src/main/java/cn/seecoder/fdroidrepository/Service/LikeService.java → src/main/java/cn/seecoder/fdroidrepository/Service/ILikeService.java

@@ -1,6 +1,6 @@
 package cn.seecoder.fdroidrepository.Service;
 
-public interface LikeService {
+public interface ILikeService {
     /**
      * 如果该用户没有给评论点过赞,则点赞;反之取消点赞
      * @param userId userId

+ 1 - 2
src/main/java/cn/seecoder/fdroidrepository/Service/UserService.java → src/main/java/cn/seecoder/fdroidrepository/Service/IUserService.java

@@ -2,10 +2,9 @@ package cn.seecoder.fdroidrepository.Service;
 
 import cn.seecoder.fdroidrepository.DataObject.User;
 import cn.seecoder.fdroidrepository.Service.ServiceImpl.USER_STATUS;
-import org.springframework.stereotype.Service;
 
 
-public interface UserService {
+public interface IUserService {
     User findByUsername(String username);
 
     void save(User user);

+ 4 - 0
src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/ApkService.java

@@ -0,0 +1,4 @@
+package cn.seecoder.fdroidrepository.Service.ServiceImpl;
+
+public class ApkService {
+}

+ 4 - 4
src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/CommentServiceImpl.java → src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/CommentService.java

@@ -2,7 +2,7 @@ package cn.seecoder.fdroidrepository.Service.ServiceImpl;
 
 import cn.seecoder.fdroidrepository.DataObject.Comment;
 import cn.seecoder.fdroidrepository.Mapper.CommentMapper;
-import cn.seecoder.fdroidrepository.Service.CommentService;
+import cn.seecoder.fdroidrepository.Service.ICommentService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -11,7 +11,7 @@ import java.util.List;
 
 // CommentServiceImpl.java
 @Service
-public class CommentServiceImpl implements CommentService {
+public class CommentService implements ICommentService {
     @Autowired
     private CommentMapper commentMapper;
 
@@ -21,8 +21,8 @@ public class CommentServiceImpl implements CommentService {
     }
 
     @Override
-    public void saveComment(Comment comment) {
+    public int saveComment(Comment comment) {
         comment.setCreatedAt(LocalDateTime.now());
-        commentMapper.save(comment);
+        return commentMapper.save(comment);
     }
 }

+ 1 - 1
src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/FollowService.java

@@ -19,7 +19,7 @@ public class FollowService {
     private RedisTemplate redisTemplate;
 
     @Autowired
-    private UserServiceImpl userService;
+    private UserService userService;
 
     public void follow(int followerId, int followeeId) throws InterruptedException {
         String followeeKey = RedisUtil.getFolloweeKey(String.valueOf(followerId));

+ 2 - 3
src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/ForumPostServiceImpl.java → src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/ForumPostService.java

@@ -2,17 +2,16 @@ package cn.seecoder.fdroidrepository.Service.ServiceImpl;
 
 import cn.seecoder.fdroidrepository.DataObject.ForumPost;
 import cn.seecoder.fdroidrepository.Mapper.ForumPostMapper;
-import cn.seecoder.fdroidrepository.Service.ForumPostService;
+import cn.seecoder.fdroidrepository.Service.IForumPostService;
 import cn.seecoder.fdroidrepository.security.SensitiveFilter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.time.LocalDateTime;
 import java.util.List;
 
 // ForumPostServiceImpl.java
 @Service
-public class ForumPostServiceImpl implements ForumPostService {
+public class ForumPostService implements IForumPostService {
     @Autowired
     private ForumPostMapper forumPostMapper;
 

+ 43 - 0
src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/MailService.java

@@ -0,0 +1,43 @@
+package cn.seecoder.fdroidrepository.Service.ServiceImpl;
+
+import cn.seecoder.fdroidrepository.DataObject.User;
+import cn.seecoder.fdroidrepository.utils.MailClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.context.Context;
+
+@Service
+public class MailService {
+    @Value("${community.path.domain}")
+    private String domain;
+
+    @Value("${server.servlet.context-path}")
+    private String contextPath;
+
+    @Autowired
+    private MailClient mailClient;
+
+    @Autowired
+    private TemplateEngine templateEngine;
+
+    public void activateMail(User user){
+        Context context = new Context();
+        context.setVariable("email",user.getEmail());
+        String url = domain + contextPath + "/api/users/activation/" + user.getUserId() + "/" + user.getActivationCode();
+        context.setVariable("url",url);
+        String content = templateEngine.process("mail/activation",context);
+        mailClient.sendMail(user.getEmail(),"activate account",content);
+    }
+
+    public void followMail(String targetMail ,String username){
+        Context context = new Context();
+        context.setVariable("email",targetMail);
+        context.setVariable("username",username);
+        String content = templateEngine.process("mail/follow",context);
+        mailClient.sendMail(targetMail,"following noticement",content);
+    }
+
+
+}

+ 2 - 2
src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/RedisLikeService.java → src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/RedisILikeService.java

@@ -1,13 +1,13 @@
 package cn.seecoder.fdroidrepository.Service.ServiceImpl;
 
-import cn.seecoder.fdroidrepository.Service.LikeService;
+import cn.seecoder.fdroidrepository.Service.ILikeService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.core.SetOperations;
 import org.springframework.stereotype.Service;
 
 @Service
-public class RedisLikeService implements LikeService {
+public class RedisILikeService implements ILikeService {
     @Autowired
     private RedisTemplate redisTemplate;
 

+ 5 - 23
src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/UserServiceImpl.java → src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/UserService.java

@@ -3,12 +3,10 @@ package cn.seecoder.fdroidrepository.Service.ServiceImpl;
 
 import cn.seecoder.fdroidrepository.DataObject.User;
 import cn.seecoder.fdroidrepository.Mapper.UserMapper;
-import cn.seecoder.fdroidrepository.Service.UserService;
+import cn.seecoder.fdroidrepository.Service.IUserService;
 import cn.seecoder.fdroidrepository.config.WebSecurityConfig;
-import cn.seecoder.fdroidrepository.utils.MailClient;
 import cn.seecoder.fdroidrepository.utils.Tools;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.userdetails.UserDetails;
@@ -16,28 +14,17 @@ import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.stereotype.Service;
 
-import org.thymeleaf.TemplateEngine;
-import org.thymeleaf.context.Context;
-
 import java.util.*;
 
 @Service
-public class UserServiceImpl implements UserService, UserDetailsService {
-
-    @Value("${community.path.domain}")
-    private String domain;
-
-    @Value("${server.servlet.context-path}")
-    private String contextPath;
+public class UserService implements IUserService, UserDetailsService {
 
-    @Autowired
-    private MailClient mailClient;
 
     @Autowired
-    private TemplateEngine templateEngine;
+    private UserMapper userMapper;
 
     @Autowired
-    private UserMapper userMapper;
+    private MailService mailService;
 
     @Override
     public User findByUsername(String username) {
@@ -57,12 +44,7 @@ public class UserServiceImpl implements UserService, UserDetailsService {
 
 
         //send email
-        Context context = new Context();
-        context.setVariable("email",user.getEmail());
-        String url = domain + contextPath + "/api/users/activation/" + user.getUserId() + "/" + user.getActivationCode();
-        context.setVariable("url",url);
-        String content = templateEngine.process("mail/activation",context);
-        mailClient.sendMail(user.getEmail(),"activate account",content);
+        mailService.activateMail(user);
     }
 
     @Override

+ 7 - 2
src/main/java/cn/seecoder/fdroidrepository/aspect/ServiceLogAspect.java

@@ -32,8 +32,13 @@ public class ServiceLogAspect {
     @Before("pointcut()")
     public void before(JoinPoint joinPoint){
         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
-        HttpServletRequest request = attributes.getRequest();
-        String ip = request.getRemoteHost();
+        String ip = "";
+        if(attributes==null){
+            ip="kafka";
+        }else{
+            HttpServletRequest request = attributes.getRequest();
+            ip = request.getRemoteHost();
+        }
         String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
         String target = joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName();
         logger.info(String.format("用户[%s],在[%s],访问了[%s]。", ip, now, target));

+ 9 - 0
src/main/java/cn/seecoder/fdroidrepository/common/constants.java

@@ -0,0 +1,9 @@
+package cn.seecoder.fdroidrepository.common;
+
+public class constants {
+    public final static String TOPIC_COMMENT="comment";
+
+    public final static String TOPIC_FOLLOW = "follow";
+
+    public final static String TOPIC_LIKE="like";
+}

+ 2 - 3
src/main/java/cn/seecoder/fdroidrepository/config/WebSecurityConfig.java

@@ -1,13 +1,12 @@
 package cn.seecoder.fdroidrepository.config;
 
-import cn.seecoder.fdroidrepository.Service.ServiceImpl.UserServiceImpl;
+import cn.seecoder.fdroidrepository.Service.ServiceImpl.UserService;
 import cn.seecoder.fdroidrepository.security.JwtAuthenticationEntryPoint;
 import cn.seecoder.fdroidrepository.security.JwtAuthenticationTokenFilter;
 import cn.seecoder.fdroidrepository.utils.JwtUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.core.env.Environment;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.config.BeanIds;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -45,7 +44,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     public static final String TOKEN_PREFIX = "Bearer ";
 
     @Autowired
-    private UserServiceImpl userDetailsService;
+    private UserService userDetailsService;
 
     @Autowired
     private JwtAuthenticationEntryPoint unauthorizedHandler;

+ 51 - 0
src/main/java/cn/seecoder/fdroidrepository/event/EventConsumer.java

@@ -0,0 +1,51 @@
+package cn.seecoder.fdroidrepository.event;
+
+
+
+import cn.seecoder.fdroidrepository.DataObject.Event;
+import cn.seecoder.fdroidrepository.DataObject.User;
+import cn.seecoder.fdroidrepository.Service.ServiceImpl.MailService;
+import cn.seecoder.fdroidrepository.Service.ServiceImpl.UserService;
+import com.alibaba.fastjson2.JSONObject;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Component;
+
+import static cn.seecoder.fdroidrepository.common.constants.*;
+
+@Component
+public class EventConsumer {
+    private static final Logger logger = LoggerFactory.getLogger(EventConsumer.class);
+
+    @Autowired
+    private MailService mailService;
+
+    @Autowired
+    private UserService userService;
+
+    @KafkaListener(topics = {TOPIC_COMMENT,TOPIC_LIKE,TOPIC_FOLLOW})
+    public void handleCommentMessage(ConsumerRecord record){
+        if(record == null||record.value()==null){
+            logger.error("message is null!");
+            return;
+        }
+        Event event = JSONObject.parseObject(record.value().toString(), Event.class);
+        if(event==null){
+            logger.error("消息格式错误!");
+            return;
+        }
+        switch (event.getTopic()){
+            case TOPIC_FOLLOW:
+                User follower = userService.loadUserByUserId((int) event.getEntityUserId());
+                String mailAddr = follower.getEmail();
+                User followee = userService.loadUserByUserId((int) event.getUserId());
+                mailService.followMail(mailAddr,followee.getUsername());
+        }
+
+    }
+
+
+}

+ 19 - 0
src/main/java/cn/seecoder/fdroidrepository/event/EventProducer.java

@@ -0,0 +1,19 @@
+package cn.seecoder.fdroidrepository.event;
+
+
+import cn.seecoder.fdroidrepository.DataObject.Event;
+import com.alibaba.fastjson2.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class EventProducer {
+
+    @Autowired
+    private KafkaTemplate kafkaTemplate;
+
+    public void fireEvent(Event event) {
+        kafkaTemplate.send(event.getTopic(), JSONObject.toJSONString(event));
+    }
+}

+ 5 - 0
src/main/resources/application.properties

@@ -65,4 +65,9 @@ spring.mail.default-encoding=UTF-8
 
 jwt.secret-key=abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg
 
+# kafka配置
+spring.kafka.bootstrap-servers=127.0.0.1:9092
+spring.kafka.consumer.group-id=fdroid-consumer-group
+spring.kafka.consumer.enable-auto-commit=true
+spring.kafka.consumer.auto-commit-interval=3000
 

+ 30 - 0
src/main/resources/templates/mail/follow.html

@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>关注通知</title>
+  <style>
+    body {
+      font-family: Arial, sans-serif;
+      text-align: center;
+      margin: 50px;
+    }
+
+    h1 {
+      color: #333;
+    }
+
+    p {
+      color: #666;
+    }
+  </style>
+</head>
+<body>
+<h1>关注通知</h1>
+<p>
+  <b th:text="${email}">xxx@xxx.com</b>, 您好!
+</p>
+<p><b th:text="${username}">某人</b>关注了您!</p>
+</body>
+</html>