فهرست منبع

Merge branch 'redesign‌-260706' into 260902_redesign_fix

shizhongqi 1 روز پیش
والد
کامیت
c38f57b083

+ 184 - 0
app-mall/controllers/BaseController.php

@@ -12,6 +12,11 @@ use common\components\jwt;
 use common\components\util;
 use Yii;
 
+/**
+ * 商城端 API 基类
+ * 从 account / hdId 解析门店与顾客上下文。
+ * 已登录但请求里的 hdId 不属于当前店时会清空 shop,浏览/进店类接口需按 account 重取门店再建客。
+ */
 class BaseController extends PublicController
 {
 
@@ -86,4 +91,183 @@ class BaseController extends PublicController
         return parent::beforeAction($action);
     }
 
+    /**
+     * 按 account 恢复门店与 mainId。
+     * BaseController 在 hdId 与当前用户/门店不匹配时会清空 shop,分享进店或换店后仍带旧 hdId 时必须走这里,否则浏览接口会误报无效门店。
+     * @return array shop、shopId、mainId
+     */
+    protected function recoverShopContext()
+    {
+        $shopId = intval($this->shopId);
+        if ($shopId <= 0) {
+            util::fail('shopId不存在');
+        }
+        $shop = $this->shop;
+        if (empty($shop)) {
+            // 被越权保护清空后,仍以当前 account 为准取门店,而不是沿用请求里的旧 hdId
+            $shop = ShopClass::getById($shopId, true);
+            if (!empty($shop)) {
+                $this->shop = $shop;
+            }
+        }
+        if (empty($shop)) {
+            util::fail('无效门店');
+        }
+        $mainId = intval($this->mainId);
+        if ($mainId <= 0) {
+            $mainId = intval($shop->mainId ?? 0);
+            $this->mainId = $mainId;
+            if ($mainId > 0 && empty($this->main)) {
+                $this->main = MainClass::getById($mainId, true);
+            }
+        }
+        if ($mainId <= 0) {
+            util::fail('mainId不存在');
+        }
+        return [
+            'shop' => $shop,
+            'shopId' => $shopId,
+            'mainId' => $mainId,
+        ];
+    }
+
+    /**
+     * 登录态解析当前门店 hdId。
+     * 已有 xhHd 则直接取 id;没有则走 CustomClass::buildRelation,与 ShopController::actionInfo 建客路径一致,避免分享直达漏掉拉新。
+     * 建客失败返回 hdId=0,由调用方决定是否阻断(浏览不阻断,下单再校验)。
+     * @param object|null $shop 门店对象,建客时必须有
+     * @param int $shopId 门店 id(xhShop.id)
+     * @return array hdId、inviteBound,以及可能带上的 hd/custom
+     * @throws \yii\db\IntegrityException
+     */
+    protected function resolveLoggedInHd($shop, $shopId)
+    {
+        $empty = [
+            'hdId' => 0,
+            'inviteBound' => 0,
+            'hd' => null,
+            'custom' => null,
+            'customId' => 0,
+        ];
+        $hd = HdClass::getByCondition(
+            ['shopId' => $shopId, 'userId' => $this->userId],
+            false,
+            null,
+            'id, customId'
+        );
+        if (!empty($hd)) {
+            return [
+                'hdId' => intval($hd['id']),
+                'inviteBound' => 0,
+                'hd' => null,
+                'custom' => null,
+                'customId' => intval($hd['customId'] ?? 0),
+            ];
+        }
+
+        $user = $this->user;
+        if (empty($shop) || empty($user)) {
+            return $empty;
+        }
+
+        $inviterCustomId = (int)Yii::$app->request->get('inviterCustomId', 0);
+        if ($inviterCustomId <= 0) {
+            $inviterCustomId = (int)Yii::$app->request->post('inviterCustomId', 0);
+        }
+        $respond = CustomClass::buildRelation($shop, $user, $inviterCustomId);
+        if (empty($respond) || empty($respond['hd'])) {
+            return $empty;
+        }
+
+        $hd = $respond['hd'];
+        $custom = $respond['custom'] ?? null;
+        $hdId = intval(is_object($hd) ? $hd->id : ($hd['id'] ?? 0));
+        $customId = 0;
+        if (!empty($custom)) {
+            $customId = intval(is_object($custom) ? $custom->id : ($custom['id'] ?? 0));
+        }
+        return [
+            'hdId' => $hdId,
+            'inviteBound' => !empty($respond['inviteBound']) ? (int)$respond['inviteBound'] : 0,
+            'hd' => $hd,
+            'custom' => $custom,
+            'customId' => $customId,
+        ];
+    }
+
+    /**
+     * 浏览/进店:先恢复门店,登录用户,再解析或创建当前店 hd。
+     * 游客不建客;建客失败不阻断浏览,调用方把 hdId=0 回给前端即可。
+     * @return array shop、shopId、mainId、hdId、inviteBound、resolved
+     * @throws \yii\db\IntegrityException
+     */
+    protected function recoverShopAndResolveHd()
+    {
+        $ctx = $this->recoverShopContext();
+        $resolved = [
+            'hdId' => 0,
+            'inviteBound' => 0,
+            'hd' => null,
+            'custom' => null,
+            'customId' => 0,
+        ];
+        if (intval($this->userId) > 0) {
+            $resolved = $this->resolveLoggedInHd($ctx['shop'], $ctx['shopId']);
+        }
+        $ctx['hdId'] = intval($resolved['hdId'] ?? 0);
+        $ctx['inviteBound'] = intval($resolved['inviteBound'] ?? 0);
+        $ctx['resolved'] = $resolved;
+        return $ctx;
+    }
+
+    /**
+     * 把 resolveLoggedInHd 的结果写回控制器,供开团/参团等需要 $this->custom、$this->hd 的逻辑使用。
+     * @param array $resolved resolveLoggedInHd 返回值
+     * @throws \Exception
+     */
+    protected function applyResolvedHd($resolved)
+    {
+        $hdId = intval($resolved['hdId'] ?? 0);
+        if ($hdId <= 0) {
+            return;
+        }
+        $this->hdId = $hdId;
+        $hd = $resolved['hd'] ?? null;
+        if (empty($hd) || !is_object($hd)) {
+            $hd = HdClass::getById($hdId, true);
+        }
+        if (empty($hd) || !is_object($hd)) {
+            return;
+        }
+        $this->hd = $hd;
+        $custom = $resolved['custom'] ?? null;
+        $customId = intval($hd->customId ?? ($resolved['customId'] ?? 0));
+        if ((empty($custom) || !is_object($custom)) && $customId > 0) {
+            $custom = CustomClass::getById($customId, true);
+        }
+        if (!empty($custom) && is_object($custom)) {
+            $this->custom = $custom;
+            $this->customId = intval($custom->id ?? $customId);
+        }
+    }
+
+    /**
+     * 下单类接口:恢复门店并确保当前用户已绑定顾客。
+     * 未登录或建客失败时直接报请先登录,避免用空 custom / 空 shop 继续下单。
+     * @return array recoverShopAndResolveHd 的上下文
+     * @throws \yii\db\IntegrityException
+     */
+    protected function ensureCustomerContext()
+    {
+        $ctx = $this->recoverShopAndResolveHd();
+        if (intval($this->userId) <= 0 || empty($this->user)) {
+            util::fail('请先登录');
+        }
+        $this->applyResolvedHd($ctx['resolved']);
+        if (empty($this->custom)) {
+            util::fail('请先登录');
+        }
+        return $ctx;
+    }
+
 }

