作者 邓超

优化查询

<?php
namespace Controller\fob_ai;
use Controller\Base;
use Lib\Es\Es;
use Lib\Mail\MailFun;
use Lib\Verify;
use Model\folderSql;
use function Swoole\Coroutine\Http\request;
/**
* 黑格 fob 那边专用 业务 ai 邮件 第二个版本
* 为定制逻辑
* @author:dc
* @time 2024/7/18 11:40
* Class MailList
* @package Controller\fob_ai
*/
class MailListV2Es2 extends Base {
/**
* @var Es
*/
public $es;
public function __construct()
{
$this->es = \es('email_lists');
}
/**
* 当前邮箱下指定的文件夹
* @param string $folder
* @return array
* @throws \Lib\Err
* @author:dc
* @time 2024/7/19 11:37
*/
private function getFolderId(string $folder,$emails=[]){
if(!$emails){
$emails = $this->getEmails('id');
}
$k = md5(json_encode($emails));
// 查询 文件夹
static $folderList;
if(empty($folderList[$k])){
$folderList[$k] = db()->all(folderSql::all($emails));
}
$folder_id = [];
// 文件夹id
if($folderList[$k]){
foreach ($folderList[$k] as $item){
if(folderAlias($item['folder']) == $folder){
$folder_id[] = $item['id'];
}
}
}
if(!$folder_id){
app()->e('folder_not_fount');
}
return $folder_id;
}
/**
* 处理like条件
* @param $str
* @return string
* @author:dc
* @time 2024/9/4 11:14
*/
private function getLikeStr($str){
if(str_starts_with($str, '^')){
return addslashes(mb_substr($str,1,99)).'%%';
}
return '%%'.addslashes($str).'%%';
}
/**
* 邮件列表
* 接收参数
* page 分页
* limit 每页数量
* mail_id 指定邮件id
* attachment 是否附件
* emails 邮箱
* folder 文件夹
* from_not_in 不在这些发件箱的邮件
* @throws \Lib\Err
* @author:dc
* @time 2024/7/18 11:40
*/
public function lists(){
// 分页 页数
$page = app()->request('page',1,'intval') ? : 1;
$limit = app()->request('limit',20,'intval') ? : 1;
// 指定id
$ids = app()->requestArr('mail_id');
foreach ($ids as $i=>$d){
if(!is_numeric($d)){
unset($ids[$i]);
}
}
// 目录
$folder = app()->request('folder','收件箱');
// 只允许查询这里文件夹
if(!in_array($folder,['收件箱','发件箱','垃圾箱','星标邮件','预热收件箱','预热发件箱','自动回复收件箱','Starred','草稿箱'])){
app()->e('folder_not_fount');
}
// $where['is_hots'] = 0;
// 项目id
$where['postid'] = intval(app()->request('postid'))?:-1;
$where['folder_as_int'] = 404; // 搜索那个文件夹
if($folder=='收件箱'){
$where['is_hots'] = 0;
$where['is_auto'] = 0;
$where['folder_as_int'] = folder2int($folder);
}elseif($folder=='发件箱'){
$where['is_hots'] = 0;
$where['folder_as_int'] = folder2int($folder);
}
else if($folder=='星标邮件'||$folder=='Starred'){
$where['flagged'] = 1; // 星标
$where['folder_as_int'] = [1,2,3,4,5]; // 所有
}elseif ($folder=='预热收件箱'){
$where['is_hots'] = 1;
$where['folder_as_int'] = folder2int('收件箱');
}elseif ($folder=='预热发件箱'){
$where['is_hots'] = 1;
$where['folder_as_int'] = folder2int('发件箱');
} elseif ($folder=='自动回复收件箱'){
$where['is_auto'] = 1;
$where['folder_as_int'] = folder2int('收件箱');
}elseif ($folder=='草稿箱' || $folder=='垃圾箱'){
$where['folder_as_int'] = folder2int($folder); // 所有
}
// 已读/未读
if(paramHas('seen')){
$seen = app()->request('seen',-1,'intval');
if(in_array($seen,[0,1])){
$where['seen'] = $seen;
}
}
// 搜索标题
$keyword = app()->request('keyword');
if($keyword){
$where['subject'] = $keyword;
}
// 联系人
$address = app()->request('address');
if($address){
if(is_array($address)){
// 发贱人
if(Verify::sEmail($address['from']??'')){
if(str_contains($folder, '发件箱')){
$where['to'] = $address['from'];
}else{
$where['from.email'] = $address['from'];
}
}
}
}
if($ids) $where['uuid'] = $ids;
if(app()->request('attachment',0,'bool_Val')){
// 附件
$where['is_file'] = 1; //附件
}
// 软删
$where['deleted'] = 0;
$query = [
'bool'=>[
'must' => []
]
];
foreach ($where as $k=>$v){
if($k=='subject'){
if($v){
$query['bool']['must'][] = [
'query_string' => [
'query'=>'"'.addcslashes($v,'"').'"',
"default_field"=> "subject"
]
];
}
}elseif (in_array($k,['from.email','to'])){
$query['bool']['must'][] = [
'match_phrase' => [
$k=>$v
]
];
}else{
$query['bool']['must'][] = [
is_array($v)?'terms':'term' => [
$k=>$v
]
];
}
}
if($assign = $this->assignSql($folder)){
$query['bool']['must'][] = $assign;
}
if(str_contains($folder, '收件箱') &&empty($where['is_hots']) &&empty($where['is_auto']) && empty($where['flagged'])){
$from_not_in_like = app()->request('from_not_in_like');
if ($from_not_in_like) {
$from_not_in_like = is_array($from_not_in_like) ? $from_not_in_like : [$from_not_in_like];
$notinquery = [];
foreach ($from_not_in_like as $k => $sub) {
if($sub){
$notinquery[] = ['match_phrase'=>['from.email'=>$sub]];
}
}
if($notinquery){
$query['bool']['must_not'] = $notinquery;
}
}
}
$result = $this->es->search(['query'=>['constant_score'=>['filter'=>$query]]],($page-1) * $limit,$limit,['udate'=>"desc"]);
$total = $result['hits']['total']['value']??0;
$lists = $result['hits']['hits']??[];
// map
$lists = array_map(function ($v){
$v = $v['_source'];
$v['id'] = $v['uuid'];
$v['from_name'] = $v['from']['name']??'';
$v['from'] = $v['from']['email']??'';
$v['uuid'] = md5($v['email_id'].'-'.$v['folder_id'].'-'.$v['uid']);
if(!empty($v['description'])){
$v['description'] = @html_entity_decode($v['description'], ENT_COMPAT, 'UTF-8');
}
$v['to_name'] = @json_decode($v['to_name'],true);
$v['to_name'] = $v['to_name']?:[];
if($v['to_name']){
if(!empty($v['to_name'][0]['email'])){
$v['to'] = $v['to_name'][0]['email'];
}
$v['to_name'] = MailFun::mb_coding($v['to_name'][0]['name']??'');
}
if(is_array($v['to_name'])){
$v['to_name'] = '';
}
// 邮件箱
$v['folder_name'] = db()->cache(86400)->value(folderSql::first($v['folder_id'],'folder'));
// 暂时这样吧
$info = db()->first(\Model\listsSql::first('id = '.$v['id'],'`flagged`,`seen`,`deleted`'));
if($info){
$v['flagged'] = $info['flagged'];
$v['seen'] = $info['seen'];
$v['deleted'] = $info['deleted'];
}
// 手动触发同步es
redis()->rPush('sync_to_es',$v['id']);
return $v;
},$lists?:[]);
$lists = array_filter($lists,function ($v){
if($v['deleted']){
return false;
}
return true;
});
app()->_json(listsPage($lists,$total,$page,$limit));
}
private function assignSql($folder){
// 被分配的
$assign = app()->request('assign');
// 说明是子账号
if(!empty($assign['assign'])){
return [
'bool' => [
'should' =>[
[
'query_string'=>[
'query'=>(str_contains($folder, '发件箱')?'to':'from.email').':('.implode(' OR ',array_map(function($e){return '"'.$e.'"';},$assign['from'])).')'
]
],
[
'terms'=>[
'email_id'=>$assign['email_id']
]
]
]
]
];
}
return [];
}
private function assignSql3($folder){
// 被分配的
$assign = app()->request('assign');
if(!empty($assign['email_id'])){
// 此处请求中的
$email = array_diff($this->getEmails('id'),$assign['email_id']);
if($email){
$fids = $this->getFolderId($folder,$email);
// 有目录id和from
if($fids){
return ['terms'=>['folder_id'=>$fids]];
}
}
return ['term'=>['folder_id'=>-1]];
}
return ['terms'=>['folder_id'=> $this->getFolderId($folder)]];
}
public function count(){
if(!$this->getEmails('id')){
app()->e('email_request_required');
}
$body = [];
$body['query'] = [
'bool'=>[
'must'=>[
['term'=>['deleted'=>0]]
]
]
];
// 时间 必须是数组
$udate = app()->request('udate');
if($udate && is_array($udate) && count($udate) == 2){
$udate = array_values($udate);
$udate = array_map(function ($v){
if(is_numeric($v)) return $v;
return strtotime($v);
},$udate);
// 时间范围搜索
$body['query']['bool']['must'][] = [
'range' => [
'udate' => [
'gte' => $udate[0], // 大于等于开始日期
'lte' => $udate[1] // 小于等于结束日期
]
]
];
}
// show_count_filed
$show_count_filed = app()->requestArr('show_count_filed',['inbox', 'send', 'unseen', 'flagged', 'junk', 'hot_inbox', 'hot_send']);
// 发件箱
if(in_array('hot_send',$show_count_filed)){
// 预热发件箱
$fCount = $this->countHot($body,'发件箱');
}
// 预热收件箱
if(in_array('hot_inbox',$show_count_filed)) {
$sCount = $this->countHot($body,'收件箱');
}
if(in_array('send',$show_count_filed)) {
$faCount = $this->countMail($body,'发件箱');
}
// 垃圾箱
if(in_array('junk',$show_count_filed)) {
$lajiCount = $this->countMail($body,'垃圾箱');
}
// 收件箱
if(in_array('inbox',$show_count_filed) || in_array('replied',$show_count_filed)) {
$repliedCount = $shouCount = $this->countMail($body,'收件箱');
}
// 未读
if(in_array('unseen',$show_count_filed)) {
$seenCount = $this->countMail($body,'收件箱', 0);
}
// 星标
if(in_array('flagged',$show_count_filed)) {
$flaggedCount = $this->countFlagged($body);
}
$data = [];
if(isset($shouCount)) $data['inbox'] = $shouCount;
if(isset($repliedCount)) $data['replied'] = $repliedCount;
if(isset($faCount)) $data['send'] = $faCount;
if(isset($seenCount)) $data['unseen'] = $seenCount;
if(isset($flaggedCount)) $data['flagged'] = $flaggedCount;
if(isset($lajiCount)) $data['junk'] = $lajiCount;
if(isset($sCount)) $data['hot_inbox'] = $sCount;
if(isset($fCount)) $data['hot_send'] = $fCount;
app()->_json($data);
}
/**
* 统计预热邮件
* @param $body
* @param $folder
* @author:dc
* @time 2025/3/6 15:24
*/
private function countHot($body,$folder){
$body['query']['bool']['must'][] = ['term'=>['is_hots'=>1]];
$body['query']['bool']['must'][] = $this->assignSql3($folder);
return $this->es->count($body);
}
private function countMail($body,$folder,$seen=null){
if($folder == '收件箱'){
$body['query']['bool']['must'][] = ['term'=>['is_auto'=>0]];
$from_not_in_like = app()->request('from_not_in_like');
if ($from_not_in_like) {
$from_not_in_like = is_array($from_not_in_like) ? $from_not_in_like : [$from_not_in_like];
$notinquery = [];
foreach ($from_not_in_like as $k => $sub) {
if($sub){
$notinquery[] = ['match_phrase'=>['from.email'=>$sub]];
}
}
if($notinquery){
$body['query']['bool']['must_not'] = $notinquery;
}
}
}
if($seen!==null){
$body['query']['bool']['must'][] = ['term'=>['seen'=>$seen]];
}
$body['query']['bool']['must'][] = ['term'=>['is_hots'=>0]];
$body['query']['bool']['must'][] = $this->assignSql($folder);
return $this->es->count($body);
}
/**
* 统计星标
* @param $body
* @return int|mixed
* @author:dc
* @time 2025/3/6 16:01
*/
private function countFlagged($body){
$body['query']['bool']['must'][] = ['term'=>['flagged'=>1]];
$body['query']['bool']['must'][] = $this->assignSql('收件箱');
return $this->es->count($body);
}
}
... ...
... ... @@ -25,6 +25,10 @@ return [
'fob/es/lists' => [\Controller\fob_ai\MailListV2Es::class, 'lists'],
'fob/es/count' => [\Controller\fob_ai\MailListV2Es::class, 'count'],// 统计数量
// 查询es的
'fob/es/lists2' => [\Controller\fob_ai\MailListV2Es2::class, 'lists'],
'fob/es/count2' => [\Controller\fob_ai\MailListV2Es2::class, 'count'],// 统计数量
// 代理 ip分配
'proxy/server' => [\Controller\ServerProxy::class,'index'],
... ...