浏览代码

快递信息同步失败成功占比

ANG YU 4 年之前
父节点
当前提交
0a0ac7b46a

+ 45 - 0
app/Filters/OrderPackageReceivedSyncRecordFilters.php

@@ -0,0 +1,45 @@
+<?php
+
+
+namespace App\Filters;
+
+
+use Illuminate\Http\Request;
+use Illuminate\Database\Query\Builder;
+
+class OrderPackageReceivedSyncRecordFilters
+{
+    protected $request;
+    /**
+     * @var $queryBuilder Builder
+     */
+    protected $queryBuilder;
+    protected $filters = ['start', 'end', 'logistic_name'];
+
+    public function __construct(Request $request)
+    {
+        $this->request = $request;
+    }
+
+    public function apply($builder)
+    {
+        $this->queryBuilder = $builder;
+        $filters = array_filter($this->request->only($this->filters));
+        foreach ($filters as $filter => $value) {
+            if (method_exists($this, $filter)) {
+                $this->$filter($value, $this->queryBuilder);
+            }
+        }
+        return $this->queryBuilder;
+    }
+
+    public function start($start)
+    {
+        $this->queryBuilder->whereDate('recorded_at', '>=', $start);
+    }
+
+    public function end($end)
+    {
+        $this->queryBuilder->whereDate('recorded_at', '<=', $end);
+    }
+}

+ 30 - 0
app/Http/Controllers/ControlPanelController.php

@@ -3,7 +3,9 @@
 namespace App\Http\Controllers;
 
 use App\Components\AsyncResponse;
+use App\Filters\OrderPackageReceivedSyncRecordFilters;
 use App\OrderPackageCountingRecord;
+use App\OrderPackageReceivedSyncRecord;
 use App\Owner;
 use App\Services\CacheService;
 use App\Services\CheckActiveMenuService;
@@ -160,6 +162,34 @@ class ControlPanelController extends Controller
         $this->success(["title"=>$title,"data"=>$data]);
     }
 
