PHPIndex

This page lists files in the current directory. You can view content, get download/execute commands for Wget, Curl, or PowerShell, or filter the list using wildcards (e.g., `*.sh`).

notice.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/plugins/msgWarning/controller/sys/notice.class.php'
View Content
<?php 
/**
 * 发送通知
 * 短信、邮件添加到队列,第三方直接发送
 */
class msgWarningSysNotice extends Controller {
	protected $pluginName;
	protected $evntInfo;
	public function __construct() {
		parent::__construct();
		$this->pluginName = 'msgWarningPlugin';
		// TODO 暂不支持短信通知——需要申请模板
    }

	/**
	 * 按事件发送通知
	 * @param [type] $evntInfo
	 * @param [type] $msg
	 * @return void
	 */
	public function send($evntInfo) {
		// 通知方式
		$methods = _get($evntInfo, 'notice.method', '');
		$methods = explode(',', $methods);
		if (empty($methods)) return;

		// 通知目标
		// $target = _get($evntInfo, 'notice.target', array()); // 原始值
		$target = _get($evntInfo, 'target', array());
		if (empty($target)) return;

		// 通知内容
		$message = _get($evntInfo, 'message', '');
		if (empty($message)) return;

		// 通知日志相关数据
		$logs = array(
			'event'		=> $evntInfo['event'],
			'title'		=> $evntInfo['title'],
			'userID'	=> 0,
			'method'	=> '',
			'target'	=> '',
		);
		$this->evntInfo = $evntInfo;
		foreach ($methods as $method) {
			switch ($method) {
				case 'email':
				case 'sms':
					$alias = array('email' => 'email', 'sms' => 'phone');
					$check = $alias[$method];
					// 获取用户列表
					$where = array(
						'userID' => array('in', $target),
						$check	 => array('<>',''),	// sms/email分别查询,无需用or
					);
					$users = Model('User')->where($where)->field('userID,name,nickName,email,phone')->select();
					if (empty($users)) continue;

					// 发送消息
					$logs['method'] = $method;
					foreach ($users as $user) {
						$name	= !empty($user['nickName']) ? $user['nickName'] : $user['name'];
						$value	= $user[$check];
						if (empty($value)) continue;
						// if (!Input::check($value, $check)) continue;	// TODO 国外手机号检查不通过,暂不处理——实际发送时也会检查,此处无需处理
						$logs['userID'] = $user['userID'];
						$logs['target'] = $value;

						// 发送消息
						$user = array('name' => $name, $check => $value);
						$func = 'by' . ucfirst($method);	// bySms、byEmail
						$this->$func($user, $message, $logs);
					}
					break;
				case 'weixin':
				case 'dding':
					$logs['method'] = $method;
					$logs['target'] = '';
					$this->byThird($target, $message, $logs);
					break;
			}
		}
		TaskQueue::addSubmit();
	}

	/**
	 * 通过第三方(钉钉、企业微信)发送通知
	 * @param [type] $user
	 * @param [type] $content	['','']
	 * @param [type] $logs
	 * @return void
	 */
	public function byThird($user, $content, $logs){
		// 获取有效发送用户;无效的写失败日志
		$userDef = $user;
		$this->filterThirdUsers($user);
		$userDiff = array_diff($userDef, $user);
		if (!empty($userDiff)) {
			$logs['status'] = 0;
			$logs['desc'] = LNG('msgWarning.main.invalidUser');
			$this->addUsersLog($userDiff, $logs);
		}
		if (empty($user)) return;

		// 发送消息
		$typeArr = array(
			'weixin' => 'weChat',
			'dding'	 => 'dingTalk'
		);
		$title = $logs['title'];
		$content = array_merge(array('**'.$title.'**'), $content);
		$data = array(
			'msg'		=> array(
				'type'		=> 'markdown',	// text、markdown
				'title'		=> $title,		// 企业微信用不上
				'content'	=> implode("\n\n", $content)	// 一个\n无效
			),
			'target'	=> array(
				'user'	=> implode(',', $user)
			),
			'type'		=> $typeArr[$logs['method']]
		);
		$rest = Action('msgGatewayPlugin')->send($data);

		// 记录日志
		$logs['status'] = $rest['code'] ? 1 : 0;
		$logs['desc'] = _get($rest, 'data', '');
		$this->addUsersLog($user, $logs);
	}
	// 钉钉/企微 过滤非绑定用户——前面已经筛选过一次,这里没必要关联主表(user)查询
	private function filterThirdUsers(&$user) {
		$where = array(
			'userID' => array('in', $user),
			'key'	 => 'uid'
		);
		$data = Model('user_meta')->where($where)->field('userID,value')->select();
		$data = array_to_keyvalue($data, '', 'userID');
		$user = array_intersect($user, $data);
	}
	private function addUsersLog($user, $logs) {
		foreach ($user as $userID) {
			$logs['userID'] = $userID;
			$this->addLog($logs);
		}
	}

