Selaa lähdekoodia

ID 193
控制台,为包裹的异常做一个饼状图,将所有(包括正常状态)按日显示,需要有权限控制,即货主权限仅能看到自己权限内的包裹(注意要有二至三层缓存:数据库计算的结果+Cache)
将该需求以教学辅助的方式分发给别人

ANG YU 5 vuotta sitten
vanhempi
commit
7f91a5db9e

+ 45 - 0
app/Console/Commands/UpdateOrderPackageExceptionTypeCountingRecord.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Services\LogService;
+use Illuminate\Console\Command;
+
+class UpdateOrderPackageExceptionTypeCountingRecord extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'updateOrderPackageExceptionTypeCountingRecord';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '更新order_package_exception_type_counting_records';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return int
+     */
+    public function handle()
+    {
+        LogService::log(__CLASS__,"更新order_package_exception_type_counting_records",'');
+        ini_set('memory_limit','2226M');
+        app('OrderPackageExceptionTypeCountingRecordService')->updateOrCreate(7);
+    }
+}

+ 1 - 0
app/Console/Kernel.php

@@ -72,6 +72,7 @@ class  Kernel extends ConsoleKernel
         $schedule->command('sync:batch')->everyMinute();
         $schedule->command('sync:order')->everyMinute();
         $schedule->command('syncOrderPackageLogisticRouteTask')->dailyAt('1:20');//同步快递信息到orderPackage
+        $schedule->command('updateOrderPackageExceptionTypeCountingRecord')->dailyAt('2:20');//更新OrderPackageExceptionTypeCountingRecord
         $schedule->command('SyncWmsCommoditiesInformation')->everyMinute();
         $schedule->command('clear:cancelledOrder')->everyTenMinutes();
         $schedule->command('WasSyncWmsAsnInformation')->everyMinute();

+ 41 - 0
app/Events/UpdateOrderPackageExceptionListenerEvent.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace App\Events;
+
+use Illuminate\Broadcasting\Channel;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Broadcasting\PresenceChannel;
+use Illuminate\Broadcasting\PrivateChannel;
+use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+class UpdateOrderPackageExceptionListenerEvent
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    /**
+     * @var $order_package_ids array
+     */
+    public $order_package_ids;
+
+    /**
+     * UpdateOrderPackageExceptionListenerEvent constructor.
+     * @param $order_package_ids
+     */
+    public function __construct($order_package_ids)
+    {
+        $this->order_package_ids = $order_package_ids;
+    }
+
+
+    /**
+     * Get the channels the event should broadcast on.
+     *
+     * @return \Illuminate\Broadcasting\Channel|array
+     */
+    public function broadcastOn()
+    {
+        return new PrivateChannel('channel-name');
+    }
+}

+ 15 - 1
app/Http/Controllers/ControlPanelController.php

@@ -160,6 +160,20 @@ class ControlPanelController extends Controller
         $this->success(["title"=>$title,"data"=>$data]);
     }
 
