Parcourir la source

Merge branch 'redesign‌-260706' of git.huaml.com:zhh/huahuibao into redesign‌-260706

shizhongqi il y a 5 jours
Parent
commit
0afb57f413

+ 1 - 1
app-ghs/controllers/ConsoleController.php

@@ -173,7 +173,7 @@ class ConsoleController extends BaseController
         //$notice[] = ['title' => '如果你发现一个问题很重要,一直没给你处理,在小群多反应几次,用反应次数强调重要性,目前批发店多反应问题多,都在排队。', 'action' => '', 'page' => ''];
         //$notice[] = ['title' => '新功能:客户端和后台使用跑腿的流程', 'action' => '', 'page' => '/admin/book/detail?id=use_pt'];
         //$notice[] = ['title' => '明年1月1号起,所有人介绍新批发店的分红政策,将会统一调整,请知悉', 'action' => '', 'page' => ''];
-        //$notice[] = ['title' => '急事请连续打二次电话 15280215347', 'action' => '', 'page' => ''];
+        //$notice[] = ['title' => '急事请连续打二次电话 15280215347', 'action' => '', 'page' => ''];
 
         $mainId = $this->mainId;
         $respond = StatSaleClass::profile($mainId);

+ 189 - 0
app-ghs/controllers/MainInviteController.php

@@ -0,0 +1,189 @@
+<?php
+/**
+ * ghsApp 门店邀请 / 我的分佣(邀请花店开店)
+ * 数据与 hd 共用 xhMainInvite、xhMainInviteFlow 等表
+ */
+namespace ghs\controllers;
+
+use bizHd\shop\classes\MainInviteClass;
+use bizHd\shop\classes\MainInviteCommissionClass;
+use bizHd\shop\classes\MainInviteFlowClass;
+use bizHd\wx\classes\WxOpenClass;
+use common\components\dict;
+use common\components\dirUtil;
+use common\components\imgUtil;
+use common\components\miniUtil;
+use common\components\stringUtil;
+use common\components\util;
+use Yii;
+
+class MainInviteController extends BaseController
+{
+
+    public $guestAccess = [];
+
+    /**
+     * 我的分佣首页
+     */
+    public function actionIndex()
+    {
+        $mainId = (int) $this->mainId;
+        $invite = MainInviteClass::ensureByMainId($mainId);
+        $inviteData = [
+            'inviteCode' => '',
+            'inviteCodeMask' => '',
+            'totalCommission' => '0.00',
+            'ableCommission' => '0.00',
+            'commissionAmount' => '0.00',
+            'hdDiscountAmount' => '0.00',
+            'inviteCount' => 0,
+        ];
+        if (!empty($invite)) {
+            $code = $invite['inviteCode'] ?? '';
+            $inviteData = [
+                'inviteCode' => $code,
+                'inviteCodeMask' => MainInviteCommissionClass::maskInviteCode($code),
+                'totalCommission' => round((float) ($invite['totalCommission'] ?? 0), 2),
+                'ableCommission' => round((float) ($invite['ableCommission'] ?? 0), 2),
+                'commissionAmount' => round((float) ($invite['commissionAmount'] ?? 0), 2),
+                'hdDiscountAmount' => round((float) ($invite['hdDiscountAmount'] ?? 0), 2),
+                'inviteCount' => (int) ($invite['inviteCount'] ?? 0),
+            ];
+        }
+
+        $recentRaw = MainInviteCommissionClass::getRecentList($mainId, 5);
+        $recentList = [];
+        foreach ($recentRaw as $row) {
+            $recentList[] = MainInviteCommissionClass::formatRecentRow($row);
+        }
+
+        util::success([
+            'invite' => $inviteData,
+            'recentList' => $recentList,
+        ]);
+    }
+
+    /**
+     * 邀约花店列表
+     */
+    public function actionInviteShopList()
+    {
+        $get = Yii::$app->request->get();
+        $keyword = trim($get['keyword'] ?? '');
+        $data = MainInviteCommissionClass::getInviteShopList($this->mainId, $keyword);
+        util::success($data);
+    }
+
+    /**
+     * 佣金变动明细分页
+     */
+    public function actionCommissionList()
+    {
+        $list = MainInviteFlowClass::getFlowList($this->mainId);
+        util::success($list);
+    }
+
+    /**
+     * 可提现佣金全额存入中央钱包余额
+     */
+    public function actionDepositBalance()
+    {
+        $mainId = (int) $this->mainId;
+        $shopId = (int) $this->shopId;
+        $shopName = trim((string) ($this->shop->name ?? ''));
+        try {
+            $result = MainInviteClass::depositAbleCommissionToWallet($mainId, $shopId, $shopName);
+            $invite = MainInviteClass::getByMainId($mainId);
+            $inviteData = [
+                'totalCommission' => round((float) ($invite['totalCommission'] ?? 0), 2),
+                'ableCommission' => round((float) ($invite['ableCommission'] ?? 0), 2),
+            ];
+            util::success(array_merge($result, ['invite' => $inviteData]));
+        } catch (\Exception $e) {
+            util::fail($e->getMessage());
+        }
+    }
+
+    /**
+     * 生成 hdApp 邀请开店海报(参考 main/get-mall-poster:太阳码 + OSS 背景合成)
+     * GET:可选 env_version=develop|trial|release
+     */
+    public function actionGetInviteHdPoster()
+    {
+        $mainId = (int) $this->mainId;
+        $invite = MainInviteClass::ensureByMainId($mainId);
+        $inviteCode = trim((string) ($invite['inviteCode'] ?? ''));
+        if ($inviteCode === '') {
+            util::fail('暂无邀请码');
+        }
+
+        $envVersion = miniUtil::normalizeMiniEnvVersion(Yii::$app->request->get('env_version', 'release'));
+
+        $posterCacheKey = 'wx_mini_invite_hd_poster_v3_' . $mainId . '_' . $inviteCode . '_' . $envVersion;
+        $cachedPoster = Yii::$app->redis->executeCommand('GET', [$posterCacheKey]);
+        if (!empty($cachedPoster)) {
+            $respond = json_decode($cachedPoster, true);
+            if (!empty($respond['imgUrl']) && !empty($respond['miniCodeUrl'])) {
+                util::success($respond);
+            }
+        }
+
+        $merchant = WxOpenClass::getWxInfo();
+        $page = 'pagesClient/official/applyInvite';
+        $ptStyle = dict::getDict('ptStyle', 'hd');
+        $scene = 'inviteCode=' . $inviteCode;
+        // 开通页可能尚未发布到体验版,check_path=false 仍可生成码图;并校验本地 JPEG 避免缓存错误 JSON
+        $imgUrl = $this->ensureValidInviteHdMiniCode($merchant, $page, $scene, $ptStyle, $envVersion);
+        $miniCodeUrl = imgUtil::groupImg($imgUrl);
+        $titleBase64 = stringUtil::ossBase64('邀请开店');
+        $prefix = imgUtil::getPrefix();
+        $url = $prefix . 'shop/miniCodeBg.jpg?x-oss-process=image';
+        $miniCodeBase64 = stringUtil::ossBase64($imgUrl);
+        $url .= '/watermark,image_' . $miniCodeBase64 . ',g_north,x_0,y_40';
+        $url .= '/watermark,text_' . $titleBase64 . ',g_north,x_0,y_530,size_50';
+        $respond = [
+            'imgUrl' => $url,
+            'miniCodeUrl' => $miniCodeUrl,
+            'inviteCode' => $inviteCode,
+        ];
+
+        Yii::$app->redis->executeCommand('SETEX', [$posterCacheKey, 86400 * 3, json_encode($respond)]);
+
+        util::success($respond);
+    }
+
+    /**
+     * 生成 hd 邀请开店小程序码相对路径,无效缓存文件时删除并重试一次
+     */
+    private function ensureValidInviteHdMiniCode($merchant, $page, $scene, $ptStyle, $envVersion)
+    {
+        $relative = miniUtil::generateUnlimitedMiniCode($merchant, $page, $scene, $ptStyle, $envVersion, false);
+        $fullPath = dirUtil::getImgDir() . $relative;
+        if ($this->isValidJpegFile($fullPath)) {
+            return $relative;
+        }
+        if (is_file($fullPath)) {
+            @unlink($fullPath);
+        }
+        $relative = miniUtil::generateUnlimitedMiniCode($merchant, $page, $scene, $ptStyle, $envVersion, false);
+        $fullPath = dirUtil::getImgDir() . $relative;
+        if (!$this->isValidJpegFile($fullPath)) {
+            if (is_file($fullPath)) {
+                @unlink($fullPath);
+            }
+            util::fail('小程序码生成失败,请确认花店端已上传体验版并包含开通页');
+        }
+        return $relative;
+    }
+
+    /** 判断本地文件是否为有效 JPEG(微信失败时可能写入 JSON 文本) */
+    private function isValidJpegFile($fullPath)
+    {
+        if (!is_file($fullPath) || filesize($fullPath) < 100) {
+            return false;
+        }
+        $head = @file_get_contents($fullPath, false, null, 0, 2);
+        return $head === "\xFF\xD8";
+    }
+
+}

