diff --git a/app/Amqp/Consumer/CouponAutoDispenseConsumer.php b/app/Amqp/Consumer/CouponAutoDispenseConsumer.php index badffba..fb23ebc 100644 --- a/app/Amqp/Consumer/CouponAutoDispenseConsumer.php +++ b/app/Amqp/Consumer/CouponAutoDispenseConsumer.php @@ -39,7 +39,7 @@ class CouponAutoDispenseConsumer extends ConsumerMessage */ public function consumeMessage($data, AMQPMessage $message): Result { - if (!$data['coupon_dispense_id']) { + if (!$data['coupon_dispense_id'] || !$data['user_id']) { $this->log->error('CouponAutoDispenseConsumer:error:NoData:'.json_encode($data)); return Result::ACK; } @@ -48,6 +48,7 @@ class CouponAutoDispenseConsumer extends ConsumerMessage $service = new AutoDispenseService(); $service->couponDispenseId = $data['coupon_dispense_id']; + $service->userId = $data['user_id']; $service->handle(); diff --git a/app/Amqp/Consumer/WxSubMessageConsumer.php b/app/Amqp/Consumer/WxSubMessageConsumer.php new file mode 100644 index 0000000..905c17f --- /dev/null +++ b/app/Amqp/Consumer/WxSubMessageConsumer.php @@ -0,0 +1,73 @@ +log->error('WxSubMessageConsumer:error:NoData:'.json_encode($data)); + return Result::ACK; + } + + $openId = $this->userThirdModel->where('user_id',$data['user_id'])->where('type',ThirdCode::WX_LOGIN)->value('open_id') ?? null; + if (empty($openId)) { + $this->log->error('WxSubMessageConsumer:error:NoOpenId:'.json_encode($data)); + return Result::ACK; + } + + try { + $this->sendSubMessage(WxMiniCode::TEMPLATE_ID_LIST[$data['type']],$openId,$data['data'],$data['page'] ?? null); + } catch (Exception|ErrException $e) { + $this->log->error('WxSubMessageConsumer:error:'.$e->getMessage().':data:'.json_encode($data)); + } + + return Result::ACK; + } +} diff --git a/app/Amqp/Producer/WxSubMessageProducer.php b/app/Amqp/Producer/WxSubMessageProducer.php new file mode 100644 index 0000000..51e4889 --- /dev/null +++ b/app/Amqp/Producer/WxSubMessageProducer.php @@ -0,0 +1,26 @@ + {"type":"1","user_id":"1","data":[],"page":null} + */ + $this->payload = $data; + } +} diff --git a/app/Constants/Common/WxMiniCode.php b/app/Constants/Common/WxMiniCode.php new file mode 100644 index 0000000..7d30f1f --- /dev/null +++ b/app/Constants/Common/WxMiniCode.php @@ -0,0 +1,24 @@ + self::SEND_SUB_MESSAGE_GET_COUPON_TEMPLATE_ID, + self::SEND_MSG_TYPE_DELIVER => self::SEND_SUB_MESSAGE_DELIVER_TEMPLATE_ID + ]; + +} \ No newline at end of file diff --git a/app/Model/CouponDispenseUser.php b/app/Model/CouponDispenseUser.php index 7be162f..49c2c70 100644 --- a/app/Model/CouponDispenseUser.php +++ b/app/Model/CouponDispenseUser.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Model; use App\Constants\Common\CouponCode; +use Hyperf\Database\Model\Builder; use Hyperf\DbConnection\Model\Model; /** @@ -58,6 +59,16 @@ class CouponDispenseUser extends Model ->toArray(); } + /** + * @param int $dispenseId + * @param int $userId + * @return Builder|\Hyperf\Database\Model\Model|null + */ + public function getInfoByDispenseIdAndUserId(int $dispenseId, int $userId): \Hyperf\Database\Model\Model|Builder|null + { + return $this->where('dispense_id', $dispenseId)->where('user_id', $userId)->first(); + } + /** * @param array $userIds * @param int $count diff --git a/app/Service/Admin/Coupon/DispenseAddService.php b/app/Service/Admin/Coupon/DispenseAddService.php index 48496d5..67ea6c7 100644 --- a/app/Service/Admin/Coupon/DispenseAddService.php +++ b/app/Service/Admin/Coupon/DispenseAddService.php @@ -71,6 +71,12 @@ class DispenseAddService extends BaseService #[Inject] protected CouponTemplate $couponTemplateModel; + /** + * @var Producer + */ + #[Inject] + protected Producer $producer; + /** * @var int */ @@ -132,14 +138,7 @@ class DispenseAddService extends BaseService $this->addSubTableInformation($this->dispenseId); }); - if ($this->claimRule == CouponCode::DISPENSE_CLAIM_RULE_POST_ACCOUNT) { - //mq发放优惠券 - $message = new CouponAutoDispenseProducer([ - 'coupon_dispense_id' => $this->dispenseId, - ]); - $producer = ApplicationContext::getContainer()->get(Producer::class); - $producer->produce($message); - } + $this->sendAutoMq(); return $this->return->success(); }catch (Exception $e) { @@ -147,6 +146,28 @@ class DispenseAddService extends BaseService } } + /** + * @return void + */ + private function sendAutoMq(): void + { + if ($this->claimRule != CouponCode::DISPENSE_CLAIM_RULE_POST_ACCOUNT) return; + + if (empty($this->userIds)) return; + + foreach ($this->userIds as $userId) { + //mq发放优惠券 + $message = new CouponAutoDispenseProducer([ + 'coupon_dispense_id' => $this->dispenseId, + 'user_id' => $userId, + ]); +// $producer = ApplicationContext::getContainer()->get(Producer::class); + $this->producer->produce($message); + } + + //todo 加一个缓存进度控制器 有没有必要 + } + /** * @param int $dispenseId * @return void diff --git a/app/Service/Amqp/Coupon/AutoDispenseService.php b/app/Service/Amqp/Coupon/AutoDispenseService.php index 2ea9b38..8cc4e39 100644 --- a/app/Service/Amqp/Coupon/AutoDispenseService.php +++ b/app/Service/Amqp/Coupon/AutoDispenseService.php @@ -10,19 +10,22 @@ declare(strict_types=1); namespace App\Service\Amqp\Coupon; +use App\Amqp\Producer\WxSubMessageProducer; use App\Constants\Common\CouponCode; +use App\Constants\Common\WxMiniCode; use App\Model\CouponDispenseLog; use App\Model\CouponDispenseUser; +use App\Model\CouponTemplate; use App\Model\UserCoupon; +use App\Service\ServiceTrait\Api\CouponTrait; use Exception; -use Hyperf\Coroutine\Coroutine; +use Hyperf\Amqp\Producer; use Hyperf\DbConnection\Db; use Hyperf\Di\Annotation\Inject; class AutoDispenseService { - - const int SINGLE_MAX = 1; + use CouponTrait; /** * @var CouponDispenseLog @@ -36,59 +39,108 @@ class AutoDispenseService #[Inject] protected CouponDispenseUser $couponDispenseUserModel; + /** + * @var UserCoupon + */ + #[Inject] + protected UserCoupon $userCouponModel; + + /** + * @var CouponTemplate + */ + #[Inject] + protected CouponTemplate $couponTemplateModel; + + /** + * @var Producer + */ + #[Inject] + protected Producer $producer; + /** * @var int */ public int $couponDispenseId; + /** + * @var int + */ + public int $userId; + /** * @return void - * @throws Exception */ public function handle(): void { $dispenseInfo = $this->couponDispenseLogModel->getInfoById($this->couponDispenseId); - if (empty($dispenseInfo)) throw new Exception('分发记录不存在'); - $userIds = $this->couponDispenseUserModel->getNoSendUserListByDispenseId($this->couponDispenseId); - if (empty($userIds)) throw new Exception('该分发已发送完毕'); + $templateInfo = $this->couponTemplateModel->getInfoById($dispenseInfo->coupon_template_id); + if (empty($templateInfo)) throw new Exception('优惠券模板不存在'); - $partArr = array_chunk($userIds, self::SINGLE_MAX); + $validityTime = $this->getValidityTime((int)$dispenseInfo->validity_time_type, $dispenseInfo->validity_time_value); + //有效期不存在或者到期销毁当前分发逻辑 + if (empty($validityTime) || empty($validityTime['start_time']) || empty($validityTime['end_time']) || $validityTime['end_time'] < date('Y-m-d H:i:s')) { + $dispenseInfo->status = CouponCode::DISPENSE_NULL; + if (!$dispenseInfo->save()) throw new Exception('修改分发记录状态失败'); + throw new Exception('该分发逻辑已失效'); + } - foreach ($partArr as $onePart) { - $insertData = []; - foreach ($onePart as $oneUser) { - // 模拟数据 - $oneUserData = [ - 'user_id' => $oneUser, - 'coupon_template_id' => $dispenseInfo->coupon_template_id, - 'coupon_name' => $dispenseInfo->coupon_name, - 'coupon_dispense_id' => $dispenseInfo->id, - 'status' => CouponCode::COUPON_STATUS_UNUSED, - 'validity_start_time' => $dispenseInfo->validity_start_time, - 'validity_end_time' => $dispenseInfo->validity_end_time, - ]; + $userDispenseInfo = $this->couponDispenseUserModel->getInfoByDispenseIdAndUserId($this->couponDispenseId,$this->userId); + if (empty($userDispenseInfo)) throw new Exception('该用户不属于该分发逻辑'); - $copies = array_map(function () use ($oneUserData) { - return $oneUserData; - }, array_fill(0, $dispenseInfo->item_count, null)); + $userCount = $this->userCouponModel->where('coupon_dispense_id',$this->couponDispenseId)->where('user_id',$this->userId)->count() ?? 0; + if ($userCount >= $dispenseInfo->item_count) throw new Exception('该用户该分发逻辑已分发完毕'); - $insertData = array_merge($insertData, $copies); - } + // 数据 + $oneData = [ + 'user_id' => $this->userId, + 'coupon_template_id' => $dispenseInfo->coupon_template_id, + 'coupon_name' => $dispenseInfo->coupon_name, + 'coupon_dispense_id' => $dispenseInfo->id, + 'status' => CouponCode::COUPON_STATUS_UNUSED, + 'validity_start_time' => $validityTime['start_time'], + 'validity_end_time' => $validityTime['end_time'], + ]; - Db::transaction(function () use ($insertData,$dispenseInfo,$onePart) { - //添加优惠券信息 - if (!(new UserCoupon())->insert($insertData)) throw new Exception('写入数据失败'); - //修改分发用户信息 - if (!(new CouponDispenseUser())->updateReceiveCountByUserIds($onePart,$dispenseInfo->item_count)) throw new Exception('修改分发用户信息失败'); + $insertData = array_map(function () use ($oneData) { + return $oneData; + }, array_fill(0, $dispenseInfo->item_count - $userCount, null)); - //修改分发 log 信息 (修改receive_count = origin_value + count($insertData) ps : 查询还没发的用户 * item_count / total_count = 完成百分比) - if (!(new CouponDispenseLog())->updateReceiveCountById($dispenseInfo->id,count($insertData))) throw new Exception('修改分发log信息失败'); - }); + Db::transaction(function () use ($insertData,$dispenseInfo,$userDispenseInfo) { + // 添加优惠券信息 + if (!(new UserCoupon())->insert($insertData)) throw new Exception('写入数据失败'); - // 休息1秒 - Coroutine::sleep(1); + // 修改分发用户信息 + $userDispenseInfo->receive_count = $userDispenseInfo->receive_count + count($insertData); + $userDispenseInfo->is_receive = CouponCode::DISPENSE_STATUS_IS_RECEIVED; + if (!$userDispenseInfo->save()) throw new Exception('修改分发用户信息失败'); + + //修改分发 log 信息 (修改receive_count = origin_value + count($insertData) ps : 查询还没发的用户 * item_count / total_count = 完成百分比) + $dispenseInfo->receive_count = $dispenseInfo->receive_count + count($insertData); + if (!$dispenseInfo->save()) throw new Exception('修改分发log信息失败'); + }); + + $nominalValue = match ($templateInfo->coupon_type) { + CouponCode::COUPON_TYPE_INSTANT_REDUCTION => $templateInfo->amount.'元', + CouponCode::COUPON_TYPE_DISCOUNT => ($templateInfo->ratio * 10).'%', + }; + + foreach ($insertData as $item) { + $msgData = [ + 'thing2' => ['value' => $dispenseInfo->coupon_name], + 'thing9' => ['value' => $nominalValue], + 'thing8' => ['value' => $validityTime['start_time'].'/'.$validityTime['end_time']], + 'thing4' => ['value' => 'xxx'], + ]; + + $message = new WxSubMessageProducer([ + 'type' => WxMiniCode::SEND_MSG_TYPE_GET_COUPON, + 'user_id' => $this->userId, + 'data' => $msgData, + 'page' => null + ]); + $this->producer->produce($message); } } } \ No newline at end of file diff --git a/app/Service/Api/Coupon/ReceiveService.php b/app/Service/Api/Coupon/ReceiveService.php index a547ac1..36adc3b 100644 --- a/app/Service/Api/Coupon/ReceiveService.php +++ b/app/Service/Api/Coupon/ReceiveService.php @@ -16,11 +16,14 @@ use App\Extend\DateUtil; use App\Model\CouponDispenseLog; use App\Model\UserCoupon; use App\Service\Api\BaseService; +use App\Service\ServiceTrait\Api\CouponTrait; use Hyperf\DbConnection\Db; use Hyperf\Di\Annotation\Inject; class ReceiveService extends BaseService { + use CouponTrait; + /** * @var CouponDispenseLog */ @@ -71,22 +74,10 @@ class ReceiveService extends BaseService if (empty($couponArr)) return; foreach ($couponArr as $item) { - $validityTime = []; - switch ($item['validity_time_type']) { - case CouponCode::VALIDITY_TIME_TYPE_CYCLE: - $validityValue = json_decode($item['validity_time_value'],true); - $validityTime = [ - 'start_time' => date('Y-m-d H:i:s'), - 'end_time' => date('Y-m-d H:i:s', time() + ($validityValue['day_num'] * DateUtil::DAY)) - ]; - break; - case CouponCode::VALIDITY_TIME_TYPE_FIX: - $validityTime = json_decode($item['validity_time_value'],true); - break; - } + $validityTime = $this->getValidityTime((int)$item['validity_time_type'], $item['validity_time_value']); //有效期不存在或者到期销毁当前分发逻辑 - if (($validityTime['end_time'] ?? date('Y-m-d H:i:s')) < date('Y-m-d H:i:s')) { + if (empty($validityTime) || ($validityTime['end_time'] ?? date('Y-m-d H:i:s')) < date('Y-m-d H:i:s')) { $this->destroyDispenseData[] = $item['id']; continue; } diff --git a/app/Service/ServiceTrait/Api/CouponTrait.php b/app/Service/ServiceTrait/Api/CouponTrait.php index a5adf2f..bb28337 100644 --- a/app/Service/ServiceTrait/Api/CouponTrait.php +++ b/app/Service/ServiceTrait/Api/CouponTrait.php @@ -4,6 +4,7 @@ namespace App\Service\ServiceTrait\Api; use App\Constants\Common\CouponCode; use App\Constants\Common\OrderCode; +use App\Extend\DateUtil; use App\Model\UserCoupon; use Exception; use Hyperf\Di\Annotation\Inject; @@ -38,4 +39,25 @@ trait CouponTrait if (!$couponInfo->save()) throw new Exception('CancelOrderConsumer:error:couponStatusUpdateError:'.json_encode($orderInfo->toArray())); } + + /** + * @param int $type + * @param string $value + * @return array + */ + protected function getValidityTime(int $type,string $value): array + { + switch ($type) { + case CouponCode::VALIDITY_TIME_TYPE_CYCLE: + $validityValue = json_decode($value,true); + return [ + 'start_time' => date('Y-m-d H:i:s'), + 'end_time' => date('Y-m-d H:i:s', time() + ($validityValue['day_num'] * DateUtil::DAY)) + ]; + case CouponCode::VALIDITY_TIME_TYPE_FIX: + return json_decode($value,true); + default: + return []; + } + } } \ No newline at end of file diff --git a/app/Service/ServiceTrait/Api/WxMiniTrait.php b/app/Service/ServiceTrait/Api/WxMiniTrait.php index b345746..5cf282a 100644 --- a/app/Service/ServiceTrait/Api/WxMiniTrait.php +++ b/app/Service/ServiceTrait/Api/WxMiniTrait.php @@ -15,6 +15,7 @@ use App\Cache\Redis\Common\CommonRedisKey; use App\Cache\Redis\RedisCache; use App\Constants\RedisCode; use App\Exception\ErrException; +use App\Extend\SystemUtil; use App\Lib\Log; use GuzzleHttp\Exception\GuzzleException; use Hyperf\Di\Annotation\Inject; @@ -194,4 +195,54 @@ trait WxMiniTrait throw new ErrException($e->getMessage()); } } + + /** + * @param string $templateId + * @param string $toUserOpenId + * @param array $data + * @param string|null $page + * @return mixed + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + protected function sendSubMessage(string $templateId, string $toUserOpenId, array $data ,string $page = null): mixed + { + $url = sprintf( + '/cgi-bin/message/subscribe/send?access_token=%s', + $this->getAccessTokenCache() + ); + + $params = [ + 'template_id' => $templateId, + 'touser' => $toUserOpenId, + 'data' => $data, + 'miniprogram_state' => !SystemUtil::checkProEnv() ? 'developer' : 'formal', //不是正式环境 使用开发版 + 'lang' => 'zh_CN' + ]; + if (!empty($page)) $params['page'] = $page; + + try { + $wxResponse = $this->clientFactory->create([ + 'base_uri' => $this->BaseUri, + 'timeout' => 5 + ])->post($url,[ + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'body' => json_encode($params) + ]); + + $contents = $wxResponse->getBody()->getContents(); + + $this->log->callbackLog(__class__.':微信服务器返回send_sub_message信息:'.json_encode($contents)); + + $res = json_decode($contents,true); + + if (!empty($res['errcode']) && $res['errcode'] != 0) throw new ErrException($res['errmsg'] ?? '系统繁忙'); + + return $res; + }catch (GuzzleException $e){ + throw new ErrException($e->getMessage()); + } + } } \ No newline at end of file