	/**
	 * 通过邮件发送通知
	 * @param [type] $user
	 * @param [type] $content	['','']
	 * @return void
	 */
	public function byEmail($user, $content, $logs){
		$title = $logs['title'];
		$content = array_merge(array($title), $content);
        $systemName = Model('SystemOption')->get('systemName');
		$data = array(
			'type'			=> 'email',
			'input'			=> $user['email'],  // 邮箱地址
			'action'		=> 'email_sys_notice',
			'config'		=> array(
				'address'	=> $user['email'],
				'subject'	=> "[{$systemName}]".$title,
				'content'	=> array(
					'type'  => 'notice', 
					'data'  => array(
						'user' => $user['name'],
						'text' => $content	// 数组
					)
				),
				'system'	=> array(	// 系统信息
					'icon'	=> STATIC_PATH.'images/icon/fav.png',
					'name'	=> $systemName,
					'desc'	=> Model('SystemOption')->get('systemDesc')
				),
			)
		);
		return $this->addTaskQueue($user, $data, $logs);
    }

	/**
	 * 通过短信发送通知——暂不支持
	 * @param [type] $user
	 * @param [type] $content	[msg]
	 * @return void
	 */
	public function bySms($user, $content, $logs) {
		// content为变量名(模板中固定),对应内容长度不能超过35个字符
		$content = array('content' => msubstr($content[0], 0, 35));
        $data = array(
			'type'			=> 'sms',
			'input'			=> $user['phone'],  // 手机号码
			'action'		=> 'phone_sys_notice',
			'config'		=> array(
				'content'	=> array(
					'type'	=> 'notice',		// code/notice
					'data'	=> $content,		// 变量不能超过35个字符
					'code'	=> 'xxxxxx',		// TODO 模板ID,待完善
				),
			)
		);
		return $this->addTaskQueue($user, $data, $logs);
    }


	/**
	 * 旧消息发送添加到任务队列
	 * @return void
	 */
	public function oldTaskQueue(){
		$key = $this->pluginName.'.msgQueue';
		$cache = Cache::get($key);
		if (!$cache) return;

		$call = $this->pluginName.'.sys.notice.byQueue';
		foreach ($cache as $i => $args) {
			$rest = TaskQueue::add($call, $args);
			if (!$rest) break;	// 添加失败直接终止
			unset($cache[$i]);
		}
		$cache = array_filter($cache);
		Cache::set($key, $cache, 3600*24);
	}

	/**
	 * 消息发送添加到任务队列
	 * @param [type] $data	消息发送参数
	 * @return void
	 */
	public function addTaskQueue($user, $data, $logs){
		$call = $this->pluginName.'.sys.notice.byQueue';
		$args = array($user, $data, $logs);
		$rest = TaskQueue::add($call, $args);
		if ($rest) return;
		// 添加失败,存入缓存,下次任务开始时读取然后继续添加
		$key = $this->pluginName.'.msgQueue';
		$cache = Cache::get($key);
		if (!$cache) $cache = array();
		$cache[] = $args;
		Cache::set($key, $cache, 3600*24);
	}

	/**
	 * 通过任务队列发送sms、email消息
	 * @param [type] $user	[name=>'',phone/email=>'']
	 * @param [type] $data	消息参数
	 * @param [type] $logs	日志参数
	 * @return void
	 */
	public function byQueue($user, $data, $logs){
		// 发送消息
		$func = $data['type'];	// sms、email,不直接使用send,避免再次检测格式
		$rest = Action('user.msg')->$func($data);

		// 写入日志
		$data = array(
			'status' => $rest['code'] ? 1 : 0,
			'desc'	 => _get($rest, 'data', ''),
		);
		$data = array_merge($logs, $data);
		$this->addLog($data);
		return;
	}

