From 109e087bfd55d24d34f45eec9bfb17d2561e3b40 Mon Sep 17 00:00:00 2001 From: hokli <2629910752@qq.com> Date: Fri, 18 Jul 2025 11:58:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=8A=E4=BC=A0=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/AccountBlockAdminService.java | 13 +- .../TencentCosUploadController.java | 17 ++ .../feedback/AccountBlockAdminController.java | 4 +- .../common/utils/FileTypeDetector.java | 239 ++++++++++++++++++ .../service/account/AccountBlockService.java | 8 + 5 files changed, 277 insertions(+), 4 deletions(-) create mode 100644 accompany-base/accompany-core/src/main/java/com/accompany/common/utils/FileTypeDetector.java diff --git a/accompany-admin/accompany-admin-service/src/main/java/com/accompany/admin/service/AccountBlockAdminService.java b/accompany-admin/accompany-admin-service/src/main/java/com/accompany/admin/service/AccountBlockAdminService.java index 65e6d1bee..be72d4d7a 100644 --- a/accompany-admin/accompany-admin-service/src/main/java/com/accompany/admin/service/AccountBlockAdminService.java +++ b/accompany-admin/accompany-admin-service/src/main/java/com/accompany/admin/service/AccountBlockAdminService.java @@ -44,10 +44,11 @@ public class AccountBlockAdminService extends BaseService { * @param blockValue * @return */ - public IPage getAccountBlockList(Integer pageSize, Integer pageNum, Integer type, String blockValue) { + public IPage getAccountBlockList(Integer pageSize, Integer pageNum, Integer type, String blockValue, String blockDesc) { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.lambda().eq(type != null && type > 0, AccountBlock::getBlockType, type) .like(StringUtils.isNotBlank(blockValue), AccountBlock::getBlockValue, blockValue) + .like(StringUtils.isNotEmpty(blockDesc), AccountBlock::getBlockDesc, blockDesc) .orderByDesc(AccountBlock::getCreateTime); IPage page = new Page<>(pageNum, pageSize); return accountBlockService.page(page, wrapper); @@ -70,6 +71,7 @@ public class AccountBlockAdminService extends BaseService { String endBlockTime, String blockDesc, Boolean wallStatus, String adminName, String remark, String blockImgUrl, Byte blockDevice) throws Exception { List list = new ArrayList<>(); + Map accountBlockMap = accountBlockService.mapByBlockValue(blockValueList); blockValueList.stream().forEach(blockValue -> { AccountBlock accountBlock = new AccountBlock(); accountBlock.setBlockValue(blockValue); @@ -83,7 +85,12 @@ public class AccountBlockAdminService extends BaseService { } } accountBlock.setBlockStatus(1); - accountBlock.setBlockDesc(blockDesc); + AccountBlock oldRecord = accountBlockMap.get(blockValue); + if (oldRecord != null) { + accountBlock.setBlockDesc(String.format("%s+%s" , oldRecord.getBlockDesc(), blockDesc)); + } else { + accountBlock.setBlockDesc(blockDesc); + } accountBlock.setBlockStartTime(DateTimeUtil.convertStrToDate(startBlockTime, DateTimeUtil.DEFAULT_DATE_MINUTE_PATTERN)); accountBlock.setBlockEndTime(DateTimeUtil.convertStrToDate(endBlockTime, DateTimeUtil.DEFAULT_DATE_MINUTE_PATTERN)); accountBlock.setSource(BlockStatusEnum.BLOCKING.getValue()); @@ -146,4 +153,6 @@ public class AccountBlockAdminService extends BaseService { } return result; } + + } diff --git a/accompany-admin/accompany-admin-web/src/main/java/com/accompany/admin/controller/TencentCosUploadController.java b/accompany-admin/accompany-admin-web/src/main/java/com/accompany/admin/controller/TencentCosUploadController.java index 90926b9a5..9450158d6 100644 --- a/accompany-admin/accompany-admin-web/src/main/java/com/accompany/admin/controller/TencentCosUploadController.java +++ b/accompany-admin/accompany-admin-web/src/main/java/com/accompany/admin/controller/TencentCosUploadController.java @@ -2,6 +2,8 @@ package com.accompany.admin.controller; import com.accompany.common.result.BusiResult; import com.accompany.common.tencent.cos.TencentCosUploadService; +import com.accompany.common.utils.FileTypeDetector; +import com.alibaba.fastjson.JSONObject; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; @@ -12,6 +14,7 @@ import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import javax.servlet.http.HttpServletRequest; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -45,4 +48,18 @@ public class TencentCosUploadController extends BaseController { String fileUrl = uploadService.uploadByStream(file.getInputStream(), fileName); return BusiResult.success(fileUrl); } + + @SneakyThrows + @PostMapping("/uploadFile") + public BusiResult uploadFileV2(@RequestParam("file") MultipartFile file) { + String fileName = file.getOriginalFilename(); + InputStream inputStream = file.getInputStream(); + String fileType = FileTypeDetector.detectFileType(inputStream); + String fileUrl = uploadService.uploadByStream(inputStream, fileName); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("url", fileUrl); + jsonObject.put("fileName", fileName); + jsonObject.put("fileType", fileType); + return BusiResult.success(jsonObject); + } } \ No newline at end of file diff --git a/accompany-admin/accompany-admin-web/src/main/java/com/accompany/admin/controller/feedback/AccountBlockAdminController.java b/accompany-admin/accompany-admin-web/src/main/java/com/accompany/admin/controller/feedback/AccountBlockAdminController.java index 80441bfff..59839c5da 100644 --- a/accompany-admin/accompany-admin-web/src/main/java/com/accompany/admin/controller/feedback/AccountBlockAdminController.java +++ b/accompany-admin/accompany-admin-web/src/main/java/com/accompany/admin/controller/feedback/AccountBlockAdminController.java @@ -43,8 +43,8 @@ public class AccountBlockAdminController extends BaseController { @ApiOperation("查询封禁列表") @GetMapping(value = "/page") - public BusiResult> getAccountBlockList(Integer pageSize, Integer pageNum, Integer type, String blockValue) { - IPage pageInfo = accountBlockAdminService.getAccountBlockList(pageSize, pageNum, type, blockValue); + public BusiResult> getAccountBlockList(Integer pageSize, Integer pageNum, Integer type, String blockValue, String blockDesc) { + IPage pageInfo = accountBlockAdminService.getAccountBlockList(pageSize, pageNum, type, blockValue, blockDesc); return BusiResult.success(pageInfo); } diff --git a/accompany-base/accompany-core/src/main/java/com/accompany/common/utils/FileTypeDetector.java b/accompany-base/accompany-core/src/main/java/com/accompany/common/utils/FileTypeDetector.java new file mode 100644 index 000000000..b1fb94443 --- /dev/null +++ b/accompany-base/accompany-core/src/main/java/com/accompany/common/utils/FileTypeDetector.java @@ -0,0 +1,239 @@ +package com.accompany.common.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.io.InputStream; + +@Slf4j +public class FileTypeDetector { + + public static String detectFileType(InputStream is) throws IOException { + + try { + // 读取文件头部的字节(通常16-32字节足够) + byte[] header = new byte[32]; + int bytesRead = is.read(header); + + if (bytesRead < 4) { + return "UNKNOWN"; + } + + // 根据魔数判断文件类型 + if (isPng(header)) { + return "PNG"; + } else if (isJpeg(header)) { + return "JPEG"; + } else if (isGif(header)) { + return "GIF"; + } else if (isMp3(header)) { + return "MP3"; + } else if (isMp4(header)) { + return "MP4"; + } else if (isAvi(header)) { + return "AVI"; + } else if (isWebP(header)) { + return "WEBP"; + } else if (isBmp(header)) { + return "BMP"; + } else if (isTiff(header)) { + return "TIFF"; + } + } catch (IOException e) { + log.error("FileTypeDetector.detectFileType, e:{}", e.getMessage(), e); + } + + return "UNKNOWN"; + } + + private static boolean isPng(byte[] header) { + return header[0] == (byte) 0x89 && + header[1] == 'P' && + header[2] == 'N' && + header[3] == 'G'; + } + + private static boolean isJpeg(byte[] header) { + return header[0] == (byte) 0xFF && + header[1] == (byte) 0xD8; + } + + private static boolean isGif(byte[] header) { + return header[0] == 'G' && + header[1] == 'I' && + header[2] == 'F'; + } + + private static boolean isPdf(byte[] header) { + return header[0] == '%' && + header[1] == 'P' && + header[2] == 'D' && + header[3] == 'F'; + } + + /** + * 检测是否为MP3文件 + * MP3文件可能有多种签名格式 + * @param header 文件头部字节数组 + * @return 如果是MP3文件返回true + */ + private static boolean isMp3(byte[] header) { + // 检查ID3标签 (ID3v2) + if (header.length >= 3 && + header[0] == 'I' && header[1] == 'D' && header[2] == '3') { + return true; + } + + // 检查MPEG帧头 (FF E0-FB) + if (header.length >= 2 && + header[0] == (byte) 0xFF && (header[1] & 0xE0) == 0xE0) { + // 检查有效的MPEG版本和层 + byte versionLayer = (byte) ((header[1] & 0x18) >>> 3); + byte layer = (byte) ((header[1] & 0x06) >>> 1); + return versionLayer != 1 && layer != 0; + } + + // 检查ID3v1标签 (文件末尾的"TAG") + // 注意: 这个方法无法检测到ID3v1标签,因为它位于文件末尾 + + return false; + } + + /** + * 检测是否为MP4文件 + * MP4文件基于"ftyp"盒子识别 + * @param header 文件头部字节数组 + * @return 如果是MP4文件返回true + */ + private static boolean isMp4(byte[] header) { + // 需要至少12字节来检测 + if (header.length < 12) { + return false; + } + + // 检查文件大小和"ftyp"标记 + // MP4文件通常以大小(4字节)和"ftyp"(4字节)开始 + // 但有些MP4文件可能在前面有额外的元数据 + + // 查找"ftyp"标记(66 74 79 70) + for (int i = 0; i <= header.length - 8; i++) { + if (header[i] == 'f' && header[i+1] == 't' && + header[i+2] == 'y' && header[i+3] == 'p') { + // 检查常见的MP4子类型 + if (i + 8 < header.length) { + // 检查子类型(如"mp42", "isom", "avc1"等) + String majorBrand = new String(header, i+4, 4); + return majorBrand.equals("mp42") || majorBrand.equals("isom") || + majorBrand.equals("avc1") || majorBrand.equals("iso2") || + majorBrand.equals("M4V ") || majorBrand.equals("M4A "); + } + return true; + } + } + + // 检查一些MP4变体可能以"moov"开头 + if (header[4] == 'm' && header[5] == 'o' && + header[6] == 'o' && header[7] == 'v') { + return true; + } + + return false; + } + + /** + * 检测是否为AVI格式文件 + * AVI文件签名: RIFF....AVI + * @param header 文件头部字节数组(至少需要12字节) + * @return 如果是AVI文件返回true + */ + private static boolean isAvi(byte[] header) { + // 最少需要12字节来识别AVI文件 + if (header.length < 12) { + return false; + } + + // 检查RIFF签名(52 49 46 46) + boolean isRiff = header[0] == 'R' && + header[1] == 'I' && + header[2] == 'F' && + header[3] == 'F'; + + // 检查AVI签名(41 56 49 20 或 41 56 49 58) + boolean isAvi = (header[8] == 'A' && + header[9] == 'V' && + header[10] == 'I' && + (header[11] == ' ' || header[11] == 'X')); + + return isRiff && isAvi; + } + /** + * 检测是否为WebP格式文件 + * WebP签名: RIFF....WEBP + * @param header 文件头部字节数组(至少需要12字节) + * @return 如果是WebP文件返回true + */ + private static boolean isWebP(byte[] header) { + // 最少需要12字节来识别WebP文件 + if (header.length < 12) { + return false; + } + + // 检查RIFF签名(52 49 46 46) + boolean isRiff = header[0] == 'R' && + header[1] == 'I' && + header[2] == 'F' && + header[3] == 'F'; + + // 检查WEBP签名(57 45 42 50) + boolean isWebP = header[8] == 'W' && + header[9] == 'E' && + header[10] == 'B' && + header[11] == 'P'; + + return isRiff && isWebP; + } + /** + * 检测是否为BMP格式文件 + * BMP签名: BM (42 4D) + * @param header 文件头部字节数组(至少需要2字节) + * @return 如果是BMP文件返回true + */ + private static boolean isBmp(byte[] header) { + // 最少需要2字节来识别BMP文件 + if (header.length < 2) { + return false; + } + + // 检查BM签名(42 4D) + return header[0] == 'B' && header[1] == 'M'; + } + + /** + * 检测是否为TIFF格式文件 + * TIFF签名: + * 小端序: II 2A 00 + * 大端序: MM 00 2A + * @param header 文件头部字节数组(至少需要4字节) + * @return 如果是TIFF文件返回true + */ + private static boolean isTiff(byte[] header) { + // 最少需要4字节来识别TIFF文件 + if (header.length < 4) { + return false; + } + + // 检查小端序格式(II 2A 00) + boolean isLittleEndian = header[0] == 'I' && + header[1] == 'I' && + header[2] == 0x2A && + header[3] == 0x00; + + // 检查大端序格式(MM 00 2A) + boolean isBigEndian = header[0] == 'M' && + header[1] == 'M' && + header[2] == 0x00 && + header[3] == 0x2A; + + return isLittleEndian || isBigEndian; + } +} diff --git a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/account/AccountBlockService.java b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/account/AccountBlockService.java index fac1cd420..3d6c1c2d1 100644 --- a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/account/AccountBlockService.java +++ b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/account/AccountBlockService.java @@ -387,4 +387,12 @@ public class AccountBlockService extends ServiceImpl mapByBlockValue(List blockValueList) { + List accountBlocks = baseMapper.selectByIds(blockValueList); + if (CollectionUtils.isEmpty(accountBlocks)) { + return Collections.emptyMap(); + } + return accountBlocks.stream().collect(Collectors.toMap(AccountBlock::getBlockValue, x -> x)); + } }