修改促销功能

Change-Id: Ie7c073982b358af10d5b7d6066cc72c8709ac6fa
diff --git a/src/main/java/com/example/myproject/controller/TorrentController.java b/src/main/java/com/example/myproject/controller/TorrentController.java
index aecfc64..6aeb214 100644
--- a/src/main/java/com/example/myproject/controller/TorrentController.java
+++ b/src/main/java/com/example/myproject/controller/TorrentController.java
@@ -1,323 +1,3 @@
-// package com.example.myproject.controller;
-
-// import com.example.myproject.common.base.PageUtil;
-// import com.example.myproject.entity.TorrentEntity;
-// import com.example.myproject.service.TorrentService;
-// import com.example.myproject.service.PromotionService;
-// import com.example.myproject.dto.param.TorrentParam;
-// import com.example.myproject.dto.vo.TorrentVO;
-// import com.example.myproject.common.base.Result;
-// import com.example.myproject.dto.param.TorrentUploadParam;
-// import com.example.myproject.dto.TorrentUpdateDTO;
-// import com.example.myproject.dto.PromotionCreateDTO;
-// import com.example.myproject.entity.Promotion;
-// import com.example.myproject.service.UserService;
-
-// import io.swagger.v3.oas.annotations.Operation;
-// import io.swagger.v3.oas.annotations.media.Content;
-// import io.swagger.v3.oas.annotations.media.Schema;
-// import io.swagger.v3.oas.annotations.responses.ApiResponse;
-
-// import org.springframework.beans.factory.annotation.Autowired;
-// import org.springframework.http.HttpStatus;
-// import org.springframework.http.ResponseEntity;
-// import org.springframework.validation.annotation.Validated;
-// import org.springframework.web.bind.annotation.*;
-// import org.springframework.web.multipart.MultipartFile;
-
-// import javax.servlet.http.HttpServletRequest;
-// import javax.servlet.http.HttpServletResponse;
-// import java.io.File;
-// import java.io.IOException;
-// import java.util.List;
-
-// import cn.dev33.satoken.annotation.SaCheckLogin;
-// import cn.dev33.satoken.stp.StpUtil;
-
-// @RestController
-// @RequestMapping("/seeds")
-// public class TorrentController {
-
-//     @Autowired
-//     private TorrentService torrentService;
-
-//     @Autowired
-//     private PromotionService promotionService;
-
-//     @Autowired
-//     private UserService userService;
-
-
-//     @SaCheckLogin
-//     @Operation(summary = "种子列表查询", description = "种子列表条件查询-分页-排序")
-//     @ApiResponse(responseCode = "0", description = "操作成功",
-//             content = {@Content(mediaType = "application/json",
-//                     schema = @Schema(implementation = TorrentVO.class))
-//             })
-//     @PostMapping("/list")
-//     public Result list(@RequestBody TorrentParam param) {
-//         // 构建排序和模糊查询条件
-//         param.validOrder(param.getOrderKey(TorrentEntity.class));
-//         param.buildLike();
-
-//         PageUtil.startPage(param);
-
-//         // 查询数据
-//         List<TorrentEntity> list = torrentService.search(param);
-
-//         // 返回分页结果
-//         return Result.ok(list, PageUtil.getPage(list));
-//     }
-
-//     @SaCheckLogin
-//     @Operation(summary = "种子详情查询")
-//     @ApiResponse(responseCode = "0", description = "操作成功", content = {
-//             @Content(mediaType = "application/json", schema = @Schema(implementation =
-//                     TorrentEntity.class))
-//     })
-//     @PostMapping("/info/{id}")
-//     public Result info(@PathVariable("id")Long id) {
-
-//         TorrentEntity entity = torrentService.selectBySeedId(id);
-//         return Result.ok(entity);
-//     }
-
-// @Operation(summary = "上传种子")
-
-//     @PostMapping("/upload")
-//     public Result uploadTorrent(
-//             @RequestParam("file") MultipartFile file,
-//             @RequestParam("coverImage") MultipartFile coverImage,
-//             @ModelAttribute @Validated TorrentUploadParam param,
-//             HttpServletRequest request
-//             ) throws IOException {
-//         try {
-//             // 验证用户权限
-//             // Long userId = StpUtil.getLoginIdAsLong();
-//             String userId = String.valueOf(param.getUploader());
-//             param.setUploader(userId);
-
-//             // 验证文件大小和类型
-//             if (file.isEmpty() || file.getSize() > 10 * 1024 * 1024) { // 10MB限制
-//                 return Result.error("文件大小不符合要求");
-//             }
-
-//             if (!file.getOriginalFilename().toLowerCase().endsWith(".torrent")) {
-//                 return Result.error("只支持.torrent文件");
-//             }
-//             if (!coverImage.isEmpty()) {
-//                 String originalFilename = coverImage.getOriginalFilename();
-//                 String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
-//                 String fileName = System.currentTimeMillis() + suffix;
-
-//                 if (!originalFilename.toLowerCase().matches(".*\\.(jpg|jpeg|png)$")) {
-//                     return Result.error("仅支持 JPG/PNG 格式的封面图片");
-//                 }
-
-
-//                 // 项目根目录下的 /uploads/torrents/
-//                 String uploadDir = System.getProperty("user.dir") + "/uploads/torrents/";
-//                 File dir = new File(uploadDir);
-//                 if (!dir.exists()) dir.mkdirs(); // 自动创建目录
-
-//                 File dest = new File(uploadDir + fileName);
-//                 coverImage.transferTo(dest);
-
-
-//                 String imageUrl = request.getScheme() + "://" + request.getServerName() + ":" +
-//                         request.getServerPort() + "/torrent-images/" + fileName;
-
-
-//                 param.setImageUrl(imageUrl);
-//             }
-
-//             torrentService.uploadTorrent(file, param);
-//             return Result.ok();
-//         } catch (Exception e) {
-//             return Result.error("种子上传失败: " + e.getMessage());
-//         }
-//     }
-
-//     /**
-//      * 获取种子文件
-//      */
-//     @GetMapping("/{seed_id}/download")
-//     public void downloadTorrent(
-//             @PathVariable("seed_id") Long seedId,
-//             @RequestParam("passkey") String passkey,
-//             HttpServletResponse response) throws IOException {
-
-//         // 获取种子实体
-//         TorrentEntity entity = torrentService.selectBySeedId(seedId);
-//         if (entity == null) {
-//             response.sendError(HttpServletResponse.SC_NOT_FOUND, "种子不存在");
-//             return;
-//         }
-
-//         // 获取并处理种子文件内容(需在service中实现passkey注入)
-//         byte[] torrentBytes = torrentService.fetch(seedId, passkey);
-
-//         // 设置下载文件名
-//         String filename = entity.getFileName();
-//         if (filename == null || filename.isBlank()) {
-//             filename = seedId + ".torrent";
-//         }
-//         if (!filename.toLowerCase().endsWith(".torrent")) {
-//             filename = filename + ".torrent";
-//         }
-//         filename = java.net.URLEncoder.encode(filename, java.nio.charset.StandardCharsets.UTF_8).replaceAll("\\+",
-//                 "%20");
-
-//         // 设置响应头
-//         response.setCharacterEncoding(java.nio.charset.StandardCharsets.UTF_8.name());
-//         response.setContentLength(torrentBytes.length);
-//         response.setContentType("application/x-bittorrent");
-//         response.setHeader("Content-Disposition", "attachment;filename=" + filename);
-
-//         // 写入文件内容
-//         response.getOutputStream().write(torrentBytes);
-//         response.getOutputStream().flush();
-//     }
-
-//     /**
-//      * 收藏或者取消收藏
-//      */
-//     @PostMapping("/{seed_id}/favorite-toggle")
-//     public Result favorite(
-//             @PathVariable("seed_id") Long seedId,
-//             @RequestParam("user_id") Long  userId) {
-//         try {
-
-//             return  torrentService.favorite(seedId, userId);
-//         } catch (Exception e) {
-//             return Result.error("失败: ");
-//         }
-//     }
-
-//     @SaCheckLogin
-//     @Operation(summary = "删除种子")
-//     @DeleteMapping("/{torrentId}")
-//     public Result deleteTorrent(@PathVariable Long torrentId) {
-//         try {
-//            //  验证用户权限
-//             Long userId = StpUtil.getLoginIdAsLong();
-//             if (!torrentService.canUserDeleteTorrent(torrentId, userId)) {
-//                 return Result.error("没有权限删除此种子");
-//             }
-
-//             torrentService.deleteTorrent(torrentId);
-//             return Result.ok();
-//         } catch (Exception e) {
-//             return Result.error("删除失败: " + e.getMessage());
-//         }
-//     }
-
-//     @SaCheckLogin
-//     @Operation(summary = "修改种子信息")
-//     @PutMapping("/{torrentId}")
-//     public Result updateTorrent(
-//             @PathVariable Long torrentId,
-//             @RequestBody @Validated TorrentUpdateDTO updateDTO) {
-//         try {
-//             // 验证用户权限
-//             Long userId = StpUtil.getLoginIdAsLong();
-//             if (!torrentService.canUserUpdateTorrent(torrentId, userId)) {
-//                 return Result.error("没有权限修改此种子");
-//             }
-
-//             torrentService.updateTorrent(torrentId, updateDTO);
-//             return Result.ok();
-//         } catch (Exception e) {
-//             return Result.error("更新失败: " + e.getMessage());
-//         }
-//     }
-
-//     @SaCheckLogin
-//     @Operation(summary = "创建促销活动")
-//     @PostMapping("/promotions")
-//     public Result createPromotion(@RequestBody @Validated PromotionCreateDTO promotionDTO) {
-//         try {
-//             // 验证用户权限(只有管理员可以创建促销)
-// //            if (!StpUtil.hasRole("admin")) {
-// //                return Result.error("没有权限创建促销活动");
-// //            }
-// //
-//             Promotion promotion = promotionService.createPromotion(promotionDTO);
-//             return Result.ok(promotion);
-//         } catch (Exception e) {
-//             return Result.error("创建促销失败: " + e.getMessage());
-//         }
-//     }
-
-//     @SaCheckLogin
-//     @Operation(summary = "获取促销活动列表")
-//     @GetMapping("/promotions")
-//     public Result getPromotions() {
-//         try {
-//             List<Promotion> promotions = promotionService.getAllActivePromotions();
-//             return Result.ok(promotions);
-//         } catch (Exception e) {
-//             return Result.error("获取促销列表失败: " + e.getMessage());
-//         }
-//     }
-
-//     @SaCheckLogin
-//     @Operation(summary = "获取促销详情")
-//     @GetMapping("/promotions/{promotionId}")
-//     public Result getPromotionDetails(@PathVariable Long promotionId) {
-//         try {
-//             Promotion promotion = promotionService.getPromotionById(promotionId);
-//             if (promotion == null) {
-//                 return Result.error("促销活动不存在");
-//             }
-//             return Result.ok(promotion);
-//         } catch (Exception e) {
-//             return Result.error("获取促销详情失败: " + e.getMessage());
-//         }
-//     }
-
-//     @SaCheckLogin
-//     @Operation(summary = "删除促销活动")
-//     @DeleteMapping("/promotions/{promotionId}")
-//     public Result deletePromotion(@PathVariable Long promotionId) {
-//         try {
-//             // 验证用户权限(只有管理员可以删除促销)
-//             if (!StpUtil.hasRole("admin")) {
-//                 return Result.error("没有权限删除促销活动");
-//             }
-
-//             promotionService.deletePromotion(promotionId);
-//             return Result.ok();
-//         } catch (Exception e) {
-//             return Result.error("删除促销失败: " + e.getMessage());
-//         }
-//     }
-
-//     // 下载种子(包含反作弊机制)
-//     @PostMapping("/{torrentId}/download")
-//     public ResponseEntity<?> downloadTorrent(@PathVariable Long torrentId,
-//                                            @RequestParam Long userId) {
-// //        // 验证用户身份和权限
-// //        if (!userService.validateUser(userId)) {
-// //            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
-// //        }
-
-//         // 检查用户上传量是否足够
-//         if (!torrentService.checkUserUploadRatio(userId)) {
-//             return ResponseEntity.status(HttpStatus.FORBIDDEN)
-//                 .body("上传量不足,无法下载");
-//         }
-
-//         // 应用促销折扣(如果有)
-//         double downloadSize = torrentService.calculateDownloadSize(torrentId, userId);
-
-//         // 记录下载
-//         torrentService.recordDownload(torrentId, userId, downloadSize);
-
-//         return ResponseEntity.ok().build();
-//     }
-
-// }
 package com.example.myproject.controller;
 
 import cn.hutool.core.util.StrUtil;
