From ead6af62d0389040cc35b99f0259a72c0f2772ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=92=8B=E5=A5=89=E5=90=89?= <709979501@qq.com> Date: Fri, 19 May 2023 00:09:33 +0800 Subject: [PATCH 1/2] finished task for wirecraft --- pom.xml | 156 ++++++++++++++++++ .../test/backend/TestBackendApplication.java | 13 ++ .../test/backend/annotation/Authorized.java | 12 ++ .../test/backend/config/SecurityConfig.java | 30 ++++ .../jiang/test/backend/config/WebConfig.java | 20 +++ .../test/backend/constant/ApiConstants.java | 22 +++ .../backend/controller/FriendController.java | 97 +++++++++++ .../backend/controller/UserController.java | 83 ++++++++++ .../jiang/test/backend/entity/Follower.java | 28 ++++ .../com/jiang/test/backend/entity/User.java | 47 ++++++ .../exception/GlobalExceptionHandler.java | 15 ++ .../interceptor/AuthorizationInterceptor.java | 37 +++++ .../repository/FollowerRepository.java | 18 ++ .../backend/repository/UserRepository.java | 10 ++ .../test/backend/service/FriendService.java | 21 +++ .../test/backend/service/UserService.java | 16 ++ .../service/impl/FriendServiceImpl.java | 133 +++++++++++++++ .../backend/service/impl/UserServiceImpl.java | 51 ++++++ .../test/backend/utils/DistanceUtils.java | 27 +++ src/main/resources/application.properties | 10 ++ .../V0.1__create-user-and-followers.sql | 39 +++++ src/main/resources/logback.xml | 27 +++ src/test/java/FriendServiceTest.java | 5 + src/test/java/UserServiceTest.java | 64 +++++++ 24 files changed, 981 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/com/jiang/test/backend/TestBackendApplication.java create mode 100644 src/main/java/com/jiang/test/backend/annotation/Authorized.java create mode 100644 src/main/java/com/jiang/test/backend/config/SecurityConfig.java create mode 100644 src/main/java/com/jiang/test/backend/config/WebConfig.java create mode 100644 src/main/java/com/jiang/test/backend/constant/ApiConstants.java create mode 100644 src/main/java/com/jiang/test/backend/controller/FriendController.java create mode 100644 src/main/java/com/jiang/test/backend/controller/UserController.java create mode 100644 src/main/java/com/jiang/test/backend/entity/Follower.java create mode 100644 src/main/java/com/jiang/test/backend/entity/User.java create mode 100644 src/main/java/com/jiang/test/backend/exception/GlobalExceptionHandler.java create mode 100644 src/main/java/com/jiang/test/backend/interceptor/AuthorizationInterceptor.java create mode 100644 src/main/java/com/jiang/test/backend/repository/FollowerRepository.java create mode 100644 src/main/java/com/jiang/test/backend/repository/UserRepository.java create mode 100644 src/main/java/com/jiang/test/backend/service/FriendService.java create mode 100644 src/main/java/com/jiang/test/backend/service/UserService.java create mode 100644 src/main/java/com/jiang/test/backend/service/impl/FriendServiceImpl.java create mode 100644 src/main/java/com/jiang/test/backend/service/impl/UserServiceImpl.java create mode 100644 src/main/java/com/jiang/test/backend/utils/DistanceUtils.java create mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/db/migration/V0.1__create-user-and-followers.sql create mode 100644 src/main/resources/logback.xml create mode 100644 src/test/java/FriendServiceTest.java create mode 100644 src/test/java/UserServiceTest.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3a3c394 --- /dev/null +++ b/pom.xml @@ -0,0 +1,156 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.4.RELEASE + + + com.test-backend-java + test-backend-java + 0.0.1-SNAPSHOT + test-backend-java + Demo project for Spring Boot + + + 11 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + mysql + mysql-connector-java + runtime + + + + log4j + log4j + 1.2.17 + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + com.alibaba + fastjson + 1.2.54 + + + + + org.projectlombok + lombok + 1.18.12 + provided + + + + com.jiang.core + core + 1.0-SNAPSHOT + + + + + + + + + + + + + + + + + + + + + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-oauth2-client + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + + + + ch.qos.logback + logback-classic + + + + + org.flywaydb + flyway-core + 7.11.1 + + + + + + + + + kr.motd.maven + os-maven-plugin + 1.5.0.Final + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + + + + diff --git a/src/main/java/com/jiang/test/backend/TestBackendApplication.java b/src/main/java/com/jiang/test/backend/TestBackendApplication.java new file mode 100644 index 0000000..8cd5260 --- /dev/null +++ b/src/main/java/com/jiang/test/backend/TestBackendApplication.java @@ -0,0 +1,13 @@ +package com.jiang.test.backend; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class TestBackendApplication { + + public static void main(String[] args) { + SpringApplication.run(TestBackendApplication.class, args); + } + +} diff --git a/src/main/java/com/jiang/test/backend/annotation/Authorized.java b/src/main/java/com/jiang/test/backend/annotation/Authorized.java new file mode 100644 index 0000000..4ddd723 --- /dev/null +++ b/src/main/java/com/jiang/test/backend/annotation/Authorized.java @@ -0,0 +1,12 @@ +package com.jiang.test.backend.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Authorized { +} + diff --git a/src/main/java/com/jiang/test/backend/config/SecurityConfig.java b/src/main/java/com/jiang/test/backend/config/SecurityConfig.java new file mode 100644 index 0000000..88ae45e --- /dev/null +++ b/src/main/java/com/jiang/test/backend/config/SecurityConfig.java @@ -0,0 +1,30 @@ +package com.jiang.test.backend.config; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests() + .antMatchers("/login").authenticated() + .anyRequest().permitAll() + .and() + .formLogin().disable(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + // 在这里配置模拟的授权用户,可以根据需要自定义 + auth.inMemoryAuthentication() + .withUser("username") + .password("{noop}password") // {noop} 表示密码不加密 + .roles("USER"); + } +} diff --git a/src/main/java/com/jiang/test/backend/config/WebConfig.java b/src/main/java/com/jiang/test/backend/config/WebConfig.java new file mode 100644 index 0000000..9b01fec --- /dev/null +++ b/src/main/java/com/jiang/test/backend/config/WebConfig.java @@ -0,0 +1,20 @@ +package com.jiang.test.backend.config; + +import com.jiang.test.backend.interceptor.AuthorizationInterceptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +@Configuration +public class WebConfig extends WebMvcConfigurerAdapter { + + @Autowired + private AuthorizationInterceptor authorizationInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(authorizationInterceptor) + .addPathPatterns("/api/**"); // 设置需要进行授权检查的接口路径 + } +} diff --git a/src/main/java/com/jiang/test/backend/constant/ApiConstants.java b/src/main/java/com/jiang/test/backend/constant/ApiConstants.java new file mode 100644 index 0000000..77c1ec2 --- /dev/null +++ b/src/main/java/com/jiang/test/backend/constant/ApiConstants.java @@ -0,0 +1,22 @@ +package com.jiang.test.backend.constant; + +public final class ApiConstants { + + public static final String COMMON_URL = "/api"; + public static final String GET_URL = "/user/getUser/{id}"; + public static final String POST_URL = "/user/addUser"; + public static final String UPDATE_URL = "/user/updateUser"; + public static final String DELETE_URL = "/user/delUser"; + + public static final String GET_FOLLOWERS_URL = "/friend/delUser"; + public static final String ADD_FOLLOWERS_URL = "/friend/delUser"; + public static final String REMOVE_FOLLOWERS_URL = "/friend/delUser"; + public static final String GET_FRIENDS_URL = "/friend/delUser"; + public static final String GET_COMMON_FRIENDS_URL = "/friend/delUser"; + public static final String GET_NEARBY_FRIENDS_URL = "/friend/delUser"; + + + private ApiConstants() { + // 私有构造函数,防止实例化 + } +} \ No newline at end of file diff --git a/src/main/java/com/jiang/test/backend/controller/FriendController.java b/src/main/java/com/jiang/test/backend/controller/FriendController.java new file mode 100644 index 0000000..c07554e --- /dev/null +++ b/src/main/java/com/jiang/test/backend/controller/FriendController.java @@ -0,0 +1,97 @@ +package com.jiang.test.backend.controller; + +import com.jiang.test.backend.annotation.Authorized; +import com.jiang.test.backend.constant.ApiConstants; +import com.jiang.test.backend.entity.User; +import com.jiang.test.backend.service.FriendService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping(ApiConstants.COMMON_URL) +public class FriendController { + + @Autowired + FriendService friendService; + + /** + * 获取用户的所有关注者 + * @param userId + * @return List + */ + @GetMapping("/friend/{userId}") + @Authorized + public ResponseEntity> getFollowers(@PathVariable int userId) { + List followers = friendService.getFollowers(userId); + return ResponseEntity.ok(followers); + } + + /** + * 添加关注者 + * 请求体中包含要添加的关注者的ID。 + * @param userId + * @param followerId + * @return + */ + @PostMapping("/friend/{userId}/follower/{followerId}") + @Authorized + public ResponseEntity addFollower(@PathVariable int userId, @PathVariable int followerId) { + friendService.addFollower(userId,followerId); + return ResponseEntity.ok("Follower added successfully."); + } + + /** + * 删除关注者 + * @param userId + * @param followerId + * @return + */ + @DeleteMapping("/friend/{userId}/followers/{followerId}") + @Authorized + public ResponseEntity removeFollower(@PathVariable int userId, @PathVariable int followerId) { + friendService.removeFollower(userId,followerId); + return ResponseEntity.ok("Follower removed successfully."); + } + + /** + * 获取用户的所有好友 + * @param userId + * @return + */ + @GetMapping("/friend/{userId}/friends") + @Authorized + public ResponseEntity> getFriends(@PathVariable int userId) { + List friends = friendService.getFriends(userId); + return ResponseEntity.ok(friends); + } + + /** + * 获取共同的朋友 + * @param userId + * @param otherUserId + * @return + */ + @GetMapping("/friend/{userId}/friends/common") + @Authorized + public ResponseEntity> getCommonFriends(@PathVariable int userId, @RequestParam int otherUserId) { + List commonFriends = friendService.getCommonFriends(userId,otherUserId); + return ResponseEntity.ok(commonFriends); + } + + /** + * 获取最近的朋友信息 + * @param userId + * @param distance + * @return + */ + @GetMapping("/friend/{userName}/distance/{disdance}") + @Authorized + public ResponseEntity> getNearbyFriends(@PathVariable int userId, + @PathVariable double distance) { + List nearbyFriends = friendService.getNearbyFriends(userId,distance); + return ResponseEntity.ok(nearbyFriends); + } +} diff --git a/src/main/java/com/jiang/test/backend/controller/UserController.java b/src/main/java/com/jiang/test/backend/controller/UserController.java new file mode 100644 index 0000000..3799edd --- /dev/null +++ b/src/main/java/com/jiang/test/backend/controller/UserController.java @@ -0,0 +1,83 @@ +package com.jiang.test.backend.controller; + +import com.jiang.test.backend.annotation.Authorized; +import com.jiang.test.backend.constant.ApiConstants; +import com.jiang.test.backend.entity.User; +import com.jiang.test.backend.service.UserService; +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.Optional; + +@RestController +@RequestMapping(ApiConstants.COMMON_URL) +public class UserController { + + @Autowired + private UserService userService; + + /** + * getUser + * @param id userId + * @return User user信息 + * @description 根据id获取user + */ + @GetMapping(ApiConstants.GET_URL) + @Authorized + public ResponseEntity getUser(@PathVariable String id){ + Optional user = userService.getUserById(Integer.parseInt(id)); + return user.map(value -> new ResponseEntity<>(value, HttpStatus.OK)) + .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); + } + + /** + * addUser + * @param user user信息 + * @return user user信息 + * @description 添加user + */ + @PostMapping(ApiConstants.POST_URL) + @Authorized + public ResponseEntity addUser(@RequestBody User user){ + User createdUser = userService.addUser(user); + return new ResponseEntity<>(createdUser, HttpStatus.CREATED); + } + + /** + * updateUser + * @param user user信息 + * @return user user信息 + * @description 更新user + */ + @PutMapping(ApiConstants.UPDATE_URL) + @Authorized + public ResponseEntity updateUser(@RequestBody User user){ + Optional existingUser = userService.getUserById(user.getId()); + if (existingUser.isPresent()) { + User updatedUser = userService.updateUser(user); + return new ResponseEntity<>(updatedUser, HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + /** + * updateUser + * @param id userId + * @return void + * @description 根据userId 删除User + */ + @DeleteMapping(ApiConstants.DELETE_URL) + @Authorized + public ResponseEntity delUser(@RequestParam int id){ + Optional existingUser = userService.getUserById(id); + if (existingUser.isPresent()) { + userService.deleteUserById(id); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } +} diff --git a/src/main/java/com/jiang/test/backend/entity/Follower.java b/src/main/java/com/jiang/test/backend/entity/Follower.java new file mode 100644 index 0000000..9cd3df0 --- /dev/null +++ b/src/main/java/com/jiang/test/backend/entity/Follower.java @@ -0,0 +1,28 @@ +package com.jiang.test.backend.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "tbl_user") +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Follower { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @Column(name = "user_id") + private int userId; + + @Column(name = "follower_id") + private int followerId; + +} diff --git a/src/main/java/com/jiang/test/backend/entity/User.java b/src/main/java/com/jiang/test/backend/entity/User.java new file mode 100644 index 0000000..71977dc --- /dev/null +++ b/src/main/java/com/jiang/test/backend/entity/User.java @@ -0,0 +1,47 @@ +package com.jiang.test.backend.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +@Entity +@Table(name = "tbl_user") +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class User { + + @Column(name = "id") + @Id + private int id; + + @Column(name = "name") + private String name; + + @Column(name = "dob") + private Date dob; + + @Column(name = "address") + private String address; + + @Column(name = "description") + private String description; + + @Column(name = "created_at") + private Date createdAt; + + @Column(name = "latitude") + private Double latitude; + + @Column(name = "longitude") + private Double longitude; + +} diff --git a/src/main/java/com/jiang/test/backend/exception/GlobalExceptionHandler.java b/src/main/java/com/jiang/test/backend/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..b5abeee --- /dev/null +++ b/src/main/java/com/jiang/test/backend/exception/GlobalExceptionHandler.java @@ -0,0 +1,15 @@ +package com.jiang.test.backend.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception ex) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Internal Server Error"); + } +} diff --git a/src/main/java/com/jiang/test/backend/interceptor/AuthorizationInterceptor.java b/src/main/java/com/jiang/test/backend/interceptor/AuthorizationInterceptor.java new file mode 100644 index 0000000..f483ea2 --- /dev/null +++ b/src/main/java/com/jiang/test/backend/interceptor/AuthorizationInterceptor.java @@ -0,0 +1,37 @@ +package com.jiang.test.backend.interceptor; + +import com.jiang.test.backend.annotation.Authorized; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Component +@Slf4j +public class AuthorizationInterceptor extends HandlerInterceptorAdapter { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + // 检查是否有 @Authorized 注解 + if (handler instanceof HandlerMethod) { + HandlerMethod handlerMethod = (HandlerMethod) handler; + if (handlerMethod.hasMethodAnnotation(Authorized.class)) { + // 进行授权检查逻辑 + if (!isAuthorized(request)) { + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + return false; // 返回 false,拦截请求 + } + } + } + return true; // 允许请求继续处理 + } + + private boolean isAuthorized(HttpServletRequest request) { + // 进行授权检查的逻辑,例如检查请求头、访问令牌等 + log.info("Authorization checking"); + return true; + } +} diff --git a/src/main/java/com/jiang/test/backend/repository/FollowerRepository.java b/src/main/java/com/jiang/test/backend/repository/FollowerRepository.java new file mode 100644 index 0000000..d773c78 --- /dev/null +++ b/src/main/java/com/jiang/test/backend/repository/FollowerRepository.java @@ -0,0 +1,18 @@ +package com.jiang.test.backend.repository; + +import com.jiang.test.backend.entity.Follower; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface FollowerRepository extends JpaRepository { + + List findByUserId(int userId); + + Optional findByUserIdAndFollowerId(int userId, int followerId); + + +} diff --git a/src/main/java/com/jiang/test/backend/repository/UserRepository.java b/src/main/java/com/jiang/test/backend/repository/UserRepository.java new file mode 100644 index 0000000..2d0ef0e --- /dev/null +++ b/src/main/java/com/jiang/test/backend/repository/UserRepository.java @@ -0,0 +1,10 @@ +package com.jiang.test.backend.repository; + +import com.jiang.test.backend.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRepository extends JpaRepository { + +} diff --git a/src/main/java/com/jiang/test/backend/service/FriendService.java b/src/main/java/com/jiang/test/backend/service/FriendService.java new file mode 100644 index 0000000..54d86cf --- /dev/null +++ b/src/main/java/com/jiang/test/backend/service/FriendService.java @@ -0,0 +1,21 @@ +package com.jiang.test.backend.service; + +import com.jiang.test.backend.entity.User; + +import java.util.List; + +public interface FriendService { + + List getFollowers(int userId); + + void addFollower(int userId, int followerId); + + void removeFollower(int userId, int followerId); + + List getFriends(int userId); + + List getCommonFriends(int userId1, int userId2); + + List getNearbyFriends(int userId, double distance); + +} diff --git a/src/main/java/com/jiang/test/backend/service/UserService.java b/src/main/java/com/jiang/test/backend/service/UserService.java new file mode 100644 index 0000000..273af4d --- /dev/null +++ b/src/main/java/com/jiang/test/backend/service/UserService.java @@ -0,0 +1,16 @@ +package com.jiang.test.backend.service; + +import com.jiang.test.backend.entity.User; + +import java.util.Optional; + +public interface UserService { + + Optional getUserById(int id); + + User addUser(User user); + + User updateUser(User user); + + void deleteUserById(int id); +} diff --git a/src/main/java/com/jiang/test/backend/service/impl/FriendServiceImpl.java b/src/main/java/com/jiang/test/backend/service/impl/FriendServiceImpl.java new file mode 100644 index 0000000..27f99aa --- /dev/null +++ b/src/main/java/com/jiang/test/backend/service/impl/FriendServiceImpl.java @@ -0,0 +1,133 @@ +package com.jiang.test.backend.service.impl; + +import com.jiang.test.backend.entity.Follower; +import com.jiang.test.backend.entity.User; +import com.jiang.test.backend.repository.FollowerRepository; +import com.jiang.test.backend.repository.UserRepository; +import com.jiang.test.backend.service.FriendService; +import com.jiang.test.backend.service.UserService; +import com.jiang.test.backend.utils.DistanceUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +@Service +public class FriendServiceImpl implements FriendService { + + private static final Logger logger = LoggerFactory.getLogger(UserService.class); + + @Autowired + FollowerRepository followerRepository; + + @Autowired + UserRepository userRepository; + + @Override + public List getFollowers(int userId) { + List followers = followerRepository.findByUserId(userId); + // 将 Follower 转换为 User 对象 + List followerUsers = new ArrayList<>(); + for (Follower follower : followers) { + User followerUser = convertFollowerToUser(follower); + followerUsers.add(followerUser); + } + return followerUsers; + } + + private User convertFollowerToUser(Follower follower) { + return userRepository.findById(follower.getFollowerId()).get(); + } + + @Override + public void addFollower(int userId, int followerId) { + Optional existingFollower = followerRepository.findByUserIdAndFollowerId(userId, followerId); + if (existingFollower.isPresent()) { + logger.error("Follower already exists."); + throw new IllegalArgumentException("Follower already exists."); + } + Follower follower = new Follower(); + follower.setUserId(userId); + follower.setFollowerId(followerId); + followerRepository.save(follower); + } + + @Override + public void removeFollower(int userId, int followerId) { + Optional follower = followerRepository.findByUserIdAndFollowerId(userId, followerId); + if (follower.isPresent()) { + followerRepository.delete(follower.get()); + } else { + logger.error("Follower not found."); + throw new IllegalArgumentException("Follower not found."); + } + } + + @Override + public List getFriends(int userId) { + List followers = followerRepository.findByUserId(userId); + List friendsList = new ArrayList<>(); + for (int i = 0; i < followers.size(); i++) { + List followers1 = getFollowers(followers.get(i).getFollowerId()); + if (CollectionUtils.isEmpty(followers1)){ + friendsList.add(followers1.get(0)); + } + } + return friendsList; + } + + @Override + public List getCommonFriends(int userId1, int userId2) { + + List commonFriends = new ArrayList<>(); + + // 获取用户1的好友列表 + List friends1 = getFriends(userId1); + + // 获取用户2的好友列表 + List friends2 = getFriends(userId2); + + // 遍历用户1的好友列表,判断是否为用户2的好友 + for (User friend1 : friends1) { + if (friends2.contains(friend1)) { + commonFriends.add(friend1); + } + } + + return commonFriends; + } + + @Override + public List getNearbyFriends(int userId, double distance) { + + List nearbyFriendsList = new ArrayList<>(); + + User userInfo = userRepository.findById(userId).get(); + Double latitude1 = userInfo.getLatitude(); + Double longitude1 = userInfo.getLongitude(); + + //获取所有朋友 + List friendList = getFriends(userId); + + if (CollectionUtils.isEmpty(friendList)) { + logger.info("No Friend"); + return null; + } + + //计算小于规定距离的 + for (User user:friendList) { + double calculateDistance = DistanceUtils.calculateDistance(latitude1, longitude1, user.getLatitude(), user.getLongitude()); + if (calculateDistance getUserById(int id){ + logger.info("User get: {}", id); + return userRepository.findById(id); + } + + @Override + public User addUser(User user) { + User saveUser = userRepository.save(user); + logger.info("User saved: {}", user); + return saveUser; + } + + @Override + public User updateUser(User user) { + User updateUser = userRepository.save(user); + logger.info("User updated: {}", updateUser); + return updateUser; + } + + @Override + public void deleteUserById(int id) { + logger.info("User deleted: {}", id); + userRepository.deleteById(id); + } + + +} diff --git a/src/main/java/com/jiang/test/backend/utils/DistanceUtils.java b/src/main/java/com/jiang/test/backend/utils/DistanceUtils.java new file mode 100644 index 0000000..628d5ae --- /dev/null +++ b/src/main/java/com/jiang/test/backend/utils/DistanceUtils.java @@ -0,0 +1,27 @@ +package com.jiang.test.backend.utils; + +public class DistanceUtils { + + public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) { + double earthRadius = 6371.0; // 地球半径(单位:公里) + + // 将经纬度转换为弧度 + double lat1Rad = Math.toRadians(lat1); + double lon1Rad = Math.toRadians(lon1); + double lat2Rad = Math.toRadians(lat2); + double lon2Rad = Math.toRadians(lon2); + + // 计算经纬度的差值 + double dLon = lon2Rad - lon1Rad; + double dLat = lat2Rad - lat1Rad; + + // 应用Haversine公式计算距离 + double a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.pow(Math.sin(dLon / 2), 2); + double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + double distance = earthRadius * c; + + return distance; + } + + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..096396c --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,10 @@ +spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&serverTimezone=UTC +spring.datasource.username=root +spring.datasource.password=123456 +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver + +spring.jpa.properties.hibernate.hbm2ddl.auto=update + +spring.devtools.restart.enabled=true + +spring.flyway.enabled=false \ No newline at end of file diff --git a/src/main/resources/db/migration/V0.1__create-user-and-followers.sql b/src/main/resources/db/migration/V0.1__create-user-and-followers.sql new file mode 100644 index 0000000..8a8b820 --- /dev/null +++ b/src/main/resources/db/migration/V0.1__create-user-and-followers.sql @@ -0,0 +1,39 @@ +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for tbl_user +-- ---------------------------- +DROP TABLE IF EXISTS `tbl_user`; +CREATE TABLE `tbl_user` ( + `id` int(0) NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `dob` date NULL DEFAULT NULL, + `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `created_at` date NULL DEFAULT NULL, + `latitude` double(10, 2) NULL DEFAULT NULL, + `longitude` double(10, 2) NULL DEFAULT NULL, + `follower_id` int(0) NULL DEFAULT NULL, + `user_id` int(0) NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +SET FOREIGN_KEY_CHECKS = 1; + + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for tbl_follower +-- ---------------------------- +DROP TABLE IF EXISTS `tbl_follower`; +CREATE TABLE `tbl_follower` ( + `user_id` int(0) NULL DEFAULT NULL, + `follower_id` int(0) NULL DEFAULT NULL, + `id` int(0) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..d532a5a --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,27 @@ + + + + + + + + + ${LOG_PATTERN} + + + + + + logs/application.log + + ${LOG_PATTERN} + + + + + + + + + + diff --git a/src/test/java/FriendServiceTest.java b/src/test/java/FriendServiceTest.java new file mode 100644 index 0000000..ab7b8c4 --- /dev/null +++ b/src/test/java/FriendServiceTest.java @@ -0,0 +1,5 @@ +public class FriendServiceTest { + + //todo + +} diff --git a/src/test/java/UserServiceTest.java b/src/test/java/UserServiceTest.java new file mode 100644 index 0000000..2663b3d --- /dev/null +++ b/src/test/java/UserServiceTest.java @@ -0,0 +1,64 @@ +import com.jiang.test.backend.entity.User; +import com.jiang.test.backend.repository.UserRepository; +import com.jiang.test.backend.service.impl.UserServiceImpl; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.Date; +import java.util.Optional; + +public class UserServiceTest { + + @InjectMocks + private UserServiceImpl userService; + + @Mock + private UserRepository userRepository; + + User user; + + @BeforeEach + public void setUp(){ + MockitoAnnotations.initMocks(this); + user = User.builder() + .id(3) + .address("shenzhen") + .name("jiang") + .dob(new Date()) + .description("test") + .createdAt(new Date()) + .build(); + } + + @Test + public void testGetUser(){ + Mockito.when(userRepository.findById(Mockito.any())).thenReturn(Optional.of(user)); + Optional user1 = userService.getUserById(3); + Assertions.assertEquals(user,user1.get()); + } + + @Test + public void testAddUser(){ + Mockito.when(userRepository.save(Mockito.any())).thenReturn(user); + User user1 = userService.addUser(user); + Assertions.assertEquals(3,user1.getId()); + } + + @Test + public void testUpdateUser(){ + Mockito.when(userRepository.save(Mockito.any())).thenReturn(user); + User user1 = userService.updateUser(user); + Assertions.assertEquals(3,user1.getId()); + } + + @Test + public void testDelUser(){ + Mockito.doNothing().when(userRepository).deleteById(3); + userService.deleteUserById(3); + } +} From e408f3135469ea5af8a89ae10dfaf6f7869b9dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=92=8B=E5=A5=89=E5=90=89?= <709979501@qq.com> Date: Fri, 19 May 2023 01:11:11 +0800 Subject: [PATCH 2/2] finished fix issue --- .../test/backend/constant/ApiConstants.java | 12 ++++++------ .../backend/controller/FriendController.java | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/jiang/test/backend/constant/ApiConstants.java b/src/main/java/com/jiang/test/backend/constant/ApiConstants.java index 77c1ec2..315c155 100644 --- a/src/main/java/com/jiang/test/backend/constant/ApiConstants.java +++ b/src/main/java/com/jiang/test/backend/constant/ApiConstants.java @@ -8,12 +8,12 @@ public final class ApiConstants { public static final String UPDATE_URL = "/user/updateUser"; public static final String DELETE_URL = "/user/delUser"; - public static final String GET_FOLLOWERS_URL = "/friend/delUser"; - public static final String ADD_FOLLOWERS_URL = "/friend/delUser"; - public static final String REMOVE_FOLLOWERS_URL = "/friend/delUser"; - public static final String GET_FRIENDS_URL = "/friend/delUser"; - public static final String GET_COMMON_FRIENDS_URL = "/friend/delUser"; - public static final String GET_NEARBY_FRIENDS_URL = "/friend/delUser"; + public static final String GET_FOLLOWERS_URL = "/friend/{userId}"; + public static final String ADD_FOLLOWERS_URL = "/friend/addFollower"; + public static final String REMOVE_FOLLOWERS_URL = "/friend/removeFollower"; + public static final String GET_FRIENDS_URL = "/friend/getFriends/{userId}"; + public static final String GET_COMMON_FRIENDS_URL = "/friend/{userId}/friends/{otherUserId}"; + public static final String GET_NEARBY_FRIENDS_URL = "/friend/{userId}/distance/{disdance}"; private ApiConstants() { diff --git a/src/main/java/com/jiang/test/backend/controller/FriendController.java b/src/main/java/com/jiang/test/backend/controller/FriendController.java index c07554e..8d20bca 100644 --- a/src/main/java/com/jiang/test/backend/controller/FriendController.java +++ b/src/main/java/com/jiang/test/backend/controller/FriendController.java @@ -22,7 +22,7 @@ public class FriendController { * @param userId * @return List */ - @GetMapping("/friend/{userId}") + @GetMapping(ApiConstants.GET_FOLLOWERS_URL) @Authorized public ResponseEntity> getFollowers(@PathVariable int userId) { List followers = friendService.getFollowers(userId); @@ -36,9 +36,9 @@ public ResponseEntity> getFollowers(@PathVariable int userId) { * @param followerId * @return */ - @PostMapping("/friend/{userId}/follower/{followerId}") + @PostMapping(ApiConstants.ADD_FOLLOWERS_URL) @Authorized - public ResponseEntity addFollower(@PathVariable int userId, @PathVariable int followerId) { + public ResponseEntity addFollower(@RequestParam int userId, @RequestParam int followerId) { friendService.addFollower(userId,followerId); return ResponseEntity.ok("Follower added successfully."); } @@ -49,9 +49,9 @@ public ResponseEntity addFollower(@PathVariable int userId, @PathVariabl * @param followerId * @return */ - @DeleteMapping("/friend/{userId}/followers/{followerId}") + @DeleteMapping(ApiConstants.REMOVE_FOLLOWERS_URL) @Authorized - public ResponseEntity removeFollower(@PathVariable int userId, @PathVariable int followerId) { + public ResponseEntity removeFollower(@RequestParam int userId, @RequestParam int followerId) { friendService.removeFollower(userId,followerId); return ResponseEntity.ok("Follower removed successfully."); } @@ -61,7 +61,7 @@ public ResponseEntity removeFollower(@PathVariable int userId, @PathVari * @param userId * @return */ - @GetMapping("/friend/{userId}/friends") + @GetMapping(ApiConstants.GET_FRIENDS_URL) @Authorized public ResponseEntity> getFriends(@PathVariable int userId) { List friends = friendService.getFriends(userId); @@ -74,9 +74,9 @@ public ResponseEntity> getFriends(@PathVariable int userId) { * @param otherUserId * @return */ - @GetMapping("/friend/{userId}/friends/common") + @GetMapping(ApiConstants.GET_COMMON_FRIENDS_URL) @Authorized - public ResponseEntity> getCommonFriends(@PathVariable int userId, @RequestParam int otherUserId) { + public ResponseEntity> getCommonFriends(@PathVariable int userId, @PathVariable int otherUserId) { List commonFriends = friendService.getCommonFriends(userId,otherUserId); return ResponseEntity.ok(commonFriends); } @@ -87,7 +87,7 @@ public ResponseEntity> getCommonFriends(@PathVariable int userId, @Re * @param distance * @return */ - @GetMapping("/friend/{userName}/distance/{disdance}") + @GetMapping(ApiConstants.GET_NEARBY_FRIENDS_URL) @Authorized public ResponseEntity> getNearbyFriends(@PathVariable int userId, @PathVariable double distance) {