+    public function exceptionTypeApi(Request $request)
+    {
+        $ownerIds=$request->owner_ids;
+        if (!$ownerIds || in_array('all',$ownerIds)) $ownerIds = $this->getCountingOwnerIds(null);
+        $service = app('OrderPackageExceptionTypeCountingRecordService');
+
+        $data = $service->get([
+            'start_date' => $request->start,
+            'end_date' => $request->end,
+            'owner_ids' => $ownerIds,
+        ]);
+        $this->success(["title"=>'',"data"=>$data]);
+    }
+
     /**
      * 根据指定日期获取目标统计数据
      *
@@ -172,7 +186,7 @@ class ControlPanelController extends Controller
             $sql = <<<sql
 SELECT DATE_FORMAT(order_packages.created_at,'%Y-%m-%d') date,
 SUM(CASE WHEN order_packages.weighed_at IS NOT NULL THEN 1 ELSE 0 END) AS count,
-COUNT(1) total FROM order_packages LEFT JOIN orders ON order_packages.order_id=orders.id WHERE orders.wms_status != '订单取消' 
+COUNT(1) total FROM order_packages LEFT JOIN orders ON order_packages.order_id=orders.id WHERE orders.wms_status != '订单取消'
 AND order_packages.created_at >= '{$date} 00:00:00' GROUP BY date
 sql;
 

+ 6 - 0
app/Http/Controllers/PackageLogisticController.php

@@ -2,6 +2,7 @@
 
 namespace App\Http\Controllers;
 
+use App\Events\UpdateOrderPackageExceptionListenerEvent;
 use App\Filters\OrderPackageFilters;
 use App\Logistic;
 use App\OrderPackage;
@@ -45,6 +46,8 @@ class PackageLogisticController extends Controller
             $data['exception_type'] = $request->input('exception_type');
         }
         OrderPackage::query()->where('id', $request['id'])->update($data);
+        //更新统计数据
+        event(new UpdateOrderPackageExceptionListenerEvent([$request['id']]));
     }
 
     public function batchUpdate(Request $request)
@@ -57,5 +60,8 @@ class PackageLogisticController extends Controller
         }
         $logistic_numbers = $request->input('logistic_numbers');
         OrderPackage::query()->whereIn('logistic_number', $logistic_numbers)->update($data);
+        //更新统计数据
+        event(new UpdateOrderPackageExceptionListenerEvent($logistic_numbers));
+
     }
 }

+ 4 - 0
app/Listeners/AddOrUpdateOrderIssuesListener.php

@@ -3,6 +3,7 @@
 namespace App\Listeners;
 
 use App\Events\AddOrUpdateOrderIssues;
+use App\Events\UpdateOrderPackageExceptionListenerEvent;
 use App\OrderIssue;
 use App\OrderPackage;
 use Illuminate\Contracts\Queue\ShouldQueue;
@@ -36,5 +37,8 @@ class AddOrUpdateOrderIssuesListener implements ShouldQueue
                 'exception_type' => '其他',
                 'exception' => '是',
             ]);
+        //更新统计数据
+        $orderPackageIds = OrderPackage::query()->whereIn('order_id', $order_ids)->pluck('id');
+        event(new UpdateOrderPackageExceptionListenerEvent($orderPackageIds));
     }
 }

+ 5 - 1
app/Listeners/UpdateOrderPackageExceptionListener.php

@@ -3,6 +3,7 @@
 namespace App\Listeners;
 
 use App\Events\OrderIssueProcessLogCreateEvent;
+use App\Events\UpdateOrderPackageExceptionListenerEvent;
 use App\OrderPackage;
 use Illuminate\Contracts\Queue\ShouldQueue;
 use Illuminate\Queue\InteractsWithQueue;
@@ -37,10 +38,13 @@ class UpdateOrderPackageExceptionListener implements ShouldQueue
             default:
                 $status = '无';
         }
-        OrderPackage::query()->whereIn('id', $event->orderPackageIds)->update([
+        $orderPackageIds = $event->orderPackageIds;
+        OrderPackage::query()->whereIn('id', $orderPackageIds)->update([
             'exception_type' => '无',
             'exception' => '否',
             'status' => $status,
         ]);
+        //更新统计数据
+        event(new UpdateOrderPackageExceptionListenerEvent($orderPackageIds));
     }
 }

+ 42 - 0
app/Listeners/UpdateOrderPackageExceptionTypeCountingRecordListener.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace App\Listeners;
+
+use App\Events\UpdateOrderPackageExceptionListenerEvent;
+use App\OrderPackage;
+use App\Services\OrderPackageExceptionTypeCountingRecordService;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Support\Facades\DB;
+
+class UpdateOrderPackageExceptionTypeCountingRecordListener implements ShouldQueue
+{
+    /**
+     * Create the event listener.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        //
+    }
+
+    /**
+     * Handle the event.
+     *
+     * @param UpdateOrderPackageExceptionListenerEvent $event
+     * @return void
+     */
+    public function handle(UpdateOrderPackageExceptionListenerEvent $event)
+    {
+        $order_package_ids = $event->order_package_ids;
+        $dates = OrderPackage::query()->select(DB::raw("DATE_FORMAT( sent_at, '%Y-%m-%d' ) AS sent_at_date "))->whereIn('id', $order_package_ids)->get();
+        /**
+         * @var $service OrderPackageExceptionTypeCountingRecordService
+         */
+        $service = app('OrderPackageExceptionTypeCountingRecordService');
+        foreach ($dates as $date) {
+            $service->updateOrCreateByDate($date);
+        }
+    }
+}

