Commit ad7f2c82 authored by 鲁鸿波's avatar 鲁鸿波

所有作业3天未完成的,需要重新发起申请。

危险作业到交底环节后按固定时间判断是否超时,超时自动取消
parent 3954a3b7
......@@ -437,3 +437,62 @@ COMMENT ON COLUMN t_train_examination_manage_retake.pass_score IS '及格分数'
ALTER TABLE t_hazard_disclosure ADD guardian_signature varchar(255) NULL;
COMMENT ON COLUMN t_hazard_disclosure.guardian_signature IS '监护人签字';
#20251205上线需求
ALTER TABLE t_hazard_work_plan ADD hazard_license_pass_time datetime;
COMMENT ON COLUMN t_hazard_work_plan.hazard_license_pass_time IS '安全许可证通过时间';
ALTER TABLE t_hazard_work_plan ADD is_fully_invested varchar(50);
COMMENT ON COLUMN t_hazard_work_plan.is_fully_invested IS '是否满仓(1:是,2:否)';
ALTER TABLE t_hazard_work_plan ADD is_full varchar(50);
COMMENT ON COLUMN t_hazard_work_plan.is_full IS '是否7~9人(1:是,2:否)';
INSERT INTO "t_sys_dict_type" ("dict_id", "dict_name", "dict_type", "is_sys", "status", "create_by", "create_date", "update_by", "update_date", "remarks", "parent_id", "parent_ids", "tree_sort", "cascaded") VALUES ('1', '危险作业超时检测是否可以执行', 'plan_expired', '1', '0', NULL, '2025-11-28 15:30:11', NULL, '2025-11-28 15:30:11', NULL, NULL, NULL, NULL, '0');
INSERT INTO t_sys_dict_data" ("dict_data_id", "dict_id", "dict_key", "dict_value", "tree_sort", "status", "create_by", "create_date", "update_by", "update_date", "remarks", "parent_id", "parent_ids") VALUES ('4', '1', '1', '可以执行', '0', '0', NULL, '2025-11-28 15:31:44', NULL, '2025-11-28 15:31:44', NULL, '0', '0,');
添加表t_hazard_work_plan_expired_log
-- public.t_hazard_work_plan_expired_log definition
-- Drop table
-- DROP TABLE public.t_hazard_work_plan_expired_log;
CREATE TABLE public.t_hazard_work_plan_expired_log (
id varchar(64) NOT NULL,
plan_id varchar(64) NULL, -- 作业id
code varchar(30) NULL, -- 作业编码
work_type varchar(50) NULL, -- 作业类型
danger_level varchar(50) NULL, -- 作业级别
expired_reason varchar(200) NULL, -- 超时原因(三天内未完成,安全许可申请审批通过后未在规定时间内完成)
before_expiration varchar(100) NULL, -- 关闭前状态
hazard_license_pass_time sys."datetime" NULL, -- 安全许可申请通过时间
actual_duration_hours numeric(10, 2) NULL, -- 实际持续小时数
plan_create_date sys."timestamp" NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 作业创建时间
status varchar(1) NULL DEFAULT '0'::varchar,
create_by varchar(64) NULL,
create_date "timestamp" NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_by varchar(64) NULL,
update_date "timestamp" NOT NULL DEFAULT CURRENT_TIMESTAMP,
remarks varchar(100) NULL
);
COMMENT ON TABLE public.t_hazard_work_plan_expired_log IS '危险作业-超时自动关闭记录表';
-- Column comments
COMMENT ON COLUMN public.t_hazard_work_plan_expired_log.plan_id IS '作业id';
COMMENT ON COLUMN public.t_hazard_work_plan_expired_log.code IS '作业编码';
COMMENT ON COLUMN public.t_hazard_work_plan_expired_log.work_type IS '作业类型';
COMMENT ON COLUMN public.t_hazard_work_plan_expired_log.danger_level IS '作业级别';
COMMENT ON COLUMN public.t_hazard_work_plan_expired_log.expired_reason IS '超时原因(三天内未完成,安全许可申请审批通过后未在规定时间内完成)';
COMMENT ON COLUMN public.t_hazard_work_plan_expired_log.before_expiration IS '关闭前状态';
COMMENT ON COLUMN public.t_hazard_work_plan_expired_log.hazard_license_pass_time IS '安全许可申请通过时间';
COMMENT ON COLUMN public.t_hazard_work_plan_expired_log.actual_duration_hours IS '实际持续小时数';
COMMENT ON COLUMN public.t_hazard_work_plan_expired_log.plan_create_date IS '作业创建时间';
package com.testor.common.scheduler;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.testor.biz.sys.dict.data.model.domain.SysDictData;
import com.testor.module.hazard.dao.THazardWorkPlanDao;
import com.testor.module.hazard.dao.THazardWorkPlanExpiredLogDao;
import com.testor.module.hazard.model.domain.THazardWorkPlan;
import com.testor.module.hazard.model.domain.THazardWorkPlanExpiredLog;
import com.testor.module.hazard.model.enums.WorkPlanStatusEnum;
import com.testor.module.sys.service.NewSysDictDataService;
import com.testor.common.util.DangerousOperationValidator;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* HazardWorkPlanScheduler - 优化版(版本 B)
*
* - 字典缓存(可刷新)
* - 统一过期处理流程(日志 -> 停止流程 -> 更新业务状态)
* - 更稳健的 flowable 操作(先查再删,删除历史)
* - 单个 plan 异常捕获,不影响其他计划处理
*
* 注意:
* - 如果你的 DAO 操作需要事务,请在单独需要原子性的私有方法上添加 @Transactional
* - 如果你希望字典立即生效可调用 refreshDictCache()
*/
@Component
@Slf4j
public class HazardWorkPlanScheduler {
private static final String DICT_ENABLE_ID = "1";
private static final String DICT_ENABLE_VALUE = "可以执行";
// 字典id
private static final String DICT_WORK_TYPE = "42a87414a06a4f57b9d3ffb1907284b4";
private static final String DICT_WORK_LEVEL = "222842ea3bb4468bbbeaa3856a4f2731";
@Autowired
private THazardWorkPlanDao hazardWorkPlanDao;
@Autowired
private NewSysDictDataService dictDataService;
@Autowired
private THazardWorkPlanExpiredLogDao hazardWorkPlanExpiredLogDao;
@Autowired
private RuntimeService runtimeService;
@Autowired
private HistoryService historyService;
// 缓存:key = dictValue, value = SysDictData
private final Map<String, SysDictData> workTypeCache = new ConcurrentHashMap<>();
private final Map<String, SysDictData> workLevelCache = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
refreshDictCache();
}
/**
* 定时刷新字典缓存(每 10 分钟刷新一次,保证字典变更能生效)
*/
@Scheduled(cron = "0 0/10 * * * ?")
public void refreshDictCacheScheduled() {
refreshDictCache();
}
private void refreshDictCache() {
try {
Map<String, SysDictData> typeMap = dictDataService.getDictId(DICT_WORK_TYPE)
.stream()
.collect(Collectors.toMap(SysDictData::getDictValue, Function.identity(), (a, b) -> a));
Map<String, SysDictData> levelMap = dictDataService.getDictId(DICT_WORK_LEVEL)
.stream()
.collect(Collectors.toMap(SysDictData::getDictValue, Function.identity(), (a, b) -> a));
workTypeCache.clear();
workTypeCache.putAll(typeMap);
workLevelCache.clear();
workLevelCache.putAll(levelMap);
log.info("HazardWorkPlanScheduler 字典缓存刷新成功:workType={}, workLevel={}",
workTypeCache.size(), workLevelCache.size());
} catch (Exception ex) {
log.error("刷新字典缓存失败", ex);
}
}
/**
* 每5分钟检查安全许可通过后仍在运行的作业
*/
@Scheduled(cron = "0 0/5 * * * ?")
public void refreshSwitch() {
if (!isSchedulerEnabled()) {
log.debug("refreshSwitch: 调度被禁用(字典值非 {})", DICT_ENABLE_VALUE);
return;
}
log.info("开始---refreshSwitch----查询所有危险作业已安全许可申请审批通过后未完成的作业");
List<THazardWorkPlan> plans;
try {
plans = hazardWorkPlanDao.selectHazardLicensePassTime();
} catch (Exception e) {
log.error("查询 selectHazardLicensePassTime 失败", e);
return;
}
if (plans == null || plans.isEmpty()) {
log.info("refreshSwitch: 无需处理的计划");
return;
}
for (THazardWorkPlan plan : plans) {
try {
processPlanWithCaches(plan);
} catch (Exception e) {
// 单个 plan 异常不影响其他计划
log.error("处理计划异常,planId={}, code={}", plan == null ? null : plan.getId(),
plan == null ? null : plan.getCode(), e);
}
}
log.info("结束---refreshSwitch-----查询所有危险作业已安全许可申请审批通过后未完成的作业");
}
/**
* 每小时检查所有未完成作业
*/
@Scheduled(cron = "0 0 * * * ?")
public void refreshSwitch1() {
if (!isSchedulerEnabled()) {
log.debug("refreshSwitch1: 调度被禁用(字典值非 {})", DICT_ENABLE_VALUE);
return;
}
log.info("开始---refreshSwitch1-------查询所有危险作业未完成的作业");
List<THazardWorkPlan> plans;
try {
plans = hazardWorkPlanDao.selectNotCompleteList();
} catch (Exception e) {
log.error("查询 selectNotCompleteList 失败", e);
return;
}
if (plans == null || plans.isEmpty()) {
log.info("refreshSwitch1: 无需处理的计划");
return;
}
for (THazardWorkPlan plan : plans) {
try {
checkThreeDaysExpiredRelaxed(plan);
} catch (Exception e) {
log.error("宽松检查处理异常,planId={}, code={}", plan == null ? null : plan.getId(),
plan == null ? null : plan.getCode(), e);
}
}
log.info("结束---refreshSwitch1-------查询所有危险作业未完成的作业");
}
/****************************
* 核心处理函数
****************************/
private void processPlanWithCaches(THazardWorkPlan plan) {
if (plan == null) {
return;
}
// 从缓存读取字典,减少 DB 查询
SysDictData workTypeDict = workTypeCache.get(plan.getWorkType());
SysDictData workLevelDict = workLevelCache.get(plan.getWorkLevel());
if (workTypeDict == null || workLevelDict == null) {
log.warn("字典缺失,跳过处理 planId={}, workType={}, workLevel={}",
plan.getId(), plan.getWorkType(), plan.getWorkLevel());
return;
}
processHazardWorkPlan(plan, workTypeDict, workLevelDict);
}
public void processHazardWorkPlan(THazardWorkPlan plan, SysDictData workTypeDict, SysDictData workLevelDict) {
if (workTypeDict == null || workLevelDict == null || plan == null) {
return;
}
String workTypeName = workTypeDict.getDictValue();
String dangerLevelName = workLevelDict.getDictValue();
DangerousOperationValidator.OperationType operationType =
DangerousOperationValidator.OperationType.fromDictValue(workTypeName);
DangerousOperationValidator.DangerLevel dangerLevel =
DangerousOperationValidator.DangerLevel.fromDictValue(dangerLevelName);
if (operationType == null || dangerLevel == null) {
log.warn("无法识别的作业类型或危险等级: {} - {} for planId={}", workTypeName, dangerLevelName, plan.getId());
return;
}
// 计算从审批通过到现在的小时数
double actualHours = calculateHoursBetween(plan.getHazardLicensePassTime());
// 验证规则
String validationResult = DangerousOperationValidator.validateOperation(
workTypeName, dangerLevelName, actualHours, plan);
if (!"在安全时间内".equals(validationResult)) {
handleExpiredOperation(plan, validationResult, operationType, dangerLevel, actualHours);
} else {
log.debug("作业在安全时间内, planId={}, hours={}", plan.getId(), actualHours);
}
}
/**
* 处理过期(核心步骤:记录日志 -> 停止流程 -> 更新业务状态)
*/
@Transactional
public void handleExpiredOperation(THazardWorkPlan plan,
String validationResult,
DangerousOperationValidator.OperationType operationType,
DangerousOperationValidator.DangerLevel dangerLevel,
double actualHours) {
log.warn("作业过期 - planId={}, code={}, type={}, level={}, hours={}, reason={}",
plan.getId(), plan.getCode(),
operationType == null ? null : operationType.getName(),
dangerLevel == null ? null : dangerLevel.getLevel(),
actualHours, validationResult);
// 1) 记录过期日志
try {
THazardWorkPlanExpiredLog expiredLog = new THazardWorkPlanExpiredLog();
expiredLog.setPlanId(plan.getId());
expiredLog.setCode(plan.getCode());
expiredLog.setWorkType(operationType == null ? null : operationType.getName());
expiredLog.setDangerLevel(dangerLevel == null ? null : dangerLevel.getLevel());
expiredLog.setExpiredReason("作业未在安全时间内完成: " + validationResult);
expiredLog.setBeforeExpiration(plan.getStatus());
expiredLog.setHazardLicensePassTime(plan.getHazardLicensePassTime());
expiredLog.setActualDurationHours(BigDecimal.valueOf(actualHours));
expiredLog.setPlanCreateDate(plan.getCreateDate());
expiredLog.setCreateBy("system-auto");
expiredLog.setCreateDate(new Date());
hazardWorkPlanExpiredLogDao.insert(expiredLog);
} catch (Exception e) {
log.error("插入过期日志失败,planId={}", plan.getId(), e);
}
// 2) 停止 Flowable 流程
stopFlowableProcess(plan);
// 3) 更新业务状态
try {
updateOperationStatus(plan, WorkPlanStatusEnum.TIMEOUT_CANCELED.getValue());
} catch (Exception e) {
log.error("更新作业状态失败,planId={}, code={}", plan.getId(), plan.getCode(), e);
}
}
/**
* 检查3天过期逻辑
*/
private void checkThreeDaysExpiredRelaxed(THazardWorkPlan plan) {
if (plan == null) {
return;
}
double hoursSinceCreation = calculateHoursBetween(plan.getHazardLicensePassTime());
if (hoursSinceCreation > 72) {
handleThreeDaysExpired(plan, hoursSinceCreation);
} else {
log.debug("检查通过 - planId={}, hoursSinceCreation={}", plan.getId(), hoursSinceCreation);
}
}
private void handleThreeDaysExpired(THazardWorkPlan plan, double actualHours) {
log.warn("3天过期 - planId={}, code={}, hoursSinceCreation={}",
plan.getId(), plan.getCode(), actualHours);
// 记录日志
try {
THazardWorkPlanExpiredLog expiredLog = new THazardWorkPlanExpiredLog();
expiredLog.setPlanId(plan.getId());
expiredLog.setCode(plan.getCode());
expiredLog.setWorkType(plan.getWorkType());
expiredLog.setDangerLevel(plan.getWorkLevel());
expiredLog.setExpiredReason("作业创建后3天内未完成,需要重新发起申请");
expiredLog.setBeforeExpiration(plan.getWorkStatus());
expiredLog.setHazardLicensePassTime(plan.getHazardLicensePassTime());
expiredLog.setActualDurationHours(BigDecimal.valueOf(actualHours));
expiredLog.setPlanCreateDate(plan.getCreateDate());
expiredLog.setStatus("0");
expiredLog.setCreateBy("system-relaxed");
expiredLog.setCreateDate(new Date());
hazardWorkPlanExpiredLogDao.insert(expiredLog);
} catch (Exception e) {
log.error("记录3天过期日志失败,planId={}", plan.getId(), e);
}
// 停止流程并更新状态
stopFlowableProcess(plan);
try {
// 更新状态
updateOperationStatus(plan, WorkPlanStatusEnum.TIMEOUT_CANCELED.getValue());
} catch (Exception e) {
log.error("3天过期后更新作业状态失败,planId={}", plan.getId(), e);
}
}
/**
* 检查调度是否启用(通过字典开关)
*/
private boolean isSchedulerEnabled() {
try {
List<SysDictData> dict = dictDataService.getDictId(DICT_ENABLE_ID);
if (dict != null && !dict.isEmpty()) {
return DICT_ENABLE_VALUE.equals(dict.get(0).getDictValue());
}
} catch (Exception e) {
log.error("检查调度开关失败", e);
}
return false;
}
/**
* 计算从 start 到当前的小时数(对 null 做防护)
*/
private double calculateHoursBetween(Date start) {
if (start == null) {
return 0d;
}
long diffMillis = System.currentTimeMillis() - start.getTime();
return diffMillis / (1000.0 * 60 * 60);
}
/**
* 停止流程(更稳健的实现:先查询流程实例是否存在,再删除运行时并删除历史)
* 内部捕获异常,保证调度器稳定运行。
*
* 注意:如果你希望在删除历史前保留审计,请删除 historyService.deleteHistoricProcessInstance 调用。
*/
private void stopFlowableProcess(THazardWorkPlan plan) {
if (plan == null) {
return;
}
String processInstanceId = plan.getProcessId(); // 保持你当前字段名
if (StringUtils.isBlank(processInstanceId)) {
log.debug("stopFlowableProcess: processId 为空,跳过,planId={}", plan.getId());
return;
}
try {
ProcessInstance pi = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
if (pi == null) {
log.info("流程实例不存在或已结束,跳过删除,processInstanceId={}, planId={}", processInstanceId, plan.getId());
// 如果你希望同时清理历史未完成记录,可尝试删除历史(此处不做以免误删)
try {
// 如果确实需要删除历史:historyService.deleteHistoricProcessInstance(processInstanceId);
} catch (Exception ex) {
log.warn("尝试删除历史流程失败,processInstanceId={}", processInstanceId, ex);
}
return;
}
// 删除运行时流程实例
runtimeService.deleteProcessInstance(processInstanceId, "系统超时自动取消");
// 根据业务决定是否删除历史记录;此处删除历史,避免残留(可注释)
try {
historyService.deleteHistoricProcessInstance(processInstanceId);
} catch (Exception e) {
// 删除历史非关键路径:记录日志即可
log.warn("删除历史流程实例失败 processInstanceId={}, err={}", processInstanceId, e.getMessage());
}
log.info("已停止流程实例 processInstanceId={} (planId={})", processInstanceId, plan.getId());
} catch (Exception e) {
log.error("停止流程失败 processInstanceId={}, planId={}", processInstanceId, plan.getId(), e);
}
}
/**
* 更新业务作业状态(局部更新,避免覆盖其他字段)
* 如果你希望该更新与过期日志插入为同一事务,请在本方法或调用端加 @Transactional
*/
private void updateOperationStatus(THazardWorkPlan plan, String newStatus) {
if (plan == null) {
return;
}
try {
THazardWorkPlan update = new THazardWorkPlan();
update.setId(plan.getId());
update.setWorkStatus(newStatus);
update.setUpdateDate(new Date()); // 假设字段名为 updateDate;如果不同请改为你项目字段
hazardWorkPlanDao.updateById(update);
log.info("更新作业状态成功 planId={}, newStatus={}", plan.getId(), newStatus);
} catch (Exception e) {
log.error("更新作业状态失败 planId={}, newStatus={}", plan.getId(), newStatus, e);
throw e;
}
}
}
package com.testor.common.util;
import com.testor.module.hazard.model.domain.THazardWorkPlan;
import java.util.HashMap;
import java.util.Map;
......@@ -14,7 +16,8 @@ public class DangerousOperationValidator {
IN_WAREHOUSE("进仓作业"),
OUT_WAREHOUSE("出仓作业"),
FLAG_WAREHOUSE("平仓作业"),
IN_OUT_WAREHOUSE("进出仓作业");
IN_OUT_WAREHOUSE("进出仓作业"),
CIRCULATION_FUMIGATION("环流熏蒸作业");
private final String name;
OperationType(String name) {
......@@ -93,6 +96,12 @@ public class DangerousOperationValidator {
// 其他等级不设置限制
TIME_LIMITS.put(OperationType.FUMIGATION, fumigationMap);
// 环流熏蒸
Map<DangerLevel, Integer> circulationFumigationMap = new HashMap<>();
circulationFumigationMap.put(DangerLevel.HIGH, 7 * 24);
// 其他等级不设置限制
TIME_LIMITS.put(OperationType.CIRCULATION_FUMIGATION, circulationFumigationMap);
// 进仓
Map<DangerLevel, Integer> inWarehouseMap = new HashMap<>();
inWarehouseMap.put(DangerLevel.HIGH, 12);
......@@ -122,7 +131,7 @@ public class DangerousOperationValidator {
TIME_LIMITS.put(OperationType.IN_OUT_WAREHOUSE, inOutWarehouseMap);
}
public static String validateOperation(String workTypeDictValue, String workLevelDictValue, double actualHours) {
public static String validateOperation(String workTypeDictValue, String workLevelDictValue, double actualHours, THazardWorkPlan plan) {
// 转换为枚举类型
OperationType type = OperationType.fromDictValue(workTypeDictValue);
DangerLevel level = DangerLevel.fromDictValue(workLevelDictValue);
......@@ -138,8 +147,8 @@ public class DangerousOperationValidator {
return "在安全时间内";
}
// 获取具体等级的时间限制
Integer timeLimit = levelMap.get(level);
// 特殊处理:进出仓、进仓、出仓作业的较大危险级别
Integer timeLimit = getAdjustedTimeLimit(type, level, plan);
// 如果没有找到该等级的限制,返回安全
if (timeLimit == null) {
......@@ -156,4 +165,59 @@ public class DangerousOperationValidator {
return "在安全时间内";
}
/**
* 获取调整后的时间限制(处理特殊情况)
*/
private static Integer getAdjustedTimeLimit(OperationType type, DangerLevel level, THazardWorkPlan plan) {
// 获取默认的时间限制
Map<DangerLevel, Integer> levelMap = TIME_LIMITS.get(type);
if (levelMap == null) {
return null;
}
Integer timeLimit = levelMap.get(level);
if (timeLimit == null) {
return null;
}
//如果是非北良的熏蒸作业高度为1小时,如果是北良的为7天
if (level == DangerLevel.HIGH){
}
// 特殊规则:进出仓、进仓、出仓作业的较大危险级别
if (level == DangerLevel.MEDIUM &&
(type == OperationType.IN_OUT_WAREHOUSE ||
type == OperationType.IN_WAREHOUSE ||
type == OperationType.OUT_WAREHOUSE ||
type == OperationType.FLAG_WAREHOUSE)) {
// 检查是否满足特殊条件:isFullyInvested为1或者isFull为1
boolean isSpecialCondition = isSpecialConditionMet(plan);
if (isSpecialCondition) {
// 满足条件:时间限制为24小时
timeLimit = 24;
} else {
// 不满足条件:时间限制为72小时
timeLimit = 72;
}
}
return timeLimit;
}
/**
* 检查是否满足特殊条件:isFullyInvested为1或者isFull为1
*/
private static boolean isSpecialConditionMet(THazardWorkPlan plan) {
if (plan == null) {
return false;
}
String isEscalated = plan.getIsEscalated();
String isFull = plan.getIsFull();
return "1".equals(isEscalated) || "1".equals(isFull);
}
}
\ No newline at end of file
......@@ -306,7 +306,7 @@ public class THazardWorkPlanController extends SuperController
}
}
@ApiOperation(value = "危险作业计划时间是否合规", notes = "危险作业计划时间是否合规")
/*@ApiOperation(value = "危险作业计划时间是否合规", notes = "危险作业计划时间是否合规")
@GetMapping(value = "/validateOperation")
public BaseResponse validateOperation(String workType, String workLevel, String scheduledStartTime,String scheduledEndTime) throws BusinessException {
BaseResponse<BizGeneralResponse> baseResponse = new BaseResponse<>();
......@@ -314,7 +314,7 @@ public class THazardWorkPlanController extends SuperController
baseResponse.setMsg(result);
return baseResponse;
}
}*/
@ApiOperation(value = "任务转签", notes = "任务转签")
@GetMapping(value = "/transferTask")
......
package com.testor.module.hazard.controller;
import com.testor.module.hazard.model.domain.THazardWorkPlan;
import com.testor.module.hazard.model.domain.THazardWorkPlanExpiredLog;
import com.testor.module.hazard.service.THazardWorkPlanExpiredLogService;
import com.tongtech.tfw.backend.common.biz.models.BaseResponse;
import com.tongtech.tfw.backend.core.helper.ObjectHelper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/hazard/expiredLog")
@Api(value = "THazardWorkPlanExpiredLog", tags = "危险作业计划过期日志")
@Slf4j
public class THazardWorkPlanExpiredLogController {
@Autowired
private THazardWorkPlanExpiredLogService hazardWorkPlanExpiredLogService;
@ApiOperation(value = "危险作业计划自动过期", notes = "危险作业计划自动过期")
@GetMapping(value = "/refreshSwitch")
public BaseResponse refreshSwitch() {
BaseResponse baseResponse=new BaseResponse<>();
hazardWorkPlanExpiredLogService.refreshSwitch();
baseResponse.setData(true);
baseResponse.setMsg("操作成功");
return baseResponse;
}
@ApiOperation(value = "危险作业三天内未完成自动过期", notes = "危险作业三天内未完成自动过期")
@GetMapping(value = "/refreshSwitch1")
public BaseResponse refreshSwitch1() {
BaseResponse baseResponse=new BaseResponse<>();
hazardWorkPlanExpiredLogService.refreshSwitch1();
baseResponse.setData(true);
baseResponse.setMsg("操作成功");
return baseResponse;
}
@ApiOperation(value = "根据计划id查询过期日志", notes = "根据计划id查询过期日志")
@GetMapping(value = "/selectByPlanId")
public BaseResponse<THazardWorkPlanExpiredLog> selectByPlanId(@RequestParam String planId) {
BaseResponse<THazardWorkPlanExpiredLog> baseResponse=new BaseResponse<>();
THazardWorkPlanExpiredLog hazardWorkPlanExpiredLog= hazardWorkPlanExpiredLogService.selectByPlanId(planId);
baseResponse.setData(hazardWorkPlanExpiredLog);
return baseResponse;
}
}
......@@ -10,6 +10,8 @@ import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* 危险作业计划Mapper接口
*
......@@ -29,4 +31,17 @@ public interface THazardWorkPlanDao extends SuperDao<THazardWorkPlan>
@Select("SELECT * FROM t_hazard_work_plan WHERE code = #{code} and status = 0")
THazardWorkPlan selectByCode(@Param("code") String code);
/**
* 查询所有危险作业已安全许可申请审批通过后未完成的作业
* @return
*/
List<THazardWorkPlan> selectHazardLicensePassTime();
/**
* 查询所有危险作业未完成的作业
* @return
*/
List<THazardWorkPlan> selectNotCompleteList();
}
package com.testor.module.hazard.dao;
import com.testor.module.hazard.model.domain.THazardWorkPlan;
import com.tongtech.tfw.backend.common.models.supers.SuperDao;
import com.testor.module.hazard.model.domain.THazardWorkPlanExpiredLog;
public interface THazardWorkPlanExpiredLogDao extends SuperDao<THazardWorkPlanExpiredLog>{
}
......@@ -309,6 +309,24 @@ public class THazardWorkPlan extends SuperModel
@TableField("guardian_signature")
private String guardianSignature;
@ApiModelProperty(value = "安全许可证通过时间")
@TableField("hazard_license_pass_time")
private Date hazardLicensePassTime;
/**
* 是否满仓(1:是,2:否)
*/
@ApiModelProperty(value = "是否满仓(1:是,2:否)")
@TableField("is_fully_invested")
private String isFullyInvested;
/**
* 是否7~9人(1:是,2:否)
*/
@ApiModelProperty(value = "是否7~9人(1:是,2:否)")
@TableField("is_full")
private String isFull;
@ApiModelProperty(value = "当前流程环节")
@TableField(exist = false)
private String currentTaskName;
......
package com.testor.module.hazard.model.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.tongtech.tfw.backend.common.models.supers.SuperModel;
import org.springframework.format.annotation.DateTimeFormat;
/**
* 危险作业-超时自动关闭记录表对象 t_hazard_work_plan_expired_log
*
* @author testor-framework
* @date 2025-01-XX
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("t_hazard_work_plan_expired_log")
@ApiModel(value = "THazardWorkPlanExpiredLog对象", description = "危险作业-超时自动关闭记录表")
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class THazardWorkPlanExpiredLog extends SuperModel
{
private static final long serialVersionUID = 1L;
/** id */
@ApiModelProperty(value = "id")
@TableId("id")
private String id;
/** 作业id */
@ApiModelProperty(value = "作业id")
@TableField("plan_id")
private String planId;
/** 作业编码 */
@ApiModelProperty(value = "作业编码")
@TableField("code")
private String code;
/** 作业类型 */
@ApiModelProperty(value = "作业类型")
@TableField("work_type")
private String workType;
/** 危险等级 */
@ApiModelProperty(value = "危险等级")
@TableField("danger_level")
private String dangerLevel;
/** 超时原因 */
@ApiModelProperty(value = "超时原因")
@TableField("expired_reason")
private String expiredReason;
/** 关闭前状态 */
@ApiModelProperty(value = "关闭前状态")
@TableField("before_expiration")
private String beforeExpiration;
/** 安全许可申请通过时间 */
@ApiModelProperty(value = "安全许可申请通过时间")
@TableField("hazard_license_pass_time")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date hazardLicensePassTime;
/** 实际持续小时数 */
@ApiModelProperty(value = "实际持续小时数")
@TableField("actual_duration_hours")
private BigDecimal actualDurationHours;
/** 作业创建时间 */
@ApiModelProperty(value = "作业创建时间")
@TableField("plan_create_date")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date planCreateDate;
// 常量定义
public static final String ID = "id";
public static final String PLAN_ID = "plan_id";
public static final String CODE = "code";
public static final String WORK_TYPE = "work_type";
public static final String DANGER_LEVEL = "danger_level";
public static final String EXPIRED_REASON = "expired_reason";
public static final String BEFORE_EXPIRATION = "before_expiration";
public static final String HAZARD_LICENSE_PASS_TIME = "hazard_license_pass_time";
public static final String ACTUAL_DURATION_HOURS = "actual_duration_hours";
public static final String PLAN_CREATE_DATE = "plan_create_date";
}
\ No newline at end of file
......@@ -62,7 +62,9 @@ public enum WorkPlanStatusEnum {
IN_PROGRESS("10", "进行中"),
TO_BE_CLOSED("11", "待安全许可证关闭");
TO_BE_CLOSED("11", "待安全许可证关闭"),
TIMEOUT_CANCELED("12", "已超时自动取消");
private final String value; // 状态值
private final String description; // 状态描述
......
package com.testor.module.hazard.service;
import com.testor.module.hazard.model.domain.THazardWorkPlan;
import com.testor.module.hazard.model.domain.THazardWorkPlanExpiredLog;
import com.tongtech.tfw.backend.common.models.supers.SuperService;
public interface THazardWorkPlanExpiredLogService extends SuperService<THazardWorkPlanExpiredLog> {
/**
* 危险作业计划自动过期
*/
public void refreshSwitch();
/**
* 危险作业三天内未完成自动过期
*/
public void refreshSwitch1();
/**
* 根据计划id查询过期日志
* @param planId
* @return
*/
public THazardWorkPlanExpiredLog selectByPlanId(String planId);
}
......@@ -100,7 +100,7 @@ public interface THazardWorkPlanService extends SuperService<THazardWorkPlan> {
* @param scheduledEndTime 计划结束时间
* @return
*/
String validateOperation(String workType, String workLevel, String scheduledStartTime,String scheduledEndTime);
//String validateOperation(String workType, String workLevel, String scheduledStartTime,String scheduledEndTime);
/**
* 任务转签
......
package com.testor.module.hazard.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.testor.biz.sys.dict.data.model.domain.SysDictData;
import com.testor.common.util.DangerousOperationValidator;
import com.testor.module.hazard.dao.THazardWorkPlanDao;
import com.testor.module.hazard.dao.THazardWorkPlanExpiredLogDao;
import com.testor.module.hazard.model.domain.THazardWorkPlan;
import com.testor.module.hazard.model.domain.THazardWorkPlanExpiredLog;
import com.testor.module.hazard.model.enums.WorkPlanStatusEnum;
import com.testor.module.hazard.service.THazardWorkPlanExpiredLogService;
import com.testor.module.sys.service.NewSysDictDataService;
import com.tongtech.tfw.backend.common.models.supers.SuperServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
@Slf4j
@Service
public class THazardWorkPlanExpiredLogServiceImpl extends SuperServiceImpl<THazardWorkPlanExpiredLogDao, THazardWorkPlanExpiredLog> implements THazardWorkPlanExpiredLogService {
private static final String DICT_ENABLE_ID = "1";
private static final String DICT_ENABLE_VALUE = "可以执行";
// 字典id
private static final String DICT_WORK_TYPE = "42a87414a06a4f57b9d3ffb1907284b4";
private static final String DICT_WORK_LEVEL = "222842ea3bb4468bbbeaa3856a4f2731";
@Autowired
private THazardWorkPlanDao hazardWorkPlanDao;
@Autowired
private NewSysDictDataService dictDataService;
@Autowired
private THazardWorkPlanExpiredLogDao hazardWorkPlanExpiredLogDao;
@Autowired
private RuntimeService runtimeService;
@Autowired
private HistoryService historyService;
// 缓存:key = dictValue, value = SysDictData
private final Map<String, SysDictData> workTypeCache = new ConcurrentHashMap<>();
private final Map<String, SysDictData> workLevelCache = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
refreshDictCache();
}
/**
* 定时刷新字典缓存(每 10 分钟刷新一次,保证字典变更能生效)
*/
public void refreshDictCacheScheduled() {
refreshDictCache();
}
private void refreshDictCache() {
try {
Map<String, SysDictData> typeMap = dictDataService.getDictId(DICT_WORK_TYPE)
.stream()
.collect(Collectors.toMap(SysDictData::getDictValue, Function.identity(), (a, b) -> a));
Map<String, SysDictData> levelMap = dictDataService.getDictId(DICT_WORK_LEVEL)
.stream()
.collect(Collectors.toMap(SysDictData::getDictValue, Function.identity(), (a, b) -> a));
workTypeCache.clear();
workTypeCache.putAll(typeMap);
workLevelCache.clear();
workLevelCache.putAll(levelMap);
log.info("HazardWorkPlanScheduler 字典缓存刷新成功:workType={}, workLevel={}",
workTypeCache.size(), workLevelCache.size());
} catch (Exception ex) {
log.error("刷新字典缓存失败", ex);
}
}
/**
* 每5分钟检查安全许可通过后仍在运行的作业
*/
@Override
public void refreshSwitch() {
if (!isSchedulerEnabled()) {
log.debug("refreshSwitch: 调度被禁用(字典值非 {})", DICT_ENABLE_VALUE);
return;
}
log.info("开始---refreshSwitch----查询所有危险作业已安全许可申请审批通过后未完成的作业");
List<THazardWorkPlan> plans;
try {
plans = hazardWorkPlanDao.selectHazardLicensePassTime();
} catch (Exception e) {
log.error("查询 selectHazardLicensePassTime 失败", e);
return;
}
if (plans == null || plans.isEmpty()) {
log.info("refreshSwitch: 无需处理的计划");
return;
}
for (THazardWorkPlan plan : plans) {
try {
processPlanWithCaches(plan);
} catch (Exception e) {
// 单个 plan 异常不影响其他计划
log.error("处理计划异常,planId={}, code={}", plan == null ? null : plan.getId(),
plan == null ? null : plan.getCode(), e);
}
}
log.info("结束---refreshSwitch-----查询所有危险作业已安全许可申请审批通过后未完成的作业");
}
/**
* 每小时检查所有未完成作业
*/
@Override
public void refreshSwitch1() {
if (!isSchedulerEnabled()) {
log.debug("refreshSwitch1: 调度被禁用(字典值非 {})", DICT_ENABLE_VALUE);
return;
}
log.info("开始---refreshSwitch1-------查询所有危险作业未完成的作业");
List<THazardWorkPlan> plans;
try {
plans = hazardWorkPlanDao.selectNotCompleteList();
} catch (Exception e) {
log.error("查询 selectNotCompleteList 失败", e);
return;
}
if (plans == null || plans.isEmpty()) {
log.info("refreshSwitch1: 无需处理的计划");
return;
}
for (THazardWorkPlan plan : plans) {
try {
checkThreeDaysExpiredRelaxed(plan);
} catch (Exception e) {
log.error("宽松检查处理异常,planId={}, code={}", plan == null ? null : plan.getId(),
plan == null ? null : plan.getCode(), e);
}
}
log.info("结束---refreshSwitch1-------查询所有危险作业未完成的作业");
}
/****************************
* 核心处理函数
****************************/
private void processPlanWithCaches(THazardWorkPlan plan) {
if (plan == null) {
return;
}
// 从缓存读取字典,减少 DB 查询
SysDictData workTypeDict = workTypeCache.get(plan.getWorkType());
SysDictData workLevelDict = workLevelCache.get(plan.getWorkLevel());
if (workTypeDict == null || workLevelDict == null) {
log.warn("字典缺失,跳过处理 planId={}, workType={}, workLevel={}",
plan.getId(), plan.getWorkType(), plan.getWorkLevel());
return;
}
processHazardWorkPlan(plan, workTypeDict, workLevelDict);
}
public void processHazardWorkPlan(THazardWorkPlan plan, SysDictData workTypeDict, SysDictData workLevelDict) {
if (workTypeDict == null || workLevelDict == null || plan == null) {
return;
}
String workTypeName = workTypeDict.getDictValue();
String dangerLevelName = workLevelDict.getDictValue();
DangerousOperationValidator.OperationType operationType =
DangerousOperationValidator.OperationType.fromDictValue(workTypeName);
DangerousOperationValidator.DangerLevel dangerLevel =
DangerousOperationValidator.DangerLevel.fromDictValue(dangerLevelName);
if (operationType == null || dangerLevel == null) {
log.warn("无法识别的作业类型或危险等级: {} - {} for planId={}", workTypeName, dangerLevelName, plan.getId());
return;
}
// 计算从审批通过到现在的小时数
double actualHours = calculateHoursBetween(plan.getHazardLicensePassTime());
// 验证规则
String validationResult = DangerousOperationValidator.validateOperation(
workTypeName, dangerLevelName, actualHours, plan);
if (!"在安全时间内".equals(validationResult)) {
handleExpiredOperation(plan, validationResult, operationType, dangerLevel, actualHours);
} else {
log.debug("作业在安全时间内, planId={}, hours={}", plan.getId(), actualHours);
}
}
/**
* 处理过期(核心步骤:记录日志 -> 停止流程 -> 更新业务状态)
*/
@Transactional
public void handleExpiredOperation(THazardWorkPlan plan,
String validationResult,
DangerousOperationValidator.OperationType operationType,
DangerousOperationValidator.DangerLevel dangerLevel,
double actualHours) {
log.warn("作业过期 - planId={}, code={}, type={}, level={}, hours={}, reason={}",
plan.getId(), plan.getCode(),
operationType == null ? null : operationType.getName(),
dangerLevel == null ? null : dangerLevel.getLevel(),
actualHours, validationResult);
// 1) 记录过期日志
try {
THazardWorkPlanExpiredLog expiredLog = new THazardWorkPlanExpiredLog();
expiredLog.setPlanId(plan.getId());
expiredLog.setCode(plan.getCode());
expiredLog.setWorkType(operationType == null ? null : operationType.getName());
expiredLog.setDangerLevel(dangerLevel == null ? null : dangerLevel.getLevel());
expiredLog.setExpiredReason("作业未在安全时间内完成: " + validationResult);
expiredLog.setBeforeExpiration(plan.getStatus());
expiredLog.setHazardLicensePassTime(plan.getHazardLicensePassTime());
expiredLog.setActualDurationHours(BigDecimal.valueOf(actualHours));
expiredLog.setPlanCreateDate(plan.getCreateDate());
expiredLog.setCreateBy("system-auto");
expiredLog.setCreateDate(new Date());
hazardWorkPlanExpiredLogDao.insert(expiredLog);
} catch (Exception e) {
log.error("插入过期日志失败,planId={}", plan.getId(), e);
}
// 2) 停止 Flowable 流程
stopFlowableProcess(plan);
// 3) 更新业务状态
try {
updateOperationStatus(plan, WorkPlanStatusEnum.TIMEOUT_CANCELED.getValue());
} catch (Exception e) {
log.error("更新作业状态失败,planId={}, code={}", plan.getId(), plan.getCode(), e);
}
}
/**
* 检查3天过期逻辑
*/
private void checkThreeDaysExpiredRelaxed(THazardWorkPlan plan) {
if (plan == null) {
return;
}
double hoursSinceCreation = calculateHoursBetween(plan.getCreateDate());
if (hoursSinceCreation > 72) {
handleThreeDaysExpired(plan, hoursSinceCreation);
} else {
log.debug("检查通过 - planId={}, hoursSinceCreation={}", plan.getId(), hoursSinceCreation);
}
}
private void handleThreeDaysExpired(THazardWorkPlan plan, double actualHours) {
log.warn("3天过期 - planId={}, code={}, hoursSinceCreation={}",
plan.getId(), plan.getCode(), actualHours);
// 记录日志
try {
THazardWorkPlanExpiredLog expiredLog = new THazardWorkPlanExpiredLog();
expiredLog.setPlanId(plan.getId());
expiredLog.setCode(plan.getCode());
expiredLog.setWorkType(plan.getWorkType());
expiredLog.setDangerLevel(plan.getWorkLevel());
expiredLog.setExpiredReason("作业创建后3天内未完成,需要重新发起申请");
expiredLog.setBeforeExpiration(plan.getWorkStatus());
expiredLog.setHazardLicensePassTime(plan.getHazardLicensePassTime());
expiredLog.setActualDurationHours(BigDecimal.valueOf(actualHours));
expiredLog.setPlanCreateDate(plan.getCreateDate());
expiredLog.setStatus("0");
expiredLog.setCreateBy("system-relaxed");
expiredLog.setCreateDate(new Date());
hazardWorkPlanExpiredLogDao.insert(expiredLog);
} catch (Exception e) {
log.error("记录3天过期日志失败,planId={}", plan.getId(), e);
}
// 停止流程并更新状态
stopFlowableProcess(plan);
try {
// 更新状态
updateOperationStatus(plan, WorkPlanStatusEnum.TIMEOUT_CANCELED.getValue());
} catch (Exception e) {
log.error("3天过期后更新作业状态失败,planId={}", plan.getId(), e);
}
}
/**
* 检查调度是否启用(通过字典开关)
*/
private boolean isSchedulerEnabled() {
try {
List<SysDictData> dict = dictDataService.getDictId(DICT_ENABLE_ID);
if (dict != null && !dict.isEmpty()) {
return DICT_ENABLE_VALUE.equals(dict.get(0).getDictValue());
}
} catch (Exception e) {
log.error("检查调度开关失败", e);
}
return false;
}
/**
* 计算从 start 到当前的小时数(对 null 做防护)
*/
private double calculateHoursBetween(Date start) {
if (start == null) {
return 0d;
}
long diffMillis = System.currentTimeMillis() - start.getTime();
return diffMillis / (1000.0 * 60 * 60);
}
/**
* 停止流程(更稳健的实现:先查询流程实例是否存在,再删除运行时并删除历史)
* 内部捕获异常,保证调度器稳定运行。
*
* 注意:如果你希望在删除历史前保留审计,请删除 historyService.deleteHistoricProcessInstance 调用。
*/
private void stopFlowableProcess(THazardWorkPlan plan) {
if (plan == null) {
return;
}
String processInstanceId = plan.getProcessId(); // 保持你当前字段名
if (StringUtils.isBlank(processInstanceId)) {
log.debug("stopFlowableProcess: processId 为空,跳过,planId={}", plan.getId());
return;
}
try {
ProcessInstance pi = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
if (pi == null) {
log.info("流程实例不存在或已结束,跳过删除,processInstanceId={}, planId={}", processInstanceId, plan.getId());
// 如果你希望同时清理历史未完成记录,可尝试删除历史(此处不做以免误删)
try {
// 如果确实需要删除历史:historyService.deleteHistoricProcessInstance(processInstanceId);
} catch (Exception ex) {
log.warn("尝试删除历史流程失败,processInstanceId={}", processInstanceId, ex);
}
return;
}
// 删除运行时流程实例
runtimeService.deleteProcessInstance(processInstanceId, "系统超时自动取消");
// 根据业务决定是否删除历史记录;此处删除历史,避免残留(可注释)
try {
historyService.deleteHistoricProcessInstance(processInstanceId);
} catch (Exception e) {
// 删除历史非关键路径:记录日志即可
log.warn("删除历史流程实例失败 processInstanceId={}, err={}", processInstanceId, e.getMessage());
}
log.info("已停止流程实例 processInstanceId={} (planId={})", processInstanceId, plan.getId());
} catch (Exception e) {
log.error("停止流程失败 processInstanceId={}, planId={}", processInstanceId, plan.getId(), e);
}
}
/**
* 更新业务作业状态(局部更新,避免覆盖其他字段)
* 如果你希望该更新与过期日志插入为同一事务,请在本方法或调用端加 @Transactional
*/
private void updateOperationStatus(THazardWorkPlan plan, String newStatus) {
if (plan == null) {
return;
}
try {
THazardWorkPlan update = new THazardWorkPlan();
update.setId(plan.getId());
update.setWorkStatus(newStatus);
update.setUpdateDate(new Date()); // 假设字段名为 updateDate;如果不同请改为你项目字段
hazardWorkPlanDao.updateById(update);
log.info("更新作业状态成功 planId={}, newStatus={}", plan.getId(), newStatus);
} catch (Exception e) {
log.error("更新作业状态失败 planId={}, newStatus={}", plan.getId(), newStatus, e);
throw e;
}
}
@Override
public THazardWorkPlanExpiredLog selectByPlanId(String planId) {
return hazardWorkPlanExpiredLogDao.selectOne(new LambdaQueryWrapper<THazardWorkPlanExpiredLog>()
.eq(THazardWorkPlanExpiredLog::getPlanId, planId));
}
}
......@@ -627,6 +627,7 @@ public class THazardWorkPlanServiceImpl extends SuperServiceImpl<THazardWorkPlan
THazardWorkPlan updWorkPlan = new THazardWorkPlan();
updWorkPlan.setId(id);
updWorkPlan.setHazardLicensePassTime(new Date());
this.updateById(updWorkPlan);
}
......@@ -885,9 +886,9 @@ public class THazardWorkPlanServiceImpl extends SuperServiceImpl<THazardWorkPlan
return null;
}
@Override
/*@Override
public String validateOperation(String workType, String workLevel, String scheduledStartTime, String scheduledEndTime) {
/*中粮/北良规则如下
*//*中粮/北良规则如下
动火作业高度危险: 8小时内
动火作业较大危险: 12小时内
动火作业一般危险: 72小时内
......@@ -899,7 +900,7 @@ public class THazardWorkPlanServiceImpl extends SuperServiceImpl<THazardWorkPlan
熏蒸作业高度: 7天内
进出仓高度危险作业: 12小时内
进出仓较大危险作业: 24小时内
进出仓一般危险作业: 72小时内*/
进出仓一般危险作业: 72小时内*//*
SysDictData workTypeDictData = sysDictDataService.getDictDataById(workType);
SysDictData workLevelDictData = sysDictDataService.getDictDataById(workLevel);
String result = "在安全时间内";
......@@ -912,7 +913,7 @@ public class THazardWorkPlanServiceImpl extends SuperServiceImpl<THazardWorkPlan
);
}
return result;
}
}*/
// 添加时区支持
......
......@@ -33,6 +33,9 @@
<id column="cancel_opinion" property="cancelOpinion"/>
<id column="revoke_opinion" property="revokeOpinion"/>
<id column="guardian_signature" property="guardianSignature"/>
<id column="hazard_license_pass_time" property="hazardLicensePassTime"/>
<id column="is_fully_invested" property="isFullyInvested"/>
<id column="is_full" property="isFull"/>
</resultMap>
<select id="selectToDoList" resultType="com.tongtech.tfw.workflow.apis.task.model.dto.TodoTask">
SELECT DISTINCT
......@@ -79,4 +82,17 @@
</if>
ORDER BY task.CREATE_TIME_ desc
</select>
<select id="selectHazardLicensePassTime" resultType="com.testor.module.hazard.model.domain.THazardWorkPlan">
select * from t_hazard_work_plan
where status ='0' and hazard_license_pass_time is not null
and work_status in('4','8','9','10')
</select>
<select id="selectNotCompleteList" resultType="com.testor.module.hazard.model.domain.THazardWorkPlan">
select * from t_hazard_work_plan
where status ='0'
and work_status not in('0','5','6','12')
</select>
</mapper>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.testor.module.hazard.dao.THazardWorkPlanExpiredLogDao">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.testor.module.hazard.model.domain.THazardWorkPlanExpiredLog">
<id column="id" property="id"/>
<result column="plan_id" property="planId"/>
<result column="code" property="code"/>
<result column="work_type" property="workType"/>
<result column="danger_level" property="dangerLevel"/>
<result column="expired_reason" property="expiredReason"/>
<result column="before_expiration" property="beforeExpiration"/>
<result column="hazard_license_pass_time" property="hazardLicensePassTime"/>
<result column="actual_duration_hours" property="actualDurationHours"/>
<result column="plan_create_date" property="planCreateDate"/>
<!-- SuperModel 的字段 -->
<result column="status" property="status"/>
<result column="create_by" property="createBy"/>
<result column="create_date" property="createDate"/>
<result column="update_by" property="updateBy"/>
<result column="update_date" property="updateDate"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, plan_id, code, work_type, danger_level, expired_reason, before_expiration,
hazard_license_pass_time, actual_duration_hours, allowed_duration_hours,
plan_create_date, expired_time, remark, status, create_by, create_date, update_by, update_date
</sql>
</mapper>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment