147 lines
5.8 KiB
TypeScript
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 })
|
|
}
|
|
}
|