Files
zyt/server/app/adminapi/controller/qywx/CustomerController.php
T

917 lines
35 KiB
PHP
Raw 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\controller\qywx;
use app\adminapi\controller\BaseAdminController;
use app\adminapi\lists\qywx\CustomerLists;
use app\adminapi\logic\qywx\CustomerLogic;
use think\facade\Cache;
use think\facade\Log;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
/**
* 企业微信客户管理控制器(精简版)
*/
class CustomerController extends BaseAdminController
{
// 免登录验证
public array $notNeedLogin = ['lists', 'stats', 'getStaffList', 'import', 'batchGetCustomers', 'getSyncSettings', 'saveSyncSettings', 'sync', 'asyncSync', 'getSyncStatus', 'getSyncTaskStatus'];
/**
* @notes 批量获取企业微信客户(支持分页)
*/
public function batchGetCustomers()
{
try {
$userId = input('userid', '');
$cursor = input('cursor', '');
$limit = (int)input('limit', 50);
if (empty($userId)) {
return $this->fail('缺少用户ID参数');
}
$service = new \app\common\service\wechat\WechatWorkService();
$result = $service->batchGetCustomerDetails($userId, $cursor, $limit);
if ($result === false) {
return $this->fail('获取客户列表失败,可能是权限不足');
}
// 构建响应数据
$response = [
'customers' => $result['customers'] ?? [],
'next_cursor' => $result['next_cursor'] ?? '',
'count' => $result['count'] ?? 0,
'limit' => $limit
];
return $this->success('获取成功', $response);
} catch (\Throwable $e) {
return $this->fail('获取失败: ' . $e->getMessage());
}
}
/**
* @notes 客户列表
*/
public function lists()
{
return $this->dataLists(new CustomerLists());
}
/**
* @notes 获取企业微信token
*/
public function getAccessToken()
{
$token = CustomerLogic::getAccessToken();
if ($token === false) {
return $this->fail(CustomerLogic::getError());
}
return $this->success('获取成功', ['access_token' => $token]);
}
/**
* @notes 获取外部联系人列表
*/
public function getExternalContacts()
{
$userId = input('user_id', '');
$contacts = CustomerLogic::getExternalContacts($userId);
if ($contacts === false) {
return $this->fail(CustomerLogic::getError());
}
return $this->success('获取成功', $contacts);
}
/**
* @notes 获取客户详情
*/
public function getContactDetail()
{
$externalUserId = input('external_user_id', '');
if (empty($externalUserId)) {
return $this->fail('缺少外部联系人ID');
}
$detail = CustomerLogic::getContactDetail($externalUserId);
if ($detail === false) {
return $this->fail(CustomerLogic::getError());
}
return $this->success('获取成功', $detail);
}
/**
* @notes 获取统计信息
*/
public function stats()
{
$stats = CustomerLogic::getStats();
return $this->data($stats);
}
/**
* @notes 按时间点统计用户数量
*/
public function countByTime()
{
try {
$time = input('time', time());
$timestamp = is_numeric($time) ? (int)$time : strtotime($time);
if (!$timestamp) {
return $this->fail('时间格式错误');
}
$count = \app\common\model\QywxExternalContact::where('created_at', '<=', $timestamp)->count();
return $this->success('获取成功', [
'count' => $count,
'time' => date('Y-m-d H:i:s', $timestamp)
]);
} catch (\Throwable $e) {
return $this->fail('获取失败: ' . $e->getMessage());
}
}
/**
* @notes 获取企业微信员工列表
*/
public function getStaffList()
{
try {
$service = new \app\common\service\wechat\WechatWorkService();
$userList = $service->getDepartmentUserList();
// 确保返回的数据是正确的UTF-8编码
$userList = array_map(function($user) {
return $this->recursiveConvert($user);
}, $userList);
return $this->success('获取成功', $userList);
} catch (\Throwable $e) {
error_log('getStaffList error: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
error_log('Stack trace: ' . $e->getTraceAsString());
return $this->fail('获取失败: ' . $e->getMessage());
}
}
/**
* @notes 获取同步设置
*/
public function getSyncSettings()
{
// 从缓存获取同步设置
$cacheKey = 'qywx_sync_settings';
$settings = Cache::get($cacheKey, [
'auto_sync' => false,
'interval' => 3600,
'limit' => 30,
'days' => 7
]);
return $this->success('获取成功', $settings);
}
/**
* @notes 保存同步设置
*/
public function saveSyncSettings()
{
$settings = input('post.');
// 验证并设置默认值
$validatedSettings = [
'auto_sync' => (bool)($settings['auto_sync'] ?? false),
'interval' => max(60, (int)($settings['interval'] ?? 3600)), // 最小1分钟
'limit' => min(100, max(10, (int)($settings['limit'] ?? 30))), // 10-100之间
'days' => min(30, max(1, (int)($settings['days'] ?? 7))) // 1-30天之间
];
// 保存到缓存
$cacheKey = 'qywx_sync_settings';
Cache::set($cacheKey, $validatedSettings, 86400 * 30); // 缓存30天
return $this->success('保存成功', $validatedSettings);
}
/**
* @notes 同步企业微信客户
*/
public function sync()
{
try {
$limit = (int)input('limit', 30);
$fullSync = (bool)input('full_sync', false);
$syncMode = input('sync_mode', 'all');
$selectedUsers = input('selected_users', []);
$days = (int)input('days', 7); // 最近几天的数据
// 设置执行超时时间
set_time_limit(120);
Log::info('开始同步企业微信客户', [
'limit' => $limit,
'full_sync' => $fullSync,
'sync_mode' => $syncMode,
'selected_users' => $selectedUsers,
'days' => $days
]);
$service = new \app\common\service\wechat\WechatWorkService();
// 1. 获取员工列表
$userList = $service->getDepartmentUserList();
if (empty($userList)) {
Log::warning('未获取到员工列表');
return $this->fail('未获取到员工列表');
}
// 按同步模式过滤员工
if ($syncMode === 'specific' && !empty($selectedUsers)) {
$userList = array_filter($userList, function($user) use ($selectedUsers) {
return in_array($user['userid'] ?? '', $selectedUsers);
});
}
$syncCount = 0;
$newCount = 0;
$updateCount = 0;
$processedCustomers = [];
// 获取上次同步时间(使用缓存存储,避免session丢失)
$cacheKey = 'qywx_last_sync_time';
$lastSyncTime = Cache::get($cacheKey, 0);
$currentTime = time();
// 计算时间范围(最近N天)
$timeLimit = $currentTime - ($days * 86400);
// 2. 遍历每个员工,获取其客户列表
foreach ($userList as $user) {
$userId = $user['userid'] ?? '';
if (empty($userId)) {
continue;
}
Log::info('开始同步员工客户', ['user_id' => $userId, 'user_name' => $user['name'] ?? '']);
// 获取该成员的客户列表(支持分页)
$cursor = '';
$hasMore = true;
while ($hasMore && $syncCount < $limit) {
$customerResult = $service->getExternalContactList($userId, $cursor, 100);
if (empty($customerResult) || empty($customerResult['external_userid'])) {
Log::info('员工无客户数据', ['user_id' => $userId]);
break;
}
$customerList = $customerResult['external_userid'] ?? [];
$cursor = $customerResult['next_cursor'] ?? '';
$hasMore = !empty($cursor);
Log::info('获取到员工客户列表', [
'user_id' => $userId,
'customer_count' => count($customerList),
'has_more' => $hasMore
]);
// 3. 处理客户,限制总数量
foreach ($customerList as $externalUserId) {
// 达到限制数量,停止同步
if ($syncCount >= $limit) {
$hasMore = false;
break;
}
// 避免重复处理同一个客户
if (isset($processedCustomers[$externalUserId])) {
continue;
}
$processedCustomers[$externalUserId] = true;
// 增量同步:检查客户是否已存在且更新时间较新
if (!$fullSync) {
$existing = \app\common\model\QywxExternalContact::where('external_user_id', $externalUserId)->find();
if ($existing && $existing->update_time && strtotime($existing->update_time) > time() - 86400) {
continue;
}
}
// 获取客户详情
$detail = $service->getExternalContactDetail($externalUserId);
if (empty($detail)) {
Log::warning('获取客户详情失败', ['external_user_id' => $externalUserId]);
continue;
}
$externalContact = $detail['external_contact'] ?? [];
$followUsers = $detail['follow_user'] ?? [];
// 获取客户实际添加时间
$createTime = time();
if (!empty($followUsers)) {
// 取第一个跟进人的添加时间
$firstFollow = reset($followUsers);
if (isset($firstFollow['create_time'])) {
$createTime = $firstFollow['create_time'];
}
}
// 增量同步:只同步上次同步时间之后的客户
if (!$fullSync && $createTime <= $lastSyncTime) {
continue;
}
// 时间范围过滤:只同步最近N天的客户
if ($createTime < $timeLimit) {
continue;
}
// 保存或更新客户信息
$data = [
'external_user_id' => $externalContact['external_userid'] ?? '',
'name' => $externalContact['name'] ?? '',
'avatar' => $externalContact['avatar'] ?? '',
'type' => $externalContact['type'] ?? 1,
'gender' => $externalContact['gender'] ?? 0,
'unionid' => $externalContact['unionid'] ?? '',
'position' => $externalContact['position'] ?? '',
'corp_name' => $externalContact['corp_name'] ?? '',
'corp_full_name' => $externalContact['corp_full_name'] ?? '',
'external_profile' => json_encode($externalContact['external_profile'] ?? [], JSON_UNESCAPED_UNICODE),
'follow_users' => json_encode($followUsers, JSON_UNESCAPED_UNICODE),
];
// 处理添加人信息
if (!empty($followUsers)) {
// 取第一个跟进人作为添加人
$firstFollow = reset($followUsers);
if (isset($firstFollow['userid']) && isset($firstFollow['name'])) {
$data['first_staff_id'] = $firstFollow['userid'];
$data['first_staff_name'] = $firstFollow['name'];
$data['first_add_time'] = date('Y-m-d H:i:s', $createTime);
$data['last_staff_id'] = $firstFollow['userid'];
$data['last_staff_name'] = $firstFollow['name'];
$data['last_add_time'] = date('Y-m-d H:i:s', $createTime);
}
}
$existing = \app\common\model\QywxExternalContact::where('external_user_id', $data['external_user_id'])->find();
if ($existing) {
$existing->save($data);
$updateCount++;
Log::info('更新客户信息', ['external_user_id' => $data['external_user_id'], 'name' => $data['name']]);
} else {
\app\common\model\QywxExternalContact::create($data);
$newCount++;
Log::info('新增客户信息', ['external_user_id' => $data['external_user_id'], 'name' => $data['name']]);
}
$syncCount++;
}
}
// 达到限制数量,停止同步
if ($syncCount >= $limit) {
break;
}
}
// 保存本次同步时间(使用缓存存储)
Cache::set($cacheKey, $currentTime, 86400 * 30); // 缓存30天
// 更新同步状态
$syncStatus = [
'last_sync_time' => date('Y-m-d H:i:s', $currentTime),
'sync_count' => $syncCount,
'new_count' => $newCount,
'update_count' => $updateCount,
'status' => 'completed'
];
Cache::set('qywx_sync_status', $syncStatus, 86400 * 30);
Log::info('同步企业微信客户完成', [
'sync_count' => $syncCount,
'new_count' => $newCount,
'update_count' => $updateCount,
'last_sync_time' => date('Y-m-d H:i:s', $currentTime)
]);
return $this->success('同步成功', [
'sync_count' => $syncCount,
'new_count' => $newCount,
'update_count' => $updateCount,
'last_sync_time' => date('Y-m-d H:i:s', $currentTime),
'time_limit' => date('Y-m-d H:i:s', $timeLimit)
]);
} catch (\Throwable $e) {
Log::error('同步企业微信客户失败', [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
]);
return $this->fail('同步失败: ' . $e->getMessage());
}
}
/**
* @notes 获取同步状态
*/
public function getSyncStatus()
{
try {
// 从缓存获取同步状态
$syncStatus = Cache::get('qywx_sync_status', [
'last_sync_time' => '',
'sync_count' => 0,
'new_count' => 0,
'update_count' => 0,
'status' => 'idle'
]);
return $this->success('获取成功', $syncStatus);
} catch (\Throwable $e) {
return $this->fail('获取失败: ' . $e->getMessage());
}
}
/**
* @notes 导出客户数据
*/
public function export()
{
try {
$customers = \app\common\model\QywxExternalContact::select();
// 准备导出数据
$exportData = [];
foreach ($customers as $customer) {
$followUsers = json_decode($customer->follow_users, true) ?? [];
$followUserNames = array_column($followUsers, 'name');
$exportData[] = [
'客户名称' => $customer->name,
'External ID' => $customer->external_user_id,
'性别' => $customer->gender == 1 ? '男' : ($customer->gender == 2 ? '女' : '未知'),
'类型' => $customer->type == 1 ? '微信' : '企微',
'企业名称' => $customer->corp_name,
'职位' => $customer->position,
'跟进人' => implode(', ', $followUserNames),
'添加时间' => $customer->created_at,
'更新时间' => $customer->updated_at
];
}
// 生成CSV文件
$filename = 'qywx_customers_' . date('YmdHis') . '.csv';
$filePath = sys_get_temp_dir() . '/' . $filename;
$fp = fopen($filePath, 'w');
if ($fp) {
// 写入表头
fputcsv($fp, array_keys($exportData[0]));
// 写入数据
foreach ($exportData as $row) {
fputcsv($fp, $row);
}
fclose($fp);
// 下载文件
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename=' . $filename);
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
unlink($filePath);
exit;
}
return $this->fail('导出失败');
} catch (\Throwable $e) {
return $this->fail('导出失败: ' . $e->getMessage());
}
}
/**
* @notes 导入客户数据
*/
public function import()
{
try {
// 记录开始时间
$startTime = microtime(true);
// 检查文件上传
if (empty($_FILES['file'])) {
$file = request()->file('file');
if (!$file) {
return $this->fail('请选择文件');
}
} else {
$file = request()->file('file');
}
if (!$file) {
return $this->fail('请选择文件');
}
// 记录文件信息
$originalName = $file->getOriginalName();
$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
$size = $file->getSize();
$tmpName = $file->getRealPath();
// 检查扩展名
$allowedExts = ['csv', 'xlsx'];
if (!in_array($ext, $allowedExts)) {
return $this->fail('请上传CSV或XLSX文件');
}
// 检查文件大小(限制10MB
if ($size > 10 * 1024 * 1024) {
return $this->fail('文件大小不能超过10MB');
}
$importCount = 0;
$skipCount = 0;
$batchSize = 50; // 每批插入50条,减少内存消耗
$batchData = [];
// 设置内存限制
ini_set('memory_limit', '256M');
// 设置执行时间
set_time_limit(120);
if ($ext === 'csv') {
// 读取CSV文件
$fp = fopen($tmpName, 'r');
if (!$fp) {
return $this->fail('文件读取失败');
}
try {
// 读取表头
$header = fgetcsv($fp);
if (!$header) {
fclose($fp);
return $this->fail('文件格式错误');
}
// 转换表头编码
$header = array_map(function($item) {
return $this->safeConvert($item);
}, $header);
// 映射表头
$headerMap = [];
foreach ($header as $index => $field) {
$field = trim($field);
$headerMap[$field] = $index;
}
// 读取数据
$rowNumber = 1;
while (($row = fgetcsv($fp)) !== false) {
$rowNumber++;
// 跳过空行
if (empty(array_filter($row))) {
continue;
}
// 转换行数据编码
$row = array_map(function($item) {
return $this->safeConvert($item);
}, $row);
// 解析数据
$data = $this->parseImportData($row, $headerMap);
// 检查必填字段
if (empty($data['name'])) {
$skipCount++;
continue;
}
// 添加到批次
$batchData[] = $data;
$importCount++;
// 达到批次大小,执行插入
if (count($batchData) >= $batchSize) {
try {
\app\common\model\QywxExternalContact::insertAll($batchData);
} catch (\Exception $e) {
fclose($fp);
return $this->fail('数据插入失败: ' . $e->getMessage());
}
$batchData = [];
}
}
// 插入剩余数据
if (!empty($batchData)) {
try {
\app\common\model\QywxExternalContact::insertAll($batchData);
} catch (\Exception $e) {
fclose($fp);
return $this->fail('数据插入失败: ' . $e->getMessage());
}
}
} catch (\Exception $e) {
fclose($fp);
return $this->fail('CSV文件解析失败: ' . $e->getMessage());
} finally {
if (isset($fp) && is_resource($fp)) {
fclose($fp);
}
}
} else {
// 读取XLSX文件
try {
$spreadsheet = IOFactory::load($tmpName);
$worksheet = $spreadsheet->getActiveSheet();
// 读取表头(第一行)
$header = [];
$highestColumn = $worksheet->getHighestColumn();
$highestColumnIndex = Coordinate::columnIndexFromString($highestColumn);
for ($col = 1; $col <= $highestColumnIndex; $col++) {
$value = $worksheet->getCellByColumnAndRow($col, 1)->getValue();
$header[] = trim($this->safeConvert($value));
}
// 映射表头
$headerMap = [];
foreach ($header as $index => $field) {
$headerMap[$field] = $index;
}
// 读取数据(从第二行开始)
$highestRow = $worksheet->getHighestRow();
for ($row = 2; $row <= $highestRow; $row++) {
$rowData = [];
for ($col = 1; $col <= $highestColumnIndex; $col++) {
$value = $worksheet->getCellByColumnAndRow($col, $row)->getValue();
$rowData[] = $this->safeConvert($value);
}
// 跳过空行
if (empty(array_filter($rowData))) {
continue;
}
// 解析数据
$data = $this->parseImportData($rowData, $headerMap);
// 检查必填字段
if (empty($data['name'])) {
$skipCount++;
continue;
}
// 添加到批次
$batchData[] = $data;
$importCount++;
// 达到批次大小,执行插入
if (count($batchData) >= $batchSize) {
try {
\app\common\model\QywxExternalContact::insertAll($batchData);
} catch (\Exception $e) {
return $this->fail('数据插入失败: ' . $e->getMessage());
}
$batchData = [];
}
}
// 插入剩余数据
if (!empty($batchData)) {
try {
\app\common\model\QywxExternalContact::insertAll($batchData);
} catch (\Exception $e) {
return $this->fail('数据插入失败: ' . $e->getMessage());
}
}
} catch (\Exception $e) {
return $this->fail('XLSX文件解析失败: ' . $e->getMessage());
}
}
return $this->success('导入成功', [
'import_count' => $importCount,
'skip_count' => $skipCount
]);
} catch (\Throwable $e) {
return $this->fail('导入失败: ' . $e->getMessage());
}
}
/**
* @notes 解析导入数据
*/
private function parseImportData($row, $headerMap)
{
// 解析数据
$data = [
'name' => isset($headerMap['客户名称']) ? ($this->safeConvert($row[$headerMap['客户名称']] ?? '')) : '',
'external_user_id' => 'import_' . uniqid(),
'avatar' => '',
'type' => 1,
'gender' => 0,
'unionid' => '',
'position' => isset($headerMap['职务']) ? ($this->safeConvert($row[$headerMap['职务']] ?? '')) : '',
'corp_name' => isset($headerMap['企业']) ? ($this->safeConvert($row[$headerMap['企业']] ?? '')) : '',
'corp_full_name' => isset($headerMap['企业']) ? ($this->safeConvert($row[$headerMap['企业']] ?? '')) : '',
'external_profile' => json_encode([], JSON_UNESCAPED_UNICODE),
'follow_users' => json_encode([[
'name' => isset($headerMap['添加人']) ? ($this->safeConvert($row[$headerMap['添加人']] ?? '')) : '',
'userid' => isset($headerMap['添加人账号']) ? ($this->safeConvert($row[$headerMap['添加人账号']] ?? '')) : ''
]], JSON_UNESCAPED_UNICODE),
'first_staff_name' => isset($headerMap['添加人']) ? ($this->safeConvert($row[$headerMap['添加人']] ?? '')) : '',
'last_staff_name' => isset($headerMap['添加人']) ? ($this->safeConvert($row[$headerMap['添加人']] ?? '')) : '',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
// 处理添加时间
if (isset($headerMap['添加时间'])) {
$addTime = $row[$headerMap['添加时间']] ?? '';
if (!empty($addTime)) {
// 尝试解析时间格式
$timestamp = strtotime($addTime);
if ($timestamp) {
$data['first_add_time'] = date('Y-m-d H:i:s', $timestamp);
$data['last_add_time'] = date('Y-m-d H:i:s', $timestamp);
}
}
}
// 处理其他字段
if (isset($headerMap['手机'])) {
$data['phone'] = $this->safeConvert($row[$headerMap['手机']] ?? '');
}
if (isset($headerMap['邮箱'])) {
$data['email'] = $this->safeConvert($row[$headerMap['邮箱']] ?? '');
}
if (isset($headerMap['地址'])) {
$data['address'] = $this->safeConvert($row[$headerMap['地址']] ?? '');
}
if (isset($headerMap['电话'])) {
$data['phone'] = $this->safeConvert($row[$headerMap['电话']] ?? '');
}
return $data;
}
/**
* @notes 安全的编码转换
*/
private function safeConvert($value)
{
$type = gettype($value);
if ($type === 'NULL') {
return '';
}
if ($type === 'array') {
return json_encode($value, JSON_UNESCAPED_UNICODE);
}
if ($type === 'object') {
$strValue = method_exists($value, '__toString') ? (string)$value : json_encode($value, JSON_UNESCAPED_UNICODE);
if ($strValue === false) {
return '';
}
$result = @mb_convert_encoding($strValue, 'UTF-8', 'auto');
return $result ?: $strValue;
}
if ($type !== 'string') {
if ($type === 'boolean') {
return $value ? '1' : '0';
}
if ($type === 'integer' || $type === 'double') {
return (string)$value;
}
return '';
}
if ($value === '') {
return '';
}
$result = @mb_convert_encoding($value, 'UTF-8', 'auto');
return $result ?: $value;
}
/**
* @notes 递归转换数组中的所有字符串为UTF-8编码
*/
private function recursiveConvert($data)
{
if (is_null($data)) {
return '';
}
if (is_array($data)) {
foreach ($data as $key => $value) {
$data[$key] = $this->recursiveConvert($value);
}
} elseif (is_string($data)) {
$data = mb_convert_encoding($data, 'UTF-8', 'auto');
}
return $data;
}
/**
* @notes 异步同步企业微信客户
*/
public function asyncSync()
{
try {
$limit = (int)input('limit', 30);
$fullSync = (bool)input('full_sync', false);
$syncMode = input('sync_mode', 'all');
$selectedUsers = input('selected_users', []);
$days = (int)input('days', 7); // 最近几天的数据
// 生成同步任务ID
$taskId = uniqid('qywx_sync_');
// 构建同步参数
$params = [
'limit' => $limit,
'full_sync' => $fullSync,
'sync_mode' => $syncMode,
'selected_users' => $selectedUsers,
'days' => $days,
'task_id' => $taskId
];
// 保存任务状态
$cacheKey = 'qywx_sync_task_' . $taskId;
Cache::set($cacheKey, [
'status' => 'pending',
'progress' => 0,
'start_time' => time(),
'params' => $params
], 86400);
// 执行异步同步
$this->executeAsyncSync($params, $taskId);
return $this->success('同步任务已启动', [
'task_id' => $taskId,
'status' => 'pending'
]);
} catch (\Throwable $e) {
return $this->fail('启动同步任务失败: ' . $e->getMessage());
}
}
/**
* @notes 获取同步任务状态
*/
public function getSyncTaskStatus()
{
try {
$taskId = input('task_id', '');
if (empty($taskId)) {
return $this->fail('缺少任务ID');
}
$cacheKey = 'qywx_sync_task_' . $taskId;
$taskInfo = Cache::get($cacheKey);
if (!$taskInfo) {
return $this->fail('任务不存在');
}
return $this->success('获取成功', $taskInfo);
} catch (\Throwable $e) {
return $this->fail('获取任务状态失败: ' . $e->getMessage());
}
}
/**
* @notes 执行异步同步
*/
private function executeAsyncSync($params, $taskId)
{
// 构建命令
$phpPath = PHP_BINARY;
$scriptPath = realpath(__DIR__ . '/../../../../') . '/sync_qywx.php';
// 生成临时参数文件
$paramFile = sys_get_temp_dir() . '/' . $taskId . '.json';
file_put_contents($paramFile, json_encode($params));
// 构建执行命令
$command = "{$phpPath} {$scriptPath} {$paramFile} {$taskId} > NUL 2>&1 &";
// 执行后台命令
exec($command);
}
}