Files
huishou/fix-server-phone.js
T
2026-07-27 14:07:26 +08:00

791 lines
28 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const { Client } = require('ssh2');
const conn = new Client();
conn.on('ready', () => {
conn.exec(`cat > /www/huishou/server/server.js << 'ENDOFFILE'
const express = require('express')
const cors = require('cors')
const bodyParser = require('body-parser')
const path = require('path')
const fs = require('fs')
const multer = require('multer')
const XLSX = require('xlsx')
const app = express()
const port = process.env.PORT || 4000
app.use(cors({
origin: '*',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization']
}))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(express.static(path.join(__dirname, '../dist/build/h5')))
app.use('/admin', express.static(path.join(__dirname, '../admin')))
app.use('/wx', express.static(path.join(__dirname, '../dist/build/mp-weixin')))
const iconDir = path.join(__dirname, 'icons')
if (!fs.existsSync(iconDir)) {
fs.mkdirSync(iconDir, { recursive: true })
}
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, iconDir)
},
filename: function (req, file, cb) {
const ext = path.extname(file.originalname)
const filename = 'icon_' + Date.now() + ext
cb(null, filename)
}
})
const upload = multer({
storage: storage,
limits: { fileSize: 5 * 1024 * 1024 },
fileFilter: function (req, file, cb) {
const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif']
if (allowedTypes.includes(file.mimetype)) {
cb(null, true)
} else {
cb(new Error('Only PNG, JPG, JPEG, GIF images are allowed'))
}
}
})
app.use('/api/icons', express.static(iconDir))
const dataDir = path.join(__dirname, 'data')
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, { recursive: true })
}
const pricesPath = path.join(dataDir, 'prices.json')
const ordersPath = path.join(dataDir, 'orders.json')
const usersPath = path.join(dataDir, 'users.json')
const citiesPath = path.join(dataDir, 'cities.json')
const districtsPath = path.join(dataDir, 'districts.json')
const stationsPath = path.join(dataDir, 'stations.json')
const siteUsersPath = path.join(dataDir, 'site_users.json')
const regionsPath = path.join(dataDir, 'regions.json')
const adminPath = path.join(dataDir, 'admin.json')
const loadData = (filePath, defaultData) => {
try {
if (fs.existsSync(filePath)) {
const content = fs.readFileSync(filePath, 'utf-8')
return JSON.parse(content)
}
} catch (err) {
console.error('Failed to load data:', err)
}
return defaultData
}
const saveData = (filePath, data) => {
try {
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8')
} catch (err) {
console.error('Failed to save data:', err)
}
}
const defaultPrices = [
{ category: '001', name: '纸类', unit: 'kg', price: 1.2, icon: '' },
{ category: '002', name: '金属', unit: 'kg', price: 2.5, icon: '' },
{ category: '003', name: '易拉罐', unit: 'piece', price: 0.1, icon: '' },
{ category: '004', name: '塑料', unit: 'kg', price: 3.0, icon: '' },
{ category: '005', name: '织物', unit: 'kg', price: 0.8, icon: '' },
{ category: '006', name: '塑料瓶', unit: 'piece', price: 0.05, icon: '' }
]
const defaultOrders = []
const defaultUsers = [
{ id: '1', name: 'Admin', phone: '18698102228', password: '123456', role: 'admin', createTime: '2024-01-01' },
{ id: '2', name: 'Collector Wang', phone: '18698102228', password: '123456', role: 'collector', createTime: '2024-01-15' }
]
const defaultCities = [
{ id: '1', name: 'Tianjin' },
{ id: '2', name: 'Beijing' },
{ id: '3', name: 'Shanghai' },
{ id: '4', name: 'Guangzhou' }
]
const defaultDistricts = [
{ id: 'd1', cityId: '1', name: 'Gaoxin' },
{ id: 'd2', cityId: '1', name: 'Xiqing' },
{ id: 'd3', cityId: '1', name: 'Baoshui' },
{ id: 'd4', cityId: '2', name: 'Dongcheng' },
{ id: 'd5', cityId: '2', name: 'Xicheng' },
{ id: 'd6', cityId: '2', name: 'Chaoyang' },
{ id: 'd7', cityId: '2', name: 'Haidian' },
{ id: 'd8', cityId: '3', name: 'Huangpu' },
{ id: 'd9', cityId: '3', name: 'Xuhui' },
{ id: 'd10', cityId: '3', name: 'Pudong' },
{ id: 'd11', cityId: '4', name: 'Tianhe' },
{ id: 'd12', cityId: '4', name: 'Haizhu' },
{ id: 'd13', cityId: '4', name: 'Yuexiu' }
]
const defaultStations = [
{ id: 's1', cityId: '1', districtId: 'd1', name: 'Ronghui Jingyuan' },
{ id: 's2', cityId: '1', districtId: 'd1', name: 'Jinhui Jiangshan Mingzhu' },
{ id: 's3', cityId: '1', districtId: 'd2', name: 'Huaxing Li' },
{ id: 's4', cityId: '1', districtId: 'd2', name: 'Huaxing Nan Li' },
{ id: 's5', cityId: '1', districtId: 'd3', name: 'Yijing Lanting' }
]
const defaultSiteUsers = [
{ id: 'su1', stationId: 's1', name: 'Xiwei Laoshan', type: 'merchant' },
{ id: 'su2', stationId: 's1', name: 'New Merchant', type: 'merchant' },
{ id: 'su3', stationId: 's1', name: 'New Individual', type: 'individual' },
{ id: 'su4', stationId: 's2', name: 'New Merchant', type: 'merchant' },
{ id: 'su5', stationId: 's2', name: 'New Individual', type: 'individual' },
{ id: 'su6', stationId: 's3', name: 'New Merchant', type: 'merchant' },
{ id: 'su7', stationId: 's3', name: 'New Individual', type: 'individual' },
{ id: 'su8', stationId: 's4', name: 'New Merchant', type: 'merchant' },
{ id: 'su9', stationId: 's4', name: 'New Individual', type: 'individual' },
{ id: 'su10', stationId: 's5', name: 'New Merchant', type: 'merchant' },
{ id: 'su11', stationId: 's5', name: 'New Individual', type: 'individual' }
]
const defaultRegions = []
const defaultAdmin = [
{ id: 1, username: 'admin', phone: '18698102228', password: '123456' }
]
let prices = loadData(pricesPath, defaultPrices)
let orders = loadData(ordersPath, defaultOrders)
let users = loadData(usersPath, defaultUsers)
let cities = loadData(citiesPath, defaultCities)
let districts = loadData(districtsPath, defaultDistricts)
let stations = loadData(stationsPath, defaultStations)
let siteUsers = loadData(siteUsersPath, defaultSiteUsers)
let regions = loadData(regionsPath, defaultRegions)
let admin = loadData(adminPath, defaultAdmin)
console.log('Database initialized')
console.log('Prices:', JSON.stringify(prices, null, 2))
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '../dist/build/h5/index.html'))
})
app.get('/api', (req, res) => {
res.send(\`
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Yi Fen Bao - Backend Service</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 1000px; margin: 0 auto; padding: 40px 20px; background: #f5f7fa; }
.header { background: linear-gradient(135deg, #3366ff 0%, #254edb 100%); color: white; padding: 40px; border-radius: 16rpx; margin-bottom: 30px; }
.header h1 { margin: 0; font-size: 28px; }
.header p { margin: 10px 0 0; opacity: 0.9; }
.card { background: white; border-radius: 12rpx; padding: 30px; margin-bottom: 20px; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.05); }
.card h2 { margin: 0 0 20px; font-size: 20px; color: #333; }
.endpoint { display: flex; align-items: center; padding: 12px 0; border-bottom: 1rpx solid #f0f0f0; }
.endpoint:last-child { border-bottom: none; }
.method { padding: 4px 12px; border-radius: 4rpx; font-size: 12px; font-weight: 600; margin-right: 16px; }
.method.get { background: #e6f7ff; color: #1890ff; }
.method.post { background: #f6ffed; color: #52c41a; }
.method.put { background: #fff7e6; color: #fa8c16; }
.method.delete { background: #fff2f0; color: #ff4d4f; }
.path { color: #333; font-family: monospace; }
</style>
</head>
<body>
<div class="header">
<h1>Yi Fen Bao</h1>
<p>Backend Service Running</p>
</div>
<div class="card">
<h2>Frontend Applications</h2>
<div class="endpoint"><span class="method get">GET</span><span class="path">/</span>H5 App</div>
<div class="endpoint"><span class="method get">GET</span><span class="path">/admin</span>Admin Panel</div>
<div class="endpoint"><span class="method get">GET</span><span class="path">/wx</span>WeChat Mini Program</div>
</div>
<div class="card">
<h2>API Endpoints</h2>
<div class="endpoint"><span class="method get">GET</span><span class="path">/api/prices</span>Get price list</div>
<div class="endpoint"><span class="method get">GET</span><span class="path">/api/orders</span>Get orders</div>
<div class="endpoint"><span class="method post">POST</span><span class="path">/api/orders</span>Create order</div>
<div class="endpoint"><span class="method put">PUT</span><span class="path">/api/orders/:id</span>Update order</div>
<div class="endpoint"><span class="method delete">DELETE</span><span class="path">/api/orders/:id</span>Delete order</div>
<div class="endpoint"><span class="method get">GET</span><span class="path">/api/users</span>Get users</div>
<div class="endpoint"><span class="method post">POST</span><span class="path">/api/users/login</span>User login</div>
<div class="endpoint"><span class="method post">POST</span><span class="path">/api/users</span>Register user</div>
<div class="endpoint"><span class="method post">POST</span><span class="path">/api/admin/login</span>Admin login</div>
<div class="endpoint"><span class="method get">GET</span><span class="path">/api/cities</span>Get cities</div>
<div class="endpoint"><span class="method get">GET</span><span class="path">/api/districts/:cityId</span>Get districts</div>
<div class="endpoint"><span class="method get">GET</span><span class="path">/api/stations/:cityId/:districtId</span>Get stations</div>
<div class="endpoint"><span class="method get">GET</span><span class="path">/api/site_users/:stationId</span>Get site users</div>
</div>
</body>
</html>
\`)
})
app.get('/api/prices', (req, res) => {
res.json({
date: new Date().toISOString().split('T')[0],
prices: prices
})
})
app.get('/api/statistics', (req, res) => {
const totalOrders = orders.length
const totalAmount = orders.reduce((sum, order) => sum + (order.totalAmount || 0), 0)
const totalWeight = orders.reduce((sum, order) => {
return sum + order.items.reduce((itemSum, item) => itemSum + (item.weight || 0), 0)
}, 0)
const avgAmount = totalOrders > 0 ? totalAmount / totalOrders : 0
const statusStats = {
pending: orders.filter(o => o.status === 'pending').length,
completed: orders.filter(o => o.status === 'completed').length,
cancelled: orders.filter(o => o.status === 'cancelled').length
}
const categoryStats = {}
orders.forEach(order => {
order.items.forEach(item => {
const category = item.category || item.name || '其他'
if (!categoryStats[category]) {
categoryStats[category] = { name: item.name || category, orders: 0, weight: 0, amount: 0 }
}
categoryStats[category].orders++
categoryStats[category].weight += item.weight || 0
categoryStats[category].amount += item.amount || 0
})
})
const categoryStatsArray = Object.values(categoryStats).map(cat => ({
...cat,
weightPercent: totalWeight > 0 ? (cat.weight / totalWeight * 100) : 0,
amountPercent: totalAmount > 0 ? (cat.amount / totalAmount * 100) : 0
}))
res.json({
data: {
totalOrders,
totalAmount,
totalWeight,
avgAmount,
statusStats,
categoryStats: categoryStatsArray
}
})
})
app.get('/api/orders', (req, res) => {
const processedOrders = orders.map(order => {
const items = order.items.map(item => {
const weight = item.weight || item.count || 0
const price = item.price || 0
return {
...item,
amount: item.amount !== undefined ? item.amount : weight * price
}
})
const totalAmount = order.totalAmount !== undefined
? order.totalAmount
: items.reduce((sum, item) => sum + (item.amount || 0), 0)
return {
...order,
items,
totalAmount
}
})
res.json({ data: processedOrders.sort((a, b) => new Date(b.createTime) - new Date(a.createTime)) })
})
app.get('/api/orders/:id', (req, res) => {
const order = orders.find(o => o.id === req.params.id)
res.json(order || {})
})
app.post('/api/orders', (req, res) => {
const { items, totalWeight, totalPrice, city, district, community, user, phone, address } = req.body
const id = Date.now().toString() + Math.random().toString(36).substr(2, 9)
const createTime = new Date().toISOString()
const processedItems = items.map(item => {
const weight = item.weight || item.count || 0
const price = item.price || 0
return {
...item,
amount: weight * price
}
})
const totalAmount = processedItems.reduce((sum, item) => sum + (item.amount || 0), 0)
const newOrder = { id, items: processedItems, totalAmount, totalWeight, totalPrice, city, district, community, user, phone, address, status: 'pending', createTime }
orders.push(newOrder)
saveData(ordersPath, orders)
res.json({ success: true, order: newOrder })
})
app.put('/api/orders/:id', (req, res) => {
const id = req.params.id
const index = orders.findIndex(o => o.id === id)
if (index === -1) {
res.json({ success: false, message: 'Not found' })
return
}
const { status, items, totalWeight, totalPrice } = req.body
if (status !== undefined) orders[index].status = status
if (items !== undefined) orders[index].items = items
if (totalWeight !== undefined) orders[index].totalWeight = totalWeight
if (totalPrice !== undefined) orders[index].totalPrice = totalPrice
saveData(ordersPath, orders)
res.json({ success: true, message: 'Updated' })
})
app.delete('/api/orders/:id', (req, res) => {
const id = req.params.id
const index = orders.findIndex(o => o.id === id)
if (index === -1) {
res.json({ success: false, message: 'Not found' })
return
}
orders.splice(index, 1)
saveData(ordersPath, orders)
res.json({ success: true, message: 'Deleted' })
})
app.get('/api/users', (req, res) => {
res.json(users.map(u => ({ id: u.id, name: u.name, phone: u.phone, role: u.role, createTime: u.createTime })))
})
app.post('/api/users/login', (req, res) => {
const { phone, password } = req.body
const user = users.find(u => u.phone === phone && u.password === password)
if (user) {
res.json({ success: true, data: { id: user.id, name: user.name, phone: user.phone, role: user.role } })
} else {
res.json({ success: false, message: 'Wrong credentials' })
}
})
app.post('/api/users', (req, res) => {
const { name, phone, password, role = 'collector' } = req.body
const existing = users.find(u => u.phone === phone)
if (existing) {
res.json({ success: false, message: 'Phone exists' })
return
}
const id = Date.now().toString()
const createTime = new Date().toISOString().split('T')[0]
const newUser = { id, name, phone, password, role, createTime }
users.push(newUser)
saveData(usersPath, users)
res.json({ success: true, data: newUser })
})
app.post('/api/admin/login', (req, res) => {
const { phone, password } = req.body
const adminUser = admin.find(a => a.phone === phone && a.password === password)
if (adminUser) {
res.json({ success: true, data: { id: adminUser.id, username: adminUser.username, phone: adminUser.phone } })
} else {
res.json({ success: false, message: '手机号或密码错误' })
}
})
app.get('/api/cities', (req, res) => {
res.json(cities)
})
app.get('/api/districts/:cityId', (req, res) => {
res.json(districts.filter(d => d.cityId === req.params.cityId))
})
app.get('/api/stations/:cityId/:districtId', (req, res) => {
res.json(stations.filter(s => s.cityId === req.params.cityId && s.districtId === req.params.districtId))
})
app.get('/api/site_users/:stationId', (req, res) => {
res.json(siteUsers.filter(u => u.stationId === req.params.stationId))
})
app.get('/api/regions/cities', (req, res) => {
res.json({ success: true, data: cities })
})
app.post('/api/regions/cities', (req, res) => {
const { name } = req.body
if (!name) {
res.json({ success: false, message: '请输入城市名称' })
return
}
const existing = cities.find(c => c.name === name)
if (existing) {
res.json({ success: true, data: existing })
return
}
const newCity = { id: Date.now().toString(36), name }
cities.push(newCity)
saveData(citiesPath, cities)
res.json({ success: true, data: newCity })
})
app.put('/api/regions/cities/:id', (req, res) => {
const { id } = req.params
const { name } = req.body
const index = cities.findIndex(c => c.id === id)
if (index === -1) {
res.json({ success: false, message: '城市不存在' })
return
}
cities[index].name = name
saveData(citiesPath, cities)
res.json({ success: true, data: cities[index] })
})
app.delete('/api/regions/cities/:id', (req, res) => {
const { id } = req.params
const index = cities.findIndex(c => c.id === id)
if (index === -1) {
res.json({ success: false, message: '城市不存在' })
return
}
cities.splice(index, 1)
districts = districts.filter(d => d.cityId !== id)
stations = stations.filter(s => s.cityId !== id)
siteUsers = siteUsers.filter(u => {
const station = stations.find(s => s.id === u.stationId)
return station !== undefined
})
saveData(citiesPath, cities)
saveData(districtsPath, districts)
saveData(stationsPath, stations)
saveData(siteUsersPath, siteUsers)
res.json({ success: true })
})
app.get('/api/regions/districts', (req, res) => {
const result = districts.map(d => {
const city = cities.find(c => c.id === d.cityId)
return { ...d, cityName: city ? city.name : '' }
})
res.json({ success: true, data: result })
})
app.post('/api/regions/districts', (req, res) => {
const { cityId, name } = req.body
if (!cityId || !name) {
res.json({ success: false, message: '请填写完整信息' })
return
}
const existing = districts.find(d => d.cityId === cityId && d.name === name)
if (existing) {
res.json({ success: true, data: existing })
return
}
const newDistrict = { id: Date.now().toString(36), cityId, name }
districts.push(newDistrict)
saveData(districtsPath, districts)
res.json({ success: true, data: newDistrict })
})
app.put('/api/regions/districts/:id', (req, res) => {
const { id } = req.params
const { cityId, name } = req.body
const index = districts.findIndex(d => d.id === id)
if (index === -1) {
res.json({ success: false, message: '区域不存在' })
return
}
districts[index] = { ...districts[index], cityId, name }
saveData(districtsPath, districts)
res.json({ success: true, data: districts[index] })
})
app.delete('/api/regions/districts/:id', (req, res) => {
const { id } = req.params
const index = districts.findIndex(d => d.id === id)
if (index === -1) {
res.json({ success: false, message: '区域不存在' })
return
}
districts.splice(index, 1)
stations = stations.filter(s => s.districtId !== id)
siteUsers = siteUsers.filter(u => {
const station = stations.find(s => s.id === u.stationId)
return station !== undefined
})
saveData(districtsPath, districts)
saveData(stationsPath, stations)
saveData(siteUsersPath, siteUsers)
res.json({ success: true })
})
app.get('/api/regions/stations', (req, res) => {
const result = stations.map(s => {
const city = cities.find(c => c.id === s.cityId)
const district = districts.find(d => d.id === s.districtId)
return { ...s, cityName: city ? city.name : '', districtName: district ? district.name : '' }
})
res.json({ success: true, data: result })
})
app.post('/api/regions/stations', (req, res) => {
const { cityId, districtId, name } = req.body
if (!cityId || !districtId || !name) {
res.json({ success: false, message: '请填写完整信息' })
return
}
const newStation = { id: Date.now().toString(36), cityId, districtId, name }
stations.push(newStation)
saveData(stationsPath, stations)
res.json({ success: true, data: newStation })
})
app.put('/api/regions/stations/:id', (req, res) => {
const { id } = req.params
const { cityId, districtId, name } = req.body
const index = stations.findIndex(s => s.id === id)
if (index === -1) {
res.json({ success: false, message: '站点不存在' })
return
}
stations[index] = { ...stations[index], cityId, districtId, name }
saveData(stationsPath, stations)
res.json({ success: true, data: stations[index] })
})
app.delete('/api/regions/stations/:id', (req, res) => {
const { id } = req.params
const index = stations.findIndex(s => s.id === id)
if (index === -1) {
res.json({ success: false, message: '站点不存在' })
return
}
stations.splice(index, 1)
siteUsers = siteUsers.filter(u => u.stationId !== id)
saveData(stationsPath, stations)
saveData(siteUsersPath, siteUsers)
res.json({ success: true })
})
app.get('/api/regions/site-users', (req, res) => {
const result = siteUsers.map(u => {
const station = stations.find(s => s.id === u.stationId)
return { ...u, stationName: station ? station.name : '' }
})
res.json({ success: true, data: result })
})
app.post('/api/regions/site-users', (req, res) => {
const { stationId, name, type = 'merchant' } = req.body
if (!stationId || !name) {
res.json({ success: false, message: '请填写完整信息' })
return
}
const newUser = { id: Date.now().toString(36), stationId, name, type }
siteUsers.push(newUser)
saveData(siteUsersPath, siteUsers)
res.json({ success: true, data: newUser })
})
app.put('/api/regions/site-users/:id', (req, res) => {
const { id } = req.params
const { stationId, name, type } = req.body
const index = siteUsers.findIndex(u => u.id === id)
if (index === -1) {
res.json({ success: false, message: '用户不存在' })
return
}
siteUsers[index] = { ...siteUsers[index], stationId, name, type }
saveData(siteUsersPath, siteUsers)
res.json({ success: true, data: siteUsers[index] })
})
app.delete('/api/regions/site-users/:id', (req, res) => {
const { id } = req.params
const index = siteUsers.findIndex(u => u.id === id)
if (index === -1) {
res.json({ success: false, message: '用户不存在' })
return
}
siteUsers.splice(index, 1)
saveData(siteUsersPath, siteUsers)
res.json({ success: true })
})
app.get('/api/regions', (req, res) => {
res.json(regions)
})
app.post('/api/regions', (req, res) => {
const { city, district, community } = req.body
const existing = regions.find(r => r.city === city && r.district === district && r.community === community)
if (existing) {
res.json({ success: true, message: 'Exists' })
return
}
regions.push({ id: regions.length + 1, city, district, community })
saveData(regionsPath, regions)
res.json({ success: true, message: 'Added' })
})
app.delete('/api/regions/:id', (req, res) => {
const id = parseInt(req.params.id)
const index = regions.findIndex(r => r.id === id)
if (index === -1) {
res.json({ success: false, message: 'Not found' })
return
}
regions.splice(index, 1)
saveData(regionsPath, regions)
res.json({ success: true, message: 'Deleted' })
})
const importStorage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, dataDir)
},
filename: function (req, file, cb) {
const ext = path.extname(file.originalname)
const filename = 'import_' + Date.now() + ext
cb(null, filename)
}
})
const importUpload = multer({
storage: importStorage,
limits: { fileSize: 10 * 1024 * 1024 },
fileFilter: function (req, file, cb) {
const allowedTypes = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel']
const allowedExtensions = ['.xlsx', '.xls']
const ext = path.extname(file.originalname).toLowerCase()
if (allowedTypes.includes(file.mimetype) || allowedExtensions.includes(ext)) {
cb(null, true)
} else {
cb(new Error('只支持 Excel 文件 (.xlsx, .xls)'))
}
}
})
app.post('/api/regions/import', importUpload.single('file'), (req, res) => {
try {
if (!req.file) {
res.json({ success: false, message: '请选择文件' })
return
}
const filePath = path.join(dataDir, req.file.filename)
const workbook = XLSX.readFile(filePath)
const sheetName = workbook.SheetNames[0]
const worksheet = workbook.Sheets[sheetName]
const jsonData = XLSX.utils.sheet_to_json(worksheet)
let addedCities = 0
let addedDistricts = 0
let addedStations = 0
let skippedCities = 0
let skippedDistricts = 0
let skippedStations = 0
for (const row of jsonData) {
const cityName = row['城市'] || row['city'] || row['City'] || ''
const districtName = row['区域'] || row['district'] || row['District'] || row['区'] || ''
const stationName = row['站点'] || row['station'] || row['Station'] || row['社区'] || row['community'] || ''
if (!cityName) continue
let cityId = cities.find(c => c.name === cityName)?.id
if (!cityId) {
cityId = Date.now().toString(36) + Math.random().toString(36).substr(2, 5)
cities.push({ id: cityId, name: cityName })
addedCities++
} else {
skippedCities++
}
if (districtName) {
let districtId = districts.find(d => d.cityId === cityId && d.name === districtName)?.id
if (!districtId) {
districtId = Date.now().toString(36) + Math.random().toString(36).substr(2, 5)
districts.push({ id: districtId, cityId, name: districtName })
addedDistricts++
} else {
skippedDistricts++
}
if (stationName) {
const existsStation = stations.find(s => s.cityId === cityId && s.districtId === districtId && s.name === stationName)
if (!existsStation) {
const stationId = Date.now().toString(36) + Math.random().toString(36).substr(2, 5)
stations.push({ id: stationId, cityId, districtId, name: stationName })
addedStations++
} else {
skippedStations++
}
}
}
}
saveData(citiesPath, cities)
saveData(districtsPath, districts)
saveData(stationsPath, stations)
fs.unlinkSync(filePath)
res.json({
success: true,
message: `导入成功新增城市:${addedCities}, 新增区域:${addedDistricts}, 新增站点:${addedStations}`,
data: {
addedCities,
addedDistricts,
addedStations,
skippedCities,
skippedDistricts,
skippedStations
}
})
} catch (error) {
console.error('导入失败:', error)
res.json({ success: false, message: '导入失败: ' + error.message })
}
})
app.listen(port, '0.0.0.0', () => {
console.log(`Server running on http://0.0.0.0:${port}`)
})
ENDOFFILE`, (err, stream) => {
if (err) { console.log('ERR:', err); conn.end(); return; }
stream.on('close', () => {
console.log('=== 重启后端容器 ===');
conn.exec('cd /www/huishou && docker-compose restart backend', (err2, stream2) => {
if (err2) { console.log('ERR2:', err2); conn.end(); return; }
stream2.on('data', (d) => console.log(d.toString()));
stream2.on('close', () => {
setTimeout(() => {
console.log('\n=== 测试管理员登录 ===');
conn.exec('curl -s -X POST http://127.0.0.1:4000/api/admin/login -H "Content-Type: application/json" -d \'{"phone":"18698102228","password":"123456"}\'', (err3, stream3) => {
if (err3) { console.log('ERR3:', err3); conn.end(); return; }
stream3.on('data', (d) => console.log(d.toString()));
stream3.on('close', () => conn.end());
});
}, 3000);
});
});
});
});
});
conn.connect({ host: 'hs.yifenbao.cn', port: 22, username: 'root', password: 'Torch1112' });