423 lines
11 KiB
PHP
423 lines
11 KiB
PHP
<?php
|
|||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace app\service\api\member;
|
||
|
|
|
||
|
|
use app\dao\member\MemberDao;
|
||
|
|
use app\dao\member\MemberPointsDao;
|
||
|
|
use app\adminapi\event\PointsChangedEvent;
|
||
|
|
use app\enum\member\PointSource;
|
||
|
|
use app\enum\member\PointType;
|
||
|
|
use core\base\BaseService;
|
||
|
|
use support\Container;
|
||
|
|
use support\Log;
|
||
|
|
|
||
|
|
class MemberPointsService extends BaseService
|
||
|
|
{
|
||
|
|
|
||
|
|
public function __construct(
|
||
|
|
private readonly MemberPointsDao $memberPointsDao,
|
||
|
|
MemberDao $dao
|
||
|
|
)
|
||
|
|
{
|
||
|
|
$this->dao = $dao;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 增加会员积分
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
* @param int $points 积分数量
|
||
|
|
* @param string $remark 备注
|
||
|
|
* @param PointSource $source 积分来源
|
||
|
|
* @param int|string|null $relatedId 关联ID
|
||
|
|
*
|
||
|
|
* @return float 新积分余额
|
||
|
|
* @throws \Exception
|
||
|
|
*/
|
||
|
|
public function addPoints(
|
||
|
|
int|string $memberId,
|
||
|
|
int $points,
|
||
|
|
string $remark = '',
|
||
|
|
PointSource $source = PointSource::OTHER,
|
||
|
|
int|string|null $relatedId = null,
|
||
|
|
?array $extra = null
|
||
|
|
): float
|
||
|
|
{
|
||
|
|
$member = $this->dao->find($memberId);
|
||
|
|
if (!$member) {
|
||
|
|
throw new \Exception('会员不存在');
|
||
|
|
}
|
||
|
|
|
||
|
|
$oldPoints = $member->points;
|
||
|
|
$newPoints = $oldPoints + $points;
|
||
|
|
|
||
|
|
$event = new PointsChangedEvent(
|
||
|
|
$memberId,
|
||
|
|
$points,
|
||
|
|
$source,
|
||
|
|
PointType::INCREASE,
|
||
|
|
$oldPoints,
|
||
|
|
$newPoints,
|
||
|
|
$remark,
|
||
|
|
$relatedId,
|
||
|
|
$extra
|
||
|
|
);
|
||
|
|
$event->dispatch();
|
||
|
|
|
||
|
|
return $this->getPointsBalance($memberId);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 减少会员积分
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
* @param int $points 积分数量
|
||
|
|
* @param string $remark 备注
|
||
|
|
* @param PointSource $source 积分来源
|
||
|
|
* @param int|string|null $relatedId 关联ID
|
||
|
|
*
|
||
|
|
* @return float 新积分余额
|
||
|
|
* @throws \Exception
|
||
|
|
*/
|
||
|
|
public function deductPoints(
|
||
|
|
int|string $memberId,
|
||
|
|
int $points,
|
||
|
|
string $remark = '',
|
||
|
|
PointSource $source = PointSource::OTHER,
|
||
|
|
int|string|null $relatedId = null,
|
||
|
|
?array $extra = null
|
||
|
|
): float
|
||
|
|
{
|
||
|
|
$member = $this->dao->find($memberId);
|
||
|
|
if (!$member) {
|
||
|
|
throw new \Exception('会员不存在');
|
||
|
|
}
|
||
|
|
|
||
|
|
$oldPoints = $member->points;
|
||
|
|
if ($oldPoints < $points) {
|
||
|
|
throw new \Exception('积分不足');
|
||
|
|
}
|
||
|
|
$newPoints = $oldPoints - $points;
|
||
|
|
|
||
|
|
$event = new PointsChangedEvent(
|
||
|
|
$memberId,
|
||
|
|
$points,
|
||
|
|
$source,
|
||
|
|
PointType::DECREASE,
|
||
|
|
$oldPoints,
|
||
|
|
$newPoints,
|
||
|
|
$remark,
|
||
|
|
$relatedId,
|
||
|
|
$extra
|
||
|
|
);
|
||
|
|
$event->dispatch();
|
||
|
|
|
||
|
|
return $this->getPointsBalance($memberId);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取会员积分记录
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
* @param int $limit 记录数量
|
||
|
|
*
|
||
|
|
* @return array 积分记录列表
|
||
|
|
*/
|
||
|
|
public function getPointsRecords(int|string $memberId, int $limit = 20): array
|
||
|
|
{
|
||
|
|
return $this->memberPointsDao->getMemberPoints($memberId, $limit);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取会员积分余额
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
*
|
||
|
|
* @return float 积分余额
|
||
|
|
*/
|
||
|
|
public function getPointsBalance(int|string $memberId): float
|
||
|
|
{
|
||
|
|
return $this->memberPointsDao->getMemberBalance($memberId);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 检查会员积分是否足够
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
* @param int $points 需要的积分数量
|
||
|
|
*
|
||
|
|
* @return bool 积分是否足够
|
||
|
|
*/
|
||
|
|
public function isPointsEnough(int|string $memberId, int $points): bool
|
||
|
|
{
|
||
|
|
$balance = $this->getPointsBalance($memberId);
|
||
|
|
return $balance >= $points;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取积分流水记录(分页)
|
||
|
|
*
|
||
|
|
* @param array $params 查询参数
|
||
|
|
*
|
||
|
|
* @return array
|
||
|
|
* @throws \Exception
|
||
|
|
*/
|
||
|
|
public function getPointsRecord(array $params): array
|
||
|
|
{
|
||
|
|
$memberId = $params['member_id'] ?? 0;
|
||
|
|
$page = $params['page'] ?? 1;
|
||
|
|
$limit = $params['limit'] ?? 20;
|
||
|
|
$type = $params['type'] ?? null;
|
||
|
|
|
||
|
|
if (!$memberId) {
|
||
|
|
throw new \Exception('会员ID不能为空');
|
||
|
|
}
|
||
|
|
|
||
|
|
$offset = ($page - 1) * $limit;
|
||
|
|
|
||
|
|
$query = $this->memberPointsDao->query()
|
||
|
|
->where('member_id', '=', $memberId);
|
||
|
|
|
||
|
|
if ($type) {
|
||
|
|
$query->where('type', '=', $type);
|
||
|
|
}
|
||
|
|
|
||
|
|
$total = $query->count();
|
||
|
|
$items = $query->orderBy('created_at', 'desc')
|
||
|
|
->offset($offset)
|
||
|
|
->limit($limit)
|
||
|
|
->get()
|
||
|
|
->toArray();
|
||
|
|
|
||
|
|
return [
|
||
|
|
'items' => $items,
|
||
|
|
'total' => $total,
|
||
|
|
'page' => $page,
|
||
|
|
'limit' => $limit,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取会员积分总额统计
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
*
|
||
|
|
* @return array
|
||
|
|
* @throws \Exception
|
||
|
|
*/
|
||
|
|
public function getPointsTotal(int|string $memberId): array
|
||
|
|
{
|
||
|
|
if (!$memberId) {
|
||
|
|
throw new \Exception('会员ID不能为空');
|
||
|
|
}
|
||
|
|
|
||
|
|
$currentPoints = $this->getPointsBalance($memberId);
|
||
|
|
|
||
|
|
$monthStart = date('Y-m-01 00:00:00');
|
||
|
|
$monthEnd = date('Y-m-t 23:59:59');
|
||
|
|
|
||
|
|
$monthlyPoints = $this->memberPointsDao->query()
|
||
|
|
->where('member_id', '=', $memberId)
|
||
|
|
->where('type', '=', PointType::INCREASE->value)
|
||
|
|
->whereBetween('created_at', [$monthStart, $monthEnd])
|
||
|
|
->sum('points');
|
||
|
|
|
||
|
|
$totalPoints = $this->memberPointsDao->query()
|
||
|
|
->where('member_id', '=', $memberId)
|
||
|
|
->where('type', '=', PointType::INCREASE->value)
|
||
|
|
->sum('points');
|
||
|
|
|
||
|
|
return [
|
||
|
|
'current_points' => (int)$currentPoints,
|
||
|
|
'monthly_points' => (int)($monthlyPoints ?? 0),
|
||
|
|
'total_points' => (int)($totalPoints ?? 0),
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取会员等级信息
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
*
|
||
|
|
* @return array
|
||
|
|
*/
|
||
|
|
public function getMemberLevel(int|string $memberId): array
|
||
|
|
{
|
||
|
|
if (!$memberId) {
|
||
|
|
throw new \Exception('会员ID不能为空');
|
||
|
|
}
|
||
|
|
|
||
|
|
$member = $this->dao->find($memberId);
|
||
|
|
|
||
|
|
if (!$member) {
|
||
|
|
throw new \Exception('会员不存在');
|
||
|
|
}
|
||
|
|
|
||
|
|
return [
|
||
|
|
'level_id' => $member->level_id,
|
||
|
|
'points' => $member->points,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 会员签到获取积分
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
* @param int $continuousDays 连续签到天数
|
||
|
|
*
|
||
|
|
* @return int 获得的积分数量
|
||
|
|
* @throws \Exception
|
||
|
|
*/
|
||
|
|
public function getSignPoints(int|string $memberId, int $continuousDays): int
|
||
|
|
{
|
||
|
|
// 每次签到固定+1积分
|
||
|
|
$points = 1;
|
||
|
|
|
||
|
|
// 触发积分增加事件
|
||
|
|
$this->addPoints(
|
||
|
|
$memberId,
|
||
|
|
$points,
|
||
|
|
'每日签到',
|
||
|
|
PointSource::SIGN_IN
|
||
|
|
);
|
||
|
|
|
||
|
|
return $points;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 会员购物获取积分
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
* @param float $amount 购物金额
|
||
|
|
* @param int|string $orderId 订单ID
|
||
|
|
*
|
||
|
|
* @return int 获得的积分数量
|
||
|
|
* @throws \Exception
|
||
|
|
*/
|
||
|
|
public function getShoppingPoints(int|string $memberId, float $amount, int|string $orderId): int
|
||
|
|
{
|
||
|
|
// 计算购物积分(例如:每消费10元获得1积分)
|
||
|
|
$points = (int)($amount / 10);
|
||
|
|
|
||
|
|
if ($points > 0) {
|
||
|
|
$this->addPoints(
|
||
|
|
$memberId,
|
||
|
|
$points,
|
||
|
|
'购物获得积分',
|
||
|
|
PointSource::SHOPPING,
|
||
|
|
$orderId
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return $points;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 会员邀请获取积分
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
* @param int|string $invitedMemberId 被邀请的会员ID
|
||
|
|
*
|
||
|
|
* @return int 获得的积分数量
|
||
|
|
* @throws \Exception
|
||
|
|
*/
|
||
|
|
public function getInvitePoints(int|string $memberId, int|string $invitedMemberId): int
|
||
|
|
{
|
||
|
|
// 邀请奖励积分
|
||
|
|
$points = 50;
|
||
|
|
|
||
|
|
$this->addPoints(
|
||
|
|
$memberId,
|
||
|
|
$points,
|
||
|
|
'邀请好友注册',
|
||
|
|
PointSource::INVITE,
|
||
|
|
$invitedMemberId
|
||
|
|
);
|
||
|
|
|
||
|
|
return $points;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 会员注册获取积分
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
*
|
||
|
|
* @return int 获得的积分数量
|
||
|
|
* @throws \Exception
|
||
|
|
*/
|
||
|
|
public function getRegisterPoints(int|string $memberId): int
|
||
|
|
{
|
||
|
|
// 注册奖励积分
|
||
|
|
$points = 100;
|
||
|
|
|
||
|
|
$this->addPoints(
|
||
|
|
$memberId,
|
||
|
|
$points,
|
||
|
|
'新会员注册奖励',
|
||
|
|
PointSource::REGISTER
|
||
|
|
);
|
||
|
|
|
||
|
|
return $points;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 会员兑换商品扣减积分
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
* @param int $points 兑换需要的积分
|
||
|
|
* @param int|string $productId 兑换商品ID
|
||
|
|
*
|
||
|
|
* @return float 扣减后的积分余额
|
||
|
|
* @throws \Exception
|
||
|
|
*/
|
||
|
|
public function deductExchangePoints(int|string $memberId, int $points, int|string $productId): float
|
||
|
|
{
|
||
|
|
return $this->deductPoints(
|
||
|
|
$memberId,
|
||
|
|
$points,
|
||
|
|
'兑换商品',
|
||
|
|
PointSource::EXCHANGE,
|
||
|
|
$productId
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 积分过期扣减
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
* @param int $points 过期的积分数量
|
||
|
|
*
|
||
|
|
* @return float 扣减后的积分余额
|
||
|
|
* @throws \Exception
|
||
|
|
*/
|
||
|
|
public function deductExpiredPoints(int|string $memberId, int $points): float
|
||
|
|
{
|
||
|
|
return $this->deductPoints(
|
||
|
|
$memberId,
|
||
|
|
$points,
|
||
|
|
'积分过期',
|
||
|
|
PointSource::EXPIRED
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 违规处罚扣减积分
|
||
|
|
*
|
||
|
|
* @param int|string $memberId 会员ID
|
||
|
|
* @param int $points 处罚的积分数量
|
||
|
|
* @param string $reason 处罚原因
|
||
|
|
*
|
||
|
|
* @return float 扣减后的积分余额
|
||
|
|
* @throws \Exception
|
||
|
|
*/
|
||
|
|
public function deductPenaltyPoints(int|string $memberId, int $points, string $reason): float
|
||
|
|
{
|
||
|
|
return $this->deductPoints(
|
||
|
|
$memberId,
|
||
|
|
$points,
|
||
|
|
'违规处罚: ' . $reason,
|
||
|
|
PointSource::PENALTY
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|