Files
zyt/server/app/adminapi/controller/qywx/MessageController.php
T
2026-05-11 17:49:38 +08:00

172 lines
6.0 KiB
PHP
Executable File
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\MsgArchiveLists;
use app\adminapi\lists\qywx\MsgSendTaskLists;
use app\adminapi\lists\qywx\MsgSessionLists;
use app\adminapi\logic\qywx\MessageLogic;
use app\adminapi\validate\qywx\MessageValidate;
use app\common\service\wechat\QywxMsgArchiveService;
use app\common\service\wechat\WechatWorkService;
use think\facade\Log;
/**
* 企业微信 员工↔客户 消息收发
*
* 接口一览:
* GET qywx.message/session_list 会话列表(左侧)
* GET qywx.message/archive_list 会话消息历史(中间)
* POST qywx.message/mark_read 清零会话未读
* POST qywx.message/send 创建企业群发(员工代发)任务
* GET qywx.message/send_task_list 群发任务列表
* GET qywx.message/send_task_detail 任务送达详情(会回调企微查询)
* GET qywx.message/staff_list 可代发员工(admin 表 work_wechat_userid 不空者)
* GET qywx.message/customer_of_staff 某员工已添加的客户
* POST qywx.message/upload_to_qywx 上传媒体文件到企微,返回 media_id(供附件使用)
* POST qywx.message/pull_archive 手动触发一次存档拉取(便于调试;cron 正常也会跑)
* GET qywx.message/archive_status 会话存档模块状态(SDK/私钥诊断)
*/
class MessageController extends BaseAdminController
{
public function session_list()
{
return $this->dataLists(new MsgSessionLists());
}
public function archive_list()
{
return $this->dataLists(new MsgArchiveLists());
}
public function mark_read()
{
$sessionId = (int) $this->request->post('session_id', 0);
if ($sessionId <= 0) {
return $this->fail('session_id 无效');
}
MessageLogic::markSessionRead($sessionId);
return $this->success('ok');
}
public function send()
{
$params = (new MessageValidate())->post()->goCheck('send');
$result = MessageLogic::createSendTask($params, $this->adminId);
if ($result === false) {
return $this->fail(MessageLogic::getError());
}
return $this->success('已提交,员工手机端确认后将发出', $result);
}
public function send_task_list()
{
return $this->dataLists(new MsgSendTaskLists());
}
public function send_task_detail()
{
$taskId = (int) $this->request->get('task_id', 0);
$cursor = (string) $this->request->get('cursor', '');
if ($taskId <= 0) {
return $this->fail('task_id 无效');
}
return $this->data(MessageLogic::querySendTaskResult($taskId, $cursor));
}
public function staff_list()
{
$keyword = trim((string) $this->request->get('keyword', ''));
return $this->data(MessageLogic::staffList($keyword));
}
public function customer_of_staff()
{
$staff = trim((string) $this->request->get('staff_userid', ''));
$keyword = trim((string) $this->request->get('keyword', ''));
$limit = (int) $this->request->get('limit', 200);
return $this->data(MessageLogic::customerOfStaff($staff, $keyword, $limit));
}
/**
* 上传素材到企业微信,返回 media_id(3 天有效)
*
* 入参:multipart 文件字段 filequery / form 字段 type = image|voice|video|file
*
* 使用场景:前端先上传本地文件到自家服务器(/upload/image 等)得到本地 URL 用于预览;
* 确认发送时再调本接口上传到企微拿 media_id,填入 add_msg_template 的 attachments。
*/
public function upload_to_qywx()
{
$type = (string) $this->request->param('type', 'image');
if (!in_array($type, ['image', 'voice', 'video', 'file'], true)) {
return $this->fail('不支持的媒体类型');
}
$file = $this->request->file('file');
if (!$file) {
return $this->fail('未上传文件');
}
try {
$tempDir = runtime_path() . 'qywx_upload_tmp' . DIRECTORY_SEPARATOR;
if (!is_dir($tempDir)) {
@mkdir($tempDir, 0755, true);
}
$savePath = $tempDir . uniqid('qywx_', true) . '_' . $file->getOriginalName();
// @phpstan-ignore-next-line move returns File
move_uploaded_file($file->getPathname(), $savePath);
$service = new WechatWorkService('customer_contact');
$resp = $service->uploadMedia($type, $savePath, $file->getOriginalName());
@unlink($savePath);
$errcode = isset($resp['errcode']) ? (int) $resp['errcode'] : -1;
if ($errcode !== 0 || empty($resp['media_id'])) {
Log::warning('企微上传素材失败: ' . json_encode($resp, JSON_UNESCAPED_UNICODE));
return $this->fail('企微上传失败: ' . ($resp['errmsg'] ?? '未知错误'));
}
return $this->success('ok', [
'media_id' => (string) $resp['media_id'],
'type' => $resp['type'] ?? $type,
'created_at' => $resp['created_at'] ?? time(),
]);
} catch (\Throwable $e) {
Log::error('upload_to_qywx 异常: ' . $e->getMessage());
return $this->fail($e->getMessage());
}
}
public function pull_archive()
{
$maxBatches = (int) $this->request->param('max_batches', 5);
$download = (bool) $this->request->param('download', false);
$pull = QywxMsgArchiveService::pullLoop(max(1, $maxBatches));
$media = null;
if ($download && $pull['enabled']) {
$media = QywxMsgArchiveService::downloadPendingMedia(200);
}
return $this->data([
'pull' => $pull,
'media' => $media,
]);
}
public function archive_status()
{
return $this->data(MessageLogic::archiveStatus());
}
}