'use strict'; const { isString, isArray, cloneDeep, head: getHead, get } = 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 { async create(data) { assert(data); // TODO:保存数据 const res = await this.model.create(data); return res; } async update(filter, update, { projection } = {}) { assert(filter); assert(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(); return await this.model.findOne(filter, projection).exec(); } async delete(filter) { assert(filter); const { _id, id } = filter; if (_id || id) { await this.model.findByIdAndDelete(_id || id).exec(); } else { await this.model.deleteMany(filter).exec(); } return 'deleted'; } async fetch(filter, { sort, desc, projection } = {}) { assert(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 }), {}); } return await this.model.findOne(filter, projection).exec(); } 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 = this.dealFilter(condition); const isOpenMultiTenancy = get(this.app, 'plugins.multiTenancy'); if (isOpenMultiTenancy) { const _tenant = this.isMultiTenancy(); if (_tenant) condition._tenant = _tenant; } 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 = this.getSelectFalse(); if (defaultProject)pipeline.push({ $project: defaultProject }); } const rs = await this.model.aggregate(pipeline); // const rs = await this.model.find(trimData(condition), projection, { skip, limit, sort }).exec(); return rs; } async count(filter) { let condition = cloneDeep(filter); condition = this.dealFilter(condition); // 分站模式确认 const isOpenMultiTenancy = get(this.app, 'plugins.multiTenancy'); if (isOpenMultiTenancy) { const _tenant = this.isMultiTenancy(); if (_tenant) condition._tenant = _tenant; } let count = 0; 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; } /** * 判断默认model是否是分站模式 * 是分站模式且不是master,就返回分站标识 */ isMultiTenancy() { const is_multi = this.model.prototype.schema.options['multi-tenancy']; const tenant = this.model.prototype.schema.options['x-tenant']; if (is_multi && tenant !== 'master') return tenant; } 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] }; } } 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;