'use strict'; const { isString, isArray, cloneDeep, head: getHead, get, omit } = require('lodash'); const { isNullOrUndefined, trimData } = require('naf-core').Util; const assert = require('assert'); const { ObjectId } = require('mongoose').Types; const { BusinessError, ErrorCode } = require('naf-core').Error; const { NafService } = require('./naf-service'); class CrudService extends NafService { /** * 创建前处理函数 * @param {Object} data 请求参数体 */ async beforeCreate(data) { return data; } /** * 创建后处理函数 * @param {Object} body 请求参数体 * @param {Object} data 数据 */ async afterCreate(body, data) { return data; } async create(data) { assert(data); // TODO:保存数据 data = await this.beforeCreate(data); let res = await this.model.create(data); res = await this.afterCreate(data, res); return res; } /** * 修改前处理函数;需要将 查询条件和数据返回 (提供重写用,免去中间件) * @param {Object} filter 查询条件 * @param {Object} update 数据 * @return {Object} 返回查询条件和数据 */ async beforeUpdate(filter, update) { return { filter, update }; } /** * 修改后处理函数;需要将 数据返回 (提供重写用,免去中间件) * @param {Object} filter 查询条件 * @param {Object} body 请求参数体 * @param {Object} data 数据 * @return {Object} 返回修改后的数据 */ async afterUpdate(filter, body, data) { return data; } async update(filter, update, { projection } = {}) { assert(filter); assert(update); const beforeUpdateResult = await this.beforeUpdate(filter, update); filter = beforeUpdateResult.filter; update = beforeUpdateResult.body; const { _id, id } = filter; if (_id || id) filter = { _id: ObjectId(_id || id) }; // TODO:检查数据是否存在 const entity = await this.model.findOne(filter).exec(); if (isNullOrUndefined(entity)) throw new BusinessError(ErrorCode.DATA_NOT_EXIST); // TODO: 修改数据 entity.set(trimData(update)); await entity.save(); let reSearchData = await this.model.findOne(filter, projection).exec(); reSearchData = await this.afterUpdate(filter, update, reSearchData); return reSearchData; } /** * 删除前处理函数 * @param {Object} filter 查询条件 * @return {Object} 查询条件 */ async beforeDelete(filter) { return filter; } /** * 删除后处理函数 * @param {Object} filter 查询条件 */ async afterDelete(filter) {} async delete(filter) { assert(filter); filter = await this.beforeDelete(filter); const { _id, id } = filter; if (_id || id) { await this.model.findByIdAndDelete(_id || id).exec(); } else { await this.model.deleteMany(filter).exec(); } await this.afterDelete(filter); return 'deleted'; } /** * 查询前处理函数 * @param {Object} filter 查询条件 * @return {Object} 查询条件 */ async beforeFetch(filter) { return filter; } /** * 查询后处理函数 * @param {Object} filter 查询条件 * @param {Object} data 数据 * @return {Object} 查询条件 */ async afterFetch(filter, data) { return data; } async fetch(filter, { sort, desc, projection } = {}) { assert(filter); filter = await this.beforeFetch(filter); const { _id, id } = filter; if (_id || id) filter = { _id: ObjectId(_id || id) }; // 处理排序 if (sort && isString(sort)) { sort = { [sort]: desc ? -1 : 1 }; } else if (sort && isArray(sort)) { sort = sort.map((f) => ({ [f]: desc ? -1 : 1 })).reduce((p, c) => ({ ...p, ...c }), {}); } let res = await this.model.findOne(filter, projection).exec(); res = await this.afterFetch(filter, res); return res; } async beforeQuery(filter) { return filter; } async afterQuery(filter, data) { return data; } async query(filter, { skip = 0, limit, sort, desc, projection } = {}) { // 处理排序 if (sort && isString(sort)) { sort = { [sort]: desc ? -1 : 1 }; } else if (sort && isArray(sort)) { sort = sort.map((f) => ({ [f]: desc ? -1 : 1 })).reduce((p, c) => ({ ...p, ...c }), {}); } let condition = cloneDeep(filter); condition = await this.beforeQuery(condition); condition = this.dealFilter(condition); // const pipeline = [{ $match: condition }]; // // 先排序 // if (sort) pipeline.push({ $sort: sort }); // // 再进行分页 // if (limit) { // pipeline.push({ $skip: parseInt(skip) }); // pipeline.push({ $limit: parseInt(limit) }); // } // // 再将数据过滤 // if (projection) pipeline.push({ $project: projection }); // else { // const defaultProject = await this.getSelectFalse(); // if (defaultProject) pipeline.push({ $project: defaultProject }); // } // let rs = await this.model.aggregate(pipeline); // 过滤出ref字段 const refMods = await this.getRefMods(); const populate = refMods.map((i) => i.col); // 带ref查询 let rs = await this.model.find(trimData(condition), projection, { skip, limit, sort }).populate(populate).exec(); rs = JSON.parse(JSON.stringify(rs)); // 整理ref数据 rs = rs.map((i) => { for (const obj of refMods) { const { col, prop, type } = obj; if (!prop) continue; if (isArray(prop)) { for (const p of prop) { if (type === 'String') i[`${col}_${p}`] = get(i, `${col}.${p}`); if (type === 'Array') { const list = []; const oList = get(i, `${col}`); for (const d of oList) { const obj = { _id: d._id }; obj[p] = get(d, p); list.push(obj); } i[`${col}_${p}`] = list; } } i[col] = get(i, `${col}._id`); } } return i; }); rs = await this.afterQuery(filter, rs); return rs; } async getRefMods() { const mod = await this.getModel(); const refMods = []; for (const key in mod) { if (!mod[key].ref) continue; refMods.push({ col: key, prop: mod[key].getProp, type: mod[key].type.name }); } return refMods; } async count(filter) { let condition = cloneDeep(filter); condition = await this.beforeQuery(condition); condition = this.dealFilter(condition); const count = await this.model.count(condition); // const res = await this.model.aggregate([{ $match: condition }, { $count: 'id' }]); // if (res && isArray(res)) { // const head = getHead(res); // count = get(head, 'id', 0); // } return count; } async queryAndCount(filter, options) { filter = this.dealFilter(filter); const total = await this.count(filter); if (total === 0) return { total, data: [] }; const rs = await this.query(filter, options); return { total, data: rs }; } dealFilter(filter) { return this.turnFilter(this.turnDateRangeQuery(filter)); } turnFilter(filter) { const str = /^%\S*%$/; let keys = Object.keys(filter); for (const key of keys) { const res = key.match(str); if (res) { const newKey = key.slice(1, key.length - 1); if (!ObjectId.isValid(filter[key])) filter[newKey] = new RegExp(filter[key]); delete filter[key]; } } // 再次过滤数据,将数组的数据都变成{$in:value},因为查询变成了聚合查询 keys = Object.keys(filter); for (const key of keys) { if (isArray(filter[key])) { filter[key] = { $in: filter[key] }; } else if (filter[key] === 'true' || filter[key] === 'false') { // 布尔类型的值检查,如果是布尔类型,则将字符串转为布尔 filter[key] = filter[key] === 'true'; } } return filter; } turnDateRangeQuery(filter) { const keys = Object.keys(filter); const times = []; for (const k of keys) { if (k.includes('@')) { const karr = k.split('@'); if (karr.length === 2) { const prefix = karr[0]; const type = karr[1]; if (type === 'start') { if (filter[k] && filter[k] !== '') { const obj = { key: prefix, opera: '$gte', value: new Date(filter[k]) }; times.push(obj); } } else { if (filter[k] && filter[k] !== '') { const obj = { key: prefix, opera: '$lte', value: new Date(filter[k]) }; times.push(obj); } } delete filter[k]; } } } for (const i of times) { const { key, opera, value } = i; if (!filter[key]) { filter[key] = {}; } filter[key][opera] = value; } return filter; } /** * 获取model的配置 */ async getModel() { const obj = this.model.prototype.schema.obj; return obj; } /** * 读取model中不显示的字段 */ async getSelectFalse() { const obj = await this.getModel(); const project = {}; for (const k in obj) { const v = obj[k]; if (v && v.select === false) project[k] = 0; } if (Object.keys(project).length > 0) return project; } } module.exports.CrudService = CrudService;