crud-service.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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. const queryRefs = _.get(this.app, 'config.queryRefs');
  145. let rs;
  146. if (queryRefs) {
  147. // 带ref查询
  148. rs = await this.model.find(trimData(condition), projection, { skip, limit, sort }).populate(populate).exec();
  149. rs = JSON.parse(JSON.stringify(rs));
  150. // 整理ref数据
  151. rs = rs.map((i) => {
  152. for (const obj of refMods) {
  153. const { col, prop, type } = obj;
  154. if (!prop) continue;
  155. if (_.isArray(prop)) {
  156. for (const p of prop) {
  157. if (type === 'String') i[`${col}_${p}`] = _.get(i, `${col}.${p}`);
  158. if (type === 'Array') {
  159. const list = [];
  160. const oList = _.get(i, `${col}`);
  161. for (const d of oList) {
  162. const obj = { _id: d._id };
  163. obj[p] = _.get(d, p);
  164. list.push(obj);
  165. }
  166. i[`${col}_${p}`] = list;
  167. }
  168. }
  169. i[col] = _.get(i, `${col}._id`);
  170. }
  171. }
  172. return i;
  173. });
  174. } else {
  175. rs = await this.model.find(trimData(condition), projection, { skip, limit, sort }).exec();
  176. }
  177. rs = await this.afterQuery(filter, rs);
  178. return rs;
  179. }
  180. async count(filter) {
  181. let condition = _.cloneDeep(filter);
  182. condition = await this.beforeQuery(condition);
  183. condition = this.dealFilter(condition);
  184. const count = await this.model.count(condition);
  185. return count;
  186. }
  187. async queryAndCount(filter, options) {
  188. filter = this.dealFilter(filter);
  189. const total = await this.count(filter);
  190. if (total === 0) return { total, data: [] };
  191. const rs = await this.query(filter, options);
  192. return { total, data: rs };
  193. }
  194. dealFilter(filter) {
  195. return this.turnFilter(this.turnDateRangeQuery(filter));
  196. }
  197. turnFilter(filter) {
  198. const str = /^%\S*%$/;
  199. // $是mongodb固定条件,不用处理;大多为手写特殊处理过的条件
  200. let keys = Object.keys(filter).filter((f) => !f.includes('$'));
  201. for (const key of keys) {
  202. const res = key.match(str);
  203. if (res) {
  204. const newKey = key.slice(1, key.length - 1);
  205. if (!ObjectId.isValid(filter[key])) filter[newKey] = new RegExp(filter[key]);
  206. delete filter[key];
  207. }
  208. }
  209. // 再次过滤数据,将数组的数据都变成{$in:value},因为查询变成了聚合查询
  210. keys = Object.keys(filter).filter((f) => !f.includes('$'));
  211. for (const key of keys) {
  212. if (_.isArray(filter[key])) {
  213. filter[key] = { $in: filter[key] };
  214. } else if (filter[key] === 'true' || filter[key] === 'false') {
  215. // 布尔类型的值检查,如果是布尔类型,则将字符串转为布尔
  216. filter[key] = filter[key] === 'true';
  217. }
  218. }
  219. return filter;
  220. }
  221. turnDateRangeQuery(filter) {
  222. const keys = Object.keys(filter);
  223. const times = [];
  224. for (const k of keys) {
  225. if (k.includes('@')) {
  226. const karr = k.split('@');
  227. if (karr.length === 2) {
  228. const prefix = karr[0];
  229. const type = karr[1];
  230. if (type === 'start') {
  231. if (filter[k] && filter[k] !== '') {
  232. const obj = { key: prefix, opera: '$gte', value: filter[k] };
  233. times.push(obj);
  234. }
  235. } else {
  236. if (filter[k] && filter[k] !== '') {
  237. const obj = { key: prefix, opera: '$lte', value: filter[k] };
  238. times.push(obj);
  239. }
  240. }
  241. delete filter[k];
  242. }
  243. }
  244. }
  245. for (const i of times) {
  246. const { key, opera, value } = i;
  247. if (!filter[key]) {
  248. filter[key] = {};
  249. }
  250. filter[key][opera] = value;
  251. }
  252. return filter;
  253. }
  254. /**
  255. * 读取model中不显示的字段
  256. */
  257. async getSelectFalse() {
  258. const obj = await this.getModel();
  259. const project = {};
  260. for (const k in obj) {
  261. const v = obj[k];
  262. if (v && v.select === false) project[k] = 0;
  263. }
  264. if (Object.keys(project).length > 0) return project;
  265. }
  266. getRefMods() {
  267. // 找到该表的schema(表结构)
  268. const mod = this.getSchema();
  269. const populate = this.resetPopulate(mod);
  270. const refMods = [];
  271. for (const key in mod) {
  272. if (!mod[key].ref && !mod[key].refPath) continue;
  273. const obj = { col: key, prop: mod[key].getProp, type: mod[key].type.name };
  274. refMods.push(obj);
  275. }
  276. return { refMods, populate };
  277. }
  278. // 格式化model路径
  279. formatModelPath(str) {
  280. let arr = str.split('.');
  281. arr = arr.map((i) => _.upperFirst(i));
  282. const modelPath = arr.join('.');
  283. return modelPath;
  284. }
  285. // 获取model的模式
  286. getSchema(path) {
  287. const model = this.getModel(path);
  288. return _.get(model, 'prototype.schema.obj');
  289. }
  290. // 获取model实例
  291. getModel(path) {
  292. if (!path) return this.model;
  293. let model = _.get(this.ctx.model, path);
  294. const clients = this.app.mongooseDB.clients;
  295. if (clients && !model) {
  296. model = _.get(this.ctx.model, `${this.app.config.defaultModule}.${path}`);
  297. }
  298. return model;
  299. }
  300. // 针对每个表进行检查
  301. resetPopulate(schema) {
  302. const arr = [];
  303. for (const key in schema) {
  304. const e = schema[key];
  305. const { ref } = e;
  306. if (!ref) continue;
  307. const obj = { path: key };
  308. const modelPath = this.formatModelPath(ref);
  309. const model = this.getModel(modelPath);
  310. obj.model = model;
  311. const msch = this.getSchema(modelPath);
  312. const popu = this.resetPopulate(msch);
  313. if (popu.length > 0) obj.populate = popu;
  314. arr.push(obj);
  315. }
  316. return arr;
  317. }
  318. }
  319. // 聚合式查询
  320. // const pipeline = [{ $match: condition }];
  321. // // 先排序
  322. // if (sort) pipeline.push({ $sort: sort });
  323. // // 再进行分页
  324. // if (limit) {
  325. // pipeline.push({ $skip: parseInt(skip) });
  326. // pipeline.push({ $limit: parseInt(limit) });
  327. // }
  328. // // 再将数据过滤
  329. // if (projection) pipeline.push({ $project: projection });
  330. // else {
  331. // const defaultProject = await this.getSelectFalse();
  332. // if (defaultProject) pipeline.push({ $project: defaultProject });
  333. // }
  334. // let rs = await this.model.aggregate(pipeline);
  335. module.exports.CrudService = CrudService;