邹景立 d2b499d14a refactor: 使用 BC Math 替换浮点运算以提高精度
将多处基于浮点的数值计算替换为 BC Math 字符串运算以避免浮点精度问题,涉及支付、退款、转账、比较与统计逻辑的重构。主要改动包括:

- 将比较与判断替换为 bccomp,累加与合并使用 bcadd,乘以 100 等使用 bcmul;
- 将部分初始数值与统计结果从数值类型改为字符串形式(如 '0.00'),并调整相关返回类型(如 Payment::paidAmount 改为返回 string);
- 修正订单/退款金额计算与超额校验逻辑以使用高精度算术;
- 更新微信支付相关 SDK 调用中金额乘 100 的计算以避免精度误差;
- 在若干插件中用高精度运算替换 floatval/int 转换(包括 SystemQueue、Wemall、Wuma 等);
- 更新文档(readme)添加 BC Math/高精度计算等说明并统一版权年份至 2014-2026;
- 新增 .copilot-commit-message-instructions.md(提交信息规范)。

此改动旨在增强金融/金额相关业务的计算正确性与一致性,避免因浮点运算导致的金额误差。
2026-02-01 13:27:10 +08:00

186 lines
8.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
/**
* +----------------------------------------------------------------------
* | Payment Plugin for ThinkAdmin
* +----------------------------------------------------------------------
* | 版权所有 2014~2026 ThinkAdmin [ thinkadmin.top ]
* +----------------------------------------------------------------------
* | 官方网站: https://thinkadmin.top
* +----------------------------------------------------------------------
* | 开源协议 ( https://mit-license.org )
* | 免责声明 ( https://thinkadmin.top/disclaimer )
* | 会员特权 ( https://thinkadmin.top/vip-introduce )
* +----------------------------------------------------------------------
* | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
* | github 代码仓库https://github.com/zoujingli/ThinkAdmin
* +----------------------------------------------------------------------
*/
namespace plugin\wemall\command;
use plugin\wemall\model\PluginWemallOrder;
use plugin\wemall\model\PluginWemallOrderItem;
use plugin\wemall\service\ConfigService;
use plugin\wemall\service\UserAction;
use plugin\wemall\service\UserOrder;
use think\admin\Command;
use think\console\Input;
use think\console\Output;
/**
* 商城订单自动清理.
* @class Clear
*/
class Clear extends Command
{
/**
* 当前配置参数.
* @var array
*/
protected $config;
/**
* 指令参数配置.
*/
protected function configure()
{
$this->setName('xdata:mall:clear');
$this->setDescription('清理商城订单数据');
}
/**
* 业务指令执行.
* @throws \think\admin\Exception
*/
protected function execute(Input $input, Output $output)
{
$this->config = ConfigService::get();
$this->_autoCancelOrder();
$this->_autoRemoveOrder();
$this->_autoConfirmOrder();
$this->_autoCommentOrder();
}
/**
* 自动完成订单评论.
* @throws \think\admin\Exception
*/
protected function _autoCommentOrder()
{
if (empty($this->config['comment_auto'])) {
$this->queue->message(0, 0, '未启用订单自动评论功能!');
} else {
try {
$time = time() - intval(strval($this->config['comment_time']) * 3600);
$remark = $this->config['comment_text'] ?? '系统默认好评!';
$where = [['status', '=', 6], ['create_time', '<', date('Y-m-d H:i:s', $time)]];
[$count, $total] = [0, ($items = PluginWemallOrder::mk()->where($where)->select())->count()];
$items->map(function (PluginWemallOrder $order) use ($total, &$count, $remark) {
$this->queue->message($total, ++$count, "开始评论订单 {$order->getAttr('order_no')}");
$order->save(['status' => 7]);
$order->items()->select()->map(function (PluginWemallOrderItem $item) use ($remark) {
UserAction::comment($item, '5.0', $remark, '');
});
$this->queue->message($total, $count, "完成评论订单 {$order->getAttr('order_no')}", 1);
});
} catch (\Exception $exception) {
$this->setQueueError($exception->getMessage());
}
}
}
/**
* 自动完成签收订单.
* @throws \think\admin\Exception
*/
protected function _autoConfirmOrder()
{
if (empty($this->config['receipt_auto'])) {
$this->queue->message(0, 0, '未启用订单自动签收功能!');
} else {
try {
$time = time() - intval(strval($this->config['receipt_time']) * 3600);
$where = [['status', '=', 5], ['create_time', '<', date('Y-m-d H:i:s', $time)]];
$remark = $this->config['receipt_text'] ?? '系统自动签收订单!';
[$count, $total] = [0, ($items = PluginWemallOrder::mk()->where($where)->select())->count()];
$items->map(function (PluginWemallOrder $order) use ($total, &$count, $remark) {
$this->queue->message($total, ++$count, "开始签收订单 {$order->getAttr('order_no')}");
$order->save(['status' => 6, 'confirm_time' => date('Y-m-d H:i:s'), 'confirm_remark' => $remark]);
$this->app->event->trigger('PluginWemallOrderConfirm', $order);
$this->queue->message($total, $count, "完成签收订单 {$order->getAttr('order_no')}", 1);
});
} catch (\Exception $exception) {
$this->setQueueError($exception->getMessage());
}
}
}
/**
* 取消30分钟未支付订单.
* @throws \think\admin\Exception
*/
private function _autoCancelOrder()
{
if (empty($this->config['cancel_auto'])) {
$this->queue->message(0, 0, '未启用订单自动取消功能!');
} else {
try {
$time = time() - intval(strval($this->config['cancel_time']) * 3600);
$remark = $this->config['cancel_text'] ?? '自动取消未完成支付';
$where = [['status', 'in', [1, 2, 3]], ['create_time', '<', date('Y-m-d H:i:s', $time)]];
[$count, $total] = [0, ($items = PluginWemallOrder::mk()->where($where)->select())->count()];
$items->map(function (PluginWemallOrder $order) use ($total, &$count, $remark) {
if ($order->payment()->findOrEmpty()->isExists()) {
$this->queue->message($total, ++$count, "订单 {$order->getAttr('order_no')} 存在支付记录");
} else {
$this->queue->message($total, ++$count, "开始取消订单 {$order->getAttr('order_no')}");
$order->save(['status' => 0, 'cancel_status' => 1, 'cancel_time' => date('Y-m-d H:i:s'), 'cancel_remark' => $remark]);
UserOrder::stock($order->getAttr('order_no'));
$this->queue->message($total, $count, "完成取消订单 {$order->getAttr('order_no')}", 1);
}
});
} catch (\Exception $exception) {
$this->setQueueError($exception->getMessage());
}
}
}
/**
* 清理已取消的订单.
* @throws \think\admin\Exception
*/
private function _autoRemoveOrder()
{
if (empty($this->config['remove_auto'])) {
$this->queue->message(0, 0, '未启用订单自动清理功能!');
} else {
try {
$time = time() - intval(strval($this->config['remove_time']) * 3600);
$remark = $this->config['remove_text'] ?? '系统自动清理已取消的订单!';
$where = [['status', '=', 0], ['deleted_status', '=', 0], ['create_time', '<', date('Y-m-d H:i:s', $time)]];
[$count, $total] = [0, ($items = PluginWemallOrder::mk()->where($where)->select())->count()];
$items->map(function (PluginWemallOrder $order) use ($total, &$count, $remark) {
if ($order->payment()->findOrEmpty()->isExists()) {
$this->queue->message($total, ++$count, "订单 {$order->getAttr('order_no')} 存在支付记录");
} else {
$this->queue->message($total, ++$count, "开始清理订单 {$order->getAttr('order_no')}");
$order->save([
'status' => 0,
'deleted_time' => date('Y-m-d H:i:s'),
'deleted_status' => 1,
'deleted_remark' => $remark,
]);
// 触发订单删除事件
$this->app->event->trigger('PluginWemallOrderRemove', $order);
$this->queue->message($total, $count, "完成清理订单 {$order->getAttr('order_no')}", 1);
}
});
} catch (\Exception $exception) {
$this->setQueueError($exception->getMessage());
}
}
}
}