소스 검색

Merge remote-tracking branch 'origin/liuzj-06-29' into stable

# Conflicts:
#	modules/Statistic/routes/route.php
zqwang 1 년 전
부모
커밋
6e27e02865
49개의 변경된 파일1539개의 추가작업 그리고 194개의 파일을 삭제
  1. 11 2
      app/Console/Commands/ContentManage/ContentTest.php
  2. 13 14
      app/Console/Commands/Statistic/CompanyDayCharge.php
  3. 12 15
      app/Console/Commands/Statistic/CompanyMonthCharge.php
  4. 6 5
      app/Console/Commands/Statistic/OptimizerMonthCharge.php
  5. 99 10
      app/Console/Commands/Statistic/PromotionDayCharge.php
  6. 0 62
      app/Console/Commands/Video/WechatCheckGetTask.php
  7. 2 1
      composer.json
  8. 1 1
      config/cache.php
  9. 1 1
      config/database.php
  10. 2 0
      modules/Channel/Http/Controllers/AdvertiserController.php
  11. 29 0
      modules/Channel/Http/Controllers/CompanyUserController.php
  12. 133 0
      modules/Channel/Http/Controllers/WechatOpenPlatformController.php
  13. 12 0
      modules/Channel/Models/WechatAuthorizationInfo.php
  14. 76 0
      modules/Channel/Services/WechatOpenPlatform/WechatOpenPlatformService.php
  15. 28 0
      modules/Channel/routes/route.php
  16. 7 3
      modules/Common/Errors/Errors.php
  17. 61 0
      modules/Common/Support/Http/HttpRequest.php
  18. 13 0
      modules/Common/Support/Http/WechatURL.php
  19. 34 0
      modules/Common/Support/Wechat/Crypt/ErrorCode.php
  20. 52 0
      modules/Common/Support/Wechat/Crypt/PKCS7Encoder.php
  21. 103 0
      modules/Common/Support/Wechat/Crypt/Prpcrypt.php
  22. 4 0
      modules/Common/Support/Wechat/Crypt/ReadMe.txt
  23. 36 0
      modules/Common/Support/Wechat/Crypt/SHA1.php
  24. 145 0
      modules/Common/Support/Wechat/Crypt/WXBizMsgCrypt.php
  25. 52 0
      modules/Common/Support/Wechat/Crypt/XMLParse.php
  26. 41 0
      modules/Common/Support/Wechat/Crypt/demo.php
  27. 25 0
      modules/Common/Support/Wechat/OpenPlatform/OPApplication.php
  28. 23 8
      modules/Statistic/Http/Controllers/ChargeTJController.php
  29. 156 0
      modules/Statistic/Http/Controllers/ROITJController.php
  30. 8 0
      modules/Statistic/routes/route.php
  31. 3 1
      modules/Tuiguang/Http/Controllers/PromotionController.php
  32. 18 1
      modules/Video/Http/Controllers/EpisodeController.php
  33. 45 4
      modules/Video/Http/Controllers/VideoSeriesWechatCheckController.php
  34. 60 8
      modules/Video/Http/Controllers/WechatCheckController.php
  35. 9 47
      modules/Video/Services/WechatCheckSyncService.php
  36. 18 0
      modules/Video/config/wechat.php
  37. 5 0
      modules/Video/routes/route.php
  38. 8 0
      resources/views/wechat/openPlatform/authSuccess.blade.php
  39. 1 1
      tests/Channel/Http/Controllers/AdvertiserControllerTest.php
  40. 19 0
      tests/Channel/Http/Controllers/CompanyUserControllerTest.php
  41. 28 0
      tests/Channel/Http/Controllers/WechatOpenPlatformControllerTest.php
  42. 54 0
      tests/Common/Support/Wechat/Crypt/WXBizMsgCryptTest.php
  43. 19 0
      tests/Console/Commands/Statistic/PromotionDayChargeTest.php
  44. 46 0
      tests/Statistic/Http/Controllers/ROITJControllerTest.php
  45. 3 1
      tests/Tuiguang/Http/Controllers/PromotionControllerTest.php
  46. 1 1
      tests/UsedTestCase.php
  47. 3 3
      tests/Video/Http/Controllers/EpisodeControllerTest.php
  48. 9 1
      tests/Video/Http/Controllers/VideoSeriesWechatCheckControllerTest.php
  49. 5 4
      tests/Video/Http/Controllers/WechatCheckControllerTest.php

+ 11 - 2
app/Console/Commands/ContentManage/ContentTest.php

@@ -4,6 +4,7 @@ namespace App\Console\Commands\ContentManage;
 
 
 use http\Exception\RuntimeException;
 use http\Exception\RuntimeException;
 use Illuminate\Console\Command;
 use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Redis;
 use Illuminate\Support\Facades\Redis;
 use Modules\Common\Errors\Errors;
 use Modules\Common\Errors\Errors;
 use Modules\Common\Exceptions\CommonBusinessException;
 use Modules\Common\Exceptions\CommonBusinessException;
@@ -29,7 +30,15 @@ class ContentTest extends Command
      */
      */
     public function handle(): void
     public function handle(): void
     {
     {
-       myLog('kkkkk')->info(date('Y-m-d H:i:s'));
-       throw new RuntimeException('kljkjljkjkl');
+       $result = DB::table('promotions')
+           ->where(['puid' => 0])
+           ->get();
+       foreach ($result as $item) {
+           DB::table('promotions')
+               ->where('id', $item->id)
+               ->update([
+                   'puid' => DB::table('users')->where('id', $item->uid)->value('pid')
+               ]);
+       }
     }
     }
 }
 }

+ 13 - 14
app/Console/Commands/Statistic/CompanyDayCharge.php

@@ -49,20 +49,19 @@ class CompanyDayCharge extends Command
                     DB::raw("sum(if(status <> 'unpaid' and order_type not in ('coin', 'first_coin'), 1, 0)) as vip_pay_count"),
                     DB::raw("sum(if(status <> 'unpaid' and order_type not in ('coin', 'first_coin'), 1, 0)) as vip_pay_count"),
                     DB::raw("sum(if(status = 'unpaid' and order_type not in ('coin', 'first_coin'), 1, 0)) as vip_unpay_count"),
                     DB::raw("sum(if(status = 'unpaid' and order_type not in ('coin', 'first_coin'), 1, 0)) as vip_unpay_count"),
                 )->first();
                 )->first();
-            if($info) {
-                DB::table('tj_company_day_charge')
-                    ->insert([
-                        'day_at' => $date,
-                        'pay_money' => $info->pay_money,
-                        'common_pay_count' => $info->common_pay_count,
-                        'common_unpay_count' => $info->common_unpay_count,
-                        'vip_pay_count' => $info->vip_pay_count,
-                        'vip_unpay_count' => $info->vip_unpay_count,
-                        'company_uid' => $companyUid,
-                        'created_at' => $now,
-                        'updated_at' => $now,
-                    ]);
-            }
+            DB::table('tj_company_day_charge')
+                ->insert([
+                    'day_at' => $date,
+                    'pay_money' => $info->pay_money ?? 0,
+                    'common_pay_count' => $info->common_pay_count ?? 0,
+                    'common_unpay_count' => $info->common_unpay_count ?? 0,
+                    'vip_pay_count' => $info->vip_pay_count ?? 0,
+                    'vip_unpay_count' => $info->vip_unpay_count ?? 0,
+                    'company_uid' => $companyUid,
+                    'created_at' => $now,
+                    'updated_at' => $now,
+                ]);
+
         }
         }
 
 
     }
     }

+ 12 - 15
app/Console/Commands/Statistic/CompanyMonthCharge.php

@@ -54,21 +54,18 @@ class CompanyMonthCharge extends Command
                     DB::raw("sum(vip_unpay_count) as vip_unpay_count"),
                     DB::raw("sum(vip_unpay_count) as vip_unpay_count"),
                     DB::raw("sum(vip_pay_count) as vip_pay_count"),
                     DB::raw("sum(vip_pay_count) as vip_pay_count"),
                 )->first();
                 )->first();
-            $now = date('Y-m-d H:i:s');
-            if($info) {
-                DB::table('tj_company_month_charge')
-                    ->insert([
-                        'month_at' => $month,
-                        'pay_money' => $info->pay_money,
-                        'common_pay_count' => $info->common_pay_count,
-                        'common_unpay_count' => $info->common_unpay_count,
-                        'vip_pay_count' => $info->vip_pay_count,
-                        'vip_unpay_count' => $info->vip_unpay_count,
-                        'company_uid' => $companyUid,
-                        'created_at' => $now,
-                        'updated_at' => $now,
-                    ]);
-            }
+            DB::table('tj_company_month_charge')
+                ->insert([
+                    'month_at' => $month,
+                    'pay_money' => $info->pay_money ?? 0,
+                    'common_pay_count' => $info->common_pay_count ?? 0,
+                    'common_unpay_count' => $info->common_unpay_count ?? 0,
+                    'vip_pay_count' => $info->vip_pay_count ?? 0,
+                    'vip_unpay_count' => $info->vip_unpay_count ?? 0,
+                    'company_uid' => $companyUid,
+                    'created_at' => $now,
+                    'updated_at' => $now,
+                ]);
         }
         }
     }
     }
 }
 }

+ 6 - 5
app/Console/Commands/Statistic/OptimizerMonthCharge.php

@@ -59,7 +59,12 @@ class OptimizerMonthCharge extends Command
                 ->get();
                 ->get();
             $insertData = [];
             $insertData = [];
             foreach ($result as $item) {
             foreach ($result as $item) {
-                $info = (array)$item;
+                $info['pay_money'] = $item->pay_money ?? 0;
+                $info['common_pay_count'] = $item->common_pay_count ?? 0;
+                $info['common_unpay_count'] = $item->common_unpay_count ?? 0;
+                $info['vip_unpay_count'] = $item->vip_unpay_count ?? 0;
+                $info['vip_pay_count'] = $item->vip_pay_count ?? 0;
+                $info['miniprogram_id'] = $item->miniprogram_id ?? 0;
                 $info['created_at'] = $info['updated_at'] = $now;
                 $info['created_at'] = $info['updated_at'] = $now;
                 $info['month_at'] = $month;
                 $info['month_at'] = $month;
                 $info['user_id'] = $userId;
                 $info['user_id'] = $userId;
@@ -70,9 +75,5 @@ class OptimizerMonthCharge extends Command
             DB::table('tj_optimizer_month_charge')
             DB::table('tj_optimizer_month_charge')
                 ->insert($insertData);
                 ->insert($insertData);
         }
         }
-
-
-        DB::table('tj_optimizer_month_charge')
-            ->insert($insertData);
     }
     }
 }
 }

+ 99 - 10
app/Console/Commands/Statistic/PromotionDayCharge.php

