Files
DianZhanDemo/server/lib/crud-handler.ts
ch197511161 aaaf08e8f3 init6
2025-12-11 02:09:07 +08:00

338 lines
8.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 通用 CRUD 处理器基类
* Generic CRUD Handler Base Class
*
* 提供标准的 HTTP 请求处理和响应格式化
* Provides standard HTTP request handling and response formatting
*/
import type { H3Event } from 'h3'
import { getQuery, getRouterParams, readBody, createError } from 'h3'
import { validateQuery, validateBody, validateParams } from '@nuxt4crud/shared'
import type {
BaseEntity,
CreateInput,
UpdateInput,
QueryParams,
ApiResponse,
PaginatedResponse,
} from '@nuxt4crud/shared'
/**
* CRUD 处理器基类
* CRUD Handler Base Class
*/
export abstract class BaseCrudHandler<
T extends BaseEntity,
CreateInputType extends CreateInput,
UpdateInputType extends UpdateInput,
QueryParamsType extends QueryParams = QueryParams,
> {
constructor(
protected service: any, // 实际的服务实例
protected schemas: {
query?: any
create?: any
update?: any
params?: any
} = {}
) {}
/**
* 处理列表查询请求
* Handle list query request
*/
async handleList(event: H3Event): Promise<ApiResponse<PaginatedResponse<T>>> {
try {
const query = this.schemas.query
? validateQuery(this.schemas.query, getQuery(event))
: getQuery(event)
const result = await this.service.findMany(query)
return {
success: true,
data: result,
}
} catch (error: any) {
return this.handleError(error)
}
}
/**
* 处理单个记录查询请求
* Handle single record query request
*/
async handleGet(event: H3Event): Promise<ApiResponse<T>> {
try {
const params = this.schemas.params
? validateParams(this.schemas.params, getRouterParams(event))
: getRouterParams(event)
const id = Number(params.id)
const result = await this.service.findById(id)
if (!result) {
throw createError({
statusCode: 404,
statusMessage: '记录不存在',
})
}
return {
success: true,
data: result,
}
} catch (error: any) {
return this.handleError(error)
}
}
/**
* 兼容单个或多个ID的查询请求支持 GET 与 POST
* Flexible handler to fetch by a single ID or multiple IDs (supports GET and POST)
*
* - GET: 路径参数 `id`,支持逗号分隔(如 `/api/.../1,2,3`
* - POST: 请求体包含 `id: number` 或 `ids: number[] | string`
*/
async handleGetFlexible(event: H3Event): Promise<ApiResponse<T | T[]>> {
try {
const method = event?.node?.req?.method || 'GET'
// POST 支持通过请求体传输多个ID
if (method === 'POST') {
const body = await readBody(event)
if (!body || (body.id === undefined && body.ids === undefined)) {
throw createError({ statusCode: 400, statusMessage: '请求体必须包含 id 或 ids' })
}
// 归一化为ID数组
const ids: number[] = Array.isArray(body.ids)
? body.ids.map((v: any) => Number(v)).filter((v: number) => Number.isFinite(v) && v > 0)
: typeof body.ids === 'string'
? body.ids
.split(',')
.map(s => Number(s.trim()))
.filter(n => Number.isFinite(n) && n > 0)
: body.id !== undefined
? [Number(body.id)].filter(n => Number.isFinite(n) && n > 0)
: []
if (ids.length === 0) {
throw createError({ statusCode: 400, statusMessage: '无效的ID或ID数组' })
}
// 单个ID与多个ID统一处理
if (ids.length === 1) {
const one = await this.service.findById(ids[0])
if (!one) {
throw createError({ statusCode: 404, statusMessage: '记录不存在' })
}
return { success: true, data: one }
} else {
const list = await this.service.findByIds(ids)
if (!list || list.length === 0) {
throw createError({ statusCode: 404, statusMessage: '记录不存在' })
}
const partial = list.length < ids.length
return {
success: true,
data: list,
message: partial ? '部分ID未找到' : undefined,
}
}
}
// GET 支持 `/api/.../[id]` 单个ID或逗号分隔的多个ID
const params = getRouterParams(event)
if (!params || params.id === undefined) {
throw createError({ statusCode: 400, statusMessage: '缺少路径参数 id' })
}
const rawId = String(params.id)
const ids = rawId
.split(',')
.map(s => Number(s.trim()))
.filter(n => Number.isFinite(n) && n > 0)
if (ids.length === 0) {
throw createError({ statusCode: 400, statusMessage: '无效的路径参数 id' })
}
if (ids.length === 1) {
const one = await this.service.findById(ids[0])
if (!one) {
throw createError({ statusCode: 404, statusMessage: '记录不存在' })
}
return { success: true, data: one }
} else {
const list = await this.service.findByIds(ids)
if (!list || list.length === 0) {
throw createError({ statusCode: 404, statusMessage: '记录不存在' })
}
const partial = list.length < ids.length
return {
success: true,
data: list,
message: partial ? '部分ID未找到' : undefined,
}
}
} catch (error: any) {
return this.handleError(error)
}
}
/**
* 处理创建请求
* Handle create request
*/
async handleCreate(event: H3Event): Promise<ApiResponse<T>> {
try {
const body = await readBody(event)
if (!body) {
throw createError({
statusCode: 400,
statusMessage: '请求体不能为空',
})
}
const data = this.schemas.create ? validateBody(this.schemas.create, body) : body
const result = await this.service.create(data)
return {
success: true,
data: result,
message: '创建成功',
}
} catch (error: any) {
return this.handleError(error)
}
}
/**
* 处理更新请求
* Handle update request
*/
async handleUpdate(event: H3Event): Promise<ApiResponse<T>> {
try {
const params = this.schemas.params
? validateParams(this.schemas.params, getRouterParams(event))
: getRouterParams(event)
const id = Number(params.id)
const body = await readBody(event)
if (!body) {
throw createError({
statusCode: 400,
statusMessage: '请求体不能为空',
})
}
const data = this.schemas.update ? validateBody(this.schemas.update, body) : body
const result = await this.service.update(id, data)
return {
success: true,
data: result,
message: '更新成功',
}
} catch (error: any) {
return this.handleError(error)
}
}
/**
* 处理删除请求
* Handle delete request
*/
async handleDelete(event: H3Event): Promise<ApiResponse<void>> {
try {
const params = this.schemas.params
? validateParams(this.schemas.params, getRouterParams(event))
: getRouterParams(event)
const id = Number(params.id)
await this.service.delete(id)
return {
success: true,
message: '删除成功',
}
} catch (error: any) {
return this.handleError(error)
}
}
/**
* 错误处理
* Error handling
*/
protected handleError(error: any): ApiResponse {
console.error('CRUD Handler Error:', error)
if (error.statusCode === 404) {
return {
success: false,
message: error.message || '记录不存在',
}
}
if (error.statusCode === 400) {
return {
success: false,
message: error.message || '请求参数错误',
validationErrors: error.validationErrors,
}
}
if (error.statusCode === 409) {
return {
success: false,
message: error.message || '数据冲突',
}
}
return {
success: false,
message: error.message || '服务器内部错误',
}
}
}
/**
* 创建 CRUD 处理器工厂函数
* Create CRUD handler factory function
*/
export function createCrudHandlers<
T extends BaseEntity,
CreateInputType extends CreateInput,
UpdateInputType extends UpdateInput,
QueryParamsType extends QueryParams = QueryParams,
>(
service: any,
schemas: {
query?: any
create?: any
update?: any
params?: any
} = {}
) {
const handler = new BaseCrudHandler<T, CreateInputType, UpdateInputType, QueryParamsType>(
service,
schemas
)
return {
list: (event: H3Event) => handler.handleList(event),
get: (event: H3Event) => handler.handleGet(event),
create: (event: H3Event) => handler.handleCreate(event),
update: (event: H3Event) => handler.handleUpdate(event),
delete: (event: H3Event) => handler.handleDelete(event),
}
}