crud-service.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. 'use strict';
  2. const _ = require('lodash');
  3. const { isNullOrUndefined, trimData } = require('naf-core').Util;
  4. const assert = require('assert');
  5. const { ObjectId } = require('mongoose').Types;
  6. const { BusinessError, ErrorCode } = require('naf-core').Error;
  7. const { NafService } = require('./naf-service');
  8. class CrudService extends NafService {
  9. /**
  10. * 创建前处理函数
  11. * @param {Object} data 请求参数体
  12. */
  13. async beforeCreate(data) {
  14. return data;
  15. }
  16. /**
  17. * 创建后处理函数
  18. * @param {Object} body 请求参数体
  19. * @param {Object} data 数据
  20. */
  21. async afterCreate(body, data) {
  22. return data;
  23. }
  24. async create(data) {
  25. assert(data);
  26. // TODO:保存数据
  27. data = await this.beforeCreate(data);
  28. let res = await this.model.create(data);
  29. res = await this.afterCreate(data, res);
  30. return res;
  31. }
  32. /**
  33. * 修改前处理函数;需要将 查询条件和数据返回 (提供重写用,免去中间件)
  34. * @param {Object} filter 查询条件
  35. * @param {Object} update 数据
  36. * @return {Object} 返回查询条件和数据
  37. */
  38. async beforeUpdate(filter, update) {
  39. return { filter, update };
  40. }
  41. /**
  42. * 修改后处理函数;需要将 数据返回 (提供重写用,免去中间件)
  43. * @param {Object} filter 查询条件
  44. * @param {Object} body 请求参数体
  45. * @param {Object} data 数据
  46. * @return {Object} 返回修改后的数据
  47. */
  48. async afterUpdate(filter, body, data) {
  49. return data;
  50. }
  51. async update(filter, update, { projection } = {}) {
  52. assert(filter);
  53. assert(update);
  54. const beforeUpdateResult = await this.beforeUpdate(filter, update);
  55. filter = beforeUpdateResult.filter;
  56. update = beforeUpdateResult.update;
  57. const { _id, id } = filter;
  58. if (_id || id) filter = { _id: ObjectId(_id || id) };
  59. // TODO:检查数据是否存在
  60. const entity = await this.model.findOne(filter).exec();
  61. if (isNullOrUndefined(entity)) throw new BusinessError(ErrorCode.DATA_NOT_EXIST);
  62. // TODO: 修改数据
  63. entity.set(trimData(update));
  64. await entity.save();
  65. let reSearchData = await this.model.findOne(filter, projection).exec();
  66. reSearchData = await this.afterUpdate(filter, update, reSearchData);
  67. return reSearchData;
  68. }
  69. /**
  70. * 删除前处理函数
  71. * @param {Object} filter 查询条件
  72. * @return {Object} 查询条件
  73. */
  74. async beforeDelete(filter) {
  75. return filter;
  76. }
  77. /**
  78. * 删除后处理函数
  79. * @param {Object} filter 查询条件
  80. */
  81. async afterDelete(filter) {}
  82. async delete(filter) {
  83. assert(filter);
  84. filter = await this.beforeDelete(filter);
  85. const { _id, id } = filter;
  86. if (_id || id) {
  87. await this.model.findByIdAndDelete(_id || id).exec();
  88. } else {
  89. await this.model.deleteMany(filter).exec();
  90. }
  91. await this.afterDelete(filter);
  92. return 'deleted';
  93. }
  94. /**
  95. * 查询前处理函数
  96. * @param {Object} filter 查询条件
  97. * @return {Object} 查询条件
  98. */
  99. async beforeFetch(filter) {
  100. return filter;
  101. }
  102. /**
  103. * 查询后处理函数
  104. * @param {Object} filter 查询条件
  105. * @param {Object} data 数据
  106. * @return {Object} 查询条件
  107. */
  108. async afterFetch(filter, data) {
  109. return data;
  110. }
  111. async fetch(filter, { sort, desc, projection } = {}) {
  112. assert(filter);
  113. filter = await this.beforeFetch(filter);
  114. const { _id, id } = filter;
  115. if (_id || id) filter = { _id: ObjectId(_id || id) };
  116. // 处理排序
  117. if (sort && _.isString(sort)) {
  118. sort = { [sort]: desc ? -1 : 1 };
  119. } else if (sort && _.isArray(sort)) {
  120. sort = sort.map(f => ({ [f]: desc ? -1 : 1 })).reduce((p, c) => ({ ...p, ...c }), {});
  121. }
  122. let res = await this.model.findOne(filter, projection).exec();
  123. res = await this.afterFetch(filter, res);
  124. return res;
  125. }
  126. async beforeQuery(filter) {
  127. return filter;
  128. }
  129. async afterQuery(filter, data) {
  130. return data;
  131. }
  132. async query(filter, { skip = 0, limit, sort, desc, projection } = {}) {
  133. // 处理排序
  134. if (sort && _.isString(sort)) {
  135. sort = { [sort]: desc ? -1 : 1 };
  136. } else if (sort && _.isArray(sort)) {
  137. sort = sort.map(f => ({ [f]: desc ? -1 : 1 })).reduce((p, c) => ({ ...p, ...c }), {});
  138. }
  139. let condition = _.cloneDeep(filter);
  140. condition = await this.beforeQuery(condition);
  141. condition = this.dealFilter(condition);
  142. // 过滤出ref字段
  143. const { refMods, populate } = this.getRefMods();
  144. // 带ref查询
  145. let rs = await this.model.find(trimData(condition), projection, { skip, limit, sort }).populate(populate).exec();
  146. rs = JSON.parse(JSON.stringify(rs));
  147. // 整理ref数据
  148. rs = rs.map(i => {
  149. for (const obj of refMods) {
  150. const { col, prop, type } = obj;
  151. if (!prop) continue;
  152. if (_.isArray(prop)) {
  153. for (const p of prop) {
  154. if (type === 'String') i[`${col}_${p}`] = _.get(i, `${col}.${p}`);
  155. if (type === 'Array') {
  156. const list = [];
  157. const oList = _.get(i, `${col}`);
  158. for (const d of oList) {
  159. const obj = { _id: d._id };
  160. obj[p] = _.get(d, p);
  161. list.push(obj);
  162. }
  163. i[`${col}_${p}`] = list;
  164. }
  165. }
  166. i[col] = _.get(i, `${col}._id`);
  167. }
  168. }
  169. return i;
  170. });
  171. rs = await this.afterQuery(filter, rs);
  172. return rs;
  173. }
  174. async count(filter) {
  175. let condition = _.cloneDeep(filter);
  176. condition = await this.beforeQuery(condition);
  177. condition = this.dealFilter(condition);
  178. const count = await this.model.count(condition);
  179. return count;
  180. }
  181. async queryAndCount(filter, options) {
  182. filter = this.dealFilter(filter);
  183. const total = await this.count(filter);
  184. if (total === 0) return { total, data: [] };
  185. const rs = await this.query(filter, options);
  186. return { total, data: rs };
  187. }
  188. dealFilter(filter) {
  189. return this.turnFilter(this.turnDateRangeQuery(filter));
  190. }
  191. turnFilter(filter) {
  192. const str = /^%\S*%$/;
  193. // $是mongodb固定条件,不用处理;大多为手写特殊处理过的条件
  194. let keys = Object.keys(filter).filter(f => !f.includes('$'));
  195. for (const key of keys) {
  196. const res = key.match(str);
  197. if (res) {
  198. const newKey = key.slice(1, key.length - 1);
  199. if (!ObjectId.isValid(filter[key])) filter[newKey] = new RegExp(filter[key]);
  200. delete filter[key];
  201. }
  202. }
  203. // 再次过滤数据,将数组的数据都变成{$in:value},因为查询变成了聚合查询
  204. keys = Object.keys(filter).filter(f => !f.includes('$'));
  205. for (const key of keys) {
  206. if (_.isArray(filter[key])) {
  207. filter[key] = { $in: filter[key] };
  208. } else if (filter[key] === 'true' || filter[key] === 'false') {
  209. // 布尔类型的值检查,如果是布尔类型,则将字符串转为布尔
  210. filter[key] = filter[key] === 'true';
  211. }
  212. }
  213. return filter;
  214. }
  215. turnDateRangeQuery(filter) {
  216. const keys = Object.keys(filter);
  217. const times = [];
  218. for (const k of keys) {
  219. if (k.includes('@')) {
  220. const karr = k.split('@');
  221. if (karr.length === 2) {
  222. const prefix = karr[0];
  223. const type = karr[1];
  224. if (type === 'start') {
  225. if (filter[k] && filter[k] !== '') {
  226. const obj = { key: prefix, opera: '$gte', value: new Date(filter[k]) };
  227. times.push(obj);
  228. }
  229. } else {
  230. if (filter[k] && filter[k] !== '') {
  231. const obj = { key: prefix, opera: '$lte', value: new Date(filter[k]) };
  232. times.push(obj);
  233. }
  234. }
  235. delete filter[k];
  236. }
  237. }
  238. }
  239. for (const i of times) {
  240. const { key, opera, value } = i;
  241. if (!filter[key]) {
  242. filter[key] = {};
  243. }
  244. filter[key][opera] = value;
  245. }
  246. return filter;
  247. }
  248. /**
  249. * 读取model中不显示的字段
  250. */
  251. async getSelectFalse() {
  252. const obj = await this.getModel();
  253. const project = {};
  254. for (const k in obj) {
  255. const v = obj[k];
  256. if (v && v.select === false) project[k] = 0;
  257. }
  258. if (Object.keys(project).length > 0) return project;
  259. }
  260. getRefMods() {
  261. // 找到该表的schema(表结构)
  262. const mod = this.getSchema();
  263. const populate = this.resetPopulate(mod);
  264. const refMods = [];
  265. for (const key in mod) {
  266. if (!mod[key].ref && !mod[key].refPath) continue;
  267. const obj = { col: key, prop: mod[key].getProp, type: mod[key].type.name };
  268. refMods.push(obj);
  269. }
  270. return { refMods, populate };
  271. }
  272. // 格式化model路径
  273. formatModelPath(str) {
  274. let arr = str.split('.');
  275. arr = arr.map(i => _.upperFirst(i));
  276. const modelPath = arr.join('.');
  277. return modelPath;
  278. }
  279. // 获取model的模式
  280. getSchema(path) {
  281. const model = this.getModel(path);
  282. return _.get(model, 'prototype.schema.obj');
  283. }
  284. // 获取model实例
  285. getModel(path) {
  286. if (!path) return this.model;
  287. let model = _.get(this.ctx.model, path);
  288. const clients = this.app.mongooseDB.clients;
  289. if (clients && !model) {
  290. model = _.get(this.ctx.model, `${this.app.config.defaultModule}.${path}`);
  291. }
  292. return model;
  293. }
  294. // 针对每个表进行检查
  295. resetPopulate(schema) {
  296. const arr = [];
  297. for (const key in schema) {
  298. const e = schema[key];
  299. const { ref } = e;
  300. if (!ref) continue;
  301. const obj = { path: key };
  302. const modelPath = this.formatModelPath(ref);
  303. const model = this.getModel(modelPath);
  304. obj.model = model;
  305. const msch = this.getSchema(modelPath);
  306. const popu = this.resetPopulate(msch);
  307. if (popu.length > 0) obj.populate = popu;
  308. arr.push(obj);
  309. }
  310. return arr;
  311. }
  312. }
  313. // 聚合式查询
  314. // const pipeline = [{ $match: condition }];
  315. // // 先排序
  316. // if (sort) pipeline.push({ $sort: sort });
  317. // // 再进行分页
  318. // if (limit) {
  319. // pipeline.push({ $skip: parseInt(skip) });
  320. // pipeline.push({ $limit: parseInt(limit) });
  321. // }
  322. // // 再将数据过滤
  323. // if (projection) pipeline.push({ $project: projection });
  324. // else {
  325. // const defaultProject = await this.getSelectFalse();
  326. // if (defaultProject) pipeline.push({ $project: defaultProject });
  327. // }
  328. // let rs = await this.model.aggregate(pipeline);
  329. module.exports.CrudService = CrudService;