This commit is contained in:
ch197511161
2025-12-11 02:09:07 +08:00
parent 54d6acbce3
commit aaaf08e8f3
84 changed files with 4131 additions and 0 deletions

337
server/lib/crud-handler.ts Normal file
View File

@@ -0,0 +1,337 @@
/**
* 通用 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),
}
}