@@ -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(); | |||||
}, | }, | ||||
}, | }, | ||||
}; | }; | ||||