+ 17 - 0
app/OrderPackageExceptionTypeCountingRecord.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+
+class OrderPackageExceptionTypeCountingRecord extends Model
+{
+    use ModelLogChanging;
+    public $timestamps = false;
+
+
+    //
+    public $fillable = ["sent_at_date", 'exception_type', 'exception_type_count', 'owner_id',];
+}

+ 7 - 4
app/Providers/EventServiceProvider.php

@@ -35,11 +35,14 @@ class EventServiceProvider extends ServiceProvider
         'App\Events\SendEmailEvent' => [
             'App\Listeners\SendEmailListener'
         ],
-        'App\Events\AddOrUpdateOrderIssues' => [
-            'App\Listeners\AddOrUpdateOrderIssuesListener',
+        'App\Events\AddOrUpdateOrderIssues' => [//问题件新增或更新
+            'App\Listeners\AddOrUpdateOrderIssuesListener',//将对应的order_packages的数据的异常装变更
         ],
-        'App\Events\OrderIssueProcessLogCreateEvent' => [
-            'App\Listeners\UpdateOrderPackageExceptionListener',
+        'App\Events\OrderIssueProcessLogCreateEvent' => [//orderIssue增添日志时
+            'App\Listeners\UpdateOrderPackageExceptionListener',//将对应的order_packages的数据的异常装变更为无
+        ],
+        'App\Events\UpdateOrderPackageExceptionListenerEvent' => [//order_packages的数据的异常数据变更时
+            'App\Listeners\UpdateOrderPackageExceptionTypeCountingRecordListener',//更新OrderPackageExceptionTypeCountingRecord的统计信息
         ],
     ];
 

+ 68 - 0
app/Services/OrderPackageExceptionTypeCountingRecordService.php

@@ -0,0 +1,68 @@
+<?php
+
+namespace App\Services;
+
+use App\Traits\ServiceAppAop;
+use App\OrderPackageExceptionTypeCountingRecord;
+use Carbon\Carbon;
+use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\DB;
+
+class OrderPackageExceptionTypeCountingRecordService
+{
+    use ServiceAppAop;
+
+    protected $modelClass = OrderPackageExceptionTypeCountingRecord::class;
+
+    /**
+     * 初始化一个月的统计数据
+     */
+    public function updateOrCreate($days)
+    {
+        DB::table("order_packages")
+            ->select(['exception_type',
+                DB::raw('count( * ) AS exception_type_count'),
+                DB::raw("DATE_FORMAT( sent_at, '%Y-%m-%d' ) AS sent_at_date "),
+                'owner_id',
+            ])
+            ->where('sent_at', '>=', now()->subDays($days)->startOfDay())
+            ->whereNotNull(['owner_id', 'sent_at'])
+            ->groupBy(['exception_type',
+                'sent_at_date',
+                'owner_id'])->orderBy('sent_at_date')->chunk(1000, function ($items) {
+                foreach ($items as $item) {
+                    OrderPackageExceptionTypeCountingRecord::query()->updateOrCreate((array)$item);
+                }
+            });
+    }
+
+    public function updateOrCreateByDate($date)
+    {
+        DB::table("order_packages")
+            ->select(['exception_type',
+                DB::raw('count( * ) AS exception_type_count'),
+                DB::raw("DATE_FORMAT( sent_at, '%Y-%m-%d' ) AS sent_at_date "),
+                'owner_id',
+            ])
+            ->where('sent_at', '>=', Carbon::parse($date)->startOfDay())
+            ->where('sent_at', '<=', Carbon::parse($date)->endOfDay())
+            ->whereNotNull(['owner_id', 'sent_at'])
+            ->groupBy(['exception_type',
+                'sent_at_date',
+                'owner_id'])->orderBy('sent_at_date')->chunk(1000, function ($items) {
+                foreach ($items as $item) {
+                    OrderPackageExceptionTypeCountingRecord::query()->updateOrCreate((array)$item);
+                }
+            });
+    }
+
+    public function get(array $kvPairs): ?Collection
+    {
+        return OrderPackageExceptionTypeCountingRecord::query()
+            ->select(['exception_type', DB::raw('sum(exception_type_count) as count')])
+            ->where('sent_at_date', '>=', Carbon::parse($kvPairs['start_date'])->startOfDay())
+            ->where('sent_at_date', '<=', Carbon::parse($kvPairs['end_date'])->endOfDay())
+            ->whereIn('owner_id', $kvPairs['owner_ids'])
+            ->groupBy('exception_type')->get();
+    }
+}

+ 12 - 0
database/factories/OrderPackageExceptionTypeCountingRecordFactory.php

@@ -0,0 +1,12 @@
+<?php
+
+/** @var \Illuminate\Database\Eloquent\Factory $factory */
+
+use App\OrderPackageExceptionTypeCountingRecord;
+use Faker\Generator as Faker;
+
+$factory->define(OrderPackageExceptionTypeCountingRecord::class, function (Faker $faker) {
+    return [
+        //
+    ];
+});

+ 32 - 0
database/migrations/2021_05_07_141019_add_index_exception_type_sent_at_owner_id_order_packages_table.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddIndexExceptionTypeSentAtOwnerIdOrderPackagesTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('order_packages', function (Blueprint $table) {
+            $table->index(['sent_at', 'exception_type', 'owner_id']);
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('order_packages', function (Blueprint $table) {
+            $table->dropIndex(['sent_at', 'exception_type', 'owner_id']);
+        });
+    }
+}

+ 35 - 0
database/migrations/2021_05_07_141912_create_order_package_exception_type_counting_records_table.php

@@ -0,0 +1,35 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateOrderPackageExceptionTypeCountingRecordsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('order_package_exception_type_counting_records', function (Blueprint $table) {
+            $table->id();
+            $table->date("sent_at_date")->comment("发出日期");
+            $table->string('exception_type')->comment('异常名称');
+            $table->integer('exception_type_count')->comment('异常类型的统计数量');
+            $table->unsignedBigInteger('owner_id')->comment('货主ID');
+            $table->index(['sent_at_date', 'exception_type_count', 'owner_id'],'order_packages_s_et_o_index');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('order_package_exception_type_counting_records');
+    }
+}

+ 16 - 0
database/seeds/OrderPackageExceptionTypeCountingRecordSeeder.php

@@ -0,0 +1,16 @@
+<?php
+
+use Illuminate\Database\Seeder;
+
+class OrderPackageExceptionTypeCountingRecordSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        //
+    }
+}

