Compare commits
13 Commits
959d6fb5c7
...
8ebaf5de8e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ebaf5de8e | ||
|
|
5f7eeb3596 | ||
|
|
064443f740 | ||
|
|
6809750cfa | ||
|
|
49cb62b287 | ||
|
|
5cbda28594 | ||
|
|
11c5481287 | ||
|
|
67e0cada00 | ||
|
|
cdf51cc85f | ||
|
|
9277d1690c | ||
|
|
24ac681cb3 | ||
|
|
c46da52942 | ||
|
|
7209b52e3d |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -110,3 +110,4 @@ Network Trash Folder
|
|||||||
Temporary Items
|
Temporary Items
|
||||||
.apdisk
|
.apdisk
|
||||||
**/temp/
|
**/temp/
|
||||||
|
.build
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import {$, fetch, fs, glob, os, path, spinner, syncProcessCwd, usePowerShell} from 'zx'
|
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'
|
import md5file from 'md5-file'
|
||||||
|
|
||||||
syncProcessCwd(true)
|
syncProcessCwd(true)
|
||||||
@@ -41,20 +41,70 @@ const millisecondToString = (timestamp) => {
|
|||||||
return parts.join('')
|
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) => {
|
export const run_deploy = async (project) => {
|
||||||
|
if (!(await isModified(project))) {
|
||||||
|
console.log(`✅ Skip deploy ${project}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
let output = await spinner(
|
let output = await spinner(
|
||||||
`Deploying project ${project}`,
|
`Deploying project ${project}`,
|
||||||
() => $`mvn -pl ${project} clean deploy -D skipTests -s ${maven_setting}`
|
() => $`mvn -pl ${project} clean deploy -D skipTests -s ${maven_setting}`
|
||||||
)
|
)
|
||||||
console.log(`✅ Finished deploy ${project} (${millisecondToString(output['duration'])})`)
|
console.log(`✅ Finished deploy ${project} (${millisecondToString(output['duration'])})`)
|
||||||
|
await updateModifiedTime(project)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const run_deploy_root = async () => {
|
export const run_deploy_root = async () => {
|
||||||
|
if (!(await isModified('pom.xml'))) {
|
||||||
|
console.log(`✅ Skip deploy root`)
|
||||||
|
return
|
||||||
|
}
|
||||||
let output = await spinner(
|
let output = await spinner(
|
||||||
`Deploying root`,
|
`Deploying root`,
|
||||||
() => $`mvn clean deploy -N -D skipTests -s ${maven_setting}`
|
() => $`mvn clean deploy -N -D skipTests -s ${maven_setting}`
|
||||||
)
|
)
|
||||||
console.log(`✅ Finished deploy root (${millisecondToString(output['duration'])})`)
|
console.log(`✅ Finished deploy root (${millisecondToString(output['duration'])})`)
|
||||||
|
await updateModifiedTime('pom.xml')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const run_deploy_batch = async (projects) => {
|
export const run_deploy_batch = async (projects) => {
|
||||||
|
|||||||
3
bin/test.js
Normal file
3
bin/test.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import {isModified} from './library.js'
|
||||||
|
|
||||||
|
console.log(await isModified('/Users/lanyuanxiaoyao/Project/IdeaProjects/hudi-service/pom.xml'))
|
||||||
82
service-ai/database/20250702.sql
Normal file
82
service-ai/database/20250702.sql
Normal 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;
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -30,7 +30,6 @@ public class SecurityConfig {
|
|||||||
configuration.addAllowedHeader("*");
|
configuration.addAllowedHeader("*");
|
||||||
configuration.addAllowedMethod("*");
|
configuration.addAllowedMethod("*");
|
||||||
configuration.setMaxAge(7200L);
|
configuration.setMaxAge(7200L);
|
||||||
configuration.setAllowPrivateNetwork(true);
|
|
||||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
source.registerCorsConfiguration("/**", configuration);
|
source.registerCorsConfiguration("/**", configuration);
|
||||||
return new CorsFilter(source);
|
return new CorsFilter(source);
|
||||||
|
|||||||
@@ -82,6 +82,13 @@
|
|||||||
<groupId>org.noear</groupId>
|
<groupId>org.noear</groupId>
|
||||||
<artifactId>solon-ai-dialect-openai</artifactId>
|
<artifactId>solon-ai-dialect-openai</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hibernate.orm</groupId>
|
||||||
|
<artifactId>hibernate-ant</artifactId>
|
||||||
|
<version>6.6.8.Final</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import jakarta.persistence.MappedSuperclass;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
import org.hibernate.annotations.GenericGenerator;
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
public class IdOnlyEntity {
|
public class IdOnlyEntity {
|
||||||
|
@Comment("记录唯一标记")
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(generator = "snowflake")
|
@GeneratedValue(generator = "snowflake")
|
||||||
@GenericGenerator(name = "snowflake", strategy = "com.lanyuanxiaoyao.service.ai.web.configuration.SnowflakeIdGenerator")
|
@GenericGenerator(name = "snowflake", strategy = "com.lanyuanxiaoyao.service.ai.web.configuration.SnowflakeIdGenerator")
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.time.LocalDateTime;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
import org.springframework.data.annotation.CreatedDate;
|
import org.springframework.data.annotation.CreatedDate;
|
||||||
import org.springframework.data.annotation.LastModifiedDate;
|
import org.springframework.data.annotation.LastModifiedDate;
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
@@ -22,8 +23,10 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
public class SimpleEntity extends IdOnlyEntity {
|
public class SimpleEntity extends IdOnlyEntity {
|
||||||
|
@Comment("记录创建时间")
|
||||||
@CreatedDate
|
@CreatedDate
|
||||||
private LocalDateTime createdTime;
|
private LocalDateTime createdTime;
|
||||||
|
@Comment("记录更新时间")
|
||||||
@LastModifiedDate
|
@LastModifiedDate
|
||||||
private LocalDateTime modifiedTime;
|
private LocalDateTime modifiedTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
package com.lanyuanxiaoyao.service.ai.web.controller.task;
|
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.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.base.entity.SimpleItem;
|
||||||
import com.lanyuanxiaoyao.service.ai.web.entity.FlowTaskTemplate;
|
import com.lanyuanxiaoyao.service.ai.web.entity.FlowTaskTemplate;
|
||||||
import com.lanyuanxiaoyao.service.ai.web.service.task.FlowTaskTemplateService;
|
import com.lanyuanxiaoyao.service.ai.web.service.task.FlowTaskTemplateService;
|
||||||
|
import java.util.Map;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.mapstruct.Context;
|
||||||
import org.mapstruct.factory.Mappers;
|
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.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@@ -15,13 +24,32 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("flow_task/template")
|
@RequestMapping("flow_task/template")
|
||||||
public class TaskTemplateController extends SimpleControllerSupport<FlowTaskTemplate, TaskTemplateController.SaveItem, TaskTemplateController.ListItem, TaskTemplateController.DetailItem> {
|
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);
|
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
|
@Override
|
||||||
protected SaveItemMapper<FlowTaskTemplate, SaveItem> saveItemMapper() {
|
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
|
@Override
|
||||||
@@ -31,18 +59,24 @@ public class TaskTemplateController extends SimpleControllerSupport<FlowTaskTemp
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DetailItemMapper<FlowTaskTemplate, DetailItem> detailItemMapper() {
|
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
|
@Data
|
||||||
public static final class SaveItem {
|
public static final class SaveItem {
|
||||||
|
private Long id;
|
||||||
private String name;
|
private String name;
|
||||||
private String description;
|
private String description;
|
||||||
private String inputSchema;
|
private Map<String, Object> inputSchema;
|
||||||
private String flow;
|
|
||||||
|
|
||||||
@org.mapstruct.Mapper
|
@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 {
|
public static class DetailItem extends SimpleItem {
|
||||||
private String name;
|
private String name;
|
||||||
private String description;
|
private String description;
|
||||||
private String inputSchema;
|
private Map<String, Object> inputSchema;
|
||||||
private String flow;
|
|
||||||
|
|
||||||
@org.mapstruct.Mapper
|
@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<>() {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import lombok.Getter;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
import org.hibernate.annotations.DynamicUpdate;
|
import org.hibernate.annotations.DynamicUpdate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,11 +24,17 @@ import org.hibernate.annotations.DynamicUpdate;
|
|||||||
@DynamicUpdate
|
@DynamicUpdate
|
||||||
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_file")
|
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_file")
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
@Comment("记录上传的文件存储信息")
|
||||||
public class DataFile extends SimpleEntity {
|
public class DataFile extends SimpleEntity {
|
||||||
|
@Comment("文件名称")
|
||||||
private String filename;
|
private String filename;
|
||||||
|
@Comment("文件大小,单位是byte")
|
||||||
private Long size;
|
private Long size;
|
||||||
|
@Comment("文件的md5编码,用于校验文件的完整性")
|
||||||
private String md5;
|
private String md5;
|
||||||
|
@Comment("文件在主机上存储的实际路径")
|
||||||
private String path;
|
private String path;
|
||||||
|
@Comment("文件类型,通常记录的是文件的后缀名")
|
||||||
private String type;
|
private String type;
|
||||||
|
|
||||||
public DataFile(String filename) {
|
public DataFile(String filename) {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import jakarta.persistence.Column;
|
|||||||
import jakarta.persistence.ConstraintMode;
|
import jakarta.persistence.ConstraintMode;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.EntityListeners;
|
import jakarta.persistence.EntityListeners;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
import jakarta.persistence.FetchType;
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.ForeignKey;
|
import jakarta.persistence.ForeignKey;
|
||||||
import jakarta.persistence.JoinTable;
|
import jakarta.persistence.JoinTable;
|
||||||
@@ -17,6 +19,7 @@ import java.util.Set;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
import org.hibernate.annotations.DynamicUpdate;
|
import org.hibernate.annotations.DynamicUpdate;
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
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 = {
|
@NamedEntityGraph(name = "feedback.detail", attributeNodes = {
|
||||||
@NamedAttributeNode("pictures")
|
@NamedAttributeNode("pictures")
|
||||||
})
|
})
|
||||||
|
@Comment("报障信息记录")
|
||||||
public class Feedback extends SimpleEntity {
|
public class Feedback extends SimpleEntity {
|
||||||
|
@Comment("原始报障说明")
|
||||||
@Column(nullable = false, columnDefinition = "longtext")
|
@Column(nullable = false, columnDefinition = "longtext")
|
||||||
private String source;
|
private String source;
|
||||||
|
@Comment("报障相关截图")
|
||||||
@OneToMany(fetch = FetchType.EAGER)
|
@OneToMany(fetch = FetchType.EAGER)
|
||||||
@JoinTable(catalog = Constants.DATABASE_NAME, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
@JoinTable(catalog = Constants.DATABASE_NAME, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||||
@ToString.Exclude
|
@ToString.Exclude
|
||||||
private Set<DataFile> pictures;
|
private Set<DataFile> pictures;
|
||||||
|
@Comment("AI的分析结果")
|
||||||
@Column(columnDefinition = "longtext")
|
@Column(columnDefinition = "longtext")
|
||||||
private String analysis;
|
private String analysis;
|
||||||
|
@Comment("AI的解决方案")
|
||||||
@Column(columnDefinition = "longtext")
|
@Column(columnDefinition = "longtext")
|
||||||
private String conclusion;
|
private String conclusion;
|
||||||
|
@Comment("报障处理状态")
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
private Status status = Status.ANALYSIS_PROCESSING;
|
private Status status = Status.ANALYSIS_PROCESSING;
|
||||||
|
|
||||||
public enum Status {
|
public enum Status {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import jakarta.persistence.Column;
|
|||||||
import jakarta.persistence.ConstraintMode;
|
import jakarta.persistence.ConstraintMode;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.EntityListeners;
|
import jakarta.persistence.EntityListeners;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
import jakarta.persistence.ForeignKey;
|
import jakarta.persistence.ForeignKey;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
@@ -13,6 +15,7 @@ import jakarta.persistence.Table;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
import org.hibernate.annotations.DynamicUpdate;
|
import org.hibernate.annotations.DynamicUpdate;
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
|
||||||
@@ -23,16 +26,23 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|||||||
@DynamicUpdate
|
@DynamicUpdate
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_flow_task")
|
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_flow_task")
|
||||||
|
@Comment("流程任务记录")
|
||||||
public class FlowTask extends SimpleEntity {
|
public class FlowTask extends SimpleEntity {
|
||||||
|
@Comment("流程任务对应的模板")
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
@JoinColumn(nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||||
private FlowTaskTemplate template;
|
private FlowTaskTemplate template;
|
||||||
|
@Comment("任务输入")
|
||||||
@Column(columnDefinition = "longtext")
|
@Column(columnDefinition = "longtext")
|
||||||
private String input;
|
private String input;
|
||||||
|
@Comment("任务运行状态")
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
private Status status = Status.RUNNING;
|
private Status status = Status.RUNNING;
|
||||||
|
@Comment("任务运行产生的报错")
|
||||||
@Column(columnDefinition = "longtext")
|
@Column(columnDefinition = "longtext")
|
||||||
private String error;
|
private String error;
|
||||||
|
@Comment("任务运行结果")
|
||||||
@Column(columnDefinition = "longtext")
|
@Column(columnDefinition = "longtext")
|
||||||
private String result;
|
private String result;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import jakarta.persistence.Table;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
import org.hibernate.annotations.DynamicUpdate;
|
import org.hibernate.annotations.DynamicUpdate;
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
|
||||||
@@ -19,14 +20,17 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|||||||
@DynamicUpdate
|
@DynamicUpdate
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_flow_task_template")
|
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_flow_task_template")
|
||||||
|
@Comment("流程任务模板")
|
||||||
public class FlowTaskTemplate extends SimpleEntity {
|
public class FlowTaskTemplate extends SimpleEntity {
|
||||||
|
@Comment("模板名称")
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String name;
|
private String name;
|
||||||
|
@Comment("模板功能、内容说明")
|
||||||
private String description;
|
private String description;
|
||||||
|
@Comment("模板入参Schema")
|
||||||
@Column(nullable = false, columnDefinition = "longtext")
|
@Column(nullable = false, columnDefinition = "longtext")
|
||||||
private String inputSchema;
|
private String inputSchema;
|
||||||
@Column(nullable = false, columnDefinition = "text")
|
@Comment("前端流程图数据")
|
||||||
private String flow;
|
|
||||||
@Column(columnDefinition = "longtext")
|
@Column(columnDefinition = "longtext")
|
||||||
private String flowGraph;
|
private String flowGraph;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import jakarta.persistence.Column;
|
|||||||
import jakarta.persistence.ConstraintMode;
|
import jakarta.persistence.ConstraintMode;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.EntityListeners;
|
import jakarta.persistence.EntityListeners;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
import jakarta.persistence.FetchType;
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.ForeignKey;
|
import jakarta.persistence.ForeignKey;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
@@ -14,6 +16,7 @@ import jakarta.persistence.Table;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
import org.hibernate.annotations.DynamicUpdate;
|
import org.hibernate.annotations.DynamicUpdate;
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
|
||||||
@@ -28,10 +31,14 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|||||||
@DynamicUpdate
|
@DynamicUpdate
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_group")
|
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_group")
|
||||||
|
@Comment("知识库内的逻辑分组,比如一个文件是一个分组或一次上传的所有文本是一个分组,可以自由使用而不是限于文件范畴")
|
||||||
public class Group extends SimpleEntity {
|
public class Group extends SimpleEntity {
|
||||||
|
@Comment("分组名称")
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String name;
|
private String name;
|
||||||
|
@Comment("分组处理状态")
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
private Status status = Status.RUNNING;
|
private Status status = Status.RUNNING;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import jakarta.persistence.CascadeType;
|
|||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.EntityListeners;
|
import jakarta.persistence.EntityListeners;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
import jakarta.persistence.FetchType;
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
@@ -13,6 +15,7 @@ import java.util.Set;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import org.hibernate.annotations.Comment;
|
||||||
import org.hibernate.annotations.DynamicUpdate;
|
import org.hibernate.annotations.DynamicUpdate;
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
|
||||||
@@ -27,16 +30,23 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|||||||
@DynamicUpdate
|
@DynamicUpdate
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_knowledge")
|
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_knowledge")
|
||||||
|
@Comment("知识库")
|
||||||
public class Knowledge extends SimpleEntity {
|
public class Knowledge extends SimpleEntity {
|
||||||
|
@Comment("知识库对应的向量库名")
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Long vectorSourceId;
|
private Long vectorSourceId;
|
||||||
|
@Comment("知识库名称")
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String name;
|
private String name;
|
||||||
|
@Comment("知识库说明")
|
||||||
@Column(nullable = false, columnDefinition = "longtext")
|
@Column(nullable = false, columnDefinition = "longtext")
|
||||||
private String description;
|
private String description;
|
||||||
|
@Comment("知识库策略")
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
private Strategy strategy = Strategy.Cosine;
|
private Strategy strategy = Strategy.Cosine;
|
||||||
|
|
||||||
|
@Comment("知识库下包含的分组")
|
||||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "knowledge", cascade = CascadeType.ALL)
|
@OneToMany(fetch = FetchType.LAZY, mappedBy = "knowledge", cascade = CascadeType.ALL)
|
||||||
@ToString.Exclude
|
@ToString.Exclude
|
||||||
private Set<Group> groups;
|
private Set<Group> groups;
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,11 +2,15 @@ package com.lanyuanxiaoyao.service.configuration;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
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.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
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
|
* Spring Security Config
|
||||||
@@ -25,6 +29,19 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
|||||||
this.securityProperties = securityProperties;
|
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
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
http.authorizeHttpRequests()
|
http.authorizeHttpRequests()
|
||||||
@@ -36,7 +53,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
|||||||
.csrf()
|
.csrf()
|
||||||
.disable()
|
.disable()
|
||||||
.cors()
|
.cors()
|
||||||
.disable()
|
.and()
|
||||||
.formLogin()
|
.formLogin()
|
||||||
.disable();
|
.disable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import org.springframework.security.core.userdetails.User;
|
|||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
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;
|
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,24 +33,12 @@ public class SecurityConfiguration {
|
|||||||
.httpBasic()
|
.httpBasic()
|
||||||
.disable()
|
.disable()
|
||||||
.cors()
|
.cors()
|
||||||
.configurationSource(corsConfigurationSource())
|
.disable()
|
||||||
.and()
|
|
||||||
.csrf()
|
.csrf()
|
||||||
.disable()
|
.disable()
|
||||||
.build();
|
.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
|
@Bean
|
||||||
public MapReactiveUserDetailsService userDetailsService(SecurityProperties securityProperties) {
|
public MapReactiveUserDetailsService userDetailsService(SecurityProperties securityProperties) {
|
||||||
UserDetails user = User.builder()
|
UserDetails user = User.builder()
|
||||||
|
|||||||
@@ -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 {ConfigProvider} from 'antd'
|
||||||
import React from 'react'
|
import {dateFormat} from 'licia'
|
||||||
|
import React, {useMemo} from 'react'
|
||||||
import {Outlet, useLocation, useNavigate} from 'react-router'
|
import {Outlet, useLocation, useNavigate} from 'react-router'
|
||||||
|
import styled from 'styled-components'
|
||||||
import {menus} from '../route.tsx'
|
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',
|
icon: 'http://132.126.207.124:8686/udal-manager/static/favicon.ico',
|
||||||
title: 'CSV-HUDI处理平台',
|
title: 'CSV-HUDI处理平台',
|
||||||
desc: 'Hudi 批量割接、稽核任务管理平台',
|
desc: 'Hudi 批量割接、稽核任务管理平台',
|
||||||
url: 'http://132.126.207.124:8686/udal-manager/',
|
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 App: React.FC = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
const currentYear = useMemo(() => dateFormat(new Date(), 'yyyy'), [])
|
||||||
return (
|
return (
|
||||||
<ProLayout
|
<ProLayoutDiv>
|
||||||
token={{
|
<ProLayout
|
||||||
colorTextAppListIcon: '#dfdfdf',
|
siderWidth={180}
|
||||||
colorTextAppListIconHover: '#ffffff',
|
token={{
|
||||||
header: {
|
colorTextAppListIcon: '#dfdfdf',
|
||||||
colorBgHeader: '#292f33',
|
colorTextAppListIconHover: '#ffffff',
|
||||||
colorHeaderTitle: '#ffffff',
|
header: {
|
||||||
colorTextMenu: '#dfdfdf',
|
colorBgHeader: '#292f33',
|
||||||
colorTextMenuSecondary: '#dfdfdf',
|
colorHeaderTitle: '#ffffff',
|
||||||
colorTextMenuSelected: '#ffffff',
|
colorTextMenu: '#dfdfdf',
|
||||||
colorTextMenuActive: '#ffffff',
|
colorTextMenuSecondary: '#dfdfdf',
|
||||||
colorBgMenuItemSelected: '#22272b',
|
colorTextMenuSelected: '#ffffff',
|
||||||
colorTextRightActionsItem: '#dfdfdf',
|
colorTextMenuActive: '#ffffff',
|
||||||
},
|
colorBgMenuItemSelected: '#22272b',
|
||||||
}}
|
colorTextRightActionsItem: '#dfdfdf',
|
||||||
appList={apps}
|
},
|
||||||
disableMobile={true}
|
pageContainer: {
|
||||||
logo={<img src="icon.png" alt="logo"/>}
|
paddingBlockPageContainerContent: 0,
|
||||||
title="Hudi 服务总台"
|
paddingInlinePageContainerContent: 0,
|
||||||
route={menus}
|
marginBlockPageContainerContent: 0,
|
||||||
location={{pathname: location.pathname}}
|
marginInlinePageContainerContent: 0,
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
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
|
||||||
</ConfigProvider>
|
theme={{
|
||||||
</ProLayout>
|
components: {
|
||||||
|
Card: {
|
||||||
|
bodyPadding: 0,
|
||||||
|
bodyPaddingSM: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Outlet/>
|
||||||
|
</ConfigProvider>
|
||||||
|
</ProLayout>
|
||||||
|
</ProLayoutDiv>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ const FlowableDiv = styled.div`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export type FlowEditorProps = {
|
||||||
|
}
|
||||||
|
|
||||||
function FlowEditor() {
|
function FlowEditor() {
|
||||||
const [messageApi, contextHolder] = message.useMessage()
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
const [nodeDef] = useState<{
|
const [nodeDef] = useState<{
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import {Handle, type NodeProps, Position} from '@xyflow/react'
|
import {Handle, type NodeProps, Position} from '@xyflow/react'
|
||||||
import AmisNode from './AmisNode.tsx'
|
|
||||||
import {Tag} from 'antd'
|
import {Tag} from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import AmisNode from './AmisNode.tsx'
|
||||||
|
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
index: 1
|
index: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: 2
|
index: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: 3
|
index: 3,
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const SwitchNode = (props: NodeProps) => AmisNode({
|
const SwitchNode = (props: NodeProps) => AmisNode({
|
||||||
@@ -21,6 +21,7 @@ const SwitchNode = (props: NodeProps) => AmisNode({
|
|||||||
defaultNodeName: '分支节点',
|
defaultNodeName: '分支节点',
|
||||||
defaultNodeDescription: '根据不同的情况前往不同的分支',
|
defaultNodeDescription: '根据不同的情况前往不同的分支',
|
||||||
columnSchema: [],
|
columnSchema: [],
|
||||||
|
// @ts-ignore
|
||||||
extraNodeDescription: nodeData => {
|
extraNodeDescription: nodeData => {
|
||||||
return (
|
return (
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
@@ -32,6 +33,7 @@ const SwitchNode = (props: NodeProps) => AmisNode({
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
// @ts-ignore
|
||||||
handlers: nodeData => {
|
handlers: nodeData => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -47,7 +49,7 @@ const SwitchNode = (props: NodeProps) => AmisNode({
|
|||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export default React.memo(SwitchNode)
|
export default React.memo(SwitchNode)
|
||||||
48
service-web/client/src/pages/ai/task/InputSchema.tsx
Normal file
48
service-web/client/src/pages/ai/task/InputSchema.tsx
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {amisRender, commonInfo, crudCommonOptions, paginationTemplate,} from '../../../../util/amis.tsx'
|
|
||||||
import {useNavigate} from 'react-router'
|
import {useNavigate} from 'react-router'
|
||||||
|
import {amisRender, commonInfo, crudCommonOptions, paginationTemplate} from '../../../../util/amis.tsx'
|
||||||
|
|
||||||
const FlowTaskTemplate: React.FC = () => {
|
const FlowTaskTemplate: React.FC = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@@ -20,8 +20,8 @@ const FlowTaskTemplate: React.FC = () => {
|
|||||||
page: {
|
page: {
|
||||||
index: '${page}',
|
index: '${page}',
|
||||||
size: '${perPage}',
|
size: '${perPage}',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
...crudCommonOptions(),
|
...crudCommonOptions(),
|
||||||
...paginationTemplate(
|
...paginationTemplate(
|
||||||
@@ -32,6 +32,8 @@ const FlowTaskTemplate: React.FC = () => {
|
|||||||
type: 'action',
|
type: 'action',
|
||||||
label: '',
|
label: '',
|
||||||
icon: 'fa fa-plus',
|
icon: 'fa fa-plus',
|
||||||
|
tooltip: '新增',
|
||||||
|
tooltipPlacement: 'top',
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
onEvent: {
|
onEvent: {
|
||||||
click: {
|
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',
|
type: 'action',
|
||||||
label: '删除',
|
label: '删除',
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
import {isEmpty, isEqual} from 'licia'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {useParams} from 'react-router'
|
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import {amisRender, commonInfo, horizontalFormOptions} from '../../../../util/amis.tsx'
|
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`
|
const TemplateEditDiv = styled.div`
|
||||||
.antd-EditorControl {
|
.antd-EditorControl {
|
||||||
@@ -13,7 +14,6 @@ const TemplateEditDiv = styled.div`
|
|||||||
const FlowTaskTemplateEdit: React.FC = () => {
|
const FlowTaskTemplateEdit: React.FC = () => {
|
||||||
const {template_id} = useParams()
|
const {template_id} = useParams()
|
||||||
const preloadTemplateId = isEqual(template_id, '-1') ? undefined : template_id
|
const preloadTemplateId = isEqual(template_id, '-1') ? undefined : template_id
|
||||||
console.log('preloadTemplateId', preloadTemplateId)
|
|
||||||
return (
|
return (
|
||||||
<TemplateEditDiv className="task-template-edit h-full">
|
<TemplateEditDiv className="task-template-edit h-full">
|
||||||
{amisRender({
|
{amisRender({
|
||||||
@@ -26,73 +26,130 @@ const FlowTaskTemplateEdit: React.FC = () => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${commonInfo.baseAiUrl}/flow_task/template/save`,
|
url: `${commonInfo.baseAiUrl}/flow_task/template/save`,
|
||||||
data: {
|
data: {
|
||||||
name: '${template.name}',
|
id: '${id|default:undefined}',
|
||||||
description: '${template.description}',
|
name: '${name}',
|
||||||
inputSchema: '${template.inputSchema}',
|
description: '${description}',
|
||||||
flow: '${template.flow}',
|
inputSchema: '${inputSchema|default:undefined}',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
initApi: preloadTemplateId
|
initApi: preloadTemplateId
|
||||||
? {
|
? {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: `${commonInfo.baseAiUrl}/flow_task/template/detail/${preloadTemplateId}`,
|
url: `${commonInfo.baseAiUrl}/flow_task/template/detail/${preloadTemplateId}`,
|
||||||
// @ts-ignore
|
|
||||||
adaptor: (payload, response, api, context) => {
|
|
||||||
return {
|
|
||||||
...payload,
|
|
||||||
data: {
|
|
||||||
template: payload.data,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
wrapWithPanel: false,
|
wrapWithPanel: false,
|
||||||
...horizontalFormOptions(),
|
...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: [
|
body: [
|
||||||
|
{
|
||||||
|
type: 'hidden',
|
||||||
|
name: 'id',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'input-text',
|
type: 'input-text',
|
||||||
name: 'template.name',
|
name: 'name',
|
||||||
label: '名称',
|
label: '名称',
|
||||||
required: true,
|
required: true,
|
||||||
|
clearable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
name: 'template.description',
|
name: 'description',
|
||||||
label: '描述',
|
label: '描述',
|
||||||
required: true,
|
required: true,
|
||||||
|
clearable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'group',
|
type: 'group',
|
||||||
body: [
|
body: [
|
||||||
{
|
{
|
||||||
type: 'editor',
|
type: 'wrapper',
|
||||||
required: true,
|
size: 'none',
|
||||||
label: '入参表单',
|
body: [
|
||||||
description: '使用amis代码编写入参表单结构,流程会解析所有name对应的变量传入流程开始阶段;只需要编写form的columns部分',
|
{
|
||||||
name: 'template.inputSchema',
|
type: 'input-kvs',
|
||||||
value: '[]',
|
name: 'inputSchema',
|
||||||
language: 'json',
|
label: '输入变量',
|
||||||
options: {
|
addButtonText: '新增入参',
|
||||||
wordWrap: 'bounded',
|
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',
|
type: 'amis',
|
||||||
name: 'template.inputSchema',
|
name: 'inputPreview',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'editor',
|
|
||||||
required: true,
|
|
||||||
label: '任务流程',
|
|
||||||
name: 'template.flow',
|
|
||||||
description: '使用标准的LiteFlow语法',
|
|
||||||
language: 'xml',
|
|
||||||
options: {
|
|
||||||
wordWrap: 'bounded',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'button-toolbar',
|
type: 'button-toolbar',
|
||||||
|
|||||||
@@ -18,10 +18,15 @@ import {values} from 'licia'
|
|||||||
import {Navigate, type RouteObject} from 'react-router'
|
import {Navigate, type RouteObject} from 'react-router'
|
||||||
import Conversation from './pages/ai/Conversation.tsx'
|
import Conversation from './pages/ai/Conversation.tsx'
|
||||||
import Feedback from './pages/ai/feedback/Feedback.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 DataDetail from './pages/ai/knowledge/DataDetail.tsx'
|
||||||
import DataImport from './pages/ai/knowledge/DataImport.tsx'
|
import DataImport from './pages/ai/knowledge/DataImport.tsx'
|
||||||
import DataSegment from './pages/ai/knowledge/DataSegment.tsx'
|
import DataSegment from './pages/ai/knowledge/DataSegment.tsx'
|
||||||
import Knowledge from './pages/ai/knowledge/Knowledge.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 App from './pages/App.tsx'
|
||||||
import Cloud from './pages/overview/Cloud.tsx'
|
import Cloud from './pages/overview/Cloud.tsx'
|
||||||
import Overview from './pages/overview/Overview.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 YarnCluster from './pages/overview/YarnCluster.tsx'
|
||||||
import Test from './pages/Test.tsx'
|
import Test from './pages/Test.tsx'
|
||||||
import {commonInfo} from './util/amis.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[] = [
|
export const routes: RouteObject[] = [
|
||||||
{
|
{
|
||||||
@@ -245,8 +245,8 @@ export const menus = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '1089caa6-9477-44a5-99f1-a9c179f6cfd3',
|
path: '1089caa6-9477-44a5-99f1-a9c179f6cfd3',
|
||||||
name: '任务',
|
name: '流程任务',
|
||||||
icon: <FileTextOutlined/>,
|
icon: <GatewayOutlined/>,
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/ai/flow_task',
|
path: '/ai/flow_task',
|
||||||
@@ -255,7 +255,7 @@ export const menus = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/ai/flow_task_template',
|
path: '/ai/flow_task_template',
|
||||||
name: '任务模板',
|
name: '流程模板',
|
||||||
icon: <FileTextOutlined/>,
|
icon: <FileTextOutlined/>,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import {isEqual} from 'licia'
|
|||||||
export const commonInfo = {
|
export const commonInfo = {
|
||||||
debug: isEqual(import.meta.env.MODE, 'development'),
|
debug: isEqual(import.meta.env.MODE, 'development'),
|
||||||
baseUrl: 'http://132.126.207.130:35690/hudi_services/service_web',
|
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://132.126.207.130:35690/hudi_services/service_ai_web',
|
||||||
baseAiUrl: 'http://localhost:8080',
|
// baseAiUrl: 'http://localhost:8080',
|
||||||
authorizationHeaders: {
|
authorizationHeaders: {
|
||||||
'Authorization': 'Basic QXhoRWJzY3dzSkRiWU1IMjpjWXhnM2I0UHRXb1ZENVNqRmF5V3h0blNWc2p6UnNnNA==',
|
'Authorization': 'Basic QXhoRWJzY3dzSkRiWU1IMjpjWXhnM2I0UHRXb1ZENVNqRmF5V3h0blNWc2p6UnNnNA==',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|||||||
2
service-web/client/src/vite-env.d.ts
vendored
2
service-web/client/src/vite-env.d.ts
vendored
@@ -1 +1,3 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare const __APP_VERSION__: string
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import react from '@vitejs/plugin-react-swc'
|
import react from '@vitejs/plugin-react-swc'
|
||||||
import {defineConfig, type UserConfig} from 'vite'
|
import {defineConfig, type UserConfig} from 'vite'
|
||||||
import obfuscatorPlugin from 'vite-plugin-javascript-obfuscator'
|
import obfuscatorPlugin from 'vite-plugin-javascript-obfuscator'
|
||||||
|
import packageJson from './package.json'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig(({mode}) => {
|
export default defineConfig(({mode}) => {
|
||||||
let config: UserConfig = {
|
let config: UserConfig = {
|
||||||
|
define: {
|
||||||
|
__APP_VERSION__: JSON.stringify(packageJson.version) ?? '0.0.0',
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
react(),
|
react(),
|
||||||
obfuscatorPlugin({
|
obfuscatorPlugin({
|
||||||
|
|||||||
Reference in New Issue
Block a user