diff --git a/backend/application/common/service/DemandService.php b/backend/application/common/service/DemandService.php
index 673ab80..47cef90 100644
--- a/backend/application/common/service/DemandService.php
+++ b/backend/application/common/service/DemandService.php
@@ -351,17 +351,18 @@ class DemandService
$winContract = ContractService::updateCustomerContractData($demandId, $formData);
//如果新的合同金额与旧的合同金额不符,则通过修改尾款订单的金额的方式来弥补差价
$firstContract = ContractService::getFirstContract($demandId);
- if ($firstContract && $firstContract->total_amount != $winContract->total_amount && $winContract->status2 != ContractService::STATUS_FINISHED) {
- $diffAmount = $winContract->total_amount - $firstContract->total_amount;
- //更新尾款订单的金额
+ if ($firstContract && bccomp((string)$firstContract->total_amount, (string)$winContract->total_amount, 2) !== 0 && $winContract->status2 != ContractService::STATUS_FINISHED) {
+ // 尾款 = 新合同总额 - 首付款,不按固定 50% 重算
+ $firstOrder = OrderService::getDemandOrder($demand->parent_order_no, OrderService::ORDER_TYPE_FIRST_PAYMENT);
$finalOrder = OrderService::getDemandOrder($demand->parent_order_no, OrderService::ORDER_TYPE_FINAL_PAYMENT);
- $finalOrder->total_amount += $diffAmount;
- $finalOrder->goods_amount += $diffAmount;
- $finalOrder->payment_amount += $diffAmount;
- $saveRes = $finalOrder->save();
- if (!$saveRes) {
- exception('弥补订单差价金额失败');
+ if (!$firstOrder || !$finalOrder) {
+ exception('获取分期订单失败');
}
+ $finalAmount = bcsub((string)$winContract->total_amount, (string)$firstOrder->payment_amount, 2);
+ if (bccomp($finalAmount, '0', 2) < 0) {
+ exception('合同金额不能小于已确定的首付款');
+ }
+ OrderService::applyOrderAmount($finalOrder, $finalAmount);
}
//获取签约订单用于支付
$order = OrderService::getDemandOrder($demand->parent_order_no, $contractType);
diff --git a/backend/application/common/service/OrderService.php b/backend/application/common/service/OrderService.php
index b00c556..f64c778 100644
--- a/backend/application/common/service/OrderService.php
+++ b/backend/application/common/service/OrderService.php
@@ -202,16 +202,19 @@ class OrderService
}
$winBid = DemandBiddingService::getOne(['demand_id' => $demandId, 'bidding_status' => DemandBiddingService::STATUS_BIDDING_WIN]);
$orderInfo = self::where(['parent_order_no' => $demand->parent_order_no])->find();
- $ratio = 0.5; //分期比率50%
+ // 首付为总金额 50% 四舍五入到整数元,尾款为剩余未付金额
+ list($firstAmount, $finalAmount) = self::splitDemandPayment($totalCost);
if (!$orderInfo) {
$promoterId = PromotionService::getPromoterId(intval($demand->user_id));
- $installments = 2;//分期数
- for ($i = 1; $i <= $installments; $i++) {
- $total_amount = $i == 1 ? $totalCost * $ratio : $totalCost * (1 - $ratio);
+ $installments = [
+ self::ORDER_TYPE_FIRST_PAYMENT => $firstAmount,
+ self::ORDER_TYPE_FINAL_PAYMENT => $finalAmount,
+ ];
+ foreach ($installments as $orderType => $total_amount) {
$goodsDataList = [[
'spu_id' => 0,
'sku_id' => 0,
- 'goods_name' => '需求签约-' . ($i == 1 ? '首付款' : '尾款') . $demand->parent_order_no,
+ 'goods_name' => '需求签约-' . ($orderType == self::ORDER_TYPE_FIRST_PAYMENT ? '首付款' : '尾款') . $demand->parent_order_no,
'goods_picture_url' => 'https://soft-market.oss-cn-shenzhen.aliyuncs.com/images/contract.png',
'origin_price' => $total_amount,
'actual_price' => $total_amount,
@@ -229,7 +232,7 @@ class OrderService
'parent_order_no' => $demand->parent_order_no,
'order_no' => generate_order_sn(),
'order_status' => OrderService::ORDER_STATUS_WAIT_PAY,
- 'order_type' => $i,
+ 'order_type' => $orderType,
'total_amount' => $total_amount,
'payment_amount' => $total_amount,
'goods_amount' => $total_amount,
@@ -259,7 +262,7 @@ class OrderService
if (!$contractInfo) {
//创建合同数据
$firstContract = ContractService::getFirstContract($demandId);
- $depositAmount = $firstContract ? $firstContract->deposit_amount : $formData['totalCost'] * $ratio;
+ $depositAmount = $firstContract ? $firstContract->deposit_amount : $firstAmount;
ContractService::createData($demandId, $winBid->id ?? 0, $depositAmount, $formData);
}
return $contractInfo;
@@ -268,6 +271,61 @@ class OrderService
}
}
+ /**
+ * 需求分期。首付为总金额的 50% 四舍五入到整数元,尾款为总额减去首付
+ * @param float|string $totalCost
+ * @return array [首付款, 尾款],均为两位小数字符串
+ * @throws \Exception
+ */
+ public static function splitDemandPayment($totalCost)
+ {
+ $total = bcadd((string)$totalCost, '0', 2);
+ if (bccomp($total, '0.01', 2) < 0) {
+ throw new \Exception('合同金额不能小于0.01元');
+ }
+ $half = bcmul($total, '0.5', 4);
+ $first = bcadd(bcadd($half, '0.5', 0), '0', 2);
+ if (bccomp($first, $total, 2) === 1) {
+ $first = $total;
+ }
+ $final = bcsub($total, $first, 2);
+
+ return [$first, $final];
+ }
+
+ /**
+ * 把订单和订单商品金额改成指定金额
+ * @param \think\Model $order
+ * @param float|string $amount
+ * @return \think\Model
+ * @throws \Exception
+ */
+ public static function applyOrderAmount($order, $amount)
+ {
+ $amount = bcadd((string)$amount, '0', 2);
+ $order->total_amount = $amount;
+ $order->goods_amount = $amount;
+ $order->payment_amount = $amount;
+ if ($order->save() === false) {
+ throw new \Exception('更新订单金额失败');
+ }
+ $goodsList = $order->orderGoods;
+ if ($goodsList) {
+ foreach ($goodsList as $goods) {
+ $goods->origin_price = $amount;
+ $goods->actual_price = $amount;
+ $goods->item_total_amount = $amount;
+ $goods->item_payment_amount = $amount;
+ $goods->goods_payment_price = $amount;
+ if ($goods->save() === false) {
+ throw new \Exception('更新订单商品金额失败');
+ }
+ }
+ }
+
+ return $order;
+ }
+
public static function getDemandOrder($parentOrderNo, $contractType)
{
$res = self::getOne(['parent_order_no' => $parentOrderNo, 'order_type' => $contractType], ['orderGoods', 'orderPayment']);
diff --git a/frontend/miniapp/pages/demand/contract/index.js b/frontend/miniapp/pages/demand/contract/index.js
index 433f86f..104c857 100644
--- a/frontend/miniapp/pages/demand/contract/index.js
+++ b/frontend/miniapp/pages/demand/contract/index.js
@@ -54,7 +54,10 @@ Page({
attachFiles: [],
// 签约条款
agreementChecked: false
- }
+ },
+ firstAmount: '',
+ finalAmount: '',
+ costTips: '首付款按总费用的50%四舍五入到整数元,尾款为剩余未付金额'
},
/**
@@ -88,20 +91,65 @@ Page({
platformName: payPlatform === 'ios' ? 'iOS' : '安卓',
};
},
+
+ // 首付为总金额 50% 四舍五入到整数元,尾款为剩余未付金额。与后端 splitDemandPayment 一致
+ installmentPreview(amount) {
+ const text = String(amount == null ? '' : amount).trim();
+ if (!text || Number(text) <= 0) {
+ return { firstAmount: '', finalAmount: '' };
+ }
+ const parts = text.split('.');
+ const yuanPart = parseInt(parts[0], 10);
+ if (Number.isNaN(yuanPart)) {
+ return { firstAmount: '', finalAmount: '' };
+ }
+ const dec = (parts[1] || '0').padEnd(2, '0').slice(0, 2);
+ const fen = yuanPart * 100 + parseInt(dec, 10);
+ const firstYuan = Math.floor((fen + 100) / 200);
+ const firstFen = Math.min(firstYuan * 100, fen);
+ const finalFen = fen - firstFen;
+ const format = (value) => (value / 100).toFixed(2);
+ return {
+ firstAmount: format(firstFen),
+ finalAmount: format(finalFen),
+ };
+ },
+
+ buildCostTips(installment, income, commissionPercent) {
+ const percent = commissionPercent !== undefined ? commissionPercent : this.data.commissionPercent;
+ const splitText = installment.firstAmount
+ ? `首付款${installment.firstAmount}元(总费用的50%四舍五入到整数元),尾款${installment.finalAmount}元(剩余未付金额)`
+ : '首付款按总费用的50%四舍五入到整数元,尾款为剩余未付金额';
+ if (this.data.source == 'dev') {
+ return `预计到手金额 ${income.actualAccount}元,已扣平台服务费${percent}%和${income.platformName}支付费率${income.payFeePercent}%。${splitText}`;
+ }
+ return splitText;
+ },
+
+ fillPaymentPreview(amount, incomePatch, commissionPercent) {
+ const income = this.incomePreview(amount, incomePatch);
+ const installment = this.installmentPreview(amount);
+ return {
+ ...income,
+ ...installment,
+ costTips: this.buildCostTips(installment, income, commissionPercent),
+ };
+ },
// 拉取平台服务费和支付费率,并重算预计到手金额
async loadCommissionRatio() {
const rates = await fetchIncomeRates();
+ const commissionPercent = toPercent(rates.commissionRatio);
this.setData({
commissionRatio: rates.commissionRatio,
- commissionPercent: toPercent(rates.commissionRatio),
+ commissionPercent,
androidPayFeeRate: rates.androidPayFeeRate,
iosPayFeeRate: rates.iosPayFeeRate,
androidFeePercent: toPercent(rates.androidPayFeeRate),
iosFeePercent: toPercent(rates.iosPayFeeRate),
- ...this.incomePreview(this.data.formData?.totalCost, {
+ ...this.fillPaymentPreview(this.data.formData?.totalCost, {
...rates,
payPlatform: this.data.payPlatform,
- }),
+ }, commissionPercent),
});
},
@@ -235,7 +283,7 @@ Page({
onTotalCostChange(e) {
this.setData({
'formData.totalCost': e.detail.value,
- ...this.incomePreview(e.detail.value),
+ ...this.fillPaymentPreview(e.detail.value),
});
},
@@ -559,7 +607,7 @@ Page({
'formData.taskContent': resData.task_content,
'formData.needDays': resData.need_days,
'formData.totalCost': resData.total_amount,
- ...this.incomePreview(resData.total_amount, { payPlatform: resData.pay_platform }),
+ ...this.fillPaymentPreview(resData.total_amount, { payPlatform: resData.pay_platform }),
});
// 客户签约时,附件展示开发者签约时上传的附件,仅可预览,不可删除和上传
@@ -690,10 +738,13 @@ Page({
});
return;
}
+ const payAmount = res.data && res.data.payment_amount ? res.data.payment_amount : '';
wx.showModal({
title: '提示',
showCancel: false,
- content: '签约完成,去支付首付款(总金额的50%)',
+ content: payAmount
+ ? `签约完成,去支付首付款${payAmount}元。尾款为合同剩余未付金额`
+ : '签约完成,去支付首付款',
success: (res2) => {
if (res2.confirm) {
//支付预付款
diff --git a/frontend/miniapp/pages/demand/contract/index.wxml b/frontend/miniapp/pages/demand/contract/index.wxml
index ce2e179..92b9252 100644
--- a/frontend/miniapp/pages/demand/contract/index.wxml
+++ b/frontend/miniapp/pages/demand/contract/index.wxml
@@ -64,7 +64,7 @@
/>
-
+