feat: add file copying functionality to dataset directory and update base path configuration

This commit is contained in:
Dallas98
2025-11-14 18:05:40 +08:00
committed by GitHub
parent d9e163c163
commit 5638bdcf1c
9 changed files with 248 additions and 185 deletions

View File

@@ -74,7 +74,7 @@ public class TagController {
} }
@DeleteMapping @DeleteMapping
public ResponseEntity<Response<Valid>> deleteTag(@RequestParam(value = "ids") @Valid @Size(max = 10) List<String> ids) { public ResponseEntity<Response<Void>> deleteTag(@RequestParam(value = "ids") @Valid @Size(max = 10) List<String> ids) {
try { try {
tagApplicationService.deleteTag(ids.stream().filter(StringUtils::isNoneBlank).distinct().toList()); tagApplicationService.deleteTag(ids.stream().filter(StringUtils::isNoneBlank).distinct().toList());
return ResponseEntity.ok(Response.ok(null)); return ResponseEntity.ok(Response.ok(null));

View File

@@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.List; import java.util.List;
/** /**
@@ -31,7 +32,9 @@ public class SysParamApplicationService {
* @return 系统参数列表 * @return 系统参数列表
*/ */
public List<SysParam> list() { public List<SysParam> list() {
return sysParamRepository.list(); List<SysParam> sysParams = sysParamRepository.list();
sysParams.sort(Comparator.comparing(SysParam::getParamType));
return sysParams;
} }
/** /**

View File

@@ -1,6 +1,7 @@
package com.datamate.common.setting.interfaces.rest; package com.datamate.common.setting.interfaces.rest;
import com.datamate.common.setting.application.SysParamApplicationService; import com.datamate.common.setting.application.SysParamApplicationService;
import com.datamate.common.setting.interfaces.rest.dto.ParamRequest;
import com.datamate.common.setting.domain.entity.SysParam; import com.datamate.common.setting.domain.entity.SysParam;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -33,11 +34,11 @@ public class SysParamController {
* 根据参数id修改系统参数值 * 根据参数id修改系统参数值
* *
* @param paramId 参数id * @param paramId 参数id
* @param paramValue 参数值 * @param paramRequest 参数值请求体
*/ */
@PutMapping("/{paramId}") @PutMapping("/{paramId}")
public void updateParamValueById(@PathVariable("paramId") String paramId, @RequestBody String paramValue) { public void updateParamValueById(@PathVariable("paramId") String paramId, @RequestBody ParamRequest paramRequest) {
sysParamApplicationService.updateParamValueById(paramId, paramValue); sysParamApplicationService.updateParamValueById(paramId, paramRequest.paramValue());
} }
/** /**
@@ -50,3 +51,4 @@ public class SysParamController {
sysParamApplicationService.deleteParamById(paramId); sysParamApplicationService.deleteParamById(paramId);
} }
} }

View File

@@ -0,0 +1,4 @@
package com.datamate.common.setting.interfaces.rest.dto;
public record ParamRequest(String paramValue, boolean isEnabled) {
}

View File

@@ -53,7 +53,7 @@ interface ProviderI {
isEnabled: boolean; isEnabled: boolean;
} }
export default function EnvironmentAccess() { export default function ModelAccess() {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [showModelDialog, setShowModelDialog] = useState(false); const [showModelDialog, setShowModelDialog] = useState(false);
const [isEditMode, setIsEditMode] = useState(false); const [isEditMode, setIsEditMode] = useState(false);

View File

@@ -27,16 +27,16 @@ export default function SettingsPage() {
icon: <Component className="w-4 h-4" />, icon: <Component className="w-4 h-4" />,
label: "模型接入", label: "模型接入",
}, },
// { {
// key: "system-config", key: "system-config",
// icon: <SettingOutlined />, icon: <SettingOutlined />,
// label: "参数配置", label: "参数配置",
// }, },
// { {
// key: "webhook-config", key: "webhook-config",
// icon: <ApiOutlined />, icon: <ApiOutlined />,
// label: "Webhook", label: "Webhook",
// }, },
]} ]}
selectedKeys={[activeTab]} selectedKeys={[activeTab]}
onClick={({ key }) => { onClick={({ key }) => {

View File

@@ -1,107 +1,205 @@
import { Divider, Input, Select, Switch, Button, Form, App, Table } from "antd"; import { Divider, Input, Select, Switch, Button, Table, Spin } from "antd";
import { PlusOutlined } from "@ant-design/icons"; import { useEffect, useState } from "react";
import { useState } from "react"; import { getSysParamList, updateSysParamValue } from './settings.apis';
interface SystemParam {
id: string;
paramKey: string;
paramValue: string;
description: string;
isEnabled: boolean;
paramType?: string;
optionList?: string;
isBuiltIn?: boolean;
canModify?: boolean;
}
export default function SystemConfig() { export default function SystemConfig() {
const { message } = App.useApp(); const [sysParams, setSysParams] = useState<SystemParam[]>([]);
// System Settings State const [loading, setLoading] = useState(true);
const [systemConfig, setSystemConfig] = useState({ const [editingParams, setEditingParams] = useState<Record<string, string>>({});
siteName: "DataMate", const [tempEditingValues, setTempEditingValues] = useState<Record<string, string>>({});
maxFileSize: "100",
autoBackup: true, // 获取系统参数列表
logLevel: "info", const fetchSysParams = async () => {
sessionTimeout: "30", try {
enableNotifications: true, setLoading(true);
const response = await getSysParamList();
setSysParams(response.data || []);
// 初始化编辑状态
const initialEditState: Record<string, string> = {};
response.data?.forEach((param: SystemParam) => {
initialEditState[param.id] = param.paramValue;
}); });
const configData = [ setEditingParams(initialEditState);
{ } catch (error) {
key: "1", console.error('获取系统参数失败:', error);
parameter: "站点名称", } finally {
value: systemConfig.siteName, setLoading(false);
description: "系统的显示名称", }
},
{
key: "2",
parameter: "最大文件大小 (MB)",
value: systemConfig.maxFileSize,
description: "允许上传的最大文件大小",
},
{
key: "3",
parameter: "自动备份",
value: systemConfig.autoBackup ? "启用" : "禁用",
description: "定期自动备份系统数据",
},
{
key: "4",
parameter: "日志级别",
value: systemConfig.logLevel,
description: "系统日志的详细程度",
},
{
key: "5",
parameter: "会话超时 (分钟)",
value: systemConfig.sessionTimeout,
description: "用户会话的超时时间",
},
];
const logLevelOptions = [
{ value: "debug", label: "Debug" },
{ value: "info", label: "Info" },
{ value: "warn", label: "Warning" },
{ value: "error", label: "Error" },
];
const handleSaveSystemSettings = () => {
// Save system settings logic
console.log("Saving system settings:", systemConfig);
message.success("系统设置已保存");
}; };
// 组件加载时获取数据
useEffect(() => {
fetchSysParams();
}, []);
// 处理参数值更新 - 立即更新(用于开关和下拉框)
const handleImmediateUpdate = async (param: SystemParam, newValue: string | boolean) => {
try {
const stringValue = typeof newValue === 'boolean' ? newValue.toString() : newValue;
// 更新本地临时状态
setTempEditingValues(prev => ({ ...prev, [param.id]: stringValue }));
setEditingParams(prev => ({ ...prev, [param.id]: stringValue }));
// 调用后端更新接口 - 修改为适应新的接口格式
await updateSysParamValue({
id: param.id,
paramValue: stringValue
});
// 更新本地状态
setSysParams(prev => prev.map(p =>
p.id === param.id ? { ...p, paramValue: stringValue } : p
));
} catch (error) {
console.error('更新参数失败:', error);
// 恢复原始值
setEditingParams(prev => ({ ...prev, [param.id]: param.paramValue }));
setTempEditingValues(prev => ({ ...prev, [param.id]: param.paramValue }));
}
};
// 处理输入框值变化 - 仅更新临时状态
const handleInputChange = (param: SystemParam, newValue: string) => {
setTempEditingValues(prev => ({ ...prev, [param.id]: newValue }));
};
// 处理输入框失焦 - 发起后端请求
const handleInputBlur = async (param: SystemParam) => {
const newValue = tempEditingValues[param.id];
if (newValue !== undefined && newValue !== param.paramValue) {
try {
// 调用后端更新接口
await updateSysParamValue({
id: param.id,
paramValue: newValue
});
// 更新本地状态
setSysParams(prev => prev.map(p =>
p.id === param.id ? { ...p, paramValue: newValue } : p
));
setEditingParams(prev => ({ ...prev, [param.id]: newValue }));
} catch (error) {
console.error('更新参数失败:', error);
// 恢复原始值
setTempEditingValues(prev => ({ ...prev, [param.id]: param.paramValue }));
setEditingParams(prev => ({ ...prev, [param.id]: param.paramValue }));
}
}
};
// 获取选项列表 - 解析逗号分隔的字符串
const getOptionList = (optionListStr?: string) => {
if (!optionListStr) return [];
try {
// 按逗号分割字符串并去除首尾空格
return optionListStr.split(',').map(option => ({
value: option.trim(),
label: option.trim()
}));
} catch (error) {
console.error('解析选项列表失败:', error);
return [];
}
};
// 表格列定义
const columns = [ const columns = [
{ {
title: "参数", title: "参数",
dataIndex: "parameter", dataIndex: "paramKey",
key: "parameter", key: "paramKey",
width: 180,
}, },
{ {
title: "值", title: "参数值",
dataIndex: "value", dataIndex: "paramValue",
key: "value", key: "paramValue",
width: 200,
render: (value: string, record: SystemParam) => {
// 使用临时编辑值或当前值
const displayValue = tempEditingValues[record.id] ?? editingParams[record.id] ?? value;
// 对于boolean类型,使用开关按钮
if (record.paramType === 'boolean') {
const isChecked = displayValue.toLowerCase() === 'true';
return (
<Switch
checked={isChecked}
onChange={(checked) => handleImmediateUpdate(record, checked)}
disabled={!record.canModify}
/>
);
}
// 对于有选项列表的参数,强制使用下拉框
if (record.optionList && record.optionList.trim()) {
const options = getOptionList(record.optionList);
return (
<Select
value={displayValue}
onChange={(newValue) => handleImmediateUpdate(record, newValue)}
options={options}
disabled={!record.canModify}
style={{ width: '150px' }}
placeholder="请选择值"
/>
);
}
// 对于数字类型
if (record.paramType === 'integer' || record.paramType === 'number') {
return (
<Input
type="number"
value={displayValue}
onChange={(e) => handleInputChange(record, e.target.value)}
onBlur={() => handleInputBlur(record)}
disabled={!record.canModify}
style={{ width: '150px' }}
/>
);
}
// 默认为文本输入
return (
<Input
value={displayValue}
onChange={(e) => handleInputChange(record, e.target.value)}
onBlur={() => handleInputBlur(record)}
disabled={!record.canModify}
style={{ width: '150px' }}
/>
);
},
}, },
{ {
title: "描述", title: "描述",
dataIndex: "description", dataIndex: "description",
key: "description", key: "description",
width: 300,
}, },
{ {
title: "是否启用", title: "是否启用",
dataIndex: "enabled", dataIndex: "isEnabled",
key: "enabled", key: "isEnabled",
render: (_: any, record: any) => ( width: 100,
<Switch render: (isEnabled: boolean) => (
checked={ <Switch checked={isEnabled} disabled={true} />
record.key === "3"
? systemConfig.autoBackup
: record.key === "5"
? systemConfig.enableNotifications
: false
}
onChange={(checked) => {
if (record.key === "3") {
setSystemConfig((prevConfig) => ({
...prevConfig,
autoBackup: checked,
}));
} else if (record.key === "5") {
setSystemConfig((prevConfig) => ({
...prevConfig,
enableNotifications: checked,
}));
}
}}
/>
), ),
} }
]; ];
@@ -109,81 +207,23 @@ export default function SystemConfig() {
return ( return (
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">
<div className="flex items-top justify-between"> <div className="flex items-top justify-between">
<h2 className="text-lg font-medium mb-4"></h2> <h2 className="text-lg font-medium mb-4"></h2>
<Button <Button onClick={fetchSysParams}></Button>
type="primary"
icon={<PlusOutlined />}
onClick={() => {
setIsEditMode(false);
form.resetFields();
setNewModel({
name: "",
provider: "",
model: "",
apiKey: "",
endpoint: "",
});
setShowModelDialog(true);
}}
>
</Button>
</div> </div>
<div className="flex-1 border-card overflow-auto p-6"> <div className="flex-1 border-card overflow-auto p-6">
<Table columns={columns} data={configData} /> {loading ? (
<Form <div className="flex justify-center items-center h-64">
onValuesChange={(changedValues) => { <Spin size="large" />
setSystemConfig((prevConfig) => ({
...prevConfig,
...changedValues,
}));
}}
layout="vertical"
>
<div className="grid grid-cols-2 gap-6 mt-6">
<Form.Item name="siteName" label="站点名称">
<Input />
</Form.Item>
<Form.Item name="maxFileSize" label="最大文件大小 (MB)">
<Input type="number" />
</Form.Item>
<Form.Item name="logLevel" label="日志级别">
<Select options={logLevelOptions}></Select>
</Form.Item>
<Form.Item name="sessionTimeout" label="会话超时 (分钟)">
<Input type="number" />
</Form.Item>
</div> </div>
<Divider /> ) : (
<div className="space-y-4"> <Table
<h4 className="font-medium"></h4> columns={columns}
<div className="space-y-3"> dataSource={sysParams}
<div className="flex items-center justify-between"> pagination={false}
<div> size="middle"
<span></span> rowKey="id"
<p className="text-sm text-gray-500"></p> />
</div> )}
<Form.Item name="autoBackup" valuePropName="checked">
<Switch />
</Form.Item>
</div>
<div className="flex items-center justify-between">
<div>
<span></span>
<p className="text-sm text-gray-500"></p>
</div>
<Form.Item name="enableNotifications" valuePropName="checked">
<Switch />
</Form.Item>
</div>
</div>
</div>
<div className="flex justify-end mt-6">
<Button type="primary" onClick={handleSaveSystemSettings}>
</Button>
</div>
</Form>
</div> </div>
</div> </div>
); );

View File

@@ -28,3 +28,13 @@ export function deleteModelByIdUsingDelete(id: string | number) {
return del(`/api/models/${id}`); return del(`/api/models/${id}`);
} }
// 获取系统参数列表
export function getSysParamList() {
return get('/api/sys-param/list');
}
// 更新系统参数值
export const updateSysParamValue = async (params: { id: string; paramValue: string }) => {
return put(`/api/sys-param/${params.id}`, params);
};

View File

@@ -23,8 +23,8 @@ CREATE TABLE IF NOT EXISTS t_sys_param
id VARCHAR(36) PRIMARY KEY COMMENT '主键ID', id VARCHAR(36) PRIMARY KEY COMMENT '主键ID',
param_key VARCHAR(100) NOT NULL COMMENT '设置项键', param_key VARCHAR(100) NOT NULL COMMENT '设置项键',
param_value TEXT NOT NULL COMMENT '设置项值', param_value TEXT NOT NULL COMMENT '设置项值',
param_type VARCHAR(50) DEFAULT 'string' COMMENT '设置项类型( string、integer、boolean)', param_type VARCHAR(50) DEFAULT 'string' COMMENT '设置项类型( string、number、boolean 三种类型',
option_list TEXT COMMENT '选项列表(如 JSON 格式,仅对 enum 类型有效)', option_list TEXT COMMENT '选项列表(逗号分隔,仅对 enum 类型有效)',
description VARCHAR(255) DEFAULT '' COMMENT '设置项描述', description VARCHAR(255) DEFAULT '' COMMENT '设置项描述',
is_built_in TINYINT DEFAULT 0 COMMENT '是否内置:1-是,0-否', is_built_in TINYINT DEFAULT 0 COMMENT '是否内置:1-是,0-否',
can_modify TINYINT DEFAULT 1 COMMENT '是否可修改:1-可修改,0-不可修改', can_modify TINYINT DEFAULT 1 COMMENT '是否可修改:1-可修改,0-不可修改',
@@ -37,10 +37,14 @@ CREATE TABLE IF NOT EXISTS t_sys_param
) ENGINE = InnoDB ) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='设置管理表'; DEFAULT CHARSET = utf8mb4 COMMENT ='设置管理表';
insert ignore into t_sys_param (id, param_key, param_value, param_type, option_list, description, is_built_in, can_modify, insert ignore into t_sys_param (id, param_key, param_value, param_type, option_list, description, is_built_in,
can_modify,
is_enabled, created_by, updated_by) is_enabled, created_by, updated_by)
values ('1', 'sys.knowledge.base.count', '200', 'integer', '', '知识库最大数量', 1, 1, 1, 'system', 'system'), values ('1', 'sys.knowledge.base.count', '200', 'number', '10,200,500', '知识库最大数量', 1, 1, 1, 'system', 'system'),
('2', 'SEARCH_API', 'tavily', 'string', '', 'deer-flow使用的搜索引擎', 1, 1, 1, 'system', 'system'), ('2', 'SEARCH_API', 'tavily', 'string', '', 'deer-flow使用的搜索引擎', 1, 1, 1, 'system', 'system'),
('3', 'TAVILY_API_KEY', 'tvly-dev-xxx', 'string', '', 'deer-flow使用的搜索引擎所需的apiKey', 1, 1, 1, 'system', 'system'), ('3', 'TAVILY_API_KEY', 'tvly-dev-xxx', 'string', '', 'deer-flow使用的搜索引擎所需的apiKey', 1, 1, 1, 'system',
('4', 'BRAVE_SEARCH_API_KEY', 'api-xxx', 'string', '', 'deer-flow使用的搜索引擎所需的apiKey', 1, 1, 1, 'system', 'system'), 'system'),
('5', 'JINA_API_KEY', '', 'string', '', 'deer-flow使用的JINA搜索引擎所需的apiKey', 1, 1, 1, 'system', 'system'); ('4', 'BRAVE_SEARCH_API_KEY', 'api-xxx', 'string', '', 'deer-flow使用的搜索引擎所需的apiKey', 1, 1, 1, 'system',
'system'),
('5', 'JINA_API_KEY', '', 'string', '', 'deer-flow使用的JINA搜索引擎所需的apiKey', 1, 1, 1, 'system', 'system'),
('6', 'test_bool', 'true', 'boolean', '', '测试布尔值', 1, 1, 1, 'system', 'system');