BaseController.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. <?php
  2. namespace mall\controllers;
  3. use biz\shop\classes\ShopClass;
  4. use bizHd\custom\classes\CustomClass;
  5. use bizMall\hd\classes\HdClass;
  6. use bizMall\shop\classes\MainClass;
  7. use bizMall\user\classes\UserClass;
  8. use common\components\dict;
  9. use common\components\jwt;
  10. use common\components\util;
  11. use Yii;
  12. /**
  13. * 商城端 API 基类
  14. * 从 account / hdId 解析门店与顾客上下文。
  15. * 已登录但请求里的 hdId 不属于当前店时会清空 shop,浏览/进店类接口需按 account 重取门店再建客。
  16. */
  17. class BaseController extends PublicController
  18. {
  19. public $validate = true;
  20. public $guestAccess = [];
  21. public $userId = 0, $user, $shopId, $shop;
  22. public $sjId, $sj, $sjExtend, $customId = 0, $custom, $hd, $hdId, $mainId = 0, $main;
  23. public $isWx;
  24. public function beforeAction($action)
  25. {
  26. Yii::$app->params['ptStyle'] = dict::getDict('ptStyle', 'mall');
  27. $this->isWx = util::isWx();
  28. $userId = jwt::getLoginId();
  29. if (empty($userId)) {
  30. if (!in_array($action->id, $this->guestAccess)) {
  31. util::fail('请先登录哈');
  32. }
  33. }
  34. $userId = intval($userId);
  35. $this->userId = $userId;
  36. $user = UserClass::getById($userId, true);
  37. if (empty($user)) {
  38. if (!in_array($action->id, $this->guestAccess)) {
  39. util::fail('请先登录呢');
  40. }
  41. }
  42. $this->user = $user;
  43. $shopId = Yii::$app->request->get('account'); // 从 $_GET 中取
  44. $shopId = empty($shopId) ? Yii::$app->request->post('account') : $shopId; // 从 $_POST 中取
  45. $shopId = intval($shopId);
  46. if (!empty($shopId)) {
  47. $this->shopId = $shopId;
  48. $shop = ShopClass::getById($shopId, true);
  49. if (!empty($shop)) {
  50. $this->shop = $shop;
  51. $mainId = $shop->mainId ?? 0;
  52. $main = MainClass::getById($mainId, true);
  53. $this->main = $main;
  54. $this->mainId = $mainId;
  55. $hdId = Yii::$app->request->get('hdId', 0);
  56. if (empty($hdId)) {
  57. //兼容早期代码,勿删,扫码付款页也用到,只写POST方式
  58. $hdId = Yii::$app->request->post('hdId', 0);
  59. }
  60. if (!empty($hdId)) {
  61. $hd = HdClass::getById($hdId, true);
  62. if (!empty($hd)) {
  63. if (!empty($this->userId) && $hd->userId == $this->userId && $shopId == $hd->shopId) {
  64. // 已登录且 hd 归属当前用户:绑定门店客户上下文
  65. $this->hd = $hd;
  66. $this->hdId = $hdId;
  67. $customId = $hd->customId ?? 0;
  68. $custom = CustomClass::getById($customId, true);
  69. $this->custom = $custom;
  70. $this->customId = $customId;
  71. } elseif (!empty($this->userId)) {
  72. // 已登录但 hd 与用户不匹配:清空店铺,防止恶意越权
  73. $this->shop = null;
  74. $this->main = null;
  75. $this->mainId = 0;
  76. }
  77. // 未登录访客:保留 shop/main,仅不绑定 hd/custom,便于浏览分类/首页
  78. }
  79. }
  80. }
  81. }
  82. return parent::beforeAction($action);
  83. }
  84. /**
  85. * 按 account 恢复门店与 mainId。
  86. * BaseController 在 hdId 与当前用户/门店不匹配时会清空 shop,分享进店或换店后仍带旧 hdId 时必须走这里,否则浏览接口会误报无效门店。
  87. * @return array shop、shopId、mainId
  88. */
  89. protected function recoverShopContext()
  90. {
  91. $shopId = intval($this->shopId);
  92. if ($shopId <= 0) {
  93. util::fail('shopId不存在');
  94. }
  95. $shop = $this->shop;
  96. if (empty($shop)) {
  97. // 被越权保护清空后,仍以当前 account 为准取门店,而不是沿用请求里的旧 hdId
  98. $shop = ShopClass::getById($shopId, true);
  99. if (!empty($shop)) {
  100. $this->shop = $shop;
  101. }
  102. }
  103. if (empty($shop)) {
  104. util::fail('无效门店');
  105. }
  106. $mainId = intval($this->mainId);
  107. if ($mainId <= 0) {
  108. $mainId = intval($shop->mainId ?? 0);
  109. $this->mainId = $mainId;
  110. if ($mainId > 0 && empty($this->main)) {
  111. $this->main = MainClass::getById($mainId, true);
  112. }
  113. }
  114. if ($mainId <= 0) {
  115. util::fail('mainId不存在');
  116. }
  117. return [
  118. 'shop' => $shop,
  119. 'shopId' => $shopId,
  120. 'mainId' => $mainId,
  121. ];
  122. }
  123. /**
  124. * 登录态解析当前门店 hdId。
  125. * 已有 xhHd 则直接取 id;没有则走 CustomClass::buildRelation,与 ShopController::actionInfo 建客路径一致,避免分享直达漏掉拉新。
  126. * 建客失败返回 hdId=0,由调用方决定是否阻断(浏览不阻断,下单再校验)。
  127. * @param object|null $shop 门店对象,建客时必须有
  128. * @param int $shopId 门店 id(xhShop.id)
  129. * @return array hdId、inviteBound,以及可能带上的 hd/custom
  130. * @throws \yii\db\IntegrityException
  131. */
  132. protected function resolveLoggedInHd($shop, $shopId)
  133. {
  134. $empty = [
  135. 'hdId' => 0,
  136. 'inviteBound' => 0,
  137. 'hd' => null,
  138. 'custom' => null,
  139. 'customId' => 0,
  140. ];
  141. $hd = HdClass::getByCondition(
  142. ['shopId' => $shopId, 'userId' => $this->userId],
  143. false,
  144. null,
  145. 'id, customId'
  146. );
  147. if (!empty($hd)) {
  148. return [
  149. 'hdId' => intval($hd['id']),
  150. 'inviteBound' => 0,
  151. 'hd' => null,
  152. 'custom' => null,
  153. 'customId' => intval($hd['customId'] ?? 0),
  154. ];
  155. }
  156. $user = $this->user;
  157. if (empty($shop) || empty($user)) {
  158. return $empty;
  159. }
  160. $inviterCustomId = (int)Yii::$app->request->get('inviterCustomId', 0);
  161. if ($inviterCustomId <= 0) {
  162. $inviterCustomId = (int)Yii::$app->request->post('inviterCustomId', 0);
  163. }
  164. $respond = CustomClass::buildRelation($shop, $user, $inviterCustomId);
  165. if (empty($respond) || empty($respond['hd'])) {
  166. return $empty;
  167. }
  168. $hd = $respond['hd'];
  169. $custom = $respond['custom'] ?? null;
  170. $hdId = intval(is_object($hd) ? $hd->id : ($hd['id'] ?? 0));
  171. $customId = 0;
  172. if (!empty($custom)) {
  173. $customId = intval(is_object($custom) ? $custom->id : ($custom['id'] ?? 0));
  174. }
  175. return [
  176. 'hdId' => $hdId,
  177. 'inviteBound' => !empty($respond['inviteBound']) ? (int)$respond['inviteBound'] : 0,
  178. 'hd' => $hd,
  179. 'custom' => $custom,
  180. 'customId' => $customId,
  181. ];
  182. }
  183. /**
  184. * 浏览/进店:先恢复门店,登录用户,再解析或创建当前店 hd。
  185. * 游客不建客;建客失败不阻断浏览,调用方把 hdId=0 回给前端即可。
  186. * @return array shop、shopId、mainId、hdId、inviteBound、resolved
  187. * @throws \yii\db\IntegrityException
  188. */
  189. protected function recoverShopAndResolveHd()
  190. {
  191. $ctx = $this->recoverShopContext();
  192. $resolved = [
  193. 'hdId' => 0,
  194. 'inviteBound' => 0,
  195. 'hd' => null,
  196. 'custom' => null,
  197. 'customId' => 0,
  198. ];
  199. if (intval($this->userId) > 0) {
  200. $resolved = $this->resolveLoggedInHd($ctx['shop'], $ctx['shopId']);
  201. }
  202. $ctx['hdId'] = intval($resolved['hdId'] ?? 0);
  203. $ctx['inviteBound'] = intval($resolved['inviteBound'] ?? 0);
  204. $ctx['resolved'] = $resolved;
  205. return $ctx;
  206. }
  207. /**
  208. * 把 resolveLoggedInHd 的结果写回控制器,供开团/参团等需要 $this->custom、$this->hd 的逻辑使用。
  209. * @param array $resolved resolveLoggedInHd 返回值
  210. * @throws \Exception
  211. */
  212. protected function applyResolvedHd($resolved)
  213. {
  214. $hdId = intval($resolved['hdId'] ?? 0);
  215. if ($hdId <= 0) {
  216. return;
  217. }
  218. $this->hdId = $hdId;
  219. $hd = $resolved['hd'] ?? null;
  220. if (empty($hd) || !is_object($hd)) {
  221. $hd = HdClass::getById($hdId, true);
  222. }
  223. if (empty($hd) || !is_object($hd)) {
  224. return;
  225. }
  226. $this->hd = $hd;
  227. $custom = $resolved['custom'] ?? null;
  228. $customId = intval($hd->customId ?? ($resolved['customId'] ?? 0));
  229. if ((empty($custom) || !is_object($custom)) && $customId > 0) {
  230. $custom = CustomClass::getById($customId, true);
  231. }
  232. if (!empty($custom) && is_object($custom)) {
  233. $this->custom = $custom;
  234. $this->customId = intval($custom->id ?? $customId);
  235. }
  236. }
  237. /**
  238. * 下单类接口:恢复门店并确保当前用户已绑定顾客。
  239. * 未登录或建客失败时直接报请先登录,避免用空 custom / 空 shop 继续下单。
  240. * @return array recoverShopAndResolveHd 的上下文
  241. * @throws \yii\db\IntegrityException
  242. */
  243. protected function ensureCustomerContext()
  244. {
  245. $ctx = $this->recoverShopAndResolveHd();
  246. if (intval($this->userId) <= 0 || empty($this->user)) {
  247. util::fail('请先登录');
  248. }
  249. $this->applyResolvedHd($ctx['resolved']);
  250. if (empty($this->custom)) {
  251. util::fail('请先登录');
  252. }
  253. return $ctx;
  254. }
  255. }