@@ -5,6 +5,7 @@ namespace App\Console\Commands\Statistic;
 use Illuminate\Console\Command;
 use Illuminate\Console\Command;
 use Illuminate\Support\Arr;
 use Illuminate\Support\Arr;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Redis;
 
 
 class PromotionDayCharge extends Command
 class PromotionDayCharge extends Command
 {
 {
@@ -53,6 +54,7 @@ class PromotionDayCharge extends Command
                     $promotionDayCharge = $this->promotionDayCharge($item->promotion_id, $date);
                     $promotionDayCharge = $this->promotionDayCharge($item->promotion_id, $date);
                     $newUserCharge = $this->newUserCharge($item->promotion_id, $date);
                     $newUserCharge = $this->newUserCharge($item->promotion_id, $date);
                     $chargeInfo = $this->getPromotionData($promotionDayCharge, $newUserCharge);
                     $chargeInfo = $this->getPromotionData($promotionDayCharge, $newUserCharge);
+                    $chargeInfo['new_user_num'] = $this->getNewUserNum($item->promotion_id, $date);
                     $promotionData[] = array_merge( $chargeInfo, [
                     $promotionData[] = array_merge( $chargeInfo, [
                         'promotion_id' => $item->promotion_id, 'day_at' => $date,
                         'promotion_id' => $item->promotion_id, 'day_at' => $date,
                         'created_at' => $now, 'updated_at' => $now,
                         'created_at' => $now, 'updated_at' => $now,
@@ -79,15 +81,80 @@ class PromotionDayCharge extends Command
             DB::table('tj_optimizer_day_charge')
             DB::table('tj_optimizer_day_charge')
                 ->insert($items->values()->toArray());
                 ->insert($items->values()->toArray());
         }
         }
+
+
+        DB::table('promotions')
+            ->leftJoin('users', 'users.id', '=' , 'promotions.uid')
+            ->select('promotions.id', 'promotions.uid', 'promotions.miniprogram_id', 'users.pid as puid')
+            ->orderBy('promotions.id')
+            ->chunk(100, function ($items) use ($date){
+                $promotionIds = $items->pluck('id');
+                $now = date('Y-m-d H:i:s');
+                $alreadyExistsPromotionIds = DB::table('tj_promotion_day_charge')
+                    ->where('day_at', $date)
+                    ->whereIn('promotion_id', $promotionIds)
+                    ->select('promotion_id')
+                    ->get()->pluck('promotion_id');
+                foreach ($items as $item) {
+                    if($alreadyExistsPromotionIds->contains($item->id)) {
+                        continue;
+                    }
+                    DB::table('tj_promotion_day_charge')
+                        ->insert([
+                            'promotion_id' => $item->id, 'day_at' => $date,
+                            'created_at' => $now, 'updated_at' => $now,
+                            'user_id' => $item->uid, 'puser_id' => $item->puid,
+                            'miniprogram_id' => $item->miniprogram_id
+                        ]);
+                }
+            });
+
+        DB::table('users')
+            ->join('user_has_roles', 'users.id', 'user_has_roles.user_id')
+            ->where(['user_has_roles.role_id' => 2, 'users.deleted_at' => 0])
+            ->distinct()
+            ->select('users.id', 'users.pid')
+            ->orderBy('users.id')
+            ->chunk(50, function ($items) use($date) {
+                $now = date('Y-m-d H:i:s');
+                foreach ($items as $item) {
+                    $miniprogramIds = DB::table('user_has_miniprograms')
+                        ->where('uid', $item->id)
+                        ->where('is_enabled', 1)
+                        ->select('miniprogram_id')
+                        ->get()->pluck('miniprogram_id');
+                    foreach ($miniprogramIds as $miniprogramId) {
+                        if(DB::table('tj_optimizer_day_charge')
+                            ->where([
+                                'day_at' => $date,
+                                'user_id' => $item->id, 'miniprogram_id' => $miniprogramId,
+                            ])->exists()) {
+                            continue;
+                        }
+                        DB::table('tj_optimizer_day_charge')
+                            ->insert([
+                                'day_at' => $date, 'user_id' => $item->id, 'miniprogram_id' => $miniprogramId,
+                                'puser_id' => $item->pid, 'created_at' => $now, 'updated_at' => $now
+                            ]);
+                    }
+                }
+            });
     }
     }
 
 
-    public function getPromotionData($promotionDayCharge, $newUserCharge, ) {
+    /**
+     * 某个推广链接在某天的新用户数量
+     * @param $promotionId 推广链接id
+     * @param $date  日期
+     * @return mixed
+     */
+    private function getNewUserNum($promotionId, $date) {
+        return intval(Redis::hget(sprintf('promotion:newUserCount:%s', $date), $promotionId));
+    }
+
+    public function getPromotionData($promotionDayCharge, $newUserCharge) {
         return [
         return [
             'pay_money' => $promotionDayCharge['pay_money'] ?? 0,
             'pay_money' => $promotionDayCharge['pay_money'] ?? 0,
             'pay_count' => $promotionDayCharge['pay_count'] ?? 0,
             'pay_count' => $promotionDayCharge['pay_count'] ?? 0,
-            'new_user_pay_money' => $newUserCharge['new_user_pay_money'] ?? 0,
-            'new_user_common_pay_money' => $newUserCharge['new_user_common_pay_money'] ?? 0,
-            'new_user_vip_pay_money' => $newUserCharge['new_user_vip_pay_money'] ?? 0,
             'common_pay_money' => $promotionDayCharge['common_pay_money'] ?? 0,
             'common_pay_money' => $promotionDayCharge['common_pay_money'] ?? 0,
             'common_pay_uv' => $promotionDayCharge['common_pay_uv'] ?? 0,
             'common_pay_uv' => $promotionDayCharge['common_pay_uv'] ?? 0,
             'common_pay_count' => $promotionDayCharge['common_pay_count'] ?? 0,
             'common_pay_count' => $promotionDayCharge['common_pay_count'] ?? 0,
@@ -96,6 +163,12 @@ class PromotionDayCharge extends Command
             'vip_pay_uv' => $promotionDayCharge['vip_pay_uv'] ?? 0,
             'vip_pay_uv' => $promotionDayCharge['vip_pay_uv'] ?? 0,
             'vip_pay_count' => $promotionDayCharge['vip_pay_count'] ?? 0,
             'vip_pay_count' => $promotionDayCharge['vip_pay_count'] ?? 0,
             'vip_unpay_count' => $promotionDayCharge['vip_unpay_count'] ?? 0,
             'vip_unpay_count' => $promotionDayCharge['vip_unpay_count'] ?? 0,
+            'new_user_pay_money' => $newUserCharge['new_user_pay_money'] ?? 0,
+            'new_user_common_pay_money' => $newUserCharge['new_user_common_pay_money'] ?? 0,
+            'new_user_vip_pay_money' => $newUserCharge['new_user_vip_pay_money'] ?? 0,
+            'new_user_pay_uv' => $newUserCharge['new_user_pay_uv'] ?? 0,
+            'new_user_vip_pay_uv' => $newUserCharge['new_user_vip_pay_uv'] ?? 0,
+            'new_user_common_pay_uv' =>$newUserCharge['new_user_common_pay_uv'] ?? 0,
         ];
         ];
     }
     }
 
 
@@ -109,15 +182,27 @@ class PromotionDayCharge extends Command
                 DB::raw("sum(if(status <> 'unpaid', price, 0)) as pay_money"),
                 DB::raw("sum(if(status <> 'unpaid', price, 0)) as pay_money"),
                 // 普通支付金额
                 // 普通支付金额
                 DB::raw("sum(if(status <> 'unpaid' and order_type in ('coin', 'first_coin'), price, 0)) as common_pay_money"),
                 DB::raw("sum(if(status <> 'unpaid' and order_type in ('coin', 'first_coin'), price, 0)) as common_pay_money"),
+                // 累计充值人数
+                DB::raw("count(distinct if(status <> 'unpaid', concat(uid, ranse_created_at), null)) as pay_uv"),
+                // 普通支付人数
+                DB::raw("count(distinct if(order_type in ('coin', 'first_coin') and status <> 'unpaid', concat(uid, ranse_created_at), null)) as common_pay_uv"),
+                // vip支付人数
+                DB::raw("count(distinct if(order_type not in ('coin', 'first_coin') and status <> 'unpaid', concat(uid, ranse_created_at), null)) as vip_pay_uv"),
             )->first();
             )->first();
         if($info) {
         if($info) {
             return [
             return [
-                // 新用户支付总额
+                // 新用户支付总额
                 'new_user_pay_money' => $info->pay_money,
                 'new_user_pay_money' => $info->pay_money,
-                // 新用户普通支付总额
+                // 新用户普通支付总额
                 'new_user_common_pay_money' => $info->common_pay_money,
                 'new_user_common_pay_money' => $info->common_pay_money,
-                // 新用户会员支付总额
-                'new_user_vip_pay_money' => $info->pay_money - $info->common_pay_money
+                // 新增用户会员支付总额
+                'new_user_vip_pay_money' => $info->pay_money - $info->common_pay_money,
+                // 新增用户在{$date}充值人数
+                'new_user_pay_uv' => $info->pay_uv,
+                // 新增用户在{$date}vip充值支付人数
+                'new_user_vip_pay_uv' => $info->vip_pay_uv,
+                // 新增用户在{$date}普通充值支付人数
+                'new_user_common_pay_uv' => $info->vip_pay_uv,
             ];
             ];
         } else {
         } else {
             return null;
             return null;
@@ -128,6 +213,7 @@ class PromotionDayCharge extends Command
         $info = DB::table('orders')
         $info = DB::table('orders')
             ->where('promotion_id', $promotionId)
             ->where('promotion_id', $promotionId)
             ->whereBetween('created_at', [$date, $date. ' 23:59:59'])
             ->whereBetween('created_at', [$date, $date. ' 23:59:59'])
+            ->where('ranse_created_at', '>', '2000-01-01')
             ->select(
             ->select(
                 // 未支付金额
                 // 未支付金额
                 DB::raw("sum(if(status = 'unpaid', price, 0)) as unpay_money"),
                 DB::raw("sum(if(status = 'unpaid', price, 0)) as unpay_money"),
@@ -143,10 +229,13 @@ class PromotionDayCharge extends Command
                 DB::raw("sum(if(order_type in ('coin', 'first_coin'), 1,  0)) as common_count"),
                 DB::raw("sum(if(order_type in ('coin', 'first_coin'), 1,  0)) as common_count"),
                 // 普通支付金额
                 // 普通支付金额
                 DB::raw("sum(if(order_type in ('coin', 'first_coin') and status <> 'unpaid', price, 0)) as common_pay_money"),
                 DB::raw("sum(if(order_type in ('coin', 'first_coin') and status <> 'unpaid', price, 0)) as common_pay_money"),
+                // NOTE!!!,uid, ranse_created_at 唯一确定一个用户
+                // 累计充值人数
+                DB::raw("count(distinct if(status <> 'unpaid', concat(uid, ranse_created_at), null)) as pay_uv"),
                 // 普通支付人数
                 // 普通支付人数
-                DB::raw("count(distinct if(order_type in ('coin', 'first_coin') and status <> 'unpaid', uid, null)) as common_pay_uv"),
+                DB::raw("count(distinct if(order_type in ('coin', 'first_coin') and status <> 'unpaid', concat(uid, ranse_created_at), null)) as common_pay_uv"),
                 // vip支付人数
                 // vip支付人数
-                DB::raw("count(distinct if(order_type not in ('coin', 'first_coin') and status <> 'unpaid', uid, null)) as vip_pay_uv"),
+                DB::raw("count(distinct if(order_type not in ('coin', 'first_coin') and status <> 'unpaid', concat(uid, ranse_created_at), null)) as vip_pay_uv"),
                 // vip未支付笔数
                 // vip未支付笔数
                 DB::raw("sum(if(order_type not in ('coin', 'first_coin') and status = 'unpaid', 1,  0)) as vip_unpay_count"),
                 DB::raw("sum(if(order_type not in ('coin', 'first_coin') and status = 'unpaid', 1,  0)) as vip_unpay_count"),
             )->first();
             )->first();

+ 0 - 62
app/Console/Commands/Video/WechatCheckGetTask.php

@@ -1,62 +0,0 @@
-<?php
-
-namespace App\Console\Commands\Video;
-
-use Illuminate\Console\Command;
-use Illuminate\Support\Facades\DB;
-use Modules\Video\Services\WechatCheckSyncService;
-
-class WechatCheckGetTask extends Command
-{
-    /**
-     * The name and signature of the console command.
-     *
-     * @var string
-     */
-    protected $signature = 'Video:WechatCheckGetTask {--task_ids= : 英文逗号分割的任务id}';
-
-    /**
-     * The console command description.
-     *
-     * @var string
-     */
-    protected $description = '查询短剧同步到微信的任务状态';
-
-    /**
-     * Execute the console command.
-     */
-    public function handle(): void
-    {
-        $task_ids = $this->option('task_ids');
-        $taskIdArr = null;
-        if($task_ids) {
-            $taskIdArr = explode(',', trim($task_ids, ','));
-        }
-        DB::table('video_series_wechat_check')
-            ->whereIn('sync_status', [1,2])
-            ->where('sync_task_id', '<>', '')
-            ->where('is_enabled', 1)
-            ->when($taskIdArr, function ($query, $taskIdArr) {
-                return $query->whereIn('task_id', $taskIdArr);
-            })->orderBy('id', 'asc')
-            ->chunk(100, function ($items) {
-                $now = date('Y-m-d H:i:s');
-                foreach ($items as $item) {
-                    $taskInfo = WechatCheckSyncService::getTask($item);
-                    if($taskInfo && 1 == $taskInfo['task_type']) {
-                        if(in_array($taskInfo['status'], [3,4])) {
-                            DB::table('video_series_wechat_check')
-                                ->where(['id' => $item->id])
-                                ->update([
-                                    'status' => $taskInfo['status'],
-                                    'remark' => $taskInfo['errmsg'] ?? '',
-                                    'media_id' => $taskInfo['media_id'] ?? '',
-                                    'updated_at' => $now,
-                                    'sync_task_info' => \json_encode($taskInfo),
-                                ]);
-                        }
-                    }
-                }
-            });
-    }
-}

+ 2 - 1
composer.json

@@ -21,7 +21,8 @@
         "phpoffice/phpspreadsheet": "^1.28",
         "phpoffice/phpspreadsheet": "^1.28",
         "predis/predis": "^2.1",
         "predis/predis": "^2.1",
         "qiniu/php-sdk": "^7.4",
         "qiniu/php-sdk": "^7.4",
-        "vinkla/hashids": "*"
+        "vinkla/hashids": "*",
+        "w7corp/easywechat": "^6.12"
     },
     },
     "require-dev": {
     "require-dev": {
         "fakerphp/faker": "^1.9.1",
         "fakerphp/faker": "^1.9.1",

+ 1 - 1
config/cache.php

@@ -15,7 +15,7 @@ return [
     |
     |
     */
     */
 
 
-    'default' => env('CACHE_DRIVER', 'file'),
+    'default' => env('CACHE_DRIVER', 'redis'),
 
 
     /*
     /*
     |--------------------------------------------------------------------------
     |--------------------------------------------------------------------------

+ 1 - 1
config/database.php

@@ -143,7 +143,7 @@ return [
             'username' => env('REDIS_USERNAME'),
             'username' => env('REDIS_USERNAME'),
             'password' => env('REDIS_PASSWORD'),
             'password' => env('REDIS_PASSWORD'),
             'port' => env('REDIS_PORT', '6379'),
             'port' => env('REDIS_PORT', '6379'),
-            'database' => env('REDIS_CACHE_DB', '1'),
+            'database' => env('REDIS_CACHE_DB', '0'),
         ],
         ],
         // 普通redis队列
         // 普通redis队列
         'queue-redis' => [
         'queue-redis' => [

+ 2 - 0
modules/Channel/Http/Controllers/AdvertiserController.php

@@ -75,6 +75,7 @@ class AdvertiserController extends CatchController
         $userContext = $this->getUserContext(null);
         $userContext = $this->getUserContext(null);
         $res =   DB::table('users')
         $res =   DB::table('users')
             ->leftJoin('user_has_miniprograms', 'users.id', 'user_has_miniprograms.uid')
             ->leftJoin('user_has_miniprograms', 'users.id', 'user_has_miniprograms.uid')
+            ->leftJoin('wechat_authorization_infos as author', 'author.user_id', 'users.id')
             ->where([
             ->where([
                 'users.deleted_at' => 0,
                 'users.deleted_at' => 0,
             ])->where('users.pid', '<>', 0)
             ])->where('users.pid', '<>', 0)
@@ -95,6 +96,7 @@ class AdvertiserController extends CatchController
                 'users.id', 'users.username', 'users.email', 'users.status', 'users.remark',
                 'users.id', 'users.username', 'users.email', 'users.status', 'users.remark',
                 DB::raw("from_unixtime(users.created_at) as created_at"),
                 DB::raw("from_unixtime(users.created_at) as created_at"),
                 DB::raw("group_concat(distinct if(user_has_miniprograms.is_enabled = 1, user_has_miniprograms.miniprogram_id, null)  separator ',') as miniProgramIds"),
                 DB::raw("group_concat(distinct if(user_has_miniprograms.is_enabled = 1, user_has_miniprograms.miniprogram_id, null)  separator ',') as miniProgramIds"),
+                DB::raw("group_concat(distinct author.nick_name  separator ',') as gzh_names"),
                 DB::raw("NULL as miniPrograms")
                 DB::raw("NULL as miniPrograms")
             )->groupBy('users.id')
             )->groupBy('users.id')
             ->orderBy('users.id','desc')
             ->orderBy('users.id','desc')

+ 29 - 0
modules/Channel/Http/Controllers/CompanyUserController.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace Modules\Channel\Http\Controllers;
+
+use Catch\Base\CatchController;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+
+class CompanyUserController extends CatchController
+{
+    /**
+     * 商户列表
+     * @param Request $request
+     */
+    public function list(Request $request) {
+        $username = $request->input('username');
+        return DB::table('users')
+            ->join('user_has_roles','users.id', 'user_has_roles.user_id')
+            ->when($username, function ($query, $username){
+                return $query->where('users.username', 'like', '%'. $username. '%');
+            })
+            ->where([
+                'user_has_roles.role_id' => 1,
+                'users.status' => 1,
+                'users.deleted_at' => 0
+            ])->select('users.username', 'users.id')
+            ->get();
+    }
+}

+ 133 - 0
modules/Channel/Http/Controllers/WechatOpenPlatformController.php

@@ -0,0 +1,133 @@
+<?php
+
+namespace Modules\Channel\Http\Controllers;
+
+use Catch\Base\CatchController;
+use EasyWeChat\OpenPlatform\Application;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Redis;
+use Modules\Channel\Models\WechatAuthorizationInfo;
+use Modules\Channel\Services\WechatOpenPlatform\WechatOpenPlatformService;
+use Modules\Common\Errors\Errors;
+use Modules\Common\Exceptions\CommonBusinessException;
+use Modules\User\Http\Controllers\UserTrait;
+use Symfony\Component\Cache\Adapter\RedisAdapter;
+
+class WechatOpenPlatformController extends CatchController
+{
+    use UserTrait;
+    use ValidatesRequests;
+
+    /**
+     * 三方授权跳转页
+     * @param Request $request
+     * @return string
+     * @throws \EasyWeChat\Kernel\Exceptions\HttpException
+     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
+     * @throws \Illuminate\Validation\ValidationException
+     * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
+     * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
+     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
+     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
+     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
+     */
+    public function preauth(Request $request) {
+        $this->validate($request, [
+            'user_id' => 'required'
+        ]);
+        $currentUser = $this->getCurrentUser();
+        $componentInfo = WechatOpenPlatformService::getComponentInfoByCompanyUid($currentUser->id);
+        $app = WechatOpenPlatformService::buildApplication($componentInfo);
+        $user_id = $request->input('user_id');
+        $url = $app->createPreAuthorizationUrl(sprintf('%s/api/channel/openPlatform/auth/%s/%s',config('app.url'), $componentInfo->app_id, $user_id), []);
+        return $url;
+    }
+
+    /**
+     * 三方授权回调
+     * @param Request $request
+     * @param $component_appid
+     * @param $user_id
+     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application
+     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
+     * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
+     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
+     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
+     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
+     */
+    public function auth(Request $request, $component_appid, $user_id) {
+        $auth_code = $request->input('auth_code');
+        $auth_user = DB::table('users')
+            ->where([
+                'id' => $user_id, 'deleted_at' => 0, 'status' => 1
+            ])
+            ->where('pid', '<>', 0)
+            ->select('pid', 'id')->first();
+        if(!$auth_user) {
+            CommonBusinessException::throwError(Errors::OPENPLATFORM_OPTIMIZER_INFO_ERROR);
+        }
+        $componentInfo = WechatOpenPlatformService::getComponentInfoByAppid($component_appid);
+        $app = WechatOpenPlatformService::buildApplication($componentInfo);
+        $client = $app->getClient();
+        $response = $client->postJson('/cgi-bin/component/api_query_auth', [
+            'component_appid' => $component_appid,
+            'authorization_code' => $auth_code
+        ]);
+        $parsedContent = \json_decode($response->getContent(), true);
+        $authorizer_appid = $parsedContent['authorization_info']['authorizer_appid'];
+        $authorizer_refresh_token = $parsedContent['authorization_info']['authorizer_refresh_token'];
+
+        $response = $client->postJson('/cgi-bin/component/api_get_authorizer_info', [
+            'component_appid' => $component_appid,
+            'authorizer_appid' => $authorizer_appid
+        ]);
+        $parsedContent = \json_decode($response->getContent(), true);
+
+        WechatAuthorizationInfo::updateOrCreate([
+            'authorizer_appid' => $authorizer_appid,
+            'component_appid' => $component_appid,
+            'user_id' => $user_id,
+            'puser_id' => $auth_user->pid,
+        ], [
+            'authorizer_refresh_token' => $authorizer_refresh_token,
+            'nick_name' => $parsedContent['authorizer_info']['nick_name'],
+        ]);
+
+        return view('wechat.openPlatform.authSuccess')->with('url', sprintf('%s/#/user/advertiser',config('app.url')));
+    }
+
+    /**
+     * 处理授权事件
+     * @param Request $request
+     * @param $component_appid
+     */
+    public function authorCommand(Request $request, $component_appid) {
+        $componentInfo = WechatOpenPlatformService::getComponentInfoByAppid($component_appid);
+        $app = WechatOpenPlatformService::buildApplication($componentInfo);
+
+        $server = $app->getServer();
+
+        $server->handleAuthorized(function($message, \Closure $next) {
+            myLog('authorCommand')->info('handleAuthorized', ['message' => $message]);
+            return $next($message);
+        });
+        $server->handleUnauthorized(function($message, \Closure $next) {
+            WechatOpenPlatformService::handleUnauthorized($message);
+            return $next($message);
+        });
+
+        return $server->serve();
+    }
+
+    public function infoCommand(Request $request, $authorizer_appid, $component_appid) {
+        $componentInfo = WechatOpenPlatformService::getComponentInfoByAppid($component_appid);
+        $app = WechatOpenPlatformService::buildApplication($componentInfo);
+
+        $server = $app->getServer();
+
+        return $server->serve();
+    }
+}

+ 12 - 0
modules/Channel/Models/WechatAuthorizationInfo.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Modules\Channel\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class WechatAuthorizationInfo extends Model
+{
+    protected $table = 'wechat_authorization_infos';
+
+    protected $guarded = [];
+}

+ 76 - 0
modules/Channel/Services/WechatOpenPlatform/WechatOpenPlatformService.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace Modules\Channel\Services\WechatOpenPlatform;
+
+use EasyWeChat\OpenPlatform\Application;
+use EasyWeChat\OpenPlatform\Message;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+use Modules\Common\Errors\Errors;
+use Modules\Common\Exceptions\CommonBusinessException;
+
+class WechatOpenPlatformService
+{
+    public static function getComponentInfoByCompanyUid($companyUid) {
+        $componentInfo = DB::table('wechat_open_platform_infos')
+            ->where([
+                'company_uid' => $companyUid,
+                'is_enabled' => 1,
+            ])->orderBy('id', 'desc')
+            ->first();
+        if(!$componentInfo) {
+            CommonBusinessException::throwError(Errors::OPENPLATFORM_COMPANY_INFO_NOT_EXISTS);
+        }
+        return $componentInfo;
+    }
+
+    public static function getComponentInfoByAppid($componentAppid) {
+        $componentInfo =  DB::table('wechat_open_platform_infos')
+            ->where([
+                'app_id' => $componentAppid,
+                'is_enabled' => 1,
+            ])->orderBy('id', 'desc')
+            ->first();
+        if(!$componentInfo) {
+            CommonBusinessException::throwError(Errors::OPENPLATFORM_COMPANY_INFO_NOT_EXISTS);
+        }
+        return $componentInfo;
+    }
+
+    /**
+     * 构造app
+     * @param $componentInfo
+     * @return Application
+     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
+     */
+    public static function buildApplication($componentInfo) {
+        $config = [
+            'app_id' => $componentInfo->app_id, // 开放平台账号的 appid
+            'secret' => $componentInfo->secret,   // 开放平台账号的 secret
+            'token' => $componentInfo->token,  // 开放平台账号的 token
+            'aes_key' => $componentInfo->aes_key,   // 明文模式请勿填写 EncodingAESKey
+            'http' => [
+                'throw'  => true, // 状态码非 200、300 时是否抛出异常,默认为开启
+                'timeout' => 5.0,
+                'retry' => true, // 使用默认重试配置
+            ],
+        ];
+        $app = new Application($config);
+        $app->setCache(Cache::store('redis'));
+        return $app;
+    }
+
+    /**
+     * 取消授权
+     * @param Message $message
+     */
+    public static function handleUnauthorized($message) {
+        $component_appid = $message['AppId'];
+        $authorizer_appid = $message['AuthorizerAppid'];
+        DB::table('wechat_authorization_infos')
+            ->where([
+                'component_appid' => $component_appid,
+                'authorizer_appid' => $authorizer_appid,
+            ])->delete();
+    }
+}

+ 28 - 0
modules/Channel/routes/route.php

@@ -2,8 +2,10 @@
 
 
 use Illuminate\Support\Facades\Route;
 use Illuminate\Support\Facades\Route;
 use Modules\Channel\Http\Controllers\AdvertiserController;
 use Modules\Channel\Http\Controllers\AdvertiserController;
+use Modules\Channel\Http\Controllers\CompanyUserController;
 use Modules\Channel\Http\Controllers\PayTemplateController;
 use Modules\Channel\Http\Controllers\PayTemplateController;
 use Modules\Channel\Http\Controllers\UserMiniprogramController;
 use Modules\Channel\Http\Controllers\UserMiniprogramController;
+use Modules\Channel\Http\Controllers\WechatOpenPlatformController;
 
 
 Route::prefix('channel')->group(function () {
 Route::prefix('channel')->group(function () {
     Route::post('advertiser/add', [AdvertiserController::class, 'addAdvertiser']);
     Route::post('advertiser/add', [AdvertiserController::class, 'addAdvertiser']);
@@ -47,5 +49,31 @@ Route::prefix('channel')->group(function () {
     Route::any('promotions/options', [\Modules\Channel\Http\Controllers\OrdersController::class, 'promotionsOptions']);
     Route::any('promotions/options', [\Modules\Channel\Http\Controllers\OrdersController::class, 'promotionsOptions']);
     Route::any('miniprogram/use_list', [\Modules\Channel\Http\Controllers\OrdersController::class, 'userUseList']);
     Route::any('miniprogram/use_list', [\Modules\Channel\Http\Controllers\OrdersController::class, 'userUseList']);
     Route::any('promotions/users', [\Modules\Channel\Http\Controllers\OrdersController::class, 'promotionsUsers']);
     Route::any('promotions/users', [\Modules\Channel\Http\Controllers\OrdersController::class, 'promotionsUsers']);
+
+    Route::prefix('companyUser')->group(function(){
+        Route::get('list', [CompanyUserController::class, 'list']);
+    });
+
+
+    /**
+     * 第三方开放平台
+     */
+    Route::prefix('openPlatform')->group(function(){
+        Route::get('auth/{component_appid}/{user_id}', [WechatOpenPlatformController::class, 'auth'])->withoutMiddleware(config('catch.route.middlewares'));
+        Route::get('preauth', [WechatOpenPlatformController::class, 'preauth'])->middleware(['roleCheck:company']);
+    });
+});
+
+/**
+ * 微信专用
+ */
+Route::prefix('wechat')->group(function(){
+    /**
+     * 三方平台
+     */
+    Route::prefix('openPlatform')->group(function(){
+        Route::post('authorCommand/{component_appid}', [WechatOpenPlatformController::class, 'authorCommand'])->withoutMiddleware(config('catch.route.middlewares'));
+        Route::post('infoCommand/{authorizer_appid}/{component_appid}', [WechatOpenPlatformController::class, 'infoCommand'])->withoutMiddleware(config('catch.route.middlewares'));
+    });
 });
 });
 
 

+ 7 - 3
modules/Common/Errors/Errors.php

@@ -30,8 +30,12 @@ class Errors
     public const  TIXIAN_ONLY_ONCE_EVERY_DAY = [500204, '每天只能提现一次'];
     public const  TIXIAN_ONLY_ONCE_EVERY_DAY = [500204, '每天只能提现一次'];
     public const  OPERATION_FIRST_PAGE_LIST_NOT_EXISTS = [500301, '首页列表配置项不存在'];
     public const  OPERATION_FIRST_PAGE_LIST_NOT_EXISTS = [500301, '首页列表配置项不存在'];
     public const  VIDEO_SERIES_NOT_EXISTS = [500302, '剧集不存在'];
     public const  VIDEO_SERIES_NOT_EXISTS = [500302, '剧集不存在'];
-    public const REQUEST_HTTP_STATUS_ERROR = [500401, '请求上游接口返回http状态码有误'];
-    public const REQUEST_CODE_STATUS_ERROR = [500402, '请求上游接口返回code状态码有误'];
-    public const SYNC_WECHAT_NOT_OK = [500302, '剧集没有成功同步到微信'];
+    public const  REQUEST_HTTP_STATUS_ERROR = [500401, '请求上游接口返回http状态码有误'];
+    public const  REQUEST_CODE_STATUS_ERROR = [500402, '请求上游接口返回code状态码有误'];
+    public const  SYNC_WECHAT_NOT_OK = [500303, '剧集没有成功同步到微信'];
     public const  MINIPROGRAM_OWNER_ACCOUNT_ROLE_ERROR= [5000501, '所分配的账号不是投放公司账号'];
     public const  MINIPROGRAM_OWNER_ACCOUNT_ROLE_ERROR= [5000501, '所分配的账号不是投放公司账号'];
+    public const  GET_WECHAT_MEDIA_LINK_ERROR = [500304, '获取短剧播放链接失败'];
+    public const  OPENPLATFORM_COMPANY_INFO_NOT_EXISTS= [5000601, '公司没有对应的开放平台信息'];
+    public const  OPENPLATFORM_OPTIMIZER_INFO_ERROR= [5000602, '优化师信息有误'];
+    public const  WECHAT_CHECK_RECORD_NOT_EXISTS = [500305, '微信提审记录不存在'];
 }
 }

+ 61 - 0
modules/Common/Support/Http/HttpRequest.php

@@ -0,0 +1,61 @@
+<?php
+namespace Modules\Common\Support\Http;
+
+use GuzzleHttp\Client;
+
+class HttpRequest
+{
+
+    /**
+     * 发送post请求
+     * @param $url 请求地址
+     * @param $accessToken 请求access_token
+     * @param $postMessage 请求体
+     * @return array
+     */
+    public static function simplePost($url, $postMessage) {
+        $client = new Client(['timeout' => 10]);
+        try {
+            $res = $client->post(
+                $url,
+                [
+                    'json' => $postMessage
+                ]);
+            $httpStatusCode = $res->getStatusCode();
+            $httpContent = $res->getBody()->getContents();
+            $parsedContent = json_decode($httpContent, true);
+            if (200 == $httpStatusCode) {
+                return $parsedContent;
+            }
+        } catch (\Exception $exception) {
+            myLog('HttpRequest')->error('请求失败:', [
+                'url' => $url,
+                'json' => $postMessage,
+                'exceptionMessage' => $exception->getMessage(),
+            ]);
+        }
+        return false;
+    }
+
+    public static function simpleGet($url, $query) {
+        $client = new Client(['timeout' => 10]);
+        try {
+            $response = $client->get($url, [
+                'query' => $query
+            ]);
+            $httpStatusCode = $response->getStatusCode();
+            $httpContent = $response->getBody()->getContents();
+            $parsedContent = json_decode($httpContent, true);
+            if (200 == $httpStatusCode)  {
+                return $parsedContent;
+            }
+        } catch (\Exception $exception) {
+            myLog('HttpRequest')->error('请求失败:', [
+                'url' => $url,
+                'query' => $query,
+                'exceptionMessage' => $exception->getMessage(),
+            ]);
+        }
+        return false;
+    }
+}

+ 13 - 0
modules/Common/Support/Http/WechatURL.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace Modules\Common\Support\Http;
+
+class WechatURL
+{
+    // 短剧拉取上传
+    public const vod_pullupload = 'https://api.weixin.qq.com/wxa/sec/vod/pullupload?access_token=';
+    // 短剧拉取任务查询
+    public const vod_gettask = 'https://api.weixin.qq.com/wxa/sec/vod/gettask?access_token=';
+    // 获取短剧播放链接
+    public const vod_getmedialink = 'https://api.weixin.qq.com/wxa/sec/vod/getmedialink?access_token=';
+}

+ 34 - 0
modules/Common/Support/Wechat/Crypt/ErrorCode.php

@@ -0,0 +1,34 @@
+<?php
+namespace Modules\Common\Support\Wechat\Crypt;
+/**
+ * error code 说明.
+ * <ul>
+ *    <li>-40001: 签名验证错误</li>
+ *    <li>-40002: xml解析失败</li>
+ *    <li>-40003: sha加密生成签名失败</li>
+ *    <li>-40004: encodingAesKey 非法</li>
+ *    <li>-40005: appid 校验错误</li>
+ *    <li>-40006: aes 加密失败</li>
+ *    <li>-40007: aes 解密失败</li>
+ *    <li>-40008: 解密后得到的buffer非法</li>
+ *    <li>-40009: base64加密失败</li>
+ *    <li>-40010: base64解密失败</li>
+ *    <li>-40011: 生成xml失败</li>
+ * </ul>
+ */
+class ErrorCode
+{
+	public static $OK = 0;
+	public static $ValidateSignatureError = -40001;
+	public static $ParseXmlError = -40002;
+	public static $ComputeSignatureError = -40003;
+	public static $IllegalAesKey = -40004;
+	public static $ValidateAppidError = -40005;
+	public static $EncryptAESError = -40006;
+	public static $DecryptAESError = -40007;
+	public static $IllegalBuffer = -40008;
+	public static $EncodeBase64Error = -40009;
+	public static $DecodeBase64Error = -40010;
+	public static $GenReturnXmlError = -40011;
+}
+

+ 52 - 0
modules/Common/Support/Wechat/Crypt/PKCS7Encoder.php

@@ -0,0 +1,52 @@
+<?php
+namespace Modules\Common\Support\Wechat\Crypt;
+
+/**
+ * PKCS7Encoder class
+ *
+ * 提供基于PKCS7算法的加解密接口.
+ */
+class PKCS7Encoder
+{
+	public static $block_size = 32;
+
+	/**
+	 * 对需要加密的明文进行填充补位
+	 * @param $text 需要进行填充补位操作的明文
+	 * @return 补齐明文字符串
+	 */
+	function encode($text)
+	{
+		$block_size = PKCS7Encoder::$block_size;
+		$text_length = strlen($text);
+		//计算需要填充的位数
+		$amount_to_pad = PKCS7Encoder::$block_size - ($text_length % PKCS7Encoder::$block_size);
+		if ($amount_to_pad == 0) {
+			$amount_to_pad = PKCS7Encoder::$block_size;
+		}
+		//获得补位所用的字符
+		$pad_chr = chr($amount_to_pad);
+		$tmp = "";
+		for ($index = 0; $index < $amount_to_pad; $index++) {
+			$tmp .= $pad_chr;
+		}
+		return $text . $tmp;
+	}
+
+	/**
+	 * 对解密后的明文进行补位删除
+	 * @param decrypted 解密后的明文
+	 * @return 删除填充补位后的明文
+	 */
+	function decode($text)
+	{
+
+		$pad = ord(substr($text, -1));
+		if ($pad < 1 || $pad > 32) {
+			$pad = 0;
+		}
+		return substr($text, 0, (strlen($text) - $pad));
+	}
+
+}
+

+ 103 - 0
modules/Common/Support/Wechat/Crypt/Prpcrypt.php

@@ -0,0 +1,103 @@
+<?php
+namespace Modules\Common\Support\Wechat\Crypt;
+
+/**
+ * Prpcrypt class
+ *
+ * 提供接收和推送给公众平台消息的加解密接口.
+ */
+class Prpcrypt
+{
+	public $key;
+
+	public function __construct($k)
+	{
+		$this->key = base64_decode($k . "=");
+	}
+
+	/**
+	 * 对明文进行加密
+	 * @param string $text 需要加密的明文
+	 * @return string 加密后的密文
+	 */
+	public function encrypt($text, $appid)
+	{
+
+		try {
+			//获得16位随机字符串,填充到明文之前
+			$random = $this->getRandomStr();
+			$text = $random . pack("N", strlen($text)) . $text . $appid;
+			//使用自定义的填充方式对明文进行补位填充
+			$pkc_encoder = new PKCS7Encoder;
+			$text = $pkc_encoder->encode($text);
+
+            $encrypted = @openssl_encrypt($text, 'aes-128-cbc', $this->key, 0);
+
+			//print(base64_encode($encrypted));
+			//使用BASE64对加密后的字符串进行编码
+			return array(ErrorCode::$OK, base64_encode($encrypted));
+		} catch (Exception $e) {
+			//print $e;
+			return array(ErrorCode::$EncryptAESError, null);
+		}
+	}
+
+	/**
+	 * 对密文进行解密
+	 * @param string $encrypted 需要解密的密文
+	 * @return string 解密得到的明文
+	 */
+	public function decrypt($encrypted, $appid)
+	{
+
+		try {
+			//使用BASE64对需要解密的字符串进行解码
+			$ciphertext_dec = base64_decode($encrypted);
+            $decrypted = openssl_decrypt($ciphertext_dec, 'aes-128-cbc', $this->key);
+		} catch (Exception $e) {
+			return array(ErrorCode::$DecryptAESError, null);
+		}
+
+
+		try {
+			//去除补位字符
+			$pkc_encoder = new PKCS7Encoder;
+			$result = $pkc_encoder->decode($decrypted);
+			//去除16位随机字符串,网络字节序和AppId
+			if (strlen($result) < 16)
+				return "";
+			$content = substr($result, 16, strlen($result));
+			$len_list = unpack("N", substr($content, 0, 4));
+			$xml_len = $len_list[1];
+			$xml_content = substr($content, 4, $xml_len);
+			$from_appid = substr($content, $xml_len + 4);
+		} catch (Exception $e) {
+			//print $e;
+			return array(ErrorCode::$IllegalBuffer, null);
+		}
+		if ($from_appid != $appid)
+			return array(ErrorCode::$ValidateAppidError, null);
+		return array(0, $xml_content);
+
+	}
+
+
+	/**
+	 * 随机生成16位字符串
+	 * @return string 生成的字符串
+	 */
+	function getRandomStr()
+	{
+
+		$str = "";
+		$str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
+		$max = strlen($str_pol) - 1;
+		for ($i = 0; $i < 16; $i++) {
+			$str .= $str_pol[mt_rand(0, $max)];
+		}
+		return $str;
+	}
+
+}
+
+?>

+ 4 - 0
modules/Common/Support/Wechat/Crypt/ReadMe.txt

@@ -0,0 +1,4 @@
+注意事项:
+1.WXBizMsgCrypt.php文件提供了WXBizMsgCrypt类的实现,是用户接入企业微信的接口类。Sample.php提供了示例以供开发者参考。errorCode.php, pkcs7Encoder.php, sha1.php, xmlparse.php文件是实现这个类的辅助类,开发者无须关心其具体实现。
+2.WXBizMsgCrypt类封装了 DecryptMsg, EncryptMsg两个接口,分别用于开发者解密以及开发者回复消息的加密。使用方法可以参考Sample.php文件。
+3.加解密协议请参考微信公众平台官方文档。

+ 36 - 0
modules/Common/Support/Wechat/Crypt/SHA1.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace Modules\Common\Support\Wechat\Crypt;
+
+/**
+ * SHA1 class
+ *
+ * 计算公众平台的消息签名接口.
+ */
+class SHA1
+{
+	/**
+	 * 用SHA1算法生成安全签名
+	 * @param string $token 票据
+	 * @param string $timestamp 时间戳
+	 * @param string $nonce 随机字符串
+	 * @param string $encrypt 密文消息
+	 */
+	public function getSHA1($token, $timestamp, $nonce, $encrypt_msg)
+	{
+		//排序
+		try {
+			$array = array($encrypt_msg, $token, $timestamp, $nonce);
+			sort($array, SORT_STRING);
+			$str = implode($array);
+			return array(ErrorCode::$OK, sha1($str));
+		} catch (Exception $e) {
+			//print $e . "\n";
+			return array(ErrorCode::$ComputeSignatureError, null);
+		}
+	}
+
+}
+
+
+?>

+ 145 - 0
modules/Common/Support/Wechat/Crypt/WXBizMsgCrypt.php

@@ -0,0 +1,145 @@
+<?php
+namespace Modules\Common\Support\Wechat\Crypt;
+/**
+ * 对公众平台发送给公众账号的消息加解密示例代码.
+ *
+ * @copyright Copyright (c) 1998-2014 Tencent Inc.
+ */
+
+
+/**
+ * 1.第三方回复加密消息给公众平台;
+ * 2.第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。
+ */
+class WXBizMsgCrypt
+{
+	private $token;
+	private $encodingAesKey;
+	private $appId;
+
+	/**
+	 * 构造函数
+	 * @param $token string 公众平台上,开发者设置的token
+	 * @param $encodingAesKey string 公众平台上,开发者设置的EncodingAESKey
+	 * @param $appId string 公众平台的appId
+	 */
+	public function __construct($token, $encodingAesKey, $appId)
+	{
+		$this->token = $token;
+		$this->encodingAesKey = $encodingAesKey;
+		$this->appId = $appId;
+	}
+
+	/**
+	 * 将公众平台回复用户的消息加密打包.
+	 * <ol>
+	 *    <li>对要发送的消息进行AES-CBC加密</li>
+	 *    <li>生成安全签名</li>
+	 *    <li>将消息密文和安全签名打包成xml格式</li>
+	 * </ol>
+	 *
+	 * @param $replyMsg string 公众平台待回复用户的消息,xml格式的字符串
+	 * @param $timeStamp string 时间戳,可以自己生成,也可以用URL参数的timestamp
+	 * @param $nonce string 随机串,可以自己生成,也可以用URL参数的nonce
+	 * @param &$encryptMsg string 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,
+	 *                      当return返回0时有效
+	 *
+	 * @return int 成功0,失败返回对应的错误码
+	 */
+	public function encryptMsg($replyMsg, $timeStamp, $nonce, &$encryptMsg)
+	{
+		$pc = new Prpcrypt($this->encodingAesKey);
+
+		//加密
+		$array = $pc->encrypt($replyMsg, $this->appId);
+		$ret = $array[0];
+		if ($ret != 0) {
+			return $ret;
+		}
+
+		if ($timeStamp == null) {
+			$timeStamp = time();
+		}
+		$encrypt = $array[1];
+
+		//生成安全签名
+		$sha1 = new SHA1;
+		$array = $sha1->getSHA1($this->token, $timeStamp, $nonce, $encrypt);
+		$ret = $array[0];
+		if ($ret != 0) {
+			return $ret;
+		}
+		$signature = $array[1];
+
+		//生成发送的xml
+		$xmlparse = new XMLParse;
+		$encryptMsg = $xmlparse->generate($encrypt, $signature, $timeStamp, $nonce);
+		return ErrorCode::$OK;
+	}
+
+
+	/**
+	 * 检验消息的真实性,并且获取解密后的明文.
+	 * <ol>
+	 *    <li>利用收到的密文生成安全签名,进行签名验证</li>
+	 *    <li>若验证通过,则提取xml中的加密消息</li>
+	 *    <li>对消息进行解密</li>
+	 * </ol>
+	 *
+	 * @param $msgSignature string 签名串,对应URL参数的msg_signature
+	 * @param $timestamp string 时间戳 对应URL参数的timestamp
+	 * @param $nonce string 随机串,对应URL参数的nonce
+	 * @param $postData string 密文,对应POST请求的数据
+	 * @param &$msg string 解密后的原文,当return返回0时有效
+	 *
+	 * @return int 成功0,失败返回对应的错误码
+	 */
+	public function decryptMsg($msgSignature, $timestamp = null, $nonce, $postData, &$msg)
+	{
+		if (strlen($this->encodingAesKey) != 43) {
+			return ErrorCode::$IllegalAesKey;
+		}
+
+		$pc = new Prpcrypt($this->encodingAesKey);
+
+		//提取密文
+		$xmlparse = new XMLParse;
+		$array = $xmlparse->extract($postData);
+		$ret = $array[0];
+
+		if ($ret != 0) {
+			return $ret;
+		}
+
+		if ($timestamp == null) {
+			$timestamp = time();
+		}
+
+		$encrypt = $array[1];
+		$touser_name = $array[2];
+
+		//验证安全签名
+		$sha1 = new SHA1;
+		$array = $sha1->getSHA1($this->token, $timestamp, $nonce, $encrypt);
+		$ret = $array[0];
+
+		if ($ret != 0) {
+			return $ret;
+		}
+
+		$signature = $array[1];
+		if ($signature != $msgSignature) {
+			return ErrorCode::$ValidateSignatureError;
+		}
+
+		$result = $pc->decrypt($encrypt, $this->appId);
+		if ($result[0] != 0) {
+			return $result[0];
+		}
+		$msg = $result[1];
+
+		return ErrorCode::$OK;
+	}
+
+}
+

+ 52 - 0
modules/Common/Support/Wechat/Crypt/XMLParse.php

@@ -0,0 +1,52 @@
+<?php
+namespace Modules\Common\Support\Wechat\Crypt;
+
+/**
+ * XMLParse class
+ *
+ * 提供提取消息格式中的密文及生成回复消息格式的接口.
+ */
+class XMLParse
+{
+
+	/**
+	 * 提取出xml数据包中的加密消息
+	 * @param string $xmltext 待提取的xml字符串
+	 * @return string 提取出的加密消息字符串
+	 */
+	public function extract($xmltext)
+	{
+		libxml_disable_entity_loader(true);
+		try {
+			$xml = new \DOMDocument();
+			$xml->loadXML($xmltext);
+			$array_e = $xml->getElementsByTagName('Encrypt');
+			$array_a = $xml->getElementsByTagName('ToUserName');
+			$encrypt = $array_e->item(0)->nodeValue;
+			$tousername = $array_a->item(0)->nodeValue;
+			return array(0, $encrypt, $tousername);
+		} catch (Exception $e) {
+			//print $e . "\n";
+			return array(ErrorCode::$ParseXmlError, null, null);
+		}
+	}
+
+	/**
+	 * 生成xml消息
+	 * @param string $encrypt 加密后的消息密文
+	 * @param string $signature 安全签名
+	 * @param string $timestamp 时间戳
+	 * @param string $nonce 随机字符串
+	 */
+	public function generate($encrypt, $signature, $timestamp, $nonce)
+	{
+		$format = "<xml>
+<Encrypt><![CDATA[%s]]></Encrypt>
+<MsgSignature><![CDATA[%s]]></MsgSignature>
+<TimeStamp>%s</TimeStamp>
+<Nonce><![CDATA[%s]]></Nonce>
+</xml>";
+		return sprintf($format, $encrypt, $signature, $timestamp, $nonce);
+	}
+
+}

+ 41 - 0
modules/Common/Support/Wechat/Crypt/demo.php

@@ -0,0 +1,41 @@
+<?php
+namespace Modules\Common\Support\Wechat\Crypt;
+
+
+
+// 第三方发送消息给公众平台
+$encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";
+$token = "pamtest";
+$timeStamp = "1409304348";
+$nonce = "xxxxxx";
+$appId = "wxb11529c136998cb6";
+$text = "<xml><ToUserName><![CDATA[oia2Tj我是中文jewbmiOUlr6X-1crbLOvLw]]></ToUserName><FromUserName><![CDATA[gh_7f083739789a]]></FromUserName><CreateTime>1407743423</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[eYJ1MbwPRJtOvIEabaxHs7TX2D-HV71s79GUxqdUkjm6Gs2Ed1KF3ulAOA9H1xG0]]></MediaId><Title><![CDATA[testCallBackReplyVideo]]></Title><Description><![CDATA[testCallBackReplyVideo]]></Description></Video></xml>";
+
+
+$pc = new WXBizMsgCrypt($token, $encodingAesKey, $appId);
+$encryptMsg = '';
+$errCode = $pc->encryptMsg($text, $timeStamp, $nonce, $encryptMsg);
+if ($errCode == 0) {
+	print("加密后: " . $encryptMsg . "\n");
+} else {
+	print($errCode . "\n");
+}
+
+$xml_tree = new DOMDocument();
+$xml_tree->loadXML($encryptMsg);
+$array_e = $xml_tree->getElementsByTagName('Encrypt');
+$array_s = $xml_tree->getElementsByTagName('MsgSignature');
+$encrypt = $array_e->item(0)->nodeValue;
+$msg_sign = $array_s->item(0)->nodeValue;
+
+$format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%s]]></Encrypt></xml>";
+$from_xml = sprintf($format, $encrypt);
+
+// 第三方收到公众号平台发送的消息
+$msg = '';
+$errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $from_xml, $msg);
+if ($errCode == 0) {
+	print("解密后: " . $msg . "\n");
+} else {
+	print($errCode . "\n");
+}