+ 93 - 0
resources/views/control/panel.blade.php

@@ -364,7 +364,35 @@
                     </div>
                 </div>
                 @endcan
+                <div class="col-6">
+                    <div class="card">
+                        <div class="card-header">
+                            <div class="flex-column">
+                                <el-date-picker size="small" class="col-6 date" @blur="loadExceptionTypeInfo()"
+                                                type="daterange" align="right"
+                                                v-model="searchOption.exceptionTypeDate" unlink-panels range-separator="-"
+                                                start-placeholder="开始日期" end-placeholder="结束日期"
+                                                value-format="yyyy-MM-dd">
+                                </el-date-picker>
+                                <el-select class="col-3"  placeholder="请选择对应货主" multiple v-model="selectExceptionTypeOwners" size="small" style="width: 20%"  @change="loadExceptionTypeInfo()">
+                                    <el-option label="选择所有" value="all"></el-option>
+                                    <el-option v-for="item in owners" :label="item.name" :value="item.id" :key="item.id"></el-option>
+                                </el-select>
+                                <label class="col-3 ">
+                                    <select class="form-control rounded" v-model="searchOption.exceptionTypeSelect"
+                                            @change="switchExceptionTypeDate()">
+                                        <option v-for="(date,i) in dateOptions" :value="i">@{{ date.text }}</option>
+                                    </select>
+                                </label>
+                            </div>
+                        </div>
+                        <div class="card-body row">
+                            <div id="exceptionType" class="col-12" style="min-height: 500px"></div>
+                        </div>
+                    </div>
+                </div>
             </div>
+
         </div>
     </div>
 @endsection
@@ -380,6 +408,7 @@
                 owners:{!! $owners !!},
                 selectOrderOwners: [],
                 selectLogisticsOwners: [],
