import { Request, Response } from 'express' import prisma from '../utils/prisma' import { successResponse, errorResponse } from '../utils/response' import { logOperation } from './operationLogController' import { calculateEquipmentValues, syncMountedChildrenState } from '../utils/assetValue' const buildBorrowNotes = (notes?: string, isLongTerm?: boolean) => { const longTerm = Boolean(isLongTerm) return longTerm && notes ? `${notes};在职长期` : longTerm ? '在职长期' : notes || undefined } const getChildrenSnapshot = async (equipmentId: number) => { const children = await prisma.equipment.findMany({ where: { parent_id: equipmentId }, select: { id: true, equipment_code: true, equipment_name: true, equipment_model: true, equipment_type: true, unit_price: true } }) return children.length > 0 ? JSON.stringify(children) : undefined } const batchBorrowTransaction = async ( equipment: any, person: any, expectedReturnTime?: string, isLongTerm?: boolean, notes?: string ) => { const childrenSnapshot = await getChildrenSnapshot(equipment.id) const finalNotes = buildBorrowNotes(notes, isLongTerm) return prisma.$transaction(async (tx) => { await tx.borrowRecord.create({ data: { equipment_id: equipment.id, equipment_code: equipment.equipment_code, person_id: person.id, person_name: person.real_name, person_nickname: person.nickname, person_identifier: person.id_number, expected_return_time: isLongTerm ? null : (expectedReturnTime ? new Date(expectedReturnTime) : null), is_long_term: Boolean(isLongTerm), notes: finalNotes, status: '已借出', children_snapshot: childrenSnapshot } }) const nextRemainingQuantity = Math.max(Number(equipment.remaining_quantity) - 1, 0) const nextStatus = nextRemainingQuantity > 0 ? '可用' : '已借出' const values = calculateEquipmentValues({ unitPrice: Number(equipment.unit_price), totalQuantity: Number(equipment.total_quantity), remainingQuantity: nextRemainingQuantity, status: nextStatus }) const updated = await tx.equipment.updateMany({ where: { id: equipment.id, status: '可用', remaining_quantity: equipment.remaining_quantity }, data: { remaining_quantity: nextRemainingQuantity, total_value: values.total_value, borrowed_value: values.borrowed_value, available_value: values.available_value, status: nextStatus } }) if (updated.count !== 1) { throw new Error('资产库存已变化,请刷新后重试') } await syncMountedChildrenState(tx, equipment.id, '已借出') }) } const batchReturnTransaction = async ( equipment: any, borrowRecord: any, returnCondition: string, notes?: string ) => { return prisma.$transaction(async (tx) => { const updatedRecord = await tx.borrowRecord.updateMany({ where: { id: borrowRecord.id, status: '已借出' }, data: { return_time: new Date(), status: '已归还', return_condition: returnCondition, notes } }) if (updatedRecord.count !== 1) { throw new Error('该借用记录已归还,请刷新后重试') } const nextRemainingQuantity = Math.min( Number(equipment.total_quantity), Number(equipment.remaining_quantity) + 1 ) const values = calculateEquipmentValues({ unitPrice: Number(equipment.unit_price), totalQuantity: Number(equipment.total_quantity), remainingQuantity: nextRemainingQuantity, status: '可用' }) const updatedEquipment = await tx.equipment.updateMany({ where: { id: equipment.id, remaining_quantity: { lt: equipment.total_quantity } }, data: { remaining_quantity: nextRemainingQuantity, total_value: values.total_value, borrowed_value: values.borrowed_value, available_value: values.available_value, status: '可用' } }) if (updatedEquipment.count !== 1) { throw new Error('资产库存已达到上限,请检查归还记录') } await syncMountedChildrenState(tx, equipment.id, '可用') }) } // 批量借出 export const batchBorrow = async (req: Request, res: Response) => { try { const { equipment_ids, person_id, expected_return_time, is_long_term, notes } = req.body if (!Array.isArray(equipment_ids) || equipment_ids.length === 0) { return errorResponse(res, '请选择要借出的资产') } const person = await prisma.person.findUnique({ where: { id: Number(person_id) } }) if (!person || person.status !== 'active') { return errorResponse(res, '使用者不存在或已禁用') } const results: any[] = [] let successCount = 0 let failedCount = 0 for (const equipmentId of equipment_ids) { try { const equipment = await prisma.equipment.findUnique({ where: { id: Number(equipmentId) } }) if (!equipment) { results.push({ equipment_id: equipmentId, status: 'failed', message: '资产不存在' }) failedCount++ continue } if (equipment.parent_id) { results.push({ equipment_id: equipmentId, equipment_code: equipment.equipment_code, status: 'failed', message: '该子资产当前已挂载,请先卸载后再借用' }) failedCount++ continue } if (equipment.status !== '可用' || equipment.remaining_quantity <= 0) { results.push({ equipment_id: equipmentId, equipment_code: equipment.equipment_code, status: 'failed', message: '资产不可借用' }) failedCount++ continue } await batchBorrowTransaction( equipment, person, expected_return_time, is_long_term, notes ) results.push({ equipment_id: equipmentId, equipment_code: equipment.equipment_code, status: 'success', message: '借出成功' }) successCount++ } catch (error: any) { results.push({ equipment_id: equipmentId, status: 'failed', message: error.message }) failedCount++ } } // 记录操作日志 const userId = res.locals.user?.id const username = res.locals.user?.username || res.locals.user?.name || 'unknown' const successCodes = results.filter(r => r.status === 'success').map(r => r.equipment_code).join(', ') if (successCount > 0) { await logOperation( userId, username, '批量借出', `批量借出 ${successCount} 件资产给 ${person.real_name}(${person.nickname}),资产编码:${successCodes}`, undefined, undefined, req.ip ) } successResponse(res, { success_count: successCount, failed_count: failedCount, results }, '批量借出完成') } catch (error: any) { errorResponse(res, error.message || '批量借出失败') } } // 批量归还 export const batchReturn = async (req: Request, res: Response) => { try { const { equipment_ids, return_condition = '正常', notes } = req.body if (!Array.isArray(equipment_ids) || equipment_ids.length === 0) { return errorResponse(res, '请选择要归还的资产') } const results: any[] = [] let successCount = 0 let failedCount = 0 for (const equipmentId of equipment_ids) { try { const equipment = await prisma.equipment.findUnique({ where: { id: Number(equipmentId) } }) if (!equipment) { results.push({ equipment_id: equipmentId, status: 'failed', message: '资产不存在' }) failedCount++ continue } const activeBorrowRecords = await prisma.borrowRecord.findMany({ where: { equipment_id: equipment.id, status: '已借出' }, orderBy: { borrow_time: 'desc' }, take: 2 }) const borrowRecord = activeBorrowRecords[0] if (!borrowRecord) { results.push({ equipment_id: equipmentId, equipment_code: equipment.equipment_code, status: 'failed', message: '未找到借用记录' }) failedCount++ continue } if (activeBorrowRecords.length > 1) { results.push({ equipment_id: equipmentId, equipment_code: equipment.equipment_code, status: 'failed', message: '存在多条未归还记录,请使用单个归还' }) failedCount++ continue } await batchReturnTransaction(equipment, borrowRecord, return_condition, notes) results.push({ equipment_id: equipmentId, equipment_code: equipment.equipment_code, status: 'success', message: '归还成功' }) successCount++ } catch (error: any) { results.push({ equipment_id: equipmentId, status: 'failed', message: error.message }) failedCount++ } } // 记录操作日志 const userId = res.locals.user?.id const username = res.locals.user?.username || res.locals.user?.name || 'unknown' const successCodes = results.filter(r => r.status === 'success').map(r => r.equipment_code).join(', ') if (successCount > 0) { await logOperation( userId, username, '批量归还', `批量归还 ${successCount} 件资产,归还状态:${return_condition},资产编码:${successCodes}`, undefined, undefined, req.ip ) } successResponse(res, { success_count: successCount, failed_count: failedCount, results }, '批量归还完成') } catch (error: any) { errorResponse(res, error.message || '批量归还失败') } } // 批量转移部门 export const batchTransfer = async (req: Request, res: Response) => { try { const { equipment_ids, department_id } = req.body if (!Array.isArray(equipment_ids) || equipment_ids.length === 0) { return errorResponse(res, '请选择要转移的资产') } const department = await prisma.department.findUnique({ where: { id: Number(department_id) } }) if (!department || department.status !== 'active') { return errorResponse(res, '目标部门不存在或已停用') } const results: any[] = [] let successCount = 0 let failedCount = 0 for (const equipmentId of equipment_ids) { try { const equipment = await prisma.equipment.findUnique({ where: { id: Number(equipmentId) } }) if (!equipment) { results.push({ equipment_id: equipmentId, status: 'failed', message: '资产不存在' }) failedCount++ continue } await prisma.equipment.update({ where: { id: equipment.id }, data: { department_id: department.id } }) results.push({ equipment_id: equipmentId, equipment_code: equipment.equipment_code, status: 'success', message: '转移成功' }) successCount++ } catch (error: any) { results.push({ equipment_id: equipmentId, status: 'failed', message: error.message }) failedCount++ } } // 记录操作日志 const userId = res.locals.user?.id const username = res.locals.user?.username || res.locals.user?.name || 'unknown' const successCodes = results.filter(r => r.status === 'success').map(r => r.equipment_code).join(', ') if (successCount > 0) { await logOperation( userId, username, '批量转移', `批量转移 ${successCount} 件资产到部门 ${department.name},资产编码:${successCodes}`, undefined, undefined, req.ip ) } successResponse(res, { success_count: successCount, failed_count: failedCount, results }, '批量转移完成') } catch (error: any) { errorResponse(res, error.message || '批量转移失败') } }