瀏覽代碼

巨量账户级充值回传

liuzejian 2 年之前
父節點
當前提交
1135320101

+ 249 - 0
app/Jobs/Callback/RechargeReport.php

@@ -0,0 +1,249 @@
+<?php
+
+namespace App\Jobs\OrangeSite;
+
+use App\Modules\Callback\Models\ChannelCallbackConfig;
+use App\Modules\Callback\Services\CallbackBaseService;
+use App\Modules\OrangeSite\Services\CheckReport\EnableReportCheck;
+use App\Modules\OrangeSite\Services\CheckReport\OrderAlreadyReportCheck;
+use App\Modules\OrangeSite\Services\CheckReport\PromotionProtectCheck;
+use App\Modules\OrangeSite\Services\CheckReport\RechargeAmountCheck;
+use App\Modules\OrangeSite\Services\CheckReport\ReportRateCheck;
+use App\Modules\OrangeSite\Services\CheckReport\ReportRechargeTypeCheck;
+use App\Modules\OrangeSite\Services\OrangeSitePromotionService;
+use App\Modules\OrangeSite\Services\OrangeSiteRechargeReportCheckContext;
+use App\Modules\OrangeSite\Services\OrangeSiteReportRateService;
+use App\Modules\Order\Models\ReportUserChargeRecord;
+use App\Modules\Trace\TraceContext;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Pipeline\Pipeline;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+use Illuminate\Support\Facades\DB;
+
+class RechargeReport implements ShouldQueue
+{
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+
+    public const QUEUE = '{orangeSite}.report.recharge';
+    public const CONNECTION = 'redis_queue';
+
+    public const PLATFORM = 'orange';
+    /**
+     * @var array
+     * <pre>
+     * [
+     *      'channelId' => 'xxx, // 站点id
+     *      'orderId' => 'xxx', // 订单id
+     *      'uid' => 'xxx', // 充值用户uid
+     * ]
+     * </pre>
+     */
+    private $reportInfo;
+
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct(array $reportInfo)
+    {
+        $this->reportInfo = $reportInfo;
+    }
+
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        $traceContext = new TraceContext();
+        \Log::info('[orangeSiteRechargeReport]开始回传', [
+            'reportInfo' => $this->reportInfo,
+            'traceInfo' => $traceContext->getTraceInfo()
+        ]);
+        $user = DB::table('users')->find(($this->reportInfo['uid'] ?? -1));
+        $order = DB::table('orders')->where([
+            ['id', '=', ($this->reportInfo['orderId'] ?? -1)],
+            ['status', '=', 'PAID']
+        ])->first();
+        $channelCallbackConfig = ChannelCallbackConfig::where('channel_id', ($this->reportInfo['channelId'] ?? -1))
+            ->where('platform', self::PLATFORM)->first();
+
+        $channelSettingRate = OrangeSiteReportRateService::getChannelSettingReportRate($user->distribution_channel_id);
+        $reportCheckContext = new OrangeSiteRechargeReportCheckContext($user, $order, $channelCallbackConfig,
+            $traceContext, $channelSettingRate);
+
+        try {
+            $reportCheckContext->checkNecessaryInfo();
+        } catch (\Exception $exception) {
+            \Log::error('[orangeSiteRechargeReport]回传基础信息有误', [
+                'message' => $exception->getMessage(),
+                'traceInfo' => $traceContext->getTraceInfo()
+            ]);
+            dingTalkAlert(config('common.orangeSite.dingTalkUrl'), "[orangeSite]订单回传基础信息有误报警:\r\n" .
+                \json_encode([
+                    'reportInfo' => $this->reportInfo,
+                    'message' => $exception->getMessage(),
+                    'traceInfo' => $traceContext->getTraceInfo()
+                ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
+            return;
+        }
+
+
+        $pipes = [
+            EnableReportCheck::class,
+            OrderAlreadyReportCheck::class,
+            ReportRechargeTypeCheck::class,
+            RechargeAmountCheck::class,
+            PromotionProtectCheck::class,
+            ReportRateCheck::class
+        ];
+
+        $content = app(Pipeline::class)
+            ->send($reportCheckContext)
+            ->through($pipes)
+            ->then(function ($content) {
+                return $content;
+            });
+        $result = $content->result;
+        \Log::info('[orangeSiteRechargeReport]回传判定结果', [
+            'result' => $result,
+            'traceInfo' => $traceContext->getTraceInfo()]);
+        if ($result['needRecord'] ?? true) {
+            $this->saveReportUserChargeReportInfo($reportCheckContext, $result);
+        }
+        if ($result['result']) {
+            $this->reportRecharge($reportCheckContext, $result);
+        }
+    }
+
+    private function reportRecharge(OrangeSiteRechargeReportCheckContext $reportCheckContext, array $checkResult)
+    {
+        $params = [
+            'uid' => $reportCheckContext->user->id,
+            'source' => 'zsy',
+            'amount' => $reportCheckContext->order->price ?? 0,
+            'link_source' => 'tiktok_event',
+        ];
+
+
+        $result = $this->reprotResultDeal(CallbackBaseService::directlyCharge($params));
+
+        $resultContent = \json_decode(($result['content'] ?? '[]'), true);
+
+        $updateInfo = [
+            'status' => $result['status'] ?? 0,
+            'request_param' => $result['request_param'] ?? '',
+            'updated_at' => date('Y-m-d H:i:s'),
+            'content' => \json_encode(array_merge($resultContent,
+                ['log_content' => ($reportCheckContext->result['content'] ?? '')]))
+        ];
+
+        \Log::info('[orangeSiteRechargeReport]请求回传接口结果:' . ($updateInfo['status'] ? '成功' : '失败'), [
+            'status' => $updateInfo['status'],
+            'traceInfo' => $reportCheckContext->traceContext->getTraceInfo()]);
+
+        DB::transaction(function () use ($reportCheckContext, $updateInfo, $checkResult, $result) {
+            DB::table('report_user_charge_records')->where([
+                'order_no' => $reportCheckContext->order->trade_no,
+                'uid' => $reportCheckContext->order->uid,
+                'link_source' => $reportCheckContext->platform,
+            ])->update($updateInfo);
+
+            if (1 == $result['status']) {
+                /**
+                 * 如果是计划保护回传成功的,只增加 orange_site_recharge_report_protect_records.current_protect_num,
+                 * 而不增加 orange_site_recharge_report_records.rate_total_num 和 orange_site_recharge_report_records.rate_success_num
+                 */
+                if ('promotionProtect' == ($checkResult['result_ok_reason'] ?? '')) {
+                    \Log::debug('[orangeSiteRechargeReport]回传成功,增加对应计划的成功数', [
+                        'traceInfo' => $reportCheckContext->traceContext->getTraceInfo(),
+                        'promotionId' => $reportCheckContext->reportInfo['promotionId']]);
+
+                    OrangeSitePromotionService::setCurrentPromotionProtectNum($reportCheckContext->user->distribution_channel_id,
+                        $reportCheckContext->reportInfo['promotionId'],
+                        $reportCheckContext->channelCallbackConfig->eligible_count ?: 0);
+                } else {
+                    OrangeSiteReportRateService::increaseSuccessNum($reportCheckContext->user->distribution_channel_id,
+                        $reportCheckContext->reportInfo['configRate']);
+                }
+
+            } else {
+                if ('promotionProtect' != ($checkResult['result_ok_reason'] ?? '')) {
+                    \Log::error('[orangeSiteRechargeReport]订单回传失败', [
+                        'traceInfo' => $reportCheckContext->traceContext->getTraceInfo()]);
+                    OrangeSiteReportRateService::increaseFailNum($reportCheckContext->user->distribution_channel_id,
+                        $reportCheckContext->reportInfo['configRate']);
+                } else {
+                    \Log::error('[orangeSiteRechargeReport]计划保护订单回传失败', [
+                        'traceInfo' => $reportCheckContext->traceContext->getTraceInfo()]);
+                }
+            }
+        });
+    }
+
+
+    /**
+     * [调用tiktok 接口回传后,数据处理逻辑]
+     * @return [type]          [description]
+     */
+    public function reprotResultDeal($result)
+    {
+        $status = 0;
+        $content = '';
+        $result_data = isset($result['data']) ? $result['data'] : [];
+        if (!empty($result_data['content'])) {
+            $content = $result_data['content'];
+            $con_arr = json_decode($content, true);
+            $content = json_encode($con_arr);
+            if (isset($con_arr['code']) && 0 == $con_arr['code']) {
+                //tiktok_event
+                $status = 1;
+            }
+        }
+        //处理请求参数
+        $request_param = '';
+        if (isset($result_data['query_params'])) {
+            if (is_string($result_data['query_params'])) {
+                $request_param = $result_data['query_params'];
+            }
+            if (is_array($result_data['query_params'])) {
+                $request_param = json_encode($result_data['query_params']);
+            }
+        }
+        $data = [
+            'status' => $status,
+            'request_param' => $request_param,
+        ];
+        if ($content) {
+            $data['content'] = $content;
+        }
+        return $data;
+    }
+
+    private function saveReportUserChargeReportInfo(OrangeSiteRechargeReportCheckContext $reportCheckContext, array $result)
+    {
+        $data = [
+            'uid' => $this->reportInfo['uid'],
+            'config_percent' => round($reportCheckContext->reportInfo['configRate'] / 100, 4),
+            'report_percent' => round($reportCheckContext->reportInfo['reportRate'] / 100, 4),
+            'order_no' => $reportCheckContext->order->trade_no,
+            'timestamp' => time(),
+            'status' => 0,
+            'type' => $result['type'] ?? '',
+            'content' => $result['content'] ?? '',
+            'channel_id' => $reportCheckContext->user->distribution_channel_id,
+            'request_param' => '',
+            'link_source' => $reportCheckContext->platform,
+        ];
+        ReportUserChargeRecord::updateOrCreate([
+            'order_no' => $data['order_no'],
+            'uid' => $data['uid'],
+            'link_source' => $data['link_source'],
+        ], $data);
+    }
+}

+ 55 - 0
app/Jobs/Callback/ReportCharge.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace App\Jobs\Callback;
+
+use App\Service\Callback\JuliangAccountReportChargeService;
+use App\Service\Util\Support\Trace\TraceContext;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldBeUnique;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+use Predis\Command\Traits\DB;
+
+class ReportCharge implements ShouldQueue
+{
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+
+    /***
+     * @var array
+     * <pre>
+     * [
+     *  'uid' => '1',
+     *  'orderId' => 1
+     *  'ranseId' => 1,
+     * ]
+     * </pre>
+     */
+    private $info;
+    /**
+     * Create a new job instance.
+     */
+    public function __construct($info)
+    {
+        $this->info = $info;
+    }
+
+    /**
+     * Execute the job.
+     */
+    public function handle(): void
+    {
+        /**
+         * 1, 所有的订单日志都记录
+         */
+        $traceContext = TraceContext::newFromParent($this->info['traceInfo']);
+        myLog('reportCharge')->info('开始处理订单回传', [
+            'orderInfo' => $this->info,
+            'traceInfo' => $traceContext->getTraceInfo()
+        ]);
+
+        $reportService = new JuliangAccountReportChargeService($this->info['uid'], $this->info['orderId'], $this->info['traceInfo']);
+        $reportService->report();
+    }
+}

+ 314 - 0
app/Service/Callback/JuliangAccountReportChargeService.php

@@ -0,0 +1,314 @@
+<?php
+
+namespace App\Service\Callback;
+
+use App\Service\Callback\Tiktok\TiktokEventReportService;
+use App\Service\Util\Support\Trace\TraceContext;
+use Illuminate\Support\Facades\DB;
+
+class JuliangAccountReportChargeService
+{
+    private $uid;
+    private $orderId;
+    /**
+     * @var TraceContext
+     */
+    private $traceContext;
+    private $order;
+    private $result;
+    private $trackRecord;
+    private $ranseInfo;
+    private $callbackConfig;
+    private $promotion;
+    private $rateConfig;
+    public function __construct($uid, $orderId, $traceInfo)
+    {
+        $this->uid = $uid;
+        $this->orderId = $orderId;
+        $this->traceContext = TraceContext::newFromParent($traceInfo);
+        $this->result = [
+            'need_report' => true,
+            'config_rate' => 0,
+            'current_rate' => 0,
+        ];
+    }
+
+    public function report() {
+        $this->order = DB::table('orders')->where('id', $this->orderId)->first();
+        $this->fillRanseInfo();
+        $this->fillTrackInfo();
+        $this->judgeOrderAlreadyDeal();
+        $this->judgeIsFirstCharge();
+        $this->judgeChargeMoney();
+        $this->judgePromotionProtect();
+        $this->judgeCallbackRate();
+
+        dump($this->result);
+        $this->reportJuliang();
+
+        $this->saveRecord();
+    }
+
+    private function saveRecord() {
+        if($this->result['save_record'] ?? true) {
+            $now = date('Y-m-d H:i:s');
+            if($this->result['info_type'] == 'promotion_protect') {
+                if($this->result['report_success']) {
+                    $protectedRecord = DB::table('juliang_account_promotion_protect_record')
+                        ->where(['adv_promotion_id' => $this->trackRecord->adv_promotion_id,
+                            'optimizer_uid' => $this->promotion->uid, 'is_enabeld' => 1])
+                        ->first();
+                    if($protectedRecord) {
+                        DB::table('juliang_account_promotion_protect_record')
+                            ->where(['id' => $protectedRecord->id])
+                        ->where('protected_num', '<', $this->callbackConfig->protect_num)
+                            ->increment('protected_num', 1, ['updated_at' => $now]);
+                    } else {
+                        DB::table('juliang_account_promotion_protect_record')
+                            ->insert([
+                                'adv_promotion_id' => $this->trackRecord->adv_promotion_id,
+                                'optimizer_uid' => $this->promotion->uid,
+                                'advertiser_id' => $this->callbackConfig->adv_account_id,
+                                'protected_num' => 1,
+                                'is_enabled' => 1,
+                                'created_at' => $now,
+                                'updated_at' => $now,
+                            ]);
+                    }
+                }
+            }
+
+            $chargeRecord = DB::table('callback_report_charge_record')->where([
+                'order_no' => $this->order->trade_no
+            ])->first();
+            if(!$chargeRecord) {
+                DB::table('callback_report_charge_record')
+                    ->insert([
+                        'uid' => $this->uid,
+                        'order_id' => $this->orderId,
+                        'order_no' => $this->order->trade_no,
+                        'optimizer_uid' => $this->promotion->uid,
+                        'filter_type' => $this->result['info_type'] ?? 'ok',
+                        'filter_reason' => $this->result['info_str'] ?? '允许回传',
+                        'report_success' => intval($this->result['report_success'] ?? false),
+                        'report_result' => \json_encode($this->result['report_result'] ?? []),
+                        'created_at' => $now,
+                        'updated_at' => $now,
+                        'advertiser_id' => $this->trackRecord->advertiser_id,
+                        'adv_promotion_id' => $this->trackRecord->adv_promotion_id,
+                        'config_rate' => $this->result['config_rate'],
+                        'current_rate' => $this->result['current_rate'],
+                    ]);
+            } else {
+                DB::table('callback_report_charge_record')
+                    ->where(['id' => $chargeRecord->id])
+                    ->update([
+                        'uid' => $this->uid,
+                        'order_id' => $this->orderId,
+                        'order_no' => $this->order->trade_no,
+                        'optimizer_uid' => $this->promotion->uid,
+                        'filter_type' => $this->result['info_type'] ?? 'ok',
+                        'filter_reason' => $this->result['info_str'] ?? '允许回传',
+                        'report_success' => intval($this->result['report_success'] ?? false),
+                        'report_result' => \json_encode($this->result['report_result'] ?? []),
+                        'updated_at' => $now,
+                        'advertiser_id' => $this->trackRecord->advertiser_id,
+                        'adv_promotion_id' => $this->trackRecord->adv_promotion_id,
+                        'config_rate' => $this->result['config_rate'],
+                        'current_rate' => $this->result['current_rate'],
+                    ]);
+            }
+
+            DB::table('juliang_account_rate_config_log')
+                ->where(['id' => $this->rateConfig->id])
+                ->update([
+                    'report_count' => $this->rateConfig->report_count + intval($this->result['report_success']),
+                    'unreport_count' => $this->rateConfig->report_count + intval(!$this->result['report_success']),
+                    'total_count' => $this->rateConfig->total_count + 1,
+                    'updated_at' => $now,
+                ]);
+        }
+    }
+
+    private function judgeCallbackRate(){
+        if(!$this->result['need_report']) {
+            return;
+        }
+
+        $this->rateConfig = DB::table('juliang_account_rate_config_log')
+            ->where(['company_uid' => $this->promotion->uid,
+                'account_id' => $this->trackRecord->advertiser_id, 'is_enabled' => 1])
+            ->first();
+        if(!$this->rateConfig) {
+            $this->result['need_report'] = false;
+            $this->result['info_type'] = 'no_rate_config';
+            $this->result['info_str'] = '没有可用的回传比率设置';
+            return;
+        }
+        $configRate = $this->rateConfig->config_per;
+        if(0 == $configRate) {
+            $this->result['need_report'] = false;
+            $this->result['info_type'] = 'rate_eq_0';
+            $this->result['info_str'] = '设定比例为0不执行回传';
+            return;
+        }
+        $currentTotalCount = $this->rateConfig->total_count;
+        $currentReportCount = $this->rateConfig->report_count;
+        if(0 == $currentTotalCount) {
+            $currentRate = 0;
+        } else {
+            $currentRate = min(1, round($currentReportCount / $currentTotalCount, 4)) * 100;
+        }
+        if($currentRate <= $configRate) {
+            $this->result['need_report'] = true;
+            $this->result['config_rate'] = $configRate;
+            $this->result['current_rate'] = $currentRate;
+        } else {
+            $this->result['need_report'] = false;
+            $this->result['info_type'] = 'rate_filter';
+            $this->result['info_type'] = '比率过滤';
+            $this->result['config_rate'] = $configRate;
+            $this->result['current_rate'] = $currentRate;
+        }
+    }
+
+    private function judgePromotionProtect() {
+        if(!$this->result['need_report']) {
+            return;
+        }
+        if(0 == $this->callbackConfig->protect_num) {
+            $this->result['need_report'] = true;
+        } else {
+            $advPromotionId = $this->trackRecord->adv_promotion_id;
+            $protectedRecord = DB::table('juliang_account_promotion_protect_record')
+                ->where(['adv_promotion_id' => $advPromotionId, 'optimizer_uid' => $this->promotion->uid, 'is_enabled' => 1])
+                ->first();
+            if(!$protectedRecord || $protectedRecord->protected_num < $this->callbackConfig->protect_num) {
+                $this->result['need_report'] = true;
+                $this->result['save_record'] = true;
+                $this->result['save_record_type'] = 'protect_promotion';
+                $this->result['info_type'] = 'promotion_protect';
+                $this->result['info_str'] = '计划被保护';
+            }
+        }
+    }
+
+
+    private function judgeOrderAlreadyDeal() {
+        if(!$this->result['need_report']) {
+            return;
+        }
+        if(DB::table('callback_report_charge_record')
+            ->where(['order_id'  => $this->order->id])
+            ->exists()) {
+            $this->result['need_report'] = false;
+            $this->result['save_record'] = false;
+            $this->result['info_type'] = 'order_exists';
+            $this->result['info_str'] = '订单已经处理';
+        }
+    }
+
+    private function judgeChargeMoney() {
+        if(!$this->result['need_report']) {
+            return;
+        }
+        $moneyRange = $this->callbackConfig->moneyRange;
+        if($this->order->price >= $moneyRange->min && $this->order->price <= $moneyRange->max) {
+            $this->result['need_report'] = true;
+        } else {
+            $this->result['need_report'] = false;
+            $this->result['info_type'] = 'money_filter';
+            $this->result['info_str'] = sprintf('充值金额[%s元]不在回传区间范围内[%s - %s]', $this->order->price,
+            $moneyRange->minStr, $moneyRange->maxStr);
+        }
+    }
+
+
+    private function judgeIsFirstCharge() {
+        if(!$this->result['need_report']) {
+            return;
+        }
+        $isFirstCharge = DB::table('orders')
+            ->where([
+                'uid' => $this->uid,
+                'status' => 'paid',
+                'promotion_id' => $this->trackRecord->ranse_id,
+            ])->where('created_at', '>=', $this->trackRecord->ranse_start_at)
+            ->where('created_at', '<=', $this->trackRecord->ranse_end_at)
+            ->where('id', '<', $this->orderId)
+            ->count() == 0;
+        if($isFirstCharge) {
+            $this->result['need_report'] = true;
+        } else {
+            $this->result['need_report'] = false;
+            $this->result['info_type'] = 'neq_first_charge';
+            $this->result['info_str'] = '非首充';
+        }
+    }
+
+    private function fillRanseInfo() {
+        $ranseId = $this->order->promotion_id;
+        $this->promotion = DB::table('promotions')->where('id', $ranseId)->first();
+        $this->callbackConfig = DB::table('juliang_account_callback_config')
+            ->where(['id' => $this->promotion->callback_config_id])
+            ->first();
+        if(!$this->callbackConfig) {
+            $this->result['need_report'] = false;
+            $this->result['info_type'] = 'no_callback_config';
+            $this->result['info_str'] = '没有回传配置';
+            return;
+        }
+        if(0 == $this->callbackConfig->state) {
+            $this->result['need_report'] = false;
+            $this->result['info_type'] = 'callback_close';
+            $this->result['info_str'] = '回传配置关闭';
+            return;
+        }
+
+
+        $min = max(0, $this->callbackConfig->min_money);
+        if (0 >= $this->callbackConfig->max_money) {
+            $max = PHP_INT_MAX;
+            $maxStr = '不限';
+        } else {
+            $max = $this->callbackConfig->max_money;
+            $maxStr = $max . '元';
+        }
+
+        $minStr = $min . '元';
+        $this->callbackConfig->moneyRange = (object)compact('min', 'max', 'minStr', 'maxStr');
+    }
+    private function reportJuliang() {
+        if(!$this->result['need_report']) {
+            return;
+        }
+        $service = new TiktokEventReportService;
+        $reportResult = $service->reportCharge((object)[
+            'callback' => $this->trackRecord->callback
+        ]);
+        $reportContent = \json_decode($reportResult->content, true);
+        if($reportResult['result']  && 0 == $reportContent['code']) {
+            $this->result['report_success'] = true;
+        } else {
+            $this->result['report_success'] = false;
+        }
+        $this->result['report_result'] = $reportResult;
+        $this->result['need_report'] = false;
+    }
+    private function fillTrackInfo() {
+        if(!$this->result['need_report']) {
+            return;
+        }
+        $this->trackRecord = DB::table('callback_report_ranse_record')
+            ->where(['uid' => $this->uid, 'ranse_id' => $this->order->promotion_id])
+            ->where('ranse_start_at', '<=', $this->order->created_at)
+            ->where('ranse_end_at', '>=', $this->order->created_at)
+            ->orderBy('id', 'desc')
+            ->first();
+        if(!$this->trackRecord) {
+            $this->result['need_report'] = false;
+            $this->result['info_type'] = 'no_track_info';
+            $this->result['info_str'] = '无匹配用户';
+        }
+    }
+}

+ 36 - 0
app/Service/Callback/Services/CheckReport/EnableReportCheck.php

@@ -0,0 +1,36 @@
+<?php
+
+
+namespace App\Modules\OrangeSite\Services\CheckReport;
+
+
+use App\Modules\OrangeSite\Services\OrangeSiteRechargeReportCheckContext;
+use App\Modules\OrangeSite\Services\OrangeSiteReportSettingService;
+use Closure;
+use Log;
+
+class EnableReportCheck
+{
+    /**
+     * @param $context OrangeSiteRechargeReportCheckContext
+     * @param Closure $next
+     * @return mixed
+     */
+    public function handle($context, Closure $next)
+    {
+        if (OrangeSiteReportSettingService::isEnableReport($context->user->distribution_channel_id)) {
+            return $next($context);
+        } else {
+            Log::debug('[orangeSiteRechargeReport]站点没有开启回传', ['traceInfo' => $context->traceContext->getTraceInfo()]);
+            $context->result = [
+                'result' => false,
+                'type' => '',
+                'needRecord' => false,
+                'firstReason' => 'EnableReportCheck',
+                'content' => '没有开启回传'
+            ];
+            return $context;
+        }
+
+    }
+}

+ 45 - 0
app/Service/Callback/Services/CheckReport/OrderAlreadyReportCheck.php

@@ -0,0 +1,45 @@
+<?php
+
+
+namespace App\Modules\OrangeSite\Services\CheckReport;
+
+
+use App\Modules\OrangeSite\Services\OrangeSiteRechargeReportCheckContext;
+use Closure;
+use Illuminate\Support\Facades\DB;
+use Log;
+
+class OrderAlreadyReportCheck
+{
+    /**
+     * 订单已经存在 report_user_charge_records,那么就不需要处理了.也不需要重复记录入该表了.
+     * @param $context OrangeSiteRechargeReportCheckContext
+     * @param Closure $next
+     * @return mixed
+     */
+    public function handle($context, Closure $next)
+    {
+
+        $exists = DB::table('report_user_charge_records')
+            ->where([
+                ['uid', '=', $context->user->id],
+                ['order_no', '=', $context->order->trade_no],
+                ['link_source', '=', $context->platform]
+            ])->exists();
+        if ($exists) {
+            Log::debug('[orangeSiteRechargeReport]订单已经处理', [
+                'traceInfo' => $context->traceContext->getTraceInfo()
+            ]);
+            $context->result = [
+                'result' => false,
+                'type' => '',
+                'needRecord' => false,
+                'firstReason' => 'OrderAlreadyReportCheck',
+                'content' => '订单已经处理'
+            ];
+            return $context;
+        } else {
+            return $next($context);
+        }
+    }
+}

+ 63 - 0
app/Service/Callback/Services/CheckReport/PromotionProtectCheck.php

@@ -0,0 +1,63 @@
+<?php
+
+
+namespace App\Modules\OrangeSite\Services\CheckReport;
+
+
+use App\Modules\OrangeSite\Services\OrangeSitePromotionService;
+use App\Modules\OrangeSite\Services\OrangeSiteRechargeReportCheckContext;
+use Closure;
+use Log;
+
+class PromotionProtectCheck
+{
+    /**
+     * 判断计划保护数
+     * @param $context OrangeSiteRechargeReportCheckContext
+     * @param Closure $next
+     * @return mixed
+     */
+    public function handle($context, Closure $next)
+    {
+        $channelPromotionProtectSettingNum = $context->channelCallbackConfig->eligible_count ?: 0;
+        $userBindInfo = OrangeSitePromotionService::getUserBindInfo($context->user->id);
+        if (OrangeSitePromotionService::judgeNoUserMatch($userBindInfo)) {
+            Log::debug('[orangeSiteRechargeReport]当前用户没有匹配的report_user_bind_record', [
+                'traceInfo' => $context->traceContext->getTraceInfo()]);
+            $context->result = [
+                'result' => false,
+                'type' => 'no_user_match',
+                'firstReason' => 'PromotionProtectCheck',
+                'content' => '无用户匹配'
+            ];
+            return $context;
+        }
+        $promotionId = $userBindInfo->promotion_id ?? '';
+        if (!$promotionId) {
+            \Log::debug('[orangeSiteRechargeReport]当前用户没有匹配的promotionId,不走计划保护数', [
+                'traceInfo' => $context->traceContext->getTraceInfo()
+            ]);
+            return $next($context);
+        }
+        $context->reportInfo['promotionId'] = $promotionId;
+        $currentProtectNum = OrangeSitePromotionService::getCurrentPromotionProtectNum($context->user->distribution_channel_id,
+            $promotionId);
+        if ($currentProtectNum < $channelPromotionProtectSettingNum) {
+            Log::debug('[orangeSiteRechargeReport]处于计划保护数之内', [
+                'promotionId' => $promotionId,
+                'currentProtectNum' => $currentProtectNum,
+                'channelPromotionProtectSettingNum' => $channelPromotionProtectSettingNum,
+                'traceInfo' => $context->traceContext->getTraceInfo()]);
+            // !! 如果满足计划保护,那么 result 为true ,直接回传,不经过比例
+            $context->result['result'] = true;
+            $context->result['type'] = 'protected_eligible';
+            $context->result['firstReason'] = 'PromotionProtectCheck';
+            $context->result['result_ok_reason'] = 'promotionProtect';
+            $context->result['content'] = '订单被计划保护';
+            return $context;
+        } else {
+            return $next($context);
+        }
+
+    }
+}

+ 39 - 0
app/Service/Callback/Services/CheckReport/RechargeAmountCheck.php

@@ -0,0 +1,39 @@
+<?php
+
+
+namespace App\Modules\OrangeSite\Services\CheckReport;
+
+
+use App\Modules\OrangeSite\Services\OrangeSiteRechargeReportCheckContext;
+use App\Modules\OrangeSite\Services\OrangeSiteReportSettingService;
+use Closure;
+use Log;
+
+class RechargeAmountCheck
+{
+    /**
+     * 判断金额回传区间
+     * @param $context OrangeSiteRechargeReportCheckContext
+     * @param Closure $next
+     * @return mixed
+     */
+    public function handle($context, Closure $next)
+    {
+        $rechargeAmountRange = OrangeSiteReportSettingService::buildRechargeAmountRange($context->channelCallbackConfig);
+        if (OrangeSiteReportSettingService::judgeRechargeAmountRange($context->order->price, $rechargeAmountRange)) {
+            return $next($context);
+        } else {
+            Log::debug('[orangeSiteRechargeReport]充值金额不满足配置区间', [
+                'orderPrice' => $context->order->price,
+                'rechargeAmountRange' => $rechargeAmountRange,
+                'traceInfo' => $context->traceContext->getTraceInfo()]);
+            $context->result = [
+                'type' => 'amount_filter',
+                'firstReason' => 'RechargeAmountCheck',
+                'result' => false,
+                'content' => "订单金额:{$context->order->price}元不满足回传金额上下限[{$rechargeAmountRange['minStr']} - {$rechargeAmountRange['maxStr']}]",
+            ];
+            return $context;
+        }
+    }
+}

+ 48 - 0
app/Service/Callback/Services/CheckReport/ReportRateCheck.php

@@ -0,0 +1,48 @@
+<?php
+
+
+namespace App\Modules\OrangeSite\Services\CheckReport;
+
+
+use App\Modules\OrangeSite\Services\OrangeSiteRechargeReportCheckContext;
+use App\Modules\OrangeSite\Services\OrangeSiteReportRateService;
+use Closure;
+use Illuminate\Support\Facades\DB;
+use Log;
+
+class ReportRateCheck
+{
+    /**
+     * @param $context OrangeSiteRechargeReportCheckContext
+     * @param Closure $next
+     * @return mixed
+     */
+    public function handle($context, Closure $next)
+    {
+        $currentRate = OrangeSiteReportRateService::getCurrentChannelReportRate($context->user->distribution_channel_id);
+        $channelSettingRate = $context->reportInfo['configRate'];
+        $context->reportInfo['reportRate'] = $currentRate;
+        if ($currentRate <= $channelSettingRate) {
+            Log::debug('[orangeSiteRechargeReport]订单通过比例', [
+                'traceInfo' => $context->traceContext->getTraceInfo(),
+                'channelSettingRate' => $channelSettingRate,
+                'currentRate' => $currentRate]);
+            return $next($context);
+        } else {
+            Log::debug('[orangeSiteRechargeReport]订单被比例过滤', [
+                'traceInfo' => $context->traceContext->getTraceInfo(),
+                'channelSettingRate' => $channelSettingRate,
+                'currentRate' => $currentRate]);
+            DB::transaction(function () use ($context, $channelSettingRate) {
+                OrangeSiteReportRateService::increaseFailNum($context->user->distribution_channel_id, $channelSettingRate);
+            });
+            $context->result = [
+                'result' => false,
+                'firstReason' => 'ReportRateCheck',
+                'type' => 'percent_filter',
+                'content' => '比例过滤'
+            ];
+            return $context;
+        }
+    }
+}

+ 54 - 0
app/Service/Callback/Services/CheckReport/ReportRechargeTypeCheck.php

@@ -0,0 +1,54 @@
+<?php
+
+
+namespace App\Modules\OrangeSite\Services\CheckReport;
+
+
+use App\Modules\Callback\Services\CallbackBaseService;
+use App\Modules\OrangeSite\Services\OrangeSiteRechargeReportCheckContext;
+use Closure;
+use Log;
+
+class ReportRechargeTypeCheck
+{
+    /**
+     * 判断订单,是不是满足如下回传类型之一:
+     * 回传当天自然日注册且首次充值的用户
+     * 回传24小时注册且首次充值的用户
+     * @param $context OrangeSiteRechargeReportCheckContext
+     * @param Closure $next
+     * @return mixed
+     */
+    public function handle($context, Closure $next)
+    {
+        $typeMap = [
+            'no_current_day_register' => '非充值当天注册用户',
+            'no_register_24_charge' => '非充值24小时内注册用户'
+        ];
+        $reportType = $context->channelCallbackConfig->callback_type;
+        $result = CallbackBaseService::callbackType($reportType, $context->user->created_at);
+        if (is_array($result) && array_key_exists('result', $result) && (false == $result['result'])) {
+            Log::debug('[orangeSiteRechargeReport]用户注册时间不满足回传类型配置',
+                ['traceInfo' => $context->traceContext->getTraceInfo()]);
+            $result['content'] = $typeMap[$result['type']] ?? '';
+            $context->result = $result;
+            $context->result['firstReason'] = 'ReportRechargeTypeCheck';
+            $context->result['type'] = trim($result['type'], 'no_');
+            return $context;
+        }
+
+        if ($context->order->pay_type != 1) {
+            Log::debug('[orangeSiteRechargeReport]当前订单非用户首充',
+                ['traceInfo' => $context->traceContext->getTraceInfo()]);
+            $context->result = [
+                'result' => false,
+                'type' => 'feedback_user',
+                'firstReason' => 'ReportRechargeTypeCheck',
+                'content' => '用户非首充',
+            ];
+            return $context;
+        }
+
+        return $next($context);
+    }
+}

+ 91 - 0
app/Service/Callback/Services/OrangeSitePromotionService.php

@@ -0,0 +1,91 @@
+<?php
+
+
+namespace App\Modules\OrangeSite\Services;
+
+
+use Illuminate\Support\Facades\DB;
+
+class OrangeSitePromotionService
+{
+    public static function getCurrentPromotionProtectNum($channelId, $promotionId)
+    {
+        return DB::table('orange_site_recharge_report_protect_records')
+                ->where([
+                    ['channel_id', '=', $channelId],
+                    ['use_type', '=', 'promotionId'],
+                    ['use_type_id', '=', $promotionId],
+                    ['is_enabled', '=', 1]
+                ])->orderBy('id', 'desc')
+                ->first()->current_protect_num ?? 0;
+    }
+
+    public static function setCurrentPromotionProtectNum($channelId, $promotionId, $maxNum)
+    {
+        $now = date('Y-m-d H:i:s');
+        $exists = DB::table('orange_site_recharge_report_protect_records')
+            ->where([
+                'channel_id' => $channelId,
+                'use_type' => 'promotionId',
+                'use_type_id' => $promotionId,
+                'is_enabled' => 1
+            ])->exists();
+        if ($exists) {
+            DB::table('orange_site_recharge_report_protect_records')
+                ->where([
+                    'channel_id' => $channelId,
+                    'use_type' => 'promotionId',
+                    'use_type_id' => $promotionId,
+                    'is_enabled' => 1,
+                ])->where('current_protect_num', '<', $maxNum)
+                ->increment('current_protect_num', 1, ['updated_at' => $now]);
+        } else {
+            DB::table('orange_site_recharge_report_protect_records')
+                ->insert([
+                    'channel_id' => $channelId,
+                    'use_type' => 'promotionId',
+                    'use_type_id' => $promotionId,
+                    'is_enabled' => 1,
+                    'current_protect_num' => 1,
+                    'created_at' => $now,
+                    'updated_at' => $now,
+                ]);
+        }
+
+    }
+
+    /**
+     * 获取用户匹配信息
+     * @param $uid
+     * @return mixed
+     */
+    public static function getUserBindInfo($uid)
+    {
+        return DB::table('report_user_bind_records')
+            ->where(['uid' => $uid])
+            ->select('platform', 'uid', 'promotion_id')
+            ->first();
+    }
+
+    /**
+     * 判断是否有用户匹配track , true-无用户匹配,false-有用户匹配
+     * @param $userBindRecord
+     * @return bool
+     */
+    public static function judgeNoUserMatch($userBindRecord)
+    {
+        if (!$userBindRecord) {
+            return true;
+        }
+        if ('no_track' == $userBindRecord->platform) {
+            return true;
+        }
+
+        if ('orange' != $userBindRecord->platform) {
+            return true;
+        }
+
+        return false;
+
+    }
+}

+ 83 - 0
app/Service/Callback/Services/OrangeSiteRechargeReportCheckContext.php

@@ -0,0 +1,83 @@
+<?php
+
+
+namespace App\Modules\OrangeSite\Services;
+
+
+use App\Modules\Trace\TraceContext;
+use RuntimeException;
+
+class OrangeSiteRechargeReportCheckContext
+{
+    /**
+     * @var string 平台,橙子建站
+     */
+    public $platform = 'orange';
+    /**
+     * @var 订单的用户 表 users 记录
+     */
+    public $user;
+    /**
+     * @var 订单信息 表 orders 记录
+     */
+    public $order;
+    /**
+     * @var 订单关联站点的回传配置信息  表 channel_callback_configs 记录
+     */
+    public $channelCallbackConfig;
+    /**
+     * @var array 判定过程中的附带信息.
+     * <pre>
+     * [
+     *  'configRate' => 32, // 当前站点设置的回传比例为 32%
+     *  'reportRate' => 43,  // 当前站点实际的回传比例[在本单回传之前的比例] 为 43%
+     * ]
+     * </pre>
+     */
+    public $reportInfo;
+    /**
+     * @var array 根据配置,对当前订单的回传与否的判定结果
+     * <pre>
+     * [
+     *      'result' => true|false  , // true-表示订单回传,false-订单不回传
+     *      'type' => 'xxx',  // 不回传的原因:amount_filter-金额过滤
+     *      'content' => 'xxx', // 具体的提示信息
+     *      'needRecord' => true | false , // true-需要记录到report_user_charge_records表中,false-不记录
+     * ]
+     * <pre>
+     */
+    public $result;
+    /**
+     * @var TraceContext traceInfo 信息
+     */
+    public $traceContext;
+
+    public function __construct($user, $order, $channelCallbackConfig, $traceContext, $channelSettingRate = 0)
+    {
+        $this->user = $user;
+        $this->order = $order;
+        $this->channelCallbackConfig = $channelCallbackConfig;
+        $this->traceContext = $traceContext;
+        $this->result = [
+            'result' => true,
+            'needRecord' => true,
+        ];
+        $this->reportInfo = [
+            'configRate' => $channelSettingRate,
+            'reportRate' => 0
+        ];
+    }
+
+    public function checkNecessaryInfo()
+    {
+        if (!$this->user) {
+            throw new RuntimeException('用户信息不存在');
+        }
+        if (!$this->order) {
+            throw new RuntimeException('订单信息不存在');
+        }
+        if (!$this->channelCallbackConfig) {
+            throw new RuntimeException('站点回传配置信息不存在');
+        }
+    }
+}

+ 110 - 0
app/Service/Callback/Services/OrangeSiteReportRateService.php

@@ -0,0 +1,110 @@
+<?php
+
+
+namespace App\Modules\OrangeSite\Services;
+
+
+use Illuminate\Support\Facades\DB;
+use Redis;
+
+class OrangeSiteReportRateService
+{
+    public static function getCurrentChannelReportRate($channelId)
+    {
+        $record = DB::table('orange_site_recharge_report_records')
+            ->where([
+                ['channel_id', '=', $channelId],
+                ['is_enabled', '=', 1]
+            ])->orderBy('id', 'desc')
+            ->first();
+        if (!$record) {
+            return 0;
+        } else {
+            if (!$record->rate_total_num) {
+                return 0;
+            }
+            return min(1, round($record->rate_success_num / $record->rate_total_num, 4)) * 100;
+        }
+    }
+
+    public static function increaseSuccessNum($channelId, $channelSettingRate)
+    {
+        $now = date('Y-m-d H:i:s');
+        $exists = DB::table('orange_site_recharge_report_records')
+            ->where([
+                ['channel_id', '=', $channelId],
+                ['is_enabled', '=', 1]
+            ])->orderBy('id', 'desc')->first();
+
+        if ($exists) {
+            DB::table('orange_site_recharge_report_records')
+                ->where([
+                    ['channel_id', '=', $channelId],
+                    ['is_enabled', '=', 1]
+                ])->update([
+                    'rate_total_num' => $exists->rate_total_num + 1,
+                    'rate_success_num' => $exists->rate_success_num + 1,
+                    'updated_at' => $now
+                ]);
+        } else {
+            DB::table('orange_site_recharge_report_records')
+                ->insert([
+                    'channel_id' => $channelId,
+                    'is_enabled' => 1,
+                    'rate_total_num' => 1,
+                    'rate_success_num' => 1,
+                    'channel_setting_rate' => $channelSettingRate,
+                    'created_at' => $now,
+                    'updated_at' => $now
+                ]);
+        }
+
+    }
+
+    public static function increaseFailNum($channelId, $channelSettingRate)
+    {
+        $now = date('Y-m-d H:i:s');
+        $exists = DB::table('orange_site_recharge_report_records')
+            ->where([
+                ['channel_id', '=', $channelId],
+                ['is_enabled', '=', 1]
+            ])->orderBy('id', 'desc')->first();
+
+        if ($exists) {
+            DB::table('orange_site_recharge_report_records')
+                ->where([
+                    ['channel_id', '=', $channelId],
+                    ['is_enabled', '=', 1]
+                ])->update([
+                    'rate_total_num' => $exists->rate_total_num + 1,
+                    'updated_at' => $now
+                ]);
+        } else {
+            DB::table('orange_site_recharge_report_records')
+                ->insert([
+                    'channel_id' => $channelId,
+                    'is_enabled' => 1,
+                    'rate_total_num' => 1,
+                    'rate_success_num' => 0,
+                    'channel_setting_rate' => $channelSettingRate,
+                    'created_at' => $now,
+                    'updated_at' => $now,
+                ]);
+        }
+
+    }
+
+    /**
+     * 获取站点当前的橙子建站的回传比例
+     * @param $channelId
+     * @return int
+     */
+    public static function getChannelSettingReportRate($channelId)
+    {
+        $rate = Redis::hget('channel:setting:' . $channelId, 'orange_site_report_rate');
+        if (!$rate) {
+            $rate = 100;
+        }
+        return (int)$rate;
+    }
+}

+ 49 - 0
app/Service/Callback/Services/OrangeSiteReportSettingService.php

@@ -0,0 +1,49 @@
+<?php
+
+
+namespace App\Modules\OrangeSite\Services;
+
+use Redis;
+
+class OrangeSiteReportSettingService
+{
+    public static function isEnableReport($channelId)
+    {
+        return Redis::hget('channel:setting:' . $channelId, 'orange_site_report');
+    }
+
+    public static function judgeRechargeAmountRange($orderPrice, $rechargeAmountRange)
+    {
+        return $orderPrice >= $rechargeAmountRange['min'] && $orderPrice <= $rechargeAmountRange['max'];
+    }
+
+    /**
+     * 站点回传配置关于金额区间的提取
+     * 1, 如果没有设置回传配置,默认 30-PHP_INT_MAX
+     * 2, 如果 设置的上限为0 ,那么默认为 PHP_INT_MAX
+     * @param $channelCallBackConfig 站点回传配置信息
+     * @return array
+     */
+    public static function buildRechargeAmountRange($channelCallBackConfig)
+    {
+        if (!$channelCallBackConfig) {
+            $min = 30;
+            $max = PHP_INT_MAX;
+            $maxStr = '不限';
+        } else {
+            $min = max(0, $channelCallBackConfig->min_callback_money);
+            if (0 >= $channelCallBackConfig->max_callback_money) {
+                $max = PHP_INT_MAX;
+                $maxStr = '不限';
+            } else {
+                $max = $channelCallBackConfig->max_callback_money;
+                $maxStr = $max . '元';
+            }
+        }
+
+        $minStr = $min . '元';
+        return compact('min', 'max', 'minStr', 'maxStr');
+    }
+
+
+}

+ 2 - 1
composer.json

@@ -9,7 +9,8 @@
         "guzzlehttp/guzzle": "^7.2",
         "laravel/framework": "^10.8",
         "laravel/sanctum": "^3.2",
-        "laravel/tinker": "^2.8"
+        "laravel/tinker": "^2.8",
+        "predis/predis": "^2.1"
     },
     "require-dev": {
         "fakerphp/faker": "^1.9.1",

+ 59 - 1
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "8e6e7fe5fd9d19060838a2fee2857ca4",
+    "content-hash": "6a77229974aee7110d16b94db07815e2",
     "packages": [
         {
             "name": "brick/math",
@@ -2272,6 +2272,64 @@
             "time": "2023-02-25T19:38:58+00:00"
         },
         {
+            "name": "predis/predis",
+            "version": "v2.1.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/predis/predis.git",
+                "reference": "a77a43913a74f9331f637bb12867eb8e274814e5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/predis/predis/zipball/a77a43913a74f9331f637bb12867eb8e274814e5",
+                "reference": "a77a43913a74f9331f637bb12867eb8e274814e5",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^3.3",
+                "phpstan/phpstan": "^1.9",
+                "phpunit/phpunit": "^8.0 || ~9.4.4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Predis\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Till Krüss",
+                    "homepage": "https://till.im",
+                    "role": "Maintainer"
+                }
+            ],
+            "description": "A flexible and feature-complete Redis client for PHP.",
+            "homepage": "http://github.com/predis/predis",
+            "keywords": [
+                "nosql",
+                "predis",
+                "redis"
+            ],
+            "support": {
+                "issues": "https://github.com/predis/predis/issues",
+                "source": "https://github.com/predis/predis/tree/v2.1.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sponsors/tillkruss",
+                    "type": "github"
+                }
+            ],
+            "time": "2023-03-02T18:32:04+00:00"
+        },
+        {
             "name": "psr/container",
             "version": "2.0.2",
             "source": {

+ 1 - 1
config/database.php

@@ -148,7 +148,7 @@ return [
             'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
         ],
 
-        'default' => [
+        'default1' => [
             'url' => env('REDIS_URL'),
             'host' => env('REDIS_HOST', '127.0.0.1'),
             'username' => env('REDIS_USERNAME'),

+ 2 - 2
config/queue.php

@@ -13,7 +13,7 @@ return [
     |
     */
 
-    'default' => env('QUEUE_CONNECTION', 'sync'),
+    'default' => env('QUEUE_CONNECTION', 'redis'),
 
     /*
     |--------------------------------------------------------------------------
@@ -64,7 +64,7 @@ return [
 
         'redis' => [
             'driver' => 'redis',
-            'connection' => 'default',
+            'connection' => 'default1',
             'queue' => env('REDIS_QUEUE', 'default'),
             'retry_after' => 90,
             'block_for' => null,

+ 2 - 2
tests/Jobs/Callback/JuliangAccountReportRanseTest.php

@@ -14,8 +14,8 @@ class JuliangAccountReportRanseTest extends \Tests\TestCase
             'uid' => 1,
             'ranseId' => 2,
             'ip' => '192.168.1.123',
-            'ranseStartAt' => '2023-05-18 14:14:14',
-            'ranseEndAt' => '2023-05-18 15:14:14',
+            'ranseStartAt' => '1684396482',
+            'ranseEndAt' => '1684407282',
             'traceInfo' => (new TraceContext())->getTraceInfo()
         ]);
         $job->handle();

+ 41 - 0
tests/Jobs/Callback/ReportChargeTest.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace Tests\Jobs\Callback;
+
+use App\Jobs\Callback\ReportCharge;
+use App\Service\Util\Support\Trace\TraceContext;
+use Illuminate\Support\Facades\DB;
+use PHPUnit\Framework\TestCase;
+
+class ReportChargeTest extends \Tests\TestCase
+{
+
+    public function testHandle()
+    {
+//        $this->prepareData();
+        $traceContext = new TraceContext();
+        $reportCharge = new ReportCharge(['uid' => 1000, 'orderId' => 10001,
+            'traceInfo' => $traceContext->getTraceInfo()]);
+        $reportCharge->handle();
+    }
+
+    private function prepareData() {
+        $promotionId = 100;
+        $optimizerId = 5;
+        $orders = [
+            [
+                'id' => 10000,
+                'uid' => 1000,
+                'promotion_id' => $promotionId,
+                'user_id' => $optimizerId,
+                'price' => rand(1, 1000),
+                'pay_product_id' => 1,
+                'status' => 'PAID',
+                'trade_no' => uniqid(),
+                'created_at' => '2023-05-19 10:00:01',
+                'updated_at' => date('Y-m-d H:i:s'),
+            ]
+        ];
+        DB::table('orders')->insert($orders);
+    }
+}