issue-ai/src/app/api/tickets/[id]/route.ts

147 lines
5.8 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { getDb } from '@/lib/db'
import { initDatabase } from '@/lib/db-schema'
import { getCurrentUser } from '@/lib/auth'
import { hasPermission } from '@/lib/permissions'
import { getAssetByIp } from '@/lib/assets-client'
export async function GET(_request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
initDatabase()
const user = await getCurrentUser()
if (!user) return NextResponse.json({ error: '未登录' }, { status: 401 })
const { id } = await params
const db = getDb()
const ticket = db.prepare('SELECT * FROM tickets WHERE id = ?').get(id)
if (!ticket) return NextResponse.json({ error: '工单不存在' }, { status: 404 })
const steps = db.prepare('SELECT * FROM ticket_steps WHERE ticket_id = ? ORDER BY step_order').all((ticket as any).id)
// Try to get device info from assets API
let assetInfo = null
const t = ticket as any
if (t.device_ip) {
try {
assetInfo = await getAssetByIp(t.device_ip)
} catch {
// Assets API may not be available
}
}
return NextResponse.json({ ticket, steps, assetInfo })
} catch (e) {
const msg = e instanceof Error ? e.message : '查询失败'
return NextResponse.json({ error: msg }, { status: 500 })
}
}
export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
initDatabase()
const user = await getCurrentUser()
if (!user) return NextResponse.json({ error: '未登录' }, { status: 401 })
if (!hasPermission(user, 'tickets:write')) return NextResponse.json({ error: '权限不足' }, { status: 403 })
const { id } = await params
const body = await request.json()
const db = getDb()
const existing = db.prepare('SELECT * FROM tickets WHERE id = ?').get(id) as Record<string, unknown>
if (!existing) return NextResponse.json({ error: '工单不存在' }, { status: 404 })
// 非管理员不可编辑已办工单
const completedStatuses = ['resolved', 'closed']
if (user.role !== 'admin' && completedStatuses.includes(existing.current_status as string)) {
return NextResponse.json({ error: '已办工单仅管理员可修改' }, { status: 403 })
}
// 自动计算处理时长
if (body.close_time && existing.assign_time) {
const assignMs = new Date(existing.assign_time as string).getTime()
const closeMs = new Date(body.close_time).getTime()
if (!isNaN(assignMs) && !isNaN(closeMs)) {
body.duration_minutes = Math.round((closeMs - assignMs) / 60000)
}
}
// resolved 但未传 close_time 时自动设置为北京时间
if (body.current_status === 'resolved' && !body.close_time && !existing.close_time) {
const d = new Date(Date.now() + 8 * 60 * 60 * 1000)
body.close_time = d.toISOString().slice(0, 19)
}
const fields: string[] = []
const values: unknown[] = []
const updatable = [
'device_ip', 'device_sn', 'device_name', 'content', 'assign_time', 'close_time',
'duration_minutes', 'availability', 'process_summary', 'conclusion',
'ticket_type', 'fault_category', 'fault_subcategory', 'parts_replaced', 'parts_name',
'current_status', 'counted_in_sla', 'responsibility',
]
for (const key of updatable) {
if (key in body) {
fields.push(`${key} = ?`)
values.push(body[key])
}
}
if (fields.length > 0) {
fields.push('updated_by = ?')
values.push(user.id)
fields.push("updated_at = datetime('now')")
values.push(id)
db.prepare(`UPDATE tickets SET ${fields.join(', ')} WHERE id = ?`).run(...values)
}
// Update steps if provided
if (body.steps && Array.isArray(body.steps)) {
db.prepare('DELETE FROM ticket_steps WHERE ticket_id = ?').run(id)
const stmt = db.prepare('INSERT INTO ticket_steps (ticket_id, step_order, time_node, handler, description) VALUES (?, ?, ?, ?, ?)')
for (let i = 0; i < body.steps.length; i++) {
const step = body.steps[i]
stmt.run(id, i + 1, step.time_node || null, step.handler || null, step.description || null)
}
}
const ticket = db.prepare('SELECT * FROM tickets WHERE id = ?').get(id)
return NextResponse.json({ ticket })
} catch (e) {
const msg = e instanceof Error ? e.message : '更新失败'
return NextResponse.json({ error: msg }, { status: 500 })
}
}
export async function DELETE(_request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
initDatabase()
const user = await getCurrentUser()
if (!user) return NextResponse.json({ error: '未登录' }, { status: 401 })
if (!hasPermission(user, 'tickets:write')) return NextResponse.json({ error: '权限不足' }, { status: 403 })
const { id } = await params
const db = getDb()
const existing = db.prepare('SELECT current_status, created_by FROM tickets WHERE id = ?').get(id) as { current_status: string; created_by: number | null } | undefined
if (!existing) return NextResponse.json({ error: '工单不存在' }, { status: 404 })
// 非管理员不可删除已办工单,非创建人不可删除待办工单
const completedStatuses = ['resolved', 'closed']
if (user.role !== 'admin') {
if (completedStatuses.includes(existing.current_status)) {
return NextResponse.json({ error: '已办工单仅管理员可删除' }, { status: 403 })
}
if (existing.created_by !== user.id) {
return NextResponse.json({ error: '仅创建人和管理员可删除此工单' }, { status: 403 })
}
}
db.prepare('DELETE FROM tickets WHERE id = ?').run(id)
return NextResponse.json({ success: true })
} catch (e) {
const msg = e instanceof Error ? e.message : '删除失败'
return NextResponse.json({ error: msg }, { status: 500 })
}
}