+
+    /**
+     * 快递信息同步失败成功占比
+     * @param OrderPackageReceivedSyncRecordFilters $filters
+     * @param Request $request
+     * @return array
+     */
+    public function orderPackageReceivedSyncRecordApi(OrderPackageReceivedSyncRecordFilters $filters, Request $request)
+    {
+        $orderPackageReceivedSyncRecords = OrderPackageReceivedSyncRecord::query()->filter($filters)->get();
+        $data = [];
+        foreach ($orderPackageReceivedSyncRecords as $orderPackageReceivedSyncRecord) {
+            $total = $orderPackageReceivedSyncRecord->succeed_count + $orderPackageReceivedSyncRecord->failed_count;
+            $data[] = [
+                'date' => $orderPackageReceivedSyncRecord->recorded_at,
+                'total'=> $total,
+                'count'=>$orderPackageReceivedSyncRecord->succeed_count,
+                'value' => (int)($orderPackageReceivedSyncRecord->succeed_count / $total*100),
+                'logistic_name' =>$orderPackageReceivedSyncRecord->logistic_name
+            ];
+        }
+        $title = [];
+        foreach ($data as $data_item) {
+            $title[] = $data_item['date'];
+        }
+        return ['success' =>true, 'data' =>['data'=>$data,'title'=>$title]];
+    }
+
     public function exceptionTypeApi(Request $request)
     {
         $ownerIds=$request->owner_ids;

+ 22 - 0
app/OrderPackageReceivedSyncRecord.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+
+class OrderPackageReceivedSyncRecord extends Model
+{
+    use ModelLogChanging;
+
+    //
+    public $fillable = ['logistic_name', 'recorded_at', 'succeed_count', 'failed_count'];
+
+    public $timestamps = false;
+
+    public function scopeFilter($query, $filters)
+    {
+        return $filters->apply($query);
+    }
+}

+ 152 - 85
app/Providers/AppServiceProvider.php

@@ -3,6 +3,10 @@
 namespace App\Providers;
 
 use App\Http\Controllers\Controller;
+use App\Jobs\LogisticSFSync;
+use App\Jobs\LogisticYDSync;
+use App\Jobs\LogisticYTOSync;
+use App\Jobs\LogisticZopSync;
 use App\Services\AuthorityService;
 use App\Services\BatchService;
 use App\Services\CacheService;
@@ -44,6 +48,7 @@ use App\Services\OracleActAllocationDetailService;
 use App\Services\OrderIssueProcessLogService;
 use App\Services\OrderIssueRejectedBillService;
 use App\Services\OrderIssueService;
+use App\Services\OrderPackageReceivedSyncRecordService;
 use App\Services\OrderPackageReceivedSyncService;
 use App\Services\OrderPackageService;
 use App\Services\OrderService;
@@ -106,6 +111,8 @@ use App\Services\DischargeTaskService;
 use App\Services\DeliveryAppointmentService;
 use App\Services\StationCacheShelfGridService;
 use Illuminate\Queue\Events\JobFailed;
+use Illuminate\Queue\Events\JobProcessed;
+use Illuminate\Queue\Events\JobProcessing;
 use Illuminate\Support\Facades\Queue;
 use Illuminate\Support\Facades\Schema;
 use Illuminate\Support\Facades\View;
@@ -140,7 +147,13 @@ class AppServiceProvider extends ServiceProvider
         //
         Schema::defaultStringLength(191);
         Queue::failing(function (JobFailed $event) {
-            (new Controller())->log(__METHOD__,'EventError_',json_encode($event));
+            (new Controller())->log(__METHOD__, 'EventError_', json_encode($event));
+            $payload = $event->job->payload();
+            $displayName = $payload['displayName'];
+            //快递信息同步失败计数
+            if ($this->isLogisticSyncJob($displayName)) {
+                $this->logisticSyncRecord($displayName, 'failed_count');
+            }
         });
         //扩展身份证验证规则
         Validator::extend('identity_cards', function($attribute, $value, $parameters) {
@@ -157,6 +170,19 @@ class AppServiceProvider extends ServiceProvider
         \Illuminate\Database\Eloquent\Builder::macro('sql', function(){
             return ($this->getQuery()->sql());
         });
+
+        Queue::before(function (JobProcessing $event) {
+
+        });
+        //任务成功后的回调
+        Queue::after(function (JobProcessed $event) {
+            //快递信息同步成功计数
+            $payload = $event->job->payload();
+            $displayName = $payload['displayName'];
+            if ($this->isLogisticSyncJob($displayName)) {
+                $this->logisticSyncRecord($displayName, 'succeed_count');
+            }
+        });
     }
 
     private function loadingService(){
@@ -186,91 +212,132 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('ForeignHaiRoboticsService',ForeignHaiRoboticsService::class);
         app()->singleton('ForeignZhenCangService',ForeignZhenCangService::class);
         app()->singleton('LogisticZopService', LogisticZopService::class);
-        app()->singleton('InventoryAccountMissionService',InventoryAccountMissionService::class);
-        app()->singleton('InventoryCompareService',InventoryCompareService::class);
-        app()->singleton('InventoryDailyLogService',InventoryDailyLogService::class);
-        app()->singleton('LaborReportsCountingRecordService',LaborReportsCountingRecordService::class);
-        app()->singleton('LogService',LogService::class);
-        app()->singleton('LogisticSFService',LogisticSFService::class);
-        app()->singleton('LogisticService',LogisticService::class);
-        app()->singleton('LogisticYDService',LogisticYDService::class);
-        app()->singleton('LogisticYTOService',LogisticYTOService::class);
-        app()->singleton('MaterialBoxService',MaterialBoxService::class);
-        app()->singleton('OracleActAllocationDetailService',OracleActAllocationDetailService::class);
-        app()->singleton('OracleBasCustomerService',OracleBasCustomerService::class);
-        app()->singleton('OracleBasSkuService',OracleBasSkuService::class);
-        app()->singleton('OracleDocAsnDetailService',OracleDocAsnDetailService::class);
-        app()->singleton('OracleDocOrderHeaderService',OracleDOCOrderHeaderService::class);
-        app()->singleton('OracleDocWaveDetailService',OracleDocWaveDetailService::class);
-        app()->singleton('OrderCommodityAssignService',OrderCommodityAssignService::class);
-        app()->singleton('OrderCommodityService',OrderCommodityService::class);
-        app()->singleton('OrderFreezeService',OrderFreezeService::class);
-        app()->singleton('OrderIssuePerformanceService',OrderIssuePerformanceService::class);
-        app()->singleton('OrderIssueProcessLogService',OrderIssueProcessLogService::class);
-        app()->singleton('OrderIssueRejectedBillService',OrderIssueRejectedBillService::class);
-        app()->singleton('OrderIssueService',OrderIssueService::class);
-        app()->singleton('OrderIssueWorkLoadService',OrderIssueWorkLoadService::class);
-        app()->singleton('OrderPackageCommoditiesService',OrderPackageCommoditiesService::class);
-        app()->singleton('OrderPackageCommoditySerialNumberService',OrderPackageCommoditySerialNumberService::class);
-        app()->singleton('OrderPackageExceptionTypeCountingRecordService',OrderPackageExceptionTypeCountingRecordService::class);
-        app()->singleton('OrderPackageReceivedSyncService',OrderPackageReceivedSyncService::class);
-        app()->singleton('OrderPackageService',OrderPackageService::class);
-        app()->singleton('OrderService',OrderService::class);
-        app()->singleton('OrderTrackingService',OrderTrackingService::class);
-        app()->singleton('OwnerAreaReportService',OwnerAreaReportService::class);
-        app()->singleton('OwnerBillReportService',OwnerBillReportService::class);
-        app()->singleton('OwnerFeeDetailService',OwnerFeeDetailService::class);
-        app()->singleton('OwnerMaterialService',OwnerMaterialService::class);
-        app()->singleton('OwnerMaterialService',OwnerMaterialService::class);
-        app()->singleton('OwnerPriceDirectLogisticService',OwnerPriceDirectLogisticService::class);
-        app()->singleton('OwnerPriceExpressService',OwnerPriceExpressService::class);
-        app()->singleton('OwnerPriceLogisticService',OwnerPriceLogisticService::class);
-        app()->singleton('OwnerPriceOperationItemService',OwnerPriceOperationItemService::class);
-        app()->singleton('OwnerPriceOperationService',OwnerPriceOperationService::class);
-        app()->singleton('OwnerReportService',OwnerReportService::class);
-        app()->singleton('OwnerService',OwnerService::class);
-        app()->singleton('OwnerStoragePriceModelService',OwnerStoragePriceModelService::class);
-        app()->singleton('PackageService',PackageService::class);
-        app()->singleton('PackageStatisticsService',PackageStatisticsService::class);
-        app()->singleton('ProcessMethodService',ProcessMethodService::class);
-        app()->singleton('ProcessService',ProcessService::class);
-        app()->singleton('ProcessStatisticService',ProcessStatisticService::class);
-        app()->singleton('ProcessesContentService',ProcessesContentService::class);
-        app()->singleton('ProcurementService',ProcurementService::class);
-        app()->singleton('ProcurementTotalBillService',ProcurementTotalBillService::class);
-        app()->singleton('ProcurementWeiXinSendMessageService',ProcurementWeiXinSendMessageService::class);
-        app()->singleton('ProvinceService',ProvinceService::class);
-        app()->singleton('RealtimePendingOrdersService',RealtimePendingOrdersService::class);
-        app()->singleton('RegionService',RegionService::class);
-        app()->singleton('RejectedBillItemService',RejectedBillItemService::class);
-        app()->singleton('RejectedBillService',RejectedBillService::class);
-        app()->singleton('RejectedService',RejectedService::class);
-        app()->singleton('ShopService',ShopService::class);
-        app()->singleton('StationCacheShelfGridService',StationCacheShelfGridService::class);
-        app()->singleton('StationRuleBatchService',StationRuleBatchService::class);
-        app()->singleton('StationService',StationService::class);
-        app()->singleton('StationTaskBatchService',StationTaskBatchService::class);
-        app()->singleton('StationTaskBatchTypeService',StationTaskBatchTypeService::class);
-        app()->singleton('StationTaskChildService',StationTaskChildService::class);
-        app()->singleton('StationTaskCommodityService',StationTaskCommodityService::class);
-        app()->singleton('StationTaskMaterialBoxService',StationTaskMaterialBoxService::class);
-        app()->singleton('StationTaskService',StationTaskService::class);
-        app()->singleton('StationTaskTypeService',StationTaskTypeService::class);
-        app()->singleton('StationTypeBinMonitorService',StationTypeBinMonitorService::class);
-        app()->singleton('StationTypeService',StationTypeService::class);
-        app()->singleton('StoreCheckingReceiveItemService',StoreCheckingReceiveItemService::class);
-        app()->singleton('StoreCheckingReceiveService',StoreCheckingReceiveService::class);
-        app()->singleton('StoreItemService',StoreItemService::class);
-        app()->singleton('StoreService',StoreService::class);
-        app()->singleton('SupplierService',SupplierService::class);
-        app()->singleton('UnitService',UnitService::class);
-        app()->singleton('UserOwnerGroupService',UserOwnerGroupService::class);
-        app()->singleton('UserService',UserService::class);
-        app()->singleton('UserWorkgroupService',UserWorkgroupService::class);
-        app()->singleton('WarehouseService',WarehouseService::class);
-        app()->singleton('WaybillFinancialService',WaybillFinancialService::class);
-        app()->singleton('WeighExceptedService',WeighExceptedService::class);
+        app()->singleton('InventoryAccountMissionService', InventoryAccountMissionService::class);
+        app()->singleton('InventoryCompareService', InventoryCompareService::class);
+        app()->singleton('InventoryDailyLogService', InventoryDailyLogService::class);
+        app()->singleton('LaborReportsCountingRecordService', LaborReportsCountingRecordService::class);
+        app()->singleton('OrderPackageReceivedSyncRecordService', OrderPackageReceivedSyncRecordService::class);
+        app()->singleton('LogService', LogService::class);
+        app()->singleton('LogisticSFService', LogisticSFService::class);
+        app()->singleton('LogisticService', LogisticService::class);
+        app()->singleton('LogisticYDService', LogisticYDService::class);
+        app()->singleton('LogisticYTOService', LogisticYTOService::class);
+        app()->singleton('MaterialBoxService', MaterialBoxService::class);
+        app()->singleton('OracleActAllocationDetailService', OracleActAllocationDetailService::class);
+        app()->singleton('OracleBasCustomerService', OracleBasCustomerService::class);
+        app()->singleton('OracleBasSkuService', OracleBasSkuService::class);
+        app()->singleton('OracleDocAsnDetailService', OracleDocAsnDetailService::class);
+        app()->singleton('OracleDocOrderHeaderService', OracleDOCOrderHeaderService::class);
+        app()->singleton('OracleDocWaveDetailService', OracleDocWaveDetailService::class);
+        app()->singleton('OrderCommodityAssignService', OrderCommodityAssignService::class);
+        app()->singleton('OrderCommodityService', OrderCommodityService::class);
+        app()->singleton('OrderFreezeService', OrderFreezeService::class);
+        app()->singleton('OrderIssuePerformanceService', OrderIssuePerformanceService::class);
+        app()->singleton('OrderIssueProcessLogService', OrderIssueProcessLogService::class);
+        app()->singleton('OrderIssueRejectedBillService', OrderIssueRejectedBillService::class);
+        app()->singleton('OrderIssueService', OrderIssueService::class);
+        app()->singleton('OrderIssueWorkLoadService', OrderIssueWorkLoadService::class);
+        app()->singleton('OrderPackageCommoditiesService', OrderPackageCommoditiesService::class);
+        app()->singleton('OrderPackageCommoditySerialNumberService', OrderPackageCommoditySerialNumberService::class);
+        app()->singleton('OrderPackageExceptionTypeCountingRecordService', OrderPackageExceptionTypeCountingRecordService::class);
+        app()->singleton('OrderPackageReceivedSyncService', OrderPackageReceivedSyncService::class);
+        app()->singleton('OrderPackageService', OrderPackageService::class);
+        app()->singleton('OrderService', OrderService::class);
+        app()->singleton('OrderTrackingService', OrderTrackingService::class);
+        app()->singleton('OwnerAreaReportService', OwnerAreaReportService::class);
+        app()->singleton('OwnerBillReportService', OwnerBillReportService::class);
+        app()->singleton('OwnerFeeDetailService', OwnerFeeDetailService::class);
+        app()->singleton('OwnerMaterialService', OwnerMaterialService::class);
+        app()->singleton('OwnerMaterialService', OwnerMaterialService::class);
+        app()->singleton('OwnerPriceDirectLogisticService', OwnerPriceDirectLogisticService::class);
+        app()->singleton('OwnerPriceExpressService', OwnerPriceExpressService::class);
+        app()->singleton('OwnerPriceLogisticService', OwnerPriceLogisticService::class);
+        app()->singleton('OwnerPriceOperationItemService', OwnerPriceOperationItemService::class);
+        app()->singleton('OwnerPriceOperationService', OwnerPriceOperationService::class);
+        app()->singleton('OwnerReportService', OwnerReportService::class);
+        app()->singleton('OwnerService', OwnerService::class);
+        app()->singleton('OwnerStoragePriceModelService', OwnerStoragePriceModelService::class);
+        app()->singleton('PackageService', PackageService::class);
+        app()->singleton('PackageStatisticsService', PackageStatisticsService::class);
+        app()->singleton('ProcessMethodService', ProcessMethodService::class);
+        app()->singleton('ProcessService', ProcessService::class);
+        app()->singleton('ProcessStatisticService', ProcessStatisticService::class);
+        app()->singleton('ProcessesContentService', ProcessesContentService::class);
+        app()->singleton('ProcurementService', ProcurementService::class);
+        app()->singleton('ProcurementTotalBillService', ProcurementTotalBillService::class);
+        app()->singleton('ProcurementWeiXinSendMessageService', ProcurementWeiXinSendMessageService::class);
+        app()->singleton('ProvinceService', ProvinceService::class);
+        app()->singleton('RealtimePendingOrdersService', RealtimePendingOrdersService::class);
+        app()->singleton('RegionService', RegionService::class);
+        app()->singleton('RejectedBillItemService', RejectedBillItemService::class);
+        app()->singleton('RejectedBillService', RejectedBillService::class);
+        app()->singleton('RejectedService', RejectedService::class);
+        app()->singleton('ShopService', ShopService::class);
+        app()->singleton('StationCacheShelfGridService', StationCacheShelfGridService::class);
+        app()->singleton('StationRuleBatchService', StationRuleBatchService::class);
+        app()->singleton('StationService', StationService::class);
+        app()->singleton('StationTaskBatchService', StationTaskBatchService::class);
+        app()->singleton('StationTaskBatchTypeService', StationTaskBatchTypeService::class);
+        app()->singleton('StationTaskChildService', StationTaskChildService::class);
+        app()->singleton('StationTaskCommodityService', StationTaskCommodityService::class);
+        app()->singleton('StationTaskMaterialBoxService', StationTaskMaterialBoxService::class);
+        app()->singleton('StationTaskService', StationTaskService::class);
+        app()->singleton('StationTaskTypeService', StationTaskTypeService::class);
+        app()->singleton('StationTypeBinMonitorService', StationTypeBinMonitorService::class);
+        app()->singleton('StationTypeService', StationTypeService::class);
+        app()->singleton('StoreCheckingReceiveItemService', StoreCheckingReceiveItemService::class);
+        app()->singleton('StoreCheckingReceiveService', StoreCheckingReceiveService::class);
+        app()->singleton('StoreItemService', StoreItemService::class);
+        app()->singleton('StoreService', StoreService::class);
+        app()->singleton('SupplierService', SupplierService::class);
+        app()->singleton('UnitService', UnitService::class);
+        app()->singleton('UserOwnerGroupService', UserOwnerGroupService::class);
+        app()->singleton('UserService', UserService::class);
+        app()->singleton('UserWorkgroupService', UserWorkgroupService::class);
+        app()->singleton('WarehouseService', WarehouseService::class);
+        app()->singleton('WaybillFinancialService', WaybillFinancialService::class);
+        app()->singleton('WeighExceptedService', WeighExceptedService::class);
     }
 
+    /**
+     * 快递同步接口 同步情况统计
+     */
+    private function logisticSyncRecord($displayName, $column_name): void
+    {
+
+
+        /**
+         * @var OrderPackageReceivedSyncRecordService $orderPackageReceivedSyncRecordService
+         */
+        $orderPackageReceivedSyncRecordService = app('OrderPackageReceivedSyncRecordService');
+        switch ($displayName) {
+            case LogisticZopSync::class:
+                $orderPackageReceivedSyncRecordService->createOrIncrementSucceededCount('中通', now()->toDateString(), $column_name);
+                break;
+            case LogisticSFSync::class:
+                $orderPackageReceivedSyncRecordService->createOrIncrementSucceededCount('顺丰', now()->toDateString(), $column_name);
+                break;
+            case LogisticYDSync::class:
+                $orderPackageReceivedSyncRecordService->createOrIncrementSucceededCount('韵达', now()->toDateString(), $column_name);
+                break;
+            case LogisticYTOSync::class:
+                $orderPackageReceivedSyncRecordService->createOrIncrementSucceededCount('圆通', now()->toDateString(), $column_name);
+                break;
+            default:
+                $orderPackageReceivedSyncRecordService->createOrIncrementSucceededCount('其他', now()->toDateString(), $column_name);
+        }
+    }
 
+    /**
+     * 判断当前任务类型是否为快递信息同步
+     * @param $displayName
+     * @return bool
+     */
+    private function isLogisticSyncJob($displayName): bool
+    {
+        return ($displayName == LogisticZopSync::class)
+            || ($displayName == LogisticSFSync::class)
+            || ($displayName == LogisticYDSync::class)
+            || ($displayName == LogisticYTOSync::class);
+    }
 }

+ 1 - 4
app/Services/LogisticZopService.php

@@ -30,13 +30,10 @@ class LogisticZopService implements LogisticRouteInterface
 
     public function format($nativeResponse)
     {
-        if (is_null($nativeResponse)) {
+        if (is_null($nativeResponse)||isEmpty($nativeResponse->result)) {//返回的结果为空,或者路由为[] 直接返回就好
             return [];
         }
         $nativeRoutes = $nativeResponse->result;
-//        $nativeMessages = $nativeResponse->messages;
-//        $nativeStatus = $nativeResponse->status;
-//        $nativeStatusCode = $nativeResponse->statusCode;
         $this->logistic_number = $nativeRoutes[0]->billCode;
         $order_package = OrderPackage::query()->where('logistic_number', $this->logistic_number)->first();
 

+ 33 - 0
app/Services/OrderPackageReceivedSyncRecordService.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Services;
+
+use App\Traits\ServiceAppAop;
+use App\OrderPackageReceivedSyncRecord;
+
+class OrderPackageReceivedSyncRecordService
+{
+    use ServiceAppAop;
+
+    protected $modelClass = OrderPackageReceivedSyncRecord::class;
+
+    /**
+     * @param $logistic_name string 成员商名称
+     * @param $recorded_at string 统计日期
+     * @param $column_name string 操作列名称 succeed_count or failed_count
+     */
+    public function createOrIncrementSucceededCount(string $logistic_name, string $recorded_at, string $column_name)
+    {
+        $orderPackageReceivedSyncRecord = OrderPackageReceivedSyncRecord::query()->where('logistic_name', $logistic_name)->whereDate('recorded_at', $recorded_at)->first();
+
+        if (is_null($orderPackageReceivedSyncRecord)) {
+            OrderPackageReceivedSyncRecord::query()->create([
+                'logistic_name' => $logistic_name,
+                'recorded_at' => $recorded_at,
+                $column_name => 1,
+            ]);
+        } else {
+            $orderPackageReceivedSyncRecord->increment($column_name);
+        }
+    }
+}

+ 16 - 0
database/factories/OrderPackageReceivedSyncRecordFactory.php

@@ -0,0 +1,16 @@
+<?php
+
+/** @var \Illuminate\Database\Eloquent\Factory $factory */
+
+use App\OrderPackageReceivedSyncRecord;
+use Faker\Generator as Faker;
+
+$factory->define(OrderPackageReceivedSyncRecord::class, function (Faker $faker) {
+    $logistic_names = ['中通', '顺丰', '韵达', '圆通', '其他'];
+    return [
+        'logistic_name' => $faker->randomElement($logistic_names),
+        'recorded_at' => now()->toDateString(),
+        'succeed_count' => random_int(10000, 20000),
+        'failed_count' => random_int(10000, 20000),
+    ];
+});

+ 34 - 0
database/migrations/2021_06_01_103343_create_order_package_received_sync_records_table.php

@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateOrderPackageReceivedSyncRecordsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('order_package_received_sync_records', function (Blueprint $table) {
+            $table->id();
+            $table->string('logistic_name')->comment('承运商名称');
+            $table->date('recorded_at')->comment('归档日期');
+            $table->integer('succeed_count')->unsigned()->default(0)->comment('成功计数');
+            $table->integer('failed_count')->unsigned()->default(0)->comment('失败计数');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('order_package_received_sync_records');
+    }
+}

+ 24 - 0
database/seeds/OrderPackageReceivedSyncRecordSeeder.php

@@ -0,0 +1,24 @@
+<?php
+
+use Illuminate\Database\Seeder;
+
+class OrderPackageReceivedSyncRecordSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     * @return void
+     */
+    public function run()
+    {
+        $logistic_names = ['中通', '顺丰', '韵达', '圆通', '其他'];
+        $recorded_ats = now()->subMonth()->daysUntil(now());
+        foreach ($recorded_ats as $recorded_at) {
+            foreach ($logistic_names as $logistic_name) {
+                factory(\App\OrderPackageReceivedSyncRecord::class)->create([
+                    'recorded_at' => $recorded_at->toDateString(),
+                    'logistic_name' => $logistic_name,
+                ]);
+            }
+        }
+    }
+}

+ 90 - 1
resources/views/control/panel.blade.php

@@ -364,6 +364,7 @@
                     </div>
                 </div>
                 @endcan
+                {{--异常分布图--}}
                 <div class="col-6">
                     <div class="card">
                         <div class="card-header">
@@ -392,7 +393,32 @@
                     </div>
                 </div>
             </div>
-
+            <div class="row my-3">
+                {{--快递接口请求成功失败统计图--}}
+                <div class="col-6">
+                    <div class="card">
+                        <div class="card-header">
+                            <div class="row">
+                                <el-date-picker size="small" class="col-6 date" @blur="loadOrderPackageReceivedSyncRecordInfo()"
+                                                type="daterange" align="right"
+                                                v-model="searchOption.OrderPackageReceivedSyncRecordDate" unlink-panels range-separator="-"
+                                                start-placeholder="开始日期" end-placeholder="结束日期"
+                                                value-format="yyyy-MM-dd">
+                                </el-date-picker>
+                                <label class="col-3 offset-3">
+                                    <select class="form-control rounded" v-model="searchOption.OrderPackageReceivedSyncRecordSelect"
+                                            @change="switchOrderPackageReceivedSyncRecordDate()">
+                                        <option v-for="(date,i) in dateOptions" :value="i">@{{ date.text }}</option>
+                                    </select>
+                                </label>
+                            </div>
+                        </div>
+                        <div class="card-body row">
+                            <div id="orderPackageReceivedSyncRecord" class="col-12" style="min-height: 500px"></div>
+                        </div>
+                    </div>
+                </div>
+            </div>
         </div>
     </div>
 @endsection
@@ -513,6 +539,8 @@
                     exceptionTypeDate: [],
                     weightSelect:"",
                     exceptionTypeSelect:"",
+                    OrderPackageReceivedSyncRecordDate: [],
+                    OrderPackageReceivedSyncRecordSelect:"",
                 },
             },
             watch:{
@@ -537,8 +565,10 @@
                 $('#list').removeClass('d-none');
                 let index = 4;
                 this.searchOption.weightSelect = index;
+                this.searchOption.OrderPackageReceivedSyncRecordSelect = 2;
                 this.searchOption.exceptionTypeSelect = index;
                 this.searchOption.weightDate = [this.dateOptions[index].start, this.dateOptions[index].end];
+                this.searchOption.OrderPackageReceivedSyncRecordDate = [this.dateOptions[2].start, this.dateOptions[2].end];
                 this.searchOption.exceptionTypeDate = [this.dateOptions[index].start, this.dateOptions[index].end];
                 let _this = this;
                 this.warehousesOrders.forEach(function (item) {
@@ -576,6 +606,8 @@
                 this.cardPool.weight = echarts.init(document.getElementById("weight"));
                 this.loadWeightInfo();
                 @endcan
+                this.cardPool.orderPackageReceivedSyncRecord = echarts.init(document.getElementById("orderPackageReceivedSyncRecord"));
+                this.loadOrderPackageReceivedSyncRecordInfo();
                 this.cardPool.exceptionType = echarts.init(document.getElementById("exceptionType"));
                 this.loadExceptionTypeInfo();
             },
@@ -933,6 +965,24 @@
                         this.cardPool.weight.setOption(this._setWeightData(res.title,res.data));
                     });
                 },