+ 0 - 6
app-ghs/controllers/TestController.php

@@ -238,12 +238,6 @@ class TestController extends BaseController
 
     public function actionHtml()
     {
-        $c = new Converter(null, [
-            "fmt" => "png",
-            "in" => "/tmp/a.html",
-            "out" => "/tmp/test.png"
-        ]);
-        $c->convert();
     }
 
     //欠款的查询与跟踪 ssh 20240103

+ 42 - 9
app-hd/controllers/ApplyController.php

@@ -3,6 +3,7 @@
 namespace hd\controllers;
 
 use biz\shop\classes\ShopClass;
+use biz\renew\classes\RenewClass;
 use biz\shop\services\ShopAdminService;
 use biz\sj\classes\SjClass;
 use bizGhs\admin\classes\AdminClass;
@@ -81,8 +82,9 @@ class ApplyController extends BaseController
     {
         $get = Yii::$app->request->get();
         $inviteCode = trim($get['inviteCode'] ?? '');
-        $data = MainInviteClass::resolveRegisterFeePreview($inviteCode);
-        util::success($data);
+        $allowInviteBenefit = $this->canUseRegisterInviteBenefit();
+        $data = MainInviteClass::resolveRegisterFeePreview($inviteCode, $allowInviteBenefit);
+        util::success($this->appendInviteBenefitFlags($data));
     }
 
     /**
@@ -117,7 +119,7 @@ class ApplyController extends BaseController
                 $data['inviteCommissionAmount'] = round((float) ($apply['inviteCommissionAmount'] ?? 0), 2);
             }
         }
-        util::success($data);
+        util::success($this->appendInviteBenefitFlags($data));
     }
 
     /**
@@ -128,14 +130,23 @@ class ApplyController extends BaseController
         if ($this->adminId <= 0 || $this->shopId <= 0) {
             util::fail('请先登录并选择门店');
         }
-        $post = Yii::$app->request->post();
-        $inviteCode = trim((string) ($post['inviteCode'] ?? ''));
+        $allowInviteBenefit = $this->canUseRegisterInviteBenefit();
         $apply = $this->findRegisterApplyForAdmin($this->adminId, $this->shopId);
         if (empty($apply)) {
             util::fail('未找到注册申请记录');
         }
         $applyId = (int) ($apply['id'] ?? 0);
-        $fee = MainInviteClass::resolveRegisterFeeSubmit($inviteCode);
+        // 已有 xhRenew 付费成功:续费不再更新邀请减免
+        if (!$allowInviteBenefit) {
+            $preview = MainInviteClass::resolveRegisterFeePreview('', false);
+            util::success($this->appendInviteBenefitFlags(array_merge($preview, [
+                'applyId' => $applyId,
+            ])));
+            return;
+        }
+        $post = Yii::$app->request->post();
+        $inviteCode = trim((string) ($post['inviteCode'] ?? ''));
+        $fee = MainInviteClass::resolveRegisterFeeSubmit($inviteCode, true);
         if ($inviteCode !== '' && !empty($fee['inviteNotFound'])) {
             util::fail('邀请人不存在');
         }
@@ -146,13 +157,13 @@ class ApplyController extends BaseController
             'hdDiscountAmount' => $hdDiscountAmount,
             'inviteCommissionAmount' => $inviteCommissionAmount,
         ]);
-        $preview = MainInviteClass::resolveRegisterFeePreview($inviteCode);
-        util::success(array_merge($preview, [
+        $preview = MainInviteClass::resolveRegisterFeePreview($inviteCode, true);
+        util::success($this->appendInviteBenefitFlags(array_merge($preview, [
             'applyId' => $applyId,
             'inviteCode' => $inviteCode,
             'hdDiscountAmount' => $hdDiscountAmount,
             'inviteCommissionAmount' => $inviteCommissionAmount,
-        ]));
+        ])));
     }
 
     //邀请花店海报 ssh 20210610
@@ -455,4 +466,26 @@ class ApplyController extends BaseController
         return $apply;
     }
 
+    /**
+     * 是否仍可使用邀请码减免与佣金(无 xhRenew 付费成功记录时为 true)
+     */
+    private function canUseRegisterInviteBenefit(): bool
+    {
+        if ($this->mainId <= 0 && $this->shopId <= 0) {
+            return true;
+        }
+        return !RenewClass::hasPaidMemberRenew((int) $this->mainId, (int) $this->shopId);
+    }
+
+    /**
+     * 补充首次付费邀请权益标记
+     */
+    private function appendInviteBenefitFlags(array $data): array
+    {
+        $available = $this->canUseRegisterInviteBenefit();
+        $data['inviteBenefitAvailable'] = $available ? 1 : 0;
+        $data['hasPaidRegisterRenew'] = $available ? 0 : 1;
+        return $data;
+    }
+
 }

+ 5 - 1
app-hd/controllers/CustomRechargeController.php

