From 7fa34a93b039be9efc286216b15d20698ea741c9 Mon Sep 17 00:00:00 2001 From: zhaodengke Date: Thu, 26 Oct 2023 11:06:15 +0800 Subject: [PATCH] state --- .../web/controller/file/StateController.java | 97 +++++++++ .../src/main/resources/application-prod.yml | 11 +- ruoyi-admin/src/main/resources/logback.xml | 2 +- .../com/ruoyi/file/object/ProjectState.java | 13 ++ .../com/ruoyi/file/object/StateMachine.java | 128 +++++++++-- .../com/ruoyi/file/service/StateService.java | 90 ++++++++ .../framework/config/SecurityConfig.java | 1 + .../service/impl/SysUserServiceImpl.java | 3 +- ruoyi-ui/src/api/file/state.js | 36 ++++ ruoyi-ui/src/views/file/project/index.vue | 21 ++ .../src/views/file/state/ProjectState.vue | 65 ++++++ ruoyi-ui/src/views/file/state/index.vue | 203 ++++++++++++++++++ ruoyi-ui/src/views/system/user/index.vue | 9 +- 13 files changed, 644 insertions(+), 35 deletions(-) create mode 100644 ruoyi-admin/src/main/java/com/ruoyi/web/controller/file/StateController.java create mode 100644 ruoyi-file/src/main/java/com/ruoyi/file/service/StateService.java create mode 100644 ruoyi-ui/src/api/file/state.js create mode 100644 ruoyi-ui/src/views/file/state/ProjectState.vue create mode 100644 ruoyi-ui/src/views/file/state/index.vue diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/file/StateController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/file/StateController.java new file mode 100644 index 0000000..32e069e --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/file/StateController.java @@ -0,0 +1,97 @@ +package com.ruoyi.web.controller.file; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.file.model.FileModel; +import com.ruoyi.file.model.FileSystemFilter; +import com.ruoyi.file.object.ProjectState; +import com.ruoyi.file.object.StateMachine; +import com.ruoyi.file.service.FileSystemService; +import com.ruoyi.file.service.StateService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 状态Controller + * + * @author zhao + * @date 2023-10-19 + */ +@Api(tags = "状态") +@RestController +@RequestMapping("/state") +public class StateController extends BaseController +{ + @Autowired + private StateService stateService; + + /** + * 查询项目状态列表 + */ + @ApiOperation("查询项目状态列表") + @PreAuthorize("@ss.hasPermi('admin:state:list')") + @GetMapping("/list") + public AjaxResult list() + { + List detail = stateService.list(); + return AjaxResult.success(detail); + } + + /** + * 查询项目状态 + */ + @ApiOperation("查询项目状态") + @PreAuthorize("@ss.hasPermi('admin:state:detail')") + @GetMapping("/detail/{token}") + public AjaxResult detail(@PathVariable String token) + { + ProjectState detail = stateService.detail(token); + return null != detail ? AjaxResult.success(detail) : AjaxResult.error(); + } + + /** + * GC + */ + @ApiOperation("GC") + @PreAuthorize("@ss.hasPermi('admin:state:gc')") + @PostMapping("/gc") + public AjaxResult gc() + { + long count = stateService.gc(); + return AjaxResult.success(count); + } + + /** + * 查询状态机信息 + */ + @ApiOperation("查询状态机信息") + @PreAuthorize("@ss.hasPermi('admin:state:machine')") + @GetMapping("/machine") + public AjaxResult stateMachine() + { + StateMachine stateMachine = stateService.stateMachine(); + return AjaxResult.success(stateMachine); + } + + /** + * 备份 + */ + @ApiOperation("dump") + @PreAuthorize("@ss.hasPermi('admin:state:dump')") + @PostMapping("/dump") + public AjaxResult dump() + { + stateService.dump(); + return AjaxResult.success(); + } + +} diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index 1b743f5..f16d764 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -6,9 +6,9 @@ spring: druid: # 主库数据源 master: - url: jdbc:mysql://127.0.0.1:3306/rongxin_gxzb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 - username: xxx - password: xxxx + url: jdbc:mysql://localhost:3318/nsgk_file_transfer?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: gly + password: glyadmin # 从库数据源 slave: # 从数据源开关/默认关闭 @@ -71,7 +71,7 @@ spring: # 地址 host: rongxin-redis-m-svc.prod # 端口,默认为6379 - port: 6279 + port: 6379 # 数据库索引 database: 0 # 密码 @@ -94,8 +94,7 @@ spring: # 生产环境配置 server: # 服务器的HTTP端口 - port: ${NODE_PORT} - address: ${NODE_HOST} + port: 8077 aliyun: oss: diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml index e8f81c9..0044836 100644 --- a/ruoyi-admin/src/main/resources/logback.xml +++ b/ruoyi-admin/src/main/resources/logback.xml @@ -1,7 +1,7 @@ - + diff --git a/ruoyi-file/src/main/java/com/ruoyi/file/object/ProjectState.java b/ruoyi-file/src/main/java/com/ruoyi/file/object/ProjectState.java index da1eede..283292f 100644 --- a/ruoyi-file/src/main/java/com/ruoyi/file/object/ProjectState.java +++ b/ruoyi-file/src/main/java/com/ruoyi/file/object/ProjectState.java @@ -1,6 +1,11 @@ package com.ruoyi.file.object; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; + import java.util.Date; +import java.util.List; +import java.util.Map; public final class ProjectState { @@ -123,6 +128,14 @@ public final class ProjectState return sb.toString(); } + public static ProjectState FromMap(Map parms) + { + CopyOptions options = CopyOptions.create(); + options.setIgnoreError(true); + ProjectState projectState = BeanUtil.mapToBean(parms, ProjectState.class, false, options); + return projectState; + } + public ProjectState() {} public ProjectState(String token) diff --git a/ruoyi-file/src/main/java/com/ruoyi/file/object/StateMachine.java b/ruoyi-file/src/main/java/com/ruoyi/file/object/StateMachine.java index 70c3551..7beb4b6 100644 --- a/ruoyi-file/src/main/java/com/ruoyi/file/object/StateMachine.java +++ b/ruoyi-file/src/main/java/com/ruoyi/file/object/StateMachine.java @@ -1,7 +1,12 @@ package com.ruoyi.file.object; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; + +import java.util.ArrayList; import java.util.Date; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; public final class StateMachine @@ -21,16 +26,19 @@ public final class StateMachine public long gcCount; // GC次数 public Date lastGCTime; // 最近GC时间 - private final Map projectStateMap = new LinkedHashMap<>(); - public static final StateMachine INSTANCE = new StateMachine(); + public Date startTime = new Date(); // 本次开始时间 + public Date lastEndTime; // 上次结束时间 + + private final Map stateMap = new LinkedHashMap<>(); + public static final StateMachine INSTANCE = new StateMachine(); public synchronized void AddState(ProjectState state) { - ProjectState projectState = projectStateMap.get(state.token); + ProjectState projectState = stateMap.get(state.token); if(null != projectState) projectState.Sum(state); else - projectStateMap.put(state.token, state); + stateMap.put(state.token, state); Sum(state); } @@ -55,13 +63,13 @@ public final class StateMachine this.maxHandleDuration += state.maxHandleDuration; } - private ProjectState GetState(String token) + private ProjectState State(String token) { - ProjectState state = projectStateMap.get(token); + ProjectState state = stateMap.get(token); if(null == state) { state = new ProjectState(token); - projectStateMap.put(token, state); + stateMap.put(token, state); } return state; } @@ -69,94 +77,94 @@ public final class StateMachine public void AddUploadSize(String token, long uploadSize) { this.uploadSize += uploadSize; - GetState(token).AddUploadSize(uploadSize); + State(token).AddUploadSize(uploadSize); } public void AddWriteSize(String token, long writeSize) { this.writeSize += writeSize; - GetState(token).AddWriteSize(writeSize); + State(token).AddWriteSize(writeSize); } public void AddNumUpload(String token, long numUpload) { this.numUpload += numUpload; - GetState(token).AddNumUpload(numUpload); + State(token).AddNumUpload(numUpload); } public void AddNumWrite(String token, long numWrite) { this.numWrite += numWrite; - GetState(token).AddNumWrite(numWrite); + State(token).AddNumWrite(numWrite); } public void AddNumError(String token, long numError) { this.numError += numError; - GetState(token).AddNumError(numError); + State(token).AddNumError(numError); } public void SetLastUploadTime(String token, Date lastUploadTime) { this.lastUploadTime = lastUploadTime; - GetState(token).SetLastUploadTime(lastUploadTime); + State(token).SetLastUploadTime(lastUploadTime); } public void SetLastWriteTime(String token, Date lastWriteTime) { this.lastWriteTime = lastWriteTime; - GetState(token).SetLastWriteTime(lastWriteTime); + State(token).SetLastWriteTime(lastWriteTime); } public void SetLastErrorTime(String token, Date lastErrorTime) { this.lastErrorTime = lastErrorTime; - GetState(token).SetLastErrorTime(lastErrorTime); + State(token).SetLastErrorTime(lastErrorTime); } public void SetMaxUploadSize(String token, long maxUploadSize) { if(maxUploadSize > this.maxUploadSize) this.maxUploadSize = maxUploadSize; - GetState(token).SetMaxUploadSize(maxUploadSize); + State(token).SetMaxUploadSize(maxUploadSize); } public void SetMaxWriteSize(String token, long maxWriteSize) { if(maxWriteSize > this.maxWriteSize) this.maxWriteSize += maxWriteSize; - GetState(token).SetMaxWriteSize(maxWriteSize); + State(token).SetMaxWriteSize(maxWriteSize); } public void SetMaxHandleDuration(String token, long maxHandleDuration) { if(maxHandleDuration > this.maxHandleDuration) this.maxHandleDuration += maxHandleDuration; - GetState(token).SetMaxHandleDuration(maxHandleDuration); + State(token).SetMaxHandleDuration(maxHandleDuration); } public void IncrUploadSize(String token, long uploadSize) { this.uploadSize += uploadSize; - GetState(token).AddUploadSize(uploadSize); + State(token).AddUploadSize(uploadSize); } public void IncrNumUpload(String token) { this.numUpload++; - GetState(token).AddNumUpload(1); + State(token).AddNumUpload(1); } public void IncrNumWrite(String token) { this.numWrite++; - GetState(token).AddNumWrite(1); + State(token).AddNumWrite(1); } public void IncrNumError(String token) { this.numError++; - GetState(token).AddNumError(1); + State(token).AddNumError(1); } public void GC() @@ -166,5 +174,81 @@ public final class StateMachine lastGCTime = new Date(); } + public ProjectState GetState(String token) + { + return stateMap.get(token); + } + + public List GetStates() + { + return new ArrayList<>(stateMap.values()); + } + + public void FromMap(Map parms) + { + CopyOptions options = CopyOptions.create(); + options.setIgnoreProperties("startTime"); + options.setIgnoreError(true); + BeanUtil.fillBeanWithMap(parms, this, options); + Object obj = parms.get("stateMap"); + if(obj instanceof Map) + { + Map m = (Map) obj; + m.forEach((k, v) -> { + ProjectState s = ProjectState.FromMap((Map)v); + stateMap.put(s.token, s); + }); + } + } + + public Map ToMap() + { + Map map = BeanUtil.beanToMap(this); + map.put("stateMap", stateMap); + return map; + } + + public void Finish() + { + lastEndTime = new Date(); + } + + public String ToString(String split) + { + StringBuilder sb = new StringBuilder(); + sb.append("上传文件大小: ").append(uploadSize); + sb.append(split); + sb.append("写入文件大小: ").append(writeSize); + sb.append(split); + sb.append("上传文件数: ").append(numUpload); + sb.append(split); + sb.append("写入文件数: ").append(numWrite); + sb.append(split); + sb.append("上传错误数: ").append(numError); + sb.append(split); + sb.append("最近上传时间: ").append(lastUploadTime); + sb.append(split); + sb.append("最近写入时间: ").append(lastWriteTime); + sb.append(split); + sb.append("最近错误时间: ").append(lastErrorTime); + sb.append(split); + sb.append("最大上传文件大小: ").append(maxUploadSize); + sb.append(split); + sb.append("最大写入文件大小: ").append(maxWriteSize); + sb.append(split); + sb.append("最大处理间隔: ").append(maxHandleDuration); + sb.append(split); + sb.append("GC次数: ").append(gcCount); + sb.append(split); + sb.append("最近GC时间: ").append(lastGCTime); + sb.append(split); + sb.append("本次开始时间: ").append(startTime); + sb.append(split); + sb.append("上次结束时间: ").append(lastEndTime); + sb.append(split); + sb.append("记录数: ").append(stateMap.size()); + return sb.toString(); + } + private StateMachine() {} } diff --git a/ruoyi-file/src/main/java/com/ruoyi/file/service/StateService.java b/ruoyi-file/src/main/java/com/ruoyi/file/service/StateService.java new file mode 100644 index 0000000..3d0540a --- /dev/null +++ b/ruoyi-file/src/main/java/com/ruoyi/file/service/StateService.java @@ -0,0 +1,90 @@ +package com.ruoyi.file.service; + +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.file.object.ProjectState; +import com.ruoyi.file.object.StateMachine; +import com.ruoyi.file.utils.ProjectUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Service +@Slf4j +public class StateService +{ + private static final String STATE_MACHINE_KEY = "STATE_MACHINE"; + + public ProjectState detail(String token) + { + return StateMachine.INSTANCE.GetState(token); + } + + public List list() + { + return StateMachine.INSTANCE.GetStates(); + } + + public long gc() + { + StateMachine.INSTANCE.GC(); + return StateMachine.INSTANCE.gcCount; + } + + public StateMachine stateMachine() + { + return StateMachine.INSTANCE; + } + + public void dump() + { + destroy(); + } + + @PostConstruct + public void init() + { + try + { + RedisCache redisCache = SpringUtils.getBean(RedisCache.class); + String stateMachine = redisCache.getCacheObject(STATE_MACHINE_KEY); + if(StringUtils.isNotEmpty(stateMachine)) + { + Map map = JSON.parseObject(stateMachine, Map.class); + StateMachine.INSTANCE.FromMap(map); + log.info("读取持久化状态机信息: "); + log.info(StateMachine.INSTANCE.ToString("; ")); + } + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + @PreDestroy + public void destroy() + { + try + { + StateMachine.INSTANCE.Finish(); + Map map = StateMachine.INSTANCE.ToMap(); + String json = JSON.toJSONString(map); + RedisCache redisCache = SpringUtils.getBean(RedisCache.class); + redisCache.setCacheObject(STATE_MACHINE_KEY, json); + log.info("备份持久化状态机信息: "); + log.info(StateMachine.INSTANCE.ToString("; ")); + } + catch(Exception e) + { + e.printStackTrace(); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index e76c461..3a32325 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -128,6 +128,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter .antMatchers("/open/**").permitAll() // 静态资源 .antMatchers("/static/**").permitAll() + .antMatchers("/adminLogout").permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java index 428db32..398e6a6 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java @@ -229,7 +229,8 @@ public class SysUserServiceImpl implements ISysUserService { if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) { - throw new ServiceException("不允许操作超级管理员用户"); + if(!SecurityUtils.getLoginUser().getUser().isAdmin()) + throw new ServiceException("不允许操作超级管理员用户"); } } diff --git a/ruoyi-ui/src/api/file/state.js b/ruoyi-ui/src/api/file/state.js new file mode 100644 index 0000000..05b93ea --- /dev/null +++ b/ruoyi-ui/src/api/file/state.js @@ -0,0 +1,36 @@ +import request from '@/utils/request' + +export function stateList() { + return request({ + url: '/state/list', + method: 'get', + }) +} + +export function stateDetail(token) { + return request({ + url: '/state/detail/' + token, + method: 'get', + }) +} + +export function gc() { + return request({ + url: '/state/gc', + method: 'post', + }) +} + +export function stateMachine() { + return request({ + url: '/state/machine', + method: 'get', + }) +} + +export function dump() { + return request({ + url: '/state/dump', + method: 'post', + }) +} diff --git a/ruoyi-ui/src/views/file/project/index.vue b/ruoyi-ui/src/views/file/project/index.vue index 6f09ec6..a8e9732 100644 --- a/ruoyi-ui/src/views/file/project/index.vue +++ b/ruoyi-ui/src/views/file/project/index.vue @@ -64,6 +64,7 @@ + 状态 删除 @@ -136,6 +137,11 @@ + + + + + @@ -143,11 +149,15 @@ import {listProject, getProject, delProject, addProject, updateProject, switchDisable} from "@/api/file/project"; import DeptSelect from "@/components/common/DeptSelect.vue"; import ServerFileChooser from "@/components/ServerFileChooser.vue"; +import FullscreenDialog from "@/components/FullscreenDialog.vue"; +import ProjectState from "@/views/file/state/ProjectState.vue"; export default { name: "Project", components: { + ProjectState, + FullscreenDialog, ServerFileChooser, DeptSelect }, @@ -225,6 +235,8 @@ export default { // 对话框显示只读的详情 viewOpen: false, fileOpen: false, + stateOpen: false, + projectToken: null, }; }, created() { @@ -248,6 +260,7 @@ export default { this.open = false; this.viewOpen = false; this.fileOpen = false; + this.stateOpen = false; this.reset(); }, // 表单重置 @@ -382,6 +395,14 @@ export default { this.$modal.msgSuccess(what + "成功"); }).catch(() => {}); }, + viewProjectState(row) { + this.projectToken = row.token; + this.stateOpen = true; + }, + closeProjectState() { + this.stateOpen = false; + this.projectToken = null; + }, }, }; diff --git a/ruoyi-ui/src/views/file/state/ProjectState.vue b/ruoyi-ui/src/views/file/state/ProjectState.vue new file mode 100644 index 0000000..7645a77 --- /dev/null +++ b/ruoyi-ui/src/views/file/state/ProjectState.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/ruoyi-ui/src/views/file/state/index.vue b/ruoyi-ui/src/views/file/state/index.vue new file mode 100644 index 0000000..fc907f5 --- /dev/null +++ b/ruoyi-ui/src/views/file/state/index.vue @@ -0,0 +1,203 @@ + + + diff --git a/ruoyi-ui/src/views/system/user/index.vue b/ruoyi-ui/src/views/system/user/index.vue index e494a60..cc7f79b 100644 --- a/ruoyi-ui/src/views/system/user/index.vue +++ b/ruoyi-ui/src/views/system/user/index.vue @@ -200,7 +200,7 @@ width="120" class-name="small-padding fixed-width" > -