From 99f528e235f11126fea44480c6e8888a9e463f2f Mon Sep 17 00:00:00 2001
From: wlzboy <66905212@qq.com>
Date: 星期六, 08 十一月 2025 21:09:53 +0800
Subject: [PATCH] feat:任务附件上传和同步
---
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java | 89 +++++--
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskAttachmentMapper.java | 11
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacySystemSyncTask.java | 28 ++
ruoyi-ui/src/views/task/general/detail.vue | 170 +++++++++++--
ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskAttachmentController.java | 2
ruoyi-system/src/main/java/com/ruoyi/system/service/ITaskAttachmentSyncService.java | 10
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskAttachmentSyncServiceImpl.java | 107 ++++++++
ruoyi-system/src/main/resources/mapper/system/SysTaskAttachmentMapper.xml | 26 ++
sql/attachment_sync_job.sql | 239 +++++++++++++++++++
sql/sys_attachment_category_dict.sql | 14 +
10 files changed, 639 insertions(+), 57 deletions(-)
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskAttachmentController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskAttachmentController.java
index 7175013..39189cc 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskAttachmentController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskAttachmentController.java
@@ -80,7 +80,7 @@
@PostMapping("/upload/{taskId}")
public AjaxResult upload(@PathVariable("taskId") Long taskId,
@RequestParam("file") MultipartFile file,
- @RequestParam(value = "category", required = false) String category) {
+ @RequestParam(value = "category", required = true) String category) {
try {
Long attachmentId= sysTaskService.uploadAttachment(taskId, file, category);
if (attachmentId > 0) {
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacySystemSyncTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacySystemSyncTask.java
index 89bc162..bd8c852 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacySystemSyncTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/LegacySystemSyncTask.java
@@ -29,6 +29,9 @@
@Autowired
private ITaskStatusPushService taskStatusPushService;
+
+ @Autowired
+ private ITaskAttachmentSyncService taskAttachmentSyncService;
@@ -131,4 +134,29 @@
log.error("浠诲姟鐘舵�佹帹閫佸紓甯�", e);
}
}
+
+ /**
+ * 鎵归噺鍚屾浠诲姟闄勪欢鍒版棫绯荤粺ImageData琛�
+ *
+ * 浣跨敤绀轰緥:
+ * 鍦ㄧ郴缁熺鐞� -> 瀹氭椂浠诲姟涓坊鍔�:
+ * 浠诲姟鍚嶇О: 浠诲姟闄勪欢鍚屾
+ * 浠诲姟缁勫悕: DEFAULT
+ * 璋冪敤鐩爣瀛楃涓�: legacySystemSyncTask.syncPendingAttachments()
+ * cron琛ㄨ揪寮�: 0 0/5 * * * ? (姣�5鍒嗛挓鎵ц涓�娆�)
+ *
+ * 鍚屾鏉′欢:
+ * 1. 鎵�灞炰换鍔$殑璋冨害鍗曞凡鍚屾鎴愬姛 (dispatch_sync_status = 2)
+ * 2. 闄勪欢鏈悓姝� (synced_to_image_data = 0 鎴� null)
+ * 3. 鏈夎皟搴﹀崟ID鍜屾湇鍔″崟ID
+ */
+ public void syncPendingAttachments() {
+ log.info("寮�濮嬫墽琛屼换鍔¢檮浠跺悓姝ュ畾鏃朵换鍔�");
+ try {
+ int successCount = taskAttachmentSyncService.batchSyncPendingAttachments();
+ log.info("浠诲姟闄勪欢鍚屾瀹屾垚锛屾垚鍔熷悓姝�: {} 涓檮浠�", successCount);
+ } catch (Exception e) {
+ log.error("浠诲姟闄勪欢鍚屾寮傚父", e);
+ }
+ }
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskAttachmentMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskAttachmentMapper.java
index 41f51ba..72a548c 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskAttachmentMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTaskAttachmentMapper.java
@@ -74,4 +74,15 @@
* @return 缁撴灉
*/
public int deleteSysTaskAttachmentByTaskId(Long taskId);
+
+ /**
+ * 鏌ヨ寰呭悓姝ョ殑浠诲姟闄勪欢鍒楄〃
+ * 鏌ヨ鏉′欢锛�
+ * 1. 鎵�灞炰换鍔$殑璋冨害鍗曞凡鍚屾鎴愬姛 (dispatch_sync_status = 2)
+ * 2. 闄勪欢鏈悓姝� (synced_to_image_data = 0 鎴� null)
+ * 3. 鏈夎皟搴﹀崟ID鍜屾湇鍔″崟ID
+ *
+ * @return 寰呭悓姝ョ殑闄勪欢鍒楄〃
+ */
+ public List<SysTaskAttachment> selectPendingSyncAttachments();
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ITaskAttachmentSyncService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ITaskAttachmentSyncService.java
index fc2545e..8be9b6e 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ITaskAttachmentSyncService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ITaskAttachmentSyncService.java
@@ -32,5 +32,15 @@
*/
List<SysTaskAttachment> syncTaskAttachmentsToImageData(List<SysTaskAttachment> attachmentList, Long serviceOrderId, Long dispatchOrdId, Integer oaUserId);
+ /**
+ * 鎵归噺鍚屾寰呭悓姝ョ殑浠诲姟闄勪欢鍒癐mageData
+ * 鏌ヨ鏉′欢锛�
+ * 1. 鎵�灞炰换鍔$殑璋冨害鍗曞凡鍚屾鎴愬姛
+ * 2. 闄勪欢鏈悓姝�
+ * 3. 鏈夎皟搴﹀崟ID鍜屾湇鍔″崟ID
+ *
+ * @return 鎴愬姛鍚屾鐨勯檮浠舵暟閲�
+ */
+ int batchSyncPendingAttachments();
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java
index 593f13c..508eb71 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java
@@ -1,15 +1,12 @@
package com.ruoyi.system.service.impl;
+import java.io.*;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
@@ -518,7 +515,9 @@
public Long uploadAttachment(Long taskId, MultipartFile file, String category) {
try {
// 涓婁紶鏂囦欢锛岃繑鍥炵浉瀵硅矾寰勶紙濡傦細/task/2025/01/15/xxx.jpg锛�
- String fileName = FileUploadUtils.upload("/task", file);
+ String fileName = category+"_"+System.currentTimeMillis()+"_"+file.getOriginalFilename();
+
+ fileName=saveLocalPath(fileName,file.getInputStream());
SysTaskAttachment attachment = new SysTaskAttachment();
attachment.setTaskId(taskId);
@@ -543,7 +542,7 @@
}
- return result;
+ return attachment.getAttachmentId();
} catch (IOException e) {
throw new RuntimeException("鏂囦欢涓婁紶澶辫触锛�" + e.getMessage());
}
@@ -577,26 +576,8 @@
String fileName = "wx_" + mediaId.substring(0, Math.min(20, mediaId.length())) + "_" + System.currentTimeMillis() + ".jpg";
// 淇濆瓨鍒版湰鍦�
- String baseDir = FileUploadUtils.getDefaultBaseDir();
- String datePath = DateUtils.datePath();
- String uploadDir = baseDir + "/task/" + datePath;
-
- // 鍒涘缓鐩綍
- File uploadPath = new File(uploadDir);
- if (!uploadPath.exists()) {
- uploadPath.mkdirs();
- }
-
- // 淇濆瓨鏂囦欢
- String filePath = uploadDir + "/" + fileName;
- File file = new File(filePath);
- try (FileOutputStream fos = new FileOutputStream(file)) {
- fos.write(fileBytes);
- }
-
- // 鐢熸垚鐩稿璺緞锛堜笉鍖呭惈baseDir锛�
- String relativeFilePath = "/task/" + datePath + "/" + fileName;
-
+ String relativeFilePath = saveLocalPath(fileName, fileBytes);
+
// 淇濆瓨闄勪欢璁板綍
SysTaskAttachment attachment = new SysTaskAttachment();
attachment.setTaskId(taskId);
@@ -626,7 +607,61 @@
throw new RuntimeException("浠庡井淇′笂浼犳枃浠跺け璐ワ細" + e.getMessage());
}
}
-
+
+ private static String saveLocalPath(String fileName, byte[] fileBytes) throws IOException {
+ String baseDir = FileUploadUtils.getDefaultBaseDir();
+ String datePath = DateUtils.datePath();
+ String uploadDir = baseDir + "/task/" + datePath;
+
+ // 鍒涘缓鐩綍
+ File uploadPath = new File(uploadDir);
+ if (!uploadPath.exists()) {
+ uploadPath.mkdirs();
+ }
+
+ // 淇濆瓨鏂囦欢
+ String filePath = uploadDir + "/" + fileName;
+ File file = new File(filePath);
+ try (FileOutputStream fos = new FileOutputStream(file)) {
+ fos.write(fileBytes);
+ }
+
+ // 鐢熸垚鐩稿璺緞锛堜笉鍖呭惈baseDir锛�
+ String relativeFilePath = "/task/" + datePath + "/" + fileName;
+ return relativeFilePath;
+ }
+
+ private String saveLocalPath(String fileName,InputStream stream){
+ String baseDir = FileUploadUtils.getDefaultBaseDir();
+ String datePath = DateUtils.datePath();
+ String uploadDir = baseDir + "/task/" + datePath;
+
+ // 鍒涘缓鐩綍
+ File uploadPath = new File(uploadDir);
+ if (!uploadPath.exists()) {
+ uploadPath.mkdirs();
+ }
+
+ // 淇濆瓨鏂囦欢
+ String filePath = uploadDir + "/" + fileName;
+ //灏唅nputstream鍐欏叆鏂囦欢
+ try (OutputStream os = new FileOutputStream(filePath)) {
+ byte[] buffer = new byte[1024]; // 缂撳啿鍖猴紝鍑忓皯 IO 娆℃暟
+ int bytesRead;
+ // 寰幆璇诲彇杈撳叆娴佷腑鐨勬暟鎹紝鍐欏叆杈撳嚭娴�
+ while ((bytesRead = stream.read(buffer)) != -1) {
+ os.write(buffer, 0, bytesRead);
+ }
+ os.flush(); // 寮哄埗鍒锋柊缂撳啿鍖猴紝纭繚鏁版嵁鍐欏叆鏂囦欢
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // 鐢熸垚鐩稿璺緞锛堜笉鍖呭惈baseDir锛�
+ String relativeFilePath = "/task/" + datePath + "/" + fileName;
+ return relativeFilePath;
+ }
+
/**
* 浠� URL 涓嬭浇鏂囦欢
*/
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskAttachmentSyncServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskAttachmentSyncServiceImpl.java
index 4b4ea83..19ceac4 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskAttachmentSyncServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskAttachmentSyncServiceImpl.java
@@ -6,6 +6,7 @@
import com.ruoyi.common.enums.DataSourceType;
import com.ruoyi.system.domain.ImageData;
import com.ruoyi.system.domain.SysTaskAttachment;
+import com.ruoyi.system.domain.SysTaskEmergency;
import com.ruoyi.system.file.FileUploadResponse;
import com.ruoyi.system.file.IFileUploadService;
import com.ruoyi.system.imagedata.IImageDataService;
@@ -29,12 +30,13 @@
* @author ruoyi
*/
@Service
-@DataSource(DataSourceType.SQLSERVER)
public class TaskAttachmentSyncServiceImpl implements ITaskAttachmentSyncService {
private static final Logger log = LoggerFactory.getLogger(TaskAttachmentSyncServiceImpl.class);
-
+ @Autowired
+ private SysTaskAttachmentMapper taskAttachmentMapper;
+
@Autowired
private IImageDataService imageDataService;
@@ -166,6 +168,107 @@
return null;
}
+ /**
+ * 鎵归噺鍚屾寰呭悓姝ョ殑浠诲姟闄勪欢鍒癐mageData
+ */
+ @Override
+ public int batchSyncPendingAttachments() {
+ log.info("寮�濮嬫墽琛屾壒閲忛檮浠跺悓姝ヤ换鍔�");
+
+ try {
+ // 鏌ヨ寰呭悓姝ョ殑闄勪欢鍒楄〃
+ List<SysTaskAttachment> pendingAttachments = taskAttachmentMapper.selectPendingSyncAttachments();
+
+ if (pendingAttachments == null || pendingAttachments.isEmpty()) {
+ log.info("娌℃湁寰呭悓姝ョ殑闄勪欢");
+ return 0;
+ }
+
+ log.info("鏌ヨ鍒� {} 涓緟鍚屾鐨勯檮浠�", pendingAttachments.size());
+
+ // 鎸変换鍔D鍒嗙粍鍚屾
+ int successCount = 0;
+ Long currentTaskId = null;
+ Long serviceOrderId = null;
+ Long dispatchOrdId = null;
+ Integer oaUserId = null;
+
+ for (SysTaskAttachment attachment : pendingAttachments) {
+ try {
+ // 濡傛灉鏄柊浠诲姟锛岄渶瑕佽幏鍙栦换鍔′俊鎭�
+ if (!attachment.getTaskId().equals(currentTaskId)) {
+ currentTaskId = attachment.getTaskId();
+ // 閫氳繃鑱旇〃鏌ヨ宸茬粡鍖呭惈浜嗕换鍔′俊鎭紝杩欓噷闇�瑕佸崟鐙煡璇�
+ SysTaskEmergency emergencyInfo = getEmergencyInfoByTaskId(currentTaskId);
+ if (emergencyInfo != null) {
+ serviceOrderId = emergencyInfo.getLegacyServiceOrdId();
+ dispatchOrdId = emergencyInfo.getLegacyDispatchOrdId();
+ // 鑾峰彇浠诲姟鍒涘缓浜虹殑OA鐢ㄦ埛ID
+ oaUserId = getCreatorOaUserId(currentTaskId);
+ } else {
+ log.warn("浠诲姟ID={} 鐨勬�ユ晳杞繍淇℃伅涓虹┖锛岃烦杩�", currentTaskId);
+ continue;
+ }
+ }
+
+ // 鍚屾鍗曚釜闄勪欢
+ Long imageDataId = syncAttachmentToImageData(attachment, serviceOrderId, dispatchOrdId, oaUserId);
+
+ if (imageDataId != null && imageDataId > 0) {
+ // 鏇存柊闄勪欢鍚屾鐘舵��
+ attachment.setSyncedToImageData(1);
+ attachment.setSyncTime(new Date());
+ attachment.setImageDataId(imageDataId);
+ taskAttachmentMapper.updateSysTaskAttachment(attachment);
+
+ successCount++;
+ log.info("闄勪欢ID={} 鍚屾鎴愬姛锛孖mageDataId={}",
+ attachment.getAttachmentId(), imageDataId);
+ }
+ } catch (Exception e) {
+ log.error("鍚屾闄勪欢ID={} 澶辫触", attachment.getAttachmentId(), e);
+ // 缁х画澶勭悊涓嬩竴涓檮浠�
+ }
+ }
+
+ log.info("闄勪欢鍚屾瀹屾垚锛屾垚鍔熷悓姝� {}/{} 涓檮浠�", successCount, pendingAttachments.size());
+ return successCount;
+
+ } catch (Exception e) {
+ log.error("鎵归噺闄勪欢鍚屾寮傚父", e);
+ return 0;
+ }
+ }
+
+ /**
+ * 鏍规嵁浠诲姟ID鑾峰彇鎬ユ晳杞繍淇℃伅
+ */
+ @Autowired
+ private com.ruoyi.system.mapper.SysTaskEmergencyMapper taskEmergencyMapper;
+
+ private SysTaskEmergency getEmergencyInfoByTaskId(Long taskId) {
+ return taskEmergencyMapper.selectSysTaskEmergencyByTaskId(taskId);
+ }
+
+ /**
+ * 鑾峰彇浠诲姟鍒涘缓浜虹殑OA鐢ㄦ埛ID
+ */
+ @Autowired
+ private com.ruoyi.system.mapper.SysTaskMapper taskMapper;
+
+ @Autowired
+ private com.ruoyi.system.mapper.SysUserMapper userMapper;
+
+ private Integer getCreatorOaUserId(Long taskId) {
+ com.ruoyi.system.domain.SysTask task = taskMapper.selectSysTaskByTaskId(taskId);
+ if (task != null && task.getCreatorId() != null) {
+ com.ruoyi.common.core.domain.entity.SysUser user = userMapper.selectUserById(task.getCreatorId());
+ if (user != null) {
+ return user.getOaUserId();
+ }
+ }
+ return null;
+ }
/**
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysTaskAttachmentMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysTaskAttachmentMapper.xml
index 05a426a..c017f7b 100644
--- a/ruoyi-system/src/main/resources/mapper/system/SysTaskAttachmentMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/SysTaskAttachmentMapper.xml
@@ -109,4 +109,30 @@
<delete id="deleteSysTaskAttachmentByTaskId" parameterType="Long">
delete from sys_task_attachment where task_id = #{taskId}
</delete>
+
+ <!-- 鏌ヨ寰呭悓姝ョ殑浠诲姟闄勪欢鍒楄〃 -->
+ <select id="selectPendingSyncAttachments" resultMap="SysTaskAttachmentResult">
+ SELECT
+ a.attachment_id,
+ a.task_id,
+ a.file_name,
+ a.file_path,
+ a.file_size,
+ a.file_type,
+ a.attachment_category,
+ a.upload_time,
+ a.upload_by,
+ a.synced_to_image_data,
+ a.sync_time,
+ a.image_data_id
+ FROM sys_task_attachment a
+ INNER JOIN sys_task t ON a.task_id = t.task_id
+ INNER JOIN sys_task_emergency e ON t.task_id = e.task_id
+ WHERE t.task_type = 'EMERGENCY_TRANSFER'
+ AND e.dispatch_sync_status = 2
+ AND e.legacy_dispatch_ord_id IS NOT NULL
+ AND e.legacy_service_ord_id IS NOT NULL
+ AND (a.synced_to_image_data = 0 OR a.synced_to_image_data IS NULL)
+ ORDER BY a.upload_time ASC
+ </select>
</mapper>
diff --git a/ruoyi-ui/src/views/task/general/detail.vue b/ruoyi-ui/src/views/task/general/detail.vue
index 0450def..3685884 100644
--- a/ruoyi-ui/src/views/task/general/detail.vue
+++ b/ruoyi-ui/src/views/task/general/detail.vue
@@ -203,11 +203,38 @@
</div>
<el-table :data="taskDetail.attachments" v-loading="attachmentLoading">
- <el-table-column label="鏂囦欢鍚�" align="center" prop="fileName" />
- <el-table-column label="鏂囦欢绫诲瀷" align="center" prop="fileType" />
- <el-table-column label="鏂囦欢澶у皬" align="center" prop="fileSize">
+ <el-table-column label="缂╃暐鍥�" align="center" width="120">
<template slot-scope="scope">
- <span>{{ formatFileSize(scope.row.fileSize) }}</span>
+ <el-image
+ v-if="isImage(scope.row.fileType)"
+ :src="scope.row.fileUrl"
+ :preview-src-list="[scope.row.fileUrl]"
+ fit="cover"
+ style="width: 80px; height: 80px; border-radius: 4px; cursor: pointer;"
+ >
+ <div slot="error" class="image-slot">
+ <i class="el-icon-picture-outline" style="font-size: 40px; color: #C0C4CC;"></i>
+ </div>
+ </el-image>
+ <div v-else style="text-align: center;">
+ <i class="el-icon-document" style="font-size: 40px; color: #909399;"></i>
+ </div>
+ </template>
+ </el-table-column>
+ <el-table-column label="涓氬姟鍒嗙被" align="center" prop="attachmentCategory" width="150">
+ <template slot-scope="scope">
+ <dict-tag :options="dict.type.sys_attachment_category" :value="scope.row.attachmentCategory"/>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍚屾鐘舵��" align="center" width="120">
+ <template slot-scope="scope">
+ <el-tag v-if="scope.row.syncedToImageData === 0" type="info" size="small">
+ <i class="el-icon-warning"></i> 鏈悓姝�
+ </el-tag>
+ <el-tag v-else-if="scope.row.syncedToImageData === 1" type="success" size="small">
+ <i class="el-icon-success"></i> 宸插悓姝�
+ </el-tag>
+ <span v-else style="color: #C0C4CC;">--</span>
</template>
</el-table-column>
<el-table-column label="涓婁紶鏃堕棿" align="center" prop="uploadTime" width="180">
@@ -388,21 +415,42 @@
</el-dialog>
<!-- 涓婁紶闄勪欢瀵硅瘽妗� -->
- <el-dialog title="涓婁紶闄勪欢" :visible.sync="uploadOpen" width="500px" append-to-body>
- <el-upload
- class="upload-demo"
- drag
- :action="uploadUrl"
- :headers="uploadHeaders"
- :data="uploadData"
- :on-success="handleUploadSuccess"
- :on-error="handleUploadError"
- :before-upload="beforeUpload"
- multiple>
- <i class="el-icon-upload"></i>
- <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
- <div class="el-upload__tip" slot="tip">鍙兘涓婁紶jpg/png/pdf/doc/docx鏂囦欢锛屼笖涓嶈秴杩�10MB</div>
- </el-upload>
+ <el-dialog title="涓婁紶闄勪欢" :visible.sync="uploadOpen" width="500px" append-to-body @close="cancelUpload">
+ <el-form ref="uploadForm" :model="uploadForm" :rules="uploadRules" label-width="100px">
+ <el-form-item label="涓氬姟鍒嗙被" prop="category">
+ <el-select v-model="uploadForm.category" placeholder="璇烽�夋嫨涓氬姟鍒嗙被" clearable style="width: 100%;">
+ <el-option
+ v-for="dict in dict.type.sys_attachment_category"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="闄勪欢" prop="files">
+ <el-upload
+ ref="upload"
+ class="upload-demo"
+ :action="uploadUrl"
+ :headers="uploadHeaders"
+ :data="uploadData"
+ :on-success="handleUploadSuccess"
+ :on-error="handleUploadError"
+ :before-upload="beforeUpload"
+ :file-list="fileList"
+ :auto-upload="false"
+ multiple
+ drag>
+ <i class="el-icon-upload"></i>
+ <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+ <div class="el-upload__tip" slot="tip">鍙兘涓婁紶jpg/png/pdf/doc/docx鏂囦欢锛屼笖涓嶈秴杩�100MB</div>
+ </el-upload>
+ </el-form-item>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="primary" @click="submitUpload">纭� 瀹�</el-button>
+ <el-button @click="cancelUpload">鍙� 娑�</el-button>
+ </div>
</el-dialog>
</div>
</template>
@@ -414,7 +462,7 @@
export default {
name: "TaskDetail",
- dicts: ['sys_task_type', 'sys_task_status', 'sys_vehicle_type', 'sys_task_vehicle_status', 'sys_user_sex', 'hospital_department'],
+ dicts: ['sys_task_type', 'sys_task_status', 'sys_vehicle_type', 'sys_task_vehicle_status', 'sys_user_sex', 'hospital_department', 'sys_attachment_category'],
data() {
return {
// 浠诲姟璇︽儏
@@ -433,6 +481,12 @@
vehicleAssignOpen: false,
// 鏄惁鏄剧ず涓婁紶瀵硅瘽妗�
uploadOpen: false,
+ // 涓婁紶琛ㄥ崟
+ uploadForm: {
+ category: null
+ },
+ // 鏂囦欢鍒楄〃
+ fileList: [],
// 缂栬緫琛ㄥ崟
editForm: {},
// 鍒嗛厤琛ㄥ崟
@@ -449,7 +503,7 @@
vehicleLoading: false,
attachmentLoading: false,
// 涓婁紶鐩稿叧
- uploadUrl: process.env.VUE_APP_BASE_API + "/task/attachment/upload/" + this.$route.params.taskId,
+ uploadUrl: process.env.VUE_APP_BASE_API + "/task/attachment/upload/" + (new URLSearchParams(window.location.search).get('taskId') || ''),
uploadHeaders: {
Authorization: "Bearer " + getToken()
},
@@ -480,12 +534,19 @@
vehicleIds: [
{ required: true, message: "杞﹁締涓嶈兘涓虹┖", trigger: "change" }
]
+ },
+ uploadRules: {
+ category: [
+ { required: true, message: "涓氬姟鍒嗙被涓嶈兘涓虹┖", trigger: "change" }
+ ]
}
};
},
created() {
this.getTaskDetail();
this.getUserList();
+ // 鍒濆鍖栦笂浼燯RL
+ this.uploadUrl = process.env.VUE_APP_BASE_API + "/task/attachment/upload/" + this.$route.params.taskId;
},
methods: {
/** 鑾峰彇浠诲姟璇︽儏 */
@@ -554,6 +615,10 @@
},
/** 涓婁紶闄勪欢 */
handleUpload() {
+ this.uploadForm = {
+ category: null
+ };
+ this.fileList = [];
this.uploadOpen = true;
},
/** 鍙栨秷杞﹁締鍒嗛厤 */
@@ -648,22 +713,67 @@
},
/** 涓婁紶鍓嶆鏌� */
beforeUpload(file) {
+ // 妫�鏌ユ槸鍚﹂�夋嫨浜嗕笟鍔″垎绫�
+ if (!this.uploadForm.category) {
+ this.$message.error('璇峰厛閫夋嫨涓氬姟鍒嗙被!');
+ return false;
+ }
+
+ // 鏇存柊uploadData锛岀‘淇濇瘡娆′笂浼犻兘甯︽湁category鍙傛暟
+ this.uploadData = {
+ category: this.uploadForm.category
+ };
+
const isValidType = ['image/jpeg', 'image/png', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'].includes(file.type);
- const isLt10M = file.size / 1024 / 1024 < 10;
+ const isLt10M = file.size / 1024 / 1024 < 100;
if (!isValidType) {
this.$message.error('鍙兘涓婁紶 JPG/PNG/PDF/DOC/DOCX 鏍煎紡鐨勬枃浠�!');
+ return false;
}
if (!isLt10M) {
- this.$message.error('涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 10MB!');
+ this.$message.error('涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 100MB!');
+ return false;
}
- return isValidType && isLt10M;
+ return true;
+ },
+ /** 鎻愪氦涓婁紶 */
+ submitUpload() {
+ this.$refs["uploadForm"].validate(valid => {
+ if (valid) {
+ // 妫�鏌ユ槸鍚﹂�夋嫨浜嗘枃浠�
+ const fileList = this.$refs.upload.uploadFiles;
+ if (!fileList || fileList.length === 0) {
+ this.$message.warning('璇烽�夋嫨瑕佷笂浼犵殑鏂囦欢');
+ return;
+ }
+
+ // 瑙﹀彂涓婁紶
+ this.$refs.upload.submit();
+ }
+ });
+ },
+ /** 鍙栨秷涓婁紶 */
+ cancelUpload() {
+ this.uploadOpen = false;
+ this.uploadForm = {
+ category: null
+ };
+ this.fileList = [];
+ if (this.$refs.upload) {
+ this.$refs.upload.clearFiles();
+ }
},
/** 涓婁紶鎴愬姛 */
handleUploadSuccess(response, file, fileList) {
- this.$modal.msgSuccess("涓婁紶鎴愬姛");
- this.uploadOpen = false;
- this.getTaskDetail();
+ // 妫�鏌ユ槸鍚︽墍鏈夋枃浠堕兘涓婁紶瀹屾垚
+ const allDone = fileList.every(f => f.status === 'success' || f.status === 'fail');
+
+ if (allDone) {
+ this.$modal.msgSuccess("涓婁紶鎴愬姛");
+ this.cancelUpload();
+ this.getTaskDetail();
+ }
},
/** 涓婁紶澶辫触 */
handleUploadError(err, file, fileList) {
@@ -687,6 +797,12 @@
return typeItem ? typeItem.label : vehicleType;
}
return vehicleType;
+ },
+ /** 鍒ゆ柇鏄惁涓哄浘鐗囩被鍨� */
+ isImage(fileType) {
+ if (!fileType) return false;
+ const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
+ return imageTypes.includes(fileType.toLowerCase());
}
}
};
diff --git a/sql/attachment_sync_job.sql b/sql/attachment_sync_job.sql
new file mode 100644
index 0000000..0c739b8
--- /dev/null
+++ b/sql/attachment_sync_job.sql
@@ -0,0 +1,239 @@
+-- ==========================================
+-- 浠诲姟闄勪欢鍚屾瀹氭椂浠诲姟閰嶇疆鑴氭湰
+-- ==========================================
+--
+-- 鍔熻兘璇存槑:
+-- 灏嗗凡鍚屾璋冨害鍗曠殑浠诲姟闄勪欢鍚屾鍒版棫绯荤粺ImageData琛�
+--
+-- 浣跨敤鍦烘櫙:
+-- 1. 鏂扮郴缁熶笂浼犱簡浠诲姟闄勪欢
+-- 2. 浠诲姟鐨勮皟搴﹀崟宸叉垚鍔熷悓姝ュ埌鏃х郴缁�
+-- 3. 闄勪欢闇�瑕佸悓姝ュ埌鏃х郴缁熶互渚挎棫绯荤粺鏌ョ湅
+--
+-- 鍚屾鏉′欢:
+-- 1. 浠诲姟绫诲瀷涓烘�ユ晳杞繍 (task_type = 'EMERGENCY_TRANSFER')
+-- 2. 璋冨害鍗曞凡鍚屾鎴愬姛 (dispatch_sync_status = 2)
+-- 3. 鏈夎皟搴﹀崟ID鍜屾湇鍔″崟ID (legacy_dispatch_ord_id IS NOT NULL AND legacy_service_ord_id IS NOT NULL)
+-- 4. 闄勪欢鏈悓姝� (synced_to_image_data = 0 OR synced_to_image_data IS NULL)
+--
+-- 鎵ц棰戠巼寤鸿:
+-- - 鐢熶骇鐜: 姣�5鍒嗛挓鎵ц涓�娆� (0 0/5 * * * ?)
+-- - 娴嬭瘯鐜: 姣�2鍒嗛挓鎵ц涓�娆� (0 0/2 * * * ?)
+--
+-- 娉ㄦ剰浜嬮」:
+-- 1. 渚濊禆璋冨害鍗曞悓姝ュ畬鎴�
+-- 2. 浼氫笂浼犳枃浠跺苟鐢熸垚缂╃暐鍥�
+-- 3. 闄勪欢鍒嗙被鏄犲皠瑙乀askAttachmentSyncServiceImpl绫�
+-- ==========================================
+
+-- 鎻掑叆瀹氭椂浠诲姟閰嶇疆
+INSERT INTO `sys_job` (
+ `job_name`,
+ `job_group`,
+ `invoke_target`,
+ `cron_expression`,
+ `misfire_policy`,
+ `concurrent`,
+ `status`,
+ `create_by`,
+ `create_time`,
+ `update_by`,
+ `update_time`,
+ `remark`
+) VALUES (
+ '浠诲姟闄勪欢鍚屾',
+ 'DEFAULT',
+ 'legacySystemSyncTask.syncPendingAttachments()',
+ '0 0/5 * * * ?',
+ '2',
+ '1',
+ '0',
+ 'admin',
+ NOW(),
+ 'admin',
+ NOW(),
+ '灏嗗凡鍚屾璋冨害鍗曠殑浠诲姟闄勪欢鍚屾鍒版棫绯荤粺ImageData琛紝姣�5鍒嗛挓鎵ц涓�娆°�傚悓姝ユ潯浠讹細璋冨害鍗曞凡鍚屾銆侀檮浠舵湭鍚屾銆佹湁鏈嶅姟鍗曞拰璋冨害鍗旾D銆�'
+);
+
+-- ==========================================
+-- 楠岃瘉鏌ヨ
+-- ==========================================
+
+-- 1. 鏌ョ湅瀹氭椂浠诲姟鏄惁娣诲姞鎴愬姛
+SELECT
+ job_id,
+ job_name,
+ job_group,
+ invoke_target,
+ cron_expression,
+ CASE status
+ WHEN '0' THEN '姝e父'
+ WHEN '1' THEN '鏆傚仠'
+ END AS job_status,
+ remark
+FROM sys_job
+WHERE job_name = '浠诲姟闄勪欢鍚屾';
+
+-- 2. 鏌ョ湅闇�瑕佸悓姝ョ殑闄勪欢鏁伴噺
+SELECT
+ COUNT(*) AS total_attachments,
+ SUM(CASE WHEN a.attachment_category = '1' THEN 1 ELSE 0 END) AS consent_form,
+ SUM(CASE WHEN a.attachment_category = '2' THEN 1 ELSE 0 END) AS patient_data,
+ SUM(CASE WHEN a.attachment_category = '3' THEN 1 ELSE 0 END) AS operation_record,
+ SUM(CASE WHEN a.attachment_category = '4' THEN 1 ELSE 0 END) AS before_departure,
+ SUM(CASE WHEN a.attachment_category = '5' THEN 1 ELSE 0 END) AS after_departure,
+ SUM(CASE WHEN a.attachment_category = '6' THEN 1 ELSE 0 END) AS seat_belt
+FROM sys_task_attachment a
+INNER JOIN sys_task t ON a.task_id = t.task_id
+INNER JOIN sys_task_emergency e ON t.task_id = e.task_id
+WHERE t.task_type = 'EMERGENCY_TRANSFER'
+ AND e.dispatch_sync_status = 2
+ AND e.legacy_dispatch_ord_id IS NOT NULL
+ AND e.legacy_service_ord_id IS NOT NULL
+ AND (a.synced_to_image_data = 0 OR a.synced_to_image_data IS NULL);
+
+-- 3. 鏌ョ湅寰呭悓姝ラ檮浠惰鎯咃紙鏈�杩�10鏉★級
+SELECT
+ a.attachment_id,
+ t.task_code,
+ a.file_name,
+ CASE a.attachment_category
+ WHEN '1' THEN '鐭ユ儏鍚屾剰涔�'
+ WHEN '2' THEN '鐥呬汉璧勬枡'
+ WHEN '3' THEN '鎿嶄綔璁板綍'
+ WHEN '4' THEN '鍑鸿溅鍓�'
+ WHEN '5' THEN '鍑鸿溅鍚�'
+ WHEN '6' THEN '绯诲畨鍏ㄥ甫'
+ ELSE '鏈垎绫�'
+ END AS category_name,
+ e.legacy_service_ord_id,
+ e.legacy_dispatch_ord_id,
+ a.upload_time,
+ a.synced_to_image_data,
+ a.sync_time
+FROM sys_task_attachment a
+INNER JOIN sys_task t ON a.task_id = t.task_id
+INNER JOIN sys_task_emergency e ON t.task_id = e.task_id
+WHERE t.task_type = 'EMERGENCY_TRANSFER'
+ AND e.dispatch_sync_status = 2
+ AND e.legacy_dispatch_ord_id IS NOT NULL
+ AND e.legacy_service_ord_id IS NOT NULL
+ AND (a.synced_to_image_data = 0 OR a.synced_to_image_data IS NULL)
+ORDER BY a.upload_time DESC
+LIMIT 10;
+
+-- 4. 鏌ョ湅宸插悓姝ラ檮浠剁粺璁�
+SELECT
+ COUNT(*) AS synced_attachments,
+ MIN(a.sync_time) AS first_sync_time,
+ MAX(a.sync_time) AS last_sync_time
+FROM sys_task_attachment a
+WHERE a.synced_to_image_data = 1
+ AND a.sync_time IS NOT NULL;
+
+-- 5. 鎸変换鍔$粺璁″緟鍚屾闄勪欢
+SELECT
+ t.task_id,
+ t.task_code,
+ e.legacy_service_ord_id,
+ e.legacy_dispatch_ord_id,
+ COUNT(a.attachment_id) AS attachment_count,
+ GROUP_CONCAT(
+ CASE a.attachment_category
+ WHEN '1' THEN '鐭ユ儏鍚屾剰涔�'
+ WHEN '2' THEN '鐥呬汉璧勬枡'
+ WHEN '3' THEN '鎿嶄綔璁板綍'
+ WHEN '4' THEN '鍑鸿溅鍓�'
+ WHEN '5' THEN '鍑鸿溅鍚�'
+ WHEN '6' THEN '绯诲畨鍏ㄥ甫'
+ END
+ SEPARATOR ', '
+ ) AS categories
+FROM sys_task t
+INNER JOIN sys_task_emergency e ON t.task_id = e.task_id
+INNER JOIN sys_task_attachment a ON t.task_id = a.task_id
+WHERE t.task_type = 'EMERGENCY_TRANSFER'
+ AND e.dispatch_sync_status = 2
+ AND e.legacy_dispatch_ord_id IS NOT NULL
+ AND e.legacy_service_ord_id IS NOT NULL
+ AND (a.synced_to_image_data = 0 OR a.synced_to_image_data IS NULL)
+GROUP BY t.task_id, t.task_code, e.legacy_service_ord_id, e.legacy_dispatch_ord_id
+ORDER BY attachment_count DESC;
+
+-- ==========================================
+-- 闄勪欢鍒嗙被鏄犲皠瑙勫垯鍙傝��
+-- ==========================================
+/*
+闄勪欢鍒嗙被 -> ImageData绫诲瀷:
+1-鐭ユ儏鍚屾剰涔� -> ImageType: 1
+2-鐥呬汉璧勬枡 -> ImageType: 2
+3-鎿嶄綔璁板綍 -> ImageType: 3
+4-鍑鸿溅鍓� -> ImageType: 4
+5-鍑鸿溅鍚� -> ImageType: 5
+6-绯诲畨鍏ㄥ甫 -> ImageType: 6
+
+璇︾粏璇存槑瑙�: com.ruoyi.system.service.impl.TaskAttachmentSyncServiceImpl.getImageTypeByCategory()
+*/
+
+-- ==========================================
+-- 鍚屾娴佺▼璇存槑
+-- ==========================================
+/*
+1. 鏌ヨ寰呭悓姝ラ檮浠跺垪琛紙绗﹀悎鏉′欢鐨勯檮浠讹級
+2. 鎸変换鍔″垎缁勶紝鑾峰彇鏈嶅姟鍗旾D銆佽皟搴﹀崟ID銆佸垱寤轰汉OA鐢ㄦ埛ID
+3. 璇诲彇鏈湴闄勪欢鏂囦欢
+4. 涓婁紶鍒版枃浠舵湇鍔″櫒骞剁敓鎴愮缉鐣ュ浘
+5. 灏嗛檮浠朵俊鎭啓鍏ユ棫绯荤粺ImageData琛�
+ - DOrdIDDt: 璋冨害鍗旾D
+ - SOrdIDDt: 鏈嶅姟鍗旾D
+ - ImageType: 闄勪欢鍒嗙被瀵瑰簲鐨勭被鍨�
+ - ImageUrl: 鍘熷浘璺緞
+ - ImageUrls: 缂╃暐鍥捐矾寰�
+ - UpImageTime: 涓婁紶鏃堕棿
+ - UpImageOAid: 涓婁紶鑰匫A鐢ㄦ埛ID
+6. 鏇存柊闄勪欢鍚屾鐘舵��
+ - synced_to_image_data = 1
+ - sync_time = 褰撳墠鏃堕棿
+ - image_data_id = ImageData琛↖D
+*/
+
+-- ==========================================
+-- 鎵嬪姩瑙﹀彂娴嬭瘯
+-- ==========================================
+/*
+-- 鍦ㄥ畾鏃朵换鍔$鐞嗛〉闈㈡墜鍔ㄦ墽琛屼竴娆′换鍔★紝鎴栬�呭湪浠g爜涓皟鐢�:
+-- legacySystemSyncTask.syncPendingAttachments()
+
+-- 鏌ョ湅鍚屾鏃ュ織锛堥渶瑕佹煡鐪嬪簲鐢ㄦ棩蹇楁枃浠讹級
+*/
+
+-- ==========================================
+-- 鏁呴殰鎺掓煡SQL
+-- ==========================================
+
+-- 鏌ョ湅鍚屾澶辫触鐨勯檮浠讹紙濡傛灉鏈夐敊璇褰曪級
+SELECT
+ a.attachment_id,
+ t.task_code,
+ a.file_name,
+ a.file_path,
+ a.upload_time,
+ a.synced_to_image_data,
+ a.sync_time
+FROM sys_task_attachment a
+INNER JOIN sys_task t ON a.task_id = t.task_id
+WHERE a.synced_to_image_data = 0
+ AND a.upload_time < DATE_SUB(NOW(), INTERVAL 1 HOUR)
+ORDER BY a.upload_time DESC;
+
+-- 鏌ョ湅鏌愪釜浠诲姟鐨勬墍鏈夐檮浠跺悓姝ョ姸鎬�
+-- SELECT
+-- a.attachment_id,
+-- a.file_name,
+-- a.attachment_category,
+-- a.synced_to_image_data,
+-- a.sync_time,
+-- a.image_data_id
+-- FROM sys_task_attachment a
+-- WHERE a.task_id = ? -- 鏇挎崲涓哄叿浣撶殑浠诲姟ID
+-- ORDER BY a.upload_time DESC;
diff --git a/sql/sys_attachment_category_dict.sql b/sql/sys_attachment_category_dict.sql
new file mode 100644
index 0000000..1e14fc0
--- /dev/null
+++ b/sql/sys_attachment_category_dict.sql
@@ -0,0 +1,14 @@
+-- 浠诲姟闄勪欢鍒嗙被瀛楀吀鏁版嵁
+-- 鎻掑叆瀛楀吀绫诲瀷
+INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, update_by, update_time, remark)
+VALUES ('浠诲姟闄勪欢鍒嗙被', 'sys_attachment_category', '0', 'admin', sysdate(), 'admin', sysdate(), '浠诲姟闄勪欢鐨勪笟鍔″垎绫�');
+
+-- 鎻掑叆瀛楀吀鏁版嵁
+INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, update_by, update_time, remark)
+VALUES
+(1, '鐭ユ儏鍚屾剰涔�', '1', 'sys_attachment_category', '', 'primary', 'N', '0', 'admin', sysdate(), 'admin', sysdate(), '鐭ユ儏鍚屾剰涔﹂檮浠�'),
+(2, '鐥呬汉璧勬枡', '2', 'sys_attachment_category', '', 'success', 'N', '0', 'admin', sysdate(), 'admin', sysdate(), '鐥呬汉璧勬枡闄勪欢'),
+(3, '鎿嶄綔璁板綍', '3', 'sys_attachment_category', '', 'info', 'N', '0', 'admin', sysdate(), 'admin', sysdate(), '鎿嶄綔璁板綍闄勪欢'),
+(4, '鍑鸿溅鍓�', '4', 'sys_attachment_category', '', 'warning', 'N', '0', 'admin', sysdate(), 'admin', sysdate(), '鍑鸿溅鍓嶉檮浠�'),
+(5, '鍑鸿溅鍚�', '5', 'sys_attachment_category', '', 'danger', 'N', '0', 'admin', sysdate(), 'admin', sysdate(), '鍑鸿溅鍚庨檮浠�'),
+(6, '绯诲畨鍏ㄥ甫', '6', 'sys_attachment_category', '', 'default', 'N', '0', 'admin', sysdate(), 'admin', sysdate(), '绯诲畨鍏ㄥ甫闄勪欢');
--
Gitblit v1.9.1