@@ -182,7 +182,11 @@ class CustomRechargeController extends BaseController
             $response = $laResource->driveWxPay($wxParams);
             if (isset($response['code']) == false || $response['code'] != 'BBS00000') {
                 $errMsg = $response['msg'] ?? '';
-                noticeUtil::push('编号19881507:' . $errMsg . ' | ' . $ghsName . ' | ' . $customName . ' | ' . $actPrice, '15280215347');
+                $errCode = $response['code'] ?? '';
+                noticeUtil::push('编号19881507:' . $errCode . ' | ' . $errMsg . ' | ' . $ghsName . ' | ' . $customName . ' | ' . $actPrice, '15280215347');
+                if($errCode == 'BBS11109'){
+                    util::fail('超过单笔限额,请分二笔支付');
+                }
                 util::fail('支付失败');
             }
             $newParams = isset($response['resp_data']['acc_resp_fields']) ? $response['resp_data']['acc_resp_fields'] : [];

+ 23 - 5
app-hd/controllers/GhsBalanceChangeController.php

@@ -4,24 +4,42 @@ namespace hd\controllers;
 
 use bizHd\ghs\classes\GhsBalanceChangeClass;
 use bizHd\ghs\classes\GhsClass;
-use common\components\dict;
 use common\components\util;
 use Yii;
 
+/**
+ * 花店端供货商余额变动
+ * 小程序登录态与 H5 扫码付款(salt)共用 list 接口
+ */
 class GhsBalanceChangeController extends BaseController
 {
+    /** H5 付款页未登录也可查明细,依赖 salt 校验归属 */
+    public $guestAccess = ['list'];
 
-    //余额变动明细
+    /**
+     * 余额变动明细列表
+     * 无 salt:校验当前店铺是否拥有该供货商;有 salt:与 get-ghs-data 一致按 salt 放行(H5 访客)
+     */
     public function actionList()
     {
         $get = Yii::$app->request->get();
         $ghsId = $get['ghsId'] ?? 0;
+        $salt = $get['salt'] ?? '';
         $ghs = GhsClass::getById($ghsId, true);
-        GhsClass::valid($ghs, $this->shopId);
+        if (empty($ghs)) {
+            util::fail('无法查看');
+        }
+        if (empty($salt)) {
+            GhsClass::valid($ghs, $this->shopId);
+        } else {
+            // 扫码付款链接携带 salt,与小程序余额明细共用同一套数据
+            if ($ghs->salt != $salt) {
+                util::fail('无法查看');
+            }
+        }
         $where = [];
         $where['ghsId'] = $ghsId;
         $list = GhsBalanceChangeClass::getChangeList($where);
         util::success($list);
     }
-
-}
+}

+ 7 - 7
app-hd/controllers/GhsController.php

@@ -55,7 +55,7 @@ class GhsController extends BaseController
         $perKmPrice = $shCurrentConfig['perKmPrice'] ?? 0;//超出起步,每公里加收金额
         $changeRate = $shCurrentConfig['changeRate'] ?? 0;//负值时为降价百分比,正值时为涨价百分比
         if ($startKm <= 0 || $startPrice <= 0 || $perKmPrice <= 0) {
-            util::success(['sendCost' => 0, 'distanceKm' => 0,'distance'=>0]);
+            util::success(['sendCost' => 0, 'distanceKm' => 0, 'distance' => 0]);
         }
         //计算客户到店的距离,骑电动车
         $distanceRespond = mapUtil::calcShopDistanceByEleBike($custom, $ghs, 0);
@@ -64,7 +64,7 @@ class GhsController extends BaseController
         $distance = floatval($distance);
         $sendDistance = floatval($sendDistance);
         if ($sendDistance <= 0) {
-            util::success(['sendCost' => 0, 'distanceKm' => 0,'distance'=>0]);
+            util::success(['sendCost' => 0, 'distanceKm' => 0, 'distance' => 0]);
         }
         // 计算运费逻辑
         $sendCost = $startPrice;
@@ -79,7 +79,7 @@ class GhsController extends BaseController
             $sendCost = bcmul($sendCost, $rate, 2);
         }
         $sendCost = floatval($sendCost);
-        util::success(['sendCost' => $sendCost, 'distanceKm' => $sendDistance,'distance'=>$distance]);
+        util::success(['sendCost' => $sendCost, 'distanceKm' => $sendDistance, 'distance' => $distance]);
     }
 
     //提示花店有多个供货商
@@ -379,8 +379,8 @@ class GhsController extends BaseController
         /*****此区域部分可以考虑删除******/
 
         //是否使用新的配送方式,关键词 sh_method_area
-        $newShMethod = dict::getNewMethodShop();
-        $ghsInfo['useNewShMethod'] = in_array($ghsShopId, $newShMethod) ? 1 : 0;
+        $newStatus = dict::getNewMethod($ghsShopId);
+        $ghsInfo['useNewShMethod'] = $newStatus ? 1 : 0;
 
         util::success($ghsInfo);
     }
@@ -443,8 +443,8 @@ class GhsController extends BaseController
             $ghs['pfLevel'] = $ghsShop->pfLevel ?? 0;
 
             //是否使用新的配送方式,关键词 sh_method_area
