app/pagesTask/create-emergency.vue
@@ -916,7 +916,10 @@ if (!this.validateForm()) { return } this.doSubmitTask(); }, checkTaskDp(){ // è·åå½åæ¥æï¼æ ¼å¼YYYY-MM-DDï¼ const today = new Date() const createDate = today.getFullYear() + '-' + @@ -946,7 +949,6 @@ this.$modal.showToast('æ£æ¥å¤±è´¥ï¼è¯·éè¯') }) }, // æ§è¡å®é çæäº¤æä½ doSubmitTask() { this.$modal.confirm('ç¡®å®è¦ä¿åä»»å¡åï¼').then(() => { app/pagesTask/detail.vue
@@ -5,6 +5,9 @@ <uni-icons type="arrowleft" size="20"></uni-icons> </view> <view class="title">ä»»å¡è¯¦æ </view> <view class="edit-btn" @click="handleEdit" v-if="taskDetail && !isTaskFinished"> <uni-icons type="compose" size="20" color="#007AFF"></uni-icons> </view> </view> <scroll-view class="detail-content" scroll-y="true" v-if="taskDetail"> @@ -64,6 +67,17 @@ {{ isAssigneeReady(assignee) ? '已就绪' : 'æªå°±ç»ª' }} </view> </view> </view> <!-- å½åç»å½äººæ¯è¯¥æ§è¡äººä¸æªå°±ç»ªæ¶æ¾ç¤ºå°±ç»ªæé® --> <view v-if="showAssigneeReadyFeature() && isAssigneeSelf(assignee) && !isAssigneeReady(assignee) && taskDetail.taskStatus === 'PENDING'" class="assignee-ready-btn" :data-user-id="assignee.userId || assignee.oaUserId" :data-user-name="assignee.userName" :data-index="index" @click="handleReadyClick" > ç¹å»å°±ç»ª </view> </view> </view> @@ -373,6 +387,51 @@ <text>å è½½ä¸...</text> </view> <!-- 强å¶å®æå¯¹è¯æ¡ --> <uni-popup ref="forceCompletePopup" type="center" :is-mask-click="false"> <view class="force-complete-dialog"> <view class="dialog-title">请è¾å ¥æ¶é´</view> <view class="time-picker-item"> <view class="time-label">转è¿å¼å§æ¶é´</view> <picker mode="date" :value="getDateFromDateTime(forceCompleteForm.actualStartTime)" @change="selectStartDate" > <view class="picker-value">{{ getDateFromDateTime(forceCompleteForm.actualStartTime) || 'è¯·éæ©æ¥æ' }}</view> </picker> <picker mode="time" :value="getTimeFromDateTime(forceCompleteForm.actualStartTime)" @change="selectStartTime" > <view class="picker-value">{{ getTimeFromDateTime(forceCompleteForm.actualStartTime) || 'è¯·éæ©æ¶é´' }}</view> </picker> </view> <view class="time-picker-item"> <view class="time-label">转è¿ç»ææ¶é´</view> <picker mode="date" :value="getDateFromDateTime(forceCompleteForm.actualEndTime)" @change="selectEndDate" > <view class="picker-value">{{ getDateFromDateTime(forceCompleteForm.actualEndTime) || 'è¯·éæ©æ¥æ' }}</view> </picker> <picker mode="time" :value="getTimeFromDateTime(forceCompleteForm.actualEndTime)" @change="selectEndTime" > <view class="picker-value">{{ getTimeFromDateTime(forceCompleteForm.actualEndTime) || 'è¯·éæ©æ¶é´' }}</view> </picker> </view> <view class="dialog-buttons"> <button class="cancel-btn" @click="closeForceCompleteDialog">åæ¶</button> <button class="confirm-btn" @click="confirmForceComplete">ç¡®å®</button> </view> </view> </uni-popup> <!-- åæ¶åå éæ©å¯¹è¯æ¡ --> <uni-popup ref="cancelPopup" type="center" :is-mask-click="false"> <view class="cancel-dialog"> @@ -395,22 +454,9 @@ <!-- æä½æé®åºå --> <view class="action-buttons" v-if="taskDetail"> <!-- å¾ å¤çç¶æ: æ¾ç¤ºç¼è¾ãåºåãåæ¶ --> <!-- å¾ å¤çç¶æ: æ¾ç¤ºåºåãåæ¶ã强å¶å®æ --> <template v-if="taskDetail.taskStatus === 'PENDING'"> <button class="action-btn edit" @click="handleEdit" > ä¿®æ¹ </button> <template v-if="isCurrentUserAssignee()"> <button v-if="showAssigneeReadyFeature() && isMultipleAssignees() && !isCurrentUserReady()" class="action-btn primary" @click="handleReadyAction()" > 就绪 </button> <button class="action-btn primary" @click="handleDepartAction()" @@ -423,17 +469,17 @@ > åæ¶ </button> <button class="action-btn force-complete" @click="showForceCompleteTimeDialog()" > 强å¶å®æ </button> </template> </template> <!-- åºåä¸ç¶æ: æ¾ç¤ºç¼è¾ãå·²å°è¾¾ã强å¶ç»æ --> <!-- åºåä¸ç¶æ: æ¾ç¤ºå·²å°è¾¾ã强å¶ç»æ --> <template v-else-if="taskDetail.taskStatus === 'DEPARTING'"> <button class="action-btn edit" @click="handleEdit" > ä¿®æ¹ </button> <template v-if="isCurrentUserAssignee()"> <button class="action-btn primary" @@ -450,14 +496,8 @@ </template> </template> <!-- å·²å°è¾¾ç¶æ: æ¾ç¤ºç¼è¾ãå·²è¿ç¨ --> <!-- å·²å°è¾¾ç¶æ: æ¾ç¤ºå·²è¿ç¨ --> <template v-else-if="taskDetail.taskStatus === 'ARRIVED'"> <button class="action-btn edit" @click="handleEdit" > ä¿®æ¹ </button> <template v-if="isCurrentUserAssignee()"> <button class="action-btn primary" @@ -468,14 +508,8 @@ </template> </template> <!-- è¿ç¨ä¸ç¶æ: æ¾ç¤ºç¼è¾ã已宿 --> <!-- è¿ç¨ä¸ç¶æ: æ¾ç¤ºå·²å®æ --> <template v-else-if="taskDetail.taskStatus === 'RETURNING'"> <button class="action-btn edit" @click="handleEdit" > ä¿®æ¹ </button> <template v-if="isCurrentUserAssignee()"> <button class="action-btn primary" @@ -521,7 +555,12 @@ paymentInfo: null, // æ¯ä»ä¿¡æ¯ cancelReasonList: [], // åæ¶åå å表 showCancelDialog: false, // æ¾ç¤ºåæ¶åå å¯¹è¯æ¡ selectedCancelReason: '' // éä¸çåæ¶åå selectedCancelReason: '', // éä¸çåæ¶åå showForceCompleteDialog: false, // æ¾ç¤ºå¼ºå¶å®æå¯¹è¯æ¡ forceCompleteForm: { actualStartTime: '', actualEndTime: '' } } }, computed: { @@ -1393,17 +1432,52 @@ return assignee && (assignee.userId === userId || assignee.oaUserId === userId) }, // æ§è¡äººç¹å»â就绪â markAssigneeReady(assignee) { if (!assignee || !this.taskDetail) { this.$modal.showToast('æ§è¡äººæä»»å¡ä¿¡æ¯ä¸åå¨') // å¤ç就绪æé®ç¹å»ï¼éè¿data屿§è·åæ§è¡äººä¿¡æ¯ï¼ handleReadyClick(e) { const dataset = e.currentTarget.dataset const index = dataset.index console.log('handleReadyClick - dataset:', dataset) console.log('handleReadyClick - index:', index) if (index === undefined || index === null) { this.$modal.showToast('æ æ³è·åæ§è¡äººç´¢å¼') return } const assignee = this.taskDetail.assignees[index] console.log('handleReadyClick - assignee:', assignee) if (!assignee) { this.$modal.showToast('æ§è¡äººä¿¡æ¯ä¸åå¨') return } this.markAssigneeReady(assignee) }, // æ§è¡äººç¹å»"就绪" markAssigneeReady(assignee) { console.log('markAssigneeReady 被è°ç¨ï¼åæ°:', assignee) console.log('taskDetail:', this.taskDetail) if (!this.taskDetail) { this.$modal.showToast('ä»»å¡ä¿¡æ¯ä¸åå¨') return } if (!assignee) { this.$modal.showToast('æ§è¡äººä¿¡æ¯ä¸åå¨') return } const userId = assignee.userId || assignee.oaUserId console.log('æ§è¡äººID:', userId) if (!userId) { this.$modal.showToast('æ æ³è¯å«æ§è¡äººID') return } this.$modal.showLoading && this.$modal.showLoading('æäº¤ä¸...') setAssigneeReady(this.taskId).then(() => { this.$modal.hideLoading && this.$modal.hideLoading() @@ -1581,7 +1655,10 @@ // 鿩忶åå selectCancelReason(e) { this.selectedCancelReason = e.detail.value const index = parseInt(e.detail.value) if (this.cancelReasonList && this.cancelReasonList[index]) { this.selectedCancelReason = this.cancelReasonList[index].value } }, // 另忶åå çç¶ææ´æ° @@ -1596,6 +1673,167 @@ } const reason = this.cancelReasonList.find(r => r.value === value) return reason ? reason.label : value }, // æ¾ç¤ºå¼ºå¶å®ææ¶é´å¯¹è¯æ¡ showForceCompleteTimeDialog() { // æ ¡éªä»»å¡æ¯å¦æ»¡è¶³å¼ºå¶å®ææ¡ä»¶ const validation = this.validateForceComplete() if (!validation.valid) { this.$modal.showToast(validation.message) return } // é置表å const now = new Date() const nowDateStr = this.formatDateForPicker(now) const nowTimeStr = this.formatTimeForPicker(now) // å¼å§æ¶é´ï¼ä¼å 使ç¨é¢çº¦æ¶é´ï¼å¦ææ²¡æå使ç¨å½åæ¶é´ let startTimeStr = nowDateStr + ' ' + nowTimeStr if (this.taskDetail && this.taskDetail.plannedStartTime) { const plannedTime = new Date(this.taskDetail.plannedStartTime) const year = plannedTime.getFullYear() // æ£æ¥æ¯å¦æ¯æææ¶é´ï¼æé¤1900ã1970çæ æå¹´ä»½ï¼ if (year > 2000) { const plannedDateStr = this.formatDateForPicker(plannedTime) const plannedTimeStr = this.formatTimeForPicker(plannedTime) startTimeStr = plannedDateStr + ' ' + plannedTimeStr } } this.forceCompleteForm = { actualStartTime: startTimeStr, actualEndTime: nowDateStr + ' ' + nowTimeStr } this.$refs.forceCompletePopup.open() }, // æ ¡éªæ¯å¦å¯ä»¥å¼ºå¶å®æ validateForceComplete() { if (!this.taskDetail) { return { valid: false, message: 'ä»»å¡ä¿¡æ¯ä¸åå¨' } } // æ£æ¥æ¯å¦ææ§è¡äºº if (!this.taskDetail.assignees || this.taskDetail.assignees.length === 0) { return { valid: false, message: '请å åé æ§è¡äºº' } } // æ£æ¥æ¯å¦æè½¦è¾ if (!this.taskDetail.assignedVehicles || this.taskDetail.assignedVehicles.length === 0) { return { valid: false, message: '请å åé æ§è¡è½¦è¾' } } return { valid: true } }, // å ³é强å¶å®æå¯¹è¯æ¡ closeForceCompleteDialog() { this.$refs.forceCompletePopup.close() }, // 确认强å¶å®æ confirmForceComplete() { // æ ¡éªæ¶é´ if (!this.forceCompleteForm.actualStartTime || !this.forceCompleteForm.actualEndTime) { this.$modal.showToast('è¯·éæ©å¼å§åç»ææ¶é´') return } const startTime = new Date(this.forceCompleteForm.actualStartTime) const endTime = new Date(this.forceCompleteForm.actualEndTime) if (startTime >= endTime) { this.$modal.showToast('ç»ææ¶é´å¿ 须大äºå¼å§æ¶é´') return } this.$refs.forceCompletePopup.close() // è°ç¨APIæ´æ°ä»»å¡ this.forceCompleteTask() }, // 强å¶å®æä»»å¡ forceCompleteTask() { uni.showLoading({ title: 'å¤çä¸...' }) const statusData = { taskStatus: 'COMPLETED', actualStartTime: this.forceCompleteForm.actualStartTime, actualEndTime: this.forceCompleteForm.actualEndTime, remark: '强å¶å®æä»»å¡' } changeTaskStatus(this.taskId, statusData).then(response => { uni.hideLoading() this.$modal.showToast('ä»»å¡å·²å®æ') // éæ°å 载任å¡è¯¦æ this.loadTaskDetail() }).catch(error => { uni.hideLoading() console.error('强å¶å®æä»»å¡å¤±è´¥:', error) this.$modal.showToast('æä½å¤±è´¥ï¼è¯·éè¯') }) }, // éæ©å¼å§æ¥æ selectStartDate(e) { const date = e.detail.value const time = this.getTimeFromDateTime(this.forceCompleteForm.actualStartTime) || '00:00' this.forceCompleteForm.actualStartTime = date + ' ' + time }, // éæ©å¼å§æ¶é´ selectStartTime(e) { const time = e.detail.value const date = this.getDateFromDateTime(this.forceCompleteForm.actualStartTime) || this.formatDateForPicker(new Date()) this.forceCompleteForm.actualStartTime = date + ' ' + time }, // éæ©ç»ææ¥æ selectEndDate(e) { const date = e.detail.value const time = this.getTimeFromDateTime(this.forceCompleteForm.actualEndTime) || '00:00' this.forceCompleteForm.actualEndTime = date + ' ' + time }, // éæ©ç»ææ¶é´ selectEndTime(e) { const time = e.detail.value const date = this.getDateFromDateTime(this.forceCompleteForm.actualEndTime) || this.formatDateForPicker(new Date()) this.forceCompleteForm.actualEndTime = date + ' ' + time }, // 仿¥ææ¶é´åç¬¦ä¸²ä¸æåæ¥æ getDateFromDateTime(dateTimeStr) { if (!dateTimeStr) return '' return dateTimeStr.split(' ')[0] || '' }, // 仿¥ææ¶é´åç¬¦ä¸²ä¸æåæ¶é´ getTimeFromDateTime(dateTimeStr) { if (!dateTimeStr) return '' return dateTimeStr.split(' ')[1] || '' }, // æ ¼å¼åæ¥æä¸º picker éè¦çæ ¼å¼ (YYYY-MM-DD) formatDateForPicker(date) { const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') return `${year}-${month}-${day}` }, // æ ¼å¼åæ¶é´ä¸º picker éè¦çæ ¼å¼ (HH:mm) formatTimeForPicker(date) { const hour = String(date.getHours()).padStart(2, '0') const minute = String(date.getMinutes()).padStart(2, '0') return `${hour}:${minute}` }, } @@ -1626,9 +1864,19 @@ } .title { flex: 1; font-size: 36rpx; font-weight: bold; color: #333; } .edit-btn { width: 60rpx; height: 60rpx; display: flex; align-items: center; justify-content: center; cursor: pointer; } } @@ -1726,6 +1974,9 @@ } .assignee-role { display: flex; align-items: center; .role-tag { display: inline-block; padding: 4rpx 12rpx; @@ -1746,15 +1997,6 @@ } } .assignee-ready-btn { margin-left: 12rpx; padding: 8rpx 16rpx; font-size: 24rpx; border-radius: 6rpx; background-color: #34C759; color: #fff; border: none; } .ready-badge { display: inline-block; margin-left: 12rpx; @@ -1771,6 +2013,17 @@ } } } } .assignee-ready-btn { margin-left: 12rpx; padding: 8rpx 16rpx; font-size: 24rpx; border-radius: 6rpx; background-color: #34C759; color: #fff; border: none; flex-shrink: 0; } } } @@ -1964,6 +2217,11 @@ color: white; } &.force-complete { background-color: #5856d6; color: white; } &:first-child { margin-left: 0; } @@ -2057,5 +2315,74 @@ } } } // 强å¶å®æå¯¹è¯æ¡æ ·å¼ .force-complete-dialog { width: 600rpx; background-color: white; border-radius: 20rpx; padding: 40rpx; .dialog-title { font-size: 32rpx; font-weight: bold; text-align: center; margin-bottom: 30rpx; color: #333; } .time-picker-item { margin-bottom: 30rpx; .time-label { font-size: 28rpx; color: #333; margin-bottom: 15rpx; font-weight: 500; } picker { display: inline-block; width: 48%; &:first-of-type { margin-right: 4%; } .picker-value { padding: 20rpx; background-color: #f5f5f5; border-radius: 10rpx; font-size: 26rpx; color: #333; text-align: center; } } } .dialog-buttons { display: flex; gap: 20rpx; margin-top: 40rpx; button { flex: 1; height: 80rpx; border-radius: 10rpx; font-size: 30rpx; border: none; &.cancel-btn { background-color: #f0f0f0; color: #666; } &.confirm-btn { background-color: #5856d6; color: white; } } } } } </style> ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java
@@ -1,10 +1,14 @@ package com.ruoyi.web.controller.task; import java.util.Date; import java.util.List; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.system.domain.SysTaskEmergency; import com.ruoyi.system.service.*; import org.springframework.beans.factory.annotation.Qualifier; @@ -306,6 +310,23 @@ sysTaskService.saveCancelInfo(taskId, request.getCancelReason()); } // 妿æ¯å¼ºå¶å®æï¼æ´æ°å®é å¼å§æ¶é´åç»ææ¶é´ if (newStatus == TaskStatus.COMPLETED && request.getActualStartTime() != null && request.getActualEndTime() != null) { SysTask task = new SysTask(); task.setTaskId(taskId); task.setTaskStatus(newStatus.getCode()); //å°String转æDate task.setActualStartTime(DateUtils.parseDate(request.getActualStartTime())); task.setActualEndTime(DateUtils.parseDate(request.getActualEndTime())); task.setRemark(request.getRemark()); task.setUpdateBy(SecurityUtils.getUsername()); task.setUpdateTime(DateUtils.getNowDate()); int result = sysTaskService.forceCompleteTask(task); return toAjax(result); } // 妿å å«GPSä½ç½®ä¿¡æ¯ï¼ä½¿ç¨å¸¦ä½ç½®çæ¹æ³ if (request.getLatitude() != null && request.getLongitude() != null) { String address= mapService.reverseGeocoding(request.getLongitude(), request.getLatitude()); @@ -463,6 +484,12 @@ // åæ¶ç¸å ³å段 private String cancelReason; // åæ¶åå ï¼å ³èæ°æ®åå ¸task_cancel_reasonï¼ // 强å¶å®æç¸å ³å段 @JsonFormat(pattern = "yyyy-MM-dd HH:mm") private String actualStartTime; // å®é å¼å§æ¶é´ @JsonFormat(pattern = "yyyy-MM-dd HH:mm") private String actualEndTime; // å®é ç»ææ¶é´ public String getTaskStatus() { return taskStatus; } @@ -566,5 +593,21 @@ public void setCancelReason(String cancelReason) { this.cancelReason = cancelReason; } public String getActualStartTime() { return actualStartTime; } public void setActualStartTime(String actualStartTime) { this.actualStartTime = actualStartTime; } public String getActualEndTime() { return actualEndTime; } public void setActualEndTime(String actualEndTime) { this.actualEndTime = actualEndTime; } } } ruoyi-system/src/main/java/com/ruoyi/system/listener/DispatchOrdRunningListener.java
@@ -6,7 +6,9 @@ import com.ruoyi.system.domain.SysTaskEmergency; import com.ruoyi.system.domain.enums.TaskStatus; import com.ruoyi.system.event.TaskStatusChangedEvent; import com.ruoyi.system.mapper.DispatchOrdMapper; import com.ruoyi.system.mapper.LegacyTransferSyncMapper; import com.ruoyi.system.mapper.SysDictDataMapper; import com.ruoyi.system.mapper.SysTaskEmergencyMapper; import com.ruoyi.system.mapper.SysTaskMapper; import com.ruoyi.system.mapper.SysUserMapper; @@ -46,6 +48,12 @@ @Autowired private SysUserMapper sysUserMapper; @Autowired private DispatchOrdMapper dispatchOrdMapper; @Autowired private SysDictDataMapper sysDictDataMapper; /** * çå¬ä»»å¡ç¶æåæ´äºä»¶ @@ -111,6 +119,11 @@ log.debug("ä»»å¡ç¶æä¸éè¦åæ¥å°æ§ç³»ç»ï¼ä»»å¡ID: {}, ç¶æ: {}", event.getTaskId(), newTaskStatus.getInfo()); return; } // 妿æ¯åæ¶ç¶æï¼åæ¥åæ¶åå å°DispatchOrd表 if (TaskStatus.CANCELLED.equals(newTaskStatus) && emergency.getCancelReason() != null) { syncCancelReasonToDispatchOrd(emergency.getLegacyDispatchOrdId(), emergency.getCancelReason()); } // æå ¥ç¶æåæ´è®°å½å°DispatchOrd_Running表 @@ -192,4 +205,53 @@ // 䏿åºå¼å¸¸ï¼é¿å å½±å主æµç¨ } } /** * 忥忶åå å°DispatchOrd表 * * @param dispatchOrdId è°åº¦åID * @param cancelReason åæ¶åå ï¼åå ¸valueï¼ */ private void syncCancelReasonToDispatchOrd(Long dispatchOrdId, String cancelReason) { try { if (cancelReason == null || cancelReason.isEmpty()) { log.debug("åæ¶åå 为空ï¼è·³è¿åæ¥ï¼DispatchOrdID: {}", dispatchOrdId); return; } // å°cancelReasonï¼åå ¸valueï¼è½¬æ¢ä¸ºInteger Integer cancelReasonId = null; try { cancelReasonId = Integer.valueOf(cancelReason); } catch (NumberFormatException e) { log.error("åæ¶åå æ ¼å¼éè¯¯ï¼æ æ³è½¬æ¢ä¸ºæ°åï¼cancelReason: {}, DispatchOrdID: {}", cancelReason, dispatchOrdId); return; } // 仿°æ®åå ¸ä¸æ¥è¯¢åæ¶åå ææ¬ String cancelReasonText = sysDictDataMapper.selectDictLabel("task_cancel_reason", cancelReason); if (cancelReasonText == null || cancelReasonText.isEmpty()) { log.warn("æªæ¾å°åæ¶åå 对åºçææ¬ï¼cancelReason: {}, DispatchOrdID: {}", cancelReason, dispatchOrdId); cancelReasonText = cancelReason; // 使ç¨åå¼ä½ä¸ºé¢å¤ } log.info("å¼å§åæ¥åæ¶åå å°DispatchOrdï¼DispatchOrdID: {}, åæ¶åå ID: {}, åæ¶åå ææ¬: {}", dispatchOrdId, cancelReasonId, cancelReasonText); // è°ç¨Mapperæ´æ°DispatchOrd表 int rows = dispatchOrdMapper.updateDispatchOrdCancelReason(dispatchOrdId, cancelReasonId, cancelReasonText); if (rows > 0) { log.info("æå忥忶åå å°DispatchOrdï¼DispatchOrdID: {}, åæ¶åå : {} ({})", dispatchOrdId, cancelReasonText, cancelReasonId); } else { log.warn("忥忶åå å¤±è´¥ï¼æªæ¾å°å¯¹åºçè°åº¦åï¼DispatchOrdID: {}", dispatchOrdId); } } catch (Exception e) { log.error("忥忶åå å°DispatchOrdå¼å¸¸ï¼DispatchOrdID: {}, åæ¶åå : {}", dispatchOrdId, cancelReason, e); // 䏿åºå¼å¸¸ï¼é¿å å½±å主æµç¨ } } } ruoyi-system/src/main/java/com/ruoyi/system/mapper/DispatchOrdMapper.java
@@ -85,4 +85,16 @@ * @return å½±åè¡æ° */ public int updateDispatchOrdState(@Param("dispatchOrdID") Long dispatchOrdID, @Param("dispatchOrdState") Integer dispatchOrdState); /** * æ´æ°è°åº¦ååæ¶åå * * @param dispatchOrdID è°åº¦åID * @param cancelReasonId åæ¶åå ID * @param cancelReasonText åæ¶åå ææ¬ * @return å½±åè¡æ° */ public int updateDispatchOrdCancelReason(@Param("dispatchOrdID") Long dispatchOrdID, @Param("cancelReasonId") Integer cancelReasonId, @Param("cancelReasonText") String cancelReasonText); } ruoyi-system/src/main/java/com/ruoyi/system/service/IDispatchOrdService.java
@@ -86,4 +86,6 @@ * @return å½±åè¡æ° */ public int updateDispatchOrdState(Long dispatchOrdID, Integer dispatchOrdState); public void cancelDispatchOrd(Long dispatchOrdID,Integer cancelReason,String cancelCReasonTxt); } ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTaskService.java
@@ -125,6 +125,14 @@ public int changeTaskStatus(Long taskId, TaskStatus newStatus, String remark); /** * 强å¶å®æä»»å¡ï¼æå®å®é å¼å§æ¶é´åç»ææ¶é´ï¼ * * @param task ä»»å¡ä¿¡æ¯ï¼å å«taskIdãtaskStatusãactualStartTimeãactualEndTimeãremarkï¼ * @return ç»æ */ public int forceCompleteTask(SysTask task); /** * åæ´ä»»å¡ç¶æï¼å«GPSä½ç½®ä¿¡æ¯ï¼ * * @param taskId ä»»å¡ID ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DispatchOrdServiceImpl.java
@@ -122,5 +122,10 @@ return dispatchOrdMapper.updateDispatchOrdState(dispatchOrdID, dispatchOrdState); } @Override public void cancelDispatchOrd(Long dispatchOrdID, Integer cancelReason, String cancelCReasonTxt) { dispatchOrdMapper.updateDispatchOrdCancelReason(dispatchOrdID, cancelReason, cancelCReasonTxt); } } ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java
@@ -931,6 +931,67 @@ } /** * 强å¶å®æä»»å¡ï¼æå®å®é å¼å§æ¶é´åç»ææ¶é´ï¼ * * @param task ä»»å¡ä¿¡æ¯ * @return ç»æ */ @Override public int forceCompleteTask(SysTask task) { if (task == null || task.getTaskId() == null) { throw new RuntimeException("ä»»å¡ä¿¡æ¯ä¸è½ä¸ºç©º"); } SysTask oldTask = sysTaskMapper.selectSysTaskByTaskId(task.getTaskId()); if (oldTask == null) { throw new RuntimeException("ä»»å¡ä¸åå¨"); } // æ ¡éªå¼å§æ¶é´åç»ææ¶é´ if (task.getActualStartTime() == null || task.getActualEndTime() == null) { throw new RuntimeException("å®é å¼å§æ¶é´åç»ææ¶é´ä¸è½ä¸ºç©º"); } if (task.getActualStartTime().after(task.getActualEndTime())) { throw new RuntimeException("ç»ææ¶é´å¿ 须大äºå¼å§æ¶é´"); } // è®°å½æ§ç¶æ String oldStatus = oldTask.getTaskStatus(); TaskStatus oldTaskStatus = TaskStatus.getByCode(oldStatus); // æ´æ°ä»»å¡ int result = sysTaskMapper.updateTaskStatus(task); // è®°å½æä½æ¥å¿ if (result > 0) { recordTaskLog(task.getTaskId(), "FORCE_COMPLETE", "强å¶å®æä»»å¡", oldStatus, task.getTaskStatus(), SecurityUtils.getUserId(), SecurityUtils.getUsername()); // åå¸ä»»å¡ç¶æåæ´äºä»¶ TaskStatus newTaskStatus = TaskStatus.getByCode(task.getTaskStatus()); eventPublisher.publishEvent(new TaskStatusChangedEvent( this, task.getTaskId(), oldTask.getTaskCode(), oldStatus, task.getTaskStatus(), oldTaskStatus != null ? oldTaskStatus.getInfo() : "æªç¥", newTaskStatus != null ? newTaskStatus.getInfo() : "æªç¥", null, // assigneeIds SecurityUtils.getUserId(), SecurityUtils.getUserId(), null, // longitude null, // latitude null // address )); } return result; } /** * åæ´ä»»å¡ç¶æï¼å«GPSä½ç½®ä¿¡æ¯ï¼ * * @param taskId ä»»å¡ID ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskStatusPushServiceImpl.java
@@ -129,6 +129,10 @@ log.info("åæ¶è½¬è¿ä»»å¡: {}", emergency.getLegacyServiceOrdId()); cancelTask(emergency.getLegacyServiceOrdId(), emergency.getCancelReason(), emergency.getCancelBy()); } if(LongUtil.isNotEmpty(emergency.getLegacyDispatchOrdId())){ log.info("åæ¶è°åº¦å: {}", emergency.getLegacyDispatchOrdId()); cancelDispatch(emergency.getLegacyDispatchOrdId(), emergency.getCancelReason(), emergency.getCancelBy()); } } // æ¨éç¶æå°æ§ç³»ç» boolean result = updateLegacyTaskStatus(emergency.getLegacyDispatchOrdId(), targetStatusCode); @@ -149,6 +153,10 @@ log.error("ãæ°æ¨æ§ãæ¨éä»»å¡ç¶æå¼å¸¸ï¼ä»»å¡ID: {}", taskId, e); return false; } } private void cancelDispatch(Long legacyDispatchOrdId, String cancelReason, String cancelReasonText) { dispatchOrdService.cancelDispatchOrd(legacyDispatchOrdId, Integer.parseInt(cancelReason), cancelReasonText); } /** @@ -223,7 +231,7 @@ private void cancelTask(Long serviceOrderId, String cancelReason, String cancelBy){ // åæ¶ä»»å¡ï¼æ´æ°SQL Serverä¸çServiceOrder表 try { if (serviceOrderId == null || serviceOrderId <= 0) { if (LongUtil.isEmpty(serviceOrderId)) { log.warn("ServiceOrderIDä¸ºç©ºï¼æ æ³åæ¶ä»»å¡"); return; } ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TaskSyncUtilService.java
@@ -228,12 +228,12 @@ params.put("CommissionScenarioID", "0"); // ä¼å¾®ç»©ææ¹æ¡ params.put("ServiceOrdOperationRemarks", "æ°ç³»ç»åæ¥å建"); // æä½å¤æ³¨ params.put("ServiceOrdEstimatedOrderDate", ""); // é¢è®¡æ´¾åæ¶é´ params.put("ServiceOrdSource", "10"); // è®¢åæ¥æºï¼10=æ°ç³»ç»ï¼ params.put("ServiceOrdSource", "0"); // è®¢åæ¥æºï¼10=æ°ç³»ç»ï¼ params.put("OrderLevel", "0"); // æ¥çç级 params.put("ServiceOrdDepartureType", "1"); // é¢çº¦ç±»å params.put("ConditionLevel", "0"); // ç éçº§å« params.put("DirectionType", "0"); // 转è¿å»å params.put("ServiceOrd_m", ""); // æ¥æºå ¥å£ params.put("ServiceOrd_m", "1"); // æ¥æºå ¥å£ params.put("FromHQ2_is", "0"); // å¹¿å·æ»é¨æ¨é任塿 è®° params.put("OrderPrice_Auto", "0"); // 订åèªå¨æ¥ä»·åèå¼ ruoyi-system/src/main/resources/mapper/system/DispatchOrdMapper.xml
@@ -107,5 +107,13 @@ set DispatchOrdState = #{dispatchOrdState} where DispatchOrdID = #{dispatchOrdID} </update> <!-- æ´æ°è°åº¦ååæ¶åå --> <update id="updateDispatchOrdCancelReason"> update DispatchOrd set DispatchOrdCancelReason = #{cancelReasonId}, DispatchOrdCancelReasonTXT = #{cancelReasonText} where DispatchOrdID = #{dispatchOrdID} </update> </mapper> È¡ÏûÔÒòͬ²½µ½DispatchOrd¹¦ÄÜ˵Ã÷.md
New file @@ -0,0 +1,273 @@ # åæ¶åå 忥å°DispatchOrdåè½è¯´æ ## åè½æ¦è¿° å¨è½¬è¿ä»»å¡åæ¶æ¶ï¼èªå¨å°åæ¶åå 忥尿§ç³»ç»çDispatchOrd表ä¸ï¼è®°å½å°`DispatchOrdCancelReason`ï¼åæ¶åå IDï¼å`DispatchOrdCancelReasonTXT`ï¼åæ¶åå ææ¬ï¼å段ã ## å®ç°æ¹å¼ ### 1. æ ¸å¿æµç¨ å½ä»»å¡ç¶æåæ´ä¸º"已忶"æ¶ï¼ 1. çå¬å¨(`DispatchOrdRunningListener`)æ£æµå°åæ¶ç¶æ 2. ä»`sys_task_emergency`表è·ååæ¶åå ï¼cancelReasonåæ®µï¼å卿°æ®åå ¸valueï¼ 3. å°åæ¶åå value转æ¢ä¸ºIntegerä½ä¸ºDispatchOrdCancelReason 4. 仿°æ®åå ¸`task_cancel_reason`æ¥è¯¢å¯¹åºç䏿æ ç¾ä½ä¸ºDispatchOrdCancelReasonTXT 5. æ§è¡UPDATEè¯å¥æ´æ°DispatchOrd表 ### 2. æ°æ®æ å°å ³ç³» | æ°ç³»ç»å段 | æ°ç³»ç»è¡¨ | æ§ç³»ç»å段 | æ§ç³»ç»è¡¨ | 说æ | |-----------|---------|-----------|---------|------| | cancel_reason | sys_task_emergency | DispatchOrdCancelReason | DispatchOrd | åæ¶åå IDï¼æ°åï¼ | | åå ¸label | sys_dict_data | DispatchOrdCancelReasonTXT | DispatchOrd | åæ¶åå ææ¬ï¼ä¸æï¼ | ### 3. åæ¶åå åå ¸ æ°æ®åå ¸ç±»åï¼`task_cancel_reason` å å«26ä¸ªåæ¶åå éé¡¹ï¼ 1. ä»·æ ¼ä¸æ¥å 2. æ¶é´ç´§æ¥ 3. åºè½¦éåº¦æ ¢ 4. éæ©å ¶ä»è½¦ 5. ç 人没æçå½ä½å¾ 6. å»çæ¤å£«åä¸è¶³ 7. ç 人æ 嵿å 8. å ¶ä» 9. ç¬¬ä¸æ¹åæ¶ 10. æµè¯è®¢å 11. ä¼ ææ§ç¾ç 12. 家屿æº/ææ¥/䏿¥çµè¯ 13. æ¤å£«ä¸è¶³ 14. å»çä¸è¶³ 15. 设å¤ä¸è¶³(å¼å¸æº) 16. 家屿²¡è系好åºä½ 17. ç§»äº¤åæ¯æºææ§è¡ 18. 移交åäºå¤(æ¹æ±) 19. 移交åäºå¤(èå) 20. å®¶å±ä¸è¯éé²ä¿¡æ¯ 21. æå¨å»é¢/ç®çå°å»é¢æ´¾è½¦ 22. èªé©¾è½¦æ¥éæ£è 23. éæ©å ¶ä»æºæè½¦è¾ 24. å¤èéç¥åæ¶ 25. å¤èæ åé¦ 26. 家屿 åå ç´æ¥åç¥åæ¶ ## 代ç ä¿®æ¹æ¸ å ### 1. DispatchOrdMapper.java **æä»¶è·¯å¾**: `ruoyi-system/src/main/java/com/ruoyi/system/mapper/DispatchOrdMapper.java` **æ°å¢æ¹æ³**: ```java /** * æ´æ°è°åº¦ååæ¶åå * * @param dispatchOrdID è°åº¦åID * @param cancelReasonId åæ¶åå ID * @param cancelReasonText åæ¶åå ææ¬ * @return å½±åè¡æ° */ public int updateDispatchOrdCancelReason(@Param("dispatchOrdID") Long dispatchOrdID, @Param("cancelReasonId") Integer cancelReasonId, @Param("cancelReasonText") String cancelReasonText); ``` ### 2. DispatchOrdMapper.xml **æä»¶è·¯å¾**: `ruoyi-system/src/main/resources/mapper/system/DispatchOrdMapper.xml` **æ°å¢SQL**: ```xml <!-- æ´æ°è°åº¦ååæ¶åå --> <update id="updateDispatchOrdCancelReason"> update DispatchOrd set DispatchOrdCancelReason = #{cancelReasonId}, DispatchOrdCancelReasonTXT = #{cancelReasonText} where DispatchOrdID = #{dispatchOrdID} </update> ``` ### 3. DispatchOrdRunningListener.java **æä»¶è·¯å¾**: `ruoyi-system/src/main/java/com/ruoyi/system/listener/DispatchOrdRunningListener.java` **æ°å¢ä¾èµæ³¨å ¥**: ```java @Autowired private DispatchOrdMapper dispatchOrdMapper; @Autowired private SysDictDataMapper sysDictDataMapper; ``` **æ°å¢import**: ```java import com.ruoyi.system.mapper.DispatchOrdMapper; import com.ruoyi.system.mapper.SysDictDataMapper; ``` **ä¿®æ¹handleTaskStatusChangedEventæ¹æ³**: å¨ç¶æè½¬æ¢åå¢å åæ¶åå 忥é»è¾ï¼ç¬¬122-126è¡ï¼ï¼ ```java // 妿æ¯åæ¶ç¶æï¼åæ¥åæ¶åå å°DispatchOrd表 if (TaskStatus.CANCELLED.equals(newTaskStatus) && emergency.getCancelReason() != null) { syncCancelReasonToDispatchOrd(emergency.getLegacyDispatchOrdId(), emergency.getCancelReason()); } ``` **æ°å¢æ¹æ³**: ```java /** * 忥忶åå å°DispatchOrd表 * * @param dispatchOrdId è°åº¦åID * @param cancelReason åæ¶åå ï¼åå ¸valueï¼ */ private void syncCancelReasonToDispatchOrd(Long dispatchOrdId, String cancelReason) { try { if (cancelReason == null || cancelReason.isEmpty()) { log.debug("åæ¶åå 为空ï¼è·³è¿åæ¥ï¼DispatchOrdID: {}", dispatchOrdId); return; } // å°cancelReasonï¼åå ¸valueï¼è½¬æ¢ä¸ºInteger Integer cancelReasonId = null; try { cancelReasonId = Integer.valueOf(cancelReason); } catch (NumberFormatException e) { log.error("åæ¶åå æ ¼å¼éè¯¯ï¼æ æ³è½¬æ¢ä¸ºæ°åï¼cancelReason: {}, DispatchOrdID: {}", cancelReason, dispatchOrdId); return; } // 仿°æ®åå ¸ä¸æ¥è¯¢åæ¶åå ææ¬ String cancelReasonText = sysDictDataMapper.selectDictLabel("task_cancel_reason", cancelReason); if (cancelReasonText == null || cancelReasonText.isEmpty()) { log.warn("æªæ¾å°åæ¶åå 对åºçææ¬ï¼cancelReason: {}, DispatchOrdID: {}", cancelReason, dispatchOrdId); cancelReasonText = cancelReason; // 使ç¨åå¼ä½ä¸ºé¢å¤ } log.info("å¼å§åæ¥åæ¶åå å°DispatchOrdï¼DispatchOrdID: {}, åæ¶åå ID: {}, åæ¶åå ææ¬: {}", dispatchOrdId, cancelReasonId, cancelReasonText); // è°ç¨Mapperæ´æ°DispatchOrd表 int rows = dispatchOrdMapper.updateDispatchOrdCancelReason(dispatchOrdId, cancelReasonId, cancelReasonText); if (rows > 0) { log.info("æå忥忶åå å°DispatchOrdï¼DispatchOrdID: {}, åæ¶åå : {} ({})", dispatchOrdId, cancelReasonText, cancelReasonId); } else { log.warn("忥忶åå å¤±è´¥ï¼æªæ¾å°å¯¹åºçè°åº¦åï¼DispatchOrdID: {}", dispatchOrdId); } } catch (Exception e) { log.error("忥忶åå å°DispatchOrdå¼å¸¸ï¼DispatchOrdID: {}, åæ¶åå : {}", dispatchOrdId, cancelReason, e); // 䏿åºå¼å¸¸ï¼é¿å å½±å主æµç¨ } } ``` ## æ§è¡SQLç¤ºä¾ å½åæ¶åå 为"ä»·æ ¼ä¸æ¥å"ï¼value=1ï¼æ¶ï¼å®é æ§è¡çSQLï¼ ```sql UPDATE DispatchOrd SET DispatchOrdCancelReason = 1, DispatchOrdCancelReasonTXT = 'ä»·æ ¼ä¸æ¥å' WHERE DispatchOrdID = 12345 ``` ## 忥æ¡ä»¶ åæ¶åå 忥éè¦æ»¡è¶³ä»¥ä¸æ¡ä»¶ï¼ 1. â æ§ç³»ç»åæ¥å·²å¯ç¨ 2. â ä»»å¡ç±»åä¸ºæ¥æè½¬è¿ä»»å¡ï¼EMERGENCY_TRANSFERï¼ 3. â ä»»å¡å·²åæ¥å°æ§ç³»ç»ï¼ælegacyDispatchOrdIdï¼ 4. â ä»»å¡ç¶æåæ´ä¸º"已忶"ï¼CANCELLEDï¼ 5. â ä»»å¡è®°å½äºåæ¶åå ï¼cancelReasonä¸ä¸ºç©ºï¼ ## æ§è¡æ¶æº **弿¥æ§è¡**ï¼ä»»å¡ç¶æåæ´ä¸º"已忶"æ¶ï¼éè¿äºä»¶çå¬å¨å¼æ¥å¤çï¼ä¸å½±å主ä¸å¡æµç¨ã ## æ¥å¿è¯´æ ### æ£å¸¸æ¥å¿ ``` INFO - æ¶å°ä»»å¡ç¶æåæ´äºä»¶ï¼åå¤åæ¥å°DispatchOrd_Runningï¼ä»»å¡IDï¼1001ï¼æ§ç¶æï¼PENDINGï¼æ°ç¶æï¼CANCELLED INFO - å¼å§åæ¥åæ¶åå å°DispatchOrdï¼DispatchOrdID: 12345, åæ¶åå ID: 1, åæ¶åå ææ¬: ä»·æ ¼ä¸æ¥å INFO - æå忥忶åå å°DispatchOrdï¼DispatchOrdID: 12345, åæ¶åå : ä»·æ ¼ä¸æ¥å (1) ``` ### è·³è¿æ¥å¿ ``` DEBUG - åæ¶åå 为空ï¼è·³è¿åæ¥ï¼DispatchOrdID: 12345 ``` ### å¼å¸¸æ¥å¿ ``` WARN - æªæ¾å°åæ¶åå 对åºçææ¬ï¼cancelReason: 99, DispatchOrdID: 12345 ERROR - åæ¶åå æ ¼å¼éè¯¯ï¼æ æ³è½¬æ¢ä¸ºæ°åï¼cancelReason: abc, DispatchOrdID: 12345 WARN - 忥忶åå å¤±è´¥ï¼æªæ¾å°å¯¹åºçè°åº¦åï¼DispatchOrdID: 12345 ERROR - 忥忶åå å°DispatchOrdå¼å¸¸ï¼DispatchOrdID: 12345, åæ¶åå : 1 ``` ## éªè¯æ¹æ³ ### 1. æ°æ®åºéªè¯ æ¥è¯¢DispatchOrd表çåæ¶åå åæ®µï¼ ```sql SELECT DispatchOrdID, DispatchOrdState, DispatchOrdCancelReason, DispatchOrdCancelReasonTXT FROM DispatchOrd WHERE DispatchOrdID = 12345 ``` ### 2. æµè¯æ¥éª¤ 1. 卿°ç³»ç»å建转è¿ä»»å¡å¹¶åæ¥å°æ§ç³»ç» 2. å¨ä»»å¡è¯¦æ 页ç¹å»"åæ¶"æé® 3. 鿩忶åå ï¼å¦"ä»·æ ¼ä¸æ¥å"ï¼ 4. ç¡®è®¤åæ¶ 5. æ£æ¥åºç¨æ¥å¿ï¼ç¡®è®¤åæ¥æå 6. æ¥è¯¢DispatchOrd表ï¼éªè¯åæ¶åå åæ®µå·²æ´æ° ### 3. é¢æç»æ - DispatchOrdCancelReason = 1 - DispatchOrdCancelReasonTXT = "ä»·æ ¼ä¸æ¥å" ## ç¹æ§è¯´æ 1. **弿¥å¤ç**ï¼ä¸é»å¡ä¸»ä¸å¡æµç¨ï¼æé«ç³»ç»ååºé度 2. **å®¹éæºå¶**ï¼åæ¥å¤±è´¥ä¸å½±åä»»å¡åæ¶æä½ 3. **宿´æ¥å¿**ï¼è®°å½è¯¦ç»ç忥è¿ç¨åç»æ 4. **æ°æ®è½¬æ¢**ï¼èªå¨å°åå ¸value转æ¢ä¸ºIDåææ¬ 5. **äºä»¶é©±å¨**ï¼å©ç¨Springäºä»¶æºå¶å®ç°è§£è¦ 6. **èªå¨åæ¥**ï¼æ éæå¨è§¦åï¼ç¶æåæ´æ¶èªå¨æ§è¡ ## 注æäºé¡¹ 1. **ä» éåæ¶ç¶æ**ï¼åªæä»»å¡ç¶æåæ´ä¸ºCANCELLEDæ¶æä¼åæ¥åæ¶åå 2. **ä¾èµè°åº¦å忥**ï¼å¿ é¡»å 忥尿§ç³»ç»æè½æ´æ°åæ¶åå 3. **å¼å¸¸ä¸ä¸æ**ï¼åæ¥å¼å¸¸åªè®°å½æ¥å¿ï¼ä¸æåºå¼å¸¸ï¼ä¸å½±å主æµç¨ 4. **æ°æ®åºå段**ï¼ç¡®ä¿DispatchOrd表æDispatchOrdCancelReasonåDispatchOrdCancelReasonTXTåæ®µ 5. **åå ¸ä¾èµ**ï¼ä¾èµæ°æ®åå ¸task_cancel_reasonæ£ç¡®é ç½® ## ç¸å ³ææ¡£ - [åæ¶åå åè½å®ç°è¯´æ.md](./åæ¶åå åè½å®ç°è¯´æ.md) - [转è¿ä»»å¡ç¶æåæ´è®°å½åæ¥åè½è¯´æ.md](./prd/转è¿ä»»å¡ç¶æåæ´è®°å½åæ¥åè½è¯´æ.md) - [æ§ç³»ç»åæ¥-å¿«éå¼å§.md](./prd/æ§ç³»ç»åæ¥-å¿«éå¼å§.md) ## æ´æ°æ¥å¿ - 2025-12-26: å®ç°åæ¶åå 忥å°DispatchOrdåè½