Files
huangjingfen/pro_v3.5.1/app/services/hjf/PointsRewardServices.php
mac d0cd7e4667 fix(fsgx): 伞下人数与待释放积分发放链路
- spread/people 增加 umbrellaCount、umbrellaOrderCount;MemberLevelServices 递归统计伞下人数
- 佣金记录页使用新字段展示团队业绩
- 报单积分改由 HjfOrderPayJob 在等级升级后发放,避免 grade=0 时序问题
- PointsRewardServices::reward 按 points_release_log 幂等防重复
- 移除 backOrderBrokerage 内同步 grantFrozenPointsByBrokerage

Made-with: Cursor
2026-03-24 23:27:55 +08:00

166 lines
5.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace app\services\hjf;
use app\dao\hjf\PointsReleaseLogDao;
use app\dao\user\UserDao;
use app\services\agent\AgentLevelServices;
use app\services\BaseServices;
use app\services\user\UserBillServices;
use think\annotation\Inject;
use think\facade\Db;
use think\facade\Log;
/**
* 积分奖励服务(级差计算)—— 改造复用版
*
* 改造要点PRD 3.2.2
* - 使用 eb_user.agent_level (FK → eb_agent_level.id) 获取会员等级
* - 从 eb_agent_level 表的 direct_reward_points / umbrella_reward_points 字段读取奖励积分
* - 不再使用独立的 member_level 字段和系统配置表中的 hjf_reward_* 键
*
* Class PointsRewardServices
* @package app\services\hjf
*/
class PointsRewardServices extends BaseServices
{
#[Inject]
protected PointsReleaseLogDao $logDao;
#[Inject]
protected UserDao $userDao;
#[Inject]
protected AgentLevelServices $agentLevelServices;
#[Inject]
protected UserBillServices $userBillServices;
/**
* 对一笔报单订单发放积分奖励
*
* @param int $orderDbId 订单表主键 id用于 user_bill.link_id 关联后台订单
*/
public function reward(int $orderUid, string $orderId, int $orderDbId = 0): void
{
try {
// 幂等检查:若该订单已有积分奖励记录则跳过,防止重复发放
$exists = Db::name('points_release_log')
->where('order_id', $orderId)
->whereIn('type', ['reward_direct', 'reward_umbrella'])
->count();
if ($exists > 0) {
Log::info("[PointsReward] 订单 {$orderId} 已有积分奖励记录,跳过重复发放");
return;
}
$buyer = $this->userDao->get($orderUid);
if (!$buyer || !$buyer['spread_uid']) {
return;
}
$this->propagateReward($buyer['spread_uid'], $orderUid, $orderId, 0, 0, $orderDbId);
} catch (\Throwable $e) {
Log::error("[PointsReward] 积分奖励失败 orderUid={$orderUid} orderId={$orderId}: " . $e->getMessage());
}
}
/**
* 向上递归发放级差积分
*
* @param int $uid 当前被奖励用户
* @param int $fromUid 触发方(下级)用户 ID
* @param string $orderId 来源订单号
* @param int $lowerReward 下级已获得的直推/伞下奖励积分(用于级差扣减)
* @param int $depth 递归深度
*/
private function propagateReward(
int $uid,
int $fromUid,
string $orderId,
int $lowerReward,
int $depth = 0,
int $orderDbId = 0
): void {
if ($depth >= 10 || $uid <= 0) {
return;
}
$user = $this->userDao->get($uid);
if (!$user) {
return;
}
$agentLevelId = (int)($user['agent_level'] ?? 0);
$grade = $this->agentLevelServices->getGradeByLevelId($agentLevelId);
if ($grade === 0) {
if ($user['spread_uid']) {
$this->propagateReward((int)$user['spread_uid'], $uid, $orderId, 0, $depth + 1, $orderDbId);
}
return;
}
$isDirect = ($depth === 0);
$reward = $isDirect
? $this->agentLevelServices->getDirectRewardPoints($agentLevelId)
: $this->agentLevelServices->getUmbrellaRewardPoints($agentLevelId);
$actual = max(0, $reward - $lowerReward);
if ($actual > 0) {
$this->grantFrozenPoints(
$uid,
$actual,
$orderId,
$isDirect ? 'reward_direct' : 'reward_umbrella',
($isDirect ? '直推奖励' : '伞下奖励(级差)') . " - 来源订单 {$orderId}",
$orderDbId
);
}
if ($user['spread_uid']) {
$this->propagateReward(
(int)$user['spread_uid'],
$uid,
$orderId,
$reward,
$depth + 1,
$orderDbId
);
}
}
/**
* 写入待释放积分frozen_points并记录明细
*/
private function grantFrozenPoints(
int $uid,
int $points,
string $orderId,
string $type,
string $mark,
int $orderDbId = 0
): void {
Db::transaction(function () use ($uid, $points, $orderId, $type, $mark, $orderDbId) {
$this->userDao->bcInc($uid, 'frozen_points', $points, 'uid');
$this->logDao->save([
'uid' => $uid,
'points' => $points,
'pm' => 1,
'type' => $type,
'title' => ($type === 'reward_direct') ? '直推奖励' : '伞下奖励',
'mark' => $mark,
'status' => 'frozen',
'order_id' => $orderId,
]);
// PRD §3.3:营销后台积分日志读 eb_user_bill双写待释放积分明细不增加可消费 integral
$integralBalance = (int)($this->userDao->value(['uid' => $uid], 'integral') ?: 0);
$billType = ($type === 'reward_direct') ? 'hjf_reward_direct_integral' : 'hjf_reward_umbrella_integral';
$this->userBillServices->income($billType, $uid, $points, $integralBalance, $orderDbId, 0, $mark);
});
}
}