CI批量更新$this->db->update_batch();

$this->db->update_batch();

生成一条update命令是以你提供的数据为基础的,并执行查询。你可以传递一个数组或对象的参数给update_batch()函数。下面是一个使用一个数组作为参数的示例:Generates an update string based on the data you supply, and runs the query. You can either pass an array or an object to the function. Here is an example using an array:

 1 $data = array(
 2    array(
 3       'title' => 'My title' ,
 4       'name' => 'My Name 2' ,
 5       'date' => 'My date 2'
 6    ),
 7    array(
 8       'title' => 'Another title' ,
 9       'name' => 'Another Name 2' ,
10       'date' => 'Another date 2'
11    )
12 );
13 
14 $this->db->update_batch('mytable', $data, 'title'); 
15 
16 // Produces: 
17 // UPDATE `mytable` SET `name` = CASE
18 // WHEN `title` = 'My title' THEN 'My Name 2'
19 // WHEN `title` = 'Another title' THEN 'Another Name 2'
20 // ELSE `name` END,
21 // `date` = CASE 
22 // WHEN `title` = 'My title' THEN 'My date 2'
23 // WHEN `title` = 'Another title' THEN 'Another date 2'
24 // ELSE `date` END
25 // WHERE `title` IN ('My title','Another title')

参数1:表名 参数2:如上所示的二维数组 参数3:键名.


提示: 所有的值都会自动进行安全性过滤.

 

即:

UPDATE `mytable`
SET `name` = CASE
WHEN `title` = 'My title' THEN
'My Name 2'
WHEN `title` = 'Another title' THEN
'Another Name 2'
ELSE
`name`
END,
`date` = CASE
WHEN `title` = 'My title' THEN
'My date 2'
WHEN `title` = 'Another title' THEN
'Another date 2'
ELSE
`date`
END
WHERE
`title` IN ('My title', 'Another title')

-----------------------------------------------------------

比如要批量更新状态未读为已读:

$data = array(
array(
'id' => '1' ,
'status' => 'READ'

),
array(
'id' => '2' ,
'status' => 'READ'
)
);

$this->db->update_batch('mytable', $data, 'id');

 

