Notes 12 day12 异常

day12 异常

概述

异常 指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。

注意

异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行.

异常的体系

 

Error : 严重错误,无法通过处理解决的错误

Exception异常:异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的

异常(Exception)的分类

【编译时期】 和 【运行时期】 异常

  • 编译时期异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)

  • 运行时期异常:runtime异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)

 

异常处理方式

JVM默认处理异常的方式

==如果程序出现了问题,我们没有做任何处理,最终JVM 会做默认的处理,==

  • 处理方式有如下两个步骤:

    • 把异常的名称,错误原因及异常出现的位置等信息输出在了控制台

    • 程序停止执行

try-catch方式处理异常

定义格式

try {
    可能出现异常的代码;
    正常代码啊//不会再执行
} catch(异常类名 变量名) {
    异常的处理代码;
}

执行流程

  • 程序从 try 里面的代码开始执行

  • 出现异常,就会跳转到对应的 catch 里面去执行

  • 执行完毕之后,程序还可以继续往下执行

两个异常

public static void main(String[] args) {
        System.out.println("我在前面执行");
​
        int a = 1;
        int b = 0;
        int[] arr = {1,2,3};
        try {
            System.out.println(arr[4]);//出错
            System.out.println(a/b);
        } catch (ArithmeticException e) {
            e.printStackTrace();
            System.out.println("除数不能为0");
        } catch (IndexOutOfBoundsException e2){
            e2.printStackTrace();
            System.out.println("你访问的脚标已超出!!");
        }
​
        System.out.println("我在后面执行");
    }

Exception大的异常

    public static void main(String[] args) {
        System.out.println("我在前面执行");
​
        int a = 1;
        int b = 0;
        int[] arr = {1,2,3};
        try {
            System.out.println(arr[4]);//出错
            System.out.println(a/b);
        } catch (Exception e) {//抓取最大异常,但并不明确异常类型
            e.printStackTrace();
            System.out.println("有问题");
        }
​
        System.out.println("我在后面执行");
    }

注意事项

/**
  * 1:能明确的尽量明确,不要用大的来处理。
  * 2:平级关系的异常谁前谁后无所谓,如果出现了子父关系,父必须在后面。
  */
public static void main(String[] args) {
        int a = 1;
        int b = 0;
        int[] arr = {1,2,3};
        try {
            System.out.println(arr[4]);
            System.out.println(a/b);
        } catch (ArithmeticException e) { //平级关系的异常谁前谁后无所谓
            e.printStackTrace();
            System.out.println("除数不能为0");
        } catch (IndexOutOfBoundsException e2){ //平级关系的异常谁前谁后无所谓
            e2.printStackTrace();
            System.out.println("你访问的脚标已超出!!");
        } catch (Exception e3){ //大的异常类型放在最后面
            e3.printStackTrace();
            System.out.println("有问题");
        }
    }

    /**
     * try..catch (ArithmeticException | IndexOutOfBoundsException e){}
     * 注意:这个方法虽然简洁,但是也不够好。
     *          A:处理方式是一致的。(实际开发中,好多时候可能就是针对同类型的问题,给出同一个处理)
     *          B:多个异常间必须是平级关系
     */
    public static void main(String[] args) {
        int a = 1;
        int b = 0;
        int[] arr = {1,2,3};
        try {
            System.out.println(arr[4]);
            System.out.println(a/b);
        } catch (ArithmeticException | IndexOutOfBoundsException e) {
            e.printStackTrace();
            System.out.println("除数不能为0");
        }
    }

try中的变量作用只能作用在try中的代码块中

throws 处理方式

什么是throws处理

用在方法的声明上,一般是自己解决不了的问题,直接抛给调用者解决

声明异常格式

修饰符 返回值类型 方法名() throws 异常类名 {}  //一般使用准确的异常类型 ,也可以同时抛出多个类型  当一个方法可能抛出多个异常时,可以使用throws关键字将这些异常列在方法签名中。关于父类异常和子类异常的顺序,按照规则应该将父类异常放在子类异常之后。

throw 处理方式

抛出一个异常对象,一次只能抛出一个,但是可以分多次进行抛出