+ 25 - 0
modules/Common/Support/Wechat/OpenPlatform/OPApplication.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Modules\Common\Support\Wechat\OpenPlatform;
+
+use EasyWeChat\OpenPlatform\Application;
+
+class OPApplication extends Application
+{
+    public function getAuthorizerAccessToken(string $appId, string $refreshToken): string
+    {
+        $component_appid = $this->getAccount()->getAppId();
+        $cacheKey = sprintf('open-platform.authorizer_access_token.%s.%s.%s', $component_appid, $appId, md5($refreshToken));
+
+        /** @phpstan-ignore-next-line */
+        $authorizerAccessToken = (string) $this->getCache()->get($cacheKey);
+
+        if (! $authorizerAccessToken) {
+            $response = $this->refreshAuthorizerToken($appId, $refreshToken);
+            $authorizerAccessToken = (string) $response['authorizer_access_token'];
+            $this->getCache()->set($cacheKey, $authorizerAccessToken, intval($response['expires_in'] ?? 7200) - 500);
+        }
+
+        return $authorizerAccessToken;
+    }
+}

+ 23 - 8
modules/Statistic/Http/Controllers/ChargeTJController.php

@@ -71,8 +71,8 @@ class ChargeTJController extends CatchController
         foreach ($result as $item) {
         foreach ($result as $item) {
             $item->common_pay_money_per = $item->common_pay_uv ? bcdiv($item->common_pay_money, $item->common_pay_uv ,2 ) : 0;
             $item->common_pay_money_per = $item->common_pay_uv ? bcdiv($item->common_pay_money, $item->common_pay_uv ,2 ) : 0;
             $item->vip_pay_money_per = $item->vip_pay_uv ? bcdiv($item->vip_pay_money, $item->vip_pay_uv ,2 ) : 0;
             $item->vip_pay_money_per = $item->vip_pay_uv ? bcdiv($item->vip_pay_money, $item->vip_pay_uv ,2 ) : 0;
-            $item->common_pay_rate = $item->common_pay_count ? bcdiv($item->common_pay_count,100 * ($item->common_pay_count + $item->common_unpay_count) ,2 ) . '%' : 0 .'%';
-            $item->vip_pay_rate = $item->vip_pay_count ? bcdiv($item->vip_pay_count ,100 * ($item->vip_pay_count + $item->vip_unpay_count), 2 ) . '%' : 0 .'%';
+            $item->common_pay_rate = $item->common_pay_count ? bcdiv(100 * $item->common_pay_count, ($item->common_pay_count + $item->common_unpay_count) ,2 ) . '%' : 0 .'%';
+            $item->vip_pay_rate = $item->vip_pay_count ? bcdiv(100 * $item->vip_pay_count , ($item->vip_pay_count + $item->vip_unpay_count), 2 ) . '%' : 0 .'%';
             $item->username = $users->get($item->user_id)->username ?? '';
             $item->username = $users->get($item->user_id)->username ?? '';
             $item->miniprogram_name = $miniprograms->get($item->miniprogram_id)->name ?? '';
             $item->miniprogram_name = $miniprograms->get($item->miniprogram_id)->name ?? '';
         }
         }