public function forwardDeliveryFeeVerifyDo($yearMonth):bool{ $monthTimeRange = $this->getMonthTimeRange($yearMonth); $itemizationMonthList = ItemizationModel::whereBetween('business_time',[$monthTimeRange['startTime'],$monthTimeRange['endTime']])->select()->toArray(); //遍历选中月份的正向配送明细进行规则计算 foreach($itemizationMonthList as $key => $value){ $warehouse = DeliveryRangeModel::where('province',$value['destination_province'])->field('warehouse')->find(); //本应发货仓 (计算规则: 根据正向配送费明细表la_delivery_fee的目的省 与 配送范围表la_delivery_range的省份匹配 省份对应的仓库名称就是本应发货仓) $itemizationMonthList[$key]['original_warehouse'] = $warehouse->warehouse ? $warehouse->warehouse : ''; //是否跨仓 (计算规则: 若正向配送费明细表la_delivery_fee的仓库名称等于上面拿到的本应发货仓,则标记不跨仓否则标记跨仓 1夸仓 0不夸仓) $itemizationMonthList[$key]['is_cross_warehouse'] = $value['warehouse_name'] == $warehouse->warehouse ? '0' : '1'; //理论重量 (计算规则: 正向配送明细表la_delivery_fee当中的logistics_order_code物流履约单号去匹配正向配送货品明细表la_delivery_goods_detail当中的logistics_order_no物流履约单号 // 两者是一对多的关系-->一个明细表的物流履约单号对应一个或者多个货品明细表里的物流履约单号; // 然后拿着正向配送货品明细表la_delivery_goods_detail当中的货品件数quantity,根据字段货品ID-->product_id匹配仓库费基础物量重量信息表la_warehouse_facility当中的京东商品代码jd_product_code或者 // 菜鸟货品ID-->cainiao_product_id去查对应的单品重量,然后用单品重量*正向配送货品明细表la_delivery_goods_detail当中的货品件数quantity之和就是理论重量) if(!empty($value["logistics_order_code"])){ $goodsDetailsInfo = GoodsDetailModel::where('logistics_order_no',$value['logistics_order_code'])->field('product_id,quantity')->select()->toArray(); //print_r($goodsDetailsInfo);die; if(!empty($goodsDetailsInfo)){ $weight = 0; foreach($goodsDetailsInfo as $k => $v){ //single_item_weight单位为克 $singleItemWeightPro = GoodsWeightModel::where('cainiao_product_id',$v['product_id'])->field('single_item_weight')->find(); $weight += $v['quantity'] * $singleItemWeightPro->single_item_weight; } //TODO: 3.1415926 保留三位小数 直接就是 3.141 暂定 $itemizationMonthList[$key]['theoretical_weight'] = $weight; }else{ //$itemizationMonthList[$key]['theoretical_weight'] = 0; //如果明细表有单号但是货品明细表匹配不到则跳过 不再进行接下来的复杂运算 continue; } }else{ //$itemizationMonthList[$key]['theoretical_weight'] = 0; //如果明细表单号都没有则直接跳过 不再进行接下来的复杂运算 continue; } //Kg (计算规则: 理论重量/1000) //TODO: Kg是否要四舍五入还是直接向上取整? $itemizationMonthList[$key]['kg'] = $itemizationMonthList[$key]['theoretical_weight'] / 1000; //重量差异 (计算规则: 仓库名称当中如果包含成都两个关键字,也就是成都仓那么【计费包材体积1kg计泡8000】<1000,取【计费包材体积1kg计泡8000】/1000-Kg;计费包材体积1kg计泡8000】>1000,取max( 计费包材体积1kg计泡8000,计费包材体积计泡8000)/1000-Kg; // 如果包含沈阳两个关键字,也就是沈阳仓那么取max(计费包材体积1kg计泡8000,计费包材体积计泡8000)/1000-Kg) if (str_contains($value['warehouse_name'], "成都")) { $weight_difference = 0; if($value['bubble_volume_1kg'] < 1000){ $weight_difference = $value['bubble_volume_1kg'] / 1000 - $itemizationMonthList[$key]['kg']; }elseif($value['bubble_volume_1kg'] > 1000){ $weight_difference = max($value['bubble_volume_1kg'],$value['bubble_volume']) / 1000 - $itemizationMonthList[$key]['kg']; } $itemizationMonthList[$key]['weight_difference'] = $weight_difference; } elseif(str_contains($value['warehouse_name'], "沈阳")) { $itemizationMonthList[$key]['weight_difference'] = max($value['bubble_volume_1kg'],$value['bubble_volume']) / 1000 - $itemizationMonthList[$key]['kg']; } //计费重量 (计算规则: 若0<kg<0.5,则取0.5;若0.5<kg<=1 则取 1;若 kg>1,则向上取整) $itemizationMonthList[$key]['charge_weight'] = $itemizationMonthList[$key]['kg'] > 1 ? ceil($itemizationMonthList[$key]['kg']) : ($itemizationMonthList[$key]['kg'] < 0.5 ? 0.5 : 1); //理论费用 (计算规则: 仓库名称+目的省匹配正向配送费报价表:la_delivery_fee_quote,基于计费重量匹配报价表计算金额) $deliveryFeeQuoteInfo = DeliveryQuoteModel::where('destination','=',$value['destination_province'])->where('warehouse','=',$value['warehouse_name'])->find()->toArray(); $theoreticalFee = $this->calculateTheoreticalFee($itemizationMonthList[$key]['charge_weight'],$deliveryFeeQuoteInfo); $itemizationMonthList[$key]['theoretical_fee'] = $theoreticalFee; //理论费用差异 (计算规则:计费金额-理论费用) $itemizationMonthList[$key]['fee_difference'] = $value['charge_amount'] - $theoreticalFee; //不跨仓费用 (计算规则:本应发货仓+目的省 正向配送费报价表:la_delivery_fee_quote,基于计费重量匹配报价表计算金额); //不跨仓的话那么 不跨仓费用应该就等于理论费用 if($itemizationMonthList[$key]['is_cross_warehouse'] == '1'){ //跨仓了就要重新计算不跨仓费用 $deliveryFeeQuoteYesInfo = DeliveryQuoteModel::where('destination','=',$value['destination_province'])->where('warehouse','=',$itemizationMonthList[$key]['original_warehouse'])->find()->toArray(); $itemizationMonthList[$key]['non_cross_warehouse_fee'] = $this->calculateTheoreticalFee($itemizationMonthList[$key]['charge_weight'],$deliveryFeeQuoteYesInfo); }else{ $itemizationMonthList[$key]['non_cross_warehouse_fee'] = $theoreticalFee; } //跨仓差异 (计算规则:计费金额-不跨仓费用) $itemizationMonthList[$key]['cross_warehouse_difference'] = $value['charge_amount'] - $itemizationMonthList[$key]['non_cross_warehouse_fee']; } //因为涉及到的关联关系比较多,未必每一行数据都能正确匹配到相关数据,所以我们只对物流履约单号匹配成功的数据做更新操作 foreach($itemizationMonthList as $key => $value){ if(empty($value['original_warehouse']) || empty($value['theoretical_weight']) || $value['theoretical_weight'] == 0){ //记录日志 Log::warning('【正向配送费核对仓库名称或物流履约单号不匹配】--月份为:'.$yearMonth."的费用核对匹配不到仓库名称或者物流履约单号,明细数据id为".$value['id']); unset($itemizationMonthList[$key]); } } //批量分批次更新数据库 $status = true; $batchSize = 50; // 每批10条记录 $totalCount = count($itemizationMonthList); $batchCount = ceil($totalCount / $batchSize); $itemizationModel = new ItemizationModel(); for ($i = 0; $i < $batchCount; $i++) { $batchData = array_slice($itemizationMonthList, $i * $batchSize, $batchSize); try { $itemizationModel->startTrans(); // 使用批量更新替代循环单条更新 $itemizationModel->saveAll($batchData); $itemizationModel->commit(); } catch (\Exception $e) { $itemizationModel->rollback(); //记录日志 Log::error('【正向配送费核对异常】--月份为:'.$yearMonth."的费用核对发生错误:" . $e->getMessage()); //其中一个批次数据处理失败则直接退出循环 不再继续执行后续批次的数据处理 报错给前端显示 $status = false; break; } } return $status; } 我现在需要对数据库当中接近100万的数据进行处理,这是我之前写的业务逻辑代码肯定不行,因为不能一次性读取100万出来去处理,可以使用使用游标分批处理chunk, 然后再高效批量更新,你看一下是我其他模块的处理业务逻辑的代码就是这么做的: public function ReverseDeliveryFeeVerifyDo($yearMonth): bool { $monthTimeRange = $this->getMonthTimeRange($yearMonth); //使用键值对缓存报价数据 $quoteMap = QuoteModel::column('price', 'warehous'); if (empty($quoteMap)) { Log::warning("【逆向配送费报价数据缺失】月份:{$yearMonth}"); return false; } $status = true; $batchSize = 500; //批次大小即每次读取的数据条数 $reconnectInterval = 200; //每10万条重新连接一次数据库 200批次后重连(每200批=10万条) $updateCount = 0; try { //使用游标分批处理(减少内存占用) $query = ItemizationModel::whereBetween('business_time', [ $monthTimeRange['startTime'], $monthTimeRange['endTime'] ])->field('id,billing_amount,warehouse_name'); $query->chunk($batchSize, function ($items) use ($quoteMap, &$updateCount, &$status, $reconnectInterval) { static $batchCount = 0; $batchCount++; //批量更新缓冲区 $updateBuffer = []; foreach ($items as $item) { if (isset($quoteMap[$item->warehouse_name])) { $updateBuffer[$item->id] = [ 'theoretical_amount' => $quoteMap[$item->warehouse_name], 'fee_difference' => $item->billing_amount - $quoteMap[$item->warehouse_name] ]; } else { $updateBuffer[$item->id] = [ 'theoretical_amount' => null, 'fee_difference' => null ]; } } //高效批量更新 $this->batchUpdate($updateBuffer); $updateCount += count($updateBuffer); // 条件重连(减少不必要重连) if ($batchCount % $reconnectInterval === 0) { $this->reconnectDatabase(); Log::info("逆向配送费用核对已处理 {$updateCount} 条,进行数据库重连"); } unset($items, $updateBuffer); }); } catch (\Exception $e) { Log::error("逆向配送费核对异常: {$e->getMessage()}"); $status = false; } Log::info("逆向配送费用核对处理完成,共更新 {$updateCount} 条记录"); return $status; } //高效批量更新方法(单SQL更新多条) private function batchUpdate(array $data) { if (empty($data)) return; $table = 'la_reverse_delivery_fee'; $cases = []; $ids = []; $params = []; // 构建CASE WHEN更新语句 foreach (['theoretical_amount', 'fee_difference'] as $field) { $cases[$field] = 'CASE id '; foreach ($data as $id => $row) { $cases[$field] .= "WHEN {$id} THEN ? "; $params[] = $row[$field]; $ids[] = $id; } $cases[$field] .= 'END'; } $idsStr = implode(',', $ids); /* 最后的执行sql可以视为(案例sql): UPDATE la_reverse_delivery_fee SET theoretical_amount = CASE id WHEN 1001 THEN 100 -- 当ID=1001时,更新为100 WHEN 1002 THEN 200 -- 当ID=1002时,更新为200 END, fee_difference = CASE id WHEN 1001 THEN 5 -- 当ID=1001时,更新为5 WHEN 1002 THEN 10 -- 当ID=1002时,更新为10 END WHERE id IN (1001,1002); 其实就是巧妙地利用了 SQL 的CASE WHEN语法,将多行更新合并为单条 SQL,是一种常见的数据库优化技巧! */ $sql = "UPDATE {$table} SET theoretical_amount = {$cases['theoretical_amount']}, fee_difference = {$cases['fee_difference']} WHERE id IN ({$idsStr})"; Db::execute($sql, $params); } //数据库重连方法 private function reconnectDatabase() { try { $connection = \think\facade\Db::connect(); $connection->close(); $connection->connect(); } catch (\Exception $e) { //print_r("数据库连接失败(直接干死结束进程):".$e->getMessage());die; Log::error('【逆向配送费用核对数据库重连失败】' . $e->getMessage()); } } /** * @notes 根据年月比如2025-05获取当月时间范围 精确到秒 (git有问题未解决 暂时无法写入common.php当中 临时放这) * @param string $yearMonth * @return array * @author 胡军 * @date 2025/06/19 */ private function getMonthTimeRange(string $yearMonth): array { // 验证输入格式 (YYYY-MM) if (!preg_match('/^\d{4}-(0[1-9]|1[0-2])$/', $yearMonth)) { throw new InvalidArgumentException('输入格式不正确,必须为YYYY-MM格式'); } list($year, $month) = explode('-', $yearMonth); // 构建开始时间 $startTime = "{$year}-{$month}-01 00:00:00"; // 使用DateTime类计算当月最后一天 $lastDay = (new \DateTime("{$year}-{$month}-01")) ->modify('last day of this month') ->format('d'); // 构建结束时间 $endTime = "{$year}-{$month}-{$lastDay} 23:59:59"; return [ 'startTime' => $startTime, 'endTime' => $endTime ]; } 请模仿下面的帮我处理一下我之前的直接取出100万数据进行处理的老代码
07-06
public function ReverseDeliveryFeeVerifyDo($yearMonth): bool { $monthTimeRange = $this->getMonthTimeRange($yearMonth); // 一次性查询出逆向配送费报价表数据 避免重复查询数据库 $quoteList = QuoteModel::field('warehous,price')->select()->toArray(); $quoteMap = []; if (!empty($quoteList)) { $quoteMap = array_column($quoteList, 'price', 'warehous'); } else { //Log::warning('【逆向配送费核对仓库名称不匹配】--月份为:' . $yearMonth . "的费用核对匹配不到逆向配送费报价表的仓库名称"); return false; } $status = true; // 初始化状态 $itemizationModel = new ItemizationModel(); $batchCount = 0; // 批次计数器 $reconnectInterval = 100; // 每100批重连一次 try { // 使用chunk方法分批处理数据,每批处理50条 ItemizationModel::whereBetween('business_time', [ $monthTimeRange['startTime'], $monthTimeRange['endTime'] ])->field('id,billing_amount,warehouse_name,theoretical_amount,fee_difference')->chunk(10, function ($items) use ($yearMonth, $quoteMap, &$status, $itemizationModel, &$batchCount, $reconnectInterval) { // 批次计数递增 $batchCount++; // 达到重连间隔时,重新连接数据库 if ($batchCount % $reconnectInterval === 0) { $this->reconnectDatabase(); //Log::info("【数据库重连】已处理{$batchCount}批,重新连接数据库"); } $batchData = []; foreach ($items as $item) { $itemData = $item->toArray(); // 转换为数组便于处理 if (!empty($itemData['warehouse_name']) && isset($quoteMap[$itemData['warehouse_name']])) { $itemData['theoretical_amount'] = $quoteMap[$itemData['warehouse_name']]; $itemData['fee_difference'] = $itemData['billing_amount'] - $quoteMap[$itemData['warehouse_name']]; } else { $itemData['theoretical_amount'] = ''; $itemData['fee_difference'] = ''; //Log::warning('【逆向配送费核对仓库名称不匹配】--月份为:' . $yearMonth . "的费用核对匹配不到逆向配送费报价表的仓库名称,明细数据id为" . $itemData['id']); } $batchData[] = $itemData; unset($item, $itemData); // 释放单条数据内存 } // 使用模型批量更新 if (!empty($batchData)) { $itemizationModel->saveAll($batchData); } // 释放批次数据内存并触发垃圾回收 unset($items, $batchData); gc_collect_cycles(); }); } catch (\Exception $e) { print_r("报错了直接干死整个进程,具体报错信息如下:".$e->getMessage());die; //Log::error('【逆向配送费核对异常】--月份为' . $yearMonth . "的费用核对发生错误:" . $e->getMessage()); $status = false; } // 释放主函数中的大型变量 unset($quoteList, $quoteMap, $itemizationModel); gc_collect_cycles(); return $status; } /** * 数据库重连方法 */ private function reconnectDatabase() { try { $connection = \think\facade\Db::connect(); $connection->close(); $connection->connect(); } catch (\Exception $e) { print_r("数据库连接失败:".$e->getMessage());die; Log::error('【数据库重连失败】' . $e->getMessage()); } } | 我利用上面的代码读取mysql的数据然后进行逻辑运算然后再更新回去 100万的数据到了80万左右就会报错:SQLSTATE[HY000]: General error: 2006 MySQL server has gone away 错误 我的mysql的配置如下: [mysqld] #服务Id唯一 server-id = 1 port = 3306 log-error = /var/log/mysql/error.log # 只能用IP地址 skip_name_resolve # 数据库默认字符集 character-set-server = utf8 # 数据库字符集对应一些排序等规则 collation-server = utf8_general_ci # 设置client连接mysql时的字符集,防止乱码 init_connect='SET NAMES utf8' # 最大连接数 max_connections = 10000 # 增加连接错误上限 max_connect_errors = 10000 # 服务器等待客户端请求的超时时间(秒),默认8小时 wait_timeout = 28800 # 交互式连接超时时间(秒),默认28800 interactive_timeout = 28800 # 关闭前等待活动的时间(秒),默认28800 net_read_timeout = 28800 net_write_timeout = 28800 我的php.ini里设置如下: post_max_size = 100M upload_max_filesize = 50M memory_limit = 1024M 我的nginx里面的设置如下: client_max_body_size 100M; client_body_buffer_size 1024k; proxy_connect_timeout 3600; # 与上游服务器的连接超时 proxy_read_timeout 3600; # 从上游服务器读取响应的超时 proxy_send_timeout 3600; # 发送请求到上游服务器的超时 fastcgi_connect_timeout 3600; fastcgi_read_timeout 3600; fastcgi_send_timeout 3600; 请帮我分析一下这到底是什么问题导致的呢?
07-06
<?php namespace app\common\service\wareHousFee\jingdong\wareHousingFees; use app\common\model\wareHousFee\jingdong\wareHousingFees\WareHousingFeesItemizationModel; use app\common\model\wareHousFee\jingdong\wareHousingFees\WareHousingFeesQuoteModel; use think\facade\Db; use think\facade\Log; class WareHousingFeeService { public function __construct(){} /** * @notes 京东仓储服务费核对逻辑 * @param $yearMonth * @return bool * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @author 胡军 * @date 2025/06/27 */ public function wareHousingFeeVerifyDo(string $yearMonth): bool { // 1. 获取月份时间范围 $monthTimeRange = $this->getMonthTimeRange($yearMonth); if (!$monthTimeRange) { Log::error("无效的月份格式: {$yearMonth}"); return false; } // 2. 预加载报价数据 $quoteList = WareHousingFeesQuoteModel::where('service_type', 'IN', ['出库单', '退供单', '退货单']) ->column('first_three_items,additional_items', 'service_type'); //print_r($quoteList);die; /*Array ( [出库单] => Array ( [first_three_items] => 1.500 [additional_items] => 0.250 [service_type] => 出库单 ) [退供单] => Array ( [first_three_items] => 0.500 [additional_items] => 0.500 [service_type] => 退供单 ) [退货单] => Array ( [first_three_items] => 3.000 [additional_items] => 0.500 [service_type] => 退货单 ) )*/ if (empty($quoteList)) { Log::warning("京东仓储服务费报价表为空,月份: {$yearMonth}"); return false; } // 3. 配置参数(从环境变量获取或使用默认值) $batchSize = 5; // 批次大小即每次读取的数据条数 $reconnectInterval = 200; // 每10万条重新连接一次数据库(200批次=10万条) $gcInterval = 20; // 垃圾回收间隔 $updateCount = 0; // 处理计数 $batchCount = 0; // 批次计数 $status = true; // 整体状态 // 性能监控变量 $startTime = microtime(true); $startMemory = memory_get_usage(); try { // 4. 创建基础查询 $query = WareHousingFeesItemizationModel::whereBetween('business_time', [ $monthTimeRange['startTime'], $monthTimeRange['endTime'] ]); // 5. 分批次处理数据 $query->chunk($batchSize, function($items) use ( &$updateCount, &$batchCount, &$status, $quoteList, $reconnectInterval, $gcInterval, $yearMonth, $startTime, $startMemory ) { print_r($items); $batchCount++; //print_r($items->toArray());die; // 6. 分组统计(按单据号和单据类型分组) $groupedData = []; foreach ($items as $item) { $key = $item->document_number . '|' . $item->document_type; //echo $key; //ESL00000022972118012|出库单 //ESL00000022971633940|退货单 //ESL00000022971819716|退供单 //ESL00000022972118012|出库单 if (!isset($groupedData[$key])) { $groupedData[$key] = [ 'document_number' => $item->document_number, 'document_type' => $item->document_type, 'total_quantity' => 0, 'total_settlement_amount' => 0 ]; } $groupedData[$key]['total_quantity'] += $item->quantity;// 数量累加 $groupedData[$key]['total_settlement_amount'] += $item->settlement_amount;//结算金额累加 } /*echo "<pre>"; print_r($groupedData); echo "</pre>";die; Array ( [ESL00000022972118012|出库单] => Array ( [document_number] => ESL00000022972118012 [document_type] => 出库单 [total_quantity] => 4 [total_settlement_amount] => 3 ) [ESL00000022971633940|退货单] => Array ( [document_number] => ESL00000022971633940 [document_type] => 退货单 [total_quantity] => 1 [total_settlement_amount] => 1.5 ) [ESL00000022971819716|退供单] => Array ( [document_number] => ESL00000022971819716 [document_type] => 退供单 [total_quantity] => 1 [total_settlement_amount] => 0 ) )*/ // 7. 计算理论金额和差额 $updateData = []; foreach ($groupedData as $data) { //单据类型 $docType = $data['document_type']; //数量累加 $totalQty = $data['total_quantity']; //结算金额累加 $settlementAmount = $data['total_settlement_amount']; //理论金额 $theoreticalAmount = 0; //判断类型 根据报价单以及数量 拿到具体的首三件以及大于三件的具体价格进行计算 switch ($docType) { case '出库单': if (isset($quoteList['出库单'])) { $quote = $quoteList['出库单']; $theoreticalAmount = ($totalQty <= 3) ? $quote['first_three_items'] : $quote['first_three_items'] + ($totalQty - 3) * $quote['additional_items']; } break; case '退供单': if (isset($quoteList['退供单'])) { $quote = $quoteList['退供单']; $theoreticalAmount = $quote['first_three_items'] * $totalQty; } break; case '退货单': if (isset($quoteList['退货单'])) { $quote = $quoteList['退货单']; $theoreticalAmount = ($totalQty <= 3) ? $quote['first_three_items'] : $quote['first_three_items'] + ($totalQty - 3) * $quote['additional_items']; } break; } // 跳过理论金额为0的异常情况 if ($theoreticalAmount == 0) { Log::warning("京东仓储费计算失败: {$data['document_number']}, 单据类型: {$docType}"); continue; } // 计算费用差额: 结算金额累加 - 理论金额 $balance = $settlementAmount - $theoreticalAmount; $updateData[] = [ //单据编号 'document_number' => $data['document_number'], //单据类型 'document_type' => $docType, //总数量 'total_quantity' => $totalQty, //结算总金额 'total_settlement_amount' => $settlementAmount, //理论金额 'theoretical_amount' => $theoreticalAmount, //差异 'balance' => $balance, //所属年月 'yearMonth' => $yearMonth ]; } // 8. $updateData为最后要插入结果表的数据 批量更新数据库(支持插入和更新) if (!empty($updateData)) { $this->batchUpsert($updateData); $updateCount += count($updateData); } // 9. 内存管理(释放不再使用的变量) unset($items, $groupedData, $updateData); // 10. 定期执行垃圾回收 if ($batchCount % $gcInterval === 0) { gc_collect_cycles(); } // 11. 定期重连数据库(避免长连接超时) if ($batchCount % $reconnectInterval === 0) { $this->reconnectDatabase(); Log::info("京东仓储服务费用核对已处理 {$updateCount} 条,进行数据库重连"); } // 12. 输出进度日志(每10批次记录一次) if ($batchCount % 10 === 0) { $memUsage = round((memory_get_usage() - $startMemory) / 1024 / 1024, 2); $timeElapsed = round(microtime(true) - $startTime, 2); Log::info("处理进度: {$updateCount}条, 内存占用: {$memUsage}MB, 耗时: {$timeElapsed}秒"); } }); }catch (\Throwable $e) { Log::error("京东仓储费核对系统运行异常: {$e->getMessage()}"); $status = false; } finally { Db::close(); // 最终关闭数据库连接 } // 13. 生成最终统计日志 $peakMemory = round(memory_get_peak_usage() / 1024 / 1024, 2); $totalTime = round(microtime(true) - $startTime, 2); Log::info("京东仓储费核对完成: {$yearMonth}, 处理单据: {$updateCount}条, 峰值内存: {$peakMemory}MB, 总耗时: {$totalTime}秒"); return $status; } // 数据库重连方法,用于长时间运行的任务,避免连接超时 private function reconnectDatabase() { try { $connection = \think\facade\Db::connect(); $connection->close(); // 关闭当前连接 $connection->connect(); // 重新建立连接 } catch (\Exception $e) { // 记录重连失败日志,但不中断程序执行 Log::error('【菜鸟退货入仓费用核对数据库重连失败】' . $e->getMessage()); } } // 获取月份时间范围(返回包含开始和结束时间的数组) private function getMonthTimeRange(string $yearMonth): ?array { if (!preg_match('/^\d{4}-\d{2}$/', $yearMonth)) { return null; } $startTime = date('Y-m-01 00:00:00', strtotime($yearMonth)); $endTime = date('Y-m-t 23:59:59', strtotime($yearMonth)); return ['startTime' => $startTime, 'endTime' => $endTime]; } // 批量插入或更新数据(使用INSERT ON DUPLICATE KEY UPDATE语法) private function batchUpsert(array $data): void { if (empty($data)) return; // 按1000条数据分块处理,避免SQL语句过长 // TODO:根据自己服务器性能适度调整大小 反复测试找到最合适的值即可 $chunks = array_chunk($data, 1000); foreach ($chunks as $chunk) { $values = []; $params = []; // 构建SQL语句的VALUES部分 foreach ($chunk as $row) { $values[] = "(?, ?, ?, ?, ?, ?, ?)"; $params[] = $row['document_number']; $params[] = $row['document_type']; $params[] = $row['total_quantity']; $params[] = $row['total_settlement_amount']; $params[] = $row['theoretical_amount']; $params[] = $row['balance']; $params[] = $row['yearMonth']; } // 构建完整的INSERT ON DUPLICATE KEY UPDATE语句 // 使用了 MySQL 的 INSERT ... ON DUPLICATE KEY UPDATE 语法。这个语法允许我们尝试插入一行,如果该行已经存在(根据唯一索引或主键判断),则改为执行更新操作 // 务必创建la_storage_service_yes 表的唯一键索引:ALTER TABLE `la_storage_service_yes` ADD UNIQUE KEY `idx_document_number` (`document_number`); // 否则ON DUPLICATE KEY UPDATE语法不生效!!! $sql = "INSERT INTO la_storage_service_yes (document_number, document_type, total_quantity, total_settlement_amount, theoretical_amount, balance, yearMonth) VALUES " . implode(',', $values) . " ON DUPLICATE KEY UPDATE document_type = VALUES(document_type), total_quantity = VALUES(total_quantity), total_settlement_amount = VALUES(total_settlement_amount), theoretical_amount = VALUES(theoretical_amount), balance = VALUES(balance), yearMonth = VALUES(yearMonth)"; try { Db::execute($sql, $params); } catch (\Exception $e) { Log::error("京东仓储费批量更新失败: {$e->getMessage()}\nSQL语句: {$sql}"); throw $e; } } } } 结合我的代码进行改造吧!追加上临时表帮我彻底解决2000条只处理了500条的问题,记得保留注释信息
07-08
/** * @notes 菜鸟退货入仓费明细核对逻辑(优化版) * @param y e a r M o n t h ∗ @ r e t u r n b o o l ∗ @ t h r o w s D a t a N o t F o u n d E x c e p t i o n ∗ @ t h r o w s D b E x c e p t i o n ∗ @ t h r o w s M o d e l N o t F o u n d E x c e p t i o n ∗ @ a u t h o r 胡军 ∗ @ d a t e 2025 / 06 / 26 ∗ / p u b l i c f u n c t i o n c o m p u t e R e t u r n I t e m V e r i f y D o ( yearMonth∗@returnbool∗@throwsDataNotFoundException∗@throwsDbException∗@throwsModelNotFoundException∗@author胡军∗@date2025/06/26∗/publicfunctioncomputeReturnItemVerifyDo( yearMonth): bool { // 获取指定月份的时间范围(月初到月末) m o n t h T i m e R a n g e = monthTimeRange= this->getMonthTimeRange( y e a r M o n t h ) ; / / 预加载并缓存报价数据,使用键值对存储(仓库名 = > 报价信息) / / 避免在后续循环中重复查询数据库,提高性能 yearMonth);//预加载并缓存报价数据,使用键值对存储(仓库名=>报价信息)//避免在后续循环中重复查询数据库,提高性能 warehouseQuotes = []; w a r e h o u s e L i s t = C o m p u t e R e t u r n I t e m i z a t i o n M o d e l : : w h e r e B e t w e e n ( ′ b u s i n e s s t i m e ′ , [ warehouseList=ComputeReturnItemizationModel::whereBetween( ′ business t ​ ime ′ ,[ monthTimeRange[‘startTime’], m o n t h T i m e R a n g e [ ′ e n d T i m e ′ ] ] ) − > g r o u p ( ′ w a r e h o u s e n a m e ′ ) − > c o l u m n ( ′ w a r e h o u s e n a m e ′ ) ; / / 批量获取所有仓库的首件和每增加一件报价记录 ( 同一个仓库报价必须是两条并且首件报价在前每增加一件报价在后否则计算错误 ) f o r e a c h ( monthTimeRange[ ′ endTime ′ ]])−>group( ′ warehouse n ​ ame ′ )−>column( ′ warehouse n ​ ame ′ );//批量获取所有仓库的首件和每增加一件报价记录(同一个仓库报价必须是两条并且首件报价在前每增加一件报价在后否则计算错误)foreach( warehouseList as ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: …记录 firstResult = ComputeReturnFeeQuoteModel::where(‘warehouse’, w a r e h o u s e ) − > o r d e r ( ′ i d a s c ′ ) − > f i n d ( ) ; warehouse)−>order( ′ idasc ′ )−>find(); firstOrderQuote = f i r s t R e s u l t ? firstResult? firstResult->toArray() : []; // 获取该仓库的每增加一件报价记录 l a s t R e s u l t = C o m p u t e R e t u r n F e e Q u o t e M o d e l : : w h e r e ( ′ w a r e h o u s e ′ , lastResult=ComputeReturnFeeQuoteModel::where( ′ warehouse ′ , warehouse)->order(‘id desc’)->find(); l a s t O r d e r Q u o t e = lastOrderQuote= lastResult ? l a s t R e s u l t − > t o A r r a y ( ) : [ ] ; / / 仅当首件和续件报价都存在时才保存(确保数据完整性) i f ( ! e m p t y ( lastResult−>toArray():[];//仅当首件和续件报价都存在时才保存(确保数据完整性)if(!empty( firstOrderQuote) && !empty(ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: … warehouseQuotes[ w a r e h o u s e ] = [ ′ f i r s t ′ = > warehouse]=[ ′ first ′ => firstOrderQuote, // 首件价格信息 ‘last’ => ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 61: …]; }̲ } … status = true; // 处理状态标识 b a t c h S i z e = 500 ; / / 批次大小即每次读取的数据条数 batchSize=500;//批次大小即每次读取的数据条数 reconnectInterval = 200; // 每10万条重新连接一次数据库(200批次=10万条) u p d a t e C o u n t = 0 ; / / 记录已更新的总记录数 updateCount=0;//记录已更新的总记录数 batchCount = 0; // 记录当前批次数 try { // 使用游标分批处理数据,每次只加载少量数据到内存 // 避免一次性加载大量数据导致内存溢出 q u e r y = C o m p u t e R e t u r n I t e m i z a t i o n M o d e l : : w h e r e B e t w e e n ( ′ b u s i n e s s t i m e ′ , [ query=ComputeReturnItemizationModel::whereBetween( ′ business t ​ ime ′ ,[ monthTimeRange[‘startTime’], m o n t h T i m e R a n g e [ ′ e n d T i m e ′ ] ] ) ; monthTimeRange[ ′ endTime ′ ]]); query->chunk( b a t c h S i z e , f u n c t i o n ( batchSize,function( items) use ( &ParseError: KaTeX parse error: Expected 'EOF', got '&' at position 51: … &̲ updateCount, // 引用传递更新计数 & b a t c h C o u n t , / / 引用传递批次计数 batchCount,//引用传递批次计数 warehouseQuotes, // 仓库报价数据 r e c o n n e c t I n t e r v a l , / / 重连间隔 reconnectInterval,//重连间隔 yearMonth ) { b a t c h C o u n t + + ; batchCount++; updateBuffer = []; // 批量更新缓冲区,存储待更新的数据 // 遍历当前批次的所有明细记录 foreach ( i t e m s a s itemsas item) { w a r e h o u s e N a m e = warehouseName= item->warehouse_name; i t e m D a t a = itemData= item->toArray(); t h e o r e t i c a l F e e = 0 ; / / 检查仓库是否有对应的报价数据 i f ( i s s e t ( theoreticalFee=0;//检查仓库是否有对应的报价数据if(isset( warehouseQuotes[ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: … quoteData = w a r e h o u s e Q u o t e s [ warehouseQuotes[ warehouseName]; // 将CN订单SKU总重量(克)转换为千克 b u b b l e V o l u m e K g = bubbleVolumeKg= itemData[‘bubble_volume_1kg’] / 1000; q u a n t i t y = quantity= itemData[‘total_goods_quantity’]; // 确定价格类型 p r i c e T y p e = priceType= bubbleVolumeKg <= 0.5 ? ‘tiny’ : ( b u b b l e V o l u m e K g < = 3 ? ′ s m a l l ′ : ( bubbleVolumeKg<=3? ′ small ′ :( bubbleVolumeKg <= 5 ? ‘medium’ : ( b u b b l e V o l u m e K g < = 10 ? ′ l a r g e ′ : ′ t e n ′ ) ) ) ; / / 计算费用 bubbleVolumeKg<=10? ′ large ′ : ′ ten ′ )));//计算费用 theoreticalFee += ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: …irst']["price_{ priceType}"]; if (ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: … theoreticalFee += ( q u a n t i t y − 1 ) ∗ quantity−1)∗ quoteData[‘last’]["price_{ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 11: priceType}̲"]; … yearMonth . “的订单明细核对匹配不到退货入仓费报价表la_compute_return_fee_quote当中的仓库名称,明细数据id为” . i t e m D a t a [ ′ i d ′ ] ) ; / / 未匹配时设置为 n u l l 以便标识 itemData[ ′ id ′ ]);//未匹配时设置为null以便标识 theoreticalFee = null; } // 准备更新数据,存入缓冲区 u p d a t e B u f f e r [ updateBuffer[ itemData[‘id’]] = [ ‘theoretical_fee’ => t h e o r e t i c a l F e e , / / 理论费 用 ′ f e e d i f f e r e n c e ′ = > theoreticalFee,//理论费用 ′ fee d ​ ifference ′ => itemData[‘billing_amount’] - (ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 71: … }̲ … this->batchUpdate(‘la_compute_return_fee’, u p d a t e B u f f e r , [ ′ t h e o r e t i c a l f e e ′ , ′ f e e d i f f e r e n c e ′ ] ) ; updateBuffer,[ ′ theoretical f ​ ee ′ , ′ fee d ​ ifference ′ ]); updateCount += count( u p d a t e B u f f e r ) ; / / 更新总计数 / / 定期重连数据库,避免长时间运行导致连接断开 i f ( updateBuffer);//更新总计数//定期重连数据库,避免长时间运行导致连接断开if( batchCount % ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: … this->reconnectDatabase(); Log::info(“菜鸟退货入仓费用核对已处理 {ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 13: updateCount}̲ 条,进行数据库重连"); … items, ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 29: …); }̲); } ca… e) { // 记录异常信息,确保错误可追溯(保留原始注释逻辑) Log::error(‘【菜鸟退货入仓费明细核对异常】–月份为:’ . y e a r M o n t h . " 的费用核对发生错误 : " . yearMonth."的费用核对发生错误:". e->getMessage()); ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 37: …标记处理失败 }̲ // 记录处… updateCount} 条记录”); return $ status; } /** * 高效批量更新方法(单SQL更新多条) * @param string $ table 表名 * @param array $ data 更新数据,格式:[id => ['field1' => 'value1', 'field2' => 'value2']] * @param array $ fields 需要更新的字段 */ private function batchUpdate(string $ table, array $ data, array $ fields) { // 数据为空时直接返回,避免无效操作 if (empty($ data) || empty($ fields)) return; $ cases = []; // 存储CASE WHEN语句的数组 $ ids = []; // 存储所有需要更新的ID $ params = []; // 存储预处理语句的参数 // 构建CASE WHEN更新语句(每个字段一个CASE WHEN) foreach ($ fields as $ field) { $ cases[$ field] = 'CASE id '; // 为每个ID和对应字段值构建WHEN子句 foreach ($ data as $ id => $ row) { $ cases[$ field] .= "WHEN {$ id} THEN ? "; // 占位符用于预处理语句 $ params[] = $ row[$ field]; // 对应的值 $ ids[] = $ id; // 记录ID } $ cases[$ field] .= 'END'; // 结束CASE语句 } // 去重并拼接ID列表 $ idsStr = implode(',', array_unique($ ids)); // 构建SET子句(每个字段的CASE WHEN语句) $ setClauses = []; foreach ($ fields as $ field) { $ setClauses[] = "{$ field} = {$ cases[$ field]}"; } // 构建完整的UPDATE SQL语句(保留SQL案例注释) /* 最后的执行sql可以视为(案例sql): UPDATE la_reverse_delivery_fee SET theoretical_amount = CASE id WHEN 1001 THEN 100 -- 当ID=1001时,更新为100 WHEN 1002 THEN 200 -- 当ID=1002时,更新为200 END, fee_difference = CASE id WHEN 1001 THEN 5 -- 当ID=1001时,更新为5 WHEN 1002 THEN 10 -- 当ID=1002时,更新为10 END WHERE id IN (1001,1002); 其实就是巧妙地利用了 SQL 的CASE WHEN语法,将多行更新合并为单条 SQL,是一种常见的数据库优化技巧! */ $ sql = "UPDATE {$ table} SET " . implode(', ', $ setClauses) . " WHERE id IN ({$ idsStr})"; // 执行预处理语句,提高安全性和性能 Db::execute($ sql, $ params); } // 数据库重连方法,用于长时间运行的任务,避免连接超时 private function reconnectDatabase() { try { $ connection = \think\facade\Db::connect(); $ connection->close(); // 关闭当前连接 $ connection->connect(); // 重新建立连接 } catch (\Exception $ e) { // 记录重连失败日志,但不中断程序执行 Log::error('【菜鸟退货入仓费用核对数据库重连失败】' . $ e->getMessage()); } } /** * @notes 根据年月比如2025-05获取当月时间范围 精确到秒 * @param string y e a r M o n t h ∗ @ r e t u r n a r r a y ∗ @ a u t h o r 胡军 ∗ @ d a t e 2025 / 06 / 23 ∗ / p r i v a t e f u n c t i o n g e t M o n t h T i m e R a n g e ( s t r i n g yearMonth∗@returnarray∗@author胡军∗@date2025/06/23∗/privatefunctiongetMonthTimeRange(string yearMonth): array { // 验证输入格式 (YYYY-MM) if (!preg_match(‘/^\d{4}-(0[1-9]|1[0-2]) / ′ , / ′ , yearMonth)) { throw new InvalidArgumentException(‘输入格式不正确,必须为YYYY-MM格式’); } list( y e a r , year, month) = explode(’-', y e a r M o n t h ) ; / / 构建开始时间 yearMonth);//构建开始时间 startTime = “{ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 6: year}̲-{ month}-01 00:00:00”; // 使用DateTime类计算当月最后一天 ParseError: KaTeX parse error: Undefined control sequence: \DateTime at position 17: …lastDay = (new \̲D̲a̲t̲e̲T̲i̲m̲e̲("{ year}-{ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 7: month}̲-01")) … endTime = "{ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 6: year}̲-{ month}-{ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 9: lastDay}̲ 23:59:59"; … startTime, ‘endTime’ => $ endTime ]; } 这是我之前的业务模块处理100万数据核对的代码,也是您给出的方案,那么我上次提问的代码: /** * @notes 京东仓储服务费核对逻辑 * @param y e a r M o n t h ∗ @ r e t u r n b o o l ∗ @ t h r o w s D a t a N o t F o u n d E x c e p t i o n ∗ @ t h r o w s D b E x c e p t i o n ∗ @ t h r o w s M o d e l N o t F o u n d E x c e p t i o n ∗ @ a u t h o r 胡军 ∗ @ d a t e 2025 / 06 / 27 ∗ / p u b l i c f u n c t i o n w a r e H o u s i n g F e e V e r i f y D o ( yearMonth∗@returnbool∗@throwsDataNotFoundException∗@throwsDbException∗@throwsModelNotFoundException∗@author胡军∗@date2025/06/27∗/publicfunctionwareHousingFeeVerifyDo( yearMonth):bool{ m o n t h T i m e R a n g e = monthTimeRange= this->getMonthTimeRange( y e a r M o n t h ) ; / / 获取时间范围内的数据并进行分组统计 / / t o t a l q u a n t i t y 总数 / / t o t a l s e t t l e m e n t a m o u n t 总的结算金额 yearMonth);//获取时间范围内的数据并进行分组统计//total q ​ uantity总数//total s ​ ettlement a ​ mount总的结算金额 itemizationMonthList = WareHousingFeesItemizationModel::whereBetween(‘business_time’, [ m o n t h T i m e R a n g e [ ′ s t a r t T i m e ′ ] , monthTimeRange[ ′ startTime ′ ], monthTimeRange[‘endTime’]]) ->field([ ‘document_number’, ‘document_type’, ‘SUM(quantity) as total_quantity’, ‘SUM(settlement_amount) as total_settlement_amount’ ]) ->group(‘document_number, document_type’) ->select() ->toArray(); //一次性读取报价单避免foreach循环 提升效率 q u o t e L i s t = W a r e H o u s i n g F e e s Q u o t e M o d e l : : s e l e c t ( ) − > t o A r r a y ( ) ; quoteList=WareHousingFeesQuoteModel::select()−>toArray(); quoteListRst = []; foreach ( q u o t e L i s t a s quoteListas item) { q u o t e L i s t R s t [ quoteListRst[ item[‘service_type’]] = ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 16: item; }̲ if(!em… quoteListRst)){ foreach( i t e m i z a t i o n M o n t h L i s t a s itemizationMonthListas key => ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: … itemizationMonthList[ k e y ] [ ′ t h e o r e t i c a l a m o u n t ′ ] = 0 ; i f ( key][ ′ theoretical a ​ mount ′ ]=0;if( value[‘document_type’] == ‘出库单’ && !empty(ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: … // value[‘total_quantity’] 数量 if(ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: … itemizationMonthList[ k e y ] [ ′ t h e o r e t i c a l a m o u n t ′ ] = key][ ′ theoretical a ​ mount ′ ]= quoteListRst[‘出库单’][‘first_three_items’]; } else { i t e m i z a t i o n M o n t h L i s t [ itemizationMonthList[ key][‘theoretical_amount’] = q u o t e L i s t R s t [ ′ 出库 单 ′ ] [ ′ f i r s t t h r e e i t e m s ′ ] + ( quoteListRst[ ′ 出库单 ′ ][ ′ first t ​ hree i ​ tems ′ ]+( value[‘total_quantity’] - 3) * ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 63: … }̲ … value[‘document_type’] == ‘退供单’ && !empty(ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: … itemizationMonthList[ k e y ] [ ′ t h e o r e t i c a l a m o u n t ′ ] = key][ ′ theoretical a ​ mount ′ ]= quoteListRst[‘退供单’][‘first_three_items’] * ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 43: … }̲ … value[‘document_type’] == ‘退货单’ && !empty(ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: … if( value[‘total_quantity’] <= 3){ i t e m i z a t i o n M o n t h L i s t [ itemizationMonthList[ key][‘theoretical_amount’] = ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 64: … }̲ else { … itemizationMonthList[ k e y ] [ ′ t h e o r e t i c a l a m o u n t ′ ] = key][ ′ theoretical a ​ mount ′ ]= quoteListRst[‘退货单’][‘first_three_items’] + ( v a l u e [ ′ t o t a l q u a n t i t y ′ ] − 3 ) ∗ value[ ′ total q ​ uantity ′ ]−3)∗ quoteListRst[‘退货单’][‘additional_items’]; } } //正常计算出来的理论金额不应该是0 那么这个时候就要记录日志便于排查 if( i t e m i z a t i o n M o n t h L i s t [ itemizationMonthList[ key][‘theoretical_amount’] == 0){ //echo v a l u e [ ′ d o c u m e n t n u m b e r ′ ] . P H P E O L ; L o g : : w a r n i n g ( ′ 【京东仓储服务费明细核对】 − − 月份为 : ′ . value[ ′ document n ​ umber ′ ].PHP E ​ OL;Log::warning( ′ 【京东仓储服务费明细核对】−−月份为: ′ . yearMonth.“的京东仓储服务费订单明细核对匹配不到京东仓储服务费报价表la_storage_service_quotes当中的类型,明细单据编号为”. v a l u e [ ′ d o c u m e n t n u m b e r ′ ] ) ; u n s e t ( value[ ′ document n ​ umber ′ ]);unset( itemizationMonthList[ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 55: … }̲else{ … itemizationMonthList[ k e y ] [ ′ b a l a n c e ′ ] = key][ ′ balance ′ ]= value[‘total_settlement_amount’] - i t e m i z a t i o n M o n t h L i s t [ itemizationMonthList[ key][‘theoretical_amount’]; } } //批量分批次更新数据库 s t a t u s = t r u e ; status=true; batchSize = 50; // 每批10条记录 t o t a l C o u n t = c o u n t ( totalCount=count( itemizationMonthList); b a t c h C o u n t = c e i l ( batchCount=ceil( totalCount / b a t c h S i z e ) ; batchSize); itemizationModel = new WareHousingFeesItemVeryModel(); for ( i = 0 ; i=0; i < b a t c h C o u n t ; batchCount; i++) { b a t c h D a t a = a r r a y s l i c e ( batchData=array s ​ lice( itemizationMonthList, i ∗ i∗ batchSize, b a t c h S i z e ) ; batchSize); documentNumbers = array_column(ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: … itemizationModel->startTrans(); // 批量删除操作(根据单据号) if (!empty(ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: … itemizationModel->whereIn(‘document_number’, ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 50: … }̲ … itemizationModel->saveAll( b a t c h D a t a ) ; batchData); itemizationModel->commit(); } catch (\Exception ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: … itemizationModel->rollback(); //记录日志 Log::error(‘【京东仓储服务费明细核对异常】–月份为:’. y e a r M o n t h . " 的费用核对发生错误 : " . yearMonth."的费用核对发生错误:". e->getMessage()); //其中一个批次数据处理失败则直接退出循环 不再继续执行后续批次的数据处理 报错给前端显示 ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 61: … }̲ } … status; }else{ return false; } } /** * @notes 根据年月比如2025-05获取当月时间范围 精确到秒 (git有问题未解决 暂时无法写入common.php当中 临时放这) * @param string $ yearMonth * @return array * @author 胡军 * @date 2025/06/27 */ private function getMonthTimeRange(string $ yearMonth): array { // 验证输入格式 (YYYY-MM) if (!preg_match('/^\d{4}-(0[1-9]|1[0-2])$/', $ yearMonth)) { throw new InvalidArgumentException('输入格式不正确,必须为YYYY-MM格式'); } list($ year, $ month) = explode('-', $ yearMonth); // 构建开始时间 $ startTime = "{$ year}-{$ month}-01 00:00:00"; // 使用DateTime类计算当月最后一天 $ lastDay = (new \DateTime("{$ year}-{$ month}-01")) ->modify('last day of this month') ->format('d'); // 构建结束时间 $ endTime = "{$ year}-{$ month}-{$ lastDay} 23:59:59"; return [ 'startTime' => $ startTime, 'endTime' => $ endTime ]; } 是不是也可以按照这种优化思路去改造呢?
最新发布
07-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值