'use strict'; const _ = 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'); const mongoose = require('mongoose'); 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.update; 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); // 过滤出ref字段 const { refMods, populate } = this.getRefMods(); const queryRefs = _.get(this.app, 'config.queryRefs'); let rs; if (queryRefs) { // 带ref查询 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; }); } else { rs = await this.model.find(trimData(condition), projection, { skip, limit, sort }).exec(); } rs = await this.afterQuery(filter, rs); return rs; } async count(filter) { let condition = _.cloneDeep(filter); condition = await this.beforeQuery(condition); condition = this.dealFilter(condition); const count = await this.model.count(condition); 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*%$/; // $是mongodb固定条件,不用处理;大多为手写特殊处理过的条件 let keys = Object.keys(filter).filter((f) => !f.includes('$')); 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).filter((f) => !f.includes('$')); 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: filter[k] }; times.push(obj); } } else { if (filter[k] && filter[k] !== '') { const obj = { key: prefix, opera: '$lte', value: 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 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; } getRefMods() { // 找到该表的schema(表结构) const mod = this.getSchema(); const populate = this.resetPopulate(mod); const refMods = []; for (const key in mod) { if (!mod[key].ref && !mod[key].refPath) continue; const obj = { col: key, prop: mod[key].getProp, type: mod[key].type.name }; refMods.push(obj); } return { refMods, populate }; } // 格式化model路径 formatModelPath(str) { let arr = str.split('.'); arr = arr.map((i) => _.upperFirst(i)); const modelPath = arr.join('.'); return modelPath; } // 获取model的模式 getSchema(path) { const model = this.getModel(path); let schema = null; let version = mongoose.version; version = parseInt(version.split('.').join('')); if(version<573) schema = _.get(model, 'prototype.schema.obj'); else schema = _.get(model, 'prototype.schema.obj.obj'); return schema; } // 获取model实例 getModel(path) { if (!path) return this.model; let model = _.get(this.ctx.model, path); const clients = this.app.mongooseDB.clients; if (clients && !model) { model = _.get(this.ctx.model, `${this.app.config.defaultModule}.${path}`); } return model; } // 针对每个表进行检查 resetPopulate(schema) { const arr = []; for (const key in schema) { const e = schema[key]; const { ref } = e; if (!ref) continue; const obj = { path: key }; const modelPath = this.formatModelPath(ref); const model = this.getModel(modelPath); obj.model = model; const msch = this.getSchema(modelPath); const popu = this.resetPopulate(msch); if (popu.length > 0) obj.populate = popu; arr.push(obj); } return arr; } } // 聚合式查询 // 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); module.exports.CrudService = CrudService;