+                loadOrderPackageReceivedSyncRecordInfo(){
+                    window.tempTip.setDuration(3000);
+                    if (!this.searchOption.OrderPackageReceivedSyncRecordDate[0]){
+                        window.tempTip.show("开始时间未选择");
+                        return;
+                    }
+                    if (!this.searchOption.OrderPackageReceivedSyncRecordDate[1]){
+                        window.tempTip.show("结束时间未选择");
+                        return;
+                    }
+                    this.cardPool.orderPackageReceivedSyncRecord.showLoading('default',{text:"加 载 中",color:'#C0C0C0'});
+                    let url = "{{url('apiLocal/control/panel/menu/orderPackageReceivedSyncRecordApi')}}";
+                    let params = {start:this.searchOption.OrderPackageReceivedSyncRecordDate[0],end:this.searchOption.OrderPackageReceivedSyncRecordDate[1]};
+                    window.tempTip.postBasicRequest(url,params,res=>{
+                        this.cardPool.orderPackageReceivedSyncRecord.hideLoading();
+                        this.cardPool.orderPackageReceivedSyncRecord.setOption(this._setOrderPackageReceivedSyncRecordData(res.title, res.data));
+                    });
+                },
                 loadExceptionTypeInfo() {
                     window.tempTip.setDuration(3000);
                     if (!this.searchOption.exceptionTypeDate[0]){
@@ -956,6 +1006,11 @@
                     this.searchOption.weightDate = [obj.start,obj.end];
                     this.loadWeightInfo();
                 },
+                switchOrderPackageReceivedSyncRecordDate(){
+                    let obj = this.dateOptions[this.searchOption.OrderPackageReceivedSyncRecordSelect];
+                    this.searchOption.OrderPackageReceivedSyncRecordDate = [obj.start,obj.end];
+                    this.loadOrderPackageReceivedSyncRecordInfo();
+                },
                 switchExceptionTypeDate(){
                     let obj = this.dateOptions[this.searchOption.exceptionTypeSelect];
                     this.searchOption.exceptionTypeDate = [obj.start,obj.end];
@@ -995,6 +1050,40 @@
                         }]
                     };
                 },
