init6
This commit is contained in:
337
server/lib/crud-handler.ts
Normal file
337
server/lib/crud-handler.ts
Normal 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),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user