222 lines
12 KiB
TypeScript
222 lines
12 KiB
TypeScript
import { int, mysqlEnum, mysqlTable, text, timestamp, varchar, bigint, json, uniqueIndex } from "drizzle-orm/mysql-core";
|
||
import type { PointAttribute } from "../shared/pointcloudMetadata";
|
||
|
||
// ─── 本地用户 ──────────────────────────────────────────────────────────────────
|
||
|
||
export const users = mysqlTable("users", {
|
||
id: int("id").autoincrement().primaryKey(),
|
||
username: varchar("username", { length: 64 }).notNull().unique(),
|
||
passwordHash: varchar("passwordHash", { length: 255 }),
|
||
name: text("name"),
|
||
email: varchar("email", { length: 320 }),
|
||
role: mysqlEnum("role", ["user", "admin", "super_admin"]).default("user").notNull(),
|
||
isActive: int("isActive").default(1).notNull(),
|
||
mustChangePassword: int("mustChangePassword").default(1).notNull(),
|
||
sessionVersion: int("sessionVersion").default(0).notNull(),
|
||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||
lastLoginAt: timestamp("lastLoginAt"),
|
||
});
|
||
|
||
export type User = typeof users.$inferSelect;
|
||
export type InsertUser = typeof users.$inferInsert;
|
||
|
||
// ─── 车系 ─────────────────────────────────────────────────────────────────────
|
||
// 例:复兴号、和谐号
|
||
|
||
export const vehicleSeries = mysqlTable("vehicle_series", {
|
||
id: int("id").autoincrement().primaryKey(),
|
||
name: varchar("name", { length: 100 }).notNull(),
|
||
description: text("description"),
|
||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||
});
|
||
|
||
export type VehicleSeries = typeof vehicleSeries.$inferSelect;
|
||
export type InsertVehicleSeries = typeof vehicleSeries.$inferInsert;
|
||
|
||
// ─── 车型 ─────────────────────────────────────────────────────────────────────
|
||
// 例:CR400AF,归属于某车系
|
||
|
||
export const vehicleModels = mysqlTable("vehicle_models", {
|
||
id: int("id").autoincrement().primaryKey(),
|
||
seriesId: int("seriesId").notNull(),
|
||
name: varchar("name", { length: 100 }).notNull(),
|
||
description: text("description"),
|
||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||
});
|
||
|
||
export type VehicleModel = typeof vehicleModels.$inferSelect;
|
||
export type InsertVehicleModel = typeof vehicleModels.$inferInsert;
|
||
|
||
// ─── 检测对象 ────────────────────────────────────────────────────────────────
|
||
// 车型下真正执行检测的对象,例如头车、中间车。默认资源只影响新建任务。
|
||
export const inspectionObjects = mysqlTable("inspection_objects", {
|
||
id: int("id").autoincrement().primaryKey(),
|
||
modelId: int("modelId").notNull(),
|
||
name: varchar("name", { length: 100 }).notNull(),
|
||
description: text("description"),
|
||
status: mysqlEnum("status", ["draft", "active"]).default("draft").notNull(),
|
||
defaultModelPointcloudId: int("defaultModelPointcloudId"),
|
||
defaultModelCadFileId: int("defaultModelCadFileId"),
|
||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||
}, (table) => ({
|
||
modelNameUnique: uniqueIndex("uk_inspection_objects_model_name").on(table.modelId, table.name),
|
||
}));
|
||
|
||
export type InspectionObject = typeof inspectionObjects.$inferSelect;
|
||
export type InsertInspectionObject = typeof inspectionObjects.$inferInsert;
|
||
|
||
// ─── 车型 CAD 文件 ─────────────────────────────────────────────────────────────
|
||
// 一个车型可关联多个 CAD 文件(车头、车身、转向架等),低频变更
|
||
|
||
export const modelCadFiles = mysqlTable("model_cad_files", {
|
||
id: int("id").autoincrement().primaryKey(),
|
||
modelId: int("modelId").notNull(),
|
||
name: varchar("name", { length: 255 }).notNull(),
|
||
format: varchar("format", { length: 32 }).notNull(),
|
||
fileSize: bigint("fileSize", { mode: "number" }).notNull(),
|
||
filePath: varchar("filePath", { length: 512 }).notNull(),
|
||
contentScope: mysqlEnum("contentScope", ["whole_vehicle"]).default("whole_vehicle").notNull(),
|
||
status: mysqlEnum("status", ["available", "disabled"]).default("available").notNull(),
|
||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||
});
|
||
|
||
export type ModelCadFile = typeof modelCadFiles.$inferSelect;
|
||
export type InsertModelCadFile = typeof modelCadFiles.$inferInsert;
|
||
|
||
export const modelCadInspectionObjects = mysqlTable("model_cad_inspection_objects", {
|
||
cadFileId: int("cadFileId").notNull(),
|
||
inspectionObjectId: int("inspectionObjectId").notNull(),
|
||
}, (table) => ({
|
||
cadObjectUnique: uniqueIndex("uk_model_cad_inspection_object").on(table.cadFileId, table.inspectionObjectId),
|
||
}));
|
||
|
||
// ─── 车型点云文件 ─────────────────────────────────────────────────────────────
|
||
// 一个车型可维护多个通用点云文件,供任务按需关联复用
|
||
|
||
export const modelPointcloudFiles = mysqlTable("model_pointcloud_files", {
|
||
id: int("id").autoincrement().primaryKey(),
|
||
modelId: int("modelId").notNull(),
|
||
name: varchar("name", { length: 255 }).notNull(),
|
||
fileSize: bigint("fileSize", { mode: "number" }).notNull(),
|
||
filePath: varchar("filePath", { length: 512 }).notNull(),
|
||
inspectionObjectId: int("inspectionObjectId"),
|
||
status: mysqlEnum("status", ["available", "disabled"]).default("available").notNull(),
|
||
metadataStatus: mysqlEnum("metadataStatus", ["pending", "ready", "unsupported", "error"]).default("pending").notNull(),
|
||
pointCount: bigint("pointCount", { mode: "number" }),
|
||
pointAttributes: json("pointAttributes").$type<PointAttribute[]>(),
|
||
pointDataFormat: varchar("pointDataFormat", { length: 32 }),
|
||
bytesPerPoint: int("bytesPerPoint"),
|
||
metadataError: text("metadataError"),
|
||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||
});
|
||
|
||
export type ModelPointcloudFile = typeof modelPointcloudFiles.$inferSelect;
|
||
export type InsertModelPointcloudFile = typeof modelPointcloudFiles.$inferInsert;
|
||
|
||
// ─── 任务 CAD 文件 ─────────────────────────────────────────────────────────────
|
||
// 每个任务可关联任意个 CAD 文件(与车型级 CAD 独立,用于本次检测的实测参考)
|
||
|
||
export const taskCadFiles = mysqlTable("task_cad_files", {
|
||
id: int("id").autoincrement().primaryKey(),
|
||
taskId: int("taskId").notNull(),
|
||
name: varchar("name", { length: 255 }).notNull(),
|
||
format: varchar("format", { length: 32 }).notNull(),
|
||
fileSize: bigint("fileSize", { mode: "number" }).notNull(),
|
||
filePath: varchar("filePath", { length: 512 }).notNull(),
|
||
/** 来源:uploaded = 用户上传;model_library = 车型 CAD 库关联 */
|
||
source: varchar("source", { length: 32 }).default("uploaded").notNull(),
|
||
/** 来源车型 CAD 文件 ID(仅 model_library 时有值) */
|
||
sourceModelCadFileId: int("sourceModelCadFileId"),
|
||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||
});
|
||
|
||
export type TaskCadFile = typeof taskCadFiles.$inferSelect;
|
||
export type InsertTaskCadFile = typeof taskCadFiles.$inferInsert;
|
||
|
||
// ─── 检测任务 ─────────────────────────────────────────────────────────────────
|
||
// 每次出厂/维保检测对应一条任务记录,归属于某车型
|
||
|
||
export const inspectionTasks = mysqlTable("inspection_tasks", {
|
||
id: int("id").autoincrement().primaryKey(),
|
||
modelId: int("modelId").notNull(),
|
||
inspectionObjectId: int("inspectionObjectId"),
|
||
name: varchar("name", { length: 255 }).notNull(),
|
||
inspector: varchar("inspector", { length: 100 }),
|
||
notes: text("notes"),
|
||
status: mysqlEnum("status", ["created", "pending", "processing", "completed", "error"]).default("created").notNull(),
|
||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||
completedAt: timestamp("completedAt"),
|
||
}, (table) => {
|
||
return {
|
||
modelNameUnique: uniqueIndex("uk_inspection_tasks_model_name").on(table.modelId, table.name),
|
||
};
|
||
});
|
||
|
||
export type InspectionTask = typeof inspectionTasks.$inferSelect;
|
||
export type InsertInspectionTask = typeof inspectionTasks.$inferInsert;
|
||
|
||
// ─── 任务点云文件 ──────────────────────────────────────────────────────────────
|
||
// 每次检测上传的点云文件(可多个),归属于某检测任务
|
||
|
||
export const taskPointcloudFiles = mysqlTable("task_pointcloud_files", {
|
||
id: int("id").autoincrement().primaryKey(),
|
||
taskId: int("taskId").notNull(),
|
||
name: varchar("name", { length: 255 }).notNull(),
|
||
fileSize: bigint("fileSize", { mode: "number" }).notNull(),
|
||
filePath: varchar("filePath", { length: 512 }).notNull(),
|
||
/** 来源:uploaded = 用户上传;registered = 配准算法产出;model_library = 车型点云库关联 */
|
||
source: varchar("source", { length: 32 }).default("uploaded").notNull(),
|
||
/** 产出该点云的分析结果 ID(仅 registered 时有值) */
|
||
sourceResultId: int("sourceResultId"),
|
||
/** 生成该配准产物的原始任务点云 ID(仅 registered 时有值) */
|
||
sourcePointcloudFileId: int("sourcePointcloudFileId"),
|
||
/** 来源车型点云文件 ID(仅 model_library 时有值) */
|
||
sourceModelPointcloudId: int("sourceModelPointcloudId"),
|
||
metadataStatus: mysqlEnum("metadataStatus", ["pending", "ready", "unsupported", "error"]).default("pending").notNull(),
|
||
pointCount: bigint("pointCount", { mode: "number" }),
|
||
pointAttributes: json("pointAttributes").$type<PointAttribute[]>(),
|
||
pointDataFormat: varchar("pointDataFormat", { length: 32 }),
|
||
bytesPerPoint: int("bytesPerPoint"),
|
||
metadataError: text("metadataError"),
|
||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||
});
|
||
|
||
export type TaskPointcloudFile = typeof taskPointcloudFiles.$inferSelect;
|
||
export type InsertTaskPointcloudFile = typeof taskPointcloudFiles.$inferInsert;
|
||
|
||
// ─── 任务分析结果 ──────────────────────────────────────────────────────────────
|
||
// 对某检测任务运行分析后保存的结果,支持回放
|
||
|
||
export const taskAnalysisResults = mysqlTable("task_analysis_results", {
|
||
id: int("id").autoincrement().primaryKey(),
|
||
taskId: int("taskId").notNull(),
|
||
type: mysqlEnum("type", [
|
||
"error_analysis",
|
||
"measurement",
|
||
"compression",
|
||
"smooth_heatmap",
|
||
"assembly_inspection",
|
||
"obj_deviation",
|
||
// 通用算法插件统一以 'plugin' 类型记录;具体算法以 pluginId 区分
|
||
"plugin",
|
||
]).notNull(),
|
||
/** 仅 type='plugin' 时使用:manifest.id(如 coupler-panel) */
|
||
pluginId: varchar("pluginId", { length: 64 }),
|
||
/** 算法 result.json 的 schema 版本,便于回放时严格校验 */
|
||
schemaVersion: int("schemaVersion"),
|
||
status: mysqlEnum("status", ["pending", "processing", "completed", "error", "cancelled"]).default("pending").notNull(),
|
||
errorStats: json("errorStats"), // { min, max, mean, stdDev, totalPoints, withinTolerance, outOfTolerance }
|
||
measurements: json("measurements"), // [{ name, value, unit, tolerance, status }]
|
||
resultData: json("resultData"), // 其他分析数据(如压缩比等)
|
||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||
});
|
||
|
||
export type TaskAnalysisResult = typeof taskAnalysisResults.$inferSelect;
|
||
export type InsertTaskAnalysisResult = typeof taskAnalysisResults.$inferInsert;
|