	/**
	 * 写入通知日志
	 * @param [type] $data
	 * @return void
	 */
	public function addLog($data) {
	    Action($this->pluginName)->loadLib('logs')->add($data);
	}

}
storage.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/plugins/msgWarning/controller/sys/storage.class.php'
View Content
<?php 
/**
 * 存储相关:通知任务检查、存储列表状态更新
 */
class msgWarningSysStorage extends Controller {
	protected $pluginName;
	protected $stCacheKey;
	public function __construct() {
		parent::__construct();
		$this->pluginName = 'msgWarningPlugin';
		$this->stCacheKey = 'io.error.list.get';
    }

	public function index() {
		// return array();
	}

	/**
	 * 检查存储列表,返回不可用存储
	 * @param boolean $task 是否为计划任务调用,如果是,则不使用缓存
	 * @return void
	 */
    public function checkStoreList ($task=false) {
        $cckey = $this->stCacheKey;
        $cache = Cache::get($cckey);
        if ($cache !== false && !$task) return $cache;

        // 获取有数据的存储
        $ids = Model('File')->field('ioType')->group('ioType')->select();
        $ids = array_to_keyvalue($ids, '', 'ioType');

        // 判断存储是否可访问
        $model = Model('Storage');
        $data = array();
		$list = $model->driverListSystem();
		foreach($list as $item) {
            $id = $item['id'];
			$driver = strtolower($item['driver']);
			if (in_array($driver, array('baidu','onedrive'))) continue;	// 百度、onedrive不检查

			// 1.检查(网络)存储是否可访问
			$chks = $this->checkStoreUrl($item);
			// 2.实际检查存储是否可用
            if ($chks === true) {
                try {
                    $chks = $model->checkConfig($item,true);
                } catch (Exception $e) {$chks = false;}
            }
			if ($chks !== true) {
                unset($item['config']);
                $item['sysdata'] = in_array($id, $ids) ? 1 : 0; // 是否含有系统数据
                $data[$id] = $item;
            }
		}
		// TODO 如果去掉taskFreq(频率跟随计划任务),则此处需要调整
        Cache::set($cckey, $data); // 5分钟——不限定5分钟,由计划任务刷新(存储检查事件默认为5分钟)
        return $data;
    }

	// 检查(网络)存储是否可访问
	public function checkStoreUrl($info, $timeout = 10) {
		$url = _get($info, 'config.domain', '');	// 对象存储
		if (empty($url)) {
			$url = _get($info, 'config.server', '');// ftp
		}
		if (empty($url)) {
			$url = _get($info, 'config.host', '');	// webdav
		}
		if (empty($url)) return true;
		$res  = parse_url($url);
		$port = (empty($res["port"]) || $res["port"] == '80')?'':':'.$res["port"];
		$path = preg_replace("/\/+/","/",$res["path"]);
		$opt  = array();
		// ftp额外处理:name/pass、默认端口
		if (strtolower($info['driver']) == 'ftp') {
			if (!$port) $port = ':21';
			$opt[CURLOPT_USERPWD] = $info['config']['username'].':'.$info['config']['userpass'];
		}
		$url  = _get($res,'scheme',http_type())."://".$res["host"].$port.$path;
		return $this->url_request_check($url, $opt, $timeout);
	}
	// 检查地址是否可访问
	private function url_request_check($url, $opt=false, $timeout = 10) {
		if (!filter_var($url, FILTER_VALIDATE_URL)) {return false;}
		$ch = curl_init($url);
		$op = array(
			CURLOPT_NOBODY 			=> true,	// 不获取响应体
			CURLOPT_FOLLOWLOCATION 	=> false,	// 不跟随重定向——3xx
			CURLOPT_TIMEOUT 		=> $timeout,// 超时时间
			CURLOPT_CONNECTTIMEOUT 	=> $timeout,// 连接超时时间
			CURLOPT_RETURNTRANSFER 	=> true,	// 返回响应体
			CURLOPT_SSL_VERIFYPEER 	=> false,	// 忽略 SSL 验证
			CURLOPT_HEADER 			=> true,	// 返回响应头
			CURLOPT_USERAGENT 		=> 'URL Accessibility Checker',
		);
		if ($opt) $op = $opt + $op;
		curl_setopt_array($ch, $op);
		$response = curl_exec($ch);
		$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
		// $error = curl_error($ch);
		curl_close($ch);
		// 非 0 状态码表示服务器已响应,说明地址可访问(包括4xx、5xx)
		return ($response !== false && $httpCode > 0);
	}

