13 Commits

Author SHA1 Message Date
v-zhangjc9
8ebaf5de8e feat(web): 优化流程任务编辑 2025-07-03 19:18:17 +08:00
v-zhangjc9
5f7eeb3596 feat(ai-web): 完成任务模板的CRUD 2025-07-03 18:01:36 +08:00
v-zhangjc9
064443f740 feat(ai-web): 移除liteflow入参,该用自己实现的流程引擎 2025-07-03 16:10:02 +08:00
v-zhangjc9
6809750cfa fix(ai-web): 不能设置私有网络访问,会拒绝所有的跨域访问 2025-07-03 10:37:29 +08:00
v-zhangjc9
49cb62b287 fix(bin): 修复pom文件名错误 2025-07-03 10:36:57 +08:00
v-zhangjc9
5cbda28594 fix(gateway): 网关导致重复跨域标记,关闭网关的跨域设置 2025-07-03 10:36:33 +08:00
v-zhangjc9
11c5481287 fix(bin): 修复项目编译时间不正确导致无法识别项目修改状态 2025-07-03 10:01:47 +08:00
v-zhangjc9
67e0cada00 feat(bin): 优化打包脚本,遇到没有修改的模块就跳过发布流程 2025-07-02 17:29:28 +08:00
v-zhangjc9
cdf51cc85f fix(configuration): 优化安全配置 2025-07-02 16:25:06 +08:00
v-zhangjc9
9277d1690c feat(web): 增加常用app list 2025-07-02 15:48:22 +08:00
v-zhangjc9
24ac681cb3 feat(web): 增加装饰 2025-07-02 13:06:10 +08:00
v-zhangjc9
c46da52942 fix(web): 修复访问地址改到了本地测试 2025-07-02 11:28:44 +08:00
v-zhangjc9
7209b52e3d feat(ai-web): 增加JPA建表语句自动生成 2025-07-02 10:54:32 +08:00
32 changed files with 622 additions and 185 deletions

1
.gitignore vendored
View File

@@ -110,3 +110,4 @@ Network Trash Folder
Temporary Items
.apdisk
**/temp/
.build

View File