他的处理原理和jvm机的处理原理一样,第一步给出详细的异常信息,第二步停止程序的正常运行

区别:它可以通过给抛出异常对象有参构造传入想要输出的字符串,在控制台输出来

定义格式

修饰符 返回值类型 方法名(参数列表)  {
    if (判断条件) {
        throw new 异常对象("异常的原因");    
    }
}

throw 与 throws的区别

1.throw代表动作,表示抛出一个异常的动作; throws代表一种状态,代表方法可能有异常抛出
2.throw用在方法实现中,而throws用在方法声明中
3.throw只能用于抛出一种异常,而throws可以抛出多个异常

Throwable类中常见方法

Throwable 是异常的根类

常用的方法

String getMessage() 获取的异常的原因

String toString() 获取的是异常的类型和原因

void printStackTrace() 获取的是异常的类型 原因 和位置

finally 代码块

无论try 和 catch 中的代码是否执行 finally 中的代码都要执行

如果在finally之前有return

即使finally前有return,finally也要在return之前执行

自定义异常

为什么要自定义异常

在开发中总是有些异常情况是SUN没有定义好的,此时我们根据自己业务的异常情况来定义异常类

一个类继承了java.lang.Exception 类 她就成了编译异常类

一个类继承了`java.lang.RuntimeException类 她就成了运行时异常类

自定义异常类的定义格式

public class RegistException extends RuntimeException {
    public RegistException() {
    }

public RegistException(String message) {
    super(message);
}

}

final,finally和finalize的区别

  final:最终的意思,可以修饰类,成员变量,成员方法
         修饰类,类不能被继承
         修饰变量,变量是常量
         修饰方法,方法不能被重写
  finally:是异常处理的一部分,一般用于释放资源。
         一般来说,代码肯定会执行,特殊情况:在执行到finally之前jvm退出了
  finalize:是Object类的一个方法,用于垃圾回收,在调用GC方法之前会执行,从下向上执行finalize方法
/** * @notes 京东收到派服务费核对逻辑 * @param $yearMonth * @return bool * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @author 胡军 * @date 2025/07/01 */ public function deliveryPrimaryRegionsVerifyDo($yearMonth):bool{ $monthTimeRange = $this->getMonthTimeRange($yearMonth); $status = true; // 初始化状态 // 使用use关键字将$yearMonth传入闭包 如果闭包返回 false,chunk() 会立即停止处理后续数据块 DeliveryPrimaryRegionsItemizationModel::whereBetween('order_time', [ $monthTimeRange['startTime'], $monthTimeRange['endTime'] ])->chunk(100, function ($items) use ($yearMonth,&$status) { // 临时存储当前批次的结果 $itemizationMonthList = []; foreach ($items as $item) { //print_r($item->id."处理完成".PHP_EOL); $processRet = $this->processItem($item, $yearMonth); if(!empty($processRet)){ $itemizationMonthList[] = $processRet; } } // 优化后的事务处理->将整个批次作为一个事务处理,减少事务开启次数 $itemizationModel = new DeliveryPrimaryRegionsItemizationModel(); try { $itemizationModel->startTrans(); // 直接处理整个批次数据 $itemizationModel->saveAll($itemizationMonthList); $itemizationModel->commit(); } catch (\Exception $e) { $itemizationModel->rollback(); Log::error('【京东收派服务费明细核对】--月份为:'.$yearMonth."的费用核对发生错误:" . $e->getMessage()); $status = false; return false; } }); return $status; } // 将处理逻辑封装到单独方法当中 private function processItem($item,$yearMonth) { //实际配送特惠送区域型 $deliveryPrimary = DeliveryPrimaryRegionsQuoteModel::where('origin_province','=',$item['origin_province']) ->where('origin_location','=',$item['origin_city']) ->where('destination_province','=',$item['destination_province']) ->where('destination_location','=',$item['destination_city']) ->find(); if(!empty($deliveryPrimary)){ $deliveryPrimaryData = $deliveryPrimary->toArray(); //1、实际配送特惠送区域型->始发省&始发市&目的省&目的市去匹配对应收派服务费报价表中的始发省&始发市&目的省&目的市对应的京东标快分区值 $item['actual_delivery_area_type'] = $deliveryPrimaryData['jd_standard_partition']; }else{ //记录错误日志 Log::warning('【京东收派服务费明细核对】--月份为:'.$yearMonth."的明细表la_primary_regions_mx当中的[始发省&始发市&目的省&目的市] 没有匹配到 京东收派服务费报价表:la_primary_regions_quote对应的 [始发省&始发市&目的省&目的市],明细记录id为".$item['id']); return []; } //本应发货特惠送区域类型 $laPrimaryRegions = DeliveryPrimaryRegionsModel::where('province','=',$item['destination_province']) ->where('city','=',$item['destination_city'])->find(); if(!empty($laPrimaryRegions)){ //2、本应发货特惠送区域类型->目的省&目的市匹配对应一级区域表中的省&市对应的京东标快分区值 $laPrimaryRegionsData = $laPrimaryRegions->toArray(); $item['expected_delivery_area_type'] = $laPrimaryRegionsData['jd_zone']; }else{ //记录错误日志 Log::warning('【京东收派服务费明细核对】--月份为:'.$yearMonth."的明细表la_primary_regions_mx当中的[目的省&目的市] 没有匹配到 京东收派服务费一级区域表:la_primary_regions对应的 [目的省&目的市],明细记录id为".$item['id']); return []; } //3、是否跨仓->当实际配送特惠送区域型=实际配送特惠送区域时写入不跨仓否则跨仓 $item['is_cross_warehouse'] = $item['actual_delivery_area_type'] == $item['expected_delivery_area_type'] ? '2' : '1'; //4、理论重量: 收派服务费核对明细的平台订单号关联la_scm_day_outbound_data当中的billNo字段,查询该订单billNo下货品goodsNo及数量quantity,再通过关联基础数据物料la_warehouse_facility表的单品重量(通过条码)计算该订单下各货品的重量,并按照订单号分组、求和。 $scmDayOutboundData = ScmDayOutboundDataModel::where('billNo','=',$item['platform_order_number'])->select()->toArray(); if(!empty($scmDayOutboundData)){ $weightTotal = 0; foreach($scmDayOutboundData as $k => $v){ //skuBarcode $goodsWeightModel = GoodsWeightModel::where('barcode','=',$v['skuBarcode'])->field('single_item_weight')->find(); if(!empty($goodsWeightModel)){ $goodsWeight = $goodsWeightModel->toArray(); //la_scm_day_outbound_data数量*la_warehouse_facility单品重量(kg) $weightTotal += $v['quantity'] * $goodsWeight['single_item_weight']; }else{ //记录错误日志 Log::warning('【京东收派服务费明细核对】--月份为:'.$yearMonth."的明细表平台订单号关联la_scm_day_outbound_data当中的billNo字段 但是billNo字段关联基础数据物料la_warehouse_facility表的条码skuBarcode失败,明细记录id为".$item['id']); return []; } } $item['theoretical_weight'] = $weightTotal; }else{ Log::warning('【京东收派服务费明细核对】--月份为:'.$yearMonth."的明细表la_primary_regions_mx当中的平台订单号platform_order_number 没有匹配到 每日出库数据明细表:la_scm_day_outbound_data对应的关联单号billNo,明细记录id为".$item['id']); return []; } //5、重量取整-> 理论重量向上取0.5 if(is_numeric($item['theoretical_weight'])) { //如果是浮点数那么向上取0.5 $floatValue = (float)$item['theoretical_weight']; $item['weight_rounded'] = ceil($floatValue * 2) / 2; }else{ //如果是整数那么就直接赋值即可 $item['weight_rounded'] = $item['theoretical_weight']; } //6、重量差异-> 计费重量-重量取整 $item['weight_difference'] = $item['billing_weight'] - $item['weight_rounded']; //7、金额核验->(存在重复代码待优化 赶工期......) // ①费用类型=快递改址费 5; // ②费用类型=保价费 0.3; // ③费用类型=快递_转寄服务费 5; // ④费用类型=快递超长超重,【计费重量】*1元/kg // ⑤费用类型=快递运费,始发省&始发市&目的省&目的市,匹配对应 收派服务费报价表中的 始发省&始发市&目的省&目的市 对应的报价,使用【计费重量】按照报价计算: // 若 计费重量<=1 计算公式为:首重金额*0.28; // 若1<计费用重量<=30 计算公式为:(首重金额+(计费重量-1)*续重1金额)*0.28; // 若 计费重量>30 计算公式为:(首重金额+(计费重量-1)*续重1金额+(计费重量-30)*续重2金额)*0.28 if($item['fee_type'] == '快递改址费' || $item['fee_type'] == '快递_转寄服务费'){ $item['amount_verification'] = 5; }elseif($item['fee_type'] == '保价费'){ $item['amount_verification'] = 0.3; }elseif($item['fee_type'] == '快递超长超重'){ $item['amount_verification'] = $item['billing_weight'] * 1; }elseif($item['fee_type'] == '快递运费'){ if($item['billing_weight'] <= 1){ $item['amount_verification'] = $deliveryPrimary->first_weight * 0.28; }elseif($item['billing_weight'] <= 30){ $item['amount_verification'] = ($deliveryPrimary->first_weight + ($item['billing_weight'] - 1) * $deliveryPrimary->continued_weight1) * 0.28; }else{ // >30的情况 $item['amount_verification'] = ($deliveryPrimary->first_weight + ($item['billing_weight'] - 1) * $deliveryPrimary->continued_weight1 + ($item['billing_weight'] - 30) * $deliveryPrimary->continued_weight2) * 0.28; } }else{ Log::warning('【京东收派服务费明细核对】--月份为:'.$yearMonth."的明细表la_primary_regions_mx当中的费用类型fee_type 没有匹配到 京东收派服务费报价表:la_delivery_primary_regions对应的费用类型,明细记录id为".$item['id']); return []; } //8、核验差异—>结算金额-金额核验 $item['verification_difference'] = $item['settlement_amount'] - $item['amount_verification']; //9、理论计费->(存在重复代码待优化 赶工期......) // ①费用类型=快递改址费 5; // ②费用类型=保价费 0.3; // ③费用类型=快递_转寄服务费 5; // ④费用类型=快递超长超重,【计费重量】*1元/kg // ⑤费用类型=快递运费, 始发省&始发市&目的省&目的市去匹配对应 收派服务费报价表中的 始发省&始发市&目的省&目的市 当中对应的报价,使用【理论重量】向上取整 1,按照报价计算 // 若理论重量取整后<=1 计算公式为->首重*0.28; // 若1<理论重量<=30 计算公式为:(首重+(理论重量-1)*续重1)*0.28; // 若理论重量>30 计算公式为:(首重+(理论重量-1)*续重1+(理论重量-30)*续重2)*0.28 if($item['fee_type'] == '快递改址费' || $item['fee_type'] == '快递_转寄服务费'){ $item['theoretical_billing'] = 5; }elseif($item['fee_type'] == '保价费'){ $item['theoretical_billing'] = 0.3; }elseif($item['fee_type'] == '快递超长超重'){ $item['theoretical_billing'] = $item['billing_weight'] * 1; }elseif($item['fee_type'] == '快递运费'){ $roundedUp = ceil($item['theoretical_weight']); if($roundedUp <= 1){ $item['theoretical_billing'] = $deliveryPrimary->first_weight * 0.28; }elseif($roundedUp <= 30){ $item['theoretical_billing'] = ($deliveryPrimary->first_weight + ($item['billing_weight'] - 1) * $deliveryPrimary->continued_weight1) * 0.28; }else{ // >30的情况 $item['theoretical_billing'] = ($deliveryPrimary->first_weight + ($item['billing_weight'] - 1) * $deliveryPrimary->continued_weight1 + ($item['billing_weight'] - 30) * $deliveryPrimary->continued_weight2) * 0.28; } }else{ Log::warning('【京东收派服务费明细核对】--月份为:'.$yearMonth."的明细表la_primary_regions_mx当中的费用类型fee_type 没有匹配到 京东收派服务费报价表:la_delivery_primary_regions对应的费用类型,明细记录id为".$item['id']); return []; } //10、理论差异->结算金额-理论计费 $item['theoretical_difference'] = $item['settlement_amount'] - $item['theoretical_billing']; //11、不跨仓收费->(存在重复代码待优化 赶工期......) // ①费用类型=快递改址费 5; // ②费用类型=保价费 0.3; // ③费用类型=快递_转寄服务费 5; // ④费用类型=快递超长超重,【计费重量】*1元/kg // ⑤费用类型=快递运费,目的省&目的市,匹配对应 收派服务费报价表中的 目的省&目的市 对应的报价,使用【计费重量】按照一级区域报价计算 // 若计费重量<=1 计算公式为:首重*0.28; // 若1<计费用重量<=30 计算公式为:(首重+(计费重量-1)*续重1)*0.28; // 若计费重量>30 计算公式为:(首重+(计费重量-1)*续重1+(计费重量-30)*续重2)*0.28; $deliveryPrimaryRegions = DeliveryPrimaryRegionsModel::where('province','=',$item['destination_province']) ->where('city','=',$item['destination_city']) ->find(); if(empty($deliveryPrimaryRegions)){ //记录错误日志 Log::warning('【京东收派服务费明细核对】--月份为:'.$yearMonth."的明细表la_primary_regions_mx当中的[目的省&目的市] 没有匹配到 京东收派服务费一级区域表:la_primary_regions对应的 [目的省&目的市],明细记录id为".$item['id']); return []; } if($item['fee_type'] == '快递改址费' || $item['fee_type'] == '快递_转寄服务费'){ $item['non_cross_warehouse_fee'] = 5; }elseif($item['fee_type'] == '保价费'){ $item['non_cross_warehouse_fee'] = 0.3; }elseif($item['fee_type'] == '快递超长超重'){ $item['non_cross_warehouse_fee'] = $item['billing_weight'] * 1; }elseif($item['fee_type'] == '快递运费'){ if($item['billing_weight'] <= 1){ $item['non_cross_warehouse_fee'] = $deliveryPrimaryRegions->first_wt * 0.28; }elseif($item['billing_weight'] <= 30){ $item['non_cross_warehouse_fee'] = ($deliveryPrimaryRegions->first_wt + ($item['billing_weight'] - 1) * $deliveryPrimaryRegions->add_wt1) * 0.28; }else{ // >30的情况 $item['non_cross_warehouse_fee'] = ($deliveryPrimaryRegions->first_wt + ($item['billing_weight'] - 1) * $deliveryPrimaryRegions->add_wt1 + ($item['billing_weight'] - 30) * $deliveryPrimaryRegions->add_wt2) * 0.28; } }else{ Log::warning('【京东收派服务费明细核对】--月份为:'.$yearMonth."的明细表la_primary_regions_mx当中的[目的省&目的市] 没有匹配到 京东收派服务费一级区域表:la_primary_regions对应的 [目的省&目的市],明细记录id为".$item['id']); return []; } //12、跨仓差异->结算金额-不跨仓收费 $item['cross_warehouse_difference'] = $item['settlement_amount'] - $item['theoretical_billing']; return $item->toArray(); } /** * @notes 根据年月比如2025-05获取当月时间范围 精确到秒 (git有问题未解决 暂时无法写入common.php当中 临时放这) * @param string $yearMonth * @return array * @author 胡军 * @date 2025/07/01 */ 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万数据的代码,虽然使用了分块处理但是我感觉还有优化的空间,我之前优化完的代码就很好我可以提供给你代码做参考: /** * @notes 非销售出库明细核对逻辑(优化版) * @param $yearMonth * @return bool * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @author 胡军 * @date 2025/06/25 */ public function nonSalesFeeVerifyDo($yearMonth): bool { // 获取指定月份的时间范围(月初到月末) $monthTimeRange = $this->getMonthTimeRange($yearMonth); // 预加载并缓存报价数据,使用键值对存储(仓库名 => 报价信息) // 避免在后续循环中重复查询数据库,提高性能 $warehouseQuotes = []; $warehouseList = NonSalesItemizationModel::whereBetween('business_time', [ $monthTimeRange['startTime'], $monthTimeRange['endTime'] ])->group('warehouse_name')->column('warehouse_name'); // 批量获取所有仓库的报价记录 foreach ($warehouseList as $warehouse) { // 获取该仓库的报价记录 $quoteResult = NonSalesQuoteModel::where('warehouse', $warehouse)->select()->toArray(); if (!empty($quoteResult)) { $newQuoteResult = []; foreach ($quoteResult as $item) { $newQuoteResult[$item['fee_item']] = $item; } $warehouseQuotes[$warehouse] = $newQuoteResult; } } $status = true; // 处理状态标识 $batchSize = 500; // 批次大小即每次读取的数据条数 $reconnectInterval = 200; // 每10万条重新连接一次数据库(200批次=10万条) $updateCount = 0; // 记录已更新的总记录数 $batchCount = 0; // 记录当前批次数 try { // 使用游标分批处理数据,每次只加载少量数据到内存 // 避免一次性加载大量数据导致内存溢出 $query = NonSalesItemizationModel::whereBetween('business_time', [ $monthTimeRange['startTime'], $monthTimeRange['endTime'] ]); $query->chunk($batchSize, function ($items) use ( &$status, // 引用传递处理状态 &$updateCount, // 引用传递更新计数 &$batchCount, // 引用传递批次计数 $warehouseQuotes, // 仓库报价数据 $reconnectInterval, // 重连间隔 $yearMonth ) { $batchCount++; $updateBuffer = []; // 批量更新缓冲区,存储待更新的数据 // 遍历当前批次的所有明细记录 foreach ($items as $item) { $warehouseName = $item->warehouse_name; $itemData = $item->toArray(); // 检查仓库是否有对应的报价数据 if (isset($warehouseQuotes[$warehouseName])) { $quoteData = $warehouseQuotes[$warehouseName]; // 处理数量数据,确保不为空 $boxCount = !empty($itemData['box_count']) ? $itemData['box_count'] : 0; $looseItems = !empty($itemData['loose_items']) ? $itemData['loose_items'] : 0; // 计算理论费用 $theoreticalFee = $boxCount * $quoteData['整箱']['amount'] + $looseItems * $quoteData['拆零']['amount']; // 计算费用差异 $feeDifference = $itemData['charge_amount'] - $theoreticalFee; } else { // 记录无匹配报价的日志,方便后续排查 Log::warning('【菜鸟非销售出库费明细核对报价单仓库名称不匹配】--月份为:' . $yearMonth . "的订单明细核对匹配不到非销售出库费报价表la_nonsales_fee_quote当中的仓库名称,明细数据id为" . $itemData['id']); // 对于未匹配到的记录,设置为null以便标识 $theoreticalFee = null; $feeDifference = null; } // 准备更新数据,存入缓冲区 $updateBuffer[$itemData['id']] = [ 'theoretical_fee' => $theoreticalFee, // 理论费用 'fee_difference' => $feeDifference // 费用差异 ]; } // 使用高效的批量更新方法将缓冲区数据写入数据库 $this->batchUpdate('la_nosales_fee', $updateBuffer, [ 'theoretical_fee', 'fee_difference' ]); $updateCount += count($updateBuffer); // 更新总计数 // 定期重连数据库,避免长时间运行导致连接断开 if ($batchCount % $reconnectInterval === 0) { $this->reconnectDatabase(); Log::info("菜鸟非销售出库费用核对已处理 {$updateCount} 条,进行数据库重连"); } // 释放内存,避免内存泄漏 unset($items, $updateBuffer); }); } catch (\Exception $e) { // 记录异常信息,确保错误可追溯 Log::error('【菜鸟非销售出库费明细核对异常】--月份为:' . $yearMonth . "的费用核对发生错误:" . $e->getMessage()); $status = false; // 标记处理失败 } // 记录处理完成信息,包含更新的总记录数 Log::info("菜鸟非销售出库费用核对处理完成,共更新 {$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): 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 $yearMonth * @return array * @author 胡军 * @date 2025/06/25 */ 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-09
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值