实现后台及前台通过API访问UV埋点,所有代码全部保存
This commit is contained in:
261
src/app/model/ai/AiChatHistory.php
Normal file
261
src/app/model/ai/AiChatHistory.php
Normal file
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
/**
|
||||
* AI聊天历史记录模型
|
||||
* 负责处理AI对话中的聊天记录数据存储和管理
|
||||
*/
|
||||
namespace app\model\ai;
|
||||
|
||||
use think\facade\Db;
|
||||
use app\model\BaseModel;
|
||||
use Exception;
|
||||
|
||||
class AiChatHistory extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 日志文件名称
|
||||
* @var string
|
||||
*/
|
||||
private $log_file = 'ai_chat_history.log';
|
||||
/**
|
||||
* 表名
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ai_chat_history';
|
||||
|
||||
/**
|
||||
* 保存聊天记录
|
||||
* @param array $data 聊天记录数据
|
||||
* @return array
|
||||
*/
|
||||
public function saveHistory($data)
|
||||
{
|
||||
try {
|
||||
// 验证必要字段
|
||||
if (empty($data['site_id']) || empty($data['user_id']) || empty($data['session_id']) ||
|
||||
empty($data['platform']) || empty($data['user_message']) || empty($data['ai_message'])) {
|
||||
return $this->error('', 'PARAMETERS_INCOMPLETE');
|
||||
}
|
||||
|
||||
// 补充默认字段
|
||||
$data['create_time'] = $data['create_time'] ?? time();
|
||||
$data['ip'] = $data['ip'] ?? request()->ip();
|
||||
|
||||
// 保存数据
|
||||
$result = Db::name($this->name)->insert($data);
|
||||
|
||||
if ($result) {
|
||||
return $this->success(['id' => Db::name($this->name)->getLastInsID()]);
|
||||
} else {
|
||||
return $this->error('', 'SAVE_FAILED');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// 记录错误日志
|
||||
log_write('Save chat history error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'SAVE_EXCEPTION');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聊天历史记录列表
|
||||
* @param array $where 条件数组
|
||||
* @param array $field 查询字段
|
||||
* @param string $order 排序方式
|
||||
* @param int $page 页码
|
||||
* @param int $page_size 每页数量
|
||||
* @return array
|
||||
*/
|
||||
public function getHistoryList($where = [], $field = ['*'], $order = 'create_time ASC', $page = 1, $page_size = 20)
|
||||
{
|
||||
try {
|
||||
// 计算偏移量
|
||||
$offset = ($page - 1) * $page_size;
|
||||
|
||||
// 查询列表
|
||||
$list = Db::name($this->name)
|
||||
->field($field)
|
||||
->where($where)
|
||||
->order($order)
|
||||
->limit($offset, $page_size)
|
||||
->select();
|
||||
|
||||
// 查询总数
|
||||
$total = Db::name($this->name)
|
||||
->where($where)
|
||||
->count();
|
||||
|
||||
return $this->success([
|
||||
'list' => $list,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'page_size' => $page_size,
|
||||
'total_page' => ceil($total / $page_size)
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
log_write('Get chat history list error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'GET_LIST_FAILED');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话ID获取聊天记录
|
||||
* @param string $session_id 会话ID
|
||||
* @param array $where 额外条件
|
||||
* @param int $page 页码
|
||||
* @param int $page_size 每页数量
|
||||
* @return array
|
||||
*/
|
||||
public function getHistoryBySessionId($session_id, $where = [], $page = 1, $page_size = 20)
|
||||
{
|
||||
if (empty($session_id)) {
|
||||
return $this->error('', 'SESSION_ID_EMPTY');
|
||||
}
|
||||
|
||||
$where['session_id'] = $session_id;
|
||||
return $this->getHistoryList($where, ['*'], 'create_time ASC', $page, $page_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的所有聊天记录
|
||||
* @param string $user_id 用户ID
|
||||
* @param array $where 额外条件
|
||||
* @param int $page 页码
|
||||
* @param int $page_size 每页数量
|
||||
* @return array
|
||||
*/
|
||||
public function getUserHistory($user_id, $where = [], $page = 1, $page_size = 20)
|
||||
{
|
||||
if (empty($user_id)) {
|
||||
return $this->error('', 'USER_ID_EMPTY');
|
||||
}
|
||||
|
||||
$where['user_id'] = $user_id;
|
||||
return $this->getHistoryList($where, ['*'], 'create_time DESC', $page, $page_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除聊天记录
|
||||
* @param array $where 条件数组
|
||||
* @return array
|
||||
*/
|
||||
public function deleteHistory($where)
|
||||
{
|
||||
try {
|
||||
if (empty($where)) {
|
||||
return $this->error('', 'DELETE_CONDITION_EMPTY');
|
||||
}
|
||||
|
||||
$result = Db::name($this->name)->where($where)->delete();
|
||||
|
||||
if ($result !== false) {
|
||||
return $this->success(['deleted' => $result]);
|
||||
} else {
|
||||
return $this->error('', 'DELETE_FAILED');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
log_write('Delete chat history error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'DELETE_EXCEPTION');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话ID删除聊天记录
|
||||
* @param string $session_id 会话ID
|
||||
* @param array $where 额外条件
|
||||
* @return array
|
||||
*/
|
||||
public function deleteHistoryBySessionId($session_id, $where = [])
|
||||
{
|
||||
if (empty($session_id)) {
|
||||
return $this->error('', 'SESSION_ID_EMPTY');
|
||||
}
|
||||
|
||||
$where['session_id'] = $session_id;
|
||||
return $this->deleteHistory($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的会话消息统计
|
||||
* @param string $user_id 用户ID
|
||||
* @param array $where 额外条件
|
||||
* @return array
|
||||
*/
|
||||
public function getUserMessageStats($user_id, $where = [])
|
||||
{
|
||||
try {
|
||||
if (empty($user_id)) {
|
||||
return $this->error('', 'USER_ID_EMPTY');
|
||||
}
|
||||
|
||||
$where['user_id'] = $user_id;
|
||||
|
||||
// 统计总消息数
|
||||
$total_count = Db::name($this->name)->where($where)->count();
|
||||
|
||||
// 统计今日消息数
|
||||
$today_count = Db::name($this->name)
|
||||
->where($where)
|
||||
->whereTime('create_time', 'today')
|
||||
->count();
|
||||
|
||||
// 统计最近消息时间
|
||||
$last_message = Db::name($this->name)
|
||||
->where($where)
|
||||
->order('create_time DESC')
|
||||
->find();
|
||||
|
||||
return $this->success([
|
||||
'total_count' => $total_count,
|
||||
'today_count' => $today_count,
|
||||
'last_message_time' => $last_message ? $last_message['create_time'] : 0
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
log_write('Get user message stats error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'GET_STATS_FAILED');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期的聊天记录
|
||||
* @param int $days 保留天数
|
||||
* @param array $where 额外条件
|
||||
* @return array
|
||||
*/
|
||||
public function cleanupExpiredHistory($days = 30, $where = [])
|
||||
{
|
||||
try {
|
||||
$expire_time = time() - ($days * 24 * 60 * 60);
|
||||
$where['create_time'] = ['<', $expire_time];
|
||||
|
||||
return $this->deleteHistory($where);
|
||||
} catch (Exception $e) {
|
||||
log_write('Cleanup expired history error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'CLEANUP_FAILED');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存聊天记录
|
||||
* @param array $data 聊天记录数组
|
||||
* @return array
|
||||
*/
|
||||
public function batchSaveHistory($data)
|
||||
{
|
||||
try {
|
||||
if (empty($data) || !is_array($data)) {
|
||||
return $this->error('', 'DATA_EMPTY');
|
||||
}
|
||||
|
||||
// 批量插入数据
|
||||
$result = Db::name($this->name)->insertAll($data);
|
||||
|
||||
if ($result) {
|
||||
return $this->success(['count' => $result]);
|
||||
} else {
|
||||
return $this->error('', 'BATCH_SAVE_FAILED');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
log_write('Batch save chat history error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'BATCH_SAVE_EXCEPTION');
|
||||
}
|
||||
}
|
||||
}
|
||||
266
src/app/model/ai/AiChatSession.php
Normal file
266
src/app/model/ai/AiChatSession.php
Normal file
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AI聊天会话模型
|
||||
* 用于管理AI聊天的会话信息
|
||||
*/
|
||||
namespace app\model\ai;
|
||||
|
||||
use \app\model\BaseModel;
|
||||
use think\facade\Db;
|
||||
|
||||
class AiChatSession extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 日志文件名称
|
||||
* @var string
|
||||
*/
|
||||
private $log_file = 'ai_chat_session.log';
|
||||
/**
|
||||
* 表名
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ai_chat_session';
|
||||
|
||||
/**
|
||||
* 获取会话信息
|
||||
* @param array $where 条件
|
||||
* @param array $field 字段
|
||||
* @return array
|
||||
*/
|
||||
public function getSessionInfo($where = [], $field = ['*'])
|
||||
{
|
||||
try {
|
||||
$data = Db::name($this->name)
|
||||
->where($where)
|
||||
->field($field)
|
||||
->find();
|
||||
|
||||
return $this->success($data);
|
||||
} catch (\Exception $e) {
|
||||
log_write('Get session info error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'GET_SESSION_INFO_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话列表
|
||||
* @param array $where 条件
|
||||
* @param array $field 字段
|
||||
* @param string $order 排序
|
||||
* @param int $page 页码
|
||||
* @param int $page_size 每页数量
|
||||
* @return array
|
||||
*/
|
||||
public function getSessionList($where = [], $field = ['*'], $order = 'last_active_time DESC', $page = 1, $page_size = 20)
|
||||
{
|
||||
try {
|
||||
$count = Db::name($this->name)
|
||||
->where($where)
|
||||
->count();
|
||||
|
||||
$list = [];
|
||||
if ($count > 0) {
|
||||
$list = Db::name($this->name)
|
||||
->where($where)
|
||||
->field($field)
|
||||
->order($order)
|
||||
->page($page, $page_size)
|
||||
->select();
|
||||
}
|
||||
|
||||
return $this->success(json_encode([
|
||||
'list' => $list,
|
||||
'total' => $count,
|
||||
'page' => $page,
|
||||
'page_size' => $page_size
|
||||
]));
|
||||
} catch (\Exception $e) {
|
||||
log_write('Get session list error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'GET_SESSION_LIST_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建会话
|
||||
* @param array $data 数据
|
||||
* @return array
|
||||
*/
|
||||
public function createSession($data = [])
|
||||
{
|
||||
try {
|
||||
// 确保必要字段存在
|
||||
if (empty($data['session_id']) || empty($data['user_id']) || empty($data['site_id'])) {
|
||||
return $this->error('', 'MISSING_REQUIRED_FIELDS');
|
||||
}
|
||||
|
||||
// 检查会话是否已存在
|
||||
$exists = Db::name($this->name)
|
||||
->where('session_id', $data['session_id'])
|
||||
->count();
|
||||
|
||||
if ($exists > 0) {
|
||||
return $this->error('', 'SESSION_ALREADY_EXISTS');
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
$data['create_time'] = $data['create_time'] ?? time();
|
||||
$data['last_active_time'] = $data['last_active_time'] ?? time();
|
||||
|
||||
$result = Db::name($this->name)->insert($data);
|
||||
|
||||
if ($result) {
|
||||
return $this->success(json_encode(['session_id' => $data['session_id']]));
|
||||
} else {
|
||||
return $this->error('', 'CREATE_SESSION_FAILED');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
log_write('Create session error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'CREATE_SESSION_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会话
|
||||
* @param array $where 条件
|
||||
* @param array $data 数据
|
||||
* @return array
|
||||
*/
|
||||
public function updateSession($where = [], $data = [])
|
||||
{
|
||||
try {
|
||||
if (empty($where)) {
|
||||
return $this->error('', 'WHERE_CONDITION_EMPTY');
|
||||
}
|
||||
|
||||
$result = Db::name($this->name)
|
||||
->where($where)
|
||||
->update($data);
|
||||
|
||||
return $this->success(['affected_rows' => $result]);
|
||||
} catch (\Exception $e) {
|
||||
log_write('Update session error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'UPDATE_SESSION_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除会话
|
||||
* @param array $where 条件
|
||||
* @return array
|
||||
*/
|
||||
public function deleteSession($where = [])
|
||||
{
|
||||
try {
|
||||
if (empty($where)) {
|
||||
return $this->error('', 'WHERE_CONDITION_EMPTY');
|
||||
}
|
||||
|
||||
// 开启事务
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 删除会话
|
||||
$result = Db::name($this->name)
|
||||
->where($where)
|
||||
->delete();
|
||||
|
||||
// 删除相关的聊天历史
|
||||
Db::name('shop_ai_chat_history')
|
||||
->where($where)
|
||||
->delete();
|
||||
|
||||
Db::commit();
|
||||
return $this->success(['affected_rows' => $result]);
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
throw $e;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
log_write('Delete session error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'DELETE_SESSION_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会话最后活动时间
|
||||
* @param string $session_id 会话ID
|
||||
* @param int $time 时间戳
|
||||
* @return array
|
||||
*/
|
||||
public function updateLastActiveTime($session_id, $time = null)
|
||||
{
|
||||
$time = $time ?? time();
|
||||
return $this->updateSession(['session_id' => $session_id], ['last_active_time' => $time]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的活跃会话数
|
||||
* @param int $user_id 用户ID
|
||||
* @param int $site_id 站点ID
|
||||
* @param int $days 天数
|
||||
* @return array
|
||||
*/
|
||||
public function getUserActiveSessionsCount($user_id, $site_id, $days = 7)
|
||||
{
|
||||
try {
|
||||
$start_time = time() - ($days * 24 * 3600);
|
||||
|
||||
$count = Db::name($this->name)
|
||||
->where('user_id', $user_id)
|
||||
->where('site_id', $site_id)
|
||||
->where('last_active_time', '>=', $start_time)
|
||||
->count();
|
||||
|
||||
return $this->success(['count' => $count]);
|
||||
} catch (\Exception $e) {
|
||||
log_write('Get active sessions count error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'GET_ACTIVE_SESSIONS_COUNT_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期会话
|
||||
* @param int $days 天数
|
||||
* @return array
|
||||
*/
|
||||
public function cleanupExpiredSessions($days = 30)
|
||||
{
|
||||
try {
|
||||
$expire_time = time() - ($days * 24 * 3600);
|
||||
|
||||
// 开启事务
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 找出所有过期会话ID
|
||||
$expired_sessions = Db::name($this->name)
|
||||
->where('last_active_time', '<', $expire_time)
|
||||
->column('session_id');
|
||||
|
||||
if (!empty($expired_sessions)) {
|
||||
// 删除过期会话
|
||||
$session_result = Db::name($this->name)
|
||||
->where('session_id', 'in', $expired_sessions)
|
||||
->delete();
|
||||
|
||||
// 删除相关聊天历史
|
||||
$history_result = Db::name('shop_ai_chat_history')
|
||||
->where('session_id', 'in', $expired_sessions)
|
||||
->delete();
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
return $this->success([
|
||||
'expired_count' => count($expired_sessions),
|
||||
'session_deleted' => $session_result ?? 0,
|
||||
'history_deleted' => $history_result ?? 0
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
throw $e;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
log_write('Cleanup expired sessions error: ' . $e->getMessage(), 'error', $this->log_file);
|
||||
return $this->error('', 'CLEANUP_EXPIRED_SESSIONS_ERROR');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,34 +30,53 @@ class StatShop extends BaseModel
|
||||
*/
|
||||
public function addShopStat($data)
|
||||
{
|
||||
$carbon = Carbon::now();
|
||||
$dir = __UPLOAD__.'/stat/stat_shop/';
|
||||
if (!is_dir($dir) && !mkdir($dir, 0777, true) && !is_dir($dir)) {
|
||||
return $this->error(sprintf('Directory "%s" was not created', $dir));
|
||||
}
|
||||
$filename = $dir.$carbon->year.'_'.$carbon->month.'_'.$carbon->day.'_'.$carbon->second.'_'.unique_random().'.json';
|
||||
$stat_extend = new Stat($filename, 'stat_shop',$data['site_id']);
|
||||
$stat_extend->handleData($data);//写入文件
|
||||
log_write('店铺按天新统计数据开始添加', 'debug');
|
||||
try{
|
||||
$carbon = Carbon::now();
|
||||
$dir = __UPLOAD__.'/stat/stat_shop/';
|
||||
if (!is_dir($dir) && !mkdir($dir, 0777, true) && !is_dir($dir)) {
|
||||
return $this->error(sprintf('Directory "%s" was not created', $dir));
|
||||
}
|
||||
$filename = $dir.$carbon->year.'_'.$carbon->month.'_'.$carbon->day.'_'.$carbon->second.'_'.unique_random().'.json';
|
||||
$stat_extend = new Stat($filename, 'stat_shop',$data['site_id']);
|
||||
$stat_extend->handleData($data);//写入文件
|
||||
|
||||
//增加当天时统计
|
||||
$this->addShopHourStat($data, $carbon);
|
||||
//增加当天时统计
|
||||
$this->addShopHourStat($data, $carbon);
|
||||
}catch (\Exception $e){
|
||||
log_write('店铺按天新统计数据添加失败:' . $e->getMessage(), 'error');
|
||||
return $this->error('店铺按天新统计数据添加失败');
|
||||
}
|
||||
|
||||
log_write('店铺按天新统计数据已添加', 'debug');
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从stat_shop目录下读取所有文件,将数据处理后,写入数据表中
|
||||
* 处理完每个文件后,删除文件
|
||||
*/
|
||||
public function cronShopStat()
|
||||
{
|
||||
log_write('店铺按天统计数据开始处理', 'debug');
|
||||
$path = __UPLOAD__.'/stat/stat_shop';
|
||||
if(!is_dir($path)) return;
|
||||
if(!is_dir($path)) {
|
||||
log_write('店铺按天统计数据处理失败:目录不存在', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->scanFile($path);
|
||||
if(empty($result)) return;
|
||||
if(empty($result)) {
|
||||
log_write('店铺按天统计数据处理失败:目录下无文件', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$json_array = [];
|
||||
foreach ($result as $key => $val){
|
||||
$stat_extend = new Stat($path.'/'.$val, 'stat_shop');
|
||||
$json_array[] = $stat_extend->load();
|
||||
unlink($path.'/'.$val);
|
||||
unlink($path.'/'.$val); // 处理完文件后,删除文件
|
||||
}
|
||||
|
||||
|
||||
@@ -83,8 +102,10 @@ class StatShop extends BaseModel
|
||||
foreach ($data_array as $json_k => $json_v){
|
||||
$system_stat->addStatShopModel($json_v);
|
||||
}
|
||||
log_write('店铺按天统计数据处理成功', 'debug');
|
||||
} catch (\Exception $e) {
|
||||
|
||||
log_write('店铺按天统计数据处理失败:' . $e->getMessage(), 'error');
|
||||
return $this->error('店铺按天统计数据处理失败');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -97,23 +118,37 @@ class StatShop extends BaseModel
|
||||
*/
|
||||
public function addShopHourStat($data, $carbon)
|
||||
{
|
||||
$dir = __UPLOAD__.'/stat/stat_shop_hour/';
|
||||
if (!is_dir($dir) && !mkdir($dir, 0777, true) && !is_dir($dir)) {
|
||||
return $this->error(sprintf('Directory "%s" was not created', $dir));
|
||||
}
|
||||
$filename = $dir.$carbon->year.'_'.$carbon->month.'_'.$carbon->day.'_'.$carbon->hour.'_'.$carbon->second.'_'.unique_random().'.json';
|
||||
$stat_extend = new Stat($filename, 'stat_shop_hour',$data['site_id']);
|
||||
$stat_extend->handleData($data);//写入文件
|
||||
log_write('店铺按小时新统计数据开始添加', 'debug');
|
||||
try{
|
||||
$dir = __UPLOAD__.'/stat/stat_shop_hour/';
|
||||
if (!is_dir($dir) && !mkdir($dir, 0777, true) && !is_dir($dir)) {
|
||||
return $this->error(sprintf('Directory "%s" was not created', $dir));
|
||||
}
|
||||
$filename = $dir.$carbon->year.'_'.$carbon->month.'_'.$carbon->day.'_'.$carbon->hour.'_'.$carbon->second.'_'.unique_random().'.json';
|
||||
$stat_extend = new Stat($filename, 'stat_shop_hour',$data['site_id']);
|
||||
$stat_extend->handleData($data);//写入文件
|
||||
log_write('店铺按小时新统计数据已添加', 'debug');
|
||||
}catch (\Exception $e){
|
||||
log_write('店铺按小时新统计数据添加失败:' . $e->getMessage(), 'error');
|
||||
return $this->error('店铺按小时新统计数据添加失败');
|
||||
}
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
public function cronShopStatHour()
|
||||
{
|
||||
log_write('系统计划任务开始执行店铺按小时统计');
|
||||
$path = __UPLOAD__.'/stat/stat_shop_hour';
|
||||
if(!is_dir($path)) return;
|
||||
if(!is_dir($path)) {
|
||||
log_write('系统计划任务执行店铺按小时统计异常:目录不存在' . $path . ',请检查目录是否存在');
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->scanFile($path);
|
||||
if(empty($result)) return;
|
||||
if(empty($result)) {
|
||||
log_write('系统计划任务执行店铺按小时统计异常:目录下无文件' . $path . ',请检查是否有文件存在');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$json_array = [];
|
||||
@@ -145,8 +180,9 @@ class StatShop extends BaseModel
|
||||
foreach ($json_array as $json_k => $json_v){
|
||||
$system_stat->addStatShopHourModel($json_v);
|
||||
}
|
||||
log_write('系统计划任务执行店铺按小时统计完成');
|
||||
} catch (\Exception $e) {
|
||||
|
||||
log_write('系统计划任务执行店铺按小时统计异常:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace app\model\system;
|
||||
|
||||
use app\dict\system\ScheduleDict;
|
||||
@@ -15,7 +16,7 @@ use think\facade\Queue;
|
||||
class Cron extends BaseModel
|
||||
{
|
||||
|
||||
public $time_diff = 60;//默认半个小时检测一次
|
||||
public $time_diff = 60; //默认半个小时检测一次
|
||||
|
||||
/**
|
||||
* 添加计划任务
|
||||
@@ -25,7 +26,7 @@ class Cron extends BaseModel
|
||||
* @param string $event 执行事件
|
||||
* @param int $execute_time 待执行时间
|
||||
* @param int $relate_id 关联id
|
||||
* @param int $period_type 周期类型
|
||||
* @param int $period_type 周期类型 0 分钟 1 天 2 周 3 月
|
||||
*/
|
||||
public function addCron($type, $period, $name, $event, $execute_time, $relate_id, $period_type = 0)
|
||||
{
|
||||
@@ -40,6 +41,7 @@ class Cron extends BaseModel
|
||||
'create_time' => time()
|
||||
];
|
||||
$res = model('cron')->add($data);
|
||||
log_write('添加计划任务:' . json_encode($data), 'debug');
|
||||
return $this->success($res);
|
||||
}
|
||||
|
||||
@@ -59,87 +61,98 @@ class Cron extends BaseModel
|
||||
*/
|
||||
public function execute($type = 'default')
|
||||
{
|
||||
if(config('cron.default') != $type)
|
||||
log_write('计划任务开始执行', 'debug');
|
||||
if (config('cron.default') != $type) {
|
||||
log_write('计划任务方式不匹配<不能执行/model/system/Cron/execute>:' . config('cron.default') . ' != ' . $type, 'debug');
|
||||
return true;
|
||||
}
|
||||
|
||||
Log::write('计划任务方式'.$type);
|
||||
//写入计划任务标记运行
|
||||
$this->writeSchedule();
|
||||
$system_config_model = new SystemConfig();
|
||||
$config = $system_config_model->getSystemConfig()[ 'data' ] ?? [];
|
||||
$is_open_queue = $config[ 'is_open_queue' ] ?? 0;
|
||||
$query_execute_time = $is_open_queue == 1 ? time() + 60 : time();
|
||||
$list = model('cron')->getList([ [ 'execute_time', '<=', $query_execute_time ] ]);
|
||||
$now_time = time();
|
||||
if (!empty($list)) {
|
||||
foreach ($list as $k => $v) {
|
||||
$event_res = checkQueue($v, function($params) {
|
||||
//加入消息队列
|
||||
$job_handler_classname = 'Cronexecute';
|
||||
try {
|
||||
if ($params[ 'execute_time' ] <= time()) {
|
||||
Queue::push($job_handler_classname, $params);
|
||||
} else {
|
||||
Queue::later($params[ 'execute_time' ] - time(), $job_handler_classname, $params);
|
||||
log_write('当前执行方式:' . $type, 'debug');
|
||||
|
||||
try {
|
||||
//写入计划任务标记运行
|
||||
$this->writeSchedule();
|
||||
$system_config_model = new SystemConfig();
|
||||
$config = $system_config_model->getSystemConfig()['data'] ?? [];
|
||||
$is_open_queue = $config['is_open_queue'] ?? 0;
|
||||
$query_execute_time = $is_open_queue == 1 ? time() + 60 : time();
|
||||
$list = model('cron')->getList([['execute_time', '<=', $query_execute_time]]);
|
||||
$now_time = time();
|
||||
log_write('计划任务开始执行,查询计划任务列表', 'debug');
|
||||
|
||||
if (!empty($list)) {
|
||||
foreach ($list as $k => $v) {
|
||||
$event_res = checkQueue($v, function ($params) {
|
||||
//加入消息队列
|
||||
$job_handler_classname = 'Cronexecute';
|
||||
try {
|
||||
if ($params['execute_time'] <= time()) {
|
||||
Queue::push($job_handler_classname, $params);
|
||||
} else {
|
||||
Queue::later($params['execute_time'] - time(), $job_handler_classname, $params);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$res = $this->error($e->getMessage(), $e->getMessage());
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$res = $this->error($e->getMessage(), $e->getMessage());
|
||||
}
|
||||
return $res ?? $this->success();
|
||||
}, function($params) {
|
||||
try {
|
||||
$res = event($params[ 'event' ], [ 'relate_id' => $params[ 'relate_id' ] ]);
|
||||
} catch (\Exception $e) {
|
||||
$res = $this->error($e->getMessage(), $e->getMessage());
|
||||
return $res ?? $this->success();
|
||||
}, function ($params) {
|
||||
try {
|
||||
log_write('调用事件名称:' . $params['name'], 'debug');
|
||||
$res = event($params['event'], ['relate_id' => $params['relate_id']]);
|
||||
} catch (\Exception $e) {
|
||||
$res = $this->error($e->getMessage(), $e->getMessage());
|
||||
}
|
||||
|
||||
$data_log = [
|
||||
'name' => $params['name'],
|
||||
'event' => $params['event'],
|
||||
'relate_id' => $params['relate_id'],
|
||||
'message' => json_encode($res)
|
||||
];
|
||||
$this->addCronLog($data_log);
|
||||
return $res;
|
||||
});
|
||||
|
||||
//定义最新的执行时间或错误
|
||||
$event_code = $event_res['code'] ?? 0;
|
||||
if ($event_code < 0) {
|
||||
Log::write($event_res);
|
||||
continue;
|
||||
}
|
||||
|
||||
$data_log = [
|
||||
'name' => $params[ 'name' ],
|
||||
'event' => $params[ 'event' ],
|
||||
'relate_id' => $params[ 'relate_id' ],
|
||||
'message' => json_encode($res)
|
||||
];
|
||||
$this->addCronLog($data_log);
|
||||
return $res;
|
||||
});
|
||||
//循环任务
|
||||
if ($v['type'] == 2) {
|
||||
$period = $v['period'] == 0 ? 1 : $v['period'];
|
||||
switch ($v['period_type']) {
|
||||
case 0: //分
|
||||
|
||||
//定义最新的执行时间或错误
|
||||
$event_code = $event_res[ 'code' ] ?? 0;
|
||||
if ($event_code < 0) {
|
||||
Log::write($event_res);
|
||||
continue;
|
||||
}
|
||||
$execute_time = $now_time + $period * 60;
|
||||
break;
|
||||
case 1: //天
|
||||
|
||||
//循环任务
|
||||
if ($v[ 'type' ] == 2) {
|
||||
$period = $v[ 'period' ] == 0 ? 1 : $v[ 'period' ];
|
||||
switch ( $v[ 'period_type' ] ) {
|
||||
case 0://分
|
||||
$execute_time = strtotime('+' . $period . 'day', $v['execute_time']);
|
||||
break;
|
||||
case 2: //周
|
||||
|
||||
$execute_time = $now_time + $period * 60;
|
||||
break;
|
||||
case 1://天
|
||||
$execute_time = strtotime('+' . $period . 'week', $v['execute_time']);
|
||||
break;
|
||||
case 3: //月
|
||||
|
||||
$execute_time = strtotime('+' . $period . 'day', $v[ 'execute_time' ]);
|
||||
break;
|
||||
case 2://周
|
||||
|
||||
$execute_time = strtotime('+' . $period . 'week', $v[ 'execute_time' ]);
|
||||
break;
|
||||
case 3://月
|
||||
|
||||
$execute_time = strtotime('+' . $period . 'month', $v[ 'execute_time' ]);
|
||||
break;
|
||||
$execute_time = strtotime('+' . $period . 'month', $v['execute_time']);
|
||||
break;
|
||||
}
|
||||
model('cron')->update(['execute_time' => $execute_time], [['id', '=', $v['id']]]);
|
||||
} else {
|
||||
model('cron')->delete([['id', '=', $v['id']]]);
|
||||
}
|
||||
model('cron')->update([ 'execute_time' => $execute_time ], [ [ 'id', '=', $v[ 'id' ] ] ]);
|
||||
|
||||
} else {
|
||||
model('cron')->delete([ [ 'id', '=', $v[ 'id' ] ] ]);
|
||||
}
|
||||
}
|
||||
// $this->setCron();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
log_write('计划任务执行异常<model/system/Cron/execute>:' . $e->getMessage(), 'debug');
|
||||
return true;
|
||||
}
|
||||
// $this->setCron();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,8 +163,8 @@ class Cron extends BaseModel
|
||||
public function addCronLog($data)
|
||||
{
|
||||
// 日常不需要添加,调试使用
|
||||
// $data[ 'execute_time' ] = time();
|
||||
// model('cron_log')->add($data);
|
||||
$data['execute_time'] = time();
|
||||
model('cron_log')->add($data);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
@@ -167,22 +180,22 @@ class Cron extends BaseModel
|
||||
if (empty($cron_cache)) {
|
||||
//todo 不存在缓存标识,并不视为任务停止
|
||||
//创建缓存标识,当前时间填充
|
||||
Cache::set('cron_cache', [ 'time' => $now_time, 'error' => '' ]);
|
||||
Cache::set('cron_cache', ['time' => $now_time, 'error' => '']);
|
||||
} else {
|
||||
$time = $cron_cache[ 'time' ];
|
||||
$error = $cron_cache[ 'error' ] ?? '';
|
||||
$attempts = $cron_cache[ 'attempts' ] ?? 0;//尝试次数
|
||||
if (!empty($error) || ( $now_time - $time ) > $diff) {
|
||||
$time = $cron_cache['time'];
|
||||
$error = $cron_cache['error'] ?? '';
|
||||
$attempts = $cron_cache['attempts'] ?? 0; //尝试次数
|
||||
if (!empty($error) || ($now_time - $time) > $diff) {
|
||||
$message = '自动任务已停止';
|
||||
if (!empty($error)) {
|
||||
$message .= ',停止原因:' . $error;
|
||||
} else {
|
||||
$system_config_model = new \app\model\system\SystemConfig();
|
||||
$config = $system_config_model->getSystemConfig()[ 'data' ] ?? [];
|
||||
$is_open_queue = $config[ 'is_open_queue' ] ?? 0;
|
||||
if (!$is_open_queue) {//如果不是消息队列的话,可以尝试异步调用一下
|
||||
$config = $system_config_model->getSystemConfig()['data'] ?? [];
|
||||
$is_open_queue = $config['is_open_queue'] ?? 0;
|
||||
if (!$is_open_queue) { //如果不是消息队列的话,可以尝试异步调用一下
|
||||
if ($attempts < 1) {
|
||||
Cache::set('cron_cache', [ 'time' => $now_time, 'error' => '', 'attempts' => 1 ]);
|
||||
Cache::set('cron_cache', ['time' => $now_time, 'error' => '', 'attempts' => 1]);
|
||||
$url = url('cron/task/execute');
|
||||
http($url, 1);
|
||||
return $this->success();
|
||||
@@ -194,10 +207,8 @@ class Cron extends BaseModel
|
||||
//判断任务是 消息队列自动任务,还是默认睡眠sleep自动任务
|
||||
return $this->error([], $message);
|
||||
}
|
||||
|
||||
}
|
||||
return $this->success();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,14 +222,14 @@ class Cron extends BaseModel
|
||||
if (empty($cron_cache)) {
|
||||
$cron_cache = [];
|
||||
}
|
||||
// $code = $params['code'] ?? 0;
|
||||
// if($code < 0){
|
||||
// $error = $params['message'] ?? '位置的错误';
|
||||
// $cron_cache['error'] = $error;
|
||||
// }
|
||||
// $code = $params['code'] ?? 0;
|
||||
// if($code < 0){
|
||||
// $error = $params['message'] ?? '位置的错误';
|
||||
// $cron_cache['error'] = $error;
|
||||
// }
|
||||
|
||||
$cron_cache[ 'time' ] = time();
|
||||
$cron_cache[ 'attempts' ] = 0;
|
||||
$cron_cache['time'] = time();
|
||||
$cron_cache['attempts'] = 0;
|
||||
Cache::set('cron_cache', $cron_cache);
|
||||
return $this->success();
|
||||
}
|
||||
@@ -229,27 +240,35 @@ class Cron extends BaseModel
|
||||
*/
|
||||
public function checkSchedule()
|
||||
{
|
||||
$file = root_path('runtime') . '.schedule';
|
||||
if (file_exists($file)) {
|
||||
$time = file_get_contents($file);
|
||||
if (!empty($time) && abs($time - time()) < 90) {
|
||||
return $this->success();
|
||||
try {
|
||||
$file = root_path('runtime') . '.schedule';
|
||||
if (file_exists($file)) {
|
||||
$time = file_get_contents($file);
|
||||
if (!empty($time) && abs($time - time()) < 90) {
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
|
||||
$remark = 'Cron计划任务已停止!当前启动的任务方式:' . ScheduleDict::getType(config('cron.default')) . '。';
|
||||
$error = self::getError(config('cron.default'));
|
||||
if (!empty($error)) {
|
||||
$remark .= json_encode($error);
|
||||
}
|
||||
log_write('Cron计划任务校验计划任务是否正常运行,计划任务异常,异常信息:' . json_encode($error) . ',文件路径:' . $file, 'warning');
|
||||
return $this->error([], $remark);
|
||||
} catch (\Exception $e) {
|
||||
log_write('Cron计划任务校验计划任务是否正常运行异常:' . $e->getMessage() . ',异常行:' . $e->getLine() . ',文件路径:' . $file, 'error');
|
||||
return $this->error([], '计划任务校验计划任务是否正常运行异常:' . $e->getMessage());
|
||||
}
|
||||
$remark = '计划任务已停止!当前启动的任务方式:'.ScheduleDict::getType(config('cron.default')).'。';
|
||||
$error = self::getError(config('cron.default'));
|
||||
if(!empty($error)){
|
||||
$remark .= $error;
|
||||
}
|
||||
return $this->error([], $remark);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入校验计划任务
|
||||
* @return true
|
||||
*/
|
||||
public function writeSchedule(){
|
||||
$file = root_path('runtime').'.schedule';
|
||||
public function writeSchedule()
|
||||
{
|
||||
$file = root_path('runtime') . '.schedule';
|
||||
file_put_contents($file, time());
|
||||
return true;
|
||||
}
|
||||
@@ -260,7 +279,8 @@ class Cron extends BaseModel
|
||||
* @param $error
|
||||
* @return true
|
||||
*/
|
||||
public static function setError($type, $error = ''){
|
||||
public static function setError($type, $error = '')
|
||||
{
|
||||
Cache::set('cron_error', [$type => $error]);
|
||||
return true;
|
||||
}
|
||||
@@ -270,10 +290,11 @@ class Cron extends BaseModel
|
||||
* @param $type
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getError($type = ''){
|
||||
public static function getError($type = '')
|
||||
{
|
||||
$error = Cache::get('cron_error');
|
||||
if(!empty($type))
|
||||
if (!empty($type))
|
||||
return $error;
|
||||
return $error[$type];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user