feat: add inspection object resource workflow
This commit is contained in:
312
server/db.ts
312
server/db.ts
@@ -6,7 +6,9 @@ import {
|
||||
users, InsertUser,
|
||||
vehicleSeries, InsertVehicleSeries, VehicleSeries,
|
||||
vehicleModels, InsertVehicleModel, VehicleModel,
|
||||
inspectionObjects, InsertInspectionObject, InspectionObject,
|
||||
modelCadFiles, InsertModelCadFile, ModelCadFile,
|
||||
modelCadInspectionObjects,
|
||||
modelPointcloudFiles, InsertModelPointcloudFile, ModelPointcloudFile,
|
||||
taskCadFiles, InsertTaskCadFile, TaskCadFile,
|
||||
inspectionTasks, InsertInspectionTask, InspectionTask,
|
||||
@@ -206,6 +208,192 @@ export async function deleteVehicleModel(id: number): Promise<void> {
|
||||
await db.delete(vehicleModels).where(eq(vehicleModels.id, id));
|
||||
}
|
||||
|
||||
// ─── 检测对象与默认输入方案 ──────────────────────────────────────────────────
|
||||
|
||||
export type InspectionObjectWithConfiguration = InspectionObject & {
|
||||
defaultPointcloudName: string | null;
|
||||
defaultCadName: string | null;
|
||||
configurationState: "missing_pointcloud" | "missing_cad" | "ready_draft" | "active";
|
||||
taskReferenceCount: number;
|
||||
pointcloudReferenceCount: number;
|
||||
cadReferenceCount: number;
|
||||
canDelete: boolean;
|
||||
};
|
||||
|
||||
export type InspectionObjectReferenceCounts = {
|
||||
taskReferenceCount: number;
|
||||
pointcloudReferenceCount: number;
|
||||
cadReferenceCount: number;
|
||||
};
|
||||
|
||||
export class InspectionObjectReferencedError extends Error {
|
||||
constructor(public readonly references: InspectionObjectReferenceCounts) {
|
||||
const parts = [
|
||||
references.taskReferenceCount > 0 ? `任务 ${references.taskReferenceCount} 个` : null,
|
||||
references.pointcloudReferenceCount > 0 ? `点云 ${references.pointcloudReferenceCount} 个` : null,
|
||||
references.cadReferenceCount > 0 ? `CAD ${references.cadReferenceCount} 个` : null,
|
||||
].filter(Boolean);
|
||||
super(`检测对象仍被以下数据引用:${parts.join("、")}。请先处理关联数据后再删除。`);
|
||||
this.name = "InspectionObjectReferencedError";
|
||||
}
|
||||
}
|
||||
|
||||
export function assertInspectionObjectDeletable(references: InspectionObjectReferenceCounts): void {
|
||||
if (references.taskReferenceCount + references.pointcloudReferenceCount + references.cadReferenceCount > 0) {
|
||||
throw new InspectionObjectReferencedError(references);
|
||||
}
|
||||
}
|
||||
|
||||
export async function listInspectionObjects(modelId: number, activeOnly = false): Promise<InspectionObjectWithConfiguration[]> {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
const conditions = [eq(inspectionObjects.modelId, modelId)];
|
||||
if (activeOnly) conditions.push(eq(inspectionObjects.status, "active"));
|
||||
const rows = await db.select().from(inspectionObjects).where(and(...conditions)).orderBy(inspectionObjects.createdAt);
|
||||
if (rows.length === 0) return [];
|
||||
const objectIds = rows.map(row => row.id);
|
||||
const pointclouds = await db.select().from(modelPointcloudFiles).where(eq(modelPointcloudFiles.modelId, modelId));
|
||||
const cadFiles = await db.select().from(modelCadFiles).where(eq(modelCadFiles.modelId, modelId));
|
||||
const taskReferences = await db.select({
|
||||
inspectionObjectId: inspectionTasks.inspectionObjectId,
|
||||
count: sql<number>`count(*)`,
|
||||
}).from(inspectionTasks).where(inArray(inspectionTasks.inspectionObjectId, objectIds)).groupBy(inspectionTasks.inspectionObjectId);
|
||||
const cadReferences = await db.select({
|
||||
inspectionObjectId: modelCadInspectionObjects.inspectionObjectId,
|
||||
count: sql<number>`count(*)`,
|
||||
}).from(modelCadInspectionObjects).where(inArray(modelCadInspectionObjects.inspectionObjectId, objectIds)).groupBy(modelCadInspectionObjects.inspectionObjectId);
|
||||
const pcById = new Map(pointclouds.map(row => [row.id, row]));
|
||||
const cadById = new Map(cadFiles.map(row => [row.id, row]));
|
||||
const taskCountByObjectId = new Map(taskReferences.map(row => [row.inspectionObjectId, Number(row.count)]));
|
||||
const cadCountByObjectId = new Map(cadReferences.map(row => [row.inspectionObjectId, Number(row.count)]));
|
||||
const pointcloudCountByObjectId = new Map<number, number>();
|
||||
for (const pointcloud of pointclouds) {
|
||||
if (pointcloud.inspectionObjectId != null) {
|
||||
pointcloudCountByObjectId.set(pointcloud.inspectionObjectId, (pointcloudCountByObjectId.get(pointcloud.inspectionObjectId) ?? 0) + 1);
|
||||
}
|
||||
}
|
||||
return rows.map(row => {
|
||||
const pc = row.defaultModelPointcloudId ? pcById.get(row.defaultModelPointcloudId) : undefined;
|
||||
const cad = row.defaultModelCadFileId ? cadById.get(row.defaultModelCadFileId) : undefined;
|
||||
const hasPc = pc?.inspectionObjectId === row.id && pc.status === "available";
|
||||
const hasCad = cad?.status === "available";
|
||||
const taskReferenceCount = taskCountByObjectId.get(row.id) ?? 0;
|
||||
const pointcloudReferenceCount = pointcloudCountByObjectId.get(row.id) ?? 0;
|
||||
const cadReferenceCount = cadCountByObjectId.get(row.id) ?? 0;
|
||||
return {
|
||||
...row,
|
||||
defaultPointcloudName: pc?.name ?? null,
|
||||
defaultCadName: cad?.name ?? null,
|
||||
configurationState: !hasPc ? "missing_pointcloud" : !hasCad ? "missing_cad" : row.status === "active" ? "active" : "ready_draft",
|
||||
taskReferenceCount,
|
||||
pointcloudReferenceCount,
|
||||
cadReferenceCount,
|
||||
canDelete: taskReferenceCount + pointcloudReferenceCount + cadReferenceCount === 0,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export async function getInspectionObjectById(id: number): Promise<InspectionObject | undefined> {
|
||||
const db = await getDb();
|
||||
if (!db) return undefined;
|
||||
const [row] = await db.select().from(inspectionObjects).where(eq(inspectionObjects.id, id));
|
||||
return row;
|
||||
}
|
||||
|
||||
export async function createInspectionObject(data: InsertInspectionObject): Promise<InspectionObject> {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
const result = await db.insert(inspectionObjects).values(data);
|
||||
return (await getInspectionObjectById(getMysqlInsertId(result)))!;
|
||||
}
|
||||
|
||||
export async function updateInspectionObject(id: number, data: Partial<InsertInspectionObject>): Promise<InspectionObject> {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
await db.update(inspectionObjects).set(data).where(eq(inspectionObjects.id, id));
|
||||
return (await getInspectionObjectById(id))!;
|
||||
}
|
||||
|
||||
export async function deleteInspectionObject(id: number): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
await db.transaction(async tx => {
|
||||
const [object] = await tx.select({ id: inspectionObjects.id }).from(inspectionObjects)
|
||||
.where(eq(inspectionObjects.id, id)).limit(1).for("update");
|
||||
if (!object) throw new Error("检测对象不存在");
|
||||
|
||||
const taskRows = await tx.select({ id: inspectionTasks.id }).from(inspectionTasks)
|
||||
.where(eq(inspectionTasks.inspectionObjectId, id)).for("update");
|
||||
const pointcloudRows = await tx.select({ id: modelPointcloudFiles.id }).from(modelPointcloudFiles)
|
||||
.where(eq(modelPointcloudFiles.inspectionObjectId, id)).for("update");
|
||||
const cadRows = await tx.select({ cadFileId: modelCadInspectionObjects.cadFileId }).from(modelCadInspectionObjects)
|
||||
.where(eq(modelCadInspectionObjects.inspectionObjectId, id)).for("update");
|
||||
assertInspectionObjectDeletable({
|
||||
taskReferenceCount: taskRows.length,
|
||||
pointcloudReferenceCount: pointcloudRows.length,
|
||||
cadReferenceCount: cadRows.length,
|
||||
});
|
||||
await tx.delete(inspectionObjects).where(eq(inspectionObjects.id, id));
|
||||
});
|
||||
}
|
||||
|
||||
export async function cadAppliesToInspectionObject(cadFileId: number, inspectionObjectId: number): Promise<boolean> {
|
||||
const db = await getDb();
|
||||
if (!db) return false;
|
||||
const [row] = await db.select().from(modelCadInspectionObjects).where(and(
|
||||
eq(modelCadInspectionObjects.cadFileId, cadFileId),
|
||||
eq(modelCadInspectionObjects.inspectionObjectId, inspectionObjectId),
|
||||
)).limit(1);
|
||||
return Boolean(row);
|
||||
}
|
||||
|
||||
export async function setCadInspectionObjects(cadFileId: number, inspectionObjectIds: number[]): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
await db.transaction(async tx => {
|
||||
const lockedObjects = await tx.select({ id: inspectionObjects.id }).from(inspectionObjects)
|
||||
.where(inArray(inspectionObjects.id, inspectionObjectIds)).for("share");
|
||||
if (lockedObjects.length !== new Set(inspectionObjectIds).size) throw new Error("Inspection object not found");
|
||||
await tx.delete(modelCadInspectionObjects).where(eq(modelCadInspectionObjects.cadFileId, cadFileId));
|
||||
if (inspectionObjectIds.length > 0) {
|
||||
await tx.insert(modelCadInspectionObjects).values(inspectionObjectIds.map(inspectionObjectId => ({ cadFileId, inspectionObjectId })));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function listCadInspectionObjectIds(cadFileIds: number[]): Promise<Record<number, number[]>> {
|
||||
const db = await getDb();
|
||||
if (!db || cadFileIds.length === 0) return {};
|
||||
const rows = await db.select().from(modelCadInspectionObjects).where(inArray(modelCadInspectionObjects.cadFileId, cadFileIds));
|
||||
return rows.reduce<Record<number, number[]>>((result, row) => {
|
||||
(result[row.cadFileId] ??= []).push(row.inspectionObjectId);
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export async function configureInspectionObject(input: {
|
||||
id: number;
|
||||
defaultModelPointcloudId: number;
|
||||
defaultModelCadFileId: number;
|
||||
active: boolean;
|
||||
}): Promise<InspectionObject> {
|
||||
const object = await getInspectionObjectById(input.id);
|
||||
const pc = await getModelPointcloudFileById(input.defaultModelPointcloudId);
|
||||
const cad = await getModelCadFileById(input.defaultModelCadFileId);
|
||||
if (!object || !pc || !cad) throw new Error("Configuration resource not found");
|
||||
if (pc.modelId !== object.modelId || pc.inspectionObjectId !== object.id || pc.status !== "available") {
|
||||
throw new Error("Pointcloud is incompatible with inspection object");
|
||||
}
|
||||
if (cad.modelId !== object.modelId || cad.status !== "available" || !await cadAppliesToInspectionObject(cad.id, object.id)) {
|
||||
throw new Error("CAD is incompatible with inspection object");
|
||||
}
|
||||
return updateInspectionObject(object.id, {
|
||||
defaultModelPointcloudId: pc.id,
|
||||
defaultModelCadFileId: cad.id,
|
||||
status: input.active ? "active" : "draft",
|
||||
});
|
||||
}
|
||||
|
||||
// ─── 车型 CAD 文件 ─────────────────────────────────────────────────────────────
|
||||
|
||||
export async function createModelCadFile(data: InsertModelCadFile): Promise<ModelCadFile> {
|
||||
@@ -224,6 +412,18 @@ export async function listModelCadFiles(modelId: number): Promise<ModelCadFile[]
|
||||
.orderBy(desc(modelCadFiles.createdAt));
|
||||
}
|
||||
|
||||
export async function listModelCadFilesForInspectionObject(modelId: number, inspectionObjectId: number): Promise<ModelCadFile[]> {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
return db.select({
|
||||
id: modelCadFiles.id, modelId: modelCadFiles.modelId, name: modelCadFiles.name,
|
||||
format: modelCadFiles.format, fileSize: modelCadFiles.fileSize, filePath: modelCadFiles.filePath,
|
||||
contentScope: modelCadFiles.contentScope, status: modelCadFiles.status, createdAt: modelCadFiles.createdAt,
|
||||
}).from(modelCadFiles).innerJoin(modelCadInspectionObjects, eq(modelCadInspectionObjects.cadFileId, modelCadFiles.id))
|
||||
.where(and(eq(modelCadFiles.modelId, modelId), eq(modelCadFiles.status, "available"), eq(modelCadInspectionObjects.inspectionObjectId, inspectionObjectId)))
|
||||
.orderBy(desc(modelCadFiles.createdAt));
|
||||
}
|
||||
|
||||
export async function getModelCadFileById(id: number): Promise<ModelCadFile | undefined> {
|
||||
const db = await getDb();
|
||||
if (!db) return undefined;
|
||||
@@ -234,6 +434,16 @@ export async function getModelCadFileById(id: number): Promise<ModelCadFile | un
|
||||
export async function deleteModelCadFile(id: number): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) return;
|
||||
const [defaultUse] = await db.select({ id: inspectionObjects.id }).from(inspectionObjects)
|
||||
.where(eq(inspectionObjects.defaultModelCadFileId, id)).limit(1);
|
||||
if (defaultUse) throw new Error("Resource is used as default input");
|
||||
const [historicalUse] = await db.select({ id: taskCadFiles.id }).from(taskCadFiles)
|
||||
.where(eq(taskCadFiles.sourceModelCadFileId, id)).limit(1);
|
||||
if (historicalUse) {
|
||||
await db.update(modelCadFiles).set({ status: "disabled" }).where(eq(modelCadFiles.id, id));
|
||||
return;
|
||||
}
|
||||
await db.delete(modelCadInspectionObjects).where(eq(modelCadInspectionObjects.cadFileId, id));
|
||||
await db.delete(modelCadFiles).where(eq(modelCadFiles.id, id));
|
||||
}
|
||||
|
||||
@@ -242,9 +452,16 @@ export async function deleteModelCadFile(id: number): Promise<void> {
|
||||
export async function createModelPointcloudFile(data: InsertModelPointcloudFile): Promise<ModelPointcloudFile> {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
const result = await db.insert(modelPointcloudFiles).values(data);
|
||||
const [row] = await db.select().from(modelPointcloudFiles).where(eq(modelPointcloudFiles.id, getMysqlInsertId(result)));
|
||||
return row;
|
||||
return db.transaction(async tx => {
|
||||
if (data.inspectionObjectId != null) {
|
||||
const [object] = await tx.select({ id: inspectionObjects.id }).from(inspectionObjects)
|
||||
.where(eq(inspectionObjects.id, data.inspectionObjectId)).limit(1).for("share");
|
||||
if (!object) throw new Error("Inspection object not found");
|
||||
}
|
||||
const result = await tx.insert(modelPointcloudFiles).values(data);
|
||||
const [row] = await tx.select().from(modelPointcloudFiles).where(eq(modelPointcloudFiles.id, getMysqlInsertId(result)));
|
||||
return row;
|
||||
});
|
||||
}
|
||||
|
||||
const modelMetadataBackfills = new Map<number, Promise<ModelPointcloudFile>>();
|
||||
@@ -283,6 +500,26 @@ export async function listModelPointcloudFiles(modelId: number): Promise<ModelPo
|
||||
return Promise.all(rows.map(backfillModelPointcloudMetadata));
|
||||
}
|
||||
|
||||
export async function listModelPointcloudFilesForInspectionObject(modelId: number, inspectionObjectId: number): Promise<ModelPointcloudFile[]> {
|
||||
const rows = await listModelPointcloudFiles(modelId);
|
||||
return rows.filter(row => row.inspectionObjectId === inspectionObjectId && row.status === "available");
|
||||
}
|
||||
|
||||
export async function assignModelPointcloudInspectionObject(id: number, inspectionObjectId: number): Promise<ModelPointcloudFile> {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
return db.transaction(async tx => {
|
||||
const [object] = await tx.select().from(inspectionObjects)
|
||||
.where(eq(inspectionObjects.id, inspectionObjectId)).limit(1).for("share");
|
||||
const [file] = await tx.select().from(modelPointcloudFiles)
|
||||
.where(eq(modelPointcloudFiles.id, id)).limit(1).for("update");
|
||||
if (!file || !object || file.modelId !== object.modelId) throw new Error("Inspection object does not belong to file model");
|
||||
await tx.update(modelPointcloudFiles).set({ inspectionObjectId }).where(eq(modelPointcloudFiles.id, id));
|
||||
const [updated] = await tx.select().from(modelPointcloudFiles).where(eq(modelPointcloudFiles.id, id));
|
||||
return updated;
|
||||
});
|
||||
}
|
||||
|
||||
export async function getModelPointcloudFileById(id: number): Promise<ModelPointcloudFile | undefined> {
|
||||
const db = await getDb();
|
||||
if (!db) return undefined;
|
||||
@@ -323,6 +560,15 @@ export async function updateModelPointcloudFileName(
|
||||
export async function deleteModelPointcloudFile(id: number): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) return;
|
||||
const [defaultUse] = await db.select({ id: inspectionObjects.id }).from(inspectionObjects)
|
||||
.where(eq(inspectionObjects.defaultModelPointcloudId, id)).limit(1);
|
||||
if (defaultUse) throw new Error("Resource is used as default input");
|
||||
const [historicalUse] = await db.select({ id: taskPointcloudFiles.id }).from(taskPointcloudFiles)
|
||||
.where(eq(taskPointcloudFiles.sourceModelPointcloudId, id)).limit(1);
|
||||
if (historicalUse) {
|
||||
await db.update(modelPointcloudFiles).set({ status: "disabled" }).where(eq(modelPointcloudFiles.id, id));
|
||||
return;
|
||||
}
|
||||
await db.delete(modelPointcloudFiles).where(eq(modelPointcloudFiles.id, id));
|
||||
}
|
||||
|
||||
@@ -380,6 +626,9 @@ export async function linkTaskCadFilesFromModel(taskId: number, modelCadFileIds:
|
||||
if (source.modelId !== task.modelId) {
|
||||
throw new Error("Cannot link CAD file from another model");
|
||||
}
|
||||
if (!task.inspectionObjectId || source.status !== "available" || !await cadAppliesToInspectionObject(source.id, task.inspectionObjectId)) {
|
||||
throw new Error("Cannot link CAD file from another inspection object");
|
||||
}
|
||||
|
||||
const record = await createTaskCadFile({
|
||||
taskId,
|
||||
@@ -407,6 +656,49 @@ export async function createInspectionTask(data: InsertInspectionTask): Promise<
|
||||
return row;
|
||||
}
|
||||
|
||||
export async function createInspectionTaskWithDefaults(data: InsertInspectionTask & { inspectionObjectId: number }): Promise<{
|
||||
task: InspectionTask;
|
||||
pointcloudFile: TaskPointcloudFile;
|
||||
cadFile: TaskCadFile;
|
||||
}> {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
return db.transaction(async tx => {
|
||||
const [object] = await tx.select().from(inspectionObjects)
|
||||
.where(eq(inspectionObjects.id, data.inspectionObjectId)).limit(1).for("share");
|
||||
if (!object || object.modelId !== data.modelId || object.status !== "active") throw new Error("Inspection object is not active for model");
|
||||
if (!object.defaultModelPointcloudId || !object.defaultModelCadFileId) throw new Error("Default input scheme is incomplete");
|
||||
const [pointcloud] = await tx.select().from(modelPointcloudFiles)
|
||||
.where(eq(modelPointcloudFiles.id, object.defaultModelPointcloudId)).limit(1);
|
||||
const [cad] = await tx.select().from(modelCadFiles)
|
||||
.where(eq(modelCadFiles.id, object.defaultModelCadFileId)).limit(1);
|
||||
const [cadApplication] = await tx.select({ cadFileId: modelCadInspectionObjects.cadFileId }).from(modelCadInspectionObjects).where(and(
|
||||
eq(modelCadInspectionObjects.cadFileId, object.defaultModelCadFileId),
|
||||
eq(modelCadInspectionObjects.inspectionObjectId, object.id),
|
||||
)).limit(1);
|
||||
if (!pointcloud || pointcloud.inspectionObjectId !== object.id || pointcloud.status !== "available") throw new Error("Default pointcloud is invalid");
|
||||
if (!cad || cad.modelId !== object.modelId || cad.status !== "available" || !cadApplication) throw new Error("Default CAD is invalid");
|
||||
|
||||
const taskInsert = await tx.insert(inspectionTasks).values(data);
|
||||
const taskId = getMysqlInsertId(taskInsert);
|
||||
const pointcloudInsert = await tx.insert(taskPointcloudFiles).values({
|
||||
taskId, name: pointcloud.name, fileSize: pointcloud.fileSize, filePath: pointcloud.filePath,
|
||||
source: "model_library", sourceModelPointcloudId: pointcloud.id,
|
||||
metadataStatus: pointcloud.metadataStatus, pointCount: pointcloud.pointCount,
|
||||
pointAttributes: pointcloud.pointAttributes, pointDataFormat: pointcloud.pointDataFormat,
|
||||
bytesPerPoint: pointcloud.bytesPerPoint, metadataError: pointcloud.metadataError,
|
||||
});
|
||||
const cadInsert = await tx.insert(taskCadFiles).values({
|
||||
taskId, name: cad.name, format: cad.format, fileSize: cad.fileSize, filePath: cad.filePath,
|
||||
source: "model_library", sourceModelCadFileId: cad.id,
|
||||
});
|
||||
const [task] = await tx.select().from(inspectionTasks).where(eq(inspectionTasks.id, taskId));
|
||||
const [pointcloudFile] = await tx.select().from(taskPointcloudFiles).where(eq(taskPointcloudFiles.id, getMysqlInsertId(pointcloudInsert)));
|
||||
const [cadFile] = await tx.select().from(taskCadFiles).where(eq(taskCadFiles.id, getMysqlInsertId(cadInsert)));
|
||||
return { task, pointcloudFile, cadFile };
|
||||
});
|
||||
}
|
||||
|
||||
export async function findInspectionTaskByModelAndName(modelId: number, name: string): Promise<InspectionTask | undefined> {
|
||||
const db = await getDb();
|
||||
if (!db) return undefined;
|
||||
@@ -482,6 +774,17 @@ export async function updateInspectionTask(id: number, data: Partial<InsertInspe
|
||||
await db.update(inspectionTasks).set(data).where(eq(inspectionTasks.id, id));
|
||||
}
|
||||
|
||||
export async function classifyInspectionTask(id: number, inspectionObjectId: number): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
await db.transaction(async tx => {
|
||||
const [object] = await tx.select({ id: inspectionObjects.id }).from(inspectionObjects)
|
||||
.where(eq(inspectionObjects.id, inspectionObjectId)).limit(1).for("share");
|
||||
if (!object) throw new Error("Inspection object not found");
|
||||
await tx.update(inspectionTasks).set({ inspectionObjectId }).where(eq(inspectionTasks.id, id));
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteInspectionTask(id: number): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) return;
|
||||
@@ -631,6 +934,9 @@ export async function linkTaskPointcloudFilesFromModel(
|
||||
if (source.modelId !== task.modelId) {
|
||||
throw new Error("Cannot link pointcloud file from another model");
|
||||
}
|
||||
if (!task.inspectionObjectId || source.inspectionObjectId !== task.inspectionObjectId || source.status !== "available") {
|
||||
throw new Error("Cannot link pointcloud file from another inspection object");
|
||||
}
|
||||
|
||||
const record = await createTaskPointcloudFile({
|
||||
taskId,
|
||||
|
||||
Reference in New Issue
Block a user