Skip to content

Commit eed68d6

Browse files
authored
[app-builder] 新增应用编排的版本管理 (#193)
* [app-builder] 修改查询历史发布版本接口,新增恢复应用到指定版本接口 * [frontend] 增加应用编排回退到历史版本功能 * [frontend] 增加最新版本tag,预览模式取消自动保存 * [frontend] 预览模式时隐藏编排页面左侧工具栏 * [app-builder] 修改检视意见,修改恢复应用接口名称,补充操作日志日志和国际化信息 * [app-builder] 修改检视意见
1 parent c071a75 commit eed68d6

File tree

41 files changed

+609
-218
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+609
-218
lines changed

app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderAppController.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ public Rsp<AppBuilderAppDto> queryLatestOrchestration(HttpClassicServerRequest h
176176
* @return 查询结果列表。
177177
*/
178178
@GetMapping(value = "/{app_id}/recentPublished", description = "查询 app 的历史发布版本")
179-
public Rsp<List<PublishedAppResDto>> recentPublished(HttpClassicServerRequest httpRequest,
179+
public Rsp<RangedResultSet<AppBuilderAppDto>> recentPublished(HttpClassicServerRequest httpRequest,
180180
@PathVariable("app_id") String appId, @PathVariable("tenant_id") String tenantId,
181181
@RequestParam(value = "offset", defaultValue = "0") long offset,
182182
@RequestParam(value = "limit", defaultValue = "10") int limit, @RequestBean AppQueryCondition cond) {
@@ -425,6 +425,23 @@ public Rsp<AppBuilderAppDto> importApp(HttpClassicServerRequest httpRequest,
425425
}
426426
}
427427

428+
/**
429+
* 恢复应用到指定历史版本。
430+
*
431+
* @param httpRequest 表示 http 请求的 {@link HttpClassicServerRequest}。
432+
* @param tenantId 表示租户唯一标识的 {@link String}。
433+
* @param appId 表示应用唯一标识的 {@link String}。
434+
* @param recoverAppId 表示指定历史版本唯一标识的 {@link String}。
435+
* @return 表示恢复后应用信息的 {@link AppBuilderAppDto}。
436+
*/
437+
@CarverSpan(value = "operation.appBuilderApp.recoverApp")
438+
@PostMapping(path = "/{app_id}/recover")
439+
public Rsp<AppBuilderAppDto> recoverApp(HttpClassicServerRequest httpRequest,
440+
@PathVariable("tenant_id") String tenantId, @PathVariable("app_id") String appId,
441+
@RequestBody String recoverAppId) {
442+
return Rsp.ok(this.appService.recoverApp(appId, recoverAppId, contextOf(httpRequest, tenantId)));
443+
}
444+
428445
private AppQueryCondition buildAppQueryCondition(AppQueryCondition cond, String type) {
429446
cond.setType(type);
430447
if (cond.getExcludeNames() == null) {

app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderAppService.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import modelengine.fit.jober.aipp.dto.template.TemplateAppCreateDto;
2424
import modelengine.fit.jober.aipp.dto.template.TemplateInfoDto;
2525
import modelengine.fit.jober.common.RangedResultSet;
26-
import modelengine.fit.http.server.HttpClassicServerRequest;
2726
import modelengine.fit.jane.common.entity.OperationContext;
2827
import modelengine.fitframework.annotation.Genericable;
2928

@@ -157,10 +156,10 @@ Rsp<RangedResultSet<AppBuilderAppMetadataDto>> list(AppQueryCondition cond, Oper
157156
* @param limit 表示获取数据的最大个数的 {@code int}。
158157
* @param appId 表示应用唯一标识的 {@link String}。
159158
* @param context 表示操作上下文的 {@link OperationContext}。
160-
* @return 获取到的历史版本信息集合的 {@link List}{@code <}{@link PublishedAppResDto}{@code >}。
159+
* @return 获取到的历史版本信息集合的 {@link RangedResultSet}{@code <}{@link PublishedAppResDto}{@code >}。
161160
*/
162161
@Genericable(id = "modelengine.fit.jober.aipp.service.app.recent.published")
163-
List<PublishedAppResDto> recentPublished(AppQueryCondition cond, long offset, int limit, String appId,
162+
RangedResultSet<AppBuilderAppDto> recentPublished(AppQueryCondition cond, long offset, int limit, String appId,
164163
OperationContext context);
165164

166165
/**
@@ -231,4 +230,15 @@ List<PublishedAppResDto> recentPublished(AppQueryCondition cond, long offset, in
231230
*/
232231
@Genericable(id = "modelengine.fit.jober.aipp.service.app.deleteTemplate")
233232
void deleteTemplate(String templateId, OperationContext context);
233+
234+
/**
235+
* 恢复应用到指定历史版本。
236+
*
237+
* @param appId 表示应用唯一标识的 {@link String}。
238+
* @param resetId 表示指定历史版本唯一标识的 {@link String}。
239+
* @param context 表示接口操作上下文的 {@link OperationContext}。
240+
* @return 表示恢复应用完成后应用详情的 {@link AppBuilderAppDto}。
241+
*/
242+
@Genericable(id = "modelengine.fit.jober.aipp.service.app.recover")
243+
AppBuilderAppDto recoverApp(String appId, String resetId, OperationContext context);
234244
}

app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderAppServiceImpl.java

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -600,15 +600,7 @@ private AppBuilderAppDto createAppWithTemplate(AppBuilderAppCreateDto dto, AppBu
600600
AppBuilderFlowGraph flowGraph = templateApp.getFlowGraph();
601601
flowGraph.setId(Entities.generateId());
602602
Map<String, Object> appearance;
603-
try {
604-
appearance = JSONObject.parseObject(flowGraph.getAppearance(), new TypeReference<Map<String, Object>>() {});
605-
} catch (JSONException e) {
606-
log.error("Import config failed, cause: {}", e);
607-
throw new AippException(AippErrCode.IMPORT_CONFIG_FIELD_ERROR, "flowGraph.appearance");
608-
}
609-
appearance.computeIfPresent("id", (key, value) -> flowGraph.getId());
610-
// 这里在创建应用时需要保证graph中的title+version唯一,否则在发布flow时会报错
611-
appearance.put("title", flowGraph.getId());
603+
appearance = this.resetGraphId(flowGraph);
612604
// 动态修改graph中的model为可选model的第一个
613605
flowGraph.setAppearance(JSONObject.toJSONString(appearance));
614606
String version = this.buildVersion(templateApp, isUpgrade);
@@ -1282,24 +1274,25 @@ public void deleteTemplate(String templateId, OperationContext context) {
12821274
}
12831275

12841276
@Override
1285-
public List<PublishedAppResDto> recentPublished(AppQueryCondition cond, long offset, int limit, String appId,
1286-
OperationContext context) {
1277+
public RangedResultSet<AppBuilderAppDto> recentPublished(AppQueryCondition cond, long offset, int limit,
1278+
String appId, OperationContext context) {
12871279
this.validateApp(appId);
12881280
try {
12891281
String aippId = MetaUtils.getAippIdByAppId(this.metaService, appId, context);
1290-
List<Meta> allPublishedMeta = MetaUtils.getAllPublishedMeta(this.metaService, aippId, context)
1291-
.stream()
1292-
.filter(meta -> !this.isAppBelong(appId, meta))
1293-
.collect(Collectors.toList());
1282+
RangedResultSet<Meta> metaRangedResultSet =
1283+
MetaUtils.getPublishedMetaByPage(this.metaService, aippId, offset, limit, context);
1284+
List<Meta> allPublishedMeta = metaRangedResultSet.getResults();
12941285
List<String> appIds = allPublishedMeta.stream()
12951286
.map(meta -> String.valueOf(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY)))
12961287
.collect(Collectors.toList());
12971288
cond.setIds(appIds);
12981289
cond.setTenantId(context.getTenantId());
12991290
List<AppBuilderApp> allPublishedApp = this.appRepository.selectWithCondition(cond);
1300-
Map<String, AppBuilderApp> appIdKeyAppValueMap =
1301-
allPublishedApp.stream().collect(Collectors.toMap(AppBuilderApp::getId, Function.identity()));
1302-
return this.buildPublishedAppResDtos(allPublishedMeta, appIdKeyAppValueMap);
1291+
Map<String, AppBuilderApp> appIdKeyAppValueMap = allPublishedApp.stream()
1292+
.map(app -> appFactory.create(app.getId()))
1293+
.collect(Collectors.toMap(AppBuilderApp::getId, Function.identity()));
1294+
return RangedResultSet.create(this.buildPublishedAppResDtos(allPublishedMeta, appIdKeyAppValueMap),
1295+
metaRangedResultSet.getRange());
13031296
} catch (AippTaskNotFoundException exception) {
13041297
throw new AippException(QUERY_PUBLICATION_HISTORY_FAILED);
13051298
}
@@ -1315,30 +1308,57 @@ public List<CheckResult> checkAvailable(List<AppCheckDto> appCheckDtos, Operatio
13151308
return results.stream().filter(result -> !result.isValid()).collect(Collectors.toList());
13161309
}
13171310

1311+
@Override
1312+
@Transactional
1313+
public AppBuilderAppDto recoverApp(String appId, String resetId, OperationContext context) {
1314+
AppBuilderApp resetApp = this.appFactory.create(resetId);
1315+
AppBuilderApp currentApp = this.appFactory.create(appId);
1316+
List<AppBuilderFormProperty> resetFormProperties = resetApp.getFormProperties();
1317+
List<AppBuilderFormProperty> currentFormProperties = currentApp.getFormProperties();
1318+
Map<String, AppBuilderFormProperty> currentPropMap = currentFormProperties.stream()
1319+
.collect(Collectors.toMap(AppBuilderFormProperty::getName, Function.identity()));
1320+
resetFormProperties.forEach(resetProp -> {
1321+
AppBuilderFormProperty currentProp = currentPropMap.get(resetProp.getName());
1322+
if (currentProp != null) {
1323+
currentProp.setDefaultValue(resetProp.getDefaultValue());
1324+
}
1325+
});
1326+
currentApp.getFormPropertyRepository().updateMany(currentFormProperties);
1327+
1328+
AppBuilderFlowGraph resetGraph = resetApp.getFlowGraph();
1329+
AppBuilderFlowGraph currentGraph = currentApp.getFlowGraph();
1330+
String currentGraphId = currentApp.getFlowGraphId();
1331+
resetGraph.setId(currentGraphId);
1332+
1333+
Map<String, Object> appearance;
1334+
appearance = this.resetGraphId(resetGraph);
1335+
currentGraph.setAppearance(JSONObject.toJSONString(appearance));
1336+
currentApp.getFlowGraphRepository().updateOne(currentGraph);
1337+
1338+
this.appFactory.update(currentApp);
1339+
return this.buildFullAppDto(currentApp);
1340+
}
1341+
13181342
private boolean isAppBelong(String appId, Meta meta) {
13191343
return Objects.equals(String.valueOf(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY)), appId);
13201344
}
13211345

1322-
private List<PublishedAppResDto> buildPublishedAppResDtos(List<Meta> metas,
1346+
private List<AppBuilderAppDto> buildPublishedAppResDtos(List<Meta> metas,
13231347
Map<String, AppBuilderApp> appIdKeyAppValueMap) {
13241348
return metas.stream()
13251349
.map(meta -> this.buildPublishedAppResDto(meta, appIdKeyAppValueMap))
13261350
.collect(Collectors.toList());
13271351
}
13281352

1329-
private PublishedAppResDto buildPublishedAppResDto(Meta meta, Map<String, AppBuilderApp> appIdKeyAppValueMap) {
1353+
private AppBuilderAppDto buildPublishedAppResDto(Meta meta, Map<String, AppBuilderApp> appIdKeyAppValueMap) {
13301354
String appId = String.valueOf(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY));
13311355
String publishedDescription = String.valueOf(meta.getAttributes().get(AippConst.ATTR_PUBLISH_DESCRIPTION));
13321356
String publishedUpdateLog = String.valueOf(meta.getAttributes().get(AippConst.ATTR_PUBLISH_UPDATE_LOG));
13331357
AppBuilderApp app = appIdKeyAppValueMap.get(appId);
1334-
return PublishedAppResDto.builder()
1335-
.appId(appId)
1336-
.appVersion(app.getVersion())
1337-
.publishedAt(meta.getCreationTime())
1338-
.publishedBy(meta.getCreator())
1339-
.publishedDescription(publishedDescription)
1340-
.publishedUpdateLog(publishedUpdateLog)
1341-
.build();
1358+
AppBuilderAppDto dto = this.buildFullAppDto(app);
1359+
dto.setPublishedDescription(publishedDescription);
1360+
dto.setPublishedUpdateLog(publishedUpdateLog);
1361+
return dto;
13421362
}
13431363

13441364
private static AppBuilderConfig resetConfig(List<AppBuilderFormProperty> formProperties, AppBuilderConfig config) {
@@ -2036,4 +2056,18 @@ private String getAttribute(Map<String, Object> attributes, String name) {
20362056
Object value = attributes.get(name);
20372057
return value == null ? StringUtils.EMPTY : String.valueOf(value);
20382058
}
2059+
2060+
private Map<String, Object> resetGraphId(AppBuilderFlowGraph flowGraph) {
2061+
Map<String, Object> appearance;
2062+
try {
2063+
appearance = JSONObject.parseObject(flowGraph.getAppearance(), new TypeReference<Map<String, Object>>() {});
2064+
} catch (JSONException e) {
2065+
log.error("Import config failed, cause: {}", e);
2066+
throw new AippException(AippErrCode.IMPORT_CONFIG_FIELD_ERROR, "flowGraph.appearance");
2067+
}
2068+
appearance.computeIfPresent("id", (key, value) -> flowGraph.getId());
2069+
// 这里在创建应用时需要保证graph中的title+version唯一,否则在发布flow时会报错
2070+
appearance.put("title", flowGraph.getId());
2071+
return appearance;
2072+
}
20392073
}

app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/MetaUtils.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,24 @@ public static List<Meta> getAllPublishedMeta(MetaService metaService, String met
128128
return getListMetaHandle(metaService, metaFilter, context);
129129
}
130130

131+
/**
132+
* 分页查询指定应用的已发布元数据列表,按更新时间倒序。
133+
*
134+
* @param metaService 表示提供元数据访问功能的 {@link MetaService}。
135+
* @param metaId 表示指定应用唯一标识的 {@link String}。
136+
* @param offset 表示偏移量的 {@code long}。
137+
* @param limit 表示单页最大数量的 {@code int}。
138+
* @param context 表示操作人上下文的 {@link OperationContext}。
139+
* @return 表示查询到的结果集的 {@link RangedResultSet}{@code <}{@link Meta}{@code >}。
140+
*/
141+
public static RangedResultSet<Meta> getPublishedMetaByPage(MetaService metaService, String metaId, long offset,
142+
int limit, OperationContext context) {
143+
MetaFilter metaFilter = getNormalMetaFilter(metaId, NormalFilterEnum.PUBLISHED);
144+
metaFilter.setOrderBys(Collections.singletonList(formatSorter(AippSortKeyEnum.UPDATE_AT.name(),
145+
DirectionEnum.DESCEND.name())));
146+
return metaService.list(metaFilter, false, offset, limit, context);
147+
}
148+
131149
/**
132150
* 查询指定aippId所有预览{@link Meta}, 按更新时间倒序
133151
*

0 commit comments

Comments
 (0)