['name'], ]; } /** * @return int[] 入参 tag_ids 规范化后的非空字符串数组(实际为 string[]) */ private function normalizeTagIds(): array { $raw = $this->params['tag_ids'] ?? null; if ($raw === null || $raw === '') { return []; } if (is_string($raw)) { $raw = explode(',', $raw); } if (!is_array($raw)) { return []; } $ids = []; foreach ($raw as $v) { $s = trim((string) $v); if ($s !== '') { $ids[] = $s; } } return array_values(array_unique($ids)); } private function baseQuery() { $query = QywxExternalContact::where($this->searchWhere); if (!empty($this->params['follow_user'])) { $kw = addcslashes((string) $this->params['follow_user'], '%_\\'); $query->whereLike('follow_users', '%' . $kw . '%'); } // 标签筛选:JOIN 关系表按 tag_id 过滤;多个标签为 OR(命中任一即返回)。 // 走 zyt_qywx_external_contact_tag.idx_tag 索引,比 LIKE follow_users 快得多 $tagIds = $this->normalizeTagIds(); if ($tagIds !== []) { $matchedExtIds = Db::name('qywx_external_contact_tag') ->whereIn('tag_id', $tagIds) ->group('external_userid') ->column('external_userid'); if ($matchedExtIds === []) { // 没人命中:直接给一个不可能成立的条件,避免下面命中所有客户 $query->whereRaw('1=0'); } else { $query->whereIn('external_userid', $matchedExtIds); } } // 添加时间筛选,两种口径: // first(默认)= 首次添加时间在窗口内(external_first_add_time 优先,0 则 create_time) // any = 企微「全部客户+时间筛选」口径:存在"添加时间在窗口内的现存跟进关系"即命中 // (含老客户被重加/被其他员工添加;关系已解除的不算),数据源为现存关系表 $addStart = trim((string) ($this->params['add_time_start'] ?? '')); $addEnd = trim((string) ($this->params['add_time_end'] ?? '')); $addMode = trim((string) ($this->params['add_time_mode'] ?? 'first')); $startTs = $addStart !== '' ? strtotime($addStart . ' 00:00:00') : false; $endTs = $addEnd !== '' ? strtotime($addEnd . ' 23:59:59') : false; if ($addMode === 'any' && ($startTs !== false || $endTs !== false)) { $followQuery = Db::name('qywx_external_contact_follow')->where('createtime', '>', 0); if ($startTs !== false) { $followQuery->where('createtime', '>=', $startTs); } if ($endTs !== false) { $followQuery->where('createtime', '<=', $endTs); } $matchedExtIds = $followQuery->group('external_userid')->column('external_userid'); if ($matchedExtIds === []) { $query->whereRaw('1=0'); } else { $query->whereIn('external_userid', $matchedExtIds); } } else { $effExpr = 'COALESCE(NULLIF(external_first_add_time, 0), create_time)'; if ($startTs !== false) { $query->whereRaw($effExpr . ' > 0 AND ' . $effExpr . ' >= ?', [$startTs]); } if ($endTs !== false) { $query->whereRaw($effExpr . ' > 0 AND ' . $effExpr . ' <= ?', [$endTs]); } } return $query; } /** * @notes 获取列表 */ public function lists(): array { // 按首次添加时间倒序:库字段 external_first_add_time;未回填(0)时回退 create_time(与列表「添加时间」展示一致) $lists = $this->baseQuery() ->orderRaw( '(COALESCE(NULLIF(external_first_add_time, 0), create_time) = 0) ASC, ' . 'COALESCE(NULLIF(external_first_add_time, 0), create_time) DESC, id DESC' ) ->limit($this->limitOffset, $this->limitLength) ->select() ->toArray(); $wxUserids = []; foreach ($lists as $item) { $raw = json_decode($item['follow_users'] ?? '[]', true); if (!is_array($raw)) { continue; } foreach ($raw as $fu) { if (!is_array($fu)) { continue; } $wx = trim((string) ($fu['userid'] ?? '')); if ($wx !== '') { $wxUserids[$wx] = true; } } } $wxUserids = array_keys($wxUserids); $adminNameByWx = []; if ($wxUserids !== []) { $adminNameByWx = Admin::whereIn('work_wechat_userid', $wxUserids)->column('name', 'work_wechat_userid'); } foreach ($lists as &$item) { $followUsers = json_decode($item['follow_users'] ?? '[]', true); $followUsers = is_array($followUsers) ? $followUsers : []; foreach ($followUsers as &$fu) { if (!is_array($fu)) { continue; } $wx = trim((string) ($fu['userid'] ?? '')); if ($wx !== '' && isset($adminNameByWx[$wx]) && $adminNameByWx[$wx] !== '') { $fu['admin_name'] = $adminNameByWx[$wx]; } } unset($fu); $item['follow_users'] = $followUsers; $followAdminIds = json_decode($item['follow_admin_ids'] ?? '[]', true); $item['follow_admin_ids'] = is_array($followAdminIds) ? $followAdminIds : []; // 解析标签 JSON 数组(值由 CustomerLogic::extractFollowUserTags 写入;按 tag_id 去重) $tags = json_decode((string) ($item['tags'] ?? '[]'), true); $item['tags'] = is_array($tags) ? $tags : []; $fromDb = (int) ($item['external_first_add_time'] ?? 0); $fromJson = CustomerLogic::minFollowCreatetime($followUsers); $item['external_first_add_time'] = $fromDb > 0 ? $fromDb : $fromJson; // 「任意添加」口径会带出软删行,前端据此显示"已删除"标记 $item['is_deleted'] = !empty($item['delete_time']) ? 1 : 0; } unset($item); return $lists; } /** * @notes 获取数量 */ public function count(): int { return $this->baseQuery()->count(); } }