refactor(dataset): 优化数据集路径管理和关联关系处理

- 移除Dataset类中initCreateParam方法的parentPath参数
- 简化handleParentChange方法中的路径构建逻辑
- 更新错误消息将"子数据集"改为"关联数据集"
- 修改前端界面将"父数据集"相关术语统一为"关联数据集"
- 在导入配置组件中添加类型定义和改进文件处理逻辑
- 限制数据源选项排除COLLECTION类型避免错误选择
This commit is contained in:
2026-01-30 16:48:39 +08:00
parent accaa47a83
commit bd37858ccc
7 changed files with 101 additions and 71 deletions

View File

@@ -73,7 +73,7 @@ public class DatasetApplicationService {
Dataset dataset = DatasetConverter.INSTANCE.convertToDataset(createDatasetRequest); Dataset dataset = DatasetConverter.INSTANCE.convertToDataset(createDatasetRequest);
Dataset parentDataset = resolveParentDataset(createDatasetRequest.getParentDatasetId(), dataset.getId()); Dataset parentDataset = resolveParentDataset(createDatasetRequest.getParentDatasetId(), dataset.getId());
dataset.setParentDatasetId(parentDataset == null ? null : parentDataset.getId()); dataset.setParentDatasetId(parentDataset == null ? null : parentDataset.getId());
dataset.initCreateParam(datasetBasePath, parentDataset == null ? null : parentDataset.getPath()); dataset.initCreateParam(datasetBasePath);
// 处理标签 // 处理标签
Set<Tag> processedTags = Optional.ofNullable(createDatasetRequest.getTags()) Set<Tag> processedTags = Optional.ofNullable(createDatasetRequest.getTags())
.filter(CollectionUtils::isNotEmpty) .filter(CollectionUtils::isNotEmpty)
@@ -291,7 +291,9 @@ public class DatasetApplicationService {
private void handleParentChange(Dataset dataset, String parentDatasetId) { private void handleParentChange(Dataset dataset, String parentDatasetId) {
String normalized = normalizeParentId(parentDatasetId); String normalized = normalizeParentId(parentDatasetId);
if (Objects.equals(dataset.getParentDatasetId(), normalized)) { String expectedPath = buildDatasetPath(datasetBasePath, dataset.getId());
if (Objects.equals(dataset.getParentDatasetId(), normalized)
&& Objects.equals(dataset.getPath(), expectedPath)) {
return; return;
} }
long childCount = datasetRepository.countByParentId(dataset.getId()); long childCount = datasetRepository.countByParentId(dataset.getId());
@@ -299,8 +301,7 @@ public class DatasetApplicationService {
throw BusinessException.of(DataManagementErrorCode.DATASET_HAS_CHILDREN); throw BusinessException.of(DataManagementErrorCode.DATASET_HAS_CHILDREN);
} }
Dataset parent = normalized == null ? null : resolveParentDataset(normalized, dataset.getId()); Dataset parent = normalized == null ? null : resolveParentDataset(normalized, dataset.getId());
String newPath = buildDatasetPath(parent == null ? datasetBasePath : parent.getPath(), dataset.getId()); moveDatasetPath(dataset, expectedPath);
moveDatasetPath(dataset, newPath);
dataset.setParentDatasetId(parent == null ? null : parent.getId()); dataset.setParentDatasetId(parent == null ? null : parent.getId());
} }

View File

@@ -114,9 +114,9 @@ public class Dataset extends BaseEntity<String> {
this.updatedAt = LocalDateTime.now(); this.updatedAt = LocalDateTime.now();
} }
public void initCreateParam(String datasetBasePath, String parentPath) { public void initCreateParam(String datasetBasePath) {
this.id = UUID.randomUUID().toString(); this.id = UUID.randomUUID().toString();
String basePath = normalizeBasePath(parentPath != null && !parentPath.isBlank() ? parentPath : datasetBasePath); String basePath = normalizeBasePath(datasetBasePath);
this.path = basePath + File.separator + this.id; this.path = basePath + File.separator + this.id;
if (this.status == null) { if (this.status == null) {
this.status = DatasetStatusType.DRAFT; this.status = DatasetStatusType.DRAFT;

View File

@@ -42,9 +42,9 @@ public enum DataManagementErrorCode implements ErrorCode {
*/ */
DIRECTORY_NOT_FOUND("data_management.0007", "目录不存在"), DIRECTORY_NOT_FOUND("data_management.0007", "目录不存在"),
/** /**
* 存在数据集 * 存在关联数据集
*/ */
DATASET_HAS_CHILDREN("data_management.0008", "存在数据集,禁止删除或移动"), DATASET_HAS_CHILDREN("data_management.0008", "存在关联数据集,禁止删除或移动"),
/** /**
* 数据集文件不存在 * 数据集文件不存在
*/ */

View File

@@ -96,7 +96,7 @@ export default function EditDataset({
<BasicInformation <BasicInformation
data={newDataset} data={newDataset}
setData={setNewDataset} setData={setNewDataset}
hidden={["datasetType"]} hidden={["datasetType", "dataSource"]}
/> />
</Form> </Form>
</Modal> </Modal>

View File

@@ -74,7 +74,7 @@ export default function BasicInformation({
value: dataset.id, value: dataset.id,
})); }));
setParentDatasetOptions([ setParentDatasetOptions([
{ label: "数据集", value: "" }, { label: "无关联数据集", value: "" },
...options, ...options,
]); ]);
} catch (error) { } catch (error) {
@@ -102,11 +102,11 @@ export default function BasicInformation({
</Form.Item> </Form.Item>
)} )}
{!hidden.includes("parentDatasetId") && ( {!hidden.includes("parentDatasetId") && (
<Form.Item name="parentDatasetId" label="数据集"> <Form.Item name="parentDatasetId" label="关联数据集">
<Select <Select
className="w-full" className="w-full"
options={parentDatasetOptions} options={parentDatasetOptions}
placeholder="选择数据集(仅支持一层)" placeholder="选择关联数据集(仅支持一层)"
/> />
</Form.Item> </Form.Item>
)} )}

View File

@@ -127,7 +127,7 @@ export default function DatasetDetail() {
if (!dataset?.parentDatasetId) { if (!dataset?.parentDatasetId) {
items.push({ items.push({
key: "children", key: "children",
label: "数据集", label: "关联数据集",
}); });
} }
return items; return items;
@@ -266,7 +266,7 @@ export default function DatasetDetail() {
? [ ? [
{ {
key: "create-child", key: "create-child",
label: "创建数据集", label: "创建关联数据集",
icon: <PlusOutlined />, icon: <PlusOutlined />,
onClick: handleCreateChildDataset, onClick: handleCreateChildDataset,
}, },
@@ -415,7 +415,7 @@ export default function DatasetDetail() {
{activeTab === "children" && ( {activeTab === "children" && (
<div className="pt-4"> <div className="pt-4">
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
<h2 className="text-base font-semibold"></h2> <h2 className="text-base font-semibold"></h2>
<span className="text-xs text-gray-500"> <span className="text-xs text-gray-500">
{childDatasets.length} {childDatasets.length}
</span> </span>
@@ -426,7 +426,7 @@ export default function DatasetDetail() {
dataSource={childDatasets} dataSource={childDatasets}
loading={childDatasetsLoading} loading={childDatasetsLoading}
pagination={false} pagination={false}
locale={{ emptyText: "暂无数据集" }} locale={{ emptyText: "暂无关联数据集" }}
/> />
</div> </div>
)} )}

View File

@@ -13,11 +13,11 @@ import Dragger from "antd/es/upload/Dragger";
* @param file 原始文件 * @param file 原始文件
* @returns 分割后的文件列表,每行一个文件 * @returns 分割后的文件列表,每行一个文件
*/ */
async function splitFileByLines(file: UploadFile): Promise<UploadFile[]> { async function splitFileByLines(file: UploadFile): Promise<UploadFile[]> {
const originFile = (file as any).originFileObj || file; const originFile = file.originFileObj ?? file;
if (!originFile || typeof originFile.text !== "function") { if (!(originFile instanceof File) || typeof originFile.text !== "function") {
return [file]; return [file];
} }
const text = await originFile.text(); const text = await originFile.text();
if (!text) return [file]; if (!text) return [file];
@@ -36,17 +36,37 @@ async function splitFileByLines(file: UploadFile): Promise<UploadFile[]> {
const newFileName = `${baseName}_${String(index + 1).padStart(padLength, "0")}${ext}`; const newFileName = `${baseName}_${String(index + 1).padStart(padLength, "0")}${ext}`;
const blob = new Blob([line], { type: "text/plain" }); const blob = new Blob([line], { type: "text/plain" });
const newFile = new File([blob], newFileName, { type: "text/plain" }); const newFile = new File([blob], newFileName, { type: "text/plain" });
return { return {
uid: `${file.uid}-${index}`, uid: `${file.uid}-${index}`,
name: newFileName, name: newFileName,
size: newFile.size, size: newFile.size,
type: "text/plain", type: "text/plain",
originFileObj: newFile as any, originFileObj: newFile as UploadFile["originFileObj"],
} as UploadFile; } as UploadFile;
}); });
} }
export default function ImportConfiguration({ type SelectOption = {
label: string;
value: string;
};
type CollectionTask = {
id: string;
name: string;
};
type ImportConfig = {
source: DataSource;
hasArchive: boolean;
splitByLine: boolean;
files?: UploadFile[];
dataSource?: string;
target?: DataSource;
[key: string]: unknown;
};
export default function ImportConfiguration({
data, data,
open, open,
onClose, onClose,
@@ -59,19 +79,23 @@ export default function ImportConfiguration({
updateEvent?: string; updateEvent?: string;
prefix?: string; prefix?: string;
}) { }) {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [collectionOptions, setCollectionOptions] = useState([]); const [collectionOptions, setCollectionOptions] = useState<SelectOption[]>([]);
const [importConfig, setImportConfig] = useState<any>({ const availableSourceOptions = dataSourceOptions.filter(
source: DataSource.UPLOAD, (option) => option.value !== DataSource.COLLECTION
hasArchive: true, );
splitByLine: false, const [importConfig, setImportConfig] = useState<ImportConfig>({
}); source: DataSource.UPLOAD,
hasArchive: true,
splitByLine: false,
});
const [currentPrefix, setCurrentPrefix] = useState<string>(""); const [currentPrefix, setCurrentPrefix] = useState<string>("");
// 本地上传文件相关逻辑 // 本地上传文件相关逻辑
const handleUpload = async (dataset: Dataset) => { const handleUpload = async (dataset: Dataset) => {
let filesToUpload = form.getFieldValue("files") || []; let filesToUpload =
(form.getFieldValue("files") as UploadFile[] | undefined) || [];
// 如果启用分行分割,处理文件 // 如果启用分行分割,处理文件
if (importConfig.splitByLine) { if (importConfig.splitByLine) {
@@ -83,14 +107,14 @@ export default function ImportConfiguration({
// 计算分片列表 // 计算分片列表
const sliceList = filesToUpload.map((file) => { const sliceList = filesToUpload.map((file) => {
const originFile = (file as any).originFileObj || file; const originFile = (file.originFileObj ?? file) as Blob;
const slices = sliceFile(originFile); const slices = sliceFile(originFile);
return { return {
originFile: originFile, // 传入真正的 File/Blob 对象 originFile: originFile, // 传入真正的 File/Blob 对象
slices, slices,
name: file.name, name: file.name,
size: originFile.size || 0, size: originFile.size || 0,
}; };
}); });
console.log("[ImportConfiguration] Uploading with currentPrefix:", currentPrefix); console.log("[ImportConfiguration] Uploading with currentPrefix:", currentPrefix);
@@ -111,10 +135,13 @@ export default function ImportConfiguration({
if (importConfig.source !== DataSource.COLLECTION) return; if (importConfig.source !== DataSource.COLLECTION) return;
try { try {
const res = await queryTasksUsingGet({ page: 0, size: 100 }); const res = await queryTasksUsingGet({ page: 0, size: 100 });
const options = res.data.content.map((task: any) => ({ const tasks = Array.isArray(res?.data?.content)
label: task.name, ? (res.data.content as CollectionTask[])
value: task.id, : [];
})); const options = tasks.map((task) => ({
label: task.name,
value: task.id,
}));
setCollectionOptions(options); setCollectionOptions(options);
} catch (error) { } catch (error) {
console.error("Error fetching collection tasks:", error); console.error("Error fetching collection tasks:", error);
@@ -123,13 +150,13 @@ export default function ImportConfiguration({
const resetState = () => { const resetState = () => {
console.log('[ImportConfiguration] resetState called, preserving currentPrefix:', currentPrefix); console.log('[ImportConfiguration] resetState called, preserving currentPrefix:', currentPrefix);
form.resetFields(); form.resetFields();
form.setFieldsValue({ files: null }); form.setFieldsValue({ files: null });
setImportConfig({ setImportConfig({
source: importConfig.source ? importConfig.source : DataSource.UPLOAD, source: DataSource.UPLOAD,
hasArchive: true, hasArchive: true,
splitByLine: false, splitByLine: false,
}); });
console.log('[ImportConfiguration] resetState done, currentPrefix still:', currentPrefix); console.log('[ImportConfiguration] resetState done, currentPrefix still:', currentPrefix);
}; };
@@ -196,12 +223,12 @@ export default function ImportConfiguration({
name="source" name="source"
rules={[{ required: true, message: "请选择数据源" }]} rules={[{ required: true, message: "请选择数据源" }]}
> >
<Radio.Group <Radio.Group
buttonStyle="solid" buttonStyle="solid"
options={dataSourceOptions} options={availableSourceOptions}
optionType="button" optionType="button"
/> />
</Form.Item> </Form.Item>
{importConfig?.source === DataSource.COLLECTION && ( {importConfig?.source === DataSource.COLLECTION && (
<Form.Item name="dataSource" label="归集任务" required> <Form.Item name="dataSource" label="归集任务" required>
<Select placeholder="请选择归集任务" options={collectionOptions} /> <Select placeholder="请选择归集任务" options={collectionOptions} />
@@ -277,12 +304,14 @@ export default function ImportConfiguration({
label="上传文件" label="上传文件"
name="files" name="files"
valuePropName="fileList" valuePropName="fileList"
getValueFromEvent={(e: any) => { getValueFromEvent={(
if (Array.isArray(e)) { event: { fileList?: UploadFile[] } | UploadFile[]
return e; ) => {
} if (Array.isArray(event)) {
return e && e.fileList; return event;
}} }
return event?.fileList;
}}
rules={[ rules={[
{ {
required: true, required: true,