逻辑修改.

This commit is contained in:
18631081161 2025-12-24 21:07:03 +08:00
parent ece1ef09ad
commit 9e45704c66
4 changed files with 84 additions and 35 deletions

View File

@ -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: '微信',

View File

@ -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) {

View File

@ -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,

View File

@ -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
}, },