| @@ -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: | druid: | ||||
| # 主库数据源 | # 主库数据源 | ||||
| master: | 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: | slave: | ||||
| # 从数据源开关/默认关闭 | # 从数据源开关/默认关闭 | ||||
| @@ -71,7 +71,7 @@ spring: | |||||
| # 地址 | # 地址 | ||||
| host: rongxin-redis-m-svc.prod | host: rongxin-redis-m-svc.prod | ||||
| # 端口,默认为6379 | # 端口,默认为6379 | ||||
| port: 6279 | |||||
| port: 6379 | |||||
| # 数据库索引 | # 数据库索引 | ||||
| database: 0 | database: 0 | ||||
| # 密码 | # 密码 | ||||
| @@ -94,8 +94,7 @@ spring: | |||||
| # 生产环境配置 | # 生产环境配置 | ||||
| server: | server: | ||||
| # 服务器的HTTP端口 | # 服务器的HTTP端口 | ||||
| port: ${NODE_PORT} | |||||
| address: ${NODE_HOST} | |||||
| port: 8077 | |||||
| aliyun: | aliyun: | ||||
| oss: | oss: | ||||
| @@ -1,7 +1,7 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||
| <configuration> | <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" /> | <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; | 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.Date; | ||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| public final class ProjectState | public final class ProjectState | ||||
| { | { | ||||
| @@ -123,6 +128,14 @@ public final class ProjectState | |||||
| return sb.toString(); | 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() {} | ||||
| public ProjectState(String token) | public ProjectState(String token) | ||||
| @@ -1,7 +1,12 @@ | |||||
| package com.ruoyi.file.object; | 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.Date; | ||||
| import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||
| import java.util.List; | |||||
| import java.util.Map; | import java.util.Map; | ||||
| public final class StateMachine | public final class StateMachine | ||||
| @@ -21,16 +26,19 @@ public final class StateMachine | |||||
| public long gcCount; // GC次数 | public long gcCount; // GC次数 | ||||
| public Date lastGCTime; // 最近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) | public synchronized void AddState(ProjectState state) | ||||
| { | { | ||||
| ProjectState projectState = projectStateMap.get(state.token); | |||||
| ProjectState projectState = stateMap.get(state.token); | |||||
| if(null != projectState) | if(null != projectState) | ||||
| projectState.Sum(state); | projectState.Sum(state); | ||||
| else | else | ||||
| projectStateMap.put(state.token, state); | |||||
| stateMap.put(state.token, state); | |||||
| Sum(state); | Sum(state); | ||||
| } | } | ||||
| @@ -55,13 +63,13 @@ public final class StateMachine | |||||
| this.maxHandleDuration += state.maxHandleDuration; | 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) | if(null == state) | ||||
| { | { | ||||
| state = new ProjectState(token); | state = new ProjectState(token); | ||||
| projectStateMap.put(token, state); | |||||
| stateMap.put(token, state); | |||||
| } | } | ||||
| return state; | return state; | ||||
| } | } | ||||
| @@ -69,94 +77,94 @@ public final class StateMachine | |||||
| public void AddUploadSize(String token, long uploadSize) | public void AddUploadSize(String token, long uploadSize) | ||||
| { | { | ||||
| this.uploadSize += uploadSize; | this.uploadSize += uploadSize; | ||||
| GetState(token).AddUploadSize(uploadSize); | |||||
| State(token).AddUploadSize(uploadSize); | |||||
| } | } | ||||
| public void AddWriteSize(String token, long writeSize) | public void AddWriteSize(String token, long writeSize) | ||||
| { | { | ||||
| this.writeSize += writeSize; | this.writeSize += writeSize; | ||||
| GetState(token).AddWriteSize(writeSize); | |||||
| State(token).AddWriteSize(writeSize); | |||||
| } | } | ||||
| public void AddNumUpload(String token, long numUpload) | public void AddNumUpload(String token, long numUpload) | ||||
| { | { | ||||
| this.numUpload += numUpload; | this.numUpload += numUpload; | ||||
| GetState(token).AddNumUpload(numUpload); | |||||
| State(token).AddNumUpload(numUpload); | |||||
| } | } | ||||
| public void AddNumWrite(String token, long numWrite) | public void AddNumWrite(String token, long numWrite) | ||||
| { | { | ||||
| this.numWrite += numWrite; | this.numWrite += numWrite; | ||||
| GetState(token).AddNumWrite(numWrite); | |||||
| State(token).AddNumWrite(numWrite); | |||||
| } | } | ||||
| public void AddNumError(String token, long numError) | public void AddNumError(String token, long numError) | ||||
| { | { | ||||
| this.numError += numError; | this.numError += numError; | ||||
| GetState(token).AddNumError(numError); | |||||
| State(token).AddNumError(numError); | |||||
| } | } | ||||
| public void SetLastUploadTime(String token, Date lastUploadTime) | public void SetLastUploadTime(String token, Date lastUploadTime) | ||||
| { | { | ||||
| this.lastUploadTime = lastUploadTime; | this.lastUploadTime = lastUploadTime; | ||||
| GetState(token).SetLastUploadTime(lastUploadTime); | |||||
| State(token).SetLastUploadTime(lastUploadTime); | |||||
| } | } | ||||
| public void SetLastWriteTime(String token, Date lastWriteTime) | public void SetLastWriteTime(String token, Date lastWriteTime) | ||||
| { | { | ||||
| this.lastWriteTime = lastWriteTime; | this.lastWriteTime = lastWriteTime; | ||||
| GetState(token).SetLastWriteTime(lastWriteTime); | |||||
| State(token).SetLastWriteTime(lastWriteTime); | |||||
| } | } | ||||
| public void SetLastErrorTime(String token, Date lastErrorTime) | public void SetLastErrorTime(String token, Date lastErrorTime) | ||||
| { | { | ||||
| this.lastErrorTime = lastErrorTime; | this.lastErrorTime = lastErrorTime; | ||||
| GetState(token).SetLastErrorTime(lastErrorTime); | |||||
| State(token).SetLastErrorTime(lastErrorTime); | |||||
| } | } | ||||
| public void SetMaxUploadSize(String token, long maxUploadSize) | public void SetMaxUploadSize(String token, long maxUploadSize) | ||||
| { | { | ||||
| if(maxUploadSize > this.maxUploadSize) | if(maxUploadSize > this.maxUploadSize) | ||||
| this.maxUploadSize = maxUploadSize; | this.maxUploadSize = maxUploadSize; | ||||
| GetState(token).SetMaxUploadSize(maxUploadSize); | |||||
| State(token).SetMaxUploadSize(maxUploadSize); | |||||
| } | } | ||||
| public void SetMaxWriteSize(String token, long maxWriteSize) | public void SetMaxWriteSize(String token, long maxWriteSize) | ||||
| { | { | ||||
| if(maxWriteSize > this.maxWriteSize) | if(maxWriteSize > this.maxWriteSize) | ||||
| this.maxWriteSize += maxWriteSize; | this.maxWriteSize += maxWriteSize; | ||||
| GetState(token).SetMaxWriteSize(maxWriteSize); | |||||
| State(token).SetMaxWriteSize(maxWriteSize); | |||||
| } | } | ||||
| public void SetMaxHandleDuration(String token, long maxHandleDuration) | public void SetMaxHandleDuration(String token, long maxHandleDuration) | ||||
| { | { | ||||
| if(maxHandleDuration > this.maxHandleDuration) | if(maxHandleDuration > this.maxHandleDuration) | ||||
| this.maxHandleDuration += maxHandleDuration; | this.maxHandleDuration += maxHandleDuration; | ||||
| GetState(token).SetMaxHandleDuration(maxHandleDuration); | |||||
| State(token).SetMaxHandleDuration(maxHandleDuration); | |||||
| } | } | ||||
| public void IncrUploadSize(String token, long uploadSize) | public void IncrUploadSize(String token, long uploadSize) | ||||
| { | { | ||||
| this.uploadSize += uploadSize; | this.uploadSize += uploadSize; | ||||
| GetState(token).AddUploadSize(uploadSize); | |||||
| State(token).AddUploadSize(uploadSize); | |||||
| } | } | ||||
| public void IncrNumUpload(String token) | public void IncrNumUpload(String token) | ||||
| { | { | ||||
| this.numUpload++; | this.numUpload++; | ||||
| GetState(token).AddNumUpload(1); | |||||
| State(token).AddNumUpload(1); | |||||
| } | } | ||||
| public void IncrNumWrite(String token) | public void IncrNumWrite(String token) | ||||
| { | { | ||||
| this.numWrite++; | this.numWrite++; | ||||
| GetState(token).AddNumWrite(1); | |||||
| State(token).AddNumWrite(1); | |||||
| } | } | ||||
| public void IncrNumError(String token) | public void IncrNumError(String token) | ||||
| { | { | ||||
| this.numError++; | this.numError++; | ||||
| GetState(token).AddNumError(1); | |||||
| State(token).AddNumError(1); | |||||
| } | } | ||||
| public void GC() | public void GC() | ||||
| @@ -166,5 +174,81 @@ public final class StateMachine | |||||
| lastGCTime = new Date(); | 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() {} | 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("/open/**").permitAll() | ||||
| // 静态资源 | // 静态资源 | ||||
| .antMatchers("/static/**").permitAll() | .antMatchers("/static/**").permitAll() | ||||
| .antMatchers("/adminLogout").permitAll() | |||||
| // 除上面外的所有请求全部需要鉴权认证 | // 除上面外的所有请求全部需要鉴权认证 | ||||
| .anyRequest().authenticated() | .anyRequest().authenticated() | ||||
| @@ -229,7 +229,8 @@ public class SysUserServiceImpl implements ISysUserService | |||||
| { | { | ||||
| if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) | 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"> | <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> | <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-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-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-menu> | ||||
| </el-dropdown> | </el-dropdown> | ||||
| @@ -136,6 +137,11 @@ | |||||
| <el-dialog title="选择目录" :visible.sync="fileOpen" width="600px" append-to-body> | <el-dialog title="选择目录" :visible.sync="fileOpen" width="600px" append-to-body> | ||||
| <ServerFileChooser v-model="form.diskPath" :file-type="2" @selected="closeFileChooser"/> | <ServerFileChooser v-model="form.diskPath" :file-type="2" @selected="closeFileChooser"/> | ||||
| </el-dialog> | </el-dialog> | ||||
| <FullscreenDialog title="项目状态" :visible.sync="stateOpen" @close="closeProjectState"> | |||||
| <ProjectState :token="projectToken"/> | |||||
| </FullscreenDialog> | |||||
| </div> | </div> | ||||
| </template> | </template> | ||||
| @@ -143,11 +149,15 @@ | |||||
| import {listProject, getProject, delProject, addProject, updateProject, switchDisable} from "@/api/file/project"; | import {listProject, getProject, delProject, addProject, updateProject, switchDisable} from "@/api/file/project"; | ||||
| import DeptSelect from "@/components/common/DeptSelect.vue"; | import DeptSelect from "@/components/common/DeptSelect.vue"; | ||||
| import ServerFileChooser from "@/components/ServerFileChooser.vue"; | import ServerFileChooser from "@/components/ServerFileChooser.vue"; | ||||
| import FullscreenDialog from "@/components/FullscreenDialog.vue"; | |||||
| import ProjectState from "@/views/file/state/ProjectState.vue"; | |||||
| export default { | export default { | ||||
| name: "Project", | name: "Project", | ||||
| components: { | components: { | ||||
| ProjectState, | |||||
| FullscreenDialog, | |||||
| ServerFileChooser, | ServerFileChooser, | ||||
| DeptSelect | DeptSelect | ||||
| }, | }, | ||||
| @@ -225,6 +235,8 @@ export default { | |||||
| // 对话框显示只读的详情 | // 对话框显示只读的详情 | ||||
| viewOpen: false, | viewOpen: false, | ||||
| fileOpen: false, | fileOpen: false, | ||||
| stateOpen: false, | |||||
| projectToken: null, | |||||
| }; | }; | ||||
| }, | }, | ||||
| created() { | created() { | ||||
| @@ -248,6 +260,7 @@ export default { | |||||
| this.open = false; | this.open = false; | ||||
| this.viewOpen = false; | this.viewOpen = false; | ||||
| this.fileOpen = false; | this.fileOpen = false; | ||||
| this.stateOpen = false; | |||||
| this.reset(); | this.reset(); | ||||
| }, | }, | ||||
| // 表单重置 | // 表单重置 | ||||
| @@ -382,6 +395,14 @@ export default { | |||||
| this.$modal.msgSuccess(what + "成功"); | this.$modal.msgSuccess(what + "成功"); | ||||
| }).catch(() => {}); | }).catch(() => {}); | ||||
| }, | }, | ||||
| viewProjectState(row) { | |||||
| this.projectToken = row.token; | |||||
| this.stateOpen = true; | |||||
| }, | |||||
| closeProjectState() { | |||||
| this.stateOpen = false; | |||||
| this.projectToken = null; | |||||
| }, | |||||
| }, | }, | ||||
| }; | }; | ||||
| </script> | </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" | width="120" | ||||
| class-name="small-padding fixed-width" | 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 | <el-button | ||||
| size="mini" | size="mini" | ||||
| type="text" | type="text" | ||||
| @@ -402,6 +402,7 @@ import { getToken } from "@/utils/auth"; | |||||
| import Treeselect from "@riophae/vue-treeselect"; | import Treeselect from "@riophae/vue-treeselect"; | ||||
| import "@riophae/vue-treeselect/dist/vue-treeselect.css"; | import "@riophae/vue-treeselect/dist/vue-treeselect.css"; | ||||
| import {listRole} from "@/api/system/role"; | import {listRole} from "@/api/system/role"; | ||||
| import {user_is_admin} from "@/utils/user"; | |||||
| export default { | export default { | ||||
| name: "User", | name: "User", | ||||
| @@ -738,10 +739,8 @@ export default { | |||||
| }, | }, | ||||
| }, | }, | ||||
| computed: { | computed: { | ||||
| allowRoleOptions() { | |||||
| if(!this.roleOptions) | |||||
| return []; | |||||
| return this.roleOptions.filter((x) => x.roleKey !== 'farmer'); | |||||
| isAdmin() { | |||||
| return user_is_admin(); | |||||
| }, | }, | ||||
| }, | }, | ||||
| }; | }; | ||||