diff --git a/addons/shopro_huifu/application/admin/view/shopro_huifu/config/index.html b/addons/shopro_huifu/application/admin/view/shopro_huifu/config/index.html index cd55c19..7f84ae0 100644 --- a/addons/shopro_huifu/application/admin/view/shopro_huifu/config/index.html +++ b/addons/shopro_huifu/application/admin/view/shopro_huifu/config/index.html @@ -46,6 +46,13 @@ + + + 需配置 crontab: shopro_huifu:onboard-auto-submit + + + + 只读 diff --git a/addons/shopro_huifu/application/admin/view/shopro_huifu/onboard/index.html b/addons/shopro_huifu/application/admin/view/shopro_huifu/onboard/index.html index 3cf659b..aa31cae 100644 --- a/addons/shopro_huifu/application/admin/view/shopro_huifu/onboard/index.html +++ b/addons/shopro_huifu/application/admin/view/shopro_huifu/onboard/index.html @@ -21,7 +21,7 @@
汇付个人进件 - 先审核进件资料,业务审核通过后再提交汇付 + 业务审核通过后由定时任务自动提交汇付;失败满 24 小时自动重提(可手动「提交汇付/重提」)
diff --git a/addons/shopro_huifu/command/OnboardAutoSubmit.php b/addons/shopro_huifu/command/OnboardAutoSubmit.php new file mode 100644 index 0000000..d087d48 --- /dev/null +++ b/addons/shopro_huifu/command/OnboardAutoSubmit.php @@ -0,0 +1,39 @@ +setName('shopro_huifu:onboard-auto-submit') + ->addOption('limit', 'l', Option::VALUE_OPTIONAL, '每轮最多处理条数', 30) + ->addOption('dry-run', null, Option::VALUE_NONE, '只扫描不调用汇付') + ->setDescription('Auto submit Huifu onboard: new drafts immediately, failed retry after cooldown'); + } + + protected function execute(Input $input, Output $output) + { + $dryRun = (bool) $input->getOption('dry-run'); + $limit = intval($input->getOption('limit')); + $output->writeln('=== 汇付进件自动提交 ===' . ($dryRun ? ' [dry-run]' : '')); + + $stats = (new HuifuOnboardAutoSubmit())->run($limit, $dryRun); + $output->writeln('扫描: ' . $stats['scanned']); + $output->writeln('首次提交: ' . $stats['submit_now']); + $output->writeln('失败重提: ' . $stats['retry']); + $output->writeln('跳过: ' . $stats['skipped']); + $output->writeln('执行失败: ' . $stats['failed']); + foreach ($stats['details'] as $line) { + $output->writeln(' · ' . $line); + } + + return $stats['failed'] > 0 ? 1 : 0; + } +} diff --git a/addons/shopro_huifu/install.sql b/addons/shopro_huifu/install.sql index 4ef2a93..96f9dbe 100644 --- a/addons/shopro_huifu/install.sql +++ b/addons/shopro_huifu/install.sql @@ -105,5 +105,7 @@ INSERT IGNORE INTO `__PREFIX__shopro_huifu_config` (`name`, `value`, `group`, `d ('onboard_cash_type', 'D1', 'onboard', '取现类型'), ('onboard_cash_fix_amt', '0.03', 'onboard', '取现固定手续费'), ('onboard_cash_fee_rate', '0', 'onboard', '取现费率'), -('onboard_skill_source', 'hfps/1.3.5;hfms/1.0.2', 'onboard', '进件SDK skill_source'); +('onboard_skill_source', 'hfps/1.3.5;hfms/1.0.2', 'onboard', '进件SDK skill_source'), +('onboard_auto_submit', '1', 'onboard', '定时自动提交汇付进件(0关1开)'), +('onboard_auto_retry_hours', '24', 'onboard', '进件失败后自动重提间隔(小时)'); diff --git a/addons/shopro_huifu/library/onboard/HuifuOnboardAdminView.php b/addons/shopro_huifu/library/onboard/HuifuOnboardAdminView.php index 5aa4d3c..7120d13 100644 --- a/addons/shopro_huifu/library/onboard/HuifuOnboardAdminView.php +++ b/addons/shopro_huifu/library/onboard/HuifuOnboardAdminView.php @@ -302,6 +302,15 @@ class HuifuOnboardAdminView return $parts ? implode('、', $parts) : '无进件角色'; } + /** + * 分销/区域公司业务审核是否已通过(status=normal) + */ + public static function isBusinessApproved(array $data) + { + $biz = self::businessStatus($data); + return ($biz['status'] ?? '') === 'normal'; + } + protected static function businessStatus(array $data) { $roleType = $data['role_type'] ?? ''; diff --git a/addons/shopro_huifu/library/onboard/HuifuOnboardAutoSubmit.php b/addons/shopro_huifu/library/onboard/HuifuOnboardAutoSubmit.php new file mode 100644 index 0000000..3d4586a --- /dev/null +++ b/addons/shopro_huifu/library/onboard/HuifuOnboardAutoSubmit.php @@ -0,0 +1,146 @@ + 0, + 'submit_now' => 0, + 'retry' => 0, + 'skipped' => 0, + 'failed' => 0, + 'details' => [], + ]; + + if (!HuifuOnboardGate::isOnboardRequired()) { + $stats['details'][] = 'skip: 汇付进件未启用'; + return $stats; + } + + if (!$this->isAutoSubmitEnabled()) { + $stats['details'][] = 'skip: onboard_auto_submit=0'; + return $stats; + } + + $limit = max(1, min(100, (int) $limit)); + $retryAfter = $this->getRetryAfterSeconds(); + $service = new HuifuOnboardService(); + $now = time(); + + $rows = Onboard::where('onboard_status', 'in', [ + Onboard::STATUS_DRAFT, + Onboard::STATUS_FAILED, + Onboard::STATUS_REJECTED, + ])->order('id asc')->limit($limit * 3)->select(); + + foreach ($rows as $row) { + if ($stats['submit_now'] + $stats['retry'] >= $limit) { + break; + } + $stats['scanned']++; + $data = $row->toArray(); + $action = $this->resolveAction($data, $now, $retryAfter); + if ($action === null) { + $stats['skipped']++; + continue; + } + + $payload = json_decode($row['payload'] ?? '', true); + if (!$payload || !is_array($payload)) { + $stats['skipped']++; + $stats['details'][] = 'id=' . $row['id'] . ' skip: 无进件资料 payload'; + continue; + } + + $line = 'id=' . $row['id'] . ' uid=' . $row['user_id'] . ' ' . $row['role_type'] + . ' action=' . $action; + if ($dryRun) { + $stats[$action === 'submit_now' ? 'submit_now' : 'retry']++; + $stats['details'][] = $line . ' (dry-run)'; + continue; + } + + try { + if ($action === 'submit_now') { + $service->submitFromDraft($row['user_id'], $row['role_type'], $row['role_ref_id']); + $stats['submit_now']++; + } else { + $service->resubmit($row['user_id'], $row['role_type'], $row['role_ref_id'], $payload); + $stats['retry']++; + } + $stats['details'][] = $line . ' ok'; + } catch (\Throwable $e) { + $stats['failed']++; + $msg = HuifuOnboardError::translate($e->getMessage()); + Log::error('[shopro_huifu] onboard auto-submit fail ' . $line . ' ' . $e->getMessage()); + $stats['details'][] = $line . ' fail: ' . $msg; + } + } + + return $stats; + } + + /** + * @return string|null submit_now|retry + */ + protected function resolveAction(array $data, $now, $retryAfter) + { + if (!HuifuOnboardAdminView::isBusinessApproved($data)) { + return null; + } + + $status = (string) ($data['onboard_status'] ?? ''); + $updatedAt = intval($data['updatetime'] ?? 0); + + if ($status === Onboard::STATUS_DRAFT) { + if ($this->hasSubmittedToHuifu($data)) { + return null; + } + return 'submit_now'; + } + + if (in_array($status, [Onboard::STATUS_FAILED, Onboard::STATUS_REJECTED], true)) { + if ($updatedAt <= 0 || ($now - $updatedAt) < $retryAfter) { + return null; + } + return 'retry'; + } + + return null; + } + + protected function hasSubmittedToHuifu(array $data) + { + if (!empty($data['basic_req_seq_id']) || !empty($data['busi_req_seq_id'])) { + return true; + } + if (!empty($data['huifu_id'])) { + return true; + } + return false; + } + + protected function isAutoSubmitEnabled() + { + return HuifuConfig::getInstance()->get('onboard_auto_submit', '1') !== '0'; + } + + protected function getRetryAfterSeconds() + { + $hours = (float) HuifuConfig::getInstance()->get('onboard_auto_retry_hours', '24'); + if ($hours <= 0) { + $hours = 24; + } + return (int) round($hours * 3600); + } +} diff --git a/addons/shopro_huifu/public/assets/js/backend/shopro_huifu/config.js b/addons/shopro_huifu/public/assets/js/backend/shopro_huifu/config.js index 1243ae2..ba277d8 100644 --- a/addons/shopro_huifu/public/assets/js/backend/shopro_huifu/config.js +++ b/addons/shopro_huifu/public/assets/js/backend/shopro_huifu/config.js @@ -25,7 +25,9 @@ define(['jquery', 'bootstrap', 'backend', 'form'], function ($, undefined, Backe onboard_settle_cycle: 'D1', onboard_settle_batch_no: '300', onboard_settle_fee_rate: '0.05', - onboard_notify_url: '' + onboard_notify_url: '', + onboard_auto_submit: '1', + onboard_auto_retry_hours: '24' } }, mounted: function () { diff --git a/addons/shopro_huifu/upgrade_huifu_onboard_auto.sql b/addons/shopro_huifu/upgrade_huifu_onboard_auto.sql new file mode 100644 index 0000000..9c48e8f --- /dev/null +++ b/addons/shopro_huifu/upgrade_huifu_onboard_auto.sql @@ -0,0 +1,4 @@ +-- 汇付进件自动提交配置(已有站点执行一次) +INSERT IGNORE INTO `__PREFIX__shopro_huifu_config` (`name`, `value`, `group`, `description`) VALUES +('onboard_auto_submit', '1', 'onboard', '定时自动提交汇付进件(0关1开)'), +('onboard_auto_retry_hours', '24', 'onboard', '进件失败后自动重提间隔(小时)'); diff --git a/application/admin/view/shopro_huifu/config/index.html b/application/admin/view/shopro_huifu/config/index.html index 3cb954a..186a940 100644 --- a/application/admin/view/shopro_huifu/config/index.html +++ b/application/admin/view/shopro_huifu/config/index.html @@ -52,6 +52,13 @@
+ + + 需配置 crontab: shopro_huifu:onboard-auto-submit + + + + 只读 diff --git a/application/admin/view/shopro_huifu/onboard/index.html b/application/admin/view/shopro_huifu/onboard/index.html index 3cf659b..aa31cae 100644 --- a/application/admin/view/shopro_huifu/onboard/index.html +++ b/application/admin/view/shopro_huifu/onboard/index.html @@ -21,7 +21,7 @@
汇付个人进件 - 先审核进件资料,业务审核通过后再提交汇付 + 业务审核通过后由定时任务自动提交汇付;失败满 24 小时自动重提(可手动「提交汇付/重提」)
diff --git a/application/command.php b/application/command.php index bef3885..afc97df 100644 --- a/application/command.php +++ b/application/command.php @@ -26,6 +26,7 @@ return [ 'addons\shopro_huifu\command\ProbeWechatOnboarding', 'addons\shopro_huifu\command\QuerySplitConfig', 'addons\shopro_huifu\command\OnboardPoll', + 'addons\shopro_huifu\command\OnboardAutoSubmit', 'addons\shopro_huifu\command\ImportBankBranch', 'addons\shopro_wechat_trade\command\InitOrderPath', 'addons\shopro_wechat_trade\command\ProbeOrderPath', diff --git a/public/assets/js/backend/shopro_huifu/config.js b/public/assets/js/backend/shopro_huifu/config.js index 5f82ecd..8c14254 100644 --- a/public/assets/js/backend/shopro_huifu/config.js +++ b/public/assets/js/backend/shopro_huifu/config.js @@ -24,7 +24,9 @@ define(['jquery', 'bootstrap', 'backend', 'form'], function ($, undefined, Backe onboard_required: '1', onboard_settle_cycle: 'D1', onboard_settle_batch_no: '300', - onboard_notify_url: '' + onboard_notify_url: '', + onboard_auto_submit: '1', + onboard_auto_retry_hours: '24' } }, mounted: function () {