feat(ai-web): 完成任务模板的CRUD

This commit is contained in:
v-zhangjc9
2025-07-03 18:01:36 +08:00
parent 064443f740
commit 5f7eeb3596
3 changed files with 195 additions and 41 deletions

View File

@@ -1,13 +1,22 @@
package com.lanyuanxiaoyao.service.ai.web.controller.task;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lanyuanxiaoyao.service.ai.core.entity.amis.AmisCrudResponse;
import com.lanyuanxiaoyao.service.ai.core.entity.amis.AmisResponse;
import com.lanyuanxiaoyao.service.ai.web.base.controller.SimpleControllerSupport;
import com.lanyuanxiaoyao.service.ai.web.base.controller.query.Query;
import com.lanyuanxiaoyao.service.ai.web.base.entity.SimpleItem;
import com.lanyuanxiaoyao.service.ai.web.entity.FlowTaskTemplate;
import com.lanyuanxiaoyao.service.ai.web.service.task.FlowTaskTemplateService;
import java.util.Map;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.mapstruct.Context;
import org.mapstruct.factory.Mappers;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -15,13 +24,32 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("flow_task/template")
public class TaskTemplateController extends SimpleControllerSupport<FlowTaskTemplate, TaskTemplateController.SaveItem, TaskTemplateController.ListItem, TaskTemplateController.DetailItem> {
public TaskTemplateController(FlowTaskTemplateService flowTaskTemplateService) {
private final ObjectMapper mapper;
public TaskTemplateController(FlowTaskTemplateService flowTaskTemplateService, Jackson2ObjectMapperBuilder builder) {
super(flowTaskTemplateService);
this.mapper = builder.build();
}
@Override
public AmisResponse<Long> save(SaveItem saveItem) throws Exception {
log.info("Save: {}", saveItem);
SaveItem.Mapper map = Mappers.getMapper(SaveItem.Mapper.class);
log.info("Mapper: {}", map.from(saveItem, mapper));
return super.save(saveItem);
}
@Override
public AmisCrudResponse list(Query query) throws Exception {
AmisCrudResponse list = super.list(query);
log.info("List: {}", list);
return list;
}
@Override
protected SaveItemMapper<FlowTaskTemplate, SaveItem> saveItemMapper() {
return Mappers.getMapper(SaveItem.Mapper.class);
SaveItem.Mapper map = Mappers.getMapper(SaveItem.Mapper.class);
return item -> map.from(item, mapper);
}
@Override
@@ -31,18 +59,24 @@ public class TaskTemplateController extends SimpleControllerSupport<FlowTaskTemp
@Override
protected DetailItemMapper<FlowTaskTemplate, DetailItem> detailItemMapper() {
return Mappers.getMapper(DetailItem.Mapper.class);
DetailItem.Mapper map = Mappers.getMapper(DetailItem.Mapper.class);
return template -> map.from(template, mapper);
}
@Data
public static final class SaveItem {
private Long id;
private String name;
private String description;
private String inputSchema;
private String flow;
private Map<String, Object> inputSchema;
@org.mapstruct.Mapper
public interface Mapper extends SaveItemMapper<FlowTaskTemplate, SaveItem> {
public static abstract class Mapper {
public abstract FlowTaskTemplate from(SaveItem saveItem, @Context ObjectMapper mapper) throws Exception;
protected String mapInputSchema(Map<String, Object> inputSchema, @Context ObjectMapper mapper) throws JsonProcessingException {
return mapper.writeValueAsString(inputSchema);
}
}
}
@@ -62,11 +96,15 @@ public class TaskTemplateController extends SimpleControllerSupport<FlowTaskTemp
public static class DetailItem extends SimpleItem {
private String name;
private String description;
private String inputSchema;
private String flow;
private Map<String, Object> inputSchema;
@org.mapstruct.Mapper
public interface Mapper extends DetailItemMapper<FlowTaskTemplate, DetailItem> {
public static abstract class Mapper {
public abstract DetailItem from(FlowTaskTemplate template, @Context ObjectMapper mapper) throws Exception;
public Map<String, Object> mapInputSchema(String inputSchema, @Context ObjectMapper mapper) throws Exception {
return mapper.readValue(inputSchema, new TypeReference<>() {});
}
}
}
}

View File

@@ -0,0 +1,48 @@
import type {Schema} from 'amis'
export const typeMap: Record<string, string> = {
text: '文本',
number: '数字',
files: '文件',
}
export type InputField = {
type: string
label: string
description?: string
}
export const generateInputForm: (inputSchema: Record<string, InputField>) => Schema = inputSchema => {
let items: Schema[] = []
for (const name of Object.keys(inputSchema)) {
let field = inputSchema[name]
// @ts-ignore
let formItem: Schema = {
name: name,
...field,
}
switch (field.type) {
case 'text':
formItem = {
...formItem,
type: 'input-text',
clearValueOnEmpty: true,
}
break
case 'number':
formItem.type = 'input-number'
break
case 'files':
formItem.type = 'input-file'
break
}
items.push(formItem)
}
return {
type: 'form',
title: '入参表单预览',
canAccessSuperData: false,
actions: [],
body: items,
}
}

View File

@@ -1,8 +1,9 @@
import {isEmpty, isEqual} from 'licia'
import React from 'react'
import {useParams} from 'react-router'
import styled from 'styled-components'
import {amisRender, commonInfo, horizontalFormOptions} from '../../../../util/amis.tsx'
import {isEqual} from 'licia'
import {generateInputForm, typeMap} from '../InputSchema.tsx'
import { useParams } from 'react-router'
const TemplateEditDiv = styled.div`
.antd-EditorControl {
@@ -13,7 +14,6 @@ const TemplateEditDiv = styled.div`
const FlowTaskTemplateEdit: React.FC = () => {
const {template_id} = useParams()
const preloadTemplateId = isEqual(template_id, '-1') ? undefined : template_id
console.log('preloadTemplateId', preloadTemplateId)
return (
<TemplateEditDiv className="task-template-edit h-full">
{amisRender({
@@ -26,62 +26,130 @@ const FlowTaskTemplateEdit: React.FC = () => {
method: 'POST',
url: `${commonInfo.baseAiUrl}/flow_task/template/save`,
data: {
name: '${template.name}',
description: '${template.description}',
inputSchema: '${template.inputSchema}',
flow: '${template.flow}',
}
id: '${id|default:undefined}',
name: '${name}',
description: '${description}',
inputSchema: '${inputSchema|default:undefined}',
},
},
initApi: preloadTemplateId
? {
method: 'GET',
url: `${commonInfo.baseAiUrl}/flow_task/template/detail/${preloadTemplateId}`,
// @ts-ignore
adaptor: (payload, response, api, context) => {
return {
...payload,
data: {
template: payload.data,
},
}
},
}
: undefined,
wrapWithPanel: false,
...horizontalFormOptions(),
onEvent: {
change: {
actions: [
{
actionType: 'validate',
},
{
actionType: 'custom',
// @ts-ignore
script: (context, doAction, event) => {
let data = event?.data ?? {}
let inputSchema = data.inputSchema ?? []
if (!isEmpty(inputSchema) && isEmpty(data?.validateResult?.error ?? undefined)) {
doAction({
actionType: 'setValue',
args: {
value: {
inputPreview: generateInputForm(inputSchema),
},
},
})
}
},
},
],
},
},
body: [
{
type: 'hidden',
name: 'id',
},
{
type: 'input-text',
name: 'template.name',
name: 'name',
label: '名称',
required: true,
clearable: true,
},
{
type: 'textarea',
name: 'template.description',
name: 'description',
label: '描述',
required: true,
clearable: true,
},
{
type: 'group',
body: [
{
type: 'editor',
required: true,
label: '入参表单',
description: '使用amis代码编写入参表单结构流程会解析所有name对应的变量传入流程开始阶段只需要编写form的columns部分',
name: 'template.inputSchema',
value: '[]',
language: 'json',
options: {
wordWrap: 'bounded',
},
type: 'wrapper',
size: 'none',
body: [
{
type: 'input-kvs',
name: 'inputSchema',
label: '输入变量',
addButtonText: '新增入参',
draggable: false,
keyItem: {
label: '参数名称',
...horizontalFormOptions(),
validations: {
isAlphanumeric: true,
},
},
valueItems: [
{
...horizontalFormOptions(),
type: 'input-text',
name: 'label',
required: true,
label: '中文名称',
clearValueOnEmpty: true,
clearable: true,
},
{
...horizontalFormOptions(),
type: 'input-text',
name: 'description',
label: '参数描述',
clearValueOnEmpty: true,
clearable: true,
},
{
...horizontalFormOptions(),
type: 'select',
name: 'type',
label: '参数类型',
required: true,
selectFirst: true,
options: Object.keys(typeMap).map(key => ({label: typeMap[key], value: key})),
},
{
...horizontalFormOptions(),
type: 'switch',
name: 'required',
label: '是否必填',
required: true,
value: true,
},
],
},
],
},
{
type: 'amis',
name: 'template.inputSchema',
}
]
name: 'inputPreview',
},
],
},
{
type: 'button-toolbar',