refactor(api)!: align client with canonical HTTP contract
Route all feature requests through business API modules, sync generated DTOs, and add communication tests and architecture documentation. BREAKING CHANGE: migrate the client to plural resource paths, canonical snake_case fields, ISO timestamps, and explicit paginated responses.
This commit is contained in:
@@ -9,12 +9,14 @@ import CloudEditModal, {
|
||||
import CloudLikeButton from '@/features/clouds/components/CloudLikeButton.vue'
|
||||
import ImageDetailModal from '@/features/clouds/components/ImageDetailModal.vue'
|
||||
import MiniLocationMap from '@/shared/map/components/MiniLocationMap.vue'
|
||||
import { ApiError, apiRequest, toApiCloud } from '@/lib/api'
|
||||
import { ApiError } from '@/lib/api'
|
||||
import { listClouds } from '@/features/clouds/api'
|
||||
import type { PublicCloudListQuery } from '@/types/api-contract'
|
||||
import { useAuthStore } from '@/features/auth/stores/auth'
|
||||
import { useCloudsStore } from '@/features/clouds/stores/clouds'
|
||||
import { useProfileStore } from '@/features/profile/stores/profile'
|
||||
|
||||
import type { ApiCloud, BackendCloudInfo } from '@/types/api'
|
||||
import type { CloudDetail } from '@/types/view-models'
|
||||
|
||||
interface GalleryCloud {
|
||||
id: string
|
||||
@@ -85,7 +87,7 @@ function formatCoordinate(value: number | null) {
|
||||
const normalizedSearch = computed(() => searchQuery.value.trim())
|
||||
const isUserSearch = computed(() => normalizedSearch.value.startsWith('@'))
|
||||
|
||||
function toGalleryCloud(row: ApiCloud) {
|
||||
function toGalleryCloud(row: CloudDetail) {
|
||||
return {
|
||||
id: row.id,
|
||||
user_id: row.user_id,
|
||||
@@ -100,7 +102,7 @@ function toGalleryCloud(row: ApiCloud) {
|
||||
status: row.status,
|
||||
is_hidden: row.is_hidden,
|
||||
cloudTypeName: row.cloud_type_name || '未知',
|
||||
cloudTypeRarity: row.cloud_type_rarity_value ?? 0,
|
||||
cloudTypeRarity: row.cloud_type_rarity ?? 0,
|
||||
username: row.username || '匿名',
|
||||
receivedLikeCount: row.received_like_count,
|
||||
} satisfies GalleryCloud
|
||||
@@ -111,23 +113,23 @@ async function loadPage(page: number) {
|
||||
loadError.value = ''
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({ page: String(page), page_size: String(PAGE_SIZE) })
|
||||
const query: PublicCloudListQuery = { page, page_size: PAGE_SIZE }
|
||||
if (normalizedSearch.value) {
|
||||
params.set('filter', isUserSearch.value ? 'owner' : 'type')
|
||||
params.set('value', normalizedSearch.value)
|
||||
query.filter = isUserSearch.value ? 'owner' : 'type'
|
||||
query.value = normalizedSearch.value
|
||||
} else if (selectedTypeId.value !== 'all') {
|
||||
const typeName = cloudsStore.cloudTypes.find(
|
||||
type => type.id === selectedTypeId.value,
|
||||
)?.name
|
||||
if (typeName) {
|
||||
params.set('filter', 'type')
|
||||
params.set('value', typeName)
|
||||
query.filter = 'type'
|
||||
query.value = typeName
|
||||
}
|
||||
}
|
||||
|
||||
const rows = await apiRequest<BackendCloudInfo[]>(`/cloud?${params}`, { auth: false })
|
||||
galleryItems.value = rows.map(toApiCloud).map(toGalleryCloud)
|
||||
hasNextPage.value = rows.length === PAGE_SIZE
|
||||
const result = await listClouds(query)
|
||||
galleryItems.value = result.items.map(toGalleryCloud)
|
||||
hasNextPage.value = result.has_more
|
||||
currentPage.value = page
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError && error.status === 404 && isUserSearch.value) {
|
||||
@@ -232,7 +234,7 @@ async function submitEditModal(value: CloudEditFormValue) {
|
||||
|
||||
try {
|
||||
const updated = await profileStore.updateCloud(authStore.user.id, current.id, {
|
||||
type_id: value.cloudCategoryId,
|
||||
cloud_type_id: value.cloudCategoryId,
|
||||
latitude: blurCoordinate(value.latitude as number),
|
||||
longitude: blurCoordinate(value.longitude as number),
|
||||
description: value.description.trim(),
|
||||
|
||||
Reference in New Issue
Block a user