feat(fsgx): HJF queue merge, brokerage timing, cycle commission, points release
- Add HJF jobs, services, DAOs, models, admin/API controllers, release command - Respect brokerage_timing (on_pay vs confirm); dispatch HjfOrderPayJob for queue goods - Queue-only cycle commission and position index fix in StoreOrderCreateServices - UserBill income types: frozen_points_brokerage, frozen_points_release - Timer: fsgx_release_frozen_points -> PointsReleaseServices - Agent tasks: no_assess filtering for direct/umbrella counts - Migrations: queue_pool, points_release_log, fsgx_v1 checklist updates - Admin/uniapp: crontab preset, membership level, user list, finance routes, docs Made-with: Cursor
This commit is contained in:
89
pro_v3.5.1/app/services/hjf/HjfAssetsServices.php
Normal file
89
pro_v3.5.1/app/services/hjf/HjfAssetsServices.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\services\hjf;
|
||||
|
||||
use app\dao\user\UserBillDao;
|
||||
use app\dao\user\UserDao;
|
||||
use app\services\BaseServices;
|
||||
use think\annotation\Inject;
|
||||
|
||||
/**
|
||||
* HJF 资产服务
|
||||
*
|
||||
* 提供用户资产总览和现金流水查询功能。
|
||||
* 复用 CRMEB 原有 eb_user(余额/积分)和 eb_user_bill(流水)字段。
|
||||
*
|
||||
* Class HjfAssetsServices
|
||||
* @package app\services\hjf
|
||||
*/
|
||||
class HjfAssetsServices extends BaseServices
|
||||
{
|
||||
#[Inject]
|
||||
protected UserDao $userDao;
|
||||
|
||||
#[Inject]
|
||||
protected UserBillDao $billDao;
|
||||
|
||||
/**
|
||||
* 获取用户资产总览
|
||||
*
|
||||
* 返回:
|
||||
* - now_money 现金余额
|
||||
* - frozen_points 待释放积分
|
||||
* - available_points 已释放积分(可消费)
|
||||
* - total_points 总积分(frozen + available)
|
||||
*
|
||||
* @param int $uid
|
||||
* @return array
|
||||
*/
|
||||
public function getOverview(int $uid): array
|
||||
{
|
||||
$user = $this->userDao->get($uid, 'uid,now_money,frozen_points,available_points');
|
||||
if (!$user) {
|
||||
return [
|
||||
'now_money' => '0.00',
|
||||
'frozen_points' => 0,
|
||||
'available_points' => 0,
|
||||
'total_points' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$frozen = (int)($user['frozen_points'] ?? 0);
|
||||
$available = (int)($user['available_points'] ?? 0);
|
||||
|
||||
return [
|
||||
'now_money' => number_format((float)($user['now_money'] ?? 0), 2, '.', ''),
|
||||
'frozen_points' => $frozen,
|
||||
'available_points' => $available,
|
||||
'total_points' => $frozen + $available,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取现金流水(分页)
|
||||
*
|
||||
* 复用 eb_user_bill 表,筛选 category='now_money' 的记录。
|
||||
*
|
||||
* @param int $uid
|
||||
* @param string $type 流水类型筛选('' = 全部,'queue_refund' 公排退款,'withdraw' 提现等)
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function getCashDetail(int $uid, string $type, int $page, int $limit): array
|
||||
{
|
||||
$where = [
|
||||
'uid' => $uid,
|
||||
'category' => 'now_money',
|
||||
];
|
||||
if ($type !== '') {
|
||||
$where['type'] = $type;
|
||||
}
|
||||
|
||||
$count = $this->billDao->count($where);
|
||||
$list = $this->billDao->getBalanceRecord($where, $page, $limit);
|
||||
|
||||
return compact('list', 'count');
|
||||
}
|
||||
}
|
||||
136
pro_v3.5.1/app/services/hjf/MemberLevelServices.php
Normal file
136
pro_v3.5.1/app/services/hjf/MemberLevelServices.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\services\hjf;
|
||||
|
||||
use app\services\agent\AgentLevelServices;
|
||||
use app\services\agent\AgentLevelTaskServices;
|
||||
use app\services\BaseServices;
|
||||
use app\services\user\UserServices;
|
||||
use think\annotation\Inject;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 会员等级升级服务(改造复用版)
|
||||
*
|
||||
* 基于 CRMEB Pro 的团队分销等级功能进行改造:
|
||||
* - 使用 eb_user.agent_level (FK → eb_agent_level.id) 代替独立的 member_level
|
||||
* - 升级条件通过 eb_agent_level_task 的 type 6/7/8 定义
|
||||
* - 升级逻辑委托给 AgentLevelServices::checkUserLevelFinish()
|
||||
*
|
||||
* 本服务保留为薄封装层,提供 HJF 特有的查询方法供控制器调用。
|
||||
*
|
||||
* Class MemberLevelServices
|
||||
* @package app\services\hjf
|
||||
*/
|
||||
class MemberLevelServices extends BaseServices
|
||||
{
|
||||
#[Inject]
|
||||
protected AgentLevelServices $agentLevelServices;
|
||||
|
||||
#[Inject]
|
||||
protected AgentLevelTaskServices $agentLevelTaskServices;
|
||||
|
||||
/**
|
||||
* 检查并执行升级(异步触发入口)
|
||||
*
|
||||
* 委托给 CRMEB 的 AgentLevelServices 复用原有升级检测流程,
|
||||
* 该流程已支持 type 6/7/8 的 HJF 任务类型。
|
||||
*/
|
||||
public function checkUpgrade(int $uid): void
|
||||
{
|
||||
try {
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$userInfo = $userServices->getUserCacheInfo($uid);
|
||||
if (!$userInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
$spreadUid = $userServices->getSpreadUid($uid, $userInfo);
|
||||
$twoSpreadUid = 0;
|
||||
if ($spreadUid > 0 && $oneUserInfo = $userServices->getUserCacheInfo($spreadUid)) {
|
||||
$twoSpreadUid = $userServices->getSpreadUid($spreadUid, $oneUserInfo, false);
|
||||
}
|
||||
$uids = array_unique([$uid, $spreadUid, $twoSpreadUid]);
|
||||
|
||||
$this->agentLevelServices->checkUserLevelFinish($uid, $uids);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error("[MemberLevel] checkUpgrade uid={$uid}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户当前会员等级 grade(0=普通, 1=创客, 2=云店, 3=服务商, 4=分公司)
|
||||
*/
|
||||
public function getUserGrade(int $uid): int
|
||||
{
|
||||
$agentLevel = (int)Db::name('user')->where('uid', $uid)->value('agent_level');
|
||||
return $this->agentLevelServices->getGradeByLevelId($agentLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户当前等级名称
|
||||
*/
|
||||
public function getUserLevelName(int $uid): string
|
||||
{
|
||||
$agentLevel = (int)Db::name('user')->where('uid', $uid)->value('agent_level');
|
||||
if ($agentLevel <= 0) {
|
||||
return '普通会员';
|
||||
}
|
||||
$maps = $this->agentLevelServices->loadHjfUserListLevelMaps();
|
||||
$info = $this->agentLevelServices->pickHjfLevelRowForUserListDisplay($agentLevel, $maps);
|
||||
|
||||
return $info['name'] ?? '普通会员';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取直推用户的报单订单数
|
||||
*/
|
||||
public function getDirectQueueOrderCount(int $uid): int
|
||||
{
|
||||
return $this->agentLevelTaskServices->getDirectQueueOrderCount($uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取直推人数
|
||||
*/
|
||||
public function getDirectSpreadCount(int $uid): int
|
||||
{
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
return (int)$userServices->count(['spread_uid' => $uid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取伞下总报单订单数(含业绩分离逻辑)
|
||||
*/
|
||||
public function getUmbrellaQueueOrderCount(int $uid): int
|
||||
{
|
||||
return $this->agentLevelTaskServices->getUmbrellaQueueOrderCount($uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动设置会员等级(管理后台使用)
|
||||
*
|
||||
* @param int $uid 用户 ID
|
||||
* @param int $grade 目标等级 grade (0-4)
|
||||
*/
|
||||
public function setUserLevel(int $uid, int $grade): void
|
||||
{
|
||||
$agentLevelId = 0;
|
||||
if ($grade > 0) {
|
||||
$agentLevelId = $this->agentLevelServices->getLevelIdByGrade($grade);
|
||||
if ($agentLevelId <= 0) {
|
||||
throw new \think\exception\ValidateException("等级 grade={$grade} 在 eb_agent_level 中不存在");
|
||||
}
|
||||
}
|
||||
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$userServices->update($uid, ['agent_level' => $agentLevelId]);
|
||||
|
||||
Log::info("[MemberLevel] 手动设置 uid={$uid} agent_level={$agentLevelId} (grade={$grade})");
|
||||
}
|
||||
}
|
||||
112
pro_v3.5.1/app/services/hjf/PointsReleaseServices.php
Normal file
112
pro_v3.5.1/app/services/hjf/PointsReleaseServices.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\services\hjf;
|
||||
|
||||
use app\dao\hjf\PointsReleaseLogDao;
|
||||
use app\dao\user\UserDao;
|
||||
use app\services\BaseServices;
|
||||
use crmeb\services\SystemConfigService;
|
||||
use think\annotation\Inject;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 积分每日释放服务
|
||||
*
|
||||
* 由定时任务(每天凌晨00:01)或 Command 触发。
|
||||
* 计算公式:release_amount = FLOOR(frozen_points × rate / 1000)
|
||||
* 其中 rate = hjf_release_rate(默认 4,即 4‰)
|
||||
*
|
||||
* Class PointsReleaseServices
|
||||
* @package app\services\hjf
|
||||
*/
|
||||
class PointsReleaseServices extends BaseServices
|
||||
{
|
||||
#[Inject]
|
||||
protected PointsReleaseLogDao $logDao;
|
||||
|
||||
#[Inject]
|
||||
protected UserDao $userDao;
|
||||
|
||||
/**
|
||||
* 执行今日积分释放(批量)
|
||||
*
|
||||
* @return array 统计:['processed' => int, 'total_released' => int]
|
||||
*/
|
||||
public function executeRelease(): array
|
||||
{
|
||||
$rate = (int)SystemConfigService::get('hjf_release_rate', 4);
|
||||
$releaseDate = date('Y-m-d');
|
||||
$processed = 0;
|
||||
$totalReleased = 0;
|
||||
|
||||
// 分批处理,每批 200 条,避免内存溢合
|
||||
$page = 1;
|
||||
$limit = 200;
|
||||
|
||||
do {
|
||||
$users = $this->userDao->selectList(
|
||||
['frozen_points' => ['>', 0]],
|
||||
'uid,frozen_points,available_points',
|
||||
$page,
|
||||
$limit,
|
||||
'uid',
|
||||
'asc'
|
||||
);
|
||||
|
||||
if (empty($users)) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($users as $user) {
|
||||
$frozenBefore = (int)$user['frozen_points'];
|
||||
// 使用 bcmath 确保精度
|
||||
$releaseAmount = (int)bcdiv(bcmul((string)$frozenBefore, (string)$rate), '1000');
|
||||
|
||||
if ($releaseAmount <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$frozenAfter = $frozenBefore - $releaseAmount;
|
||||
|
||||
try {
|
||||
Db::transaction(function () use ($user, $releaseAmount, $frozenBefore, $frozenAfter, $releaseDate) {
|
||||
// 更新用户积分字段
|
||||
$this->userDao->update($user['uid'], [
|
||||
'frozen_points' => $frozenAfter,
|
||||
'available_points' => Db::raw('available_points + ' . $releaseAmount),
|
||||
], 'uid');
|
||||
|
||||
// 写 points_release_log(本次每日释放记录)
|
||||
$this->logDao->save([
|
||||
'uid' => $user['uid'],
|
||||
'points' => $releaseAmount,
|
||||
'pm' => 1,
|
||||
'type' => 'release',
|
||||
'title' => '每日释放',
|
||||
'mark' => "积分每日自动解冻,释放日期 {$releaseDate}",
|
||||
'status' => 'released',
|
||||
'release_date' => $releaseDate,
|
||||
]);
|
||||
});
|
||||
|
||||
$totalReleased += $releaseAmount;
|
||||
$processed++;
|
||||
} catch (\Throwable $e) {
|
||||
Log::error("[PointsRelease] uid={$user['uid']} 释放失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$page++;
|
||||
} while (count($users) === $limit);
|
||||
|
||||
Log::info("[PointsRelease] 完成,processed={$processed} total_released={$totalReleased}");
|
||||
|
||||
return [
|
||||
'processed' => $processed,
|
||||
'total_released' => $totalReleased,
|
||||
'release_date' => $releaseDate,
|
||||
];
|
||||
}
|
||||
}
|
||||
135
pro_v3.5.1/app/services/hjf/PointsRewardServices.php
Normal file
135
pro_v3.5.1/app/services/hjf/PointsRewardServices.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?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 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;
|
||||
|
||||
/**
|
||||
* 对一笔报单订单发放积分奖励
|
||||
*/
|
||||
public function reward(int $orderUid, string $orderId): void
|
||||
{
|
||||
try {
|
||||
$buyer = $this->userDao->get($orderUid);
|
||||
if (!$buyer || !$buyer['spread_uid']) {
|
||||
return;
|
||||
}
|
||||
$this->propagateReward($buyer['spread_uid'], $orderUid, $orderId, 0);
|
||||
} 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
|
||||
): 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);
|
||||
}
|
||||
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}"
|
||||
);
|
||||
}
|
||||
|
||||
if ($user['spread_uid']) {
|
||||
$this->propagateReward(
|
||||
(int)$user['spread_uid'],
|
||||
$uid,
|
||||
$orderId,
|
||||
$reward,
|
||||
$depth + 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入待释放积分(frozen_points)并记录明细
|
||||
*/
|
||||
private function grantFrozenPoints(int $uid, int $points, string $orderId, string $type, string $mark): void
|
||||
{
|
||||
Db::transaction(function () use ($uid, $points, $orderId, $type, $mark) {
|
||||
$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,
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
193
pro_v3.5.1/app/services/hjf/QueuePoolServices.php
Normal file
193
pro_v3.5.1/app/services/hjf/QueuePoolServices.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\services\hjf;
|
||||
|
||||
use app\dao\hjf\QueuePoolDao;
|
||||
use app\jobs\hjf\QueueRefundJob;
|
||||
use app\services\BaseServices;
|
||||
use app\services\user\UserServices;
|
||||
use crmeb\services\CacheService;
|
||||
use crmeb\services\SystemConfigService;
|
||||
use think\annotation\Inject;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 公排池服务
|
||||
*
|
||||
* 负责:入队(enqueue)+ 退款触发条件判断 + 统计信息查询。
|
||||
* 退款的实际执行委托给 QueueRefundJob(异步)以避免支付回调阻塞。
|
||||
*
|
||||
* Class QueuePoolServices
|
||||
* @package app\services\hjf
|
||||
* @mixin QueuePoolDao
|
||||
*/
|
||||
class QueuePoolServices extends BaseServices
|
||||
{
|
||||
#[Inject]
|
||||
protected QueuePoolDao $dao;
|
||||
|
||||
/** Redis 分布式锁 Key */
|
||||
const LOCK_KEY = 'hjf:queue:enqueue_lock';
|
||||
|
||||
/** 锁超时(秒) */
|
||||
const LOCK_TTL = 10;
|
||||
|
||||
/**
|
||||
* 报单商品订单入队
|
||||
*
|
||||
* 使用 Redis SET NX EX 分布式锁保证同一时刻只有一个入队+触发检测操作执行。
|
||||
*
|
||||
* @param int $uid 用户 ID
|
||||
* @param string $orderId 原始订单号
|
||||
* @param float $amount 金额(默认 3600.00)
|
||||
* @return array 新入队记录数组
|
||||
* @throws ValidateException
|
||||
*/
|
||||
public function enqueue(int $uid, string $orderId, float $amount = 3600.00): array
|
||||
{
|
||||
$lockKey = self::LOCK_KEY;
|
||||
$lockValue = uniqid('', true);
|
||||
|
||||
// 获取 Redis 实例
|
||||
/** @var \Redis $redis */
|
||||
$redis = CacheService::getRedis();
|
||||
|
||||
// SET NX EX 原子锁
|
||||
$acquired = $redis->set($lockKey, $lockValue, ['NX', 'EX' => self::LOCK_TTL]);
|
||||
if (!$acquired) {
|
||||
throw new ValidateException('公排入队繁忙,请稍后重试');
|
||||
}
|
||||
|
||||
try {
|
||||
return Db::transaction(function () use ($uid, $orderId, $amount, $redis, $lockKey, $lockValue) {
|
||||
$queueNo = $this->dao->nextQueueNo();
|
||||
|
||||
$record = $this->dao->save([
|
||||
'uid' => $uid,
|
||||
'order_id' => $orderId,
|
||||
'amount' => $amount,
|
||||
'queue_no' => $queueNo,
|
||||
'status' => 0,
|
||||
'refund_time' => 0,
|
||||
'trigger_batch' => 0,
|
||||
]);
|
||||
|
||||
$data = $record->toArray();
|
||||
|
||||
// 检查是否触发退款条件
|
||||
$this->checkAndTriggerRefund();
|
||||
|
||||
return $data;
|
||||
});
|
||||
} finally {
|
||||
// 释放锁(Lua 原子删除,防止误删他人的锁)
|
||||
$script = <<<'LUA'
|
||||
if redis.call("GET", KEYS[1]) == ARGV[1] then
|
||||
return redis.call("DEL", KEYS[1])
|
||||
else
|
||||
return 0
|
||||
end
|
||||
LUA;
|
||||
$redis->eval($script, [$lockKey, $lockValue], 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否达到退款触发条件,若是则派发异步退款 Job
|
||||
*
|
||||
* 触发条件:当前排队中总单数 ≥ triggerMultiple(默认4),
|
||||
* 即每进入4单就对最早的1单触发退款。
|
||||
*/
|
||||
public function checkAndTriggerRefund(): void
|
||||
{
|
||||
$multiple = (int)SystemConfigService::get('hjf_trigger_multiple', 4);
|
||||
$pending = $this->dao->countPending();
|
||||
|
||||
if ($pending < $multiple) {
|
||||
return;
|
||||
}
|
||||
|
||||
$earliest = $this->dao->getEarliestPending();
|
||||
if (!$earliest) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 批次号 = 历史已退款总数 + 1
|
||||
$batchNo = $this->dao->count(['status' => 1]) + 1;
|
||||
|
||||
// 派发异步退款 Job
|
||||
QueueRefundJob::dispatch($earliest['id'], $earliest['uid'], $earliest['amount'], $batchNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的公排状态摘要(用于状态页)
|
||||
*/
|
||||
public function getUserStatus(int $uid): array
|
||||
{
|
||||
$multiple = (int)SystemConfigService::get('hjf_trigger_multiple', 4);
|
||||
$pending = $this->dao->countPending();
|
||||
$total = $this->dao->countTotal();
|
||||
|
||||
// 当前批次已入队单数(本批次进度)
|
||||
$batchCount = $pending % $multiple;
|
||||
|
||||
// 用户自己的订单
|
||||
$myOrders = $this->dao->getModel()
|
||||
->where('uid', $uid)
|
||||
->order('add_time', 'desc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
foreach ($myOrders as &$item) {
|
||||
$item['estimated_wait'] = $item['status'] === 1
|
||||
? '已退款'
|
||||
: $this->estimateWait((int)$item['queue_no'], $pending, $multiple);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return [
|
||||
'total_orders' => $total,
|
||||
'my_orders' => $myOrders,
|
||||
'progress' => [
|
||||
'current_batch_count' => $batchCount,
|
||||
'trigger_multiple' => $multiple,
|
||||
'next_refund_queue_no' => $this->dao->getEarliestPending()['queue_no'] ?? 0,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户公排历史(分页,支持按状态筛选)
|
||||
*/
|
||||
public function getUserHistory(int $uid, int $status, int $page, int $limit): array
|
||||
{
|
||||
$result = $this->dao->getUserList($uid, $status, $page, $limit);
|
||||
|
||||
foreach ($result['list'] as &$item) {
|
||||
$item['time_key'] = date('Y-m-d', (int)$item['add_time']);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单估算等待时间(基于队列位置)
|
||||
*/
|
||||
private function estimateWait(int $queueNo, int $pending, int $multiple): string
|
||||
{
|
||||
$earliest = $this->dao->getEarliestPending();
|
||||
if (!$earliest) {
|
||||
return '--';
|
||||
}
|
||||
$positionFromFront = $queueNo - (int)$earliest['queue_no'];
|
||||
if ($positionFromFront <= 0) {
|
||||
return '即将退款';
|
||||
}
|
||||
$waitCycles = (int)ceil($positionFromFront / $multiple);
|
||||
return "约等待 {$waitCycles} 轮";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user