| | |
| | | @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) { |
| | |
| | | |
| | | @Autowired |
| | | private ITaskStatusPushService taskStatusPushService; |
| | | |
| | | @Autowired |
| | | private ITaskAttachmentSyncService taskAttachmentSyncService; |
| | | |
| | | |
| | | |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | |
| | | * @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(); |
| | | } |
| | |
| | | */ |
| | | List<SysTaskAttachment> syncTaskAttachmentsToImageData(List<SysTaskAttachment> attachmentList, Long serviceOrderId, Long dispatchOrdId, Integer oaUserId); |
| | | |
| | | /** |
| | | * 批量同步待同步的任务附件到ImageData |
| | | * 查询条件: |
| | | * 1. 所属任务的调度单已同步成功 |
| | | * 2. 附件未同步 |
| | | * 3. 有调度单ID和服务单ID |
| | | * |
| | | * @return 成功同步的附件数量 |
| | | */ |
| | | int batchSyncPendingAttachments(); |
| | | |
| | | } |
| | |
| | | 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; |
| | | |
| | |
| | | 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); |
| | |
| | | |
| | | } |
| | | |
| | | return result; |
| | | return attachment.getAttachmentId(); |
| | | } catch (IOException e) { |
| | | throw new RuntimeException("文件上传失败:" + e.getMessage()); |
| | | } |
| | |
| | | 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); |
| | |
| | | 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; |
| | | //将inputstream写入文件 |
| | | 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 下载文件 |
| | | */ |
| | |
| | | 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; |
| | |
| | | * @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; |
| | | |
| | |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 批量同步待同步的任务附件到ImageData |
| | | */ |
| | | @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()); |
| | | |
| | | // 按任务ID分组同步 |
| | | 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={} 同步成功,ImageDataId={}", |
| | | 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; |
| | | } |
| | | |
| | | |
| | | /** |
| | |
| | | <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> |
| | |
| | | </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"> |
| | |
| | | </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> |
| | |
| | | |
| | | 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 { |
| | | // 任务详情 |
| | |
| | | vehicleAssignOpen: false, |
| | | // 是否显示上传对话框 |
| | | uploadOpen: false, |
| | | // 上传表单 |
| | | uploadForm: { |
| | | category: null |
| | | }, |
| | | // 文件列表 |
| | | fileList: [], |
| | | // 编辑表单 |
| | | editForm: {}, |
| | | // 分配表单 |
| | |
| | | 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() |
| | | }, |
| | |
| | | vehicleIds: [ |
| | | { required: true, message: "车辆不能为空", trigger: "change" } |
| | | ] |
| | | }, |
| | | uploadRules: { |
| | | category: [ |
| | | { required: true, message: "业务分类不能为空", trigger: "change" } |
| | | ] |
| | | } |
| | | }; |
| | | }, |
| | | created() { |
| | | this.getTaskDetail(); |
| | | this.getUserList(); |
| | | // 初始化上传URL |
| | | this.uploadUrl = process.env.VUE_APP_BASE_API + "/task/attachment/upload/" + this.$route.params.taskId; |
| | | }, |
| | | methods: { |
| | | /** 获取任务详情 */ |
| | |
| | | }, |
| | | /** 上传附件 */ |
| | | handleUpload() { |
| | | this.uploadForm = { |
| | | category: null |
| | | }; |
| | | this.fileList = []; |
| | | this.uploadOpen = true; |
| | | }, |
| | | /** 取消车辆分配 */ |
| | |
| | | }, |
| | | /** 上传前检查 */ |
| | | 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) { |
| | |
| | | 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()); |
| | | } |
| | | } |
| | | }; |
| New file |
| | |
| | | -- ========================================== |
| | | -- 任务附件同步定时任务配置脚本 |
| | | -- ========================================== |
| | | -- |
| | | -- 功能说明: |
| | | -- 将已同步调度单的任务附件同步到旧系统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. 附件分类映射见TaskAttachmentSyncServiceImpl类 |
| | | -- ========================================== |
| | | |
| | | -- 插入定时任务配置 |
| | | 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分钟执行一次。同步条件:调度单已同步、附件未同步、有服务单和调度单ID。' |
| | | ); |
| | | |
| | | -- ========================================== |
| | | -- 验证查询 |
| | | -- ========================================== |
| | | |
| | | -- 1. 查看定时任务是否添加成功 |
| | | SELECT |
| | | job_id, |
| | | job_name, |
| | | job_group, |
| | | invoke_target, |
| | | cron_expression, |
| | | CASE status |
| | | WHEN '0' THEN '正常' |
| | | 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. 按任务分组,获取服务单ID、调度单ID、创建人OA用户ID |
| | | 3. 读取本地附件文件 |
| | | 4. 上传到文件服务器并生成缩略图 |
| | | 5. 将附件信息写入旧系统ImageData表 |
| | | - DOrdIDDt: 调度单ID |
| | | - SOrdIDDt: 服务单ID |
| | | - ImageType: 附件分类对应的类型 |
| | | - ImageUrl: 原图路径 |
| | | - ImageUrls: 缩略图路径 |
| | | - UpImageTime: 上传时间 |
| | | - UpImageOAid: 上传者OA用户ID |
| | | 6. 更新附件同步状态 |
| | | - synced_to_image_data = 1 |
| | | - sync_time = 当前时间 |
| | | - image_data_id = ImageData表ID |
| | | */ |
| | | |
| | | -- ========================================== |
| | | -- 手动触发测试 |
| | | -- ========================================== |
| | | /* |
| | | -- 在定时任务管理页面手动执行一次任务,或者在代码中调用: |
| | | -- 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; |
| New file |
| | |
| | | -- 任务附件分类字典数据 |
| | | -- 插入字典类型 |
| | | 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(), '系安全带附件'); |