|
@@ -1,6 +1,6 @@
|
|
|
'use strict';
|
|
|
|
|
|
-const { isString, isArray, cloneDeep, head: getHead, get } = require('lodash');
|
|
|
+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;
|
|
@@ -8,16 +8,57 @@ const { BusinessError, ErrorCode } = require('naf-core').Error;
|
|
|
const { NafService } = require('./naf-service');
|
|
|
|
|
|
class CrudService extends NafService {
|
|
|
+ /**
|
|
|
+ * 创建前处理函数
|
|
|
+ * @param {Object} data 数据
|
|
|
+ */
|
|
|
+ beforeCreate(data) {
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 创建后处理函数
|
|
|
+ * @param {Object} data 数据
|
|
|
+ */
|
|
|
+ afterCreate(data) {
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
async create(data) {
|
|
|
assert(data);
|
|
|
// TODO:保存数据
|
|
|
- const res = await this.model.create(data);
|
|
|
+ data = await this.beforeCreate(data);
|
|
|
+ let res = await this.model.create(data);
|
|
|
+ res = await this.afterCreate(res);
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 修改前处理函数;需要将 查询条件和数据返回 (提供重写用,免去中间件)
|
|
|
+ * @param {Object} filter 查询条件
|
|
|
+ * @param {Object} update 数据
|
|
|
+ * @return {Object} 返回查询条件和数据
|
|
|
+ */
|
|
|
+ berforeUpdate(filter, update) {
|
|
|
+ return { filter, update };
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 修改后处理函数;需要将 数据返回 (提供重写用,免去中间件)
|
|
|
+ * @param {Object} filter 查询条件
|
|
|
+ * @param {Object} data 数据
|
|
|
+ * @return {Object} 返回修改后的数据
|
|
|
+ */
|
|
|
+
|
|
|
+ afterUpdate(filter, data) {
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
async update(filter, update, { projection } = {}) {
|
|
|
assert(filter);
|
|
|
assert(update);
|
|
|
+ const berforeUpdateResult = await this.berforeUpdate(filter, update);
|
|
|
+ filter = berforeUpdateResult.filter;
|
|
|
+ update = berforeUpdateResult.update;
|
|
|
const { _id, id } = filter;
|
|
|
if (_id || id) filter = { _id: ObjectId(_id || id) };
|
|
|
// TODO:检查数据是否存在
|
|
@@ -27,22 +68,57 @@ class CrudService extends NafService {
|
|
|
// TODO: 修改数据
|
|
|
entity.set(trimData(update));
|
|
|
await entity.save();
|
|
|
- return await this.model.findOne(filter, projection).exec();
|
|
|
+ let reSearchData = await this.model.findOne(filter, projection).exec();
|
|
|
+ reSearchData = await this.afterUpdate(reSearchData);
|
|
|
+ return reSearchData;
|
|
|
}
|
|
|
+ /**
|
|
|
+ * 删除前处理函数
|
|
|
+ * @param {Object} filter 查询条件
|
|
|
+ * @return {Object} 查询条件
|
|
|
+ */
|
|
|
+ beforeDelete(filter) {
|
|
|
+ return filter;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 删除后处理函数
|
|
|
+ * @param {Object} filter 查询条件
|
|
|
+ */
|
|
|
+ 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} 查询条件
|
|
|
+ */
|
|
|
+ beforeFetch(filter) {
|
|
|
+ return filter;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 查询后处理函数
|
|
|
+ * @param {Object} filter 查询条件
|
|
|
+ * @param {Object} data 数据
|
|
|
+ * @return {Object} 查询条件
|
|
|
+ */
|
|
|
+ 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) };
|
|
|
|
|
@@ -52,8 +128,16 @@ class CrudService extends NafService {
|
|
|
} 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;
|
|
|
+ }
|
|
|
|
|
|
- return await this.model.findOne(filter, projection).exec();
|
|
|
+ beforeQuery(filter) {
|
|
|
+ return filter;
|
|
|
+ }
|
|
|
+ afterQuery(filter, data) {
|
|
|
+ return data;
|
|
|
}
|
|
|
|
|
|
async query(filter, { skip = 0, limit, sort, desc, projection } = {}) {
|
|
@@ -64,58 +148,65 @@ class CrudService extends NafService {
|
|
|
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 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 = await 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();
|
|
|
+ // 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 } = obj;
|
|
|
+ if (!prop) continue;
|
|
|
+ i = { ...omit(i, [ col ]), [`${col}_${prop}`]: get(i, `${col}.${prop}`), [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 });
|
|
|
+ }
|
|
|
+ return refMods;
|
|
|
+ }
|
|
|
+
|
|
|
async count(filter) {
|
|
|
let condition = cloneDeep(filter);
|
|
|
+ condition = await this.beforeQuery(condition);
|
|
|
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);
|
|
|
- }
|
|
|
+ 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;
|
|
|
}
|
|
|
- /**
|
|
|
- * 判断默认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);
|