MainInviteController.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. <?php
  2. /**
  3. * ghsApp 门店邀请 / 我的分佣(邀请花店开店)
  4. * 数据与 hd 共用 xhMainInvite、xhMainInviteFlow 等表
  5. */
  6. namespace ghs\controllers;
  7. use bizHd\shop\classes\MainInviteClass;
  8. use bizHd\shop\classes\MainInviteCommissionClass;
  9. use bizHd\shop\classes\MainInviteFlowClass;
  10. use bizHd\wx\classes\WxOpenClass;
  11. use common\components\dict;
  12. use common\components\dirUtil;
  13. use common\components\imgUtil;
  14. use common\components\miniUtil;
  15. use common\components\stringUtil;
  16. use common\components\util;
  17. use Yii;
  18. class MainInviteController extends BaseController
  19. {
  20. public $guestAccess = [];
  21. /**
  22. * 我的分佣首页
  23. */
  24. public function actionIndex()
  25. {
  26. $mainId = (int) $this->mainId;
  27. $invite = MainInviteClass::getByMainId($mainId);
  28. $inviteData = [
  29. 'inviteCode' => '',
  30. 'inviteCodeMask' => '',
  31. 'totalCommission' => '0.00',
  32. 'ableCommission' => '0.00',
  33. 'commissionAmount' => '0.00',
  34. 'hdDiscountAmount' => '0.00',
  35. 'inviteCount' => 0,
  36. ];
  37. if (!empty($invite)) {
  38. $code = $invite['inviteCode'] ?? '';
  39. $inviteData = [
  40. 'inviteCode' => $code,
  41. 'inviteCodeMask' => MainInviteCommissionClass::maskInviteCode($code),
  42. 'totalCommission' => round((float) ($invite['totalCommission'] ?? 0), 2),
  43. 'ableCommission' => round((float) ($invite['ableCommission'] ?? 0), 2),
  44. 'commissionAmount' => round((float) ($invite['commissionAmount'] ?? 0), 2),
  45. 'hdDiscountAmount' => round((float) ($invite['hdDiscountAmount'] ?? 0), 2),
  46. 'inviteCount' => (int) ($invite['inviteCount'] ?? 0),
  47. ];
  48. }
  49. $recentRaw = MainInviteCommissionClass::getRecentList($mainId, 5);
  50. $recentList = [];
  51. foreach ($recentRaw as $row) {
  52. $recentList[] = MainInviteCommissionClass::formatRecentRow($row);
  53. }
  54. util::success([
  55. 'invite' => $inviteData,
  56. 'recentList' => $recentList,
  57. ]);
  58. }
  59. /**
  60. * 邀约花店列表
  61. */
  62. public function actionInviteShopList()
  63. {
  64. $get = Yii::$app->request->get();
  65. $keyword = trim($get['keyword'] ?? '');
  66. $data = MainInviteCommissionClass::getInviteShopList($this->mainId, $keyword);
  67. util::success($data);
  68. }
  69. /**
  70. * 佣金变动明细分页
  71. */
  72. public function actionCommissionList()
  73. {
  74. $list = MainInviteFlowClass::getFlowList($this->mainId);
  75. util::success($list);
  76. }
  77. /**
  78. * 可提现佣金全额存入中央钱包余额
  79. */
  80. public function actionDepositBalance()
  81. {
  82. $mainId = (int) $this->mainId;
  83. $shopId = (int) $this->shopId;
  84. $shopName = trim((string) ($this->shop->name ?? ''));
  85. try {
  86. $result = MainInviteClass::depositAbleCommissionToWallet($mainId, $shopId, $shopName);
  87. $invite = MainInviteClass::getByMainId($mainId);
  88. $inviteData = [
  89. 'totalCommission' => round((float) ($invite['totalCommission'] ?? 0), 2),
  90. 'ableCommission' => round((float) ($invite['ableCommission'] ?? 0), 2),
  91. ];
  92. util::success(array_merge($result, ['invite' => $inviteData]));
  93. } catch (\Exception $e) {
  94. util::fail($e->getMessage());
  95. }
  96. }
  97. /**
  98. * 生成 hdApp 邀请开店海报(参考 main/get-mall-poster:太阳码 + OSS 背景合成)
  99. * GET:可选 env_version=develop|trial|release
  100. */
  101. public function actionGetInviteHdPoster()
  102. {
  103. $mainId = (int) $this->mainId;
  104. $invite = MainInviteClass::getByMainId($mainId);
  105. $inviteCode = trim((string) ($invite['inviteCode'] ?? ''));
  106. if ($inviteCode === '') {
  107. util::fail('暂无邀请码');
  108. }
  109. $envVersion = miniUtil::normalizeMiniEnvVersion(Yii::$app->request->get('env_version', 'release'));
  110. $posterCacheKey = 'wx_mini_invite_hd_poster_v3_' . $mainId . '_' . $inviteCode . '_' . $envVersion;
  111. $cachedPoster = Yii::$app->redis->executeCommand('GET', [$posterCacheKey]);
  112. if (!empty($cachedPoster)) {
  113. $respond = json_decode($cachedPoster, true);
  114. if (!empty($respond['imgUrl']) && !empty($respond['miniCodeUrl'])) {
  115. util::success($respond);
  116. }
  117. }
  118. $merchant = WxOpenClass::getWxInfo();
  119. $page = 'pagesClient/official/applyInvite';
  120. $ptStyle = dict::getDict('ptStyle', 'hd');
  121. $scene = 'inviteCode=' . $inviteCode;
  122. // 开通页可能尚未发布到体验版,check_path=false 仍可生成码图;并校验本地 JPEG 避免缓存错误 JSON
  123. $imgUrl = $this->ensureValidInviteHdMiniCode($merchant, $page, $scene, $ptStyle, $envVersion);
  124. $miniCodeUrl = imgUtil::groupImg($imgUrl);
  125. $titleBase64 = stringUtil::ossBase64('邀请开店');
  126. $prefix = imgUtil::getPrefix();
  127. $url = $prefix . 'shop/miniCodeBg.jpg?x-oss-process=image';
  128. $miniCodeBase64 = stringUtil::ossBase64($imgUrl);
  129. $url .= '/watermark,image_' . $miniCodeBase64 . ',g_north,x_0,y_40';
  130. $url .= '/watermark,text_' . $titleBase64 . ',g_north,x_0,y_530,size_50';
  131. $respond = [
  132. 'imgUrl' => $url,
  133. 'miniCodeUrl' => $miniCodeUrl,
  134. 'inviteCode' => $inviteCode,
  135. ];
  136. Yii::$app->redis->executeCommand('SETEX', [$posterCacheKey, 86400 * 3, json_encode($respond)]);
  137. util::success($respond);
  138. }
  139. /**
  140. * 生成 hd 邀请开店小程序码相对路径,无效缓存文件时删除并重试一次
  141. */
  142. private function ensureValidInviteHdMiniCode($merchant, $page, $scene, $ptStyle, $envVersion)
  143. {
  144. $relative = miniUtil::generateUnlimitedMiniCode($merchant, $page, $scene, $ptStyle, $envVersion, false);
  145. $fullPath = dirUtil::getImgDir() . $relative;
  146. if ($this->isValidJpegFile($fullPath)) {
  147. return $relative;
  148. }
  149. if (is_file($fullPath)) {
  150. @unlink($fullPath);
  151. }
  152. $relative = miniUtil::generateUnlimitedMiniCode($merchant, $page, $scene, $ptStyle, $envVersion, false);
  153. $fullPath = dirUtil::getImgDir() . $relative;
  154. if (!$this->isValidJpegFile($fullPath)) {
  155. if (is_file($fullPath)) {
  156. @unlink($fullPath);
  157. }
  158. util::fail('小程序码生成失败,请确认花店端已上传体验版并包含开通页');
  159. }
  160. return $relative;
  161. }
  162. /** 判断本地文件是否为有效 JPEG(微信失败时可能写入 JSON 文本) */
  163. private function isValidJpegFile($fullPath)
  164. {
  165. if (!is_file($fullPath) || filesize($fullPath) < 100) {
  166. return false;
  167. }
  168. $head = @file_get_contents($fullPath, false, null, 0, 2);
  169. return $head === "\xFF\xD8";
  170. }
  171. }