201 lines
5.4 KiB
TypeScript
201 lines
5.4 KiB
TypeScript
export const DEVICE_ASSET_STATUS_VALUES = ["active", "maintenance", "disabled", "retired"] as const;
|
|
export type DeviceAssetStatus = (typeof DEVICE_ASSET_STATUS_VALUES)[number];
|
|
|
|
export const MAINTENANCE_TICKET_STATUS_VALUES = ["open", "assigned", "resolved", "closed", "cancelled"] as const;
|
|
export type MaintenanceTicketStatus = (typeof MAINTENANCE_TICKET_STATUS_VALUES)[number];
|
|
|
|
export const MAINTENANCE_TICKET_PRIORITY_VALUES = ["low", "normal", "high", "urgent"] as const;
|
|
export type MaintenanceTicketPriority = (typeof MAINTENANCE_TICKET_PRIORITY_VALUES)[number];
|
|
|
|
export const DEVICE_ASSET_STATUS_LABELS: Record<DeviceAssetStatus, string> = {
|
|
active: "运行中",
|
|
maintenance: "维护中",
|
|
disabled: "停用",
|
|
retired: "已退役",
|
|
};
|
|
|
|
export const MAINTENANCE_TICKET_STATUS_LABELS: Record<MaintenanceTicketStatus, string> = {
|
|
open: "待处理",
|
|
assigned: "已派单",
|
|
resolved: "已解决",
|
|
closed: "已关闭",
|
|
cancelled: "已取消",
|
|
};
|
|
|
|
export const MAINTENANCE_TICKET_PRIORITY_LABELS: Record<MaintenanceTicketPriority, string> = {
|
|
low: "低",
|
|
normal: "普通",
|
|
high: "高",
|
|
urgent: "紧急",
|
|
};
|
|
|
|
export type DeviceAsset = {
|
|
id: string;
|
|
organizationId: string;
|
|
name: string;
|
|
code: string;
|
|
category: string;
|
|
location: string;
|
|
status: DeviceAssetStatus;
|
|
lastInspectedAt?: string;
|
|
notes: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
};
|
|
|
|
export type MaintenanceTicket = {
|
|
id: string;
|
|
organizationId: string;
|
|
deviceId?: string;
|
|
deviceName: string;
|
|
title: string;
|
|
description: string;
|
|
priority: MaintenanceTicketPriority;
|
|
status: MaintenanceTicketStatus;
|
|
assigneeLabel: string;
|
|
resolutionNotes: string;
|
|
resolvedAt?: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
};
|
|
|
|
export type DeviceOperationsData = {
|
|
metrics: {
|
|
activeDevices: number;
|
|
maintenanceDevices: number;
|
|
openTickets: number;
|
|
urgentTickets: number;
|
|
};
|
|
devices: DeviceAsset[];
|
|
tickets: MaintenanceTicket[];
|
|
};
|
|
|
|
export type DeviceAssetInput = {
|
|
name: string;
|
|
code: string;
|
|
category: string;
|
|
location: string;
|
|
status: DeviceAssetStatus;
|
|
lastInspectedAt?: string;
|
|
notes: string;
|
|
};
|
|
|
|
export type MaintenanceTicketInput = {
|
|
deviceId?: string;
|
|
title: string;
|
|
description: string;
|
|
priority: MaintenanceTicketPriority;
|
|
status: MaintenanceTicketStatus;
|
|
assigneeLabel: string;
|
|
resolutionNotes: string;
|
|
};
|
|
|
|
type ValidationSuccess<T> = {
|
|
success: true;
|
|
data: T;
|
|
};
|
|
|
|
type ValidationFailure = {
|
|
success: false;
|
|
reason: string;
|
|
};
|
|
|
|
export type ValidationResult<T> = ValidationSuccess<T> | ValidationFailure;
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
}
|
|
|
|
function readString(source: Record<string, unknown>, key: string): string {
|
|
const value = source[key];
|
|
return typeof value === "string" ? value.trim() : "";
|
|
}
|
|
|
|
function readOptionalString(source: Record<string, unknown>, key: string): string | undefined {
|
|
const value = readString(source, key);
|
|
return value || undefined;
|
|
}
|
|
|
|
function readEnum<T extends string>(value: unknown, values: readonly T[]): T | null {
|
|
return typeof value === "string" ? values.find((item) => item === value) ?? null : null;
|
|
}
|
|
|
|
function validateIsoDate(value: string | undefined, label: string): ValidationResult<string | undefined> {
|
|
if (!value) {
|
|
return { success: true, data: undefined };
|
|
}
|
|
|
|
return Number.isNaN(new Date(value).getTime())
|
|
? { success: false, reason: `${label}无效` }
|
|
: { success: true, data: value };
|
|
}
|
|
|
|
export function validateDeviceAssetInput(value: unknown): ValidationResult<DeviceAssetInput> {
|
|
if (!isRecord(value)) {
|
|
return { success: false, reason: "请求数据格式无效" };
|
|
}
|
|
|
|
const name = readString(value, "name");
|
|
const code = readString(value, "code");
|
|
if (!name || !code) {
|
|
return { success: false, reason: "设备名称和编号不能为空" };
|
|
}
|
|
|
|
const status = readEnum(value.status, DEVICE_ASSET_STATUS_VALUES);
|
|
if (!status) {
|
|
return { success: false, reason: "设备状态无效" };
|
|
}
|
|
|
|
const lastInspectedAt = validateIsoDate(readOptionalString(value, "lastInspectedAt"), "最近巡检时间");
|
|
if (!lastInspectedAt.success) {
|
|
return lastInspectedAt;
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
data: {
|
|
name,
|
|
code,
|
|
category: readString(value, "category"),
|
|
location: readString(value, "location"),
|
|
status,
|
|
lastInspectedAt: lastInspectedAt.data,
|
|
notes: readString(value, "notes"),
|
|
},
|
|
};
|
|
}
|
|
|
|
export function validateMaintenanceTicketInput(value: unknown): ValidationResult<MaintenanceTicketInput> {
|
|
if (!isRecord(value)) {
|
|
return { success: false, reason: "请求数据格式无效" };
|
|
}
|
|
|
|
const title = readString(value, "title");
|
|
if (!title) {
|
|
return { success: false, reason: "工单标题不能为空" };
|
|
}
|
|
|
|
const priority = readEnum(value.priority, MAINTENANCE_TICKET_PRIORITY_VALUES);
|
|
if (!priority) {
|
|
return { success: false, reason: "工单优先级无效" };
|
|
}
|
|
|
|
const status = readEnum(value.status, MAINTENANCE_TICKET_STATUS_VALUES);
|
|
if (!status) {
|
|
return { success: false, reason: "工单状态无效" };
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
data: {
|
|
deviceId: readOptionalString(value, "deviceId"),
|
|
title,
|
|
description: readString(value, "description"),
|
|
priority,
|
|
status,
|
|
assigneeLabel: readString(value, "assigneeLabel"),
|
|
resolutionNotes: readString(value, "resolutionNotes"),
|
|
},
|
|
};
|
|
}
|