TaskReminderController.class.php 11.8 KB
<?php
/**
 * Created by PhpStorm.
 * 自动提醒 未完成任务人员
 * User: zhoutao
 * Date: 2017/12/2
 * Time: 下午2:53
 */

namespace Frontend\Controller\Crontab;

use Common\Model\CustomtaskModel;
use Common\Model\CustomtaskRightModel;
use Common\Model\UserTaskModel;
use Common\Service\CustomtaskRightService;
use Common\Service\CustomtaskService;
use Common\Service\UserTaskService;
use Think\Log;
use VcySDK\Member;
use VcySDK\Message;
use VcySDK\Service;

/**
 * Class TaskReminderController
 * @property CustomtaskService $customtaskServ
 * @property CustomtaskRightService $customtaskRightServ
 * @property Message $messageSdk
 * @property UserTaskService $userTaskServ
 * @property Member $userServ
 */
class TaskReminderController extends AbstractController
{
    /**
     * 任务翻页 单次处理个数
     */
    const DEAL_TASK_PAGE_LIMIT = 5;

    /**
     * Crontab 执行时间区间 (单位: 分钟)
     */
    const CRONTAB_CYCLE_TIMES = 5;

    /**
     * 常规任务权限表: 全公司
     */
    const RIGHT_ALL = 1;

    /**
     * 人员处理单次处理个数
     */
    const DEAL_MEMBER_LIMIT = 500;

    /**
     * customtaskServ
     * @var null
     */
    protected $customtaskServ = null;

    /**
     * $customtaskRightServ
     * @var null
     */
    protected $customtaskRightServ = null;

    /**
     * 消息
     * @var null
     */
    protected $messageSdk = null;

    /**
     * 用户常规任务进度表
     * @var null
     */
    protected $userTaskServ = null;
    /**
     * 用户
     * @var null
     */
    protected $userServ = null;

    public function index()
    {
        set_time_limit(0);

        \Think\Log::record('开始自动提醒' . "\n");

        $this->customtaskServ = new CustomtaskService();
        $this->customtaskRightServ = new CustomtaskRightService();
        $this->userTaskServ = new UserTaskService();

        // 查询在 任务时间范围内 的任务
        $taskTotal = $this->customtaskServ->countReminder([
            // 在任务时间范围内
            'start_time <= ?' => MILLI_TIME,
            'end_time >= ?' => MILLI_TIME,
            // 任务进行中
            'task_status' => CustomtaskModel::TASK_STATUS_ING,
            // 开启了定时提醒
            'is_auto_remind' => CustomtaskModel::IS_AUTO_REMIND_TRUE
        ]);
        \Think\Log::record("\n共有 {$taskTotal} 任务\n", Log::INFO);

        // 翻页处理 防止单次查询出来过多 内存溢出
        $time = ceil($taskTotal / self::DEAL_TASK_PAGE_LIMIT);
        for ($page = 1; $page <= $time; $page ++) {
            \Think\Log::record("\n处理第 {$page}\n", Log::INFO);

            try {
                $this->dealPageTask($page);
            } catch (\Exception $e) {
                \Think\Log::record("\n处理第 {$page} 页时发生错误\n" . $e->getMessage());
            }

            \Think\Log::record("\n结束 处理第 {$page}\n", Log::INFO);
        }

        return true;
    }