@@ -130,6 +130,11 @@ class ChargeTJController extends CatchController
         }
         }
 
 
         if($info) {
         if($info) {
+            $info->pay_money = intval($info->pay_money * 100) / 100;
+            $info->common_pay_count = intval($info->common_pay_count);
+            $info->common_unpay_count = intval($info->common_unpay_count);
+            $info->vip_pay_count = intval($info->vip_pay_count);
+            $info->vip_unpay_count = intval($info->vip_unpay_count);
             $info->company_pay_rate = $info->common_pay_count ? bcdiv($info->common_pay_count,($info->common_pay_count + $info->common_unpay_count) * 100,  2) . '%' : '0%';
             $info->company_pay_rate = $info->common_pay_count ? bcdiv($info->common_pay_count,($info->common_pay_count + $info->common_unpay_count) * 100,  2) . '%' : '0%';
             $info->vip_pay_rate = $info->vip_pay_count ? bcdiv($info->vip_pay_count, ($info->vip_pay_count + $info->vip_unpay_count) * 100, 2) . '%' : '0%';
             $info->vip_pay_rate = $info->vip_pay_count ? bcdiv($info->vip_pay_count, ($info->vip_pay_count + $info->vip_unpay_count) * 100, 2) . '%' : '0%';
         }
         }
