const express = require('express'); const db = require('../db'); const { authenticate, requireAdmin } = require('../middleware/auth'); const LedgerService = require('../services/ledgerService'); const router = express.Router(); router.get('/', async (req, res) => { try { const result = await db.query(` SELECT * FROM subcontractors ORDER BY created_at DESC LIMIT 50 `); // 为每个分包商获取联系人和收款信息 const subcontractorsWithDetails = await Promise.all( result.rows.map(async (subcontractor) => { // 获取联系人信息 const contactsResult = await db.query( `SELECT * FROM contacts WHERE entity_id = $1 AND entity_type = 'subcontractor' ORDER BY is_primary DESC`, [subcontractor.id] ); const contacts = contactsResult.rows.map(contact => ({ name: contact.name || '未命名', position: contact.position || '', phone: contact.phone || '', is_primary: contact.is_primary === true })); // 获取收款信息 const paymentInfosResult = await db.query( `SELECT * FROM subcontractor_payment_infos WHERE subcontractor_id = $1 ORDER BY is_primary DESC`, [subcontractor.id] ); const paymentInfos = paymentInfosResult.rows.map(payment => ({ id: payment.id, account_name: payment.account_name, bank_account: payment.bank_account, bank_name: payment.bank_name, qr_code: payment.qr_code, is_primary: payment.is_primary === true })); return { ...subcontractor, scope: subcontractor.specialty, features: subcontractor.category, country: subcontractor.country, remark: subcontractor.remark || '', contacts: contacts.length > 0 ? contacts : [], payment_infos: paymentInfos.length > 0 ? paymentInfos : [] }; }) ); res.json({ success: true, data: subcontractorsWithDetails, count: subcontractorsWithDetails.length }); } catch (error) { console.error('获取分包商失败:', error); res.status(500).json({ success: false, message: '获取分包商失败', error: process.env.NODE_ENV === 'development' ? error.message : '操作失败' }); } }); router.get('/:id', async (req, res) => { try { const { id } = req.params; // 获取分包商基本信息 const subcontractorResult = await db.query(` SELECT * FROM subcontractors WHERE id = $1 `, [id]); if (subcontractorResult.rows.length > 0) { const subcontractor = subcontractorResult.rows[0]; // 获取分包商的所有联系人 const contactsResult = await db.query(` SELECT * FROM contacts WHERE entity_id = $1 AND entity_type = 'subcontractor' ORDER BY is_primary DESC `, [id]); // 转换联系人数据结构 const contacts = contactsResult.rows.map(contact => ({ name: contact.name || '未命名', position: contact.position || '', phone: contact.phone || '', is_primary: contact.is_primary === true })); // 获取分包商的所有收款信息 const paymentInfosResult = await db.query(` SELECT * FROM subcontractor_payment_infos WHERE subcontractor_id = $1 ORDER BY is_primary DESC `, [id]); // 转换收款信息数据结构 const paymentInfos = paymentInfosResult.rows.map(payment => ({ id: payment.id, account_name: payment.account_name, bank_account: payment.bank_account, bank_name: payment.bank_name, qr_code: payment.qr_code, is_primary: payment.is_primary === true })); // 获取业务台账 const ledger = await LedgerService.getSubcontractorLedger(id); // 转换数据结构以匹配前端期望 const formattedSubcontractor = { id: subcontractor.id, code: `SC${String(subcontractor.id).padStart(4, '0')}`, name: subcontractor.name, scope: subcontractor.specialty || '', features: subcontractor.category || '', country: subcontractor.country || '', contacts: contacts.length > 0 ? contacts : [], payment_infos: paymentInfos.length > 0 ? paymentInfos : [], remark: subcontractor.remark || '', total_contract_amount: ledger.summary.total_contract_amount, total_paid: ledger.summary.total_paid_amount, total_payable: ledger.summary.total_unpaid_amount, ledger: ledger, created_at: subcontractor.created_at }; res.json({ success: true, data: formattedSubcontractor }); } else { res.status(404).json({ success: false, message: '分包商不存在' }); } } catch (error) { console.error('获取分包商详情失败:', error); res.status(500).json({ success: false, message: '获取分包商详情失败', error: process.env.NODE_ENV === 'development' ? error.message : '操作失败' }); } }); router.post('/', async (req, res) => { try { const { name, scope, features, country, remark, contacts, payment_infos } = req.body; // 从contacts中获取主联系人信息 const primaryContact = contacts?.find(c => c.is_primary) || contacts?.[0]; const contact_person = primaryContact?.name || ''; const phone = primaryContact?.phone || ''; const email = ''; // 前端没有email字段 const address = ''; // 前端没有address字段 const result = await db.query( `INSERT INTO subcontractors (name, address, contact_person, phone, email, specialty, category, country, remark, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING id`, [name, address, contact_person, phone, email, scope, features, country || '', remark || ''] ); const subcontractorId = (result.rows[0]?.id || result.rows?.[0]?.id); // 插入联系人数据 if (contacts && contacts.length > 0) { for (const contactItem of contacts) { await db.query( `INSERT INTO contacts (entity_id, entity_type, name, position, phone, is_primary, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING id`, [subcontractorId, 'subcontractor', contactItem.name, contactItem.position, contactItem.phone, contactItem.is_primary ? true : false] ); } } // 插入收款信息数据 if (payment_infos && payment_infos.length > 0) { for (const paymentItem of payment_infos) { await db.query( `INSERT INTO subcontractor_payment_infos (subcontractor_id, account_name, bank_name, bank_account, qr_code, is_primary, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING id`, [subcontractorId, paymentItem.account_name, paymentItem.bank_name, paymentItem.bank_account, paymentItem.qr_code, paymentItem.is_primary ? true : false] ); } } res.json({ success: true, message: '分包商创建成功', data: { id: subcontractorId, code: `SC${String(subcontractorId).padStart(4, '0')}`, name, scope, features, country, contacts: contacts || [], payment_infos: payment_infos || [], remark, total_contract_amount: 0, total_paid: 0, total_payable: 0, created_at: new Date().toISOString() } }); } catch (error) { console.error('创建分包商失败:', error); res.status(500).json({ success: false, message: '创建分包商失败', error: process.env.NODE_ENV === 'development' ? error.message : '操作失败' }); } }); router.put('/:id', async (req, res) => { try { const { id } = req.params; const { name, scope, features, country, remark, contacts, payment_infos } = req.body; // 从contacts中获取主联系人信息 const primaryContact = contacts?.find(c => c.is_primary) || contacts?.[0]; const contact_person = primaryContact?.name || ''; const phone = primaryContact?.phone || ''; const email = ''; // 前端没有email字段 const address = ''; // 前端没有address字段 await db.query( `UPDATE subcontractors SET name = $1, address = $2, contact_person = $3, phone = $4, email = $5, specialty = $6, category = $7, country = $8, remark = $9, updated_at = CURRENT_TIMESTAMP WHERE id = $10`, [name, address, contact_person, phone, email, scope, features, country || '', remark || '', id] ); // 删除旧的联系人数据 await db.query(`DELETE FROM contacts WHERE entity_id = $1 AND entity_type = 'subcontractor'`, [id]); // 插入新的联系人数据 if (contacts && contacts.length > 0) { for (const contactItem of contacts) { await db.query( `INSERT INTO contacts (entity_id, entity_type, name, position, phone, is_primary, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING id`, [id, 'subcontractor', contactItem.name, contactItem.position, contactItem.phone, contactItem.is_primary ? true : false] ); } } // 删除旧的收款信息数据 await db.query(`DELETE FROM subcontractor_payment_infos WHERE subcontractor_id = $1`, [id]); // 插入新的收款信息数据 if (payment_infos && payment_infos.length > 0) { for (const paymentItem of payment_infos) { await db.query( `INSERT INTO subcontractor_payment_infos (subcontractor_id, account_name, bank_name, bank_account, qr_code, is_primary, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING id`, [id, paymentItem.account_name, paymentItem.bank_name, paymentItem.bank_account, paymentItem.qr_code, paymentItem.is_primary ? true : false] ); } } res.json({ success: true, message: '分包商更新成功', data: { id, code: `SC${String(id).padStart(4, '0')}`, name, scope, features, country, contacts: contacts || [], payment_infos: payment_infos || [], remark, total_contract_amount: 0, total_paid: 0, total_payable: 0, created_at: new Date().toISOString() } }); } catch (error) { console.error('更新分包商失败:', error); res.status(500).json({ success: false, message: '更新分包商失败', error: process.env.NODE_ENV === 'development' ? error.message : '操作失败' }); } }); router.delete('/:id', async (req, res) => { try { const { id } = req.params; // 先删除关联的联系人数据 await db.query(`DELETE FROM contacts WHERE entity_id = $1 AND entity_type = 'subcontractor'`, [id]); // 再删除分包商数据 const result = await db.query(`DELETE FROM subcontractors WHERE id = $1`, [id]); if (result.rowCount > 0) { res.json({ success: true, message: '分包商删除成功' }); } else { res.status(404).json({ success: false, message: '分包商不存在' }); } } catch (error) { console.error('删除分包商失败:', error); res.status(500).json({ success: false, message: '删除分包商失败', error: process.env.NODE_ENV === 'development' ? error.message : '操作失败' }); } }); // ==================== 分包商收款信息管理接口 ==================== // 获取分包商的所有收款信息 router.get('/:id/payment-infos', async (req, res) => { try { const { id } = req.params; const result = await db.query(` SELECT * FROM subcontractor_payment_infos WHERE subcontractor_id = $1 ORDER BY is_primary DESC, created_at DESC `, [id]); const paymentInfos = result.rows.map(payment => ({ id: payment.id, account_name: payment.account_name, bank_account: payment.bank_account, bank_name: payment.bank_name, qr_code: payment.qr_code, is_primary: payment.is_primary === true, created_at: payment.created_at, updated_at: payment.updated_at })); res.json({ success: true, data: paymentInfos, count: paymentInfos.length }); } catch (error) { console.error('获取分包商收款信息失败:', error); res.status(500).json({ success: false, message: '获取分包商收款信息失败', error: process.env.NODE_ENV === 'development' ? error.message : '操作失败' }); } }); // 添加分包商收款信息 router.post('/:id/payment-infos', async (req, res) => { try { const { id } = req.params; const { account_name, bank_account, bank_name, qr_code, is_primary } = req.body; // 如果设置为默认账户,先取消其他账户的默认状态 if (is_primary) { await db.query( 'UPDATE subcontractor_payment_infos SET is_primary = 0 WHERE subcontractor_id = $1', [id] ); } const result = await db.query( `INSERT INTO subcontractor_payment_infos (subcontractor_id, account_name, bank_account, bank_name, qr_code, is_primary, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING id`, [id, account_name, bank_account, bank_name, qr_code, is_primary ? true : false] ); const paymentInfoId = (result.rows[0]?.id || result.rows?.[0]?.id); res.json({ success: true, message: '收款信息添加成功', data: { id: paymentInfoId, subcontractor_id: id, account_name, bank_account, bank_name, qr_code, is_primary: !!is_primary, created_at: new Date().toISOString(), updated_at: new Date().toISOString() } }); } catch (error) { console.error('添加分包商收款信息失败:', error); res.status(500).json({ success: false, message: '添加分包商收款信息失败', error: process.env.NODE_ENV === 'development' ? error.message : '操作失败' }); } }); // 更新分包商收款信息 router.put('/payment-infos/:infoId', async (req, res) => { try { const { infoId } = req.params; const { account_name, bank_account, bank_name, qr_code, is_primary } = req.body; // 先获取当前收款信息以获取分包商ID const currentInfoResult = await db.query( 'SELECT subcontractor_id FROM subcontractor_payment_infos WHERE id = $1', [infoId] ); if (currentInfoResult.rows.length === 0) { return res.status(404).json({ success: false, message: '收款信息不存在' }); } const subcontractorId = currentInfoResult.rows[0].subcontractor_id; // 如果设置为默认账户,先取消其他账户的默认状态 if (is_primary) { await db.query( 'UPDATE subcontractor_payment_infos SET is_primary = 0 WHERE subcontractor_id = $1 AND id != $2', [subcontractorId, infoId] ); } await db.query( `UPDATE subcontractor_payment_infos SET account_name = $1, bank_account = $2, bank_name = $3, qr_code = $4, is_primary = $5, updated_at = CURRENT_TIMESTAMP WHERE id = $6`, [account_name, bank_account, bank_name, qr_code, is_primary ? true : false, infoId] ); res.json({ success: true, message: '收款信息更新成功', data: { id: infoId, subcontractor_id: subcontractorId, account_name, bank_account, bank_name, qr_code, is_primary: !!is_primary, updated_at: new Date().toISOString() } }); } catch (error) { console.error('更新分包商收款信息失败:', error); res.status(500).json({ success: false, message: '更新分包商收款信息失败', error: process.env.NODE_ENV === 'development' ? error.message : '操作失败' }); } }); // 删除分包商收款信息 router.delete('/payment-infos/:infoId', async (req, res) => { try { const { infoId } = req.params; const result = await db.query('DELETE FROM subcontractor_payment_infos WHERE id = $1', [infoId]); if (result.rowCount > 0) { res.json({ success: true, message: '收款信息删除成功' }); } else { res.status(404).json({ success: false, message: '收款信息不存在' }); } } catch (error) { console.error('删除分包商收款信息失败:', error); res.status(500).json({ success: false, message: '删除分包商收款信息失败', error: process.env.NODE_ENV === 'development' ? error.message : '操作失败' }); } }); module.exports = router; router.get('/:id/events', async (req, res) => { try { const result = await db.query( 'SELECT * FROM subcontractor_events WHERE subcontractor_id = $1 ORDER BY event_date DESC, created_at DESC', [req.params.id] ); res.json({ success: true, data: result.rows }); } catch (error) { console.error('获取分包商事件失败:', error); res.status(500).json({ success: false, message: '获取分包商事件失败' }); } }); router.post('/:id/events', async (req, res) => { try { const { id } = req.params; const { project_id, event_type, score_change, event_date, title, description, created_by } = req.body; if (!event_type || !score_change || !event_date || !title) { return res.status(400).json({ success: false, message: '缺少必填字段' }); } if (!['positive', 'negative'].includes(event_type)) { return res.status(400).json({ success: false, message: '事件类型必须是positive或negative' }); } const actualChange = event_type === 'positive' ? Math.abs(score_change) : -Math.abs(score_change); const result = await db.query( `INSERT INTO subcontractor_events (subcontractor_id, project_id, event_type, score_change, event_date, title, description, created_by) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id`, [id, project_id || null, event_type, actualChange, event_date, title, description || '', created_by || null] ); await db.query( 'UPDATE subcontractors SET score = GREATEST(0, score + $1) WHERE id = $2', [actualChange, id] ); res.json({ success: true, message: '事件添加成功', data: { id: result.rows[0].id } }); } catch (error) { console.error('添加分包商事件失败:', error); res.status(500).json({ success: false, message: '添加分包商事件失败' }); } }); router.delete('/:id/events/:eventId', async (req, res) => { try { const { id, eventId } = req.params; const eventResult = await db.query('SELECT score_change FROM subcontractor_events WHERE id = $1 AND subcontractor_id = $2', [eventId, id]); if (eventResult.rows.length === 0) { return res.status(404).json({ success: false, message: '事件不存在' }); } const scoreChange = eventResult.rows[0].score_change; await db.query('DELETE FROM subcontractor_events WHERE id = $1', [eventId]); await db.query( 'UPDATE subcontractors SET score = GREATEST(0, LEAST(200, score - $1)) WHERE id = $2', [scoreChange, id] ); res.json({ success: true, message: '事件删除成功' }); } catch (error) { console.error('删除分包商事件失败:', error); res.status(500).json({ success: false, message: '删除分包商事件失败' }); } });