Files
jurong_circle_agent_black/routes/upload.js
2025-09-04 10:49:10 +08:00

474 lines
12 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 express = require('express');
const router = express.Router();
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const { agentAuth } = require('../middleware/agentAuth');
const { logger } = require('../config/logger');
// 确保上传目录存在
const uploadDir = path.join(__dirname, '../uploads');
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
// 配置multer存储
const storage = multer.diskStorage({
destination: function (req, file, cb) {
// 根据文件类型创建不同的子目录
let subDir = 'others';
if (file.fieldname === 'avatar') {
subDir = 'avatars';
} else if (file.fieldname === 'qr_code') {
subDir = 'qrcodes';
} else if (file.fieldname === 'id_card_front' || file.fieldname === 'id_card_back') {
subDir = 'idcards';
} else if (file.fieldname === 'business_license') {
subDir = 'licenses';
}
const targetDir = path.join(uploadDir, subDir);
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
cb(null, targetDir);
},
filename: function (req, file, cb) {
// 生成唯一文件名
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
const ext = path.extname(file.originalname);
cb(null, file.fieldname + '-' + uniqueSuffix + ext);
}
});
// 文件过滤器
const fileFilter = (req, file, cb) => {
// 允许的图片类型
const allowedImageTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp'];
if (allowedImageTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('只允许上传图片文件 (JPEG, PNG, GIF, WebP)'), false);
}
};
// 配置multer
const upload = multer({
storage: storage,
fileFilter: fileFilter,
limits: {
fileSize: 5 * 1024 * 1024, // 5MB限制
files: 10 // 最多10个文件
}
});
/**
* 单文件上传
* POST /api/upload/single
*/
router.post('/single', agentAuth, upload.single('file'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({
success: false,
message: '请选择要上传的文件'
});
}
const file = req.file;
const relativePath = path.relative(path.join(__dirname, '../'), file.path).replace(/\\/g, '/');
const fileUrl = `/uploads/${relativePath.split('/').slice(1).join('/')}`;
logger.info('文件上传成功', {
agentId: req.agent.id,
filename: file.filename,
originalname: file.originalname,
size: file.size,
path: fileUrl
});
res.json({
success: true,
message: '文件上传成功',
data: {
filename: file.filename,
originalname: file.originalname,
size: file.size,
mimetype: file.mimetype,
url: fileUrl,
path: fileUrl // 兼容前端可能使用的字段名
}
});
} catch (error) {
logger.error('文件上传失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: error.message || '文件上传失败'
});
}
});
/**
* 多文件上传
* POST /api/upload/multiple
*/
router.post('/multiple', agentAuth, upload.array('files', 10), async (req, res) => {
try {
if (!req.files || req.files.length === 0) {
return res.status(400).json({
success: false,
message: '请选择要上传的文件'
});
}
const files = req.files.map(file => {
const relativePath = path.relative(path.join(__dirname, '../'), file.path).replace(/\\/g, '/');
const fileUrl = `/uploads/${relativePath.split('/').slice(1).join('/')}`;
return {
filename: file.filename,
originalname: file.originalname,
size: file.size,
mimetype: file.mimetype,
url: fileUrl,
path: fileUrl
};
});
logger.info('多文件上传成功', {
agentId: req.agent.id,
count: files.length,
totalSize: req.files.reduce((sum, file) => sum + file.size, 0)
});
res.json({
success: true,
message: `成功上传${files.length}个文件`,
data: files
});
} catch (error) {
logger.error('多文件上传失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: error.message || '文件上传失败'
});
}
});
/**
* 头像上传
* POST /api/upload/avatar
*/
router.post('/avatar', agentAuth, upload.single('avatar'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({
success: false,
message: '请选择头像文件'
});
}
const file = req.file;
const relativePath = path.relative(path.join(__dirname, '../'), file.path).replace(/\\/g, '/');
const fileUrl = `/uploads/${relativePath.split('/').slice(1).join('/')}`;
logger.info('头像上传成功', {
agentId: req.agent.id,
filename: file.filename,
size: file.size
});
res.json({
success: true,
message: '头像上传成功',
data: {
filename: file.filename,
originalname: file.originalname,
size: file.size,
url: fileUrl,
path: fileUrl
}
});
} catch (error) {
logger.error('头像上传失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: error.message || '头像上传失败'
});
}
});
/**
* 收款码上传
* POST /api/upload/qrcode
*/
router.post('/qrcode', agentAuth, upload.single('qr_code'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({
success: false,
message: '请选择收款码文件'
});
}
const file = req.file;
const relativePath = path.relative(path.join(__dirname, '../'), file.path).replace(/\\/g, '/');
const fileUrl = `/uploads/${relativePath.split('/').slice(1).join('/')}`;
logger.info('收款码上传成功', {
agentId: req.agent.id,
filename: file.filename,
size: file.size
});
res.json({
success: true,
message: '收款码上传成功',
data: {
filename: file.filename,
originalname: file.originalname,
size: file.size,
url: fileUrl,
path: fileUrl
}
});
} catch (error) {
logger.error('收款码上传失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: error.message || '收款码上传失败'
});
}
});
/**
* 身份证上传
* POST /api/upload/idcard
*/
router.post('/idcard', agentAuth, upload.fields([
{ name: 'id_card_front', maxCount: 1 },
{ name: 'id_card_back', maxCount: 1 }
]), async (req, res) => {
try {
if (!req.files || (!req.files.id_card_front && !req.files.id_card_back)) {
return res.status(400).json({
success: false,
message: '请选择身份证文件'
});
}
const result = {};
if (req.files.id_card_front) {
const file = req.files.id_card_front[0];
const relativePath = path.relative(path.join(__dirname, '../'), file.path).replace(/\\/g, '/');
const fileUrl = `/uploads/${relativePath.split('/').slice(1).join('/')}`;
result.front = {
filename: file.filename,
originalname: file.originalname,
size: file.size,
url: fileUrl,
path: fileUrl
};
}
if (req.files.id_card_back) {
const file = req.files.id_card_back[0];
const relativePath = path.relative(path.join(__dirname, '../'), file.path).replace(/\\/g, '/');
const fileUrl = `/uploads/${relativePath.split('/').slice(1).join('/')}`;
result.back = {
filename: file.filename,
originalname: file.originalname,
size: file.size,
url: fileUrl,
path: fileUrl
};
}
logger.info('身份证上传成功', {
agentId: req.agent.id,
hasFront: !!result.front,
hasBack: !!result.back
});
res.json({
success: true,
message: '身份证上传成功',
data: result
});
} catch (error) {
logger.error('身份证上传失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: error.message || '身份证上传失败'
});
}
});
/**
* 营业执照上传
* POST /api/upload/license
*/
router.post('/license', agentAuth, upload.single('business_license'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({
success: false,
message: '请选择营业执照文件'
});
}
const file = req.file;
const relativePath = path.relative(path.join(__dirname, '../'), file.path).replace(/\\/g, '/');
const fileUrl = `/uploads/${relativePath.split('/').slice(1).join('/')}`;
logger.info('营业执照上传成功', {
agentId: req.agent.id,
filename: file.filename,
size: file.size
});
res.json({
success: true,
message: '营业执照上传成功',
data: {
filename: file.filename,
originalname: file.originalname,
size: file.size,
url: fileUrl,
path: fileUrl
}
});
} catch (error) {
logger.error('营业执照上传失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: error.message || '营业执照上传失败'
});
}
});
/**
* 删除文件
* DELETE /api/upload/file
*/
router.delete('/file', agentAuth, async (req, res) => {
try {
const { path: filePath } = req.body;
if (!filePath) {
return res.status(400).json({
success: false,
message: '请提供文件路径'
});
}
// 构建完整的文件路径
const fullPath = path.join(__dirname, '../', filePath);
// 检查文件是否存在
if (!fs.existsSync(fullPath)) {
return res.status(404).json({
success: false,
message: '文件不存在'
});
}
// 删除文件
fs.unlinkSync(fullPath);
logger.info('文件删除成功', {
agentId: req.agent.id,
filePath: filePath
});
res.json({
success: true,
message: '文件删除成功'
});
} catch (error) {
logger.error('文件删除失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: '文件删除失败'
});
}
});
// 错误处理中间件
router.use((error, req, res, next) => {
if (error instanceof multer.MulterError) {
if (error.code === 'LIMIT_FILE_SIZE') {
return res.status(400).json({
success: false,
message: '文件大小超过限制最大5MB'
});
}
if (error.code === 'LIMIT_FILE_COUNT') {
return res.status(400).json({
success: false,
message: '文件数量超过限制最多10个'
});
}
if (error.code === 'LIMIT_UNEXPECTED_FILE') {
return res.status(400).json({
success: false,
message: '意外的文件字段'
});
}
}
res.status(500).json({
success: false,
message: error.message || '文件上传失败'
});
});
module.exports = router;