src/main/java/com/ruoyi/basic/mapper/StorageAttachmentMapper.java
@@ -2,7 +2,6 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.basic.pojo.StorageAttachment; import org.apache.ibatis.annotations.Mapper; /** * <p> @@ -12,7 +11,6 @@ * @author ruoyi * @since 2025-05-29 */ @Mapper public interface StorageAttachmentMapper extends BaseMapper<StorageAttachment> { } src/main/java/com/ruoyi/basic/pojo/StorageAttachment.java
@@ -36,6 +36,9 @@ @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; @TableField(fill = FieldFill.INSERT) private Long tenantId; /** * é»è¾å é¤ */ @@ -62,6 +65,7 @@ @TableField(value = "storage_blob_id") private Long storageBlobId; @TableField(exist = false) private StorageBlobDTO storageBlobDTO; public StorageAttachment(String fileType, Long recordType, Long recordId) { src/main/java/com/ruoyi/basic/pojo/StorageBlob.java
@@ -1,13 +1,13 @@ package com.ruoyi.basic.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.*; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.io.Serializable; import java.time.LocalDateTime; import java.util.Date; /** @@ -28,16 +28,11 @@ @TableId(value = "id", type = IdType.AUTO) private Long id; /** å建æ¶é´ */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") // @TableField(fill = FieldFill.INSERT) private Date createTime; /** * èµæºid */ @TableField(value = "key") private String key; @TableField(value = "resource_key") private String resourceKey; /** * èµæºç±»åï¼ä¾å¦JPGå¾ççèµæºç±»å为image/jpg */ @@ -70,4 +65,28 @@ */ @TableField(value = "type") private Long type; /** * ç§æ·ID */ @TableField(fill = FieldFill.INSERT) private Long tenantId; @ApiModelProperty(value = "å建该记å½çç¨æ·") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT) private Integer createUser; @ApiModelProperty(value = "è®°å½å建æ¶é´") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; @ApiModelProperty(value = "æåä¿®æ¹è¯¥è®°å½çç¨æ·") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT_UPDATE) private Integer updateUser; @ApiModelProperty(value = "è®°å½æåæ´æ°æ¶é´") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; } src/main/java/com/ruoyi/basic/service/StorageBlobService.java
@@ -26,6 +26,9 @@ */ List<StorageBlobDTO> updateStorageBlobs(List<MultipartFile> files, String bucketName); List<StorageBlobDTO> updateStorageBlobs(List<MultipartFile> files, String bucketName,Long type); /** * æ¹éå 餿件 * @param attachment src/main/java/com/ruoyi/basic/service/impl/StorageAttachmentServiceImpl.java
@@ -80,11 +80,11 @@ @Override public void saveStorageAttachment(List<StorageAttachment> attachments, Long recordId, StorageAttachmentRecordType recordType, String fileType) { // å 餿§å¾ deleteStorageAttachment(new StorageAttachment(fileType.toString(), (long) recordType.ordinal(), recordId)); deleteStorageAttachment(new StorageAttachment(fileType, (long) recordType.ordinal(), recordId)); for (StorageAttachment attachment : attachments) { // è·åå ³èè®°å½ StorageBlob storageBlob = attachment.getStorageBlobDTO(); attachment.setName(fileType.toString()); attachment.setName(fileType); attachment.setRecordType((long) recordType.ordinal()); attachment.setRecordId(recordId); attachment.setStorageBlobId(storageBlob.getId()); src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java
@@ -9,6 +9,7 @@ import com.ruoyi.basic.pojo.StorageAttachment; import com.ruoyi.basic.pojo.StorageBlob; import com.ruoyi.basic.service.StorageBlobService; import com.ruoyi.common.exception.base.BaseException; import com.ruoyi.common.exception.file.InvalidExtensionException; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.MinioUtils; @@ -16,11 +17,15 @@ import com.ruoyi.common.utils.uuid.IdUtils; import com.ruoyi.framework.web.domain.MinioResult; import lombok.RequiredArgsConstructor; import org.apache.commons.io.FilenameUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -61,9 +66,8 @@ dto.setBucketFilename(res.getBucketFileName()); dto.setOriginalFilename(res.getOriginalName()); dto.setByteSize(file.getSize()); dto.setKey(IdUtils.simpleUUID()); dto.setResourceKey(IdUtils.simpleUUID()); dto.setBucketName(bucketName); dto.setCreateTime(DateUtils.getNowDate()); dto.setUrl(minioUtils.getPreviewUrl(res.getBucketFileName(), bucketName, false)); // æå ¥æ°æ®åº storageBlobMapper.insert(dto); @@ -79,12 +83,95 @@ } @Override public List<StorageBlobDTO> updateStorageBlobs(List<MultipartFile> files, String bucketName, Long type) { // è¥æ²¡ä¼ å ¥bucketNameï¼å使ç¨é»è®¤bucketName if (StringUtils.isEmpty(bucketName)) { bucketName = minioUtils.getDefaultBucket(); } List<StorageBlobDTO> storageBlobDTOs = new ArrayList<>(); for (MultipartFile file : files) { try { validateFileExtension(file); MinioResult res = minioUtils.upload(bucketName, file, false); StorageBlobDTO dto = buildStorageBlobDTO(file, res, bucketName, type); storageBlobMapper.insert(dto); storageBlobDTOs.add(dto); } catch (InvalidExtensionException e) { throw new RuntimeException("䏿¯æçæä»¶ç±»åï¼" + file.getOriginalFilename(), e); } catch (Exception e) { throw new RuntimeException("ä¸ä¼ æä»¶å¤±è´¥ï¼" + file.getOriginalFilename(), e); } } return storageBlobDTOs; } private StorageBlobDTO buildStorageBlobDTO(MultipartFile file, MinioResult res, String bucketName, Long type) { StorageBlobDTO dto = new StorageBlobDTO(); dto.setContentType(file.getContentType()); dto.setBucketFilename(res.getBucketFileName()); dto.setOriginalFilename(res.getOriginalName()); dto.setByteSize(file.getSize()); dto.setResourceKey(IdUtils.simpleUUID()); dto.setBucketName(bucketName); dto.setUrl(minioUtils.getPreviewUrl(res.getBucketFileName(), bucketName, false)); dto.setDownloadUrl(minioUtils.getDownloadUrl(res.getBucketFileName(), bucketName)); if (type != null) { dto.setType(type); } return dto; } private void validateFileExtension(MultipartFile file) throws InvalidExtensionException { String filename = file.getOriginalFilename(); String extension = FilenameUtils.getExtension(filename).toLowerCase(); List<String> allowedExtensions = Arrays.asList( // å¾ç "jpg", "jpeg", "png", "gif", "bmp", "webp", "tiff", "ico", "svg", // ææ¡£ "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "rtf", "md", "csv", "odt", // è§é¢ "mp4", "mov", "avi", "wmv", "flv", "mkv", "webm", "mpeg", "3gp", "MOV", // é³é¢ "mp3", "wav", "ogg", "aac", "flac", "m4a", "wma", "amr", // å缩å "zip", "rar", "7z", "tar", "gz", "bz2", "xz", // ç¼ç¨ä»£ç æä»¶ "java", "py", "js", "ts", "html", "css", "cpp", "c", "cs", "json", "xml", "sql", "yaml", "yml", "sh", "bat", // å®è£ ç¨åº & äºè¿å¶ "exe", "apk", "dmg", "msi", "bin", "iso", // 设计类 "psd", "ai", "xd", "sketch", "fig" ); if (!allowedExtensions.contains(extension)) { throw new BaseException("æä»¶ç±»åä¸è¢«å 许ï¼" + extension); } } @Override public int deleteStorageBlobs(StorageAttachment attachment) { List<StorageAttachment> attachments = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>() .eq(StorageAttachment::getRecordId, attachment.getRecordId()) .eq(StorageAttachment::getRecordType, attachment.getRecordType()) .eq(StorageAttachment::getName, attachment.getName())); List<Long> ids = attachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList()); if(CollectionUtils.isEmpty(ids)){ return 0; } List<StorageBlob> storageBlobs = storageBlobMapper.selectList(new LambdaQueryWrapper<StorageBlob>() .in(StorageBlob::getId, ids)); if (!storageBlobs.isEmpty()) { src/main/java/com/ruoyi/common/utils/MinioUtils.java
@@ -386,4 +386,48 @@ throw new UtilException("çæä¸è½½URL失败: " + e.getMessage(), e); } } public String getDownloadUrl(String bucketFileName, String bucketName) { if (StringUtils.isNotBlank(bucketFileName)) { try { // æ£æ¥æä»¶æ¯å¦åå¨ minioClient.statObject(StatObjectArgs.builder() .bucket(bucketName) .object(bucketFileName) .build()); // 设置ååºå¤´ Map<String, String> reqParams = new HashMap<>(); // æååå§æä»¶åï¼å¦æå卿¶ä¿çäºåå§åç§°ï¼ String originalFileName = extractOriginalFileName(bucketFileName); reqParams.put("response-content-disposition", "attachment; filename=\"" + URLEncoder.encode(originalFileName, String.valueOf(StandardCharsets.UTF_8)) + "\""); // æå»ºé¢ç¾åURLåæ° GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder() .method(Method.GET) .bucket(bucketName) .object(bucketFileName) .expiry(previewExpiry, TimeUnit.HOURS) .extraQueryParams(reqParams) .build(); return minioClient.getPresignedObjectUrl(args); } catch (Exception e) { throw new UtilException("MinioUtilsï¼çæä¸è½½é¾æ¥å¼å¸¸", e); } } return null; } private String extractOriginalFileName(String bucketFileName) { // 示ä¾ï¼å¦æå卿 ¼å¼ä¸º "åå§æä»¶å_UUID" int underscoreIndex = bucketFileName.lastIndexOf("_"); if (underscoreIndex > 0) { return bucketFileName.substring(0, underscoreIndex); } // å¦ææ²¡æç¹æ®æ ¼å¼ï¼ç´æ¥è¿å宿´æä»¶å return bucketFileName; } } src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
@@ -63,8 +63,8 @@ .build() /* 设置å®å ¨æ¨¡å¼ï¼swaggerå¯ä»¥è®¾ç½®è®¿é®token */ .securitySchemes(securitySchemes()) .securityContexts(securityContexts()) .pathMapping(pathMapping); .securityContexts(securityContexts()); // .pathMapping(pathMapping); } /** src/main/java/com/ruoyi/inspectiontask/pojo/QrCodeScanRecord.java
@@ -1,9 +1,6 @@ package com.ruoyi.inspectiontask.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.*; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -40,9 +37,11 @@ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime scanTime; @ApiModelProperty(value = "ç§æ·IDï¼ç¨äºå¤ç§æ·é离") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT) private Integer tenantId; /** * ç§æ·ID */ @TableField(fill = FieldFill.INSERT) private Long tenantId; @ApiModelProperty(value = "软å 餿 å¿ï¼0=æªå é¤ï¼1=å·²å é¤") private Integer deleted; @@ -59,7 +58,7 @@ @ApiModelProperty(value = "æåä¿®æ¹è¯¥è®°å½çç¨æ·") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT_UPDATE) private Integer updateBy; private Integer updateUser; @ApiModelProperty(value = "è®°å½æåæ´æ°æ¶é´") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT_UPDATE) src/main/java/com/ruoyi/project/common/CommonController.java
@@ -5,6 +5,11 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ruoyi.basic.service.StorageBlobService; import com.ruoyi.framework.web.domain.R; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -27,6 +32,7 @@ * * @author ruoyi */ @Api(tags = "éç¨æ¥å£") @RestController @RequestMapping("/common") public class CommonController @@ -69,6 +75,20 @@ } } @Autowired private StorageBlobService storageBlobService; /** * minioéç¨ä¸ä¼ 请æ±ï¼å¤ä¸ªï¼ */ @PostMapping("/minioUploads") @ApiOperation(value = "minioéç¨ä¸ä¼ 请æ±") public AjaxResult minioUploadFiles(List<MultipartFile> files, String bucketName, Long type) throws Exception { return AjaxResult.success(storageBlobService.updateStorageBlobs(files, bucketName,type)); } /** * éç¨ä¸ä¼ 请æ±ï¼åä¸ªï¼ */ src/main/resources/application-bdsm.yml
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,219 @@ # 项ç®ç¸å ³é ç½® ruoyi: # åç§° name: RuoYi # çæ¬ version: 3.8.9 # çæå¹´ä»½ copyrightYear: 2025 # æä»¶è·¯å¾ 示ä¾ï¼ Windowsé ç½®D:/ruoyi/uploadPathï¼Linuxé ç½® /home/ruoyi/uploadPathï¼ profile: /javaWork/product-inventory-management/file # è·åipå°åå¼å ³ addressEnabled: false # éªè¯ç ç±»å math æ°åè®¡ç® char å符éªè¯ captchaType: math # å¼åç¯å¢é ç½® server: # æå¡å¨çHTTP端å£ï¼é»è®¤ä¸º8080 port: 9036 servlet: # åºç¨ç访é®è·¯å¾ context-path: / tomcat: # tomcatçURIç¼ç uri-encoding: UTF-8 # è¿æ¥æ°æ»¡åçæéæ°ï¼é»è®¤ä¸º100 accept-count: 1000 threads: # tomcatæå¤§çº¿ç¨æ°ï¼é»è®¤ä¸º200 max: 800 # Tomcatå¯å¨åå§åççº¿ç¨æ°ï¼é»è®¤å¼10 min-spare: 100 # æ¥å¿é ç½® logging: level: com.ruoyi: warn org.springframework: warn minio: endpoint: http://114.132.189.42/ port: 7019 secure: false accessKey: admin secretKey: 12345678 preview-expiry: 24 # é¢è§å°åé»è®¤24å°æ¶ default-bucket: bdsm-product # ç¨æ·é ç½® user: password: # å¯ç æå¤§éè¯¯æ¬¡æ° maxRetryCount: 5 # å¯ç é宿¶é´ï¼é»è®¤10åéï¼ lockTime: 10 # Springé ç½® spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver druid: # ä¸»åºæ°æ®æº master: url: jdbc:mysql://192.168.1.185:3306/product-inventory-management-demo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: xd@123456.. # ä»åºæ°æ®æº slave: # 仿°æ®æºå¼å ³/é»è®¤å ³é enabled: false url: username: password: # åå§è¿æ¥æ° initialSize: 5 # æå°è¿æ¥æ± æ°é minIdle: 10 # æå¤§è¿æ¥æ± æ°é maxActive: 20 # é ç½®è·åè¿æ¥çå¾ è¶ æ¶çæ¶é´ maxWait: 60000 # é ç½®è¿æ¥è¶ æ¶æ¶é´ connectTimeout: 30000 # é ç½®ç½ç»è¶ æ¶æ¶é´ socketTimeout: 60000 # é ç½®é´éå¤ä¹ æè¿è¡ä¸æ¬¡æ£æµï¼æ£æµéè¦å ³éç空é²è¿æ¥ï¼å使¯æ¯«ç§ timeBetweenEvictionRunsMillis: 60000 # é ç½®ä¸ä¸ªè¿æ¥å¨æ± 䏿å°çåçæ¶é´ï¼å使¯æ¯«ç§ minEvictableIdleTimeMillis: 300000 # é ç½®ä¸ä¸ªè¿æ¥å¨æ± 䏿大çåçæ¶é´ï¼å使¯æ¯«ç§ maxEvictableIdleTimeMillis: 900000 # é ç½®æ£æµè¿æ¥æ¯å¦ææ validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false webStatFilter: enabled: true statViewServlet: enabled: true # 设置ç½ååï¼ä¸å¡«åå 许ææè®¿é® allow: url-pattern: /druid/* # æ§å¶å°ç®¡çç¨æ·ååå¯ç login-username: ruoyi login-password: 123456 filter: stat: enabled: true # æ ¢SQLè®°å½ log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true # èµæºä¿¡æ¯ messages: # å½é åèµæºæä»¶è·¯å¾ basename: i18n/messages # æä»¶ä¸ä¼ servlet: multipart: # å个æä»¶å¤§å° max-file-size: 1GB # 设置æ»ä¸ä¼ çæä»¶å¤§å° max-request-size: 2GB # æå¡æ¨¡å devtools: restart: # çé¨ç½²å¼å ³ enabled: false # redis é ç½® redis: # å°å # host: 127.0.0.1 host: 192.168.1.185 # 端å£ï¼é»è®¤ä¸º6379 port: 6379 # æ°æ®åºç´¢å¼ database: 0 # å¯ç # password: root2022! password: # è¿æ¥è¶ æ¶æ¶é´ timeout: 10s lettuce: pool: # è¿æ¥æ± ä¸çæå°ç©ºé²è¿æ¥ min-idle: 0 # è¿æ¥æ± ä¸çæå¤§ç©ºé²è¿æ¥ max-idle: 8 # è¿æ¥æ± çæå¤§æ°æ®åºè¿æ¥æ° max-active: 8 # #è¿æ¥æ± æå¤§é»å¡çå¾ æ¶é´ï¼ä½¿ç¨è´å¼è¡¨ç¤ºæ²¡æéå¶ï¼ max-wait: -1ms # tokené ç½® token: # 令çèªå®ä¹æ è¯ header: Authorization # 令çå¯é¥ secret: abcdefghijklmnopqrstuvwxyz # ä»¤çæææï¼é»è®¤30åéï¼ expireTime: 450 # MyBatis Plusé ç½® mybatis-plus: # æç´¢æå®å å«å æ ¹æ®èªå·±çé¡¹ç®æ¥ typeAliasesPackage: com.ruoyi.**.pojo # é ç½®mapperçæ«æï¼æ¾å°ææçmapper.xmlæ å°æä»¶ mapperLocations: classpath*:mapper/**/*Mapper.xml # å è½½å ¨å±çé ç½®æä»¶ configLocation: classpath:mybatis/mybatis-config.xml global-config: enable-sql-runner: true db-config: id-type: auto # PageHelperå页æä»¶ pagehelper: helperDialect: mysql supportMethodsArguments: true params: count=countSql # Swaggeré ç½® swagger: # æ¯å¦å¼å¯swagger enabled: true # 请æ±åç¼ pathMapping: /dev-api # 鲿¢XSSæ»å» xss: # è¿æ»¤å¼å ³ enabled: true # æé¤é¾æ¥ï¼å¤ä¸ªç¨éå·åéï¼ excludes: /system/notice # å¹é 龿¥ urlPatterns: /system/*,/monitor/*,/tool/* # 代ç çæ gen: # ä½è author: ruoyi # é»è®¤çæå è·¯å¾ system éæ¹æèªå·±ç模ååç§° å¦ system monitor tool packageName: com.ruoyi.project.system # èªå¨å»é¤è¡¨åç¼ï¼é»è®¤æ¯true autoRemovePre: false # 表åç¼ï¼çæç±»åä¸ä¼å å«è¡¨åç¼ï¼å¤ä¸ªç¨éå·åéï¼ tablePrefix: sys_ # æ¯å¦å è®¸çææä»¶è¦çå°æ¬å°ï¼èªå®ä¹è·¯å¾ï¼ï¼é»è®¤ä¸å 许 allowOverwrite: false file: temp-dir: /javaWork/product-inventory-management/file/temp/uploads upload-dir: /javaWork/product-inventory-management/file/prod/uploads src/main/resources/application-demo.yml
@@ -45,7 +45,7 @@ accessKey: admin secretKey: 12345678 preview-expiry: 24 # é¢è§å°åé»è®¤24å°æ¶ default-bucket: uploadPath default-bucket: demo-product # ç¨æ·é ç½® user: password: