| | |
| | | import com.core.common.DateUtils; |
| | | import com.core.common.R; |
| | | import com.zy.asrs.domain.param.CreateOutTaskBatchParam; |
| | | import com.zy.asrs.domain.param.CancelTaskParam; |
| | | import com.zy.asrs.domain.param.CompleteTaskParam; |
| | | import com.zy.asrs.entity.WrkMast; |
| | | import com.zy.asrs.service.WrkMastService; |
| | | import com.zy.common.service.CommonService; |
| | | import com.zy.common.web.BaseController; |
| | | import com.zy.system.service.HighPrivilegeGrantService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | private WrkMastService wrkMastService; |
| | | @Autowired |
| | | private CommonService commonService; |
| | | @Autowired |
| | | private HighPrivilegeGrantService highPrivilegeGrantService; |
| | | |
| | | @RequestMapping(value = "/wrkMast/list/auth") |
| | | @ManagerAuth |
| | |
| | | return R.error("生成批量出库任务失败"); |
| | | } |
| | | |
| | | @PostMapping(value = "/wrkMast/complete/auth") |
| | | @ManagerAuth(memo = "工作档完成任务") |
| | | public R completeTask(@RequestBody CompleteTaskParam param) { |
| | | if (param == null) { |
| | | return R.error("参数不能为空"); |
| | | } |
| | | highPrivilegeGrantService.assertGranted(request.getHeader("token"), "完成任务"); |
| | | return commonService.completeTask(param) ? R.ok() : R.error("任务完成失败"); |
| | | } |
| | | |
| | | @PostMapping(value = "/wrkMast/cancel/auth") |
| | | @ManagerAuth(memo = "工作档取消任务") |
| | | public R cancelTask(@RequestBody CancelTaskParam param) { |
| | | if (param == null) { |
| | | return R.error("参数不能为空"); |
| | | } |
| | | highPrivilegeGrantService.assertGranted(request.getHeader("token"), "取消任务"); |
| | | return commonService.cancelTask(param) ? R.ok() : R.error("任务取消失败"); |
| | | } |
| | | |
| | | private <T> void convert(Map<String, Object> map, QueryWrapper<T> wrapper){ |
| | | for (Map.Entry<String, Object> entry : map.entrySet()){ |
| | | String val = String.valueOf(entry.getValue()); |
| | |
| | | CURRENT_CIRCLE_TASK_CRN_NO("current_circle_task_crn_no_"), |
| | | MAIN_PROCESS_PSEUDOCODE("main_process_pseudocode"), |
| | | PLANNER_SCHEDULE("planner_schedule_"), |
| | | HIGH_PRIVILEGE_GRANT("high_privilege_grant_"), |
| | | ; |
| | | |
| | | public String key; |
| New file |
| | |
| | | package com.zy.system.controller; |
| | | |
| | | import com.core.annotations.ManagerAuth; |
| | | import com.core.common.R; |
| | | import com.zy.common.web.BaseController; |
| | | import com.zy.system.domain.param.HighPrivilegeGrantParam; |
| | | import com.zy.system.model.HighPrivilegeGrantStatus; |
| | | import com.zy.system.service.HighPrivilegeGrantService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | @RestController |
| | | public class HighPrivilegeGrantController extends BaseController { |
| | | |
| | | @Autowired |
| | | private HighPrivilegeGrantService highPrivilegeGrantService; |
| | | |
| | | @PostMapping("/highPrivilege/grant/auth") |
| | | @ManagerAuth(memo = "最高权限授权") |
| | | public R grant(@RequestBody HighPrivilegeGrantParam param) { |
| | | HighPrivilegeGrantStatus status = highPrivilegeGrantService.grant(request.getHeader("token"), param); |
| | | return R.ok().add(status); |
| | | } |
| | | |
| | | @GetMapping("/highPrivilege/status/auth") |
| | | @ManagerAuth |
| | | public R status() { |
| | | return R.ok().add(highPrivilegeGrantService.getStatus(request.getHeader("token"))); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.system.controller; |
| | | |
| | | import com.core.annotations.ManagerAuth; |
| | | import com.core.common.R; |
| | | import com.zy.common.web.BaseController; |
| | | import com.zy.system.domain.param.LogCleanupConfigParam; |
| | | import com.zy.system.domain.param.LogCleanupRunParam; |
| | | import com.zy.system.model.LogCleanupExecutionResult; |
| | | import com.zy.system.service.HighPrivilegeGrantService; |
| | | import com.zy.system.service.LogCleanupService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.LinkedHashMap; |
| | | import java.util.Map; |
| | | |
| | | @RestController |
| | | public class LogCleanupController extends BaseController { |
| | | |
| | | @Autowired |
| | | private LogCleanupService logCleanupService; |
| | | @Autowired |
| | | private HighPrivilegeGrantService highPrivilegeGrantService; |
| | | |
| | | @GetMapping("/logCleanup/config/auth") |
| | | @ManagerAuth |
| | | public R getConfig() { |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("expireDays", logCleanupService.getExpireDays()); |
| | | result.put("autoTime", "每日 23:00"); |
| | | result.put("tables", logCleanupService.getSupportedTables()); |
| | | return R.ok().add(result); |
| | | } |
| | | |
| | | @PostMapping("/logCleanup/config/save/auth") |
| | | @ManagerAuth(memo = "保存日志清理配置") |
| | | public R saveConfig(@RequestBody LogCleanupConfigParam param) { |
| | | if (param == null) { |
| | | return R.error("参数不能为空"); |
| | | } |
| | | logCleanupService.saveExpireDays(param.getExpireDays()); |
| | | return R.ok(); |
| | | } |
| | | |
| | | @PostMapping("/logCleanup/run/auth") |
| | | @ManagerAuth(memo = "手动清理日志") |
| | | public R run(@RequestBody LogCleanupRunParam param) { |
| | | if (param == null) { |
| | | return R.error("参数不能为空"); |
| | | } |
| | | highPrivilegeGrantService.assertGranted(request.getHeader("token"), "手动清理日志"); |
| | | LogCleanupExecutionResult result; |
| | | if ("selected".equals(param.getMode())) { |
| | | result = logCleanupService.cleanupSelected(logCleanupService.getExpireDays(), param.getTables()); |
| | | } else { |
| | | result = logCleanupService.cleanupAll(logCleanupService.getExpireDays()); |
| | | } |
| | | return R.ok().add(result); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.system.domain.param; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class HighPrivilegeGrantParam { |
| | | |
| | | private String account; |
| | | |
| | | private String password; |
| | | } |
| New file |
| | |
| | | package com.zy.system.domain.param; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class LogCleanupConfigParam { |
| | | |
| | | private Integer expireDays; |
| | | } |
| New file |
| | |
| | | package com.zy.system.domain.param; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class LogCleanupRunParam { |
| | | |
| | | private String mode; |
| | | |
| | | private List<String> tables; |
| | | } |
| New file |
| | |
| | | package com.zy.system.mapper; |
| | | |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | @Mapper |
| | | @Repository |
| | | public interface LogCleanupMapper { |
| | | |
| | | int deleteExpiredBatch(@Param("table") String table, |
| | | @Param("timeColumn") String timeColumn, |
| | | @Param("expireDays") int expireDays, |
| | | @Param("limit") int limit); |
| | | } |
| New file |
| | |
| | | package com.zy.system.model; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class HighPrivilegeGrantStatus { |
| | | |
| | | private boolean granted; |
| | | |
| | | private long remainingSeconds; |
| | | |
| | | private Long expireAt; |
| | | } |
| New file |
| | |
| | | package com.zy.system.model; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.util.LinkedHashMap; |
| | | import java.util.Map; |
| | | |
| | | @Data |
| | | public class LogCleanupExecutionResult { |
| | | |
| | | private String mode; |
| | | |
| | | private Integer expireDays; |
| | | |
| | | private long totalDeleted; |
| | | |
| | | private long executeTime; |
| | | |
| | | private Map<String, Long> detail = new LinkedHashMap<>(); |
| | | } |
| New file |
| | |
| | | package com.zy.system.service; |
| | | |
| | | import com.zy.system.domain.param.HighPrivilegeGrantParam; |
| | | import com.zy.system.model.HighPrivilegeGrantStatus; |
| | | |
| | | public interface HighPrivilegeGrantService { |
| | | |
| | | HighPrivilegeGrantStatus grant(String token, HighPrivilegeGrantParam param); |
| | | |
| | | HighPrivilegeGrantStatus getStatus(String token); |
| | | |
| | | void assertGranted(String token, String actionName); |
| | | } |
| New file |
| | |
| | | package com.zy.system.service; |
| | | |
| | | import com.zy.system.model.LogCleanupExecutionResult; |
| | | |
| | | import java.util.Map; |
| | | |
| | | public interface LogCleanupService { |
| | | |
| | | Integer getExpireDays(); |
| | | |
| | | void saveExpireDays(Integer expireDays); |
| | | |
| | | LogCleanupExecutionResult cleanupAll(Integer expireDays); |
| | | |
| | | LogCleanupExecutionResult cleanupSelected(Integer expireDays, java.util.List<String> tables); |
| | | |
| | | LogCleanupExecutionResult cleanupScheduled(); |
| | | |
| | | Map<String, String> getSupportedTables(); |
| | | } |
| New file |
| | |
| | | package com.zy.system.service.impl; |
| | | |
| | | import com.core.common.Cools; |
| | | import com.core.exception.CoolException; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | import com.zy.system.domain.param.HighPrivilegeGrantParam; |
| | | import com.zy.system.entity.Role; |
| | | import com.zy.system.entity.User; |
| | | import com.zy.system.model.HighPrivilegeGrantStatus; |
| | | import com.zy.system.service.HighPrivilegeGrantService; |
| | | import com.zy.system.service.RoleService; |
| | | import com.zy.system.service.UserService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | @Service("highPrivilegeGrantService") |
| | | public class HighPrivilegeGrantServiceImpl implements HighPrivilegeGrantService { |
| | | |
| | | private static final String ADMIN_ROLE_CODE = "admin"; |
| | | private static final int USER_ENABLED = 1; |
| | | private static final long GRANT_TTL_SECONDS = 30L * 60L; |
| | | |
| | | @Autowired |
| | | private UserService userService; |
| | | @Autowired |
| | | private RoleService roleService; |
| | | @Autowired |
| | | private RedisUtil redisUtil; |
| | | |
| | | @Override |
| | | public HighPrivilegeGrantStatus grant(String token, HighPrivilegeGrantParam param) { |
| | | if (Cools.isEmpty(token)) { |
| | | throw new CoolException("当前登录已失效,请重新登录"); |
| | | } |
| | | if (param == null || Cools.isEmpty(param.getAccount(), param.getPassword())) { |
| | | throw new CoolException("账号和密码不能为空"); |
| | | } |
| | | User user = userService.getByMobileWithSecurity(param.getAccount()); |
| | | if (user == null) { |
| | | throw new CoolException("账号或密码错误"); |
| | | } |
| | | if (!Integer.valueOf(USER_ENABLED).equals(user.getStatus())) { |
| | | throw new CoolException("授权账号已禁用"); |
| | | } |
| | | Role role = roleService.getById(user.getRoleId()); |
| | | if (role == null || !ADMIN_ROLE_CODE.equals(role.getCode())) { |
| | | throw new CoolException("仅admin管理员账号可获取最高权限"); |
| | | } |
| | | if (!Cools.eq(user.getPassword(), param.getPassword())) { |
| | | throw new CoolException("账号或密码错误"); |
| | | } |
| | | |
| | | long now = System.currentTimeMillis(); |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | payload.put("account", user.getMobile()); |
| | | payload.put("userId", user.getId()); |
| | | payload.put("grantTime", now); |
| | | payload.put("expireAt", now + GRANT_TTL_SECONDS * 1000L); |
| | | redisUtil.set(buildRedisKey(token), payload, GRANT_TTL_SECONDS); |
| | | return getStatus(token); |
| | | } |
| | | |
| | | @Override |
| | | public HighPrivilegeGrantStatus getStatus(String token) { |
| | | HighPrivilegeGrantStatus status = new HighPrivilegeGrantStatus(); |
| | | if (Cools.isEmpty(token)) { |
| | | status.setGranted(false); |
| | | return status; |
| | | } |
| | | String redisKey = buildRedisKey(token); |
| | | long remainingSeconds = redisUtil.getExpire(redisKey); |
| | | if (remainingSeconds <= 0 || !redisUtil.hasKey(redisKey)) { |
| | | status.setGranted(false); |
| | | status.setRemainingSeconds(0L); |
| | | status.setExpireAt(null); |
| | | return status; |
| | | } |
| | | status.setGranted(true); |
| | | status.setRemainingSeconds(remainingSeconds); |
| | | Object payload = redisUtil.get(redisKey); |
| | | if (payload instanceof Map) { |
| | | Object expireAt = ((Map<?, ?>) payload).get("expireAt"); |
| | | if (expireAt instanceof Number) { |
| | | status.setExpireAt(((Number) expireAt).longValue()); |
| | | } else if (expireAt != null) { |
| | | try { |
| | | status.setExpireAt(Long.parseLong(String.valueOf(expireAt))); |
| | | } catch (NumberFormatException ignore) { |
| | | status.setExpireAt(System.currentTimeMillis() + remainingSeconds * 1000L); |
| | | } |
| | | } |
| | | } |
| | | if (status.getExpireAt() == null) { |
| | | status.setExpireAt(System.currentTimeMillis() + remainingSeconds * 1000L); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | @Override |
| | | public void assertGranted(String token, String actionName) { |
| | | HighPrivilegeGrantStatus status = getStatus(token); |
| | | if (!status.isGranted()) { |
| | | throw new CoolException(actionName + "需要最高权限授权,请先在开发专用->系统配置完成授权"); |
| | | } |
| | | } |
| | | |
| | | private String buildRedisKey(String token) { |
| | | return RedisKeyType.HIGH_PRIVILEGE_GRANT.key + token; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.system.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.core.exception.CoolException; |
| | | import com.zy.system.entity.Config; |
| | | import com.zy.system.mapper.LogCleanupMapper; |
| | | import com.zy.system.model.LogCleanupExecutionResult; |
| | | import com.zy.system.service.ConfigService; |
| | | import com.zy.system.service.LogCleanupService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | @Slf4j |
| | | @Service("logCleanupService") |
| | | public class LogCleanupServiceImpl implements LogCleanupService { |
| | | |
| | | private static final String CONFIG_CODE_EXPIRE_DAYS = "logCleanupExpireDays"; |
| | | private static final int DEFAULT_EXPIRE_DAYS = 180; |
| | | private static final int DELETE_BATCH_LIMIT = 50000; |
| | | private static final String MODE_ALL = "all"; |
| | | private static final String MODE_SELECTED = "selected"; |
| | | private static final Map<String, String> SUPPORTED_TABLES = new LinkedHashMap<>(); |
| | | private static final Map<String, String> TIME_COLUMNS = new LinkedHashMap<>(); |
| | | |
| | | static { |
| | | register("asr_bas_crnp_opt", "堆垛机操作日志", "update_time"); |
| | | register("asr_bas_dual_crnp_opt", "双工位堆垛机操作日志", "update_time"); |
| | | register("asr_bas_rgv_err_log", "RGV故障日志", "create_time"); |
| | | register("asr_wrk_mast_log", "工作档日志", "modi_time"); |
| | | register("asr_bas_dual_crnp_err_log", "双工位堆垛机故障日志", "create_time"); |
| | | register("asr_bas_station_err_log", "站点故障日志", "create_time"); |
| | | register("asr_bas_station_opt", "站点操作日志", "update_time"); |
| | | register("asr_wrk_analysis", "任务执行分析", "finish_time"); |
| | | register("sys_http_request_log", "HTTP请求日志", "create_time"); |
| | | register("sys_operate_log", "操作日志", "create_time"); |
| | | } |
| | | |
| | | @Autowired |
| | | private ConfigService configService; |
| | | @Autowired |
| | | private LogCleanupMapper logCleanupMapper; |
| | | |
| | | @Override |
| | | public Integer getExpireDays() { |
| | | Config config = configService.getOne(new QueryWrapper<Config>().eq("code", CONFIG_CODE_EXPIRE_DAYS)); |
| | | if (config == null || config.getValue() == null || config.getValue().trim().isEmpty()) { |
| | | return DEFAULT_EXPIRE_DAYS; |
| | | } |
| | | try { |
| | | int value = Integer.parseInt(config.getValue().trim()); |
| | | return value > 0 ? value : DEFAULT_EXPIRE_DAYS; |
| | | } catch (NumberFormatException ignore) { |
| | | return DEFAULT_EXPIRE_DAYS; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void saveExpireDays(Integer expireDays) { |
| | | int normalized = normalizeExpireDays(expireDays); |
| | | Config config = configService.getOne(new QueryWrapper<Config>().eq("code", CONFIG_CODE_EXPIRE_DAYS)); |
| | | if (config == null) { |
| | | config = new Config(); |
| | | config.setName("日志流水清理保留天数"); |
| | | config.setCode(CONFIG_CODE_EXPIRE_DAYS); |
| | | config.setValue(String.valueOf(normalized)); |
| | | config.setType((short) 1); |
| | | config.setStatus((short) 1); |
| | | config.setSelectType("develop"); |
| | | configService.save(config); |
| | | return; |
| | | } |
| | | config.setValue(String.valueOf(normalized)); |
| | | configService.updateById(config); |
| | | } |
| | | |
| | | @Override |
| | | public LogCleanupExecutionResult cleanupAll(Integer expireDays) { |
| | | return executeCleanup(MODE_ALL, expireDays, new ArrayList<>(SUPPORTED_TABLES.keySet())); |
| | | } |
| | | |
| | | @Override |
| | | public LogCleanupExecutionResult cleanupSelected(Integer expireDays, List<String> tables) { |
| | | if (tables == null || tables.isEmpty()) { |
| | | throw new CoolException("请选择至少一张日志表"); |
| | | } |
| | | LinkedHashSet<String> normalized = new LinkedHashSet<>(); |
| | | for (String table : tables) { |
| | | if (table == null || table.trim().isEmpty()) { |
| | | continue; |
| | | } |
| | | String tableName = table.trim(); |
| | | if (!SUPPORTED_TABLES.containsKey(tableName)) { |
| | | throw new CoolException("存在不支持清理的日志表: " + tableName); |
| | | } |
| | | normalized.add(tableName); |
| | | } |
| | | if (normalized.isEmpty()) { |
| | | throw new CoolException("请选择至少一张日志表"); |
| | | } |
| | | return executeCleanup(MODE_SELECTED, expireDays, new ArrayList<>(normalized)); |
| | | } |
| | | |
| | | @Override |
| | | public LogCleanupExecutionResult cleanupScheduled() { |
| | | try { |
| | | return cleanupAll(getExpireDays()); |
| | | } catch (Exception ex) { |
| | | log.error("日志流水定时清理失败", ex); |
| | | throw ex; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, String> getSupportedTables() { |
| | | return new LinkedHashMap<>(SUPPORTED_TABLES); |
| | | } |
| | | |
| | | private LogCleanupExecutionResult executeCleanup(String mode, Integer expireDays, List<String> tables) { |
| | | int normalized = normalizeExpireDays(expireDays); |
| | | LogCleanupExecutionResult result = new LogCleanupExecutionResult(); |
| | | result.setMode(mode); |
| | | result.setExpireDays(normalized); |
| | | result.setExecuteTime(System.currentTimeMillis()); |
| | | long totalDeleted = 0L; |
| | | for (String table : tables) { |
| | | long tableDeleted = deleteTable(table, TIME_COLUMNS.get(table), normalized); |
| | | result.getDetail().put(table, tableDeleted); |
| | | totalDeleted += tableDeleted; |
| | | } |
| | | result.setTotalDeleted(totalDeleted); |
| | | return result; |
| | | } |
| | | |
| | | private long deleteTable(String table, String timeColumn, int expireDays) { |
| | | long deleted = 0L; |
| | | while (true) { |
| | | int affected = logCleanupMapper.deleteExpiredBatch(table, timeColumn, expireDays, DELETE_BATCH_LIMIT); |
| | | if (affected <= 0) { |
| | | break; |
| | | } |
| | | deleted += affected; |
| | | if (affected < DELETE_BATCH_LIMIT) { |
| | | break; |
| | | } |
| | | } |
| | | return deleted; |
| | | } |
| | | |
| | | private int normalizeExpireDays(Integer expireDays) { |
| | | if (expireDays == null || expireDays <= 0) { |
| | | throw new CoolException("日志保留天数必须大于0"); |
| | | } |
| | | return expireDays; |
| | | } |
| | | |
| | | private static void register(String table, String label, String timeColumn) { |
| | | SUPPORTED_TABLES.put(table, label); |
| | | TIME_COLUMNS.put(table, timeColumn); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.system.timer; |
| | | |
| | | import com.zy.system.service.LogCleanupService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | @Slf4j |
| | | @Component |
| | | public class LogCleanupScheduler { |
| | | |
| | | @Autowired |
| | | private LogCleanupService logCleanupService; |
| | | |
| | | @Scheduled(cron = "0 0 23 * * ?") |
| | | public void cleanupExpiredLogs() { |
| | | try { |
| | | logCleanupService.cleanupScheduled(); |
| | | } catch (Exception ex) { |
| | | log.error("日志流水自动清理失败", ex); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.zy.system.mapper.LogCleanupMapper"> |
| | | |
| | | <delete id="deleteExpiredBatch"> |
| | | DELETE FROM ${table} |
| | | WHERE ${timeColumn} IS NOT NULL |
| | | AND ${timeColumn} < DATE_SUB(NOW(), INTERVAL #{expireDays} DAY) |
| | | LIMIT #{limit} |
| | | </delete> |
| | | </mapper> |
| New file |
| | |
| | | -- 新增 日志流水清理 菜单,并初始化日志保留天数配置 |
| | | -- 说明:执行本脚本后,请在“角色授权”里给对应角色勾选新菜单和“查看”权限。 |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '日志流水清理保留天数', 'logCleanupExpireDays', '180', 1, 1, 'develop' |
| | | FROM dual |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM sys_config |
| | | WHERE code = 'logCleanupExpireDays' |
| | | ); |
| | | |
| | | SET @log_cleanup_parent_id := COALESCE( |
| | | ( |
| | | SELECT id |
| | | FROM sys_resource |
| | | WHERE code = 'develop' AND level = 1 |
| | | ORDER BY id |
| | | LIMIT 1 |
| | | ), |
| | | ( |
| | | SELECT id |
| | | FROM sys_resource |
| | | WHERE code = 'logReport' AND level = 1 |
| | | ORDER BY id |
| | | LIMIT 1 |
| | | ) |
| | | ); |
| | | |
| | | INSERT INTO sys_resource(code, name, resource_id, level, sort, status) |
| | | SELECT 'logCleanup/logCleanup.html', '日志流水清理', @log_cleanup_parent_id, 2, 995, 1 |
| | | FROM dual |
| | | WHERE @log_cleanup_parent_id IS NOT NULL |
| | | AND NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM sys_resource |
| | | WHERE code = 'logCleanup/logCleanup.html' AND level = 2 |
| | | ); |
| | | |
| | | UPDATE sys_resource |
| | | SET name = '日志流水清理', |
| | | resource_id = @log_cleanup_parent_id, |
| | | level = 2, |
| | | sort = 995, |
| | | status = 1 |
| | | WHERE code = 'logCleanup/logCleanup.html' AND level = 2; |
| | | |
| | | SET @log_cleanup_id := ( |
| | | SELECT id |
| | | FROM sys_resource |
| | | WHERE code = 'logCleanup/logCleanup.html' AND level = 2 |
| | | ORDER BY id |
| | | LIMIT 1 |
| | | ); |
| | | |
| | | INSERT INTO sys_resource(code, name, resource_id, level, sort, status) |
| | | SELECT 'logCleanup/logCleanup.html#view', '查看', @log_cleanup_id, 3, 1, 1 |
| | | FROM dual |
| | | WHERE @log_cleanup_id IS NOT NULL |
| | | AND NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM sys_resource |
| | | WHERE code = 'logCleanup/logCleanup.html#view' AND level = 3 |
| | | ); |
| | | |
| | | UPDATE sys_resource |
| | | SET name = '查看', |
| | | resource_id = @log_cleanup_id, |
| | | level = 3, |
| | | sort = 1, |
| | | status = 1 |
| | | WHERE code = 'logCleanup/logCleanup.html#view' AND level = 3; |
| | | |
| | | SELECT id, code, name, resource_id, level, sort, status |
| | | FROM sys_resource |
| | | WHERE code IN ( |
| | | 'logCleanup/logCleanup.html', |
| | | 'logCleanup/logCleanup.html#view' |
| | | ) |
| | | ORDER BY level, sort, id; |
| | |
| | | tableHeight: 420, |
| | | layoutTimer: null, |
| | | tableResizeHandler: null, |
| | | grantTimer: null, |
| | | dialogForm: createFormDefaults(), |
| | | dialogDisplay: createDisplayDefaults(), |
| | | dialogRules: createFormRules() |
| | | dialogRules: createFormRules(), |
| | | grantStatus: { |
| | | granted: false, |
| | | remainingSeconds: 0, |
| | | expireAt: null |
| | | }, |
| | | grantDialog: { |
| | | visible: false, |
| | | submitting: false, |
| | | form: { |
| | | account: '', |
| | | password: '' |
| | | }, |
| | | rules: { |
| | | account: [ |
| | | { required: true, message: '请输入管理员账号', trigger: 'blur' } |
| | | ], |
| | | password: [ |
| | | { required: true, message: '请输入密码', trigger: 'blur' } |
| | | ] |
| | | } |
| | | } |
| | | }; |
| | | }, |
| | | computed: { |
| | |
| | | }, |
| | | isDialogReadonly: function () { |
| | | return this.dialog.mode === 'detail'; |
| | | }, |
| | | grantStatusText: function () { |
| | | if (!this.grantStatus.granted) { |
| | | return '当前无最高权限授权'; |
| | | } |
| | | return '剩余 ' + this.formatRemainingSeconds(this.grantStatus.remainingSeconds); |
| | | } |
| | | }, |
| | | created: function () { |
| | | this.fetchSelectTypeOptions(); |
| | | this.loadTable(); |
| | | this.loadGrantStatus(); |
| | | }, |
| | | mounted: function () { |
| | | var self = this; |
| | |
| | | window.removeEventListener('resize', this.tableResizeHandler); |
| | | this.tableResizeHandler = null; |
| | | } |
| | | this.stopGrantCountdown(); |
| | | }, |
| | | methods: $.extend({}, sharedMethods, { |
| | | formatRemainingSeconds: function (seconds) { |
| | | var total = Number(seconds || 0); |
| | | var minutes; |
| | | var remainSeconds; |
| | | if (total <= 0) { |
| | | return '0秒'; |
| | | } |
| | | minutes = Math.floor(total / 60); |
| | | remainSeconds = total % 60; |
| | | if (minutes <= 0) { |
| | | return remainSeconds + '秒'; |
| | | } |
| | | if (remainSeconds === 0) { |
| | | return minutes + '分钟'; |
| | | } |
| | | return minutes + '分' + remainSeconds + '秒'; |
| | | }, |
| | | stopGrantCountdown: function () { |
| | | if (this.grantTimer) { |
| | | clearInterval(this.grantTimer); |
| | | this.grantTimer = null; |
| | | } |
| | | }, |
| | | startGrantCountdown: function () { |
| | | var self = this; |
| | | self.stopGrantCountdown(); |
| | | if (!self.grantStatus.granted || Number(self.grantStatus.remainingSeconds) <= 0) { |
| | | return; |
| | | } |
| | | self.grantTimer = setInterval(function () { |
| | | if (!self.grantStatus.granted) { |
| | | self.stopGrantCountdown(); |
| | | return; |
| | | } |
| | | if (self.grantStatus.remainingSeconds <= 1) { |
| | | self.grantStatus = { |
| | | granted: false, |
| | | remainingSeconds: 0, |
| | | expireAt: null |
| | | }; |
| | | self.stopGrantCountdown(); |
| | | return; |
| | | } |
| | | self.grantStatus.remainingSeconds -= 1; |
| | | }, 1000); |
| | | }, |
| | | applyGrantStatus: function (payload) { |
| | | var status = payload || {}; |
| | | this.grantStatus = { |
| | | granted: !!status.granted, |
| | | remainingSeconds: Number(status.remainingSeconds || 0), |
| | | expireAt: status.expireAt || null |
| | | }; |
| | | this.startGrantCountdown(); |
| | | }, |
| | | loadGrantStatus: function () { |
| | | var self = this; |
| | | $.ajax({ |
| | | url: baseUrl + '/highPrivilege/status/auth', |
| | | method: 'GET', |
| | | headers: self.authHeaders(), |
| | | success: function (res) { |
| | | if (self.handleForbidden(res)) { |
| | | return; |
| | | } |
| | | if (!res || res.code !== 200) { |
| | | return; |
| | | } |
| | | self.applyGrantStatus(res.data || {}); |
| | | } |
| | | }); |
| | | }, |
| | | resetGrantDialog: function () { |
| | | this.grantDialog.submitting = false; |
| | | this.grantDialog.form = { |
| | | account: '', |
| | | password: '' |
| | | }; |
| | | if (this.$refs.grantForm) { |
| | | this.$refs.grantForm.clearValidate(); |
| | | } |
| | | }, |
| | | openGrantDialog: function () { |
| | | this.grantDialog.visible = true; |
| | | this.$nextTick(this.resetGrantDialog); |
| | | }, |
| | | submitGrant: function () { |
| | | var self = this; |
| | | if (!self.$refs.grantForm) { |
| | | return; |
| | | } |
| | | self.$refs.grantForm.validate(function (valid) { |
| | | if (!valid) { |
| | | return false; |
| | | } |
| | | self.grantDialog.submitting = true; |
| | | $.ajax({ |
| | | url: baseUrl + '/highPrivilege/grant/auth', |
| | | method: 'POST', |
| | | contentType: 'application/json;charset=UTF-8', |
| | | headers: self.authHeaders(), |
| | | data: JSON.stringify({ |
| | | account: self.grantDialog.form.account, |
| | | password: hex_md5(self.grantDialog.form.password || '') |
| | | }), |
| | | success: function (res) { |
| | | self.grantDialog.submitting = false; |
| | | if (self.handleForbidden(res)) { |
| | | return; |
| | | } |
| | | if (!res || res.code !== 200) { |
| | | self.$message.error((res && res.msg) ? res.msg : '授权失败'); |
| | | return; |
| | | } |
| | | self.$message.success('授权成功'); |
| | | self.grantDialog.visible = false; |
| | | self.applyGrantStatus(res.data || {}); |
| | | }, |
| | | error: function () { |
| | | self.grantDialog.submitting = false; |
| | | self.$message.error('授权失败'); |
| | | } |
| | | }); |
| | | return true; |
| | | }); |
| | | }, |
| | | calculateTableHeight: function () { |
| | | var viewportHeight = window.innerHeight || document.documentElement.clientHeight || 860; |
| | | var tableWrap = this.$refs.tableWrap; |
| New file |
| | |
| | | (function () { |
| | | new Vue({ |
| | | el: "#app", |
| | | data: function () { |
| | | return { |
| | | loading: false, |
| | | saving: false, |
| | | running: false, |
| | | tableOptions: [], |
| | | result: null, |
| | | form: { |
| | | expireDays: 180, |
| | | mode: "all", |
| | | tables: [] |
| | | } |
| | | }; |
| | | }, |
| | | computed: { |
| | | resultDetails: function () { |
| | | var vm = this; |
| | | if (!vm.result || !vm.result.detail) { |
| | | return []; |
| | | } |
| | | return Object.keys(vm.result.detail).map(function (table) { |
| | | return { |
| | | table: table, |
| | | label: vm.resolveTableLabel(table), |
| | | count: vm.result.detail[table] |
| | | }; |
| | | }); |
| | | } |
| | | }, |
| | | created: function () { |
| | | this.loadConfig(); |
| | | }, |
| | | methods: { |
| | | authHeaders: function () { |
| | | return { token: localStorage.getItem("token") }; |
| | | }, |
| | | handleForbidden: function (res) { |
| | | if (res && Number(res.code) === 403) { |
| | | top.location.href = baseUrl + "/"; |
| | | return true; |
| | | } |
| | | return false; |
| | | }, |
| | | resolveTableLabel: function (table) { |
| | | var index; |
| | | for (index = 0; index < this.tableOptions.length; index += 1) { |
| | | if (this.tableOptions[index].value === table) { |
| | | return this.tableOptions[index].label; |
| | | } |
| | | } |
| | | return table; |
| | | }, |
| | | loadConfig: function () { |
| | | var vm = this; |
| | | vm.loading = true; |
| | | $.ajax({ |
| | | url: baseUrl + "/logCleanup/config/auth", |
| | | method: "GET", |
| | | headers: vm.authHeaders(), |
| | | success: function (res) { |
| | | var tables; |
| | | vm.loading = false; |
| | | if (vm.handleForbidden(res)) { |
| | | return; |
| | | } |
| | | if (!res || res.code !== 200) { |
| | | vm.$message.error((res && res.msg) ? res.msg : "加载日志清理配置失败"); |
| | | return; |
| | | } |
| | | vm.form.expireDays = Number((res.data && res.data.expireDays) || 180); |
| | | tables = (res.data && res.data.tables) || {}; |
| | | vm.tableOptions = Object.keys(tables).map(function (key) { |
| | | return { |
| | | value: key, |
| | | label: tables[key] |
| | | }; |
| | | }); |
| | | }, |
| | | error: function () { |
| | | vm.loading = false; |
| | | vm.$message.error("加载日志清理配置失败"); |
| | | } |
| | | }); |
| | | }, |
| | | saveConfig: function () { |
| | | var vm = this; |
| | | if (!vm.form.expireDays || Number(vm.form.expireDays) <= 0) { |
| | | vm.$message.warning("日志保留天数必须大于0"); |
| | | return; |
| | | } |
| | | vm.saving = true; |
| | | $.ajax({ |
| | | url: baseUrl + "/logCleanup/config/save/auth", |
| | | method: "POST", |
| | | contentType: "application/json;charset=UTF-8", |
| | | headers: vm.authHeaders(), |
| | | data: JSON.stringify({ |
| | | expireDays: Number(vm.form.expireDays) |
| | | }), |
| | | success: function (res) { |
| | | vm.saving = false; |
| | | if (vm.handleForbidden(res)) { |
| | | return; |
| | | } |
| | | if (!res || res.code !== 200) { |
| | | vm.$message.error((res && res.msg) ? res.msg : "保存日志清理配置失败"); |
| | | return; |
| | | } |
| | | vm.$message.success("保存成功"); |
| | | }, |
| | | error: function () { |
| | | vm.saving = false; |
| | | vm.$message.error("保存日志清理配置失败"); |
| | | } |
| | | }); |
| | | }, |
| | | runCleanup: function () { |
| | | var vm = this; |
| | | var requestBody; |
| | | if (vm.form.mode === "selected" && (!vm.form.tables || !vm.form.tables.length)) { |
| | | vm.$message.warning("请选择至少一张日志表"); |
| | | return; |
| | | } |
| | | requestBody = { |
| | | mode: vm.form.mode, |
| | | tables: vm.form.mode === "selected" ? vm.form.tables : [] |
| | | }; |
| | | vm.$confirm("确定立即执行日志清理吗?", "提示", { |
| | | type: "warning", |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消" |
| | | }).then(function () { |
| | | vm.running = true; |
| | | $.ajax({ |
| | | url: baseUrl + "/logCleanup/run/auth", |
| | | method: "POST", |
| | | contentType: "application/json;charset=UTF-8", |
| | | headers: vm.authHeaders(), |
| | | data: JSON.stringify(requestBody), |
| | | success: function (res) { |
| | | vm.running = false; |
| | | if (vm.handleForbidden(res)) { |
| | | return; |
| | | } |
| | | if (!res || res.code !== 200) { |
| | | vm.$message.error((res && res.msg) ? res.msg : "手动清理失败"); |
| | | return; |
| | | } |
| | | vm.result = res.data || null; |
| | | vm.$message.success("手动清理执行完成"); |
| | | }, |
| | | error: function () { |
| | | vm.running = false; |
| | | vm.$message.error("手动清理失败"); |
| | | } |
| | | }); |
| | | }).catch(function () {}); |
| | | } |
| | | } |
| | | }); |
| | | })(); |
| | |
| | | cancelButtonText: "取消" |
| | | }).then(function () { |
| | | $.ajax({ |
| | | url: baseUrl + "/openapi/completeTask", |
| | | url: baseUrl + "/wrkMast/complete/auth", |
| | | contentType: "application/json", |
| | | headers: { token: localStorage.getItem("token") }, |
| | | data: JSON.stringify({ wrkNo: row.wrkNo }), |
| | |
| | | cancelButtonText: "取消" |
| | | }).then(function () { |
| | | $.ajax({ |
| | | url: baseUrl + "/openapi/cancelTask", |
| | | url: baseUrl + "/wrkMast/cancel/auth", |
| | | contentType: "application/json", |
| | | headers: { token: localStorage.getItem("token") }, |
| | | data: JSON.stringify({ wrkNo: row.wrkNo }), |
| | |
| | | |
| | | .toolbar-ops { |
| | | justify-content: flex-end; |
| | | align-items: center; |
| | | } |
| | | |
| | | .list-toolbar .el-input__inner, |
| | |
| | | max-height: 280px; |
| | | overflow: auto; |
| | | padding-right: 4px; |
| | | } |
| | | |
| | | .grant-status-text { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | min-height: 32px; |
| | | padding: 0 6px; |
| | | color: #5c6b7a; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .dialog-panel .el-dialog { |
| | |
| | | </el-popover> |
| | | <el-button size="small" plain icon="el-icon-download" :loading="exporting" @click="exportRows">导出</el-button> |
| | | <el-button size="small" plain type="warning" icon="el-icon-refresh-right" @click="refreshCache">刷新缓存</el-button> |
| | | <el-button size="small" plain type="success" icon="el-icon-key" @click="openGrantDialog">最高权限授权</el-button> |
| | | <el-tag size="small" :type="grantStatus.granted ? 'success' : 'info'"> |
| | | {{ grantStatus.granted ? '已授权' : '未授权' }} |
| | | </el-tag> |
| | | <span class="grant-status-text">{{ grantStatusText }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <el-button v-if="!isDialogReadonly" type="primary" :loading="dialog.submitting" @click="submitDialog">保存</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | |
| | | <el-dialog |
| | | class="dialog-panel" |
| | | title="最高权限授权" |
| | | :visible.sync="grantDialog.visible" |
| | | width="460px" |
| | | :close-on-click-modal="false" |
| | | @closed="resetGrantDialog"> |
| | | <el-alert |
| | | title="仅角色编码为admin的启用管理员账号可授权,授权有效期30分钟。" |
| | | type="warning" |
| | | :closable="false" |
| | | show-icon |
| | | style="margin-bottom: 16px;"> |
| | | </el-alert> |
| | | <el-form |
| | | ref="grantForm" |
| | | :model="grantDialog.form" |
| | | :rules="grantDialog.rules" |
| | | label-width="90px" |
| | | size="small"> |
| | | <el-form-item label="账号" prop="account"> |
| | | <el-input |
| | | v-model.trim="grantDialog.form.account" |
| | | placeholder="请输入管理员账号"> |
| | | </el-input> |
| | | </el-form-item> |
| | | <el-form-item label="密码" prop="password"> |
| | | <el-input |
| | | v-model="grantDialog.form.password" |
| | | type="password" |
| | | show-password |
| | | placeholder="请输入密码" |
| | | @keyup.enter.native="submitGrant"> |
| | | </el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button @click="grantDialog.visible = false">取消</el-button> |
| | | <el-button type="primary" :loading="grantDialog.submitting" @click="submitGrant">确认授权</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | |
| | | <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script> |
| | | <script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/js/tools/md5.js"></script> |
| | | <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script> |
| | | <script type="text/javascript" src="../../static/vue/element/element.js"></script> |
| | | <script type="text/javascript" src="../../static/js/config/config.js" charset="utf-8"></script> |
| New file |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="zh-CN"> |
| | | <head> |
| | | <meta charset="UTF-8"> |
| | | <title>日志流水清理</title> |
| | | <meta name="renderer" content="webkit"> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> |
| | | <link rel="stylesheet" href="../../static/vue/element/element.css"> |
| | | <link rel="stylesheet" href="../../static/css/cool.css"> |
| | | <style> |
| | | [v-cloak] { display: none; } |
| | | html, body { |
| | | margin: 0; |
| | | min-height: 100%; |
| | | color: #243447; |
| | | font-family: "Avenir Next", "PingFang SC", "Microsoft YaHei", sans-serif; |
| | | background: |
| | | radial-gradient(1000px 420px at 0% -10%, rgba(44, 107, 193, 0.12), transparent 56%), |
| | | radial-gradient(900px 400px at 100% 0%, rgba(28, 150, 126, 0.10), transparent 58%), |
| | | linear-gradient(180deg, #f2f6fb 0%, #f8fafc 100%); |
| | | } |
| | | .page-shell { |
| | | max-width: 1380px; |
| | | margin: 0 auto; |
| | | padding: 14px; |
| | | box-sizing: border-box; |
| | | } |
| | | .card-shell { |
| | | border-radius: 24px; |
| | | border: 1px solid rgba(216, 226, 238, 0.95); |
| | | background: rgba(255, 255, 255, 0.94); |
| | | box-shadow: 0 16px 32px rgba(44, 67, 96, 0.08); |
| | | overflow: hidden; |
| | | } |
| | | .section-head { |
| | | padding: 18px 20px 10px; |
| | | border-bottom: 1px solid rgba(222, 230, 239, 0.92); |
| | | } |
| | | .section-title { |
| | | margin: 0; |
| | | font-size: 22px; |
| | | font-weight: 700; |
| | | } |
| | | .section-subtitle { |
| | | margin-top: 8px; |
| | | font-size: 13px; |
| | | color: #6c7d90; |
| | | } |
| | | .content-wrap { |
| | | padding: 18px 20px 20px; |
| | | } |
| | | .config-card, |
| | | .result-card { |
| | | border: 1px solid rgba(220, 229, 239, 0.96); |
| | | border-radius: 20px; |
| | | background: rgba(255, 255, 255, 0.96); |
| | | padding: 18px; |
| | | } |
| | | .result-card { |
| | | margin-top: 16px; |
| | | } |
| | | .inline-tip { |
| | | font-size: 12px; |
| | | color: #6f7d8c; |
| | | margin-left: 10px; |
| | | } |
| | | .table-tag-list { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | } |
| | | @media (max-width: 900px) { |
| | | .page-shell { |
| | | padding: 10px; |
| | | } |
| | | .content-wrap, |
| | | .section-head { |
| | | padding-left: 14px; |
| | | padding-right: 14px; |
| | | } |
| | | } |
| | | </style> |
| | | </head> |
| | | <body> |
| | | <div id="app" class="page-shell" v-cloak> |
| | | <section class="card-shell"> |
| | | <div class="section-head"> |
| | | <h1 class="section-title">日志流水清理</h1> |
| | | <div class="section-subtitle">系统固定每天 23:00 自动清理过期日志。手动清理需要先在系统配置中获取最高权限授权。</div> |
| | | </div> |
| | | <div class="content-wrap" v-loading="loading"> |
| | | <div class="config-card"> |
| | | <el-alert |
| | | title="手动清理会按当前保留天数删除过期数据,不会删除保留期内日志。" |
| | | type="warning" |
| | | :closable="false" |
| | | show-icon |
| | | style="margin-bottom: 18px;"> |
| | | </el-alert> |
| | | <el-form label-width="120px" size="small"> |
| | | <el-form-item label="保留天数"> |
| | | <el-input-number v-model="form.expireDays" :min="1" :step="1" controls-position="right"></el-input-number> |
| | | <span class="inline-tip">常用值:180、360</span> |
| | | </el-form-item> |
| | | <el-form-item label="自动清理时间"> |
| | | <el-tag type="info">每日 23:00</el-tag> |
| | | </el-form-item> |
| | | <el-form-item label="手动清理模式"> |
| | | <el-radio-group v-model="form.mode"> |
| | | <el-radio label="all">所有日志表</el-radio> |
| | | <el-radio label="selected">指定日志表</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.mode === 'selected'" label="日志表选择"> |
| | | <el-select |
| | | v-model="form.tables" |
| | | multiple |
| | | collapse-tags |
| | | clearable |
| | | filterable |
| | | placeholder="请选择要清理的日志表" |
| | | style="width: 100%;"> |
| | | <el-option |
| | | v-for="item in tableOptions" |
| | | :key="item.value" |
| | | :label="item.label + ' (' + item.value + ')'" |
| | | :value="item.value"> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" :loading="saving" @click="saveConfig">保存配置</el-button> |
| | | <el-button type="danger" plain :loading="running" @click="runCleanup">手动清理</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <div v-if="result" class="result-card"> |
| | | <el-descriptions title="最近一次手动清理结果" :column="3" border size="small"> |
| | | <el-descriptions-item label="清理模式">{{ result.mode === 'selected' ? '指定日志表' : '所有日志表' }}</el-descriptions-item> |
| | | <el-descriptions-item label="保留天数">{{ result.expireDays }}</el-descriptions-item> |
| | | <el-descriptions-item label="删除总数">{{ result.totalDeleted }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | <div style="margin-top: 16px;" class="table-tag-list"> |
| | | <el-tag |
| | | v-for="item in resultDetails" |
| | | :key="item.table" |
| | | type="success" |
| | | effect="plain"> |
| | | {{ item.label }}: {{ item.count }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | </div> |
| | | |
| | | <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script> |
| | | <script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script> |
| | | <script type="text/javascript" src="../../static/vue/element/element.js"></script> |
| | | <script type="text/javascript" src="../../static/js/logCleanup/logCleanup.js" charset="utf-8"></script> |
| | | </body> |
| | | </html> |