feat : finish order

This commit is contained in:
2025-04-02 16:05:24 +08:00
parent 4620a0f1ba
commit ea374d578f
8 changed files with 498 additions and 7 deletions

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Constants\Common;
use Hyperf\Constants\AbstractConstants;
use Hyperf\Constants\Annotation\Constants;
#[Constants]
class AccountCode extends AbstractConstants
{
CONST INT ACCOUNT_TYPE_BALANCE = 1;
CONST INT ACCOUNT_TYPE_POINT = 2;
// 账号业务码 1000 - 4999 为 余额 6000-9999 为积分 0-999/5000-5999为系统保留
/**
* point|积分
*/
/**
* @Message("下单完成后获取积分")
*/
CONST INT GET_POINTS_FOR_COMPLETING_AN_ORDER = 1001;
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace App\Model;
use Hyperf\DbConnection\Model\Model;
/**
* @property int $id
* @property int $user_id
* @property int $account_type
* @property int $business_code
* @property string $before_num
* @property string $change_num
* @property string $after_num
* @property int $track_user_id
* @property string $track_param
* @property string $remark
* @property string $create_time
*/
class AccountDetail extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'account_detail';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
protected array $guarded = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['id' => '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;
}

View File

@@ -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);
}
}

View File

@@ -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
{

View File

@@ -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;
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* This service file is part of item.
*
* @author ctexthuang
* @contact ctexthuang@qq.com
*/
declare(strict_types=1);
namespace App\Service\Common\Account;
use App\Exception\ErrException;
use App\Model\UserAccount;
use Hyperf\Di\Annotation\Inject;
abstract class AccountOperateInterface
{
/**
* @var UserAccount
*/
#[Inject]
protected UserAccount $userAccountModel;
/**
* 检查num是否正确
* @param $num
* @return void
*/
protected function checkNum($num): void
{
if ($num < 0) throw new ErrException('数值错误');
}
/**
* @param int $userId
* @param string $value
* @param int $businessCode
* @param array $data
* @param string $description
* @return bool
*/
abstract public function inc(
int $userId,
string $value,
int $businessCode,
array $data,
string $description = ''
): bool;
/**
* @param int $userId
* @param string $value
* @param int $businessCode
* @param array $data
* @param string $description
* @return bool
*/
abstract public function dec(
int $userId,
string $value,
int $businessCode,
array $data,
string $description = ''
): bool;
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* This service file is part of item.
*
* @author ctexthuang
* @contact ctexthuang@qq.com
*/
declare(strict_types=1);
namespace App\Service\Common\Account;
class BalanceOperateService extends AccountOperateInterface
{
public function inc(int $userId, string $value, int $businessCode, array $data, string $description = '',): bool
{
return true;
}
public function dec(int $userId, string $value, int $businessCode, array $data, string $description = '',): bool
{
return true;
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* This service file is part of item.
*
* @author ctexthuang
* @contact ctexthuang@qq.com
*/
declare(strict_types=1);
namespace App\Service\Common\Account;
use App\Constants\Common\AccountCode;
use App\Exception\ErrException;
use App\Model\AccountDetail;
class PointOperateService extends AccountOperateInterface
{
public function inc(int $userId, string $value, int $businessCode, array $data, string $description = ''): bool
{
$this->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;
}
}