From bbaf34550992df807e47ccd47f511819fe31f9bc Mon Sep 17 00:00:00 2001
From: 18631081161 <2088094923@qq.com>
Date: Wed, 24 Dec 2025 01:01:17 +0800
Subject: [PATCH] =?UTF-8?q?=E6=AF=94=E7=B4=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../013-add-dual-currency-balance.js | 42 +++++++++++
backend/src/models/Commission.js | 6 ++
backend/src/models/User.js | 8 ++
backend/src/models/Withdrawal.js | 6 ++
backend/src/scripts/addDualCurrencyFields.js | 73 +++++++++++++++++++
backend/src/services/commissionService.js | 55 +++++++++++---
backend/src/services/withdrawalService.js | 21 +++++-
miniprogram/src/locale/en.js | 2 +
miniprogram/src/locale/es.js | 2 +
miniprogram/src/locale/zh.js | 2 +
.../src/pages/me/invite-reward-page.vue | 33 +++++----
11 files changed, 222 insertions(+), 28 deletions(-)
create mode 100644 backend/src/migrations/013-add-dual-currency-balance.js
create mode 100644 backend/src/scripts/addDualCurrencyFields.js
diff --git a/backend/src/migrations/013-add-dual-currency-balance.js b/backend/src/migrations/013-add-dual-currency-balance.js
new file mode 100644
index 0000000..10c8eb8
--- /dev/null
+++ b/backend/src/migrations/013-add-dual-currency-balance.js
@@ -0,0 +1,42 @@
+'use strict';
+
+/**
+ * Migration: Add dual currency balance support
+ * - Add balance_peso field to user table
+ * - Add currency field to commissions table
+ * - Add currency field to withdrawal table
+ */
+
+module.exports = {
+ up: async (queryInterface, Sequelize) => {
+ // Add balance_peso to user table
+ await queryInterface.addColumn('user', 'balance_peso', {
+ type: Sequelize.DECIMAL(10, 2),
+ defaultValue: 0.00,
+ allowNull: false,
+ comment: '比索余额',
+ });
+
+ // Add currency to commissions table
+ await queryInterface.addColumn('commissions', 'currency', {
+ type: Sequelize.ENUM('RMB', 'PHP'),
+ defaultValue: 'RMB',
+ allowNull: false,
+ comment: '佣金货币类型',
+ });
+
+ // Add currency to withdrawal table
+ await queryInterface.addColumn('withdrawal', 'currency', {
+ type: Sequelize.ENUM('CNY', 'PHP'),
+ defaultValue: 'CNY',
+ allowNull: false,
+ comment: '提现货币类型',
+ });
+ },
+
+ down: async (queryInterface, Sequelize) => {
+ await queryInterface.removeColumn('user', 'balance_peso');
+ await queryInterface.removeColumn('commissions', 'currency');
+ await queryInterface.removeColumn('withdrawal', 'currency');
+ },
+};
diff --git a/backend/src/models/Commission.js b/backend/src/models/Commission.js
index 3297be2..8badde9 100644
--- a/backend/src/models/Commission.js
+++ b/backend/src/models/Commission.js
@@ -59,6 +59,12 @@ const Commission = sequelize.define('Commission', {
field: 'commission_amount',
comment: '佣金金额',
},
+ currency: {
+ type: DataTypes.ENUM('RMB', 'PHP'),
+ defaultValue: 'RMB',
+ allowNull: false,
+ comment: '佣金货币类型',
+ },
status: {
type: DataTypes.ENUM('credited', 'cancelled'),
defaultValue: 'credited',
diff --git a/backend/src/models/User.js b/backend/src/models/User.js
index b1ef4ed..b37ca0f 100644
--- a/backend/src/models/User.js
+++ b/backend/src/models/User.js
@@ -63,6 +63,14 @@ const User = sequelize.define('User', {
type: DataTypes.DECIMAL(10, 2),
defaultValue: 0.00,
allowNull: false,
+ comment: '人民币余额',
+ },
+ balancePeso: {
+ type: DataTypes.DECIMAL(10, 2),
+ defaultValue: 0.00,
+ allowNull: false,
+ field: 'balance_peso',
+ comment: '比索余额',
},
invitationCode: {
type: DataTypes.STRING(20),
diff --git a/backend/src/models/Withdrawal.js b/backend/src/models/Withdrawal.js
index 12490ac..01de3df 100644
--- a/backend/src/models/Withdrawal.js
+++ b/backend/src/models/Withdrawal.js
@@ -30,6 +30,12 @@ const Withdrawal = sequelize.define('Withdrawal', {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
},
+ currency: {
+ type: DataTypes.ENUM('CNY', 'PHP'),
+ defaultValue: 'CNY',
+ allowNull: false,
+ comment: '提现货币类型',
+ },
paymentMethod: {
type: DataTypes.ENUM('wechat', 'alipay', 'bank'),
allowNull: false,
diff --git a/backend/src/scripts/addDualCurrencyFields.js b/backend/src/scripts/addDualCurrencyFields.js
new file mode 100644
index 0000000..6aa4295
--- /dev/null
+++ b/backend/src/scripts/addDualCurrencyFields.js
@@ -0,0 +1,73 @@
+/**
+ * Script to add dual currency balance fields
+ * - Add balance_peso to user table
+ * - Add currency to commissions table
+ * - Add currency to withdrawal table
+ */
+
+const { sequelize } = require('../config/database');
+
+const addDualCurrencyFields = async () => {
+ try {
+ console.log('Starting dual currency fields migration...');
+
+ // Add balance_peso to user table
+ try {
+ await sequelize.query(`
+ ALTER TABLE user
+ ADD COLUMN balance_peso DECIMAL(10, 2) NOT NULL DEFAULT 0.00
+ COMMENT '比索余额'
+ `);
+ console.log('✓ Added balance_peso to user table');
+ } catch (error) {
+ if (error.message.includes('Duplicate column')) {
+ console.log('⊘ balance_peso already exists in user table');
+ } else {
+ throw error;
+ }
+ }
+
+ // Add currency to commissions table
+ try {
+ await sequelize.query(`
+ ALTER TABLE commissions
+ ADD COLUMN currency ENUM('RMB', 'PHP') NOT NULL DEFAULT 'RMB'
+ COMMENT '佣金货币类型'
+ `);
+ console.log('✓ Added currency to commissions table');
+ } catch (error) {
+ if (error.message.includes('Duplicate column')) {
+ console.log('⊘ currency already exists in commissions table');
+ } else {
+ throw error;
+ }
+ }
+
+ // Add currency to withdrawal table
+ try {
+ await sequelize.query(`
+ ALTER TABLE withdrawal
+ ADD COLUMN currency ENUM('CNY', 'PHP') NOT NULL DEFAULT 'CNY'
+ COMMENT '提现货币类型'
+ `);
+ console.log('✓ Added currency to withdrawal table');
+ } catch (error) {
+ if (error.message.includes('Duplicate column')) {
+ console.log('⊘ currency already exists in withdrawal table');
+ } else {
+ throw error;
+ }
+ }
+
+ console.log('\n========================================');
+ console.log('Dual currency migration completed!');
+ console.log('========================================\n');
+
+ process.exit(0);
+ } catch (error) {
+ console.error('Migration failed:', error);
+ process.exit(1);
+ }
+};
+
+addDualCurrencyFields();
diff --git a/backend/src/services/commissionService.js b/backend/src/services/commissionService.js
index e8e5442..423e273 100644
--- a/backend/src/services/commissionService.js
+++ b/backend/src/services/commissionService.js
@@ -37,8 +37,22 @@ const calculateCommission = async (paymentOrderId) => {
// Get current commission rate
const commissionRate = await commissionConfigService.getCommissionRate();
- // Calculate commission amount - use amountRmb if available, otherwise use amount
- const paymentAmount = parseFloat(paymentOrder.amountRmb || paymentOrder.amount || 0);
+ // Calculate commission amount - prioritize RMB, then Peso, then legacy amount
+ // Commission is calculated based on whichever currency amount is available
+ let paymentAmount = 0;
+ let currency = 'RMB';
+
+ if (paymentOrder.amountRmb && parseFloat(paymentOrder.amountRmb) > 0) {
+ paymentAmount = parseFloat(paymentOrder.amountRmb);
+ currency = 'RMB';
+ } else if (paymentOrder.amountPeso && parseFloat(paymentOrder.amountPeso) > 0) {
+ paymentAmount = parseFloat(paymentOrder.amountPeso);
+ currency = 'PHP';
+ } else if (paymentOrder.amount && parseFloat(paymentOrder.amount) > 0) {
+ paymentAmount = parseFloat(paymentOrder.amount);
+ currency = 'RMB';
+ }
+
if (paymentAmount <= 0) {
return null; // No valid payment amount
}
@@ -48,7 +62,7 @@ const calculateCommission = async (paymentOrderId) => {
const transaction = await sequelize.transaction();
try {
- // Create commission record
+ // Create commission record with currency
const commission = await Commission.create({
inviterId: inviteeUser.invitedBy,
inviteeId: inviteeUser.id,
@@ -56,13 +70,18 @@ const calculateCommission = async (paymentOrderId) => {
paymentAmount: paymentAmount,
commissionRate: commissionRate,
commissionAmount: commissionAmount,
+ currency: currency,
status: 'credited',
}, { transaction });
- // Update inviter's balance
+ // Update inviter's balance based on currency
const inviter = await User.findByPk(inviteeUser.invitedBy, { transaction });
if (inviter) {
- inviter.balance = parseFloat(inviter.balance) + commissionAmount;
+ if (currency === 'PHP') {
+ inviter.balancePeso = parseFloat(inviter.balancePeso || 0) + commissionAmount;
+ } else {
+ inviter.balance = parseFloat(inviter.balance) + commissionAmount;
+ }
await inviter.save({ transaction });
}
@@ -172,16 +191,24 @@ const getCommissionStats = async (inviterId) => {
where: { invitedBy: inviterId },
});
- // Get commission statistics
+ // Get commission statistics by currency
const commissions = await Commission.findAll({
where: { inviterId, status: 'credited' },
- attributes: ['commissionAmount'],
+ attributes: ['commissionAmount', 'currency'],
});
- const totalCommission = commissions.reduce(
- (sum, c) => sum + parseFloat(c.commissionAmount),
- 0
- );
+ // Calculate total commission by currency
+ let totalCommissionRmb = 0;
+ let totalCommissionPeso = 0;
+
+ commissions.forEach(c => {
+ const amount = parseFloat(c.commissionAmount);
+ if (c.currency === 'PHP') {
+ totalCommissionPeso += amount;
+ } else {
+ totalCommissionRmb += amount;
+ }
+ });
// Get paid invites count (invitees who have made payments)
const paidInvitesCount = await Commission.count({
@@ -193,9 +220,13 @@ const getCommissionStats = async (inviterId) => {
return {
totalInvites,
paidInvites: paidInvitesCount,
- totalCommission: totalCommission.toFixed(2),
+ totalCommission: totalCommissionRmb.toFixed(2),
+ totalCommissionRmb: totalCommissionRmb.toFixed(2),
+ totalCommissionPeso: totalCommissionPeso.toFixed(2),
commissionCount: commissions.length,
availableBalance: parseFloat(user.balance).toFixed(2),
+ availableBalanceRmb: parseFloat(user.balance).toFixed(2),
+ availableBalancePeso: parseFloat(user.balancePeso || 0).toFixed(2),
invitationCode: invitationCode,
};
};
diff --git a/backend/src/services/withdrawalService.js b/backend/src/services/withdrawalService.js
index b8fa06c..478a978 100644
--- a/backend/src/services/withdrawalService.js
+++ b/backend/src/services/withdrawalService.js
@@ -28,7 +28,7 @@ const generateWithdrawalNo = () => {
* @returns {Object} Created withdrawal
*/
const createWithdrawal = async (userId, withdrawalData) => {
- const { amount, paymentMethod, paymentDetails } = withdrawalData;
+ const { amount, currency = 'CNY', paymentMethod, paymentDetails } = withdrawalData;
// Validate amount
const withdrawalAmount = parseFloat(amount);
@@ -41,6 +41,12 @@ const createWithdrawal = async (userId, withdrawalData) => {
throw new Error(`Minimum withdrawal amount is ${MIN_WITHDRAWAL_AMOUNT}`);
}
+ // Validate currency
+ const validCurrencies = ['CNY', 'PHP'];
+ if (!validCurrencies.includes(currency)) {
+ throw new Error('Invalid currency');
+ }
+
// Validate payment method
const validMethods = ['wechat', 'alipay', 'bank'];
if (!validMethods.includes(paymentMethod)) {
@@ -66,8 +72,14 @@ const createWithdrawal = async (userId, withdrawalData) => {
throw new Error('User not found');
}
- // Check if user has sufficient balance
- const userBalance = parseFloat(user.balance);
+ // Check if user has sufficient balance based on currency
+ let userBalance;
+ if (currency === 'PHP') {
+ userBalance = parseFloat(user.balancePeso || 0);
+ } else {
+ userBalance = parseFloat(user.balance);
+ }
+
if (userBalance < withdrawalAmount) {
throw new Error('Insufficient balance');
}
@@ -90,6 +102,7 @@ const createWithdrawal = async (userId, withdrawalData) => {
userId,
withdrawalNo,
amount: withdrawalAmount,
+ currency,
paymentMethod,
paymentDetails,
status: 'waiting',
@@ -139,6 +152,7 @@ const getWithdrawals = async (userId, options = {}) => {
id: withdrawal.id,
withdrawalNo: withdrawal.withdrawalNo,
amount: parseFloat(withdrawal.amount).toFixed(2),
+ currency: withdrawal.currency || 'CNY',
paymentMethod: withdrawal.paymentMethod,
paymentDetails: withdrawal.paymentDetails,
status: withdrawal.status,
@@ -181,6 +195,7 @@ const getWithdrawalById = async (userId, withdrawalId) => {
id: withdrawal.id,
withdrawalNo: withdrawal.withdrawalNo,
amount: parseFloat(withdrawal.amount).toFixed(2),
+ currency: withdrawal.currency || 'CNY',
paymentMethod: withdrawal.paymentMethod,
paymentDetails: withdrawal.paymentDetails,
status: withdrawal.status,
diff --git a/miniprogram/src/locale/en.js b/miniprogram/src/locale/en.js
index 61db884..35be164 100644
--- a/miniprogram/src/locale/en.js
+++ b/miniprogram/src/locale/en.js
@@ -239,6 +239,8 @@ If you have privacy questions, please contact us through the application.`
paidYes: '¥ 12',
paidNo: '¥ 4',
statusWaiting: 'Waiting',
+ statusWaitingRmb: 'CNY Pending',
+ statusWaitingPeso: 'PHP Pending',
statusProcessing: 'Processing',
statusCompleted: 'Completed',
ruleTitle: 'Rules',
diff --git a/miniprogram/src/locale/es.js b/miniprogram/src/locale/es.js
index 4bf8dcd..af57ad7 100644
--- a/miniprogram/src/locale/es.js
+++ b/miniprogram/src/locale/es.js
@@ -239,6 +239,8 @@ Si tiene preguntas sobre privacidad, contáctenos a través de la aplicación.`
paidYes: '¥ 12',
paidNo: '¥ 4',
statusWaiting: 'Esperando',
+ statusWaitingRmb: 'Yuan Pendiente',
+ statusWaitingPeso: 'Peso Pendiente',
statusProcessing: 'Procesando',
statusCompleted: 'Completado',
ruleTitle: 'Reglas',
diff --git a/miniprogram/src/locale/zh.js b/miniprogram/src/locale/zh.js
index 7970049..50e7df7 100644
--- a/miniprogram/src/locale/zh.js
+++ b/miniprogram/src/locale/zh.js
@@ -255,6 +255,8 @@ export default {
paidYes: '¥ 12',
paidNo: '¥ 4',
statusWaiting: '待提现',
+ statusWaitingRmb: '人民币待提现',
+ statusWaitingPeso: '比索待提现',
statusProcessing: '提现中',
statusCompleted: '已提现',
ruleTitle: '活动规则',
diff --git a/miniprogram/src/pages/me/invite-reward-page.vue b/miniprogram/src/pages/me/invite-reward-page.vue
index 1f3b4d1..3f638a2 100644
--- a/miniprogram/src/pages/me/invite-reward-page.vue
+++ b/miniprogram/src/pages/me/invite-reward-page.vue
@@ -86,9 +86,13 @@
-
- {{ item.amount }}{{ $t('common.currency') }}
- {{ item.status }}
+
+ ¥{{ commissionStats.availableBalanceRmb || '0.00' }}
+ {{ $t('invite.statusWaitingRmb') || '人民币待提现' }}
+
+
+ ₱{{ commissionStats.availableBalancePeso || '0.00' }}
+ {{ $t('invite.statusWaitingPeso') || '比索待提现' }}
@@ -150,7 +154,7 @@
{{ item.time }}
- ¥{{ item.amount }}
+ {{ item.currency === 'PHP' ? '₱' : '¥' }}{{ item.amount }}
{{ item.status }}
@@ -335,7 +339,6 @@
cardholderName: '',
bankName: '',
swiftCode: '',
- withdrawRecords: [],
withdrawDetailList: [],
inviteRecords: [],
// 佣金统计
@@ -483,6 +486,7 @@
this.withdrawDetailList = res.data.records.map(item => ({
time: this.formatDate(item.createdAt),
amount: item.amount,
+ currency: item.currency || 'CNY',
status: this.getWithdrawStatusText(item.status)
}))
}
@@ -491,13 +495,10 @@
}
},
- // 更新提现显示
+ // 更新提现显示 - 不再需要,因为直接使用 commissionStats 中的双币种余额
updateWithdrawDisplay() {
- // 显示待提现、处理中、已完成的金额
- this.withdrawRecords = [{
- amount: this.commissionStats.availableBalance || '0',
- status: this.$t('invite.statusWaiting')
- }]
+ // 双币种余额已在 commissionStats 中返回
+ // availableBalanceRmb 和 availableBalancePeso
},
// 格式化日期
@@ -752,8 +753,14 @@
return
}
- // 最高不超过待提现金额
- const availableBalance = parseFloat(this.commissionStats.availableBalance || '0')
+ // 根据选择的货币类型检查对应的余额
+ let availableBalance = 0
+ if (this.withdrawCurrency === 'PHP') {
+ availableBalance = parseFloat(this.commissionStats.availableBalancePeso || '0')
+ } else {
+ availableBalance = parseFloat(this.commissionStats.availableBalanceRmb || this.commissionStats.availableBalance || '0')
+ }
+
if (amount > availableBalance) {
uni.showToast({
title: this.$t('invite.amountExceedsBalance') || '超出可提现金额',