Quellcode durchsuchen

Add platform audit flow for Lakala applications
关键变更包括:
1. 商户端拉卡拉进件提交改为进入平台审核;
2. 新增平台端(pt)拉卡拉进件列表、详情、保存、上传、驳回、通过接口;
3. 服务层新增平台(pt)审核状态和审核流转逻辑;
4. 优化图片上传校验、OSS 路径格式化、附件拉卡拉 URL 使用,以及微信/支付宝费率配置。

shizhongqi vor 1 Monat
Ursprung
Commit
9495b40ce3

+ 7 - 4
app-ghs/controllers/LakalaAccountController.php

@@ -128,10 +128,13 @@ class LakalaAccountController extends BaseController
     {
         $form = new FormDataForm();
         $form->loadAndValidate();
-        util::complete();
-
-        // $submit = LakalaAccountService::submit((int)$form->id, $this->mainId, $this->shopId, $form->getFormData());
-        // util::success($submit);
+        $submit = LakalaAccountService::submitForPlatformAudit(
+            (int)$form->id,
+            $this->mainId,
+            $this->shopId,
+            $form->getFormData()
+        );
+        util::success($submit);
     }
 
     public function actionRefresh()

+ 60 - 0
app-pt/controllers/LakalaApplicationController.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace pt\controllers;
+
+use bizGhs\lakala\services\LakalaAccountService;
+use common\components\util;
+use Yii;
+
+class LakalaApplicationController extends BaseController
+{
+    public function actionList()
+    {
+        $status = Yii::$app->request->get('status', '');
+        $keyword = Yii::$app->request->get('keyWord', '');
+        util::success(LakalaAccountService::platformList($status, $keyword));
+    }
+
+    public function actionDetail()
+    {
+        $id = (int)Yii::$app->request->get('id', 0);
+        util::success(LakalaAccountService::platformDetail($id));
+    }
+
+    public function actionSave()
+    {
+        $id = (int)Yii::$app->request->post('id', 0);
+        $formData = Yii::$app->request->post('formData', []);
+        $currentStep = (int)Yii::$app->request->post('currentStep', 5);
+        if (is_string($formData)) {
+            $formData = json_decode($formData, true);
+        }
+        if (!is_array($formData)) {
+            util::fail('表单数据格式错误');
+        }
+        util::success(LakalaAccountService::platformSave($id, $formData, $currentStep));
+    }
+
+    public function actionUploadFile()
+    {
+        $id = (int)Yii::$app->request->post('id', 0);
+        $content = Yii::$app->request->post('content', '');
+        $imgType = Yii::$app->request->post('imgType', '');
+        $isOcr = (int)Yii::$app->request->post('isOcr', 0);
+        util::success(LakalaAccountService::platformUpload($id, $content, $imgType, $isOcr));
+    }
+
+    public function actionReject()
+    {
+        $id = (int)Yii::$app->request->post('id', 0);
+        $reason = Yii::$app->request->post('reason', '');
+        LakalaAccountService::platformReject($id, $reason);
+        util::complete('已驳回');
+    }
+
+    public function actionApprove()
+    {
+        $id = (int)Yii::$app->request->post('id', 0);
+        util::success(LakalaAccountService::platformApprove($id));
+    }
+}

+ 1 - 0
biz-ghs/lakala/classes/LakalaApplicationClass.php

@@ -13,6 +13,7 @@ class LakalaApplicationClass extends BaseClass
 
     const STATUS_DRAFT = 'DRAFT'; // 草稿
     const STATUS_CONTRACT_PENDING = 'CONTRACT_PENDING'; // 合同待签
+    const STATUS_PLATFORM_AUDIT = 'PLATFORM_AUDIT'; // 后台审核
     const STATUS_WAIT_AUDIT = 'WAIT_AUDIT'; // 待审核
     const STATUS_REJECT = 'REJECT'; // 审核驳回
     const STATUS_APPROVED = 'APPROVED'; // 审核通过

+ 204 - 21
biz-ghs/lakala/services/LakalaAccountService.php

@@ -7,6 +7,7 @@ use bizGhs\lakala\classes\LakalaApplicationClass;
 use bizGhs\lakala\classes\LakalaApplicationVersionClass;
 use bizGhs\lakala\classes\LakalaOperationClass;
 use bizGhs\shop\classes\ShopClass;
+use common\components\imgUtil;
 use common\components\lakala\LakalaOnboardingClient;
 use common\components\oss;
 use common\components\stringUtil;
