逻辑修改.
This commit is contained in:
parent
ece1ef09ad
commit
9e45704c66
|
|
@ -54,7 +54,7 @@
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="amount" label="提现金额" width="120" align="right" sortable="custom">
|
<el-table-column prop="amount" label="提现金额" width="120" align="right" sortable="custom">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span class="amount">¥{{ row.amount }}</span>
|
<span class="amount">{{ getCurrencySymbol(row.currency) }}{{ row.amount }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="paymentMethod" label="支付方式" width="100" align="center">
|
<el-table-column prop="paymentMethod" label="支付方式" width="100" align="center">
|
||||||
|
|
@ -135,7 +135,12 @@
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="提现金额">
|
<el-descriptions-item label="提现金额">
|
||||||
<span class="amount-large">¥{{ withdrawalDetails.amount }}</span>
|
<span class="amount-large">{{ getCurrencySymbol(withdrawalDetails.currency) }}{{ withdrawalDetails.amount }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="货币类型">
|
||||||
|
<el-tag :type="withdrawalDetails.currency === 'PHP' ? 'warning' : 'primary'" size="small">
|
||||||
|
{{ withdrawalDetails.currency === 'PHP' ? '比索 (PHP)' : '人民币 (CNY)' }}
|
||||||
|
</el-tag>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="支付方式">
|
<el-descriptions-item label="支付方式">
|
||||||
<el-tag :type="getPaymentMethodType(withdrawalDetails.paymentMethod)" size="small">
|
<el-tag :type="getPaymentMethodType(withdrawalDetails.paymentMethod)" size="small">
|
||||||
|
|
@ -224,7 +229,7 @@
|
||||||
<template #default>
|
<template #default>
|
||||||
<div class="alert-content">
|
<div class="alert-content">
|
||||||
<p>提现单号:<strong>{{ approveForm.withdrawalNo }}</strong></p>
|
<p>提现单号:<strong>{{ approveForm.withdrawalNo }}</strong></p>
|
||||||
<p>提现金额:<strong class="amount">¥{{ approveForm.amount }}</strong></p>
|
<p>提现金额:<strong class="amount">{{ getCurrencySymbol(approveForm.currency) }}{{ approveForm.amount }}</strong></p>
|
||||||
<p>用户:<strong>{{ approveForm.userName }}</strong></p>
|
<p>用户:<strong>{{ approveForm.userName }}</strong></p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -265,7 +270,7 @@
|
||||||
<template #default>
|
<template #default>
|
||||||
<div class="alert-content">
|
<div class="alert-content">
|
||||||
<p>提现单号:<strong>{{ rejectForm.withdrawalNo }}</strong></p>
|
<p>提现单号:<strong>{{ rejectForm.withdrawalNo }}</strong></p>
|
||||||
<p>提现金额:<strong class="amount">¥{{ rejectForm.amount }}</strong></p>
|
<p>提现金额:<strong class="amount">{{ getCurrencySymbol(rejectForm.currency) }}{{ rejectForm.amount }}</strong></p>
|
||||||
<p>用户:<strong>{{ rejectForm.userName }}</strong></p>
|
<p>用户:<strong>{{ rejectForm.userName }}</strong></p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -328,6 +333,7 @@ const approveForm = reactive({
|
||||||
withdrawalId: '',
|
withdrawalId: '',
|
||||||
withdrawalNo: '',
|
withdrawalNo: '',
|
||||||
amount: '',
|
amount: '',
|
||||||
|
currency: 'CNY',
|
||||||
userName: '',
|
userName: '',
|
||||||
notes: ''
|
notes: ''
|
||||||
})
|
})
|
||||||
|
|
@ -340,6 +346,7 @@ const rejectForm = reactive({
|
||||||
withdrawalId: '',
|
withdrawalId: '',
|
||||||
withdrawalNo: '',
|
withdrawalNo: '',
|
||||||
amount: '',
|
amount: '',
|
||||||
|
currency: 'CNY',
|
||||||
userName: '',
|
userName: '',
|
||||||
reason: ''
|
reason: ''
|
||||||
})
|
})
|
||||||
|
|
@ -448,6 +455,7 @@ function handleApprove(row) {
|
||||||
approveForm.withdrawalId = row.id
|
approveForm.withdrawalId = row.id
|
||||||
approveForm.withdrawalNo = row.withdrawalNo
|
approveForm.withdrawalNo = row.withdrawalNo
|
||||||
approveForm.amount = row.amount
|
approveForm.amount = row.amount
|
||||||
|
approveForm.currency = row.currency || 'CNY'
|
||||||
approveForm.userName = row.user?.nickname || row.user?.realName || '-'
|
approveForm.userName = row.user?.nickname || row.user?.realName || '-'
|
||||||
approveForm.notes = ''
|
approveForm.notes = ''
|
||||||
approveDialogVisible.value = true
|
approveDialogVisible.value = true
|
||||||
|
|
@ -487,6 +495,7 @@ function handleReject(row) {
|
||||||
rejectForm.withdrawalId = row.id
|
rejectForm.withdrawalId = row.id
|
||||||
rejectForm.withdrawalNo = row.withdrawalNo
|
rejectForm.withdrawalNo = row.withdrawalNo
|
||||||
rejectForm.amount = row.amount
|
rejectForm.amount = row.amount
|
||||||
|
rejectForm.currency = row.currency || 'CNY'
|
||||||
rejectForm.userName = row.user?.nickname || row.user?.realName || '-'
|
rejectForm.userName = row.user?.nickname || row.user?.realName || '-'
|
||||||
rejectForm.reason = ''
|
rejectForm.reason = ''
|
||||||
rejectDialogVisible.value = true
|
rejectDialogVisible.value = true
|
||||||
|
|
@ -561,6 +570,10 @@ function getStatusTagType(status) {
|
||||||
return types[status] || 'info'
|
return types[status] || 'info'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCurrencySymbol(currency) {
|
||||||
|
return currency === 'PHP' ? '₱' : '¥'
|
||||||
|
}
|
||||||
|
|
||||||
function getPaymentMethodLabel(method) {
|
function getPaymentMethodLabel(method) {
|
||||||
const labels = {
|
const labels = {
|
||||||
wechat: '微信',
|
wechat: '微信',
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@ const fs = require('fs').promises;
|
||||||
const { uploadDir } = require('../middleware/upload');
|
const { uploadDir } = require('../middleware/upload');
|
||||||
const logger = require('../config/logger');
|
const logger = require('../config/logger');
|
||||||
|
|
||||||
|
// 图片处理阈值:小于此大小的图片不进行压缩处理(500KB)
|
||||||
|
const SKIP_PROCESSING_SIZE = 500 * 1024;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload and process image
|
* Upload and process image
|
||||||
* POST /api/v1/upload/image
|
* POST /api/v1/upload/image
|
||||||
|
|
@ -19,10 +22,51 @@ const uploadImage = async (req, res, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const originalPath = req.file.path;
|
const originalPath = req.file.path;
|
||||||
|
const originalSize = req.file.size;
|
||||||
|
const originalExt = path.extname(req.file.originalname).toLowerCase();
|
||||||
|
|
||||||
// Generate unique filename with .jpg extension (since we're converting to JPEG)
|
// Generate unique filename
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
const randomString = Math.random().toString(36).substring(2, 15);
|
const randomString = Math.random().toString(36).substring(2, 15);
|
||||||
|
|
||||||
|
// 对于小图片或已经是jpg/jpeg格式的小图片,跳过处理直接使用
|
||||||
|
const isSmallImage = originalSize < SKIP_PROCESSING_SIZE;
|
||||||
|
const isJpeg = ['.jpg', '.jpeg'].includes(originalExt);
|
||||||
|
|
||||||
|
if (isSmallImage && isJpeg) {
|
||||||
|
// 小的JPEG图片直接重命名使用,不进行处理
|
||||||
|
const newFilename = `${timestamp}_${randomString}.jpg`;
|
||||||
|
const newPath = path.join(uploadDir, newFilename);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.rename(originalPath, newPath);
|
||||||
|
} catch (renameError) {
|
||||||
|
// 如果重命名失败(跨分区),则复制后删除
|
||||||
|
await fs.copyFile(originalPath, newPath);
|
||||||
|
await fs.unlink(originalPath).catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageUrl = `/uploads/${newFilename}`;
|
||||||
|
|
||||||
|
logger.info('Image uploaded (skipped processing)', {
|
||||||
|
originalName: req.file.originalname,
|
||||||
|
filename: newFilename,
|
||||||
|
size: originalSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json({
|
||||||
|
code: 0,
|
||||||
|
message: 'Image uploaded successfully',
|
||||||
|
data: {
|
||||||
|
url: imageUrl,
|
||||||
|
filename: newFilename,
|
||||||
|
originalName: req.file.originalname,
|
||||||
|
size: originalSize,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 需要处理的图片
|
||||||
const processedFilename = `${timestamp}_${randomString}.jpg`;
|
const processedFilename = `${timestamp}_${randomString}.jpg`;
|
||||||
const processedPath = path.join(uploadDir, processedFilename);
|
const processedPath = path.join(uploadDir, processedFilename);
|
||||||
|
|
||||||
|
|
@ -32,49 +76,42 @@ const uploadImage = async (req, res, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Process image with Sharp
|
// Process image with Sharp - 使用更快的设置
|
||||||
const sharpInstance = sharp(originalPath);
|
const sharpInstance = sharp(originalPath, {
|
||||||
|
failOnError: false, // 不因为小错误而失败
|
||||||
|
limitInputPixels: false, // 不限制输入像素
|
||||||
|
});
|
||||||
|
|
||||||
await sharpInstance
|
await sharpInstance
|
||||||
.resize(1200, 1200, {
|
.resize(1200, 1200, {
|
||||||
fit: 'inside',
|
fit: 'inside',
|
||||||
withoutEnlargement: true,
|
withoutEnlargement: true,
|
||||||
})
|
})
|
||||||
.jpeg({
|
.jpeg({
|
||||||
quality: 85,
|
quality: 80, // 稍微降低质量以加快处理速度
|
||||||
progressive: true,
|
progressive: false, // 关闭渐进式以加快处理
|
||||||
|
mozjpeg: false, // 不使用mozjpeg以加快速度
|
||||||
})
|
})
|
||||||
.toFile(processedPath);
|
.toFile(processedPath);
|
||||||
|
|
||||||
// Destroy sharp instance to release file handles
|
// Destroy sharp instance to release file handles
|
||||||
sharpInstance.destroy();
|
sharpInstance.destroy();
|
||||||
|
|
||||||
// Delete original file if it's different from processed
|
// Delete original file - 异步删除,不等待
|
||||||
// On Windows, file deletion can fail due to file locks, so we schedule it for later
|
fs.unlink(originalPath).catch(unlinkError => {
|
||||||
if (originalPath !== processedPath) {
|
|
||||||
// Schedule deletion after a delay to avoid file lock issues on Windows
|
|
||||||
setTimeout(async () => {
|
|
||||||
try {
|
|
||||||
await fs.unlink(originalPath);
|
|
||||||
logger.info('Original file deleted successfully', { path: originalPath });
|
|
||||||
} catch (unlinkError) {
|
|
||||||
// If deletion still fails, just log it
|
|
||||||
// The file will need to be cleaned up manually or by a cleanup job
|
|
||||||
logger.warn('Could not delete original file', {
|
logger.warn('Could not delete original file', {
|
||||||
path: originalPath,
|
path: originalPath,
|
||||||
error: unlinkError.message
|
error: unlinkError.message
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
}, 500); // Wait 500ms before attempting deletion
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return relative path for the image (more portable)
|
// Return relative path for the image
|
||||||
// Frontend will prepend the base URL as needed
|
|
||||||
const imageUrl = `/uploads/${processedFilename}`;
|
const imageUrl = `/uploads/${processedFilename}`;
|
||||||
|
|
||||||
logger.info('Image uploaded and processed successfully', {
|
logger.info('Image uploaded and processed successfully', {
|
||||||
originalName: req.file.originalname,
|
originalName: req.file.originalname,
|
||||||
processedFilename,
|
processedFilename,
|
||||||
size: req.file.size,
|
originalSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
|
|
@ -89,11 +126,7 @@ const uploadImage = async (req, res, next) => {
|
||||||
});
|
});
|
||||||
} catch (processingError) {
|
} catch (processingError) {
|
||||||
// Clean up file if processing fails
|
// Clean up file if processing fails
|
||||||
try {
|
fs.unlink(originalPath).catch(() => {});
|
||||||
await fs.unlink(originalPath);
|
|
||||||
} catch (unlinkError) {
|
|
||||||
logger.error('Failed to delete file after processing error', { error: unlinkError });
|
|
||||||
}
|
|
||||||
throw processingError;
|
throw processingError;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ const getWithdrawalList = async (options = {}) => {
|
||||||
id: withdrawal.id,
|
id: withdrawal.id,
|
||||||
withdrawalNo: withdrawal.withdrawalNo,
|
withdrawalNo: withdrawal.withdrawalNo,
|
||||||
amount: parseFloat(withdrawal.amount).toFixed(2),
|
amount: parseFloat(withdrawal.amount).toFixed(2),
|
||||||
|
currency: withdrawal.currency || 'CNY',
|
||||||
paymentMethod: withdrawal.paymentMethod,
|
paymentMethod: withdrawal.paymentMethod,
|
||||||
paymentDetails: withdrawal.paymentDetails,
|
paymentDetails: withdrawal.paymentDetails,
|
||||||
status: withdrawal.status,
|
status: withdrawal.status,
|
||||||
|
|
@ -281,6 +282,7 @@ const getWithdrawalDetails = async (withdrawalId) => {
|
||||||
id: withdrawal.id,
|
id: withdrawal.id,
|
||||||
withdrawalNo: withdrawal.withdrawalNo,
|
withdrawalNo: withdrawal.withdrawalNo,
|
||||||
amount: parseFloat(withdrawal.amount).toFixed(2),
|
amount: parseFloat(withdrawal.amount).toFixed(2),
|
||||||
|
currency: withdrawal.currency || 'CNY',
|
||||||
paymentMethod: withdrawal.paymentMethod,
|
paymentMethod: withdrawal.paymentMethod,
|
||||||
paymentDetails: withdrawal.paymentDetails,
|
paymentDetails: withdrawal.paymentDetails,
|
||||||
status: withdrawal.status,
|
status: withdrawal.status,
|
||||||
|
|
|
||||||
|
|
@ -654,6 +654,7 @@ AppServer.prototype.UploadImage = async function(filePath) {
|
||||||
url: url,
|
url: url,
|
||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
name: 'image',
|
name: 'image',
|
||||||
|
timeout: 60000, // 60秒超时
|
||||||
header: {
|
header: {
|
||||||
'Authorization': authToken
|
'Authorization': authToken
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user