@@ -1,5 +1,5 @@
import {$, fetch, fs, glob, os, path, spinner, syncProcessCwd, usePowerShell} from 'zx'
import {isEqual, trim, fileSize} from "licia";
import {fileSize, isEqual, trim} from "licia";
import md5file from 'md5-file'
syncProcessCwd(true)
@@ -41,20 +41,70 @@ const millisecondToString = (timestamp) => {
return parts.join('')
}
const dotBuildPath = () => `.build`
const modifiedDataPath = () => `${dotBuildPath()}/modified_time.json`
const readModifiedTimeData = async () => {
if (!fs.existsSync(dotBuildPath())) {
fs.mkdirSync(dotBuildPath(), {recursive: true})
}
if (!(await fs.exists(modifiedDataPath()))) {
fs.writeFileSync(modifiedDataPath(), '{}')
}
return JSON.parse(await fs.readFile(modifiedDataPath(), 'utf-8'))
}
const updateModifiedTimeData = (data) => {
fs.writeFileSync(modifiedDataPath(), JSON.stringify(data, null, 2))
}
const isModified = async (target) => {
if (!target || !(await fs.exists(target))) {
throw new Error("Target 不存在")
}
let stat = fs.statSync(target)
let currentModifiedTime = stat.mtimeMs
let lastModifiedTime = (await readModifiedTimeData())[target]
return !(lastModifiedTime && isEqual(currentModifiedTime, lastModifiedTime));
}
const updateModifiedTime = async (target) => {
if (!target || !(await fs.exists(target))) {
throw new Error("Target 不存在")
}
let stat = fs.statSync(target)
let currentModifiedTime = stat.mtimeMs
let modifiedTimeData = await readModifiedTimeData()
modifiedTimeData[target] = currentModifiedTime
updateModifiedTimeData(modifiedTimeData)
}
export const run_deploy = async (project) => {
if (!(await isModified(project))) {
console.log(`✅ Skip deploy ${project}`)
return
}
let output = await spinner(
`Deploying project ${project}`,
() => $`mvn -pl ${project} clean deploy -D skipTests -s ${maven_setting}`
)
console.log(`✅ Finished deploy ${project} (${millisecondToString(output['duration'])})`)
await updateModifiedTime(project)
}
export const run_deploy_root = async () => {
if (!(await isModified('pom.xml'))) {
console.log(`✅ Skip deploy root`)
return
}
let output = await spinner(
`Deploying root`,
() => $`mvn clean deploy -N -D skipTests -s ${maven_setting}`
)
console.log(`✅ Finished deploy root (${millisecondToString(output['duration'])})`)
await updateModifiedTime('pom.xml')
}
export const run_deploy_batch = async (projects) => {

3
bin/test.js Normal file
View File

@@ -0,0 +1,3 @@
import {isModified} from './library.js'
console.log(await isModified('/Users/lanyuanxiaoyao/Project/IdeaProjects/hudi-service/pom.xml'))

View File

@@ -0,0 +1,82 @@
create table hudi_collect_build_b12.service_ai_feedback
(
id bigint not null comment '记录唯一标记',
created_time datetime(6) comment '记录创建时间',
modified_time datetime(6) comment '记录更新时间',
analysis longtext comment 'AI的分析结果',
conclusion longtext comment 'AI的解决方案',
source longtext not null comment '原始报障说明',
status enum ('ANALYSIS_PROCESSING','ANALYSIS_SUCCESS','FINISHED') not null comment '报障处理状态',
primary key (id)
) comment ='报障信息记录' charset = utf8mb4;
create table hudi_collect_build_b12.service_ai_feedback_pictures
(
feedback_id bigint not null,
pictures_id bigint not null,
primary key (feedback_id, pictures_id)
) comment ='报障相关截图' charset = utf8mb4;
alter table hudi_collect_build_b12.service_ai_feedback_pictures
add constraint UK3npjcyjyqfbdlf2v5tj64j2g3 unique (pictures_id);
create table hudi_collect_build_b12.service_ai_file
(
id bigint not null comment '记录唯一标记',
created_time datetime(6) comment '记录创建时间',
modified_time datetime(6) comment '记录更新时间',
filename varchar(255) comment '文件名称',
md5 varchar(255) comment '文件的md5编码用于校验文件的完整性',
path varchar(255) comment '文件在主机上存储的实际路径',
size bigint comment '文件大小单位是byte',
type varchar(255) comment '文件类型,通常记录的是文件的后缀名',
primary key (id)
) comment ='记录上传的文件存储信息' charset = utf8mb4;
create table hudi_collect_build_b12.service_ai_flow_task
(
id bigint not null comment '记录唯一标记',
created_time datetime(6) comment '记录创建时间',
modified_time datetime(6) comment '记录更新时间',
error longtext comment '任务运行产生的报错',
input longtext comment '任务输入',
result longtext comment '任务运行结果',
status enum ('ERROR','FINISHED','RUNNING') not null comment '任务运行状态',
template_id bigint not null comment '流程任务对应的模板',
primary key (id)
) comment ='流程任务记录' charset = utf8mb4;
create table hudi_collect_build_b12.service_ai_flow_task_template
(
id bigint not null comment '记录唯一标记',
created_time datetime(6) comment '记录创建时间',
modified_time datetime(6) comment '记录更新时间',
description varchar(255) comment '模板功能、内容说明',
flow_graph longtext comment '前端流程图数据',
input_schema longtext not null comment '模板入参Schema',
name varchar(255) not null comment '模板名称',
primary key (id)
) comment ='流程任务模板' charset = utf8mb4;
create table hudi_collect_build_b12.service_ai_group
(
id bigint not null comment '记录唯一标记',
created_time datetime(6) comment '记录创建时间',
modified_time datetime(6) comment '记录更新时间',
name varchar(255) not null comment '分组名称',
status enum ('FINISHED','RUNNING') not null comment '分组处理状态',
knowledge_id bigint not null,
primary key (id)
) comment ='知识库下包含的分组' charset = utf8mb4;
create table hudi_collect_build_b12.service_ai_knowledge
(
id bigint not null comment '记录唯一标记',
created_time datetime(6) comment '记录创建时间',
modified_time datetime(6) comment '记录更新时间',
description longtext not null comment '知识库说明',
name varchar(255) not null comment '知识库名称',
strategy enum ('Cosine','Euclid') not null comment '知识库策略',
vector_source_id bigint not null comment '知识库对应的向量库名',
primary key (id)
) comment ='知识库' charset = utf8mb4;

View File

@@ -1,18 +0,0 @@
CREATE TABLE `service_ai_feedback`
(
`id` bigint NOT NULL,
`created_time` datetime(6) DEFAULT NULL,
`modified_time` datetime(6) DEFAULT NULL,
`analysis` longtext,
`conclusion` longtext,
`source` longtext NOT NULL,
`status` tinyint NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET = utf8mb4;
CREATE TABLE `service_ai_feedback_pictures`
(
`feedback_id` bigint NOT NULL,
`pictures_id` bigint NOT NULL,
PRIMARY KEY (`feedback_id`, `pictures_id`)
) DEFAULT CHARSET = utf8mb4;

View File

@@ -1,12 +0,0 @@
CREATE TABLE `service_ai_file`
(
`id` bigint NOT NULL,
`created_time` datetime(6) DEFAULT NULL,
`modified_time` datetime(6) DEFAULT NULL,
`filename` varchar(255) DEFAULT NULL,
`md5` varchar(255) DEFAULT NULL,
`path` varchar(255) DEFAULT NULL,
`size` bigint DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET = utf8mb4;

View File

@@ -1,10 +0,0 @@
CREATE TABLE `service_ai_group`
(
`id` bigint NOT NULL,
`created_time` datetime(6) DEFAULT NULL,
`modified_time` datetime(6) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`status` tinyint NOT NULL,
`knowledge_id` bigint NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8mb4;

View File

@@ -1,11 +0,0 @@
CREATE TABLE `service_ai_knowledge`
(
`id` bigint NOT NULL,
`created_time` datetime(6) DEFAULT NULL,
`modified_time` datetime(6) DEFAULT NULL,
`description` longtext NOT NULL,
`name` varchar(255) NOT NULL,
`strategy` tinyint NOT NULL,
`vector_source_id` bigint NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET = utf8mb4;

View File

@@ -30,7 +30,6 @@ public class SecurityConfig {
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
configuration.setMaxAge(7200L);
configuration.setAllowPrivateNetwork(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return new CorsFilter(source);

View File

@@ -82,6 +82,13 @@
<groupId>org.noear</groupId>
<artifactId>solon-ai-dialect-openai</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-ant</artifactId>
<version>6.6.8.Final</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@@ -7,6 +7,7 @@ import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@@ -22,6 +23,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class IdOnlyEntity {
@Comment("记录唯一标记")
@Id
@GeneratedValue(generator = "snowflake")
@GenericGenerator(name = "snowflake", strategy = "com.lanyuanxiaoyao.service.ai.web.configuration.SnowflakeIdGenerator")

View File

@@ -6,6 +6,7 @@ import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.Comment;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@@ -22,8 +23,10 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class SimpleEntity extends IdOnlyEntity {
@Comment("记录创建时间")
@CreatedDate
private LocalDateTime createdTime;
@Comment("记录更新时间")
@LastModifiedDate
private LocalDateTime modifiedTime;
}

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

@@ -8,6 +8,7 @@ import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.DynamicUpdate;
/**
@@ -23,11 +24,17 @@ import org.hibernate.annotations.DynamicUpdate;
@DynamicUpdate
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_file")
@NoArgsConstructor
@Comment("记录上传的文件存储信息")
public class DataFile extends SimpleEntity {
@Comment("文件名称")
private String filename;
@Comment("文件大小单位是byte")
private Long size;
@Comment("文件的md5编码用于校验文件的完整性")
private String md5;
@Comment("文件在主机上存储的实际路径")
private String path;
@Comment("文件类型,通常记录的是文件的后缀名")
private String type;
public DataFile(String filename) {

View File

@@ -6,6 +6,8 @@ import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.JoinTable;
@@ -17,6 +19,7 @@ import java.util.Set;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@@ -30,18 +33,25 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@NamedEntityGraph(name = "feedback.detail", attributeNodes = {
@NamedAttributeNode("pictures")
})
@Comment("报障信息记录")
public class Feedback extends SimpleEntity {
@Comment("原始报障说明")
@Column(nullable = false, columnDefinition = "longtext")
private String source;
@Comment("报障相关截图")
@OneToMany(fetch = FetchType.EAGER)
@JoinTable(catalog = Constants.DATABASE_NAME, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
@ToString.Exclude
private Set<DataFile> pictures;
@Comment("AI的分析结果")
@Column(columnDefinition = "longtext")
private String analysis;
@Comment("AI的解决方案")
@Column(columnDefinition = "longtext")
private String conclusion;
@Comment("报障处理状态")
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private Status status = Status.ANALYSIS_PROCESSING;
public enum Status {

View File

@@ -6,6 +6,8 @@ import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
@@ -13,6 +15,7 @@ import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@@ -23,16 +26,23 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@DynamicUpdate
@EntityListeners(AuditingEntityListener.class)
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_flow_task")
@Comment("流程任务记录")
public class FlowTask extends SimpleEntity {
@Comment("流程任务对应的模板")
@ManyToOne
@JoinColumn(nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private FlowTaskTemplate template;
@Comment("任务输入")
@Column(columnDefinition = "longtext")
private String input;
@Comment("任务运行状态")
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private Status status = Status.RUNNING;
@Comment("任务运行产生的报错")
@Column(columnDefinition = "longtext")
private String error;
@Comment("任务运行结果")
@Column(columnDefinition = "longtext")
private String result;

View File

@@ -9,6 +9,7 @@ import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@@ -19,14 +20,17 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@DynamicUpdate
@EntityListeners(AuditingEntityListener.class)
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_flow_task_template")
@Comment("流程任务模板")
public class FlowTaskTemplate extends SimpleEntity {
@Comment("模板名称")
@Column(nullable = false)
private String name;
@Comment("模板功能、内容说明")
private String description;
@Comment("模板入参Schema")
@Column(nullable = false, columnDefinition = "longtext")
private String inputSchema;
@Column(nullable = false, columnDefinition = "text")
private String flow;
@Comment("前端流程图数据")
@Column(columnDefinition = "longtext")
private String flowGraph;
}

View File

@@ -6,6 +6,8 @@ import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.JoinColumn;
@@ -14,6 +16,7 @@ import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@@ -28,10 +31,14 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@DynamicUpdate
@EntityListeners(AuditingEntityListener.class)
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_group")
@Comment("知识库内的逻辑分组,比如一个文件是一个分组或一次上传的所有文本是一个分组,可以自由使用而不是限于文件范畴")
public class Group extends SimpleEntity {
@Comment("分组名称")
@Column(nullable = false)
private String name;
@Comment("分组处理状态")
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private Status status = Status.RUNNING;
@ManyToOne(fetch = FetchType.LAZY)

View File

@@ -6,6 +6,8 @@ import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
@@ -13,6 +15,7 @@ import java.util.Set;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@@ -27,16 +30,23 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@DynamicUpdate
@EntityListeners(AuditingEntityListener.class)
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_knowledge")
@Comment("知识库")
public class Knowledge extends SimpleEntity {
@Comment("知识库对应的向量库名")
@Column(nullable = false)
private Long vectorSourceId;
@Comment("知识库名称")
@Column(nullable = false)
private String name;
@Comment("知识库说明")
@Column(nullable = false, columnDefinition = "longtext")
private String description;
@Comment("知识库策略")
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private Strategy strategy = Strategy.Cosine;
@Comment("知识库下包含的分组")
@OneToMany(fetch = FetchType.LAZY, mappedBy = "knowledge", cascade = CascadeType.ALL)
@ToString.Exclude
private Set<Group> groups;

View File

@@ -0,0 +1,51 @@
package com.lanyuanxiaoyao.service.ai.web;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ClassUtil;
import jakarta.persistence.Entity;
import java.util.EnumSet;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.schema.TargetType;
import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy;
/**
* JPA直接生成建表语句
*
* @author lanyuanxiaoyao
* @version 20250702
*/
public class GenerateDDL {
public static void main(String[] args) {
String root = "/Users/lanyuanxiaoyao/Project/IdeaProjects/hudi-service/service-ai/target/sql";
FileUtil.mkdir(root);
/* ClassUtil.scanPackageBySuper("org.hibernate.dialect", Dialect.class)
.stream()
.filter(clazz -> StrUtil.startWith(clazz.getSimpleName(), "MySQL"))
.filter(clazz -> !StrUtil.startWith(clazz.getSimpleName(), "Abstract"))
.filter(clazz -> !StrUtil.startWith(clazz.getSimpleName(), "Dialect"))
.forEach(dialectClazz -> generateDDL(root, dialectClazz)); */
generateDDL(root, MySQLDialect.class);
}
private static void generateDDL(String path, Class<?> dialect) {
var metadataSources = new MetadataSources(
new StandardServiceRegistryBuilder()
.applySetting("hibernate.dialect", dialect.getName())
.applySetting("hibernate.physical_naming_strategy", CamelCaseToUnderscoresNamingStrategy.class.getName())
.applySetting("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName())
.build()
);
var classes = ClassUtil.scanPackageByAnnotation("com.lanyuanxiaoyao.service.ai.web.entity", Entity.class);
classes.forEach(metadataSources::addAnnotatedClass);
var export = new SchemaExport();
export.setFormat(true);
export.setDelimiter(";");
export.setOutputFile(path + "/" + dialect.getSimpleName() + ".sql");
export.setOverrideOutputFileContent();
export.execute(EnumSet.of(TargetType.SCRIPT), SchemaExport.Action.CREATE, metadataSources.buildMetadata());
}
}

View File

@@ -2,11 +2,15 @@ package com.lanyuanxiaoyao.service.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* Spring Security Config
@@ -25,6 +29,19 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
this.securityProperties = securityProperties;
}
@Bean
public CorsFilter corsFilter() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.addAllowedOriginPattern("*");
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
configuration.setMaxAge(7200L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return new CorsFilter(source);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
@@ -36,7 +53,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
.csrf()
.disable()
.cors()
.disable()
.and()
.formLogin()
.disable();
}

View File

@@ -10,7 +10,7 @@ import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsConfigurationSource;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
/**
@@ -33,24 +33,12 @@ public class SecurityConfiguration {
.httpBasic()
.disable()
.cors()
.configurationSource(corsConfigurationSource())
.and()
.disable()
.csrf()
.disable()
.build();
}
private CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
configuration.addAllowedOriginPattern("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
public MapReactiveUserDetailsService userDetailsService(SecurityProperties securityProperties) {
UserDetails user = User.builder()

View File

@@ -1,66 +1,128 @@
import {ProLayout} from '@ant-design/pro-components'
import {type AppItemProps, ProLayout} from '@ant-design/pro-components'
import {ConfigProvider} from 'antd'
import React from 'react'
import {dateFormat} from 'licia'
import React, {useMemo} from 'react'
import {Outlet, useLocation, useNavigate} from 'react-router'
import styled from 'styled-components'
import {menus} from '../route.tsx'
const apps: { title: string, desc: string, url: string, icon?: string }[] = [
const ProLayoutDiv = styled.div`
position: relative;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
.ant-menu-sub > .ant-menu-item {
padding-left: 16px !important;
}
`
const defaultAppIcon =
<img
src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAMAUExURUdwTBR20EyHx1iq5EWJ1hNyy0mR1EhzwnbB/8P5/Fx/vVuj5zyO4lCY102P0L3u/3K88dz4/8v1/0fW/jLh/013ujN4wBliuEuEy0FrqT13wFmCvkaH0GSLyBVVqe38/0il4FKu7crr/eL3/7Ls/zd3t1WN1TZcnTloojx6whpYpTdfoDVwtVeS1WeOyiNhsTFyuy50wHCPxDFgqLjK5G2Ow2Gy5ylpvB9uxA5x03W/71Wh4E2U2tr2/i6C3G7J+Gex7KTI8eb9/cbz/yC+/srl++P7/Znf+MPv/cn3/rz6/dL8/2fe/8v+/s/6/knN87f5/jx5uRZtwjRppU6I1liM2Bhcrmyl2kZ5uA9vzjd0wj95yjVstqze9hiD2zOH1kqv7Wqv5K7S7LLn/YfT+sfp/bPf+4jb/YbX+735/SjQ/T7J+VjV9rTs+0ns/6Dk/mPJ7eD9/b/8/Q9YxA5Zxw5Zwg5dwg5byhdn0kSO2xFezg5gwg1XwRBjwkqW4EeT3kuY20mV2BBVtg5avxFoxQ9Yu0+g3xJYslWi5h2q9SKA4AtTwAxjzUGN2UiN0VOm4yKj8NX7/0WQ3RFtyUaR1h95zSOc706J0Bt34VCi5yqw9F6s6SOI5Bx02h2M6Rh13SCX7k2c3E6a4lCd5EeQ2iVtyCJ54BVt3ROV8x+0+jS9+hGq+Ra0/WK37sPy/iOR6R1w2A5p0y9nrjJ/1EaO1yV1yDt+zBaJ4hF83FuJyg5nyhei9hJ96BN35ByV5ymq8S151muu5UCr6hzE/xS+/47h/3/r/xyG5SKV6yJ43BlswBJivA5z2A9duw5s1C590CZXpDVvuhec9BGM7xJQrxKE61yo4DOF4DeZ64jG8bbp/h/Q/j2F0xpr1xliyROA4CRst1mc10d8whNQqDqb3Sig5qrh/qLW9iZ/5UeY5pDU9UWf6J7O9oju/mDV/aXp/orj/6P7/4z3/2Pr/iLa/kaDxnuw57rf9ZfW+2Sd5G3X/7LW9zyR51TI+Dao7Zno//A+0MwAAADpdFJOUwD7Lv/+/v4CAQIK/v7+/P7+b/7+/iKh/f5LjBv8z/4d/v5cNf6A5piF/Pts9PST/tP+Qd0Oc/72Pf5B/Pv0/vv7zqD6/4qU+5JjlCv7ff79/lz628SK/pmm+/5cnh/+/vPht/r+pPKX44z7+/z7/P7+wNL////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+///////////////////////+1awirQAAA+BJREFUOMtjYCAXcMIAAVW4OKjKgJjDW8nXyytT0NOKCyjAgUudt29RwMdPb48dO/b2aEa6MkQvpjrlQqCqz3ki/j4+2VnCv44+S5XHVAm0xHPixHXuOSIiX4TfHeo87OJin/IjTYmBgwNdndvEibkF/sIiX26/+7f6yJo1K/fPtL/53BXDPKeJQkLvPx87Knz79qzOI2tWr959acFaAYE3rii2cwDVOQutU8k/+uHv4cNrQMpWrFgxTXHtWoc3gkgqORkEJzqvW3fr/f8Paye4rFzZufvSisuXr95QVJw9W+C3MlwlB4O87Tp+fn6VV2sVgOo6wequ7tkz4zWP3QHrm45IBroJJd/if37w0CygaZ0Tpi1YMO/qnr17b7yeMePFAZufSlAjQQbeyle5qbBq1apZIGUn1s87DlS3/DrPsj8vr/McdES48Lv7q4NPVq3avx+sbP2cOV0zZsxfftfuZcdsbm6Bb1YM7BATk64pPHk6C6hqGljVnK6uZSfnzl2+bObMmR3csx0OhkIUMpgkHgKatW/fvO3HjwMVdXWdPHly7oYNd3mA6jo6uLm5Q0ABDcSxCU/37du+/f79HfPnz507dwNQ0Ya7dyuudyzu6Fi8eNGiOjUukEp2BqZDcXE7dpw5c+bcuY0bK+CgbtEioLK6+kmVLKIQhRZP4iwtgWp6gKCqpwoCyivqQACorLeXhQnkSKDC83a7dgEVlfeUw0BJScm9SfVAAFTHwjIdqpBpafyuspISIEICxSWV9ZMmLQGr0wVbzcEgbb518+YDFcWlxUAAUgMGNXfqKisrl1QCFaqZgL3NwGkgzsbb2LC5u7sYAUprWu/UL2HZto1FZrosJGbYGWIMbdjYpjRuvtddU1MDVAMB3U3bljZdubJNZqceJMDZGSQv8vX1TZnS2HCguwaqqrS5ubm1CQiuRE3faQSNGQYu469glVMalnZ315SBAUghUGX7Y/3psrD0yM6gvQmoEGh5Y0Pv+Xs1pdUQlUCF7e2PzXeawgwEGQm0nI2tsaFhYdvC3qVl1WClZ1vb2+XkHskisgLQlRc0IeoWtrUxn2+urT5dXVt99ixQ4UWEC8EqNS5o8jaClLX191bXQsDps+1hWo+QLAYnIfULMrxAdf39s6dWt4BB7enT4lqb9Bg40DK2+iZea2tmZub+qbsmA0HLqZZT4vqbpBgYMIoKbZ2H166Z1U9mhYKgUw+CJdHVgd0pGvHwmllvJCMYsJ56oKPBheI+hEoGpnDdrVungsBWQwNVaQas6oCWAIWlo6WkxMTEVAOZQFo5cJW57Mgm4FYGMRUGyK0tAGzv0vrmaa6xAAAAAElFTkSuQmCC'}
/>
const apps: AppItemProps[] = [
{
icon: 'http://132.121.223.12:7001/static/webssh/favicon2.ico',
title: '运营数据汇聚平台',
desc: '企业全融合数字化平台',
url: 'http://132.121.223.12:7001/index.html/#/login',
},
{
icon: 'http://132.126.207.124:8686/udal-manager/static/favicon.ico',
title: 'CSV-HUDI处理平台',
desc: 'Hudi 批量割接、稽核任务管理平台',
url: 'http://132.126.207.124:8686/udal-manager/',
},
{
icon: defaultAppIcon,
title: '汇聚平台辅助工具',
desc: '辅助工具,用于帮助运营工作,不断改进中',
url: 'http://132.121.204.100:38080/tools/#/',
},
{
icon: defaultAppIcon,
title: 'B12-Yarn',
desc: 'B12集群Yarn页面',
url: 'http://132.126.207.125:8088/cluster/scheduler',
},
]
const App: React.FC = () => {
const navigate = useNavigate()
const location = useLocation()
const currentYear = useMemo(() => dateFormat(new Date(), 'yyyy'), [])
return (
<ProLayout
token={{
colorTextAppListIcon: '#dfdfdf',
colorTextAppListIconHover: '#ffffff',
header: {
colorBgHeader: '#292f33',
colorHeaderTitle: '#ffffff',
colorTextMenu: '#dfdfdf',
colorTextMenuSecondary: '#dfdfdf',
colorTextMenuSelected: '#ffffff',
colorTextMenuActive: '#ffffff',
colorBgMenuItemSelected: '#22272b',
colorTextRightActionsItem: '#dfdfdf',
},
}}
appList={apps}
disableMobile={true}
logo={<img src="icon.png" alt="logo"/>}
title="Hudi 服务总台"
route={menus}
location={{pathname: location.pathname}}
menu={{type: 'sub'}}
menuItemRender={(item, dom) => {
return <div onClick={() => navigate(item.path || '/')}>{dom}</div>
}}
fixSiderbar={true}
layout="mix"
splitMenus={true}
style={{minHeight: '100vh'}}
contentStyle={{backgroundColor: 'white', padding: '10px 10px 10px 20px'}}
>
<ConfigProvider
theme={{
components: {
Card: {
bodyPadding: 0,
bodyPaddingSM: 0,
},
<ProLayoutDiv>
<ProLayout
siderWidth={180}
token={{
colorTextAppListIcon: '#dfdfdf',
colorTextAppListIconHover: '#ffffff',
header: {
colorBgHeader: '#292f33',
colorHeaderTitle: '#ffffff',
colorTextMenu: '#dfdfdf',
colorTextMenuSecondary: '#dfdfdf',
colorTextMenuSelected: '#ffffff',
colorTextMenuActive: '#ffffff',
colorBgMenuItemSelected: '#22272b',
colorTextRightActionsItem: '#dfdfdf',
},
pageContainer: {
paddingBlockPageContainerContent: 0,
paddingInlinePageContainerContent: 0,
marginBlockPageContainerContent: 0,
marginInlinePageContainerContent: 0,
},
}}
appList={apps}
defaultCollapsed={false}
breakpoint={false}
disableMobile={true}
logo={<img src="icon.png" alt="logo"/>}
title="Hudi 服务总台"
route={menus}
location={{pathname: location.pathname}}
menu={{type: 'sub'}}
menuItemRender={(item) => {
return (
<div onClick={() => navigate(item.path || '/')}>
{item.icon}
<span className="ml-2">{item.name}</span>
</div>
)
}}
fixSiderbar={true}
layout="mix"
splitMenus={true}
style={{minHeight: '100vh'}}
contentStyle={{backgroundColor: 'white', padding: '10px 10px 10px 20px'}}
menuFooterRender={props => {
return (
<div className="text-sm text-center" style={{userSelect: 'none', msUserSelect: 'none'}}>
{props?.collapsed
? undefined
: <div>© 2023-{currentYear} </div>}
</div>
)
}}
>
<Outlet/>
</ConfigProvider>
</ProLayout>
<ConfigProvider
theme={{
components: {
Card: {
bodyPadding: 0,
bodyPaddingSM: 0,
},
},
}}
>
<Outlet/>
</ConfigProvider>
</ProLayout>
</ProLayoutDiv>
)
}

View File

@@ -55,6 +55,9 @@ const FlowableDiv = styled.div`
}
`
export type FlowEditorProps = {
}
function FlowEditor() {
const [messageApi, contextHolder] = message.useMessage()
const [nodeDef] = useState<{

View File

@@ -1,18 +1,18 @@
import {Handle, type NodeProps, Position} from '@xyflow/react'
import AmisNode from './AmisNode.tsx'
import {Tag} from 'antd'
import React from 'react'
import AmisNode from './AmisNode.tsx'
const cases = [
{
index: 1
index: 1,
},
{
index: 2
index: 2,
},
{
index: 3
}
index: 3,
},
]
const SwitchNode = (props: NodeProps) => AmisNode({
@@ -21,6 +21,7 @@ const SwitchNode = (props: NodeProps) => AmisNode({
defaultNodeName: '分支节点',
defaultNodeDescription: '根据不同的情况前往不同的分支',
columnSchema: [],
// @ts-ignore
extraNodeDescription: nodeData => {
return (
<div className="mt-2">
@@ -32,6 +33,7 @@ const SwitchNode = (props: NodeProps) => AmisNode({
</div>
)
},
// @ts-ignore
handlers: nodeData => {
return (
<>
@@ -47,7 +49,7 @@ const SwitchNode = (props: NodeProps) => AmisNode({
))}
</>
)
}
},
})
export default React.memo(SwitchNode)

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,6 +1,6 @@
import React from 'react'
import {amisRender, commonInfo, crudCommonOptions, paginationTemplate,} from '../../../../util/amis.tsx'
import {useNavigate} from 'react-router'
import {amisRender, commonInfo, crudCommonOptions, paginationTemplate} from '../../../../util/amis.tsx'
const FlowTaskTemplate: React.FC = () => {
const navigate = useNavigate()
@@ -20,8 +20,8 @@ const FlowTaskTemplate: React.FC = () => {
page: {
index: '${page}',
size: '${perPage}',
}
}
},
},
},
...crudCommonOptions(),
...paginationTemplate(
@@ -32,6 +32,8 @@ const FlowTaskTemplate: React.FC = () => {
type: 'action',
label: '',
icon: 'fa fa-plus',
tooltip: '新增',
tooltipPlacement: 'top',
size: 'sm',
onEvent: {
click: {
@@ -83,6 +85,25 @@ const FlowTaskTemplate: React.FC = () => {
},
},
},
{
type: 'action',
label: '编辑流程',
level: 'link',
size: 'sm',
onEvent: {
click: {
actions: [
{
actionType: 'custom',
// @ts-ignore
script: (context, doAction, event) => {
navigate(`/ai/flow_task_template/edit/${context.props.data['id']}`)
},
},
],
},
},
},
{
type: 'action',
label: '删除',

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,73 +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',
}
]
},
{
type: 'editor',
required: true,
label: '任务流程',
name: 'template.flow',
description: '使用标准的LiteFlow语法',
language: 'xml',
options: {
wordWrap: 'bounded',
},
name: 'inputPreview',
},
],
},
{
type: 'button-toolbar',

View File

@@ -18,10 +18,15 @@ import {values} from 'licia'
import {Navigate, type RouteObject} from 'react-router'
import Conversation from './pages/ai/Conversation.tsx'
import Feedback from './pages/ai/feedback/Feedback.tsx'
import FlowEditor from './pages/ai/flow/FlowEditor.tsx'
import DataDetail from './pages/ai/knowledge/DataDetail.tsx'
import DataImport from './pages/ai/knowledge/DataImport.tsx'
import DataSegment from './pages/ai/knowledge/DataSegment.tsx'
import Knowledge from './pages/ai/knowledge/Knowledge.tsx'
import FlowTask from './pages/ai/task/FlowTask.tsx'
import FlowTaskAdd from './pages/ai/task/FlowTaskAdd.tsx'
import FlowTaskTemplate from './pages/ai/task/template/FlowTaskTemplate.tsx'
import FlowTaskTemplateEdit from './pages/ai/task/template/FlowTaskTemplateEdit.tsx'
import App from './pages/App.tsx'
import Cloud from './pages/overview/Cloud.tsx'
import Overview from './pages/overview/Overview.tsx'
@@ -34,11 +39,6 @@ import Yarn from './pages/overview/Yarn.tsx'
import YarnCluster from './pages/overview/YarnCluster.tsx'
import Test from './pages/Test.tsx'
import {commonInfo} from './util/amis.tsx'
import FlowEditor from './pages/ai/flow/FlowEditor.tsx'
import FlowTaskTemplate from './pages/ai/task/template/FlowTaskTemplate.tsx'
import FlowTaskTemplateEdit from './pages/ai/task/template/FlowTaskTemplateEdit.tsx'
import FlowTask from './pages/ai/task/FlowTask.tsx'
import FlowTaskAdd from './pages/ai/task/FlowTaskAdd.tsx'
export const routes: RouteObject[] = [
{
@@ -245,8 +245,8 @@ export const menus = {
},
{
path: '1089caa6-9477-44a5-99f1-a9c179f6cfd3',
name: '任务',
icon: <FileTextOutlined/>,
name: '流程任务',
icon: <GatewayOutlined/>,
routes: [
{
path: '/ai/flow_task',
@@ -255,7 +255,7 @@ export const menus = {
},
{
path: '/ai/flow_task_template',
name: '任务模板',
name: '流程模板',
icon: <FileTextOutlined/>,
},
]

View File

@@ -10,8 +10,8 @@ import {isEqual} from 'licia'
export const commonInfo = {
debug: isEqual(import.meta.env.MODE, 'development'),
baseUrl: 'http://132.126.207.130:35690/hudi_services/service_web',
// baseAiUrl: 'http://132.126.207.130:35690/hudi_services/service_ai_web',
baseAiUrl: 'http://localhost:8080',
baseAiUrl: 'http://132.126.207.130:35690/hudi_services/service_ai_web',
// baseAiUrl: 'http://localhost:8080',
authorizationHeaders: {
'Authorization': 'Basic QXhoRWJzY3dzSkRiWU1IMjpjWXhnM2I0UHRXb1ZENVNqRmF5V3h0blNWc2p6UnNnNA==',
'Content-Type': 'application/json',

View File

@@ -1 +1,3 @@
/// <reference types="vite/client" />
declare const __APP_VERSION__: string

View File

@@ -1,10 +1,14 @@
import react from '@vitejs/plugin-react-swc'
import {defineConfig, type UserConfig} from 'vite'
import obfuscatorPlugin from 'vite-plugin-javascript-obfuscator'
import packageJson from './package.json'
// https://vite.dev/config/
export default defineConfig(({mode}) => {
let config: UserConfig = {
define: {
__APP_VERSION__: JSON.stringify(packageJson.version) ?? '0.0.0',
},
plugins: [
react(),
obfuscatorPlugin({