+ 24 - 64
app-mall/controllers/GroupBuyController.php

@@ -2,7 +2,6 @@
 
 namespace mall\controllers;
 
-use bizHd\custom\classes\CustomClass;
 use bizHd\goods\classes\GoodsClass;
 use bizHd\groupBuy\classes\GroupBuyActivityClass;
 use bizHd\groupBuy\classes\GroupBuyClass;
@@ -10,7 +9,6 @@ use bizHd\groupBuy\classes\GroupBuyMemberClass;
 use bizHd\order\classes\PsMethodClass;
 use bizHd\order\services\OrderService;
 use bizHd\shop\classes\ShopClass;
-use bizMall\hd\classes\HdClass;
 use bizMall\pictext\classes\PicTextGoodsClass;
 use common\components\business;
 use common\components\dict;
@@ -24,6 +22,7 @@ use Yii;
 /**
  * 商城端拼团接口
  * 落地页 / 团详情 / 开团 / 参团 / 我的拼团
+ * 分享直达或换店后仍带旧 hdId 时,按 account 恢复门店并建客,与 get-home 进店闭环一致
  * 开团/参团订单按团购价结算,不叠加会员折扣、门店满减、红包
  * 配送附加费与跑腿距离运费与混合结算同源(PsMethodClass::calcFeeForMallContext / calcMixRunnerFreight)
  */