@@ -163,8 +168,13 @@ class ChargeTJController extends CatchController
                 )->first();
                 )->first();
         }
         }
         if($info) {
         if($info) {
-            $info->company_pay_rate = $info->common_pay_count ? bcdiv($info->common_pay_count,($info->common_pay_count + $info->common_unpay_count) * 100,  2) . '%' : '0%';
-            $info->vip_pay_rate = $info->vip_pay_count ? bcdiv($info->vip_pay_count, ($info->vip_pay_count + $info->vip_unpay_count) * 100, 2) . '%' : '0%';
+            $info->pay_money = intval($info->pay_money * 100) / 100;
+            $info->common_pay_count = intval($info->common_pay_count);
+            $info->common_unpay_count = intval($info->common_unpay_count);
+            $info->vip_pay_count = intval($info->vip_pay_count);
+            $info->vip_unpay_count = intval($info->vip_unpay_count);
+            $info->company_pay_rate = $info->common_pay_count ? bcdiv($info->common_pay_count * 100,($info->common_pay_count + $info->common_unpay_count),  2) . '%' : '0%';
+            $info->vip_pay_rate = $info->vip_pay_count ? bcdiv($info->vip_pay_count * 100, ($info->vip_pay_count + $info->vip_unpay_count), 2) . '%' : '0%';
         }
         }
         return $info;
         return $info;
     }
     }
@@ -196,8 +206,13 @@ class ChargeTJController extends CatchController
                 )->first();
                 )->first();
         }
         }
         if($info) {
         if($info) {
-            $info->company_pay_rate = $info->common_pay_count ? bcdiv($info->common_pay_count,($info->common_pay_count + $info->common_unpay_count) * 100,  2) . '%' : '0%';
-            $info->vip_pay_rate = $info->vip_pay_count ? bcdiv($info->vip_pay_count,($info->vip_pay_count + $info->vip_unpay_count) * 100,  2) . '%' : '0%';
+            $info->pay_money = intval($info->pay_money * 100) / 100;
+            $info->common_pay_count = intval($info->common_pay_count);
+            $info->common_unpay_count = intval($info->common_unpay_count);
+            $info->vip_pay_count = intval($info->vip_pay_count);
+            $info->vip_unpay_count = intval($info->vip_unpay_count);
+            $info->company_pay_rate = $info->common_pay_count ? bcdiv($info->common_pay_count * 100,($info->common_pay_count + $info->common_unpay_count),  2) . '%' : '0%';
+            $info->vip_pay_rate = $info->vip_pay_count ? bcdiv($info->vip_pay_count * 100, ($info->vip_pay_count + $info->vip_unpay_count), 2) . '%' : '0%';
         }
         }
         return $info;
         return $info;
     }
     }
@@ -238,8 +253,8 @@ class ChargeTJController extends CatchController
             'vip_unpay_count' => $info->vip_unpay_count ?? 0 + $currentMonthInfo->vip_unpay_count ?? 0,
             'vip_unpay_count' => $info->vip_unpay_count ?? 0 + $currentMonthInfo->vip_unpay_count ?? 0,
             'vip_pay_count' => $info->vip_pay_count ?? 0 + $currentMonthInfo->vip_pay_count ?? 0,
             'vip_pay_count' => $info->vip_pay_count ?? 0 + $currentMonthInfo->vip_pay_count ?? 0,
         ];
         ];
-        $result->company_pay_rate = $result->common_pay_count ? bcdiv($result->common_pay_count,($result->common_pay_count + $result->common_unpay_count) * 100,  2) . '%' : '0%';
-        $result->vip_pay_rate = $result->vip_pay_count ? bcdiv($result->vip_pay_count,($result->vip_pay_count + $result->vip_unpay_count) * 100,  2) . '%' : '0%';
+        $result->company_pay_rate = $result->common_pay_count ? bcdiv($result->common_pay_count * 100,($result->common_pay_count + $result->common_unpay_count),  2) . '%' : '0%';
+        $result->vip_pay_rate = $result->vip_pay_count ? bcdiv($result->vip_pay_count * 100,($result->vip_pay_count + $result->vip_unpay_count),  2) . '%' : '0%';
 
 
         return $result;
         return $result;
     }
     }

+ 156 - 0
modules/Statistic/Http/Controllers/ROITJController.php

