Files
xuetang/server/app/adminapi/logic/firstvisit/WecomAcquisitionCustomerLogic.php
2026-09-08 11:40:15 +08:00

157 lines
6.9 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace app\adminapi\logic\firstvisit;
use app\common\service\DataScope\DataScopeService;
use app\common\service\qywx\QywxCustomerAcquisitionCustomerService;
use RuntimeException;
use think\facade\Db;
/** 获客客户同步与数据权限统计。 */
class WecomAcquisitionCustomerLogic
{
/** @return array<string,mixed> */
public static function sync(array $params, int $adminId, array $adminInfo): array
{
$localLinkId = max(0, (int) ($params['promotion_link_id'] ?? $params['id'] ?? 0));
$query = Db::name('qywx_promotion_link')->alias('l')
->whereNull('l.delete_time')
->where('l.remote_link_id', '<>', '')
->where('l.remote_status', 1);
self::applyScope($query, 'l', DataScopeService::getVisibleAdminIds($adminId, $adminInfo));
if ($localLinkId > 0) {
$query->where('l.id', $localLinkId);
}
$links = $query->field('l.id,l.remote_link_id')->order('l.id', 'asc')->limit(200)->select()->toArray();
if ($localLinkId > 0 && $links === []) {
throw new RuntimeException('获客链接不存在、已失效,或超出当前权限范围');
}
if ($links === []) {
throw new RuntimeException('当前数据范围内没有可同步的有效官方获客链接;已删除和历史手工链接不会参与客户同步,请先创建官方获客链接');
}
$service = new QywxCustomerAcquisitionCustomerService();
$result = ['links' => count($links), 'scanned' => 0, 'created' => 0, 'updated' => 0, 'failed' => 0, 'errors' => []];
foreach ($links as $link) {
try {
$one = $service->syncLink((string) $link['remote_link_id']);
$result['scanned'] += $one['scanned'];
$result['created'] += $one['created'];
$result['updated'] += $one['updated'];
} catch (\Throwable $e) {
$result['failed']++;
if (count($result['errors']) < 10) {
$result['errors'][] = (string) $link['remote_link_id'] . '' . $e->getMessage();
}
}
}
return $result;
}
/** @return array<string,mixed> */
public static function statistics(array $params, int $adminId, array $adminInfo): array
{
$page = max(1, (int) ($params['page_no'] ?? $params['page'] ?? 1));
$pageSize = min(100, max(1, (int) ($params['page_size'] ?? 20)));
$visibleIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
$base = self::customerQuery($params, $visibleIds);
$total = (int) (clone $base)->count();
$rows = $base
->field('c.id,c.promotion_link_id,c.link_id,c.external_userid,c.userid,c.owner_admin_id,c.dept_id,c.state,c.chat_status,c.recv_msg_cnt,c.message_count_known,c.first_acquired_time,c.last_chat_time,c.last_sync_time,c.create_time,c.update_time,a.name as owner_name,d.name as dept_name,l.name as link_name,p.name as pool_name')
->order('c.last_chat_time', 'desc')->order('c.id', 'desc')
->page($page, $pageSize)->select()->toArray();
foreach ($rows as &$row) {
$row['external_userid_masked'] = self::maskIdentifier((string) ($row['external_userid'] ?? ''));
unset($row['external_userid']);
$row['has_messaged'] = (int) ($row['chat_status'] ?? 0) === 1;
$row['message_count_known'] = (int) ($row['message_count_known'] ?? 0);
$row['received_message_count'] = (int) ($row['recv_msg_cnt'] ?? 0);
}
unset($row);
$summaryQuery = self::customerQuery($params, $visibleIds);
$summaryRow = $summaryQuery->fieldRaw(
'COUNT(*) AS customer_count, '
. 'COALESCE(SUM(CASE WHEN c.message_count_known = 1 THEN c.recv_msg_cnt ELSE 0 END),0) AS recv_msg_cnt, '
. 'SUM(CASE WHEN c.chat_status = 1 THEN 1 ELSE 0 END) AS started_chat_count, '
. 'SUM(CASE WHEN c.message_count_known = 1 THEN 1 ELSE 0 END) AS message_count_known_count'
)->find() ?: [];
return [
'meta' => [
'scope_label' => DataScopeService::scopeLabel(DataScopeService::getEffectiveScope($adminInfo)),
'generated_at' => date('Y-m-d H:i:s'),
],
'summary' => [
'customer_count' => (int) ($summaryRow['customer_count'] ?? 0),
'started_chat_count' => (int) ($summaryRow['started_chat_count'] ?? 0),
'recv_msg_cnt' => (int) ($summaryRow['recv_msg_cnt'] ?? 0),
'received_message_count' => (int) ($summaryRow['recv_msg_cnt'] ?? 0),
'message_count_known_count' => (int) ($summaryRow['message_count_known_count'] ?? 0),
],
'lists' => $rows,
'count' => $total,
'page_no' => $page,
'page_size' => $pageSize,
];
}
private static function customerQuery(array $params, ?array $visibleIds)
{
$query = Db::name('qywx_customer_acquisition_customer')->alias('c')
->leftJoin('admin a', 'a.id = c.owner_admin_id AND a.delete_time IS NULL')
->leftJoin('dept d', 'd.id = c.dept_id')
->leftJoin('qywx_promotion_link l', 'l.id = c.promotion_link_id')
->leftJoin('qywx_promotion_pool p', 'p.id = l.pool_id');
self::applyScope($query, 'c', $visibleIds);
$localLinkId = max(0, (int) ($params['promotion_link_id'] ?? 0));
if ($localLinkId > 0) {
$query->where('c.promotion_link_id', $localLinkId);
}
$userId = trim((string) ($params['userid'] ?? ''));
if ($userId !== '') {
$query->where('c.userid', $userId);
}
if (isset($params['chat_status']) && $params['chat_status'] !== '') {
$query->where('c.chat_status', max(0, (int) $params['chat_status']));
}
$keyword = trim((string) ($params['keyword'] ?? ''));
if ($keyword !== '') {
$query->whereLike('c.external_userid|c.userid|a.name|l.name', '%' . $keyword . '%');
}
return $query;
}
private static function applyScope($query, string $alias, ?array $visibleIds): void
{
if ($visibleIds === null) {
return;
}
if ($visibleIds === []) {
$query->whereRaw('1 = 0');
return;
}
$query->whereIn($alias . '.owner_admin_id', array_values(array_unique(array_map('intval', $visibleIds))));
}
private static function maskIdentifier(string $value): string
{
$value = trim($value);
$length = mb_strlen($value);
if ($length <= 0) {
return '-';
}
if ($length <= 4) {
return mb_substr($value, 0, 1) . '***';
}
if ($length <= 8) {
return mb_substr($value, 0, 2) . '***' . mb_substr($value, -1);
}
return mb_substr($value, 0, 4) . '****' . mb_substr($value, -4);
}
}