172 lines
6.0 KiB
PHP
Executable File
172 lines
6.0 KiB
PHP
Executable File
<?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 文件字段 file,query / 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());
|
||
}
|
||
}
|