ソースを参照

feat(tournaments): 添加赛事发布功能和图片压缩上传

- 新增 UN_PUBLISHED 状态枚举值(-1, "未发布")
- 添加 publishToTournament 接口用于发布赛事
- 实现赛事发布控制器方法和数据访问层更新
- 添加图片上传时的压缩功能,支持 JPEG 格式压缩
- 优化赛事编辑时的状态检查逻辑
- 添加 spring-boot-starter-test 测试依赖
fugui001 3 日 前
コミット
468a21eabd

+ 5 - 1
ruoyi-modules/ruoyi-system/pom.xml

@@ -104,7 +104,11 @@
             <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
             <version>1.1.0</version>
         </dependency>
-
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>com.aliyun</groupId>
             <artifactId>aliyun-java-sdk-core</artifactId>

+ 6 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/controller/TournamentsController.java

@@ -214,4 +214,10 @@ public class TournamentsController extends BaseController {
         ExcelUtil.exportExcel(list, "【赛事统计-导出】", TournamentsExportVo.class, response);
     }
 
+    @GetMapping("/publishToTournament/{id}")
+    public R<Void> publishToTournament(@NotNull(message = "主键不能为空")
+                                       @PathVariable Long id) {
+        return toAjax(tournamentsService.publishToTournament(id));
+    }
+
 }

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/domain/enums/GameStatus.java

