diff --git a/core/src/decorator/controller.ts b/core/src/decorator/controller.ts index 8a31203..57a8bc1 100644 --- a/core/src/decorator/controller.ts +++ b/core/src/decorator/controller.ts @@ -67,6 +67,8 @@ export interface QueryOp { where?: Function; // 查询字段 select?: string[]; + // 字段模糊查询 + fieldLike?: string[] | FieldEq[] | (string | FieldEq)[]; // 字段相等 fieldEq?: string[] | FieldEq[] | (string | FieldEq)[]; // 添加排序条件 diff --git a/core/src/rest/eps.ts b/core/src/rest/eps.ts index cd7d59f..c1e8d75 100644 --- a/core/src/rest/eps.ts +++ b/core/src/rest/eps.ts @@ -15,6 +15,7 @@ import { import { TypeORMDataSourceManager } from '@midwayjs/typeorm'; import { CoolUrlTagData } from '../tag/data'; import { TagTypes } from '../decorator/tag'; +import { CurdOption, QueryOp } from '../decorator/controller'; /** * 实体路径 @@ -54,6 +55,7 @@ export class CoolEps { const appArr = []; for (const controller of controllers) { const { prefix, module, curdOption, routerOptions } = controller; + const pageQueryOp = await this.getPageOp(curdOption); const name = curdOption?.entity?.name; (_.startsWith(prefix, '/admin/') ? adminArr : appArr).push({ module, @@ -66,6 +68,29 @@ export class CoolEps { api: routers[prefix], name, columns: entitys[name] || [], + pageQueryOp: { + keyWordLikeFields: + pageQueryOp?.keyWordLikeFields?.map(field => + field.includes('.') ? field : `a.${field}` + ) || [], + fieldEq: + pageQueryOp?.fieldEq?.map(field => + typeof field === 'string' + ? field.includes('.') + ? field + : `a.${field}` + : field + ) || [], + fieldLike: + pageQueryOp?.fieldLike?.map(field => + typeof field === 'string' + ? field.includes('.') + ? field + : `a.${field}` + : field + ) || [], + }, + pageColumns: await this.pageColumns(entitys, curdOption), prefix, }); } @@ -73,6 +98,111 @@ export class CoolEps { this.app = _.groupBy(appArr, 'module'); } + /** + * 获取分页查询配置 + * @param curdOption + * @returns + */ + async getPageOp(curdOption: CurdOption) { + let pageQueryOp: QueryOp | Function = curdOption?.pageQueryOp; + if (typeof pageQueryOp === 'function') { + pageQueryOp = await pageQueryOp(); + } + return pageQueryOp as QueryOp; + } + + /** + * 处理列 + * @param entitys + * @param entityColumns + * @param curdOption + */ + async pageColumns(entitys: Record, curdOption: CurdOption) { + const pageQueryOp = await this.getPageOp(curdOption); + // 检查 pageQueryOp 是否为对象且具有 select 属性 + if ( + pageQueryOp && + typeof pageQueryOp === 'object' && + 'select' in pageQueryOp && + curdOption?.entity?.name + ) { + const select = pageQueryOp.select; + const join = pageQueryOp.join || []; + // 所有的关联entitys + const joinEntitys: { + name: string; + alias: string; + }[] = [{ name: curdOption.entity.name, alias: 'a' }]; + + if (join.length > 0) { + joinEntitys.push( + ...join.map(item => { + return { name: item.entity.name, alias: item.alias }; + }) + ); + } + + // 处理 select + const result = []; + for (const selectItem of select) { + // 处理 'a.*' 这种情况 + if (selectItem.endsWith('.*')) { + const alias = selectItem.split('.')[0]; + const entity = joinEntitys.find(e => e.alias === alias); + if (entity) { + const entityColumns = entitys[entity.name] || []; + result.push( + ...entityColumns.map(e => { + return { + ...e, + source: `${alias}.${e.propertyName}`, + }; + }) + ); + } + continue; + } + + // 处理单个字段,如 'b.name' 或 'b.name as userName' + const asRegex = /\s+as\s+/i; + const [field, asName] = selectItem.split(asRegex).map(s => s.trim()); + const [alias, fieldName] = field.split('.'); + const entity = joinEntitys.find(e => e.alias === alias); + + if (entity) { + const entityColumns = entitys[entity.name] || []; + const column = entityColumns.find( + col => col.propertyName === fieldName + ); + if (column) { + result.push({ + ...column, + propertyName: asName || column.propertyName, + source: `${alias}.${column.propertyName}`, + }); + } + } + } + // 将 createTime 和 updateTime 移到末尾 + const finalResult = [...result]; + const timeFields = ['createTime', 'updateTime']; + const timeColumns = []; + + // 先找出并删除所有时间字段 + for (let i = finalResult.length - 1; i >= 0; i--) { + if (timeFields.includes(finalResult[i].propertyName)) { + timeColumns.unshift(finalResult.splice(i, 1)[0]); + } + } + + // 将时间字段添加到末尾 + finalResult.push(...timeColumns); + + return finalResult; + } + return []; + } + /** * 模块信息 * @param module @@ -150,13 +280,19 @@ export class CoolEps { length: e.length, comment: e.comment, nullable: e.isNullable, + defaultValue: e.default, + dict: e['dict'], + source: `a.${e.propertyName}`, }; }), o => { if (['createTime', 'updateTime'].includes(o.propertyName)) { commColums.push(o); } - return o && !['createTime', 'updateTime'].includes(o.propertyName); + return ( + o && + !['createTime', 'updateTime', 'tenantId'].includes(o.propertyName) + ); } ).concat(commColums); result[entityMetadata.name] = columns; diff --git a/core/src/service/mysql.ts b/core/src/service/mysql.ts index 38c8d52..8e2f231 100644 --- a/core/src/service/mysql.ts +++ b/core/src/service/mysql.ts @@ -470,6 +470,33 @@ export abstract class BaseMysqlService { } } } + // 字段模糊查询 + if (!_.isEmpty(option.fieldLike)) { + for (let key of option.fieldLike) { + // 如果key有包含.的情况下操作 + if (typeof key === 'string' && key.includes('.')) { + const keys = key.split('.'); + const lastKey = keys.pop(); + key = { requestParam: lastKey, column: key }; + } + // 单表字段无别名的情况下操作 + if (typeof key === 'string') { + if (query[key] || query[key] === 0) { + find.andWhere(`${key} like :${key}`, { + [key]: `%${query[key]}%`, + }); + this.sqlParams.push(`%${query[key]}%`); + } + } else { + if (query[key.requestParam] || query[key.requestParam] === 0) { + find.andWhere(`${key.column} like :${key.column}`, { + [key.column]: `%${query[key.requestParam]}%`, + }); + this.sqlParams.push(`%${query[key.requestParam]}%`); + } + } + } + } } else { sqlArr.push(selects.join(',')); } diff --git a/core/src/service/postgres.ts b/core/src/service/postgres.ts index 8481428..73c581f 100644 --- a/core/src/service/postgres.ts +++ b/core/src/service/postgres.ts @@ -532,6 +532,40 @@ export abstract class BasePgService { } } } + // 字段模糊查询 + if (!_.isEmpty(option.fieldLike)) { + for (let key of option.fieldLike) { + const c = {}; + let column; + // 如果key有包含.的情况下操作 + if (typeof key === 'string' && key.includes('.')) { + const keys = key.split('.'); + const lastKey = keys.pop(); + key = { requestParam: lastKey, column: key }; + column = key; + } else { + column = `a.${key}`; + } + // 单表字段无别名的情况下操作 + if (typeof key === 'string') { + if (query[key] || query[key] == 0) { + c[key] = query[key]; + find.andWhere(`${column} like :${key}`, { + [key]: `%${query[key]}%`, + }); + this.sqlParams.push(`%${query[key]}%`); + } + } else { + if (query[key.requestParam] || query[key.requestParam] == 0) { + c[key.column] = query[key.requestParam]; + find.andWhere(`${key.column} like :${key.column}`, { + [key.column]: `%${query[key.requestParam]}%`, + }); + this.sqlParams.push(`%${query[key.requestParam]}%`); + } + } + } + } } else { sqlArr.push(selects.join(',')); } diff --git a/core/src/service/sqlite.ts b/core/src/service/sqlite.ts index c9591ef..34aacdd 100644 --- a/core/src/service/sqlite.ts +++ b/core/src/service/sqlite.ts @@ -523,7 +523,33 @@ export abstract class BaseSqliteService { } else { find.andWhere(`${key.column} ${eq} :${key.column}`, c); } - // this.sqlParams.push(query[key.requestParam]); + } + } + } + } + // 字段模糊查询 + if (!_.isEmpty(option.fieldLike)) { + for (let key of option.fieldLike) { + // 如果key有包含.的情况下操作 + if (typeof key === 'string' && key.includes('.')) { + const keys = key.split('.'); + const lastKey = keys.pop(); + key = { requestParam: lastKey, column: key }; + } + // 单表字段无别名的情况下操作 + if (typeof key === 'string') { + if (query[key] || query[key] == 0) { + find.andWhere(`${key} like :${key}`, { + [key]: `%${query[key]}%`, + }); + this.sqlParams.push(`%${query[key]}%`); + } + } else { + if (query[key.requestParam] || query[key.requestParam] == 0) { + find.andWhere(`${key.column} like :${key.column}`, { + [key.column]: `%${query[key.requestParam]}%`, + }); + this.sqlParams.push(`%${query[key.requestParam]}%`); } } } diff --git a/typeorm/decorator/columns/Column.js b/typeorm/decorator/columns/Column.js index e9ef50e..16a7020 100644 --- a/typeorm/decorator/columns/Column.js +++ b/typeorm/decorator/columns/Column.js @@ -35,6 +35,7 @@ function Column(typeOrOptions, options) { if (options.type === "hstore" && !options.hstoreType) options.hstoreType = reflectMetadataType === Object ? "object" : "string"; + if (typeof typeOrOptions === "function") { // register an embedded (0, globals_1.getMetadataArgsStorage)().embeddeds.push({ @@ -61,6 +62,7 @@ function Column(typeOrOptions, options) { propertyName: propertyName, mode: "regular", options: options, + dict: options.dict, }); if (options.generated) { (0, globals_1.getMetadataArgsStorage)().generations.push({ diff --git a/typeorm/decorator/options/ColumnOptions.d.ts b/typeorm/decorator/options/ColumnOptions.d.ts index eeb8cc5..13fcef9 100644 --- a/typeorm/decorator/options/ColumnOptions.d.ts +++ b/typeorm/decorator/options/ColumnOptions.d.ts @@ -9,6 +9,10 @@ export interface ColumnOptions extends ColumnCommonOptions { * Column type. Must be one of the value from the ColumnTypes class. */ type?: ColumnType; + /** + * cool dict key + */ + dict?: string | string[]; /** * Column name in the database. */ diff --git a/typeorm/metadata/ColumnMetadata.d.ts b/typeorm/metadata/ColumnMetadata.d.ts index 434c7ba..ad832a2 100644 --- a/typeorm/metadata/ColumnMetadata.d.ts +++ b/typeorm/metadata/ColumnMetadata.d.ts @@ -11,6 +11,10 @@ import { ValueTransformer } from "../decorator/options/ValueTransformer"; */ export declare class ColumnMetadata { readonly "@instanceof": symbol; + /** + * cool + */ + dict?: string | string[]; /** * Target class where column decorator is used. * This may not be always equal to entity metadata (for example embeds or inheritance cases). diff --git a/typeorm/metadata/ColumnMetadata.js b/typeorm/metadata/ColumnMetadata.js index de2b0c4..6987e59 100644 --- a/typeorm/metadata/ColumnMetadata.js +++ b/typeorm/metadata/ColumnMetadata.js @@ -14,6 +14,7 @@ class ColumnMetadata { // --------------------------------------------------------------------- constructor(options) { this["@instanceof"] = Symbol.for("ColumnMetadata"); + this.dict = options.args.options.dict; /** * Type's length in the database. */ diff --git a/typeorm/query-builder/UpdateQueryBuilder.js b/typeorm/query-builder/UpdateQueryBuilder.js index e2a1a66..488dad2 100644 --- a/typeorm/query-builder/UpdateQueryBuilder.js +++ b/typeorm/query-builder/UpdateQueryBuilder.js @@ -10,6 +10,7 @@ const UpdateValuesMissingError_1 = require("../error/UpdateValuesMissingError"); const error_1 = require("../error"); const EntityPropertyNotFoundError_1 = require("../error/EntityPropertyNotFoundError"); const DriverUtils_1 = require("../driver/DriverUtils"); +const Broadcaster_1 = require("../subscriber/Broadcaster"); /** * Allows to build complex sql queries in a fashion way and execute those queries. */