@@ -0,0 +1,156 @@
+<?php
+
+namespace Modules\Statistic\Http\Controllers;
+
+use Catch\Base\CatchController;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+use Modules\User\Http\Controllers\UserTrait;
+
+class ROITJController extends CatchController
+{
+    use ValidatesRequests;
+    use UserTrait;
+
+    private function _sql(Request $request)
+    {
+        $this->validate($request, [
+            'start_at' => 'nullable|date_format:Y-m-d',
+            'end_at' => 'nullable|date_format:Y-m-d|after_or_equal:start_at',
+            'ranse_id' => 'nullable|integer|min:1',
+            'miniprogram_id' => 'nullable|integer|min:1',
+            'puser_id' => 'nullable|integer|min:1'
+        ]);
+
+        $startAt = $request->input('start_at');
+        $endAt = $request->input('end_at');
+        $ranseId = $request->input('ranse_id');
+        $miniprogramId = $request->input('miniprogram_id');
+        $roles = $this->listUserRoles();
+        $puserId = null;
+        $userId = null;
+        if ($roles->contains('company')) {
+            $puserId = $this->getLoginUserId();
+        }
+        if ($roles->contains('optimizer')) {
+            $userId = $this->getLoginUserId();
+        }
+        if($roles->contains('administrator')) {
+            $puserId = $request->input('puser_id');
+        }
+
+        return DB::table('tj_promotion_day_charge')
+            ->when($startAt, function ($query, $startAt) {
+                return $query->where('day_at', '>=', $startAt);
+            })->when($endAt, function ($query, $endAt) {
+                return $query->where('day_at', '<=', $endAt);
+            })->when($ranseId, function ($query, $ranseId) {
+                return $query->where('promotion_id', $ranseId);
+            })->when($miniprogramId, function ($query, $miniprogramId) {
+                return $query->where('miniprogram_id', $miniprogramId);
+            })->when($puserId, function ($query, $puserId) {
+                return $query->where('puser_id', $puserId);
+            })->when($userId, function ($query, $userId) {
+                return $query->where('user_id', $userId);
+            })->orderBy('id', 'desc');
+    }
+
+    // 推广链接投入产出每日列表
+    public function list(Request $request)
+    {
+        $sql = $this->_sql($request)->select(
+            'day_at', 'user_id', 'puser_id', 'new_user_num', 'new_user_pay_uv',
+            'cost_money', 'new_user_pay_money', 'new_user_vip_pay_money', 'new_user_common_pay_money',
+            'new_user_vip_pay_uv', 'new_user_common_pay_uv', 'miniprogram_id', 'promotion_id','id'
+        );
+        $isExport = $request->integer('is_export', false);
+        if ($isExport) {
+            $result = $sql->get();
+        } else {
+            $result = $sql->paginate($request->input('limit', 20));
+        }
+        if ($result->count()) {
+            $users = DB::table('users')
+                ->whereIn('id', $result->pluck('user_id'))
+                ->select('id', 'username')
+                ->get()->keyBy('id');
+            $miniprograms = DB::table('miniprogram')
+                ->whereIn('id', $result->pluck('miniprogram_id'))
+                ->select('id', 'name')
+                ->get()->keyBy('id');
+            $companyUserNames = DB::table('users')
+                ->whereIn('id', $result->pluck('puser_id'))->select('id','username')
+                ->get()->keyBy('id');
+            $promotions = DB::table('promotions')
+                ->whereIn('id', $result->pluck('promotion_id'))
+                ->select('id', 'name')
+                ->get()->keyBy('id');
+            foreach ($result as $item) {
+                // 商户
+                $item->company_username = $companyUserNames->get($item->puser_id)->username ?? '';
+                // 优化师
+                $item->optimizer_name = $users->get($item->user_id)->username ?? '';
+                // 小程序
+                $item->miniprogram_name = $miniprograms->get($item->miniprogram_id)->name ?? '';
+                // 推广链接
+                $item->promotion_name = $promotions->get($item->promotion_id)->name ?? '';
+                // 回本
+                $item->huiben = $item->cost_money !== '0.00' ? intval($item->new_user_pay_money * 10000 / $item->cost_money) / 100 . '%' : '0%';
+                // 新增用户成本
+                $item->new_user_cost_money = $item->new_user_num ? intval($item->cost_money * 100 / $item->new_user_num) / 100 : 0;
+                // 新增用户人均充值
+                $item->new_user_pay_money_per = $item->new_user_pay_uv ? intval($item->new_user_pay_money * 100 / $item->new_user_pay_uv) / 100 : 0;
+                // 会员成本,累计
+                $item->vip_money_per = $item->new_user_vip_pay_uv ? intval($item->cost_money * 100 / $item->new_user_vip_pay_uv) / 100 : 0;
+                // 充值成本,累计
+                $item->common_money_per = $item->new_user_common_pay_uv ? intval($item->cost_money * 100 / $item->new_user_common_pay_uv) / 100 : 0;
+                // 会员转化率
+                $item->zhuanhua_vip = $item->new_user_num ? intval($item->new_user_vip_pay_uv * 10000 / $item->new_user_num) / 100 . '%' : '0%';
+                // 充值转化率
+                $item->zhuanhua_common = $item->new_user_num ? intval($item->new_user_common_pay_uv * 10000 / $item->new_user_num) / 100 . '%' : '0%';
+                // 总计转化率
+                $item->zhuanhua_all = $item->new_user_num ? intval($item->new_user_pay_uv * 10000 / $item->new_user_num) / 100 . '%' : '0%';
+            }
+
+        }
+
+        return $result;
+    }
+
+    // 推广链接投入产出每日列表综合
+    public function listTotal(Request $request)
+    {
+        $sql = $this->_sql($request)->select('new_user_pay_money', 'cost_money');
+        $result = $sql->select(
+            DB::raw("sum(new_user_pay_money) as pay_money"),
+            DB::raw("sum(cost_money) as cost_money"),
+        )->first();
+        if ($result) {
+            return [
+                'pay_money' => $result->pay_money,
+                'cost_money' => $result->cost_money,
+            ];
+        } else {
+            return [];
+        }
+    }
+
+    // 更新投放成本
+    public function updateCostmoney(Request $request) {
+        $this->validate($request, [
+            'id' => 'required',
+            'cost_money' => 'required|min:0|max:1000000000'
+        ]);
+        DB::table('tj_promotion_day_charge')
+            ->where([
+                'id' => $request->input('id'),
+                'user_id' => $this->getLoginUserId(),
+            ])->update([
+                'cost_money' => $request->input('cost_money'),
+                'updated_at' => date('Y-m-d H:i:s')
+            ]);
+
+        return 'ok';
+    }
+}

+ 8 - 0
modules/Statistic/routes/route.php

@@ -5,6 +5,7 @@ use Modules\Statistic\Http\Controllers\ChargeTJController;
 use Modules\Statistic\Http\Controllers\HomeStatisticsController;
 use Modules\Statistic\Http\Controllers\HomeStatisticsController;
 use Modules\Statistic\Http\Controllers\UserStatisticsController;
 use Modules\Statistic\Http\Controllers\UserStatisticsController;
 use Modules\Statistic\Http\Controllers\VideoStatController;
 use Modules\Statistic\Http\Controllers\VideoStatController;
+use Modules\Statistic\Http\Controllers\ROITJController;
 
 
 Route::prefix('statistic')->group(function(){
 Route::prefix('statistic')->group(function(){
     //next
     //next
@@ -35,6 +36,13 @@ Route::prefix('statistic')->group(function(){
         Route::get('totalCharge', [ChargeTJController::class, 'totalCharge']);
         Route::get('totalCharge', [ChargeTJController::class, 'totalCharge']);
     });
     });
 
 
+    // 投入产出
+    Route::prefix('roi')->group(function(){
+        Route::get('list', [ROITJController::class, 'list']);
+        Route::get('listTotal', [ROITJController::class, 'listTotal']);
+        Route::post('updateCostmoney', [ROITJController::class, 'updateCostmoney'])->middleware('roleCheck:optimizer');
+    });
+
     // 首页统计
     // 首页统计
     Route::any("home",[HomeStatisticsController::class,"statistics"]);
     Route::any("home",[HomeStatisticsController::class,"statistics"]);
 
 

+ 3 - 1
modules/Tuiguang/Http/Controllers/PromotionController.php

@@ -116,9 +116,11 @@ class PromotionController extends CatchController
             'not_first_charge_template_id' => 'required',
             'not_first_charge_template_id' => 'required',
         ]);
         ]);
         $now = date('Y-m-d H:i:s');
         $now = date('Y-m-d H:i:s');
