| 
 | 
 | 
 <?php
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 //error_reporting();
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 require_once "../vendor/autoload.php";
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 /**
 | 
| 
 | 
 | 
  * 把mysql的数据同步到es
 | 
| 
 | 
 | 
  * @author:dc
 | 
| 
 | 
 | 
  * @time 2025/3/4 10:19
 | 
| 
 | 
 | 
  * Class SyncToEsCmd
 | 
| 
 | 
 | 
  */
 | 
| 
 | 
 | 
 class SyncToEsCmd {
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     public $isStop = false;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * 文件夹
 | 
| 
 | 
 | 
      * @var array
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public $folders  = [];
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * @var \Lib\Db
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public $fob_db;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * @var \Lib\Db
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public $db;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     public $startTime = 0;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * SyncToEsCmd constructor.
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public function __construct()
 | 
| 
 | 
 | 
     {
 | 
| 
 | 
 | 
         $this->db = db();
 | 
| 
 | 
 | 
         $this->fob_db = fob_mysql();
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $handler = function ($signal){
 | 
| 
 | 
 | 
             _echo('收到进程信号 '. $signal);
 | 
| 
 | 
 | 
             // 可以处理其他程序
 | 
| 
 | 
 | 
             $this->isStop = true;
 | 
| 
 | 
 | 
         };
 | 
| 
 | 
 | 
         pcntl_signal(SIGTERM, $handler); // 这个是kill
 | 
| 
 | 
 | 
         pcntl_signal(SIGINT, $handler); // 这个是 ctrl+c
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $this->startTime = time();
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * 是否是自动回复
 | 
| 
 | 
 | 
      * @author:dc
 | 
| 
 | 
 | 
      * @time 2025/6/6 11:07
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public function isAuto($data){
 | 
| 
 | 
 | 
         // 没有发件人的直接标记
 | 
| 
 | 
 | 
         if(!$data['from']){
 | 
| 
 | 
 | 
             return 1;
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $is_auto = $this->db->count('select count(*) from `lists_auto` where `list_id` = '.$data['id']) ? 1 : 0;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         if($is_auto) return 1;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         return isAiAutoMail($data['from'],$data['subject'],$data['body']??'') === 1 ? 1 : 0;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * @return bool
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public function isStop(): bool
 | 
| 
 | 
 | 
     {
 | 
| 
 | 
 | 
         // 检查是否接收到信号
 | 
| 
 | 
 | 
         pcntl_signal_dispatch();
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         // 是否超过来最大执行时间
 | 
| 
 | 
 | 
         if(time()-43200 > $this->startTime){
 | 
| 
 | 
 | 
             return true;
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         return $this->isStop;
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * es链接
 | 
| 
 | 
 | 
      * @var \Lib\Es\Es
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public $es;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * @var \Lib\Es\BulkData
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     protected $bulkData;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     public function handler($id){
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $this->es = es('email_lists_copy'); // 第二个库 新
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $this->bulkData = new \Lib\Es\BulkData();
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $this->bulkData($id);
 | 
| 
 | 
 | 
         $this->toDataEs(true);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * 批量处理数据并存储到ES
 | 
| 
 | 
 | 
      *
 | 
| 
 | 
 | 
      * @param string $id 数据ID,如果包含点号则只取点号前的部分
 | 
| 
 | 
 | 
      * @return void
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public function bulkData($id){
 | 
| 
 | 
 | 
         $is_check_body = false;
 | 
| 
 | 
 | 
         if(str_contains($id, '.')){
 | 
| 
 | 
 | 
             $id = explode('.',$id)[0];
 | 
| 
 | 
 | 
             $is_check_body = true;
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $data = $this->getDataByEs($id,$is_check_body);
 | 
| 
 | 
 | 
         if($data){
 | 
| 
 | 
 | 
             list($doc_id,$data) = $data;
 | 
| 
 | 
 | 
             // 主库
 | 
| 
 | 
 | 
             $this->bulkData->add('email_lists_copy',$doc_id,$data);
 | 
| 
 | 
 | 
             if($data['postid']){
 | 
| 
 | 
 | 
                 // 分库
 | 
| 
 | 
 | 
                 $this->bulkData->add('email_lists_branch_'.$data['postid'],$doc_id,$data);
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             // 个人邮箱的情况
 | 
| 
 | 
 | 
             $postids = $this->getPostids($data['email_id']);
 | 
| 
 | 
 | 
             print_r($postids);
 | 
| 
 | 
 | 
             if($postids){
 | 
| 
 | 
 | 
                 foreach ($postids as $postid){
 | 
| 
 | 
 | 
                     $data['postid'] = $postid;
 | 
| 
 | 
 | 
                     $data['source'] = 1;
 | 
| 
 | 
 | 
                     // 分库 个人邮箱
 | 
| 
 | 
 | 
                     $this->bulkData ->add('email_lists_branch_'.$postid,$doc_id,$data);
 | 
| 
 | 
 | 
                 }
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
             // 其他非fob邮件数据
 | 
| 
 | 
 | 
             if (!$data['postid']){
 | 
| 
 | 
 | 
                 // 分库 其他 非fob数据源
 | 
| 
 | 
 | 
                 $this->bulkData->add('email_lists_branch_'.$data['postid'],$doc_id,$data);
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * 个人邮箱情况
 | 
| 
 | 
 | 
      * @param $email_id
 | 
| 
 | 
 | 
      * @author:dc
 | 
| 
 | 
 | 
      * @time 2025/8/5 14:53
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     private function getPostids($email_id){
 | 
| 
 | 
 | 
         $postids = $this->fob_db->throw()->cache(1800)->all("select `post_id` from `e_mail_binds` where `source` = 1 and `email_id` = {$email_id} and `deleted_at` is null");
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         if($postids){
 | 
| 
 | 
 | 
             return array_column($postids,'post_id');
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         return [];
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * @param $id
 | 
| 
 | 
 | 
      * @param $is_check_body
 | 
| 
 | 
 | 
      * @return array|false
 | 
| 
 | 
 | 
      * @author:dc
 | 
| 
 | 
 | 
      * @time 2025/8/5 10:21
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public function getDataByEs($id,$is_check_body) {
 | 
| 
 | 
 | 
         try {
 | 
| 
 | 
 | 
             $data = $this->db->throw()->first(\Model\listsSql::first('`id` = '.$id));
 | 
| 
 | 
 | 
             if(!$data){
 | 
| 
 | 
 | 
                 $data = $this->db->throw()->first(\Model\listsSql::firstHot('`id` = '.$id));
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
         }catch (Throwable $e){
 | 
| 
 | 
 | 
             $this->log([$id]);
 | 
| 
 | 
 | 
 //            redis()->rPush('sync_to_es',$origin_id);
 | 
| 
 | 
 | 
             _echo('sync to es '.$id.":".$e->getMessage());
 | 
| 
 | 
 | 
             return false;
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         if($data){
 | 
| 
 | 
 | 
             try {
 | 
| 
 | 
 | 
                 // 文件夹
 | 
| 
 | 
 | 
                 if(empty($this->folders[$data['folder_id']])){
 | 
| 
 | 
 | 
                     $this->folders[$data['folder_id']] = $this->db->throw()->value(\Model\folderSql::first($data['folder_id'],'folder'));
 | 
| 
 | 
 | 
                 }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
                 // 为文件夹打标 方便查询
 | 
| 
 | 
 | 
                 $data['folder_as_int'] = folder2int($this->folders[$data['folder_id']]);
 | 
| 
 | 
 | 
                 $data['is_auto'] = 0;
 | 
| 
 | 
 | 
                 // 是否是自动回复
 | 
| 
 | 
 | 
                 if($data['folder_as_int'] === 1){
 | 
| 
 | 
 | 
                     // 是否检查body
 | 
| 
 | 
 | 
                     if($is_check_body){
 | 
| 
 | 
 | 
                         $body = getMailBody($data['id'],$this->db);
 | 
| 
 | 
 | 
                         if($body){
 | 
| 
 | 
 | 
                             $data['body'] = getBodyHtml($body);
 | 
| 
 | 
 | 
                         }
 | 
| 
 | 
 | 
                     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
                     $data['is_auto'] = $this->isAuto($data);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
                     unset($data['body']);
 | 
| 
 | 
 | 
                 }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
                 // postid ai邮箱要用 这个是查询黑格
 | 
| 
 | 
 | 
                 list($data['postid'],$data['source']) = $this->getPostid($data['email_id'],$data['udate']);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             }catch (Throwable $e){
 | 
| 
 | 
 | 
                 $this->log([$id]);
 | 
| 
 | 
 | 
 //                redis()->rPush('sync_to_es',$origin_id);
 | 
| 
 | 
 | 
                 _echo('sync to es '.$id.":".$e->getMessage());
 | 
| 
 | 
 | 
                 return false;
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             $data = $this->getEsData($data);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             $doc_id = $data['email_id'].'_'.$data['folder_id'].'_'.$data['uid'];
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             return [$doc_id,$data];
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
         return false;
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     public $checkEsIndex = [];
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     public function setEsMap($index){
 | 
| 
 | 
 | 
         $this->es->setIndex($index);
 | 
| 
 | 
 | 
         if($this->es->getMapping()){
 | 
| 
 | 
 | 
            return 9;
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         if(redis()->add('setmaplock:'.$index,1,20)){
 | 
| 
 | 
 | 
             return $this->es->putMapping([
 | 
| 
 | 
 | 
                 'properties' => [
 | 
| 
 | 
 | 
                     'subject' => ['type' => 'text'],
 | 
| 
 | 
 | 
                     'from' => [
 | 
| 
 | 
 | 
                         'type' => 'object', // 定义 from 字段为对象
 | 
| 
 | 
 | 
                         'properties' => [
 | 
| 
 | 
 | 
                             'name' => [
 | 
| 
 | 
 | 
                                 'type' => 'keyword' // 或者 'keyword',根据需求选择
 | 
| 
 | 
 | 
                             ],
 | 
| 
 | 
 | 
                             'email' => [
 | 
| 
 | 
 | 
                                 'type' => 'text' // email 通常使用 keyword 类型
 | 
| 
 | 
 | 
                             ]
 | 
| 
 | 
 | 
                         ]
 | 
| 
 | 
 | 
                     ],
 | 
| 
 | 
 | 
                     'to' => ['type' => 'text'],
 | 
| 
 | 
 | 
                     'cc' => ['type' => 'keyword'],
 | 
| 
 | 
 | 
                     'bcc' => ['type' => 'keyword'],
 | 
| 
 | 
 | 
                     'uid' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'udate' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'folder_id' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'email_id' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'size' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'recent' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'flagged' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'deleted' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'seen' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'draft' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'is_file' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'is_hots' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'is_auto' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'folder_as_int' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'postid' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'source' => ['type' => 'integer'],
 | 
| 
 | 
 | 
                     'created_at' => ['type' => 'date'],
 | 
| 
 | 
 | 
                     'updated_at' => ['type' => 'date'],
 | 
| 
 | 
 | 
                     'description' => ['type' => 'keyword'],
 | 
| 
 | 
 | 
                     'references' => ['type' => 'keyword']
 | 
| 
 | 
 | 
                 ]
 | 
| 
 | 
 | 
             ],$index == 'email_lists_copy' ? [
 | 
| 
 | 
 | 
                 'number_of_shards' => 21, // 设置分片数
 | 
| 
 | 
 | 
                 'number_of_replicas' => 1, // 设置副本数 暂用内存 主片+副片*
 | 
| 
 | 
 | 
             ]:[
 | 
| 
 | 
 | 
                 'number_of_shards' => 1, // 设置分片数
 | 
| 
 | 
 | 
                 'number_of_replicas' => 0, // 设置副本数 暂用内存 主片+副片*
 | 
| 
 | 
 | 
             ]
 | 
| 
 | 
 | 
             );
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
         // 暂停1秒在试
 | 
| 
 | 
 | 
         sleep(1);
 | 
| 
 | 
 | 
         return $this->setEsMap($index);
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * @var int
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     protected $pervSubmitTime = 0;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * 同步数据到es
 | 
| 
 | 
 | 
      * @param bool $nowSubmit 是否立即提交
 | 
| 
 | 
 | 
      * @return bool
 | 
| 
 | 
 | 
      * @throws \Elastic\Elasticsearch\Exception\ClientResponseException
 | 
| 
 | 
 | 
      * @throws \Elastic\Elasticsearch\Exception\ServerResponseException
 | 
| 
 | 
 | 
      * @author:dc
 | 
| 
 | 
 | 
      * @time 2025/8/7 10:29
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public function toDataEs(bool $nowSubmit){
 | 
| 
 | 
 | 
         // 不立即提交
 | 
| 
 | 
 | 
         if (!$nowSubmit){
 | 
| 
 | 
 | 
             if($this->bulkData->total() < 20){
 | 
| 
 | 
 | 
                 // 不足20条时 满足2秒也提交
 | 
| 
 | 
 | 
                 if ($this->pervSubmitTime + 2 > time()){
 | 
| 
 | 
 | 
                     return true;
 | 
| 
 | 
 | 
                 }
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
         // 为空不提交
 | 
| 
 | 
 | 
         if($this->bulkData->isEmpty()){
 | 
| 
 | 
 | 
             return true;
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         // 上一次提交的时间
 | 
| 
 | 
 | 
         $this->pervSubmitTime = time();
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         foreach ($this->bulkData->getIndexs() as $index){
 | 
| 
 | 
 | 
             $this->es->setIndex($index);
 | 
| 
 | 
 | 
             // 检查数据库是否存在
 | 
| 
 | 
 | 
             if(empty($this->checkEsIndex[$index]) && $index != 'email_lists_copy'){
 | 
| 
 | 
 | 
                 if(!redis()->has('esmapcheck:'.$index)){
 | 
| 
 | 
 | 
                     $m = $this->setEsMap($index);
 | 
| 
 | 
 | 
                     if($m !== 9) _echo("{$index} 创建索引 ".$m);
 | 
| 
 | 
 | 
                     redis()->set('esmapcheck:'.$index,1,86400);
 | 
| 
 | 
 | 
                 }
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
             // 下次不在检查
 | 
| 
 | 
 | 
             $this->checkEsIndex[$index] = 1;
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
         // 批量提交数据的
 | 
| 
 | 
 | 
         $ret = $this->es->bulk($this->bulkData);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         if(!empty($ret['errors'])){
 | 
| 
 | 
 | 
             @file_put_contents(LOG_PATH.'/sync_es_fail.error.log',print_r($ret['errors'],1)."\n",FILE_APPEND|LOCK_EX);
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
         // 清空
 | 
| 
 | 
 | 
         $this->bulkData->clear();
 | 
| 
 | 
 | 
         // 为空表示提交成功
 | 
| 
 | 
 | 
         return empty($ret['errors']);
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * 记录日志
 | 
| 
 | 
 | 
      * @param array $ids
 | 
| 
 | 
 | 
      * @param string $index
 | 
| 
 | 
 | 
      * @author:dc
 | 
| 
 | 
 | 
      * @time 2025/8/5 10:17
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public function log(array $ids){
 | 
| 
 | 
 | 
         file_put_contents(LOG_PATH.'/sync_es_fail.log',implode("\n",$ids)."\n",FILE_APPEND|LOCK_EX);
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * 项目id
 | 
| 
 | 
 | 
      * @author:dc
 | 
| 
 | 
 | 
      * @time 2025/5/20 15:44
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public function getPostid($email_id,$udate){
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         //每60秒验证一次
 | 
| 
 | 
 | 
         if(redis()->add('fob_bind_mail_times_check:'.$email_id,1,60)){
 | 
| 
 | 
 | 
             $lastpostid = $this->fob_db->throw()->value("select `post_id` from `e_mail_binds_log` where `source` = 2 and `email_id` = '{$email_id}' order by `id` desc limit 1");
 | 
| 
 | 
 | 
             $thelast = $this->fob_db->throw()->first("select * from `e_mail_binds` where `source` = 2 and `email_id` = '{$email_id}' and `deleted_at` is null order by `id` desc limit 1");
 | 
| 
 | 
 | 
             if(!$thelast){
 | 
| 
 | 
 | 
                 return [0,0];
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             $thelastpostid = $thelast['post_id'];
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             // 说明变了
 | 
| 
 | 
 | 
             if($lastpostid!=$thelastpostid){
 | 
| 
 | 
 | 
                 $ret = $this->fob_db->throw()->insert('e_mail_binds_log',[
 | 
| 
 | 
 | 
                     'post_id'   =>  $thelastpostid,
 | 
| 
 | 
 | 
                     'bind_time'   =>  date('Y-m-d H:i:s',time()-2),
 | 
| 
 | 
 | 
                     'source'   =>  $thelast['source'],
 | 
| 
 | 
 | 
                     'email'   =>  $thelast['email'],
 | 
| 
 | 
 | 
                     'email_id'   =>  $thelast['email_id'],
 | 
| 
 | 
 | 
                 ],false);
 | 
| 
 | 
 | 
                 _echo("邮箱异常分配 ".$email_id.' -- '.$lastpostid.' == '.$thelastpostid.' === '.$ret);
 | 
| 
 | 
 | 
                 if(!$ret){
 | 
| 
 | 
 | 
                     throw new Exception('新增失败');
 | 
| 
 | 
 | 
                 }
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         // 查询历史记录
 | 
| 
 | 
 | 
         $times = redis()->getSet('fob_bind_mail_times3:'.$email_id,300,function ($email_id){
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             $times = $this->fob_db->throw()->all("select `post_id`,`bind_time`,`source` from `e_mail_binds_log` where `source` = 2 and `email_id` = {$email_id} order by `bind_time` desc ");
 | 
| 
 | 
 | 
             if(!$times){
 | 
| 
 | 
 | 
                 return [];
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             return $times;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         },$email_id);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         if(is_array($times) && $times){
 | 
| 
 | 
 | 
             foreach ($times as $time){
 | 
| 
 | 
 | 
                 $t = strtotime($time['bind_time']);
 | 
| 
 | 
 | 
                 // 邮件收到的时间是否大于绑定时间
 | 
| 
 | 
 | 
                 if($udate > $t){
 | 
| 
 | 
 | 
                     $data = $time;
 | 
| 
 | 
 | 
                     break;
 | 
| 
 | 
 | 
                 }
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         // 没有找到历史,就找绑定表
 | 
| 
 | 
 | 
         if(empty($data)){
 | 
| 
 | 
 | 
             $data = redis()->getSet('fob_bind_mail3:'.$email_id,300,function ($email_id){
 | 
| 
 | 
 | 
                 return $this->fob_db->throw()->first("select `post_id`,`source` from `e_mail_binds` where `source` = 2 and `email_id` = '{$email_id}' and `deleted_at` is null order by `id` desc limit 1");
 | 
| 
 | 
 | 
             },$email_id);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         return [
 | 
| 
 | 
 | 
             $data['post_id']??0,
 | 
| 
 | 
 | 
             $data['source']??0,
 | 
| 
 | 
 | 
         ];
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     public function getEsData($data){
 | 
| 
 | 
 | 
         if(!empty($data['id'])){
 | 
| 
 | 
 | 
             $data['uuid'] = $data['id'];
 | 
| 
 | 
 | 
             unset($data['id']);
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $data['from'] = [
 | 
| 
 | 
 | 
             'email' =>  $data['from'],
 | 
| 
 | 
 | 
             'name'  =>  $data['from_name']??''
 | 
| 
 | 
 | 
         ];
 | 
| 
 | 
 | 
         unset($data['from_name']);
 | 
| 
 | 
 | 
         unset($data['date']);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $data['to_name'] = $this->trimEmail($data['to_name']??[]);
 | 
| 
 | 
 | 
         $data['cc'] = $this->trimEmail($data['cc']??[]);
 | 
| 
 | 
 | 
         $data['bcc'] = $this->trimEmail($data['bcc']??[]);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $data['description'] = str_replace(["\n"],"",$data['description']);
 | 
| 
 | 
 | 
         // unset($data['to_name']);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         if(!empty($data['created_at'])){
 | 
| 
 | 
 | 
             $data['created_at'] = date('Y-m-d\TH:i:s',strtotime($data['created_at']));
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
         if(!empty($data['updated_at'])){
 | 
| 
 | 
 | 
             $data['updated_at'] = date('Y-m-d\TH:i:s',strtotime($data['updated_at']));
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
         $data['references'] = empty($data['references']) ? '' : $data['references'];
 | 
| 
 | 
 | 
         return $data;
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     private function trimEmail($emails){
 | 
| 
 | 
 | 
         if($emails){
 | 
| 
 | 
 | 
             $emails = is_array($emails) ? $emails : @json_decode($emails,true);
 | 
| 
 | 
 | 
             if(is_array($emails) && count($emails)>100){
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
                 $emails = array_map(function ($v){
 | 
| 
 | 
 | 
                     $v['name'] = '';
 | 
| 
 | 
 | 
                     return $v;
 | 
| 
 | 
 | 
                 },$emails);
 | 
| 
 | 
 | 
                 $emails = array_slice($emails,0,100);
 | 
| 
 | 
 | 
                 return json_encode($emails);
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
         return '';
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 (new SyncToEsCmd())->handler($argv[1]);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 return 1;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
  |