fix: 修复有互相引用的字段导入问题 (#1568)
Co-authored-by: sealday <zhanglin@daoyoucloud.com> Reviewed-on: daoyoucloud/tachybase#1568 Reviewed-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: Toby <2287769986@qq.com> Co-committed-by: Toby <2287769986@qq.com>
This commit is contained in:
parent
b7ee485cd1
commit
f411efb1bc
@ -75,8 +75,10 @@ const ImportUpload = (props: any) => {
|
|||||||
message.success(`${info.file.name} ` + t('file uploaded successfully'));
|
message.success(`${info.file.name} ` + t('file uploaded successfully'));
|
||||||
refresh();
|
refresh();
|
||||||
await refreshCM();
|
await refreshCM();
|
||||||
|
window.location.reload();
|
||||||
} else if (status === 'error') {
|
} else if (status === 'error') {
|
||||||
// message.error(`${info.file.name} ` + t('file upload failed'));
|
message.error(`${info.file.name} ` + t('file upload failed'));
|
||||||
|
window.location.reload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDrop(e) {
|
onDrop(e) {
|
||||||
@ -137,7 +139,6 @@ export const ImportCollectionMetaAction = React.forwardRef((props, ref) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [dataTypes, setDataTypes] = useState<any[]>(['required']);
|
const [dataTypes, setDataTypes] = useState<any[]>(['required']);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
// const [restoreData, setRestoreData] = useState<any>(null);
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const showModal = () => {
|
const showModal = () => {
|
||||||
@ -150,12 +151,10 @@ export const ImportCollectionMetaAction = React.forwardRef((props, ref) => {
|
|||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
setIsModalOpen(false);
|
setIsModalOpen(false);
|
||||||
// setRestoreData(null);
|
|
||||||
setDataTypes(['required']);
|
setDataTypes(['required']);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* <ButtonComponent onClick={showModal}>{t('Import')}</ButtonComponent> */}
|
|
||||||
<Modal
|
<Modal
|
||||||
title={t('Import')}
|
title={t('Import')}
|
||||||
width={800}
|
width={800}
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
import fsPromises from 'fs/promises';
|
import fsPromises from 'fs/promises';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import { Readable } from 'stream';
|
|
||||||
import { Context } from '@tachybase/actions';
|
import { Context } from '@tachybase/actions';
|
||||||
import { ResourceOptions } from '@tachybase/resourcer';
|
import { ResourceOptions } from '@tachybase/resourcer';
|
||||||
import { koaMulter as multer, uid } from '@tachybase/utils';
|
import { koaMulter as multer, uid } from '@tachybase/utils';
|
||||||
|
|
||||||
import lodash from 'lodash';
|
|
||||||
|
|
||||||
export const collectionImportExportMeta: ResourceOptions = {
|
export const collectionImportExportMeta: ResourceOptions = {
|
||||||
name: 'collections',
|
name: 'collections',
|
||||||
middleware: async (ctx, next) => {
|
middleware: async (ctx, next) => {
|
||||||
@ -38,20 +35,34 @@ export const collectionImportExportMeta: ResourceOptions = {
|
|||||||
if (metaList.some((item) => item.name === name)) {
|
if (metaList.some((item) => item.name === name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const meta = await Collections.repository.findOne({
|
const metaRecord = await Collections.repository.findOne({
|
||||||
filterByTk: name,
|
filterByTk: name,
|
||||||
context: ctx,
|
context: ctx,
|
||||||
appends: ['category', 'fields'],
|
appends: ['category', 'fields'],
|
||||||
});
|
});
|
||||||
if (!meta) {
|
if (!metaRecord) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const meta = metaRecord.toJSON();
|
||||||
|
if (meta.inherits) {
|
||||||
|
for (const inherit of meta.inherits) {
|
||||||
|
await getAssociationCollection(inherit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (meta.view) {
|
||||||
|
const collection = ctx.db.getCollection(meta.viewName);
|
||||||
|
const viewDef = await ctx.db.queryInterface.viewDef(collection.getTableNameWithSchemaAsString());
|
||||||
|
const viewSql = [
|
||||||
|
`DROP VIEW IF EXISTS ${collection.getTableNameWithSchemaAsString()}`,
|
||||||
|
`CREATE VIEW ${collection.getTableNameWithSchemaAsString()} AS ${viewDef}`,
|
||||||
|
];
|
||||||
|
meta.viewSql = viewSql;
|
||||||
|
}
|
||||||
metaList.push(meta);
|
metaList.push(meta);
|
||||||
const fields = meta.dataValues.fields;
|
const fields = meta.fields;
|
||||||
for (const field of fields) {
|
for (const field of fields) {
|
||||||
ctx.logger.info('field.dataValues', field.dataValues);
|
if (field?.options?.target) {
|
||||||
if (field?.dataValues?.options?.target) {
|
await getAssociationCollection(field?.options?.target);
|
||||||
await getAssociationCollection(field?.dataValues?.options?.target);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,49 +87,102 @@ export const collectionImportExportMeta: ResourceOptions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let createCount = 0;
|
let createCount = 0;
|
||||||
|
const reverseKeys = [];
|
||||||
async function importCollection(meta) {
|
|
||||||
const CollectionRepo = ctx.db.getCollection('collections');
|
|
||||||
// 检查表是否存在
|
|
||||||
let collection = await CollectionRepo.repository.findOne({
|
|
||||||
filterByTk: meta.name,
|
|
||||||
context: ctx,
|
|
||||||
});
|
|
||||||
if (collection) {
|
|
||||||
return;
|
|
||||||
throw new Error('collection name already exists, please change the name');
|
|
||||||
}
|
|
||||||
collection = await CollectionRepo.repository.findOne({
|
|
||||||
filter: { key: meta.key },
|
|
||||||
context: ctx,
|
|
||||||
});
|
|
||||||
if (collection) {
|
|
||||||
return;
|
|
||||||
throw new Error('collection key already exists, please change the key');
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate unique key
|
|
||||||
// meta.fields.forEach((field) => {
|
|
||||||
// field.key = uid();
|
|
||||||
// });
|
|
||||||
|
|
||||||
// 检查field是否已经存在
|
// 检查field是否已经存在
|
||||||
const fieldRepo = ctx.db.getCollection('fields');
|
const fieldRepo = ctx.db.getCollection('fields');
|
||||||
const fieldKeys = meta.fields.map((field) => field.key);
|
|
||||||
|
|
||||||
const existFields = await fieldRepo.repository.findOne({
|
// old name => new name
|
||||||
filter: { key: { $in: fieldKeys } },
|
const collectionNameMap = {};
|
||||||
|
const completedCollections = new Set();
|
||||||
|
const createList = [];
|
||||||
|
|
||||||
|
const existCollections = await ctx.db.getCollection('collections').repository.find({
|
||||||
|
filter: {
|
||||||
|
$or: {
|
||||||
|
name: { $in: metaList.map((item) => item.name) },
|
||||||
|
key: { $in: metaList.map((item) => item.key) },
|
||||||
|
},
|
||||||
|
},
|
||||||
context: ctx,
|
context: ctx,
|
||||||
});
|
});
|
||||||
if (existFields) {
|
|
||||||
return;
|
const allFieldKeys = [];
|
||||||
throw new Error('field key already exists, please change the key');
|
for (const meta of metaList) {
|
||||||
|
allFieldKeys.push(...meta.fields.map((item) => item.key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const views = [];
|
||||||
|
const CollectionRepo = ctx.db.getCollection('collections');
|
||||||
|
|
||||||
|
async function importCollection(meta) {
|
||||||
|
if (!meta) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (completedCollections.has(meta.name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (meta.inherits && meta.inherits.length > 0) {
|
||||||
|
for (const inherit of meta.inherits) {
|
||||||
|
if (!completedCollections.has(inherit)) {
|
||||||
|
const inheritCollection = metaList.find((v) => v.name === inherit);
|
||||||
|
await importCollection(inheritCollection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let collection = existCollections.find((v) => v.key === meta.key);
|
||||||
|
// key和name完全相同的两个collection, 出现重复
|
||||||
|
if (collection && collection.name === meta.name) {
|
||||||
|
completedCollections.add(collection.name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// key一样 name不一样则需要生成新的key
|
||||||
|
if (collection && collection.name !== meta.name) {
|
||||||
|
meta.key = uid();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
collection = existCollections.find((v) => v.name === meta.name);
|
||||||
|
if (collection) {
|
||||||
|
// 重复导入则放过
|
||||||
|
collection.name = `t_${uid()}`;
|
||||||
|
collectionNameMap[meta.name] = collection.name;
|
||||||
|
meta.name = collection.name;
|
||||||
|
if (meta.category && meta.category.length) {
|
||||||
|
meta.category.forEach((v) => {
|
||||||
|
v.collectionCategory.collectionName = collection.name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalFieldKeys = [];
|
||||||
|
if (meta.fields) {
|
||||||
|
for (const field of meta.fields) {
|
||||||
|
if (!field.reverseKey) {
|
||||||
|
if (field.collectionName && collectionNameMap[field.collectionName]) {
|
||||||
|
field.collectionName = collectionNameMap[field.collectionName];
|
||||||
|
}
|
||||||
|
if (field.target && collectionNameMap[field.target]) {
|
||||||
|
field.target = collectionNameMap[field.target];
|
||||||
|
}
|
||||||
|
normalFieldKeys.push(field);
|
||||||
|
} else if (!reverseKeys.some((v) => v === field.key || v === field.reverseKey)) {
|
||||||
|
reverseKeys.push({ ...field, collectionName: meta.key });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
meta.fields = normalFieldKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视图
|
||||||
|
if (meta.view) {
|
||||||
|
views.push(meta);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await CollectionRepo.repository.create({
|
await CollectionRepo.repository.create({
|
||||||
values: meta,
|
values: meta,
|
||||||
context: ctx,
|
context: ctx,
|
||||||
});
|
});
|
||||||
|
completedCollections.add(meta.name);
|
||||||
createCount++;
|
createCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,8 +190,26 @@ export const collectionImportExportMeta: ResourceOptions = {
|
|||||||
await importCollection(meta);
|
await importCollection(meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const meta of views) {
|
||||||
|
if (!meta.viewSql || !meta.viewSql.length) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const sql of meta.viewSql) {
|
||||||
|
await ctx.db.sequelize.query(sql);
|
||||||
|
}
|
||||||
|
await CollectionRepo.repository.create({
|
||||||
|
values: meta,
|
||||||
|
context: ctx,
|
||||||
|
});
|
||||||
|
createCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const reverseKey of reverseKeys) {
|
||||||
|
await fieldRepo.repository.create(reverseKey);
|
||||||
|
}
|
||||||
|
|
||||||
if (!createCount) {
|
if (!createCount) {
|
||||||
throw new Error('no collection created, please check the key or name');
|
throw new Error('no collection created, please check the name');
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = { count: createCount };
|
ctx.body = { count: createCount };
|
||||||
|
Loading…
Reference in New Issue
Block a user