123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- 'use strict';
- const { CrudService } = require('naf-framework-mongoose-free/lib/service');
- const { BusinessError, ErrorCode } = require('naf-core').Error;
- const { ObjectId } = require('mongoose').Types;
- const _ = require('lodash');
- const jwt = require('jsonwebtoken');
- const assert = require('assert');
- const Excel = require('exceljs');
- const { sep } = require('path');
- const fs = require('fs');
- // 个人用户
- class PersonalService extends CrudService {
- constructor(ctx) {
- super(ctx, 'personal');
- this.redis = this.app.redis;
- this.model = this.ctx.model.User.Personal;
- this.adminModel = this.ctx.model.User.Admin;
- this.util = this.ctx.service.util.util;
- }
- async query(condition, { skip = 0, limit = 0 }) {
- const query = await this.dealQueryCondition(_.cloneDeep(condition));
- const res = await this.model
- .find(query)
- .skip(parseInt(skip))
- .limit(parseInt(limit))
- .sort({ 'meta.createdAt': -1 });
- return res;
- }
- async count(condition) {
- const query = await this.dealQueryCondition(_.cloneDeep(condition));
- const res = await this.model.count(query);
- return res;
- }
- async dealQueryCondition({ role, code, ...condition } = {}) {
- condition = this.util.dealQuery(condition);
- // 查询业务管理
- const busFind = async query => await this.adminModel.find({ ...query, role: '3' }, { code: 1 });
- // 查询机构管理
- const orgFind = async query => await this.adminModel.find({ ...query, role: '2' }, { code: 1 });
- // 查询管理员
- const aFind = async query => await this.adminModel.find({ ...query, role: '1' }, { code: 1 });
- if (role === '1' && code) {
- // 管理员查询
- // =>获取该code下的机构管理员列表 => 用机构管理员id 获取业务管理员列表 => 将code都整理出来作为查询条件
- const a = await aFind({ code });
- if (a.length <= 0) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到该管理员');
- const aid = _.get(_.head(a), '_id');
- const orgList = await orgFind({ pid: aid });
- const busList = await busFind({ pid: orgList.map(i => i._id) });
- const codes = [ ...orgList.map(i => i.code), ...busList.map(i => i.code), code ];
- condition.code = codes;
- } else if (role === '2' && code) {
- // 机构查询
- // =>获取该code下的业务管理员列表 => 将code都整理出来作为查询条件
- const o = await orgFind({ code });
- if (o.length <= 0) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到该机构');
- const oid = _.get(_.head(o), '_id');
- const busList = await busFind({ pid: oid });
- const codes = [ ...busList.map(i => i.code), code ];
- condition.code = codes;
- } else if (code) {
- // 业务查询
- // code直接查询用户返回即可
- condition.code = code;
- }
- // 没有code,超级管理员,说明不限制
- return condition;
- }
- /**
- * 创建用户
- * @param {Object} params 用户信息
- */
- async create({ password, ...data }) {
- if (!password) password = '123456';
- data.password = { secret: password };
- const { name, phone, code } = data;
- // 检查手机号
- const num = await this.model.count({ phone, code, isdel: '0' });
- if (num > 0) throw new BusinessError(ErrorCode.DATA_EXISTED, `姓名:${name} ; 手机号: ${phone} ;已有机构所属的个人用户使用该手机号`);
- return await this.model.create(data);
- }
- /**
- * 修改密码
- * @param {Object} {id,password} 用户id和密码
- */
- async password({ id, password }) {
- const object = await this.model.findById(id);
- if (!object) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到用户的信息');
- object.password = { secret: password };
- await object.save();
- }
- /**
- * 登陆
- * @param {Object} params 登陆信息
- * @property phone 手机号
- * @property password 密码
- */
- async login({ phone, password }) {
- const object = await this.model.findOne({ phone, isdel: '0' }, '+password');
- if (!object) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到用户的信息');
- const { password: op, status } = object;
- const { secret } = op;
- if (status !== '1') throw new BusinessError(ErrorCode.ACCESS_DENIED, '拒绝访问!');
- if (secret !== password) throw new BusinessError(ErrorCode.BAD_PASSWORD, '密码错误');
- const data = _.omit(JSON.parse(JSON.stringify(object)), [ 'meta', 'password', '__v' ]);
- const { secret: secrets } = this.config.jwt;
- const token = jwt.sign(data, secrets);
- // 记录登陆
- // let number = await this.redis.get('login_number') || 0;
- // number++;
- // await this.redis.set('login_number', number);
- return token;
- }
- // async delete({ id }) {
- // const object = await this.model.findById(id);
- // if (!object) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到用户的信息');
- // object.isdel = '1';
- // await object.save();
- // }
- /**
- * 个人升变专家
- * @param {Object} body 升变数据
- * 升级的时候要把所属的信息(产品,需求等)换到升级后的数据上,一种数据迁移
- */
- async upgrade({ id, ...data }) {
- assert(id, '缺少个人用户ID');
- const user = await this.model.findOne({ _id: ObjectId(id) }, '+password');
- if (!user) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到用户的信息');
- data = _.omit(data, [ 'meta', '__v' ]);
- data.user_id = id;
- // 创建专家
- const is_expert = await this.ctx.model.User.Expert.count({ user_id: ObjectId(id) });
- if (is_expert > 0) throw new BusinessError(ErrorCode.DATA_EXISTED, '已升级为专家,无需再次升级');
- await this.ctx.model.User.Expert.create(data);
- user.is_expert = true;
- await user.save();
- }
- async import({ uri }) {
- assert(uri, '未获取到文件地址');
- const domain = 'http://127.0.0.1';
- const file = await this.ctx.curl(`${domain}${uri}`);
- if (!(file && file.data)) {
- throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到指定文件');
- }
- const workbook = new Excel.Workbook();
- await workbook.xlsx.load(file.data);
- const sheet = workbook.getWorksheet(1);
- let meta = this.importMeta();
- // 根据excel,将列序号加入meta中
- const head = _.get(sheet.getRow(1), 'values', []);
- for (let i = 0; i < head.length; i++) {
- const e = head[i];
- if (!e) continue;
- const r = meta.find(f => f.column === e);
- if (r) r.index = i;
- }
- // 过滤出列表中不必要且不存在的字段
- meta = meta.filter(f => f.required || f.index);
- const errorList = [];
- const dataList = [];
- sheet.eachRow(async (row, index) => {
- if (index !== 1) {
- const values = row.values;
- const obj = {};
- for (const m of meta) {
- const { required, key, index, method, column } = m;
- const value = values[index];
- if (required && !value) {
- // 必填且没值的情况
- errorList.push({ message: `第${index}行数据,缺少必填项 ${column};` });
- continue;
- }
- if (_.isFunction(method)) obj[key] = method(value);
- else obj[key] = value;
- }
- dataList.push(obj);
- }
- });
- if (errorList.length > 0) return errorList;
- for (const i of dataList) {
- try {
- await this.create(i);
- } catch (error) {
- errorList.push(error);
- }
- }
- return errorList;
- }
- importMeta() {
- return [
- { required: true, column: '用户名', key: 'name' },
- { required: true, column: '手机号', key: 'phone' },
- { required: true, column: '登录密码', key: 'password' },
- { required: true, column: '邀请码', key: 'code' },
- { column: '身份证号', key: 'card' },
- { column: '电子邮箱', key: 'email' },
- { column: '联系地址', key: 'addr' },
- { column: '办公电话', key: 'office_phone' },
- { column: '所属辖区', key: 'juris' },
- { column: '院校', key: 'school' },
- { column: '专业', key: 'major' },
- { column: '职务职称', key: 'zwzc' },
- { column: '审核状态', key: 'status' },
- ];
- }
- async export() {
- // 查询位置
- let skip = 0;
- // 一次处理多少条数据
- const limit = 500;
- const total = await this.model.count({ isdel: 0 });
- // 循环次数
- const times = _.ceil(_.divide(total, limit));
- const nowDate = new Date().getTime();
- // 文件 名称 及 路径
- const filename = `个人用户导出-${nowDate}.xlsx`;
- const root_path = _.get(this.ctx.app.config.export, 'root_path');
- if (!fs.existsSync(`${root_path}`)) {
- // 如果不存在文件夹,就创建
- fs.mkdirSync(`${root_path}`);
- }
- const excel_path = `${sep}excel${sep}`;
- const path = `${root_path}${excel_path}`;
- if (!fs.existsSync(path)) {
- // 如果不存在文件夹,就创建
- fs.mkdirSync(path);
- }
- // 流式写入
- const options = {
- filename: `${path}${filename}`,
- };
- const workbook = new Excel.stream.xlsx.WorkbookWriter(options);
- const sheet = workbook.addWorksheet('sheet');
- const eMeta = this.exportMeta();
- const eColumn = eMeta.map(i => i.key);
- let needDeal = eMeta.map((i, index) => {
- if (_.isFunction(_.get(i, 'method'))) {
- i.index = index;
- return i;
- }
- });
- needDeal = _.compact(needDeal);
- for (let i = 1; i <= times; i++) {
- if (i === 1) {
- const head = eMeta.map(i => i.zh);
- sheet.addRow(head).commit();
- }
- const data = await this.model.find({ isdel: 0 }, '+password').skip(skip).limit(limit);
- for (const d of data) {
- const dup = Object.values(_.pick(d, eColumn));
- // 处理特殊情况
- for (const nd of needDeal) {
- const { index, method } = nd;
- dup[index] = method(dup[index]);
- }
- // 添加数据
- sheet.addRow(dup).commit();
- }
- skip += limit;
- }
- sheet.commit();
- await workbook.commit();
- return `/files/excel/${filename}`;
- }
- exportMeta() {
- return [
- { key: 'name', zh: '用户名' },
- { key: 'phone', zh: '手机号' },
- { key: 'password', zh: '登录密码', method: i => _.get(i, 'secret') },
- { key: 'code', zh: '邀请码' },
- { key: 'card', zh: '身份证号' },
- { key: 'email', zh: '邮箱' },
- { key: 'addr', zh: '地址' },
- { key: 'office_phone', zh: '办公电话' },
- { key: 'juris', zh: '所属辖区' },
- { key: 'school', zh: '院系' },
- { key: 'major', zh: '专业' },
- { key: 'zwzc', zh: '职务职称' },
- { key: 'status', zh: '审核状态' },
- ];
- }
- }
- module.exports = PersonalService;
|