作者 邓超

优化查询

  1 +<?php
  2 +
  3 +namespace Controller\fob_ai;
  4 +
  5 +
  6 +
  7 +use Controller\Base;
  8 +use Lib\Es\Es;
  9 +use Lib\Mail\MailFun;
  10 +use Lib\Verify;
  11 +use Model\folderSql;
  12 +use function Swoole\Coroutine\Http\request;
  13 +
  14 +/**
  15 + * 黑格 fob 那边专用 业务 ai 邮件 第二个版本
  16 + * 为定制逻辑
  17 + * @author:dc
  18 + * @time 2024/7/18 11:40
  19 + * Class MailList
  20 + * @package Controller\fob_ai
  21 + */
  22 +class MailListV2Es2 extends Base {
  23 +
  24 + /**
  25 + * @var Es
  26 + */
  27 + public $es;
  28 +
  29 + public function __construct()
  30 + {
  31 + $this->es = \es('email_lists');
  32 + }
  33 +
  34 + /**
  35 + * 当前邮箱下指定的文件夹
  36 + * @param string $folder
  37 + * @return array
  38 + * @throws \Lib\Err
  39 + * @author:dc
  40 + * @time 2024/7/19 11:37
  41 + */
  42 + private function getFolderId(string $folder,$emails=[]){
  43 + if(!$emails){
  44 + $emails = $this->getEmails('id');
  45 + }
  46 + $k = md5(json_encode($emails));
  47 + // 查询 文件夹
  48 + static $folderList;
  49 + if(empty($folderList[$k])){
  50 + $folderList[$k] = db()->all(folderSql::all($emails));
  51 + }
  52 + $folder_id = [];
  53 + // 文件夹id
  54 + if($folderList[$k]){
  55 + foreach ($folderList[$k] as $item){
  56 + if(folderAlias($item['folder']) == $folder){
  57 + $folder_id[] = $item['id'];
  58 + }
  59 + }
  60 + }
  61 + if(!$folder_id){
  62 + app()->e('folder_not_fount');
  63 + }
  64 +
  65 + return $folder_id;
  66 + }
  67 +
  68 +
  69 + /**
  70 + * 处理like条件
  71 + * @param $str
  72 + * @return string
  73 + * @author:dc
  74 + * @time 2024/9/4 11:14
  75 + */
  76 + private function getLikeStr($str){
  77 + if(str_starts_with($str, '^')){
  78 + return addslashes(mb_substr($str,1,99)).'%%';
  79 + }
  80 + return '%%'.addslashes($str).'%%';
  81 + }
  82 +
  83 + /**
  84 + * 邮件列表
  85 + * 接收参数
  86 + * page 分页
  87 + * limit 每页数量
  88 + * mail_id 指定邮件id
  89 + * attachment 是否附件
  90 + * emails 邮箱
  91 + * folder 文件夹
  92 + * from_not_in 不在这些发件箱的邮件
  93 + * @throws \Lib\Err
  94 + * @author:dc
  95 + * @time 2024/7/18 11:40
  96 + */
  97 + public function lists(){
  98 +
  99 + // 分页 页数
  100 + $page = app()->request('page',1,'intval') ? : 1;
  101 + $limit = app()->request('limit',20,'intval') ? : 1;
  102 +
  103 + // 指定id
  104 + $ids = app()->requestArr('mail_id');
  105 + foreach ($ids as $i=>$d){
  106 + if(!is_numeric($d)){
  107 + unset($ids[$i]);
  108 + }
  109 + }
  110 + // 目录
  111 + $folder = app()->request('folder','收件箱');
  112 + // 只允许查询这里文件夹
  113 + if(!in_array($folder,['收件箱','发件箱','垃圾箱','星标邮件','预热收件箱','预热发件箱','自动回复收件箱','Starred','草稿箱'])){
  114 + app()->e('folder_not_fount');
  115 + }
  116 +
  117 +
  118 +// $where['is_hots'] = 0;
  119 + // 项目id
  120 + $where['postid'] = intval(app()->request('postid'))?:-1;
  121 + $where['folder_as_int'] = 404; // 搜索那个文件夹
  122 + if($folder=='收件箱'){
  123 + $where['is_hots'] = 0;
  124 + $where['is_auto'] = 0;
  125 + $where['folder_as_int'] = folder2int($folder);
  126 + }elseif($folder=='发件箱'){
  127 + $where['is_hots'] = 0;
  128 + $where['folder_as_int'] = folder2int($folder);
  129 + }
  130 + else if($folder=='星标邮件'||$folder=='Starred'){
  131 + $where['flagged'] = 1; // 星标
  132 + $where['folder_as_int'] = [1,2,3,4,5]; // 所有
  133 + }elseif ($folder=='预热收件箱'){
  134 + $where['is_hots'] = 1;
  135 + $where['folder_as_int'] = folder2int('收件箱');
  136 + }elseif ($folder=='预热发件箱'){
  137 + $where['is_hots'] = 1;
  138 + $where['folder_as_int'] = folder2int('发件箱');
  139 + } elseif ($folder=='自动回复收件箱'){
  140 + $where['is_auto'] = 1;
  141 + $where['folder_as_int'] = folder2int('收件箱');
  142 + }elseif ($folder=='草稿箱' || $folder=='垃圾箱'){
  143 + $where['folder_as_int'] = folder2int($folder); // 所有
  144 + }
  145 +
  146 + // 已读/未读
  147 + if(paramHas('seen')){
  148 + $seen = app()->request('seen',-1,'intval');
  149 + if(in_array($seen,[0,1])){
  150 + $where['seen'] = $seen;
  151 + }
  152 + }
  153 + // 搜索标题
  154 + $keyword = app()->request('keyword');
  155 + if($keyword){
  156 + $where['subject'] = $keyword;
  157 + }
  158 + // 联系人
  159 + $address = app()->request('address');
  160 + if($address){
  161 + if(is_array($address)){
  162 + // 发贱人
  163 + if(Verify::sEmail($address['from']??'')){
  164 + if(str_contains($folder, '发件箱')){
  165 + $where['to'] = $address['from'];
  166 + }else{
  167 + $where['from.email'] = $address['from'];
  168 + }
  169 + }
  170 + }
  171 + }
  172 +
  173 + if($ids) $where['uuid'] = $ids;
  174 +
  175 + if(app()->request('attachment',0,'bool_Val')){
  176 + // 附件
  177 + $where['is_file'] = 1; //附件
  178 + }
  179 +
  180 + // 软删
  181 + $where['deleted'] = 0;
  182 +
  183 + $query = [
  184 + 'bool'=>[
  185 + 'must' => []
  186 + ]
  187 + ];
  188 + foreach ($where as $k=>$v){
  189 + if($k=='subject'){
  190 + if($v){
  191 + $query['bool']['must'][] = [
  192 + 'query_string' => [
  193 + 'query'=>'"'.addcslashes($v,'"').'"',
  194 + "default_field"=> "subject"
  195 + ]
  196 + ];
  197 + }
  198 +
  199 + }elseif (in_array($k,['from.email','to'])){
  200 + $query['bool']['must'][] = [
  201 + 'match_phrase' => [
  202 + $k=>$v
  203 + ]
  204 + ];
  205 + }else{
  206 + $query['bool']['must'][] = [
  207 + is_array($v)?'terms':'term' => [
  208 + $k=>$v
  209 + ]
  210 + ];
  211 + }
  212 +
  213 + }
  214 +
  215 + if($assign = $this->assignSql($folder)){
  216 + $query['bool']['must'][] = $assign;
  217 + }
  218 +
  219 +
  220 + if(str_contains($folder, '收件箱') &&empty($where['is_hots']) &&empty($where['is_auto']) && empty($where['flagged'])){
  221 + $from_not_in_like = app()->request('from_not_in_like');
  222 + if ($from_not_in_like) {
  223 + $from_not_in_like = is_array($from_not_in_like) ? $from_not_in_like : [$from_not_in_like];
  224 + $notinquery = [];
  225 + foreach ($from_not_in_like as $k => $sub) {
  226 + if($sub){
  227 + $notinquery[] = ['match_phrase'=>['from.email'=>$sub]];
  228 + }
  229 + }
  230 + if($notinquery){
  231 + $query['bool']['must_not'] = $notinquery;
  232 + }
  233 + }
  234 + }
  235 +
  236 + $result = $this->es->search(['query'=>['constant_score'=>['filter'=>$query]]],($page-1) * $limit,$limit,['udate'=>"desc"]);
  237 +
  238 +
  239 + $total = $result['hits']['total']['value']??0;
  240 + $lists = $result['hits']['hits']??[];
  241 + // map
  242 + $lists = array_map(function ($v){
  243 + $v = $v['_source'];
  244 + $v['id'] = $v['uuid'];
  245 +
  246 + $v['from_name'] = $v['from']['name']??'';
  247 + $v['from'] = $v['from']['email']??'';
  248 +
  249 + $v['uuid'] = md5($v['email_id'].'-'.$v['folder_id'].'-'.$v['uid']);
  250 + if(!empty($v['description'])){
  251 + $v['description'] = @html_entity_decode($v['description'], ENT_COMPAT, 'UTF-8');
  252 + }
  253 + $v['to_name'] = @json_decode($v['to_name'],true);
  254 + $v['to_name'] = $v['to_name']?:[];
  255 + if($v['to_name']){
  256 + if(!empty($v['to_name'][0]['email'])){
  257 + $v['to'] = $v['to_name'][0]['email'];
  258 + }
  259 + $v['to_name'] = MailFun::mb_coding($v['to_name'][0]['name']??'');
  260 + }
  261 + if(is_array($v['to_name'])){
  262 + $v['to_name'] = '';
  263 + }
  264 + // 邮件箱
  265 + $v['folder_name'] = db()->cache(86400)->value(folderSql::first($v['folder_id'],'folder'));
  266 +
  267 + // 暂时这样吧
  268 + $info = db()->first(\Model\listsSql::first('id = '.$v['id'],'`flagged`,`seen`,`deleted`'));
  269 + if($info){
  270 + $v['flagged'] = $info['flagged'];
  271 + $v['seen'] = $info['seen'];
  272 + $v['deleted'] = $info['deleted'];
  273 + }
  274 +
  275 + // 手动触发同步es
  276 + redis()->rPush('sync_to_es',$v['id']);
  277 +
  278 + return $v;
  279 + },$lists?:[]);
  280 +
  281 + $lists = array_filter($lists,function ($v){
  282 + if($v['deleted']){
  283 + return false;
  284 + }
  285 + return true;
  286 + });
  287 +
  288 +
  289 + app()->_json(listsPage($lists,$total,$page,$limit));
  290 +
  291 + }
  292 +
  293 +
  294 +
  295 + private function assignSql($folder){
  296 + // 被分配的
  297 + $assign = app()->request('assign');
  298 + // 说明是子账号
  299 + if(!empty($assign['assign'])){
  300 + return [
  301 + 'bool' => [
  302 + 'should' =>[
  303 + [
  304 + 'query_string'=>[
  305 + 'query'=>(str_contains($folder, '发件箱')?'to':'from.email').':('.implode(' OR ',array_map(function($e){return '"'.$e.'"';},$assign['from'])).')'
  306 + ]
  307 + ],
  308 + [
  309 + 'terms'=>[
  310 + 'email_id'=>$assign['email_id']
  311 + ]
  312 + ]
  313 + ]
  314 + ]
  315 + ];
  316 +
  317 + }
  318 +
  319 + return [];
  320 +
  321 + }
  322 +
  323 +
  324 + private function assignSql3($folder){
  325 + // 被分配的
  326 + $assign = app()->request('assign');
  327 +
  328 + if(!empty($assign['email_id'])){
  329 + // 此处请求中的
  330 + $email = array_diff($this->getEmails('id'),$assign['email_id']);
  331 + if($email){
  332 + $fids = $this->getFolderId($folder,$email);
  333 + // 有目录id和from
  334 + if($fids){
  335 + return ['terms'=>['folder_id'=>$fids]];
  336 + }
  337 + }
  338 +
  339 + return ['term'=>['folder_id'=>-1]];
  340 +
  341 + }
  342 +
  343 + return ['terms'=>['folder_id'=> $this->getFolderId($folder)]];
  344 +
  345 + }
  346 +
  347 +
  348 +
  349 + public function count(){
  350 + if(!$this->getEmails('id')){
  351 + app()->e('email_request_required');
  352 + }
  353 +
  354 + $body = [];
  355 + $body['query'] = [
  356 + 'bool'=>[
  357 + 'must'=>[
  358 + ['term'=>['deleted'=>0]]
  359 + ]
  360 + ]
  361 + ];
  362 + // 时间 必须是数组
  363 + $udate = app()->request('udate');
  364 + if($udate && is_array($udate) && count($udate) == 2){
  365 + $udate = array_values($udate);
  366 + $udate = array_map(function ($v){
  367 + if(is_numeric($v)) return $v;
  368 + return strtotime($v);
  369 + },$udate);
  370 + // 时间范围搜索
  371 + $body['query']['bool']['must'][] = [
  372 + 'range' => [
  373 + 'udate' => [
  374 + 'gte' => $udate[0], // 大于等于开始日期
  375 + 'lte' => $udate[1] // 小于等于结束日期
  376 + ]
  377 + ]
  378 + ];
  379 +
  380 + }
  381 +
  382 +
  383 + // show_count_filed
  384 + $show_count_filed = app()->requestArr('show_count_filed',['inbox', 'send', 'unseen', 'flagged', 'junk', 'hot_inbox', 'hot_send']);
  385 +
  386 +
  387 + // 发件箱
  388 + if(in_array('hot_send',$show_count_filed)){
  389 + // 预热发件箱
  390 + $fCount = $this->countHot($body,'发件箱');
  391 + }
  392 +
  393 + // 预热收件箱
  394 + if(in_array('hot_inbox',$show_count_filed)) {
  395 + $sCount = $this->countHot($body,'收件箱');
  396 + }
  397 +
  398 + if(in_array('send',$show_count_filed)) {
  399 + $faCount = $this->countMail($body,'发件箱');
  400 + }
  401 +// 垃圾箱
  402 + if(in_array('junk',$show_count_filed)) {
  403 + $lajiCount = $this->countMail($body,'垃圾箱');
  404 + }
  405 +
  406 + // 收件箱
  407 + if(in_array('inbox',$show_count_filed) || in_array('replied',$show_count_filed)) {
  408 + $repliedCount = $shouCount = $this->countMail($body,'收件箱');
  409 + }
  410 +
  411 + // 未读
  412 + if(in_array('unseen',$show_count_filed)) {
  413 + $seenCount = $this->countMail($body,'收件箱', 0);
  414 + }
  415 +
  416 +
  417 + // 星标
  418 + if(in_array('flagged',$show_count_filed)) {
  419 + $flaggedCount = $this->countFlagged($body);
  420 + }
  421 +
  422 + $data = [];
  423 + if(isset($shouCount)) $data['inbox'] = $shouCount;
  424 + if(isset($repliedCount)) $data['replied'] = $repliedCount;
  425 + if(isset($faCount)) $data['send'] = $faCount;
  426 + if(isset($seenCount)) $data['unseen'] = $seenCount;
  427 + if(isset($flaggedCount)) $data['flagged'] = $flaggedCount;
  428 + if(isset($lajiCount)) $data['junk'] = $lajiCount;
  429 + if(isset($sCount)) $data['hot_inbox'] = $sCount;
  430 + if(isset($fCount)) $data['hot_send'] = $fCount;
  431 +
  432 + app()->_json($data);
  433 + }
  434 +
  435 + /**
  436 + * 统计预热邮件
  437 + * @param $body
  438 + * @param $folder
  439 + * @author:dc
  440 + * @time 2025/3/6 15:24
  441 + */
  442 + private function countHot($body,$folder){
  443 + $body['query']['bool']['must'][] = ['term'=>['is_hots'=>1]];
  444 + $body['query']['bool']['must'][] = $this->assignSql3($folder);
  445 + return $this->es->count($body);
  446 + }
  447 +
  448 + private function countMail($body,$folder,$seen=null){
  449 + if($folder == '收件箱'){
  450 + $body['query']['bool']['must'][] = ['term'=>['is_auto'=>0]];
  451 +
  452 +
  453 + $from_not_in_like = app()->request('from_not_in_like');
  454 + if ($from_not_in_like) {
  455 + $from_not_in_like = is_array($from_not_in_like) ? $from_not_in_like : [$from_not_in_like];
  456 + $notinquery = [];
  457 + foreach ($from_not_in_like as $k => $sub) {
  458 + if($sub){
  459 + $notinquery[] = ['match_phrase'=>['from.email'=>$sub]];
  460 + }
  461 + }
  462 + if($notinquery){
  463 + $body['query']['bool']['must_not'] = $notinquery;
  464 + }
  465 + }
  466 +
  467 +
  468 + }
  469 + if($seen!==null){
  470 + $body['query']['bool']['must'][] = ['term'=>['seen'=>$seen]];
  471 + }
  472 + $body['query']['bool']['must'][] = ['term'=>['is_hots'=>0]];
  473 + $body['query']['bool']['must'][] = $this->assignSql($folder);
  474 + return $this->es->count($body);
  475 + }
  476 +
  477 + /**
  478 + * 统计星标
  479 + * @param $body
  480 + * @return int|mixed
  481 + * @author:dc
  482 + * @time 2025/3/6 16:01
  483 + */
  484 + private function countFlagged($body){
  485 + $body['query']['bool']['must'][] = ['term'=>['flagged'=>1]];
  486 + $body['query']['bool']['must'][] = $this->assignSql('收件箱');
  487 + return $this->es->count($body);
  488 + }
  489 +
  490 +}
  491 +
  492 +
  493 +
  494 +
  495 +
  496 +
  497 +
  498 +
  499 +
  500 +
  501 +
  502 +
  503 +
  504 +
@@ -25,6 +25,10 @@ return [ @@ -25,6 +25,10 @@ return [
25 'fob/es/lists' => [\Controller\fob_ai\MailListV2Es::class, 'lists'], 25 'fob/es/lists' => [\Controller\fob_ai\MailListV2Es::class, 'lists'],
26 'fob/es/count' => [\Controller\fob_ai\MailListV2Es::class, 'count'],// 统计数量 26 'fob/es/count' => [\Controller\fob_ai\MailListV2Es::class, 'count'],// 统计数量
27 27
  28 + // 查询es的
  29 + 'fob/es/lists2' => [\Controller\fob_ai\MailListV2Es2::class, 'lists'],
  30 + 'fob/es/count2' => [\Controller\fob_ai\MailListV2Es2::class, 'count'],// 统计数量
  31 +
28 32
29 // 代理 ip分配 33 // 代理 ip分配
30 'proxy/server' => [\Controller\ServerProxy::class,'index'], 34 'proxy/server' => [\Controller\ServerProxy::class,'index'],