|
@@ -1,8 +1,10 @@
|
|
'use strict';
|
|
'use strict';
|
|
-
|
|
|
|
|
|
+const assert = require('assert');
|
|
const _ = require('lodash');
|
|
const _ = require('lodash');
|
|
const fs = require('fs');
|
|
const fs = require('fs');
|
|
const Excel = require('exceljs');
|
|
const Excel = require('exceljs');
|
|
|
|
+const Path = require('path');
|
|
|
|
+const XLSX = require('xlsx');
|
|
const { CrudService } = require('naf-framework-mongoose/lib/service');
|
|
const { CrudService } = require('naf-framework-mongoose/lib/service');
|
|
const { BusinessError, ErrorCode } = require('naf-core').Error;
|
|
const { BusinessError, ErrorCode } = require('naf-core').Error;
|
|
const moment = require('moment');
|
|
const moment = require('moment');
|
|
@@ -15,6 +17,7 @@ class UtilService extends CrudService {
|
|
constructor(ctx) {
|
|
constructor(ctx) {
|
|
super(ctx);
|
|
super(ctx);
|
|
this.mq = this.ctx.mq;
|
|
this.mq = this.ctx.mq;
|
|
|
|
+ this.sModel = this.ctx.model.School;
|
|
}
|
|
}
|
|
async updatedate() {
|
|
async updatedate() {
|
|
let date = new Date();
|
|
let date = new Date();
|
|
@@ -198,7 +201,7 @@ XQIDAQAB
|
|
if (sort && _.isString(sort)) {
|
|
if (sort && _.isString(sort)) {
|
|
sort = { [sort]: desc ? -1 : 1 };
|
|
sort = { [sort]: desc ? -1 : 1 };
|
|
} else if (sort && _.isArray(sort)) {
|
|
} else if (sort && _.isArray(sort)) {
|
|
- sort = sort.map(f => ({ [f]: desc ? -1 : 1 })).reduce((p, c) => ({ ...p, ...c }), {});
|
|
|
|
|
|
+ sort = sort.map((f) => ({ [f]: desc ? -1 : 1 })).reduce((p, c) => ({ ...p, ...c }), {});
|
|
}
|
|
}
|
|
return { skip, limit, sort };
|
|
return { skip, limit, sort };
|
|
}
|
|
}
|
|
@@ -402,7 +405,7 @@ XQIDAQAB
|
|
const c = [];
|
|
const c = [];
|
|
if (title) {
|
|
if (title) {
|
|
const tit = new Paragraph({
|
|
const tit = new Paragraph({
|
|
- children: [ new TextRun({ text: title, bold: true }) ],
|
|
|
|
|
|
+ children: [new TextRun({ text: title, bold: true })],
|
|
heading: HeadingLevel.TITLE,
|
|
heading: HeadingLevel.TITLE,
|
|
alignment: AlignmentType.CENTER,
|
|
alignment: AlignmentType.CENTER,
|
|
});
|
|
});
|
|
@@ -410,7 +413,7 @@ XQIDAQAB
|
|
}
|
|
}
|
|
if (author) {
|
|
if (author) {
|
|
const auth = new Paragraph({
|
|
const auth = new Paragraph({
|
|
- children: [ new TextRun({ color: '#000000', text: author }) ],
|
|
|
|
|
|
+ children: [new TextRun({ color: '#000000', text: author })],
|
|
heading: HeadingLevel.HEADING_2,
|
|
heading: HeadingLevel.HEADING_2,
|
|
alignment: AlignmentType.RIGHT,
|
|
alignment: AlignmentType.RIGHT,
|
|
});
|
|
});
|
|
@@ -419,7 +422,7 @@ XQIDAQAB
|
|
if (content && _.isArray(content) && content.length > 0) {
|
|
if (content && _.isArray(content) && content.length > 0) {
|
|
for (const cont of content) {
|
|
for (const cont of content) {
|
|
const p = new Paragraph({
|
|
const p = new Paragraph({
|
|
- children: [ new TextRun({ text: cont, bold: true }) ],
|
|
|
|
|
|
+ children: [new TextRun({ text: cont, bold: true })],
|
|
});
|
|
});
|
|
c.push(p);
|
|
c.push(p);
|
|
}
|
|
}
|
|
@@ -526,6 +529,115 @@ XQIDAQAB
|
|
await archive.finalize();
|
|
await archive.finalize();
|
|
return `/files${zipPath}${fn}`;
|
|
return `/files${zipPath}${fn}`;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // 学校计划人数导出
|
|
|
|
+ async schoolDownload() {
|
|
|
|
+ const { app } = this;
|
|
|
|
+ const headList = [
|
|
|
|
+ { key: 'name', header: '学校', width: 50, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
|
|
|
|
+ { key: 'code', header: '学校代码', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
|
|
|
|
+ { key: 'class_special', header: '特殊班', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
|
|
|
|
+ { key: 'class_common', header: '普通班', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
|
|
|
|
+ ];
|
|
|
|
+ const nowDate = new Date().getTime();
|
|
|
|
+ const filename = `学校计划人数信息导出-${nowDate}.xlsx`;
|
|
|
|
+ const rootPath = `${app.config.cdn.repos_root_path}`;
|
|
|
|
+ const rooturl = `${app.config.cdn.repos_root_url_experience}`;
|
|
|
|
+ const path = `${rootPath}${rooturl}`;
|
|
|
|
+ if (!path) {
|
|
|
|
+ throw new BusinessError(ErrorCode.BUSINESS, '服务端没有设置存储路径');
|
|
|
|
+ }
|
|
|
|
+ if (!fs.existsSync(path)) {
|
|
|
|
+ // 如果不存在文件夹,就创建
|
|
|
|
+ this.mkdir(path);
|
|
|
|
+ }
|
|
|
|
+ const AggregateInfo = [
|
|
|
|
+ // 去除重复数据
|
|
|
|
+ { $group: { _id: '$code', uniqueData: { $first: '$$ROOT' } } },
|
|
|
|
+ { $replaceRoot: { newRoot: '$uniqueData' } },
|
|
|
|
+ { $addFields: { id: '$_id' } },
|
|
|
|
+ ];
|
|
|
|
+ const list = await this.sModel.aggregate(AggregateInfo);
|
|
|
|
+ const workbook = new Excel.Workbook();
|
|
|
|
+ const sheet = workbook.addWorksheet('sheet');
|
|
|
|
+ const meta = headList;
|
|
|
|
+ sheet.columns = meta;
|
|
|
|
+ sheet.addRows(list);
|
|
|
|
+ // 生成excel
|
|
|
|
+ const filepath = `${path}${filename}`;
|
|
|
|
+ if (list.length <= 0) return;
|
|
|
|
+ await workbook.xlsx.writeFile(filepath);
|
|
|
|
+ return `/files/excel/${filename}`;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 学校计划人数导入
|
|
|
|
+ async schoolImport(data) {
|
|
|
|
+ const { filepath } = data;
|
|
|
|
+ assert(filepath, 'filepath不能为空');
|
|
|
|
+ // 取得excle中数据
|
|
|
|
+ const _filepath = this.ctx.app.config.baseUrl + filepath;
|
|
|
|
+ const teadatas = await this.getImportXLSXData(_filepath);
|
|
|
|
+ const school = [];
|
|
|
|
+ for (const val of teadatas) {
|
|
|
|
+ const schoolInfo = await this.sModel.findOne({ name: val.name }).lean();
|
|
|
|
+ const classnum = [];
|
|
|
|
+ let num;
|
|
|
|
+ if (val.class_special) classnum.push({ number: val.class_special, name: '特殊班', code: '1' });
|
|
|
|
+ if (val.class_common) classnum.push({ number: val.class_common, name: '普通班', code: '0' });
|
|
|
|
+ if (val.class_special && val.class_common) num = parseInt(val.class_special) || 0 + parseInt(val.class_common) || 0;
|
|
|
|
+ school.push({
|
|
|
|
+ _id: schoolInfo._id.toString(),
|
|
|
|
+ num,
|
|
|
|
+ code: val.code,
|
|
|
|
+ classnum,
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ return { school };
|
|
|
|
+ }
|
|
|
|
+ // 获取导入的XLSX文件中的数据
|
|
|
|
+ async getImportXLSXData(filepath) {
|
|
|
|
+ const file = await this.ctx.curl(filepath);
|
|
|
|
+ const workbook = XLSX.read(file.data);
|
|
|
|
+ // 读取内容
|
|
|
|
+ let exceldata = [];
|
|
|
|
+ const sheetNames = workbook.SheetNames; // 获取表名
|
|
|
|
+ const sheet = workbook.Sheets[sheetNames[0]]; // 通过表名得到表对象
|
|
|
|
+ // 遍历26个字母
|
|
|
|
+ const theadRule = [];
|
|
|
|
+ const range = XLSX.utils.decode_range(sheet['!ref']);
|
|
|
|
+ const col_start = range.s.c;
|
|
|
|
+ const col_end = range.e.c;
|
|
|
|
+ for (let i = col_start; i <= col_end; i++) {
|
|
|
|
+ const addr = XLSX.utils.encode_col(i) + XLSX.utils.encode_row(0);
|
|
|
|
+ theadRule.push(sheet[addr].v);
|
|
|
|
+ }
|
|
|
|
+ const params = XLSX.utils.sheet_to_json(sheet); // 通过工具将表对象的数据读出来并转成json
|
|
|
|
+ if (!params) return [];
|
|
|
|
+ const length = params.length;
|
|
|
|
+ const _datas = [];
|
|
|
|
+ let data = {};
|
|
|
|
+ for (let i = 0; i < length; i++) {
|
|
|
|
+ data = params[i];
|
|
|
|
+ _datas.push({
|
|
|
|
+ name: data[theadRule[0]],
|
|
|
|
+ code: data[theadRule[1]],
|
|
|
|
+ class_special: data[theadRule[2]],
|
|
|
|
+ class_common: data[theadRule[3]],
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ exceldata = [...exceldata, ..._datas];
|
|
|
|
+ return exceldata;
|
|
|
|
+ }
|
|
|
|
+ // 创建文件夹
|
|
|
|
+ mkdir(dirname) {
|
|
|
|
+ if (fs.existsSync(dirname)) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ if (this.mkdir(Path.dirname(dirname))) {
|
|
|
|
+ fs.mkdirSync(dirname);
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
module.exports = UtilService;
|
|
module.exports = UtilService;
|