useOperationApi.ts 5 KB
import type {
  OperationTask,
  OperationTaskAction,
  OperationTaskActionPayload,
  OperationTaskListQuery,
  OperationTaskMutationResponse,
  OperationTaskResult,
  OperationTaskStatus
} from '~/types'
import { OPERATION_TASK_ACTION_VALUES, OPERATION_TASK_STATUS_VALUES } from '~/types/operation'
import { createSharedComposable } from '@vueuse/core'

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null
}

function normalizeString(value: unknown, fallback = '') {
  return typeof value === 'string' ? value : fallback
}

function normalizeNumber(value: unknown, fallback = 0) {
  const num = Number(value)
  return Number.isFinite(num) ? num : fallback
}

function isStatus(value: unknown): value is OperationTaskStatus {
  return typeof value === 'string' && OPERATION_TASK_STATUS_VALUES.includes(value as OperationTaskStatus)
}

function isAction(value: unknown): value is OperationTaskAction {
  return typeof value === 'string' && OPERATION_TASK_ACTION_VALUES.includes(value as OperationTaskAction)
}

function isResult(value: unknown): value is OperationTaskResult {
  return value === 'pending' || value === 'pass' || value === 'fail'
}

function normalizeTasks(payload: unknown): OperationTask[] {
  if (!Array.isArray(payload)) {
    return []
  }

  return payload
    .filter(isRecord)
    .map((item) => {
      const auditEvents = Array.isArray(item.auditEvents) ? item.auditEvents : []

      return {
        id: normalizeNumber(item.id),
        workOrderNo: normalizeString(item.workOrderNo),
        sn: normalizeString(item.sn),
        stepName: normalizeString(item.stepName),
        workstation: normalizeString(item.workstation),
        device: normalizeString(item.device),
        operator: normalizeString(item.operator),
        startedAt: normalizeString(item.startedAt, undefined),
        endedAt: normalizeString(item.endedAt, undefined),
        result: isResult(item.result) ? item.result : 'pending',
        status: isStatus(item.status) ? item.status : 'pending',
        nextAction: normalizeString(item.nextAction),
        auditEvents: auditEvents
          .filter(isRecord)
          .map(event => ({
            id: normalizeString(event.id),
            action: isAction(event.action) ? event.action : 'start',
            fromStatus: isStatus(event.fromStatus) ? event.fromStatus : null,
            toStatus: isStatus(event.toStatus) ? event.toStatus : 'pending',
            operator: normalizeString(event.operator),
            at: normalizeString(event.at),
            remark: normalizeString(event.remark, undefined),
            nextAction: normalizeString(event.nextAction)
          }))
      } satisfies OperationTask
    })
    .filter(item => item.id > 0 && item.sn.length > 0)
}

function normalizeMutation(payload: unknown, fallbackMessage: string): OperationTaskMutationResponse {
  if (!isRecord(payload)) {
    return {
      success: false,
      errorCode: 'REQUEST_FAILED',
      message: fallbackMessage
    }
  }

  return {
    success: Boolean(payload.success),
    errorCode: typeof payload.errorCode === 'string' ? payload.errorCode : null,
    message: typeof payload.message === 'string' && payload.message.length > 0
      ? payload.message
      : fallbackMessage,
    state: isStatus(payload.state) ? payload.state : undefined,
    nextAction: typeof payload.nextAction === 'string' ? payload.nextAction : undefined
  }
}

function buildQuery(query: OperationTaskListQuery) {
  const params = new URLSearchParams()

  if (query.workstation) {
    params.set('workstation', query.workstation)
  }

  if (query.stepName) {
    params.set('stepName', query.stepName)
  }

  if (query.operator) {
    params.set('operator', query.operator)
  }

  if (query.status) {
    params.set('status', query.status)
  }

  if (query.sn) {
    params.set('sn', query.sn)
  }

  const search = params.toString()
  return search.length > 0 ? `?${search}` : ''
}

const _useOperationApi = () => {
  const api = useApiGateway()

  const getOperationTasks = async (query: OperationTaskListQuery) => {
    const result = await api.request<unknown>(`/api/operations${buildQuery(query)}`, {
      method: 'GET'
    })

    return normalizeTasks(result)
  }

  const runOperationAction = async (id: number, action: OperationTaskAction, payload: OperationTaskActionPayload) => {
    const remotePath = action === 'complete_rework'
      ? `/api/operations/${id}/actions/${action}`
      : undefined

    try {
      const result = await api.request<unknown>(`/api/operations/${id}/actions/${action}`, {
        method: 'POST',
        body: payload,
        remotePath
      })

      return normalizeMutation(result, '工序任务状态更新失败。')
    } catch {
      return {
        success: false,
        errorCode: 'REQUEST_FAILED',
        message: '工序任务状态更新失败。'
      } satisfies OperationTaskMutationResponse
    }
  }

  return {
    getOperationTasks,
    runOperationAction
  }
}

export const useOperationApi = createSharedComposable(_useOperationApi)