useBaseInfoApi.ts 3.93 KB
import type { DeviceType, DeviceTypeCategory, DeviceTypePayload } from '~/types/device-type'
import { DEVICE_TYPE_CATEGORY_VALUES } from '~/types/device-type'
import { createSharedComposable } from '@vueuse/core'

interface MutationResponse {
  success: boolean
  errorCode: string | null
  message: string
}

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

function isDeviceTypeCategory(value: unknown): value is DeviceTypeCategory {
  return typeof value === 'string' && DEVICE_TYPE_CATEGORY_VALUES.includes(value as DeviceTypeCategory)
}

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

  return payload
    .filter(isRecord)
    .map((item) => {
      const rawCategory = item.category

      return {
        id: Number(item.id ?? 0),
        name: typeof item.name === 'string' ? item.name : '',
        model: typeof item.model === 'string' ? item.model : '',
        category: isDeviceTypeCategory(rawCategory) ? rawCategory : 'other',
        lengthMm: Number(item.lengthMm ?? 0),
        widthMm: Number(item.widthMm ?? 0),
        heightMm: Number(item.heightMm ?? 0),
        weightKg: Number(item.weightKg ?? 0),
        hasBattery: Boolean(item.hasBattery),
        batterySpec: typeof item.batterySpec === 'string' ? item.batterySpec : undefined,
        description: typeof item.description === 'string' ? item.description : undefined,
        updatedAt: typeof item.updatedAt === 'string' ? item.updatedAt : ''
      } satisfies DeviceType
    })
    .filter(item => item.id > 0 && item.name.length > 0)
}

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

  const message = typeof payload.message === 'string' && payload.message.length > 0
    ? payload.message
    : fallbackMessage

  return {
    success: typeof payload.success === 'boolean' ? payload.success : false,
    errorCode: typeof payload.errorCode === 'string' ? payload.errorCode : null,
    message
  }
}

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

  const getDeviceTypes = () => {
    return api.useApiFetch<DeviceType[]>('/api/base-info/device-types', {
      transform: normalizeDeviceTypes,
      default: () => []
    })
  }

  const createDeviceType = async (payload: DeviceTypePayload) => {
    try {
      const result = await api.request<unknown>('/api/base-info/device-types', {
        method: 'POST',
        body: payload
      })

      return normalizeMutation(result, '创建设备类型失败。')
    } catch {
      return {
        success: false,
        errorCode: 'REQUEST_FAILED',
        message: '创建设备类型失败。'
      } satisfies MutationResponse
    }
  }

  const updateDeviceType = async (id: number, payload: DeviceTypePayload) => {
    try {
      const result = await api.request<unknown>(`/api/base-info/device-types/${id}`, {
        method: 'PUT',
        body: payload
      })

      return normalizeMutation(result, '更新设备类型失败。')
    } catch {
      return {
        success: false,
        errorCode: 'REQUEST_FAILED',
        message: '更新设备类型失败。'
      } satisfies MutationResponse
    }
  }

  const deleteDeviceType = async (id: number) => {
    try {
      const result = await api.request<unknown>(`/api/base-info/device-types/${id}`, {
        method: 'DELETE'
      })

      return normalizeMutation(result, '删除设备类型失败。')
    } catch {
      return {
        success: false,
        errorCode: 'REQUEST_FAILED',
        message: '删除设备类型失败。'
      } satisfies MutationResponse
    }
  }

  return {
    getDeviceTypes,
    createDeviceType,
    updateDeviceType,
    deleteDeviceType
  }
}

export const useBaseInfoApi = createSharedComposable(_useBaseInfoApi)