SendCouponController.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. <?php
  2. namespace app\backend\modules\coupon\controllers;
  3. use app\common\components\BaseController;
  4. use app\backend\modules\member\models\MemberLevel;
  5. use app\backend\modules\member\models\MemberGroup;
  6. use app\common\exceptions\ShopException;
  7. use app\common\models\MemberCoupon;
  8. use app\common\models\McMappingFans;
  9. use app\common\models\Member;
  10. use app\common\models\Coupon;
  11. use app\common\models\CouponLog;
  12. use app\backend\modules\coupon\services\Message;
  13. use app\common\models\MemberShopInfo;
  14. use app\backend\modules\coupon\services\MessageNotice;
  15. class SendCouponController extends BaseController
  16. {
  17. const BY_MEMBERIDS = 1;
  18. const BY_MEMBER_LEVEL = 2;
  19. const BY_MEMBER_GROUP = 3;
  20. const TO_ALL_MEMBERS = 4;
  21. public $failedSend = []; //发送失败时的记录
  22. public $adminId; //后台操作者的ID
  23. public function index()
  24. {
  25. $id = request()->id;
  26. //获取会员等级列表
  27. $memberLevels = MemberLevel::getMemberLevelList();
  28. //获取会员分组列表
  29. $memberGroups = MemberGroup::getMemberGroupList();
  30. return view('coupon.send', [
  31. 'memberLevels' => $memberLevels, //用户等级列表
  32. 'memberGroups' => $memberGroups, //用户分组列表
  33. 'id' => $id
  34. ])->render();
  35. }
  36. public function sendData()
  37. {
  38. $couponId = request()->id;
  39. $couponModel = Coupon::getCouponById($couponId);
  40. //获取会员等级列表
  41. $memberLevels = MemberLevel::getMemberLevelList();
  42. //获取会员分组列表
  43. $memberGroups = MemberGroup::getMemberGroupList();
  44. if (request()->sendtype) {
  45. //获取后台操作者的ID
  46. $this->adminId = \YunShop::app()->uid;
  47. //获取会员 Member ID
  48. $sendType = request()->sendtype;
  49. switch ($sendType) {
  50. case self::BY_MEMBERIDS:
  51. $membersScope = trim(request()->send_memberid);
  52. $patternMatchNumArray = preg_match('/(\d+,)+(\d+,?)/', $membersScope); //匹配比如 "2,3,78"或者"2,3,78,"
  53. $patternMatchSingleNum = preg_match('/(\d+)(,)?/', $membersScope); //匹配单个数字
  54. $memberIds = explode(',', $membersScope);
  55. $uid = Member::getMemberId($memberIds); //提取该搜索公众号下的会员id
  56. $uids = [];
  57. foreach ($uid as $key => $item) {
  58. $uids[$key] = $item['uid']; //将查出的会员id装到数组里
  59. }
  60. $member_ids = collect($memberIds)->map(function ($item) { //循环转换为数值类型
  61. return intval($item);
  62. })->toArray();
  63. // dd($member_ids);
  64. $arr = array_diff($member_ids, $uids); //提交过来的会员id与查询出来的会员id对比,留下不存在该公众号的会员id
  65. if (!empty($arr)) { //判断是否存在不是该公众号的会员id
  66. return $this->errorJson("发放优惠券失败,请确认该" . implode(",", $arr) . "会员是否是该公众号会员");
  67. }
  68. if ($patternMatchNumArray || $patternMatchSingleNum) {
  69. $patternMatch = true;
  70. } else {
  71. $patternMatch = false;
  72. }
  73. break;
  74. case self::BY_MEMBER_LEVEL: //根据"会员等级"获取 Member IDs
  75. $sendLevel = request()->send_level;
  76. if (!$sendLevel) {
  77. return $this->errorJson('请选择会员等级!');
  78. }
  79. $res = MemberLevel::getMembersByLevel($sendLevel);
  80. if (!$res->member) {
  81. $memberIds = '';
  82. } else {
  83. $res = $res->toArray();
  84. $memberIds = array_column($res['member'], 'member_id'); //提取member_id组成新的数组
  85. }
  86. break;
  87. case self::BY_MEMBER_GROUP: //根据"会员分组"获取 Member IDs
  88. $sendGroup = request()->send_group;
  89. if (!$sendGroup) {
  90. return $this->errorJson('请选择会员组!');
  91. }
  92. $res = MemberGroup::getMembersByGroupId($sendGroup);
  93. if (!$res->member) {
  94. $memberIds = '';
  95. } else {
  96. $res = $res->toArray();
  97. $memberIds = array_column($res['member'], 'member_id'); //提取member_id组成新的数组
  98. }
  99. break;
  100. case self::TO_ALL_MEMBERS:
  101. // $res = Member::getMembersId();
  102. $res = MemberShopInfo::getYzMembersId();
  103. if (!$res) {
  104. $members = '';
  105. } else {
  106. $members = $res->toArray();
  107. }
  108. $memberIds = array_column($members, 'member_id');
  109. break;
  110. default:
  111. $memberIds = '';
  112. }
  113. foreach ($memberIds as $uid) {
  114. if (!is_numeric($uid)) {
  115. return $this->errorJson('会员id输入格式错误,请用“英文逗号“隔开会员ID');
  116. }
  117. }
  118. //获取发放的数量
  119. $sendTotal = request()->send_total;
  120. $getTotal = MemberCoupon::uniacid()->where("coupon_id", $couponModel->id)->count();
  121. $lastTotal = $couponModel->total - $getTotal;
  122. if (empty($memberIds)) {
  123. return $this->errorJson('该发放类型下还没有用户');
  124. } elseif (!$couponModel->status) {
  125. return $this->errorJson('优惠券已下架,请先重新上架');
  126. } elseif ($sendTotal < 1) {
  127. return $this->errorJson('发放数量必须为整数, 而且不能小于 1');
  128. } elseif (isset($patternMatch) && !$patternMatch) {
  129. return $this->errorJson('Member ID 填写不正确, 请重新设置');
  130. } elseif (($couponModel->total != -1) && ($sendTotal * count($memberIds) > $lastTotal)) {
  131. // 优惠券有限,并且发放数量超过限制
  132. if ($lastTotal < 0) {
  133. return $this->errorJson("剩余优惠券不足(准备发放" . $sendTotal * count($memberIds) . "张,此前已超发" . abs($lastTotal) . "张)");
  134. }
  135. return $this->errorJson("剩余优惠券不足(准备发放" . $sendTotal * count($memberIds) . "张,剩余{$lastTotal}张)");
  136. } else {
  137. //发放优惠券
  138. $responseData = [
  139. 'title' => htmlspecialchars_decode($couponModel->resp_title),
  140. 'image' => tomedia($couponModel->resp_thumb),
  141. 'description' => $couponModel->resp_desc ? htmlspecialchars_decode($couponModel->resp_desc) : '亲爱的 [nickname], 你获得了 1 张 "' . $couponModel->name . '" 优惠券',
  142. 'url' => $couponModel->resp_url ?: yzAppFullUrl('home'),
  143. ];
  144. $res = $this->sendCoupon($couponModel, $memberIds, $sendTotal, $responseData);
  145. if ($res) {
  146. //发送获取通知
  147. foreach ($memberIds as $memberId) {
  148. MessageNotice::couponNotice($couponModel->id, $memberId);
  149. }
  150. return $this->successJson('手动发送优惠券成功');
  151. } else {
  152. return $this->errorJson('有部分优惠券未能发送, 请检查数据库', '', 'error');
  153. }
  154. }
  155. }
  156. $data = [
  157. 'send_total' => isset($sendTotal) ? $sendTotal : 0,
  158. 'sendtype' => isset($sendType) ? $sendType : 1,
  159. 'memberLevels' => $memberLevels, //用户等级列表
  160. 'memberGroups' => $memberGroups, //用户分组列表
  161. 'send_level' => isset($sendLevel) ? $sendLevel : 1,
  162. 'memberGroupId' => isset($sendGroup) ? $sendGroup : 1,
  163. 'agentLevelId' => isset($sendLevel) ? $sendLevel : 1,
  164. ];
  165. return $this->successJson('ok', $data);
  166. }
  167. // public function index11()
  168. // {
  169. // $couponId = request()->id;
  170. // $couponModel = Coupon::getCouponById($couponId);
  171. //
  172. // //获取会员等级列表
  173. // $memberLevels = MemberLevel::getMemberLevelList();
  174. //
  175. // //获取会员分组列表
  176. // $memberGroups = MemberGroup::getMemberGroupList();
  177. //
  178. // if ($_POST) {
  179. //
  180. // //获取后台操作者的ID
  181. // $this->adminId = \YunShop::app()->uid;
  182. //
  183. // //获取会员 Member ID
  184. // $sendType = request()->sendtype;
  185. // switch ($sendType) {
  186. // case self::BY_MEMBERIDS:
  187. // $membersScope = trim(request()->send_memberid);
  188. // $patternMatchNumArray = preg_match('/(\d+,)+(\d+,?)/', $membersScope); //匹配比如 "2,3,78"或者"2,3,78,"
  189. // $patternMatchSingleNum = preg_match('/(\d+)(,)?/', $membersScope); //匹配单个数字
  190. //
  191. // $memberIds = explode(',', $membersScope);
  192. // $uid = Member::getMemberId($memberIds);//提取该搜索公众号下的会员id
  193. // $uids = [];
  194. //
  195. // foreach ($uid as $key=>$item){
  196. // $uids[$key] = $item['uid'];//将查出的会员id装到数组里
  197. // }
  198. //
  199. // $member_ids = collect($memberIds)->map(function ($item) {//循环转换为数值类型
  200. // return intval($item);
  201. // })->toArray();
  202. //
  203. //// dd($member_ids);
  204. // $arr = array_diff($member_ids,$uids);//提交过来的会员id与查询出来的会员id对比,留下不存在该公众号的会员id
  205. //
  206. // if (!empty($arr)){ //判断是否存在不是该公众号的会员id
  207. // throw new ShopException("发放优惠券失败,请确认该".implode(",", $arr)."会员是否是该公众号会员");
  208. // }
  209. //
  210. // if ($patternMatchNumArray || $patternMatchSingleNum) {
  211. // $patternMatch = true;
  212. // } else {
  213. // $patternMatch = false;
  214. // }
  215. // break;
  216. // case self::BY_MEMBER_LEVEL: //根据"会员等级"获取 Member IDs
  217. // $sendLevel = request()->send_level;
  218. // if (!$sendLevel) {
  219. // return $this->message('请选择会员等级!', '', 'error');
  220. // }
  221. // $res = MemberLevel::getMembersByLevel($sendLevel);
  222. // if ($res['member']->isEmpty()) {
  223. // $memberIds = '';
  224. // } else {
  225. // $res = $res->toArray();
  226. // $memberIds = array_column($res['member'], 'member_id'); //提取member_id组成新的数组
  227. // }
  228. // break;
  229. // case self::BY_MEMBER_GROUP: //根据"会员分组"获取 Member IDs
  230. // $sendGroup = request()->send_group;
  231. // if (!$sendGroup) {
  232. // return $this->message('请选择会员组!', '', 'error');
  233. // }
  234. // $res = MemberGroup::getMembersByGroupId($sendGroup);
  235. // if ($res['member']->isEmpty()) {
  236. // $memberIds = '';
  237. // } else {
  238. // $res = $res->toArray();
  239. // $memberIds = array_column($res['member'], 'member_id'); //提取member_id组成新的数组
  240. // }
  241. // break;
  242. // case self::TO_ALL_MEMBERS:
  243. //// $res = Member::getMembersId();
  244. // $res = MemberShopInfo::getYzMembersId();
  245. // if (!$res) {
  246. // $members = '';
  247. // } else {
  248. // $members = $res->toArray();
  249. // }
  250. // $memberIds = array_column($members, 'member_id');
  251. // break;
  252. // default:
  253. // $memberIds = '';
  254. // }
  255. //
  256. // //获取发放的数量
  257. // $sendTotal = request()->send_total;
  258. // $getTotal = MemberCoupon::uniacid()->where("coupon_id", $couponModel->id)->count();
  259. // $lastTotal = $couponModel->total - $getTotal;
  260. // if (empty($memberIds)) {
  261. // throw new ShopException('该发放类型下还没有用户');
  262. // }elseif(!$couponModel->status){
  263. // throw new ShopException('优惠券已下架,请先重新上架');
  264. // } elseif ($sendTotal < 1) {
  265. // throw new ShopException('发放数量必须为整数, 而且不能小于 1');
  266. // } elseif (isset($patternMatch) && !$patternMatch) {
  267. // throw new ShopException('Member ID 填写不正确, 请重新设置');
  268. // } elseif (($couponModel->total != -1) && ($sendTotal * count($memberIds) > $lastTotal)) {
  269. // // 优惠券有限,并且发放数量超过限制
  270. // if($lastTotal<0){
  271. // throw new ShopException("剩余优惠券不足(准备发放".$sendTotal * count($memberIds)."张,此前已超发".abs($lastTotal)."张)");
  272. // }
  273. // throw new ShopException("剩余优惠券不足(准备发放".$sendTotal * count($memberIds)."张,剩余{$lastTotal}张)");
  274. // } else {
  275. //
  276. // //发放优惠券
  277. // $responseData = [
  278. // 'title' => htmlspecialchars_decode($couponModel->resp_title),
  279. // 'image' => tomedia($couponModel->resp_thumb),
  280. // 'description' => $couponModel->resp_desc ? htmlspecialchars_decode($couponModel->resp_desc) : '亲爱的 [nickname], 你获得了 1 张 "' . $couponModel->name . '" 优惠券',
  281. // 'url' => $couponModel->resp_url ?: yzAppFullUrl('home'),
  282. // ];
  283. // $res = $this->sendCoupon($couponModel, $memberIds, $sendTotal, $responseData);
  284. // if ($res) {
  285. //
  286. // //发送获取通知
  287. // foreach ($memberIds as $memberId) {
  288. // MessageNotice::couponNotice($couponModel->id,$memberId);
  289. // }
  290. //
  291. // return $this->message('手动发送优惠券成功');
  292. // } else {
  293. // return $this->message('有部分优惠券未能发送, 请检查数据库', '', 'error');
  294. // }
  295. // }
  296. // }
  297. //
  298. // return view('coupon.send', [
  299. // 'send_total' => isset($sendTotal) ? $sendTotal : 0,
  300. // 'sendtype' => isset($sendType) ? $sendType : 1,
  301. // 'memberLevels' => $memberLevels, //用户等级列表
  302. // 'memberGroups' => $memberGroups, //用户分组列表
  303. // 'send_level' => isset($sendLevel) ? $sendLevel : 1,
  304. // 'memberGroupId' => isset($sendGroup) ? $sendGroup : 1,
  305. // 'agentLevelId' => isset($sendLevel) ? $sendLevel : 1,
  306. // ])->render();
  307. // }
  308. //发放优惠券
  309. //array $members
  310. public function sendCoupon($couponModel, $memberIds, $sendTotal, $responseData)
  311. {
  312. $data = [
  313. 'uniacid' => \YunShop::app()->uniacid,
  314. 'coupon_id' => $couponModel->id,
  315. 'get_type' => 0,
  316. 'used' => 0,
  317. 'get_time' => strtotime('now'),
  318. ];
  319. foreach ($memberIds as $memberId) {
  320. $messageData = $responseData;
  321. // //获取Openid
  322. // $memberOpenid = McMappingFans::getFansById($memberId)->openid;
  323. for ($i = 0; $i < $sendTotal; $i++) {
  324. $memberCoupon = new MemberCoupon;
  325. $data['uid'] = $memberId;
  326. $res = $memberCoupon->create($data);
  327. //写入log
  328. if ($res) { //发放优惠券成功
  329. $log = '手动发放优惠券成功: 管理员( ID 为 ' . $this->adminId . ' )成功发放1张优惠券( ID为 ' . $couponModel->id . ' )给用户( Member ID 为 ' . $memberId . ' )';
  330. } else { //发放优惠券失败
  331. $log = '手动发放优惠券失败: 管理员( ID 为 ' . $this->adminId . ' )发放优惠券( ID为 ' . $couponModel->id . ' )给用户( Member ID 为 ' . $memberId . ' )时失败!';
  332. $this->failedSend[] = $log; //失败时, 记录 todo 最后需要展示出来
  333. \Log::info($log);
  334. }
  335. $this->log($log, $couponModel, $memberId);
  336. }
  337. if (!empty($messageData['title'])) { //没有关注公众号的用户是没有 openid
  338. $templateId = \Setting::get('coupon_template_id'); //模板消息ID
  339. $nickname = Member::getMemberById($memberId)->nickname;
  340. $dynamicData = [
  341. 'nickname' => $nickname,
  342. 'couponname' => $couponModel->name,
  343. ];
  344. $messageData['title'] = self::dynamicMsg($messageData['title'], $dynamicData);
  345. $messageData['description'] = self::dynamicMsg($messageData['description'], $dynamicData);
  346. Message::message($messageData, $templateId, $memberId); //默认使用微信"客服消息"通知, 对于超过 48 小时未和平台互动的用户, 使用"模板消息"通知
  347. }
  348. }
  349. if (empty($this->failedSend)) {
  350. return true;
  351. } else {
  352. return false;
  353. }
  354. }
  355. //写入日志
  356. public function log($log, $couponModel, $memberId)
  357. {
  358. $logData = [
  359. 'uniacid' => \YunShop::app()->uniacid,
  360. 'logno' => $log,
  361. 'member_id' => $memberId,
  362. 'couponid' => $couponModel->id,
  363. 'paystatus' => 0, //todo 手动发放的不需要支付?
  364. 'creditstatus' => 0, //todo 手动发放的不需要支付?
  365. 'paytype' => 0, //todo 这个字段什么含义?
  366. 'getfrom' => 0,
  367. 'status' => 0,
  368. 'createtime' => time(),
  369. ];
  370. $res = CouponLog::create($logData);
  371. return $res;
  372. }
  373. //动态显示内容
  374. protected static function dynamicMsg($msg, $data)
  375. {
  376. if (preg_match('/\[nickname\]/', $msg)) {
  377. $msg = str_replace('[nickname]', $data['nickname'], $msg);
  378. }
  379. if (preg_match('/\[couponname\]/', $msg)) {
  380. $msg = str_replace('[couponname]', $data['couponname'], $msg);
  381. }
  382. return $msg;
  383. }
  384. }