代币优化

This commit is contained in:
Dev
2026-10-27 18:30:40 +08:00
parent f53c142a56
commit 25804d500e
11 changed files with 66 additions and 27 deletions
@@ -11,6 +11,7 @@ return [
'Files' => '附件',
'Desc' => '需求详情',
'Contact' => '联系方式',
'Pay_platform' => '发布终端',
'Budget_amount' => '预算(元)',
'Limit_time' => '工期(天)',
'Weigh' => '排序',
@@ -8,6 +8,7 @@ use app\common\service\ContractService;
use app\common\service\DemandBiddingService;
use app\common\service\DemandService;
use app\common\service\OrderShipService;
use app\common\service\VirtualPayService;
use think\exception\HttpResponseException;
use think\Lang;
use think\Validate;
@@ -273,6 +274,9 @@ class Demand extends Api
try {
$demandId = $this->request->param('demandId', 0);
$info = ContractService::getWinBiddingContract($demandId);
$demand = DemandService::getOne(['id' => $demandId]);
$platform = $demand ? VirtualPayService::normalizePayPlatform($demand->pay_platform) : '';
$info->pay_platform = $platform !== '' ? $platform : 'android';
if (empty($info->data_json['customData']) && ($firstContract = ContractService::getFirstContract($demandId))) {
$data_json = $info->data_json;
$data_json['customData'] = $firstContract->data_json['customData'] ?? [];
@@ -151,6 +151,8 @@ class DemandService
'files' => $data['files'] ?? [],
'budget_amount' => $data['budget_amount'] ?? 0,
'limit_time' => $data['limit_time'] ?? 0,
// 客户发布需求时的手机系统,报价预估按这个终端。真正付款可能换设备,以支付记录为准
'pay_platform' => VirtualPayService::normalizePayPlatform($data['pay_platform'] ?? '') ?: 'android',
'publish_status' => CommonService::ONGOING,
'status' => self::STATUS_NO
];
@@ -481,8 +481,9 @@ class VirtualPayService
}
/**
* 代币数量,保留两位小数。1元=1代币时,0.01元对应 0.01 个代币
* @param float|string $amountYuan
* @return int
* @return float
* @throws \Exception
*/
private static function coinQuantity($amountYuan)
@@ -495,11 +496,9 @@ class VirtualPayService
if ($coinPerYuan < 1) {
$coinPerYuan = 1;
}
if (($fen * $coinPerYuan) % 100 !== 0) {
throw new \Exception('订单金额须为整数元才能按1元=1代币支付');
}
$quantity = bcdiv(bcmul((string)$fen, (string)$coinPerYuan, 0), '100', 2);
return intdiv($fen * $coinPerYuan, 100);
return (float)$quantity;
}
/**
+1 -1
View File
@@ -40,7 +40,7 @@ return [
'transfer_authorization_notify_url' => Env::get('app.url') . '/api/account/transferAuthorizationNotify',
],
// 小程序虚拟支付(类目不支持普通微信支付时使用)
// 代币比例须与 MP「虚拟支付-代币配置」一致:1元=1代币。buyQuantity 只能是整数,订单金额须为整数元
// 代币比例须与 MP「虚拟支付-代币配置」一致:1元=1代币。数量保留两位小数,0.01元=0.01代币
'virtual_pay' => [
'offer_id' => Env::get('wechat.vp_offer_id', ''),
'app_key' => Env::get('wechat.vp_app_key', ''), // 现网 AppKey,env=0
@@ -60,6 +60,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
class: 'autocontent',
formatter: Table.api.formatter.content
},
{field: 'pay_platform', title: __('Pay_platform'), searchList: {"android":"安卓","ios":"苹果"}, formatter: Table.api.formatter.normal},
{field: 'budget_amount', title: __('Budget_amount'), operate: 'BETWEEN'},
{field: 'limit_time', title: __('Limit_time'), operate: false},
{field: 'weigh', title: __('Weigh'), operate: false},
@@ -9,8 +9,10 @@ Page({
*/
data: {
demandId: 0,
androidAccount: 0,
iosAccount: 0,
actualAccount: 0,
payPlatform: 'android', // 客户发布需求时的手机系统
platformName: '安卓',
payFeePercent: 1,
commissionRatio: 0.1, // 平台服务费比例,来自 common/getMiniConfig
commissionPercent: 10,
androidPayFeeRate: 0.01,
@@ -73,12 +75,17 @@ Page({
},
incomePreview(amount, rates) {
const commissionRatio = rates ? rates.commissionRatio : this.data.commissionRatio;
const androidPayFeeRate = rates ? rates.androidPayFeeRate : this.data.androidPayFeeRate;
const iosPayFeeRate = rates ? rates.iosPayFeeRate : this.data.iosPayFeeRate;
const commissionRatio = rates && rates.commissionRatio !== undefined ? rates.commissionRatio : this.data.commissionRatio;
const androidPayFeeRate = rates && rates.androidPayFeeRate !== undefined ? rates.androidPayFeeRate : this.data.androidPayFeeRate;
const iosPayFeeRate = rates && rates.iosPayFeeRate !== undefined ? rates.iosPayFeeRate : this.data.iosPayFeeRate;
const rawPlatform = rates && rates.payPlatform !== undefined ? rates.payPlatform : this.data.payPlatform;
const payPlatform = rawPlatform === 'ios' ? 'ios' : 'android';
const payFeeRate = payPlatform === 'ios' ? iosPayFeeRate : androidPayFeeRate;
return {
androidAccount: calcActualAccount(amount, commissionRatio, androidPayFeeRate),
iosAccount: calcActualAccount(amount, commissionRatio, iosPayFeeRate),
actualAccount: calcActualAccount(amount, commissionRatio, payFeeRate),
payPlatform,
payFeePercent: toPercent(payFeeRate),
platformName: payPlatform === 'ios' ? 'iOS' : '安卓',
};
},
// 拉取平台服务费和支付费率,并重算预计到手金额
@@ -91,7 +98,10 @@ Page({
iosPayFeeRate: rates.iosPayFeeRate,
androidFeePercent: toPercent(rates.androidPayFeeRate),
iosFeePercent: toPercent(rates.iosPayFeeRate),
...this.incomePreview(this.data.formData?.totalCost, rates),
...this.incomePreview(this.data.formData?.totalCost, {
...rates,
payPlatform: this.data.payPlatform,
}),
});
},
@@ -549,7 +559,7 @@ Page({
'formData.taskContent': resData.task_content,
'formData.needDays': resData.need_days,
'formData.totalCost': resData.total_amount,
...this.incomePreview(resData.total_amount),
...this.incomePreview(resData.total_amount, { payPlatform: resData.pay_platform }),
});
// 客户签约时,附件展示开发者签约时上传的附件,仅可预览,不可删除和上传
@@ -64,7 +64,7 @@
/>
</view>
<t-input label="总费用" placeholder="请输入总费用" suffix="元" align="right" type="number" tips="{{source == 'dev' ? '预计到手 安卓' + androidAccount + '元 / iOS' + iosAccount + '元,已扣平台服务费' + commissionPercent + '%和支付费率' + androidFeePercent + '%/' + iosFeePercent + '%' : '总费用 = 首付款50% + 尾款50%'}}" disabled="{{source != 'dev'}}" value="{{formData.totalCost}}" bind:change="onTotalCostChange" />
<t-input label="总费用" placeholder="请输入总费用" suffix="元" align="right" type="number" tips="{{source == 'dev' ? '预计到手金额 ' + actualAccount + '元,已扣平台服务费' + commissionPercent + '%和' + platformName + '支付费率' + payFeePercent + '%' : '总费用 = 首付款50% + 尾款50%'}}" disabled="{{source != 'dev'}}" value="{{formData.totalCost}}" bind:change="onTotalCostChange" />
<t-input label="开发工期" placeholder="请输入开发工期" suffix="天" align="right" type="number" disabled="{{source != 'dev'}}" value="{{formData.needDays}}" bind:change="onDevelopmentPeriodChange" />
</view>
+20 -10
View File
@@ -11,8 +11,10 @@ Page({
quotedPrice: '',
needDays: '',
remark: '',
androidAccount: 0,
iosAccount: 0,
actualAccount: 0,
payPlatform: 'android', // 客户发布需求时的手机系统
platformName: '安卓',
payFeePercent: 1,
commissionRatio: 0.1, // 平台服务费比例,来自 common/getMiniConfig
commissionPercent: 10,
androidPayFeeRate: 0.01,
@@ -41,7 +43,7 @@ Page({
const demandInfo = res.data;
this.setData({
demandInfo,
...this.incomePreview(this.getQuoteAmount(demandInfo))
...this.incomePreview(this.getQuoteAmount(demandInfo), { payPlatform: demandInfo.pay_platform })
}, () => resolve()); // setData 回调:页面已重新渲染
},
() => {
@@ -55,14 +57,19 @@ Page({
getQuoteAmount(demandInfo) {
return demandInfo?.myBidding?.quoted_price || demandInfo?.demand_contracts?.total_amount || this.data.quotedPrice || 0;
},
// 按当前报价计算安卓、iOS 两种预计到手
// 按客户发布需求时的手机系统计算预计到手
incomePreview(amount, rates) {
const commissionRatio = rates ? rates.commissionRatio : this.data.commissionRatio;
const androidPayFeeRate = rates ? rates.androidPayFeeRate : this.data.androidPayFeeRate;
const iosPayFeeRate = rates ? rates.iosPayFeeRate : this.data.iosPayFeeRate;
const commissionRatio = rates && rates.commissionRatio !== undefined ? rates.commissionRatio : this.data.commissionRatio;
const androidPayFeeRate = rates && rates.androidPayFeeRate !== undefined ? rates.androidPayFeeRate : this.data.androidPayFeeRate;
const iosPayFeeRate = rates && rates.iosPayFeeRate !== undefined ? rates.iosPayFeeRate : this.data.iosPayFeeRate;
const rawPlatform = rates && rates.payPlatform !== undefined ? rates.payPlatform : this.data.payPlatform;
const payPlatform = rawPlatform === 'ios' ? 'ios' : 'android';
const payFeeRate = payPlatform === 'ios' ? iosPayFeeRate : androidPayFeeRate;
return {
androidAccount: calcActualAccount(amount, commissionRatio, androidPayFeeRate),
iosAccount: calcActualAccount(amount, commissionRatio, iosPayFeeRate),
actualAccount: calcActualAccount(amount, commissionRatio, payFeeRate),
payPlatform,
payFeePercent: toPercent(payFeeRate),
platformName: payPlatform === 'ios' ? 'iOS' : '安卓',
};
},
// 拉取平台服务费和支付费率,并重算预计到手金额
@@ -75,7 +82,10 @@ Page({
iosPayFeeRate: rates.iosPayFeeRate,
androidFeePercent: toPercent(rates.androidPayFeeRate),
iosFeePercent: toPercent(rates.iosPayFeeRate),
...this.incomePreview(this.getQuoteAmount(this.data.demandInfo), rates),
...this.incomePreview(this.getQuoteAmount(this.data.demandInfo), {
...rates,
payPlatform: (this.data.demandInfo && this.data.demandInfo.pay_platform) || this.data.payPlatform,
}),
});
},
// 取文件扩展名对应的 openDocument 类型(默认 pdf)
@@ -44,7 +44,7 @@
</t-collapse-panel>
<t-collapse-panel header="{{opType=='quote' ? '我的报价' : '开发人员报价'}}" value="{{3}}" expandIcon>
<view>
<t-input style="--td-input-border-color: rgb(238, 116, 2);" placeholder="填写报价金额" suffix="元" align="right" type="number" value="{{opType=='quote' ? demandInfo.myBidding.quoted_price || demandInfo.demand_contracts.total_amount ||quotedPrice : demandInfo.winBidding.quoted_price}}" disabled="{{opType=='quote' && (!demandInfo.myBidding && !demandInfo.demand_contracts) ? false : true}}" tips="{{opType=='quote' && androidAccount > 0 ? '预计到手 安卓' + androidAccount + '元 / iOS' + iosAccount + '元,已扣平台服务费' + commissionPercent + '%和支付费率' + androidFeePercent + '%/' + iosFeePercent + '%' : ''}}" data-field="quotedPrice" bindchange="onInputChange">
<t-input style="--td-input-border-color: rgb(238, 116, 2);" placeholder="填写报价金额" suffix="元" align="right" type="number" value="{{opType=='quote' ? demandInfo.myBidding.quoted_price || demandInfo.demand_contracts.total_amount ||quotedPrice : demandInfo.winBidding.quoted_price}}" disabled="{{opType=='quote' && (!demandInfo.myBidding && !demandInfo.demand_contracts) ? false : true}}" tips="{{opType=='quote' && actualAccount > 0 ? '预计到手金额 ' + actualAccount + '元,已扣平台服务费' + commissionPercent + '%和' + platformName + '支付费率' + payFeePercent + '%' : ''}}" data-field="quotedPrice" bindchange="onInputChange">
<view slot="label" class="custom-label">报价</view>
</t-input>
<t-input placeholder="填写预计工期" suffix="天" align="right" disabled="{{opType=='quote' && (!demandInfo.myBidding && !demandInfo.demand_contracts) ? false : true}}" type="number" value="{{opType=='quote' ? demandInfo.myBidding.need_days || demandInfo.demand_contracts.need_days || needDays : demandInfo.winBidding.need_days}}" data-field="needDays" bindchange="onInputChange">
@@ -4,6 +4,17 @@ import { fetchConfig } from '../../../services/home/home';
// 联系方式:仅允许英文、数字、下划线、中划线(不含中文与特殊字符)
const CONTACT_REG = /^[A-Za-z0-9_-]+$/;
// 客户发布需求时的手机系统。iOS 单独记,其余按安卓
function currentPayPlatform() {
let platform = '';
try {
platform = String((wx.getSystemInfoSync() || {}).platform || '').toLowerCase();
} catch (err) {
platform = '';
}
return platform === 'ios' ? 'ios' : 'android';
}
Page({
/**
@@ -170,6 +181,7 @@ Page({
budget_amount: budgetAmount,
limit_time: limitTime,
contact: contact,
pay_platform: currentPayPlatform(),
}, 'POST').then(res => {
if (res.code === 1) {
wx.requestSubscribeMessage({