@@ -35,6 +35,10 @@ | |||
<artifactId>spring-boot-starter-freemarker</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.quartz-scheduler</groupId> | |||
<artifactId>quartz</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>net.coobird</groupId> | |||
<artifactId>thumbnailator</artifactId> | |||
<version>[0.4, 0.5)</version> | |||
@@ -26,6 +26,7 @@ public class BpmStartedListener implements IStartListener { | |||
SwConsts.SysParam.RUN_PROJECTS = "bpm"; | |||
SysServiceFactory.getInstance().reg(new OneTimeTaskCleanService()); | |||
TreeHelper.regTreeHelper(ModelCatalog.ENTITY_NAME, ModelCatalogTreeHelper.class); | |||
} | |||
@Override | |||
@@ -36,4 +37,9 @@ public class BpmStartedListener implements IStartListener { | |||
CacheManager.getIntance().init(); | |||
OneTimeServiceFactory.getInstance().start(); | |||
} | |||
@Override | |||
public void close() { | |||
OneTimeServiceFactory.getInstance().stop(); | |||
} | |||
} |
@@ -0,0 +1,57 @@ | |||
package cc.smtweb.system.bpm.web.sys.base.job; | |||
import cc.smtweb.framework.core.common.SwConsts; | |||
import cc.smtweb.framework.core.db.DbEngine; | |||
import cc.smtweb.framework.core.db.jdbc.ISimpleWorker; | |||
import cc.smtweb.framework.core.systask.SingleRequestHelper; | |||
import cc.smtweb.framework.core.util.DateUtil; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.quartz.Job; | |||
import org.quartz.JobExecutionContext; | |||
import org.quartz.JobExecutionException; | |||
/** | |||
* Created by Akmm at 2022-09-14 09:51 | |||
* 定时任务的基类 | |||
*/ | |||
@Slf4j | |||
public abstract class BaseJob implements Job { | |||
@Override | |||
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { | |||
JobEntity job = (JobEntity) jobExecutionContext.getJobDetail().getJobDataMap().get("job"); | |||
final String id = this.getClass().getName(); | |||
SingleRequestHelper.singleRequest(SwConsts.REDIS_KEY_BASE_JOB, id, job.getName() + "任务执行中,本次忽略.........", () -> { | |||
JobLog jlog = startLog(job); | |||
try { | |||
String info = work(); | |||
endLog(jlog, true, info); | |||
} catch (Exception e) { | |||
endLog(jlog, false, e.getMessage()); | |||
log.error(job.getName() + ":::执行失败", e); | |||
} | |||
}); | |||
} | |||
//任务执行信息,可返回执行结果 | |||
protected abstract String work(); | |||
//存任务日志-开始 | |||
private JobLog startLog(JobEntity job) { | |||
JobLog jlog = new JobLog(); | |||
jlog.setId(DbEngine.getInstance().nextId()); | |||
jlog.setSjId(job.getId()); | |||
jlog.setBeginTime(DateUtil.nowDateTimeLong()); | |||
DbEngine.getInstance().doTransSingle(() -> DbEngine.getInstance().findDao(JobLog.class).insertEntity(jlog)); | |||
return jlog; | |||
} | |||
//存任务日志-结束 | |||
private void endLog(JobLog jlog, boolean sucess, String info) { | |||
jlog.setEndTime(DateUtil.nowDateTimeLong()); | |||
jlog.setSuccess(sucess); | |||
jlog.setInfo(info); | |||
DbEngine.getInstance().doTransSingle(() -> DbEngine.getInstance().findDao(JobLog.class).updateEntity(jlog)); | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
package cc.smtweb.system.bpm.web.sys.base.job; | |||
import cc.smtweb.framework.core.annotation.SwCache; | |||
import cc.smtweb.framework.core.cache.AbstractEntityCache; | |||
import cc.smtweb.framework.core.cache.CacheManager; | |||
/** | |||
* Created by 1 at 2022-09-13 19:35:56 | |||
* 实体【[定时任务](SYS_JOB)】的缓存类 | |||
*/ | |||
@SwCache(ident = "SYS_JOB", title = "定时任务") | |||
public class JobCache extends AbstractEntityCache<JobEntity> { | |||
//缓存key:按编码 | |||
public final static String mk_code = "code"; | |||
public static JobCache getInstance() { | |||
return CacheManager.getIntance().getCache(JobCache.class); | |||
} | |||
public JobCache() { | |||
//缓存key:按编码 | |||
regMap(mk_code, "sj_code"); | |||
} | |||
//缓存key:按编码 | |||
public final JobEntity getByCode(String key) { | |||
return getByKey(mk_code, key); | |||
} | |||
} |
@@ -0,0 +1,81 @@ | |||
package cc.smtweb.system.bpm.web.sys.base.job; | |||
import cc.smtweb.framework.core.annotation.SwTable; | |||
import cc.smtweb.framework.core.db.impl.DefaultEntity; | |||
/** | |||
* Created by 1 at 2022-09-13 19:35:56 | |||
* 实体【[定时任务](SYS_JOB)】的Entity类 | |||
*/ | |||
@SwTable("SYS_JOB") | |||
public class JobEntity extends DefaultEntity { | |||
public static final String ENTITY_NAME = "SYS_JOB"; | |||
public JobEntity() { | |||
super(ENTITY_NAME); | |||
} | |||
/** 主键 */ | |||
public long getId() { | |||
return getLong("sj_id"); | |||
} | |||
/** 主键 */ | |||
public void setId(long sj_id) { | |||
put("sj_id", sj_id); | |||
} | |||
/** 编码 */ | |||
public String getCode() { | |||
return getStr("sj_code"); | |||
} | |||
/** 编码 */ | |||
public void setCode(String sj_code) { | |||
put("sj_code", sj_code); | |||
} | |||
/** 名称 */ | |||
public String getName() { | |||
return getStr("sj_name"); | |||
} | |||
/** 名称 */ | |||
public void setName(String sj_name) { | |||
put("sj_name", sj_name); | |||
} | |||
/** 执行类的路径 */ | |||
public String getExeClass() { | |||
return getStr("sj_exe_class"); | |||
} | |||
/** 执行类的路径 */ | |||
public void setExeClass(String sj_exe_class) { | |||
put("sj_exe_class", sj_exe_class); | |||
} | |||
/** cron表达式 */ | |||
public String getCron() { | |||
return getStr("sj_cron"); | |||
} | |||
/** cron表达式 */ | |||
public void setCron(String sj_cron) { | |||
put("sj_cron", sj_cron); | |||
} | |||
/** 是否启用 */ | |||
public boolean isEnable() { | |||
return getBool("sj_enable"); | |||
} | |||
/** 是否启用 */ | |||
public void setEnable(boolean sj_enable) { | |||
setBool("sj_enable", sj_enable); | |||
} | |||
/** 备注 */ | |||
public String getRemark() { | |||
return getStr("sj_remark"); | |||
} | |||
/** 备注 */ | |||
public void setRemark(String sj_remark) { | |||
put("sj_remark", sj_remark); | |||
} | |||
} |
@@ -0,0 +1,102 @@ | |||
package cc.smtweb.system.bpm.web.sys.base.job; | |||
import cc.smtweb.framework.core.annotation.SwTable; | |||
import cc.smtweb.framework.core.common.SwMap; | |||
import cc.smtweb.framework.core.db.impl.DefaultEntity; | |||
/** | |||
* Created by 1 at 2022-09-14 10:09:03 | |||
* 实体【[定时任务执行日志](SYS_JOB_LOG)】的Entity类 | |||
*/ | |||
@SwTable("SYS_JOB_LOG") | |||
public class JobLog extends DefaultEntity { | |||
public static final String ENTITY_NAME = "SYS_JOB_LOG"; | |||
public JobLog() { | |||
super(ENTITY_NAME); | |||
} | |||
/** | |||
* 主键 | |||
*/ | |||
public long getId() { | |||
return getLong("sjl_id"); | |||
} | |||
/** | |||
* 主键 | |||
*/ | |||
public void setId(long sjl_id) { | |||
put("sjl_id", sjl_id); | |||
} | |||
/** | |||
* 任务id | |||
*/ | |||
public long getSjId() { | |||
return getLong("sjl_sj_id"); | |||
} | |||
/** | |||
* 任务id | |||
*/ | |||
public void setSjId(long sjl_sj_id) { | |||
put("sjl_sj_id", sjl_sj_id); | |||
} | |||
/** | |||
* 开始时间 | |||
*/ | |||
public long getBeginTime() { | |||
return getLong("sjl_begin_time"); | |||
} | |||
/** | |||
* 开始时间 | |||
*/ | |||
public void setBeginTime(long sjl_begin_time) { | |||
put("sjl_begin_time", sjl_begin_time); | |||
} | |||
/** | |||
* 完成时间 | |||
*/ | |||
public long getEndTime() { | |||
return getLong("sjl_end_time"); | |||
} | |||
/** | |||
* 完成时间 | |||
*/ | |||
public void setEndTime(long sjl_end_time) { | |||
put("sjl_end_time", sjl_end_time); | |||
} | |||
/** | |||
* 是否成功 | |||
*/ | |||
public boolean isSuccess() { | |||
return getBool("sjl_success"); | |||
} | |||
/** | |||
* 是否成功 | |||
*/ | |||
public void setSuccess(boolean sjl_success) { | |||
setBool("sjl_success", sjl_success); | |||
} | |||
/** | |||
* 执行情况 | |||
*/ | |||
public String getInfo() { | |||
return getStr("sjl_info"); | |||
} | |||
/** | |||
* 执行情况 | |||
*/ | |||
public void setInfo(String sjl_info) { | |||
put("sjl_info", sjl_info); | |||
} | |||
} |
@@ -0,0 +1,113 @@ | |||
package cc.smtweb.system.bpm.web.sys.base.job; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.quartz.*; | |||
import org.quartz.impl.StdSchedulerFactory; | |||
import java.util.*; | |||
/** | |||
* Created by Akmm at 13-2-27 下午9:32 | |||
* 定时任务工厂 | |||
*/ | |||
@Slf4j | |||
public class JobUtils { | |||
private static Scheduler scheduler = null; | |||
private static JobUtils instance = null; | |||
private Map<String, JobEntity> mapJob = new HashMap<>(); | |||
public static Scheduler getScheduler() { | |||
return scheduler; | |||
} | |||
public static JobUtils getInstance() { | |||
if (instance == null) { | |||
synchronized (JobUtils.class) { | |||
if (instance == null) { | |||
instance = new JobUtils(); | |||
} | |||
} | |||
} | |||
return instance; | |||
} | |||
private JobUtils() { | |||
try { | |||
SchedulerFactory schedulerFactory = new StdSchedulerFactory(); | |||
scheduler = schedulerFactory.getScheduler(); | |||
} catch (Exception e) { | |||
log.error("计划任务初始化失败", e); | |||
e.printStackTrace(); | |||
} | |||
} | |||
//初始化所有任务 | |||
private void initJob() throws Exception { | |||
Collection<JobEntity> list = JobCache.getInstance().getAll(); | |||
for (JobEntity job : list) { | |||
if (job.isEnable()) { | |||
addJob(job); | |||
} | |||
} | |||
} | |||
public void start() { | |||
try { | |||
clear(); | |||
if (!scheduler.isStarted()) scheduler.start(); | |||
initJob(); | |||
} catch (Exception e) { | |||
log.error("计划任务开始失败", e); | |||
e.printStackTrace(); | |||
} | |||
} | |||
public void stop() { | |||
try { | |||
if (!scheduler.isShutdown()) { | |||
clear(); | |||
scheduler.shutdown(); | |||
} | |||
} catch (Exception e) { | |||
log.error("计划任务开始失败", e); | |||
e.printStackTrace(); | |||
} | |||
} | |||
public void clear() throws SchedulerException { | |||
for (JobEntity job : mapJob.values()) { | |||
try { | |||
deleteJob(job); | |||
} catch (Exception e) { | |||
log.debug("任务" + job.getId() + "删除失败!"); | |||
} | |||
} | |||
} | |||
//添加任务 | |||
public synchronized void addJob(JobEntity job) throws Exception { | |||
String jobId = String.valueOf(job.getId()); | |||
JobDetail jobDetail = JobBuilder.newJob((Class<? extends org.quartz.Job>) Class.forName(job.getExeClass())) | |||
.withIdentity(jobId).build(); | |||
jobDetail.getJobDataMap().put("job", job); | |||
// 按新的cronExpression表达式构建一个新的trigger | |||
CronTrigger trigger = TriggerBuilder.newTrigger() | |||
.withIdentity(jobId) | |||
.startNow() | |||
.withSchedule(CronScheduleBuilder.cronSchedule(job.getCron())) | |||
.build(); | |||
scheduler.scheduleJob(jobDetail, trigger); | |||
} | |||
//删除任务 | |||
public synchronized void deleteJob(JobEntity job) throws Exception { | |||
String jobId = String.valueOf(job.getId()); | |||
scheduler.pauseTrigger(TriggerKey.triggerKey(jobId)); | |||
scheduler.unscheduleJob(TriggerKey.triggerKey(jobId)); | |||
scheduler.deleteJob(JobKey.jobKey(jobId)); | |||
} | |||
} |
@@ -21,8 +21,8 @@ public class OneTimeTaskCleanService extends BaseSysService { | |||
} | |||
@Override | |||
public void work() throws Exception { | |||
public void work() { | |||
String t = DateUtil.getNowYm() + "01000000"; | |||
DbEngine.getInstance().update("delete from " + EntityHelper.getSchemaTableName(OnetimeTask.ENTITY_NAME) + " where statu=? and create_time<?", OneTimeStatu.SUCCESS.value, t); | |||
DbEngine.getInstance().update("delete from " + EntityHelper.getSchemaTableName(OnetimeTask.ENTITY_NAME) + " where sot_statu=? and sot_create_time<?", OneTimeStatu.SUCCESS.value, t); | |||
} | |||
} |
@@ -0,0 +1,34 @@ | |||
package cc.smtweb.framework.core; | |||
import cc.smtweb.framework.core.common.SwConsts; | |||
import cc.smtweb.framework.core.mvc.controller.IStartListener; | |||
import cc.smtweb.framework.core.mvc.controller.scan.ApplicationScanner; | |||
import cc.smtweb.framework.core.mvc.controller.scan.BeanManager; | |||
import cc.smtweb.framework.core.systask.SysServiceFactory; | |||
import cc.smtweb.framework.core.systask.WebStartedEvent; | |||
import lombok.SneakyThrows; | |||
import org.springframework.boot.context.event.ApplicationStartedEvent; | |||
import org.springframework.context.ApplicationListener; | |||
import org.springframework.context.ConfigurableApplicationContext; | |||
import org.springframework.context.event.ContextClosedEvent; | |||
import org.springframework.core.annotation.Order; | |||
import org.springframework.stereotype.Component; | |||
import java.util.Comparator; | |||
import java.util.List; | |||
/** | |||
* 服务停止监听 | |||
*/ | |||
@Component | |||
public class CoreApplicationClosedListener implements ApplicationListener<ContextClosedEvent> { | |||
@Override | |||
public void onApplicationEvent(ContextClosedEvent event) { | |||
SysServiceFactory.getInstance().stop(); | |||
List<IStartListener> list = BeanManager.getInstance().getStartListeners(); | |||
list.sort(Comparator.comparingInt(IStartListener::order)); | |||
for (IStartListener sl : list) { | |||
sl.close(); | |||
} | |||
} | |||
} |
@@ -4,6 +4,7 @@ import cc.smtweb.framework.core.common.SwConsts; | |||
import cc.smtweb.framework.core.mvc.controller.IStartListener; | |||
import cc.smtweb.framework.core.mvc.controller.scan.ApplicationScanner; | |||
import cc.smtweb.framework.core.mvc.controller.scan.BeanManager; | |||
import cc.smtweb.framework.core.systask.SysServiceFactory; | |||
import cc.smtweb.framework.core.systask.WebStartedEvent; | |||
import lombok.SneakyThrows; | |||
import org.springframework.boot.context.event.ApplicationStartedEvent; | |||
@@ -38,6 +39,7 @@ public class CoreApplicationStartedListener implements ApplicationListener<Appli | |||
for (IStartListener sl : list) { | |||
sl.run(); | |||
} | |||
SysServiceFactory.getInstance().start(); | |||
// 通知 controller 正式使用 | |||
applicationContext.publishEvent(new WebStartedEvent()); | |||
SwConsts.SysParam.SYS_STARTED = true; | |||
@@ -37,9 +37,4 @@ public class CoreAutoConfiguration implements WebMvcConfigurer { | |||
public ControllerConfig coreControllerConfig() { | |||
return new ControllerConfig("core", "cc.smtweb.framework.core"); | |||
} | |||
@Override | |||
public void addInterceptors(InterceptorRegistry registry) { | |||
registry.addInterceptor(new CoreInterceptor()); | |||
} | |||
} |
@@ -356,7 +356,7 @@ public class JdbcEngine { | |||
} | |||
//独立事务 | |||
public void doTransSingle(IDbWorker worker) throws Exception { | |||
public void doTransSingle(IDbWorker worker) throws SwException { | |||
JdbcTrans jdbcTrans = openTrans(); | |||
try { | |||
worker.work(); | |||
@@ -364,11 +364,22 @@ public class JdbcEngine { | |||
} catch (Exception e) { | |||
jdbcTrans.rollback(); | |||
worker.doAfterDbRollback(); | |||
throw e; | |||
throw new SwException(e); | |||
} | |||
worker.doAfterDbCommit(); | |||
} | |||
public void doTransSingle(ISimpleWorker worker) throws SwException { | |||
JdbcTrans jdbcTrans = openTrans(); | |||
try { | |||
worker.work(); | |||
jdbcTrans.commit(); | |||
} catch (Exception e) { | |||
jdbcTrans.rollback(); | |||
throw new SwException(e); | |||
} | |||
} | |||
/** | |||
* 获取原始数据库连接执行命令 | |||
@@ -1,5 +1,6 @@ | |||
package cc.smtweb.framework.core.mvc.config; | |||
import cc.smtweb.framework.core.CoreInterceptor; | |||
import cc.smtweb.framework.core.cache.CacheManager; | |||
import cc.smtweb.framework.core.cache.redis.RedisManager; | |||
import cc.smtweb.framework.core.db.jdbc.IdGenerator; | |||
@@ -19,10 +20,7 @@ import org.springframework.context.annotation.Primary; | |||
import org.springframework.http.MediaType; | |||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; | |||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; | |||
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; | |||
import org.springframework.web.servlet.config.annotation.CorsRegistry; | |||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; | |||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | |||
import org.springframework.web.servlet.config.annotation.*; | |||
import java.util.List; | |||
@@ -49,9 +47,6 @@ public class WebMvcConfig implements WebMvcConfigurer { | |||
private static final String TRUE_VALUE = "true"; | |||
@Autowired | |||
private RedisManager redisManager; | |||
@Bean | |||
public SessionManager sessionManager(RedisManager redisManager, IdGenerator idGenerator) { | |||
return new SessionManager(redisManager, idGenerator); | |||
@@ -68,6 +63,11 @@ public class WebMvcConfig implements WebMvcConfigurer { | |||
} | |||
@Override | |||
public void addInterceptors(InterceptorRegistry registry) { | |||
registry.addInterceptor(new CoreInterceptor()); | |||
} | |||
@Override | |||
public void addCorsMappings(CorsRegistry registry) { | |||
// 2.4 以前使用 .allowedOrigins("*") | |||
registry.addMapping("/**") | |||
@@ -13,4 +13,7 @@ public interface IStartListener { | |||
//启动事件 | |||
void run(); | |||
//服务停止时 | |||
default void close(){} | |||
} |
@@ -23,17 +23,14 @@ public abstract class BaseSysService implements Runnable { | |||
public void run() { | |||
try { | |||
final String id = this.getClass().getName(); | |||
SingleRequestHelper.singleRequest(SwConsts.REDIS_KEY_BASE_JOB, id, "任务执行中,本次忽略.........", new SingleRequestHelper.ISingleWork() { | |||
@Override | |||
public void doSingleWork() throws Exception { | |||
long time = System.currentTimeMillis(); | |||
if (isPrintLog()) { | |||
log.debug(getTitle() + ":::开始执行........."); | |||
} | |||
work(); | |||
if (isPrintLog()) { | |||
log.debug(getTitle() + ":::执行完成,耗时:" + (System.currentTimeMillis() - time) + "毫秒........."); | |||
} | |||
SingleRequestHelper.singleRequest(SwConsts.REDIS_KEY_BASE_JOB, id, "任务执行中,本次忽略.........", () -> { | |||
long time = System.currentTimeMillis(); | |||
if (isPrintLog()) { | |||
log.debug(getTitle() + ":::开始执行........."); | |||
} | |||
work(); | |||
if (isPrintLog()) { | |||
log.debug(getTitle() + ":::执行完成,耗时:" + (System.currentTimeMillis() - time) + "毫秒........."); | |||
} | |||
}); | |||
} catch (Exception e) { | |||
@@ -42,5 +39,5 @@ public abstract class BaseSysService implements Runnable { | |||
} | |||
//主要要干的活 | |||
protected abstract void work() throws Exception; | |||
protected abstract void work(); | |||
} |
@@ -8,15 +8,15 @@ import cc.smtweb.framework.core.util.StringUtil; | |||
public class SingleRequestHelper { | |||
public static interface ISingleWork { | |||
void doSingleWork() throws Exception; | |||
void doSingleWork(); | |||
} | |||
public static void singleRequest(String region_key, String key, ISingleWork singleWork) throws Exception { | |||
public static void singleRequest(String region_key, String key, ISingleWork singleWork) { | |||
singleRequest(region_key, key, "其他人正在执行该操作,请等待!", singleWork); | |||
} | |||
public static void singleRequest(String region_key, String key, String tip_msg, ISingleWork singleWork) throws Exception { | |||
public static void singleRequest(String region_key, String key, String tip_msg, ISingleWork singleWork) { | |||
boolean clearCache = false; | |||
try { | |||
String lastTime = RedisManager.getInstance().hGet(region_key, key, String.class); | |||
@@ -39,7 +39,7 @@ public class SysServiceFactory { | |||
//启动任务 | |||
protected void start() { | |||
public void start() { | |||
if (schedule != null) { | |||
stop(); | |||
} | |||
@@ -52,7 +52,7 @@ public class SysServiceFactory { | |||
} | |||
//停止任务 | |||
protected void stop() { | |||
public void stop() { | |||
schedule.shutdown(); | |||
} | |||
} |