@@ -38,13 +37,13 @@ class GroupBuyController extends BaseController
      * 团购商品落地页:活动商品信息 + 商品详情图文 + 当前最快成团开放团
      * GET goodsId
      * 返回字段与商品详情页对齐:picTextGoods(标题/content JSON)供前端 rich-media-viewer 渲染
+     * 登录态按 account 恢复门店并建客,回传 hdId / inviteBound,与 get-home 进店闭环一致
      */
     public function actionGoodsLanding()
     {
-        $mainId = intval($this->mainId);
-        if ($mainId <= 0) {
-            util::fail('无效门店');
-        }
+        $ctx = $this->recoverShopAndResolveHd();
+        $mainId = $ctx['mainId'];
+
         $goodsId = intval(Yii::$app->request->get('goodsId', 0));
         if ($goodsId <= 0) {
             util::fail('请选择商品');
@@ -118,19 +117,21 @@ class GroupBuyController extends BaseController
             // 花束是否包运费:结算页跑腿模式(bouquetIncluded)与混合单同源判断
             'freightType' => !empty($goods) ? intval($goods->freightType ?? 0) : 0,
             'openGroup' => $openGroup,
+            // 供 mallApp 写入本地 hdId,避免后续开团/参团仍带旧店 hdId
+            'hdId' => $ctx['hdId'],
+            'inviteBound' => $ctx['inviteBound'],
         ]);
     }
 
     /**
      * 拼团详情
      * GET id
+     * 游客可浏览;已登录则按 account 恢复门店并建客,回传 hdId 供前端缓存
      */
     public function actionDetail()
     {
-        $mainId = intval($this->mainId);
-        if ($mainId <= 0) {
-            util::fail('无效门店');
-        }
+        $ctx = $this->recoverShopAndResolveHd();
+        $mainId = $ctx['mainId'];
         $id = intval(Yii::$app->request->get('id', 0));
         if ($id <= 0) {
             util::fail('请选择拼团');
@@ -148,29 +149,8 @@ class GroupBuyController extends BaseController
             $landing['freightType'] = !empty($landingGoods) ? intval($landingGoods->freightType ?? 0) : 0;
         }
         $detail['activityGoods'] = $landing;
-
-        $currentShopId = intval(Yii::$app->request->get('account', 0));
-        $hd = HdClass::getByCondition(['shopId' => $currentShopId, 'userId' => $this->userId], false, null, 'id, customId');
-        if (empty($hd)) {
-            // util::error(errCode::GROUP_BUY_HD_NOT_BOUND, '客户暂无关联此店');
-
-            // 客户与当前门店无关系时,立即建立
-            if (empty($this->user)) {
-                util::fail('请先登录');
-            }
-            $shop = ShopClass::getById($currentShopId, true);
-            if (empty($shop)) {
-                util::fail('门店不存在');
-            }
-            $respond = CustomClass::buildRelation($shop, $this->user);
-            if (empty($respond) || empty($respond['hd'])) {
-                util::fail('关联门店失败');
-            }
-            $hdId = intval($respond['hd']->id);
-        } else {
-            $hdId = intval($hd['id']);
-        }
-        $detail['hdId'] = $hdId;
+        $detail['hdId'] = $ctx['hdId'];
+        $detail['inviteBound'] = $ctx['inviteBound'];
 
         util::success($detail);
     }