	/**
	 * 存储管理(修改、删除)之后,清除缓存
	 * @param [type] $result
	 * @return void
	 */
    public function editAfter($result) {
        if (!$result || !$result['code']) return;
		$id = $this->in['id'];
		if (!$id) return;
		$cache = Cache::get($this->stCacheKey);
		if (!$cache || !isset($cache[$id])) return;
		// 删除当前存储的状态缓存
		unset($cache[$id]);
		Cache::set($this->stCacheKey, $cache);
    }

	/**
	 * 存储列表,检查状态
	 * @param [type] $list
	 * @return void
	 */
    public function listAfter($list) {
        // if (!isset($this->in['usage'])) return;  // 因为缓存的关系,并非每次都有该请求
        $cache = Cache::get($this->stCacheKey);
        if (!$cache) return;	// TODO 没有的时候也许应该重新获取——但是注意耗时问题
        foreach ($list as &$item) {
            if (isset($cache[$item['id']])) {
                $item['status'] = 0;
            }
        }
        return $list;
    }

}
task.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/plugins/msgWarning/controller/sys/task.class.php'
View Content
<?php 
class msgWarningSysTask extends Controller {
	public function __construct() {
		parent::__construct();
		$this->pluginName = 'msgWarningPlugin';
    }
	public function getConfig() {
		return Action($this->pluginName)->getConfig();
	}

	// 更新同步计划任务
	public function updateTask($status){
		// 删除旧任务
		$this->delTask($this->pluginName.'.warning');

		// 任务不存在:状态为0,返回;否则新增
		if(!$task = $this->getTask()) {
			return $this->addTask($status);
		}
		// 任务已存在:更新
		$data = array(
			'name'	 => $task['name'],
			'enable' => $status
		);
		Model('SystemTask')->update($task['id'], $data);
	}
	private function addTask($status){
		$data = array (
			'name'	=> LNG('msgWarning.meta.name'),
			'type'	=> 'method',
			'event' => $this->pluginName.'.autoTask',
			'time'	=> '{"type":"minute","month":"1","week":"1","day":"08:00","minute":"1"}',
			'desc'	=> LNG('msgWarning.main.taskDesc'),
			'enable' => $status,
			'system' => 1,
		);
		return Model('SystemTask')->add($data);
	}
	// 获取计划任务,通过id查找有问题(卸载时可能没有删除,导致无法查找也无法新增)
	public function getTask($event=false){
		if (!$event) $event = $this->pluginName.'.autoTask';
		return Model('SystemTask')->findByKey('event', $event);
	}
	// 删除计划任务
	public function delTask($event=false){
		if(!$task = $this->getTask($event)) return;
		Model('SystemTask')->remove($task['id'], true);
	}


