From ea374d578f03919b7eb9841dba77d9a45cd13e57 Mon Sep 17 00:00:00 2001 From: ctexthuang Date: Wed, 2 Apr 2025 16:05:24 +0800 Subject: [PATCH] feat : finish order --- app/Constants/Common/AccountCode.php | 25 ++ app/Model/AccountDetail.php | 42 ++++ app/Model/UserAccount.php | 54 +++++ .../Admin/Coupon/DispenseAddService.php | 4 +- .../Order/GoodOrderFinishSuccessorService.php | 219 +++++++++++++++++- .../Account/AccountOperateInterface.php | 67 ++++++ .../Common/Account/BalanceOperateService.php | 24 ++ .../Common/Account/PointOperateService.php | 70 ++++++ 8 files changed, 498 insertions(+), 7 deletions(-) create mode 100644 app/Constants/Common/AccountCode.php create mode 100644 app/Model/AccountDetail.php create mode 100644 app/Service/Common/Account/AccountOperateInterface.php create mode 100644 app/Service/Common/Account/BalanceOperateService.php create mode 100644 app/Service/Common/Account/PointOperateService.php diff --git a/app/Constants/Common/AccountCode.php b/app/Constants/Common/AccountCode.php new file mode 100644 index 0000000..83116e0 --- /dev/null +++ b/app/Constants/Common/AccountCode.php @@ -0,0 +1,25 @@ + 'integer', 'user_id' => 'integer', 'account_type' => 'integer', 'business_code' => 'integer', 'track_user_id' => 'integer']; + + const string CREATED_AT = 'create_time'; + const null UPDATED_AT = null; +} diff --git a/app/Model/UserAccount.php b/app/Model/UserAccount.php index 5dd7eeb..a1add04 100644 --- a/app/Model/UserAccount.php +++ b/app/Model/UserAccount.php @@ -32,4 +32,58 @@ class UserAccount extends Model const CREATED_AT = null; const UPDATED_AT = null; + + /** + * @param int $userId + * @return \Hyperf\Database\Model\Model|UserAccount|null + */ + public function getAccountByUserId(int $userId): \Hyperf\Database\Model\Model|UserAccount|null + { + return $this->where('user_id',$userId)->first(); + } + + /** + * 增加积分 + * @param $userId + * @param $num + * @return int + */ + public function incPointByUserId($userId, $num): int + { + return $this->where('id', $userId)->increment('integral', $num); + } + + /** + * 减少积分 + * @param $userId + * @param $num + * @return int + */ + public function decIntegralByUserId($userId, $num): int + { + return $this->where('id', $userId)->decrement('integral', $num); + } + + + /** + * 增加余额 + * @param $userId + * @param $num + * @return int + */ + public function incBalanceByUserId($userId, $num): int + { + return $this->where('id', $userId)->increment('balance', $num); + } + + /** + * 减少余额 + * @param $userId + * @param $num + * @return int + */ + public function decBalanceByUserId($userId, $num): int + { + return $this->where('id', $userId)->decrement('balance', $num); + } } diff --git a/app/Service/Admin/Coupon/DispenseAddService.php b/app/Service/Admin/Coupon/DispenseAddService.php index 3d0cea6..8c695bb 100644 --- a/app/Service/Admin/Coupon/DispenseAddService.php +++ b/app/Service/Admin/Coupon/DispenseAddService.php @@ -127,6 +127,8 @@ class DispenseAddService extends BaseService /** * @return array + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function handle(): array { @@ -155,8 +157,6 @@ class DispenseAddService extends BaseService /** * @return void - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ private function sendAutoMq(): void { diff --git a/app/Service/Amqp/Order/GoodOrderFinishSuccessorService.php b/app/Service/Amqp/Order/GoodOrderFinishSuccessorService.php index 1edf5b9..990f2dd 100644 --- a/app/Service/Amqp/Order/GoodOrderFinishSuccessorService.php +++ b/app/Service/Amqp/Order/GoodOrderFinishSuccessorService.php @@ -10,10 +10,26 @@ declare(strict_types=1); namespace App\Service\Amqp\Order; +use App\Amqp\Producer\WxSubMessageProducer; +use App\Cache\Redis\Common\ConfigCache; +use App\Constants\Common\AccountCode; +use App\Constants\Common\CouponCode; use App\Constants\Common\OrderCode; +use App\Constants\Common\UserCode; +use App\Constants\Common\WxMiniCode; +use App\Constants\ConfigCode; +use App\Exception\ErrException; +use App\Model\CouponTemplate; use App\Model\Order; +use App\Model\UserCoupon; +use App\Model\UserInvite; +use App\Service\Common\Account\PointOperateService; use Exception; +use Hyperf\Amqp\Producer; +use Hyperf\DbConnection\Db; use Hyperf\Di\Annotation\Inject; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; class GoodOrderFinishSuccessorService { @@ -33,7 +49,36 @@ class GoodOrderFinishSuccessorService */ protected Order $orderInfo; - public function handle() + /** + * @var ConfigCache + */ + #[Inject] + protected ConfigCache $configCache; + + /** + * @var UserInvite + */ + #[Inject] + protected UserInvite $userInviteModel; + + /** + * @var PointOperateService + */ + #[Inject] + protected PointOperateService $pointOperateService; + + /** + * @var CouponTemplate + */ + #[Inject] + protected CouponTemplate $couponTemplateModel; + + /** + * @return void + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function handle(): void { $this->orderInfo = $this->orderModel->find($this->orderId); @@ -41,18 +86,182 @@ class GoodOrderFinishSuccessorService if ($this->orderInfo->status != OrderCode::FINISH) throw new Exception('订单状态错误'); - $this->addOrderPoint(); + try { + Db::beginTransaction(); - $this->addInvite(); + $this->addOrderPoint(); + + $this->addInvite(); + + Db::commit(); + }catch (Exception|ErrException $e) { + Db::rollBack(); + throw new Exception($e->getMessage()); + } } - private function addOrderPoint() + /** + * @return void + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + private function addOrderPoint(): void { + $ratio = $this->configCache->getConfigValueByKey(ConfigCode::CONSUMPTION_RATIO); + if ($ratio <= 0) return; + + $points = bcmul($this->orderInfo->actual_price,(string)$ratio,0); + + $this->pointOperateService->inc( + $this->orderInfo->user_id, + $points, + AccountCode::GET_POINTS_FOR_COMPLETING_AN_ORDER, + ['orderId' => $this->orderId] + ); } - private function addInvite() + /** + * @var UserInvite|null + */ + protected UserInvite|null $inviteInfo; + + /** + * @var array + */ + protected array $couponTemplateList; + + /** + * @var Producer + */ + #[Inject] + protected Producer $producer; + + /** + * @return void + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + private function addInvite(): void { + $this->inviteInfo = $this->userInviteModel->where('user_id',$this->orderInfo->user_id)->first(); + if (empty($this->inviteInfo)) return; + + if ($this->inviteInfo->status != UserCode::INVITE_STATUS_REGISTER) return; + + $invitingCouponTemplateId = $this->configCache->getConfigValueByKey(ConfigCode::INVITING_PARTY_COUPON); + $invitingCouponValidity = $this->configCache->getConfigValueByKey(ConfigCode::INVITING_PARTY_COUPON_VALIDITY); + $inviteeCouponTemplateId = $this->configCache->getConfigValueByKey(ConfigCode::INVITED_PARTY_COUPON); + $inviteeCouponValidity = $this->configCache->getConfigValueByKey(ConfigCode::INVITEE_COUPON_VALIDITY); + + $invitingCouponTemplateId = explode(',', $invitingCouponTemplateId); + $inviteeCouponTemplateId = explode(',', $inviteeCouponTemplateId); + $couponTemplateId = array_filter(array_merge($invitingCouponTemplateId,$inviteeCouponTemplateId)); + if (empty($couponTemplateId)) return; + + $this->couponTemplateList = $this->couponTemplateModel->getDataByIds($couponTemplateId); + if (empty($this->couponTemplateList)) return; + + $invitingData = []; + $inviteeData = []; + // 随便一个为0代表不赠送 + if ($invitingCouponTemplateId != 0 && $invitingCouponValidity != 0) $invitingData = $this->invitingAction($invitingCouponTemplateId,$invitingCouponValidity); + + if ($inviteeCouponTemplateId != 0 && $inviteeCouponValidity != 0) $inviteeData = $this->inviteeAction($inviteeCouponTemplateId,$inviteeCouponValidity); + $insertData = array_merge($invitingData,$inviteeData); + + if (empty($insertData)) return; + + foreach ($insertData as $item) { + $nominalValue = match ($this->couponTemplateList[$item['coupon_template_id']]['coupon_type']) { + CouponCode::COUPON_TYPE_INSTANT_REDUCTION => $this->couponTemplateList[$item['coupon_template_id']]['amount'].'元', + CouponCode::COUPON_TYPE_DISCOUNT => ($this->couponTemplateList[$item['coupon_template_id']]['ratio'] * 10).'%', + }; + + $msgData = [ + 'thing2' => ['value' => $item['coupon_name']], + 'thing9' => ['value' => $nominalValue], + 'thing8' => ['value' => $item['validity_start_time'].'/'.$item['validity_end_time']], + 'thing4' => ['value' => 'xxx'], + ]; + + $message = new WxSubMessageProducer([ + 'type' => WxMiniCode::SEND_MSG_TYPE_GET_COUPON, + 'user_id' => $item['user_id'], + 'data' => $msgData, + 'page' => null + ]); + $this->producer->produce($message); + } } + + /** + * @param array $invitingCouponTemplateId + * @param int $invitingCouponValidity + * @return array + * @throws Exception + */ + private function invitingAction(array $invitingCouponTemplateId,int $invitingCouponValidity): array + { + $insertCoupon = []; + foreach ($invitingCouponTemplateId as $one){ + if ($this->couponTemplateList[$one]['status'] == CouponCode::COUPON_TEMPLATE_STATUS_ENABLE) continue; + + $insertCoupon[] = [ + 'coupon_template_id' => $one, + 'coupon_dispense_id' => CouponCode::SYSTEMIC_DISTRIBUTION, + 'user_id' => $this->inviteInfo->invitee_user_id, + 'status' => CouponCode::COUPON_STATUS_UNUSED, + 'coupon_name' => $this->couponTemplateList[$one]['name'], + 'validity_start_time' => date('Y-m-d H:i:s'), + 'validity_end_time' => date('Y-m-d', strtotime('+'.$invitingCouponValidity.' days')), + ]; + } + + if (empty($insertCoupon)) return []; + + if (!(new UserCoupon)->insert($insertCoupon)) throw new Exception('赠送邀请优惠券失败:data:'.json_encode([ + 'invite' => $this->inviteInfo->toArray(), + 'coupon_template_id' => $invitingCouponTemplateId, + 'CouponValidity' => $invitingCouponValidity + ])); + + return $insertCoupon; + } + + /** + * @param array $inviteeCouponTemplateId + * @param int $inviteeCouponValidity + * @return array + * @throws Exception + */ + private function inviteeAction(array $inviteeCouponTemplateId,int $inviteeCouponValidity): array + { + $insertCoupon = []; + foreach ($inviteeCouponTemplateId as $one){ + if ($this->couponTemplateList[$one]['status'] == CouponCode::COUPON_TEMPLATE_STATUS_ENABLE) continue; + + $insertCoupon[] = [ + 'coupon_template_id' => $one, + 'coupon_dispense_id' => CouponCode::SYSTEMIC_DISTRIBUTION, + 'user_id' => $this->inviteInfo->user_id, + 'status' => CouponCode::COUPON_STATUS_UNUSED, + 'coupon_name' => $this->couponTemplateList[$one]['name'], + 'validity_start_time' => date('Y-m-d H:i:s'), + 'validity_end_time' => date('Y-m-d', strtotime('+'.$inviteeCouponValidity.' days')), + ]; + } + + if (empty($insertCoupon)) return []; + + if (!(new UserCoupon)->insert($insertCoupon)) throw new Exception('赠送被邀请优惠券失败:data:'.json_encode([ + 'invite' => $this->inviteInfo->toArray(), + 'coupon_template_id' => $inviteeCouponTemplateId, + 'CouponValidity' => $inviteeCouponValidity + ])); + + return $insertCoupon; + } + } \ No newline at end of file diff --git a/app/Service/Common/Account/AccountOperateInterface.php b/app/Service/Common/Account/AccountOperateInterface.php new file mode 100644 index 0000000..af058a6 --- /dev/null +++ b/app/Service/Common/Account/AccountOperateInterface.php @@ -0,0 +1,67 @@ +checkNum($value); + + $account = $this->userAccountModel->getAccountByUserId($userId); + + if (!$account) throw new ErrException('请检查账户信息'); + + $insertModel = new AccountDetail(); + + $insertModel->user_id = $userId; + $insertModel->account_type = AccountCode::ACCOUNT_TYPE_POINT; + $insertModel->business_code = $businessCode; + $insertModel->before_num = $account->integral; + $insertModel->change_num = $value; + $insertModel->after_num = $account->integral + (float)$value; + $insertModel->track_user_id = 0; + $insertModel->track_param = json_encode($data); + $insertModel->remark = empty($description) ? AccountCode::getMessage($businessCode) : $description; + + if (!$insertModel->save() || !$this->userAccountModel->incPointByUserId($userId, $value)) throw new ErrException('添加账户记录异常'); + + return true; + } + + public function dec(int $userId, string $value, int $businessCode, array $data, string $description = ''): bool + { + $this->checkNum($value); + + $account = $this->userAccountModel->getAccountByUserId($userId); + + if (!$account) throw new ErrException('请检查账户信息'); + + if ($value > $account->integral) throw new ErrException('账户余额不足'); + + $insertModel = new AccountDetail(); + + $insertModel->user_id = $userId; + $insertModel->account_type = AccountCode::ACCOUNT_TYPE_POINT; + $insertModel->business_code = $businessCode; + $insertModel->before_num = $account->integral; + $insertModel->change_num = '-'.$value; + $insertModel->after_num = $account->integral - (float)$value; + $insertModel->track_user_id = 0; + $insertModel->track_param = json_encode($data); + $insertModel->remark = empty($description) ? AccountCode::getMessage($businessCode) : $description; + + if (!$insertModel->save() || !$this->userAccountModel->decIntegralByUserId($userId, $value)) throw new ErrException('删除账户记录异常'); + + return true; + } +} \ No newline at end of file