+                _setOrderPackageReceivedSyncRecordData(title,data){
+                    return {
+                        title: {
+                            text: '快递信息同步成功失败占比',
+                            left: 'left'
+                        },
+                        tooltip: {
+                            trigger: 'item',
+                            formatter: function (params) {
+                                return params.data.date + "<br>" + "总量:<span class='text-success font-weight-bold'>" + params.data.total + "</span><br>" + "成功:<span class='text-info font-weight-bold'>" + params.data.count + "</span><br>"+ "承运商:<span class='text-info font-weight-bold'>" + params.data.logistic_name + "</span>";
+                            }
+                        }, xAxis: {
+                            data: title
+                        }, yAxis: {
+                            axisLabel: {
+                                show: true,
+                                interval: 'auto',
+                                formatter: '{value} %'
+                            },
+                            max: 100
+                        }, label: {
+                            show: true,
+                            position: 'top',
+                            formatter: '{c}%',
+                            color: "red"
+                        }, series: [{
+                            type: "bar",
+                            data: data,
+                            itemStyle: {
+                                color: "RGB(62,157,231)",
+                            }
+                        }]
+                    };
+                },
                 _setExceptionTypeData(data) {
                     let resData = [];
                     data.forEach(item => {

+ 1 - 0
routes/apiLocal.php

@@ -127,6 +127,7 @@ Route::group(['prefix'=>'control'],function () {
     Route::post('panel/menu/laborReportsUserGroupsCountApi','ControlPanelController@laborReportsUserGroupsCountApi');
     Route::post('panel/menu/weightApi','ControlPanelController@weightApi');
     Route::post('panel/menu/exceptionTypeApi','ControlPanelController@exceptionTypeApi');
+    Route::post('panel/menu/orderPackageReceivedSyncRecordApi','ControlPanelController@orderPackageReceivedSyncRecordApi');
 });
 
 /** 耗材 */