@@ -182,11 +162,9 @@ class GroupBuyController extends BaseController
     {
         $form = new CreateGroupBuyForm();
         $form->loadAndValidate();
-        $mainId = intval($this->mainId);
+        $ctx = $this->ensureCustomerContext();
+        $mainId = $ctx['mainId'];
         $custom = $this->custom;
-        if (empty($custom)) {
-            util::fail('请先登录');
-        }
         $customId = intval($custom->id);
         $activityGoodsId = intval($form->activityGoodsId);
         $goodsNum = max(1, intval($form->goodsNum));
@@ -234,26 +212,9 @@ class GroupBuyController extends BaseController
     {
         $form = new JoinGroupBuyForm();
         $form->loadAndValidate();
-        $mainId = intval($this->mainId);
+        $ctx = $this->ensureCustomerContext();
+        $mainId = $ctx['mainId'];
         $custom = $this->custom;
-        $currentShopId = intval(Yii::$app->request->get('account', 0));
-        if (empty($custom)) {
-            $hd = HdClass::getByCondition(['shopId' => $currentShopId, 'userId' => $this->userId], false, null, 'id, customId');
-            if (empty($hd)) {
-                // 客户与当前门店无关系时,立即建立
-                if (empty($this->user) || empty($this->shop)) {
-                    util::fail('请先登录');
-                }
-                $respond = CustomClass::buildRelation($this->shop, $this->user);
-                if (empty($respond) || empty($respond['custom'])) {
-                    util::fail('关联门店失败');
-                }
-                $custom = $respond['custom'];
-            } else {
-                $customId = $hd['customId'];
-                $custom = CustomClass::getById($customId, true);
-            }
-        }
         $customId = intval($custom->id);
         $groupBuyId = intval($form->groupBuyId);
         $goodsNum = max(1, intval($form->goodsNum));
@@ -308,16 +269,13 @@ class GroupBuyController extends BaseController
 
     /**
      * 我的拼团列表
+     * 换店后可能仍带旧 hdId,先恢复门店并绑定当前店顾客再查列表
      */
     public function actionMyList()
     {
-        $mainId = intval($this->mainId);
-        $custom = $this->custom;
-        if (empty($custom)) {
-            util::fail('请先登录');
-        }
+        $ctx = $this->ensureCustomerContext();
         util::success([
-            'list' => GroupBuyClass::getMyList($mainId, intval($custom->id)),
+            'list' => GroupBuyClass::getMyList($ctx['mainId'], intval($this->custom->id)),
         ]);
     }
 
@@ -348,6 +306,8 @@ class GroupBuyController extends BaseController
             util::fail('请先登录');
         }
         $hd = $this->hd;
+        $hdId = !empty($hd) ? intval($hd->id ?? 0) : 0;
+        $hdName = !empty($hd) ? strval($hd->name ?? '') : '';
         $user = $this->user;
         $saleGoodsId = intval($row['goodsId']);
         $goodsInfo = GoodsClass::getById($saleGoodsId, true);
@@ -489,8 +449,8 @@ class GroupBuyController extends BaseController
             'customId' => intval($custom->id),
             'customName' => strval($custom->name ?? ''),
             'customNamePy' => stringUtil::py(strval($custom->name ?? '')),
-            'hdId' => intval($hd->id ?? 0),
-            'hdName' => strval($hd->name ?? ''),
+            'hdId' => $hdId,
+            'hdName' => $hdName,
             'bookName' => strval($user->name ?? ''),
             'bookMobile' => !empty($form->bookMobile) && stringUtil::isMobile($form->bookMobile)
                 ? $form->bookMobile

+ 5 - 78
app-mall/controllers/HomePageConfigController.php

@@ -2,15 +2,11 @@
 
 namespace mall\controllers;
 
-use biz\shop\classes\ShopClass;
-use bizHd\custom\classes\CustomClass;
 use bizHd\homePageConfig\classes\HomePageConfigClass;
 use bizHd\homePageConfig\classes\HomePageDisplayClass;
 use bizHd\homePageConfig\classes\HomePageModuleClass;
-use bizMall\hd\classes\HdClass;
 use common\components\util;
 use mall\models\homePageConfig\GetSectionGoodsForm;
-use Yii;
 
 /**
  * 商城端首页配置读取接口
@@ -39,38 +35,11 @@ class HomePageConfigController extends BaseController
      */
     public function actionGetHome()
     {
-        $shopId = intval($this->shopId);
-        if ($shopId <= 0) {
-            util::fail('shopId不存在');
-        }
-
-        $shop = $this->shop;
-        if (empty($shop)) {
-            // BaseController 在请求 hdId 与当前用户不匹配时会清空 shop,这里按 account 重新取门店
-            $shop = ShopClass::getById($shopId, true);
-        }
-        if (empty($shop)) {
-            util::fail('无效门店');
-        }
-        $mainId = intval($this->mainId);
-        if ($mainId <= 0) {
-            $mainId = intval($shop->mainId);
-        }
-        if ($mainId <= 0) {
-            util::fail('mainId不存在');
-        }
-
-        $hdId = 0;
-        $inviteBound = 0;
-        if (intval($this->userId) > 0) {
-            $resolved = $this->resolveLoggedInHd($shop, $shopId);
-            $hdId = $resolved['hdId'];
-            $inviteBound = $resolved['inviteBound'];
-        }
-
-        $re = HomePageDisplayClass::buildHome($mainId, $shopId);
-        $re['hdId'] = $hdId;
-        $re['inviteBound'] = $inviteBound;
+        // 换店/分享直达时请求可能仍带旧 hdId,先按 account 恢复门店再解析当前店绑定
+        $ctx = $this->recoverShopAndResolveHd();
+        $re = HomePageDisplayClass::buildHome($ctx['mainId'], $ctx['shopId']);
+        $re['hdId'] = $ctx['hdId'];
+        $re['inviteBound'] = $ctx['inviteBound'];
         util::success($re);
     }
 
@@ -126,48 +95,6 @@ class HomePageConfigController extends BaseController
         ));
     }
 
