| @@ -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<ProjectState> 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(); | |||
| } | |||
| } | |||
| @@ -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: | |||
| @@ -1,7 +1,7 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <configuration> | |||
| <!-- 日志存放路径 --> | |||
| <property name="log.path" value="/mnt/data/logs/rongxin.gxzb.api" /> | |||
| <property name="log.path" value="D:/NsgkSoft/files/nsgk/logs" /> | |||
| <!-- 日志输出格式 --> | |||
| <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /> | |||
| @@ -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) | |||
| @@ -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<String, ProjectState> projectStateMap = new LinkedHashMap<>(); | |||
| public static final StateMachine INSTANCE = new StateMachine(); | |||
| public Date startTime = new Date(); // 本次开始时间 | |||
| public Date lastEndTime; // 上次结束时间 | |||
| private final Map<String, ProjectState> 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<ProjectState> 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<String, Object> 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() {} | |||
| } | |||
| @@ -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<ProjectState> 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(); | |||
| } | |||
| } | |||
| } | |||
| @@ -128,6 +128,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter | |||
| .antMatchers("/open/**").permitAll() | |||
| // 静态资源 | |||
| .antMatchers("/static/**").permitAll() | |||
| .antMatchers("/adminLogout").permitAll() | |||
| // 除上面外的所有请求全部需要鉴权认证 | |||
| .anyRequest().authenticated() | |||
| @@ -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("不允许操作超级管理员用户"); | |||
| } | |||
| } | |||
| @@ -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', | |||
| }) | |||
| } | |||
| @@ -64,6 +64,7 @@ | |||
| <el-dropdown :split-button="false" type="text" class="dropdown el-dropdown-link"> | |||
| <span style="cursor: pointer; font-size: 10px; color:#1890FF"><!--更多--><i class="el-icon-arrow-down el-icon--right"></i></span> | |||
| <el-dropdown-menu slot="dropdown" style="padding: 10px"> | |||
| <el-button size="mini" type="text" icon="el-icon-view" @click="viewProjectState(scope.row)" v-hasPermi="['admin:state:detail']">状态</el-button> | |||
| <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['file:project:remove']">删除</el-button> | |||
| </el-dropdown-menu> | |||
| </el-dropdown> | |||
| @@ -136,6 +137,11 @@ | |||
| <el-dialog title="选择目录" :visible.sync="fileOpen" width="600px" append-to-body> | |||
| <ServerFileChooser v-model="form.diskPath" :file-type="2" @selected="closeFileChooser"/> | |||
| </el-dialog> | |||
| <FullscreenDialog title="项目状态" :visible.sync="stateOpen" @close="closeProjectState"> | |||
| <ProjectState :token="projectToken"/> | |||
| </FullscreenDialog> | |||
| </div> | |||
| </template> | |||
| @@ -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; | |||
| }, | |||
| }, | |||
| }; | |||
| </script> | |||
| @@ -0,0 +1,65 @@ | |||
| <template> | |||
| <Descriptions> | |||
| <el-descriptions-item label="上传字节数">{{state.uploadSize}}</el-descriptions-item> | |||
| <el-descriptions-item label="写入字节数">{{state.writeSize}}</el-descriptions-item> | |||
| <el-descriptions-item label="上传文件数">{{state.numUpload}}</el-descriptions-item> | |||
| <el-descriptions-item label="写入文件数">{{state.numWrite}}</el-descriptions-item> | |||
| <el-descriptions-item label="处理错误数">{{state.numError}}</el-descriptions-item> | |||
| <el-descriptions-item label="最近上传时间">{{state.lastUploadTime}}</el-descriptions-item> | |||
| <el-descriptions-item label="最近写入时间">{{state.lastWriteTime}}</el-descriptions-item> | |||
| <el-descriptions-item label="最近错误时间">{{state.lastErrorTime}}</el-descriptions-item> | |||
| <el-descriptions-item label="最大上传字节数">{{state.maxUploadSize}}</el-descriptions-item> | |||
| <el-descriptions-item label="最大写入字节数">{{state.maxWriteSize}}</el-descriptions-item> | |||
| <el-descriptions-item label="最长处理时间">{{state.maxHandleDuration}}</el-descriptions-item> | |||
| </Descriptions> | |||
| </template> | |||
| <script> | |||
| import Descriptions from "@/components/common/Descriptions.vue"; | |||
| import {stateDetail} from "@/api/file/state"; | |||
| export default { | |||
| name: 'ProjectState', | |||
| components: {Descriptions}, | |||
| props: { | |||
| token: { | |||
| type: String, | |||
| } | |||
| }, | |||
| data() { | |||
| return { | |||
| state: {}, | |||
| }; | |||
| }, | |||
| created() { | |||
| this.getStateDetail(); | |||
| }, | |||
| methods: { | |||
| getStateDetail() { | |||
| this.state = {}; | |||
| if(!this.token) | |||
| return; | |||
| stateDetail(this.token).then((resp) => { | |||
| if(resp.data) | |||
| { | |||
| this.state = resp.data; | |||
| } | |||
| else | |||
| { | |||
| this.$message.error('暂无状态'); | |||
| } | |||
| }); | |||
| }, | |||
| }, | |||
| watch: { | |||
| token(newVal) { | |||
| this.getStateDetail(); | |||
| }, | |||
| }, | |||
| } | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| </style> | |||
| @@ -0,0 +1,203 @@ | |||
| <template> | |||
| <div class="app-container"> | |||
| <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> | |||
| <el-form-item> | |||
| <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | |||
| <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | |||
| </el-form-item> | |||
| </el-form> | |||
| <el-row :gutter="10" class="mb8"> | |||
| <el-col :span="1.5"> | |||
| <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="gc" v-hasPermi="['admin:state:gc']">GC</el-button> | |||
| </el-col> | |||
| <el-col :span="1.5"> | |||
| <el-button type="primary" plain icon="el-icon-download" size="mini" @click="dump" v-hasPermi="['admin:state:gc']">Dump</el-button> | |||
| </el-col> | |||
| <right-toolbar :showSearch.sync="showSearch" @queryTable="refresh"></right-toolbar> | |||
| </el-row> | |||
| <Descriptions> | |||
| <el-descriptions-item label="上传字节数">{{state.uploadSize}}</el-descriptions-item> | |||
| <el-descriptions-item label="写入字节数">{{state.writeSize}}</el-descriptions-item> | |||
| <el-descriptions-item label="上传文件数">{{state.numUpload}}</el-descriptions-item> | |||
| <el-descriptions-item label="写入文件数">{{state.numWrite}}</el-descriptions-item> | |||
| <el-descriptions-item label="处理错误数">{{state.numError}}</el-descriptions-item> | |||
| <el-descriptions-item label="最近上传时间">{{state.lastUploadTime}}</el-descriptions-item> | |||
| <el-descriptions-item label="最近写入时间">{{state.lastWriteTime}}</el-descriptions-item> | |||
| <el-descriptions-item label="最近错误时间">{{state.lastErrorTime}}</el-descriptions-item> | |||
| <el-descriptions-item label="最大上传字节数">{{state.maxUploadSize}}</el-descriptions-item> | |||
| <el-descriptions-item label="最大写入字节数">{{state.maxWriteSize}}</el-descriptions-item> | |||
| <el-descriptions-item label="最长处理时间">{{state.maxHandleDuration}}</el-descriptions-item> | |||
| <el-descriptions-item label="GC次数">{{state.gcCount}}</el-descriptions-item> | |||
| <el-descriptions-item label="最近GC时间">{{state.lastGCTime}}</el-descriptions-item> | |||
| <el-descriptions-item label="本次开始时间">{{state.startTime}}</el-descriptions-item> | |||
| <el-descriptions-item label="上次结束时间">{{state.lastEndTime}}</el-descriptions-item> | |||
| <el-descriptions-item label="记录数">{{total}}</el-descriptions-item> | |||
| </Descriptions> | |||
| <el-table v-loading="loading" :data="stateList" @selection-change="handleSelectionChange"> | |||
| <!--<el-table-column type="selection" width="55" align="center" />--> | |||
| <el-table-column type="index" label="序号" width="50" align="center" /> | |||
| <!--<el-table-column label="ID" align="center" prop="id" min-width="60" />--> | |||
| <el-table-column label="标识" align="center" prop="token" /> | |||
| <el-table-column label="上传字节数" align="center" prop="uploadSize" /> | |||
| <el-table-column label="上传文件数" align="center" prop="numUpload" /> | |||
| <el-table-column label="处理错误数" align="center" prop="numError" /> | |||
| <el-table-column label="最近上传时间" align="center" prop="lastUploadTime" /> | |||
| <el-table-column label="最大上传字节数" align="center" prop="maxUploadSize" /> | |||
| <el-table-column label="最长处理时间" align="center" prop="maxHandleDuration" /> | |||
| <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | |||
| <template slot-scope="scope"> | |||
| <el-button size="mini" type="text" icon="el-icon-view" @click="handleLook(scope.row)" v-hasPermi="['admin:state:detail']">查看</el-button> | |||
| </template> | |||
| </el-table-column> | |||
| </el-table> | |||
| <FullscreenDialog title="项目状态" :visible.sync="viewOpen" @close="closeProjectState"> | |||
| <ProjectState :token="projectToken"/> | |||
| </FullscreenDialog> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| 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"; | |||
| import {stateList, gc, stateMachine, dump} from "@/api/file/state"; | |||
| import Descriptions from "@/components/common/Descriptions.vue"; | |||
| export default { | |||
| name: "State", | |||
| components: { | |||
| Descriptions, | |||
| ProjectState, | |||
| FullscreenDialog, | |||
| ServerFileChooser, | |||
| DeptSelect | |||
| }, | |||
| dicts: ['sys_bool'], | |||
| data() { | |||
| return { | |||
| // 遮罩层 | |||
| loading: false, | |||
| // 遮罩按钮新增点击状态 | |||
| diglogStatus: true, | |||
| // 导出遮罩层 | |||
| exportLoading: false, | |||
| // 选中数组 | |||
| ids: [], | |||
| // 非单个禁用 | |||
| single: true, | |||
| // 非多个禁用 | |||
| multiple: true, | |||
| // 默认隐藏搜索条件 | |||
| showSearch: false, | |||
| // 总条数 | |||
| total: 0, | |||
| // 项目表格数据 | |||
| stateList: [], | |||
| // 弹出层标题 | |||
| title: "", | |||
| // 是否显示弹出层 | |||
| open: false, | |||
| // 是否禁用 字典 sys_bool字典 | |||
| disabledOptions: [], | |||
| // 查询参数 | |||
| queryParams: { | |||
| // 分页 | |||
| pageNum: 1, | |||
| pageSize: 10, | |||
| // 查询排序 | |||
| //orderByColumn: "id", | |||
| //isAsc: "desc", | |||
| // 翻译字典 | |||
| //translate_dict: "1", | |||
| }, | |||
| // 表单参数 | |||
| form: {}, | |||
| // 详情组件列数 | |||
| descColumn: 2, | |||
| // 详情组件Label所占百分比, 最大={100 / descColumn}. 内容所占百分比={100 / descColumn - descLabelWidth} | |||
| descLabelWidth: 15, | |||
| // 对话框显示只读的详情 | |||
| viewOpen: false, | |||
| projectToken: null, | |||
| state: {}, | |||
| }; | |||
| }, | |||
| created() { | |||
| this.getStateMachine(); | |||
| this.getList(); | |||
| }, | |||
| methods: { | |||
| /** 查询项目列表 */ | |||
| getList() { | |||
| this.loading = true; | |||
| stateList(this.queryParams).then(response => { | |||
| this.stateList = response.data; | |||
| this.total = this.stateList.length; | |||
| }).finally(() => this.loading = false ); | |||
| }, | |||
| // 取消按钮 | |||
| cancel() { | |||
| this.open = false; | |||
| this.viewOpen = false; | |||
| this.reset(); | |||
| }, | |||
| // 表单重置 | |||
| reset() { | |||
| this.form = { | |||
| }; | |||
| }, | |||
| refresh() { | |||
| this.getStateMachine(); | |||
| this.getList(); | |||
| }, | |||
| /** 搜索按钮操作 */ | |||
| handleQuery() { | |||
| this.queryParams.pageNum = 1; | |||
| this.getList(); | |||
| }, | |||
| /** 重置按钮操作 */ | |||
| resetQuery() { | |||
| this.resetForm("queryForm"); | |||
| this.handleQuery(); | |||
| }, | |||
| // 多选框选中数据 | |||
| handleSelectionChange(selection) { | |||
| this.ids = selection.map(item => item.id) | |||
| this.single = selection.length!==1 | |||
| this.multiple = !selection.length | |||
| }, | |||
| /** 查看按钮操作 */ | |||
| handleLook(row) { | |||
| this.projectToken = row.token; | |||
| this.viewOpen = true; | |||
| }, | |||
| closeProjectState() { | |||
| this.viewOpen = false; | |||
| this.projectToken = null; | |||
| }, | |||
| gc() { | |||
| gc().then((resp) => { | |||
| this.$message.success('成功'); | |||
| }); | |||
| }, | |||
| getStateMachine() { | |||
| this.state = {}; | |||
| stateMachine().then((resp) => { | |||
| this.state = resp.data; | |||
| }); | |||
| }, | |||
| dump() { | |||
| dump().then((resp) => { | |||
| this.$message.success('成功'); | |||
| }); | |||
| }, | |||
| }, | |||
| }; | |||
| </script> | |||
| @@ -200,7 +200,7 @@ | |||
| width="120" | |||
| class-name="small-padding fixed-width" | |||
| > | |||
| <template slot-scope="scope" v-if="scope.row.userId !== 1"> | |||
| <template slot-scope="scope" v-if="scope.row.userId !== 1 || isAdmin"> | |||
| <el-button | |||
| size="mini" | |||
| type="text" | |||
| @@ -402,6 +402,7 @@ import { getToken } from "@/utils/auth"; | |||
| import Treeselect from "@riophae/vue-treeselect"; | |||
| import "@riophae/vue-treeselect/dist/vue-treeselect.css"; | |||
| import {listRole} from "@/api/system/role"; | |||
| import {user_is_admin} from "@/utils/user"; | |||
| export default { | |||
| name: "User", | |||
| @@ -738,10 +739,8 @@ export default { | |||
| }, | |||
| }, | |||
| computed: { | |||
| allowRoleOptions() { | |||
| if(!this.roleOptions) | |||
| return []; | |||
| return this.roleOptions.filter((x) => x.roleKey !== 'farmer'); | |||
| isAdmin() { | |||
| return user_is_admin(); | |||
| }, | |||
| }, | |||
| }; | |||