diff --git a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/controller/task/TaskTemplateController.java b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/controller/task/TaskTemplateController.java index 236f80d..c61e9e2 100644 --- a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/controller/task/TaskTemplateController.java +++ b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/controller/task/TaskTemplateController.java @@ -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 { - public TaskTemplateController(FlowTaskTemplateService flowTaskTemplateService) { + private final ObjectMapper mapper; + + public TaskTemplateController(FlowTaskTemplateService flowTaskTemplateService, Jackson2ObjectMapperBuilder builder) { super(flowTaskTemplateService); + this.mapper = builder.build(); + } + + @Override + public AmisResponse 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 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 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 inputSchema; @org.mapstruct.Mapper - public interface Mapper extends SaveItemMapper { + public static abstract class Mapper { + public abstract FlowTaskTemplate from(SaveItem saveItem, @Context ObjectMapper mapper) throws Exception; + + protected String mapInputSchema(Map inputSchema, @Context ObjectMapper mapper) throws JsonProcessingException { + return mapper.writeValueAsString(inputSchema); + } } } @@ -62,11 +96,15 @@ public class TaskTemplateController extends SimpleControllerSupport inputSchema; @org.mapstruct.Mapper - public interface Mapper extends DetailItemMapper { + public static abstract class Mapper { + public abstract DetailItem from(FlowTaskTemplate template, @Context ObjectMapper mapper) throws Exception; + + public Map mapInputSchema(String inputSchema, @Context ObjectMapper mapper) throws Exception { + return mapper.readValue(inputSchema, new TypeReference<>() {}); + } } } } diff --git a/service-web/client/src/pages/ai/task/InputSchema.tsx b/service-web/client/src/pages/ai/task/InputSchema.tsx new file mode 100644 index 0000000..48f2ac6 --- /dev/null +++ b/service-web/client/src/pages/ai/task/InputSchema.tsx @@ -0,0 +1,48 @@ +import type {Schema} from 'amis' + +export const typeMap: Record = { + text: '文本', + number: '数字', + files: '文件', +} + +export type InputField = { + type: string + label: string + description?: string +} + +export const generateInputForm: (inputSchema: Record) => 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, + } +} \ No newline at end of file diff --git a/service-web/client/src/pages/ai/task/template/FlowTaskTemplateEdit.tsx b/service-web/client/src/pages/ai/task/template/FlowTaskTemplateEdit.tsx index 44389d2..3f3fdde 100644 --- a/service-web/client/src/pages/ai/task/template/FlowTaskTemplateEdit.tsx +++ b/service-web/client/src/pages/ai/task/template/FlowTaskTemplateEdit.tsx @@ -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 ( {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',