    /**
     * 批量处理任务提醒
     * @param $page
     * @return bool
     */
    private function dealPageTask($page)
    {
        // 查询任务
        list($page, $limit) = page_limit($page, self::DEAL_TASK_PAGE_LIMIT);
        $taskList = $this->customtaskServ->listReminder([
                // 在任务时间范围内
                'start_time <= ?' => MILLI_TIME,
                'end_time >= ?' => MILLI_TIME,
                // 任务进行中
                'task_status' => CustomtaskModel::TASK_STATUS_ING,
                // 开启了定时提醒
                'is_auto_remind' => CustomtaskModel::IS_AUTO_REMIND_TRUE
            ], [$page, $limit], [],
                // 取用到的字段
                implode(
                    ',',
                    [
                        'customtask_id',
                        'interval_time',
                        'reminder_time',
                        'start_time',
                        'end_time',
                        'domain',
                        'task_name',
                    ]
                )
            );

        // 遍历每个任务
        foreach ($taskList as $item) {
            // 提醒周期时间长度
            $intervalTime = $item['interval_time'] * 3600 * 1000;
            // 提醒时间点
            $reminderTime =
                // 如果没有最近一次提醒时间 那就从任务开始时间加上周期时间 (小时 -> 毫秒)
                (empty($item['reminder_time']) ? $item['start_time'] : $item['reminder_time'])
                + $intervalTime;
            // 如果当前时间 和 上一次提醒时间 相差超过一个提醒周期
            if (MILLI_TIME > $reminderTime) {
                // 把周期时间补上
                $reminderTime += ceil((MILLI_TIME - $reminderTime) / ($intervalTime)) * $intervalTime;
            }

            // 在 Crontab 执行时间区间 (当前时间 到 当前时间加上 Crontab 执行区间时间 为范围)
            $inReminderRange = ($reminderTime >= MILLI_TIME) &&
                // (分钟 -> 毫秒)
                ($reminderTime <= (MILLI_TIME + self::CRONTAB_CYCLE_TIMES * 60 * 1000));
            // 提醒时间点不在 此次提醒点
            if (!$inReminderRange) {
                continue;
            }

            // 初始化 SDK
            $config = array(
                'apiUrl' => cfg('UC_APIURL'),
                'enumber' => $item['domain'],
                'pluginIdentifier' => 'yuanquan',
                'thirdIdentifier' => cfg('SDK_THIRD_IDENTIFIER'),
                'logPath' => RUNTIME_PATH . '/Logs/VcySDK/',
                'apiSecret' => cfg('API_SECRET'),
                'apiSigExpire' => cfg('API_SIG_EXPIRE'),
                'fileConvertApiUrl' => cfg('FILE_CONVERT_API_URL')
            );
            $service = &Service::instance();
            $service->initSdk($config);
            $this->messageSdk = new Message($service);
            $this->userServ = new Member($service);

            // 处理单个任务提醒
            try {
                $this->dealSingleTask($item);
            } catch (\Exception $e) {
                \Think\Log::record("\n处理ID: {$item['customtask_id']} 任务提醒时发生错误\n" . $e->getMessage());
            }

            // 更新提醒时间
            $this->customtaskServ->updateWithOutDomain($item['customtask_id'], ['reminder_time' => $reminderTime]);
        }

        return true;
    }