+        $currentUser = $this->getCurrentUser();
         DB::table('promotions')
         DB::table('promotions')
             ->insert([
             ->insert([
-                'uid' => $this->getLoginUserId(),
+                'uid' => $currentUser->id,
+                'puid' => $currentUser->pid,
                 'miniprogram_id' => $request->input('miniprogram_id'),
                 'miniprogram_id' => $request->input('miniprogram_id'),
                 'name' => $request->input('name'),
                 'name' => $request->input('name'),
                 'video_id' => $request->input('video_id'),
                 'video_id' => $request->input('video_id'),

+ 18 - 1
modules/Video/Http/Controllers/EpisodeController.php

@@ -30,9 +30,22 @@ class EpisodeController extends CatchController
             ->where([
             ->where([
                 'video_id' => $request->integer('video_id'),
                 'video_id' => $request->integer('video_id'),
                 'is_enabled' => 1
                 'is_enabled' => 1
-            ])->select('series_name', 'series_sequence', 'video_key', 'duration')
+            ])->select('series_name', 'series_sequence', 'video_key', 'duration', 'id', 'video_id')
             ->orderBy('series_sequence', 'asc')
             ->orderBy('series_sequence', 'asc')
             ->paginate($request->integer('limit', 15));
             ->paginate($request->integer('limit', 15));
+        /**
+         * 增加微信审核状态
+         */
+        $wechatCheckStatus = null;
+        if($request->input('need_wechat_status', 0)) {
+            $serieIds = $videoSeries->pluck('id');
+            $wechatCheckStatus = DB::table('video_series_wechat_check')
+                ->whereIn('series_id', $serieIds)
+                ->where('is_enabled', 1)
+                ->select('series_id', 'check_status')
+                ->get()->keyBy('series_id');
+        }
+        $wechatCheckStatusMap = config('video.wechat.checkStatus');
         foreach ($videoSeries as $series) {
         foreach ($videoSeries as $series) {
             $series->series_name = sprintf('第%s集', $series->series_sequence);
             $series->series_name = sprintf('第%s集', $series->series_sequence);
             $series->is_charge = $series->series_sequence >= $video->charge_sequence;
             $series->is_charge = $series->series_sequence >= $video->charge_sequence;
@@ -40,6 +53,10 @@ class EpisodeController extends CatchController
             $series->public_video_url = config('common.qiniu.publicVideoLinkDomain') . DIRECTORY_SEPARATOR . $series->video_key;
             $series->public_video_url = config('common.qiniu.publicVideoLinkDomain') . DIRECTORY_SEPARATOR . $series->video_key;
             $series->download_video_url = QiniuTokenService::getPrivateSourceDownloadUrl(config('common.qiniu.sourceVideoLinkDomain') . DIRECTORY_SEPARATOR .
             $series->download_video_url = QiniuTokenService::getPrivateSourceDownloadUrl(config('common.qiniu.sourceVideoLinkDomain') . DIRECTORY_SEPARATOR .
                 $series->video_key.'?attname='.urlencode($series->series_name).'.mp4');
                 $series->video_key.'?attname='.urlencode($series->series_name).'.mp4');
+            $wechat_check_status = $wechatCheckStatus[$series->id]->check_status ?? 0;
+            $series->wechat_check_status = $wechat_check_status;
+            $series->wechat_check_status_str = $wechatCheckStatusMap[$wechat_check_status] ?? '';
+
         }
         }
 
 
         return $videoSeries;
         return $videoSeries;

+ 45 - 4
modules/Video/Http/Controllers/VideoSeriesWechatCheckController.php

@@ -7,6 +7,7 @@ use Illuminate\Foundation\Validation\ValidatesRequests;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Redis;
 use Illuminate\Support\Facades\Redis;
+use Illuminate\Support\Facades\Response;
 use Modules\Common\Errors\Errors;
 use Modules\Common\Errors\Errors;
 use Modules\Common\Exceptions\CommonBusinessException;
 use Modules\Common\Exceptions\CommonBusinessException;
 use Modules\Manage\Services\WechatMiniprogramService;
 use Modules\Manage\Services\WechatMiniprogramService;
@@ -28,7 +29,8 @@ class VideoSeriesWechatCheckController extends CatchController
             ->join('videos', 'video_series.video_id', 'videos.id')
             ->join('videos', 'video_series.video_id', 'videos.id')
             ->whereIn('video_series.id', $series_ids)
             ->whereIn('video_series.id', $series_ids)
             ->where(['video_series.is_enabled' => 1])
             ->where(['video_series.is_enabled' => 1])
-            ->select('videos.name', 'video_series.series_sequence','video_series.id', 'video_series.video_key')
+            ->select('video_series.video_id', 'videos.name', 'video_series.series_sequence',
+                'video_series.id', 'video_series.video_key')
             ->get();
             ->get();
         if(collect($series_ids)->count() != $series->count()) {
         if(collect($series_ids)->count() != $series->count()) {
             CommonBusinessException::throwError(Errors::VIDEO_SERIES_NOT_EXISTS);
             CommonBusinessException::throwError(Errors::VIDEO_SERIES_NOT_EXISTS);
@@ -39,7 +41,11 @@ class VideoSeriesWechatCheckController extends CatchController
         foreach ($series as $item) {
         foreach ($series as $item) {
             $item->video_url = config('common.qiniu.publicVideoLinkDomain') . DIRECTORY_SEPARATOR . $item->video_key;
             $item->video_url = config('common.qiniu.publicVideoLinkDomain') . DIRECTORY_SEPARATOR . $item->video_key;
             $item->media_name = sprintf('%s - 第%s集', $item->name, $item->series_sequence);
             $item->media_name = sprintf('%s - 第%s集', $item->name, $item->series_sequence);
-            $taskId = WechatCheckSyncService::pullupload($item, $accessToken);
+            $parsedContent = WechatCheckSyncService::pullupload($item, $accessToken);
+            if(false === $parsedContent || (0 != ($parsedContent['errcode'] ?? 0))) {
+                CommonBusinessException::throwError(Errors::SYNC_WECHAT_NOT_OK);
+            }
+            $taskId = $parsedContent['task_id'];
             DB::table('video_series_wechat_check')
             DB::table('video_series_wechat_check')
                 ->where(['series_id' => $item->id, 'is_enabled' => 1])
                 ->where(['series_id' => $item->id, 'is_enabled' => 1])
                 ->update(['is_enabled' => 0, 'updated_at' => $now]);
                 ->update(['is_enabled' => 0, 'updated_at' => $now]);
@@ -47,7 +53,8 @@ class VideoSeriesWechatCheckController extends CatchController
                 ->insert([
                 ->insert([
                     'series_id' => $item->id,
                     'series_id' => $item->id,
                     'sync_task_id' => $taskId, 'appid' => $appid,
                     'sync_task_id' => $taskId, 'appid' => $appid,
-                    'created_at' => $now, 'updated_at' => $now
+                    'created_at' => $now, 'updated_at' => $now,
+                    'video_id' => $item->video_id,
                 ]);
                 ]);
         }
         }
 
 
@@ -55,7 +62,7 @@ class VideoSeriesWechatCheckController extends CatchController
     }
     }
 
 
     /**
     /**
-     * 获取微信那边的剧集播放链接
+     * 获取微信的剧集播放链接
      * @param Request $request
      * @param Request $request
      * @return mixed
      * @return mixed
      * @throws \Illuminate\Validation\ValidationException
      * @throws \Illuminate\Validation\ValidationException
@@ -73,8 +80,42 @@ class VideoSeriesWechatCheckController extends CatchController
         }
         }
 
 
         $medialinkInfo = WechatCheckSyncService::getMedialinkInfo($seriesId);
         $medialinkInfo = WechatCheckSyncService::getMedialinkInfo($seriesId);
+        if(false === $medialinkInfo) {
+            CommonBusinessException::throwError(Errors::GET_WECHAT_MEDIA_LINK_ERROR);
+        }
         $link = $medialinkInfo['mp4_url'];
         $link = $medialinkInfo['mp4_url'];
         Redis::setex($redisKey, 6000, $link);
         Redis::setex($redisKey, 6000, $link);
         return $link;
         return $link;
     }
     }
+
+    /**
+     * 获取审核证据
+     * todo:fix
+     * @param Request $request
+     * @throws \Illuminate\Validation\ValidationException
+     */
+    public function listEvidence(Request $request) {
+        $this->validate($request, [
+            'series_id' => 'required'
+        ]);
+        $info = DB::table('video_series_wechat_check')
+            ->where([
+                'series_id' => $request->input('series_id'),
+                'is_enabled' => 1
+            ])->orderBy('id', 'desc')
+            ->select('evidence_material_id_list', 'appid')->first();
+        if($info) {
+            $evidence_material_id_lists = \json_decode($info->evidence_material_id_list, true);
+            $accessToken = WechatMiniprogramService::getDuanjuCheckAccessToken($info->appid);
+        }
+        return 'todo:待完善';
+    }
+
+    public function test(Request $request) {
+        foreach (['1228789.jpg'] as $i) {
+            $path = storage_path(uniqid());
+            file_put_contents($path, file_put_contents(app_path($i)));
+            return response()->file($path);
+        }
+    }
 }
 }

+ 60 - 8
modules/Video/Http/Controllers/WechatCheckController.php

@@ -7,6 +7,9 @@ use Catch\Base\CatchController;
 use Illuminate\Foundation\Validation\ValidatesRequests;
 use Illuminate\Foundation\Validation\ValidatesRequests;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\DB;
+use Modules\Common\Errors\Errors;
+use Modules\Common\Exceptions\CommonBusinessException;
+use Modules\Manage\Services\WechatMiniprogramService;
 
 
 /**
 /**
  * 微信提审
  * 微信提审
@@ -24,11 +27,14 @@ class WechatCheckController extends CatchController
             'producer' => 'required|string|max:256',
             'producer' => 'required|string|max:256',
             'playwright' => 'required|string|max:256',
             'playwright' => 'required|string|max:256',
             'production_license_img' => 'required|url',
             'production_license_img' => 'required|url',
-            'authorized_img' => 'required|url'
+            'authorized_img' => 'required|url',
+            'registration_number' => 'required'
         ]);
         ]);
 
 
         $data = $request->all();
         $data = $request->all();
+        $appid = WechatMiniprogramService::getDuanjuCheckAppid();
         $data['created_at'] = $data['updated_at'] = date('Y-m-d H:i:s');
         $data['created_at'] = $data['updated_at'] = date('Y-m-d H:i:s');
+        $data['appid'] = $appid;
         DB::table('video_wechat_check')
         DB::table('video_wechat_check')
             ->insert($data);
             ->insert($data);
 
 
@@ -39,7 +45,25 @@ class WechatCheckController extends CatchController
      * 修改
      * 修改
      * @param Request $request
      * @param Request $request
      */
      */
-    public function update(Request $request) {}
+    public function update(Request $request) {
+        $this->validate($request, [
+            'id'=>'required',
+            'producer' => 'required|string|max:256',
+            'playwright' => 'required|string|max:256',
+            'production_license_img' => 'required|url',
+            'authorized_img' => 'required|url',
+            'registration_number' => 'required'
+        ]);
+
+        $data = $request->only(['producer', 'playwright', 'production_license_img', 'authorized_img', 'registration_number']);
+        $data['updated_at'] = date('Y-m-d H:i:s');
+        DB::table('video_wechat_check')
+            ->where('id', $request->input('id'))
+            ->where('is_enabled', 1)
+            ->whereIn('status', [0,4])
+            ->update($data);
+        return 'ok';
+    }
 
 
     /**
     /**
      * 删除
      * 删除
@@ -48,6 +72,7 @@ class WechatCheckController extends CatchController
     public function delete(Request $request) {
     public function delete(Request $request) {
         $this->validate($request, ['id' => 'required']);
         $this->validate($request, ['id' => 'required']);
         DB::table('video_wechat_check')
         DB::table('video_wechat_check')
+            ->whereIn('status', [0,4])
             ->where([
             ->where([
                 'id' => $request->input('id'),
                 'id' => $request->input('id'),
                 'is_enabled' => 1,
                 'is_enabled' => 1,
@@ -66,12 +91,12 @@ class WechatCheckController extends CatchController
         $videoId = $request->input('video_id');
         $videoId = $request->input('video_id');
         $producer = $request->input('producer');
         $producer = $request->input('producer');
         $playwright = $request->input('playwright');
         $playwright = $request->input('playwright');
-        $status = $request->input('status',0);
+        $status = $request->input('status','0');
 
 
-        return DB::table('video_wechat_check as check')
+        $result =  DB::table('video_wechat_check as check')
             ->join('videos', 'videos.id', 'check.video_id')
             ->join('videos', 'videos.id', 'check.video_id')
+            ->whereIn('check.status', explode(',', $status))
             ->where([
             ->where([
-                'check.status' => $status,
                 'check.is_enabled' => 1,
                 'check.is_enabled' => 1,
             ])->when($videoId, function ($query, $videoId) {
             ])->when($videoId, function ($query, $videoId) {
                 return $query->where('check.video_id', $videoId);
                 return $query->where('check.video_id', $videoId);
@@ -82,22 +107,49 @@ class WechatCheckController extends CatchController
             })->select('check.id', 'videos.name', 'videos.note', 'videos.total_episode_num',
             })->select('check.id', 'videos.name', 'videos.note', 'videos.total_episode_num',
                 'videos.cover_image','check.status','check.producer',
                 'videos.cover_image','check.status','check.producer',
             'check.playwright', 'check.production_license_img', 'check.authorized_img', 'check.apply_at',
             'check.playwright', 'check.production_license_img', 'check.authorized_img', 'check.apply_at',
-            'check.check_at', 'check.check_reason')
+            'check.check_at', 'check.check_reason', 'check.registration_number', 'check.video_id')
             ->orderBy('check.id','desc')
             ->orderBy('check.id','desc')
-            ->paginate($request->input('limit', 10));
+            ->paginate($request->input('limit', 20));
+        $statusMap = config('video.wechat.dramaCheckStatus');
+        foreach ($result as $item) {
+            $item->status_str = $statusMap[$item->status] ?? '';
+        }
 
 
+        return $result;
     }
     }
 
 
     /**
     /**
      * 微信提审
      * 微信提审
      * @param Request $request
      * @param Request $request
      */
      */
-    public function wechatCheck(Request $request) {
+    public function check(Request $request) {
         $this->validate($request, [
         $this->validate($request, [
             'ids' => 'required|array'
             'ids' => 'required|array'
         ]);
         ]);
         $ids = $request->input('ids');
         $ids = $request->input('ids');
         $now = date('Y-m-d H:i:s');
         $now = date('Y-m-d H:i:s');
+
+        foreach ($ids as $id) {
+            $record = DB::table('video_wechat_check as check')
+                ->join('videos', 'videos.id', 'check.video_id')
+                ->where(['check.is_enabled' => 1, 'check.id' => $id])
+                ->select('check.video_id', 'videos.total_episode_num', 'videos.name')
+                ->first();
+            if(!$record) {
+                CommonBusinessException::throwError(Errors::WECHAT_CHECK_RECORD_NOT_EXISTS);
+            }
+
+            $medias = DB::table('video_series_wechat_check')
+                ->where('video_id', $record->video_id)
+                ->where(['sync_status' => 4, 'is_enabled' => 1])
+                ->where('media_id', '<>', 0)
+                ->get();
+            if($medias->count() != $record->total_episode_num) {
+                CommonBusinessException::throwError([Errors::SYNC_WECHAT_NOT_OK[0],
+                    sprintf('%s,同步到微信:%s集,总集数:%s集', $record->name, $medias->count(), $record->total_episode_num)]);
+            }
+        }
+
         DB::table('video_wechat_check')
         DB::table('video_wechat_check')
             ->whereIn('id', $ids)
             ->whereIn('id', $ids)
             ->whereIn('status', [0, 4])
             ->whereIn('status', [0, 4])

+ 9 - 47
modules/Video/Services/WechatCheckSyncService.php

@@ -6,6 +6,8 @@ use GuzzleHttp\Client;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\DB;
 use Modules\Common\Errors\Errors;
 use Modules\Common\Errors\Errors;
 use Modules\Common\Exceptions\CommonBusinessException;
 use Modules\Common\Exceptions\CommonBusinessException;
+use Modules\Common\Support\Http\HttpRequest;
+use Modules\Common\Support\Http\WechatURL;
 use Modules\Manage\Services\WechatMiniprogramService;
 use Modules\Manage\Services\WechatMiniprogramService;
 
 
 
 
@@ -19,12 +21,10 @@ class WechatCheckSyncService
      * @throws \GuzzleHttp\Exception\GuzzleException
      * @throws \GuzzleHttp\Exception\GuzzleException
      */
      */
     public static function pullupload($item, $accessToken) {
     public static function pullupload($item, $accessToken) {
-        $url = 'https://api.weixin.qq.com/wxa/sec/vod/pullupload?access_token='.$accessToken;
-        $parsedContent = self::postWechat($url, [
+        return HttpRequest::simplePost(WechatURL::vod_pullupload. $accessToken, [
             'media_name' => $item->media_name,
             'media_name' => $item->media_name,
             'media_url' => $item->video_url
             'media_url' => $item->video_url
         ]);
         ]);
-        return $parsedContent['task_id'];
     }
     }
 
 
     /**
     /**
@@ -46,58 +46,20 @@ class WechatCheckSyncService
         }
         }
         $mediaId = $syncInfo->media_id;
         $mediaId = $syncInfo->media_id;
         $accessToken = WechatMiniprogramService::getDuanjuCheckAccessToken($syncInfo->appid);
         $accessToken = WechatMiniprogramService::getDuanjuCheckAccessToken($syncInfo->appid);
-        $url = 'https://api.weixin.qq.com/wxa/sec/vod/getmedialink?access_token='.$accessToken;
 
 
-        $parsedContent = self::postWechat($url, [
+        return HttpRequest::simplePost(WechatURL::vod_getmedialink. $accessToken, [
             'media_id' => $mediaId,
             'media_id' => $mediaId,
             't' => time() + 7200,
             't' => time() + 7200,
         ]);
         ]);
-        return $parsedContent['media_info'];
     }
     }
 
 
+    /**
+     * 短剧播放链接保存的rediskey
+     * @param $seriesId
+     * @return string
+     */
     public static function getWechatMediaLinkRedisKey($seriesId) {
     public static function getWechatMediaLinkRedisKey($seriesId) {
         return 'wechat.medialink.'.$seriesId;
         return 'wechat.medialink.'.$seriesId;
     }
     }
 
 
-    public static function getTask($syncInfo) {
-        try {
-            $accessToken = WechatMiniprogramService::getDuanjuCheckAccessToken($syncInfo->appid);
-            $url = 'https://api.weixin.qq.com/wxa/sec/vod/gettask?access_token='.$accessToken;
-            $parsedContent = self::postWechat($url, [
-                'task_id' => $syncInfo->sync_task_id
-            ]);
-            return $parsedContent['task_info'];
-        } catch (\Exception $exception) {
-            return [];
-        }
-
-    }
-
-    /**
-     *  post 请求微信上游
-     * @param $url
-     * @param $data
-     * @throws \GuzzleHttp\Exception\GuzzleException
-     */
-    public static function postWechat($url, $data) {
-        $client = new Client(['timeout' => 3]);
-        $httpResult = $client->post($url, $data);
-
-        $httpStatus = $httpResult->getStatusCode();
-        if(200 != $httpStatus) {
-            CommonBusinessException::throwError(Errors::REQUEST_HTTP_STATUS_ERROR);
-        }
-        $httpContent = $httpResult->getBody()->getContents();
-        $parsedContent = \json_decode($httpContent, true);
-        if(0 != ($parsedContent['errcode'] ?? 0)) {
-            myLog('WechatCheckSync')->error('请求微信异常', [
-                'url' => $url,
-                'data' => $data,
-                'errMsg' => $httpContent
-            ]);
-            CommonBusinessException::throwError(Errors::REQUEST_CODE_STATUS_ERROR);
-        }
-
-        return $parsedContent;
-    }
 }
 }

+ 18 - 0
modules/Video/config/wechat.php

@@ -0,0 +1,18 @@
+<?php
+
+return [
+    'checkStatus' => [
+        '0' =>  '',
+        '1' => '审核中',
+        '2' => '审核驳回',
+        '3' => '审核通过'
+    ],
+    'dramaCheckStatus' => [
+        '0' => '',
+        '1' => '审核中',
+        '2' => '最终失败',
+        '3' => '审核通过',
+        '4' => '驳回重新提审',
+        '5' => '审核中'
+    ],
+];

+ 5 - 0
modules/Video/routes/route.php

@@ -21,10 +21,15 @@ Route::prefix('videoStock')->group(function () {
 
 
     Route::prefix('wechatCheck')->group(function(){
     Route::prefix('wechatCheck')->group(function(){
         Route::post('add', [WechatCheckController::class, 'add']);
         Route::post('add', [WechatCheckController::class, 'add']);
+        Route::post('check', [WechatCheckController::class, 'check']);
         Route::get('list', [WechatCheckController::class, 'list']);
         Route::get('list', [WechatCheckController::class, 'list']);
         Route::post('delete', [WechatCheckController::class, 'delete']);
         Route::post('delete', [WechatCheckController::class, 'delete']);
+        Route::post('update', [WechatCheckController::class, 'update']);
         Route::prefix('videoSeries')->group(function(){
         Route::prefix('videoSeries')->group(function(){
             Route::post('syncWechat', [VideoSeriesWechatCheckController::class, 'syncWechat']);
             Route::post('syncWechat', [VideoSeriesWechatCheckController::class, 'syncWechat']);
+            Route::post('medialink', [VideoSeriesWechatCheckController::class, 'medialink']);
+            Route::post('listEvidence', [VideoSeriesWechatCheckController::class, 'listEvidence']);
+            Route::get('test', [VideoSeriesWechatCheckController::class, 'test'])->withoutMiddleware(config('catch.route.middlewares'));
         });
         });
     });
     });
 
 

+ 8 - 0
resources/views/wechat/openPlatform/authSuccess.blade.php

@@ -0,0 +1,8 @@
+<h1>
+    授权成功, 正在跳转 : <a href="{{ $url }}">{{ $url }}</a>
+</h1>
+<script>
+    setTimeout(function () {
+        window.open("{{ $url }}", '_self')
+    }, 500)
+</script>

+ 1 - 1
tests/Channel/Http/Controllers/AdvertiserControllerTest.php

@@ -32,7 +32,7 @@ class AdvertiserControllerTest extends UsedTestCase
 //                'email' => 'aa1@test.com',
 //                'email' => 'aa1@test.com',
 //                'miniProgramId' => 3,
 //                'miniProgramId' => 3,
             ]));
             ]));