	/**
	 * 获取通知事件列表,执行通知
	 * 计划任务定期获取通知消息,有前端方式则写入缓存;后端则发送消息通知;
	 * 前端通知:前端只从缓存中获取消息,获取后清除自己(直到下次计划任务再次写入);前端请求同计划任务,每分钟请求一次
	 * @return void
	 */
	public function notice($runTask){
		if (!$runTask) $this->webNotice();
		$plugin = Action($this->pluginName);
		$ntcApi = $plugin->apiAct('sys', 'notice');	// 发送通知类

		// 旧数据处理——重新写入队列
		$ntcApi->oldTaskQueue();

		// 给通知事件列表分类
		$evntData = array();
		$evntList = $plugin->loadLib('evnt')->listData();
		foreach ($evntList as $evntInfo) {
			if ($evntInfo['status'] != '1') continue;
			$clsKey = $evntInfo['class'];
			if (!isset($evntData[$clsKey])) {
				$evntData[$clsKey] = array();
			}
			$evntData[$clsKey][] = $evntInfo;
		}
		unset($evntList);
		
		// 通知消息缓存,用于前端获取
		$wbcache = array();

		// TODO 前端提醒好像有点问题,存储异常3分钟提醒一次
		
		// 获取通知消息
		foreach ($evntData as $clsKey => $evntList) {
		    $msgAct = $plugin->apiAct('msg', $clsKey);	// 获取通知(内容)类
			foreach ($evntList as $evntInfo) {
				$event = $evntInfo['event'];
				// 每次计划任务执行时,清空前端缓存——不能清空,清空则消息只保留1分钟,1分钟内没通知到的(没登录),就接收不到此次通知了
				// $wbcache[$event] = array();

				// 1.通知对象过滤
				if (!$this->filterTarget($evntInfo)) continue;
				// 更新任务执行时间
				$this->updateNtcEvnt($evntInfo, 'tskTime');
				
				// 2.获取通知消息
				$message = $msgAct->index($evntInfo);
				$tempMsg = Hook::trigger('msgWarning.msg.'.$event, $evntInfo);	// 附加通知;eg: msgWarning.msg.sysStoreDefErr
				if($tempMsg && is_array($tempMsg)){
					$message = $tempMsg; 
				}
				if (!$message) continue;
				$evntInfo['message'] = $message;

				// 检查通知频率,判断是否需要通知
				$timeFreq = intval(_get($evntInfo, 'notice.timeFreq', 0));
				$timeLast = intval(_get($evntInfo, 'result.ntcTime', 0));
				if ($timeFreq < 1) $timeFreq = 1;	// 时间间隔至少1分钟
				if (($timeLast + ($timeFreq * 60)) > time()) continue;
				// 更新通知时间、次数
				$this->updateNtcEvnt($evntInfo, 'ntcTime');

				// 有前端通知方式,则写入缓存,供前端请求获取
				// 3.前端通知:写入缓存,前端用户只获取一次(清除自己的id);
				$methods = explode(',', $evntInfo['notice']['method']);
				$wbcache[$event] = $this->ntcCache($evntInfo, $methods);

				// 4.计划任务通知
				if (!array_intersect($methods, array('sms', 'email', 'weixin', 'dding', 'feishu'))) {
					continue;
				}
				// 4.1 预警级+ 写入log日志
				if ($evntInfo['level'] >= 3) {
					write_log('【系统通知】'.$evntInfo['title'].': '.implode('; ', $message), 'warning');
				}

				// 4.2 发送通知
				$ntcApi->send($evntInfo);
			}
		}
		// 存前端缓存——未被覆盖的继续保留,用户23点登录系统接收到的可能是8点的通知
		$cckey = $this->pluginName.'.webNtcList.'.date('Ymd');
		$cache = Cache::get($cckey);
		if (!is_array($cache)) $cache = array();
		$cache = array_merge($cache, $wbcache);
		Cache::set($cckey, $cache, 3600*24);
		return true;
	}

	// 通知事件过滤
	public function filterTarget(&$evntInfo){
		// 是否启用
		if (!$evntInfo['status']) return false;

		$notice = _get($evntInfo, 'notice', array());	// 通知设置
		$result = _get($evntInfo, 'result', array());	// 通知结果
		$target = json_decode($notice['target'], true);
		if (empty($target)) $target = array();

		// 通知次数
		$cntMax		= intval(_get($notice, 'cntMax', 0));
		$cntMaxDay	= intval(_get($notice, 'cntMaxDay', 0));
		if ($cntMax > 0 && intval($result['cntTotal']) >= $cntMax) return false;
		if ($cntMaxDay > 0 && intval($result['cntToday']) >= $cntMaxDay) return false;

		// 通知时段
		$timeNow = time();
		if (strtotime($notice['timeFrom']) > $timeNow || strtotime($notice['timeTo']) < $timeNow) return false;

		// 任务执行频率——注意这里不是通知频率:事件任务执行后,如果有消息,再根据通知频率判断是否通知
		$taskFreq = intval(_get($evntInfo, 'taskFreq', 0));
		$timeLast = intval(_get($result, 'tskTime', 0));
		// 频率为1分钟的不用检查,避免和计划任务冲突
		if ($taskFreq > 1) {
			if (($timeLast + ($taskFreq * 60)) > $timeNow) return false;
		}

		// 通知方式
		if (empty($notice['method'])) return false;
		// toAll:将[系统通知]添加到通知方式中——前端限制取消后,此处没有必要强制添加
		if ($evntInfo['toAll'] == '1') {
			if (stripos($notice['method'], 'kwarn') === false) {
				$evntInfo['notice']['method'] .= ',kwarn';
			}
		}

		// 通知对象
		if (empty($target['user']) && empty($target['group'])) return false;
		$users = $this->getTargetUsers($target);
		if (empty($users)) return false;

		$evntInfo['target'] = $users;	// 实际通知对象,用于邮件短信;企微和钉钉需传原始值通知
		return true;
	}

