Files
huangjingfen/pro_v3.5.1/app/services/system/timer/SystemTimerServices.php
panchengyong ac86ec57cf fix(fsgx): 积分释放定时任务、佣金轮巡与 UniApp 体验
- PointsReleaseServices: 使用 Db::table 查询与更新,构造函数注入 UserDao;日志与账单独立 try/catch\n- SystemTimer: implement_timer 捕获后重新抛出异常,便于 run_now 返回错误\n- SystemTimerServices / 控制器: runNow 返回任务结果并在 API 中带回 result\n- StoreOrderCreateServices: 报单佣金位次修正与多件轮巡\n- UniApp: 佣金记录跳转 type=2、余额提现免手续费展示、状态页与资产页头部渐变与提现页一致\n- docs: 增加 fsgx-issues-0328-1 问题跟踪

Made-with: Cursor
2026-03-29 10:36:52 +08:00

387 lines
15 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
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\timer;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use app\dao\system\timer\SystemTimerDao;
use app\listener\system\timer\SystemTimer as SystemTimerListener;
use think\annotation\Inject;
/**
* 定时器service
* Class SystemTimerServices
* @package app\services\system\timer
* @mixin SystemTimerDao
*/
class SystemTimerServices extends BaseServices
{
/**
* @var array|string[]
*/
protected array $taskName = [
'auto_cancel' => '自动取消订单',
'auto_take' => '自动确认收货',
'auto_comment' => '自动好评',
'auto_clear_integral' => '自动清空用户积分',
'auto_off_user_svip' => '自动取消用户到期svip',
'auto_agent' => '自动解绑上下级',
'auto_clear_poster' => '自动清除昨日海报',
'auto_sms_code' => '更新短信状态',
'auto_live' => '自动更新直播产品状态和直播间状态',
'auto_pink' => '拼团状态自动更新',
'auto_show' => '自动上下架商品',
'auto_channel' => '渠道码定时任务',
'auto_moment' => '定时创建发送朋友圈任务',
'auto_group_task' => '定时发送群发任务',
'auto_seckill' => '定时清理秒杀数据过期的数据缓存',
'rebate_points_orders' => '未支付积分订单退积分',
'reminder_unverified_remind' => '次卡商品未核销短信提醒',
'sign_remind_time' => '用户签到提醒',
'auto_presale_product' => '预售商品到期处理数据',
'auto_card_code' => '清理到期礼品卡',
'holiday_gift_push_task'=>'节日有礼赠送礼品',
'fsgx_release_frozen_points' => 'fsgx每日积分释放',
];
/**
* @var SystemTimerDao
*/
#[Inject]
protected SystemTimerDao $dao;
/**
* 获取定时任务名称
* @return array|string[]
*/
public function getTasKName()
{
return $this->taskName;
}
/**
* 获取定时器列表
* @param array $where
* @return array
*/
public function getTimerList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, $page, $limit);
foreach ($list as $key => &$item) {
$item['last_execution_time'] = $item['last_execution_time'] > 0 ? date('Y-m-d H:i:s', $item['last_execution_time']) : '';
switch ($item['type']) {
case 1:
$item['execution_cycle'] = '每隔' . $item['cycle'] . '分钟执行';
break;
case 2:
$arr = explode('/', $item['cycle']);
$item['execution_cycle'] = '每' . $arr[0] . '小时, 第' . $arr[1] . '分钟 执行';
break;
case 3:
$item['execution_cycle'] = '每小时, 第' . $item['cycle'] . '分钟执行';
break;
case 4:
$arr = explode('/', $item['cycle']);
$item['execution_cycle'] = '每天, ' . $arr[0] . '点' . $arr[1] . '分 执行';
break;
case 5:
$arr = explode('/', $item['cycle']);
$item['execution_cycle'] = '每隔' . $arr[0] . '天, ' . $arr[1] . '点' . $arr[2] . '分 执行';
break;
case 6:
$arr = explode('/', $item['cycle']);
$item['execution_cycle'] = '每周' . $arr[0] . ', ' . $arr[1] . '点' . $arr[2] . '分执行';
break;
case 7:
$arr = explode('/', $item['cycle']);
$item['execution_cycle'] = '每月, ' . $arr[0] . '日 ' . $arr[1] . '点' . $arr[2] . '分执行';
break;
case 8:
$arr = explode('/', $item['cycle']);
$item['execution_cycle'] = '每年' . $arr[0] . '月' . $arr[1] . '日 ' . $arr[2] . '点' . $arr[3] . '分执行';
break;
default:
$item['execution_cycle'] = '周期有误!';
break;
}
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**设置或更新定时任务缓存
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function setAllTimerCache()
{
$where['is_del'] = 0;
$list = $this->dao->getList($where, 0, 0);
foreach ($list as $key => &$value) {
if (!$value['update_execution_time']) {
$update_time = time();
$value['update_execution_time'] = $update_time;
$this->dao->update($value['id'], ['update_execution_time' => $update_time]);
}
}
$this->dao->cacheCreate($list);
}
/**
* 设置定时器状态
* @param $id
* @param $is_show
*/
public function setShow(int $id, int $is_show)
{
$this->dao->update($id, ['is_open' => $is_show]);
}
/**
* 删除定时器
* @param string $id
*/
public function del(int $id)
{
if (!$id) throw new AdminException('请选择要删除的定时任务');
$this->dao->delete((int)$id);
}
/**
* 新增数据
* @param $data
*/
public function createData($data)
{
if ($this->dao->be(['mark' => $data['mark'], 'name' => $data['name']])) {
throw new AdminException('该定时任务已经存在');
}
$data['add_time'] = time();
$data['update_execution_time'] = time();
$res = $this->dao->save($data);
if (!$res) throw new AdminException('添加失败');
return $res;
}
/**
* 保存修改数据
* @param $id
* @param $data
*/
public function editData($id, $data)
{
if (!$this->dao->be(['id' => $id])) {
throw new AdminException('该定时任务不存在');
}
$data['update_execution_time'] = time();
$res = $this->dao->update($id, $data);
if (!$res) throw new AdminException('修改失败');
return $res;
}
/**更新缓存
* @param $id
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function updateOneTimerCache($id)
{
if (!$id) throw new AdminException('参数错误');
$timer = $this->dao->get($id);
$this->dao->cacheUpdate($timer->toArray());
return true;
}
/**删除缓存
* @param $id
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function delOneTimerCache($id)
{
if (!$id) throw new AdminException('参数错误');
$this->dao->cacheDelById($id);
return true;
}
/**获取单条定时器数据
* @param $id
* @return void
*/
public function getOneTimer($id)
{
if (!$id) throw new AdminException('请选择要删除的定时任务');
$timer = $this->dao->get($id);
if (!$timer) throw new AdminException('该定时任务不存在');
return $timer->toArray();
}
/**
* 手动立即执行指定定时任务HTTP 请求上下文,绕过 Swoole Cron 构造,用反射直接调用 implement_timer
* @param string $mark
* @return mixed 任务返回值(如 executeRelease 返回的处理统计数组),可为 null
*/
public function runNow(string $mark): mixed
{
$this->update(['mark' => $mark], ['last_execution_time' => time()]);
try {
$ref = new \ReflectionClass(SystemTimerListener::class);
$instance = $ref->newInstanceWithoutConstructor();
return $instance->implement_timer($mark);
} catch (\Throwable $e) {
$taskName = $this->taskName[$mark] ?? $mark;
throw new \RuntimeException("定时任务[{$taskName}]执行失败: " . $e->getMessage(), 0, $e);
}
}
/**获取下次执行时间
* @param $type
* @param $cycle
* @param $time
* @param $start_time
* @return array
*/
public function getTimerCycleTime($type, $cycle, $time, $start_time = 0)
{
if (!$time) $time = time();
switch ($type) {
case 1: // N分钟
$cycle_time = 60 * $cycle;
$i = $time - $start_time;
$more = $i % $cycle_time;
if ($more == 0) {
$cycle_time = $time;
} else {
$cycle_time = $time - $more + $cycle_time;
}
break;
case 2: //N小时
$arr = explode('/', $cycle);
$todaystart = strtotime(date('Y-m-d H' . ':00:00', $time));
$start_time = strtotime(date('Y-m-d H' . ':00:00', $start_time));
if ($arr[0] <= 0) {
$cycle_time = 60 * $arr[1];
$i = $time - $start_time;
$more = $i % $cycle_time;
if ($more == 0) {
$cycle_time = $time;
} else {
$cycle_time = $time - $more + $cycle_time;
}
} else {
$h = ($todaystart - $start_time) / 3600;
$h = floor($h);
$more = $h % $arr[0];
if ($more == 0) {
$cycle_time = 60 * $arr[1];
$cycle_time1 = $todaystart + $cycle_time;
if ($cycle_time1 < $time) {
$cycle_time = 60 * 60 * $arr[0] + 60 * $arr[1];
}
} else {
$sh = $arr[0] - $more;
$cycle_time = 60 * 60 * $sh + 60 * $arr[1];
}
$cycle_time = $todaystart + $cycle_time;
}
break;
case 3: //每小时
$cycle_time = strtotime(date('Y-m-d ' . 'H:' . $cycle . ':00', $time));
break;
case 4: //每天
$arr = explode('/', $cycle);
$cycle_time = strtotime(date('Y-m-d ' . $arr[0] . ':' . $arr[1] . ':00', $time));
break;
case 5: //N天
$arr = explode('/', $cycle);
$todaystart = strtotime(date('Y-m-d ' . '00:00:00', $time));
$start_time = strtotime(date('Y-m-d ' . '00:00:00', $start_time));
$d = ($todaystart - $start_time) / 86400;
$d = floor($d);
$more = $d % $arr[0];
if ($more == 0) {
$cycle_time = 60 * 60 * $arr[1] + 60 * $arr[2];
$cycle_time1 = $todaystart + $cycle_time;
if ($cycle_time1 < $time) {
$cycle_time = 60 * 60 * 24 * $more + 60 * 60 * $arr[1] + 60 * $arr[2];
}
} else {
$sd = $arr[0] - $more;
$cycle_time = 60 * 60 * 24 * $sd + 60 * 60 * $arr[1] + 60 * $arr[2];
}
$cycle_time = $todaystart + $cycle_time;
break;
case 6: //每星期
$arr = explode('/', $cycle);
$todaystart = strtotime(date('Y-m-d ' . '00:00:00', $time));
$w = date("w");
if ($w > $arr[0]) {
$d = 7 - $w + $arr[0];
$cycle_time = $todaystart + 60 * 60 * 24 * $d + 60 * 60 * $arr[1] + 60 * $arr[2];
} else if ($w == $arr[0]) {
$to_time = 60 * 60 * $arr[1] + 60 * $arr[2];
$to_time = $todaystart + $to_time;
if ($time > $to_time) {
$d = 7 - $w + $arr[0];
$cycle_time = $todaystart + 60 * 60 * 24 * $d + 60 * 60 * $arr[1] + 60 * $arr[2];
} else {
$cycle_time = $to_time;
}
} else {
$d = $arr[0] - $w;
$cycle_time = $todaystart + 60 * 60 * 24 * $d + 60 * 60 * $arr[1] + 60 * $arr[2];
}
break;
case 7: //每月
$arr = explode('/', $cycle);
$current_d = date("d");
$firstDate = date('Y-m-01', $time);
$max_d = date('d', strtotime("$firstDate + 1 month -1 day"));
$todaystart = strtotime(date('Y-m-d ' . '00:00:00', $time));
if ($current_d > $arr[0]) {
$d = $max_d - $current_d + $arr[0];
$cycle_time = $todaystart + 60 * 60 * 24 * $d + 60 * 60 * $arr[1] + 60 * $arr[2];
} elseif ($current_d == $arr[0]) {
$to_time = 60 * 60 * $arr[1] + 60 * $arr[2];
$to_time = $todaystart + $to_time;
if ($time > $to_time) {
$d = $max_d - $current_d + $arr[0];
$cycle_time = $todaystart + 60 * 60 * 24 * $d + 60 * 60 * $arr[1] + 60 * $arr[2];
} else {
$cycle_time = $to_time;
}
} else {
$d = $arr[0] - $current_d;
$cycle_time = $todaystart + 60 * 60 * 24 * $d + 60 * 60 * $arr[1] + 60 * $arr[2];
}
break;
case 8: //每年
$arr = explode('/', $cycle);
$cycle_time = strtotime(date('Y-' . $arr[0] . '-' . $arr[1] . ' ' . $arr[2] . ':' . $arr[3] . ':00', $time));
break;
default:
$cycle_time = 0;
break;
}
return ['cycle_time' => $cycle_time];
}
}