2020-12-01 20:11:39 +08:00
|
|
|
|
import _ from 'lodash';
|
|
|
|
|
import BaseModel from './base';
|
2020-12-21 10:42:44 +08:00
|
|
|
|
import Field from './field';
|
2020-12-01 20:11:39 +08:00
|
|
|
|
import { TableOptions } from '@nocobase/database';
|
2020-12-08 09:59:41 +08:00
|
|
|
|
import { SaveOptions, Op } from 'sequelize';
|
2020-12-04 21:09:39 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成随机数据库表名
|
|
|
|
|
*
|
|
|
|
|
* 策略:暂时使用 3+2
|
|
|
|
|
* 1. 自增 id
|
|
|
|
|
* 2. 随机字母
|
|
|
|
|
* 3. 时间戳
|
|
|
|
|
* 4. 转拼音
|
|
|
|
|
* 5. 常见词翻译
|
|
|
|
|
*
|
|
|
|
|
* @param title 显示的名称
|
|
|
|
|
*/
|
|
|
|
|
export function generateCollectionName(title?: string): string {
|
|
|
|
|
return `t_${Date.now().toString(36)}_${Math.random().toString(36).replace('0.', '').slice(-4).padStart(4, '0')}`;
|
|
|
|
|
}
|
2020-10-24 15:34:43 +08:00
|
|
|
|
|
2020-12-08 09:59:41 +08:00
|
|
|
|
export interface LoadOptions {
|
|
|
|
|
reset?: boolean;
|
|
|
|
|
where?: any;
|
2020-12-23 20:29:11 +08:00
|
|
|
|
skipExisting?: boolean;
|
2020-12-08 09:59:41 +08:00
|
|
|
|
[key: string]: any;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface MigrateOptions {
|
|
|
|
|
[key: string]: any;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-01 20:11:39 +08:00
|
|
|
|
export class CollectionModel extends BaseModel {
|
|
|
|
|
|
2020-12-04 21:09:39 +08:00
|
|
|
|
generateName() {
|
|
|
|
|
this.set('name', generateCollectionName());
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-01 20:11:39 +08:00
|
|
|
|
/**
|
|
|
|
|
* 通过 name 获取 collection
|
|
|
|
|
*
|
|
|
|
|
* @param name
|
|
|
|
|
*/
|
|
|
|
|
static async findByName(name: string) {
|
|
|
|
|
return this.findOne({ where: { name } });
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-08 09:59:41 +08:00
|
|
|
|
/**
|
|
|
|
|
* DOTO:
|
|
|
|
|
* - database.table 初始化可能存在一些缺陷
|
|
|
|
|
* - 是否需要考虑关系数据的重载?
|
|
|
|
|
*
|
|
|
|
|
* @param opts
|
|
|
|
|
*/
|
|
|
|
|
async loadTableOptions(opts: any = {}) {
|
2020-12-01 20:11:39 +08:00
|
|
|
|
const options = await this.getOptions();
|
|
|
|
|
const prevTable = this.database.getTable(this.get('name'));
|
|
|
|
|
const prevOptions = prevTable ? prevTable.getOptions() : {};
|
|
|
|
|
// table 是初始化和重新初始化
|
2020-12-08 09:59:41 +08:00
|
|
|
|
const table = this.database.table({...prevOptions, ...options});
|
|
|
|
|
// 如果关系表未加载,一起处理
|
2020-12-23 20:29:11 +08:00
|
|
|
|
// const associationTableNames = [];
|
|
|
|
|
// for (const [key, association] of table.getAssociations()) {
|
|
|
|
|
// // TODO:是否需要考虑重载的情况?(暂时是跳过处理)
|
|
|
|
|
// if (!this.database.isDefined(association.options.target)) {
|
|
|
|
|
// continue;
|
|
|
|
|
// }
|
|
|
|
|
// associationTableNames.push(association.options.target);
|
|
|
|
|
// }
|
|
|
|
|
// console.log({associationTableNames});
|
|
|
|
|
// if (associationTableNames.length) {
|
|
|
|
|
// await CollectionModel.load({
|
|
|
|
|
// ...opts,
|
|
|
|
|
// where: {
|
|
|
|
|
// name: {
|
|
|
|
|
// [Op.in]: associationTableNames,
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
// }
|
2020-12-08 09:59:41 +08:00
|
|
|
|
return table;
|
2020-12-07 17:21:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 迁移
|
|
|
|
|
*/
|
2020-12-08 09:59:41 +08:00
|
|
|
|
async migrate(options: MigrateOptions = {}) {
|
2021-01-07 14:54:01 +08:00
|
|
|
|
const { isNewRecord } = options;
|
2020-12-08 14:35:50 +08:00
|
|
|
|
const table = await this.loadTableOptions(options);
|
2021-01-07 14:54:01 +08:00
|
|
|
|
// 如果不是新增数据,force 必须为 false
|
|
|
|
|
if (!isNewRecord) {
|
|
|
|
|
return await table.sync({
|
|
|
|
|
force: false,
|
|
|
|
|
alter: {
|
|
|
|
|
drop: false,
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// TODO: 暂时加了个 collectionSync 解决 collection.create 的数据不清空问题
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
const sync = this.sequelize.options.collectionSync;
|
|
|
|
|
return await table.sync(sync || {
|
2020-12-01 20:11:39 +08:00
|
|
|
|
force: false,
|
|
|
|
|
alter: {
|
|
|
|
|
drop: false,
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getFieldsOptions() {
|
|
|
|
|
const fieldsOptions = [];
|
|
|
|
|
const fields = await this.getFields();
|
|
|
|
|
for (const field of fields) {
|
|
|
|
|
fieldsOptions.push(await field.getOptions());
|
2020-10-24 15:34:43 +08:00
|
|
|
|
}
|
2020-12-01 20:11:39 +08:00
|
|
|
|
return fieldsOptions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getOptions(): Promise<TableOptions> {
|
|
|
|
|
return {
|
2020-12-11 10:37:43 +08:00
|
|
|
|
...this.get(),
|
2020-12-01 20:11:39 +08:00
|
|
|
|
fields: await this.getFieldsOptions(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-08 09:59:41 +08:00
|
|
|
|
/**
|
|
|
|
|
* TODO:需要考虑是初次加载还是重载
|
|
|
|
|
*
|
|
|
|
|
* @param options
|
|
|
|
|
*/
|
|
|
|
|
static async load(options: LoadOptions = {}) {
|
2020-12-23 20:29:11 +08:00
|
|
|
|
const { skipExisting = false, reset = false, where = {}, transaction } = options;
|
2020-12-08 09:59:41 +08:00
|
|
|
|
const collections = await this.findAll({
|
2020-12-08 14:35:50 +08:00
|
|
|
|
transaction,
|
2020-12-08 09:59:41 +08:00
|
|
|
|
where,
|
|
|
|
|
});
|
2020-12-07 17:21:14 +08:00
|
|
|
|
for (const collection of collections) {
|
2020-12-23 20:29:11 +08:00
|
|
|
|
if (skipExisting && this.database.isDefined(collection.get('name'))) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-12-08 09:59:41 +08:00
|
|
|
|
await collection.loadTableOptions({
|
2020-12-08 14:35:50 +08:00
|
|
|
|
transaction,
|
2020-12-08 09:59:41 +08:00
|
|
|
|
reset,
|
|
|
|
|
});
|
2020-12-07 17:21:14 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-01 20:11:39 +08:00
|
|
|
|
static async import(data: TableOptions, options: SaveOptions = {}): Promise<CollectionModel> {
|
|
|
|
|
data = _.cloneDeep(data);
|
2020-12-16 08:42:37 +08:00
|
|
|
|
// @ts-ignore
|
|
|
|
|
const { update } = options;
|
2020-12-20 12:52:15 +08:00
|
|
|
|
let collection: CollectionModel;
|
2020-12-16 08:42:37 +08:00
|
|
|
|
if (data.name) {
|
|
|
|
|
collection = await this.findOne({
|
|
|
|
|
...options,
|
|
|
|
|
where: {
|
|
|
|
|
name: data.name,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
} else if (data.title) {
|
|
|
|
|
collection = await this.findOne({
|
|
|
|
|
...options,
|
|
|
|
|
where: {
|
|
|
|
|
title: data.title,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (collection && update) {
|
|
|
|
|
await collection.update(data, options);
|
|
|
|
|
}
|
|
|
|
|
if (!collection) {
|
|
|
|
|
collection = await this.create(data, options);
|
|
|
|
|
}
|
2020-12-01 20:11:39 +08:00
|
|
|
|
const associations = ['fields', 'tabs', 'actions', 'views'];
|
|
|
|
|
for (const key of associations) {
|
|
|
|
|
if (!Array.isArray(data[key])) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-12-16 08:42:37 +08:00
|
|
|
|
const Model = this.database.getModel(key);
|
2020-12-21 10:42:44 +08:00
|
|
|
|
let ids = [];
|
2020-12-25 16:15:58 +08:00
|
|
|
|
const View = this.database.getModel('views');
|
2020-12-16 20:47:10 +08:00
|
|
|
|
for (const index in data[key]) {
|
2020-12-21 10:42:44 +08:00
|
|
|
|
if (key === 'fields') {
|
|
|
|
|
ids = await Field.import(data[key], {
|
|
|
|
|
...options,
|
|
|
|
|
collectionName: collection.name,
|
|
|
|
|
});
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-12-16 08:42:37 +08:00
|
|
|
|
let model;
|
2020-12-16 20:47:10 +08:00
|
|
|
|
const item = data[key][index];
|
2020-12-16 08:42:37 +08:00
|
|
|
|
if (item.name) {
|
|
|
|
|
model = await Model.findOne({
|
|
|
|
|
...options,
|
|
|
|
|
where: {
|
|
|
|
|
collection_name: collection.name,
|
|
|
|
|
name: item.name,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
} else if (item.title) {
|
|
|
|
|
model = await Model.findOne({
|
|
|
|
|
...options,
|
|
|
|
|
where: {
|
|
|
|
|
collection_name: collection.name,
|
|
|
|
|
title: item.title,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (model && update) {
|
2020-12-16 20:47:10 +08:00
|
|
|
|
await model.update({...item, sort: index+1}, options);
|
2020-12-16 08:42:37 +08:00
|
|
|
|
}
|
|
|
|
|
if (!model) {
|
|
|
|
|
model = await Model.create({
|
|
|
|
|
...item,
|
2020-12-16 20:47:10 +08:00
|
|
|
|
sort: index+1,
|
2020-12-16 08:42:37 +08:00
|
|
|
|
collection_name: collection.name,
|
|
|
|
|
}, options);
|
|
|
|
|
}
|
2020-12-20 12:52:15 +08:00
|
|
|
|
if (model) {
|
2020-12-25 16:15:58 +08:00
|
|
|
|
if (key === 'tabs') {
|
|
|
|
|
let associationField;
|
|
|
|
|
if (item.association) {
|
|
|
|
|
associationField = await Field.findOne({
|
|
|
|
|
where: {
|
|
|
|
|
name: item.association,
|
|
|
|
|
collection_name: collection.name,
|
|
|
|
|
},
|
|
|
|
|
});
|
2021-01-05 17:31:41 +08:00
|
|
|
|
// TODO: tabs 表还未创建,暂时先这么处理
|
|
|
|
|
if (associationField) {
|
|
|
|
|
await model.updateAssociations({
|
|
|
|
|
associationField: associationField.id,
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-12-25 16:15:58 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-20 12:52:15 +08:00
|
|
|
|
ids.push(model.id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (collection.get('internal')) {
|
|
|
|
|
await collection.updateAssociations({
|
|
|
|
|
[key]: ids,
|
|
|
|
|
});
|
2020-12-16 08:42:37 +08:00
|
|
|
|
}
|
2020-12-01 20:11:39 +08:00
|
|
|
|
}
|
|
|
|
|
return collection;
|
2020-10-24 15:34:43 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default CollectionModel;
|