+                selectExceptionTypeOwners: [],
                 warehousesOrders:{!! $warehousesOrders !!},
                 orderCountingRecords:{},
                 logisticsCountingRecords:{},
@@ -481,7 +510,9 @@
                 cardPool:{},
                 searchOption:{
                     weightDate:[],
+                    exceptionTypeDate: [],
                     weightSelect:"",
+                    exceptionTypeSelect:"",
                 },
             },
             watch:{
@@ -506,7 +537,9 @@
                 $('#list').removeClass('d-none');
                 let index = 4;
                 this.searchOption.weightSelect = index;
+                this.searchOption.exceptionTypeSelect = index;
                 this.searchOption.weightDate = [this.dateOptions[index].start, this.dateOptions[index].end];
+                this.searchOption.exceptionTypeDate = [this.dateOptions[index].start, this.dateOptions[index].end];
                 let _this = this;
                 this.warehousesOrders.forEach(function (item) {
                     _this.totalOrders.total += parseInt(item.total);
@@ -543,6 +576,8 @@
                 this.cardPool.weight = echarts.init(document.getElementById("weight"));
                 this.loadWeightInfo();
                 @endcan
+                this.cardPool.exceptionType = echarts.init(document.getElementById("exceptionType"));
+                this.loadExceptionTypeInfo();
             },
             methods: {
                 switchDataPanel_forOrderCountingRecords(fromUnit, toUnit) {
@@ -898,11 +933,34 @@
                         this.cardPool.weight.setOption(this._setWeightData(res.title,res.data));
                     });
                 },
+                loadExceptionTypeInfo() {
+                    window.tempTip.setDuration(3000);
+                    if (!this.searchOption.exceptionTypeDate[0]){
+                        window.tempTip.show("开始时间未选择");
+                        return;
+                    }
+                    if (!this.searchOption.exceptionTypeDate[1]){
+                        window.tempTip.show("结束时间未选择");
+                        return;
+                    }
+                    this.cardPool.exceptionType.showLoading('default',{text:"加 载 中",color:'#C0C0C0'});
+                    let url = "{{url('apiLocal/control/panel/menu/exceptionTypeApi')}}";
+                    let params = {start:this.searchOption.exceptionTypeDate[0],end:this.searchOption.exceptionTypeDate[1],owner_ids:this.selectExceptionTypeOwners};
+                    window.tempTip.postBasicRequest(url,params,res=>{
+                        this.cardPool.exceptionType.hideLoading();
+                        this.cardPool.exceptionType.setOption(this._setExceptionTypeData(res.data));
+                    });
+                },
                 switchWeightDate(){
                     let obj = this.dateOptions[this.searchOption.weightSelect];
                     this.searchOption.weightDate = [obj.start,obj.end];
                     this.loadWeightInfo();
                 },
+                switchExceptionTypeDate(){
+                    let obj = this.dateOptions[this.searchOption.exceptionTypeSelect];
+                    this.searchOption.exceptionTypeDate = [obj.start,obj.end];
+                    this.loadExceptionTypeInfo();
+                },
                 _setWeightData(title, data){
                     return {
                         title: {
@@ -937,6 +995,41 @@
                         }]
                     };
                 },
+                _setExceptionTypeData(data) {
+                    let resData = [];
+                    data.forEach(item => {
+                        resData.push({
+                            value:item.count,
+                            name:item.exception_type
+                        })
+                    })
+                    return {
+                        title: {
+                            text: '异常分布',
+                            left: 'left'
+                        },
+                        tooltip: {
+                            trigger: 'item',
+                            formatter: '{a} <br/>{b} : {c} ({d}%)'
+                        },
+                        series: [
+                            {
+                                name: '异常分布',
+                                type: 'pie',
+                                radius: '55%',
+                                center: ['50%', '60%'],
+                                data: resData,
+                                emphasis: {
+                                    itemStyle: {
+                                        shadowBlur: 10,
+                                        shadowOffsetX: 0,
+                                        shadowColor: 'rgba(0, 0, 0, 0.5)'
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                }
             }
         });
     </script>

+ 1 - 0
routes/apiLocal.php

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