From bf296f8d1b279240966bbc98c045f09e09c9a88f Mon Sep 17 00:00:00 2001 From: "huoyong.msb" Date: Wed, 23 Sep 2020 11:50:11 +0800 Subject: [PATCH] feat: add backup trigger --- src/routes/repository.ts | 37 +++++++++++++++++++- src/service/migrate.ts | 73 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 103 insertions(+), 7 deletions(-) diff --git a/src/routes/repository.ts b/src/routes/repository.ts index e175c9b..5407adb 100644 --- a/src/routes/repository.ts +++ b/src/routes/repository.ts @@ -965,6 +965,8 @@ router.post('/property/update', isLoggedIn, async (ctx) => { router.post('/properties/update', isLoggedIn, async (ctx, next) => { const itfId = +ctx.query.itf + let needBackup = false + let changeCount = 0 let { properties, summary } = ctx.request.body as { properties: Property[], summary: Interface } properties = Array.isArray(properties) ? properties : [properties] @@ -1010,6 +1012,7 @@ router.post('/properties/update', isLoggedIn, async (ctx, next) => { for (const deletedProperty of deletedProperties) { deletedPropertyLog.push(pLog(deletedProperty, '删除了')) } + changeCount += deletedProperties.length deletedPropertyLog.length && itfPropertiesChangeLog.push(deletedPropertyLog.join(LOG_SUB_SEPERATOR)) let result = await Property.destroy({ @@ -1033,6 +1036,7 @@ router.post('/properties/update', isLoggedIn, async (ctx, next) => { changed.push(`类型${o.type} => ${item.type}`) } changed.length && updatedPropertyLog.push(`${pLog(item, '更新了')} ${changed.join(' ')}`) + changeCount += changed.length } let affected = await Property.update(item, { where: { id: item.id }, @@ -1055,6 +1059,7 @@ router.post('/properties/update', isLoggedIn, async (ctx, next) => { item.id = created.id result += 1 } + changeCount += newProperties.length addedPropertyLog.length && itfPropertiesChangeLog.push(addedPropertyLog.join(LOG_SUB_SEPERATOR)) // 同步 parentId for (let item of newProperties) { @@ -1067,12 +1072,17 @@ router.post('/properties/update', isLoggedIn, async (ctx, next) => { include: (QueryInclude.RepositoryHierarchy as any).include[0].include, }) + if (changeCount >= 5) { + needBackup = true + } + if (itfPropertiesChangeLog.length) { await RepositoryService.addHistoryLog({ entityId: itf.id, entityType: Consts.ENTITY_TYPE.INTERFACE, - changeLog: `接口 ${itf.name}(${itf.url}) 参数变更: ${itfPropertiesChangeLog.join(LOG_SEPERATOR)}`, + changeLog: `接口 ${itf.name}(${itf.url}) 参数变更: ${itfPropertiesChangeLog.join(LOG_SEPERATOR)}${needBackup ? ', 改动较大已备份数据。' : ''}`, userId: ctx.session.id, + ...needBackup ? { relatedJSONData: JSON.stringify({ "itf": itf, "properties": properties }) } : {}, }) } @@ -1154,6 +1164,31 @@ router.post('/repository/importswagger', isLoggedIn, async (ctx) => { } }) +router.post('/repository/importRAP2Backup', isLoggedIn, async (ctx) => { + const { repositoryId, swagger, modId } = ctx.request.body + // 权限判断 + if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY_SET, ctx.session.id, repositoryId)) { + ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY + return + } + + try { + await MigrateService.importInterfaceFromJSON(swagger, ctx.session.id, repositoryId, modId) + ctx.body = { + isOk: 'success', + message: '导入成功', + repository: { + id: 1, + } + } + } catch (ex) { + ctx.body = { + isOk: 'failure', + message: `导入失败: ${ex.message}`, + } + } +}) + router.post('/repository/importJSON', isLoggedIn, async ctx => { const { data } = ctx.request.body diff --git a/src/service/migrate.ts b/src/service/migrate.ts index 7116eda..aa0924b 100644 --- a/src/service/migrate.ts +++ b/src/service/migrate.ts @@ -1171,6 +1171,67 @@ export default class MigrateService { } } + public static async importInterfaceFromJSON(data: any, curUserId: number, repositoryId: number, modId: number) { + + let itfData = data.itf + let properties = data.properties + + const itf = await Interface.create({ + moduleId: modId, + name: itfData.name, + description: itfData.description || '', + url: itfData.url, + priority: 1, + creatorId: curUserId, + repositoryId, + method: itfData.method, + }) + + if (!properties) { + properties = [] + } + + const idMaps: any = {} + + await Promise.all( + properties.map(async (pData, index) => { + const property = await Property.create({ + scope: pData.scope, + name: pData.name, + rule: pData.rule, + value: pData.value, + type: pData.type, + description: pData.description, + pos: pData.pos, + priority: 1 + index, + interfaceId: itf.id, + creatorId: curUserId, + moduleId: modId, + repositoryId, + parentId: -1, + }) + idMaps[pData.id] = property.id + }), + ) + + await Promise.all( + properties.map(async pData => { + const newId = idMaps[pData.id] + const newParentId = idMaps[pData.parentId] + await Property.update( + { + parentId: newParentId, + }, + { + where: { + id: newId, + }, + }, + ) + }), + ) + await RedisService.delCache(CACHE_KEY.REPOSITORY_GET, repositoryId) + } /** 可以直接让用户把自己本地的 data 数据导入到 RAP 中 */ public static async importRepoFromJSON(data: JsonData, curUserId: number, createRepo: boolean = false, orgId?: number) { function parseJSON(str: string) { @@ -1187,12 +1248,12 @@ export default class MigrateService { throw new Error("orgId is essential while createRepo = true") } const repo = await Repository.create({ - name: data.name, - description: data.description, - visibility: true, - ownerId: curUserId, - creatorId: curUserId, - organizationId: orgId, + name: data.name, + description: data.description, + visibility: true, + ownerId: curUserId, + creatorId: curUserId, + organizationId: orgId, }) data.id = repo.id }