Skip to content

Instantly share code, notes, and snippets.

@Diluka
Created September 19, 2019 14:57
Show Gist options
  • Save Diluka/1d326621ce60d63572f58fc42843c518 to your computer and use it in GitHub Desktop.
Save Diluka/1d326621ce60d63572f58fc42843c518 to your computer and use it in GitHub Desktop.
CRUD common query for sequelize with routing-controllers
import {
Authorized,
BadRequestError,
BodyParam,
ForbiddenError,
JsonController,
NotFoundError,
Put,
QueryParam
} from "routing-controllers";
import {IIncludeOptions, Sequelize} from "sequelize-typescript";
import {Inject} from "typedi";
import {BaseModel} from "../../common/domain/base-model";
import {Role, User} from "../../model/User";
import * as _ from "lodash";
import {Pageable, PageInfo} from "../../common/query/pagination";
import {plainToClass} from "class-transformer";
import {validateOrReject} from "class-validator";
import {Transaction} from "../../decorator/transaction.decorator";
import {LogInvoke} from "../../decorator/log-invoke.decorator";
const DO_NOT_MODIFIED_MODELS = ["User", "WxUser", "SysVar", "ACL"];
@Authorized(Role.ADMIN)
@JsonController("/commons/crud")
export class CrudController {
@Inject()
db: Sequelize;
@LogInvoke("创建")
@Transaction()
@Put("/create")
async create(@BodyParam("data", {required: true}) data: any,
@BodyParam("options", {required: true}) options: any,
@QueryParam("method", {required: true}) method: string,
@QueryParam("model", {required: true}) model: string) {
this.filterModels(model);
const M = this.getModel(model);
this.processOptions(options);
if (_.isArray(data)) {
let entities = plainToClass(M, data);
for (const entity of entities) {
await validateOrReject(entity);
}
switch (method) {
case "bulkCreate":
return M.bulkCreate(data, options);
default:
throw new BadRequestError(`不支持的创建方法:${method}`);
}
} else {
let entity = plainToClass(M, data);
await validateOrReject(entity);
switch (method) {
case "create":
case "insert":
return M.create(data, options);
case "findOrCreate":
return M.findOrCreate({defaults: data, ...options});
default:
throw new BadRequestError(`不支持的创建方法:${method}`);
}
}
}
@LogInvoke("取回")
@Transaction()
@Put("/retrieve")
async retrieve(@BodyParam("data") data: any,
@BodyParam("options", {required: true}) options: any,
@QueryParam("method", {required: true}) method: string,
@QueryParam("model", {required: true}) model: string) {
const M = this.getModel(model);
this.processOptions(options);
switch (method) {
case "findById":
case "findByPrimary":
return M.findById(data, options);
case "findOne":
return M.findOne(options);
case "findAll":
return M.findAll(options);
case "findAndCountAll":
let page = await M.findAndCountAll(options);
let pageable = new Pageable();
pageable.type = "row";
pageable.limit = options.limit || page.count;
pageable.offset = options.offset || 0;
pageable.normalize();
return new PageInfo(page, pageable);
case "count":
let count = await M.count(options);
return {count};
case "max":
let max = await M.max(data, options);
return {max};
case "min":
let min = await M.min(data, options);
return {min};
case "sum":
let sum = await M.sum(data, options);
return {sum};
case "avg":
let value = await M.aggregate(data, method, options);
return {[method]: value};
default:
throw new BadRequestError(`不支持的查询方法:${method}`);
}
}
@LogInvoke("更新")
@Transaction()
@Put("/update")
async update(@BodyParam("data", {required: true}) data: any,
@BodyParam("options", {required: true}) options: any,
@QueryParam("method", {required: true}) method: string,
@QueryParam("model", {required: true}) model: string) {
this.filterModels(model);
const M = this.getModel(model);
this.processOptions(options);
switch (method) {
case "update":
return {result: await M.update(data, options)};
case "insertOrUpdate":
case "upsert":
return {result: await M.upsert(data, options)};
case "increment":
return {result: await M.increment(data, options)};
default:
throw new BadRequestError(`不支持的更新方法:${method}`);
}
}
@LogInvoke("销毁")
@Transaction()
@Put("/destroy")
async destroy(@BodyParam("data") data: any,
@BodyParam("options", {required: true}) options: any,
@QueryParam("method", {required: true}) method: string,
@QueryParam("model", {required: true}) model: string) {
this.filterModels(model);
const M = this.getModel(model);
this.processOptions(options);
switch (method) {
case "destroy":
return {result: await M.destroy(options)};
case "truncate":
return M.truncate(options);
case "restore":
return M.restore(options);
default:
throw new BadRequestError(`不支持的删除方法:${method}`);
}
}
private getModel(model: string) {
const M: typeof BaseModel & (new() => BaseModel<any>) = this.db._[model] as any;
if (!M) {
throw new NotFoundError(`实体不存在:${model}`);
}
return M;
}
private processOptions(options: any) {
if (options) {
let include = _.get(options, "include") as IIncludeOptions[];
if (include) {
this.processInclude(include);
}
delete options.transaction;
}
}
private processInclude(include: IIncludeOptions[]) {
if (_.isArray(include)) {
for (let o of include) {
if (_.isString(o.model)) {
o.model = this.getModel(o.model);
}
this.processInclude(o.include as any[]);
}
}
}
private filterModels(model: string) {
if (DO_NOT_MODIFIED_MODELS.includes(model)) {
throw new ForbiddenError(`不允许修改的实体:${model}`);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment