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