@@ -336,6 +16,7 @@
 import com.example.myproject.dto.PromotionCreateDTO;
 import com.example.myproject.entity.Promotion;
 import com.example.myproject.service.UserService;
+import com.example.myproject.repository.UserRepository;
 
 import com.turn.ttorrent.bcodec.BEValue;
 import com.turn.ttorrent.bcodec.BEncoder;
@@ -363,6 +44,9 @@
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.*;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
 
 import cn.dev33.satoken.annotation.SaCheckLogin;
 import cn.dev33.satoken.stp.StpUtil;
@@ -383,6 +67,8 @@
     private UserService userService;
     private final List<BEValue> peers = new Vector<>();
 
+    @Autowired
+    private UserRepository userRepository;
 
     @SaCheckLogin
     @Operation(summary = "种子列表查询", description = "种子列表条件查询-分页-排序")
@@ -425,7 +111,7 @@
             @RequestParam("coverImage") MultipartFile coverImage,
             @ModelAttribute @Validated TorrentUploadParam param,
             HttpServletRequest request
-            ) throws IOException {
+    ) throws IOException {
         try {
             // 验证用户权限
             // Long userId = StpUtil.getLoginIdAsLong();
@@ -535,7 +221,7 @@
     @DeleteMapping("/{torrentId}")
     public Result deleteTorrent(@PathVariable Long torrentId) {
         try {
-           //  验证用户权限
+            //  验证用户权限
             Long userId = StpUtil.getLoginIdAsLong();
             if (!torrentService.canUserDeleteTorrent(torrentId, userId)) {
                 return Result.error("没有权限删除此种子");
@@ -617,8 +303,10 @@
     @DeleteMapping("/promotions/{promotionId}")
     public Result deletePromotion(@PathVariable Long promotionId) {
         try {
-            // 验证用户权限(只有管理员可以删除促销)
-            if (!StpUtil.hasRole("admin")) {
+            Long userId = StpUtil.getLoginIdAsLong();
+            // 查询用户
+            var userOpt = userRepository.findById(userId);
+            if (userOpt.isEmpty() || !"admin".equals(userOpt.get().getRole())) {
                 return Result.error("没有权限删除促销活动");
             }
 
@@ -630,6 +318,7 @@
     }
     @GetMapping("/announce")
     public ResponseEntity<byte[]> announce(TrackerProtocol trackerProtocol, HttpServletRequest request, @RequestParam(value = "info_hash") String encodedInfoHash){
+        log.info("client report: {}", trackerProtocol);
         HashMap<String, BEValue> map = new HashMap<>();
         if (StrUtil.isBlank(trackerProtocol.getIp())) {
             trackerProtocol.setIp(request.getRemoteAddr());
diff --git a/src/main/java/com/example/myproject/mapper/PromotionMapper.java b/src/main/java/com/example/myproject/mapper/PromotionMapper.java
index a478835..b6079b3 100644
--- a/src/main/java/com/example/myproject/mapper/PromotionMapper.java
+++ b/src/main/java/com/example/myproject/mapper/PromotionMapper.java
@@ -44,6 +44,11 @@
      * 根据ID更新促销活动(例如软删除)
      */
     int updateById(Promotion promotion);
+    /**
+     * 根据ID更新促销活动(例如软删除)
+     */
+    @Update("update promotion set is_deleted = 1 WHERE id = #{id}")
+    int updateStatusById(@Param("id") Long id);
 
     /**
      * 根据ID查询促销活动
diff --git a/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java b/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java
index 9d34cbc..5da10e5 100644
--- a/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java
+++ b/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java
@@ -4,6 +4,8 @@
 import com.example.myproject.mapper.PromotionMapper;
 import com.example.myproject.service.PromotionService;
 import com.example.myproject.dto.PromotionCreateDTO;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -39,15 +41,18 @@
         promotion.setEndTime(promotionDTO.getEndTime());
         promotion.setDiscountPercentage(promotionDTO.getDiscountPercentage());
 
-        // 把List<Long>转换成逗号分隔字符串
-        String applicableTorrentIdsStr = promotionDTO.getApplicableTorrentIds().stream()
-                .map(String::valueOf)
-                .collect(Collectors.joining(","));
-        promotion.setApplicableTorrentIds(applicableTorrentIdsStr);
+        // 把List<Long>转换成JSON字符串
+        ObjectMapper objectMapper = new ObjectMapper();
+        try {
+            String applicableTorrentIdsJson = objectMapper.writeValueAsString(promotionDTO.getApplicableTorrentIds());
+            promotion.setApplicableTorrentIds(applicableTorrentIdsJson);
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException("适用种子ID序列化失败", e);
+        }
 
         promotion.setCreateTime(now);
         promotion.setUpdateTime(now);
-        promotion.setIsDeleted(false);
+        promotion.setIsDeleted(true);
 
         promotionMapper.insert(promotion);
         return promotion;
@@ -77,9 +82,8 @@
         }
 
         // 软删除
-        promotion.setIsDeleted(true);
         promotion.setUpdateTime(LocalDateTime.now());
-        promotionMapper.updateById(promotion);
+        promotionMapper.updateStatusById(promotion.getId());
     }
 
     @Override