单据多分录中如何对某个页签中分录行数据增加做前处理和后处理?

对于单据单分录,如果我们想对某行数据在增加时或删除时做处理,直接用框架提供的事件即可,但对于多分录目前框架无法直接处理,需要我们自己在代码中来处理,具体例子如下:

/**
 * output package name
 */
package com.kingdee.eas.huar.projectmgr.client;

...

/**
 * output class name
 */
public class PrjEstimateEditUI extends AbstractPrjEstimateEditUI implements IDetailPanelListener {
 private static final Logger logger = CoreUIObject.getLogger(PrjEstimateEditUI.class);

 ...
 /**
  * output class constructor
  */
 public PrjEstimateEditUI() throws Exception {
  super();
 }

 protected void initListener() {
  super.initListener();
  Container pnlPlaceCirc = kdtPlaceCircs.getParent().getParent();
  if (pnlPlaceCirc instanceof DetailPanel) {
   DetailPanel detailPanel = (DetailPanel) pnlPlaceCirc;
   detailPanel.addAddListener(this);
   detailPanel.addInsertListener(this);
  }

  cmbxMakeMethod.addItemListener(new ItemListener() {

   public void itemStateChanged(ItemEvent e) {
    if (e.getStateChange() == ItemEvent.SELECTED) {
     boolean enabled = e.getItem() == RentCalcMethodEnum.pressAndEngineFixedRent;
     txtNomMonthRate.setEnabled(enabled);
    }
   }
  });
 }

 public void beforeEvent(DetailPanelEvent e) throws Exception {
  PrjEstimatePlaceCircInfo prjEstimatePlaceCirc = (PrjEstimatePlaceCircInfo) e.getObjectValue();
  prjEstimatePlaceCirc.setCurrency(currency);
  prjEstimatePlaceCirc.setCreator(SysContext.getSysContext().getCurrentUserInfo().getPerson());
  prjEstimatePlaceCirc.setCreateTime(new Date());
 }

 public void afterEvent(DetailPanelEvent e) throws Exception {
  // TODO 自动生成方法存根

 }
}

<?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; use think\db\exception\DataNotFoundException; use think\db\exception\DbException; use think\db\exception\ModelNotFoundException; class WareHousingFeeService { public function __construct() {} /** * @notes 京东仓储服务费核对逻辑(流式优化版) * @param string $yearMonth 年月字符串(格式:YYYY-MM) * @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'); if (empty($quoteList)) { Log::warning("京东仓储服务费报价表为空,月份: {$yearMonth}"); return false; } // 3. 配置参数(从环境变量获取或使用默认值) $batchSize = 2; // 增大批次大小以提高效率 $reconnectInterval = 1; // 每20万条重新连接一次数据库(100批次=20万条) $gcInterval = 20; // 垃圾回收间隔 $updateCount = 0; // 处理计数 $batchCount = 0; // 批次计数 $status = true; // 整体状态 // 性能监控变量 $startTime = microtime(true); try { // 4. 创建基础查询 $query = WareHousingFeesItemizationModel::whereBetween('business_time', [ $monthTimeRange['startTime'], $monthTimeRange['endTime'] ]); // 5. 分批次处理数据并直接计算更新 $query->chunk($batchSize, function($items) use ( &$batchCount, &$updateCount, $quoteList, $yearMonth, $reconnectInterval, $gcInterval ) { $batchCount++; // 6. 分组统计当前批次数据 $batchGroupedData = []; foreach ($items as $item) { $key = $item->document_number . '|' . $item->document_type; // 初始化分组数据 if (!isset($batchGroupedData[$key])) { $batchGroupedData[$key] = [ 'document_number' => $item->document_number, 'document_type' => $item->document_type, 'total_quantity' => 0, 'total_settlement_amount' => 0 ]; } // 累加数量金额 $batchGroupedData[$key]['total_quantity'] += $item->quantity; $batchGroupedData[$key]['total_settlement_amount'] += $item->settlement_amount; // 及时释放内存 unset($item); } // 7. 计算当前批次的理论金额差额 $updateData = []; foreach ($batchGroupedData 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; } // 跳过计算失败的单据 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. 增量更新结果表 if (!empty($updateData)) { $this->incrementalUpsert($updateData); $updateCount += count($updateData); Log::info("批次 {$batchCount} 已更新 " . count($updateData) . " 条记录"); } // 9. 内存管理(释放不再使用的变量) unset($items, $batchGroupedData, $updateData); // 10. 定期执行垃圾回收 if ($batchCount % $gcInterval === 0) { gc_collect_cycles(); } // 11. 定期重连数据库(避免长连接超时) if ($batchCount % $reconnectInterval === 0) { $this->reconnectDatabase(); Log::info("京东仓储服务费用核对已处理 {$batchCount} 批次,进行数据库重连"); } }); } catch (\Throwable $e) { Log::error("京东仓储费核对系统运行异常: {$e->getMessage()}"); $status = false; } finally { // 12. 确保关闭数据库连接 Db::close(); } // 13. 生成最终统计日志 $peakMemory = round(memory_get_peak_usage() / 1024 / 1024, 2); $totalTime = round(microtime(true) - $startTime, 2); Log::info("京东仓储费核对完成: {$yearMonth}, 处理单据: {$updateCount}条, 批次: {$batchCount}, 峰值内存: {$peakMemory}MB, 总耗时: {$totalTime}秒"); return $status; } /** * 增量更新方法(处理相同单据的累加) * @param array $updateData 要更新的数据 */ private function incrementalUpsert(array $updateData): void { if (empty($updateData)) return; // 按1000条数据分块处理,避免SQL语句过长 $chunks = array_chunk($updateData, 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语句 $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 total_quantity = total_quantity + VALUES(total_quantity), total_settlement_amount = total_settlement_amount + VALUES(total_settlement_amount), theoretical_amount = theoretical_amount + VALUES(theoretical_amount), balance = balance + VALUES(balance), yearMonth = VALUES(yearMonth)"; try { Db::execute($sql, $params); } catch (\Exception $e) { Log::error("京东仓储费增量更新失败: {$e->getMessage()}\nSQL语句: {$sql}"); throw $e; } } } /** * 数据库重连方法,用于长时间运行的任务,避免连接超时 */ private function reconnectDatabase() { try { $connection = \think\facade\Db::connect(); $connection->close(); // 关闭当前连接 $connection->connect(); // 重新建立连接 } catch (\Exception $e) { Log::error('【京东仓储服务费核对数据库重连失败】' . $e->getMessage()); } } /** * 获取月份时间范围(返回包含开始结束时间的数组) * @param string $yearMonth 年月字符串(格式:YYYY-MM) * @return array|null 时间范围数组或null(无效格式时) */ 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]; } } 正如你前面所分析的: // 8. 增量更新结果表 if (!empty($updateData)) { $this->incrementalUpsert($updateData); $updateCount += count($updateData); Log::info("批次 {$batchCount} 已更新 " . count($updateData) . " 条记录"); } 这里并不是简简单单的增量更新,而是需要根据类型数量重新计算结算金额、理论金额、差异 是不是应该优化一下?可是正如你前面所讲如果结果表数据量100万的话是不是又有查询的性能损耗,是不是应该创建相关查询索引?请回答我的问题并重新帮我完善代码,要求在我的代码的基础上进行优化调整给出完整代码注释信息
最新发布
07-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值