@@ -4,7 +4,7 @@ import lombok.Getter;
 
 @Getter
 public enum GameStatus {
-
+    UN_PUBLISHED(-1, "未发布"),
     NOT_STARTED(0, "未开始"),
     RUNNING(1, "进行中"),
     HAND_FOR_HAND(2, "同步发牌"),

+ 3 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/mapper/TournamentsMapper.java

@@ -34,6 +34,9 @@ public interface TournamentsMapper extends BaseMapperPlus<Tournaments, Tournamen
     @InterceptorIgnore(tenantLine = "true")
     int updateTournamentsById(Tournaments update);
 
+    @InterceptorIgnore(tenantLine = "true")
+    int updateTournamentsBeginById(Tournaments update);
+
     @InterceptorIgnore(tenantLine = "true")
     int insertTournament(Tournaments insert);
 

+ 6 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/service/ITournamentsService.java

@@ -156,5 +156,11 @@ public interface ITournamentsService {
      */
     List<TournamentsExportVo> exportStatisticsPageList(TournamentsBo bo);
 
+    /**
+     * 发布赛事
+     * @param tournamentId
+     * @return
+     */
+    Boolean publishToTournament(@Param("tournamentId") Long tournamentId);
 
 }

+ 116 - 6
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/service/impl/TournamentsServiceImpl.java

@@ -28,12 +28,23 @@ import org.dromara.system.domain.vo.SysOssVo;
 import org.dromara.system.service.ISysOssService;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mock.web.MockMultipartFile;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.dromara.business.domain.bo.TournamentsBo;
 import org.dromara.business.service.ITournamentsService;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.stream.ImageOutputStream;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.text.ParseException;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -41,9 +52,9 @@ import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatterBuilder;
 import java.time.temporal.ChronoField;
 import java.util.*;
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.stream.Collectors;
-
 /**
  * 【请填写功能名称】Service业务层处理
  *
@@ -405,6 +416,7 @@ public class TournamentsServiceImpl implements ITournamentsService {
                 log.error("奖励列表为空");
                 return false;
             }
+            add.setStatus(Long.valueOf(GameStatus.UN_PUBLISHED.getCode()));
             Long userId = LoginHelper.getUserId();
             add.setCreateUserId(userId);
             // 处理开始时间
@@ -582,8 +594,8 @@ public class TournamentsServiceImpl implements ITournamentsService {
             //增加已经报名限制
 
             TournamentsVo tournamentsVo = baseMapper.selectVoByIdInfo(bo.getId());
-            if(tournamentsVo!=null && tournamentsVo.getStatus()!=0){
-                throw new RuntimeException("本场赛事已经开始!禁止编辑调整!");
+            if(tournamentsVo != null && (tournamentsVo.getStatus() != 0 || tournamentsVo.getStatus() != -1)){
+                throw new RuntimeException("本场赛事已经开始或已关闭!禁止编辑调整!");
             }
 
          /*   int selectParticipantsTotal = participantsMapper.selectParticipantsInfoTotal(bo.getId());
@@ -691,15 +703,101 @@ public class TournamentsServiceImpl implements ITournamentsService {
         }
     }
 
+    /**
+     * 上传赛事图片(支持图片压缩)
+     */
+    /**
+     * 上传赛事图片(支持图片压缩)
+     */
     @Override
     public SysOssVo uploadTournament(MultipartFile file) {
         log.info("uploadTournament开始上传文件: {}, 大小: {} bytes",
             file.getOriginalFilename(), file.getSize());
-        // 1. 上传文件到 OSS
-        SysOssVo sysOssVo = ossService.uploadInputstream(file);
-        return sysOssVo;
+
+        try {
+            // 判断是否为图片文件
+            if (isImageFile(file)) {
+                // 压缩图片并获取压缩后的字节数组
+                byte[] compressedBytes = compressImageToBytes(file);
+
+                // 创建一个 ByteArrayInputStream,用于上传
+                ByteArrayInputStream inputStream = new ByteArrayInputStream(compressedBytes);
+
+                // 创建一个新的 MultipartFile
+                MultipartFile compressedFile = new MockMultipartFile(
+                    file.getOriginalFilename(),
+                    file.getOriginalFilename(),
+                    file.getContentType(),
+                    inputStream
+                );
+
+                // 使用压缩后的流上传
+                SysOssVo sysOssVo = ossService.uploadInputstream(compressedFile);
+                return sysOssVo;
+            } else {
+                // 非图片文件直接上传
+                SysOssVo sysOssVo = ossService.uploadInputstream(file);
+                return sysOssVo;
+            }
+        } catch (Exception e) {
+            log.error("文件上传失败", e);
+            throw new RuntimeException("文件上传失败:" + e.getMessage());
+        }
+    }
+    /**
+     * 判断是否为图片文件
+     */
+    private boolean isImageFile(MultipartFile file) {
+        String contentType = file.getContentType();
+        return contentType != null && contentType.startsWith("image/");
     }
+    /**
+     * 压缩图片
+     */
+    /**
+     * 压缩图片为字节数组
+     */
+    private byte[] compressImageToBytes(MultipartFile originalFile) throws IOException {
+        BufferedImage originalImage = ImageIO.read(originalFile.getInputStream());
+
+        // 设置压缩参数
+        int width = originalImage.getWidth();
+        int height = originalImage.getHeight();
+
+        // 按比例缩放,最大不超过 1920x1080
+        double scale = Math.min(1920.0 / width, 1080.0 / height);
+        if (scale < 1.0) {
+            width = (int) (width * scale);
+            height = (int) (height * scale);
+        }
+
+        // 创建缩放后的图片
+        BufferedImage scaledImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        Graphics2D g2d = scaledImage.createGraphics();
+        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+        g2d.drawImage(originalImage, 0, 0, width, height, null);
+        g2d.dispose();
+
+        // 转换为字节数组
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        // 使用JPEG格式,质量为0.8(可调整)
+        ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
+        ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
+        writer.setOutput(ios);
+
+        ImageWriteParam param = writer.getDefaultWriteParam();
+        if (param.canWriteCompressed()) {
+            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+            param.setCompressionQuality(0.7f); // 压缩质量,0.0-1.0之间,降低质量以进一步减小文件大小
+        }
 
+        writer.write(null, new IIOImage(scaledImage, null, null), param);
+        writer.dispose();
+        ios.close();
+
+        // 返回压缩后的字节数组
+        return baos.toByteArray();
+    }
     @Override
     public Boolean deleteTournament(Long id) {
         TournamentsVo tournamentsVo = baseMapper.selectVoByIdInfo(id);
@@ -1056,6 +1154,18 @@ public class TournamentsServiceImpl implements ITournamentsService {
         return exportVoList;
     }
 
+    @Override
+    public Boolean publishToTournament(Long tournamentId) {
+        Tournaments update = new Tournaments();
+        update.setId(tournamentId);
+        update.setStatus(0L);
+        boolean flag = baseMapper.updateTournamentsBeginById(update) > 0;
+        if (!flag) {
+            throw new RuntimeException("赛事修改失败");
+        }
+        return true;
+    }
+
 
     private Date parseDateTime(String dateTimeStr) throws ParseException {
         if (dateTimeStr == null || dateTimeStr.isBlank()) {

+ 3 - 2
ruoyi-modules/ruoyi-system/src/main/resources/mapper/business/TournamentsMapper.xml

@@ -16,8 +16,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectVoByIdInfo" resultType="org.dromara.business.domain.vo.TournamentsVo">
        SELECT id, name, start_time, end_time,game_type, starting_chips, level_duration, late_registration_level, max_players, status, created_at, updated_at,sign_time,competition_icon,tournaments_bi_id,robot_count,is_delete,delay_card_time,delay_card_num,action_time,competition_bg,min_players,game_variant,target_tournament_id,qualifier_value,qualifier_type,start_blind_level,rebuy,delay_show,remark FROM tournaments WHERE id =  #{id}
     </select>
-
-
+    <update id="updateTournamentsBeginById">
+       UPDATE tournaments SET status = #{status} WHERE id = #{id}
+    </update>
     <update id="updateTournamentsById">
         UPDATE tournaments
         <set>