瀏覽代碼

refactor to ts

Michael Wang 3 年之前
父節點
當前提交
118b2b7a2a
共有 19 個文件被更改,包括 1025 次插入954 次删除
  1. 4 3
      package.json
  2. 1 1
      src/background.ts
  3. 0 85
      src/lib/api.js
  4. 94 0
      src/lib/api.ts
  5. 20 20
      src/lib/config.ts
  6. 0 106
      src/lib/db.js
  7. 110 0
      src/lib/db.ts
  8. 0 12
      src/lib/env.js
  9. 12 0
      src/lib/env.ts
  10. 0 364
      src/lib/image.js
  11. 449 0
      src/lib/image.ts
  12. 0 30
      src/lib/logger.js
  13. 30 0
      src/lib/logger.ts
  14. 0 39
      src/lib/promise-pool.js
  15. 37 0
      src/lib/promise-pool.ts
  16. 0 105
      src/lib/sync.js
  17. 146 0
      src/lib/sync.ts
  18. 0 67
      src/main.js
  19. 122 122
      yarn.lock

+ 4 - 3
package.json

@@ -16,8 +16,6 @@
   "main": "background.js",
   "dependencies": {
     "core-js": "^3.6.5",
-    "vue": "^3.0.0",
-    "vue-router": "^4.0.0-0",
     "gm": "^1.23.1",
     "image-size": "^0.6.3",
     "jquery": "^3.3.1",
@@ -28,10 +26,13 @@
     "mysql": "^2.16.0",
     "request": "^2.88.0",
     "requestretry": "^3.1.0",
-    "thinkjs-util": ">=0.0.1"
+    "thinkjs-util": ">=0.0.1",
+    "vue": "^3.0.0",
+    "vue-router": "^4.0.0-0"
   },
   "devDependencies": {
     "@types/electron-devtools-installer": "^2.2.0",
+    "@types/mysql": "^2.15.19",
     "@typescript-eslint/eslint-plugin": "^4.18.0",
     "@typescript-eslint/parser": "^4.18.0",
     "@vue/cli-plugin-babel": "~4.5.0",

+ 1 - 1
src/background.ts

@@ -28,7 +28,7 @@ async function createWindow() {
   if (process.env.WEBPACK_DEV_SERVER_URL) {
     // Load the url of the dev server if in development mode
     await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string);
-    // if (!process.env.IS_TEST) win.webContents.openDevTools();
+    if (!process.env.IS_TEST) win.webContents.openDevTools();
   } else {
     createProtocol("app");
     // Load the index.html when not in development

+ 0 - 85
src/lib/api.js

@@ -1,85 +0,0 @@
-const request = require('requestretry')
-const env = require('./env.js')
-const logger = require('./logger.js')('api.js')
-
-async function execute(uri, method, form) {
-    return new Promise((resolve, reject) => {
-        request({
-            url: env.server.host + uri,
-            method: method,
-            json: true,
-            timeout: 10000,
-            form: form || {},
-            headers: {
-                'auth-info': 'loginname=' + env.loginName + ';password=' + env.password
-            },
-            maxAttempts: 50,
-            retryDelay: 500,
-            retryStrategy: request.RetryStrategies.HTTPOrNetworkError
-        }, function (error, response, body) {
-            if (response.statusCode == 200) {
-                resolve(body);
-            } else {
-                let message = response.statusCode + ' ' + (error || '') + (response.headers['error-info'] || '')
-                logger.error(message)
-                reject(message)
-            }
-        })
-    })
-}
-
-module.exports.login = function () {
-    return execute('/api/user/login', 'GET');
-}
-
-module.exports.getExams = function (pageNumber, pageSize) {
-    let uri = '/api/exams'
-    let param = [];
-    if (pageNumber != undefined) {
-        param.push('pageNumber=' + pageNumber)
-    }
-    if (pageSize != undefined) {
-        param.push('pageSize=' + pageSize)
-    }
-    if (param.length > 0) {
-        uri = uri + '?' + param.join('&')
-    }
-    return execute(uri, 'GET');
-}
-
-module.exports.getStudents = function (examId, pageNumber, pageSize, params) {
-    let form = {
-        examId: examId,
-        pageNumber: pageNumber,
-        pageSize: pageSize
-    }
-    if (params != undefined) {
-        for (let key in params) {
-            if (params[key] && params[key] != '') {
-                form[key] = params[key]
-            }
-        }
-    }
-    return execute('/api/exam/students', 'POST', form);
-}
-
-module.exports.countStudents = function (examId, params) {
-    params = params || {}
-    params.examId = examId
-    return execute('/api/exam/students/count', 'POST', params)
-}
-
-module.exports.getPackages = function (examId, upload, withUrl) {
-    let uri = '/api/package/count/' + examId
-    let param = []
-    if (upload != undefined) {
-        param.push('upload=' + (upload ? 'true' : 'false'))
-    }
-    if (withUrl != undefined) {
-        param.push('withUrl=' + (withUrl ? 'true' : 'false'))
-    }
-    if (param.length > 0) {
-        uri = uri + '?' + param.join('&')
-    }
-    return execute(uri, 'GET')
-}

+ 94 - 0
src/lib/api.ts

@@ -0,0 +1,94 @@
+const request = require("requestretry");
+const env = require("./env");
+import _logger from "./logger";
+const logger = _logger("api.js");
+
+async function execute(uri, method, form) {
+  return new Promise((resolve, reject) => {
+    request(
+      {
+        url: env.server.host + uri,
+        method: method,
+        json: true,
+        timeout: 10000,
+        form: form || {},
+        headers: {
+          "auth-info":
+            "loginname=" + env.loginName + ";password=" + env.password,
+        },
+        maxAttempts: 50,
+        retryDelay: 500,
+        retryStrategy: request.RetryStrategies.HTTPOrNetworkError,
+      },
+      function (error, response, body) {
+        if (response.statusCode == 200) {
+          resolve(body);
+        } else {
+          const message =
+            response.statusCode +
+            " " +
+            (error || "") +
+            (response.headers["error-info"] || "");
+          logger.error(message);
+          reject(message);
+        }
+      }
+    );
+  });
+}
+
+export function login() {
+  return execute("/api/user/login", "GET");
+}
+
+export function getExams(pageNumber, pageSize) {
+  let uri = "/api/exams";
+  const param = [];
+  if (pageNumber != undefined) {
+    param.push("pageNumber=" + pageNumber);
+  }
+  if (pageSize != undefined) {
+    param.push("pageSize=" + pageSize);
+  }
+  if (param.length > 0) {
+    uri = uri + "?" + param.join("&");
+  }
+  return execute(uri, "GET");
+}
+
+export function getStudents(examId, pageNumber, pageSize, params) {
+  const form = {
+    examId: examId,
+    pageNumber: pageNumber,
+    pageSize: pageSize,
+  };
+  if (params != undefined) {
+    for (const key in params) {
+      if (params[key] && params[key] != "") {
+        form[key] = params[key];
+      }
+    }
+  }
+  return execute("/api/exam/students", "POST", form);
+}
+
+export function countStudents(examId, params) {
+  params = params || {};
+  params.examId = examId;
+  return execute("/api/exam/students/count", "POST", params);
+}
+
+export function getPackages(examId, upload, withUrl) {
+  let uri = "/api/package/count/" + examId;
+  const param = [];
+  if (upload != undefined) {
+    param.push("upload=" + (upload ? "true" : "false"));
+  }
+  if (withUrl != undefined) {
+    param.push("withUrl=" + (withUrl ? "true" : "false"));
+  }
+  if (param.length > 0) {
+    uri = uri + "?" + param.join("&");
+  }
+  return execute(uri, "GET");
+}

+ 20 - 20
src/lib/config.ts

@@ -1,7 +1,7 @@
 import fs from "fs";
 import path from "path";
 import moment from "moment";
-import env from "./env.js";
+import env from "./env";
 
 console.log(__dirname);
 const configJson = fs.readFileSync(path.join(__dirname, "../config.json"));
@@ -9,25 +9,25 @@ const store = JSON.parse(configJson.toString());
 
 export default store;
 
-// const timeLog = path.join(__dirname, "../../logs/time.log");
-// let syncTime = {};
-// if (fs.existsSync(path.join(__dirname, "../../logs/time.log"))) {
-//   try {
-//     syncTime = fs.readFileSync(timeLog).toJSON();
-//   } catch (err) {
-//     syncTime = {};
-//   }
-// }
-// store["syncTime"] = syncTime;
+const timeLog = path.join(__dirname, "../../logs/time.log");
+let syncTime = {};
+if (fs.existsSync(path.join(__dirname, "../../logs/time.log"))) {
+  try {
+    syncTime = fs.readFileSync(timeLog).toJSON();
+  } catch (err) {
+    syncTime = {};
+  }
+}
+store["syncTime"] = syncTime;
 
 // module.exports = store;
 
-// module.exports.updateSyncTime = function () {
-//   if (env.server && env.exam) {
-//     syncTime[env.server.host + "_" + env.exam.id] = moment().format(
-//       "YYYY-MM-DD HH:mm:ss"
-//     );
-//     store["syncTime"] = syncTime;
-//     fs.writeFileSync(timeLog, JSON.stringify(syncTime));
-//   }
-// };
+export function updateSyncTime(): void {
+  if (env.server && env.exam) {
+    syncTime[env.server.host + "_" + env.exam.id] = moment().format(
+      "YYYY-MM-DD HH:mm:ss"
+    );
+    store["syncTime"] = syncTime;
+    fs.writeFileSync(timeLog, JSON.stringify(syncTime));
+  }
+}

+ 0 - 106
src/lib/db.js

@@ -1,106 +0,0 @@
-const mysql = require('mysql')
-const config = require('./config.js')
-
-var pool = undefined;
-
-function initPool() {
-    pool = mysql.createPool({
-        host: config.db.host,
-        port: config.db.port,
-        user: config.db.user,
-        password: config.db.password,
-        database: config.db.database || '',
-        charset: 'utf8',
-        connectionLimit: 10
-    });
-}
-
-function getTransactionConnection() {
-    return new Promise((resolve, reject) => {
-        if (pool == undefined) {
-            reject('db not init');
-        }
-        pool.getConnection((err, connection) => {
-            if (err) {
-                reject(err);
-            } else {
-                connection.beginTransaction((err) => {
-                    if (err) {
-                        reject(err);
-                    } else {
-                        resolve(connection);
-                    }
-                });
-            }
-        });
-    });
-}
-
-function query(connection, sql, datas) {
-    return new Promise((resolve, reject) => {
-        connection.query(sql, datas, (err, results, fields) => {
-            if (err) {
-                reject(err);
-            } else {
-                resolve(results, fields);
-            }
-        });
-    });
-}
-
-module.exports.init = function () {
-    if (pool != undefined) {
-        pool.end();
-        pool = undefined;
-    }
-    initPool();
-}
-
-module.exports.close = function(){
-    if(pool!=undefined){
-        pool.end();
-        pool = undefined;
-    }
-}
-
-module.exports.query = function (sql, datas) {
-    return new Promise((resolve, reject) => {
-        pool.query(sql, datas, (err, results, fields) => {
-            if (err) {
-                reject(err);
-            } else {
-                resolve(results, fields);
-            }
-        });
-    });
-}
-
-module.exports.batchQuery = function (sql, datas) {
-    return new Promise((resolve, reject) => {
-        getTransactionConnection().then(connection => {
-            let results = [];
-            for (var i = 0; i < datas.length; i++) {
-                results.push(query(connection, sql, datas[i]));
-            }
-
-            Promise.all(results).then(() => {
-                connection.commit(err => {
-                    if (err) {
-                        connection.rollback(() => {
-                            reject(err);
-                        });
-                    } else {
-                        connection.release();
-                        resolve(datas.length);
-                    }
-                });
-            }).catch(err => {
-                connection.rollback(() => {
-                    reject(err);
-                })
-            });
-        }).catch(err => {
-            reject(err);
-        });
-    });
-}

+ 110 - 0
src/lib/db.ts

@@ -0,0 +1,110 @@
+import mysql from "mysql";
+import config from "./config";
+
+let pool = undefined;
+
+function initPool() {
+  pool = mysql.createPool({
+    host: config.db.host,
+    port: config.db.port,
+    user: config.db.user,
+    password: config.db.password,
+    database: config.db.database || "",
+    charset: "utf8",
+    connectionLimit: 10,
+  });
+}
+
+function getTransactionConnection() {
+  return new Promise((resolve, reject) => {
+    if (pool == undefined) {
+      reject("db not init");
+    }
+    pool.getConnection((err, connection) => {
+      if (err) {
+        reject(err);
+      } else {
+        connection.beginTransaction((err) => {
+          if (err) {
+            reject(err);
+          } else {
+            resolve(connection);
+          }
+        });
+      }
+    });
+  });
+}
+
+function query(connection, sql, datas) {
+  return new Promise((resolve, reject) => {
+    connection.query(sql, datas, (err, results, fields) => {
+      if (err) {
+        reject(err);
+      } else {
+        resolve(results, fields);
+      }
+    });
+  });
+}
+
+export funtion init () {
+  if (pool != undefined) {
+    pool.end();
+    pool = undefined;
+  }
+  initPool();
+};
+
+export funtion close () {
+  if (pool != undefined) {
+    pool.end();
+    pool = undefined;
+  }
+};
+
+export funtion query (sql, datas) {
+  return new Promise((resolve, reject) => {
+    pool.query(sql, datas, (err, results, fields) => {
+      if (err) {
+        reject(err);
+      } else {
+        resolve(results, fields);
+      }
+    });
+  });
+};
+
+export funtion batchQuery (sql, datas) {
+  return new Promise((resolve, reject) => {
+    getTransactionConnection()
+      .then((connection) => {
+        const results = [];
+        for (let i = 0; i < datas.length; i++) {
+          results.push(query(connection, sql, datas[i]));
+        }
+
+        Promise.all(results)
+          .then(() => {
+            connection.commit((err) => {
+              if (err) {
+                connection.rollback(() => {
+                  reject(err);
+                });
+              } else {
+                connection.release();
+                resolve(datas.length);
+              }
+            });
+          })
+          .catch((err) => {
+            connection.rollback(() => {
+              reject(err);
+            });
+          });
+      })
+      .catch((err) => {
+        reject(err);
+      });
+  });
+};

+ 0 - 12
src/lib/env.js

@@ -1,12 +0,0 @@
-var store = {}
-
-module.exports = store;
-
-module.exports.merge = function (obj) {
-    if (obj == undefined) {
-        return
-    }
-    for (var field in obj) {
-        store[field] = obj[field]
-    }
-}

+ 12 - 0
src/lib/env.ts

@@ -0,0 +1,12 @@
+const store = {};
+
+export default store;
+
+export function merge(obj): void {
+  if (obj == undefined) {
+    return;
+  }
+  for (const field in obj) {
+    store[field] = obj[field];
+  }
+}

+ 0 - 364
src/lib/image.js

@@ -1,364 +0,0 @@
-//云端图片操作工具
-const EventEmitter = require('events')
-const api = require('./api.js')
-const env = require('./env.js')
-const config = require('./config.js')
-const logger = require('./logger.js')('image.js')
-const downloadLogger = require('./logger.js')('download')
-const fs = require('fs')
-const path = require('path')
-const readline = require('readline')
-const request_util = require('requestretry')
-const sizeOf = require('image-size')
-const mustache = require('mustache')
-const mkdirp = require('mkdirp')
-const gm = config.imagemagick != undefined ? require('gm').subClass({
-    imageMagick: true,
-    appPath: config.imagemagick
-}) : require('gm')
-
-class executor extends EventEmitter {
-
-    async readFile(file) {
-        return new Promise(resolve => {
-            var data = []
-            if (fs.existsSync(file)) {
-                let reader = readline.createInterface({
-                    input: fs.createReadStream(file)
-                })
-                reader.on('line', line => {
-                    data.push(line)
-                })
-                reader.on('close', () => {
-                    resolve(data)
-                })
-            } else {
-                resolve(data)
-            }
-        })
-    }
-
-    async addWatermark(image, file, student, index, trackMode) {
-        let fontFile = config.watermark.fontFile
-        let color = config.watermark.color
-        let size = sizeOf(image)
-        let imgData = gm(image)
-        //添加第一页的得分明细
-        if (index == 1) {
-            //初始坐标
-            let x = 30
-            let y = 10
-            //最大宽/高限制
-            let fontSize = config.watermark.fontSize || 30
-            let maxX = size.width / 2 - x * 2
-            let height = fontSize + 10
-            //计算总分
-            let totalScore = (parseFloat(student.objectiveScore) || 0) + (parseFloat(student.subjectiveScore) || 0)
-            //显示总分明细
-            imgData.font(fontFile, fontSize).fill(color)
-            imgData.drawText(x, y += height, '成绩明细')
-            //普通考试模式,按客观+主观模式显示总分
-            if (trackMode === '1') {
-                imgData.drawText(x, y += height, '总分=(客观+主观) | ' + totalScore + '=' + student.objectiveScore + '+' + student.subjectiveScore)
-            }
-            //研究生考试模式,只显示总分
-            else if (trackMode === '2') {
-                imgData.drawText(x, y += height, '总分=' + totalScore + '分')
-            }
-            //显示客观题明细
-            if (student.objectiveScoreDetail && student.objectiveScoreDetail.length > 0) {
-                let lines = []
-                let array = []
-                //前置提示文字的字符数
-                let count = 10
-                lines.push(array)
-                for (let i = 0; i < student.objectiveScoreDetail.length; i++) {
-                    let detail = student.objectiveScoreDetail[i]
-                    let content = detail.answer + ':' + detail.score
-                    //超长后另起一行显示客观题
-                    if ((count + content.length) * fontSize * 0.7 > maxX) {
-                        array = []
-                        lines.push(array)
-                        count = 10
-                    }
-                    array.push(content)
-                    count += content.length
-                }
-                //显示所有行的客观题明细
-                for (let l = 0; l < lines.length; l++) {
-                    imgData.drawText(x, y += height, '客观题识别结果 | ' + lines[l].join(';'))
-                }
-            }
-            //显示复核人
-            if (student.inspector) {
-                imgData.drawText(x, y += height, '复核人: ' + student.inspector.loginName)
-            }
-            //显示主观题明细
-            if (student.subjectiveScoreDetail && student.subjectiveScoreDetail.length > 0) {
-                //普通考试模式,按小题显示明细
-                if (trackMode === '1') {
-                    let title = '主观题号 | 分数 | 评卷员 | 仲裁员'
-                    let startY = y
-                    let width = title.length * fontSize
-                    imgData.drawText(x, y += height, title)
-                    for (let i = 0; i < student.subjectiveScoreDetail.length; i++) {
-                        let detail = student.subjectiveScoreDetail[i]
-                        //超过最大高度了则另起一列
-                        if ((y + height + 15) > size.height) {
-                            y = startY
-                            x += width
-                            imgData.drawText(x, y += height, title)
-                        }
-                        let content = detail.mainNumber + '-' + detail.subNumber + ' : ' + detail.score +
-                            ' ' + (detail.marker || '') +
-                            ' ' + (detail.header || '')
-                        width = Math.max(width, content.length * fontSize)
-                        imgData.drawText(x, y += height, content)
-                    }
-                }
-                //研究生考试模式,按分组显示明细
-                else if (trackMode === '2') {
-                    let title = '评卷分组 | 总分 | 评卷员 | 仲裁员'
-                    let startY = y
-                    let width = title.length * fontSize
-                    imgData.drawText(x, y += height, title)
-                    //所有小题得分按评卷分组聚合
-                    let maxGroupNumber = 0
-                    let groups = {}
-                    for (let i = 0; i < student.subjectiveScoreDetail.length; i++) {
-                        let detail = student.subjectiveScoreDetail[i]
-                        let group = groups[detail.groupNumber]
-                        if (group == undefined) {
-                            group = {
-                                number: detail.groupNumber,
-                                score: 0,
-                                title: {},
-                                titleString: [],
-                                marker: {},
-                                markerString: [],
-                                header: {},
-                                headerString: []
-                            }
-                            groups[detail.groupNumber] = group
-                            maxGroupNumber = Math.max(maxGroupNumber, group.number)
-                        }
-                        group.score = group.score + detail.score
-                        if (detail.mainTitle && !group.title[detail.mainTitle]) {
-                            group.titleString.push(detail.mainTitle)
-                            group.title[detail.mainTitle] = true
-                        }
-                        if (detail.marker && !group.marker[detail.marker]) {
-                            group.markerString.push(detail.marker)
-                            group.marker[detail.marker] = true
-                        }
-                        if (detail.header && !group.header[detail.header]) {
-                            group.headerString.push(detail.header)
-                            group.header[detail.header] = true
-                        }
-                    }
-                    for (let i = 1; i <= maxGroupNumber; i++) {
-                        let group = groups[i]
-                        if (group != undefined) {
-                            //超过最大高度了则另起一列
-                            if ((y + height + 15) > size.height) {
-                                y = startY
-                                x += width
-                                imgData.drawText(x, y += height, title)
-                            }
-                            let content = group.number + '(' + group.titleString.join(',') + ')' +
-                                ' ' + group.score +
-                                ' ' + group.markerString.join(',') +
-                                ' ' + group.headerString.join(',')
-                            width = Math.max(width, content.length * fontSize)
-                            imgData.drawText(x, y += height, content)
-                        }
-                    }
-                }
-            }
-        }
-        //显示评卷标记
-        if (student.tags != undefined && student.tags[index] != undefined) {
-            let fontSize = 60
-            let height = fontSize + 10
-            imgData.font(fontFile, fontSize).fill(color)
-            let tags = student.tags[index]
-            for (let i = 0; i < tags.length; i++) {
-                let tag = tags[i]
-                if (tag.content != undefined) {
-                    let top = tag.top
-                    for (let j = 0; j < tag.content.length; j++) {
-                        imgData.drawText(tag.left, top, tag.content[j])
-                        top += height
-                    }
-                }
-            }
-        }
-
-        return new Promise((resolve, reject) => {
-            imgData.write(file, error => {
-                if (error) {
-                    logger.error('add watermark error: ' + file)
-                    logger.error(error)
-                    reject(error)
-                } else {
-                    resolve()
-                }
-            })
-        })
-    }
-
-    async downloadUrl(url) {
-        return new Promise((resolve, reject) => {
-            request_util({
-                url: url,
-                method: 'GET',
-                encoding: null,
-                timeout: 3000,
-                maxAttempts: 3,
-                retryDelay: 500,
-                retryStrategy: request_util.RetryStrategies.HTTPOrNetworkError
-            }, function (error, response, body) {
-                if (!error && response.statusCode == 200) {
-                    resolve(body)
-                } else {
-                    logger.error(error || (url + ' download error'))
-                    error = error || {}
-                    error.code = response ? response.statusCode : 500
-                    reject(error)
-                }
-            })
-        })
-    }
-
-    async downloadFile(type, append, url, localTemplate, data, dir, index, watermark, trackMode) {
-        data.index = index
-        let local = path.join(dir, mustache.render(localTemplate, data))
-        mkdirp.sync(path.dirname(local))
-
-        //续传模式下,判断目标文件是否存在,存在则直接跳过
-        if (append && fs.existsSync(local)) {
-            return Promise.resolve()
-        } else {
-            let imgData
-            try {
-                imgData = await this.downloadUrl(url)
-            } catch (err) {
-                if (err.code === 404) {
-                    //文件不存在,记录日志并跳过
-                    downloadLogger.error('404 ' + type + ' ' + url)
-                    return Promise.resolve()
-                } else {
-                    logger.error(err)
-                    return Promise.reject(err)
-                }
-            }
-
-            //是否需要添加分数水印
-            if (watermark) {
-                return this.addWatermark(imgData, local, data, index, trackMode)
-            } else {
-                return new Promise((resolve, reject) => {
-                    fs.writeFile(local, imgData, err => {
-                        if (err) {
-                            logger.error('write image file error: ' + local)
-                            logger.error(err)
-                            reject(err)
-                        } else {
-                            resolve()
-                        }
-                    })
-                })
-            }
-        }
-    }
-
-    async downloadSheet(dir, template, append, failover, watermark, trackMode, params) {
-        params.upload = true
-        params.withSheetUrl = true
-        params.withScoreDetail = watermark === true
-        params.withMarkTrack = watermark === true
-        params.withGroupScoreTrack = watermark === true && trackMode === '1'
-
-        try {
-            let totalCount = await api.countStudents(env.examId, params)
-            this.emit('total', totalCount)
-
-            let count = 0
-            let pageNumber = 0
-            this.emit('count', 0)
-            for (; ;) {
-                pageNumber++
-                let array = await api.getStudents(env.examId, pageNumber, 10, params)
-                if (array == undefined || array.length == 0) {
-                    break
-                }
-                for (let i = 0; i < array.length; i++) {
-                    let promises = []
-                    let student = array[i]
-                    student.examId = env.examId
-                    for (let i = 0; i < student.sheetUrls.length; i++) {
-                        promises.push(this.downloadFile('sheet', append, student.sheetUrls[i], template, student, dir, i + 1, watermark, trackMode))
-                    }
-                    try {
-                        //等待所有图片下载完毕
-                        await Promise.all(promises)
-                        count++
-                        this.emit('count', count)
-                    } catch (err) {
-                        //判断是否异常终止
-                        if (failover) {
-                            throw err
-                        } else {
-                            logger.error('download sheet error:' + err)
-                            logger.error(err)
-                            continue
-                        }
-                    }
-                }
-            }
-            this.emit('finish')
-        } catch (error) {
-            logger.error('download sheet error:' + error)
-            logger.error(error)
-            this.emit('error', error)
-        }
-    }
-
-    async downloadPackage(dir, template, append, failover) {
-        try {
-            let array = await api.getPackages(env.examId, true, true)
-            this.emit('total', array.length)
-            let count = 0
-            this.emit('count', 0)
-            for (let i = 0; i < array.length; i++) {
-                let p = array[i]
-                p.examId = env.examId
-                for (let i = 0; i < p.urls.length; i++) {
-                    try {
-                        await this.downloadFile('package', append, p.urls[i], template, p, dir, i + 1)
-                    } catch (err) {
-                        //判断是否异常终止
-                        if (failover) {
-                            throw err
-                        } else {
-                            logger.error('download package error: ' + err)
-                            logger.error(err)
-                            continue
-                        }
-                    }
-                }
-                count++
-                this.emit('count', count)
-            }
-            this.emit('finish')
-        } catch (error) {
-            logger.error('download package error: ' + error)
-            logger.error(error)
-            this.emit('error', error)
-        }
-    }
-}
-
-module.exports = function () {
-    return new executor()
-}

+ 449 - 0
src/lib/image.ts

@@ -0,0 +1,449 @@
+//云端图片操作工具
+import EventEmitter from "events";
+import api from "./api";
+import env from "./env";
+import config from "./config";
+import fs from "fs";
+import path from "path";
+import readline from "readline";
+import request_util from "requestretry";
+import sizeOf from "image-size";
+import mustache from "mustache";
+import mkdirp from "mkdirp";
+
+import _logger from "./logger";
+const logger = _logger("image.js");
+const downloadLogger = _logger("download");
+
+const gm =
+  config.imagemagick != undefined
+    ? require("gm").subClass({
+        imageMagick: true,
+        appPath: config.imagemagick,
+      })
+    : require("gm");
+
+class executor extends EventEmitter {
+  async readFile(file) {
+    return new Promise((resolve) => {
+      const data = [];
+      if (fs.existsSync(file)) {
+        const reader = readline.createInterface({
+          input: fs.createReadStream(file),
+        });
+        reader.on("line", (line) => {
+          data.push(line);
+        });
+        reader.on("close", () => {
+          resolve(data);
+        });
+      } else {
+        resolve(data);
+      }
+    });
+  }
+
+  async addWatermark(image, file, student, index, trackMode) {
+    const fontFile = config.watermark.fontFile;
+    const color = config.watermark.color;
+    const size = sizeOf(image);
+    const imgData = gm(image);
+    //添加第一页的得分明细
+    if (index == 1) {
+      //初始坐标
+      let x = 30;
+      let y = 10;
+      //最大宽/高限制
+      const fontSize = config.watermark.fontSize || 30;
+      const maxX = size.width / 2 - x * 2;
+      const height = fontSize + 10;
+      //计算总分
+      const totalScore =
+        (parseFloat(student.objectiveScore) || 0) +
+        (parseFloat(student.subjectiveScore) || 0);
+      //显示总分明细
+      imgData.font(fontFile, fontSize).fill(color);
+      imgData.drawText(x, (y += height), "成绩明细");
+      //普通考试模式,按客观+主观模式显示总分
+      if (trackMode === "1") {
+        imgData.drawText(
+          x,
+          (y += height),
+          "总分=(客观+主观) | " +
+            totalScore +
+            "=" +
+            student.objectiveScore +
+            "+" +
+            student.subjectiveScore
+        );
+      }
+      //研究生考试模式,只显示总分
+      else if (trackMode === "2") {
+        imgData.drawText(x, (y += height), "总分=" + totalScore + "分");
+      }
+      //显示客观题明细
+      if (
+        student.objectiveScoreDetail &&
+        student.objectiveScoreDetail.length > 0
+      ) {
+        const lines = [];
+        let array = [];
+        //前置提示文字的字符数
+        let count = 10;
+        lines.push(array);
+        for (let i = 0; i < student.objectiveScoreDetail.length; i++) {
+          const detail = student.objectiveScoreDetail[i];
+          const content = detail.answer + ":" + detail.score;
+          //超长后另起一行显示客观题
+          if ((count + content.length) * fontSize * 0.7 > maxX) {
+            array = [];
+            lines.push(array);
+            count = 10;
+          }
+          array.push(content);
+          count += content.length;
+        }
+        //显示所有行的客观题明细
+        for (let l = 0; l < lines.length; l++) {
+          imgData.drawText(
+            x,
+            (y += height),
+            "客观题识别结果 | " + lines[l].join(";")
+          );
+        }
+      }
+      //显示复核人
+      if (student.inspector) {
+        imgData.drawText(
+          x,
+          (y += height),
+          "复核人: " + student.inspector.loginName
+        );
+      }
+      //显示主观题明细
+      if (
+        student.subjectiveScoreDetail &&
+        student.subjectiveScoreDetail.length > 0
+      ) {
+        //普通考试模式,按小题显示明细
+        if (trackMode === "1") {
+          const title = "主观题号 | 分数 | 评卷员 | 仲裁员";
+          const startY = y;
+          let width = title.length * fontSize;
+          imgData.drawText(x, (y += height), title);
+          for (let i = 0; i < student.subjectiveScoreDetail.length; i++) {
+            const detail = student.subjectiveScoreDetail[i];
+            //超过最大高度了则另起一列
+            if (y + height + 15 > size.height) {
+              y = startY;
+              x += width;
+              imgData.drawText(x, (y += height), title);
+            }
+            const content =
+              detail.mainNumber +
+              "-" +
+              detail.subNumber +
+              " : " +
+              detail.score +
+              " " +
+              (detail.marker || "") +
+              " " +
+              (detail.header || "");
+            width = Math.max(width, content.length * fontSize);
+            imgData.drawText(x, (y += height), content);
+          }
+        }
+        //研究生考试模式,按分组显示明细
+        else if (trackMode === "2") {
+          const title = "评卷分组 | 总分 | 评卷员 | 仲裁员";
+          const startY = y;
+          let width = title.length * fontSize;
+          imgData.drawText(x, (y += height), title);
+          //所有小题得分按评卷分组聚合
+          let maxGroupNumber = 0;
+          const groups = {};
+          for (let i = 0; i < student.subjectiveScoreDetail.length; i++) {
+            const detail = student.subjectiveScoreDetail[i];
+            let group = groups[detail.groupNumber];
+            if (group == undefined) {
+              group = {
+                number: detail.groupNumber,
+                score: 0,
+                title: {},
+                titleString: [],
+                marker: {},
+                markerString: [],
+                header: {},
+                headerString: [],
+              };
+              groups[detail.groupNumber] = group;
+              maxGroupNumber = Math.max(maxGroupNumber, group.number);
+            }
+            group.score = group.score + detail.score;
+            if (detail.mainTitle && !group.title[detail.mainTitle]) {
+              group.titleString.push(detail.mainTitle);
+              group.title[detail.mainTitle] = true;
+            }
+            if (detail.marker && !group.marker[detail.marker]) {
+              group.markerString.push(detail.marker);
+              group.marker[detail.marker] = true;
+            }
+            if (detail.header && !group.header[detail.header]) {
+              group.headerString.push(detail.header);
+              group.header[detail.header] = true;
+            }
+          }
+          for (let i = 1; i <= maxGroupNumber; i++) {
+            const group = groups[i];
+            if (group != undefined) {
+              //超过最大高度了则另起一列
+              if (y + height + 15 > size.height) {
+                y = startY;
+                x += width;
+                imgData.drawText(x, (y += height), title);
+              }
+              const content =
+                group.number +
+                "(" +
+                group.titleString.join(",") +
+                ")" +
+                " " +
+                group.score +
+                " " +
+                group.markerString.join(",") +
+                " " +
+                group.headerString.join(",");
+              width = Math.max(width, content.length * fontSize);
+              imgData.drawText(x, (y += height), content);
+            }
+          }
+        }
+      }
+    }
+    //显示评卷标记
+    if (student.tags != undefined && student.tags[index] != undefined) {
+      const fontSize = 60;
+      const height = fontSize + 10;
+      imgData.font(fontFile, fontSize).fill(color);
+      const tags = student.tags[index];
+      for (let i = 0; i < tags.length; i++) {
+        const tag = tags[i];
+        if (tag.content != undefined) {
+          let top = tag.top;
+          for (let j = 0; j < tag.content.length; j++) {
+            imgData.drawText(tag.left, top, tag.content[j]);
+            top += height;
+          }
+        }
+      }
+    }
+
+    return new Promise((resolve, reject) => {
+      imgData.write(file, (error) => {
+        if (error) {
+          logger.error("add watermark error: " + file);
+          logger.error(error);
+          reject(error);
+        } else {
+          resolve();
+        }
+      });
+    });
+  }
+
+  async downloadUrl(url) {
+    return new Promise((resolve, reject) => {
+      request_util(
+        {
+          url: url,
+          method: "GET",
+          encoding: null,
+          timeout: 3000,
+          maxAttempts: 3,
+          retryDelay: 500,
+          retryStrategy: request_util.RetryStrategies.HTTPOrNetworkError,
+        },
+        function (error, response, body) {
+          if (!error && response.statusCode == 200) {
+            resolve(body);
+          } else {
+            logger.error(error || url + " download error");
+            error = error || {};
+            error.code = response ? response.statusCode : 500;
+            reject(error);
+          }
+        }
+      );
+    });
+  }
+
+  async downloadFile(
+    type,
+    append,
+    url,
+    localTemplate,
+    data,
+    dir,
+    index,
+    watermark,
+    trackMode
+  ) {
+    data.index = index;
+    const local = path.join(dir, mustache.render(localTemplate, data));
+    mkdirp.sync(path.dirname(local));
+
+    //续传模式下,判断目标文件是否存在,存在则直接跳过
+    if (append && fs.existsSync(local)) {
+      return Promise.resolve();
+    } else {
+      let imgData;
+      try {
+        imgData = await this.downloadUrl(url);
+      } catch (err) {
+        if (err.code === 404) {
+          //文件不存在,记录日志并跳过
+          downloadLogger.error("404 " + type + " " + url);
+          return Promise.resolve();
+        } else {
+          logger.error(err);
+          return Promise.reject(err);
+        }
+      }
+
+      //是否需要添加分数水印
+      if (watermark) {
+        return this.addWatermark(imgData, local, data, index, trackMode);
+      } else {
+        return new Promise((resolve, reject) => {
+          fs.writeFile(local, imgData, (err) => {
+            if (err) {
+              logger.error("write image file error: " + local);
+              logger.error(err);
+              reject(err);
+            } else {
+              resolve();
+            }
+          });
+        });
+      }
+    }
+  }
+
+  async downloadSheet(
+    dir,
+    template,
+    append,
+    failover,
+    watermark,
+    trackMode,
+    params
+  ) {
+    params.upload = true;
+    params.withSheetUrl = true;
+    params.withScoreDetail = watermark === true;
+    params.withMarkTrack = watermark === true;
+    params.withGroupScoreTrack = watermark === true && trackMode === "1";
+
+    try {
+      const totalCount = await api.countStudents(env.examId, params);
+      this.emit("total", totalCount);
+
+      let count = 0;
+      let pageNumber = 0;
+      this.emit("count", 0);
+      for (;;) {
+        pageNumber++;
+        const array = await api.getStudents(env.examId, pageNumber, 10, params);
+        if (array == undefined || array.length == 0) {
+          break;
+        }
+        for (let i = 0; i < array.length; i++) {
+          const promises = [];
+          const student = array[i];
+          student.examId = env.examId;
+          for (let i = 0; i < student.sheetUrls.length; i++) {
+            promises.push(
+              this.downloadFile(
+                "sheet",
+                append,
+                student.sheetUrls[i],
+                template,
+                student,
+                dir,
+                i + 1,
+                watermark,
+                trackMode
+              )
+            );
+          }
+          try {
+            //等待所有图片下载完毕
+            await Promise.all(promises);
+            count++;
+            this.emit("count", count);
+          } catch (err) {
+            //判断是否异常终止
+            if (failover) {
+              throw err;
+            } else {
+              logger.error("download sheet error:" + err);
+              logger.error(err);
+              continue;
+            }
+          }
+        }
+      }
+      this.emit("finish");
+    } catch (error) {
+      logger.error("download sheet error:" + error);
+      logger.error(error);
+      this.emit("error", error);
+    }
+  }
+
+  async downloadPackage(dir, template, append, failover) {
+    try {
+      const array = await api.getPackages(env.examId, true, true);
+      this.emit("total", array.length);
+      let count = 0;
+      this.emit("count", 0);
+      for (let i = 0; i < array.length; i++) {
+        const p = array[i];
+        p.examId = env.examId;
+        for (let i = 0; i < p.urls.length; i++) {
+          try {
+            await this.downloadFile(
+              "package",
+              append,
+              p.urls[i],
+              template,
+              p,
+              dir,
+              i + 1
+            );
+          } catch (err) {
+            //判断是否异常终止
+            if (failover) {
+              throw err;
+            } else {
+              logger.error("download package error: " + err);
+              logger.error(err);
+              continue;
+            }
+          }
+        }
+        count++;
+        this.emit("count", count);
+      }
+      this.emit("finish");
+    } catch (error) {
+      logger.error("download package error: " + error);
+      logger.error(error);
+      this.emit("error", error);
+    }
+  }
+}
+
+export default function () {
+  return new executor();
+}

+ 0 - 30
src/lib/logger.js

@@ -1,30 +0,0 @@
-const log4js = require('log4js')
-const path = require('path')
-const config = require('./config.js')
-
-log4js.configure({
-    appenders: {
-        everything: {
-            type: 'dateFile',
-            filename: path.join(__dirname, '../../logs/app.log')
-        },
-        download: {
-            type: 'file',
-            filename: path.join(__dirname, '../../logs/download.log')
-        }
-    },
-    categories: {
-        default: {
-            appenders: ['everything'],
-            level: config.logger.level
-        },
-        download: {
-            appenders: ['download'],
-            level: config.logger.level
-        }
-    }
-})
-
-module.exports = function (module) {
-    return log4js.getLogger(module)
-}

+ 30 - 0
src/lib/logger.ts

@@ -0,0 +1,30 @@
+import log4js from "log4js";
+import path from "path";
+import config from "./config";
+
+log4js.configure({
+  appenders: {
+    everything: {
+      type: "dateFile",
+      filename: path.join(__dirname, "../../logs/app.log"),
+    },
+    download: {
+      type: "file",
+      filename: path.join(__dirname, "../../logs/download.log"),
+    },
+  },
+  categories: {
+    default: {
+      appenders: ["everything"],
+      level: config.logger.level,
+    },
+    download: {
+      appenders: ["download"],
+      level: config.logger.level,
+    },
+  },
+});
+
+export default function (module) {
+  return log4js.getLogger(module);
+}

+ 0 - 39
src/lib/promise-pool.js

@@ -1,39 +0,0 @@
-//Promise并发控制池
-const EventEmitter = require('events')
-
-class PromisePool extends EventEmitter {
-
-    constructor(concurrent, promiseCreator) {
-        super()
-        this.concurrent = concurrent || 5
-        this.promiseCreator = promiseCreator
-    }
-
-    start(datas) {
-        let self = this
-        let index = 0
-        let running = 0
-        return new Promise(resolved => {
-            let execute = () => {
-                running++
-                self.promiseCreator(datas[index++]).finally(() => {
-                    self.emit('count', index)
-                    running--
-                    if (running < self.concurrent && datas.length > index) {
-                        execute()
-                    } else if (datas.length == index && running == 0) {
-                        resolved()
-                    }
-                })
-            }
-            while (running < self.concurrent && datas.length > index) {
-                execute()
-            }
-        })
-    }
-
-}
-
-exports.create = function(concurrent, promiseCreator) {
-    return new PromisePool(concurrent, promiseCreator)
-}

+ 37 - 0
src/lib/promise-pool.ts

@@ -0,0 +1,37 @@
+//Promise并发控制池
+import EventEmitter from "events";
+
+class PromisePool extends EventEmitter {
+  constructor(concurrent, promiseCreator) {
+    super();
+    this.concurrent = concurrent || 5;
+    this.promiseCreator = promiseCreator;
+  }
+
+  start(datas) {
+    const self = this;
+    let index = 0;
+    let running = 0;
+    return new Promise((resolved) => {
+      const execute = () => {
+        running++;
+        self.promiseCreator(datas[index++]).finally(() => {
+          self.emit("count", index);
+          running--;
+          if (running < self.concurrent && datas.length > index) {
+            execute();
+          } else if (datas.length == index && running == 0) {
+            resolved();
+          }
+        });
+      };
+      while (running < self.concurrent && datas.length > index) {
+        execute();
+      }
+    });
+  }
+}
+
+export function create(concurrent, promiseCreator) {
+  return new PromisePool(concurrent, promiseCreator);
+}

+ 0 - 105
src/lib/sync.js

@@ -1,105 +0,0 @@
-//云端数据同步导本地
-const EventEmitter = require('events')
-const db = require('./db.js')
-const env = require('./env.js')
-const api = require('./api.js')
-const logger = require('./logger.js')('sync.js')
-
-class executor extends EventEmitter {
-
-    async start() {
-        try {
-            db.init()
-
-            let totalCount = await api.countStudents(env.examId)
-            logger.info('student count: ' + totalCount)
-            this.emit('total', totalCount)
-
-            let promises = []
-            let count = 0
-            let pageNumber = 0
-            this.emit('student', 0)
-            while (true) {
-                pageNumber++
-                logger.info('student page=' + pageNumber)
-                let array = await api.getStudents(env.examId, pageNumber, 100, {
-                    withScoreDetail: true
-                })
-                if (array == undefined || array.length == 0) {
-                    break
-                }
-                let datas = []
-                for (var i = 0; i < array.length; i++) {
-                    var obj = array[i]
-                    var objList = []
-                    var subList = []
-                    if (obj.objectiveScoreDetail) {
-                        obj.objectiveScoreDetail.forEach(detail => {
-                            let score = Math.round(detail.score * 10 / 10)
-                            objList.push(detail.answer + ':' + score)
-                        })
-                    }
-                    if (obj.subjectiveScoreDetail) {
-                        obj.subjectiveScoreDetail.forEach(detail => {
-                            let score = Math.round(detail.score * 10 / 10)
-                            subList.push(score)
-                        })
-                    }
-                    datas.push([obj['id'], env.examId, obj['schoolId'], obj['examNumber'], obj['examNumber'], obj['name'], obj['studentCode'], obj['subjectCode'],
-                        obj['subjectName'], obj['campusName'], obj['packageCode'], obj['batchCode'], obj['sheetCount'], obj['sliceCount'], obj['answers'],
-                        obj['upload'] ? 1 : 0, obj['absent'] ? 1 : 0, obj['breach'] ? 1 : 0, obj['manualAbsent'] ? 1 : 0, obj['objectiveScore'].length > 0 ? obj['objectiveScore'] : 0,
-                        obj['subjectiveScore'].length > 0 ? obj['subjectiveScore'] : 0, objList.join(';'), subList.join(';'), obj['subjectiveStatus'] || 'UNMARK',
-                        obj['examSite'], obj['examRoom'], obj['remark'], obj['college'] || '', obj['className'] || '', obj['teacher'] || '', obj['paperType'] || ''
-                    ])
-                }
-                //console.log('get:' + array.length)
-                promises.push(new Promise((resolve, reject) => {
-                    db.batchQuery('replace into eb_exam_student(id, exam_id, school_id\
-                        , exam_number, secret_number, name, student_code, subject_code, subject_name, campus_name\
-                        , package_code, batch_code, sheet_count, slice_count, answers, is_upload\
-                        , is_absent, is_manual_absent, is_breach, is_exception\
-                        , objective_score, subjective_score, objective_score_list, subjective_score_list, subjective_status\
-                        , exam_site, exam_room, remark, college, class_name, teacher, paper_type) \
-                        values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0,?,?,?,?,?,?,?,?,?,?,?,?)',
-                        datas).then(() => {
-                        count += datas.length
-                        this.emit('student', count)
-                        resolve()
-                    }).catch((err) => {
-                        reject(err)
-                    })
-                }))
-            }
-
-            //get and save package
-            this.emit('package', 0)
-            let packages = await api.getPackages(env.examId)
-            let packageData = []
-            for (let i = 0; i < packages.length; i++) {
-                let obj = packages[i]
-                packageData.push([env.examId, obj['code'], obj['picCount']])
-            }
-            logger.info('package count:' + packageData.length)
-            promises.push(new Promise((resolve, reject) => {
-                db.batchQuery('replace into eb_exam_package(exam_id, code, pic_count) values (?,?,?)', packageData).then(() => {
-                    this.emit('package', packageData.length)
-                    resolve()
-                }).catch((err) => {
-                    reject(err)
-                })
-            }))
-
-            await Promise.all(promises)
-            this.emit('finish')
-        } catch (err) {
-            this.emit('error', err)
-            logger.error(err)
-        } finally {
-            //console.log(moment().format('YYYY-MM-DD HH:mm:ss'))
-        }
-    }
-}
-
-module.exports = function () {
-    return new executor()
-}

+ 146 - 0
src/lib/sync.ts

@@ -0,0 +1,146 @@
+//云端数据同步导本地
+import EventEmitter from "events";
+import db from "./db";
+import env from "./env";
+import api from "./api";
+
+import _logger from "./logger";
+const logger = _logger("sync.js");
+
+class executor extends EventEmitter {
+  async start() {
+    try {
+      db.init();
+
+      const totalCount = await api.countStudents(env.examId);
+      logger.info("student count: " + totalCount);
+      this.emit("total", totalCount);
+
+      const promises = [];
+      let count = 0;
+      let pageNumber = 0;
+      this.emit("student", 0);
+      while (true) {
+        pageNumber++;
+        logger.info("student page=" + pageNumber);
+        const array = await api.getStudents(env.examId, pageNumber, 100, {
+          withScoreDetail: true,
+        });
+        if (array == undefined || array.length == 0) {
+          break;
+        }
+        const datas = [];
+        for (let i = 0; i < array.length; i++) {
+          const obj = array[i];
+          var objList = [];
+          var subList = [];
+          if (obj.objectiveScoreDetail) {
+            obj.objectiveScoreDetail.forEach((detail) => {
+              const score = Math.round((detail.score * 10) / 10);
+              objList.push(detail.answer + ":" + score);
+            });
+          }
+          if (obj.subjectiveScoreDetail) {
+            obj.subjectiveScoreDetail.forEach((detail) => {
+              const score = Math.round((detail.score * 10) / 10);
+              subList.push(score);
+            });
+          }
+          datas.push([
+            obj["id"],
+            env.examId,
+            obj["schoolId"],
+            obj["examNumber"],
+            obj["examNumber"],
+            obj["name"],
+            obj["studentCode"],
+            obj["subjectCode"],
+            obj["subjectName"],
+            obj["campusName"],
+            obj["packageCode"],
+            obj["batchCode"],
+            obj["sheetCount"],
+            obj["sliceCount"],
+            obj["answers"],
+            obj["upload"] ? 1 : 0,
+            obj["absent"] ? 1 : 0,
+            obj["breach"] ? 1 : 0,
+            obj["manualAbsent"] ? 1 : 0,
+            obj["objectiveScore"].length > 0 ? obj["objectiveScore"] : 0,
+            obj["subjectiveScore"].length > 0 ? obj["subjectiveScore"] : 0,
+            objList.join(";"),
+            subList.join(";"),
+            obj["subjectiveStatus"] || "UNMARK",
+            obj["examSite"],
+            obj["examRoom"],
+            obj["remark"],
+            obj["college"] || "",
+            obj["className"] || "",
+            obj["teacher"] || "",
+            obj["paperType"] || "",
+          ]);
+        }
+        //console.log('get:' + array.length)
+        promises.push(
+          new Promise((resolve, reject) => {
+            db.batchQuery(
+              "replace into eb_exam_student(id, exam_id, school_id\
+                        , exam_number, secret_number, name, student_code, subject_code, subject_name, campus_name\
+                        , package_code, batch_code, sheet_count, slice_count, answers, is_upload\
+                        , is_absent, is_manual_absent, is_breach, is_exception\
+                        , objective_score, subjective_score, objective_score_list, subjective_score_list, subjective_status\
+                        , exam_site, exam_room, remark, college, class_name, teacher, paper_type) \
+                        values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0,?,?,?,?,?,?,?,?,?,?,?,?)",
+              datas
+            )
+              .then(() => {
+                count += datas.length;
+                this.emit("student", count);
+                resolve();
+              })
+              .catch((err) => {
+                reject(err);
+              });
+          })
+        );
+      }
+
+      //get and save package
+      this.emit("package", 0);
+      const packages = await api.getPackages(env.examId);
+      const packageData = [];
+      for (let i = 0; i < packages.length; i++) {
+        const obj = packages[i];
+        packageData.push([env.examId, obj["code"], obj["picCount"]]);
+      }
+      logger.info("package count:" + packageData.length);
+      promises.push(
+        new Promise((resolve, reject) => {
+          db.batchQuery(
+            "replace into eb_exam_package(exam_id, code, pic_count) values (?,?,?)",
+            packageData
+          )
+            .then(() => {
+              this.emit("package", packageData.length);
+              resolve();
+            })
+            .catch((err) => {
+              reject(err);
+            });
+        })
+      );
+
+      await Promise.all(promises);
+      this.emit("finish");
+    } catch (err) {
+      this.emit("error", err);
+      logger.error(err);
+    } finally {
+      //console.log(moment().format('YYYY-MM-DD HH:mm:ss'))
+    }
+  }
+}
+
+module.exports = function () {
+  return new executor();
+};

+ 0 - 67
src/main.js

@@ -1,67 +0,0 @@
-// Modules to control application life and create native browser window
-const {
-    app,
-    BrowserWindow
-} = require('electron')
-const config = require('./lib/config.js')
-
-// Keep a global reference of the window object, if you don't, the window will
-// be closed automatically when the JavaScript object is garbage collected.
-let mainWindow
-
-function createWindow() {
-    //console.log(app.getAppPath()
-
-    // Create the browser window.
-    mainWindow = new BrowserWindow({
-        width: 1100,
-        height: 720,
-        resizable: false,
-        webPreferences: {
-            enableRemoteModule: true,
-            nodeIntegration: true,
-            nodeIntegrationInWorker: true
-        }
-    })
-
-    // and load the index.html of the app.
-    mainWindow.loadFile('view/login.html')
-
-    // Open the DevTools.
-    if (config.openDevTools === true) {
-        mainWindow.webContents.openDevTools()
-    }
-
-    // Emitted when the window is closed.
-    mainWindow.on('closed', function () {
-        // Dereference the window object, usually you would store windows
-        // in an array if your app supports multi windows, this is the time
-        // when you should delete the corresponding element.
-        mainWindow = null
-    })
-}
-
-// This method will be called when Electron has finished
-// initialization and is ready to create browser windows.
-// Some APIs can only be used after this event occurs.
-app.on('ready', createWindow)
-
-// Quit when all windows are closed.
-app.on('window-all-closed', function () {
-    // On OS X it is common for applications and their menu bar
-    // to stay active until the user quits explicitly with Cmd + Q
-    //if (process.platform !== 'darwin') {
-    app.quit()
-    //}
-})
-
-app.on('activate', function () {
-    // On OS X it's common to re-create a window in the app when the
-    // dock icon is clicked and there are no other windows open.
-    if (mainWindow === null) {
-        createWindow()
-    }
-})
-
-// In this file you can include the rest of your app's specific main process
-// code. You can also put them in separate files and require them here.

File diff suppressed because it is too large
+ 122 - 122
yarn.lock


Some files were not shown because too many files changed in this diff