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 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: '13800138000', 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', 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(` Yi Fen Bao - Backend Service

Yi Fen Bao

Backend Service Running

Frontend Applications

GET/H5 App
GET/adminAdmin Panel
GET/wxWeChat Mini Program

API Endpoints

GET/api/pricesGet price list
POST/api/pricesAdd price
PUT/api/prices/:categoryUpdate price
DELETE/api/prices/:categoryDelete price
GET/api/ordersGet orders
POST/api/ordersCreate order
PUT/api/orders/:idUpdate order
DELETE/api/orders/:idDelete order
GET/api/usersGet users
POST/api/users/loginUser login
POST/api/usersRegister user
POST/api/admin/loginAdmin login
GET/api/citiesGet cities
GET/api/districts/:cityIdGet districts
GET/api/stations/:cityId/:districtIdGet stations
GET/api/site_users/:stationIdGet site users
`) }) app.get('/api/prices', (req, res) => { res.json({ date: new Date().toISOString().split('T')[0], prices: prices }) }) app.post('/api/prices', upload.single('icon'), (req, res) => { const { category, name, unit, price } = req.body let icon = '' if (req.file) { icon = '/api/icons/' + req.file.filename } const existing = prices.find(p => p.category === category) if (existing) { res.json({ success: false, message: 'Already exists' }) return } prices.push({ category, name, unit, price: parseFloat(price), icon }) saveData(pricesPath, prices) res.json({ success: true, message: 'Added' }) }) app.put('/api/prices/:category', upload.single('icon'), (req, res) => { const category = req.params.category const { name, unit, price, icon } = req.body const index = prices.findIndex(p => p.category === category) if (index === -1) { res.json({ success: false, message: 'Not found' }) return } let newIcon = icon || '' if (req.file) { newIcon = '/api/icons/' + req.file.filename } prices[index] = { ...prices[index], name, unit, price: parseFloat(price), icon: newIcon } saveData(pricesPath, prices) res.json({ success: true, message: 'Updated' }) }) app.delete('/api/prices/:category', (req, res) => { const category = req.params.category const index = prices.findIndex(p => p.category === category) if (index === -1) { res.json({ success: false, message: 'Not found' }) return } prices.splice(index, 1) saveData(pricesPath, prices) res.json({ success: true, message: 'Deleted' }) }) 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 { username, password } = req.body const adminUser = admin.find(a => a.username === username && a.password === password) if (adminUser) { res.json({ success: true, data: { username: adminUser.username } }) } else { res.json({ success: false, message: 'Wrong credentials' }) } }) 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', (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' }) }) app.listen(port, () => { console.log(`Server running on http://localhost:${port}`) })