    /**
     * 处理单个任务提醒
     * @param $task
     * @return bool
     */
    private function dealSingleTask($task)
    {
        // 格式化发送的信息
        if (mb_strlen($task['task_name']) > 20) {
            $task['task_name'] = mb_substr($task['task_name'], 0, 20) . '...';
        }
        $task['start_time'] = rgmdate($task['start_time'], 'Y-m-d H:i');
        $task['end_time'] = rgmdate($task['end_time'], 'Y-m-d H:i');
        $msg = [
            'articles' => [[
                'title' => cfg('NOTICE_PREFIX', null, '') . '您还有一个任务未完成,赶紧处理下吧',
                'description' => "任务名称:{$task['task_name']}\n任务时间:{$task['start_time']}~{$task['end_time']}",
                'url' => oaUrl(
                    'Frontend/Index/Detail/Index',
                    ['customtask_id' => $task['customtask_id']],
                    $task['domain']
                ),
            ]]
        ];

        // 权限对象
        $rightList = $this->customtaskRightServ->listWithOutDomian(['customtask_id' => $task['customtask_id']]);
        // 完成人员
        $completeList = $this->userTaskServ->listWithOutDomian([
            'customtask_id' => $task['customtask_id'],
            'complete_status' => UserTaskModel::COMPLETE_STATUS_OVER
        ], [], [], 'uid');
        $completeList = empty($completeList) ? [] : array_column($completeList, 'uid');

        // 如果全公司
        if (in_array(self::RIGHT_ALL, array_column($rightList, 'obj_type'))) {
            // 如果有完成的人员
            if (!empty($completeList)) {
                // 则过滤
                $userResult = $this->userServ->listAll([], 1, self::DEAL_MEMBER_LIMIT);
                $userList = array_column($userResult['list'], 'memUid');
                // 去掉已经完成的人员
                $msg['toUser'] = array_diff($userList, $completeList);
                $this->sendNews($msg);
                // 继续剩下的页数
                $times = ceil($userResult['total'] / self::DEAL_MEMBER_LIMIT);
                for ($i = 2; $i <= $times; $i ++) {
                    $userResult = $this->userServ->listAll([], $i, self::DEAL_MEMBER_LIMIT);
                    $userList = array_column($userResult['list'], 'memUid');
                    // 去掉已经完成的人员
                    $msg['toUser'] = array_diff($userList, $completeList);
                    $this->sendNews($msg);
                }
            } else {
                // 没有就发送
                $msg['toUser'] = '@all';
                $this->sendNews($msg);
            }
            return true;
        } else {
            $targeList = [];
            // 目标
            $targeConds = [];
            foreach ($rightList as $item) {
                switch ($item['obj_type']) {
                    // 部门
                    case CustomtaskRightModel::OBJ_TYPE_DEP:
                        $targeConds['dpIdList'][] = $item['obj_id'];
                        // 获取子部门
                        $targeConds['departmentChildrenFlag'] = 1;
                        break;
                    // 人员
                    case CustomtaskRightModel::OBJ_TYPE_MEMBER:
                        $targeConds['memUids'][] = $item['obj_id'];
                        break;
                    // 职位
                    case CustomtaskRightModel::OBJ_TYPE_JOB:
                        $targeConds['jobIdList'][] = $item['obj_id'];
                        break;
                    // 角色
                    case CustomtaskRightModel::OBJ_TYPE_ROLE:
                        $targeConds['roleIdList'][] = $item['obj_id'];
                        break;
                    default:
                        break;
                }
            }
            if (empty($targeConds)) {
                return true;
            }

            // 获取人员列表 (第一页查询)
            $userResult = $this->userServ->listAll($targeConds, 1, self::DEAL_MEMBER_LIMIT);
            $userList = array_column($userResult['list'], 'memUid');
            // 合并上 设置里的 人员
            $userList = array_merge($userList, $targeList);
            // 去掉已经完成的人员
            $msg['toUser'] = array_diff($userList, $completeList);
            $this->sendNews($msg);

            // 发完了
            if ($userResult['total'] <= self::DEAL_MEMBER_LIMIT) {
                return true;
            }

            // 已经发送过的人员
            $completeList = array_merge($completeList, $targeList);
            // 继续剩下的页数
            $times = ceil($userResult['total'] / self::DEAL_MEMBER_LIMIT);
            for ($i = 2; $i <= $times; $i ++) {
                $userResult = $this->userServ->listAll($targeConds, $i, self::DEAL_MEMBER_LIMIT);
                $userList = array_column($userResult['list'], 'memUid');
                // 去掉已经完成的人员
                $msg['toUser'] = array_diff($userList, $completeList);
                $this->sendNews($msg);
            }
        }

        return true;
    }

    /**
     * 发送消息
     * @param $msg
     * @return bool
     */
    private function sendNews($msg)
    {
        $msg['toUser'] = implode('|', $msg['toUser']);

        try {
            $this->messageSdk->sendNews($msg);
        } catch (\Exception $e) {
            \Think\Log::record('发送消息出错:::' . $e->getMessage());
        }

        return true;
    }
}