crud-service.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. 'use strict';
  2. const { isString, isArray, cloneDeep, head: getHead, get, omit, last } = 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 } = await 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 getRefMods() {
  175. const mod = await this.getModel();
  176. const refMods = [];
  177. const populate = [];
  178. for (const key in mod) {
  179. if (!mod[key].ref && !mod[key].refPath) continue;
  180. const obj = { col: key, prop: mod[key].getProp, type: mod[key].type.name };
  181. if (mod[key].ref) {
  182. const ref = mod[key].ref;
  183. if (ref.includes('.')) {
  184. // 说明是跨数据源
  185. const arr = ref.split('.');
  186. const conn = this.app.mongooseDB.get(getHead(arr));
  187. const refModel = last(arr);
  188. const schema = get(this.ctx.model, `${refModel}.schema`);
  189. const model = conn.model(refModel, schema);
  190. const p = { path: key, model };
  191. populate.push(p);
  192. } else {
  193. const p = { path: key };
  194. populate.push(p);
  195. }
  196. } else if (mod[key].refPath) {
  197. const p = { path: key };
  198. populate.push(p);
  199. }
  200. refMods.push(obj);
  201. }
  202. return { refMods, populate };
  203. }
  204. async count(filter) {
  205. let condition = cloneDeep(filter);
  206. condition = await this.beforeQuery(condition);
  207. condition = this.dealFilter(condition);
  208. const count = await this.model.count(condition);
  209. return count;
  210. }
  211. async queryAndCount(filter, options) {
  212. filter = this.dealFilter(filter);
  213. const total = await this.count(filter);
  214. if (total === 0) return { total, data: [] };
  215. const rs = await this.query(filter, options);
  216. return { total, data: rs };
  217. }
  218. dealFilter(filter) {
  219. return this.turnFilter(this.turnDateRangeQuery(filter));
  220. }
  221. turnFilter(filter) {
  222. const str = /^%\S*%$/;
  223. let keys = Object.keys(filter);
  224. for (const key of keys) {
  225. const res = key.match(str);
  226. if (res) {
  227. const newKey = key.slice(1, key.length - 1);
  228. if (!ObjectId.isValid(filter[key])) filter[newKey] = new RegExp(filter[key]);
  229. delete filter[key];
  230. }
  231. }
  232. // 再次过滤数据,将数组的数据都变成{$in:value},因为查询变成了聚合查询
  233. keys = Object.keys(filter);
  234. for (const key of keys) {
  235. if (isArray(filter[key])) {
  236. filter[key] = { $in: filter[key] };
  237. } else if (filter[key] === 'true' || filter[key] === 'false') {
  238. // 布尔类型的值检查,如果是布尔类型,则将字符串转为布尔
  239. filter[key] = filter[key] === 'true';
  240. }
  241. }
  242. return filter;
  243. }
  244. turnDateRangeQuery(filter) {
  245. const keys = Object.keys(filter);
  246. const times = [];
  247. for (const k of keys) {
  248. if (k.includes('@')) {
  249. const karr = k.split('@');
  250. if (karr.length === 2) {
  251. const prefix = karr[0];
  252. const type = karr[1];
  253. if (type === 'start') {
  254. if (filter[k] && filter[k] !== '') {
  255. const obj = { key: prefix, opera: '$gte', value: new Date(filter[k]) };
  256. times.push(obj);
  257. }
  258. } else {
  259. if (filter[k] && filter[k] !== '') {
  260. const obj = { key: prefix, opera: '$lte', value: new Date(filter[k]) };
  261. times.push(obj);
  262. }
  263. }
  264. delete filter[k];
  265. }
  266. }
  267. }
  268. for (const i of times) {
  269. const { key, opera, value } = i;
  270. if (!filter[key]) {
  271. filter[key] = {};
  272. }
  273. filter[key][opera] = value;
  274. }
  275. return filter;
  276. }
  277. /**
  278. * 获取model的配置
  279. */
  280. async getModel() {
  281. const obj = this.model.prototype.schema.obj;
  282. return obj;
  283. }
  284. /**
  285. * 读取model中不显示的字段
  286. */
  287. async getSelectFalse() {
  288. const obj = await this.getModel();
  289. const project = {};
  290. for (const k in obj) {
  291. const v = obj[k];
  292. if (v && v.select === false) project[k] = 0;
  293. }
  294. if (Object.keys(project).length > 0) return project;
  295. }
  296. }
  297. // 聚合式查询
  298. // const pipeline = [{ $match: condition }];
  299. // // 先排序
  300. // if (sort) pipeline.push({ $sort: sort });
  301. // // 再进行分页
  302. // if (limit) {
  303. // pipeline.push({ $skip: parseInt(skip) });
  304. // pipeline.push({ $limit: parseInt(limit) });
  305. // }
  306. // // 再将数据过滤
  307. // if (projection) pipeline.push({ $project: projection });
  308. // else {
  309. // const defaultProject = await this.getSelectFalse();
  310. // if (defaultProject) pipeline.push({ $project: defaultProject });
  311. // }
  312. // let rs = await this.model.aggregate(pipeline);
  313. module.exports.CrudService = CrudService;