-        $res->dump();
+        $this->dumpJson($res);
     }
     }
 
 
     public function testgetAdvertiser() {
     public function testgetAdvertiser() {

+ 19 - 0
tests/Channel/Http/Controllers/CompanyUserControllerTest.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Tests\Channel\Http\Controllers;
+
+use Modules\Channel\Http\Controllers\CompanyUserController;
+use PHPUnit\Framework\TestCase;
+use Tests\UsedTestCase;
+
+class CompanyUserControllerTest extends UsedTestCase
+{
+
+    public function testList()
+    {
+        $res = $this->withHeaders([
+            'Authorization' => 'Bearer '. $this->token,
+        ])->json('get','http://localhost/api/channel/companyUser/list');
+        $this->dumpJson($res);
+    }
+}

+ 28 - 0
tests/Channel/Http/Controllers/WechatOpenPlatformControllerTest.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace Tests\Channel\Http\Controllers;
+
+use Illuminate\Support\Facades\DB;
+use Modules\Channel\Http\Controllers\WechatOpenPlatformController;
+use PHPUnit\Framework\TestCase;
+use Tests\UsedTestCase;
+
+class WechatOpenPlatformControllerTest extends UsedTestCase
+{
+
+    public function testAuth()
+    {
+        $res = $this->withHeaders([
+        ])->json('get','http://localhost/api/channel/openPlatform/auth/wxf313b3506bac2647/26?auth_code=queryauthcode@@@K_MNMi2oLcnszHdG1o5R9rSYcrxFrBmTwtug04MDjk2PrjEfl1mVjYSfPSgJzodUyIvOKXnVaTtz-oLPUw10Ng&expires_in=3600');
+        $res->dump();
+    }
+    public function testauthorCommand()
+    {
+        $res = $this->withHeaders([
+            'Authorization' => 'Bearer '. $this->token,
+        ])->json('post','http://localhost/api/channel/openPlatform/authorCommand/wxf313b3506bac2647', [
+
+        ]);
+        $res->dump();
+    }
+}

+ 54 - 0
tests/Common/Support/Wechat/Crypt/WXBizMsgCryptTest.php

@@ -0,0 +1,54 @@
+<?php
+
+namespace Tests\Common\Support\Wechat\Crypt;
+
+use Modules\Common\Support\Wechat\Crypt\WXBizMsgCrypt;
+use PHPUnit\Framework\TestCase;
+use Tests\UsedTestCase;
+
+class WXBizMsgCryptTest extends UsedTestCase
+{
+    public function testDemo() {
+        // 第三方发送消息给公众平台
+        $encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";
+        $token = "pamtest";
+        $timeStamp = "1409304348";
+        $nonce = "xxxxxx";
+        $appId = "wxb11529c136998cb6";
+        $text = "<xml><ToUserName><![CDATA[oia2Tj我是中文jewbmiOUlr6X-1crbLOvLw]]></ToUserName><FromUserName><![CDATA[gh_7f083739789a]]></FromUserName><CreateTime>1407743423</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[eYJ1MbwPRJtOvIEabaxHs7TX2D-HV71s79GUxqdUkjm6Gs2Ed1KF3ulAOA9H1xG0]]></MediaId><Title><![CDATA[testCallBackReplyVideo]]></Title><Description><![CDATA[testCallBackReplyVideo]]></Description></Video></xml>";
+
+
+        $pc = new WXBizMsgCrypt($token, $encodingAesKey, $appId);
+        $encryptMsg = '';
+        $errCode = $pc->encryptMsg($text, $timeStamp, $nonce, $encryptMsg);
+        if ($errCode == 0) {
+            print("加密后: " . $encryptMsg . "\n");
+        } else {
+            print($errCode . "\n");
+        }
+
+        $xml_tree = new \DOMDocument();
+        $xml_tree->loadXML($encryptMsg);
+        $array_e = $xml_tree->getElementsByTagName('Encrypt');
+        $array_s = $xml_tree->getElementsByTagName('MsgSignature');
+        $encrypt = $array_e->item(0)->nodeValue;
+        $msg_sign = $array_s->item(0)->nodeValue;
+
+        $format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%s]]></Encrypt></xml>";
+        $from_xml = sprintf($format, $encrypt);
+
+// 第三方收到公众号平台发送的消息
+        $msg = '';
+        $errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $from_xml, $msg);
+        if ($errCode == 0) {
+            print("解密后: " . $msg . "\n");
+
+            $xml = simplexml_load_string($msg, "SimpleXMLElement", LIBXML_NOCDATA);
+            $json = json_encode($xml);
+            $array = json_decode($json,TRUE);
+            dump($array);
+        } else {
+            print($errCode . "\n");
+        }
+    }
+}

+ 19 - 0
tests/Console/Commands/Statistic/PromotionDayChargeTest.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Tests\Console\Commands\Statistic;
+
+use App\Console\Commands\Statistic\PromotionDayCharge;
+use PHPUnit\Framework\TestCase;
+
+class PromotionDayChargeTest extends \Tests\TestCase
+{
+
+    public function testHandle()
+    {
+        for($i = 0; $i > -27; $i--) {
+            $date = date('Y-m-d', strtotime(sprintf('%s day', $i)));
+            $data[] = 'php artisan Statistic:CompanyDayCharge --date='.$date;
+        }
+        dump(join(' && ', $data));
+    }
+}

+ 46 - 0
tests/Statistic/Http/Controllers/ROITJControllerTest.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace Tests\Statistic\Http\Controllers;
+
+use PHPUnit\Framework\TestCase;
+use Tests\UsedTestCase;
+
+class ROITJControllerTest extends UsedTestCase
+{
+
+    public function testList()
+    {
+        $res = $this->withHeaders([
+            'Authorization' => 'Bearer '. $this->token,
+        ])->json('get','http://localhost/api/statistic/roi/list', [
+//            'user_id' => 10,
+//            'limit' => 3,
+//            'start_at' => '2023-06-03',
+//            'end_at' => '2023-06-04',
+        ]);
+        $this->dumpJson($res);
+    }
+    public function testlistTotal()
+    {
+        $res = $this->withHeaders([
+            'Authorization' => 'Bearer '. $this->token,
+        ])->json('get','http://localhost/api/statistic/roi/listTotal', [
+//            'user_id' => 10,
+            'limit' => 3,
+//            'start_at' => '2023-06-03',
+//            'end_at' => '2023-06-04',
+        ]);
+        $this->dumpJson($res);
+    }
+
+    public function testupdateCostmoney()
+    {
+        $res = $this->withHeaders([
+            'Authorization' => 'Bearer '. $this->token,
+        ])->json('post','http://localhost/api/statistic/roi/updateCostmoney', [
+            'cost_money' => 15,
+            'id' => 1
+        ]);
+        $this->dumpJson($res);
+    }
+}

+ 3 - 1
tests/Tuiguang/Http/Controllers/PromotionControllerTest.php

@@ -16,8 +16,10 @@ class PromotionControllerTest extends UsedTestCase
         ])->json('post','http://localhost/api/tuiguang/promotion/add', [
         ])->json('post','http://localhost/api/tuiguang/promotion/add', [
             'video_id' => 6,
             'video_id' => 6,
             'series_sequence'  => 4,
             'series_sequence'  => 4,
-            'name' => 'kkkkddd',
+            'name' => 'kkkkddd的发的',
             'miniprogram_id' => 2,
             'miniprogram_id' => 2,
+            'first_charge_template_id' => 1,
+            'not_first_charge_template_id' => 2
         ]);
         ]);
 
 
         $res->dump();
         $res->dump();

+ 1 - 1
tests/UsedTestCase.php

@@ -13,7 +13,7 @@ abstract class UsedTestCase extends BaseTestCase
     {
     {
         parent::setUp(); // TODO: Change the autogenerated stub
         parent::setUp(); // TODO: Change the autogenerated stub
         $tokenInfo = $this->post('http://localhost/api/login', [
         $tokenInfo = $this->post('http://localhost/api/login', [
-//            'email' => 'catch@admin.com',
+            'email' => 'catch@admin.com',
             'remember' => false,
             'remember' => false,
             'email' => 'xiaoli@qq.com',
             'email' => 'xiaoli@qq.com',
             'password' => 'catchadmin',
             'password' => 'catchadmin',

+ 3 - 3
tests/Video/Http/Controllers/EpisodeControllerTest.php

@@ -14,10 +14,10 @@ class EpisodeControllerTest extends UsedTestCase
         $res = $this->withHeaders([
         $res = $this->withHeaders([
             'Authorization' => 'Bearer '. $this->token,
             'Authorization' => 'Bearer '. $this->token,
         ])->json('get','http://localhost/api/videoStock/episode/downloadList', [
         ])->json('get','http://localhost/api/videoStock/episode/downloadList', [
-            'video_id' => 3
+            'video_id' => 3,
+            'need_wechat_status' => 1
         ]);
         ]);
-//        $this->dumpJson($res);
-        $res->dump();
+        $this->dumpJson($res);
     }
     }
 
 
     public function testAdd()
     public function testAdd()

+ 9 - 1
tests/Video/Http/Controllers/VideoSeriesWechatCheckControllerTest.php

@@ -16,7 +16,15 @@ class VideoSeriesWechatCheckControllerTest extends UsedTestCase
         ])->json('post','http://localhost/api/videoStock/wechatCheck/videoSeries/syncWechat', [
         ])->json('post','http://localhost/api/videoStock/wechatCheck/videoSeries/syncWechat', [
             'series_ids' => [81,82,83,84,85],
             'series_ids' => [81,82,83,84,85],
         ]);
         ]);
-        $res->dump();
+        $this->dumpJson($res);
+    }
+    public function testmedialink()
+    {
+        $res = $this->withHeaders([
+            'Authorization' => 'Bearer '. $this->token,
+        ])->json('post','http://localhost/api/videoStock/wechatCheck/videoSeries/medialink', [
+            'series_id' => 81,
+        ]);
         $this->dumpJson($res);
         $this->dumpJson($res);
     }
     }
 }
 }

+ 5 - 4
tests/Video/Http/Controllers/WechatCheckControllerTest.php

@@ -28,10 +28,11 @@ class WechatCheckControllerTest extends UsedTestCase
         $res = $this->withHeaders([
         $res = $this->withHeaders([
             'Authorization' => 'Bearer '. $this->token,
             'Authorization' => 'Bearer '. $this->token,
         ])->json('get','http://localhost/api/videoStock/wechatCheck/list', [
         ])->json('get','http://localhost/api/videoStock/wechatCheck/list', [
-            'video_id' => 12,
-            'producer' => 'zzz',
-            'playwright' =>  'xxxx',
+//            'video_id' => 12,
+//            'producer' => 'zzz',
+//            'playwright' =>  'xxxx',
+        'status' => '5'
         ]);
         ]);
-        $res->dump();
+        $this->dumpJson($res);
     }
     }
 }
 }