From 3a050c5c986c03a66eb8a0a7356ec634d7e02277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E9=94=90=E6=B3=A2?= <10549902+zhuangruibo@user.noreply.gitee.com> Date: Sat, 30 May 2026 21:34:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=A7=E6=B3=95=E7=AB=AF-=E8=AE=A1=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AppEnfCpReserveController.java | 8 ++ .../quartz/job/CpReserveOverdueCancelJob.java | 41 ++++++ .../service/IEfcodeCpReserveService.java | 18 +++ .../service/IEfcodeCpSettingService.java | 17 +++ .../impl/EfcodeCpReserveServiceImpl.java | 123 ++++++++++++++++-- .../impl/EfcodeCpSettingServiceImpl.java | 8 ++ .../enforcementcode/EfcodeCpReserveMapper.xml | 1 + .../enforcementcode/EfcodeCpTaskMapper.xml | 4 - .../src/api/enforcement/reserve.js | 3 + .../my/reserve/cp_reserve_manual.vue | 7 +- .../my/reserve/cp_reserve_system.vue | 62 +++++---- .../enforcement/my/task/cp_task_mine.vue | 30 ++++- .../cp/AppEnfCpReserveOverdueCancelTest.java | 56 ++++++++ 13 files changed, 332 insertions(+), 46 deletions(-) create mode 100644 zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CpReserveOverdueCancelJob.java create mode 100644 zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/cp/AppEnfCpReserveOverdueCancelTest.java diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfCpReserveController.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfCpReserveController.java index 5b902af4..3fd56858 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfCpReserveController.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfCpReserveController.java @@ -43,6 +43,14 @@ public class AppEnfCpReserveController extends EfcodeCpReserveController { return cpReserveService.queryPageList(bo, pageQuery); } + /** + * 获取预约记录详情(用于操作前获取最新数据) + */ + @GetMapping("/detail/{id}") + public R getDetail(@PathVariable String id) { + return R.ok(cpReserveService.queryById(id)); + } + /** * 同意预约(系统匹配专用分支) * diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CpReserveOverdueCancelJob.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CpReserveOverdueCancelJob.java new file mode 100644 index 00000000..87b3ab55 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CpReserveOverdueCancelJob.java @@ -0,0 +1,41 @@ +package com.zdxt.enforcementcode.quartz.job; + +import com.zdxt.enforcementcode.service.IEfcodeCpReserveService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 预约超期自动取消 - 定时任务 + *

定期扫描待确认状态的预约记录,超过配置天数未处理的自动设置为超期取消

+ * + * @author zrb + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class CpReserveOverdueCancelJob { + + private final IEfcodeCpReserveService cpReserveService; + + /** + * 任务执行入口(public,供 JobInvokeUtil 通过反射调用) + *

按配置的超期天数取消手动预约和系统匹配中超期未处理的待确认记录

+ */ + public void execute() { + log.info("[CpReserveOverdueCancelJob] 定时任务触发:预约超期自动取消"); + int manualCount = cpReserveService.cancelOverdueManualReserves(false); + int systemCount = cpReserveService.cancelOverdueSystemReserves(false); + log.info("[CpReserveOverdueCancelJob] 执行完成:手动预约取消{}条,系统匹配取消{}条", manualCount, systemCount); + } + + /** + * 强制取消入口(忽略时间限制,将所有待处理记录全部取消) + */ + public void executeForce() { + log.info("[CpReserveOverdueCancelJob] 强制触发:预约全部取消"); + int manualCount = cpReserveService.cancelOverdueManualReserves(true); + int systemCount = cpReserveService.cancelOverdueSystemReserves(true); + log.info("[CpReserveOverdueCancelJob] 强制执行完成:手动预约取消{}条,系统匹配取消{}条", manualCount, systemCount); + } +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpReserveService.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpReserveService.java index b6c0347d..61a03934 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpReserveService.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpReserveService.java @@ -127,4 +127,22 @@ public interface IEfcodeCpReserveService { * @return 同组其他预约记录(关联计划信息) */ List queryGroupMembers(String planGroupId, String excludeId); + + /** + * 手动预约超期取消 + *

将手动预约(resFrom=0)中超过配置天数未处理的待确认记录设置为超期取消

+ * + * @param ignoreTime 是否忽略时间限制,true=将所有待处理记录全部取消 + * @return 取消的记录数 + */ + int cancelOverdueManualReserves(boolean ignoreTime); + + /** + * 系统匹配预约超期取消 + *

将系统匹配(resFrom=1)中超过配置天数未处理的待确认记录设置为超期取消

+ * + * @param ignoreTime 是否忽略时间限制,true=将所有待处理记录全部取消 + * @return 取消的记录数 + */ + int cancelOverdueSystemReserves(boolean ignoreTime); } diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpSettingService.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpSettingService.java index cad55578..9c176fbd 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpSettingService.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpSettingService.java @@ -46,6 +46,11 @@ public interface IEfcodeCpSettingService { */ String SETTING_ID_SYSTEM_MATCH_WEEKS = "CP_SETTING_SYSTEM_MATCH_WEEKS"; + /** + * 设置项主键:预约自动取消时长(天) + */ + String SETTING_ID_RESERVE_CANCEL_DURATION = "CP_SETTING_RESERVE_CANCEL_DURATION"; + /** * 默认值:下阶段计划提交最晚时间 */ @@ -76,6 +81,11 @@ public interface IEfcodeCpSettingService { */ int DEFAULT_SYSTEM_MATCH_WEEKS = 2; + /** + * 默认值:预约自动取消时长(天) + */ + int DEFAULT_RESERVE_CANCEL_DURATION = 7; + /** * 查询行政执法检查计划-设置 * @@ -126,6 +136,13 @@ public interface IEfcodeCpSettingService { */ Integer getSystemMatchWeeks(); + /** + * 获取预约自动取消时长(天) + * + * @return 数值,未配置或无法解析时返回默认值 {@link #DEFAULT_RESERVE_CANCEL_DURATION} + */ + Integer getReserveCancelDuration(); + /** * 分页查询行政执法检查计划-设置列表 * diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java index 4aabc9fd..ae0012ef 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java @@ -496,15 +496,15 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService { cpPlanMapper.updateById(planUpdate); } - // 后置处理:检查是否所有同组记录都已同意,若是则生成任务 - afterApproveCheck(reserve); + // 后置处理:检查同组是否全部处理完成,若至少一家同意则生成任务 + afterSystemMatchGroupCheck(reserve); return R.ok(); } /** - * 同意操作后置检查:检查同组是否全部同意,若是则生成任务 + * 系统匹配后置检查:检查同组是否全部处理完成,若是且至少一家同意则生成任务 */ - private void afterApproveCheck(EfcodeCpReserve reserve) { + private void afterSystemMatchGroupCheck(EfcodeCpReserve reserve) { if (StringUtils.isBlank(reserve.getPlanGroupId())) { return; } @@ -513,16 +513,24 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService { Wrappers.lambdaQuery() .eq(EfcodeCpReserve::getPlanGroupId, reserve.getPlanGroupId()) ); - // 检查是否全部同意 - boolean allApproved = allGroupReserves.stream() - .allMatch(r -> Long.valueOf(1L).equals(r.getOpStatus())); - if (!allApproved) { - log.info("[预约后置] planGroupId={} 尚有未同意的记录,等待其他用户操作", reserve.getPlanGroupId()); + // 检查是否全部处理完成(无待确认记录) + boolean allProcessed = allGroupReserves.stream() + .noneMatch(r -> Long.valueOf(3L).equals(r.getOpStatus())); + if (!allProcessed) { + log.info("[系统匹配后置] planGroupId={} 尚有未处理的记录,等待其他用户操作", reserve.getPlanGroupId()); return; } - // 全部同意:生成1条任务,所有同组计划共享该 task_id + // 筛选同意的记录 + List approvedReserves = allGroupReserves.stream() + .filter(r -> Long.valueOf(1L).equals(r.getOpStatus())) + .collect(java.util.stream.Collectors.toList()); + if (approvedReserves.isEmpty()) { + log.info("[系统匹配后置] planGroupId={} 全部拒绝,不生成任务", reserve.getPlanGroupId()); + return; + } + // 至少一家同意:生成1条任务,只关联同意的计划 String sharedTaskId = null; - for (EfcodeCpReserve r : allGroupReserves) { + for (EfcodeCpReserve r : approvedReserves) { EfcodeCpPlan plan = cpPlanMapper.selectById(r.getPlanId()); if (plan == null || StringUtils.isNotBlank(plan.getTaskId())) continue; if (sharedTaskId == null) { @@ -535,8 +543,24 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService { cpPlanMapper.updateById(planUpdate); } } - log.info("[预约后置] planGroupId={} 全部同意,生成任务taskId={},关联{}条计划", - reserve.getPlanGroupId(), sharedTaskId, allGroupReserves.size()); + log.info("[系统匹配后置] planGroupId={} 全部处理完成,同意{}家/拒绝{}家,生成任务taskId={}", + reserve.getPlanGroupId(), approvedReserves.size(), + allGroupReserves.size() - approvedReserves.size(), sharedTaskId); + } + + /** + * 手动预约后置检查:同意后直接为该计划生成任务 + */ + private void afterManualMatchComplete(EfcodeCpReserve reserve) { + if (StringUtils.isBlank(reserve.getPlanId())) { + return; + } + EfcodeCpPlan plan = cpPlanMapper.selectById(reserve.getPlanId()); + if (plan == null || StringUtils.isNotBlank(plan.getTaskId())) { + return; + } + //String taskId = cpTaskService.forceGenerateTask(plan); + //log.info("[手动预约后置] 计划[{}] 同意后生成任务taskId={}", reserve.getPlanId(), taskId); } /** @@ -568,6 +592,8 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService { reserve.setOpTime(new Date()); baseMapper.updateById(reserve); afterOperate(reserve, "approve"); + // 后置处理:手动预约同意后直接生成任务 + afterManualMatchComplete(reserve); return R.ok(); } @@ -587,6 +613,8 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService { reserve.setOpTime(new Date()); baseMapper.updateById(reserve); afterOperate(reserve, "reject"); + // 后置处理:检查同组是否全部处理完成,若至少一家同意则生成任务 + afterSystemMatchGroupCheck(reserve); return R.ok(); } @@ -691,4 +719,73 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService { } return result; } + + /** + * 手动预约超期取消 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int cancelOverdueManualReserves(boolean ignoreTime) { + return doCancelOverdueReserves(0L, ignoreTime); + } + + /** + * 系统匹配预约超期取消 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int cancelOverdueSystemReserves(boolean ignoreTime) { + return doCancelOverdueReserves(1L, ignoreTime); + } + + /** + * 执行超期取消逻辑 + * + * @param resFrom 预约来源:0=手动预约,1=系统匹配 + * @param ignoreTime 是否忽略时间,true=将所有待处理记录全部取消 + * @return 取消的记录数 + */ + private int doCancelOverdueReserves(Long resFrom, boolean ignoreTime) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery() + .eq(EfcodeCpReserve::getResFrom, resFrom) + .eq(EfcodeCpReserve::getOpStatus, 3L); // 待确认 + + if (!ignoreTime) { + // 读取配置的超期天数 + int cancelDays = cpSettingService.getReserveCancelDuration(); + // 计算截止时间:当前时间 - cancelDays 天 + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DAY_OF_MONTH, -cancelDays); + Date deadline = cal.getTime(); + // 匹配时间(reserveTime) 在截止时间之前的记录 + lqw.le(EfcodeCpReserve::getReserveTime, deadline); + } + + List overdueList = baseMapper.selectList(lqw); + if (overdueList.isEmpty()) { + log.info("[超期取消] resFrom={} ignoreTime={} 无符合条件的记录", resFrom, ignoreTime); + return 0; + } + + Date now = new Date(); + for (EfcodeCpReserve reserve : overdueList) { + // 更新预约记录状态为超期取消(4) + EfcodeCpReserve update = new EfcodeCpReserve(); + update.setId(reserve.getId()); + update.setOpStatus(4L); + update.setOpTime(now); + baseMapper.updateById(update); + + // 同步更新关联计划的 reserve_status 为 4(重新预约中) + if (StringUtils.isNotBlank(reserve.getPlanId())) { + EfcodeCpPlan planUpdate = new EfcodeCpPlan(); + planUpdate.setId(reserve.getPlanId()); + planUpdate.setReserveStatus(4L); + cpPlanMapper.updateById(planUpdate); + } + } + + log.info("[超期取消] resFrom={} ignoreTime={} 共取消{}条记录", resFrom, ignoreTime, overdueList.size()); + return overdueList.size(); + } } diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpSettingServiceImpl.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpSettingServiceImpl.java index 9b3c61d4..b8d6d590 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpSettingServiceImpl.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpSettingServiceImpl.java @@ -92,6 +92,14 @@ public class EfcodeCpSettingServiceImpl implements IEfcodeCpSettingService { return getIntSetting(SETTING_ID_SYSTEM_MATCH_WEEKS, DEFAULT_SYSTEM_MATCH_WEEKS); } + /** + * 获取预约自动取消时长(天) + */ + @Override + public Integer getReserveCancelDuration() { + return getIntSetting(SETTING_ID_RESERVE_CANCEL_DURATION, DEFAULT_RESERVE_CANCEL_DURATION); + } + /** * 根据设置主键获取整型配置值,不存在或解析失败时返回默认值 * diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpReserveMapper.xml b/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpReserveMapper.xml index f2d02061..21e2812e 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpReserveMapper.xml +++ b/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpReserveMapper.xml @@ -18,6 +18,7 @@ r.op_set_date, r.op_set_time, r.res_from, + r.warring_message, p.plan_date, p.plan_time, p.enterprise_name, diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpTaskMapper.xml b/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpTaskMapper.xml index 5a3e9858..5d776672 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpTaskMapper.xml +++ b/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpTaskMapper.xml @@ -13,8 +13,6 @@ p.id AS plan_id, p.plan_date, p.plan_time, - p.plan_actual_date, - p.plan_actual_time, p.enforce_content, p.enterprise_name, p.is_union, @@ -67,8 +65,6 @@ p.plan_type, p.plan_date, p.plan_time, - p.plan_actual_date, - p.plan_actual_time, p.enforce_content, p.enterprise_name, p.enterprise_number, diff --git a/zdxt-web-client/zdxt-efcode-enforcement-app/src/api/enforcement/reserve.js b/zdxt-web-client/zdxt-efcode-enforcement-app/src/api/enforcement/reserve.js index 7a871cf6..74c7d88c 100644 --- a/zdxt-web-client/zdxt-efcode-enforcement-app/src/api/enforcement/reserve.js +++ b/zdxt-web-client/zdxt-efcode-enforcement-app/src/api/enforcement/reserve.js @@ -7,6 +7,9 @@ const reserveBaseUrl = "/app-enf/cpReserve"; // 预约列表(分页,支持 resFrom 过滤:0=手动预约,1=系统匹配) export const getReserveList = (params, success, error) => _get(reserveBaseUrl + "/list", params, success, error); +// 获取预约记录详情(用于操作前获取最新数据) +export const getReserveDetail = (id, success, error) => _get(reserveBaseUrl + "/detail/" + id, {}, success, error); + // 预约处理:同意(系统匹配专用,支持传选择的时间参数) export const approveReserve = (id, params, success, error) => { let url = reserveBaseUrl + "/approveBySystem/" + id; diff --git a/zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_manual.vue b/zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_manual.vue index 5ea5cf33..fa1a5733 100644 --- a/zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_manual.vue +++ b/zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_manual.vue @@ -107,13 +107,14 @@ const finished = ref(false); const refreshing = ref(false); const pageNum = ref(1); const pageSize = 10; -const filterStatus = ref(null); // null=全部, 1=已同意, 2=已拒绝, 3=待确认 +const filterStatus = ref(3); // null=全部, 1=已同意, 2=已拒绝, 3=待确认 const filterOptions = [ { label: '全部', value: null }, { label: '待确认', value: 3 }, - { label: '已同意', value: 1 }, - { label: '已拒绝', value: 2 }, + { label: '同意', value: 1 }, + { label: '拒绝', value: 2 }, + { label: '超期取消', value: 4 }, ]; // 状态文本映射 diff --git a/zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_system.vue b/zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_system.vue index 98b7f56e..57517a46 100644 --- a/zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_system.vue +++ b/zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_system.vue @@ -136,7 +136,7 @@