pom.xml
@@ -307,6 +307,14 @@ <fork>true</fork> <!-- å¦ææ²¡æè¯¥é ç½®ï¼devtoolsä¸ä¼çæ --> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>16</source> <target>16</target> </configuration> </plugin> </plugins> </build> src/main/java/com/ruoyi/basic/dto/StorageBlobDTO.java
@@ -6,4 +6,6 @@ @Data public class StorageBlobDTO extends StorageBlob { private String url; private String downloadUrl; } src/main/java/com/ruoyi/basic/pojo/StorageBlob.java
@@ -64,4 +64,10 @@ */ @TableField(value = "byte_size") private Long byteSize; /** * 0ç产å 1ç产å 2ç产é®é¢ */ @TableField(value = "type") private Long type; } src/main/java/com/ruoyi/basic/service/StorageAttachmentService.java
@@ -34,6 +34,9 @@ */ public void saveStorageAttachment(List<StorageAttachment> attachments, Long recordId, StorageAttachmentRecordType recordType, StorageAttachmentConstants fileType); public void saveStorageAttachment(List<StorageAttachment> attachments, Long recordId, StorageAttachmentRecordType recordType, String fileType); /** * å é¤éç¨æä»¶ä¸ä¼ çéä»¶ä¿¡æ¯ * @param storageAttachment æä»¶ä¿¡æ¯ src/main/java/com/ruoyi/basic/service/impl/StorageAttachmentServiceImpl.java
@@ -78,6 +78,22 @@ } @Override public void saveStorageAttachment(List<StorageAttachment> attachments, Long recordId, StorageAttachmentRecordType recordType, String fileType) { // å 餿§å¾ deleteStorageAttachment(new StorageAttachment(fileType.toString(), (long) recordType.ordinal(), recordId)); for (StorageAttachment attachment : attachments) { // è·åå ³èè®°å½ StorageBlob storageBlob = attachment.getStorageBlobDTO(); attachment.setName(fileType.toString()); attachment.setRecordType((long) recordType.ordinal()); attachment.setRecordId(recordId); attachment.setStorageBlobId(storageBlob.getId()); storageAttachmentMapper.insert(attachment); } } @Override public int deleteStorageAttachment(StorageAttachment storageAttachment) { // å å 餿ç»è¡¨ storageBlobService.deleteStorageBlobs(storageAttachment); src/main/java/com/ruoyi/common/enums/StorageAttachmentRecordType.java
@@ -9,7 +9,10 @@ @AllArgsConstructor public enum StorageAttachmentRecordType { // ä¾å å®é å¼å请å é¤ Template("Template","èä¾"); Template("Template","èä¾"), Archives("Archives","ææ¡£ç®¡ç"), InspectionTasks("InspectionTasks","ç产巡æ£"), QrCodeScanRecords("QrCodeScanRecords","äºç»´ç æ«ç è®°å½æä»¶"); private final String code; src/main/java/com/ruoyi/common/utils/MinioUtils.java
@@ -21,12 +21,10 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -306,4 +304,86 @@ return null; } /** * çæé¢è§URL * @param bucketFilename æä»¶å¨MinIOä¸çå¯ä¸æ è¯ * @param bucketName å卿¡¶åç§° * @param useDefaultExpiry æ¯å¦ä½¿ç¨é»è®¤è¿ææ¶é´ï¼true=使ç¨é»è®¤è¿ææ¶é´ï¼false=æ°¸ä¹ ææï¼ * @return é¢è§URL */ public String getPreviewUrls(String bucketFilename, String bucketName, boolean useDefaultExpiry) { if (StringUtils.isBlank(bucketFilename)) { return null; } try { // éªè¯æä»¶å卿§ minioClient.statObject(StatObjectArgs.builder() .bucket(bucketName) .object(bucketFilename) .build()); GetPresignedObjectUrlArgs.Builder builder = GetPresignedObjectUrlArgs.builder() .method(Method.GET) .bucket(bucketName) .object(bucketFilename); // è®¾ç½®è¿ææ¶é´ï¼useDefaultExpiry=true 使ç¨é ç½®çè¿ææ¶é´ if (useDefaultExpiry) { builder.expiry(previewExpiry, TimeUnit.HOURS); } return minioClient.getPresignedObjectUrl(builder.build()); } catch (Exception e) { throw new UtilException("çæé¢è§URL失败: " + e.getMessage(), e); } } /** * çæä¸è½½URLï¼å¼ºå¶æµè§å¨ä¸è½½ï¼ * @param bucketFilename æä»¶å¨MinIOä¸çå¯ä¸æ è¯ * @param bucketName å卿¡¶åç§° * @param originalFileName åå§æä»¶åï¼ç¨äºä¸è½½æ¶æ¾ç¤ºï¼ * @param useDefaultExpiry æ¯å¦ä½¿ç¨é»è®¤è¿ææ¶é´ï¼true=使ç¨é»è®¤ï¼false=æ è¿ææ¶é´ï¼ * @return ä¸è½½URL */ public String getDownloadUrls(String bucketFilename, String bucketName, String originalFileName, boolean useDefaultExpiry) { if (StringUtils.isBlank(bucketFilename)) { return null; } try { // éªè¯æä»¶å卿§ minioClient.statObject(StatObjectArgs.builder() .bucket(bucketName) .object(bucketFilename) .build()); // æ£ç¡®ç¼ç æä»¶åï¼æ¿æ¢ + 为 %20 String encodedFileName = URLEncoder.encode(originalFileName, StandardCharsets.UTF_8) .replace("+", "%20"); Map<String, String> reqParams = new HashMap<>(); reqParams.put("response-content-disposition", "attachment; filename=\"" + encodedFileName + "\""); GetPresignedObjectUrlArgs.Builder builder = GetPresignedObjectUrlArgs.builder() .method(Method.GET) .bucket(bucketName) .object(bucketFilename) .extraQueryParams(reqParams); // æ ¹æ®åæ°å³å®æ¯å¦è®¾ç½®è¿ææ¶é´ if (useDefaultExpiry) { // 使ç¨é»è®¤è¿ææ¶é´ï¼ä»é 置读åï¼ builder.expiry(previewExpiry, TimeUnit.HOURS); } else { // ä¸è®¾ç½®è¿ææ¶é´ï¼MinIO é»è®¤7å¤©ï¼ } return minioClient.getPresignedObjectUrl(builder.build()); } catch (Exception e) { throw new UtilException("çæä¸è½½URL失败: " + e.getMessage(), e); } } } src/main/java/com/ruoyi/inspectiontask/StorageAttachmentRecordType.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,23 @@ package com.ruoyi.inspectiontask; import lombok.AllArgsConstructor; import lombok.Getter; /** * éä»¶è®°å½ç±»åæä¸¾ * */ @Getter @AllArgsConstructor public enum StorageAttachmentRecordType { // ä¾å å®é å¼å请å é¤ Template("Template","èä¾"), Archives("Archives","ææ¡£ç®¡ç"), InspectionTasks("InspectionTasks","ç产巡æ£"), QrCodeScanRecords("QrCodeScanRecords","äºç»´ç æ«ç è®°å½æä»¶"); private final String code; private final String info; } src/main/java/com/ruoyi/inspectiontask/controller/InspectionTaskController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,59 @@ package com.ruoyi.inspectiontask.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.framework.web.controller.BaseController; import com.ruoyi.framework.web.domain.R; import com.ruoyi.inspectiontask.dto.InspectionTaskDto; import com.ruoyi.inspectiontask.pojo.InspectionTask; import com.ruoyi.inspectiontask.service.InspectionTaskService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.AllArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; /** * @author :yys * @date : 2025/9/19 10:52 */ @RestController @Api(tags = "å·¡æ£ä»»å¡ç®¡ç") @RequestMapping("/inspectionTask") public class InspectionTaskController extends BaseController { @Autowired private InspectionTaskService inspectionTaskService; /** * å·¡æ£ä»»å¡è¡¨è¡¨æ¥è¯¢ */ @GetMapping("/list") @ApiOperation("å·¡æ£ä»»å¡è¡¨è¡¨æ¥è¯¢") public R<IPage<InspectionTaskDto>> list(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto) { IPage<InspectionTaskDto> list = inspectionTaskService.selectInspectionTaskList(page,inspectionTaskDto); return R.ok(list); } /** * å·¡æ£ä»»å¡è¡¨æ°å¢ä¿®æ¹ */ @PostMapping("/addOrEditInspectionTask") @ApiOperation("å·¡æ£ä»»å¡è¡¨æ°å¢ä¿®æ¹") @Transactional(rollbackFor = Exception.class) public R addOrEditInspectionTask(@RequestBody InspectionTaskDto inspectionTaskDto) { return R.ok(inspectionTaskService.addOrEditInspectionTask(inspectionTaskDto)); } /** * å·¡æ£ä»»å¡è¡¨å é¤ */ @DeleteMapping("/delInspectionTask") @ApiOperation("å·¡æ£ä»»å¡è¡¨å é¤") @Transactional(rollbackFor = Exception.class) public R remove(@RequestBody Long[] ids) { return R.ok(inspectionTaskService.delByIds(ids)); } } src/main/java/com/ruoyi/inspectiontask/controller/QrCodeController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,56 @@ package com.ruoyi.inspectiontask.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.framework.web.controller.BaseController; import com.ruoyi.framework.web.domain.R; import com.ruoyi.inspectiontask.dto.QrCodeDto; import com.ruoyi.inspectiontask.pojo.QrCode; import com.ruoyi.inspectiontask.service.QrCodeService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.AllArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; /** * @author :yys * @date : 2025/9/19 10:52 */ @RestController @Api(tags = "äºç»´ç 管ç") @RequestMapping("/qrCode") public class QrCodeController extends BaseController { @Autowired private QrCodeService qrCodeService; /** * äºç»´ç 管ç表æ¥è¯¢ */ @GetMapping("/list") @ApiOperation("äºç»´ç 管ç表æ¥è¯¢") public R<IPage<QrCode>> list(Page page, QrCodeDto qrCodeDto) { IPage<QrCode> list = qrCodeService.selectQrCodeList(page, qrCodeDto); return R.ok(list); } /** * äºç»´ç 管ç表æ°å¢ä¿®æ¹ */ @PostMapping("/addOrEditQrCode") @ApiOperation("äºç»´ç 管ç表æ°å¢ä¿®æ¹") public R<Long> addOrEditQrCode(@RequestBody QrCodeDto qrCodeDto) { return R.ok(qrCodeService.addOrEditQrCode(qrCodeDto)); } /** * äºç»´ç 管ç表å é¤ */ @DeleteMapping("/delQrCode") @ApiOperation("äºç»´ç 管ç表å é¤") public R remove(@RequestBody Long[] ids) { return R.ok(qrCodeService.delByIds(ids)); } } src/main/java/com/ruoyi/inspectiontask/controller/QrCodeScanRecordController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,56 @@ package com.ruoyi.inspectiontask.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.framework.web.controller.BaseController; import com.ruoyi.framework.web.domain.R; import com.ruoyi.inspectiontask.dto.QrCodeScanRecordDto; import com.ruoyi.inspectiontask.pojo.QrCodeScanRecord; import com.ruoyi.inspectiontask.service.QrCodeScanRecordService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.AllArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; /** * @author :yys * @date : 2025/9/19 10:53 */ @RestController @Api(tags = "äºç»´ç æ«æè®°å½ç®¡ç") @RequestMapping("/qrCodeScanRecord") public class QrCodeScanRecordController extends BaseController { @Autowired private QrCodeScanRecordService qrCodeScanRecordService; /** * äºç»´ç æ«ç è®°å½è¡¨æ¥è¯¢ */ @GetMapping("/list") @ApiOperation("äºç»´ç æ«ç è®°å½è¡¨æ¥è¯¢") public R<IPage<QrCodeScanRecordDto>> list(Page<QrCodeScanRecord> page, QrCodeScanRecordDto qrCodeScanRecordDto) { IPage<QrCodeScanRecordDto> list = qrCodeScanRecordService.selectQrCodeScanRecordList(page, qrCodeScanRecordDto); return R.ok(list); } /** * äºç»´ç æ«ç è®°å½è¡¨æ°å¢ä¿®æ¹ */ @PostMapping("/addOrEditQrCodeRecord") @ApiOperation("äºç»´ç æ«ç è®°å½è¡¨æ°å¢ä¿®æ¹") public R addOrEditQrCodeRecord(@RequestBody QrCodeScanRecordDto qrCodeScanRecordDto) { return R.ok(qrCodeScanRecordService.addOrEditQrCodeRecord(qrCodeScanRecordDto)); } /** * äºç»´ç æ«ç è®°å½è¡¨å é¤ */ @DeleteMapping("/delSalesRecord") @ApiOperation("äºç»´ç æ«ç è®°å½è¡¨å é¤") public R remove(@RequestBody Long[] ids) { return R.ok(qrCodeScanRecordService.delByIds(ids)); } } src/main/java/com/ruoyi/inspectiontask/controller/TimingTaskController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,58 @@ package com.ruoyi.inspectiontask.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.framework.web.controller.BaseController; import com.ruoyi.framework.web.domain.R; import com.ruoyi.inspectiontask.dto.TimingTaskDto; import com.ruoyi.inspectiontask.pojo.TimingTask; import com.ruoyi.inspectiontask.service.TimingTaskService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.AllArgsConstructor; import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; /** * @author :yys * @date : 2025/9/19 10:53 */ @RestController @Api(tags = "宿¶ä»»å¡ç®¡ç") @RequestMapping("/timingTask") public class TimingTaskController extends BaseController { @Autowired private TimingTaskService timingTaskService; /** * 宿¶å·¡æ£ä»»å¡è¡¨æ¥è¯¢ */ @GetMapping("/list") @ApiOperation(value = "宿¶ä»»å¡å表") public R<IPage<TimingTaskDto>> list(Page<TimingTask> page, TimingTask timingTask) { IPage<TimingTaskDto> list = timingTaskService.selectTimingTaskList(page,timingTask); return R.ok(list); } /** * 宿¶å·¡æ£ä»»å¡è¡¨æ°å¢ä¿®æ¹ */ @PostMapping("/addOrEditTimingTask") @ApiOperation(value = "æ°å¢ä¿®æ¹å®æ¶ä»»å¡") public R addOrEditTimingTask(@RequestBody TimingTaskDto timingTaskDto) throws SchedulerException { return R.ok(timingTaskService.addOrEditTimingTask(timingTaskDto)); } /** * 宿¶å·¡æ£ä»»å¡è¡¨å é¤ */ @DeleteMapping("/delTimingTask") @ApiOperation(value = "å é¤å®æ¶ä»»å¡") public R remove(@RequestBody Long[] ids) { return R.ok(timingTaskService.delByIds(ids)); } } src/main/java/com/ruoyi/inspectiontask/dto/InspectionTaskDto.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,20 @@ package com.ruoyi.inspectiontask.dto; import com.ruoyi.basic.dto.StorageBlobDTO; import com.ruoyi.basic.pojo.StorageAttachment; import com.ruoyi.inspectiontask.pojo.InspectionTask; import lombok.Data; import java.util.List; @Data public class InspectionTaskDto extends InspectionTask { private List<StorageBlobDTO> storageBlobDTO; private List<StorageBlobDTO> beforeProduction; private List<StorageBlobDTO> afterProduction; private List<StorageBlobDTO> productionIssues; private List<StorageAttachment> attachments; } src/main/java/com/ruoyi/inspectiontask/dto/QrCodeDto.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,8 @@ package com.ruoyi.inspectiontask.dto; import com.ruoyi.inspectiontask.pojo.QrCode; import lombok.Data; @Data public class QrCodeDto extends QrCode { } src/main/java/com/ruoyi/inspectiontask/dto/QrCodeScanRecordDto.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,20 @@ package com.ruoyi.inspectiontask.dto; import com.ruoyi.basic.dto.StorageBlobDTO; import com.ruoyi.inspectiontask.pojo.QrCode; import com.ruoyi.inspectiontask.pojo.QrCodeScanRecord; import lombok.Data; import java.util.List; @Data public class QrCodeScanRecordDto extends QrCodeScanRecord { private QrCode qrCode; private String scanner; private List<StorageBlobDTO> storageBlobDTO; } src/main/java/com/ruoyi/inspectiontask/dto/TimingTaskDto.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,12 @@ package com.ruoyi.inspectiontask.dto; import com.ruoyi.inspectiontask.pojo.TimingTask; import lombok.Data; import java.util.List; @Data public class TimingTaskDto extends TimingTask { private List<String> inspector; } src/main/java/com/ruoyi/inspectiontask/mapper/InspectionTaskMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,11 @@ package com.ruoyi.inspectiontask.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.inspectiontask.pojo.InspectionTask; /** * @author :yys * @date : 2025/9/19 10:46 */ public interface InspectionTaskMapper extends BaseMapper<InspectionTask> { } src/main/java/com/ruoyi/inspectiontask/mapper/QrCodeMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,11 @@ package com.ruoyi.inspectiontask.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.inspectiontask.pojo.QrCode; /** * @author :yys * @date : 2025/9/19 10:46 */ public interface QrCodeMapper extends BaseMapper<QrCode> { } src/main/java/com/ruoyi/inspectiontask/mapper/QrCodeScanRecordMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,11 @@ package com.ruoyi.inspectiontask.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.inspectiontask.pojo.QrCodeScanRecord; /** * @author :yys * @date : 2025/9/19 10:46 */ public interface QrCodeScanRecordMapper extends BaseMapper<QrCodeScanRecord> { } src/main/java/com/ruoyi/inspectiontask/mapper/TimingTaskMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,11 @@ package com.ruoyi.inspectiontask.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.inspectiontask.pojo.TimingTask; /** * @author :yys * @date : 2025/9/19 10:47 */ public interface TimingTaskMapper extends BaseMapper<TimingTask> { } src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,81 @@ package com.ruoyi.inspectiontask.pojo; import com.baomidou.mybatisplus.annotation.*; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; /** * @author :yys * @date : 2025/9/19 10:25 */ @Data @ApiModel @TableName("inspection_task") public class InspectionTask { private static final long serialVersionUID = 1L; /** * å·¡æ£ä»»å¡å¯ä¸æ è¯ */ @TableId(type = IdType.AUTO) private Long id; @ApiModelProperty(value = "å·¡æ£ä»»å¡åç§°") private String taskName; @ApiModelProperty(value = "å·¡æ£äººID") private String inspectorId; @ApiModelProperty(value = "æ§è¡å·¡æ£ç人åå§å") private String inspector; @ApiModelProperty(value = "ä»»å¡éå 说ææç¹æ®æ åµè®°å½") private String remarks; @ApiModelProperty(value = "ä»»å¡ç»è®°äººID") private Long registrantId; @ApiModelProperty(value = "ä»»å¡ç»è®°äººå§å") private String registrant; @ApiModelProperty(value = "颿¬¡") private String frequencyType; @ApiModelProperty(value = "æ¶é´ç»è") private String frequencyDetail; @ApiModelProperty(value = "å·¡æ£å°ç¹è¯¦ç»æè¿°") private String inspectionLocation; @ApiModelProperty(value = "软å 餿 å¿ï¼0=æªå é¤ï¼1=å·²å é¤") private Integer deleted; @ApiModelProperty(value = "å建该记å½çç¨æ·") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT) private String 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 String updateUser; @ApiModelProperty(value = "è®°å½æåæ´æ°æ¶é´") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; @ApiModelProperty(value = "ç§æ·") @TableField(fill = FieldFill.INSERT) private Integer tenantId; } src/main/java/com/ruoyi/inspectiontask/pojo/QrCode.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,63 @@ 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.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; /** * @author :yys * @date : 2025/9/19 10:29 */ @Data @ApiModel @TableName("qr_code") public class QrCode { private static final long serialVersionUID = 1L; /** * äºç»´ç å¯ä¸æ è¯ */ @TableId(type = IdType.AUTO) private Long id; @ApiModelProperty(value = "设å¤åç§°") private String deviceName; @ApiModelProperty(value = "æå¨ä½ç½®æè¿°") private String location; @ApiModelProperty(value = "ç§æ·IDï¼ç¨äºå¤ç§æ·é离") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT) private Integer tenantId; @ApiModelProperty(value = "软å 餿 å¿ï¼0=æªå é¤ï¼1=å·²å é¤") private Integer deleted; @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/inspectiontask/pojo/QrCodeScanRecord.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,68 @@ 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.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; /** * @author :yys * @date : 2025/9/19 10:29 */ @Data @TableName("qr_code_scan_record") @ApiModel public class QrCodeScanRecord { private static final long serialVersionUID = 1L; /** * æ«ç è®°å½å¯ä¸æ è¯ */ @TableId(type = IdType.AUTO) private Long id; @ApiModelProperty(value = "å ³èçäºç»´ç ID") private Long qrCodeId; @ApiModelProperty(value = "æ«ç äººç¨æ·ID") private Long scannerId; @ApiModelProperty(value = "å®é æ«ç æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @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; @ApiModelProperty(value = "软å 餿 å¿ï¼0=æªå é¤ï¼1=å·²å é¤") private Integer deleted; @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 updateBy; @ApiModelProperty(value = "è®°å½æåæ´æ°æ¶é´") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; } src/main/java/com/ruoyi/inspectiontask/pojo/TimingTask.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,97 @@ 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.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDate; import java.time.LocalDateTime; /** * @author :yys * @date : 2025/9/19 10:27 */ @Data @ApiModel @TableName("timing_task") public class TimingTask { private static final long serialVersionUID = 1L; /** * 主é®ID */ @TableId(type = IdType.AUTO) private Long id; @ApiModelProperty(value = "ä»»å¡åç§°") private String taskName; @ApiModelProperty(value = "å·¡æ£äºº") private String inspectorIds; @ApiModelProperty(value = "å·¡æ£å°ç¹") private String inspectionLocation; @ApiModelProperty(value = "颿¬¡") private String frequencyType; @ApiModelProperty(value = "颿¬¡è¯¦æ ") private String frequencyDetail; @ApiModelProperty(value = "䏿¬¡æ§è¡æ¶é´") private LocalDateTime nextExecutionTime; @ApiModelProperty(value = "æåæ§è¡æ¶é´") private LocalDateTime lastExecutionTime; @ApiModelProperty(value = "æ¯å¦æ¿æ´»") private boolean isActive; @ApiModelProperty(value = "夿³¨") private String remarks; @ApiModelProperty(value = "ç»è®°äººid") private Long registrantId; @ApiModelProperty(value = "ç»è®°äºº") private String registrant; @ApiModelProperty(value = "ç»è®°æ¥æ") private LocalDate registrationDate; @ApiModelProperty(value = "ç¶æ") private String status; @ApiModelProperty(value = "软å 餿 å¿ï¼0=æªå é¤ï¼1=å·²å é¤") private Integer deleted; @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; @ApiModelProperty(value = "ç§æ·ID") @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT) private Integer tenantId; } src/main/java/com/ruoyi/inspectiontask/service/InspectionTaskService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,20 @@ package com.ruoyi.inspectiontask.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.inspectiontask.dto.InspectionTaskDto; import com.ruoyi.inspectiontask.pojo.InspectionTask; /** * @author :yys * @date : 2025/9/19 10:49 */ public interface InspectionTaskService extends IService<InspectionTask> { IPage<InspectionTaskDto> selectInspectionTaskList(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto); int addOrEditInspectionTask(InspectionTaskDto inspectionTaskDto); int delByIds(Long[] ids); } src/main/java/com/ruoyi/inspectiontask/service/QrCodeScanRecordService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,20 @@ package com.ruoyi.inspectiontask.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.inspectiontask.dto.QrCodeScanRecordDto; import com.ruoyi.inspectiontask.pojo.QrCodeScanRecord; /** * @author :yys * @date : 2025/9/19 10:50 */ public interface QrCodeScanRecordService extends IService<QrCodeScanRecord> { IPage<QrCodeScanRecordDto> selectQrCodeScanRecordList(Page<QrCodeScanRecord> page, QrCodeScanRecordDto qrCodeScanRecordDto); int addOrEditQrCodeRecord(QrCodeScanRecordDto qrCodeScanRecordDto); int delByIds(Long[] ids); } src/main/java/com/ruoyi/inspectiontask/service/QrCodeService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,19 @@ package com.ruoyi.inspectiontask.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.inspectiontask.dto.QrCodeDto; import com.ruoyi.inspectiontask.pojo.QrCode; /** * @author :yys * @date : 2025/9/19 10:50 */ public interface QrCodeService extends IService<QrCode>{ IPage<QrCode> selectQrCodeList(Page page, QrCodeDto qrCodeDto); Long addOrEditQrCode(QrCodeDto qrCodeDto); int delByIds(Long[] ids); } src/main/java/com/ruoyi/inspectiontask/service/TimingTaskService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,23 @@ package com.ruoyi.inspectiontask.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.inspectiontask.dto.TimingTaskDto; import com.ruoyi.inspectiontask.pojo.TimingTask; import org.quartz.SchedulerException; /** * @author :yys * @date : 2025/9/19 10:51 */ public interface TimingTaskService extends IService<TimingTask> { IPage<TimingTaskDto> selectTimingTaskList(Page<TimingTask> page, TimingTask timingTask); int addOrEditTimingTask(TimingTaskDto timingTaskDto) throws SchedulerException; int delByIds(Long[] ids); void updateTaskExecutionTime(Long taskId); } src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,257 @@ package com.ruoyi.inspectiontask.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.basic.dto.StorageBlobDTO; import com.ruoyi.basic.mapper.StorageAttachmentMapper; import com.ruoyi.basic.mapper.StorageBlobMapper; import com.ruoyi.basic.pojo.StorageAttachment; import com.ruoyi.basic.pojo.StorageBlob; import com.ruoyi.basic.service.StorageAttachmentService; import com.ruoyi.common.utils.MinioUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.inspectiontask.dto.InspectionTaskDto; import com.ruoyi.inspectiontask.mapper.InspectionTaskMapper; import com.ruoyi.inspectiontask.pojo.InspectionTask; import com.ruoyi.inspectiontask.service.InspectionTaskService; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysUserMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import static com.ruoyi.common.constant.StorageAttachmentConstants.StorageAttachmentFile; import static com.ruoyi.common.enums.StorageAttachmentRecordType.InspectionTasks; /** * @author :yys * @date : 2025/9/19 10:54 */ @Service @Slf4j public class InspectionTaskServiceImpl extends ServiceImpl<InspectionTaskMapper, InspectionTask> implements InspectionTaskService { @Autowired private InspectionTaskMapper inspectionTaskMapper; @Autowired private StorageAttachmentService storageAttachmentService; @Autowired private StorageBlobMapper storageBlobMapper; @Autowired private StorageAttachmentMapper storageAttachmentMapper; @Autowired private MinioUtils minioUtils; @Autowired private SysUserMapper sysUserMapper; @Override public IPage<InspectionTaskDto> selectInspectionTaskList(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto) { LambdaQueryWrapper<InspectionTask> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.orderByDesc(InspectionTask::getCreateTime); IPage<InspectionTask> entityPage = inspectionTaskMapper.selectPage(page, queryWrapper); // æ æ°æ®æåè¿å if (CollectionUtils.isEmpty(entityPage.getRecords())) { return new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal()); } // è·åidéå List<Long> ids = entityPage.getRecords().stream().map(InspectionTask::getId).toList(); //ç»è®°äººids List<Long> registrantIds = entityPage.getRecords().stream().map(InspectionTask::getRegistrantId).toList(); // æ¹éæ¥è¯¢ç»è®°äºº Map<Long, SysUser> sysUserMap; if (!registrantIds.isEmpty()) { List<SysUser> sysUsers = sysUserMapper.selectList(registrantIds); sysUserMap = sysUsers.stream().collect(Collectors.toMap(SysUser::getUserId, Function.identity())); } else { sysUserMap = new HashMap<>(); } //å·¡æ£äººids List<String> inspectorIds = entityPage.getRecords().stream().map(InspectionTask::getInspectorId).toList(); //è·åææä¸éå¤çç¨æ·ID Set<Long> allUserIds = entityPage.getRecords().stream() .map(InspectionTask::getInspectorId) // è·å"2,3"è¿æ ·çå符串 .filter(StringUtils::isNotBlank) .flatMap(idsStr -> Arrays.stream(idsStr.split(","))) .map(idStr -> { try { return Long.parseLong(idStr.trim()); } catch (NumberFormatException e) { return null; } }) .filter(Objects::nonNull) .collect(Collectors.toSet()); // 使ç¨SQLæ¹éæ¥è¯¢ç¨æ·ä¿¡æ¯ Map<Long, String> userIdToNameMap = allUserIds.isEmpty() ? Collections.emptyMap() : sysUserMapper.selectUsersByIds(new ArrayList<>(allUserIds)) .stream() .collect(Collectors.toMap( SysUser::getUserId, SysUser::getNickName, (existing, replacement) -> existing)); //å¤çéä»¶ Map<Long, List<StorageAttachment>> attachmentsMap = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>().in(StorageAttachment::getRecordId, ids) .eq(StorageAttachment::getRecordType, InspectionTasks.ordinal())) .stream() .collect(Collectors.groupingBy(StorageAttachment::getRecordId)); // æ¹éæ¥è¯¢ææéè¦çæä»¶æ°æ® Set<Long> blobIds = attachmentsMap.values() .stream() .flatMap(List::stream) .map(StorageAttachment::getStorageBlobId) .collect(Collectors.toSet()); Map<Long, StorageBlob> blobMap = blobIds.isEmpty() ? Collections.emptyMap() : storageBlobMapper.selectList(new LambdaQueryWrapper<StorageBlob>().in(StorageBlob::getId, blobIds)) .stream() .collect(Collectors.toMap(StorageBlob::getId, Function.identity())); List<InspectionTaskDto> dtoList = entityPage.getRecords().stream().map(inspectionTask -> { InspectionTaskDto dto = new InspectionTaskDto(); BeanUtils.copyProperties(inspectionTask, dto); // å¤å¶ä¸»å¯¹è±¡å±æ§ // 设置ç»è®°äºº SysUser sysUser = sysUserMap.get(inspectionTask.getRegistrantId()); if (sysUser != null) { dto.setRegistrant(sysUser.getNickName()); } // å¤çå·¡æ£äººåç§° if (StringUtils.isNotBlank(inspectionTask.getInspectorId())) { String inspectorNames = Arrays.stream(inspectionTask.getInspectorId().split(",")) .map(String::trim) .map(idStr -> { try { Long userId = Long.parseLong(idStr); return userIdToNameMap.getOrDefault(userId, "æªç¥ç¨æ·(" + idStr + ")"); } catch (NumberFormatException e) { return "æ æID(" + idStr + ")"; } }) .collect(Collectors.joining(",")); dto.setInspector(inspectorNames); } // åå§åä¸ä¸ªéä»¶å表 dto.setBeforeProduction(new ArrayList<>()); dto.setAfterProduction(new ArrayList<>()); dto.setProductionIssues(new ArrayList<>()); // å¤çéä»¶åç±» Optional.ofNullable(attachmentsMap.get(inspectionTask.getId())) .orElse(Collections.emptyList()) .forEach(attachment -> { StorageBlob blob = blobMap.get(attachment.getStorageBlobId()); if (blob != null) { // å建éä»¶DTO StorageBlobDTO blobDto = createBlobDto(blob); // æ ¹æ®typeåç±» switch ((int) blob.getType().longValue()) { case 0: dto.getBeforeProduction().add(blobDto); break; case 1: dto.getAfterProduction().add(blobDto); break; case 2: dto.getProductionIssues().add(blobDto); break; default: // å¯éï¼è®°å½æªå类类å break; } } }); return dto; }).collect(Collectors.toList()); // 7. æå»ºè¿åå页对象 IPage<InspectionTaskDto> resultPage = new Page<>(); BeanUtils.copyProperties(entityPage, resultPage); resultPage.setRecords(dtoList); return resultPage; } // æåå建BlobDTOçå ¬å ±æ¹æ³ private StorageBlobDTO createBlobDto(StorageBlob blob) { StorageBlobDTO dto = new StorageBlobDTO(); BeanUtils.copyProperties(blob, dto); // 设置URL dto.setUrl(minioUtils.getPreviewUrls( blob.getBucketFilename(), blob.getBucketName(), true )); // 设置ä¸è½½URL dto.setDownloadUrl(minioUtils.getDownloadUrls( blob.getBucketFilename(), blob.getBucketName(), blob.getOriginalFilename(), true )); return dto; } @Override public int addOrEditInspectionTask(InspectionTaskDto inspectionTaskDto) { InspectionTask inspectionTask = new InspectionTask(); BeanUtils.copyProperties(inspectionTaskDto, inspectionTask); inspectionTask.setRegistrantId(SecurityUtils.getLoginUser().getUserId()); inspectionTask.setRegistrant(SecurityUtils.getLoginUser().getUsername()); int i; if (Objects.isNull(inspectionTaskDto.getId())) { i = inspectionTaskMapper.insert(inspectionTask); } else { i = inspectionTaskMapper.updateById(inspectionTask); } if (inspectionTaskDto.getStorageBlobDTO() != null && !inspectionTaskDto.getStorageBlobDTO().isEmpty()) { List<StorageAttachment> attachments = new ArrayList<>(); for (StorageBlobDTO storageBlobDTO : inspectionTaskDto.getStorageBlobDTO()) { StorageAttachment storageAttachment = new StorageAttachment( StorageAttachmentFile, (long) InspectionTasks.ordinal(), inspectionTask.getId() ); storageAttachment.setStorageBlobDTO(storageBlobDTO); attachments.add(storageAttachment); } storageAttachmentService.saveStorageAttachment(attachments, inspectionTask.getId(), InspectionTasks, StorageAttachmentFile); } return i; } @Override public int delByIds(Long[] ids) { // æ£æ¥åæ° if (ids == null || ids.length == 0) { return 0; } return inspectionTaskMapper.deleteBatchIds(Arrays.asList(ids)); } } src/main/java/com/ruoyi/inspectiontask/service/impl/QrCodeScanRecordServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,194 @@ package com.ruoyi.inspectiontask.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.basic.dto.StorageBlobDTO; import com.ruoyi.basic.mapper.StorageAttachmentMapper; import com.ruoyi.basic.mapper.StorageBlobMapper; import com.ruoyi.basic.pojo.StorageAttachment; import com.ruoyi.basic.pojo.StorageBlob; import com.ruoyi.basic.service.StorageAttachmentService; import com.ruoyi.common.constant.StorageAttachmentConstants; import com.ruoyi.common.utils.MinioUtils; import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.inspectiontask.dto.QrCodeScanRecordDto; import com.ruoyi.inspectiontask.mapper.QrCodeMapper; import com.ruoyi.inspectiontask.mapper.QrCodeScanRecordMapper; import com.ruoyi.inspectiontask.pojo.QrCode; import com.ruoyi.inspectiontask.pojo.QrCodeScanRecord; import com.ruoyi.inspectiontask.service.QrCodeScanRecordService; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysUserMapper; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import static com.ruoyi.common.constant.StorageAttachmentConstants.StorageAttachmentFile; import static com.ruoyi.common.enums.StorageAttachmentRecordType.QrCodeScanRecords; /** * @author :yys * @date : 2025/9/19 10:54 */ @Service @Slf4j public class QrCodeScanRecordServiceImpl extends ServiceImpl<QrCodeScanRecordMapper, QrCodeScanRecord> implements QrCodeScanRecordService { @Autowired private QrCodeScanRecordMapper qrCodeScanRecordMapper; @Autowired private QrCodeMapper qrCodeMapper; @Autowired private StorageAttachmentService storageAttachmentService; @Autowired private StorageBlobMapper storageBlobMapper; @Autowired private StorageAttachmentMapper storageAttachmentMapper; @Autowired private MinioUtils minioUtils; @Autowired private SysUserMapper sysUserMapper; @Override public IPage<QrCodeScanRecordDto> selectQrCodeScanRecordList(Page<QrCodeScanRecord> page, QrCodeScanRecordDto qrCodeScanRecordDto) { // 1. æå»ºåºç¡æ¥è¯¢æ¡ä»¶ LambdaQueryWrapper<QrCodeScanRecord> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.orderByDesc(QrCodeScanRecord::getCreateTime); // 2. æ§è¡å页æ¥è¯¢ IPage<QrCodeScanRecord> scanRecordIPage = qrCodeScanRecordMapper.selectPage(page, queryWrapper); // 3. æ æ°æ®æåè¿å if (CollectionUtils.isEmpty(scanRecordIPage.getRecords())) { return new Page<>(scanRecordIPage.getCurrent(), scanRecordIPage.getSize(), scanRecordIPage.getTotal()); } // 4. æ¹éè·åææè®°å½IDåäºç»´ç ID List<Long> recordIds = scanRecordIPage.getRecords().stream() .map(QrCodeScanRecord::getId) .collect(Collectors.toList()); Set<Long> qrCodeIds = scanRecordIPage.getRecords().stream() .map(QrCodeScanRecord::getQrCodeId) .filter(Objects::nonNull) .collect(Collectors.toSet()); // 5. æ¹éæ¥è¯¢å ³èæ°æ®ï¼ä½¿ç¨ææ°APIï¼ // 5.1 æ¥è¯¢äºç»´ç ä¿¡æ¯ï¼æ¿æ¢selectBatchIds为selectByIdsï¼ Map<Long, QrCode> qrCodeMap = qrCodeIds.isEmpty() ? Collections.emptyMap() : qrCodeMapper.selectBatchIds(qrCodeIds).stream() .collect(Collectors.toMap(QrCode::getId, Function.identity())); // 5.2 æ¥è¯¢éä»¶å ³èå ³ç³» Map<Long, List<StorageAttachment>> attachmentsMap = storageAttachmentMapper .selectList(new LambdaQueryWrapper<StorageAttachment>() .in(StorageAttachment::getRecordId, recordIds) .eq(StorageAttachment::getRecordType, QrCodeScanRecords.ordinal())) .stream() .collect(Collectors.groupingBy(StorageAttachment::getRecordId)); // 5.3 æ¥è¯¢æä»¶æ°æ®ï¼ä½¿ç¨selectByIdsï¼ Set<Long> blobIds = attachmentsMap.values().stream() .flatMap(List::stream) .map(StorageAttachment::getStorageBlobId) .collect(Collectors.toSet()); Map<Long, StorageBlob> blobMap = blobIds.isEmpty() ? Collections.emptyMap() : storageBlobMapper.selectBatchIds(blobIds).stream() .collect(Collectors.toMap(StorageBlob::getId, Function.identity())); // 6. ç»è£ DTOæ°æ® List<QrCodeScanRecordDto> dtoList = scanRecordIPage.getRecords().stream().map(record -> { QrCodeScanRecordDto dto = new QrCodeScanRecordDto(); BeanUtils.copyProperties(record, dto); SysUser sysUser = sysUserMapper.selectUserById(record.getScannerId()); dto.setScanner(sysUser.getNickName()); // 6.1 设置äºç»´ç ä¿¡æ¯ Optional.ofNullable(qrCodeMap.get(record.getQrCodeId())) .ifPresent(qrCode -> { BeanUtils.copyProperties(qrCode, dto); // å¤å¶å°ç¶ç±» dto.setQrCode(qrCode); // è®¾ç½®å®æ´å¯¹è±¡ }); // 6.2 设置éä»¶ä¿¡æ¯ dto.setStorageBlobDTO( Optional.ofNullable(attachmentsMap.get(record.getId())) .orElse(Collections.emptyList()) .stream() .map(att -> { StorageBlobDTO blobDTO = new StorageBlobDTO(); Optional.ofNullable(blobMap.get(att.getStorageBlobId())) .ifPresent(blob -> { BeanUtils.copyProperties(blob, blobDTO); blobDTO.setUrl(minioUtils.getPreviewUrls(blob.getBucketFilename(), blob.getBucketName(), true)); blobDTO.setDownloadUrl(minioUtils.getDownloadUrls(blob.getBucketFilename(),blob.getBucketName(),blob.getOriginalFilename(),true)); }); return blobDTO; }) .filter(blobDTO -> blobDTO.getId() != null) // è¿æ»¤æ æéä»¶ .collect(Collectors.toList()) ); return dto; }).collect(Collectors.toList()); // 7. æå»ºè¿åå页对象 IPage<QrCodeScanRecordDto> resultPage = new Page<>(); BeanUtils.copyProperties(scanRecordIPage, resultPage); resultPage.setRecords(dtoList); return resultPage; } @Override public int addOrEditQrCodeRecord(QrCodeScanRecordDto qrCodeScanRecordDto) { QrCodeScanRecord qrCodeScanRecord = new QrCodeScanRecord(); BeanUtils.copyProperties(qrCodeScanRecordDto, qrCodeScanRecord); int i; if (Objects.isNull(qrCodeScanRecordDto.getId())) { i = qrCodeScanRecordMapper.insert(qrCodeScanRecord); } else { i = qrCodeScanRecordMapper.updateById(qrCodeScanRecord); } if (qrCodeScanRecordDto.getStorageBlobDTO() != null && !qrCodeScanRecordDto.getStorageBlobDTO().isEmpty()) { List<StorageAttachment> attachments = new ArrayList<>(); for (StorageBlobDTO storageBlobDTO : qrCodeScanRecordDto.getStorageBlobDTO()) { StorageAttachment storageAttachment = new StorageAttachment( StorageAttachmentFile, (long) QrCodeScanRecords.ordinal(), qrCodeScanRecord.getId() ); storageAttachment.setStorageBlobDTO(storageBlobDTO); attachments.add(storageAttachment); } storageAttachmentService.saveStorageAttachment(attachments, qrCodeScanRecord.getId(), QrCodeScanRecords, StorageAttachmentFile); } return i; } @Override public int delByIds(Long[] ids) { return qrCodeScanRecordMapper.deleteBatchIds(Arrays.asList(ids)); } } src/main/java/com/ruoyi/inspectiontask/service/impl/QrCodeServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,54 @@ package com.ruoyi.inspectiontask.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.inspectiontask.dto.QrCodeDto; import com.ruoyi.inspectiontask.mapper.QrCodeMapper; import com.ruoyi.inspectiontask.pojo.QrCode; import com.ruoyi.inspectiontask.service.QrCodeService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.Objects; /** * @author :yys * @date : 2025/9/19 10:55 */ @Service @Slf4j public class QrCodeServiceImpl extends ServiceImpl<QrCodeMapper, QrCode> implements QrCodeService { @Autowired private QrCodeMapper qrCodeMapper; @Override public IPage<QrCode> selectQrCodeList(Page page, QrCodeDto qrCodeDto) { LambdaQueryWrapper<QrCode> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.orderByDesc(QrCode::getCreateTime); return qrCodeMapper.selectPage(page, queryWrapper); } @Override public Long addOrEditQrCode(QrCodeDto qrCodeDto) { QrCode qrCode = new QrCode(); BeanUtils.copyProperties(qrCodeDto, qrCode); if (Objects.isNull(qrCodeDto.getId())) { qrCodeMapper.insert(qrCode); } else { qrCodeMapper.updateById(qrCode); } return qrCode.getId(); } @Override public int delByIds(Long[] ids) { return qrCodeMapper.deleteBatchIds(Arrays.asList(ids)); } } src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskJob.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,65 @@ package com.ruoyi.inspectiontask.service.impl; import com.ruoyi.inspectiontask.mapper.InspectionTaskMapper; import com.ruoyi.inspectiontask.pojo.InspectionTask; import com.ruoyi.inspectiontask.pojo.TimingTask; import com.ruoyi.inspectiontask.service.TimingTaskService; import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component @DisallowConcurrentExecution // ç¦æ¢å¹¶åæ§è¡åä¸ä¸ªJob public class TimingTaskJob implements Job { @Autowired private TimingTaskService timingTaskService; @Autowired private InspectionTaskMapper inspectionTaskMapper; @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); Long taskId = jobDataMap.getLong("taskId"); try { // 1. è·å宿¶ä»»å¡è¯¦æ TimingTask timingTask = timingTaskService.getById(taskId); if (timingTask == null || !timingTask.isActive()) { return; } // 2. å建并ä¿åå·¡æ£ä»»å¡è®°å½ - è¿å°±æ¯æ¨æä¾ç代ç åºè¯¥æ¾çä½ç½® InspectionTask inspectionTask = createInspectionTask(timingTask); inspectionTaskMapper.insert(inspectionTask); // 3. æ´æ°å®æ¶ä»»å¡çæ§è¡æ¶é´ timingTaskService.updateTaskExecutionTime(taskId); // 4. è®°å½æ§è¡æ¥å¿ // timingTaskService.recordExecutionLog(taskId, true, "任塿§è¡æåï¼çæå·¡æ£ä»»å¡ID: " + inspectionTask.getId()); } catch (Exception e) { // timingTaskService.recordExecutionLog(taskId, false, "任塿§è¡å¤±è´¥: " + e.getMessage()); throw new JobExecutionException(e); } } // è¿å°±æ¯æ¨æä¾ç代ç å°è£ æçæ¹æ³ private InspectionTask createInspectionTask(TimingTask timingTask) { InspectionTask inspectionTask = new InspectionTask(); // å¤å¶åºæ¬å±æ§ inspectionTask.setTaskName(timingTask.getTaskName()); inspectionTask.setInspectorId(timingTask.getInspectorIds()); inspectionTask.setInspectionLocation(timingTask.getInspectionLocation()); inspectionTask.setRemarks("èªå¨çæèªå®æ¶ä»»å¡ID: " + timingTask.getId()); inspectionTask.setRegistrantId(timingTask.getRegistrantId()); inspectionTask.setFrequencyType(timingTask.getFrequencyType()); inspectionTask.setFrequencyDetail(timingTask.getFrequencyDetail()); return inspectionTask; } } src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,234 @@ package com.ruoyi.inspectiontask.service.impl; import com.ruoyi.inspectiontask.pojo.TimingTask; import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.time.LocalTime; import java.time.ZoneId; import java.time.format.DateTimeParseException; import java.util.Arrays; import java.util.Date; import java.util.stream.Collectors; @Service public class TimingTaskScheduler { @Autowired private Scheduler scheduler; /** * æ·»å æ°ä»»å¡å°è°åº¦å¨ */ public void scheduleTimingTask(TimingTask task) throws SchedulerException { JobDetail jobDetail = buildJobDetail(task); Trigger trigger = buildJobTrigger(task, jobDetail); scheduler.scheduleJob(jobDetail, trigger); } /** * æ´æ°å·²æä»»å¡ */ public void rescheduleTimingTask(TimingTask task) throws SchedulerException { TriggerKey triggerKey = new TriggerKey("trigger_" + task.getId()); // è·åç°æè§¦åå¨å¹¶è½¬æ¢ä¸º CronTrigger Trigger oldTrigger = scheduler.getTrigger(triggerKey); if (!(oldTrigger instanceof CronTrigger)) { throw new SchedulerException("Existing trigger is not a CronTrigger"); } // æå»ºæ°è§¦åå¨ Trigger newTrigger = TriggerBuilder.newTrigger() .withIdentity(triggerKey) .withDescription(task.getTaskName()) .withSchedule(CronScheduleBuilder.cronSchedule(convertToCronExpression(task))) .startAt(Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())) .forJob(oldTrigger.getJobKey()) .build(); scheduler.rescheduleJob(triggerKey, newTrigger); } /** * æåä»»å¡ */ public void pauseTimingTask(Long taskId) throws SchedulerException { JobKey jobKey = new JobKey("timingTask_" + taskId); scheduler.pauseJob(jobKey); } /** * æ¢å¤ä»»å¡ */ public void resumeTimingTask(Long taskId) throws SchedulerException { JobKey jobKey = new JobKey("timingTask_" + taskId); scheduler.resumeJob(jobKey); } /** * å é¤ä»»å¡ */ public void unscheduleTimingTask(Long taskId) throws SchedulerException { JobKey jobKey = new JobKey("timingTask_" + taskId); scheduler.deleteJob(jobKey); } private JobDetail buildJobDetail(TimingTask task) { JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("taskId", task.getId()); return JobBuilder.newJob(TimingTaskJob.class) .withIdentity("timingTask_" + task.getId()) .withDescription(task.getTaskName()) .usingJobData(jobDataMap) .storeDurably() .build(); } private Trigger buildJobTrigger(TimingTask task, JobDetail jobDetail) { String cronExpression = convertToCronExpression(task); TriggerBuilder<CronTrigger> triggerBuilder = TriggerBuilder.newTrigger() .withIdentity("trigger_" + task.getId()) .withDescription(task.getTaskName()) .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)); if (jobDetail != null) { triggerBuilder.forJob(jobDetail); } if (task.getNextExecutionTime() != null) { triggerBuilder.startAt(Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())); } return triggerBuilder.build(); } private String convertToCronExpression(TimingTask task) { // åæ°æ ¡éª if (task == null || task.getFrequencyType() == null || task.getFrequencyDetail() == null) { throw new IllegalArgumentException("ä»»å¡åæ°ä¸è½ä¸ºç©º"); } // 使ç¨switchç¡®ä¿æ¡ä»¶äºæ¥ return switch (task.getFrequencyType().toUpperCase()) { // ç»ä¸è½¬ä¸ºå¤§åæ¯è¾ case "DAILY" -> convertDailyToCron(task.getFrequencyDetail()); case "WEEKLY" -> convertWeeklyToCron(task.getFrequencyDetail()); case "MONTHLY" -> convertMonthlyToCron(task.getFrequencyDetail()); case "QUARTERLY" -> convertQuarterlyToCron(task.getFrequencyDetail()); default -> throw new IllegalArgumentException("䏿¯æçé¢çç±»å: " + task.getFrequencyType()); }; } // æ¯æ¥ä»»å¡è½¬æ¢ private String convertDailyToCron(String frequencyDetail) { LocalTime time = parseTime(frequencyDetail); return String.format("0 %d %d * * ?", time.getMinute(), time.getHour()); } // æ¯å¨ä»»å¡è½¬æ¢ private String convertWeeklyToCron(String frequencyDetail) { String[] parts = validateAndSplit(frequencyDetail, ",", 2); String daysOfWeek = convertDayNamesToCron(parts[0]); LocalTime time = parseTime(parts[1]); return String.format("0 %d %d ? * %s", time.getMinute(), time.getHour(), daysOfWeek); } // æ¯æä»»å¡è½¬æ¢ private String convertMonthlyToCron(String frequencyDetail) { String[] parts = validateAndSplit(frequencyDetail, ",", 2); int day = validateDayOfMonth(parts[0]); LocalTime time = parseTime(parts[1]); return String.format("0 %d %d %d * ?", time.getMinute(), time.getHour(), day); } // æ¯å£åº¦ä»»å¡è½¬æ¢ private String convertQuarterlyToCron(String frequencyDetail) { String[] parts = validateAndSplit(frequencyDetail, ",", 3); int month = validateMonth(parts[0]); // éªè¯æä»½(1-12) int day = validateDayOfMonth(parts[1]); // éªè¯æ¥æ LocalTime time = parseTime(parts[2]); // è§£ææ¶é´ // 计ç®å£åº¦èµ·å§æä»½(1æ=1, 4æ=4, 7æ=7, 10æ=10) int quarterStartMonth = ((month - 1) / 3) * 3 + 1; return String.format("0 %d %d %d %d/3 ?", time.getMinute(), time.getHour(), day, quarterStartMonth); } // æ°å¢éªè¯æä»½çæ¹æ³(1-12) private int validateMonth(String monthStr) { try { int month = Integer.parseInt(monthStr); if (month < 1 || month > 12) { throw new IllegalArgumentException("æä»½å¿ é¡»å¨1-12ä¹é´"); } return month; } catch (NumberFormatException e) { throw new IllegalArgumentException("æ æçæä»½æ ¼å¼"); } } // è¾ å©æ¹æ³ï¼è§£ææ¶é´ private LocalTime parseTime(String timeStr) { try { return LocalTime.parse(timeStr); } catch (DateTimeParseException e) { throw new IllegalArgumentException("æ¶é´æ ¼å¼å¿ 须为HH:mm", e); } } // è¾ å©æ¹æ³ï¼éªè¯å¹¶åå²å符串 private String[] validateAndSplit(String input, String delimiter, int expectedParts) { String[] parts = input.split(delimiter); if (parts.length != expectedParts) { throw new IllegalArgumentException( String.format("æ ¼å¼é误ï¼åºä¸º%dé¨åç¨'%s'åé", expectedParts, delimiter)); } return parts; } // è¾ å©æ¹æ³ï¼éªè¯æä»½ä¸çæ¥ private int validateDayOfMonth(String dayStr) { int day = Integer.parseInt(dayStr); if (day < 1 || day > 31) { throw new IllegalArgumentException("æ¥æå¿ é¡»å¨1-31ä¹é´"); } return day; } // è¾ å©æ¹æ³ï¼éªè¯å£åº¦ä¸çæ private int validateMonthInQuarter(String monthStr) { int month = Integer.parseInt(monthStr); if (month < 1 || month > 3) { throw new IllegalArgumentException("å£åº¦æä»½å¿ é¡»æ¯1ã2æ3"); } return month; } // è½¬æ¢ææå åç§° private String convertDayNamesToCron(String dayNames) { return Arrays.stream(dayNames.split("\\|")) .map(this::convertSingleDayName) .collect(Collectors.joining(",")); } // 转æ¢å个ææå åç§° private String convertSingleDayName(String dayName) { switch (dayName.toUpperCase()) { case "MON": return "MON"; case "TUE": return "TUE"; case "WED": return "WED"; case "THU": return "THU"; case "FRI": return "FRI"; case "SAT": return "SAT"; case "SUN": return "SUN"; default: throw new IllegalArgumentException("æ æçææå : " + dayName); } } } src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,348 @@ package com.ruoyi.inspectiontask.service.impl; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.inspectiontask.dto.TimingTaskDto; import com.ruoyi.inspectiontask.mapper.InspectionTaskMapper; import com.ruoyi.inspectiontask.mapper.TimingTaskMapper; import com.ruoyi.inspectiontask.pojo.TimingTask; import com.ruoyi.inspectiontask.service.TimingTaskService; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysUserMapper; import lombok.extern.slf4j.Slf4j; import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.*; import java.util.*; import java.util.stream.Collectors; /** * @author :yys * @date : 2025/9/19 10:55 */ @Service @Slf4j public class TimingTaskServiceImpl extends ServiceImpl<TimingTaskMapper, TimingTask> implements TimingTaskService { @Autowired private TimingTaskMapper timingTaskMapper; @Autowired private InspectionTaskMapper inspectionTaskMapper; @Autowired private TimingTaskScheduler timingTaskScheduler; @Autowired private SysUserMapper sysUserMapper; @Override public IPage<TimingTaskDto> selectTimingTaskList(Page<TimingTask> page, TimingTask timingTask) { // 1. å å页æ¥è¯¢å®æ¶ä»»å¡æ°æ® IPage<TimingTask> taskPage = timingTaskMapper.selectPage(page, null); // 2. å¦ææ²¡ææ°æ®ï¼ç´æ¥è¿å空å页 if (taskPage.getRecords().isEmpty()) { return new Page<>(taskPage.getCurrent(), taskPage.getSize(), taskPage.getTotal()); } // 3. æ¶éææéè¦æ¥è¯¢çç¨æ·ID Set<Long> userIds = new HashSet<>(); // æ¶éç»è®°äººID taskPage.getRecords().forEach(task -> { if (task.getRegistrantId() != null) { userIds.add(task.getRegistrantId()); } }); // æ¶éå·¡æ£äººIDï¼å¤ä¸ªID以éå·åéï¼ taskPage.getRecords().forEach(task -> { if (StringUtils.isNotBlank(task.getInspectorIds())) { Arrays.stream(task.getInspectorIds().split(",")) .filter(StringUtils::isNotBlank) .map(Long::valueOf) .forEach(userIds::add); } }); // 4. æ¹éæ¥è¯¢ç¨æ·ä¿¡æ¯ Map<Long, String> userNickNameMap = new HashMap<>(); if (!userIds.isEmpty()) { List<SysUser> users = sysUserMapper.selectUserByIds((List<Long>) userIds); users.forEach(user -> userNickNameMap.put(user.getUserId(), user.getNickName())); } // 5. 转æ¢ä¸ºDTO List<TimingTaskDto> dtoList = taskPage.getRecords().stream().map(task -> { TimingTaskDto dto = new TimingTaskDto(); // å¤å¶åºæ¬å±æ§ BeanUtils.copyProperties(task, dto); // 设置ç»è®°äººæµç§° if (task.getRegistrantId() != null) { dto.setRegistrant(userNickNameMap.getOrDefault(task.getRegistrantId(), "æªç¥ç¨æ·")); } // 设置巡æ£äººæµç§°å表 if (StringUtils.isNotBlank(task.getInspectorIds())) { List<String> inspectorNickNames = Arrays.stream(task.getInspectorIds().split(",")) .filter(StringUtils::isNotBlank) .map(idStr -> { Long id = Long.valueOf(idStr); return userNickNameMap.getOrDefault(id, "æªç¥ç¨æ·"); }) .toList(); dto.setInspector(inspectorNickNames); } return dto; }).collect(Collectors.toList()); // 6. æå»ºè¿åçå页对象 Page<TimingTaskDto> resultPage = new Page<>(taskPage.getCurrent(), taskPage.getSize(), taskPage.getTotal()); resultPage.setRecords(dtoList); return resultPage; } @Override @Transactional public int addOrEditTimingTask(TimingTaskDto timingTaskDto) throws SchedulerException { TimingTask timingTask = new TimingTask(); BeanUtils.copyProperties(timingTaskDto, timingTask); // 设置å建人信æ¯åé»è®¤å¼ if (Objects.isNull(timingTaskDto.getId())) { timingTask.setRegistrationDate(LocalDate.now()); timingTask.setActive(true); // 计ç®é¦æ¬¡æ§è¡æ¶é´ LocalDateTime firstExecutionTime = calculateFirstExecutionTime(timingTask); timingTask.setNextExecutionTime(firstExecutionTime); int result = timingTaskMapper.insert(timingTask); if (result > 0) { // æ°å¢æååæ·»å å°è°åº¦å¨ timingTaskScheduler.scheduleTimingTask(timingTask); } return result; } else { int result = timingTaskMapper.updateById(timingTask); if (result > 0) { // æ´æ°æååéæ°è°åº¦ä»»å¡ timingTaskScheduler.rescheduleTimingTask(timingTask); } return result; } } private LocalDateTime calculateFirstExecutionTime(TimingTask task) { // æ ¹æ®é¢çç±»åå详æ 计ç®é¦æ¬¡æ§è¡æ¶é´ return switch (task.getFrequencyType()) { case "DAILY" -> // å¦ææ¯æ¯å¤©æ§è¡ï¼è®¡ç®ä»å¤©ææå¤©çå ·ä½æ¶é´ calculateDailyFirstExecution(task.getFrequencyDetail()); case "WEEKLY" -> // å¦ææ¯æ¯å¨æ§è¡ï¼è®¡ç®ä¸å¨çå ·ä½ææå calculateWeeklyFirstExecution(task.getFrequencyDetail()); case "MONTHLY" -> // å¦ææ¯æ¯ææ§è¡ï¼è®¡ç®ä¸ä¸ªæçå ·ä½æ¥æ calculateMonthlyFirstExecution(task.getFrequencyDetail()); case "QUARTERLY" -> // èªå®ä¹é¢çï¼å¦æ¯å°æ¶ãæ¯30åéç calculateCustomFirstExecution(task.getFrequencyDetail()); default -> throw new IllegalArgumentException("䏿¯æçé¢çç±»å: " + task.getFrequencyType()); }; } private LocalDateTime calculateDailyFirstExecution(String frequencyDetail) { // frequencyDetailå¯è½æ¯å ·ä½æ¶é´ï¼å¦ "14:30" LocalTime executionTime = LocalTime.parse(frequencyDetail); LocalDateTime now = LocalDateTime.now(); LocalDateTime todayExecution = LocalDateTime.of(now.toLocalDate(), executionTime); // 妿ä»å¤©çæ¶é´å·²è¿ï¼å宿æå¤©æ§è¡ return now.isBefore(todayExecution) ? todayExecution : todayExecution.plusDays(1); } private LocalDateTime calculateWeeklyFirstExecution(String frequencyDetail) { return null; } private LocalDateTime calculateMonthlyFirstExecution(String frequencyDetail) { return null; } private LocalDateTime calculateCustomFirstExecution(String frequencyDetail) { return null; } @Override @Transactional public void updateTaskExecutionTime(Long taskId) { TimingTask task = timingTaskMapper.selectById(taskId); if (task == null) { throw new RuntimeException("宿¶ä»»å¡ä¸åå¨ï¼ID: " + taskId); } // æ´æ°æåæ§è¡æ¶é´ä¸ºå½åæ¶é´ task.setLastExecutionTime(LocalDateTime.now()); // 计ç®ä¸æ¬¡æ§è¡æ¶é´ LocalDateTime nextExecutionTime = calculateNextExecutionTime( task.getFrequencyType(), task.getFrequencyDetail(), LocalDateTime.now() ); task.setNextExecutionTime(nextExecutionTime); // æ´æ°æ°æ®åº timingTaskMapper.updateById(task); } /** * 计ç®ä¸æ¬¡æ§è¡æ¶é´ */ private LocalDateTime calculateNextExecutionTime(String frequencyType, String frequencyDetail, LocalDateTime currentTime) { try { return switch (frequencyType) { case "DAILY" -> calculateDailyNextTime(frequencyDetail, currentTime); case "WEEKLY" -> calculateWeeklyNextTime(frequencyDetail, currentTime); case "MONTHLY" -> calculateMonthlyNextTime(frequencyDetail, currentTime); case "QUARTERLY" -> calculateQuarterlyNextTime(frequencyDetail, currentTime); default -> throw new IllegalArgumentException("䏿¯æçé¢çç±»å: " + frequencyType); }; } catch (Exception e) { throw new RuntimeException("计ç®ä¸æ¬¡æ§è¡æ¶é´å¤±è´¥: " + e.getMessage(), e); } } /** * è®¡ç®æ¯æ¥ä»»å¡ç䏿¬¡æ§è¡æ¶é´ */ private LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime current) { LocalTime executionTime = LocalTime.parse(timeStr); // è§£ææ ¼å¼ "HH:mm" LocalDateTime nextTime = LocalDateTime.of(current.toLocalDate(), executionTime); // 妿ä»å¤©çæ¶é´å·²è¿ï¼å宿æå¤© return current.isBefore(nextTime) ? nextTime : nextTime.plusDays(1); } /** * è®¡ç®æ¯å¨ä»»å¡ç䏿¬¡æ§è¡æ¶é´ */ private LocalDateTime calculateWeeklyNextTime(String detail, LocalDateTime current) { String[] parts = detail.split(","); String dayOfWeekStr = parts[0]; // å¦ "MON" æ "MON|WED|FRI" LocalTime time = LocalTime.parse(parts[1]); // æ¶é´é¨å // è§£æææå (æ¯æå¤ä¸ªææ) Set<DayOfWeek> targetDays = parseDayOfWeeks(dayOfWeekStr); // ä»å½åæ¶é´å¼å§æ¾ä¸ä¸ä¸ªç¬¦åæ¡ä»¶çææå LocalDateTime nextTime = current; while (true) { nextTime = nextTime.plusDays(1); if (targetDays.contains(nextTime.getDayOfWeek())) { return LocalDateTime.of(nextTime.toLocalDate(), time); } // 鲿¢æ é循ç¯(ç论ä¸ä¸ä¼åç) if (nextTime.isAfter(current.plusYears(1))) { throw new RuntimeException("æ æ³æ¾å°ä¸æ¬¡æ§è¡æ¶é´"); } } } /** * è®¡ç®æ¯æä»»å¡ç䏿¬¡æ§è¡æ¶é´ */ private LocalDateTime calculateMonthlyNextTime(String detail, LocalDateTime current) { String[] parts = detail.split(","); int dayOfMonth = Integer.parseInt(parts[0]); LocalTime time = LocalTime.parse(parts[1]); // ä»ä¸ä¸ªæå¼å§è®¡ç® LocalDateTime nextTime = current.plusMonths(1) .withDayOfMonth(Math.min(dayOfMonth, current.plusMonths(1).toLocalDate().lengthOfMonth())) .with(time); return nextTime; } /** * è®¡ç®æ¯å£åº¦ä»»å¡ç䏿¬¡æ§è¡æ¶é´ */ private LocalDateTime calculateQuarterlyNextTime(String detail, LocalDateTime current) { String[] parts = detail.split(","); int quarterMonth = Integer.parseInt(parts[0]); // 1=第1个æï¼2=第2个æï¼3=第3个æ int dayOfMonth = Integer.parseInt(parts[1]); LocalTime time = LocalTime.parse(parts[2]); // 计ç®å½åå£åº¦ int currentQuarter = (current.getMonthValue() - 1) / 3 + 1; int currentMonthInQuarter = (current.getMonthValue() - 1) % 3 + 1; YearMonth targetYearMonth; if (currentMonthInQuarter < quarterMonth) { // æ¬å£åº¦å è¿ææ§è¡æºä¼ targetYearMonth = YearMonth.from(current) .plusMonths(quarterMonth - currentMonthInQuarter); } else { // éè¦å°ä¸ä¸ªå£åº¦ targetYearMonth = YearMonth.from(current) .plusMonths(3 - currentMonthInQuarter + quarterMonth); } // å¤çææ«æ¥æ int adjustedDay = Math.min(dayOfMonth, targetYearMonth.lengthOfMonth()); return LocalDateTime.of( targetYearMonth.getYear(), targetYearMonth.getMonthValue(), adjustedDay, time.getHour(), time.getMinute() ); } /** * è§£æææå å符串 */ private Set<DayOfWeek> parseDayOfWeeks(String dayOfWeekStr) { Set<DayOfWeek> days = new HashSet<>(); String[] dayStrs = dayOfWeekStr.split("\\|"); for (String dayStr : dayStrs) { switch (dayStr) { case "MON": days.add(DayOfWeek.MONDAY); break; case "TUE": days.add(DayOfWeek.TUESDAY); break; case "WED": days.add(DayOfWeek.WEDNESDAY); break; case "THU": days.add(DayOfWeek.THURSDAY); break; case "FRI": days.add(DayOfWeek.FRIDAY); break; case "SAT": days.add(DayOfWeek.SATURDAY); break; case "SUN": days.add(DayOfWeek.SUNDAY); break; default: throw new IllegalArgumentException("æ æçææå : " + dayStr); } } return days; } @Override public int delByIds(Long[] ids) { return timingTaskMapper.deleteBatchIds(Arrays.asList(ids)); } } src/main/java/com/ruoyi/procurementrecord/controller/ProcurementPlanController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,67 @@ package com.ruoyi.procurementrecord.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.framework.web.controller.BaseController; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.procurementrecord.pojo.ProcurementPlan; import com.ruoyi.procurementrecord.service.ProcurementPlanService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * @author :yys * @date : 2025/9/18 16:13 */ @RestController @Api(tags = "éè´è®¡å") @RequestMapping("/procurementPlan") public class ProcurementPlanController extends BaseController { @Autowired private ProcurementPlanService procurementPlanService; @RequestMapping("/listPage") @ApiOperation("éè´è®¡å-æ¥è¯¢") public AjaxResult listPage(Page page, ProcurementPlan procurementPlan){ IPage<ProcurementPlan> result = procurementPlanService.listPage(page, procurementPlan); return AjaxResult.success(result); } @PostMapping("/add") @ApiOperation("éè´è®¡å-æ·»å ") public AjaxResult add(@RequestBody ProcurementPlan procurementPlan){ boolean result = procurementPlanService.save(procurementPlan); return result ? AjaxResult.success() : AjaxResult.error(); } @PostMapping("/update") @ApiOperation("éè´è®¡å-ä¿®æ¹") public AjaxResult update(@RequestBody ProcurementPlan procurementPlan){ boolean result = procurementPlanService.updateById(procurementPlan); return result ? AjaxResult.success() : AjaxResult.error(); } @DeleteMapping("/del") @ApiOperation("éè´è®¡å-å é¤") public AjaxResult del(@RequestBody List<Long> ids){ boolean result = procurementPlanService.removeByIds(ids); return result ? AjaxResult.success() : AjaxResult.error(); } /** * å¯¼åº * @param response */ @PostMapping("/export") public void export(HttpServletResponse response) { procurementPlanService.export(response); } } src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementPlanMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,22 @@ package com.ruoyi.procurementrecord.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.procurementrecord.pojo.ProcurementPlan; import org.apache.ibatis.annotations.Param; /** * @author :yys * @date : 2025/9/18 16:10 */ public interface ProcurementPlanMapper extends BaseMapper<ProcurementPlan> { /** * æ¥è¯¢éè´è®¡åå表 * * @param page * @param procurementPlan * @return */ IPage<ProcurementPlan> listPage(Page page,@Param("req") ProcurementPlan procurementPlan); } src/main/java/com/ruoyi/procurementrecord/pojo/ProcurementPlan.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,105 @@ package com.ruoyi.procurementrecord.pojo; import com.baomidou.mybatisplus.annotation.*; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.framework.aspectj.lang.annotation.Excel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; /** * @author :yys * @date : 2025/9/18 16:00 */ @Data @TableName("procurement_plan") @ApiModel public class ProcurementPlan { private static final long serialVersionUID = 1L; /** * åºå· */ @TableId(type = IdType.AUTO) private Long id; @ApiModelProperty(value = "ç¼ç ") @Excel(name = "ç¼ç ") private String code; @ApiModelProperty(value = "åç§°") @Excel(name = "åç§°") private String planName; @ApiModelProperty(value = "æè¿°") @Excel(name = "æè¿°") private String description; @ApiModelProperty(value = "ç¶æ") @Excel(name = "ç¶æ", readConverterExp = "disabled=ç¦ç¨,active=å¯ç¨") private String status; @ApiModelProperty(value = "æ¯å¦ç³»ç»é¢ç½®") private Boolean isSystemPreset; @ApiModelProperty(value = "èèç°æåºå") private Boolean considerExistingStock; @ApiModelProperty(value = "ä»åºè¿è¡MRPçæ§å¶") private Boolean warehouseControl; @ApiModelProperty(value = "è®¡ç®æ»éæ±") private Boolean calculateTotalDemand; @ApiModelProperty(value = "èèå®å ¨åºå") private Boolean considerSafetyStock; @ApiModelProperty(value = "èèéåº") private Boolean considerLockedStock; @ApiModelProperty(value = "ä¸èèç©æè¾ å©å±æ§") private Boolean notConsiderMaterialAux; @ApiModelProperty(value = "è´åºåä½ä¸ºéæ±") private Boolean negativeStockAsDemand; @ApiModelProperty(value = "ç©æ") private Boolean summaryMaterial; @ApiModelProperty(value = "è¾ å©å±æ§") private Boolean summaryAuxAttributes; @ApiModelProperty(value = "éæ±æ¥æ") private Boolean summaryDemandDate; @ApiModelProperty(value = "计ç®å ¬å¼") @Excel(name = "计ç®å ¬å¼") private String formula; @ApiModelProperty(value = "å建æ¶é´") @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @ApiModelProperty(value = "åå»ºç¨æ·") @TableField(fill = FieldFill.INSERT) private Integer createUser; @ApiModelProperty(value = "ä¿®æ¹æ¶é´") @TableField(fill = FieldFill.INSERT_UPDATE) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Excel(name = "æåè®¡ç®æ¶é´", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; @ApiModelProperty(value = "ä¿®æ¹ç¨æ·") @TableField(fill = FieldFill.INSERT_UPDATE) private Integer updateUser; @ApiModelProperty(value = "ç§æ·ID") @TableField(fill = FieldFill.INSERT) private Long tenantId; } src/main/java/com/ruoyi/procurementrecord/service/ProcurementPlanService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,25 @@ package com.ruoyi.procurementrecord.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.procurementrecord.pojo.ProcurementPlan; import javax.servlet.http.HttpServletResponse; /** * @author :yys * @date : 2025/9/18 16:11 */ public interface ProcurementPlanService extends IService<ProcurementPlan> { /** * æ¥è¯¢ * @param page * @param procurementPlan * @return */ IPage<ProcurementPlan> listPage(Page page, ProcurementPlan procurementPlan); void export(HttpServletResponse response); } src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementPlanServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,41 @@ package com.ruoyi.procurementrecord.service.impl; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.procurementrecord.mapper.ProcurementPlanMapper; import com.ruoyi.procurementrecord.pojo.ProcurementPlan; import com.ruoyi.procurementrecord.pojo.ProcurementPriceManagement; import com.ruoyi.procurementrecord.service.ProcurementPlanService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * @author :yys * @date : 2025/9/18 16:12 */ @Service @Slf4j public class ProcurementPlanServiceImpl extends ServiceImpl<ProcurementPlanMapper, ProcurementPlan> implements ProcurementPlanService { @Autowired private ProcurementPlanMapper procurementPlanMapper; @Override public IPage<ProcurementPlan> listPage(Page page, ProcurementPlan procurementPlan) { IPage<ProcurementPlan> result = procurementPlanMapper.listPage(page, procurementPlan); return result; } @Override public void export(HttpServletResponse response) { List<ProcurementPlan> procurementPriceManagements = procurementPlanMapper.selectList(null); ExcelUtil<ProcurementPlan> util = new ExcelUtil<ProcurementPlan>(ProcurementPlan.class); util.exportExcel(response, procurementPriceManagements, "éè´è®¡å"); } } src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java
@@ -1,10 +1,13 @@ package com.ruoyi.project.system.mapper; import java.util.ArrayList; import java.util.List; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; import com.ruoyi.project.system.domain.SysUser; import org.springframework.beans.PropertyValues; /** * ç¨æ·è¡¨ æ°æ®å± @@ -134,4 +137,8 @@ * @return ç»æ */ public SysUser checkEmailUnique(String email); List<SysUser> selectList(List<Long> registrantIds); List<SysUser> selectUsersByIds(@Param("userIds") List<Long> userIds); } src/main/resources/mapper/procurementrecord/ProcurementPlanMapper.xml
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,16 @@ <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ruoyi.procurementrecord.mapper.ProcurementPlanMapper"> <select id="listPage" resultType="com.ruoyi.procurementrecord.pojo.ProcurementPlan"> SELECT * FROM procurement_plan <where> <if test="req.status != null and req.status != ''"> AND status = #{req.status} </if> <if test="req.planName != null and req.planName != ''"> AND plan_name LIKE CONCAT('%',#{req.planName},'%') </if> </where> </select> </mapper> src/main/resources/mapper/system/SysUserMapper.xml
@@ -158,6 +158,28 @@ #{item} </foreach> </select> <select id="selectList" resultType="com.ruoyi.project.system.domain.SysUser"> SELECT user_id, nick_name FROM sys_user <where> <if test="list != null and list.size() > 0"> user_id IN <foreach item="id" collection="list" open="(" separator="," close=")"> #{id} </foreach> </if> <if test="list == null or list.size() == 0"> 1=0 <!-- 空å表æ¶è¿åç©ºç»æ --> </if> </where> </select> <select id="selectUsersByIds" resultType="com.ruoyi.project.system.domain.SysUser"> SELECT user_id, nick_name FROM sys_user WHERE user_id IN <foreach collection="userIds" item="id" open="(" separator="," close=")"> #{id} </foreach> </select> <insert id="insertUser" parameterType="com.ruoyi.project.system.domain.SysUser" useGeneratedKeys="true" keyProperty="userId"> insert into sys_user(