|
@@ -0,0 +1,155 @@
|
|
|
+import { App, Inject } from '@midwayjs/core';
|
|
|
+import { Application, Context } from '@midwayjs/koa';
|
|
|
+import { get, isNull, isFinite, isString, isUndefined } from 'lodash';
|
|
|
+import { Opera } from './dbOpera';
|
|
|
+import { completeBuilderCondition } from './conditionBuilder';
|
|
|
+/**
|
|
|
+ * query默认的查询方式(哪个字段 是 = 还是 IN 还是 LIKE)的设置函数为getQueryColumnsOpera,如果有需要重写即可,返回object
|
|
|
+ * {
|
|
|
+ * column: Opera.xxx
|
|
|
+ * }
|
|
|
+ */
|
|
|
+export abstract class BaseService {
|
|
|
+ @App()
|
|
|
+ app: Application;
|
|
|
+
|
|
|
+ @Inject()
|
|
|
+ ctx: Context;
|
|
|
+
|
|
|
+ /**service的model,数据库操作 */
|
|
|
+ abstract model: any;
|
|
|
+ /**返回{column:查询方式}.特殊的查询需要写入,不写入默认采用 = */
|
|
|
+ getQueryColumnsOpera() {
|
|
|
+ return {};
|
|
|
+ }
|
|
|
+ /** */
|
|
|
+ Opera = Opera;
|
|
|
+ /**
|
|
|
+ * 默认查询列表
|
|
|
+ * @param query @Query直接获取的参数
|
|
|
+ * @param param @Query和调用前可组织的参数
|
|
|
+ * @param {object} operas 指定查询方式
|
|
|
+ * @property {number} param.skip 查询数据起始位置
|
|
|
+ * @property {number} param.limit 查询数据的数量
|
|
|
+ * @property {object} param.order 使用的排序 {${column}:'DESC'/'ASC'}
|
|
|
+ * @property {Array<string>} selects 指定显示的字段
|
|
|
+ */
|
|
|
+ async query(query: object = {}, meta: any = {}, operas?) {
|
|
|
+ let skip = get(meta, 'skip', 0);
|
|
|
+ let limit = get(meta, 'limit', 0);
|
|
|
+ const order = get(meta, 'order', {});
|
|
|
+ const selects = get(meta, 'selects', []);
|
|
|
+ const builder = await this.model.createQueryBuilder();
|
|
|
+ if (selects.length > 0) {
|
|
|
+ // 字段是直接传来的,正常限制,需要加上model的name.否则会导致什么字段都没有
|
|
|
+ const modelName = this.model.metadata.name;
|
|
|
+ builder.select(selects.map(i => `${modelName}.${i}`));
|
|
|
+ }
|
|
|
+ // 组织查询顺序
|
|
|
+ let orderObject: any = {};
|
|
|
+ // 如果有自定义顺序,则按照自定义顺序来, 没有自定义顺序,默认按创建时间的desc查询
|
|
|
+ if (Object.keys(order).length > 0) {
|
|
|
+ for (const column in order) orderObject[column] = order[column];
|
|
|
+ } else orderObject = { id: 'DESC' };
|
|
|
+ // 没有传如何查询,就获取query查询设置的默认查询方式
|
|
|
+ if (!operas) operas = this.getQueryColumnsOpera();
|
|
|
+ completeBuilderCondition(builder, query, operas, this.model);
|
|
|
+ // 分页
|
|
|
+ if (isString(skip)) {
|
|
|
+ skip = parseInt(skip);
|
|
|
+ if (isFinite(skip)) builder.skip(skip);
|
|
|
+ } else if (isFinite(skip)) builder.skip(skip);
|
|
|
+ if (isString(limit)) {
|
|
|
+ limit = parseInt(limit);
|
|
|
+ if (isFinite(limit)) builder.take(limit);
|
|
|
+ } else if (isFinite(limit)) builder.take(limit);
|
|
|
+ // 排序
|
|
|
+ builder.orderBy(orderObject);
|
|
|
+ // 执行
|
|
|
+ if (this.app.getEnv() !== 'prod' && this.app.getEnv() !== 'production') {
|
|
|
+ console.log(builder.getSql());
|
|
|
+ }
|
|
|
+ const data = await builder.getMany();
|
|
|
+ const total = await builder.getCount();
|
|
|
+ return { data, total };
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 单查询,不止用id还可以根据别的条件,默认全等,可以在调用时,进行查询方式设置
|
|
|
+ * @param {object} query 查询条件
|
|
|
+ * @param {object} operas 指定查询方式
|
|
|
+ */
|
|
|
+ async fetch(query: object, operas = {}) {
|
|
|
+ const builder = this.model.createQueryBuilder();
|
|
|
+ completeBuilderCondition(builder, query, operas, this.model);
|
|
|
+ const result = await builder.getOne();
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**创建 */
|
|
|
+ async create(data: object) {
|
|
|
+ // 设置 创建数据的人
|
|
|
+ const user = get(this.ctx, 'user');
|
|
|
+ if (user) {
|
|
|
+ // 查询本表是否有data_onwer字段,有再添加.
|
|
|
+ const hasColumn = this.checkModelHaveColumn('data_onwer');
|
|
|
+ if (hasColumn) Object.assign(data, { data_owner: get(user, 'id') });
|
|
|
+ }
|
|
|
+ const result = await this.model.insert(data);
|
|
|
+ const id = get(result, 'identifiers.0.id');
|
|
|
+ // 没有id估计是出错了
|
|
|
+ if (!id) return;
|
|
|
+ const createData = await this.fetch({ id });
|
|
|
+ // 没有查出数据,也是有问题
|
|
|
+ if (!createData) return;
|
|
|
+ return createData;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**修改,单修改/多修改是统一修改为 */
|
|
|
+ async update(query: object = {}, data: object) {
|
|
|
+ // 没有范围的修改不允许执行
|
|
|
+ if (Object.keys(query).length <= 0) return;
|
|
|
+ // 处理数据, 只将是本表的字段拿出来保存
|
|
|
+ const columns = this.model.metadata.columns;
|
|
|
+ /**将array的列设置 转换为object,以便query使用*/
|
|
|
+ const columnsObject = {};
|
|
|
+ // 整理成object
|
|
|
+ for (const c of columns) columnsObject[c.propertyName] = c.type.toString();
|
|
|
+ const updateData = {};
|
|
|
+ const notDealColumn = ['created_time', 'update_time', 'data_owner', '__v'];
|
|
|
+ for (const column in columnsObject) {
|
|
|
+ if (notDealColumn.includes(column)) continue;
|
|
|
+ const val = data[column];
|
|
|
+ if (isNull(val) || isUndefined(val)) continue;
|
|
|
+ updateData[column] = val;
|
|
|
+ }
|
|
|
+ // 找到原数据
|
|
|
+ const originDataBuilder = this.model.createQueryBuilder();
|
|
|
+ completeBuilderCondition(originDataBuilder, query, {}, this.model);
|
|
|
+ const origin_data = await originDataBuilder.getMany(query);
|
|
|
+ if (origin_data.length <= 0) return;
|
|
|
+ await this.model.update(query, updateData);
|
|
|
+ const new_data = await originDataBuilder.getMany(query);
|
|
|
+ if (new_data.length <= 0) return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**删除,单删多删都行 */
|
|
|
+ async delete(query: object) {
|
|
|
+ // 没有范围不能删除,清空表需要特殊处理
|
|
|
+ if (query && Object.keys(query).length <= 0) return;
|
|
|
+ // 删除前,先查出来数据, 找到原数据
|
|
|
+ const originDataBuilder = this.model.createQueryBuilder();
|
|
|
+ completeBuilderCondition(originDataBuilder, query, {}, this.model);
|
|
|
+ const origin_data = await originDataBuilder.getMany(query);
|
|
|
+ if (origin_data.length <= 0) return;
|
|
|
+ const result = await this.model.delete(query);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**检查有没有指定字段,是从model的映射关系中查询下 */
|
|
|
+ private checkModelHaveColumn(columnName: string) {
|
|
|
+ const columns = this.model.metadata.columns;
|
|
|
+ const has = columns.find(f => get(f, 'propertyName') === columnName);
|
|
|
+ return !isUndefined(has);
|
|
|
+ }
|
|
|
+}
|