teacher.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. 'use strict';
  2. const assert = require('assert');
  3. const _ = require('lodash');
  4. const moment = require('moment');
  5. const XLSX = require('xlsx');
  6. const { CrudService } = require('naf-framework-mongoose/lib/service');
  7. const { BusinessError, ErrorCode } = require('naf-core').Error;
  8. const stringRandom = require('string-random');
  9. const { ObjectId } = require('mongoose').Types;
  10. class TeacherService extends CrudService {
  11. constructor(ctx) {
  12. super(ctx, 'teacher');
  13. this.model = this.ctx.model.Teacher;
  14. this.umodel = this.ctx.model.User;
  15. const { baseUrl } = _.get(this.ctx.app.config, 'mission');
  16. if (baseUrl) this.missionBase = baseUrl;
  17. this.yjy = this.ctx.model.Yjyconnect;
  18. this.export_limit = 50;
  19. }
  20. async query({ name, ...info } = {}, { skip = 0, limit = 0 } = {}) {
  21. const query = { ...info };
  22. if (name) {
  23. query.name = { $regex: name };
  24. }
  25. let rs = await this.model.find(query, null, { skip, limit }).exec();
  26. if (rs.length > 0) rs = JSON.parse(JSON.stringify(rs));
  27. const ids = rs.map((i) => ObjectId(i._id).toString());
  28. const users = await this.umodel.find({ uid: ids }).lean();
  29. const uids = users.map((i) => ObjectId(i._id).toString());
  30. const yjyUsers = await this.yjy.find({ suid: uids }).lean();
  31. for (const i of rs) {
  32. const { _id: did } = i;
  33. const user = users.find((f) => ObjectId(did).equals(f.uid));
  34. if (!user) {
  35. i.is_bind = false;
  36. continue;
  37. }
  38. const yjyUser = yjyUsers.find((f) => ObjectId(user._id).equals(f.suid));
  39. if (yjyUser) i.is_bind = true;
  40. else i.is_bind = false;
  41. }
  42. return rs;
  43. }
  44. async count({ name, ...info } = {}) {
  45. const query = { ...info };
  46. if (name) {
  47. query.name = { $regex: name };
  48. }
  49. const res = await this.model.count(query);
  50. return res;
  51. }
  52. // 根据状态删除教师信息
  53. async deleteByStatus({ status }) {
  54. await this.model.deleteMany({ status });
  55. return 'deleted';
  56. }
  57. // 查询详情
  58. async fetchTeacher({ id }) {
  59. // 将文件拼到查询到的数据后
  60. return await this.model.findById(id, '+file');
  61. }
  62. async create(data) {
  63. const { name, phone: mobile } = data;
  64. const user = await this.umodel.findOne({ mobile });
  65. if (user) {
  66. throw new BusinessError(ErrorCode.DATA_EXIST, '电话已经存在');
  67. }
  68. // 创建教师信息
  69. const res = await this.model.create(data);
  70. // 再创建教师用户
  71. const passwd = stringRandom();
  72. const newdata = {
  73. name,
  74. mobile,
  75. type: '3',
  76. uid: ObjectId(res.id || res._id).toString(),
  77. passwd: { secret: passwd },
  78. };
  79. await this.umodel.create(newdata);
  80. // 返回教师信息
  81. return this.model.findById(res._id || res.id);
  82. }
  83. async delete({ id }) {
  84. await this.model.findByIdAndDelete(id);
  85. await this.umodel.deleteOne({ uid: id, type: '3' });
  86. return 'deleted';
  87. }
  88. // 批量查询教师
  89. async fetchteachers({ ids }) {
  90. return await this.model.find({ _id: { $in: ids } });
  91. }
  92. // 检查教师
  93. async checkarrange({ id, date }) {}
  94. async status(data) {
  95. const { teachersid, zlscore, msscore, status, remark } = data;
  96. for (const teacherid of teachersid) {
  97. const teacher = await this.model.findById(teacherid);
  98. console.log(status);
  99. teacher.status = status;
  100. if (zlscore) {
  101. teacher.zlscore = zlscore;
  102. }
  103. if (msscore) {
  104. teacher.msscore = msscore;
  105. }
  106. await teacher.save();
  107. let detail = '';
  108. if (status === '1') {
  109. const userInfo = await this.umodel.findOne({ uid: teacherid }, '+passwd');
  110. if (!userInfo) continue;
  111. const passwd = _.get(userInfo, 'passwd.secret');
  112. detail = '您的账号身份已确认,密码为:' + passwd + '请尽快登录账号上传课件资料附件';
  113. } else if (status === '4') {
  114. detail = '您已通过审核被正式录入教师库';
  115. }
  116. const date = await this.ctx.service.util.updatedate();
  117. const user = await this.umodel.findOne({ uid: teacher.id, type: '3' });
  118. if (user && user.openid) {
  119. await this.ctx.service.weixin.sendTemplateMsg(this.ctx.app.config.REVIEW_TEMPLATE_ID, user.openid, '您有一个新的通知', detail, date, remark);
  120. } else {
  121. await this.ctx.service.util.sendMail(teacher.email, '账号审核', detail, '');
  122. }
  123. }
  124. }
  125. // 教室分数上传
  126. async teaimport(data) {
  127. const { filepath } = data;
  128. assert(filepath, 'filepath不能为空');
  129. // 取得excle中数据
  130. const _filepath = this.ctx.app.config.baseUrl + filepath;
  131. const teadatas = await this.getImportXLSXData(_filepath);
  132. // 将得到的数据校验
  133. const datacheck = await this.datacheck(teadatas);
  134. if (datacheck.errorcode === '1') {
  135. return datacheck;
  136. }
  137. // 将数据存入数据库中
  138. for (const tea of teadatas) {
  139. const res = await this.model.findOne({
  140. idnumber: tea.idnumber,
  141. name: tea.name,
  142. });
  143. if (res) {
  144. res.beforescore = tea.xsscore;
  145. await res.save();
  146. }
  147. }
  148. return datacheck;
  149. }
  150. // 获取导入的XLSX文件中的数据
  151. async getImportXLSXData(filepath) {
  152. const file = await this.ctx.curl(filepath);
  153. const workbook = XLSX.read(file.data);
  154. // 读取内容
  155. let exceldata = [];
  156. const sheetNames = workbook.SheetNames; // 获取表名
  157. const sheet = workbook.Sheets[sheetNames[0]]; // 通过表名得到表对象
  158. // 遍历26个字母
  159. const theadRule = [];
  160. const range = XLSX.utils.decode_range(sheet['!ref']);
  161. const col_start = range.s.c;
  162. const col_end = range.e.c;
  163. for (let i = col_start; i <= col_end; i++) {
  164. const addr = XLSX.utils.encode_col(i) + XLSX.utils.encode_row(0);
  165. theadRule.push(sheet[addr].v);
  166. }
  167. const params = XLSX.utils.sheet_to_json(sheet); // 通过工具将表对象的数据读出来并转成json
  168. if (!params) return [];
  169. const length = params.length;
  170. const _datas = [];
  171. let data = {};
  172. for (let i = 0; i < length; i++) {
  173. data = params[i];
  174. _datas.push({
  175. idnumber: data[theadRule[1]],
  176. name: data[theadRule[2]],
  177. xsscore: data[theadRule[3]],
  178. });
  179. }
  180. exceldata = [...exceldata, ..._datas];
  181. return exceldata;
  182. }
  183. // 获取导入的XLSX文件中的数据
  184. async datacheck(studatas) {
  185. let errorcode = '0';
  186. const errormsg = [];
  187. for (const data of studatas) {
  188. // 判断是否为空
  189. if (!data.idnumber) {
  190. errorcode = '1';
  191. data.msg = data.msg + '身份证号不允许为空,';
  192. }
  193. if (!data.name) {
  194. errorcode = '1';
  195. data.msg = data.msg + '姓名不允许为空,';
  196. }
  197. if (!data.xsscore) {
  198. errorcode = '1';
  199. data.msg = data.msg + '评分不允许为空,';
  200. }
  201. if (errorcode === '1') {
  202. errormsg.push(data);
  203. }
  204. }
  205. return { errorcode, errormsg };
  206. }
  207. // 建立任务
  208. async toExport(body) {
  209. const fn = `教师导出 ${moment().format('YYYY-MM-DD HH:SS:mm')}`;
  210. const data = {
  211. title: fn,
  212. params: {
  213. project: 'center',
  214. service: 'teacher',
  215. method: 'export',
  216. body,
  217. },
  218. };
  219. if (this.missionBase) {
  220. const url = `${this.missionBase}/api/mission`;
  221. const res = await this.ctx.curl(url, {
  222. method: 'post',
  223. headers: {
  224. 'content-type': 'application/json',
  225. },
  226. data,
  227. dataType: 'json',
  228. });
  229. if (res.status !== 200 || res.data.errcode !== 0) {
  230. throw new BusinessError(ErrorCode.SERVICE_FAULT, '创建任务失败');
  231. }
  232. } else {
  233. throw new BusinessError(ErrorCode.SERVICE_FAULT, '未找到任务项目设置');
  234. }
  235. }
  236. // 导出
  237. async export({ missionid, model }) {
  238. assert(missionid, '缺少任务信息,无法执行任务');
  239. try {
  240. const head = model.map((i) => {
  241. // const { zh, model } = i;
  242. // const headObj = { header: zh };
  243. // if (model) headObj.key = model;
  244. // headObj.width = 20;
  245. // return headObj;
  246. return i.zh;
  247. });
  248. let fn = '教师导出';
  249. let skip = 0;
  250. const total = await this.count({});
  251. const times = Math.ceil(total / this.export_limit);
  252. let dp = null;
  253. const { downloadPath, fn: nfn } = await this.ctx.service.util.toAsyncExcel([head], fn, dp);
  254. dp = downloadPath;
  255. fn = nfn;
  256. for (let i = 0; i < times; i++) {
  257. const data = await this.query({}, { skip, limit: this.export_limit });
  258. if (data.length <= 0) break;
  259. const dataList = data.map((i) => {
  260. const obj = [];
  261. for (const m of model) {
  262. obj.push(i[m.model]);
  263. }
  264. return obj;
  265. });
  266. await this.ctx.service.util.toAsyncExcel(dataList, fn, dp);
  267. skip = skip + this.export_limit;
  268. const per = Math.ceil(((i + 1) / times) * 100);
  269. this.ctx.service.util.updateProcess(missionid, per);
  270. }
  271. this.ctx.service.util.updateProcess(missionid, '100', '2', {
  272. uri: dp,
  273. });
  274. } catch (error) {
  275. console.log('in function:error');
  276. this.ctx.service.util.updateProcess(missionid, undefined, '3');
  277. }
  278. }
  279. }
  280. module.exports = TeacherService;