From f75ce88e632b1bc27c34cdefaefcc13e928c379e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=80=A1?= Date: Mon, 14 Jul 2025 13:36:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BB=A3=E7=A0=81=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../login/dao/mapper/EshopMenuMapper.xml | 6 +- .../login/entity/EshopMenuConverter.java | 2 + .../menu/controller/SysMenuController.java | 19 + .../system/menu/service/ISysMenuService.java | 13 + .../menu/service/impl/SysMenuServiceImpl.java | 58 +++ .../org/service/impl/SysOrgServiceImpl.java | 2 +- .../scheduled/config/ScheduledTaskConfig.java | 57 +++ .../controller/ScheduledTaskController.java | 337 ++++++++++++++++++ .../ScheduledTaskTestController.java | 332 +++++++++++++++++ .../scheduled/dao/ScheduledTaskLogMapper.java | 39 ++ .../scheduled/dao/ScheduledTaskMapper.java | 32 ++ .../dao/mapper/ScheduledTaskLogMapper.xml | 53 +++ .../dao/mapper/ScheduledTaskMapper.xml | 49 +++ .../scheduled/entity/ScheduledTask.java | 119 +++++++ .../scheduled/entity/ScheduledTaskLog.java | 107 ++++++ .../scheduler/DynamicTaskScheduler.java | 326 +++++++++++++++++ .../service/IScheduledTaskLockService.java | 30 ++ .../service/IScheduledTaskLogService.java | 55 +++ .../service/IScheduledTaskService.java | 48 +++ .../ScheduledTaskLockRedisServiceImpl.java | 52 +++ .../impl/ScheduledTaskLogServiceImpl.java | 62 ++++ .../impl/ScheduledTaskServiceImpl.java | 87 +++++ .../system/scheduled/sql/scheduled_task.sql | 71 ++++ .../ebtp/system/scheduled/util/CronUtil.java | 97 +++++ .../scheduled/util/TaskExecutorUtil.java | 154 ++++++++ .../service/impl/EbtpUserInfoServiceImpl.java | 130 ++++--- src/main/resources/application-master.yml | 8 +- 27 files changed, 2272 insertions(+), 73 deletions(-) create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/config/ScheduledTaskConfig.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/controller/ScheduledTaskController.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/controller/ScheduledTaskTestController.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/ScheduledTaskLogMapper.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/ScheduledTaskMapper.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/mapper/ScheduledTaskLogMapper.xml create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/mapper/ScheduledTaskMapper.xml create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/entity/ScheduledTask.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/entity/ScheduledTaskLog.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/scheduler/DynamicTaskScheduler.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/service/IScheduledTaskLockService.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/service/IScheduledTaskLogService.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/service/IScheduledTaskService.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/service/impl/ScheduledTaskLockRedisServiceImpl.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/service/impl/ScheduledTaskLogServiceImpl.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/service/impl/ScheduledTaskServiceImpl.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/sql/scheduled_task.sql create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/util/CronUtil.java create mode 100644 src/main/java/com/coscoshipping/ebtp/system/scheduled/util/TaskExecutorUtil.java diff --git a/src/main/java/com/coscoshipping/ebtp/system/login/dao/mapper/EshopMenuMapper.xml b/src/main/java/com/coscoshipping/ebtp/system/login/dao/mapper/EshopMenuMapper.xml index 98e5fe2..fbf19f7 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/login/dao/mapper/EshopMenuMapper.xml +++ b/src/main/java/com/coscoshipping/ebtp/system/login/dao/mapper/EshopMenuMapper.xml @@ -35,11 +35,11 @@ sp.parent_id, count( sps.menu_id ) istop from - eshop_menu sp - join eshop_role_menu srp on srp.menu_id = sp.menu_id + sys_menu sp + join sys_role_menu srp on srp.menu_id = sp.menu_id and sp.delete_flag = 'normal' and sp.menu_status = 'valid' - left join eshop_menu sps on sp.parent_id = sps.menu_id + left join sys_menu sps on sp.parent_id = sps.menu_id and sps.delete_flag = 'normal' and sps.menu_status = 'valid' where diff --git a/src/main/java/com/coscoshipping/ebtp/system/login/entity/EshopMenuConverter.java b/src/main/java/com/coscoshipping/ebtp/system/login/entity/EshopMenuConverter.java index 36d6d9c..081bd06 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/login/entity/EshopMenuConverter.java +++ b/src/main/java/com/coscoshipping/ebtp/system/login/entity/EshopMenuConverter.java @@ -22,6 +22,8 @@ public class EshopMenuConverter { private String menuOu; @ApiModelProperty("是否顶级标识:0 是,1 否") private Integer isTop; + @ApiModelProperty("父级ID") + private String parentId; @ApiModelProperty("菜单图标") private List children; @ApiModelProperty("feign调用成功或失败的标识 true:为feign调用成功 down:feign调用失败") diff --git a/src/main/java/com/coscoshipping/ebtp/system/menu/controller/SysMenuController.java b/src/main/java/com/coscoshipping/ebtp/system/menu/controller/SysMenuController.java index 07e0ee5..0e8a32b 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/menu/controller/SysMenuController.java +++ b/src/main/java/com/coscoshipping/ebtp/system/menu/controller/SysMenuController.java @@ -3,9 +3,13 @@ package com.coscoshipping.ebtp.system.menu.controller; import com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants; import com.chinaunicom.mall.ebtp.common.base.controller.BaseController; import com.chinaunicom.mall.ebtp.common.base.entity.BaseResponse; +import com.chinaunicom.mall.ebtp.common.exception.entity.BusinessException; +import com.coscoshipping.ebtp.system.login.entity.EshopMenuConverter; +import com.coscoshipping.ebtp.system.login.entity.EshopMenuQuery; import com.coscoshipping.ebtp.system.menu.dto.BaseRoleTabulation; import com.coscoshipping.ebtp.system.menu.entity.SysMenu; import com.coscoshipping.ebtp.system.menu.service.ISysMenuService; +import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; @@ -127,4 +131,19 @@ public class SysMenuController extends BaseController { public BaseResponse> getBaseRoleTabulation(@RequestParam("userId") String userId) { return BaseResponse.success(menuService.getBaseRoleTabulation(userId)); } + + /** + * 根据角色查看菜单树 + * @param body + * @return BasePageResponse + * @author chentao + * @date 2021-1-13 + * @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述] + */ + @ApiOperation(value = "根据角色查看菜单树(吉林用)", notes = "根据角色查看菜单树(吉林用)") + @PostMapping("/findMenuList") + public BaseResponse> findMenuList(@RequestBody EshopMenuQuery body) throws BusinessException { + return BaseResponse.success(menuService.findMenuList(body)); + } + } diff --git a/src/main/java/com/coscoshipping/ebtp/system/menu/service/ISysMenuService.java b/src/main/java/com/coscoshipping/ebtp/system/menu/service/ISysMenuService.java index 74df743..1602f80 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/menu/service/ISysMenuService.java +++ b/src/main/java/com/coscoshipping/ebtp/system/menu/service/ISysMenuService.java @@ -1,6 +1,9 @@ package com.coscoshipping.ebtp.system.menu.service; +import com.chinaunicom.mall.ebtp.common.exception.entity.BusinessException; +import com.coscoshipping.ebtp.system.login.entity.EshopMenuConverter; +import com.coscoshipping.ebtp.system.login.entity.EshopMenuQuery; import com.coscoshipping.ebtp.system.menu.dto.BaseRoleTabulation; import com.coscoshipping.ebtp.system.menu.entity.SysMenu; import com.coscoshipping.ebtp.system.menu.entity.TreeSelect; @@ -148,4 +151,14 @@ public interface ISysMenuService public boolean checkMenuNameUnique(SysMenu menu); List getBaseRoleTabulation(String userId); + + /** + * 根据角色查看菜单树 + * @param body + * @return BasePageResponse + * @author chentao + * @date 2021-1-13 + * @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述] + */ + List findMenuList(EshopMenuQuery body) throws BusinessException; } diff --git a/src/main/java/com/coscoshipping/ebtp/system/menu/service/impl/SysMenuServiceImpl.java b/src/main/java/com/coscoshipping/ebtp/system/menu/service/impl/SysMenuServiceImpl.java index b5bd755..57e159d 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/menu/service/impl/SysMenuServiceImpl.java +++ b/src/main/java/com/coscoshipping/ebtp/system/menu/service/impl/SysMenuServiceImpl.java @@ -4,6 +4,10 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.lang.Snowflake; import cn.hutool.core.util.IdUtil; +import com.chinaunicom.mall.ebtp.common.exception.entity.BusinessException; +import com.coscoshipping.ebtp.system.login.entity.EshopMenuConverter; +import com.coscoshipping.ebtp.system.login.entity.EshopMenuQuery; +import com.coscoshipping.ebtp.system.login.dao.EshopMenuMapper; import com.coscoshipping.ebtp.system.menu.dao.SysMenuMapper; import com.coscoshipping.ebtp.system.menu.dto.BaseFuncTabulation; import com.coscoshipping.ebtp.system.menu.dto.BaseRoleTabulation; @@ -14,6 +18,8 @@ import com.coscoshipping.ebtp.system.menu.vo.RoleTabulationVo; import com.coscoshipping.ebtp.system.role.dao.SysRoleMapper; import com.coscoshipping.ebtp.system.role.dao.SysRoleMenuMapper; import com.coscoshipping.ebtp.system.role.service.SysRoleService; +import com.google.common.collect.Lists; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -21,6 +27,7 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.*; import java.util.stream.Collectors; +import com.coscoshipping.ebtp.system.login.entity.EshopMenuConverterChildren; /** * 菜单 业务层处理 @@ -42,6 +49,9 @@ public class SysMenuServiceImpl implements ISysMenuService { @Autowired private SysRoleMenuMapper roleMenuMapper; + @Autowired + private EshopMenuMapper eshopMenuMapper; + /** * 根据用户查询系统菜单列表 * @@ -392,4 +402,52 @@ public class SysMenuServiceImpl implements ISysMenuService { return bfList; } } + + /** + * 根据角色查看菜单树 + * @param body + * @return BasePageResponse + * @author chentao + * @date 2021-1-13 + * @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述] + */ + @Override + public List findMenuList(EshopMenuQuery body) throws BusinessException { + List roleList = body.getRoleIdList(); + List allMenus = new ArrayList<>(); + Set menuIdSet = new HashSet<>(); + for (String roleId : roleList) { + List menuList = eshopMenuMapper.findTopMenuList(roleId); + for (EshopMenuConverter menu : menuList) { + if (menu != null && menu.getMenuId() != null && !menuIdSet.contains(menu.getMenuId())) { + allMenus.add(menu); + menuIdSet.add(menu.getMenuId()); + } + } + } + // 组装树结构 + Map menuMap = new HashMap<>(); + List rootMenus = new ArrayList<>(); + for (EshopMenuConverter menu : allMenus) { + menu.setChildren(new ArrayList<>()); // 初始化children + menuMap.put(menu.getMenuId(), menu); + } + for (EshopMenuConverter menu : allMenus) { + String parentId = menu.getParentId(); + if ("0".equals(parentId)) { + rootMenus.add(menu); + } else if (menuMap.containsKey(parentId)) { + // 转换为 EshopMenuConverterChildren + EshopMenuConverterChildren child = new EshopMenuConverterChildren(); + child.setPath(menu.getPath()); + child.setName(menu.getName()); + // 这里 frame 字段数据库未查出,若有需要可补充 + menuMap.get(parentId).getChildren().add(child); + } else { + rootMenus.add(menu); // 没有父节点的也作为根节点 + } + } + // 根节点的 children 字段已初始化为空 + return rootMenus; + } } diff --git a/src/main/java/com/coscoshipping/ebtp/system/org/service/impl/SysOrgServiceImpl.java b/src/main/java/com/coscoshipping/ebtp/system/org/service/impl/SysOrgServiceImpl.java index 91848ea..78c5d3f 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/org/service/impl/SysOrgServiceImpl.java +++ b/src/main/java/com/coscoshipping/ebtp/system/org/service/impl/SysOrgServiceImpl.java @@ -91,7 +91,7 @@ public class SysOrgServiceImpl extends BaseServiceImpl imp // 构建父节点到子节点的映射(key: upOrgId,value: 子节点列表) Map> parentToChildrenMap = new HashMap<>(); for (SysOrgVO orgVO : allOrgVOs) { - String parentId = StringUtils.isBlank(orgVO.getUpOrgId()) ? CommonUtil.ROOT_NODE : orgVO.getUpOrgId(); + String parentId = StringUtils.isBlank(orgVO.getUpOrgId()) ? "0" : orgVO.getUpOrgId(); parentToChildrenMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(orgVO); } diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/config/ScheduledTaskConfig.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/config/ScheduledTaskConfig.java new file mode 100644 index 0000000..d0c4f55 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/config/ScheduledTaskConfig.java @@ -0,0 +1,57 @@ +package com.coscoshipping.ebtp.system.scheduled.config; + +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.web.client.RestTemplate; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.concurrent.Executor; + +/** + * 定时任务配置类 + */ +@Configuration +@EnableAsync +public class ScheduledTaskConfig { + + /** + * 异步任务执行器 + */ + @Bean(name = "taskExecutor") + @Order(1) + public Executor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); + executor.setMaxPoolSize(20); + executor.setQueueCapacity(100); + executor.setKeepAliveSeconds(60); + executor.setThreadNamePrefix("ScheduledTask-"); + executor.setRejectedExecutionHandler(new java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } + + /** + * RestTemplate Bean + */ + @Bean(name = "scheduledRestTemplate") + @LoadBalanced + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + /** + * RedisTemplate Bean + */ + @Bean(name = "scheduledRedisTemplate") + public RedisTemplate scheduledRedisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + return redisTemplate; + } +} diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/controller/ScheduledTaskController.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/controller/ScheduledTaskController.java new file mode 100644 index 0000000..5dc9e99 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/controller/ScheduledTaskController.java @@ -0,0 +1,337 @@ +package com.coscoshipping.ebtp.system.scheduled.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.chinaunicom.mall.ebtp.common.base.entity.BaseResponse; +import com.coscoshipping.ebtp.system.scheduled.entity.ScheduledTask; +import com.coscoshipping.ebtp.system.scheduled.entity.ScheduledTaskLog; +import com.coscoshipping.ebtp.system.scheduled.scheduler.DynamicTaskScheduler; +import com.coscoshipping.ebtp.system.scheduled.service.IScheduledTaskLogService; +import com.coscoshipping.ebtp.system.scheduled.service.IScheduledTaskService; +import com.coscoshipping.ebtp.system.scheduled.util.CronUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 定时任务管理控制器 + */ +@Slf4j +@Api(tags = "定时任务管理") +@RestController +@RequestMapping("/sys/scheduled/task") +public class ScheduledTaskController { + + @Autowired + private IScheduledTaskService scheduledTaskService; + + @Autowired + private IScheduledTaskLogService taskLogService; + + @Autowired + private DynamicTaskScheduler dynamicTaskScheduler; + + /** + * 分页查询定时任务 + */ + @ApiOperation("分页查询定时任务") + @GetMapping("/list") + public BaseResponse> list( + @ApiParam("页码") @RequestParam(defaultValue = "1") Integer pageNo, + @ApiParam("页大小") @RequestParam(defaultValue = "10") Integer pageSize, + @ApiParam("任务名称") @RequestParam(required = false) String taskName, + @ApiParam("任务状态") @RequestParam(required = false) Integer status) { + + try { + Page page = new Page<>(pageNo, pageSize); + QueryWrapper queryWrapper = new QueryWrapper<>(); + + if (taskName != null && !taskName.trim().isEmpty()) { + queryWrapper.like("task_name", taskName); + } + + if (status != null) { + queryWrapper.eq("status", status); + } + + queryWrapper.orderByDesc("create_time"); + + IPage pageResult = scheduledTaskService.page(page, queryWrapper); + + return BaseResponse.success(pageResult); + + } catch (Exception e) { + log.error("分页查询定时任务失败", e); + return new BaseResponse<>(500, false, "查询失败:" + e.getMessage(), null); + } + } + + /** + * 新增定时任务 + */ + @ApiOperation("新增定时任务") + @PostMapping("/add") + public BaseResponse add(@RequestBody ScheduledTask task) { + try { + // 验证Cron表达式 + if (!CronUtil.isValidCron(task.getCronExpression())) { + return new BaseResponse<>(400, false, "Cron表达式格式不正确", false); + } + + // 设置默认值 + task.setStatus(0); // 默认禁用 + task.setCreateTime(new Date()); + task.setUpdateTime(new Date()); + + // 计算下次执行时间 + Date nextRunTime = CronUtil.getNextExecutionTime(task.getCronExpression(), new Date()); + task.setNextRunTime(nextRunTime); + + boolean success = scheduledTaskService.save(task); + + if (success) { + return BaseResponse.success(true); + } else { + return new BaseResponse<>(500, false, "添加失败", false); + } + + } catch (Exception e) { + log.error("新增定时任务失败", e); + return new BaseResponse<>(500, false, "添加失败:" + e.getMessage(), false); + } + } + + /** + * 修改定时任务 + */ + @ApiOperation("修改定时任务") + @PostMapping("/edit") + public BaseResponse edit(@RequestBody ScheduledTask task) { + try { + // 验证Cron表达式 + if (!CronUtil.isValidCron(task.getCronExpression())) { + return new BaseResponse<>(400, false, "Cron表达式格式不正确", false); + } + + // 设置更新时间 + task.setUpdateTime(new Date()); + + // 计算下次执行时间 + Date nextRunTime = CronUtil.getNextExecutionTime(task.getCronExpression(), new Date()); + task.setNextRunTime(nextRunTime); + + boolean success = scheduledTaskService.updateById(task); + + if (success) { + return BaseResponse.success(true); + } else { + return new BaseResponse<>(500, false, "修改失败", false); + } + + } catch (Exception e) { + log.error("修改定时任务失败", e); + return new BaseResponse<>(500, false, "修改失败:" + e.getMessage(), false); + } + } + + /** + * 删除定时任务 + */ + @ApiOperation("删除定时任务") + @PostMapping("/delete") + public BaseResponse delete(@RequestParam String id) { + try { + boolean success = scheduledTaskService.removeById(id); + + if (success) { + return BaseResponse.success(true); + } else { + return new BaseResponse<>(500, false, "删除失败", false); + } + + } catch (Exception e) { + log.error("删除定时任务失败", e); + return new BaseResponse<>(500, false, "删除失败:" + e.getMessage(), false); + } + } + + /** + * 启用任务 + */ + @ApiOperation("启用任务") + @PostMapping("/enable") + public BaseResponse enable(@RequestBody ScheduledTask scheduledTask) { + try { + boolean success = scheduledTaskService.enableTask(scheduledTask.getId()); + + if (success) { + return BaseResponse.success(true); + } else { + return new BaseResponse<>(500, false, "启用失败", false); + } + + } catch (Exception e) { + log.error("启用定时任务失败", e); + return new BaseResponse<>(500, false, "启用失败:" + e.getMessage(), false); + } + } + + /** + * 禁用任务 + */ + @ApiOperation("禁用任务") + @PostMapping("/disable") + public BaseResponse disable(@RequestBody ScheduledTask scheduledTask) { + try { + boolean success = scheduledTaskService.disableTask(scheduledTask.getId()); + + if (success) { + return BaseResponse.success(true); + } else { + return new BaseResponse<>(500, false, "禁用失败", false); + } + + } catch (Exception e) { + log.error("禁用定时任务失败", e); + return new BaseResponse<>(500, false, "禁用失败:" + e.getMessage(), false); + } + } + + /** + * 立即执行任务 + */ + @ApiOperation("立即执行任务") + @PostMapping("/execute") + public BaseResponse execute(@RequestParam String id) { + try { + boolean success = scheduledTaskService.executeTaskNow(id); + + if (success) { + return BaseResponse.success(true); + } else { + return new BaseResponse<>(500, false, "执行失败", false); + } + + } catch (Exception e) { + log.error("立即执行定时任务失败", e); + return new BaseResponse<>(500, false, "执行失败:" + e.getMessage(), false); + } + } + + /** + * 验证Cron表达式 + */ + @ApiOperation("验证Cron表达式") + @PostMapping("/validateCron") + public BaseResponse> validateCron(@RequestParam String cronExpression) { + try { + boolean valid = CronUtil.isValidCron(cronExpression); + + if (valid) { + Date nextTime = CronUtil.getNextExecutionTime(cronExpression, new Date()); + String description = CronUtil.getCronDescription(cronExpression); + + Map result = new HashMap<>(); + result.put("nextExecutionTime", nextTime); + result.put("description", description); + + return BaseResponse.success(result); + } else { + return new BaseResponse<>(400, false, "Cron表达式无效", null); + } + + } catch (Exception e) { + log.error("验证Cron表达式失败", e); + return new BaseResponse<>(500, false, "验证失败:" + e.getMessage(), null); + } + } + + /** + * 查询任务执行日志 + */ + @ApiOperation("查询任务执行日志") + @GetMapping("/logs") + public BaseResponse> getLogs( + @ApiParam("页码") @RequestParam(defaultValue = "1") Integer pageNo, + @ApiParam("页大小") @RequestParam(defaultValue = "10") Integer pageSize, + @ApiParam("任务ID") @RequestParam(required = false) String taskId, + @ApiParam("执行状态") @RequestParam(required = false) Integer status) { + + try { + Page page = new Page<>(pageNo, pageSize); + QueryWrapper queryWrapper = new QueryWrapper<>(); + + if (taskId != null && !taskId.trim().isEmpty()) { + queryWrapper.eq("task_id", taskId); + } + + if (status != null) { + queryWrapper.eq("status", status); + } + + queryWrapper.orderByDesc("create_time"); + + IPage pageResult = taskLogService.page(page, queryWrapper); + + return BaseResponse.success(pageResult); + + } catch (Exception e) { + log.error("查询任务执行日志失败", e); + return new BaseResponse<>(500, false, "查询失败:" + e.getMessage(), null); + } + } + + /** + * 清理执行日志 + */ + @ApiOperation("清理执行日志") + @PostMapping("/cleanLogs") + public BaseResponse cleanLogs(@RequestParam(defaultValue = "30") Integer days) { + try { + int cleanedCount = taskLogService.cleanLogsByDays(days); + return BaseResponse.success(cleanedCount); + + } catch (Exception e) { + log.error("清理执行日志失败", e); + return new BaseResponse<>(500, false, "清理失败:" + e.getMessage(), null); + } + } + + /** + * 获取调度器状态 + */ + @ApiOperation("获取调度器状态") + @GetMapping("/scheduler/status") + public BaseResponse> getSchedulerStatus() { + try { + Map result = new HashMap<>(); + result.put("runningTaskCount", dynamicTaskScheduler.getRunningTaskCount()); + result.put("runningTaskIds", dynamicTaskScheduler.getRunningTaskIds()); + + return BaseResponse.success(result); + + } catch (Exception e) { + log.error("获取调度器状态失败", e); + return new BaseResponse<>(500, false, "查询失败:" + e.getMessage(), null); + } + } + + @GetMapping("/test") + public BaseResponse test() { + System.out.println("test"); + System.out.println("test"); + System.out.println("test"); + System.out.println("test"); + System.out.println("test"); + return BaseResponse.success(Boolean.TRUE); + } + +} diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/controller/ScheduledTaskTestController.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/controller/ScheduledTaskTestController.java new file mode 100644 index 0000000..06be41a --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/controller/ScheduledTaskTestController.java @@ -0,0 +1,332 @@ +package com.coscoshipping.ebtp.system.scheduled.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.chinaunicom.mall.ebtp.common.base.entity.BaseResponse; +import com.coscoshipping.ebtp.system.scheduled.entity.ScheduledTask; +import com.coscoshipping.ebtp.system.scheduled.entity.ScheduledTaskLog; +import com.coscoshipping.ebtp.system.scheduled.scheduler.DynamicTaskScheduler; +import com.coscoshipping.ebtp.system.scheduled.service.IScheduledTaskLogService; +import com.coscoshipping.ebtp.system.scheduled.service.IScheduledTaskService; +import com.coscoshipping.ebtp.system.scheduled.util.CronUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.*; + +/** + * 定时任务测试控制器 + */ +@Slf4j +@Api(tags = "定时任务测试接口") +@RestController +@RequestMapping("/sys/scheduled/test") +public class ScheduledTaskTestController { + + @Autowired + private IScheduledTaskService scheduledTaskService; + + @Autowired + private IScheduledTaskLogService taskLogService; + + @Autowired + private DynamicTaskScheduler dynamicTaskScheduler; + + /** + * 测试接口 - 创建测试定时任务 + */ + @ApiOperation("创建测试定时任务") + @PostMapping("/createTestTask") + public BaseResponse createTestTask( + @ApiParam("任务名称") @RequestParam(defaultValue = "测试任务") String taskName, + @ApiParam("Cron表达式") @RequestParam(defaultValue = "0/30 * * * * ?") String cronExpression, + @ApiParam("目标服务") @RequestParam(defaultValue = "localhost:8080") String targetService, + @ApiParam("目标API") @RequestParam(defaultValue = "/sys/scheduled/test/echo") String targetApi) { + + try { + // 验证Cron表达式 + if (!CronUtil.isValidCron(cronExpression)) { + return new BaseResponse<>(400, false, "Cron表达式格式不正确", null); + } + + // 创建测试任务 + ScheduledTask testTask = new ScheduledTask(); + testTask.setTaskName(taskName + "_" + System.currentTimeMillis()); + testTask.setTaskDesc("自动生成的测试任务,用于验证定时任务功能"); + testTask.setCronExpression(cronExpression); + testTask.setTargetService(targetService); + testTask.setTargetApi(targetApi); + testTask.setRequestMethod("GET"); + testTask.setRequestParams("{}"); + testTask.setRequestHeaders("{}"); + testTask.setStatus(1); // 启用状态 + testTask.setCreateTime(new Date()); + testTask.setUpdateTime(new Date()); + + // 计算下次执行时间 + Date nextRunTime = CronUtil.getNextExecutionTime(cronExpression, new Date()); + testTask.setNextRunTime(nextRunTime); + + boolean success = scheduledTaskService.save(testTask); + + if (success) { + log.info("创建测试任务成功,任务ID:{}, 任务名称:{}", testTask.getId(), testTask.getTaskName()); + return BaseResponse.success(testTask); + } else { + return new BaseResponse<>(500, false, "创建测试任务失败", null); + } + + } catch (Exception e) { + log.error("创建测试任务失败", e); + return new BaseResponse<>(500, false, "创建测试任务失败:" + e.getMessage(), null); + } + } + + /** + * 测试接口 - Echo测试端点 + */ + @ApiOperation("Echo测试端点") + @GetMapping("/echo") + public BaseResponse> testEcho( + @ApiParam("测试参数") @RequestParam(required = false) String message) { + + Map result = new HashMap<>(); + result.put("timestamp", new Date()); + result.put("message", message != null ? message : "Hello from scheduled task test!"); + result.put("status", "success"); + result.put("serverInfo", "ScheduledTask Test Echo Service"); + result.put("threadName", Thread.currentThread().getName()); + + log.info("测试Echo接口被调用,参数:{}, 线程:{}", message, Thread.currentThread().getName()); + + return BaseResponse.success(result); + } + + /** + * 测试接口 - 获取任务执行统计 + */ + @ApiOperation("获取任务执行统计") + @GetMapping("/statistics") + public BaseResponse> getTaskStatistics( + @ApiParam("统计天数") @RequestParam(defaultValue = "7") Integer days) { + + try { + Map statistics = new HashMap<>(); + + // 获取任务总数 + long totalTasks = scheduledTaskService.count(); + + // 获取启用的任务数 + QueryWrapper enabledWrapper = new QueryWrapper<>(); + enabledWrapper.eq("status", 1); + long enabledTasks = scheduledTaskService.count(enabledWrapper); + + // 获取最近N天的执行日志统计 + QueryWrapper logWrapper = new QueryWrapper<>(); + logWrapper.ge("create_time", new Date(System.currentTimeMillis() - days * 24 * 60 * 60 * 1000L)); + List recentLogs = taskLogService.list(logWrapper); + + // 统计执行结果 + long successCount = recentLogs.stream().filter(log -> log.getStatus() == 1).count(); + long failureCount = recentLogs.stream().filter(log -> log.getStatus() == 0).count(); + + // 计算平均执行时间 + double avgExecutionTime = recentLogs.stream() + .filter(log -> log.getExecuteTime() != null) + .mapToLong(ScheduledTaskLog::getExecuteTime) + .average() + .orElse(0.0); + + statistics.put("totalTasks", totalTasks); + statistics.put("enabledTasks", enabledTasks); + statistics.put("disabledTasks", totalTasks - enabledTasks); + statistics.put("recentDays", days); + statistics.put("totalExecutions", recentLogs.size()); + statistics.put("successExecutions", successCount); + statistics.put("failureExecutions", failureCount); + statistics.put("successRate", recentLogs.size() > 0 ? (double) successCount / recentLogs.size() * 100 : 0); + statistics.put("avgExecutionTime", Math.round(avgExecutionTime)); + statistics.put("runningTasks", dynamicTaskScheduler.getRunningTaskCount()); + + return BaseResponse.success(statistics); + + } catch (Exception e) { + log.error("获取任务统计失败", e); + return new BaseResponse<>(500, false, "获取统计失败:" + e.getMessage(), null); + } + } + + /** + * 测试接口 - 批量创建测试任务 + */ + @ApiOperation("批量创建测试任务") + @PostMapping("/createBatchTestTasks") + public BaseResponse> createBatchTestTasks( + @ApiParam("任务数量") @RequestParam(defaultValue = "3") Integer count) { + + try { + if (count <= 0 || count > 10) { + return new BaseResponse<>(400, false, "任务数量必须在1-10之间", null); + } + + List createdTasks = new ArrayList<>(); + String[] cronExpressions = { + "0/30 * * * * ?", // 每30秒 + "0 */2 * * * ?", // 每2分钟 + "0 */5 * * * ?" // 每5分钟 + }; + + for (int i = 0; i < count; i++) { + ScheduledTask task = new ScheduledTask(); + task.setTaskName("批量测试任务_" + (i + 1) + "_" + System.currentTimeMillis()); + task.setTaskDesc("批量创建的测试任务 #" + (i + 1)); + task.setCronExpression(cronExpressions[i % cronExpressions.length]); + task.setTargetService("localhost:8080"); + task.setTargetApi("/sys/scheduled/test/echo?message=batch_task_" + (i + 1)); + task.setRequestMethod("GET"); + task.setRequestParams("{}"); + task.setRequestHeaders("{}"); + task.setStatus(1); // 启用状态 + task.setCreateTime(new Date()); + task.setUpdateTime(new Date()); + + // 计算下次执行时间 + Date nextRunTime = CronUtil.getNextExecutionTime(task.getCronExpression(), new Date()); + task.setNextRunTime(nextRunTime); + + boolean success = scheduledTaskService.save(task); + if (success) { + createdTasks.add(task); + log.info("批量创建测试任务成功,任务ID:{}, 任务名称:{}", task.getId(), task.getTaskName()); + } + } + + return BaseResponse.success(createdTasks); + + } catch (Exception e) { + log.error("批量创建测试任务失败", e); + return new BaseResponse<>(500, false, "批量创建失败:" + e.getMessage(), null); + } + } + + /** + * 测试接口 - 清理测试任务 + */ + @ApiOperation("清理测试任务") + @PostMapping("/cleanTestTasks") + public BaseResponse cleanTestTasks() { + try { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.like("task_name", "测试任务") + .or() + .like("task_name", "批量测试任务"); + + List testTasks = scheduledTaskService.list(wrapper); + int cleanedCount = 0; + + for (ScheduledTask task : testTasks) { + boolean removed = scheduledTaskService.removeById(task.getId()); + if (removed) { + cleanedCount++; + log.info("清理测试任务:{}", task.getTaskName()); + } + } + + return BaseResponse.success(cleanedCount); + + } catch (Exception e) { + log.error("清理测试任务失败", e); + return new BaseResponse<>(500, false, "清理失败:" + e.getMessage(), null); + } + } + + /** + * 测试接口 - 模拟失败的任务 + */ + @ApiOperation("模拟失败的任务端点") + @GetMapping("/error") + public BaseResponse> testError( + @ApiParam("错误类型") @RequestParam(defaultValue = "runtime") String errorType) { + + log.info("测试错误接口被调用,错误类型:{}", errorType); + + switch (errorType) { + case "timeout": + try { + Thread.sleep(35000); // 超过30秒超时 + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + break; + case "exception": + throw new RuntimeException("模拟业务异常"); + case "http500": + return new BaseResponse<>(500, false, "模拟HTTP 500错误", null); + default: + throw new RuntimeException("模拟运行时异常"); + } + + return BaseResponse.success(Collections.singletonMap("message", "不应该到达这里")); + } + + /** + * 测试接口 - 手动触发任务 + */ + @ApiOperation("手动触发指定任务") + @PostMapping("/triggerTask") + public BaseResponse triggerTask(@ApiParam("任务ID") @RequestParam String taskId) { + try { + ScheduledTask task = scheduledTaskService.getById(taskId); + if (task == null) { + return new BaseResponse<>(404, false, "任务不存在", false); + } + + dynamicTaskScheduler.triggerTask(taskId); + log.info("手动触发任务成功,任务ID:{}, 任务名称:{}", taskId, task.getTaskName()); + + return BaseResponse.success(true); + + } catch (Exception e) { + log.error("手动触发任务失败,任务ID:{}", taskId, e); + return new BaseResponse<>(500, false, "触发失败:" + e.getMessage(), false); + } + } + + /** + * 测试接口 - 获取调度器详细状态 + */ + @ApiOperation("获取调度器详细状态") + @GetMapping("/schedulerStatus") + public BaseResponse> getSchedulerStatus() { + try { + Map status = new HashMap<>(); + status.put("runningTaskCount", dynamicTaskScheduler.getRunningTaskCount()); + status.put("runningTaskIds", dynamicTaskScheduler.getRunningTaskIds()); + + // 获取运行中任务的详细信息 + List> runningTaskDetails = new ArrayList<>(); + for (String taskId : dynamicTaskScheduler.getRunningTaskIds()) { + ScheduledTask task = scheduledTaskService.getById(taskId); + if (task != null) { + Map taskInfo = new HashMap<>(); + taskInfo.put("taskId", task.getId()); + taskInfo.put("taskName", task.getTaskName()); + taskInfo.put("cronExpression", task.getCronExpression()); + taskInfo.put("nextRunTime", task.getNextRunTime()); + taskInfo.put("lastRunTime", task.getLastRunTime()); + runningTaskDetails.add(taskInfo); + } + } + status.put("runningTaskDetails", runningTaskDetails); + + return BaseResponse.success(status); + + } catch (Exception e) { + log.error("获取调度器状态失败", e); + return new BaseResponse<>(500, false, "获取状态失败:" + e.getMessage(), null); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/ScheduledTaskLogMapper.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/ScheduledTaskLogMapper.java new file mode 100644 index 0000000..f22cec7 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/ScheduledTaskLogMapper.java @@ -0,0 +1,39 @@ +package com.coscoshipping.ebtp.system.scheduled.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.coscoshipping.ebtp.system.scheduled.entity.ScheduledTaskLog; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +/** + * 定时任务执行日志Mapper接口 + */ +@Mapper +public interface ScheduledTaskLogMapper extends BaseMapper { + + /** + * 根据任务ID查询执行日志 + * @param taskId 任务ID + * @return 执行日志列表 + */ + List selectLogsByTaskId(@Param("taskId") String taskId); + + /** + * 根据时间范围查询执行日志 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 执行日志列表 + */ + List selectLogsByTimeRange(@Param("startTime") Date startTime, + @Param("endTime") Date endTime); + + /** + * 删除指定天数之前的日志 + * @param days 天数 + * @return 删除记录数 + */ + int deleteLogsByDays(@Param("days") int days); +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/ScheduledTaskMapper.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/ScheduledTaskMapper.java new file mode 100644 index 0000000..dc8f1bb --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/ScheduledTaskMapper.java @@ -0,0 +1,32 @@ +package com.coscoshipping.ebtp.system.scheduled.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.coscoshipping.ebtp.system.scheduled.entity.ScheduledTask; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 定时任务配置Mapper接口 + */ +@Mapper +public interface ScheduledTaskMapper extends BaseMapper { + + /** + * 查询启用的定时任务列表 + * @return 启用的定时任务列表 + */ + List selectEnabledTasks(); + + /** + * 更新任务执行时间 + * @param taskId 任务ID + * @param lastRunTime 上次执行时间 + * @param nextRunTime 下次执行时间 + * @return 更新结果 + */ + int updateTaskRunTime(@Param("taskId") String taskId, + @Param("lastRunTime") java.util.Date lastRunTime, + @Param("nextRunTime") java.util.Date nextRunTime); +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/mapper/ScheduledTaskLogMapper.xml b/src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/mapper/ScheduledTaskLogMapper.xml new file mode 100644 index 0000000..b43585e --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/mapper/ScheduledTaskLogMapper.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + id, task_id, task_name, start_time, end_time, execute_time, status, + result, error_msg, instance_id, target_service, target_api, + request_params, response_data, create_time + + + + + + + + + + + DELETE FROM scheduled_task_log + WHERE create_time < DATE_SUB(NOW(), INTERVAL #{days} DAY) + + + \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/mapper/ScheduledTaskMapper.xml b/src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/mapper/ScheduledTaskMapper.xml new file mode 100644 index 0000000..e9c0916 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/dao/mapper/ScheduledTaskMapper.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, task_name, task_desc, cron_expression, target_service, target_api, + request_method, request_params, request_headers, status, last_run_time, + next_run_time, create_by, create_time, update_by, update_time, remark + + + + + + + + UPDATE scheduled_task + SET last_run_time = #{lastRunTime}, + next_run_time = #{nextRunTime}, + WHERE id = #{taskId} + + + diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/entity/ScheduledTask.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/entity/ScheduledTask.java new file mode 100644 index 0000000..f346dc9 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/entity/ScheduledTask.java @@ -0,0 +1,119 @@ +package com.coscoshipping.ebtp.system.scheduled.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** + * 定时任务配置表 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("scheduled_task") +public class ScheduledTask implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 任务名称 + */ + private String taskName; + + /** + * 任务描述 + */ + private String taskDesc; + + /** + * cron表达式 + */ + private String cronExpression; + + /** + * 目标服务名称 + */ + private String targetService; + + /** + * 目标接口路径 + */ + private String targetApi; + + /** + * 请求方法类型(GET、POST等) + */ + private String requestMethod; + + /** + * 请求参数(JSON格式) + */ + private String requestParams; + + /** + * 请求头(JSON格式) + */ + private String requestHeaders; + + /** + * 任务状态(0:禁用 1:启用) + */ + private Integer status; + + /** + * 上次执行时间 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date lastRunTime; + + /** + * 下次执行时间 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date nextRunTime; + + /** + * 创建人 + */ + private String createBy; + + /** + * 创建时间 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新人 + */ + private String updateBy; + + /** + * 更新时间 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 备注 + */ + private String remark; +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/entity/ScheduledTaskLog.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/entity/ScheduledTaskLog.java new file mode 100644 index 0000000..117acc2 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/entity/ScheduledTaskLog.java @@ -0,0 +1,107 @@ +package com.coscoshipping.ebtp.system.scheduled.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** + * 定时任务执行日志表 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("scheduled_task_log") +public class ScheduledTaskLog implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + /** + * 任务ID + */ + private String taskId; + + /** + * 任务名称 + */ + private String taskName; + + /** + * 执行开始时间 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + /** + * 执行结束时间 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + /** + * 执行耗时(毫秒) + */ + private Long executeTime; + + /** + * 执行状态(0:失败 1:成功) + */ + private Integer status; + + /** + * 执行结果 + */ + private String result; + + /** + * 错误信息 + */ + private String errorMsg; + + /** + * 执行实例ID + */ + private String instanceId; + + /** + * 目标服务名称 + */ + private String targetService; + + /** + * 目标接口路径 + */ + private String targetApi; + + /** + * 请求参数 + */ + private String requestParams; + + /** + * 响应数据 + */ + private String responseData; + + /** + * 创建时间 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/scheduler/DynamicTaskScheduler.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/scheduler/DynamicTaskScheduler.java new file mode 100644 index 0000000..f14cb05 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/scheduler/DynamicTaskScheduler.java @@ -0,0 +1,326 @@ +package com.coscoshipping.ebtp.system.scheduled.scheduler; + +import com.coscoshipping.ebtp.system.scheduled.entity.ScheduledTask; +import com.coscoshipping.ebtp.system.scheduled.service.IScheduledTaskLockService; +import com.coscoshipping.ebtp.system.scheduled.service.IScheduledTaskLogService; +import com.coscoshipping.ebtp.system.scheduled.service.IScheduledTaskService; +import com.coscoshipping.ebtp.system.scheduled.util.CronUtil; +import com.coscoshipping.ebtp.system.scheduled.util.TaskExecutorUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.stream.Collectors; + +/** + * 动态任务调度器 - 基于ThreadPoolTaskScheduler实现 + */ +@Slf4j +@Component +public class DynamicTaskScheduler { + + @Autowired + private IScheduledTaskService scheduledTaskService; + + @Autowired + private IScheduledTaskLockService taskLockService; + + @Autowired + private IScheduledTaskLogService taskLogService; + + @Autowired + private TaskExecutorUtil taskExecutorUtil; + + private ThreadPoolTaskScheduler taskScheduler; + + /** + * 存储正在运行的任务 + * Key: 任务ID, Value: ScheduledFuture + */ + private final Map> runningTasks = new ConcurrentHashMap<>(); + + /** + * 存储任务的最后更新时间,用于检测任务配置变更 + * Key: 任务ID, Value: 最后更新时间 + */ + private final Map taskUpdateTimes = new ConcurrentHashMap<>(); + + @PostConstruct + public void init() { + // 初始化任务调度器 + taskScheduler = new ThreadPoolTaskScheduler(); + taskScheduler.setPoolSize(10); + taskScheduler.setThreadNamePrefix("DynamicTask-"); + taskScheduler.setWaitForTasksToCompleteOnShutdown(true); + taskScheduler.setAwaitTerminationSeconds(60); + taskScheduler.initialize(); + + // 启动任务管理器 + startTaskManager(); + + log.info("动态任务调度器初始化完成"); + } + + @PreDestroy + public void destroy() { + if (taskScheduler != null) { + // 停止所有任务 + runningTasks.values().forEach(future -> future.cancel(false)); + runningTasks.clear(); + taskUpdateTimes.clear(); + + // 关闭调度器 + taskScheduler.shutdown(); + log.info("动态任务调度器已关闭"); + } + } + + /** + * 启动任务管理器 - 定期检查任务配置变更 + */ + private void startTaskManager() { + // 初始加载所有启用的任务 + loadAllEnabledTasks(); + + // 每60秒检查一次任务配置变更 +// taskScheduler.scheduleWithFixedDelay(() -> { +// taskChanges(); +// }, 60000); + + // 每天凌晨清理过期日志 + taskScheduler.schedule(() -> { + try { + cleanExpiredLogs(); + } catch (Exception e) { + log.error("清理过期日志失败", e); + } + }, new CronTrigger("0 0 2 * * ?")); + } + + public void taskChanges() { + try { + log.info("检查任务配置变更..."); + checkTaskConfigChanges(); + clearExpiredLocks(); + } catch (Exception e) { + log.error("检查任务配置变更失败", e); + } + } + + /** + * 加载所有启用的任务 + */ + private void loadAllEnabledTasks() { + try { + List enabledTasks = scheduledTaskService.listEnabledTasks(); + for (ScheduledTask task : enabledTasks) { + scheduleTask(task); + } + log.info("加载启用任务完成,共{}个任务", enabledTasks.size()); + } catch (Exception e) { + log.error("加载启用任务失败", e); + } + } + + /** + * 检查任务配置变更 + */ + private void checkTaskConfigChanges() { + try { + List currentTasks = scheduledTaskService.listEnabledTasks(); + Set currentTaskIds = currentTasks.stream() + .map(ScheduledTask::getId) + .collect(Collectors.toSet()); + + // 检查新增或修改的任务 + for (ScheduledTask task : currentTasks) { + Date lastUpdateTime = taskUpdateTimes.get(task.getId()); + boolean isNewTask = !runningTasks.containsKey(task.getId()); + boolean isModified = lastUpdateTime == null || + (task.getUpdateTime() != null && task.getUpdateTime().after(lastUpdateTime)); + + if (isNewTask || isModified) { + log.info("检测到任务变更,重新调度任务:{}", task.getTaskName()); + rescheduleTask(task); + } + } + + // 检查已删除或禁用的任务 + runningTasks.keySet().removeIf(taskId -> { + if (!currentTaskIds.contains(taskId)) { + log.info("检测到任务已删除或禁用,停止调度:{}", taskId); + stopTask(taskId); + return true; + } + return false; + }); + + } catch (Exception e) { + log.error("检查任务配置变更失败", e); + } + } + + /** + * 调度任务 + */ + public void scheduleTask(ScheduledTask task) { + try { + if (!CronUtil.isValidCron(task.getCronExpression())) { + log.warn("任务Cron表达式无效,跳过调度:{}", task.getTaskName()); + return; + } + + // 创建任务执行器 + Runnable taskRunner = () -> executeTaskWithLock(task); + + // 使用Cron表达式调度任务 + CronTrigger cronTrigger = new CronTrigger(task.getCronExpression()); + ScheduledFuture future = taskScheduler.schedule(taskRunner, cronTrigger); + + // 保存任务信息 + runningTasks.put(task.getId(), future); + taskUpdateTimes.put(task.getId(), task.getUpdateTime() != null ? task.getUpdateTime() : new Date()); + + log.info("任务调度成功:{} [{}]", task.getTaskName(), task.getCronExpression()); + + } catch (Exception e) { + log.error("调度任务失败:{}", task.getTaskName(), e); + } + } + + /** + * 重新调度任务 + */ + public void rescheduleTask(ScheduledTask task) { + // 先停止现有任务 + stopTask(task.getId()); + // 重新调度 + scheduleTask(task); + } + + /** + * 停止任务 + */ + public void stopTask(String taskId) { + ScheduledFuture future = runningTasks.remove(taskId); + if (future != null) { + future.cancel(false); + taskUpdateTimes.remove(taskId); + log.info("任务已停止:{}", taskId); + } + } + + /** + * 带分布式锁的任务执行 + */ + private void executeTaskWithLock(ScheduledTask task) { + String instanceId = getInstanceId(); + + try { + // 尝试获取分布式锁 + boolean lockAcquired = taskLockService.tryLock(task.getId(), instanceId, 300); + if (!lockAcquired) { + log.debug("未能获取到分布式锁,跳过任务执行:{}", task.getTaskName()); + return; + } + + log.info("获取到分布式锁,开始执行定时任务:{}", task.getTaskName()); + + try { + // 更新任务执行时间 + Date currentTime = new Date(); + Date nextRunTime = CronUtil.getNextExecutionTime(task.getCronExpression(), currentTime); + scheduledTaskService.updateTaskRunTime(task.getId(), currentTime, nextRunTime); + + // 异步执行任务(使用无锁版本,因为锁已经在这里获取了) + taskExecutorUtil.executeTaskWithoutLock(task, instanceId); + + } finally { + // 释放分布式锁 + taskLockService.releaseLock(task.getId(), instanceId); + log.debug("释放分布式锁:{}", task.getTaskName()); + } + + } catch (Exception e) { + log.error("执行任务失败:{}", task.getTaskName(), e); + } + } + + /** + * 获取实例ID + */ + private String getInstanceId() { + try { + String hostName = java.net.InetAddress.getLocalHost().getHostName(); + String hostAddress = java.net.InetAddress.getLocalHost().getHostAddress(); + return hostName + ":" + hostAddress + ":dynamic"; + } catch (Exception e) { + return "unknown:dynamic"; + } + } + + /** + * 清理过期锁 + */ + private void clearExpiredLocks() { + try { + int cleanedCount = taskLockService.clearExpiredLocks(); + if (cleanedCount > 0) { + log.debug("清理过期锁完成,清理数量:{}", cleanedCount); + } + } catch (Exception e) { + log.error("清理过期锁失败", e); + } + } + + /** + * 清理过期日志 + */ + private void cleanExpiredLogs() { + try { + int cleanedCount = taskLogService.cleanLogsByDays(30); + log.info("清理过期日志完成,清理数量:{}", cleanedCount); + } catch (Exception e) { + log.error("清理过期日志失败", e); + } + } + + /** + * 手动触发任务 + */ + public void triggerTask(String taskId) { + try { + ScheduledTask task = scheduledTaskService.getById(taskId); + if (task != null) { + taskScheduler.execute(() -> executeTaskWithLock(task)); + log.info("手动触发任务:{}", task.getTaskName()); + } + } catch (Exception e) { + log.error("手动触发任务失败:{}", taskId, e); + } + } + + /** + * 获取运行中的任务数量 + */ + public int getRunningTaskCount() { + return runningTasks.size(); + } + + /** + * 获取所有运行中的任务ID + */ + public java.util.Set getRunningTaskIds() { + return runningTasks.keySet(); + } +} diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/IScheduledTaskLockService.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/IScheduledTaskLockService.java new file mode 100644 index 0000000..f0e0886 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/IScheduledTaskLockService.java @@ -0,0 +1,30 @@ +package com.coscoshipping.ebtp.system.scheduled.service; + +/** + * 定时任务分布式锁服务接口(Redis实现) + */ +public interface IScheduledTaskLockService { + + /** + * 尝试获取分布式锁 + * @param taskId 任务ID + * @param instanceId 实例ID + * @param lockTimeoutSeconds 锁超时时间(秒) + * @return 是否获取成功 + */ + boolean tryLock(String taskId, String instanceId, int lockTimeoutSeconds); + + /** + * 释放分布式锁 + * @param taskId 任务ID + * @param instanceId 实例ID + * @return 是否释放成功 + */ + boolean releaseLock(String taskId, String instanceId); + + /** + * 清理过期锁(Redis自动过期,通常无需实现) + * @return 清理的锁数量 + */ + int clearExpiredLocks(); +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/IScheduledTaskLogService.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/IScheduledTaskLogService.java new file mode 100644 index 0000000..280332a --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/IScheduledTaskLogService.java @@ -0,0 +1,55 @@ +package com.coscoshipping.ebtp.system.scheduled.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.coscoshipping.ebtp.system.scheduled.entity.ScheduledTaskLog; + +import java.util.Date; +import java.util.List; + +/** + * 定时任务执行日志服务接口 + */ +public interface IScheduledTaskLogService extends IService { + + /** + * 记录任务执行日志 + * @param taskId 任务ID + * @param taskName 任务名称 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param status 执行状态 + * @param result 执行结果 + * @param errorMsg 错误信息 + * @param instanceId 实例ID + * @param targetService 目标服务 + * @param targetApi 目标接口 + * @param requestParams 请求参数 + * @param responseData 响应数据 + * @return 保存结果 + */ + boolean saveTaskLog(String taskId, String taskName, Date startTime, Date endTime, + Integer status, String result, String errorMsg, String instanceId, + String targetService, String targetApi, String requestParams, String responseData); + + /** + * 根据任务ID查询执行日志 + * @param taskId 任务ID + * @return 执行日志列表 + */ + List listLogsByTaskId(String taskId); + + /** + * 根据时间范围查询执行日志 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 执行日志列表 + */ + List listLogsByTimeRange(Date startTime, Date endTime); + + /** + * 清理指定天数之前的日志 + * @param days 天数 + * @return 删除记录数 + */ + int cleanLogsByDays(int days); +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/IScheduledTaskService.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/IScheduledTaskService.java new file mode 100644 index 0000000..b30125b --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/IScheduledTaskService.java @@ -0,0 +1,48 @@ +package com.coscoshipping.ebtp.system.scheduled.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.coscoshipping.ebtp.system.scheduled.entity.ScheduledTask; + +import java.util.List; + +/** + * 定时任务配置服务接口 + */ +public interface IScheduledTaskService extends IService { + + /** + * 查询启用的定时任务列表 + * @return 启用的定时任务列表 + */ + List listEnabledTasks(); + + /** + * 更新任务执行时间 + * @param taskId 任务ID + * @param lastRunTime 上次执行时间 + * @param nextRunTime 下次执行时间 + * @return 更新结果 + */ + boolean updateTaskRunTime(String taskId, java.util.Date lastRunTime, java.util.Date nextRunTime); + + /** + * 启用任务 + * @param taskId 任务ID + * @return 操作结果 + */ + boolean enableTask(String taskId); + + /** + * 禁用任务 + * @param taskId 任务ID + * @return 操作结果 + */ + boolean disableTask(String taskId); + + /** + * 立即执行任务 + * @param taskId 任务ID + * @return 执行结果 + */ + boolean executeTaskNow(String taskId); +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/impl/ScheduledTaskLockRedisServiceImpl.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/impl/ScheduledTaskLockRedisServiceImpl.java new file mode 100644 index 0000000..4b35906 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/impl/ScheduledTaskLockRedisServiceImpl.java @@ -0,0 +1,52 @@ +package com.coscoshipping.ebtp.system.scheduled.service.impl; + +import com.coscoshipping.ebtp.system.scheduled.service.IScheduledTaskLockService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.time.Duration; + +/** + * 基于Redis的定时任务分布式锁实现 + */ +@Slf4j +@Service +public class ScheduledTaskLockRedisServiceImpl implements IScheduledTaskLockService { + + @Autowired + private RedisTemplate scheduledRedisTemplate; + + @Override + public boolean tryLock(String taskId, String instanceId, int lockTimeoutSeconds) { + String key = "scheduled:lock:" + taskId; + Boolean success = scheduledRedisTemplate.opsForValue().setIfAbsent(key, instanceId, Duration.ofSeconds(lockTimeoutSeconds)); + if (Boolean.TRUE.equals(success)) { + log.debug("成功获取Redis分布式锁,任务ID:{},实例ID:{}", taskId, instanceId); + return true; + } else { + log.debug("获取Redis分布式锁失败,任务ID:{},实例ID:{}", taskId, instanceId); + return false; + } + } + + @Override + public boolean releaseLock(String taskId, String instanceId) { + String key = "scheduled:lock:" + taskId; + String value = scheduledRedisTemplate.opsForValue().get(key); + if (instanceId.equals(value)) { + Boolean deleted = scheduledRedisTemplate.delete(key); + log.debug("释放Redis分布式锁,任务ID:{},实例ID:{},结果:{}", taskId, instanceId, deleted); + return Boolean.TRUE.equals(deleted); + } + log.debug("释放Redis分布式锁失败(非本实例持有),任务ID:{},实例ID:{}", taskId, instanceId); + return false; + } + + @Override + public int clearExpiredLocks() { + // Redis自动过期,无需手动清理 + return 0; + } +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/impl/ScheduledTaskLogServiceImpl.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/impl/ScheduledTaskLogServiceImpl.java new file mode 100644 index 0000000..b61ac61 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/impl/ScheduledTaskLogServiceImpl.java @@ -0,0 +1,62 @@ +package com.coscoshipping.ebtp.system.scheduled.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.coscoshipping.ebtp.system.scheduled.dao.ScheduledTaskLogMapper; +import com.coscoshipping.ebtp.system.scheduled.entity.ScheduledTaskLog; +import com.coscoshipping.ebtp.system.scheduled.service.IScheduledTaskLogService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; + +/** + * 定时任务执行日志服务实现类 + */ +@Slf4j +@Service +public class ScheduledTaskLogServiceImpl extends ServiceImpl implements IScheduledTaskLogService { + + @Override + public boolean saveTaskLog(String taskId, String taskName, Date startTime, Date endTime, + Integer status, String result, String errorMsg, String instanceId, + String targetService, String targetApi, String requestParams, String responseData) { + try { + ScheduledTaskLog taskLog = new ScheduledTaskLog(); + taskLog.setTaskId(taskId); + taskLog.setTaskName(taskName); + taskLog.setStartTime(startTime); + taskLog.setEndTime(endTime); + taskLog.setExecuteTime(endTime != null && startTime != null ? endTime.getTime() - startTime.getTime() : 0L); + taskLog.setStatus(status); + taskLog.setResult(result); + taskLog.setErrorMsg(errorMsg); + taskLog.setInstanceId(instanceId); + taskLog.setTargetService(targetService); + taskLog.setTargetApi(targetApi); + taskLog.setRequestParams(requestParams); + taskLog.setResponseData(responseData); + taskLog.setCreateTime(new Date()); + + return this.save(taskLog); + } catch (Exception e) { + log.error("保存任务执行日志失败,任务ID:{},错误信息:{}", taskId, e.getMessage(), e); + return false; + } + } + + @Override + public List listLogsByTaskId(String taskId) { + return this.baseMapper.selectLogsByTaskId(taskId); + } + + @Override + public List listLogsByTimeRange(Date startTime, Date endTime) { + return this.baseMapper.selectLogsByTimeRange(startTime, endTime); + } + + @Override + public int cleanLogsByDays(int days) { + return this.baseMapper.deleteLogsByDays(days); + } +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/impl/ScheduledTaskServiceImpl.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/impl/ScheduledTaskServiceImpl.java new file mode 100644 index 0000000..0a797c3 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/service/impl/ScheduledTaskServiceImpl.java @@ -0,0 +1,87 @@ +package com.coscoshipping.ebtp.system.scheduled.service.impl; + +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.coscoshipping.ebtp.system.scheduled.dao.ScheduledTaskMapper; +import com.coscoshipping.ebtp.system.scheduled.entity.ScheduledTask; +import com.coscoshipping.ebtp.system.scheduled.service.IScheduledTaskService; +import com.coscoshipping.ebtp.system.scheduled.scheduler.DynamicTaskScheduler; +import com.coscoshipping.ebtp.system.scheduled.util.TaskExecutorUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; + +/** + * 定时任务配置服务实现类 + */ +@Slf4j +@Service +public class ScheduledTaskServiceImpl extends ServiceImpl implements IScheduledTaskService { + + @Autowired + private DynamicTaskScheduler dynamicTaskScheduler; + + @Override + public List listEnabledTasks() { + return this.baseMapper.selectEnabledTasks(); + } + + @Override + public boolean updateTaskRunTime(String taskId, Date lastRunTime, Date nextRunTime) { + int result = this.baseMapper.updateTaskRunTime(taskId, lastRunTime, nextRunTime); + return result > 0; + } + + @Override + public boolean enableTask(String taskId) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("id", taskId); + updateWrapper.set("status", 1); +// updateWrapper.set("update_time", new Date()); + boolean result = this.update(updateWrapper); + + // 通知动态调度器重新调度任务 + if (result) { + dynamicTaskScheduler.taskChanges(); + } + + return result; + } + + @Override + public boolean disableTask(String taskId) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("id", taskId); + updateWrapper.set("status", 0); +// updateWrapper.set("update_time", new Date()); + boolean result = this.update(updateWrapper); + + // 通知动态调度器重新调度任务 + if (result) { + dynamicTaskScheduler.taskChanges(); + } + + return result; + } + + @Override + public boolean executeTaskNow(String taskId) { + try { + ScheduledTask task = this.getById(taskId); + if (task == null) { + log.error("任务不存在,任务ID:{}", taskId); + return false; + } + + // 使用动态调度器手动触发任务 + dynamicTaskScheduler.triggerTask(taskId); + return true; + } catch (Exception e) { + log.error("立即执行任务失败,任务ID:{},错误信息:{}", taskId, e.getMessage(), e); + return false; + } + } +} diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/sql/scheduled_task.sql b/src/main/java/com/coscoshipping/ebtp/system/scheduled/sql/scheduled_task.sql new file mode 100644 index 0000000..4456fa5 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/sql/scheduled_task.sql @@ -0,0 +1,71 @@ +-- 定时任务管理相关表结构 + +-- 1. 定时任务配置表 +CREATE TABLE `scheduled_task` ( + `id` varchar(32) NOT NULL COMMENT '主键ID', + `task_name` varchar(100) NOT NULL COMMENT '任务名称', + `task_desc` varchar(500) DEFAULT NULL COMMENT '任务描述', + `cron_expression` varchar(100) NOT NULL COMMENT 'cron表达式', + `target_service` varchar(100) NOT NULL COMMENT '目标服务名称', + `target_api` varchar(200) NOT NULL COMMENT '目标接口路径', + `request_method` varchar(10) DEFAULT 'POST' COMMENT '请求方法类型(GET、POST等)', + `request_params` text COMMENT '请求参数(JSON格式)', + `request_headers` text COMMENT '请求头(JSON格式)', + `status` tinyint(1) DEFAULT '0' COMMENT '任务状态(0:禁用 1:启用)', + `last_run_time` datetime DEFAULT NULL COMMENT '上次执行时间', + `next_run_time` datetime DEFAULT NULL COMMENT '下次执行时间', + `create_by` varchar(50) DEFAULT NULL COMMENT '创建人', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` varchar(50) DEFAULT NULL COMMENT '更新人', + `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`), + KEY `idx_status` (`status`), + KEY `idx_target_service` (`target_service`), + KEY `idx_create_time` (`create_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定时任务配置表'; + +-- 2. 定时任务执行日志表 +CREATE TABLE `scheduled_task_log` ( + `id` varchar(32) NOT NULL COMMENT '主键ID', + `task_id` varchar(32) NOT NULL COMMENT '任务ID', + `task_name` varchar(100) NOT NULL COMMENT '任务名称', + `start_time` datetime NOT NULL COMMENT '执行开始时间', + `end_time` datetime DEFAULT NULL COMMENT '执行结束时间', + `execute_time` bigint(20) DEFAULT '0' COMMENT '执行耗时(毫秒)', + `status` tinyint(1) DEFAULT '0' COMMENT '执行状态(0:失败 1:成功)', + `result` varchar(200) DEFAULT NULL COMMENT '执行结果', + `error_msg` text COMMENT '错误信息', + `instance_id` varchar(100) DEFAULT NULL COMMENT '执行实例ID', + `target_service` varchar(100) DEFAULT NULL COMMENT '目标服务名称', + `target_api` varchar(200) DEFAULT NULL COMMENT '目标接口路径', + `request_params` text COMMENT '请求参数', + `response_data` text COMMENT '响应数据', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `idx_task_id` (`task_id`), + KEY `idx_status` (`status`), + KEY `idx_start_time` (`start_time`), + KEY `idx_create_time` (`create_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定时任务执行日志表'; + +-- 3. 定时任务分布式锁表 +CREATE TABLE `scheduled_task_lock` ( + `task_id` varchar(32) NOT NULL COMMENT '任务ID', + `lock_instance` varchar(100) DEFAULT NULL COMMENT '锁定实例ID', + `lock_time` datetime DEFAULT NULL COMMENT '锁定时间', + `expire_time` datetime DEFAULT NULL COMMENT '锁定到期时间', + `status` tinyint(1) DEFAULT '0' COMMENT '锁定状态(0:未锁定 1:已锁定)', + PRIMARY KEY (`task_id`), + KEY `idx_expire_time` (`expire_time`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定时任务分布式锁表'; + +-- 插入示例数据 +INSERT INTO `scheduled_task` (`id`, `task_name`, `task_desc`, `cron_expression`, `target_service`, `target_api`, `request_method`, `request_params`, `request_headers`, `status`, `create_by`, `remark`) VALUES +('1', '测试任务', '这是一个测试任务', '0 */5 * * * ?', 'test-service', '/api/test', 'POST', '{"param1": "value1"}', '{"Content-Type": "application/json"}', 0, 'admin', '每5分钟执行一次的测试任务'); + +-- 创建索引优化查询性能 +CREATE INDEX idx_scheduled_task_next_run_time ON scheduled_task(next_run_time); +CREATE INDEX idx_scheduled_task_log_task_time ON scheduled_task_log(task_id, start_time); +CREATE INDEX idx_scheduled_task_lock_instance ON scheduled_task_lock(lock_instance); \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/util/CronUtil.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/util/CronUtil.java new file mode 100644 index 0000000..b7cb4bb --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/util/CronUtil.java @@ -0,0 +1,97 @@ +package com.coscoshipping.ebtp.system.scheduled.util; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.support.CronSequenceGenerator; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; + +/** + * Cron表达式工具类 + */ +@Slf4j +public class CronUtil { + + /** + * 验证Cron表达式是否有效 + * @param cronExpression cron表达式 + * @return 是否有效 + */ + public static boolean isValidCron(String cronExpression) { + try { + new CronSequenceGenerator(cronExpression); + return true; + } catch (Exception e) { + log.warn("无效的Cron表达式:{}", cronExpression, e); + return false; + } + } + + /** + * 获取下次执行时间 + * @param cronExpression cron表达式 + * @param currentTime 当前时间 + * @return 下次执行时间 + */ + public static Date getNextExecutionTime(String cronExpression, Date currentTime) { + try { + CronSequenceGenerator cron = new CronSequenceGenerator(cronExpression); + return cron.next(currentTime); + } catch (Exception e) { + log.error("计算下次执行时间失败,Cron表达式:{},当前时间:{}", cronExpression, currentTime, e); + } + return null; + } + + /** + * 判断当前时间是否应该执行任务 + * @param cronExpression cron表达式 + * @param currentTime 当前时间 + * @param lastExecutionTime 上次执行时间 + * @return 是否应该执行 + */ + public static boolean shouldExecute(String cronExpression, Date currentTime, Date lastExecutionTime) { + try { + CronSequenceGenerator cron = new CronSequenceGenerator(cronExpression); + + // 如果从未执行过,检查当前时间是否匹配 + if (lastExecutionTime == null) { + // 获取一分钟前的时间点的下次执行时间 + Date oneMinuteAgo = new Date(currentTime.getTime() - 60000); + Date nextExecution = cron.next(oneMinuteAgo); + return nextExecution != null && nextExecution.getTime() <= currentTime.getTime(); + } + + // 获取上次执行时间之后的下一次执行时间 + Date nextExecution = cron.next(lastExecutionTime); + + // 如果下次执行时间在当前时间之前或等于当前时间,则应该执行 + return nextExecution != null && nextExecution.getTime() <= currentTime.getTime(); + } catch (Exception e) { + log.error("判断任务是否应该执行失败,Cron表达式:{},当前时间:{},上次执行时间:{}", + cronExpression, currentTime, lastExecutionTime, e); + return false; + } + } + + /** + * 获取Cron表达式描述 + * @param cronExpression cron表达式 + * @return 描述信息 + */ + public static String getCronDescription(String cronExpression) { + try { + // 这里可以集成第三方库来生成更友好的描述 + // 简单实现,返回基本信息 + String[] parts = cronExpression.split("\\s+"); + if (parts.length >= 6) { + return String.format("秒:%s 分:%s 时:%s 日:%s 月:%s 周:%s", + parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]); + } + } catch (Exception e) { + log.warn("获取Cron表达式描述失败:{}", cronExpression, e); + } + return cronExpression; + } +} \ No newline at end of file diff --git a/src/main/java/com/coscoshipping/ebtp/system/scheduled/util/TaskExecutorUtil.java b/src/main/java/com/coscoshipping/ebtp/system/scheduled/util/TaskExecutorUtil.java new file mode 100644 index 0000000..085bbfd --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/scheduled/util/TaskExecutorUtil.java @@ -0,0 +1,154 @@ +package com.coscoshipping.ebtp.system.scheduled.util; + +import com.coscoshipping.ebtp.system.scheduled.entity.ScheduledTask; +import com.coscoshipping.ebtp.system.scheduled.service.IScheduledTaskLogService; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 任务执行工具类 + */ +@Slf4j +@Component +public class TaskExecutorUtil { + + @Autowired + private IScheduledTaskLogService taskLogService; + + @Autowired + @Qualifier("scheduledRestTemplate") + private RestTemplate restTemplate; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * 异步执行任务(无锁版本,用于已经获取锁的场景) + */ + @Async + public void executeTaskWithoutLock(ScheduledTask task, String instanceId) { + Date startTime = new Date(); + String result = null; + String errorMsg = null; + String responseData = null; + Integer status = 1; // 1:成功 0:失败 + + try { + log.info("开始执行定时任务,任务ID:{},任务名称:{},实例ID:{}", task.getId(), task.getTaskName(), instanceId); + + // 执行任务 + responseData = executeTaskInternal(task); + result = "执行成功"; + + log.info("定时任务执行完成,任务ID:{},任务名称:{}", task.getId(), task.getTaskName()); + + } catch (Exception e) { + status = 0; + errorMsg = e.getMessage(); + result = "执行失败"; + log.error("定时任务执行失败,任务ID:{},任务名称:{},错误信息:{}", task.getId(), task.getTaskName(), e.getMessage(), e); + } finally { + Date endTime = new Date(); + + // 记录执行日志 + taskLogService.saveTaskLog( + task.getId(), + task.getTaskName(), + startTime, + endTime, + status, + result, + errorMsg, + instanceId, + task.getTargetService(), + task.getTargetApi(), + task.getRequestParams(), + responseData + ); + } + } + + /** + * 执行任务内部方法 + */ + private String executeTaskInternal(ScheduledTask task) throws Exception { + String targetService = task.getTargetService(); + String targetApi = task.getTargetApi(); + String requestMethod = task.getRequestMethod(); + String requestParams = task.getRequestParams(); + String requestHeaders = task.getRequestHeaders(); + + // 解析请求参数 + Map params = new HashMap<>(); + if (requestParams != null && !requestParams.trim().isEmpty()) { + try { + params = objectMapper.readValue(requestParams, Map.class); + } catch (Exception e) { + log.warn("解析请求参数失败,使用空参数,任务ID:{},参数:{}", task.getId(), requestParams, e); + } + } + + // 解析请求头 + Map headers = new HashMap<>(); + if (requestHeaders != null && !requestHeaders.trim().isEmpty()) { + try { + headers = objectMapper.readValue(requestHeaders, Map.class); + } catch (Exception e) { + log.warn("解析请求头失败,使用空请求头,任务ID:{},请求头:{}", task.getId(), requestHeaders, e); + } + } + + // 尝试HTTP调用 + try { + return callByHttp(targetService, targetApi, requestMethod, params, headers); + } catch (Exception e) { + log.warn("通过HTTP调用失败,任务ID:{},错误:{}", task.getId(), e.getMessage()); + return null; + } + } + + + /** + * 通过HTTP调用 + */ + private String callByHttp(String targetService, String targetApi, String requestMethod, + Map params, Map headers) throws Exception { + // 构建请求URL + String url = "http://" + targetService + targetApi; + + // 设置请求头 + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_JSON); + for (Map.Entry header : headers.entrySet()) { + httpHeaders.add(header.getKey(), header.getValue()); + } + + // 构建请求体 + HttpEntity> requestEntity = new HttpEntity<>(params, httpHeaders); + + // 发送请求 + ResponseEntity response; + HttpMethod httpMethod = HttpMethod.valueOf(requestMethod.toUpperCase()); + + try { + response = restTemplate.exchange(url, httpMethod, requestEntity, String.class); + } catch (Exception e) { + throw new Exception("HTTP调用失败,详细信息:" + e.getMessage(), e); + } + + if (response.getStatusCode().is2xxSuccessful()) { + return response.getBody(); + } else { + throw new Exception("HTTP调用失败,状态码:" + response.getStatusCodeValue() + ",响应:" + response.getBody()); + } + } +} diff --git a/src/main/java/com/coscoshipping/ebtp/system/userinfo/service/impl/EbtpUserInfoServiceImpl.java b/src/main/java/com/coscoshipping/ebtp/system/userinfo/service/impl/EbtpUserInfoServiceImpl.java index 45a7ec6..f9aea6f 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/userinfo/service/impl/EbtpUserInfoServiceImpl.java +++ b/src/main/java/com/coscoshipping/ebtp/system/userinfo/service/impl/EbtpUserInfoServiceImpl.java @@ -10,16 +10,18 @@ import com.coscoshipping.ebtp.system.faceCompare.feign.AssessjFeignClient; import com.coscoshipping.ebtp.system.faceCompare.feign.ExtendFeignClient; import com.coscoshipping.ebtp.system.faceCompare.feign.RsmsFeignClient; import com.coscoshipping.ebtp.system.login.dao.BaseUserMapper; -import com.coscoshipping.ebtp.system.test.RSA; -import com.coscoshipping.ebtp.system.userinfo.dao.BaseRoleMapper; -import com.coscoshipping.ebtp.system.userinfo.entity.*; -import com.coscoshipping.ebtp.system.userinfo.service.EbtpUserInfoService; -import com.coscoshipping.ebtp.system.userinfo.service.UnicomOAuthClient; -import com.coscoshipping.ebtp.system.userinfo.utils.HttpsUtils; -import com.coscoshipping.ebtp.system.userinfo.utils.UserinfoConstants; import com.coscoshipping.ebtp.system.role.dao.SysRoleMapper; import com.coscoshipping.ebtp.system.role.entity.SysRole; +import com.coscoshipping.ebtp.system.test.RSA; import com.coscoshipping.ebtp.system.user.entity.SysUser; +import com.coscoshipping.ebtp.system.userinfo.dao.BaseRoleMapper; +import com.coscoshipping.ebtp.system.userinfo.entity.AuthCodeVo; +import com.coscoshipping.ebtp.system.userinfo.entity.AuthLogoutResponse; +import com.coscoshipping.ebtp.system.userinfo.entity.ExpertLoginUser; +import com.coscoshipping.ebtp.system.userinfo.entity.ExpertUpdateUser; +import com.coscoshipping.ebtp.system.userinfo.service.EbtpUserInfoService; +import com.coscoshipping.ebtp.system.userinfo.utils.HttpsUtils; +import com.coscoshipping.ebtp.system.userinfo.utils.UserinfoConstants; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -34,8 +36,8 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; - import static com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants.*; + @Slf4j @Service public class EbtpUserInfoServiceImpl implements EbtpUserInfoService { @@ -51,9 +53,6 @@ public class EbtpUserInfoServiceImpl implements EbtpUserInfoService { @Qualifier("userinfoRedisTemplate") private RedisTemplate redisTemplate; - @Resource - private UnicomOAuthClient unicomOAuthClient; - @Resource private RsmsFeignClient rsmsFeignClient; @@ -71,6 +70,7 @@ public class EbtpUserInfoServiceImpl implements EbtpUserInfoService { @Value("${auth.oauth.expert_update_url}") private String expert_update_url; + /** * 删除token缓存 * @@ -93,24 +93,19 @@ public class EbtpUserInfoServiceImpl implements EbtpUserInfoService { /** * 系统登出 + * * @param token * @return */ @Override - public AuthLogoutResponse logout(String token){ - AuthLogoutResponse response; - try{ - //调用认证中心登出 - response = unicomOAuthClient.logout(token); + public AuthLogoutResponse logout(String token) { + AuthLogoutResponse response = new AuthLogoutResponse(); + try { //如果注销成功,删除redis - try{ - if(response.isSuccess() && response.getData().equals("success")) { - redisTemplate.delete(REDIS_USER_KEY + token); - } - }finally { - return response; - } - }catch(Exception e){ + redisTemplate.delete(REDIS_USER_KEY + token); + response.setCode("0").setSuccess(true).setMessage("注销成功").setData("success"); + return response; + } catch (Exception e) { response = new AuthLogoutResponse(); response.setCode("1").setSuccess(false).setMessage("注销失败").setData("error"); return response; @@ -118,70 +113,70 @@ public class EbtpUserInfoServiceImpl implements EbtpUserInfoService { } @Override - public Map expertLoginCheck(ExpertLoginUser user){ + public Map expertLoginCheck(ExpertLoginUser user) { Boolean b = false; //先校验认证中心 - log.info("身份证解密前:"+user.getIdentityCard()); - String identityCard = RSA.decrypt(user.getIdentityCard(),privateKey); - log.info("身份证解密结果:"+identityCard); + log.info("身份证解密前:" + user.getIdentityCard()); + String identityCard = RSA.decrypt(user.getIdentityCard(), privateKey); + log.info("身份证解密结果:" + identityCard); user.setIdentityCard(identityCard); - Map map = JSON.parseObject(JSON.toJSONString(user), Map.class); - String json =""; - for(String key :map.keySet()){ - if(!"".equals(json)){ - json+="&"; + Map map = JSON.parseObject(JSON.toJSONString(user), Map.class); + String json = ""; + for (String key : map.keySet()) { + if (!"".equals(json)) { + json += "&"; } - json+=key+"="+map.get(key); + json += key + "=" + map.get(key); } String rJson = HttpsUtils.expertHttpPost(expert_url, json, user.getClientId()); Map rmap = JSON.parseObject(rJson, Map.class); b = UserinfoConstants.okCode.equals(rmap.get(UserinfoConstants.responseCode)); - if(b) { + if (b) { //手机验证码 - if(user.getPcaptcha()!=null&&!"".equals(user.getPcaptcha())&&user.getTel()!=null&&!"".equals(user.getTel())){ + if (user.getPcaptcha() != null && !"".equals(user.getPcaptcha()) && user.getTel() != null && !"".equals(user.getTel())) { /** * extend会解密 这里只是展示 */ - String cTel = RSA.decrypt(user.getTel(),privateKey); - log.info("解密后手机号:"+cTel); + String cTel = RSA.decrypt(user.getTel(), privateKey); + log.info("解密后手机号:" + cTel); AuthCodeVo vo = new AuthCodeVo(); vo.setMobile(user.getTel()); vo.setAuthCode(user.getPcaptcha()); Boolean telCheck = extendFeignClient.authCodeCheck(vo).getData(); - if(telCheck!=null&&telCheck){ + if (telCheck != null && telCheck) { //b = true; - }else{ - CommonExceptionEnum.FRAME_EXCEPTION_COMMON_DATA_OTHER_ERROR.customValidName("短信验证码已失效",true); + } else { + CommonExceptionEnum.FRAME_EXCEPTION_COMMON_DATA_OTHER_ERROR.customValidName("短信验证码已失效", true); } - }else if(user.getVerificationCode()!=null&&!"".equals(user.getVerificationCode())){//评审室口令 + } else if (user.getVerificationCode() != null && !"".equals(user.getVerificationCode())) {//评审室口令 //获取当前登录专家对应评审室 List roomIds = rsmsFeignClient.getRoomByCertificate(user.getIdentityCard()).getData(); - log.info("roomIds:"+roomIds); + log.info("roomIds:" + roomIds); //无参与评标 - CommonExceptionEnum.FRAME_EXCEPTION_COMMON_DATA_OTHER_ERROR.assertListNotNullByName("短信验证码或口令错误0021",roomIds); + CommonExceptionEnum.FRAME_EXCEPTION_COMMON_DATA_OTHER_ERROR.assertListNotNullByName("短信验证码或口令错误0021", roomIds); List assessRoomList = assessjFeignClient.getByIds(roomIds).getData(); - List haveList = assessRoomList.stream().filter(a->"2".equals(String.valueOf(a.getStatus())) - &&user.getVerificationCode().equals(String.valueOf(a.getVerificationCode()))).distinct().collect(Collectors.toList()); - log.info("-------haveList-------:"+haveList); - if(haveList!=null&&haveList.size()>0){ - // b = true; - }else{ - CommonExceptionEnum.FRAME_EXCEPTION_COMMON_DATA_OTHER_ERROR.customValidName("验证码或口令错误0022",true); + List haveList = assessRoomList.stream().filter(a -> "2".equals(String.valueOf(a.getStatus())) + && user.getVerificationCode().equals(String.valueOf(a.getVerificationCode()))).distinct().collect(Collectors.toList()); + log.info("-------haveList-------:" + haveList); + if (haveList != null && haveList.size() > 0) { + // b = true; + } else { + CommonExceptionEnum.FRAME_EXCEPTION_COMMON_DATA_OTHER_ERROR.customValidName("验证码或口令错误0022", true); } - }else{ - CommonExceptionEnum.FRAME_EXCEPTION_COMMON_DATA_OTHER_ERROR.customValidName("验证码或口令错误0000",true); + } else { + CommonExceptionEnum.FRAME_EXCEPTION_COMMON_DATA_OTHER_ERROR.customValidName("验证码或口令错误0000", true); } log.info("----------rmap------" + rmap); return rmap; - }else{ + } else { return rmap; } } @@ -189,28 +184,29 @@ public class EbtpUserInfoServiceImpl implements EbtpUserInfoService { /** * 专家面修改 + * * @param user * @return */ @Override - public Map reSetPassExpeBaseInfo(ExpertUpdateUser user){ - log.info("身份证解密前:"+user.getIdentityCard()); - String identityCard = RSA.decrypt(user.getIdentityCard(),privateKey); - log.info("身份证解密结果:"+identityCard); - log.info("新密码解密前:"+user.getNewPassword()); - String newPassword = RSA.decrypt(user.getNewPassword(),privateKey); - log.info("新密码解密结果:"+newPassword); - log.info("旧密码解密前:"+user.getOldPassword()); - String oldPassword = RSA.decrypt(user.getOldPassword(),privateKey); - log.info("新密码解密结果:"+oldPassword); - String url = expert_update_url+"?identityCard="+identityCard+"&oldPassword=" - +oldPassword+"&newPassword="+newPassword+"&newPassword1="+newPassword+"&token="+user.getToken()+"&code="+user.getCode(); + public Map reSetPassExpeBaseInfo(ExpertUpdateUser user) { + log.info("身份证解密前:" + user.getIdentityCard()); + String identityCard = RSA.decrypt(user.getIdentityCard(), privateKey); + log.info("身份证解密结果:" + identityCard); + log.info("新密码解密前:" + user.getNewPassword()); + String newPassword = RSA.decrypt(user.getNewPassword(), privateKey); + log.info("新密码解密结果:" + newPassword); + log.info("旧密码解密前:" + user.getOldPassword()); + String oldPassword = RSA.decrypt(user.getOldPassword(), privateKey); + log.info("新密码解密结果:" + oldPassword); + String url = expert_update_url + "?identityCard=" + identityCard + "&oldPassword=" + + oldPassword + "&newPassword=" + newPassword + "&newPassword1=" + newPassword + "&token=" + user.getToken() + "&code=" + user.getCode(); String rJson = HttpsUtils.authorizationCodeHttpPost(url); Map rmap = JSON.parseObject(rJson, Map.class); return rmap; } - public static void main(String args[]){ + public static void main(String args[]) { Integer s = 2; System.out.println(s.equals(2)); } diff --git a/src/main/resources/application-master.yml b/src/main/resources/application-master.yml index 73c3b7a..2810889 100644 --- a/src/main/resources/application-master.yml +++ b/src/main/resources/application-master.yml @@ -4,6 +4,8 @@ server: context-path: / spring: + main: + allow-bean-definition-overriding: true cloud: feign: client: @@ -13,7 +15,7 @@ spring: writeTimeout: 35000 nacos: discovery: - server-addr: 127.0.0.1:8848 #192.168.110.109:8848 + server-addr: 192.168.110.231:8848 aop: auto: true #开启spring的aop配置 proxy-target-class: true @@ -80,7 +82,7 @@ spring: redis: sentinel: master: mymaster - nodes: 192.168.110.231:26379 #10.60.161.59:26379, 10.60.161.59:26380, 10.60.161.59:26381 + nodes: 192.168.110.231:26379 password: pass database: sharding: 1 @@ -149,6 +151,8 @@ hystrix: forceClosed: true ribbon: + eureka: + enabled: false ReadTimeout: 200000 #请求处理的超时时间 ConnectTimeout: 200000 #请求连接超时时间 MaxAutoRetries: 1 #对当前实例的重试次数