@@ -17,7 +18,7 @@ class LakalaAccountService
 {
     const RATE = '0.33';
     const MCC = '5992';
-    const ACTIVITY_ID = '208';
+    const ACTIVITY_ID = '208'; // WECHAT_PAY -- 专业化扫码
 
     public static function list($mainId, $shopId)
     {
@@ -34,20 +35,24 @@ class LakalaAccountService
         foreach ($applications as &$application) {
             $application['formData'] = self::decode($application['formData'] ?? '');
         }
-        foreach ($accounts as &$account) {
-            if ($account['wxAuthorizeState'] == 1 && $account['zfbAuthorizeState'] == 1) {
+        foreach ($accounts as $account) {
+            if ($account->wxAuthorizeState == 1 && $account->zfbAuthorizeState == 1) {
                 continue;
             }
             self::refreshAuthorizeStates($account);
         }
+        $accountList = [];
+        foreach ($accounts as $account) {
+            $accountList[] = $account->attributes;
+        }
 
-        return ['applications' => $applications, 'accounts' => $accounts];
+        return ['applications' => $applications, 'accounts' => $accountList];
     }
 
     public static function detail($id, $mainId, $shopId)
     {
         $application = self::application($id, $mainId, $shopId);
-        $application['formData'] = self::decode($application['formData'] ?? '');
+        $application['formData'] = self::formatFormImages(self::decode($application['formData'] ?? ''));
         return $application;
     }
 
@@ -108,32 +113,43 @@ class LakalaAccountService
             util::fail('图片格式错误');
         }
         $content = base64_decode(substr($base64, strpos($base64, ',') + 1), true);
-        if ($content === false || strlen($content) > 10 * 1024 * 1024) {
-            util::fail('图片内容无效或超过10MB');
+        if ($content === false || strlen($content) > 5 * 1024 * 1024) {
+            util::fail('图片内容无效或超过5MB');
         }
-        $object = 'lakala-onboarding/' . $mainId . '/' . date('Ymd') . '/' . $id . '/' . stringUtil::uniqueFileName() . '.jpg';
-        oss::uploadContent($object, $content);
 
         $info = getimagesizefromstring($content);
+        if ($info === false) {
+            util::fail('图片内容无效');
+        }
         $allowMime = [
             'image/jpeg',
             'image/png',
             'image/gif',
-            'image/webp'
+            'image/webp',
         ];
-        if (in_array($info['mime'], $allowMime)) {
-            Yii::info('允许上传 -- ' . $info['mime']);
-        } else {
-            Yii::error('不允许上传');
+        if (!in_array($info['mime'], $allowMime, true)) {
+            Yii::error('不允许上传:' . $info['mime']);
             util::fail('图片出错,不允许上传');
         }
 
+        if (strlen($content) > 5 * 1024 * 1024) {
+            util::fail('图片超过5MB');
+        }
+
+        $extension = self::imageExtension($info['mime']);
+        $path = 'lakala-onboarding/' . $mainId . '/' . date('Ymd') . '/' . $id . '/' . stringUtil::uniqueFileName() . '.' . $extension;
+        oss::uploadContent($path, $content);
+
         $response = (new LakalaOnboardingClient())->upload($base64, $imgType, (bool)$ocr);
+        $lakalaUrl = $response['data']['url'] ?? '';
         return [
-            'ossPath' => $object,
+            'ossPath' => self::formatOssPath($path),
+            'lakalaUrl' => $lakalaUrl,
             'imgType' => $imgType,
             'batchNo' => $response['data']['batch_no'] ?? '',
-            'url' => $response['data']['url'] ?? '',
+            'ocrStatus' => $response['data']['status'] ?? '',
+            'ocrResult' => $response['data']['result'] ?? null,
+            'url' => $lakalaUrl,
             'status' => $response['data']['status'] ?? '',
             'ocr' => $response['data']['result'] ?? null,
         ];
@@ -224,6 +240,121 @@ class LakalaAccountService
         return self::detail($application->id, $mainId, $shopId);
     }
 
+    public static function submitForPlatformAudit($id, $mainId, $shopId, array $form)
+    {
+        $application = self::application($id, $mainId, $shopId, true);
+        if ($application->type === LakalaApplicationClass::TYPE_SETTLEMENT_CHANGE) {
+            return self::submitSettlementChange($application, $form);
+        }
+        self::validateOnboarding($form);
+        if ($application->contractStatus !== 'COMPLETED' || empty($application->contractNo)) {
+            util::fail('请先完成电子合同签约并刷新签约状态');
+        }
+        $application->formData = self::encode($form);
+        $application->status = LakalaApplicationClass::STATUS_PLATFORM_AUDIT;
+        $application->rejectReason = '';
+        $application->currentStep = 5;
+        $application->updateTime = date('Y-m-d H:i:s');
+        $application->save();
+        return self::detail($application->id, $mainId, $shopId);
+    }
+
+    public static function platformList($status = '', $keyword = '')
+    {
+        $where = [];
+        if ($status !== '' && $status !== '-1') {
+            $where['status'] = $status;
+        }
+        if ($keyword !== '') {
+            if (is_numeric($keyword)) {
+                $where['id'] = (int)$keyword;
+            } else {
+                $where['formData'] = ['like', $keyword];
+            }
+        }
+        $result = LakalaApplicationClass::getList('*', $where, ['id' => SORT_DESC]);
+        foreach ($result['list'] as &$item) {
+            $form = self::formatFormImages(self::decode($item['formData'] ?? ''));
+            $item['formData'] = $form;
+            $item['merchantName'] = $form['merchant']['businessName'] ?? ($form['license']['licenseName'] ?? '');
+            $item['legalName'] = $form['license']['legalName'] ?? '';
+            $item['accountName'] = $form['settlement']['accountName'] ?? '';
+        }
+        return $result;
+    }
+
+    public static function platformDetail($id)
+    {
+        $application = LakalaApplicationClass::getById($id);
+        if (empty($application)) {
+            util::fail('没有找到进件申请');
+        }
+        $application['formData'] = self::formatFormImages(self::decode($application['formData'] ?? ''));
+        return $application;
+    }
+
+    public static function platformSave($id, array $form, $step = 5)
+    {
+        $application = LakalaApplicationClass::getById($id, true);
+        if (empty($application)) {
+            util::fail('没有找到进件申请');
+        }
+        if (!in_array($application->status, [
+            LakalaApplicationClass::STATUS_DRAFT,
+            LakalaApplicationClass::STATUS_PLATFORM_AUDIT,
+            LakalaApplicationClass::STATUS_REJECT,
+            LakalaApplicationClass::STATUS_CONTRACT_PENDING,
+        ], true)) {
+            util::fail('当前申请不能修改');
+        }
+        $application->formData = self::encode($form);
+        $application->currentStep = max(1, min(5, (int)$step));
+        $application->updateTime = date('Y-m-d H:i:s');
+        $application->save();
+        return self::platformDetail($id);
+    }
+
+    public static function platformUpload($id, $base64, $imgType, $ocr = 0)
+    {
+        $application = LakalaApplicationClass::getById($id);
+        if (empty($application)) {
+            util::fail('没有找到进件申请');
+        }
+        return self::upload($id, $application['mainId'], $application['shopId'], $base64, $imgType, (int)$ocr);
+    }
+
+    public static function platformReject($id, $reason)
+    {
+        $application = LakalaApplicationClass::getById($id, true);
+        if (empty($application)) {
+            util::fail('没有找到进件申请');
+        }
+        if ($application->status !== LakalaApplicationClass::STATUS_PLATFORM_AUDIT) {
+            util::fail('仅后台审核中的申请可以驳回');
+        }
+        $application->status = LakalaApplicationClass::STATUS_REJECT;
+        $application->rejectReason = mb_substr($reason ?: '后台审核驳回', 0, 500);
+        $application->updateTime = date('Y-m-d H:i:s');
+        $application->save();
+    }
+
+    public static function platformApprove($id)
+    {
+        $application = LakalaApplicationClass::getById($id, true);
+        if (empty($application)) {
+            util::fail('没有找到进件申请');
+        }
+        if ($application->status !== LakalaApplicationClass::STATUS_PLATFORM_AUDIT) {
+            util::fail('仅后台审核中的申请可以提交拉卡拉');
+        }
+        return self::submit(
+            $application->id,
+            $application->mainId,
+            $application->shopId,
+            self::decode($application->formData)
+        );
+    }
+
     public static function refresh($id, $mainId, $shopId)
     {
         $application = self::application($id, $mainId, $shopId, true);
@@ -231,7 +362,7 @@ class LakalaAccountService
             return self::refreshChange($application);
         }
         if (empty($application->customerNo) && empty($application->merchantNo)) {
-            util::fail('申请尚未提交');
+            util::fail('申请尚未提交(流程未走完)');
         }
 
         $client = new LakalaOnboardingClient();
@@ -568,7 +699,7 @@ class LakalaAccountService
             'account_id_dt_start' => $s['accountIdStart'] ?: $l['legalIdStart'],
             'account_id_dt_end' => $s['accountIdEnd'] ?: $l['legalIdEnd'],
             'settle_type' => 'D1', 'settlement_type' => 'AUTOMATIC', 'contract_no' => $contractNo,
-            'biz_content' => ['term_num' => '1', 'mcc' => self::MCC, 'activity_id' => self::activityId(), 'fees' => $fees],
+            'biz_content' => ['term_num' => '1', 'mcc' => self::MCC, 'activity_id' => self::activityId(), 'fees' => $fees], //业务拓展信息 -- 会创建终端(此处创建:专业化扫码)
             'attchments' => self::attachments($form),
         ];
     }
@@ -672,6 +803,8 @@ class LakalaAccountService
     private static function termFees()
     {
         return [
+            ['fee' => 0.33, 'fee_type' => 'WECHAT'],
+            ['fee' => 0.33, 'fee_type' => 'ALIPAY'],
             ['fee' => 0.5, 'top_fee' => 20, 'fee_type' => 'DEBIT_CARD'],
             ['fee' => 0.6, 'fee_type' => 'CREDIT_CARD'],
         ];
@@ -690,16 +823,22 @@ class LakalaAccountService
     {
         $result = [];
         foreach ($attachments as $attachment) {
-            if (empty($attachment['url']) || empty($attachment['imgType'])) {
+            $lakalaUrl = self::attachmentLakalaUrl($attachment);
+            if ($lakalaUrl === '' || empty($attachment['imgType'])) {
                 continue;
             }
             $result[] = $merchantShape
-                ? ['id' => $attachment['url'], 'type' => $attachment['imgType']]
-                : ['img_path' => $attachment['url'], 'img_type' => $attachment['imgType']];
+                ? ['id' => $lakalaUrl, 'type' => $attachment['imgType']]
+                : ['img_path' => $lakalaUrl, 'img_type' => $attachment['imgType']];
         }
         return $result;
     }
 
+    private static function attachmentLakalaUrl(array $attachment)
+    {
+        return trim((string)($attachment['lakalaUrl'] ?? ($attachment['url'] ?? '')));
+    }
+
     private static function validateContract(array $form)
     {
         self::validateOnboarding($form, false);
@@ -759,6 +898,12 @@ class LakalaAccountService
             if (!in_array($type, $present, true)) {
                 util::fail($message);
             }
+            $matched = array_values(array_filter($attachments, function ($attachment) use ($type) {
+                return ($attachment['imgType'] ?? '') === $type && self::attachmentLakalaUrl($attachment) !== '';
+            }));
+            if (empty($matched)) {
+                util::fail($message);
+            }
         }
     }
 
@@ -819,6 +964,17 @@ class LakalaAccountService
         return $result;
     }
 
+    private static function imageExtension($mime)
+    {
+        $map = [
+            'image/jpeg' => 'jpg',
+            'image/png' => 'png',
+            'image/gif' => 'gif',
+            'image/webp' => 'webp',
+        ];
+        return $map[$mime] ?? 'png';
+    }
+
     private static function encode($value)
     {
         return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
@@ -830,4 +986,31 @@ class LakalaAccountService
         $decoded = json_decode((string)$value, true);
         return is_array($decoded) ? $decoded : [];
     }
+
+    private static function formatFormImages(array $form)
+    {
+        return self::formatOssPathFields($form);
+    }
+
+    private static function formatOssPathFields(array $data)
+    {
+        foreach ($data as $key => $value) {
+            if (is_array($value)) {
+                $data[$key] = self::formatOssPathFields($value);
+                continue;
+            }
+            if ($key === 'ossPath' && is_string($value) && $value !== '') {
+                $data[$key] = self::formatOssPath($value);
+            }
+        }
+        return $data;
+    }
+
+    private static function formatOssPath($path)
+    {
+        if (strpos($path, 'http://') === 0 || strpos($path, 'https://') === 0) {
+            return $path;
+        }
+        return imgUtil::groupImg($path);
+    }
 }

+ 2 - 2
vendor/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'project',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
-        'reference' => 'b776bfb8a18718ac9414cdb2aaa7345c1561d7fd',
+        'reference' => 'f1e3ec543d0db57be30aff94a80728e4451ce6c2',
         'name' => 'yiisoft/yii2-app-advanced',
         'dev' => true,
     ),
@@ -753,7 +753,7 @@
             'type' => 'project',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
-            'reference' => 'b776bfb8a18718ac9414cdb2aaa7345c1561d7fd',
+            'reference' => 'f1e3ec543d0db57be30aff94a80728e4451ce6c2',
             'dev_requirement' => false,
         ),
         'yiisoft/yii2-bootstrap' => array(