	// 通过form.userGroup获取用户id列表:{user:[1,2,3],group:[1,2,3]}
	private function getTargetUsers($target) {
		// 缓存不同条件下的部门用户
		static $groupUsers = array();

		$users = _get($target, 'user', array());
		$group = _get($target, 'group', array());
		if (empty($group)) return array_filter(array_unique($users));
		
		// 获取部门下的用户:有根部门则为全部用户
		$list = array();
		sort($group);
		if (in_array('1', $group)) {
			if (isset($groupUsers['1'])) {
				$list = $groupUsers['1'];
			} else {
				$list = Model('User')->where(array('status' => 1))->field('userID')->select();
				$list = array_to_keyvalue($list, '', 'userID');
				$groupUsers['1'] = $list;
			}
		} else {
			$tmpKey = implode(',', $group);
			if (isset($groupUsers[$tmpKey])) {
				$list = $groupUsers[$tmpKey];
			} else {
				$list = Model('user_group')->alias('g')->field('u.userID')
						->join('INNER JOIN user as u ON u.userID = g.userID AND u.status = 1')
						->where(array('g.groupID' => array('in', $group)))
						->select();
				$list = array_to_keyvalue($list, '', 'userID');
				$groupUsers[$tmpKey] = $list;
			}
		}
		$users = array_merge($users, $list);

		if (empty($users)) return array();
		return array_filter(array_unique($users));
	}

	// 获取用于缓存的数据:{ktips:1,2,3,kwarn:1,2,3,msg:{title,level,message}}
	private function ntcCache($evntInfo, $methods) {
		$cache = array();
		$users = implode(',', $evntInfo['target']);
		if (in_array('ktips', $methods)) {
			$cache['ktips'] = $users;
		}
		if (in_array('kwarn', $methods)) {
			if ($evntInfo['toAll'] == '1') {
				$users = 'all';	// 前端获取时,将当前用户id追加到后面,后续再获取时,如果已在其中则不提示
			}
			$cache['kwarn'] = $users;
		}
		if (!empty($cache)) {
			$cache['msg'] = array(
				'title'		=> $evntInfo['title'],
				'level'		=> intval($evntInfo['level']),
				'message' 	=> $evntInfo['message'],
			);
		}
		return $cache;
	}

	// 更新任务执行时间
	// TODO 应该写一个批量更新的方法
	private function updateNtcEvnt(&$evntInfo, $type) {
		$time = time();
		$evntInfo['result']['tskTime'] = $time;
		if ($type == 'ntcTime') {
			$evntInfo['result']['ntcTime'] = $time;
			$evntInfo['result']['cntToday'] = intval(_get($evntInfo, 'result.cntToday', 0)) + 1;
			$evntInfo['result']['cntTotal'] = intval(_get($evntInfo, 'result.cntTotal', 0)) + 1;
		}
		$update = array(
			'event' => $evntInfo['event'],
			'data'	=> array (
				'result' => $evntInfo['result'],
			)
		);
		Action($this->pluginName)->loadLib('evnt')->setConfig($update, true);
	}


	/**
	 * 前端通知
	 * @return void [ktips=>[event=>[title,level,message],...],kwarn=>[event=>[title,level,message],...]]
	 */
	public function webNotice() {
		if(!defined('USER_ID') || !USER_ID) show_json(array());
		$cckey = $this->pluginName.'.webNtcList.'.date('Ymd');
		$cache = Cache::get($cckey);
		if (empty($cache)) show_json(array());

		$data = array();
		foreach ($cache as $event => &$item) {
			if (!$item) continue;
			// kwarn通知范围为all时,获取并追加自己的id,后续再获取时,如果已存在则忽略
			if (stripos($item['kwarn'], 'all') !== false) {
				$users = explode(',', $item['kwarn']);
				if (!in_array(USER_ID, $users)) {
					$data['kwarn'][$event] = $item['msg'];
					$item['kwarn'] .= ','.USER_ID;
				}
				continue;
			}
			foreach (array('ktips', 'kwarn') as $key) {
				$users = explode(',', $item[$key]);
				if (in_array(USER_ID, $users)) {
					$data[$key][$event] = $item['msg'];
					$users = array_diff($users, array(USER_ID));
					$item[$key] = implode(',', $users);
				}
			}
		}
		unset($item);
		if (empty($data)) show_json(array());

		// 更新缓存
		Cache::set($cckey, $cache, 3600*24);
		show_json($data);
	}

}