-            $newShMethod = dict::getNewMethodShop();
-            $ghs['useNewShMethod'] = in_array($ghsShopId, $newShMethod) ? 1 : 0;
+            $newStatus = dict::getNewMethod($ghsShopId);
+            $ghs['useNewShMethod'] = $newStatus ? 1 : 0;
 
         } else {
             //没有开店也给显示供货商信息

+ 26 - 14
app-hd/controllers/MainInviteController.php

@@ -6,6 +6,7 @@ namespace hd\controllers;
 
 use bizHd\shop\classes\MainInviteClass;
 use bizHd\shop\classes\MainInviteCommissionClass;
+use bizHd\shop\classes\MainInviteFlowClass;
 use common\components\util;
 use Yii;
 
@@ -20,7 +21,7 @@ class MainInviteController extends BaseController
     public function actionIndex()
     {
         $mainId = (int) $this->mainId;
-        $invite = MainInviteClass::getByMainId($mainId);
+        $invite = MainInviteClass::ensureByMainId($mainId);
         $inviteData = [
             'inviteCode' => '',
             'inviteCodeMask' => '',
@@ -67,23 +68,34 @@ class MainInviteController extends BaseController
     }
 
     /**
-     * 佣金明细分页
+     * 佣金变动明细分页(xhMainInviteFlow)
      */
     public function actionCommissionList()
     {
-        $get = Yii::$app->request->get();
-        $settleStatus = $get['settleStatus'] ?? null;
-        if ($settleStatus === '') {
-            $settleStatus = null;
-        }
-        $list = MainInviteCommissionClass::getCommissionList($this->mainId, $settleStatus);
-        $rows = $list['list'] ?? [];
-        $formatted = [];
-        foreach ($rows as $row) {
-            $formatted[] = MainInviteCommissionClass::formatCommissionListItem($row);
-        }
-        $list['list'] = $formatted;
+        $list = MainInviteFlowClass::getFlowList($this->mainId);
         util::success($list);
     }
 
+    /**
+     * 可提现佣金全额存入中央钱包余额
+     * POST:无参,按当前登录 mainId 处理
+     */
+    public function actionDepositBalance()
+    {
+        $mainId = (int) $this->mainId;
+        $shopId = (int) $this->shopId;
+        $shopName = trim((string) ($this->shop->name ?? ''));
+        try {
+            $result = MainInviteClass::depositAbleCommissionToWallet($mainId, $shopId, $shopName);
+            $invite = MainInviteClass::getByMainId($mainId);
+            $inviteData = [
+                'totalCommission' => round((float) ($invite['totalCommission'] ?? 0), 2),
+                'ableCommission' => round((float) ($invite['ableCommission'] ?? 0), 2),
+            ];
+            util::success(array_merge($result, ['invite' => $inviteData]));
+        } catch (\Exception $e) {
+            util::fail($e->getMessage());
+        }
+    }
+
 }

+ 4 - 4
app-hd/controllers/PurchaseController.php

@@ -621,8 +621,8 @@ class PurchaseController extends BaseController
             } else {
 
                 // xhShMethod 门店:下单前按配置写入不满最低消费时的附加包装费/运费,关键词 sh_method_area
-                $newShMethod = dict::getNewMethodShop();
-                if (in_array($ghsShopId, $newShMethod)) {
+                $newStatus = dict::getNewMethod($ghsShopId);
+                if ($newStatus) {
 
                     $shCurrentConfig = shMethodClass::getConfig($ghsShopId, $ghsMainId, $sendType);
                     $shCurrentName = $shCurrentConfig['name'] ?? '';
@@ -804,8 +804,8 @@ class PurchaseController extends BaseController
 
             $respond = PurchaseService::createPurchase($post, $ghsInfo);
 
-            $newShMethod = dict::getNewMethodShop();
-            if (in_array($ghsShopId, $newShMethod)) {
+            $newStatus = dict::getNewMethod($ghsShopId);
+            if ($newStatus) {
                 // 使用 xhShMethod 配置校验配送限制,替代旧版门店硬编码规则,关键词 sh_method_area
                 $shMethodError = ShMethodClass::checkLimit(
                     $ghsShopId,

+ 10 - 4
app-hd/controllers/RenewController.php

@@ -3,6 +3,7 @@
 namespace hd\controllers;
 
 use biz\set\classes\SetClass;
+use biz\renew\classes\RenewClass;
 use biz\sj\services\MerchantExtendService;
 use biz\sj\services\MerchantService;
 use bizHd\saas\classes\ApplyClass;
@@ -105,6 +106,8 @@ class RenewController extends BaseController
         $registerConfig = dict::getDict('hdRegisterConfig');
         $prePrice = round((float) ($registerConfig['price'] ?? 0), 2);
         $hdDiscountAmount = 0;
+        $attachApplyId = 0;
+        $allowInviteBenefit = !RenewClass::hasPaidMemberRenew((int) $this->mainId, (int) $this->shopId);
         if ($applyId > 0) {
             $apply = ApplyClass::getById($applyId);
             if (empty($apply)) {
@@ -113,7 +116,10 @@ class RenewController extends BaseController
             if ((int) ($apply['adminId'] ?? 0) !== (int) $this->adminId) {
                 util::fail('申请与当前账号不匹配');
             }
-            $hdDiscountAmount = round((float) ($apply['hdDiscountAmount'] ?? 0), 2);
+            if ($allowInviteBenefit) {
+                $hdDiscountAmount = round((float) ($apply['hdDiscountAmount'] ?? 0), 2);
+                $attachApplyId = $applyId;
+            }
         }
         $actPrice = round(max(0, $prePrice - $hdDiscountAmount), 2);
 
@@ -139,7 +145,7 @@ class RenewController extends BaseController
             'status' => 0,
             'type' => 1,
             'ptStyle' => dict::getDict('ptStyle', 'hd'),
-            'remark' => $applyId > 0 ? 'applyId=' . $applyId : '',
+            'remark' => $attachApplyId > 0 ? 'applyId=' . $attachApplyId : '',
             'beforeDeadline'=>$shop->beforeDeadline ?? '',
         ];
 
@@ -155,7 +161,7 @@ class RenewController extends BaseController
                 dict::getDict('payWay', 'wxPay'),
                 $orderSn,
                 $actPrice,
-                'capitalType=' . dict::getDict('capitalType', 'xhRenew', 'id') . '&applyId=' . $applyId,
+                'capitalType=' . dict::getDict('capitalType', 'xhRenew', 'id') . '&applyId=' . $attachApplyId,
                 ''
             );
             util::success([
@@ -174,7 +180,7 @@ class RenewController extends BaseController
         }
 
         $capitalType = dict::getDict('capitalType', 'xhRenew', 'id');
-        $attach = 'capitalType=' . $capitalType . '&applyId=' . $applyId . '&sjId=' . $this->sjId;
+        $attach = 'capitalType=' . $capitalType . '&applyId=' . $attachApplyId . '&sjId=' . $this->sjId;
 
         $wx = Yii::getAlias('@vendor/weixin');
         require_once $wx . '/lib/WxPay.Api.php';

+ 1 - 1
app-pt/views/main/app.php

@@ -235,7 +235,7 @@
             }
             // 这里可以根据appType跳转到对应的下载链接
             const downloadUrls = {
-                'xiaohuabao': 'https://api.shop.hzghd.com/1312.apk', // 销花宝APP下载链接
+                'xiaohuabao': 'https://api.shop.hzghd.com/1320.apk', // 销花宝APP下载链接
                 'huazhanggui': 'https://api.shop.hzghd.com/246.apk', // 花掌柜APP下载链接
                 'huazhanggui_cashier': 'https://api.shop.hzghd.com/220.apk', // 花掌柜收银下载链接
                 'xiaohuabao_cashier': 'https://api.shop.hzghd.com/178.apk' // 销花宝收银下载链接

+ 247 - 26
biz-hd/shop/classes/MainInviteClass.php

@@ -1,12 +1,16 @@
 <?php
 /**
  * 门店邀请主数据业务
+ * 职责:邀请码、注册减免、发佣、可提现佣金存入中央钱包
  */
 
 namespace bizHd\shop\classes;
 
 use bizHd\base\classes\BaseClass;
 use bizHd\shop\classes\MainInviteCommissionClass;
+use bizHd\shop\classes\MainInviteFlowClass;
+use bizHd\shop\classes\MainWalletChangeClass;
+use bizHd\shop\classes\MainClass;
 use common\components\dict;
 use Yii;
 
@@ -26,6 +30,34 @@ class MainInviteClass extends BaseClass
         return self::getByCondition(['mainId' => (int)$mainId], false);
     }
 
+    /**
+     * 确保 xhMainInvite 存在(邀请人账户);无记录则按 dict 默认规则自动初始化
+     * 供 shopInviteHub 首页、生成邀请码等入口调用;并发下 uk_mainId 冲突时再查一次
+     *
+     * @param int $mainId 当前登录 xhMain.id
+     * @return array|null 邀请主数据行
+     */
+    public static function ensureByMainId($mainId)
+    {
+        $mainId = (int) $mainId;
+        if ($mainId <= 0) {
+            return null;
+        }
+        $row = self::getByMainId($mainId);
+        if (!empty($row)) {
+            return $row;
+        }
+        try {
+            return self::createInviteeRecord($mainId, 0);
+        } catch (\Throwable $e) {
+            $row = self::getByMainId($mainId);
+            if (!empty($row)) {
+                return $row;
+            }
+            throw $e;
+        }
+    }
+
     /**
      * 按邀请码查 xhMainInvite
      */
@@ -41,7 +73,7 @@ class MainInviteClass extends BaseClass
     /**
      * 花店注册页:会员价与邀请减免(dict + 可选邀请码)
      */
-    public static function resolveRegisterFeePreview($inviteCode = '')
+    public static function resolveRegisterFeePreview($inviteCode = '', $allowInviteBenefit = true)
     {
         $config = dict::getDict('hdRegisterConfig');
         $price = round((float)($config['price'] ?? 0), 2);
@@ -58,6 +90,10 @@ class MainInviteClass extends BaseClass
             'payAmount' => $price,
         ];
 
+        if (!$allowInviteBenefit) {
+            return $out;
+        }
+
         $code = trim((string)$inviteCode);
         if ($code === '') {
             return $out;
@@ -92,9 +128,9 @@ class MainInviteClass extends BaseClass
     /**
      * 注册提交:费用 + 邀请人 mainId + 佣金快照
      */
-    public static function resolveRegisterFeeSubmit($inviteCode = '')
+    public static function resolveRegisterFeeSubmit($inviteCode = '', $allowInviteBenefit = true)
     {
-        $preview = self::resolveRegisterFeePreview($inviteCode);
+        $preview = self::resolveRegisterFeePreview($inviteCode, $allowInviteBenefit);
         $config = dict::getDict('hdRegisterConfig');
         $configComm = round((float) ($config['commissionAmount'] ?? 0), 2);
 
@@ -103,6 +139,10 @@ class MainInviteClass extends BaseClass
         $preview['inviterMainId'] = 0;
         $preview['inviteCommissionAmount'] = 0;
 
+        if (!$allowInviteBenefit) {
+            return $preview;
+        }
+
         if (!empty($preview['inviteNotFound'])) {
             return $preview;
         }
@@ -195,33 +235,214 @@ class MainInviteClass extends BaseClass
             return;
         }
 
+        $inviteId = (int) ($inviter['id'] ?? 0);
+        if ($inviteId <= 0) {
+            return;
+        }
+
         $applyId = (int) ($apply['id'] ?? 0);
         $commissionSn = 'FY' . date('YmdHis') . $applyId;
+        $flowTime = date('Y-m-d H:i:s');
+        $lsShopId = (int) ($apply['shopId'] ?? 0);
+        $lsShopName = trim((string) ($apply['name'] ?? ''));
+        $inviteeShopName = trim((string) $inviteeShopName);
+        $inviteeMobile = trim((string) $inviteeMobile);
+        $memberPayAmount = round((float) ($actPrice ?? 0), 2);
+        $prePriceAmount = round((float) ($prePrice ?? 0), 2);
 
-        MainInviteCommissionClass::add([
-            'commissionSn' => $commissionSn,
-            'inviterMainId' => $inviterMainId,
-            'lsShopId' => (int) ($apply['shopId'] ?? 0),
-            'lsShopName' => trim((string) ($apply['name'] ?? '')),
-            'inviteeMainId' => (int) $inviteeMainId,
-            'inviteeShopName' => trim((string) $inviteeShopName),
-            'inviteeMobile' => trim((string) $inviteeMobile),
-            'inviteCode' => trim((string) ($apply['inviteCode'] ?? '')),
-            'memberTypeName' => '年度会员',
-            'amount' => round((float) ($prePrice ?? 0), 2),
-            'memberPayAmount' => round((float) ($actPrice ?? 0), 2),
-            'commissionAmount' => $commissionAmount,
-            'hdDiscountAmount' => round((float) ($apply['hdDiscountAmount'] ?? 0), 2),
-            'settleStatus' => 0,
-            'payTime' => date('Y-m-d H:i:s'),
-        ], false);
+        $transaction = Yii::$app->db->beginTransaction();
+        try {
+            $inviteRow = self::getLockById($inviteId);
+            if (empty($inviteRow) || (int) ($inviteRow->mainId ?? 0) !== $inviterMainId) {
+                throw new \Exception('邀请账户无效');
+            }
+
+            $beforeAble = round((float) ($inviteRow->ableCommission ?? 0), 2);
 
-        $inviteCount = (int) ($inviter['inviteCount'] ?? 0) + 1;
-        $totalCommission = round((float) ($inviter['totalCommission'] ?? 0) + $commissionAmount, 2);
-        self::updateById((int) $inviter['id'], [
-            'inviteCount' => $inviteCount,
-            'totalCommission' => $totalCommission,
-        ]);
+            // 1. 邀约记录(受邀花店维度)
+            $commissionLog = MainInviteCommissionClass::add([
+                'commissionSn' => $commissionSn,
+                'inviterMainId' => $inviterMainId,
+                'lsShopId' => $lsShopId,
+                'lsShopName' => $lsShopName,
+                'inviteeMainId' => (int) $inviteeMainId,
+                'inviteeShopName' => $inviteeShopName,
+                'inviteeMobile' => $inviteeMobile,
+                'inviteCode' => trim((string) ($apply['inviteCode'] ?? '')),
+                'memberTypeName' => '年度会员',
+                'amount' => $prePriceAmount,
+                'memberPayAmount' => $memberPayAmount,
+                'commissionAmount' => $commissionAmount,
+                'hdDiscountAmount' => round((float) ($apply['hdDiscountAmount'] ?? 0), 2),
+                'settleStatus' => 0,
+                'payTime' => $flowTime,
+            ], true);
+            $commissionId = (int) ($commissionLog->id ?? 0);
+
+            // 2. 累加 inviteCount / totalCommission / ableCommission
+            $inviteCount = (int) ($inviteRow->inviteCount ?? 0) + 1;
+            $totalCommission = round((float) ($inviteRow->totalCommission ?? 0) + $commissionAmount, 2);
+            $ableCommission = round($beforeAble + $commissionAmount, 2);
+            self::updateById($inviteId, [
+                'inviteCount' => $inviteCount,
+                'totalCommission' => $totalCommission,
+                'ableCommission' => $ableCommission,
+            ]);
+
+            // 3. 佣金变动流水(+获佣)
+            MainInviteFlowClass::add([
+                'mainId' => $inviterMainId,
+                'flowType' => MainInviteFlowClass::FLOW_TYPE_EARN,
+                'amount' => $commissionAmount,
+                'ableAfter' => $ableCommission,
+                'refType' => MainInviteFlowClass::REF_TYPE_COMMISSION,
+                'refId' => $commissionId,
+                'refSn' => $commissionSn,
+                'inviteeShopName' => $inviteeShopName !== '' ? $inviteeShopName : $lsShopName,
+                'inviteeMobile' => $inviteeMobile,
+                'lsShopId' => $lsShopId,
+                'baseAmount' => $memberPayAmount > 0 ? $memberPayAmount : $prePriceAmount,
+                'flowTime' => $flowTime,
+                'remark' => '邀请开通获佣',
+            ], true);
+
+            $transaction->commit();
+        } catch (\Throwable $e) {
+            $transaction->rollBack();
+            throw $e;
+        }
+    }
+
+    /**
+     * 将全部可提现佣金存入 xhMain.walletBalance(全额存入,非部分)
+     *
+     * @param int $mainId 邀请人中央 id
+     * @param int $shopId 当前门店 id(写佣金流水快照)
+     * @param string $shopName 门店名称快照
+     * @return array commissionSn、depositAmount、ableCommission、walletBalance
+     * @throws \Throwable
+     */
+    public static function depositAbleCommissionToWallet($mainId, $shopId, $shopName = '')
+    {
+        $mainId = (int) $mainId;
+        $shopId = (int) $shopId;
+        if ($mainId <= 0) {
+            throw new \Exception('账户无效');
+        }
+
+        $lockKey = 'main_invite_deposit_' . $mainId;
+        $locked = Yii::$app->redis->executeCommand('SET', [$lockKey, '1', 'NX', 'EX', 15]);
+        if ($locked === false || $locked === null) {
+            throw new \Exception('操作进行中,请稍后再试');
+        }
+
+        try {
+            return self::depositAbleCommissionToWalletInTransaction($mainId, $shopId, trim((string) $shopName));
+        } finally {
+            Yii::$app->redis->executeCommand('DEL', [$lockKey]);
+        }
+    }
+
+    /**
+     * 事务内:扣减 ableCommission、写佣金存入流水、增加钱包余额并记 xhMainWalletChange
+     */
+    protected static function depositAbleCommissionToWalletInTransaction($mainId, $shopId, $shopName)
+    {
+        $preview = self::ensureByMainId($mainId);
+        if (empty($preview)) {
+            throw new \Exception('暂无邀请账户');
+        }
+        $inviteId = (int) ($preview['id'] ?? 0);
+        if ($inviteId <= 0) {
+            throw new \Exception('暂无邀请账户');
+        }
+
+        $transaction = Yii::$app->db->beginTransaction();
+        try {
+            $invite = self::getLockById($inviteId);
+            if (empty($invite) || (int) ($invite->mainId ?? 0) !== $mainId) {
+                throw new \Exception('邀请账户无效');
+            }
+
+            $beforeAble = round((float) ($invite->ableCommission ?? 0), 2);
+            if ($beforeAble <= 0) {
+                throw new \Exception('暂无可存入佣金');
+            }
+            $amount = $beforeAble;
+            $afterAble = 0;
+
+            $main = MainClass::getLockById($mainId);
+            if (empty($main)) {
+                throw new \Exception('账户无效');
+            }
+
+            $flowTime = date('Y-m-d H:i:s');
+            $depositSn = 'CR' . date('YmdHis') . $mainId;
+
+            // 1. 扣减可提现佣金(累计 totalCommission 不变)
+            self::updateById($inviteId, ['ableCommission' => $afterAble]);
+
+            // 2. 中央钱包余额增加
+            $beforeWallet = round((float) ($main->walletBalance ?? 0), 2);
+            $afterWallet = round($beforeWallet + $amount, 2);
+            MainClass::updateById($mainId, ['walletBalance' => $afterWallet]);
+
+            // 3. 钱包变动流水
+            $capitalType = dict::getDict('capitalType', 'inviteCommissionDeposit', 'id');
+            $payWay = dict::getDict('payWay', 'unknown');
+            $ptStyle = dict::getDict('ptStyle', 'hd');
+            $fromType = dict::getDict('fromType', 'shop');
+            $ioIncome = dict::getDict('io', 'income');
+
+            $walletChange = MainWalletChangeClass::add([
+                'relateId' => 0,
+                'ptStyle' => $ptStyle,
+                'capitalType' => $capitalType,
+                'amount' => $amount,
+                'balance' => $afterWallet,
+                'io' => $ioIncome,
+                'payWay' => $payWay,
+                'fromType' => $fromType,
+                'event' => '佣金存入余额' . $depositSn,
+                'mainId' => $mainId,
+                'remark' => '邀请佣金存入中央钱包',
+            ], true);
+            $walletChangeId = (int) ($walletChange->id ?? 0);
+
+            // 4. 佣金变动流水(-存入,同步 ableAfter)
+            $flowLog = MainInviteFlowClass::add([
+                'mainId' => $mainId,
+                'flowType' => MainInviteFlowClass::FLOW_TYPE_DEPOSIT,
+                'amount' => round(0 - $amount, 2),
+                'ableAfter' => $afterAble,
+                'refType' => MainInviteFlowClass::REF_TYPE_WALLET_CHANGE,
+                'refId' => $walletChangeId,
+                'refSn' => $depositSn,
+                'inviteeShopName' => $shopName,
+                'inviteeMobile' => '',
+                'lsShopId' => $shopId,
+                'baseAmount' => $amount,
+                'flowTime' => $flowTime,
+                'remark' => '佣金存入余额',
+            ], true);
+            $flowId = (int) ($flowLog->id ?? 0);
+
+            if ($walletChangeId > 0 && $flowId > 0) {
+                MainWalletChangeClass::updateById($walletChangeId, ['relateId' => $flowId]);
+            }
+
+            $transaction->commit();
+
+            return [
+                'commissionSn' => $depositSn,
+                'depositAmount' => $amount,
+                'ableCommission' => $afterAble,
+                'walletBalance' => $afterWallet,
+            ];
+        } catch (\Throwable $e) {
+            $transaction->rollBack();
+            throw $e;
+        }
     }
 
 }

+ 10 - 2
biz-hd/shop/classes/MainInviteCommissionClass.php

@@ -37,6 +37,7 @@ class MainInviteCommissionClass extends BaseClass
         }
         return self::getLimitList('*', [
             'inviterMainId' => (int) $inviterMainId,
+            'inviteeMainId>' => 0,
         ], (int) $limit, 'addTime DESC,id DESC');
     }
 
@@ -87,8 +88,12 @@ class MainInviteCommissionClass extends BaseClass
         }
 
         $keyword = trim((string) $keyword);
+        $baseWhere = [
+            'inviterMainId' => $mainId,
+            'inviteeMainId>' => 0,
+        ];
         if ($keyword === '') {
-            $result = self::getList('*', ['inviterMainId' => $mainId], 'addTime DESC,id DESC');
+            $result = self::getList('*', $baseWhere, 'addTime DESC,id DESC');
         } else {
             // 店名 / 冗余字段 inviteeMobile 模糊匹配
             $result = self::getInviteShopListByKeyword($mainId, $keyword);
@@ -114,7 +119,10 @@ class MainInviteCommissionClass extends BaseClass
             : (int) (Yii::$app->params['pageSize'] ?? 20);
 
         $model = self::getModel();
-        $query = $model->conditionQuery(['inviterMainId' => (int) $inviterMainId]);
+        $query = $model->conditionQuery([
+            'inviterMainId' => (int) $inviterMainId,
+            'inviteeMainId>' => 0,
+        ]);
         $query->andWhere([
             'or',
             ['like', 'inviteeShopName', $keyword],

+ 144 - 0
biz-hd/shop/classes/MainInviteFlowClass.php

@@ -0,0 +1,144 @@
+<?php
+/**
+ * 邀请佣金变动流水 xhMainInviteFlow
+ * 职责:佣金变动明细分页、格式化;配合 xhMainInvite.ableCommission 落 ableAfter 快照
+ */
+namespace bizHd\shop\classes;
+
+use biz\shop\classes\ShopClass;
+use bizHd\base\classes\BaseClass;
+use common\components\dateUtil;
+use Yii;
+
+class MainInviteFlowClass extends BaseClass
+{
+
+    public static $baseFile = '\bizHd\shop\models\MainInviteFlow';
+
+    /** 邀请开通获佣 */
+    const FLOW_TYPE_EARN = 1;
+
+    /** 佣金存入中央钱包 */
+    const FLOW_TYPE_DEPOSIT = 2;
+
+    /** 退款扣回佣金 */
+    const FLOW_TYPE_REFUND = 3;
+
+    /** 关联 xhMainInviteCommission.id */
+    const REF_TYPE_COMMISSION = 1;
+
+    /** 关联 xhMainWalletChange.id */
+    const REF_TYPE_WALLET_CHANGE = 2;
+
+    /**
+     * 佣金变动明细分页(默认近30天)
+     */
+    public static function getFlowList($mainId)
+    {
+        $where = self::buildFlowListWhere($mainId);
+        $result = self::getList('*', $where, 'flowTime DESC,id DESC');
+        $rows = $result['list'] ?? [];
+        $list = [];
+        foreach ($rows as $row) {
+            $list[] = self::formatFlowListItem($row);
+        }
+        $result['list'] = $list;
+        return $result;
+    }
+
+    /**
+     * 列表查询条件:mainId + 时间筛选
+     */
+    protected static function buildFlowListWhere($mainId)
+    {
+        $where = ['mainId' => (int) $mainId];
+        $get = Yii::$app->request->get();
+        $searchTime = isset($get['searchTime']) ? trim((string) $get['searchTime']) : 'last30Days';
+        $startTime = isset($get['startTime']) ? trim((string) $get['startTime']) : '';
+        $endTime = isset($get['endTime']) ? trim((string) $get['endTime']) : '';
+        if ($searchTime !== '' && $searchTime !== 'all') {
+            $period = dateUtil::formatTime($searchTime, $startTime, $endTime);
+            if (!empty($period['startTime']) && !empty($period['endTime'])) {
+                $where['flowTime'] = ['between', [$period['startTime'], $period['endTime']]];
+            }
+        }
+        return $where;
+    }
+
+    /**
+     * 格式化单行,字段与 hdApp shopInviteCommissionList 一致
+     */
+    public static function formatFlowListItem($row)
+    {
+        $flowType = (int) ($row['flowType'] ?? 0);
+        $lsShopId = (int) ($row['lsShopId'] ?? 0);
+        $shop = [];
+        if ($lsShopId > 0) {
+            $shop = ShopClass::getById($lsShopId, false) ?: [];
+        }
+
+        $shopName = trim((string) ($row['inviteeShopName'] ?? ''));
+        $mobile = trim((string) ($row['inviteeMobile'] ?? ''));
+        $mobileMask = $mobile !== '' ? MainInviteCommissionClass::maskMobile($mobile) : '-';
+
+        if ($flowType === self::FLOW_TYPE_DEPOSIT) {
+            $orderSnLabel = '存入编号';
+            $baseAmountLabel = '存余额金额';
+            $recordType = 'deposit';
+            $shopName = '存入钱包余额';
+            $mobile = '';
+            $mobileMask = '-';
+            $shop = [];
+        } else {
+            $orderSnLabel = '会员订单号';
+            $baseAmountLabel = '会员实付金额';
+            $recordType = 'member';
+        }
+
+        $orderSn = trim((string) ($row['refSn'] ?? ''));
+        $baseAmount = round((float) ($row['baseAmount'] ?? 0), 2);
+        $rawAmount = round((float) ($row['amount'] ?? 0), 2);
+        $commissionAmount = round(abs($rawAmount), 2);
+        $commissionSign = $rawAmount < 0 ? -1 : 1;
+        if ($flowType === self::FLOW_TYPE_REFUND) {
+            $commissionSign = -1;
+        }
+
+        $createTime = trim((string) ($row['flowTime'] ?? ($row['addTime'] ?? '')));
+        if ($createTime !== '' && strlen($createTime) === 10) {
+            $createTime .= ' 00:00:00';
+        }
+
+        return [
+            'id' => $row['id'] ?? 0,
+            'shopName' => $shopName !== '' ? $shopName : '-',
+            'shopImg' => self::resolveShopImg($shop),
+            'mobile' => $mobile !== '' ? $mobile : '-',
+            'mobileMask' => $mobileMask,
+            'orderSnLabel' => $orderSnLabel,
+            'orderSn' => $orderSn !== '' ? $orderSn : '-',
+            'baseAmountLabel' => $baseAmountLabel,
+            'baseAmount' => $baseAmount,
+            'createTime' => $createTime !== '' ? $createTime : '-',
+            'commissionAmount' => $commissionAmount,
+            'commissionSign' => $commissionSign,
+            'recordType' => $recordType,
+            'flowType' => $flowType,
+            'ableAfter' => round((float) ($row['ableAfter'] ?? 0), 2),
+        ];
+    }
+
+    protected static function resolveShopImg($shop)
+    {
+        $avatar = is_array($shop) ? ($shop['avatar'] ?? '') : '';
+        if (empty($avatar)) {
+            return '';
+        }
+        if (strpos($avatar, 'http') === 0) {
+            return $avatar;
+        }
+        $host = Yii::$app->params['hdImgHost'] ?? '';
+        return $host . $avatar;
+    }
+
+}

+ 18 - 0
biz-hd/shop/models/MainInviteFlow.php

@@ -0,0 +1,18 @@
+<?php
+/**
+ * 邀请佣金变动流水 xhMainInviteFlow
+ * 用途:hdApp 佣金变动明细;与 xhMainInviteCommission(邀约记录)分离
+ */
+namespace bizHd\shop\models;
+
+use bizHd\base\models\Base;
+
+class MainInviteFlow extends Base
+{
+
+    public static function tableName()
+    {
+        return 'xhMainInviteFlow';
+    }
+
+}

+ 56 - 9
biz/renew/classes/RenewClass.php

@@ -7,6 +7,7 @@ use biz\shop\classes\ShopClass;
 use biz\sj\classes\SjClass;
 use bizHd\saas\classes\ApplyClass;
 use bizHd\shop\classes\MainInviteClass;
+use common\components\dict;
 use common\components\noticeUtil;
 use common\components\util;
 use Yii;
@@ -22,6 +23,47 @@ class RenewClass extends BaseClass
         return $data;
     }
 
+    /**
+     * 是否已有 xhRenew 付费成功记录(同一 main/shop 仅首次付费享受邀请减免与佣金)
+     */
+    public static function hasPaidMemberRenew(int $mainId, int $shopId = 0): bool
+    {
+        $ptStyle = dict::getDict('ptStyle', 'hd');
+        if ($mainId > 0) {
+            $row = self::getByCondition(['mainId' => $mainId, 'status' => 1, 'ptStyle' => $ptStyle], false);
+            if (!empty($row)) {
+                return true;
+            }
+        }
+        if ($shopId > 0) {
+            $row = self::getByCondition(['shopId' => $shopId, 'status' => 1, 'ptStyle' => $ptStyle], false);
+            return !empty($row);
+        }
+        return false;
+    }
+
+    /**
+     * 除当前订单外是否已有付费成功的 xhRenew(回调写佣金时用)
+     */
+    public static function hasOtherPaidMemberRenew(int $mainId, string $excludeOrderSn = ''): bool
+    {
+        if ($mainId <= 0) {
+            return false;
+        }
+        $ptStyle = dict::getDict('ptStyle', 'hd');
+        $list = self::getAllByCondition(['mainId' => $mainId, 'status' => 1, 'ptStyle' => $ptStyle]);
+        if (empty($list)) {
+            return false;
+        }
+        foreach ($list as $item) {
+            $sn = is_array($item) ? (string) ($item['orderSn'] ?? '') : (string) ($item->orderSn ?? '');
+            if ($excludeOrderSn !== '' && $sn !== $excludeOrderSn) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public static function thirdPay($payWay, $orderSn, $totalFee, $attach, $transactionId)
     {
         $renew = self::getByCondition(['orderSn' => $orderSn], true);
@@ -84,17 +126,22 @@ class RenewClass extends BaseClass
         $renew->status = 1;
         $renew->save();
 
-        if ($isRegisterMember) {
+        if ($isRegisterMember && !self::hasOtherPaidMemberRenew((int) $mainId, (string) $orderSn)) {
             $apply = ApplyClass::getById($applyId);
             if (!empty($apply)) {
-                MainInviteClass::grantInviterCommission(
-                    $apply,
-                    (int) $mainId,
-                    trim((string) ($apply['name'] ?? '')),
-                    trim((string) ($apply['mobile'] ?? '')),
-                    round((float) ($renew->prePrice ?? 0), 2),
-                    round((float) ($renew->actPrice ?? 0), 2)
-                );
+                try {
+                    MainInviteClass::grantInviterCommission(
+                        $apply,
+                        (int) $mainId,
+                        trim((string) ($apply['name'] ?? '')),
+                        trim((string) ($apply['mobile'] ?? '')),
+                        round((float) ($renew->prePrice ?? 0), 2),
+                        round((float) ($renew->actPrice ?? 0), 2)
+                    );
+                } catch (\Throwable $e) {
+                    $msg = '邀请发佣失败 orderSn:' . $orderSn . ' ' . $e->getMessage();
+                    noticeUtil::push($msg, '15280215347');
+                }
             }
         }
 

+ 12 - 3
common/components/dict.php

@@ -513,6 +513,8 @@ class dict
             'distributionDeposit' => ['id' => 83, 'name' => 'distributionDeposit'],
             // 花店注册年度会员支付
             'hdRegisterOrder' => ['id' => 84, 'name' => 'hdRegisterOrder'],
+            // 邀请佣金存入中央钱包(xhMainInviteFlow + xhMainWalletChange)
+            'inviteCommissionDeposit' => ['id' => 85, 'name' => 'inviteCommissionDeposit'],
         ],
         "capitalTypeList" => [//流水类型的对应链接,后台收支明细查看时跳转的链接
             0 => ['link' => '/capital/order-detail', 'name' => '网店', 'orderLink' => '/order/detail', 'id' => 0,],
@@ -625,7 +627,7 @@ class dict
             ['id' => 5, 'name' => '其它'],
         ],
         'hdRegisterConfig' => [
-            'price' => 1, //花店开通费用
+            'price' => 0.02, //花店开通费用
             'commissionAmount' => 88.00, //默认分佣金额 (默认)
             'hdDiscountAmount' => 88.00 //花店优惠金额 (默认)
         ]
@@ -704,14 +706,21 @@ class dict
         return $respond;
     }
 
-    public static function getNewMethodShop()
+    public static function getNewMethod($shopId)
     {
         $newShop = !getenv('NEW_METHOD_SHOP') ? '' : getenv('NEW_METHOD_SHOP');
         if (empty($newShop)) {
             return [];
         }
         $newShop = trim($newShop);
-        return explode(',', $newShop);
+        $arr = explode(',', $newShop);
+        if (empty($arr)) {
+            return false;
+        }
+        if (in_array($shopId, $arr)) {
+            return true;
+        }
+        return false;
     }
 
 }

+ 4 - 1
common/components/miniUtil.php

@@ -1609,7 +1609,7 @@ class miniUtil
         return $envVersion;
     }
 
-    public static function generateUnlimitedMiniCode($merchant, $page, $scene = '', $ptStyle = 0, $envVersion = 'release')
+    public static function generateUnlimitedMiniCode($merchant, $page, $scene = '', $ptStyle = 0, $envVersion = 'release', $checkPath = null)
     {
         $envVersion = self::normalizeMiniEnvVersion($envVersion);
         $id = isset($merchant['id']) ? $merchant['id'] : 0;
@@ -1631,6 +1631,9 @@ class miniUtil
             $data["page"] = $page;
         }
         $data["scene"] = $scene;
+        if ($checkPath === false) {
+            $data["check_path"] = false;
+        }
         if ($envVersion !== 'release') {
             $data["env_version"] = $envVersion;
         }

+ 20 - 0
sql/20260706_redesign.sql

@@ -383,6 +383,26 @@ CREATE TABLE IF NOT EXISTS `xhMainInviteCommission` (
     KEY `idx_relateOrderId` (`relateOrderId`) USING BTREE
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='门店邀请记录';
 
+CREATE TABLE IF NOT EXISTS `xhMainInviteFlow` (
+    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+    `mainId` int(11) NOT NULL DEFAULT 0 COMMENT '邀请人 xhMain.id',
+    `flowType` tinyint(3) unsigned NOT NULL DEFAULT 1 COMMENT '1邀请获佣 2存入余额 3退款扣回',
+    `amount` decimal(15,2) NOT NULL DEFAULT 0.00 COMMENT '变动金额:获佣为正,存入/扣回为负',
+    `ableAfter` decimal(15,2) NOT NULL DEFAULT 0.00 COMMENT '变动后可提现佣金',
+    `refType` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT '1=xhMainInviteCommission 2=xhMainWalletChange',
+    `refId` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '关联业务 id',
+    `refSn` varchar(32) NOT NULL DEFAULT '' COMMENT '分佣单号 FY… / 存入编号 CR…',
+    `inviteeShopName` varchar(80) NOT NULL DEFAULT '' COMMENT '受邀花店名快照 flowType=1',
+    `inviteeMobile` varchar(20) NOT NULL DEFAULT '' COMMENT '受邀手机号快照',
+    `lsShopId` int(11) NOT NULL DEFAULT 0 COMMENT '零售店 shopId',
+    `baseAmount` decimal(15,2) NOT NULL DEFAULT 0.00 COMMENT '会员实付或存余额金额',
+    `flowTime` datetime NOT NULL COMMENT '业务时间',
+    `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
+    `addTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY (`id`),
+    KEY `idx_main_flowTime` (`mainId`, `flowTime`),
+    KEY `idx_ref` (`refType`, `refId`)
+    ) COMMENT='邀请佣金变动流水';
 -- 花店注册付费开通:xhApply 扩展字段
 ALTER TABLE `xhApply`
   ADD COLUMN `inviteCode` varchar(32) NOT NULL DEFAULT '' COMMENT '注册时填写的邀请码' AFTER `status`,