user module API & resource module API

GET /user/thread
POST /resource/version
GET /resource/search
GET /resource/info
GET /resource/hot
GET /resource/hot/slide
GET /resource/hot-trend
PUT /resource/info

Change-Id: I39c94b06a1d69967b8e235a67a0133c8095abb38
diff --git a/src/main/java/com/g9/g9backend/controller/ResourceController.java b/src/main/java/com/g9/g9backend/controller/ResourceController.java
index c9a573c..f8d7723 100644
--- a/src/main/java/com/g9/g9backend/controller/ResourceController.java
+++ b/src/main/java/com/g9/g9backend/controller/ResourceController.java
@@ -2,7 +2,8 @@
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
-import com.g9.g9backend.mapper.UserPurchaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.g9.g9backend.pojo.*;
 import com.g9.g9backend.pojo.DTO.*;
 import com.g9.g9backend.service.*;
@@ -12,7 +13,7 @@
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.Date;
+import java.util.*;
 
 
 /**
@@ -44,7 +45,17 @@
 
     private final NotificationService notificationService;
 
-    public ResourceController(ResourceService resourceService, GameplayService gameplayService, RewardService rewardService, UserUploadService userUploadService, CommunityService communityService, UserService userService, UserPurchaseMapper userPurchaseMapper, UserPurchaseService userPurchaseService, UserLikeService userLikeService, UserCollectionService userCollectionService, NotificationService notificationService) {
+    private final ResourceVersionService resourceVersionService;
+
+    private final SearchHistoryService searchHistoryService;
+
+    private final GameVersionService gameVersionService;
+
+    private final TorrentRecordService torrentRecordService;
+
+    private final HotTrendService hotTrendService;
+
+    public ResourceController(ResourceService resourceService, GameplayService gameplayService, RewardService rewardService, UserUploadService userUploadService, CommunityService communityService, UserService userService, UserPurchaseService userPurchaseService, UserLikeService userLikeService, UserCollectionService userCollectionService, NotificationService notificationService, ResourceVersionService resourceVersionService, SearchHistoryService searchHistoryService, GameVersionService gameVersionService, TorrentRecordService torrentRecordService, HotTrendService hotTrendService) {
         this.resourceService = resourceService;
         this.gameplayService = gameplayService;
         this.rewardService = rewardService;
@@ -55,6 +66,11 @@
         this.userLikeService = userLikeService;
         this.userCollectionService = userCollectionService;
         this.notificationService = notificationService;
+        this.resourceVersionService = resourceVersionService;
+        this.searchHistoryService = searchHistoryService;
+        this.gameVersionService = gameVersionService;
+        this.torrentRecordService = torrentRecordService;
+        this.hotTrendService = hotTrendService;
     }
 
     private final Logger logger = LoggerFactory.getLogger(ResourceController.class);
@@ -99,6 +115,18 @@
     }
 
     /**
+     * 上传版本
+     *
+     * @param resourceVersion 上传版本信息
+     * @return 上传版本结果
+     */
+    @PostMapping("/version")
+    public ResponseEntity<String> uploadResourceVersion(@RequestBody ResourceVersion resourceVersion) {
+        resourceVersionService.save(resourceVersion);
+        return ResponseEntity.ok("");
+    }
+
+    /**
      * 购买资源
      *
      * @param userResourceDTO 购买资源信息
@@ -284,4 +312,254 @@
         userCollectionService.remove(userCollectionQuery);
         return ResponseEntity.noContent().build();
     }
+
+    /**
+     * 搜索资源
+     *
+     * @param userId          用户id
+     * @param searchValue     输入(模糊搜索)
+     * @param classify        类型名称
+     * @param gameplayList    主要玩法列表
+     * @param gameVersionList 版本列表
+     * @param pageNumber      第几页
+     * @param rows            行数
+     * @return 搜索资源结果
+     */
+    @GetMapping("/search")
+    public ResponseEntity<GetResourcePageDTO> getResourceSearch(@RequestParam int userId, @RequestParam String searchValue, @RequestParam String classify, @RequestParam String[] gameplayList, @RequestParam String[] gameVersionList, @RequestParam int pageNumber, @RequestParam int rows) {
+        IPage<Resource> page = new Page<>(pageNumber, rows);
+        QueryWrapper<Resource> resourceWrapper = new QueryWrapper<>();
+        // 搜索名称字段不为空时,根据搜索名称进行模糊匹配,并将该次搜索存到搜索历史表
+        if (!Objects.equals(searchValue, "")) {
+            resourceWrapper.like("resource_name", searchValue);
+            SearchHistory searchHistory = new SearchHistory();
+            searchHistory.setSearchContent(searchValue);
+            searchHistory.setUserId(userId);
+            searchHistoryService.save(searchHistory);
+        }
+        // 分类字段不为空时,根据类别筛选
+        if (!Objects.equals(classify, "")) {
+            resourceWrapper.eq("classify", classify);
+        }
+
+        // 主要玩法列表不为空时,根据主要玩法列表筛选(用户传入一个玩法列表,需要去 gameplay 表中查询该资源的所有玩法列表,只有当用户传入的玩法列表的每个值都在资源实际有的玩法列表中时,该资源才符合条件)
+        if (gameplayList.length > 0) {
+            // 该子查询统计了用户传入的玩法列表中有多少玩法是在资源的实际玩法列表中有的
+            String subQuery = "SELECT COUNT(*) " +
+                    "FROM gameplay gp " +
+                    "WHERE gp.resource_id = resource.resource_id " +
+                    "AND gp.gameplay_name IN (" +
+                    String.join(",", Arrays.stream(gameplayList).map(name -> "'" + name + "'").toArray(String[]::new)) + ")";
+            // 只有当子查询返回的数量等于用户传入的玩法列表长度时,说明用户传入的玩法列表完全被包含在资源的玩法列表中
+            resourceWrapper.apply("(" + subQuery + ") = " + gameplayList.length);
+        }
+
+        // 游戏版本列表不为空时,根据游戏版本列表筛选
+        if (gameVersionList.length > 0) {
+            // 拼接游戏版本列表的 IN 子句
+            String gameVersionInClause = String.join(",", Arrays.stream(gameVersionList)
+                    .map(v -> "'" + v + "'")
+                    .toArray(String[]::new));
+
+            // 修改 HAVING 条件逻辑,只检查输入版本列表是否是实际版本列表的子集
+            String resourceCondition = "EXISTS ("
+                    + "SELECT 1 "
+                    + "FROM resource_version rv "
+                    + "JOIN game_version gv ON gv.resource_version_id = rv.resource_version_id "
+                    + "WHERE rv.resource_id = resource.resource_id "
+                    + "GROUP BY rv.resource_id "
+                    + "HAVING COUNT(DISTINCT CASE WHEN gv.game_version_name IN (" + gameVersionInClause + ") THEN gv.game_version_name END) = " + gameVersionList.length
+                    + ")";
+
+            // 应用条件
+            resourceWrapper.apply(resourceCondition);
+        }
+
+        IPage<Resource> resourcePage = resourceService.page(page, resourceWrapper);
+        List<Resource> resourceList = resourcePage.getRecords();
+        long total = resourcePage.getTotal();
+        long pages = resourcePage.getPages();
+        long current = resourcePage.getCurrent();
+        long size = resourcePage.getSize();
+
+        List<List<Gameplay>> gameplayLists = new ArrayList<>();
+        for (Resource resource : resourceList) {
+            // 对于每个资源,获取游戏玩法列表
+            QueryWrapper<Gameplay> gameplayQuery = new QueryWrapper<>();
+            gameplayQuery.eq("resource_id", resource.getResourceId());
+            List<Gameplay> allGameplayList = gameplayService.list(gameplayQuery);
+            gameplayLists.add(allGameplayList);
+        }
+
+        GetResourcePageDTO getResourceSearchDTO = new GetResourcePageDTO(resourceList, gameplayLists, total, pages, current, size);
+        return ResponseEntity.ok(getResourceSearchDTO);
+    }
+
+    /**
+     * 获取资源信息
+     *
+     * @param resourceId 资源id
+     * @param userId     用户id
+     * @return 获取资源信息结果
+     */
+    @GetMapping("/info")
+    public ResponseEntity<GetResourceInfoDTO> getResourceInfo(@RequestParam int resourceId, @RequestParam int userId) {
+        // 获取Resource表信息
+        Resource resource = resourceService.getById(resourceId);
+
+        // 获取Gameplay列表
+        List<Gameplay> gameplayList = gameplayService.list(new QueryWrapper<Gameplay>().eq("resource_id", resourceId));
+
+        // 获取ResourceVersion列表
+        List<ResourceVersion> resourceVersionList = resourceVersionService.list(new QueryWrapper<ResourceVersion>().eq("resource_id", resourceId));
+
+        // 获取GameVersion二维列表
+        List<List<GameVersion>> gameVersionLists = new ArrayList<>();
+        for (ResourceVersion resourceVersion : resourceVersionList) {
+            List<GameVersion> gameVersionList = gameVersionService.list(new QueryWrapper<GameVersion>().eq("resource_version_id", resourceVersion.getResourceVersionId()));
+            gameVersionLists.add(gameVersionList);
+        }
+
+        // 获取TorrentRecord二维列表
+        List<List<TorrentRecord>> torrentRecordLists = new ArrayList<>();
+        for (ResourceVersion resourceVersion : resourceVersionList) {
+            List<TorrentRecord> torrentRecordList = torrentRecordService.list(new QueryWrapper<TorrentRecord>().eq("resource_version_id", resourceVersion.getResourceVersionId()));
+            torrentRecordLists.add(torrentRecordList);
+        }
+
+        // 获取用户是否收藏
+        QueryWrapper<UserCollection> userCollectionQuery = new QueryWrapper<>();
+        userCollectionQuery.eq("user_id", userId).eq("resource_id", resourceId);
+        boolean isCollect = userCollectionService.getOne(userCollectionQuery) != null;
+
+        // 获取用户是否点赞
+        QueryWrapper<UserLike> userLikeQuery = new QueryWrapper<>();
+        userLikeQuery.eq("user_id", userId).eq("resource_id", resourceId);
+        boolean isLike = userLikeService.getOne(userLikeQuery) != null;
+
+        // 获取用户是否购买
+        QueryWrapper<UserPurchase> userPurchaseQuery = new QueryWrapper<>();
+        userPurchaseQuery.eq("user_id", userId).eq("resource_id", resourceId);
+        boolean isPurchase = userPurchaseService.getOne(userPurchaseQuery) != null;
+
+        // 获取用户是否上传
+        QueryWrapper<UserUpload> userUploadQuery = new QueryWrapper<>();
+        userUploadQuery.eq("user_id", userId).eq("resource_id", resourceId);
+        boolean isUpload = userUploadService.getOne(userUploadQuery) != null;
+
+        // 获取上传者ID
+        QueryWrapper<UserUpload> findUploaderQuery = new QueryWrapper<>();
+        findUploaderQuery.eq("resource_id", resourceId);
+        int uploaderId = userUploadService.getOne(findUploaderQuery).getUserId();
+
+        GetResourceInfoDTO getResourceInfoDTO = new GetResourceInfoDTO(resource, gameplayList, resourceVersionList, gameVersionLists, torrentRecordLists, isCollect, isLike, isPurchase, isUpload, uploaderId);
+        return ResponseEntity.ok(getResourceInfoDTO);
+    }
+
+    /**
+     * 获取热门资源
+     *
+     * @param classify   资源分类
+     * @param pageNumber 页数
+     * @param rows       行数
+     * @return 获取热门资源结果
+     */
+    @GetMapping("/hot")
+    public ResponseEntity<GetResourcePageDTO> getHotResource(@RequestParam String classify, @RequestParam int pageNumber, @RequestParam int rows) {
+        IPage<Resource> page = new Page<>(pageNumber, rows);
+        QueryWrapper<Resource> resourceQuery = new QueryWrapper<>();
+        resourceQuery.eq("classify", classify);
+        // 动态计算热度并按降序排序
+        resourceQuery.orderByDesc("(downloads * 0.25 + likes * 0.25 + collections * 0.25 + comments * 0.25)");
+        IPage<Resource> resourcePage = resourceService.page(page, resourceQuery);
+        List<Resource> resourceList = resourcePage.getRecords();
+        long total = resourcePage.getTotal();
+        long pages = resourcePage.getPages();
+        long current = resourcePage.getCurrent();
+        long size = resourcePage.getSize();
+
+        List<List<Gameplay>> gameplayLists = new ArrayList<>();
+        for (Resource resource : resourceList) {
+            // 对于每个资源,获取游戏玩法列表
+            QueryWrapper<Gameplay> gameplayQuery = new QueryWrapper<>();
+            gameplayQuery.eq("resource_id", resource.getResourceId());
+            List<Gameplay> allGameplayList = gameplayService.list(gameplayQuery);
+            gameplayLists.add(allGameplayList);
+        }
+
+        GetResourcePageDTO getResourcePageDTO = new GetResourcePageDTO(resourceList, gameplayLists, total, pages, current, size);
+        return ResponseEntity.ok(getResourcePageDTO);
+    }
+
+    /**
+     * 获取热门资源幻灯片数据
+     *
+     * @return 获取用户收藏结果
+     */
+    @GetMapping("/hot/slide")
+    public ResponseEntity<GetResourceHotSlideDTO> getResourceHotSlide() {
+        QueryWrapper<Resource> resourceQuery = new QueryWrapper<>();
+        // 动态计算热度并按降序排序
+        resourceQuery.orderByDesc("(downloads * 0.25 + likes * 0.25 + collections * 0.25 + comments * 0.25)");
+        // 限制返回3条记录
+        resourceQuery.last("LIMIT 3");
+        List<Resource> resourceList = resourceService.list(resourceQuery);
+
+        GetResourceHotSlideDTO getResourceHotSlideDTO = new GetResourceHotSlideDTO(resourceList);
+        return ResponseEntity.ok(getResourceHotSlideDTO);
+    }
+
+    /**
+     * 获取热门资源趋势图
+     *
+     * @return 获取热门资源趋势图结果
+     */
+    @GetMapping("/hot-trend")
+    public ResponseEntity<GetResourceHotTrendDTO> getResourceHotTrend() {
+        List<HotTrend> hotTrendList = hotTrendService.list();
+        GetResourceHotTrendDTO getResourceHotTrendDTO = new GetResourceHotTrendDTO(hotTrendList);
+        return ResponseEntity.ok(getResourceHotTrendDTO);
+    }
+
+    /**
+     * 修改资源信息
+     *
+     * @param putResourceInfoDTO 要修改的资源信息
+     * @return 修改资源信息结果
+     */
+    @PutMapping("/info")
+    public ResponseEntity<String> putResourceInfo(@RequestBody PutResourceInfoDTO putResourceInfoDTO) {
+        // 检查资源名是否重复
+        QueryWrapper<Resource> resourceQuery = new QueryWrapper<>();
+        resourceQuery.ne("resource_id", putResourceInfoDTO.getResourceId()).eq("resource_name", putResourceInfoDTO.getResourceName());
+        Resource resource = resourceService.getOne(resourceQuery);
+
+        if (resource != null) {
+            // 资源名重复
+            logger.warn("Modification attempt failed. Resource name already exists: {}", putResourceInfoDTO.getResourceName());
+            return ResponseEntity.status(411).body("");
+        }
+
+        UpdateWrapper<Resource> resourceUpdate = new UpdateWrapper<>();
+        resourceUpdate.eq("resource_id", putResourceInfoDTO.getResourceId())
+                .set("resource_name", putResourceInfoDTO.getResourceName())
+                .set("resource_picture", putResourceInfoDTO.getResourcePicture())
+                .set("resource_summary", putResourceInfoDTO.getResourceSummary())
+                .set("resource_detail", putResourceInfoDTO.getResourceDetail())
+                .set("price", putResourceInfoDTO.getPrice());
+        resourceService.update(resourceUpdate);
+
+        QueryWrapper<Gameplay> gameplayQuery = new QueryWrapper<>();
+        gameplayQuery.eq("resource_id", putResourceInfoDTO.getResourceId());
+        gameplayService.remove(gameplayQuery);
+
+        for (int i = 0; i < putResourceInfoDTO.getGameplayList().length; i++) {
+            Gameplay gameplay = new Gameplay();
+            gameplay.setGameplayName(putResourceInfoDTO.getGameplayList()[i]);
+            gameplay.setResourceId(putResourceInfoDTO.getResourceId());
+            gameplayService.save(gameplay);
+        }
+
+        return ResponseEntity.ok("");
+    }
 }
\ No newline at end of file