Sfoglia il codice sorgente

feat: 数据同步调试

zhangjie 6 mesi fa
parent
commit
34add9f127

+ 7 - 0
src/assets/styles/element-ui-costom.scss

@@ -580,6 +580,7 @@
     }
     .el-message-box__message {
       padding: 0;
+      word-break: break-all;
     }
   }
   &__btns {
@@ -591,6 +592,12 @@
       width: 100px;
     }
   }
+
+  &.alert-message {
+    .el-message-box__status {
+      font-size: 40px !important;
+    }
+  }
 }
 
 .alert-message {

+ 5 - 5
src/constants/enumerate.js

@@ -445,9 +445,9 @@ export const DATABASE_TYPE = {
   MYSQL: "MySQL",
 };
 export const DATABASE_SYNC_TYPE = {
-  1: "组织架构",
-  2: "用户数据",
-  3: "课程数据",
-  4: "考生数据",
-  5: "命题任务数据",
+  A: "组织架构",
+  B: "用户数据",
+  C: "课程数据",
+  D: "考生数据",
+  E: "命题任务数据",
 };

+ 22 - 9
src/modules/admin/api.js

@@ -134,28 +134,41 @@ export const schoolSetDataBackup = (schoolId) => {
 
 // 数据同步 school database sync
 export const schoolSetDatabaseSyncBaseinfo = (schoolId) => {
-  return $postParam("/api/admin/set/sync1/save", { schoolId });
+  return $postParam("/api/admin/set/data/sync/select", { schoolId });
 };
 export const schoolSetDatabaseSyncBaseinfoUpdate = (datas) => {
-  return $post("/api/admin/set/sync1/save", datas);
+  return $post("/api/admin/set/data/sync/save", datas);
 };
 export const schoolSetDatabaseSyncBaseinfoTest = (datas) => {
-  return $post("/api/admin/set/sync1/save", datas);
+  return $post("/api/admin/sys/school/data/test/connect", datas, {
+    silence: true,
+  });
 };
 
 export const schoolSetDatabaseSyncList = (datas) => {
-  return $postParam("/api/admin/set/sync1/save", datas);
+  return $postParam("/api/admin/sys/school/data/param/list", datas);
 };
 export const schoolSetDatabaseSyncUpdate = (datas) => {
-  return $post("/api/admin/set/sync1/save", datas);
+  return $post("/api/admin/sys/school/data/param/save", datas);
+};
+export const schoolSetDatabaseSyncLogList = (datas) => {
+  return $postParam("/api/admin/sys/school/data/sync/log", datas);
 };
-export const schoolSetDatabaseSyncHandleSync = (id) => {
-  return $postParam("/api/admin/set/sync1/save", { id });
+export const schoolSetDatabaseSyncHandleSync = ({ schoolId, examId }) => {
+  return $postParam("/api/admin/sys/school/data/start/sync", {
+    schoolId,
+    examId,
+  });
 };
-export const schoolSetDatabaseSyncClose = (id) => {
-  return $postParam("/api/admin/set/sync1/save", { id });
+export const schoolSetDatabaseSyncEnable = ({ schoolId, examId, enable }) => {
+  return $post("/api/admin/sys/school/data/enable/sync", {
+    schoolId,
+    examId,
+    enable,
+  });
 };
 
+// log
 export const systemLogExport = () => {
   return $postParam(
     "/api/admin/common/log/download",

+ 97 - 0
src/modules/admin/components/school/DatabaseSyncLogDialog.vue

@@ -0,0 +1,97 @@
+<template>
+  <el-dialog
+    class="database-sync-log-dialog"
+    :visible.sync="modalIsShow"
+    width="1000px"
+    title="同步日志"
+    top="10vh"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    destroy-on-close
+    @opened="visibleChange"
+  >
+    <el-table ref="TableList" :data="dataList">
+      <el-table-column prop="dataType" label="同步数据类型"></el-table-column>
+      <el-table-column prop="startTime" label="同步开始时间" width="170">
+        <span slot-scope="scope">{{
+          scope.row.startTime | timestampFilter
+        }}</span>
+      </el-table-column>
+      <el-table-column prop="endTime" label="同步结束时间" width="170">
+        <span slot-scope="scope">{{
+          scope.row.endTime | timestampFilter
+        }}</span>
+      </el-table-column>
+      <el-table-column prop="count" label="同步数量" width="90">
+      </el-table-column>
+      <el-table-column prop="errorMsg" label="失败原因"></el-table-column>
+    </el-table>
+    <div class="part-page">
+      <el-pagination
+        background
+        layout="total, sizes, prev, pager, next, jumper"
+        :pager-count="5"
+        :current-page="current"
+        :total="total"
+        :page-size="size"
+        @current-change="toPage"
+        @size-change="pageSizeChange"
+      >
+      </el-pagination>
+    </div>
+
+    <template slot="footer"> </template>
+  </el-dialog>
+</template>
+
+<script>
+import { schoolSetDatabaseSyncLogList } from "../../api";
+
+export default {
+  name: "database-sync-log-dialog",
+  props: {
+    rowData: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      dataList: [],
+      current: 1,
+      size: this.GLOBAL.pageSize,
+      total: 0,
+    };
+  },
+  methods: {
+    visibleChange() {
+      this.toPage(1);
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    async getList() {
+      const datas = {
+        schoolId: this.rowData.schoolId,
+        examId: this.rowData.examId,
+        pageNumber: this.current,
+        pageSize: this.size,
+      };
+      const data = await schoolSetDatabaseSyncLogList(datas);
+      this.dataList = data.records;
+      this.total = data.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+  },
+};
+</script>

+ 37 - 31
src/modules/admin/components/school/ModifyDatabaseSync.vue

@@ -23,6 +23,7 @@
             v-model="modalForm.semesterId"
             placeholder="学期"
             filterable
+            :disabled="isEdit"
             @change="semesterChange"
           >
             <el-option
@@ -35,7 +36,12 @@
           </el-select>
         </el-form-item>
         <el-form-item prop="examId" label="考试:">
-          <el-select v-model="modalForm.examId" placeholder="考试" filterable>
+          <el-select
+            v-model="modalForm.examId"
+            placeholder="考试"
+            filterable
+            :disabled="isEdit"
+          >
             <el-option
               v-for="item in examList"
               :key="item.id"
@@ -55,37 +61,37 @@
             >
           </el-checkbox-group>
         </el-form-item>
-        <el-form-item prop="syncStartTime" label="同步开始时间:">
+        <el-form-item prop="startTime" label="同步开始时间:">
           <el-date-picker
-            v-model="modalForm.syncStartTime"
+            v-model="modalForm.startTime"
             type="datetime"
             value-format="timestamp"
             placeholder="选择时间"
-            @change="() => timeChange('syncStartTime')"
+            @change="() => timeChange('startTime')"
           >
           </el-date-picker>
         </el-form-item>
-        <el-form-item prop="syncEndTime" label="同步结束时间:">
+        <el-form-item prop="endTime" label="同步结束时间:">
           <el-date-picker
-            v-model="modalForm.syncEndTime"
+            v-model="modalForm.endTime"
             type="datetime"
             value-format="timestamp"
             placeholder="选择时间"
-            @change="() => timeChange('syncEndTime')"
+            @change="() => timeChange('endTime')"
           >
           </el-date-picker>
         </el-form-item>
-        <el-form-item prop="durationTime" label="定时同步时间:">
+        <el-form-item prop="cron" label="定时同步时间:">
           <el-input
-            v-model.trim="modalForm.durationTime"
+            v-model.trim="modalForm.cron"
             placeholder="请输入定时同步时间"
             clearable
           ></el-input>
           <p class="tips-info">如每天1点开始同步,0 0 1 * * ?</p>
         </el-form-item>
-        <el-form-item prop="warningPhone" label="同步异常预警:">
+        <el-form-item prop="phoneNumber" label="同步异常预警:">
           <el-input
-            v-model.trim="modalForm.warningPhone"
+            v-model.trim="modalForm.phoneNumber"
             placeholder="请输入同步异常预警"
             clearable
           ></el-input>
@@ -108,14 +114,13 @@ import { conditionListSemester, conditionListExam } from "../../../base/api";
 import { DATABASE_SYNC_TYPE } from "@/constants/enumerate";
 
 const initModalForm = {
-  id: null,
   semesterId: "",
   examId: "",
   dataType: "",
-  syncStartTime: "",
-  syncEndTime: "",
-  durationTime: "",
-  warningPhone: "",
+  startTime: "",
+  endTime: "",
+  cron: "",
+  phoneNumber: "",
 };
 
 export default {
@@ -134,7 +139,7 @@ export default {
   },
   computed: {
     isEdit() {
-      return !!this.instance.id;
+      return !!this.instance.examId;
     },
     title() {
       return (this.isEdit ? "编辑" : "新增") + "同步记录";
@@ -174,7 +179,7 @@ export default {
             trigger: "change",
           },
         ],
-        syncStartTime: [
+        startTime: [
           {
             required: true,
             validator: (rule, value, callback) => {
@@ -182,10 +187,7 @@ export default {
                 return callback(new Error(`请选择同步开始时间`));
               }
 
-              if (
-                this.modalForm.syncEndTime &&
-                value >= this.modalForm.syncEndTime
-              ) {
+              if (this.modalForm.endTime && value >= this.modalForm.endTime) {
                 return callback(new Error(`开始时间必须早于结束时间`));
               }
 
@@ -194,7 +196,7 @@ export default {
             trigger: "change",
           },
         ],
-        syncEndTime: [
+        endTime: [
           {
             required: true,
             validator: (rule, value, callback) => {
@@ -203,8 +205,8 @@ export default {
               }
 
               if (
-                this.modalForm.syncStartTime &&
-                value <= this.modalForm.syncStartTime
+                this.modalForm.startTime &&
+                value <= this.modalForm.startTime
               ) {
                 return callback(new Error(`结束时间必须晚于开始时间`));
               }
@@ -214,14 +216,14 @@ export default {
             trigger: "change",
           },
         ],
-        durationTime: [
+        cron: [
           {
             required: true,
             message: "请输入定时同步时间",
             trigger: "change",
           },
         ],
-        warningPhone: [
+        phoneNumber: [
           {
             required: true,
             validator: (rule, value, callback) => {
@@ -251,9 +253,9 @@ export default {
   },
   methods: {
     initData(val) {
-      if (val.id) {
+      if (val.examId) {
         this.modalForm = this.$objAssign(initModalForm, val);
-        this.modalForm.dataType = [...val.dataType];
+        this.modalForm.dataType = val.dataType ? val.dataType.split(",") : [];
       } else {
         this.modalForm = { ...initModalForm };
         this.modalForm.dataType = [];
@@ -291,7 +293,7 @@ export default {
       this.getExams();
     },
     timeChange(field) {
-      const key = field === "syncStartTime" ? "syncEndTime" : "syncStartTime";
+      const key = field === "startTime" ? "endTime" : "startTime";
       this.$refs.modalFormComp.validateField(key);
     },
     async submit() {
@@ -300,7 +302,11 @@ export default {
 
       if (this.isSubmit) return;
       this.isSubmit = true;
-      const datas = { ...this.modalForm };
+      const datas = {
+        ...this.modalForm,
+        schoolId: this.schoolId,
+        dataType: this.modalForm.dataType.join(),
+      };
       const res = await schoolSetDatabaseSyncUpdate(datas).catch(() => {});
       this.isSubmit = false;
 

+ 160 - 66
src/modules/admin/components/school/SchoolSetDatabaseSync.vue

@@ -18,13 +18,36 @@
             >
           </el-radio-group>
         </el-form-item>
-        <el-form-item prop="url" label="URL:">
-          <el-input
-            v-model.trim="modalForm.url"
-            placeholder="请输入URL"
-            clearable
-          ></el-input>
-        </el-form-item>
+        <el-row>
+          <el-col :span="8">
+            <el-form-item prop="host" label="host:">
+              <el-input
+                v-model.trim="modalForm.host"
+                placeholder="请输入URL"
+                clearable
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item prop="port" label="port:">
+              <el-input
+                v-model.trim="modalForm.port"
+                placeholder="请输入URL"
+                clearable
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item prop="dataname" label="dataname:">
+              <el-input
+                v-model.trim="modalForm.dataname"
+                placeholder="请输入URL"
+                clearable
+              ></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
         <el-row>
           <el-col :span="12">
             <el-form-item prop="username" label="用户名:">
@@ -55,7 +78,9 @@
 
     <div class="part-box part-box-pad">
       <div class="box-justify mb-2">
-        <div></div>
+        <div>
+          <el-button type="primary" @click="getList">更新列表</el-button>
+        </div>
         <div>
           <el-button type="primary" @click="toAdd">新增</el-button>
         </div>
@@ -68,39 +93,46 @@
           min-width="210"
         ></el-table-column>
         <el-table-column
-          prop="name"
+          prop="examName"
           label="考试名称"
           min-width="160"
         ></el-table-column>
         <el-table-column
-          prop="courseName"
+          prop="dataType"
           label="同步数据类型"
           width="120"
         ></el-table-column>
         <el-table-column
-          prop="courseCode"
-          label="同步记录数"
-          width="120"
+          prop="detail"
+          label="同步结果"
+          min-width="100"
         ></el-table-column>
-        <el-table-column prop="createTime" label="同步开始时间" width="170">
+        <el-table-column prop="startTime" label="同步开始时间" width="170">
           <span slot-scope="scope">{{
-            scope.row.createTime | timestampFilter
+            scope.row.startTime | timestampFilter
           }}</span>
         </el-table-column>
-        <el-table-column prop="createTime" label="同步结束时间" width="170">
+        <el-table-column prop="endTime" label="同步结束时间" width="170">
           <span slot-scope="scope">{{
-            scope.row.createTime | timestampFilter
+            scope.row.endTime | timestampFilter
           }}</span>
         </el-table-column>
-        <el-table-column
-          prop="courseCode"
-          label="状态"
-          width="100"
-        ></el-table-column>
+        <el-table-column prop="cron" label="定时同步时间" width="140">
+        </el-table-column>
+        <el-table-column prop="status" label="同步状态" width="100">
+          <template slot-scope="scope">
+            {{ scope.row.status ? "同步中" : "已同步" }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="enable" label="启用/禁用" width="100">
+          <template slot-scope="scope">
+            {{ scope.row.enable | enableFilter }}
+          </template>
+        </el-table-column>
         <el-table-column
           class-name="action-column"
           label="操作"
-          width="220"
+          width="260"
           fixed="right"
         >
           <template slot-scope="scope">
@@ -123,10 +155,10 @@
               >手动同步</el-button
             >
             <el-button
-              class="btn-danger"
+              :class="scope.row.enable ? 'btn-danger' : 'btn-primary'"
               type="text"
-              @click="toCloseSync(scope.row)"
-              >结束同步</el-button
+              @click="toEnable(scope.row)"
+              >{{ scope.row.enable ? "禁用" : "启用" }}</el-button
             >
           </template>
         </el-table-column>
@@ -146,22 +178,6 @@
       </div>
     </div>
 
-    <!-- 日志 -->
-    <el-dialog
-      class="log-dialog"
-      :visible.sync="logModalIsShow"
-      title="日志"
-      :close-on-click-modal="false"
-      :close-on-press-escape="false"
-      append-to-body
-      top="10vh"
-      width="800px"
-    >
-      <div style="min-height: 300px; overflow: auto">
-        <p v-for="(cont, cidx) in logList" :key="cidx">{{ cont }}</p>
-      </div>
-    </el-dialog>
-
     <!-- ModifyDatabaseSync -->
     <ModifyDatabaseSync
       ref="ModifyDatabaseSync"
@@ -169,6 +185,8 @@
       :school-id="school.id"
       @modified="getList"
     />
+    <!-- DatabaseSyncLogDialog -->
+    <DatabaseSyncLogDialog ref="DatabaseSyncLogDialog" :row-data="curRow" />
   </div>
 </template>
 
@@ -179,21 +197,24 @@ import {
   schoolSetDatabaseSyncBaseinfoTest,
   schoolSetDatabaseSyncList,
   schoolSetDatabaseSyncHandleSync,
-  schoolSetDatabaseSyncClose,
+  schoolSetDatabaseSyncEnable,
 } from "../../api";
 import { DATABASE_TYPE } from "@/constants/enumerate";
 import ModifyDatabaseSync from "./ModifyDatabaseSync.vue";
+import DatabaseSyncLogDialog from "./DatabaseSyncLogDialog.vue";
 
 const initModalForm = {
   type: "",
-  url: "",
+  host: "",
+  port: "",
+  dataname: "",
   username: "",
   password: "",
 };
 
 export default {
   name: "school-set-database-sync",
-  components: { ModifyDatabaseSync },
+  components: { ModifyDatabaseSync, DatabaseSyncLogDialog },
   props: {
     school: {
       type: Object,
@@ -204,6 +225,7 @@ export default {
   },
   data() {
     return {
+      baseinfo: [],
       modalForm: { ...initModalForm },
       rules: {
         type: [
@@ -213,15 +235,44 @@ export default {
             trigger: "change",
           },
         ],
-        url: [
+        host: [
+          {
+            required: true,
+            message: "请输入host",
+            trigger: "change",
+          },
+          {
+            message: "host不能超过99个字符",
+            max: 99,
+            trigger: "change",
+          },
+        ],
+        port: [
+          {
+            required: true,
+            message: "请输入port",
+            trigger: "change",
+          },
+          {
+            message: "port只能是数字",
+            pattern: /^\d*$/,
+            trigger: "change",
+          },
+          {
+            message: "port不能超过6个字符",
+            max: 6,
+            trigger: "change",
+          },
+        ],
+        dataname: [
           {
             required: true,
-            message: "请输入URL",
+            message: "请输入dataname",
             trigger: "change",
           },
           {
-            message: "url不能超过999个字符",
-            max: 999,
+            message: "dataname不能超过99个字符",
+            max: 99,
             trigger: "change",
           },
         ],
@@ -262,10 +313,33 @@ export default {
       logModalIsShow: false,
     };
   },
+  mounted() {
+    this.getBaseInfo();
+    this.toPage(1);
+  },
   methods: {
     async getBaseInfo() {
       const res = await schoolSetDatabaseSyncBaseinfo(this.school.id);
-      this.modalForm = this.$objAssign(initModalForm, res || {});
+      this.baseinfo = res.result || [];
+      // const codeMap = {
+      //   "data.datasource.type": "type",
+      //   "data.datasource.host": "host",
+      //   "data.datasource.port": "port",
+      //   "data.datasource.dataname": "dataname",
+      //   "data.datasource.username": "username",
+      //   "data.datasource.password": "password",
+      // };
+      this.baseinfo.forEach((item) => {
+        const k = item.code.replace("data.datasource.", "");
+        this.modalForm[k] = item.value;
+      });
+    },
+    getData() {
+      return this.baseinfo.map((item) => {
+        const k = item.code.replace("data.datasource.", "");
+        item.value = this.modalForm[k];
+        return item;
+      });
     },
     async toSave() {
       const valid = await this.$refs.modalFormComp.validate().catch(() => {});
@@ -273,7 +347,7 @@ export default {
 
       if (this.isSubmit) return;
       this.isSubmit = true;
-      const datas = { ...this.modalForm };
+      const datas = { param: this.getData(), schoolId: this.school.id };
       const res = await schoolSetDatabaseSyncBaseinfoUpdate(datas).catch(
         () => {}
       );
@@ -288,14 +362,21 @@ export default {
 
       if (this.isSubmit) return;
       this.isSubmit = true;
-      const datas = { ...this.modalForm };
+      const datas = { param: this.getData(), schoolId: this.school.id };
+      let errorMsg = "";
       const res = await schoolSetDatabaseSyncBaseinfoTest(datas).catch(
-        () => {}
+        (error) => {
+          errorMsg = error.message || "测试失败!";
+        }
       );
       this.isSubmit = false;
+      const result = res ? "测试成功!" : errorMsg;
 
-      if (!res) return;
-      this.$message.success("测试成功!");
+      this.$alert(result, {
+        type: res ? "success" : "error",
+        customClass: "alert-message",
+        confirmButtonText: "确定",
+      });
     },
     async getList() {
       const datas = {
@@ -304,7 +385,7 @@ export default {
         pageSize: this.size,
       };
       const data = await schoolSetDatabaseSyncList(datas);
-      this.exams = data.records;
+      this.dataList = data.records;
       this.total = data.total;
     },
     toPage(page) {
@@ -320,24 +401,37 @@ export default {
       this.$refs.ModifyDatabaseSync.open();
     },
     toViewLog(row) {
-      this.logList = (row.summary || "").split("\n");
-      this.logModalIsShow = true;
+      this.curRow = row;
+      this.$refs.DatabaseSyncLogDialog.open();
     },
     async toSync(row) {
-      const res = await schoolSetDatabaseSyncHandleSync(row.id).catch(() => {});
+      const res = await schoolSetDatabaseSyncHandleSync({
+        schoolId: row.schoolId,
+        examId: row.examId,
+      }).catch(() => {});
       if (!res) return;
       this.$message.success("操作成功!");
       this.getList();
     },
-    async toCloseSync(row) {
-      const confirm = await this.$confirm(`确定要结束当前同步记录吗?`, "提示", {
-        type: "warning",
-      }).catch(() => {});
-      if (confirm !== "confirm") return;
+    async toEnable(row) {
+      const action = row.enable ? "禁用" : "启用";
+      const result = await this.$confirm(
+        `确定要${action}当前同步记录吗?`,
+        "提示",
+        {
+          type: "warning",
+        }
+      ).catch(() => {});
+      if (result !== "confirm") return;
 
-      await schoolSetDatabaseSyncClose(row.id);
+      const enable = !row.enable;
+      await schoolSetDatabaseSyncEnable({
+        schoolId: row.schoolId,
+        examId: row.examId,
+        enable,
+      });
+      row.enable = enable;
       this.$message.success("操作成功!");
-      this.getList();
     },
   },
 };

+ 2 - 0
src/plugins/axios.js

@@ -187,6 +187,8 @@ const errorDataCallback = (response) => {
     });
     return error;
   } else {
+    if (response.config?.silence) return error;
+
     Notification.error({ title: "错误提示", message });
   }