-    /**
-     * 登录态解析当前门店 hdId。
-     * 已有 xhHd(门店已绑定该顾客)则直接取 id;没有则走 CustomClass::buildRelation,
-     * 与 ShopController::actionInfo 在无 hdId 时的建客路径一致,避免首页先建客却漏掉分享拉新。
-     * @param object|null $shop 门店对象,建客时必须有
-     * @param int $shopId 门店 id(xhShop.id)
-     * @return array{hdId:int,inviteBound:int}
-     * @throws \yii\db\IntegrityException
-     */
-    private function resolveLoggedInHd($shop, $shopId)
-    {
-        $hd = HdClass::getByCondition(
-            ['shopId' => $shopId, 'userId' => $this->userId],
-            false,
-            null,
-            'id, customId'
-        );
-        if (!empty($hd)) {
-            return [
-                'hdId' => intval($hd['id']),
-                'inviteBound' => 0,
-            ];
-        }
-
-        $user = $this->user;
-        if (empty($shop) || empty($user)) {
-            return ['hdId' => 0, 'inviteBound' => 0];
-        }
-
-        $inviterCustomId = (int)Yii::$app->request->get('inviterCustomId', 0);
-        $respond = CustomClass::buildRelation($shop, $user, $inviterCustomId);
-        if (empty($respond) || empty($respond['hd'])) {
-            return ['hdId' => 0, 'inviteBound' => 0];
-        }
-
-        $hd = $respond['hd'];
-        return [
-            'hdId' => intval(is_object($hd) ? $hd->id : ($hd['id'] ?? 0)),
-            'inviteBound' => !empty($respond['inviteBound']) ? (int)$respond['inviteBound'] : 0,
-        ];
-    }
-
     private function requireMainId()
     {
         $mainId = intval($this->mainId);