diff --git a/app/admin/lists/store_category/StoreCategoryLists.php b/app/admin/lists/store_category/StoreCategoryLists.php index e9e672069..459f3e5e0 100644 --- a/app/admin/lists/store_category/StoreCategoryLists.php +++ b/app/admin/lists/store_category/StoreCategoryLists.php @@ -57,6 +57,9 @@ class StoreCategoryLists extends BaseAdminDataLists implements ListsSearchInterf unset($priceRate[100003], $priceRate[100004]); $temp = []; foreach ($userGroups as $userGroup) { + if (!isset($priceRate[$userGroup['id']])) { + continue; + } if ($userGroup['id'] == 21 && !empty($priceRate[100001]) && empty($userGroup['rate'])) { $userGroup['rate'] = $priceRate[100001]['rate']; unset($priceRate[100001]); diff --git a/app/admin/logic/beforehand_order/BeforehandOrderLogic.php b/app/admin/logic/beforehand_order/BeforehandOrderLogic.php index e8941155d..d9bef6369 100644 --- a/app/admin/logic/beforehand_order/BeforehandOrderLogic.php +++ b/app/admin/logic/beforehand_order/BeforehandOrderLogic.php @@ -176,9 +176,12 @@ class BeforehandOrderLogic extends BaseLogic public static function generateOrder(array $params): bool { $order = BeforehandOrder::where('id', $params['id'])->find(); - if ($order['order_type'] == 4) { + if ($order['order_type'] == 4 || $order['order_type'] == 7) { throw new BusinessException('该订单类型不能生成支付订单'); } + if (!empty($order['order_sn'])) { + throw new BusinessException('当前订单已生成支付订单'); + } Db::startTrans(); try { $cart_info = BeforehandOrderCartInfo::where('bhoid', $params['id'])->select()->toArray(); @@ -198,14 +201,14 @@ class BeforehandOrderLogic extends BaseLogic } $cart_select[$k]['price'] = $v['price']; - //判断如果采购价小于售价,则采购价等于售价 - if ($v['price'] < $find['purchase'] && $find['purchase'] != 0) { - $cart_select[$k]['price'] = $find['purchase']; - } - if ($user['user_ship'] == 4 && $find['cost'] != 0) { - $cart_select[$k]['price'] = $find['cost']; - $total_prices = bcmul($find['cost'], $v['cart_num'], 2); - } +// //判断如果零售价小于供货价,则零售价等于供货价 +// if ($v['price'] < $find['purchase'] && $find['purchase'] != 0) { +// $cart_select[$k]['price'] = $find['purchase']; +// } +// if ($user['user_ship'] == 4 && $find['cost'] != 0) { +// $cart_select[$k]['price'] = $find['cost']; +// $total_prices = bcmul($find['cost'], $v['cart_num'], 2); +// } $cart_select[$k]['cost'] = $find['cost']; $cart_select[$k]['purchase'] = $find['purchase']; $cart_select[$k]['vip'] = 0; diff --git a/app/admin/logic/purchase_product_offer/PurchaseProductOfferLogic.php b/app/admin/logic/purchase_product_offer/PurchaseProductOfferLogic.php index fd04f6b2a..909780c0f 100644 --- a/app/admin/logic/purchase_product_offer/PurchaseProductOfferLogic.php +++ b/app/admin/logic/purchase_product_offer/PurchaseProductOfferLogic.php @@ -378,50 +378,49 @@ class PurchaseProductOfferLogic extends BaseLogic break; } } - if (empty($storeProductGroupPrice)) { - return; - } - foreach ($productCatePriceRate as $k => $v) { - if (empty($v['rate'])) { - continue; - } - if ($v['id'] == 4 &&$purchase>0) { - //商户 - $data['cost_lv'] = bcdiv($v['rate'], 100, 2); - $data['cost'] = bcmul($purchase, bcadd($data['cost_lv'], 1, 2), 2); - continue; - }elseif (($v['id'] == 100002 || $v['id'] == 22) &&$purchase>0) { - //零售 - $data['price_lv'] = bcdiv($v['rate'], 100, 2); - $data['price'] = bcmul($purchase, bcadd($data['price_lv'], 1, 2), 1); - if ($product['two_cate_id'] == 15268) { - $lastNum = substr($data['price'], -1); - if ($lastNum <= 2) { - $data['price'] = floor($data['price']); - } elseif ($lastNum < 5) { - $data['price'] = bcadd(floor($data['price']), 0.5, 1); - } else { - $data['price'] = bcadd(floor($data['price']), 1, 1); + if (!empty($purchase)) { + foreach ($productCatePriceRate as $k => $v) { + if (empty($v['rate'])) { + continue; + } + if ($v['id'] == 4 &&$purchase>0) { + //商户 + $data['cost_lv'] = bcdiv($v['rate'], 100, 2); + $data['cost'] = bcmul($purchase, bcadd($data['cost_lv'], 1, 2), 2); + continue; + }elseif (($v['id'] == 100002 || $v['id'] == 22) &&$purchase>0) { + //零售 + $data['price_lv'] = bcdiv($v['rate'], 100, 2); + $data['price'] = bcmul($purchase, bcadd($data['price_lv'], 1, 2), 1); + if ($product['two_cate_id'] == 15268) { + $lastNum = substr($data['price'], -1); + if ($lastNum <= 2) { + $data['price'] = floor($data['price']); + } elseif ($lastNum < 5) { + $data['price'] = bcadd(floor($data['price']), 0.5, 1); + } else { + $data['price'] = bcadd(floor($data['price']), 1, 1); + } } + continue; } - continue; - } - $baseRate = ($v['id'] == 100001 || $v['id'] == 21) ? 100 : 100 + $v['rate']; - if($purchase>0){ - $item = [ - 'product_id' => $product['id'], - 'group_id' => $v['id'], - 'group_name' => $v['title'], - 'price' => bcmul($purchase, $baseRate / 100, 2), - 'price_type' => 3, - 'base_rate' => $baseRate, - ]; - if (isset($storeProductGroupPrice[$v['id']])) { - $item['base_rate'] = $storeProductGroupPrice[$v['id']]['base_rate']; - $item['price'] = bcmul($purchase, $item['base_rate'] / 100, 2); - $item['id'] = $storeProductGroupPrice[$v['id']]['id']; + $baseRate = ($v['id'] == 100001 || $v['id'] == 21) ? 100 : 100 + $v['rate']; + if($purchase>0){ + $item = [ + 'product_id' => $product['id'], + 'group_id' => $v['id'], + 'group_name' => $v['title'], + 'price' => bcmul($purchase, $baseRate / 100, 2), + 'price_type' => 3, + 'base_rate' => $baseRate, + ]; + if (isset($storeProductGroupPrice[$v['id']])) { + $item['base_rate'] = $storeProductGroupPrice[$v['id']]['base_rate']; + $item['price'] = bcmul($purchase, $item['base_rate'] / 100, 2); + $item['id'] = $storeProductGroupPrice[$v['id']]['id']; + } + $priceConfig[] = $item; } - $priceConfig[] = $item; } } } diff --git a/app/admin/logic/store_product_price/StoreProductPriceLogic.php b/app/admin/logic/store_product_price/StoreProductPriceLogic.php index 8060df239..25e656e9e 100644 --- a/app/admin/logic/store_product_price/StoreProductPriceLogic.php +++ b/app/admin/logic/store_product_price/StoreProductPriceLogic.php @@ -101,16 +101,16 @@ class StoreProductPriceLogic extends BaseLogic 'status' => 1 ]); StoreProduct::where('id', $find['product_id'])->update([ - 'purchase' => $find['purchase'], - 'cost' => $find['cost'], - 'vip_price' => $find['cost'], - 'price' => $find['price'] + 'purchase' => $find['purchase'] ?? 0, + 'cost' => $find['cost'] ?? 0, + 'vip_price' => $find['cost'] ?? 0, + 'price' => $find['price'] ?? 0 ]); StoreBranchProduct::where('product_id', $find['product_id'])->update([ - 'purchase' => $find['purchase'], - 'cost' => $find['cost'], - 'vip_price' => $find['cost'], - 'price' => $find['price'] + 'purchase' => $find['purchase'] ?? 0, + 'cost' => $find['cost'] ?? 0, + 'vip_price' => $find['cost'] ?? 0, + 'price' => $find['price'] ?? 0 ]); self::setProductGroupPrice($find); } diff --git a/app/common/logic/ChangeLogLogic.php b/app/common/logic/ChangeLogLogic.php new file mode 100644 index 000000000..ed2773169 --- /dev/null +++ b/app/common/logic/ChangeLogLogic.php @@ -0,0 +1,24 @@ + $model, + 'link_id' => $link_id, + 'nums' => $nums, + 'pm' => $pm, + 'mark' => $info, + 'url' => $url, + 'create_time' => time() + ]); + \Chance\Log\facades\OperationLog::clearLog(); + } +} diff --git a/app/common/model/change_log/ChangeLog.php b/app/common/model/change_log/ChangeLog.php new file mode 100644 index 000000000..74a0e6981 --- /dev/null +++ b/app/common/model/change_log/ChangeLog.php @@ -0,0 +1,19 @@ +where('status', 1)->find(); + if ($dictType) { + $dictData = DictData::where('type_id', $dictType['id'])->where('name', $key)->where('status', 1)->value('value'); + return empty($dictData) ? '' : $dictData; + } + return ''; + } + } \ No newline at end of file diff --git a/app/common/service/xlsx/Beforehand.php b/app/common/service/xlsx/Beforehand.php index e21951e6f..894eac0e7 100644 --- a/app/common/service/xlsx/Beforehand.php +++ b/app/common/service/xlsx/Beforehand.php @@ -8,6 +8,8 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; +use Webman\Exception\BusinessException; + class Beforehand { public function export($data, $system_store) @@ -183,8 +185,15 @@ class Beforehand // 保存文件到 public 下 $writer = new Xlsx($spreadsheet); - $url = '/export/' . date('Y-m') . '/' . $order['info'].'出库单-'.date('Y-m-d H:i') . '.xlsx'; - $file_path = public_path() . $url; + $dir=public_path().'/export/'.date('Y-m'); + if (!file_exists($dir)) { + // 尝试创建目录 + if (!mkdir($dir)) { + throw new BusinessException('创建目录失败:/export/' . date('Y-m')); + } + } + $file_path = $dir. '/' . $order['info'].'出库单-'.date('Y-m-d H:i') . '.xlsx'; + $url = '/export/'.date('Y-m'). '/' . $order['info'].'出库单-'.date('Y-m-d H:i') . '.xlsx'; // 保存文件到 public 下 $writer->save($file_path); return getenv('APP_URL').$url; diff --git a/app/common/service/xlsx/OrderAllocation.php b/app/common/service/xlsx/OrderAllocation.php index 83ac3ace0..c50ab8dd0 100644 --- a/app/common/service/xlsx/OrderAllocation.php +++ b/app/common/service/xlsx/OrderAllocation.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; +use Webman\Exception\BusinessException; /** * 订单分配 @@ -161,8 +162,15 @@ class OrderAllocation // 保存文件到 public 下 $writer = new Xlsx($spreadsheet); - $url = '/export/' . date('Y-m') . '/' .'订单分配-'.date('Y-m-d H:i') . '.xlsx'; - $file_path = public_path() . $url; + $dir=public_path().'/export/'.date('Y-m'); + if (!file_exists($dir)) { + // 尝试创建目录 + if (!mkdir($dir)) { + throw new BusinessException('创建目录失败:/export/' . date('Y-m')); + } + } + $file_path = $dir. '/' .'订单分配-'.date('Y-m-d H:i') . '.xlsx'; + $url = '/export/'.date('Y-m'). '/' .'订单分配-'.date('Y-m-d H:i') . '.xlsx'; // 保存文件到 public 下 $writer->save($file_path); return getenv('APP_URL').$url; diff --git a/app/common/service/xlsx/OrderDetail.php b/app/common/service/xlsx/OrderDetail.php index d210cad63..e9208edc4 100644 --- a/app/common/service/xlsx/OrderDetail.php +++ b/app/common/service/xlsx/OrderDetail.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; +use Webman\Exception\BusinessException; class OrderDetail { @@ -148,8 +149,15 @@ class OrderDetail // 保存文件到 public 下 $writer = new Xlsx($spreadsheet); - $url = '/export/' . date('Y-m') . '/' . $system_store.'出库单-'.date('Y-m-d H:i') . '.xlsx'; - $file_path = public_path() . $url; + $dir=public_path().'/export/'.date('Y-m'); + if (!file_exists($dir)) { + // 尝试创建目录 + if (!mkdir($dir)) { + throw new BusinessException('创建目录失败:/export/' . date('Y-m')); + } + } + $file_path = $dir . '/' . $system_store.'出库单-'.date('Y-m-d H:i') . '.xlsx'; + $url = '/export/'.date('Y-m') . '/' . $system_store.'出库单-'.date('Y-m-d H:i') . '.xlsx'; // 保存文件到 public 下 $writer->save($file_path); return getenv('APP_URL').$url; diff --git a/app/common/service/xlsx/OrderInfo.php b/app/common/service/xlsx/OrderInfo.php index e7bbae9c0..aba991db4 100644 --- a/app/common/service/xlsx/OrderInfo.php +++ b/app/common/service/xlsx/OrderInfo.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; +use Webman\Exception\BusinessException; /** * 采购信息 @@ -124,8 +125,15 @@ class OrderInfo // 保存文件到 public 下 $writer = new Xlsx($spreadsheet); - $url = '/export/' . date('Y-m') . '/' .'采购信息-'.date('Y-m-d H:i') . '.xlsx'; - $file_path = public_path() . $url; + $dir=public_path().'/export/'.date('Y-m'); + if (!file_exists($dir)) { + // 尝试创建目录 + if (!mkdir($dir)) { + throw new BusinessException('创建目录失败:/export/' . date('Y-m')); + } + } + $file_path = $dir . '/' .'采购信息-'.date('Y-m-d H:i') . '.xlsx'; + $url = '/export/'.date('Y-m') . '/' .'采购信息-'.date('Y-m-d H:i') . '.xlsx'; // 保存文件到 public 下 $writer->save($file_path); return getenv('APP_URL').$url; diff --git a/app/common/service/xlsx/OrderList.php b/app/common/service/xlsx/OrderList.php index 61fa1abb9..a58af6184 100644 --- a/app/common/service/xlsx/OrderList.php +++ b/app/common/service/xlsx/OrderList.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; +use Webman\Exception\BusinessException; /** * 订单清单 @@ -159,8 +160,15 @@ class OrderList // 保存文件到 public 下 $writer = new Xlsx($spreadsheet); + $dir=public_path().'/export/'.date('Y-m'); + if (!file_exists($dir)) { + // 尝试创建目录 + if (!mkdir($dir)) { + throw new BusinessException('创建目录失败:/export/' . date('Y-m')); + } + } + $file_path = $dir. '/' .'供投里海农特产品下单清单-'.date('Y-m-d H:i') . '.xlsx'; $url = '/export/' . date('Y-m') . '/' .'供投里海农特产品下单清单-'.date('Y-m-d H:i') . '.xlsx'; - $file_path = public_path() . $url; // 保存文件到 public 下 $writer->save($file_path); return getenv('APP_URL').$url; diff --git a/app/common/service/xlsx/OrderOutbound.php b/app/common/service/xlsx/OrderOutbound.php index 52cf93c8f..49281bdbf 100644 --- a/app/common/service/xlsx/OrderOutbound.php +++ b/app/common/service/xlsx/OrderOutbound.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; +use Webman\Exception\BusinessException; /** * 订单出库 @@ -141,8 +142,15 @@ class OrderOutbound // 保存文件到 public 下 $writer = new Xlsx($spreadsheet); + $dir=public_path().'/export/'.date('Y-m'); + if (!file_exists($dir)) { + // 尝试创建目录 + if (!mkdir($dir)) { + throw new BusinessException('创建目录失败:/export/' . date('Y-m')); + } + } + $file_path = $dir . '/' .$title2.date('Y-m-d H:i') . '.xlsx'; $url = '/export/' . date('Y-m') . '/' .$title2.date('Y-m-d H:i') . '.xlsx'; - $file_path = public_path() . $url; // 保存文件到 public 下 $writer->save($file_path); return getenv('APP_URL').$url; diff --git a/app/common/service/xlsx/OrderOutboundFinance.php b/app/common/service/xlsx/OrderOutboundFinance.php index 862c44abf..52af0a4be 100644 --- a/app/common/service/xlsx/OrderOutboundFinance.php +++ b/app/common/service/xlsx/OrderOutboundFinance.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; +use Webman\Exception\BusinessException; /** * 财务订单出库 @@ -143,8 +144,15 @@ class OrderOutboundFinance // 保存文件到 public 下 $writer = new Xlsx($spreadsheet); + $dir=public_path().'/export/'.date('Y-m'); + if (!file_exists($dir)) { + // 尝试创建目录 + if (!mkdir($dir)) { + throw new BusinessException('创建目录失败:/export/' . date('Y-m')); + } + } + $file_path = $dir . '/' .$title2.date('Y-m-d H:i') . '.xlsx'; $url = '/export/' . date('Y-m') . '/' .$title2.date('Y-m-d H:i') . '.xlsx'; - $file_path = public_path() . $url; // 保存文件到 public 下 $writer->save($file_path); return getenv('APP_URL').$url; diff --git a/app/common/service/xlsx/OrderSupplyOutbound.php b/app/common/service/xlsx/OrderSupplyOutbound.php index fbd6fa44d..905b71a3f 100644 --- a/app/common/service/xlsx/OrderSupplyOutbound.php +++ b/app/common/service/xlsx/OrderSupplyOutbound.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; +use Webman\Exception\BusinessException; /** * 订单出库 @@ -149,8 +150,15 @@ class OrderSupplyOutbound // 保存文件到 public 下 $writer = new Xlsx($spreadsheet); + $dir=public_path().'/export/'.date('Y-m'); + if (!file_exists($dir)) { + // 尝试创建目录 + if (!mkdir($dir)) { + throw new BusinessException('创建目录失败:/export/' . date('Y-m')); + } + } + $file_path = $dir . '/' .$title2.date('Y-m-d H:i') . '.xlsx'; $url = '/export/' . date('Y-m') . '/' .$title2.date('Y-m-d H:i') . '.xlsx'; - $file_path = public_path() . $url; // 保存文件到 public 下 $writer->save($file_path); return getenv('APP_URL').$url; diff --git a/app/common/service/xlsx/ReturnSupplier.php b/app/common/service/xlsx/ReturnSupplier.php index 076724d26..6371aee6d 100644 --- a/app/common/service/xlsx/ReturnSupplier.php +++ b/app/common/service/xlsx/ReturnSupplier.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; +use Webman\Exception\BusinessException; /** * 订单退供应商 @@ -111,8 +112,15 @@ class ReturnSupplier // 保存文件到 public 下 $writer = new Xlsx($spreadsheet); + $dir=public_path().'/export/'.date('Y-m'); + if (!file_exists($dir)) { + // 尝试创建目录 + if (!mkdir($dir)) { + throw new BusinessException('创建目录失败:/export/' . date('Y-m')); + } + } + $file_path = $dir . '/' .'供投里海农特产品退供应商结算单-'.date('Y-m-d H:i') . '.xlsx'; $url = '/export/' . date('Y-m') . '/' .'供投里海农特产品退供应商结算单-'.date('Y-m-d H:i') . '.xlsx'; - $file_path = public_path() . $url; // 保存文件到 public 下 $writer->save($file_path); return getenv('APP_URL').$url; diff --git a/app/common/service/xlsx/StockReturn.php b/app/common/service/xlsx/StockReturn.php index 178b63c3d..e8ca2030b 100644 --- a/app/common/service/xlsx/StockReturn.php +++ b/app/common/service/xlsx/StockReturn.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; +use Webman\Exception\BusinessException; /** * 订单退库 @@ -127,8 +128,15 @@ class StockReturn // 保存文件到 public 下 $writer = new Xlsx($spreadsheet); + $dir=public_path().'/export/'.date('Y-m'); + if (!file_exists($dir)) { + // 尝试创建目录 + if (!mkdir($dir)) { + throw new BusinessException('创建目录失败:/export/' . date('Y-m')); + } + } + $file_path = $dir . '/' .'供投里海农特产品退库结算单-'.date('Y-m-d H:i') . '.xlsx'; $url = '/export/' . date('Y-m') . '/' .'供投里海农特产品退库结算单-'.date('Y-m-d H:i') . '.xlsx'; - $file_path = public_path() . $url; // 保存文件到 public 下 $writer->save($file_path); return getenv('APP_URL').$url; diff --git a/app/common/service/xlsx/WarehouseOrdeRentry.php b/app/common/service/xlsx/WarehouseOrdeRentry.php index 93c84ca36..d2b7c8703 100644 --- a/app/common/service/xlsx/WarehouseOrdeRentry.php +++ b/app/common/service/xlsx/WarehouseOrdeRentry.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; +use Webman\Exception\BusinessException; class WarehouseOrdeRentry { @@ -120,8 +121,15 @@ class WarehouseOrdeRentry // 保存文件到 public 下 $writer = new Xlsx($spreadsheet); + $dir=public_path().'/export/'.date('Y-m'); + if (!file_exists($dir)) { + // 尝试创建目录 + if (!mkdir($dir)) { + throw new BusinessException('创建目录失败:/export/' . date('Y-m')); + } + } + $file_path = $dir . '/' .$this->company.'公司入库单-'.date('Y-m-d H:i') . '.xlsx'; $url = '/export/' . date('Y-m') . '/' .$this->company.'公司入库单-'.date('Y-m-d H:i') . '.xlsx'; - $file_path = public_path() . $url; // 保存文件到 public 下 $writer->save($file_path); return getenv('APP_URL').$url; diff --git a/app/store/controller/beforehand_order/BeforehandOrderController.php b/app/store/controller/beforehand_order/BeforehandOrderController.php new file mode 100644 index 000000000..c903435fb --- /dev/null +++ b/app/store/controller/beforehand_order/BeforehandOrderController.php @@ -0,0 +1,310 @@ +request->get(); + $params['store_id'] = $this->request->adminInfo['store_id'] ?? 0; + $this->request->setGet($params); + return $this->dataLists(new BeforehandOrderLists()); + } + + public function warehousing_lists() + { + return $this->dataLists(new BeforehandOrderTwoLists()); + } + + public function outbound_lists() + { + return $this->dataLists(new BeforehandOrderThreeLists()); + } + + /** + * @notes 添加预订单表 + * @return \think\response\Json + * @author admin + * @date 2024/09/30 11:26 + */ + public function add() + { + $params = $this->request->post(); + $params['admin_id'] = $this->adminId; + $params['store_id'] = $this->request->adminInfo['store_id'] ?? 0; + $other_data = [ + 'nickname' => $params['nickname'] ?? '', + 'phone' => $params['phone'] ?? '', + 'address' => $params['address'] ?? '', + 'arrival_time' => $params['arrival_time'] ?? '', + 'purpose' => $params['purpose'] ?? '', + 'tables' => $params['tables'] ?? '', + 'days' => $params['days'] ?? '', + 'chef' => $params['chef'] ?? '', + 'chef_phone' => $params['chef_phone'] ?? '', + 'splitting_officer' => $params['splitting_officer'] ?? '', + 'merchandiser' => $params['merchandiser'] ?? '', + 'distribution_personnel' => $params['distribution_personnel'] ?? '', + 'transporter' => $params['transporter'] ?? '', + 'system_store_name' => $params['system_store_name'] ?? '', + 'regional_manager' => $params['regional_manager'] ?? '', + ]; + $params['other_data'] = $other_data; + $result = BeforehandOrderLogic::add($params); + return $this->success('添加成功', [], 1, 1); + } + + /** + * 生成支付订单 + */ + public function generateOrder() + { + $params = $this->request->post(); + $result = BeforehandOrderLogic::generateOrder($params); + return $this->success('生成成功', [], 1, 1); + } + + /** + * 一键出库 + */ + public function createOutboundOrder() + { + $params = $this->request->post(); + $params['admin_id'] = $this->adminId; + $result = BeforehandOrderLogic::createOutboundOrder($params); + + return $this->success('出库成功', [], 1, 1); + } + + /** + * 一键报损出库 + */ + public function createOutboundDamageOrder() + { + $params = $this->request->post(); + $params['admin_id'] = $this->adminId; + $result = BeforehandOrderLogic::createOutboundOrder($params); + + return $this->success('出库成功', [], 1, 1); + } + + /** + * @notes 订单转预定单 + * @return \think\response\Json + */ + public function orderTransferAdvanceOrder() + { + $params = $this->request->post(); + $params['admin_id'] = $this->adminId; + $params['mark'] = '订单转预定单'; + $result = BeforehandOrderLogic::orderTransferAdvanceOrder($params); + return $this->success('转单成功', [], 1, 1); + } + + /** + * @notes 编辑预订单表 + * @return \think\response\Json + * @author admin + * @date 2024/09/30 11:26 + */ + public function edit() + { + $params = $this->request->post(); + $result = BeforehandOrderLogic::edit($params); + if (true === $result) { + return $this->success('编辑成功', [], 1, 1); + } + return $this->fail(BeforehandOrderLogic::getError()); + } + + + /** + * @notes 删除预订单表 + * @return \think\response\Json + * @author admin + * @date 2024/09/30 11:26 + */ + public function delete() + { + $params = $this->request->post(); + BeforehandOrderLogic::delete($params); + return $this->success('删除成功', [], 1, 1); + } + + + /** + * @notes 获取预订单表详情 + * @return \think\response\Json + * @author admin + * @date 2024/09/30 11:26 + */ + public function detail() + { + $params = $this->request->get(); + $result = BeforehandOrderLogic::detail($params); + return $this->data($result); + } + + /** + * 导出标签 + */ + public function export() + { + $params = $this->request->post(); + $order = BeforehandOrder::where('id', $params['id'])->field('outbound_id,order_sn')->find(); + if (!$order) { + return $this->fail('未生成出库单'); + } + $warehouseOrder = WarehouseOrder::where('id', $order['outbound_id'])->field('store_id,delivery_time')->find(); + $system_store = SystemStore::where('id', $warehouseOrder['store_id'])->value('name'); + $data = WarehouseProduct::where('oid', $order['outbound_id'])->field('product_id,nums')->select() + ->each(function ($item) use ($system_store, $warehouseOrder, $order) { + $find = StoreProduct::where('id', $item['product_id'])->field('store_name,unit')->find(); + $unit_name = StoreProductUnit::where('id', $find['unit'])->value('name'); + $item['system_store'] = $system_store; + $item['subtitle'] = $item['oid'] . ' ' . convertStringToNumber($item['nums']) . '/' . $unit_name; + $item['store_name'] = $find['store_name']; + if ($warehouseOrder['oid']) { + $find = StoreOrder::where('order_id', $order['order_sn'])->field('real_name,user_address')->find(); + if ($find) { + $item['address'] = $find['real_name'] . ' ' . $find['user_address']; + } else { + $item['address'] = '无地址'; + } + } else { + $item['address'] = '无地址'; + } + }) + ->toArray(); + $file_path = (new Beforehand())->export($data, $system_store); + return $this->success('导出成功', ['url' => $file_path]); + } + + + /** + * 导出订单 + */ + public function export_order() + { + $params = $this->request->post(); + $order = BeforehandOrder::where('id', $params['id'])->find(); + + $cart_info = BeforehandOrderCartInfo::where('bhoid', $params['id'])->select(); + + $file_path = (new Beforehand())->order($order, $cart_info); + return $this->success('导出成功', ['url' => $file_path]); + } + + /** + * 导出清单 + */ + public function export_order_list() + { + $params = $this->request->post(); + $file_path = BeforehandOrderLogic::OrderList($params); + return $this->success('导出成功', ['url' => $file_path]); + } + + /** + * 导出分单 + */ + public function order_allocation() + { + $params = $this->request->post(); + $file_path = BeforehandOrderLogic::OrderAllocation($params); + return $this->success('导出成功', ['url' => $file_path]); + } + + /** + * 采购信息 + */ + public function order_info() + { + $params = $this->request->post(); + $file_path = BeforehandOrderLogic::OrderInfo($params); + return $this->success('导出成功', ['url' => $file_path]); + } + + /** + * 导出出库 + */ + public function order_outbound() + { + $params = $this->request->post(); + if (!empty($params['type']) && $params['type'] == 2) { + $file_path = BeforehandOrderLogic::OrderOutbound2($params); + } else { + $file_path = BeforehandOrderLogic::OrderOutbound($params); + } + return $this->success('导出成功', ['url' => $file_path]); + } + + /** + * 导出会员出库 + */ + public function order_outbound3() + { + $params = $this->request->post(); + $file_path = BeforehandOrderLogic::OrderOutbound3($params); + return $this->success('导出成功', ['url' => $file_path]); + } + + /** + * 导出财务出库 + */ + public function order_outbound_finance() + { + $params = $this->request->post(); + $file_path = BeforehandOrderLogic::OrderOutboundFinance($params); + return $this->success('导出成功', ['url' => $file_path]); + } + + /** + * 导出退库 + */ + public function stock_return() + { + $params = $this->request->post(); + $file_path = BeforehandOrderLogic::StockReturn($params); + return $this->success('导出成功', ['url' => $file_path]); + } + + /** + * 导出退供应商 + */ + public function return_supplier() + { + $params = $this->request->post(); + $file_path = BeforehandOrderLogic::ReturnSupplier($params); + return $this->success('导出成功', ['url' => $file_path]); + } +} diff --git a/app/store/controller/beforehand_order_cart_info/BeforehandOrderCartInfoController.php b/app/store/controller/beforehand_order_cart_info/BeforehandOrderCartInfoController.php new file mode 100644 index 000000000..dd94a3fa0 --- /dev/null +++ b/app/store/controller/beforehand_order_cart_info/BeforehandOrderCartInfoController.php @@ -0,0 +1,170 @@ +dataLists(new BeforehandOrderCartInfoLists()); + } + + + /** + * @notes 添加预订单购物详情表 + * @return \think\response\Json + * @author admin + * @date 2024/09/30 11:32 + */ + public function add() + { + $params = $this->request->post(); + $result = BeforehandOrderCartInfoLogic::add($params); + return $this->success('添加成功', [], 1, 1); + + } + /** + * @notes 添加预订单购物详情表 + * @return \think\response\Json + * @author admin + * @date 2024/09/30 11:32 + */ + public function append_add() + { + $params = $this->request->post(); + $result = BeforehandOrderCartInfoLogic::appendAdd($params); + return $this->success('追加成功', [], 1, 1); + + } + + /** + * @notes 编辑预订单购物详情无需采购 + */ + public function procurement_status(){ + $id=$this->request->post('id'); + + $res=BeforehandOrderCartInfo::where('id',$id)->find(); + $find=PurchaseProductOffer::where(['product_id'=>$res['product_id'],'order_id'=>$res['bhoid']])->find(); + if (empty($find)) { + $rawSql = "JSON_CONTAINS(source_order_info, '{\"source_order_id\": {$res['bhoid']}}')"; + $find = PurchaseProductOffer::where(['product_id' => $res['product_id']])->whereRaw($rawSql)->find(); + } + if($find){ + if($find['buyer_confirm']==1){ + return $this->fail('该商品已采购完成,无法更改状态'); + }else{ + if (!empty($find['source_order_info'])) { + $json = $find['source_order_info']; + foreach ($json as $key => $value) { + if ($value['source_order_id'] == $res['bhoid']) { + $find->need_num = $find->need_num - $value['need_num']; + unset($json[$key]); + break; + } + } + $find->source_order_info = array_values($json); + } + if ($find->need_num <= 0) { + $find->delete_time = time(); + } + $find->save(); + } + } + $res->save(['is_buyer'=>-1]); + if($res){ + return $this->success('操作成功',[],1,1); + }else{ + return $this->fail('操作失败'); + } + } + + + /** + * @notes 一键入库 + */ + public function one_click_storage(){ + $params=$this->request->post(); + $params['admin_id']=$this->adminId; + BeforehandOrderCartInfoLogic::oneClickStorage($params); + return $this->success('入库成功', [], 1, 1); + } + /** + * @notes 编辑预订单购物详情表 + * @return \think\response\Json + * @author admin + * @date 2024/09/30 11:32 + */ + public function edit() + { + $params = $this->request->post(); + $params['admin_id']=$this->adminId; + $result = BeforehandOrderCartInfoLogic::edit($params); + return $this->success('编辑成功', [], 1, 1); + + } + + + /** + * @notes 删除预订单购物详情表 + * @return \think\response\Json + * @author admin + * @date 2024/09/30 11:32 + */ + public function delete() + { + $params = $this->request->post(); + BeforehandOrderCartInfoLogic::delete($params); + return $this->success('删除成功', [], 1, 1); + } + + + /** + * @notes 获取预订单购物详情表详情 + * @return \think\response\Json + * @author admin + * @date 2024/09/30 11:32 + */ + public function detail() + { + $params = $this->request->get(); + $result = BeforehandOrderCartInfoLogic::detail($params); + return $this->data($result); + } + /** + * 修改数量 + */ + public function edit_nums() + { + $params = $this->request->post(); + $result = BeforehandOrderCartInfoLogic::editNums($params); + return $this->data($result); + } + + public function fix() + { + $params = $this->request->get(); + BeforehandOrderCartInfoLogic::fixAcceptNum($params); + return $this->data([]); + } + +} \ No newline at end of file diff --git a/app/store/controller/store_product/StoreProductController.php b/app/store/controller/store_product/StoreProductController.php index c873d0331..1372813d0 100644 --- a/app/store/controller/store_product/StoreProductController.php +++ b/app/store/controller/store_product/StoreProductController.php @@ -4,6 +4,7 @@ namespace app\store\controller\store_product; use app\admin\lists\store_branch_product\StoreBranchProductLists; +use app\admin\lists\store_product\StoreProductLists; use app\common\controller\Definitions; use app\store\controller\BaseAdminController; use app\store\logic\store_branch_product\StoreBranchProductLogic; @@ -139,4 +140,13 @@ class StoreProductController extends BaseAdminController { return $this->fail('门店禁止手动增减商品库存'); } + + /** + * 商品列表 + */ + public function adminLists() + { + return $this->dataLists(new StoreProductLists()); + } + } diff --git a/composer.json b/composer.json index 4bb6cc7ca..f84055827 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,8 @@ "intervention/image": "^3.6", "picqer/php-barcode-generator": "^2.4", "overtrue/easy-sms": "^2.6", - "phpoffice/phpword": "^1.3" + "phpoffice/phpword": "^1.3", + "chance-fyi/operation-log": "^3.0" }, "suggest": { "ext-event": "For better performance. " diff --git a/composer.lock b/composer.lock index e5a87f041..718c53a83 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e624460001f6646c62934c275bd79785", + "content-hash": "005401e7f7b2cce665fa8d6a9a1cb808", "packages": [ { "name": "aliyuncs/oss-sdk-php", @@ -132,6 +132,67 @@ ], "time": "2024-02-09T16:56:22+00:00" }, + { + "name": "chance-fyi/operation-log", + "version": "v3.0.7", + "source": { + "type": "git", + "url": "https://github.com/Chance-fyi/operation-log.git", + "reference": "bfb73bc1c3dddf91772de4f37b42a41c519c67e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Chance-fyi/operation-log/zipball/bfb73bc1c3dddf91772de4f37b42a41c519c67e5", + "reference": "bfb73bc1c3dddf91772de4f37b42a41c519c67e5", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^8.0" + }, + "require-dev": { + "fakerphp/faker": "^1.21@dev", + "friendsofphp/php-cs-fixer": "dev-master", + "hyperf/config": "^3.0@dev", + "hyperf/database": "^3.0@dev", + "hyperf/di": "^3.0@dev", + "hyperf/pimple": "^2.1", + "illuminate/database": "^8.0", + "phpstan/phpstan": "1.11.x-dev", + "phpunit/phpunit": "9.6.x-dev", + "topthink/think-orm": "2.0.x-dev" + }, + "bin": [ + "bin/chance-fyi-operation-log" + ], + "type": "library", + "extra": { + "hyperf": { + "config": "Chance\\Log\\orm\\hyperf\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Chance\\Log\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "chance", + "email": "ctx_ya@qq.com" + } + ], + "description": "Elegant logging of operations", + "support": { + "issues": "https://github.com/Chance-fyi/operation-log/issues", + "source": "https://github.com/Chance-fyi/operation-log/tree/v3.0.7" + }, + "time": "2023-12-22T08:06:25+00:00" + }, { "name": "doctrine/annotations", "version": "1.14.3", diff --git a/config/thinkorm.php b/config/thinkorm.php index 87668ed7b..6aee1e11a 100644 --- a/config/thinkorm.php +++ b/config/thinkorm.php @@ -25,7 +25,17 @@ return [ // 关闭SQL监听日志 'trigger_sql' => false, // 自定义分页类 - 'bootstrap' => '' + 'bootstrap' => '', + // 数据库类型 + 'type' => \Chance\Log\orm\think\MySqlConnection::class, + // 指定查询对象 + "query" => \Chance\Log\orm\think\Query::class, + // Builder类 + "builder" => \think\db\builder\Mysql::class, + // 模型所在的命名空间 + "modelNamespace" => "common\model", + // 日志记录的主键 + "logKey" => "id", ], 'demo' => [ // 数据库类型 diff --git a/process/Task.php b/process/Task.php index 198660c65..aa10565b3 100644 --- a/process/Task.php +++ b/process/Task.php @@ -4,11 +4,14 @@ namespace process; use app\common\enum\OrderEnum; use app\common\logic\PayNotifyLogic; +use app\common\model\dict\DictData; use app\common\model\store_branch_product\StoreBranchProduct; use app\common\model\store_order\StoreOrder; use app\common\model\store_order_cart_info\StoreOrderCartInfo; use app\common\model\store_product\StoreProduct; +use app\common\model\store_product_price\StoreProductPrice; use app\common\model\user_recharge\UserRecharge; +use support\Cache; use think\facade\Db; use Webman\RedisQueue\Redis; use Workerman\Crontab\Crontab; @@ -62,5 +65,41 @@ class Task } } }); + + $this->updateProductPrice(); } + + /** + * 将商品价格更改列表的价格同步至商品 + * @return void + */ + public function updateProductPrice() + { + new Crontab('0 */10 * * * *', function () { + $value = DictData::getDictValue('update_product_price_task', 'status'); + if ($value == 1) { + $lastProductId = Cache::get('update_product_price_last_product_id', 0); + $productIds = StoreProduct::where('purchase', 0)->where('id', '>', $lastProductId)->limit(200)->column('id'); + $lastProductId = end($productIds); + if (count($productIds) < 200) { + $lastProductId = 0; + } + Cache::set('update_product_price_last_product_id', $lastProductId); + $productPrices = StoreProductPrice::whereIn('product_id', $productIds)->where('status', 1)->distinct('product_id')->order('id desc')->select()->toArray(); + $update = []; + foreach ($productPrices as $productPrice) { + $update[] = [ + 'id' => $productPrice['product_id'], + 'purchase' => $productPrice['purchase'] ?? 0, + 'cost' => $productPrice['cost'] ?? 0, + 'price' => $productPrice['purchase'] ?? 0, + ]; + } + if (count($update) > 0) { + (new StoreProduct())->saveAll($update); + } + } + }); + } + } diff --git a/vendor/bin/chance-fyi-operation-log b/vendor/bin/chance-fyi-operation-log new file mode 100644 index 000000000..b79510a45 --- /dev/null +++ b/vendor/bin/chance-fyi-operation-log @@ -0,0 +1,119 @@ +#!/usr/bin/env php +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/chance-fyi/operation-log/bin/chance-fyi-operation-log'); + } +} + +return include __DIR__ . '/..'.'/chance-fyi/operation-log/bin/chance-fyi-operation-log'; diff --git a/vendor/chance-fyi/operation-log/.github/workflows/test.yml b/vendor/chance-fyi/operation-log/.github/workflows/test.yml new file mode 100644 index 000000000..7817a447d --- /dev/null +++ b/vendor/chance-fyi/operation-log/.github/workflows/test.yml @@ -0,0 +1,57 @@ +name: PHPUnit + +on: + push: + pull_request: + schedule: + - cron: '0 0 * * *' + +jobs: + test: + runs-on: ubuntu-22.04 + + strategy: + fail-fast: false + matrix: + php: [ 8.0, 8.1, 8.2 ] + swoole: [ '', swoole ] + + name: PHP ${{ matrix.php }} ${{ matrix.swoole }} + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Start MySQL + run: docker compose up -d mysql mysql1 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: pdo, pdo_mysql, ${{ matrix.swoole }} + ini-values: error_reporting=E_ALL + tools: composer:v2 + coverage: none + + - name: Composer install + run: composer install + + - name: Static analysis + run: composer analyse + + - name: Wait for MySQL + run: | + while ! docker compose exec mysql mysql --user=root --password=root -e "SELECT 1" >/dev/null 2>&1 || ! docker compose exec mysql1 mysql --user=root --password=root -e "SELECT 1" >/dev/null 2>&1; do + sleep 1 + done + + - name: Run tests + env: + MYSQL_HOST: 127.0.0.1 + MYSQL_PORT: 33060 + MYSQL1_PORT: 33061 + run: composer test + + - name: Close MySQL + run: docker compose down \ No newline at end of file diff --git a/vendor/chance-fyi/operation-log/.php-cs-fixer.php b/vendor/chance-fyi/operation-log/.php-cs-fixer.php new file mode 100644 index 000000000..aa0c2624b --- /dev/null +++ b/vendor/chance-fyi/operation-log/.php-cs-fixer.php @@ -0,0 +1,28 @@ +setFinder( + Finder::create() + ->in(__DIR__) + ->exclude('vendor') + ) + ->setRules([ + '@Symfony' => true, + '@PhpCsFixer' => true, + '@DoctrineAnnotation' => true, + 'list_syntax' => [ + 'syntax' => 'short' + ], + 'concat_space' => [ + 'spacing' => 'one' + ], + 'global_namespace_import' => [ + 'import_classes' => true, + 'import_constants' => true, + 'import_functions' => true, + ], + ]) + ->setUsingCache(false); \ No newline at end of file diff --git a/vendor/chance-fyi/operation-log/LICENSE b/vendor/chance-fyi/operation-log/LICENSE new file mode 100644 index 000000000..25f66dcd6 --- /dev/null +++ b/vendor/chance-fyi/operation-log/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Chance + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/chance-fyi/operation-log/README.md b/vendor/chance-fyi/operation-log/README.md new file mode 100644 index 000000000..4779ac716 --- /dev/null +++ b/vendor/chance-fyi/operation-log/README.md @@ -0,0 +1,247 @@ +支持 Laravel 的 ORM 、Hyperf 的 ORM 与 ThinkPHP 的 ORM 。可以生成增、删、改,包括批量增、删、改,以及 使用 DB 操作的日志。 + +通过~~模型事件~~与获取器,自动生成可读性高的操作日志。2.0 版本已弃用模型事件,因为批量操作没有触发模型事件,使用模型事件无法覆盖所有模型对数据库的操作以及 DB 操作。 + +### 安装 + +> composer require chance-fyi/operation-log + +### 注意 + +> 因为使用了单例,所以在常驻内存的框架中使用一定要在每次请求结束之后将生成的日志清空。 + +### 使用 Laravel 的 ORM + +首先在数据库的配置文件 `config/database.php` 中增加两个配置项 `modelNamespace` 和 `logKey`。 + +```php + env('DB_CONNECTION', 'mysql'), + ... + 'connections' => [ + 'mysql' => [ + 'driver' => 'mysql', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + ... + ... + // 模型所在的命名空间 + "modelNamespace" => "Chance\Log\Test\model", + // 日志记录的主键 + "logKey" => "id", + ], + ... + ] + ... +]; +``` + +然后注册 MySQL 数据库连接的解析器。 + +```php +\Illuminate\Database\Connection::resolverFor('mysql', function ($connection, $database, $prefix, $config) { + return new \Chance\Log\orm\illuminate\MySqlConnection($connection, $database, $prefix, $config); +}); +``` + +### 使用 ThinkPHP 的 ORM + +在数据库的配置文件 config/database.php 中增加三个配置项 `query`、`modelNamespace` 和 `logKey`,并修改 `type` 与 `builder`。 + +```php + env('database.driver', 'mysql'), + ... + 'connections' => [ + 'mysql' => [ + // 服务器地址 + 'hostname' => env('database.hostname', '127.0.0.1'), + // 数据库名 + 'database' => env('database.database', ''), + // 用户名 + 'username' => env('database.username', 'root'), + // 密码 + 'password' => env('database.password', ''), + // 端口 + 'hostport' => env('database.hostport', '3306'), + ... + ... + // 数据库类型 + 'type' => \Chance\Log\orm\think\MySqlConnection::class, + // 指定查询对象 + "query" => \Chance\Log\orm\think\Query::class, + // Builder类 + "builder" => \think\db\builder\Mysql::class, + // 模型所在的命名空间 + "modelNamespace" => "Chance\Log\Test\model", + // 日志记录的主键 + "logKey" => "id", + ], + // 更多的数据库配置信息 + ... + ], + ... +]; +``` + +### 日志主键 + +可在模型中设置`$logKey`属性修改需要记录的主键名称。 + +```php + '姓名', + 'sex' => '性别', + ]; +} +``` + +**获取器** + +设置一个名为`字段名_text`的获取器。 + +```php +sex)] ?? '未知'; + } + + // ThinkPHP ORM 获取器设置方法 + public function getSexTextAttr($key): string + { + return ['女','男'][($key ?? $this->sex)] ?? '未知'; + } +} +``` + +### 日志生成忽略的字段 + +可在模型中通过 `$ignoreLogFields` 设置该表不希望生成日志的字段。 + +```php + [ + "table1" => "app\\model\\Table1", + "table2" => "app\\model\\Table2", + ], + "database2" => [], +]); +``` + +### 获取日志信息 + +```php +\Chance\Log\facades\OperationLog::getLog(); +``` + +### 清除日志信息 + +```php +\Chance\Log\facades\OperationLog::clearLog(); +``` + +### 启用禁用 + +```php +# 启用 (默认) +\Chance\Log\facades\OperationLog::enable(); +# 禁用 +\Chance\Log\facades\OperationLog::disable(); +``` + +### 效果图 + +![image](https://user-images.githubusercontent.com/37658940/215932487-9c923053-1bdb-4198-a13e-3ca7d668d65c.png) + +![image](https://user-images.githubusercontent.com/37658940/215932628-ee02d2d4-b1a0-4fac-a53c-2eda2858c9bc.png) + +![image](https://user-images.githubusercontent.com/37658940/215932685-64cf39f3-6ac1-44c1-af29-abc7c078228c.png) + +![image](https://user-images.githubusercontent.com/37658940/215932722-99d7ad4b-01d6-4ddc-b47d-9d213c16022e.png) + +![image](https://user-images.githubusercontent.com/37658940/215932756-b8a88945-1732-4272-a843-eaf20aea528e.png) + +![image](https://user-images.githubusercontent.com/37658940/215932790-b93f54af-7a3e-4098-8765-8821d5d4fcb1.png) diff --git a/vendor/chance-fyi/operation-log/bin/chance-fyi-operation-log b/vendor/chance-fyi/operation-log/bin/chance-fyi-operation-log new file mode 100644 index 000000000..ac21ca4c3 --- /dev/null +++ b/vendor/chance-fyi/operation-log/bin/chance-fyi-operation-log @@ -0,0 +1,117 @@ +#!/usr/bin/env php +initialize(); +}// Laravel +elseif (class_exists(Illuminate\Foundation\Application::class)) { + $app = new Illuminate\Foundation\Application($dir . "/../"); + $app->singleton( + Illuminate\Contracts\Http\Kernel::class, + App\Http\Kernel::class + ); + $app->singleton( + Illuminate\Contracts\Debug\ExceptionHandler::class, + App\Exceptions\Handler::class + ); + $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); + $kernel->bootstrap(); +}// webman +elseif (class_exists(support\App::class)) { + support\App::loadAllConfig(); + support\bootstrap\LaravelDb::start(null); +} + +array_shift($argv); +$map = []; +foreach ($argv as $directory) { + if (!is_dir($directory)) { + echo "$directory is not a directory" . PHP_EOL; + exit(1); + } + $files = new RegexIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory)), "/\.php$/"); + foreach ($files as $file) { + $class = getClassNamespaceFromFile($file); + if (!class_exists($class)) { + continue; + } + $reflect = new ReflectionClass($class); + if ( + !preg_match("/\\\\model(s)?\\\\/i", $class) + ) { + continue; + } + + $object = $reflect->newInstanceArgs(); + if (class_exists(think\Model::class) && $object instanceof think\Model) { + $map[$object->getConfig("database")][$object->getTable()] = $class; + continue; + } + if (class_exists(Illuminate\Database\Eloquent\Model::class) && $object instanceof Illuminate\Database\Eloquent\Model) { + $map[$object->getConnection()->getDatabaseName()][$object->getConnection()->getTablePrefix() . $object->getTable()] = $class; + } + } +} + +$data = <<getRealPath()); + $tokens = token_get_all($content); + $namespace = ""; + $class = ""; + $count = count($tokens); + $i = 0; + while ($i < $count) { + $token = $tokens[$i]; + if (is_array($token) && $token[0] == T_NAMESPACE) { + while (++$i < $count) { + if ($tokens[$i] === ';') { + $namespace = trim($namespace); + break; + } + $namespace .= is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i]; + } + } + if ( + is_array($token) + && $i >= 2 + && $tokens[$i - 2][0] == T_CLASS + && $tokens[$i - 1][0] == T_WHITESPACE + && $token[0] == T_STRING + ) { + $class = trim($tokens[$i][1]); + break; + } + + $i++; + } + + return $namespace . "\\" . $class; +} \ No newline at end of file diff --git a/vendor/chance-fyi/operation-log/cache/table-model-mapping.php b/vendor/chance-fyi/operation-log/cache/table-model-mapping.php new file mode 100644 index 000000000..172633294 --- /dev/null +++ b/vendor/chance-fyi/operation-log/cache/table-model-mapping.php @@ -0,0 +1,9 @@ + [ + 'table1' => 'app\\model\\Table1', + 'table2' => 'app\\model\\Table2', + ], + 'database2' => [], +]; diff --git a/vendor/chance-fyi/operation-log/composer.json b/vendor/chance-fyi/operation-log/composer.json new file mode 100644 index 000000000..3cef1abd6 --- /dev/null +++ b/vendor/chance-fyi/operation-log/composer.json @@ -0,0 +1,52 @@ +{ + "name": "chance-fyi/operation-log", + "description": "Elegant logging of operations", + "type": "library", + "license": "MIT", + "autoload": { + "psr-4": { + "Chance\\Log\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Chance\\Log\\Test\\": "tests/" + } + }, + "authors": [ + { + "name": "chance", + "email": "ctx_ya@qq.com" + } + ], + "minimum-stability": "dev", + "require": { + "php": "^8.0", + "ext-json": "*" + }, + "require-dev": { + "illuminate/database": "^8.0", + "topthink/think-orm": "2.0.x-dev", + "fakerphp/faker": "^1.21@dev", + "friendsofphp/php-cs-fixer": "dev-master", + "phpunit/phpunit": "9.6.x-dev", + "phpstan/phpstan": "1.11.x-dev", + "hyperf/database": "^3.0@dev", + "hyperf/di": "^3.0@dev", + "hyperf/pimple": "^2.1", + "hyperf/config": "^3.0@dev" + }, + "scripts": { + "test": "phpunit", + "cs-fix": "php-cs-fixer fix $1", + "analyse": "phpstan analyse --memory-limit=-1" + }, + "bin": [ + "bin/chance-fyi-operation-log" + ], + "extra": { + "hyperf": { + "config": "Chance\\Log\\orm\\hyperf\\ConfigProvider" + } + } +} diff --git a/vendor/chance-fyi/operation-log/src/Facade.php b/vendor/chance-fyi/operation-log/src/Facade.php new file mode 100644 index 000000000..70624bb04 --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/Facade.php @@ -0,0 +1,36 @@ +setTableModelMapping(OperationLogFacade::getTableModelMapping()); + } + Facade::setResolvedInstance(self::class, $this); + } + + public function getLog(): string + { + $log = $this->getRawLog(); + $this->clearLog(); + + return trim(implode('', $log), PHP_EOL); + } + + public function clearLog(): void + { + $this->setRawLog(['']); + } + + public function beginTransaction(): void + { + $log = $this->getRawLog(); + $log[] = ''; + $this->setRawLog($log); + } + + public function rollBackTransaction(int $toLevel): void + { + $this->setRawLog(array_slice($this->getRawLog(), 0, $toLevel)); + if (0 === count($this->getRawLog())) { + $this->clearLog(); + } + } + + /** + * Get table comment. + */ + public function getTableComment(ThinkModel|LaravelModel|HyperfModel $model): string + { + $table = $this->getTableName($model); + if (isset($model->tableComment)) { + return $model->tableComment ?: $table; + } + + $databaseName = $this->getDatabaseName($model); + $comment = ''; + + if (empty($this->tableComment[$databaseName])) { + $this->tableComment[$databaseName] = $this->executeSQL($model, "SELECT TABLE_NAME, TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = '{$databaseName}'"); + } + + foreach ($this->tableComment[$databaseName] as $item) { + if (is_array($item) && $item['TABLE_NAME'] == $table) { + $comment = $item['TABLE_COMMENT']; + + break; + } + if (is_object($item) && $item->TABLE_NAME == $table) { + $comment = $item->TABLE_COMMENT; + + break; + } + } + + return (string) ($comment ?: $table); + } + + /** + * Get field comment. + */ + public function getColumnComment(ThinkModel|LaravelModel|HyperfModel $model, string $field): string + { + if (isset($model->columnComment)) { + return $model->columnComment[$field] ?? $field; + } + + $databaseName = $this->getDatabaseName($model); + $table = $this->getTableName($model); + $comment = ''; + + if (empty($this->columnComment[$databaseName])) { + $this->columnComment[$databaseName] = $this->executeSQL($model, "SELECT TABLE_NAME,COLUMN_NAME,COLUMN_COMMENT FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '{$databaseName}'"); + } + foreach ($this->columnComment[$databaseName] as $item) { + if (is_array($item) && $item['TABLE_NAME'] == $table && $item['COLUMN_NAME'] == $field) { + $comment = $item['COLUMN_COMMENT']; + + break; + } + if (is_object($item) && $item->TABLE_NAME == $table && $item->COLUMN_NAME == $field) { + $comment = $item->COLUMN_COMMENT; + + break; + } + } + + return (string) ($comment ?: $field); + } + + public function generateLog(ThinkModel|LaravelModel|HyperfModel $model, string $type): void + { + if ($model->doNotRecordLog ?? false) { + return; + } + $logKey = $model->logKey ?? $this->getPk($model); + $typeText = [ + self::CREATED => '创建', + self::BATCH_CREATED => '批量创建', + self::UPDATED => '修改', + self::BATCH_UPDATED => '批量修改', + self::DELETED => '删除', + self::BATCH_DELETED => '批量删除', + ][$type]; + $logHeader = "{$typeText} {$this->getTableComment($model)}" . + (in_array($type, [self::CREATED, self::UPDATED, self::BATCH_UPDATED, self::DELETED, self::BATCH_DELETED]) ? " ({$this->getColumnComment($model, $logKey)}:{$model->{$logKey}}):" : ':'); + $log = ''; + + switch ($type) { + case self::CREATED: + case self::BATCH_CREATED: + case self::DELETED: + case self::BATCH_DELETED: + foreach ($this->getAttributes($model) as $key => $value) { + if ($logKey === $key + || (isset($model->ignoreLogFields) && is_array($model->ignoreLogFields) && in_array($key, $model->ignoreLogFields))) { + continue; + } + $log .= "{$this->getColumnComment($model, $key)}:{$this->getValue($model, $key)},"; + } + + break; + + case self::UPDATED: + case self::BATCH_UPDATED: + foreach ($this->getChangedAttributes($model) as $key => $value) { + $keys = explode('.', $key); + $key = end($keys); + if ($logKey === $key + || (isset($model->ignoreLogFields) && is_array($model->ignoreLogFields) && in_array($key, $model->ignoreLogFields))) { + continue; + } + $log .= "{$this->getColumnComment($model, $key)}由:{$this->getOldValue($model, $key)} 改为:{$this->getValue($model, $key)},"; + } + + break; + } + if (!empty($log)) { + $log = mb_substr($log, 0, mb_strlen($log, 'utf8') - 1, 'utf8'); + $logs = $this->getRawLog(); + array_splice($logs, -1, 1, end($logs) . $logHeader . $log . PHP_EOL); + $this->setRawLog($logs); + } + } + + public function setTableModelMapping(array $map): void + { + $this->tableModelMapping = $map; + } + + public function getTableModelMapping(): array + { + return $this->tableModelMapping; + } + + public function status(): bool + { + if (extension_loaded('swoole') && class_exists(HyperfContext::class)) { + return HyperfContext::get(self::CONTEXT_STATUS, true); + } + + if (class_exists(WebmanContext::class)) { + return WebmanContext::get(self::CONTEXT_STATUS) ?? true; + } + + return $this->status; + } + + public function enable(): void + { + if (extension_loaded('swoole') && class_exists(HyperfContext::class)) { + HyperfContext::set(self::CONTEXT_STATUS, true); + + return; + } + + if (class_exists(WebmanContext::class)) { + WebmanContext::set(self::CONTEXT_STATUS, true); + + return; + } + + $this->status = true; + } + + public function disable(): void + { + if (extension_loaded('swoole') && class_exists(HyperfContext::class)) { + HyperfContext::set(self::CONTEXT_STATUS, false); + + return; + } + + if (class_exists(WebmanContext::class)) { + WebmanContext::set(self::CONTEXT_STATUS, false); + + return; + } + + $this->status = false; + } + + private function getRawLog() + { + if (extension_loaded('swoole') && class_exists(HyperfContext::class)) { + return HyperfContext::get(self::CONTEXT_LOG, ['']); + } + + if (class_exists(WebmanContext::class)) { + return WebmanContext::get(self::CONTEXT_LOG) ?? ['']; + } + + return $this->log; + } + + private function setRawLog(array $log): void + { + if (extension_loaded('swoole') && class_exists(HyperfContext::class)) { + HyperfContext::set(self::CONTEXT_LOG, $log); + + return; + } + + if (class_exists(WebmanContext::class)) { + WebmanContext::set(self::CONTEXT_LOG, $log); + + return; + } + + $this->log = $log; + } +} diff --git a/vendor/chance-fyi/operation-log/src/OperationLogInterface.php b/vendor/chance-fyi/operation-log/src/OperationLogInterface.php new file mode 100644 index 000000000..7a23bc846 --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/OperationLogInterface.php @@ -0,0 +1,60 @@ +insertLog($values); + + return $result; + } + + public function insertGetId(array $values, $sequence = null): int + { + $id = parent::insertGetId($values, $sequence); + + $this->insertLog($values); + + return $id; + } + + public function insertOrIgnore(array $values): int + { + $result = parent::insertOrIgnore($values); + + $this->insertLog($values); + + return $result; + } + + public function update(array $values): int + { + if (HyperfOrmLog::status()) { + $oldData = $this->get()->toArray(); + if (!empty($oldData)) { + $model = $this->generateModel(); + if (count($oldData) > 1) { + HyperfOrmLog::batchUpdated($model, $oldData, $values); + } else { + HyperfOrmLog::updated($model, (array) $oldData[0], $values); + } + } + } + + return parent::update($values); + } + + public function delete($id = null): int + { + $this->deleteLog($id); + + return parent::delete($id); + } + + public function truncate(): void + { + $this->deleteLog(); + parent::truncate(); + } + + /** + * Generate model object. + */ + private function generateModel(): Model + { + $name = $this->from; + + /** @var Connection $connection */ + $connection = $this->getConnection(); + $database = $connection->getDatabaseName(); + $table = $connection->getTablePrefix() . $name; + + $mapping = [ + OperationLog::getTableModelMapping(), + include __DIR__ . '/../../../cache/table-model-mapping.php', + ]; + foreach ($mapping as $map) { + if (is_array($map) && isset($map[$database][$table]) && class_exists($map[$database][$table])) { + return new $map[$database][$table](); + } + } + + $modelNamespace = $connection->getConfig('modelNamespace') ?: 'app\\model'; + $className = trim($modelNamespace, '\\') . '\\' . Str::studly($name); + if (class_exists($className)) { + $model = new $className(); + } else { + $model = new DbModel(); + $model->setQueryObj($connection); + $model->setTable($name); + $model->logKey = $connection->getConfig('logKey') ?: $model->getKeyName(); + } + + return $model; + } + + private function insertLog(array $values): void + { + if (HyperfOrmLog::status()) { + $model = $this->generateModel(); + if (is_array(reset($values))) { + HyperfOrmLog::batchCreated($model, $values); + } else { + /** @var Connection $connection */ + $connection = $this->getConnection(); + $id = $connection->getPdo()->lastInsertId(); + $pk = $model->getKeyName(); + $values[$pk] = $id; + HyperfOrmLog::created($model, $values); + } + } + } + + private function deleteLog($id = null): void + { + if (HyperfOrmLog::status()) { + if (!empty($id)) { + $data = [(array) $this->find($id)]; + } else { + $data = $this->get()->toArray(); + } + + if (!empty($data)) { + $model = $this->generateModel(); + if (count($data) > 1) { + HyperfOrmLog::batchDeleted($model, $data); + } else { + HyperfOrmLog::deleted($model, (array) $data[0]); + } + } + } + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/hyperf/ConfigProvider.php b/vendor/chance-fyi/operation-log/src/orm/hyperf/ConfigProvider.php new file mode 100644 index 000000000..039e04db6 --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/hyperf/ConfigProvider.php @@ -0,0 +1,33 @@ + [ + 'scan' => [ + 'paths' => [ + __DIR__, + ], + ], + ], + 'aspects' => [ + NewBaseQueryBuilderAspect::class, + ], + ]; + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/hyperf/DbModel.php b/vendor/chance-fyi/operation-log/src/orm/hyperf/DbModel.php new file mode 100644 index 000000000..c4a63a343 --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/hyperf/DbModel.php @@ -0,0 +1,28 @@ +query = $query; + } + + public function getQueryObj(): Query + { + return $this->query; + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/hyperf/Log.php b/vendor/chance-fyi/operation-log/src/orm/hyperf/Log.php new file mode 100644 index 000000000..6c25b2c4b --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/hyperf/Log.php @@ -0,0 +1,175 @@ +getKeyName(); + } + + /** + * @param Model $model + */ + public function getTableName($model): string + { + return $model->getConnection()->getTablePrefix() . $model->getTable(); + } + + /** + * @param Model $model + */ + public function getDatabaseName($model): string + { + if (method_exists($model, 'getQueryObj')) { + return $model->getQueryObj()->getDatabaseName(); + } + + return $model->getConnection()->getDatabaseName(); + } + + /** + * @param Model $model + */ + public function executeSQL($model, string $sql): array + { + if (method_exists($model, 'getQueryObj')) { + return $model->getQueryObj()->select($sql); + } + + return $model->getConnection()->select($sql); + } + + /** + * @param Model $model + */ + public function getAttributes($model): array + { + return $model->getAttributes(); + } + + /** + * @param Model $model + */ + public function getChangedAttributes($model): array + { + return $model->getChanges(); + } + + /** + * @param Model $model + */ + public function getValue($model, string $key): string + { + $keyText = $key . '_text'; + $value = $model->{$keyText} ?? $model->{$key}; + + if (is_array($value)) { + return json_encode($value, JSON_UNESCAPED_UNICODE); + } + + return (string) $value; + } + + /** + * @param Model $model + */ + public function getOldValue($model, string $key): string + { + if (str_contains($key, '->')) { + [$key, $jsonKey] = explode('->', $key, 2); + } + + $keyText = $key . '_text'; + $attributeFun = 'get' . Str::studly(Str::lower($keyText)) . 'Attribute'; + $value = (string) (method_exists($model, $attributeFun) ? $model->{$attributeFun}($model->getOriginal($key)) : $model->getOriginal($key)); + + $val = json_decode($value, true); + if (!isset($jsonKey) || is_null($val) || !is_array($val)) { + return $value; + } + + foreach (explode('->', $jsonKey) as $k) { + $val = $val[$k]; + } + + return (string) $val; + } + + /** + * @param Model $model + */ + public function created($model, array $data): void + { + $model->setRawAttributes($data); + $this->generateLog($model, self::CREATED); + } + + /** + * @param Model $model + */ + public function updated($model, array $oldData, array $data): void + { + $model->setRawAttributes($oldData, true); + $model->setRawAttributes(array_merge($oldData, $data)); + $model->syncChanges(); + $this->generateLog($model, self::UPDATED); + } + + /** + * @param Model $model + */ + public function deleted($model, array $data): void + { + $model->setRawAttributes($data); + $this->generateLog($model, self::DELETED); + } + + /** + * @param Model $model + */ + public function batchCreated($model, array $data): void + { + foreach ($data as $item) { + $model->setRawAttributes($item); + $this->generateLog($model, self::BATCH_CREATED); + } + } + + /** + * @param Model $model + */ + public function batchUpdated($model, array $oldData, array $data): void + { + foreach ($oldData as $item) { + $model->setRawAttributes((array) $item, true); + $model->setRawAttributes(array_merge((array) $item, $data)); + $model->syncChanges(); + $this->generateLog($model, self::BATCH_UPDATED); + } + } + + /** + * @param Model $model + */ + public function batchDeleted($model, array $data): void + { + foreach ($data as $item) { + $model->setRawAttributes((array) $item); + $this->generateLog($model, self::BATCH_DELETED); + } + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/hyperf/MySqlConnection.php b/vendor/chance-fyi/operation-log/src/orm/hyperf/MySqlConnection.php new file mode 100644 index 000000000..2baa2b87f --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/hyperf/MySqlConnection.php @@ -0,0 +1,33 @@ +getQueryGrammar(), + $this->getPostProcessor() + ); + } + + public function beginTransaction(): void + { + HyperfOrmLog::beginTransaction(); + parent::beginTransaction(); + } + + public function rollBack($toLevel = null): void + { + HyperfOrmLog::rollBackTransaction(is_null($toLevel) ? $this->transactions : $toLevel); + parent::rollBack($toLevel); + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/hyperf/aspect/NewBaseQueryBuilderAspect.php b/vendor/chance-fyi/operation-log/src/orm/hyperf/aspect/NewBaseQueryBuilderAspect.php new file mode 100644 index 000000000..97852ab7e --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/hyperf/aspect/NewBaseQueryBuilderAspect.php @@ -0,0 +1,29 @@ +process(); + + return new Builder($query->getConnection(), $query->getGrammar(), $query->getProcessor()); + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/illuminate/Builder.php b/vendor/chance-fyi/operation-log/src/orm/illuminate/Builder.php new file mode 100644 index 000000000..6a5a3a4ea --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/illuminate/Builder.php @@ -0,0 +1,146 @@ +insertLog($values); + + return $result; + } + + public function insertGetId(array $values, $sequence = null): int + { + $id = parent::insertGetId($values, $sequence); + + $this->insertLog($values); + + return $id; + } + + public function insertOrIgnore(array $values): int + { + $result = parent::insertOrIgnore($values); + + $this->insertLog($values); + + return $result; + } + + public function update(array $values): int + { + if (IlluminateOrmLog::status()) { + $oldData = $this->get()->toArray(); + if (!empty($oldData)) { + $model = $this->generateModel(); + if (count($oldData) > 1) { + IlluminateOrmLog::batchUpdated($model, $oldData, $values); + } else { + IlluminateOrmLog::updated($model, (array) $oldData[0], $values); + } + } + } + + return parent::update($values); + } + + public function delete($id = null): int + { + $this->deleteLog($id); + + return parent::delete($id); + } + + public function truncate(): void + { + $this->deleteLog(); + parent::truncate(); + } + + /** + * Generate model object. + */ + private function generateModel(): Model + { + $name = $this->from; + + /** @var Connection $connection */ + $connection = $this->getConnection(); + $database = $connection->getDatabaseName(); + $table = $connection->getTablePrefix() . $name; + + $mapping = [ + OperationLog::getTableModelMapping(), + include __DIR__ . '/../../../cache/table-model-mapping.php', + ]; + foreach ($mapping as $map) { + if (is_array($map) && isset($map[$database][$table]) && class_exists($map[$database][$table])) { + return new $map[$database][$table](); + } + } + + $modelNamespace = $connection->getConfig('modelNamespace') ?: 'app\\model'; + $className = trim($modelNamespace, '\\') . '\\' . Str::studly($name); + if (class_exists($className)) { + $model = new $className(); + } else { + $model = new DbModel(); + $model->setQuery($connection); + $model->setTable($name); + $model->logKey = $connection->getConfig('logKey') ?: $model->getKeyName(); + } + + return $model; + } + + private function insertLog(array $values): void + { + if (IlluminateOrmLog::status()) { + $model = $this->generateModel(); + if (is_array(reset($values))) { + IlluminateOrmLog::batchCreated($model, $values); + } else { + /** @var Connection $connection */ + $connection = $this->getConnection(); + $id = $connection->getPdo()->lastInsertId(); + $pk = $model->getKeyName(); + $values[$pk] = $id; + IlluminateOrmLog::created($model, $values); + } + } + } + + private function deleteLog($id = null): void + { + if (IlluminateOrmLog::status()) { + if (!empty($id)) { + $data = [(array) $this->find($id)]; + } else { + $data = $this->get()->toArray(); + } + + if (!empty($data)) { + $model = $this->generateModel(); + if (count($data) > 1) { + IlluminateOrmLog::batchDeleted($model, $data); + } else { + IlluminateOrmLog::deleted($model, (array) $data[0]); + } + } + } + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/illuminate/DbModel.php b/vendor/chance-fyi/operation-log/src/orm/illuminate/DbModel.php new file mode 100644 index 000000000..4bfa0ac74 --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/illuminate/DbModel.php @@ -0,0 +1,28 @@ +query = $query; + } + + public function getQuery(): Query + { + return $this->query; + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/illuminate/Log.php b/vendor/chance-fyi/operation-log/src/orm/illuminate/Log.php new file mode 100644 index 000000000..bf78bef95 --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/illuminate/Log.php @@ -0,0 +1,200 @@ +getKeyName(); + } + + /** + * @param Model $model + */ + public function getTableName($model): string + { + return $model->getConnection()->getTablePrefix() . $model->getTable(); + } + + /** + * @param Model $model + */ + public function getDatabaseName($model): string + { + if (method_exists($model, 'getQuery')) { + return $model->getQuery()->getDatabaseName(); + } + + return $model->getConnection()->getDatabaseName(); + } + + /** + * @param Model $model + */ + public function executeSQL($model, string $sql): array + { + if (method_exists($model, 'getQuery')) { + return $model->getQuery()->select($sql); + } + + return $model->getConnection()->select($sql); + } + + /** + * @param Model $model + */ + public function getAttributes($model): array + { + return $model->getAttributes(); + } + + /** + * @param Model $model + */ + public function getChangedAttributes($model): array + { + return $model->getChanges(); + } + + /** + * @param Model $model + */ + public function getValue($model, string $key): string + { + $keyText = $key . '_text'; + $value = $model->{$keyText} ?? $model->{$key}; + + if ($value instanceof ArrayObject) { + $value = $value->toArray(); + } + + if (is_array($value)) { + return json_encode($value, JSON_UNESCAPED_UNICODE); + } + + if (is_object($value) && $value instanceof Expression) { + // Compatible with version 10.x + // @phpstan-ignore-next-line + return $value->getValue($model->getConnection()->getQueryGrammar()); + } + + return (string) $value; + } + + /** + * @param Model $model + */ + public function getOldValue($model, string $key): string + { + if (str_contains($key, '->')) { + [$key, $jsonKey] = explode('->', $key, 2); + } + + $keyText = $key . '_text'; + $attributeFun = 'get' . Str::studly(Str::lower($keyText)) . 'Attribute'; + $value = (method_exists($model, $attributeFun) ? $model->{$attributeFun}($model->getOriginal($key)) : $model->getOriginal($key)); + + if ($value instanceof ArrayObject) { + $value = $value->toArray(); + } + + if (is_array($value)) { + return json_encode($value, JSON_UNESCAPED_UNICODE); + } + + $val = json_decode((string) $value, true); + if (!isset($jsonKey) || is_null($val) || !is_array($val)) { + return (string) $value; + } + + foreach (explode('->', $jsonKey) as $k) { + $val = $val[$k]; + } + + return (string) $val; + } + + /** + * @param Model $model + */ + public function created($model, array $data): void + { + $model->setRawAttributes($data); + $this->generateLog($model, self::CREATED); + } + + /** + * @param Model $model + */ + public function updated($model, array $oldData, array $data): void + { + $data = array_map(function ($value) { + return is_array($value) ? json_encode($value, JSON_UNESCAPED_UNICODE) : $value; + }, $data); + + $model->setRawAttributes($oldData, true); + $model->setRawAttributes(array_merge($oldData, $data)); + $model->syncChanges(); + $this->generateLog($model, self::UPDATED); + } + + /** + * @param Model $model + */ + public function deleted($model, array $data): void + { + $model->setRawAttributes($data); + $this->generateLog($model, self::DELETED); + } + + /** + * @param Model $model + */ + public function batchCreated($model, array $data): void + { + foreach ($data as $item) { + $model->setRawAttributes($item); + $this->generateLog($model, self::BATCH_CREATED); + } + } + + /** + * @param Model $model + */ + public function batchUpdated($model, array $oldData, array $data): void + { + foreach ($oldData as $item) { + $model->setRawAttributes((array) $item, true); + $model->setRawAttributes(array_merge((array) $item, $data)); + $model->syncChanges(); + $this->generateLog($model, self::BATCH_UPDATED); + } + } + + /** + * @param Model $model + */ + public function batchDeleted($model, array $data): void + { + foreach ($data as $item) { + $model->setRawAttributes((array) $item); + $this->generateLog($model, self::BATCH_DELETED); + } + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/illuminate/MySqlConnection.php b/vendor/chance-fyi/operation-log/src/orm/illuminate/MySqlConnection.php new file mode 100644 index 000000000..8f8779a6e --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/illuminate/MySqlConnection.php @@ -0,0 +1,33 @@ +getQueryGrammar(), + $this->getPostProcessor() + ); + } + + public function beginTransaction(): void + { + IlluminateOrmLog::beginTransaction(); + parent::beginTransaction(); + } + + public function rollBack($toLevel = null): void + { + IlluminateOrmLog::rollBackTransaction(is_null($toLevel) ? $this->transactions : $toLevel); + parent::rollBack($toLevel); + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/think/DbModel.php b/vendor/chance-fyi/operation-log/src/orm/think/DbModel.php new file mode 100644 index 000000000..7831ab31c --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/think/DbModel.php @@ -0,0 +1,34 @@ +table = $table; + parent::__construct($data); + } + + public function setQuery(Query $query): void + { + $this->query = $query; + } + + public function getQuery(): Query + { + return $this->query; + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/think/Log.php b/vendor/chance-fyi/operation-log/src/orm/think/Log.php new file mode 100644 index 000000000..08cd88a0c --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/think/Log.php @@ -0,0 +1,183 @@ +getPk(); + } + + /** + * @param Model $model + */ + public function getTableName($model): string + { + return $model->getTable(); + } + + /** + * @param Model $model + */ + public function getDatabaseName($model): string + { + if (method_exists($model, 'getQuery')) { + return $model->getQuery()->getConfig('database'); + } + + return $model->getConfig('database'); + } + + /** + * @param Model $model + * + * @throws DbException + */ + public function executeSQL($model, string $sql): mixed + { + if (method_exists($model, 'getQuery')) { + return $model->getQuery()->getConnection()->query($sql); + } + + /** @var PDOConnection $connection */ + $connection = $model->db()->getConnection(); + + return $connection->query($sql); + } + + /** + * @param Model $model + */ + public function getAttributes($model): array + { + return $model->toArray(); + } + + /** + * @param Model $model + */ + public function getChangedAttributes($model): array + { + return $model->getChangedData(); + } + + /** + * @param Model $model + */ + public function getValue($model, string $key): string + { + $keyText = $key . '_text'; + $value = $model->{$keyText} ?? $model->{$key}; + + if ($value instanceof Raw) { + return $value->getValue(); + } + if (is_array($value) || is_object($value)) { + return json_encode($value, JSON_UNESCAPED_UNICODE); + } + + return (string) $value; + } + + /** + * @param Model $model + */ + public function getOldValue($model, string $key): string + { + if (str_contains($key, '->')) { + $value = $model->getOrigin(vsprintf("json_extract(`json`, '$.name')", explode('->', $key, 2))); + + return trim($value, '"'); + } + + $keyText = $key . '_text'; + $attributeFun = 'get' . Str::studly(Str::lower($keyText)) . 'Attr'; + $value = method_exists($model, $attributeFun) ? $model->{$attributeFun}($model->getOrigin($key)) : $model->getOrigin($key); + + if (is_array($value) || is_object($value)) { + return json_encode($value, JSON_UNESCAPED_UNICODE); + } + + return (string) $value; + } + + /** + * @param Model $model + */ + public function created($model, array $data): void + { + $model->setAttrs($data); + $this->generateLog($model, self::CREATED); + } + + /** + * @param Model $model + */ + public function updated($model, array $oldData, array $data): void + { + $model->setAttrs($oldData); + $model->refreshOrigin(); + $model->setAttrs($data); + $this->generateLog($model, self::UPDATED); + } + + /** + * @param Model $model + */ + public function deleted($model, array $data): void + { + $model->setAttrs($data); + $this->generateLog($model, self::DELETED); + } + + /** + * @param Model $model + */ + public function batchCreated($model, array $data): void + { + foreach ($data as $item) { + $model->setAttrs($item); + $this->generateLog($model, self::BATCH_CREATED); + } + } + + /** + * @param Model $model + */ + public function batchUpdated($model, array $oldData, array $data): void + { + foreach ($oldData as $item) { + $model->setAttrs($item); + $model->refreshOrigin(); + $model->setAttrs($data); + $this->generateLog($model, self::BATCH_UPDATED); + } + } + + /** + * @param Model $model + */ + public function batchDeleted($model, array $data): void + { + foreach ($data as $item) { + $model->setAttrs($item); + $this->generateLog($model, self::BATCH_DELETED); + } + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/think/MySqlConnection.php b/vendor/chance-fyi/operation-log/src/orm/think/MySqlConnection.php new file mode 100644 index 000000000..8c9f30f53 --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/think/MySqlConnection.php @@ -0,0 +1,25 @@ +transTimes); + parent::rollback(); + } +} diff --git a/vendor/chance-fyi/operation-log/src/orm/think/Query.php b/vendor/chance-fyi/operation-log/src/orm/think/Query.php new file mode 100644 index 000000000..7d5df98ac --- /dev/null +++ b/vendor/chance-fyi/operation-log/src/orm/think/Query.php @@ -0,0 +1,140 @@ +getLastInsID(); + } + + $model = $this->generateModel(); + $pk = $this->getPk(); + $data = $data ?: $this->getOptions('data'); + $data[$pk] = $id; + ThinkOrmLog::created($model, $data); + } + + return $result; + } + + public function insertAll(array $dataSet = [], int $limit = 0): int + { + $result = parent::insertAll($dataSet, $limit); + + if (ThinkOrmLog::status()) { + $model = $this->generateModel(); + ThinkOrmLog::batchCreated($model, $dataSet); + } + + return $result; + } + + public function update(array $data = []): int + { + if (ThinkOrmLog::status()) { + $model = $this->generateModel(); + $newData = $data ?: $this->getOptions('data'); + $field = array_keys($newData); + $field[] = $model->logKey ?? $model->getPk(); + + $pk = $model->getPk(); + if (isset($data[$pk])) { + // 包含主键只更新一条 + $oldData = $this->find($data[$pk]); + if (!empty($oldData)) { + $oldData = [is_array($oldData) ? $oldData : $oldData->toArray()]; + } + } else { + // 条件查询或许是多条 + $oldData = $this->field($field)->select()->toArray(); + } + if (!empty($oldData)) { + if (count($oldData) > 1) { + ThinkOrmLog::batchUpdated($model, $oldData, $newData); + } else { + ThinkOrmLog::updated($model, $oldData[0], $newData); + } + } + } + + return parent::update($data); + } + + public function delete($data = null): int + { + if (ThinkOrmLog::status()) { + $model = $this->generateModel(); + if (!empty($data)) { + $pk = $model->getPk(); + $delData = $this->whereIn($pk, $data)->select()->toArray(); + } else { + $delData = $this->select()->toArray(); + } + + if (!empty($delData)) { + if (count($delData) > 1) { + ThinkOrmLog::batchDeleted($model, $delData); + } else { + ThinkOrmLog::deleted($model, $delData[0]); + } + } + } + + return parent::delete($data); + } + + /** + * Generate model object. + */ + private function generateModel(): Model + { + if ($this->getModel()) { + return $this->getModel(); + } + + $database = $this->getConfig('database'); + $table = $this->getTable(); + + $mapping = [ + OperationLog::getTableModelMapping(), + include __DIR__ . '/../../../cache/table-model-mapping.php', + ]; + foreach ($mapping as $map) { + if (is_array($map) && isset($map[$database][$table]) && class_exists($map[$database][$table])) { + return new $map[$database][$table](); + } + } + + $name = ltrim(Str::lower($table), Str::lower($this->prefix)); + $modelNamespace = $this->getConfig('modelNamespace') ?: 'app\\model'; + $className = trim($modelNamespace, '\\') . '\\' . Str::studly($name); + if (class_exists($className)) { + $model = new $className(); + } else { + $model = new DbModel($table); + $model->table($table); + $model->setQuery($this); + $model->logKey = $this->getConfig('logKey') ?: $model->getPk(); + $model->pk($this->getPk()); + } + + return $model; + } +} diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index b8213a34d..820d3a75e 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -112,6 +112,7 @@ return array( 'DI\\' => array($vendorDir . '/php-di/php-di/src'), 'Cron\\' => array($vendorDir . '/dragonmantank/cron-expression/src/Cron'), 'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'), + 'Chance\\Log\\' => array($vendorDir . '/chance-fyi/operation-log/src'), 'Carbon\\Doctrine\\' => array($vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine'), 'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'), 'App\\' => array($baseDir . '/app'), diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index c9ab0cfef..11d859c4e 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -218,6 +218,7 @@ class ComposerStaticInitcefecbcff919f3c1c8084830bbb72adc array ( 'Cron\\' => 5, 'Complex\\' => 8, + 'Chance\\Log\\' => 11, 'Carbon\\Doctrine\\' => 16, 'Carbon\\' => 7, ), @@ -658,6 +659,10 @@ class ComposerStaticInitcefecbcff919f3c1c8084830bbb72adc array ( 0 => __DIR__ . '/..' . '/markbaker/complex/classes/src', ), + 'Chance\\Log\\' => + array ( + 0 => __DIR__ . '/..' . '/chance-fyi/operation-log/src', + ), 'Carbon\\Doctrine\\' => array ( 0 => __DIR__ . '/..' . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 25ca6977b..993fec52c 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -120,6 +120,70 @@ ], "install-path": "../carbonphp/carbon-doctrine-types" }, + { + "name": "chance-fyi/operation-log", + "version": "v3.0.7", + "version_normalized": "3.0.7.0", + "source": { + "type": "git", + "url": "https://github.com/Chance-fyi/operation-log.git", + "reference": "bfb73bc1c3dddf91772de4f37b42a41c519c67e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Chance-fyi/operation-log/zipball/bfb73bc1c3dddf91772de4f37b42a41c519c67e5", + "reference": "bfb73bc1c3dddf91772de4f37b42a41c519c67e5", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^8.0" + }, + "require-dev": { + "fakerphp/faker": "^1.21@dev", + "friendsofphp/php-cs-fixer": "dev-master", + "hyperf/config": "^3.0@dev", + "hyperf/database": "^3.0@dev", + "hyperf/di": "^3.0@dev", + "hyperf/pimple": "^2.1", + "illuminate/database": "^8.0", + "phpstan/phpstan": "1.11.x-dev", + "phpunit/phpunit": "9.6.x-dev", + "topthink/think-orm": "2.0.x-dev" + }, + "time": "2023-12-22T08:06:25+00:00", + "bin": [ + "bin/chance-fyi-operation-log" + ], + "type": "library", + "extra": { + "hyperf": { + "config": "Chance\\Log\\orm\\hyperf\\ConfigProvider" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Chance\\Log\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "chance", + "email": "ctx_ya@qq.com" + } + ], + "description": "Elegant logging of operations", + "support": { + "issues": "https://github.com/Chance-fyi/operation-log/issues", + "source": "https://github.com/Chance-fyi/operation-log/tree/v3.0.7" + }, + "install-path": "../chance-fyi/operation-log" + }, { "name": "doctrine/annotations", "version": "1.14.3", diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index c2edf9aa4..871e37647 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -28,6 +28,15 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'chance-fyi/operation-log' => array( + 'pretty_version' => 'v3.0.7', + 'version' => '3.0.7.0', + 'reference' => 'bfb73bc1c3dddf91772de4f37b42a41c519c67e5', + 'type' => 'library', + 'install_path' => __DIR__ . '/../chance-fyi/operation-log', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'doctrine/annotations' => array( 'pretty_version' => '1.14.3', 'version' => '1.14.3.0',