Files
teatea-pension/modules/devices/types.ts

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"),
},
};
}