<template>
<div class="verinfo">
<h4>{{ $t("version_management.version") }}:{{verInfo.version}}</h4>
<div class="btn_wrap">
<!-- 备份列表 -->
<span class="backup_title">{{ $t("version_management.backuplist") }}</span>
<div class="backup_btn">
<el-button
type="primary"
size="small"
class="toBackUp"
@click="toOpenDialog(null,$event)"
>
{{ $t("common.backup") }}
</el-button>
</div>
</div>
<div class="backuplist">
<el-table :data="backupList" border v-loading="loading">
<!-- 备份时间 -->
<el-table-column
prop="backup_tm"
:label="$t('version_management.backup_tm')"
></el-table-column>
<!-- 备份版本号 -->
<el-table-column
prop="backup_ver"
:label="$t('version_management.backup_ver')"
></el-table-column>
<!-- 操作 -->
<el-table-column
:label="$t('common.operation')"
width="206px"
align="center"
>
<template slot-scope="scope">
<!-- 回退 -->
<el-tooltip
placement="top"
effect="light"
:content="$t('common.rollback')"
class="toRollback"
>
<i
class="iconfont icon-go-back blue"
@click="toOpenDialog(scope.row, $event)"
></i>
</el-tooltip>
<!-- 删除 -->
<el-tooltip
placement="top"
effect="light"
:content="$t('common.delete')"
>
<i
class="iconfont icon-delete red"
@click="toDeleteBackUpConfirm(scope.row)"
></i>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<div class="btn_wrap">
<!-- 版本列表 -->
<span class="backup_title">{{ $t("version_management.versionlist") }}</span>
<div class="backup_btn">
<el-button
type="primary"
size="small"
@click="toUploadForm"
>
{{ $t("common.upload") }}
</el-button>
</div>
</div>
<div class="versionlist">
<el-table :data="versionList" border v-loading="loading">
<!-- 上传时间 -->
<el-table-column
prop="upload_tm"
:label="$t('version_management.upload_tm')"
></el-table-column>
<!-- 文件名称 -->
<el-table-column
prop="file_name"
:label="$t('version_management.file_name')"
></el-table-column>
<!-- 文件MDS摘要 -->
<el-table-column
prop="file_md5"
:label="$t('version_management.file_md5')"
></el-table-column>
<!-- 文件大小 -->
<el-table-column
prop="file_size"
:label="$t('version_management.file_size')"
></el-table-column>
<el-table-column
:label="$t('common.operation')"
width="206px"
align="center"
>
<template slot-scope="scope">
<!-- 升级 -->
<el-tooltip
placement="top"
effect="light"
class="toUpgrade"
:content="$t('common.upgrade')"
>
<i
class="iconfont icon-update blue"
@click="toOpenDialog(scope.row, $event)"
></i>
</el-tooltip>
<!-- 删除 -->
<el-tooltip
placement="top"
effect="light"
:content="$t('common.delete')"
>
<i
class="iconfont icon-delete red"
@click="toDeleteVerConfirm(scope.row)"
></i>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<!-- 升级备份退回弹出框 -->
<el-dialog
:visible.sync="toOpenVisible"
:title="upTitle"
width="20%"
:close-on-click-modal="false"
:close-on-press-escape="false"
action=""
:key="updateKey"
@closed="closeDialog"
>
<div class="upTitle" v-if="upGradeTip">{{upTip}}</div>
<el-progress :percentage="percentage" v-show="upGradeProgress"></el-progress>
<div class="successOrErrorTip" v-if="successOrError">
<i :class="[isSuccess?'el-icon-success':'el-icon-warning',isSuccess?'iconGreen':'iconRed']"></i>
<span class="upTitle">{{resTip}}</span>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="toOpenVisible = false" size="small" v-if="upGradeTip">{{
$t("common.cancel")
}}</el-button>
<el-button type="primary" @click="dotoExecute" size="small" v-if="upGradeTip">{{
$t("common.confirm")
}}</el-button>
</span>
</el-dialog>
<!-- 上传版本弹框 -->
<el-dialog
:title="upTitle"
:visible.sync="uploadVisible"
:close-on-click-modal="false"
:close-on-press-escape="false"
@closed="closeUploadDialog"
:key="updateKey+'upload'"
width="25%">
<div class="upload" v-show="isupload">
<el-input
class="filename"
v-model="filename"
:readonly="true">
<i slot="suffix" class="el-input__icon el-icon-success iconGreen" v-if="successIcon"></i>
<i slot="suffix" class="el-input__icon el-icon-warning iconRed" v-if="errIcon"></i>
</el-input>
<el-upload
name="file"
ref="upload"
:on-change="handleChange"
::limit="1"
:show-file-list="false"
:auto-upload="false"
:action="'/api/srtn-cag-manage/v1/sys/ver/upload/'+devId + '/upload/'"
:before-upload="beforeUpload"
>
<el-button slot="trigger" size="mini" v-if="cancleBtn">{{
$t("version_management.selectfile")
}}</el-button>
</el-upload>
<el-button size="mini" @click="cancelUpload" v-if="!cancleBtn">{{
$t("version_management.cancelUpload")
}}</el-button>
<el-button type="text" v-if="isErrorBtn" @click="clear()">{{
$t("common.delete")
}}</el-button>
</div>
<div class="uploadtip">
<span class="title">{{isExcceedTip}}</span>
<el-progress :percentage="percentage" v-show="upGradeProgress"></el-progress>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="uploadVisible = false" v-if="isShow">{{
$t("common.cancel")
}}</el-button>
<el-button type="primary" @click="submitUpload" v-if="isShow" > {{
$t("common.confirm")
}}</el-button>
</span>
<div class="successOrErrorTip" v-if="successOrError">
<i :class="[isSuccess?'el-icon-success':'el-icon-warning',isSuccess?'iconGreen':'iconRed']"></i>
<span class="upTitle">{{resTip}}</span>
</div>
</el-dialog>
</div>
</template>
<script>
import HTTP from "@/api/httpService";
import CommonUtils from "@/utils/common";
export default {
data() {
return {
devId: "",
devName: "",
row: [],
backupList: [],
versionList: [],
loading: false, // 表格加载
verInfo: {}, // 版本信息
toOpenVisible: false,
upTitle: "",
upTip: "",
upGradeTip: true, // 是否显示确定的tip
upGradeProgress: false,
percentage: 0,
updateKey: 0, // 强制刷新标志
successOrError: false,
resTip: "",
total_space: "",
isSuccess: false,
sign: null, // 升级回退备份标志
uploadVisible: false,
filename: "", //上传文件名字
remain_space: null,
total_space: null,
isExcceedTip: "",
isShow: true,
cancleBtn: true, //取消上传按钮
isErrorBtn: false, //失败时展示删除按钮
actionUrl: "",
upFile: new FormData(),
successIcon: false, // 图标
errIcon: false,
isupload: true
};
},
methods: {
// 初始化
initInfo() {
this.getDevId();
this.getVerInfo();
this.getBackUpList();
this.getVersionList();
},
getDevId() {
let devId = sessionStorage.getItem("devId");
let devName = sessionStorage.getItem("devName");
if (devId) {
this.devId = devId;
this.devName = devName;
}
},
// 获取版本信息
getVerInfo() {
HTTP.request("getApi", {
method: "get",
urlParam: { pathParam: this.devId + "/verinfo/" },
// urlParam: { pathParam: "verinfo/" },
complete: res => {
if (res.code == 0) {
this.verInfo = res.version_info;
} else {
this.$message.error(res.msg);
console.log("QUERY FAILED!BECAUSE => " + res.msg);
}
},
error: data => {
this.$message.error(this.$t("tip_msg.network_error"));
console.log("QUERY FAILED!BECAUSE => " + data);
}
});
},
// 查询备份列表版本列表
getVersionList() {
this.loading = true;
HTTP.request("getApi", {
method: "get",
urlParam: { pathParam: this.devId + "/versionlist/" },
// urlParam: { pathParam: "versionlist/" },
complete: res => {
this.loading = false;
if (res.code == 0) {
this.versionList = res.ver_group;
} else {
this.$message.error(res.msg);
console.log("QUERY FAILED!BECAUSE => " + res.msg);
}
},
error: data => {
this.loading = false;
this.$message.error(this.$t("tip_msg.network_error"));
console.log("QUERY FAILED!BECAUSE => " + data);
}
});
},
getBackUpList() {
this.loading = true;
HTTP.request("getApi", {
method: "get",
urlParam: { pathParam: this.devId + "/backuplist/" },
// urlParam: { pathParam: "backuplist/" },
complete: res => {
this.loading = false;
if (res.code == 0) {
this.backupList = res.backup_group;
} else {
this.$message.error(res.msg);
console.log("QUERY FAILED!BECAUSE => " + res.msg);
}
},
error: data => {
this.loading = false;
this.$message.error(this.$t("tip_msg.network_error"));
console.log("QUERY FAILED!BECAUSE => " + data);
}
});
},
// 删除备份
toDeleteBackUpConfirm(row) {
this.$confirm(
this.$t("version_management.del_backup_tip"),
this.$t("version_management.del_backup_title"),
{
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning",
beforeClose: (action, instance, done) => {
if (action == "confirm") {
instance.confirmButtonLoading = true;
this.doDeleteBackUp(row, instance, done);
} else {
done();
}
}
}
)
.then(() => {})
.catch(() => {});
},
doDeleteBackUp(row, instance, done) {
HTTP.request("getApi", {
method: "post",
urlParam: { pathParam: this.devId + "/delbackup/" },
// urlParam: { pathParam: "delbackup/" },
data: {
backup_tm: row.backup_tm.toString(),
backup_ver: row.backup_ver.toString()
},
complete: res => {
if (res.code == 0) {
this.$message({
type: "success",
message: res.msg
});
done();
this.getBackUpList();
} else {
this.$message.error(res.msg);
}
instance.confirmButtonLoading = false;
},
error: data => {
this.$message.error(this.$t("tip_msg.network_error"));
console.log("QUERY FAILED!BECAUSE => " + data);
instance.confirmButtonLoading = false;
}
});
},
// 删除版本
toDeleteVerConfirm(row) {
this.$confirm(
this.$t("version_management.del_ver_tip"),
this.$t("version_management.del_ver_title"),
{
confirmButtonText: this.$t("common.confirm"),
cancelButtonText: this.$t("common.cancel"),
type: "warning",
beforeClose: (action, instance, done) => {
if (action == "confirm") {
instance.confirmButtonLoading = true;
this.doDeleteVer(row, instance, done);
} else {
done();
}
}
}
)
.then(() => {})
.catch(() => {});
},
doDeleteVer(row, instance, done) {
let param = {
file_name: row.file_name.toString(),
upload_tm: row.upload_tm.toString()
};
HTTP.request("getApi", {
method: "post",
urlParam: { pathParam: this.devId + "/delverfile/" },
// urlParam: { pathParam: "delverfile/" },
data: param,
complete: res => {
if (res.code == 0) {
this.$message({
type: "success",
message: res.msg
});
done();
this.getBackUpList();
} else {
this.$message.error(res.msg);
}
instance.confirmButtonLoading = false;
},
error: data => {
this.$message.error(this.$t("tip_msg.network_error"));
console.log("QUERY FAILED!BECAUSE => " + data);
instance.confirmButtonLoading = false;
}
});
},
// 升级,备份,回退弹框
toOpenDialog(row, event) {
this.updateKey += 1;
let className = event.currentTarget.getAttribute("class");
this.toOpenVisible = true;
// 备份
if (className.indexOf("toBackUp") > -1) {
this.upTitle = this.$t("version_management.ver_backup");
this.upTip = this.$t("version_management.ver_toBackUp_tip");
this.sign = 1;
} else if (className.indexOf("toRollback") > -1) {
// 回退
this.upTitle = this.$t("version_management.ver_rollback");
this.upTip = this.$t("version_management.ver_toRollback_tip");
this.sign = 2;
} else if (className.indexOf("toUpgrade") > -1) {
// 升级
this.upTitle = this.$t("version_management.ver_toUpGrade");
this.upTip = this.$t("version_management.ver_toUpGrade_tip");
this.sign = 3;
}
this.row = row;
},
dotoExecute(row) {
this.toOpenVisible = true;
this.upGradeTip = false;
this.percentage = 0;
if (this.sign == 1) {
this.upTitle = this.$t("version_management.backup_progress");
this.toBackUp(row);
} else if (this.sign == 2) {
// 回退
this.upTitle = this.$t("version_management.rollback_progress");
this.toRollback(this.row);
} else if (this.sign == 3) {
// 升级
this.verUpdate(this.row);
this.upTitle = this.$t("version_management.upgrade_progress");
}
},
verUpdate(row) {
let param = {
file_name: row.file_name.toString(),
upload_tm: row.upload_tm.toString()
};
HTTP.request("getApi", {
method: "post",
urlParam: { pathParam: this.devId + "/verupdate/" },
// urlParam: { pathParam: "verupdate/" },
data: param,
complete: res => {
if (res.code === 0) {
this.upGradeProgress = true;
this.addSetInterval();
} else {
this.$message.error(res.msg);
}
},
error: data => {
this.$message.error(data);
console.log("QUERY FAILED!BECAUSE => " + data);
}
});
},
closeDialog() {
this.clearAndCencle();
this.toOpenVisible = false;
this.upGradeTip = true;
this.successOrError = false;
this.upGradeProgress = false;
this.upTitle = this.$t("version_management.ver_toUpGrade");
this.initInfo();
},
// 进度条查询
getStatus() {
HTTP.request("getApi", {
method: "get",
urlParam: { pathParam: this.devId + "/getstatus/" },
// urlParam: { pathParam: "getstatus/" },
complete: res => {
if (res.code === 0) {
this.percentage = res.status;
} else {
this.upGradeProgress = false;
this.successOrError = true;
this.isSuccess = false;
this.upTitle = this.$t("version_management.error");
if (this.sign == 1) {
this.resTip = this.$t("version_management.ver_backup_error");
} else if (this.sign == 2) {
this.resTip = this.$t("version_management.ver_rollback_error");
} else if (this.sign == 3) {
this.resTip = this.$t("version_management.ver_upgrade_error");
}
this.clearAndCencle();
console.log("QUERY FAILED!BECAUSE => " + res.msg);
}
},
error: data => {
this.$message.error(data);
console.log("QUERY FAILED!BECAUSE => " + data);
}
});
},
addSetInterval() {
const that = this; // 声明一个变量指向vue实例this,保证作用域一致
this.positionTimer = setInterval(() => {
if (that.percentage == 100) {
clearInterval(this.positionTimer); // 清除定时器
this.positionTimer = null;
that.isSuccess = true;
that.upGradeProgress = false;
that.successOrError = true;
that.upTitle = that.$t("version_management.success");
if (that.sign == 1) {
that.resTip = that.$t("version_management.ver_backup_success");
} else if (that.sign == 2) {
that.resTip = that.$t("version_management.ver_rollback_success");
} else if (that.sign == 3) {
that.resTip = that.$t("version_management.ver_upgrade_success");
} else if (this.sign == 4) {
this.isupload = false;
this.resTip = that.$t("version_management.ver_upload_success");
}
} else {
if (that.sign == 4) {
that.getUpLoadStatus();
} else {
that.getStatus();
}
}
}, 5000);
},
// 备份
toBackUp() {
HTTP.request("getApi", {
method: "post",
urlParam: { pathParam: this.devId + "/verbackup/" },
// urlParam: { pathParam: "verbackup/" },
complete: res => {
if (res.code === 0) {
this.upGradeProgress = true;
this.addSetInterval();
} else {
this.$message.error(res.msg);
}
},
error: data => {
this.$message.error(data);
console.log("QUERY FAILED!BECAUSE => " + data);
}
});
},
// 回退
toRollback(row) {
let param = {
backup_tm: row.backup_tm.toString(),
backup_ver: row.backup_ver.toString()
};
HTTP.request("getApi", {
method: "post",
urlParam: { pathParam: this.devId + "/verrollback/" },
// urlParam: { pathParam: "verrollback/" },
data: param,
complete: res => {
if (res.code === 0) {
this.upGradeProgress = true;
this.addSetInterval();
} else {
this.$message.error(res.msg);
}
},
error: data => {
this.$message.error(data);
console.log("QUERY FAILED!BECAUSE => " + data);
}
});
},
// 上传容量检测
spaceCheck(file) {
let file_size = file.size / 1024 / 1024;
HTTP.request("getApi", {
method: "post",
data: { file_size: file_size.toString() },
urlParam: { pathParam: this.devId + "/spacecheck/" },
// urlParam: { pathParam: "spacecheck/" },
complete: res => {
if (res.code == 0) {
this.successIcon = true;
this.isShow = true;
} else {
this.errIcon = true;
this.isExcceedTip = res.msg;
this.isShow = false;
this.isErrorBtn = true;
console.log("QUERY FAILED!BECAUSE => " + res.msg);
}
},
error: data => {
this.$message.error(this.$t("tip_msg.network_error"));
console.log("QUERY FAILED!BECAUSE => " + data);
}
});
},
// 上传弹框
toUploadForm() {
this.updateKey += 1;
this.percentage = 0;
this.sign = 4;
this.uploadVisible = true;
this.upTitle = this.$t("version_management.ver_upload");
this.upGradeProgress = false;
this.isupload = true;
},
beforeUpload(file) {
if (file) {
this.upFile.append("file", file);
} else {
return false;
}
},
// 文件上传
submitUpload() {
this.$refs.upload.submit();
},
handleChange(file, fileList) {
if (file.response) {
if (file.response.code == 0) {
this.cancleBtn = false;
this.isShow = false;
this.addSetInterval();
} else {
this.$message.error(file.response.msg);
this.isErrorBtn = true;
this.upGradeProgress = false;
this.isShow = false;
console.log("QUERY FAILED!BECAUSE => " + file.response.msg);
}
} else {
this.clearAndCencle();
this.filename = file.name;
this.spaceCheck(file);
this.upGradeProgress = false;
this.isShow = true;
this.isErrorBtn = false;
}
},
getUpLoadStatus() {
HTTP.request("getApi", {
method: "get",
urlParam: { pathParam: this.devId + "/getuploadstatus/" },
// urlParam: { pathParam: "getuploadstatus/" },
complete: res => {
if (res.code === 0) {
this.upGradeProgress = true;
this.percentage = res.status;
} else {
this.upGradeProgress = false;
this.successOrError = true;
this.isupload = false;
this.isSuccess = false;
this.upTitle = this.$t("version_management.error");
this.resTip = res.msg;
this.clearAndCencle();
console.log("QUERY FAILED!BECAUSE => " + res.msg);
}
},
error: data => {
this.$message.error(data);
console.log("QUERY FAILED!BECAUSE => " + data);
}
});
},
// 错误直接停止定时器清空还原
clear() {
this.clearAndCencle();
this.$refs.upload.clearFiles();
this.filename = "";
this.upGradeProgress = false;
this.successIcon = false;
this.errIcon = false;
this.isShow = true;
this.isErrorBtn = false;
this.cancleBtn = true;
this.isExcceedTip = "";
this.successOrError = false;
},
closeUploadDialog() {
this.clear();
this.initInfo();
},
// 取消上传
cancelUpload() {
this.clear();
this.clearAndCencle();
},
// 清除取消定时器
clearAndCencle() {
clearInterval(this.positionTimer); // 清除定时器
this.positionTimer = null;
}
},
created() {
this.initInfo();
},
destroyed() {
clearInterval(this.positionTimer); // 清除定时器
this.positionTimer = null;
}
};
</script>
<style>
.verinfo h4 {
font-size: 14px;
font-weight: normal;
margin-bottom: 16px;
font-family: Microsoft YaHei;
font-stretch: normal;
letter-spacing: 0px;
color: #555555;
}
/* 备份 */
.verinfo .btn_wrap {
display: flex;
justify-content: space-between;
margin-bottom: 7px;
}
.backup_title {
margin-top: 10px;
font-size: 14px;
font-weight: normal;
font-family: Microsoft YaHei;
font-stretch: normal;
letter-spacing: 0px;
color: #555555;
}
.verinfo .backup_btn {
text-align: right;
}
.verinfo .iconfont {
font-size: 20px;
cursor: pointer;
}
.verinfo .iconfont.blue {
margin-right: 26px;
}
.iconRed {
color: #e34d59;
font-size: 17px;
}
.iconGreen {
color: #00a870;
font-size: 17px;
}
.verinfo .backuplist {
margin-bottom: 109px;
}
.verinfo .el-dialog__body {
padding: 1rem 1rem 2rem 2rem;
}
.verinfo .upTitle {
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: normal;
font-stretch: normal;
line-height: 19px;
letter-spacing: 0px;
color: #606266;
}
.verinfo .successOrErrorTip {
display: flex;
justify-content: flex-start;
flex: 1;
}
.verinfo .successOrErrorTip i {
margin: 0 10px;
}
.verinfo .filename {
margin: 0 14px;
width: 229px;
height: 2.5rem;
background-color: #ffffff;
border-radius: 3px;
}
.verinfo .upload {
display: flex;
}
.verinfo .uploadtip .title {
font-family: Microsoft YaHei;
font-size: 12px;
font-weight: normal;
font-stretch: normal;
letter-spacing: 0px;
color: #e34d59;
}
.verinfo .uploadtip {
width: 230px;
margin-left: 20px;
}
</style>
功能描述:
-
工程售后人员打开CAG的管理门户,在版本管理页面中,可以上传新版本,新版本保存在CAG本地。
-
在版本管理页面中,可以看到当前已部署版本的版本号。
-
在版本管理页面中,可以看到系统剩余空间
-
在版本管理页面中,可以查看已上传的版本信息。
-
在版本管理页面中,对已上传的版本,可以执行删除操作。
-
在版本管理页面中,对已上传的版本,可以执行部署升级操作。部署前,需要进行备份,便于部署失败的回退。部署升级过程需要展示:进度信息,过程信息、执行结果信息。
-
版本升级只升级本模块,分布式部署每个节点单独升级
【版本管理】
-
用户正确登录CAG的管理门户后(不登录不允许使用),点击顶部菜单 CAG配置管理->CAG接入点管理
,在网关列表中选择一个网关,点击系统设置图标。
-
在右边的菜单列表中增加一个菜单项“版本管理”
-
用户点击版本管理后,右边的页面中显示当前版本、备份列表、版本列表
-
在备份列表中显示用户备份的历史文件,版本列表中显示以下几列:
1) 备份时间,年月日时分秒
2) 备份版本号
3) 回退,操作命令,用户点击回退操作后,执行该版本的回退操作
4) 删除,操作命令,用户点击删除操作后,执行该备份版本的删除操
-
在版本列表中显示用户之前上传且没有被删除的版本信息,版本列表中显示以下几列:
1)上传时间,年月日时分秒
2) 文件名称,用户上传的版本文件名称
3) 文件MD5摘要,在系统中保存的实际文件的MD5摘要,16进制小写文本显示
4) 文件大小,在系统中保存的实际文件的大小
5) 升级,操作命令,用户点击升级操作后,执行该版本的升级操作
6) 删除,操作命令,用户点击删除操作后,执行该版本文件的删除操作
【版本上传】
-
用户点击文件上传按钮后,弹出文件选择对话框,让用户选择要上传的文件
-
用户点击确定按钮后,开始上传文件
-
文件上传前,需要检查系统的剩余磁盘空间是否足够(剩余空间-版本文件大小 >=分区总空间的30%),如果不够,不允许上传
-
文件上传时,服务端和客户端需要同时对文件的大小做限制,文件大小超过(可配置,默认为当前版本的2倍)不允许上传
-
在上传的过程中需要显示文件上传的进度(按百分比显示)
-
在上传的过程中需要用户可以取消文件上传,取消文件上传后,服务器上不保留垃圾文件
-
文件上传成功后,在版本列表中新增一条记录
-
系统最多保留5个(可配置)历史版本信息,超过最大个数后,自动删除最早的版本文件
-
如果没有上传成功,服务器上的垃圾文件需要自动清除
【版本删除】
-
用户点击某个版本的删除按钮,提示用户是否确认删除该版本文件
-
用户点击确认后,删除版本文件和文件记录
-
用户点击取消后,退出删除界面
【版本升级】
-
用户点击某个版本的升级按钮后,提示用户是否确认升级该版本,并提示用户需要手动备份
-
用户点击确定按钮后,需要检查系统的剩余磁盘空间是否足够(剩余空间-版本文件大小 >=分区总空间的30%),如果不够,不允许升级
-
在升级的过程中需要显示升级的进度(按百分比显示)
-
升级成功后,需要更新系统的版本信息
-
升级的结果需要提示用户,如果升级成功,提示用户升级成功,并显示系统的版本信息;如果升级失败,提示用户错误原因,以及可能的解决方案,并提示用户需要手工执行回退操作
【版本备份】
-
用户点击备份按钮后,提示用户是否确认执行备份操作
-
用户点击确定按钮后,需要检查系统的剩余磁盘空间是否足够(剩余空间-版本文件大小 >=分区总空间的30%),如果不够,不允许备份
-
备份当前版本文件和配置数据,并生成一条备份记录
-
在备份的过程中需要显示备份进度
-
备份的结果需要提示用户,如果备份成功,提示用户备份成功,如果备份失败,提示用户错误原因,以及可能的解决方案
-
系统最多保留5个(可配置)历史备份文件,超过最大个数后,提示用户删除多余的备份文件
【版本回退】
-
用户点击某个备份文件的回退按钮后,提示用户是否确认执行回退操作
-
用户点击确定按钮后,需要执行系统检测操作,如果检测失败,不允许回退
-
在回退的过程中需要显示回退进度
-
回退的结果需要提示用户,如果回退成功,提示用户回退成功,如果回退失败,提示用户错误原因,以及可能的解决方案
【备份删除】
-
用户点击某个备份文件的删除按钮后,提示用户是否确认执行删除操作
-
用户点击确定按钮后,删除备份文件和记录
-
用户点击取消后,退出删除界面
【日志】
-
用户在管理台上所有操作(除查询类的)都需要记录操作日志
-
操作日志需要和业务日志分离
-
操作日志的内容至少包括:操作时间、操作类型、操作结果(0:成功、其他:失败)、操作结果描述、操作人、客户端IP
-
管理员可以在管理界面查询操作日志