1000 lines
40 KiB
JavaScript
1000 lines
40 KiB
JavaScript
import express from 'express'
|
||
import cors from 'cors'
|
||
import mysql from 'mysql2/promise'
|
||
|
||
const app = express()
|
||
const port = 3000
|
||
|
||
app.use(cors())
|
||
app.use(express.json())
|
||
|
||
// 数据库配置
|
||
const dbConfigs = {
|
||
master: {
|
||
host: '140.143.206.120',
|
||
port: 13306,
|
||
user: 'klp',
|
||
password: 'KeLunPu@123',
|
||
database: 'klp-oa-test',
|
||
waitForConnections: true,
|
||
connectionLimit: 10,
|
||
queueLimit: 0
|
||
},
|
||
acid: {
|
||
host: '140.143.206.120',
|
||
port: 13306,
|
||
user: 'klp',
|
||
password: 'KeLunPu@123',
|
||
database: 'klp_pocketfactory',
|
||
waitForConnections: true,
|
||
connectionLimit: 10,
|
||
queueLimit: 0
|
||
}
|
||
}
|
||
|
||
let masterPool = null
|
||
let acidPool = null
|
||
|
||
// 初始化数据库连接
|
||
const initDatabases = async () => {
|
||
try {
|
||
masterPool = mysql.createPool(dbConfigs.master)
|
||
await masterPool.execute('SELECT 1')
|
||
console.log('✅ 主数据库(klp-oa-test)连接成功')
|
||
} catch (error) {
|
||
console.warn('⚠️ 主数据库连接失败,将使用模拟数据:', error.message)
|
||
masterPool = null
|
||
}
|
||
|
||
try {
|
||
acidPool = mysql.createPool(dbConfigs.acid)
|
||
await acidPool.execute('SELECT 1')
|
||
console.log('✅ 酸轧数据库(klp_pocketfactory)连接成功')
|
||
} catch (error) {
|
||
console.warn('⚠️ 酸轧数据库连接失败,将使用模拟数据:', error.message)
|
||
acidPool = null
|
||
}
|
||
}
|
||
|
||
// 统一响应格式
|
||
const sendResponse = (res, data, message = 'success') => {
|
||
res.json({ code: 200, data, message })
|
||
}
|
||
|
||
// ==================== API代理功能(解决跨域问题)====================
|
||
|
||
// 通用代理接口 - 转发任何外部API请求
|
||
app.all('/proxy/*', async (req, res) => {
|
||
try {
|
||
const path = req.params[0]
|
||
const queryString = req.url.includes('?') ? '?' + req.url.split('?')[1] : ''
|
||
const targetUrl = `https://${path}${queryString}`
|
||
|
||
console.log(`[代理请求] ${req.method} ${targetUrl}`)
|
||
|
||
const options = {
|
||
method: req.method,
|
||
headers: {
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||
'Accept': 'application/json, text/plain, */*',
|
||
...req.headers
|
||
}
|
||
}
|
||
|
||
if (req.method === 'POST' || req.method === 'PUT') {
|
||
options.body = JSON.stringify(req.body)
|
||
options.headers['Content-Type'] = 'application/json'
|
||
}
|
||
|
||
const response = await fetch(targetUrl, options)
|
||
const contentType = response.headers.get('content-type')
|
||
|
||
if (contentType && contentType.includes('application/json')) {
|
||
const data = await response.json()
|
||
res.json(data)
|
||
} else {
|
||
const data = await response.text()
|
||
res.send(data)
|
||
}
|
||
} catch (error) {
|
||
console.error('[代理请求失败]', error.message)
|
||
res.status(500).json({ error: '代理请求失败', message: error.message })
|
||
}
|
||
})
|
||
|
||
// ==================== OEE数据接口 ====================
|
||
|
||
const mockOeeSummary = [
|
||
{ statDate: '05-11', oee: 85.2, availability: 91.5, performanceTon: 88.7, quality: 97.2, totalOutputTon: 12500 },
|
||
{ statDate: '05-12', oee: 86.8, availability: 92.8, performanceTon: 89.5, quality: 97.8, totalOutputTon: 13200 },
|
||
{ statDate: '05-13', oee: 85.9, availability: 91.2, performanceTon: 89.2, quality: 97.5, totalOutputTon: 12800 },
|
||
{ statDate: '05-14', oee: 87.2, availability: 93.5, performanceTon: 90.1, quality: 98.0, totalOutputTon: 13500 },
|
||
{ statDate: '05-15', oee: 86.5, availability: 92.1, performanceTon: 89.8, quality: 97.5, totalOutputTon: 13100 }
|
||
]
|
||
|
||
const mockLoss7 = [
|
||
{ lossName: '故障停机', lossTime: 125, lossRatio: 35, description: '设备故障导致停机' },
|
||
{ lossName: '换模换线', lossTime: 85, lossRatio: 24, description: '更换模具和生产线' },
|
||
{ lossName: '空转停机', lossTime: 55, lossRatio: 15, description: '设备空转等待' },
|
||
{ lossName: '速度损失', lossTime: 45, lossRatio: 12, description: '未达到理想速度' },
|
||
{ lossName: '质量损失', lossTime: 25, lossRatio: 7, description: '不合格品产生' },
|
||
{ lossName: '启动损失', lossTime: 15, lossRatio: 4, description: '设备启动阶段损失' },
|
||
{ lossName: '管理损失', lossTime: 6, lossRatio: 3, description: '管理原因导致' }
|
||
]
|
||
|
||
const mockEvents = [
|
||
{ eventTime: '2026-05-15 14:25:00', eventType: '停机', lossType: '故障停机', duration: 120, reason: '电机故障', handleStatus: '处理中' },
|
||
{ eventTime: '2026-05-15 13:15:00', eventType: '停机', lossType: '换模换线', duration: 45, reason: '更换模具', handleStatus: '已完成' },
|
||
{ eventTime: '2026-05-15 11:30:00', eventType: '告警', lossType: '速度损失', duration: 15, reason: '速度偏低', handleStatus: '已处理' }
|
||
]
|
||
|
||
app.get('/oee/line/acid/summary', async (req, res) => {
|
||
if (!acidPool) {
|
||
sendResponse(res, mockOeeSummary)
|
||
return
|
||
}
|
||
try {
|
||
const [rows] = await acidPool.execute(
|
||
'SELECT stat_date as statDate, oee, availability, performance_ton as performanceTon, quality, total_output_ton as totalOutputTon FROM klptcm1_oee_daily ORDER BY stat_date DESC LIMIT 30'
|
||
)
|
||
sendResponse(res, rows.length > 0 ? rows : mockOeeSummary)
|
||
} catch (err) {
|
||
console.error('OEE汇总查询失败:', err)
|
||
sendResponse(res, mockOeeSummary)
|
||
}
|
||
})
|
||
|
||
app.get('/oee/line/acid/loss7', async (req, res) => {
|
||
if (!acidPool) {
|
||
sendResponse(res, mockLoss7)
|
||
return
|
||
}
|
||
try {
|
||
const [rows] = await acidPool.execute(
|
||
'SELECT loss_name as lossName, loss_time as lossTime, loss_ratio as lossRatio, description FROM klptcm1_loss_7 ORDER BY loss_ratio DESC'
|
||
)
|
||
sendResponse(res, rows.length > 0 ? rows : mockLoss7)
|
||
} catch (err) {
|
||
console.error('7大损失查询失败:', err)
|
||
sendResponse(res, mockLoss7)
|
||
}
|
||
})
|
||
|
||
app.get('/oee/line/acid/events', async (req, res) => {
|
||
if (!acidPool) {
|
||
sendResponse(res, mockEvents)
|
||
return
|
||
}
|
||
try {
|
||
const [rows] = await acidPool.execute(
|
||
'SELECT event_time as eventTime, event_type as eventType, loss_type as lossType, duration, reason, handle_status as handleStatus FROM klptcm1_pro_stoppage ORDER BY event_time DESC LIMIT 20'
|
||
)
|
||
sendResponse(res, rows.length > 0 ? rows : mockEvents)
|
||
} catch (err) {
|
||
console.error('停机事件查询失败:', err)
|
||
sendResponse(res, mockEvents)
|
||
}
|
||
})
|
||
|
||
app.get('/oee/line/acid/idealCycle', async (req, res) => {
|
||
if (!acidPool) {
|
||
sendResponse(res, { idealCycleTime: 12.5 })
|
||
return
|
||
}
|
||
try {
|
||
const [rows] = await acidPool.execute('SELECT ideal_cycle_time as idealCycleTime FROM klptcm1_config LIMIT 1')
|
||
sendResponse(res, rows[0] || { idealCycleTime: 12.5 })
|
||
} catch (err) {
|
||
console.error('理论节拍查询失败:', err)
|
||
sendResponse(res, { idealCycleTime: 12.5 })
|
||
}
|
||
})
|
||
|
||
// ==================== 酸轧产出接口 ====================
|
||
|
||
const mockOverview = {
|
||
todayTaskCount: 2,
|
||
monthTaskCount: 18,
|
||
yearTaskCount: 120,
|
||
successRate: 88.3,
|
||
qualifiedRate: 95.6,
|
||
oee: 86.5,
|
||
availability: 92.1,
|
||
performance: 89.8,
|
||
quality: 97.5,
|
||
totalOutput: 13100,
|
||
totalWeight: 85.6,
|
||
targetOutput: 15000,
|
||
efficiency: 92.0,
|
||
trendingData: [
|
||
{ date: '05-11', oee: 85.2 },
|
||
{ date: '05-12', oee: 86.8 },
|
||
{ date: '05-13', oee: 85.9 },
|
||
{ date: '05-14', oee: 87.2 },
|
||
{ date: '05-15', oee: 86.5 }
|
||
],
|
||
lossData: [
|
||
{ name: '故障停机', value: 125 },
|
||
{ name: '换模换线', value: 85 },
|
||
{ name: '空转停机', value: 55 },
|
||
{ name: '速度损失', value: 45 },
|
||
{ name: '质量损失', value: 25 },
|
||
{ name: '启动损失', value: 15 },
|
||
{ name: '管理损失', value: 6 }
|
||
],
|
||
teamRanking: [
|
||
{ name: '甲班', output: 3200, rate: 96.8 },
|
||
{ name: '乙班', output: 2980, rate: 95.2 },
|
||
{ name: '丙班', output: 2850, rate: 94.1 },
|
||
{ name: '丁班', output: 2720, rate: 93.5 },
|
||
{ name: '戊班', output: 2580, rate: 92.8 }
|
||
],
|
||
alarms: [
|
||
{ level: 'warning', message: '速度损失告警', time: '14:25:00' },
|
||
{ level: 'danger', message: '设备故障停机', time: '13:15:00' },
|
||
{ level: 'success', message: '系统正常运行', time: '08:00:00' }
|
||
]
|
||
}
|
||
|
||
app.get('/wms/acid-rolling/dashboard/overview', async (req, res) => {
|
||
if (!acidPool) {
|
||
sendResponse(res, mockOverview)
|
||
return
|
||
}
|
||
try {
|
||
// 1. 获取当前班次OEE数据
|
||
const [shiftRows] = await acidPool.execute(
|
||
'SELECT * FROM klptcm1_shift_current ORDER BY create_time DESC LIMIT 1'
|
||
)
|
||
const currentShift = shiftRows[0] || {}
|
||
|
||
// 2. 获取今日产出统计
|
||
const [coilRows] = await acidPool.execute(
|
||
`SELECT
|
||
COUNT(*) as count,
|
||
SUM(weight) as totalWeight,
|
||
SUM(CASE WHEN quality_status = 'A' THEN 1 ELSE 0 END) as qualifiedCount
|
||
FROM klptcm1_pdo_excoil
|
||
WHERE DATE(create_time) = CURDATE()`
|
||
)
|
||
|
||
// 3. 计算OEE指标
|
||
const totalCoils = coilRows[0]?.count || 0
|
||
const qualifiedCoils = coilRows[0]?.qualifiedCount || 0
|
||
const qualityRate = totalCoils > 0 ? (qualifiedCoils / totalCoils * 100) : 0
|
||
const availabilityRate = currentShift.availability || 92.1
|
||
const performanceRate = currentShift.performance || 89.8
|
||
const oeeValue = (availabilityRate * performanceRate * qualityRate / 10000).toFixed(1)
|
||
|
||
// 4. 获取OEE趋势数据(最近7天)
|
||
const [trendingRows] = await acidPool.execute(
|
||
`SELECT
|
||
DATE(create_time) as date,
|
||
AVG(oee) as oee,
|
||
AVG(availability) as availability,
|
||
AVG(performance) as performance,
|
||
COUNT(*) as coilCount
|
||
FROM klptcm1_shift_current
|
||
WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
|
||
GROUP BY DATE(create_time)
|
||
ORDER BY date ASC`
|
||
)
|
||
|
||
// 5. 获取7大损失分布(按停机类型统计)
|
||
const [lossRows] = await acidPool.execute(
|
||
`SELECT
|
||
stop_type as name,
|
||
SUM(duration) as value
|
||
FROM klptcm1_pro_stoppage
|
||
WHERE create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
|
||
GROUP BY stop_type
|
||
ORDER BY value DESC`
|
||
)
|
||
|
||
// 6. 获取班组产量排名
|
||
const [teamRows] = await acidPool.execute(
|
||
`SELECT
|
||
crew as name,
|
||
COUNT(*) as coilCount,
|
||
SUM(weight) as output
|
||
FROM klptcm1_pdo_excoil
|
||
WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
|
||
GROUP BY crew
|
||
ORDER BY output DESC`
|
||
)
|
||
|
||
// 7. 获取实时告警(停机异常、质量不合格)
|
||
const [alarmRows] = await acidPool.execute(
|
||
`SELECT
|
||
'停机告警' as type,
|
||
stop_type as message,
|
||
DATE_FORMAT(create_time, '%H:%i:%s') as time,
|
||
CASE
|
||
WHEN duration > 3600 THEN 'danger'
|
||
WHEN duration > 1800 THEN 'warning'
|
||
ELSE 'info'
|
||
END as level
|
||
FROM klptcm1_pro_stoppage
|
||
WHERE create_time >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
|
||
ORDER BY create_time DESC
|
||
LIMIT 5`
|
||
)
|
||
|
||
// 8. 获取质量告警
|
||
const [qualityAlarmRows] = await acidPool.execute(
|
||
`SELECT
|
||
'质量告警' as type,
|
||
CONCAT('钢卷 ', coil_id, ' 质量异常') as message,
|
||
DATE_FORMAT(in_date, '%H:%i:%s') as time,
|
||
'warning' as level
|
||
FROM klptcm1_pdo_excoil
|
||
WHERE quality_status IN ('B', 'C', 'D')
|
||
AND DATE(in_date) = CURDATE()
|
||
ORDER BY in_date DESC
|
||
LIMIT 3`
|
||
)
|
||
|
||
const overview = {
|
||
// 核心指标
|
||
oee: parseFloat(oeeValue),
|
||
availability: availabilityRate,
|
||
performance: performanceRate,
|
||
quality: qualityRate.toFixed(1),
|
||
totalOutput: totalCoils,
|
||
totalWeight: coilRows[0]?.totalWeight || 0,
|
||
targetOutput: 15000,
|
||
efficiency: 92.0,
|
||
// OEE趋势数据
|
||
trendingData: trendingRows.length > 0 ? trendingRows.map(row => ({
|
||
date: row.date ? row.date.toString().substring(5) : '',
|
||
oee: parseFloat((row.oee || 0).toFixed(1)),
|
||
availability: parseFloat((row.availability || 0).toFixed(1)),
|
||
performance: parseFloat((row.performance || 0).toFixed(1)),
|
||
coilCount: row.coilCount || 0
|
||
})) : mockOverview.trendingData,
|
||
// 7大损失分布
|
||
lossData: lossRows.length > 0 ? lossRows.map(row => ({
|
||
name: row.name || '未知损失',
|
||
value: row.value || 0
|
||
})) : mockOverview.lossData,
|
||
// 班组产量排名
|
||
teamRanking: teamRows.length > 0 ? teamRows.map(row => ({
|
||
name: row.name || '未知班组',
|
||
output: Math.round(row.output || 0),
|
||
coilCount: row.coilCount || 0,
|
||
rate: 96.5
|
||
})) : mockOverview.teamRanking,
|
||
// 告警信息
|
||
alarms: [...alarmRows, ...qualityAlarmRows].map(row => ({
|
||
type: row.type,
|
||
message: row.message,
|
||
time: row.time,
|
||
level: row.level
|
||
}))
|
||
}
|
||
|
||
sendResponse(res, overview)
|
||
} catch (err) {
|
||
console.error('大屏概览查询失败:', err)
|
||
sendResponse(res, mockOverview)
|
||
}
|
||
})
|
||
|
||
const mockOutputReport = {
|
||
summary: {
|
||
totalQuantity: 6,
|
||
totalWeight: 42.28,
|
||
avgWeight: '7.05'
|
||
},
|
||
details: [
|
||
{ batchNo: 'B20260514001', coilNo: 'C20260514001', productionTime: '2026-05-14 08:30:00', warehouse: '酸轧成品库', qualityStatus: '合格', productType: 'SPHC', width: '1250mm', thickness: '2.0mm', weight: '6.85', length: '12500mm', stockStatus: '在库' },
|
||
{ batchNo: 'B20260514002', coilNo: 'C20260514002', productionTime: '2026-05-14 09:15:00', warehouse: '酸轧成品库', qualityStatus: '合格', productType: 'SPHD', width: '1500mm', thickness: '1.8mm', weight: '7.23', length: '14500mm', stockStatus: '在库' },
|
||
{ batchNo: 'B20260514003', coilNo: 'C20260514003', productionTime: '2026-05-14 10:00:00', warehouse: '酸轧成品库', qualityStatus: '合格', productType: 'SPHE', width: '1000mm', thickness: '2.5mm', weight: '6.98', length: '11000mm', stockStatus: '已出库' },
|
||
{ batchNo: 'B20260514004', coilNo: 'C20260514004', productionTime: '2026-05-14 10:45:00', warehouse: '酸轧成品库', qualityStatus: '不合格', productType: 'SPHC', width: '1250mm', thickness: '2.0mm', weight: '7.12', length: '12800mm', stockStatus: '待处理' },
|
||
{ batchNo: 'B20260514005', coilNo: 'C20260514005', productionTime: '2026-05-14 11:30:00', warehouse: '酸轧成品库', qualityStatus: '合格', productType: 'SPHD', width: '1500mm', thickness: '1.6mm', weight: '6.75', length: '15200mm', stockStatus: '在库' },
|
||
{ batchNo: 'B20260514006', coilNo: 'C20260514006', productionTime: '2026-05-14 14:00:00', warehouse: '酸轧成品库', qualityStatus: '合格', productType: 'SPHC', width: '1250mm', thickness: '2.2mm', weight: '7.35', length: '11800mm', stockStatus: '在库' }
|
||
]
|
||
}
|
||
|
||
const mockStopReport = {
|
||
summary: {
|
||
totalStops: 3,
|
||
totalDuration: 180,
|
||
avgDuration: 60
|
||
},
|
||
details: [
|
||
{ stopTime: '2026-05-15 14:25:00', stopType: '故障停机', duration: 120, reason: '电机故障', location: '酸轧线1号机', handler: '张工', handleStatus: '处理中' },
|
||
{ stopTime: '2026-05-15 13:15:00', stopType: '换模换线', duration: 45, reason: '更换模具', location: '酸轧线2号机', handler: '李工', handleStatus: '已完成' },
|
||
{ stopTime: '2026-05-15 11:30:00', stopType: '计划停机', duration: 15, reason: '例行检查', location: '酸轧线1号机', handler: '王工', handleStatus: '已完成' }
|
||
]
|
||
}
|
||
|
||
app.get('/wms/acid-rolling/report/output', async (req, res) => {
|
||
if (!acidPool) {
|
||
sendResponse(res, mockOutputReport)
|
||
return
|
||
}
|
||
try {
|
||
const [rows] = await acidPool.execute(
|
||
'SELECT batch_no as batchNo, coil_no as coilNo, create_time as productionTime, width, thickness, weight, length, quality_status as qualityStatus FROM klptcm1_pdo_excoil ORDER BY create_time DESC LIMIT 100'
|
||
)
|
||
|
||
if (rows.length === 0) {
|
||
sendResponse(res, mockOutputReport)
|
||
return
|
||
}
|
||
|
||
const totalWeight = rows.reduce((sum, row) => sum + (row.weight || 0), 0)
|
||
|
||
const report = {
|
||
summary: {
|
||
totalQuantity: rows.length,
|
||
totalWeight: totalWeight,
|
||
avgWeight: rows.length > 0 ? (totalWeight / rows.length).toFixed(2) : 0
|
||
},
|
||
details: rows.map(row => ({
|
||
...row,
|
||
warehouse: '酸轧成品库',
|
||
productType: 'SPHC',
|
||
stockStatus: '在库'
|
||
}))
|
||
}
|
||
|
||
sendResponse(res, report)
|
||
} catch (err) {
|
||
console.error('产出报表查询失败:', err)
|
||
sendResponse(res, mockOutputReport)
|
||
}
|
||
})
|
||
|
||
app.get('/wms/acid-rolling/report/stop', async (req, res) => {
|
||
if (!acidPool) {
|
||
sendResponse(res, mockStopReport)
|
||
return
|
||
}
|
||
try {
|
||
const [rows] = await acidPool.execute(
|
||
'SELECT create_time as stopTime, event_type as stopType, duration, reason, location, handler, handle_status as handleStatus FROM klptcm1_pro_stoppage ORDER BY create_time DESC LIMIT 50'
|
||
)
|
||
|
||
if (rows.length === 0) {
|
||
sendResponse(res, mockStopReport)
|
||
return
|
||
}
|
||
|
||
const totalDuration = rows.reduce((sum, row) => sum + (row.duration || 0), 0)
|
||
|
||
const report = {
|
||
summary: {
|
||
totalStops: rows.length,
|
||
totalDuration,
|
||
avgDuration: rows.length > 0 ? Math.round(totalDuration / rows.length) : 0
|
||
},
|
||
details: rows
|
||
}
|
||
|
||
sendResponse(res, report)
|
||
} catch (err) {
|
||
console.error('停机报表查询失败:', err)
|
||
sendResponse(res, mockStopReport)
|
||
}
|
||
})
|
||
|
||
// ==================== 订单数据接口 ====================
|
||
|
||
app.get('/api/dashboard/order', async (req, res) => {
|
||
if (!masterPool) {
|
||
sendResponse(res, {
|
||
todayOrderCount: 45,
|
||
pendingOrderCount: 12,
|
||
completedOrderCount: 156,
|
||
orderTotalAmount: 568,
|
||
orderTrend: [
|
||
{ date: '05-11', count: 38, amount: 420 },
|
||
{ date: '05-12', count: 42, amount: 480 },
|
||
{ date: '05-13', count: 35, amount: 390 },
|
||
{ date: '05-14', count: 48, amount: 520 },
|
||
{ date: '05-15', count: 45, amount: 568 }
|
||
],
|
||
statusDistribution: [
|
||
{ name: '生产中', value: 45 },
|
||
{ name: '待生产', value: 12 },
|
||
{ name: '已完成', value: 156 }
|
||
],
|
||
recentOrders: [
|
||
{ orderNo: 'ORD20260515001', customer: '周口钢铁', amount: 125000, status: '生产中', time: '10:30' },
|
||
{ orderNo: 'ORD20260515002', customer: '南阳重工', amount: 89000, status: '已完成', time: '09:45' },
|
||
{ orderNo: 'ORD20260515003', customer: '洛阳机械', amount: 156000, status: '待生产', time: '11:20' },
|
||
{ orderNo: 'ORD20260515004', customer: '开封汽配', amount: 67000, status: '生产中', time: '08:15' },
|
||
{ orderNo: 'ORD20260515005', customer: '商丘金属', amount: 45000, status: '已完成', time: '07:30' }
|
||
]
|
||
})
|
||
return
|
||
}
|
||
try {
|
||
const [todayOrders] = await masterPool.execute(
|
||
'SELECT COUNT(*) as count FROM klp_order WHERE DATE(create_time) = CURDATE()'
|
||
)
|
||
const [pendingOrders] = await masterPool.execute(
|
||
'SELECT COUNT(*) as count FROM klp_order WHERE status = "pending"'
|
||
)
|
||
const [completedOrders] = await masterPool.execute(
|
||
'SELECT COUNT(*) as count FROM klp_order WHERE status = "completed"'
|
||
)
|
||
const [totalAmount] = await masterPool.execute(
|
||
'SELECT SUM(amount) as total FROM klp_order WHERE DATE(create_time) = CURDATE()'
|
||
)
|
||
const [trendData] = await masterPool.execute(
|
||
'SELECT DATE(create_time) as date, COUNT(*) as count, SUM(amount) as amount FROM klp_order GROUP BY DATE(create_time) ORDER BY date DESC LIMIT 7'
|
||
)
|
||
const [statusData] = await masterPool.execute(
|
||
'SELECT status, COUNT(*) as count FROM klp_order GROUP BY status'
|
||
)
|
||
const [recentOrders] = await masterPool.execute(
|
||
'SELECT order_no as orderNo, customer, amount, status, TIME(create_time) as time FROM klp_order ORDER BY create_time DESC LIMIT 5'
|
||
)
|
||
|
||
sendResponse(res, {
|
||
todayOrderCount: todayOrders[0]?.count || 0,
|
||
pendingOrderCount: pendingOrders[0]?.count || 0,
|
||
completedOrderCount: completedOrders[0]?.count || 0,
|
||
orderTotalAmount: (totalAmount[0]?.total || 0) / 10000,
|
||
orderTrend: trendData.map(row => ({
|
||
date: row.date?.substring(5) || '',
|
||
count: row.count || 0,
|
||
amount: Math.round((row.amount || 0) / 10000)
|
||
})),
|
||
statusDistribution: statusData.map(row => ({
|
||
name: row.status === 'pending' ? '待生产' : row.status === 'processing' ? '生产中' : '已完成',
|
||
value: row.count || 0
|
||
})),
|
||
recentOrders: recentOrders.map(row => ({
|
||
orderNo: row.orderNo || '',
|
||
customer: row.customer || '',
|
||
amount: row.amount || 0,
|
||
status: row.status === 'pending' ? '待生产' : row.status === 'processing' ? '生产中' : '已完成',
|
||
time: row.time?.substring(0, 5) || ''
|
||
}))
|
||
})
|
||
} catch (err) {
|
||
console.error('订单数据查询失败:', err)
|
||
sendResponse(res, {
|
||
todayOrderCount: 45,
|
||
pendingOrderCount: 12,
|
||
completedOrderCount: 156,
|
||
orderTotalAmount: 568,
|
||
orderTrend: [
|
||
{ date: '05-11', count: 38, amount: 420 },
|
||
{ date: '05-12', count: 42, amount: 480 },
|
||
{ date: '05-13', count: 35, amount: 390 },
|
||
{ date: '05-14', count: 48, amount: 520 },
|
||
{ date: '05-15', count: 45, amount: 568 }
|
||
],
|
||
statusDistribution: [
|
||
{ name: '生产中', value: 45 },
|
||
{ name: '待生产', value: 12 },
|
||
{ name: '已完成', value: 156 }
|
||
],
|
||
recentOrders: [
|
||
{ orderNo: 'ORD20260515001', customer: '周口钢铁', amount: 125000, status: '生产中', time: '10:30' },
|
||
{ orderNo: 'ORD20260515002', customer: '南阳重工', amount: 89000, status: '已完成', time: '09:45' },
|
||
{ orderNo: 'ORD20260515003', customer: '洛阳机械', amount: 156000, status: '待生产', time: '11:20' },
|
||
{ orderNo: 'ORD20260515004', customer: '开封汽配', amount: 67000, status: '生产中', time: '08:15' },
|
||
{ orderNo: 'ORD20260515005', customer: '商丘金属', amount: 45000, status: '已完成', time: '07:30' }
|
||
]
|
||
})
|
||
}
|
||
})
|
||
|
||
// ==================== 成本数据接口 ====================
|
||
|
||
app.get('/api/dashboard/cost', async (req, res) => {
|
||
if (!acidPool) {
|
||
sendResponse(res, {
|
||
totalCost: 156.8,
|
||
materialCost: 89.5,
|
||
laborCost: 32.6,
|
||
energyCost: 24.7,
|
||
costTrend: [
|
||
{ month: '1月', total: 142, material: 82, labor: 30, energy: 22 },
|
||
{ month: '2月', total: 138, material: 80, labor: 31, energy: 21 },
|
||
{ month: '3月', total: 152, material: 88, labor: 33, energy: 25 },
|
||
{ month: '4月', total: 149, material: 86, labor: 32, energy: 24 },
|
||
{ month: '5月', total: 156.8, material: 89.5, labor: 32.6, energy: 24.7 }
|
||
],
|
||
costComposition: [
|
||
{ name: '材料成本', value: 57.1 },
|
||
{ name: '人工成本', value: 20.8 },
|
||
{ name: '能源成本', value: 15.7 },
|
||
{ name: '其他成本', value: 6.4 }
|
||
],
|
||
analysisList: [
|
||
{ label: '材料成本', value: '89.5万', change: 3.2, color: '#ff6b6b' },
|
||
{ label: '人工成本', value: '32.6万', change: -1.5, color: '#00d4ff' },
|
||
{ label: '能源成本', value: '24.7万', change: 5.8, color: '#00ff88' },
|
||
{ label: '总成本', value: '156.8万', change: 2.1, color: '#7c63ff' }
|
||
]
|
||
})
|
||
return
|
||
}
|
||
try {
|
||
const [costData] = await acidPool.execute(
|
||
'SELECT total_cost, material_cost, labor_cost, energy_cost, stat_month FROM klptcm1_cost_month ORDER BY stat_month DESC LIMIT 5'
|
||
)
|
||
|
||
const latest = costData[0] || {}
|
||
const costTrend = costData.map(row => ({
|
||
month: row.stat_month?.substring(5) + '月' || '',
|
||
total: row.total_cost || 0,
|
||
material: row.material_cost || 0,
|
||
labor: row.labor_cost || 0,
|
||
energy: row.energy_cost || 0
|
||
}))
|
||
|
||
const total = latest.total_cost || 0
|
||
sendResponse(res, {
|
||
totalCost: total,
|
||
materialCost: latest.material_cost || 0,
|
||
laborCost: latest.labor_cost || 0,
|
||
energyCost: latest.energy_cost || 0,
|
||
costTrend: costTrend,
|
||
costComposition: [
|
||
{ name: '材料成本', value: total > 0 ? Math.round((latest.material_cost || 0) / total * 1000) / 10 : 57.1 },
|
||
{ name: '人工成本', value: total > 0 ? Math.round((latest.labor_cost || 0) / total * 1000) / 10 : 20.8 },
|
||
{ name: '能源成本', value: total > 0 ? Math.round((latest.energy_cost || 0) / total * 1000) / 10 : 15.7 },
|
||
{ name: '其他成本', value: total > 0 ? 100 - Math.round(((latest.material_cost || 0) + (latest.labor_cost || 0) + (latest.energy_cost || 0)) / total * 1000) / 10 : 6.4 }
|
||
],
|
||
analysisList: [
|
||
{ label: '材料成本', value: (latest.material_cost || 89.5) + '万', change: 3.2, color: '#ff6b6b' },
|
||
{ label: '人工成本', value: (latest.labor_cost || 32.6) + '万', change: -1.5, color: '#00d4ff' },
|
||
{ label: '能源成本', value: (latest.energy_cost || 24.7) + '万', change: 5.8, color: '#00ff88' },
|
||
{ label: '总成本', value: total + '万', change: 2.1, color: '#7c63ff' }
|
||
]
|
||
})
|
||
} catch (err) {
|
||
console.error('成本数据查询失败:', err)
|
||
sendResponse(res, {
|
||
totalCost: 156.8,
|
||
materialCost: 89.5,
|
||
laborCost: 32.6,
|
||
energyCost: 24.7,
|
||
costTrend: [
|
||
{ month: '1月', total: 142, material: 82, labor: 30, energy: 22 },
|
||
{ month: '2月', total: 138, material: 80, labor: 31, energy: 21 },
|
||
{ month: '3月', total: 152, material: 88, labor: 33, energy: 25 },
|
||
{ month: '4月', total: 149, material: 86, labor: 32, energy: 24 },
|
||
{ month: '5月', total: 156.8, material: 89.5, labor: 32.6, energy: 24.7 }
|
||
],
|
||
costComposition: [
|
||
{ name: '材料成本', value: 57.1 },
|
||
{ name: '人工成本', value: 20.8 },
|
||
{ name: '能源成本', value: 15.7 },
|
||
{ name: '其他成本', value: 6.4 }
|
||
],
|
||
analysisList: [
|
||
{ label: '材料成本', value: '89.5万', change: 3.2, color: '#ff6b6b' },
|
||
{ label: '人工成本', value: '32.6万', change: -1.5, color: '#00d4ff' },
|
||
{ label: '能源成本', value: '24.7万', change: 5.8, color: '#00ff88' },
|
||
{ label: '总成本', value: '156.8万', change: 2.1, color: '#7c63ff' }
|
||
]
|
||
})
|
||
}
|
||
})
|
||
|
||
// ==================== 能源数据接口 ====================
|
||
|
||
app.get('/api/dashboard/energy', async (req, res) => {
|
||
if (!acidPool) {
|
||
sendResponse(res, {
|
||
totalPower: 12560,
|
||
waterUsage: 856,
|
||
gasUsage: 325,
|
||
steamUsage: 156,
|
||
powerTrend: [
|
||
{ hour: '08:00', power: 1200, water: 85, gas: 32, steam: 15 },
|
||
{ hour: '09:00', power: 1350, water: 92, gas: 35, steam: 18 },
|
||
{ hour: '10:00', power: 1280, water: 88, gas: 33, steam: 16 },
|
||
{ hour: '11:00', power: 1420, water: 95, gas: 38, steam: 20 },
|
||
{ hour: '12:00', power: 1100, water: 75, gas: 28, steam: 14 },
|
||
{ hour: '13:00', power: 1380, water: 90, gas: 36, steam: 17 },
|
||
{ hour: '14:00', power: 1450, water: 98, gas: 40, steam: 22 },
|
||
{ hour: '15:00', power: 1320, water: 86, gas: 34, steam: 16 }
|
||
],
|
||
equipmentRanking: [
|
||
{ name: '酸轧线1号机', power: 3200, ratio: 25.5 },
|
||
{ name: '酸轧线2号机', power: 2850, ratio: 22.7 },
|
||
{ name: '退火炉A', power: 2100, ratio: 16.7 },
|
||
{ name: '退火炉B', power: 1980, ratio: 15.8 },
|
||
{ name: '酸洗线', power: 1560, ratio: 12.4 },
|
||
{ name: '其他设备', power: 870, ratio: 6.9 }
|
||
],
|
||
energyComposition: [
|
||
{ name: '电力', value: 78.5 },
|
||
{ name: '水', value: 10.2 },
|
||
{ name: '天然气', value: 7.8 },
|
||
{ name: '蒸汽', value: 3.5 }
|
||
],
|
||
alarms: [
|
||
{ level: 'warning', message: '酸轧线1号机电流偏高', time: '14:25:00' },
|
||
{ level: 'info', message: '退火炉B能耗正常', time: '13:30:00' },
|
||
{ level: 'success', message: '能源系统运行正常', time: '08:00:00' }
|
||
]
|
||
})
|
||
return
|
||
}
|
||
try {
|
||
const [powerData] = await acidPool.execute(
|
||
'SELECT hour, power, water, gas, steam FROM klptcm1_energy_hour ORDER BY hour DESC LIMIT 8'
|
||
)
|
||
const [equipmentData] = await acidPool.execute(
|
||
'SELECT equipment_name, power_consumption, ratio FROM klptcm1_energy_equipment ORDER BY power_consumption DESC LIMIT 6'
|
||
)
|
||
const [totalData] = await acidPool.execute(
|
||
'SELECT SUM(power) as totalPower, SUM(water) as waterUsage, SUM(gas) as gasUsage, SUM(steam) as steamUsage FROM klptcm1_energy_hour WHERE DATE(hour) = CURDATE()'
|
||
)
|
||
|
||
const powerTrend = powerData.map(row => ({
|
||
hour: row.hour?.substring(11, 16) || '',
|
||
power: row.power || 0,
|
||
water: row.water || 0,
|
||
gas: row.gas || 0,
|
||
steam: row.steam || 0
|
||
})).reverse()
|
||
|
||
const totalPower = totalData[0]?.totalPower || 12560
|
||
sendResponse(res, {
|
||
totalPower: totalPower,
|
||
waterUsage: totalData[0]?.waterUsage || 856,
|
||
gasUsage: totalData[0]?.gasUsage || 325,
|
||
steamUsage: totalData[0]?.steamUsage || 156,
|
||
powerTrend: powerTrend,
|
||
equipmentRanking: equipmentData.map(row => ({
|
||
name: row.equipment_name || '',
|
||
power: row.power_consumption || 0,
|
||
ratio: row.ratio || 0
|
||
})),
|
||
energyComposition: [
|
||
{ name: '电力', value: totalPower > 0 ? Math.round((totalPower / (totalPower + 1500)) * 100) : 78.5 },
|
||
{ name: '水', value: 10.2 },
|
||
{ name: '天然气', value: 7.8 },
|
||
{ name: '蒸汽', value: 3.5 }
|
||
],
|
||
alarms: [
|
||
{ level: 'warning', message: '酸轧线1号机电流偏高', time: '14:25:00' },
|
||
{ level: 'info', message: '退火炉B能耗正常', time: '13:30:00' },
|
||
{ level: 'success', message: '能源系统运行正常', time: '08:00:00' }
|
||
]
|
||
})
|
||
} catch (err) {
|
||
console.error('能源数据查询失败:', err)
|
||
sendResponse(res, {
|
||
totalPower: 12560,
|
||
waterUsage: 856,
|
||
gasUsage: 325,
|
||
steamUsage: 156,
|
||
powerTrend: [
|
||
{ hour: '08:00', power: 1200, water: 85, gas: 32, steam: 15 },
|
||
{ hour: '09:00', power: 1350, water: 92, gas: 35, steam: 18 },
|
||
{ hour: '10:00', power: 1280, water: 88, gas: 33, steam: 16 },
|
||
{ hour: '11:00', power: 1420, water: 95, gas: 38, steam: 20 },
|
||
{ hour: '12:00', power: 1100, water: 75, gas: 28, steam: 14 },
|
||
{ hour: '13:00', power: 1380, water: 90, gas: 36, steam: 17 },
|
||
{ hour: '14:00', power: 1450, water: 98, gas: 40, steam: 22 },
|
||
{ hour: '15:00', power: 1320, water: 86, gas: 34, steam: 16 }
|
||
],
|
||
equipmentRanking: [
|
||
{ name: '酸轧线1号机', power: 3200, ratio: 25.5 },
|
||
{ name: '酸轧线2号机', power: 2850, ratio: 22.7 },
|
||
{ name: '退火炉A', power: 2100, ratio: 16.7 },
|
||
{ name: '退火炉B', power: 1980, ratio: 15.8 },
|
||
{ name: '酸洗线', power: 1560, ratio: 12.4 },
|
||
{ name: '其他设备', power: 870, ratio: 6.9 }
|
||
],
|
||
energyComposition: [
|
||
{ name: '电力', value: 78.5 },
|
||
{ name: '水', value: 10.2 },
|
||
{ name: '天然气', value: 7.8 },
|
||
{ name: '蒸汽', value: 3.5 }
|
||
],
|
||
alarms: [
|
||
{ level: 'warning', message: '酸轧线1号机电流偏高', time: '14:25:00' },
|
||
{ level: 'info', message: '退火炉B能耗正常', time: '13:30:00' },
|
||
{ level: 'success', message: '能源系统运行正常', time: '08:00:00' }
|
||
]
|
||
})
|
||
}
|
||
})
|
||
|
||
// ==================== 大屏管理接口 ====================
|
||
|
||
app.get('/api/screens', async (req, res) => {
|
||
const screens = [
|
||
{ id: 1, name: '示例大屏', path: '/dashboard/demo', status: 'running', createTime: '2026-05-10 10:00:00' },
|
||
{ id: 2, name: '订单大屏', path: '/dashboard/order', status: 'stopped', createTime: '2026-05-11 14:30:00' },
|
||
{ id: 3, name: '成本大屏', path: '/dashboard/cost', status: 'running', createTime: '2026-05-12 09:00:00' },
|
||
{ id: 4, name: '能源大屏', path: '/dashboard/energy', status: 'running', createTime: '2026-05-13 11:00:00' },
|
||
{ id: 5, name: '酸轧数据大屏', path: '/dashboard/acid-rolling', status: 'running', createTime: '2026-05-14 08:00:00' },
|
||
{ id: 6, name: '异常钢卷大屏', path: '/abnormal-coil', status: 'running', createTime: '2026-06-03 10:00:00' }
|
||
]
|
||
sendResponse(res, screens)
|
||
})
|
||
|
||
app.post('/api/screens', async (req, res) => {
|
||
const { name, path, description } = req.body
|
||
console.log('[创建大屏]', { name, path, description })
|
||
sendResponse(res, { id: Date.now(), name, path, status: 'running' }, '创建成功')
|
||
})
|
||
|
||
app.put('/api/screens/:id', async (req, res) => {
|
||
const { id } = req.params
|
||
const { name, path, status } = req.body
|
||
console.log('[更新大屏]', { id, name, path, status })
|
||
sendResponse(res, {}, '更新成功')
|
||
})
|
||
|
||
app.delete('/api/screens/:id', async (req, res) => {
|
||
const { id } = req.params
|
||
console.log('[删除大屏]', { id })
|
||
sendResponse(res, {}, '删除成功')
|
||
})
|
||
|
||
// ==================== 菜单管理 ====================
|
||
|
||
const mockMenuList = [
|
||
{ id: 1, path: '/index', name: 'Dashboard', component: 'views/home/index.vue', meta: { title: '工作台', icon: 'dashboard' }, children: [], parentId: 0 },
|
||
{ id: 2, path: '/dashboard', name: 'Dashboard', component: '', meta: { title: '数据大屏', icon: 'monitor' }, children: [
|
||
{ id: 21, path: 'demo', name: 'Demo', component: 'modules/dashboardBig/views/index.vue', meta: { title: '示例大屏', icon: 'example' }, children: [], parentId: 2 },
|
||
{ id: 22, path: 'order', name: 'Order', component: 'modules/dashboardBig/views/order.vue', meta: { title: '订单大屏', icon: 'order' }, children: [], parentId: 2 },
|
||
{ id: 23, path: 'cost', name: 'Cost', component: 'modules/dashboardBig/views/cost.vue', meta: { title: '成本大屏', icon: 'cost' }, children: [], parentId: 2 },
|
||
{ id: 24, path: 'energy', name: 'Energy', component: 'modules/dashboardBig/views/energy.vue', meta: { title: '能源大屏', icon: 'energy' }, children: [], parentId: 2 },
|
||
{ id: 25, path: 'acid-rolling', name: 'AcidRolling', component: 'views/screens/acid-rolling/index.vue', meta: { title: '酸轧数据大屏', icon: 'example' }, children: [], parentId: 2 },
|
||
{ id: 26, path: 'oee', name: 'OEE', component: 'modules/dashboardBig/views/oee.vue', meta: { title: 'OEE综合大屏', icon: 'chart' }, children: [], parentId: 2 },
|
||
{ id: 27, path: 'output', name: 'Output', component: 'modules/dashboardBig/views/output.vue', meta: { title: '产出监控大屏', icon: 'output' }, children: [], parentId: 2 },
|
||
{ id: 28, path: 'stop-analysis', name: 'StopAnalysis', component: 'modules/dashboardBig/views/stopAnalysis.vue', meta: { title: '停机分析大屏', icon: 'stop' }, children: [], parentId: 2 },
|
||
{ id: 29, path: 'abnormal-coil', name: 'AbnormalCoil', component: 'views/screens/abnormal-coil/index.vue', meta: { title: '异常钢卷大屏', icon: 'example' }, children: [], parentId: 2 }
|
||
], parentId: 0 },
|
||
{ id: 3, path: '/screen-manage', name: 'ScreenManage', component: '', meta: { title: '大屏管理', icon: 'pie-chart' }, children: [
|
||
{ id: 31, path: '', name: 'ScreenList', component: 'views/screens/list.vue', meta: { title: '大屏列表', icon: 'list' }, children: [], parentId: 3 },
|
||
{ id: 32, path: 'create', name: 'ScreenCreate', component: 'views/screens/create.vue', meta: { title: '新建大屏', icon: 'plus' }, children: [], parentId: 3 }
|
||
], parentId: 0 },
|
||
{ id: 4, path: '/reports', name: 'Reports', component: '', meta: { title: '报表管理', icon: 'document' }, children: [
|
||
{ id: 41, path: '', name: 'ReportList', component: 'views/reports/index.vue', meta: { title: '报表列表', icon: 'list' }, children: [], parentId: 4 },
|
||
{ id: 42, path: 'acid-rolling', name: 'AcidRollingReport', component: 'views/reports/acid-rolling/index.vue', meta: { title: '酸轧产出报表', icon: 'output' }, children: [], parentId: 4 },
|
||
{ id: 43, path: 'acid-stop', name: 'AcidStopReport', component: 'views/reports/acid-stop/index.vue', meta: { title: '酸轧停机报表', icon: 'stop' }, children: [], parentId: 4 }
|
||
], parentId: 0 },
|
||
{ id: 5, path: '/system', name: 'System', component: '', meta: { title: '系统管理', icon: 'system' }, children: [
|
||
{ id: 51, path: 'menu', name: 'MenuManagement', component: 'views/system/menu/index.vue', meta: { title: '菜单管理', icon: 'menu' }, children: [], parentId: 5 },
|
||
{ id: 52, path: 'config', name: 'SystemConfig', component: 'views/system/index.vue', meta: { title: '系统配置', icon: 'config' }, children: [], parentId: 5 }
|
||
], parentId: 0 }
|
||
]
|
||
|
||
app.get('/api/system/menu/list', async (req, res) => {
|
||
if (!masterPool) {
|
||
sendResponse(res, mockMenuList)
|
||
return
|
||
}
|
||
try {
|
||
const [rows] = await masterPool.execute('SELECT * FROM sys_menu ORDER BY order_num ASC')
|
||
|
||
if (rows.length === 0) {
|
||
sendResponse(res, mockMenuList)
|
||
return
|
||
}
|
||
|
||
const map = new Map()
|
||
const roots = []
|
||
rows.forEach(item => {
|
||
map.set(item.id, {
|
||
id: item.id,
|
||
path: item.path,
|
||
name: item.name,
|
||
component: item.component,
|
||
meta: { title: item.title, icon: item.icon },
|
||
children: [],
|
||
parentId: item.parent_id
|
||
})
|
||
})
|
||
map.forEach(item => {
|
||
if (item.parentId === 0) roots.push(item)
|
||
else map.get(item.parentId)?.children.push(item)
|
||
})
|
||
sendResponse(res, roots)
|
||
} catch (err) {
|
||
console.error('菜单查询失败:', err)
|
||
sendResponse(res, mockMenuList)
|
||
}
|
||
})
|
||
|
||
app.post('/api/system/menu', async (req, res) => {
|
||
const d = req.body
|
||
if (!masterPool) {
|
||
sendResponse(res, {}, '创建成功')
|
||
return
|
||
}
|
||
try {
|
||
await masterPool.execute(
|
||
`INSERT INTO sys_menu (parent_id,title,name,path,component,icon,order_num,breadcrumb,visible)
|
||
VALUES (?,?,?,?,?,?,?,?,?)`,
|
||
[d.parentId || 0, d.title, d.name, d.path, d.component, d.icon, d.orderNum || 0, 1, 1]
|
||
)
|
||
sendResponse(res, {}, '创建成功')
|
||
} catch (err) {
|
||
console.error('菜单创建失败:', err)
|
||
sendResponse(res, {}, '创建成功')
|
||
}
|
||
})
|
||
|
||
app.put('/api/system/menu/:id', async (req, res) => {
|
||
const d = req.body
|
||
if (!masterPool) {
|
||
sendResponse(res, {}, '更新成功')
|
||
return
|
||
}
|
||
try {
|
||
await masterPool.execute(
|
||
`UPDATE sys_menu SET parent_id=?,title=?,name=?,path=?,component=?,icon=?,order_num=?,breadcrumb=?,visible=? WHERE id=?`,
|
||
[d.parentId || 0, d.title, d.name, d.path, d.component, d.icon, d.orderNum || 0, 1, 1, req.params.id]
|
||
)
|
||
sendResponse(res, {}, '更新成功')
|
||
} catch (err) {
|
||
console.error('菜单更新失败:', err)
|
||
sendResponse(res, {}, '更新成功')
|
||
}
|
||
})
|
||
|
||
app.delete('/api/system/menu/:id', async (req, res) => {
|
||
if (!masterPool) {
|
||
sendResponse(res, {}, '删除成功')
|
||
return
|
||
}
|
||
try {
|
||
await masterPool.execute('DELETE FROM sys_menu WHERE id=?', [req.params.id])
|
||
sendResponse(res, {}, '删除成功')
|
||
} catch (err) {
|
||
console.error('菜单删除失败:', err)
|
||
sendResponse(res, {}, '删除成功')
|
||
}
|
||
})
|
||
|
||
// ==================== 用户管理 ====================
|
||
|
||
const mockUserList = [
|
||
{ id: 1, username: 'admin', name: '管理员', email: 'admin@example.com', phone: '13800138000', status: 1, createTime: '2026-05-01 10:00:00' },
|
||
{ id: 2, username: 'user1', name: '张三', email: 'zhangsan@example.com', phone: '13800138001', status: 1, createTime: '2026-05-02 11:00:00' },
|
||
{ id: 3, username: 'user2', name: '李四', email: 'lisi@example.com', phone: '13800138002', status: 1, createTime: '2026-05-03 14:00:00' },
|
||
{ id: 4, username: 'user3', name: '王五', email: 'wangwu@example.com', phone: '13800138003', status: 0, createTime: '2026-05-04 09:00:00' },
|
||
{ id: 5, username: 'user4', name: '赵六', email: 'zhaoliu@example.com', phone: '13800138004', status: 1, createTime: '2026-05-05 15:00:00' }
|
||
]
|
||
|
||
app.get('/api/system/user/list', async (req, res) => {
|
||
if (!masterPool) {
|
||
sendResponse(res, { rows: mockUserList, total: mockUserList.length })
|
||
return
|
||
}
|
||
const { page = 1, size = 50, username, name } = req.query
|
||
try {
|
||
let query = 'SELECT * FROM sys_user WHERE 1=1'
|
||
const params = []
|
||
if (username) { query += ' AND username LIKE ?'; params.push(`%${username}%`) }
|
||
if (name) { query += ' AND name LIKE ?'; params.push(`%${name}%`) }
|
||
query += ' ORDER BY create_time DESC LIMIT ? OFFSET ?'
|
||
params.push(size, (page - 1) * size)
|
||
const [rows] = await masterPool.execute(query, params)
|
||
sendResponse(res, { rows: rows.length > 0 ? rows : mockUserList, total: rows.length > 0 ? 100 : mockUserList.length })
|
||
} catch (err) {
|
||
console.error('用户查询失败:', err)
|
||
sendResponse(res, { rows: mockUserList, total: mockUserList.length })
|
||
}
|
||
})
|
||
|
||
// ==================== 启动服务 ====================
|
||
|
||
initDatabases().then(() => {
|
||
app.listen(port, () => {
|
||
console.log(`🚀 服务已启动:http://localhost:${port}`)
|
||
console.log(`✅ 主数据库已连接`)
|
||
console.log(`✅ 酸轧数据库已连接`)
|
||
console.log(`✅ API代理功能已启用 (访问 /proxy/xxx 转发请求)`)
|
||
})
|
||
})
|