存客宝应用接口初始化
This commit is contained in:
71
application/chukebao/controller/AccountsController.php
Normal file
71
application/chukebao/controller/AccountsController.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class AccountsController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取账号列表(过滤掉后缀为 _offline 与 _delete 的账号)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getList()
|
||||
{
|
||||
try {
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error($e->getMessage(), $e->getCode() ?: 401);
|
||||
}
|
||||
|
||||
if (empty($companyId)) {
|
||||
return ResponseHelper::error('请先登录', 401);
|
||||
}
|
||||
|
||||
$page = max(1, intval($this->request->param('page', 1)));
|
||||
$limit = max(1, intval($this->request->param('limit', 10)));
|
||||
$keyword = trim((string)$this->request->param('keyword', ''));
|
||||
|
||||
$query = Db::table('s2_company_account')
|
||||
->alias('a')
|
||||
->join('users u', 'a.id = u.s2_accountId')
|
||||
->where([
|
||||
['a.departmentId', '=', $companyId],
|
||||
['a.status', '=', 0],
|
||||
])
|
||||
->whereNotLike('a.userName', '%_offline')
|
||||
->whereNotLike('a.userName', '%_delete');
|
||||
|
||||
if ($keyword !== '') {
|
||||
$query->where(function ($subQuery) use ($keyword) {
|
||||
$likeKeyword = '%' . $keyword . '%';
|
||||
$subQuery->whereLike('a.userName', $likeKeyword)
|
||||
->whereOrLike('a.realName', $likeKeyword)
|
||||
->whereOrLike('a.nickname', $likeKeyword);
|
||||
});
|
||||
}
|
||||
|
||||
$total = (clone $query)->count();
|
||||
$list = $query->field([
|
||||
'a.id',
|
||||
'u.id as uid',
|
||||
'a.userName',
|
||||
'a.realName',
|
||||
'a.nickname',
|
||||
'a.departmentId',
|
||||
'a.departmentName',
|
||||
'a.avatar'
|
||||
])
|
||||
->order('a.id', 'desc')
|
||||
->page($page, $limit)
|
||||
->select();
|
||||
|
||||
|
||||
|
||||
return ResponseHelper::success([
|
||||
'total' => $total,
|
||||
'list' => $list,
|
||||
]);
|
||||
}
|
||||
}
|
||||
832
application/chukebao/controller/AiChatController.php
Normal file
832
application/chukebao/controller/AiChatController.php
Normal file
@@ -0,0 +1,832 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\ai\controller\CozeAI;
|
||||
use app\ai\controller\DouBaoAI;
|
||||
use app\api\model\WechatFriendModel;
|
||||
use app\chukebao\controller\TokensRecordController as tokensRecord;
|
||||
use app\chukebao\model\AiSettings;
|
||||
use app\chukebao\model\FriendSettings;
|
||||
use app\chukebao\model\TokensCompany;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Log;
|
||||
|
||||
|
||||
/**
|
||||
* AI聊天控制器
|
||||
* 负责处理与好友的AI对话功能
|
||||
*/
|
||||
class AiChatController extends BaseController
|
||||
{
|
||||
// 对话状态常量
|
||||
const STATUS_CREATED = 'created'; // 对话已创建
|
||||
const STATUS_IN_PROGRESS = 'in_progress'; // 智能体正在处理中
|
||||
const STATUS_COMPLETED = 'completed'; // 智能体已完成处理
|
||||
const STATUS_FAILED = 'failed'; // 对话失败
|
||||
const STATUS_REQUIRES_ACTION = 'requires_action'; // 对话中断,需要进一步处理
|
||||
const STATUS_CANCELED = 'canceled'; // 对话已取消
|
||||
|
||||
// 轮询配置
|
||||
const MAX_RETRY_TIMES = 1000; // 最大重试次数
|
||||
const RETRY_INTERVAL = 500000; // 重试间隔(微秒,即500毫秒)
|
||||
|
||||
// 并发控制
|
||||
const CACHE_EXPIRE = 30; // 缓存过期时间(秒)
|
||||
|
||||
// 请求唯一标识符
|
||||
private $requestKey = '';
|
||||
private $requestId = '';
|
||||
private $currentStep = 0;
|
||||
|
||||
/**
|
||||
* AI聊天主入口
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
try {
|
||||
// 1. 参数验证和初始化
|
||||
$params = $this->validateAndInitParams();
|
||||
|
||||
if ($params === false) {
|
||||
return ResponseHelper::error('参数验证失败');
|
||||
}
|
||||
|
||||
// 并发控制:检查并处理同一用户的重复请求
|
||||
$this->requestKey = "aichat_{$params['friendId']}_{$params['wechatAccountId']}";
|
||||
$this->requestId = uniqid('req_', true);
|
||||
|
||||
$concurrentCheck = $this->handleConcurrentRequest($params);
|
||||
if ($concurrentCheck !== true) {
|
||||
return $concurrentCheck; // 返回错误响应
|
||||
}
|
||||
|
||||
$this->currentStep = 1;
|
||||
|
||||
// 2. 验证Tokens余额
|
||||
$this->updateRequestStep(2);
|
||||
if ($this->isRequestCanceled()) {
|
||||
return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消');
|
||||
}
|
||||
$hasBalance = $this->checkTokensBalance($params['companyId']);
|
||||
|
||||
if (!$hasBalance) {
|
||||
$this->clearRequestCache();
|
||||
return ResponseHelper::error('Tokens余额不足,请充值后再试');
|
||||
}
|
||||
|
||||
// 3. 获取AI配置
|
||||
$this->updateRequestStep(3);
|
||||
if ($this->isRequestCanceled()) {
|
||||
return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消');
|
||||
}
|
||||
$setting = $this->getAiSettings($params['companyId']);
|
||||
|
||||
if (!$setting) {
|
||||
$this->clearRequestCache();
|
||||
return ResponseHelper::error('未找到AI配置信息,请先配置AI策略');
|
||||
}
|
||||
|
||||
// 4. 获取好友AI设置
|
||||
$this->updateRequestStep(4);
|
||||
if ($this->isRequestCanceled()) {
|
||||
return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消');
|
||||
}
|
||||
$friendSettings = $this->getFriendSettings($params['companyId'], $params['friendId']);
|
||||
|
||||
if (!$friendSettings) {
|
||||
$this->clearRequestCache();
|
||||
return ResponseHelper::error('该好友未配置或未开启AI功能');
|
||||
}
|
||||
|
||||
// 5. 确保会话存在
|
||||
$this->updateRequestStep(5);
|
||||
if ($this->isRequestCanceled()) {
|
||||
return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消');
|
||||
}
|
||||
$conversationId = $this->ensureConversation($friendSettings, $setting, $params);
|
||||
|
||||
if (empty($conversationId)) {
|
||||
$this->clearRequestCache();
|
||||
return ResponseHelper::error('创建会话失败');
|
||||
}
|
||||
|
||||
// 6. 获取历史消息
|
||||
$this->updateRequestStep(6);
|
||||
if ($this->isRequestCanceled()) {
|
||||
return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消');
|
||||
}
|
||||
$msgData = $this->getHistoryMessages($params['friendId'], $friendSettings);
|
||||
|
||||
// 7. 创建AI对话(从这步开始需要保存对话ID以便取消)
|
||||
$this->updateRequestStep(7);
|
||||
if ($this->isRequestCanceled($conversationId, null)) {
|
||||
return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消');
|
||||
}
|
||||
$chatId = $this->createAiChat($setting, $friendSettings, $msgData);
|
||||
|
||||
if (empty($chatId)) {
|
||||
$this->clearRequestCache();
|
||||
return ResponseHelper::error('创建对话失败');
|
||||
}
|
||||
|
||||
// 保存对话ID到缓存,以便新请求可以取消
|
||||
$this->updateRequestStep(7, $conversationId, $chatId);
|
||||
|
||||
// 8. 等待AI处理完成(轮询)
|
||||
$this->updateRequestStep(8, $conversationId, $chatId);
|
||||
if ($this->isRequestCanceled($conversationId, $chatId)) {
|
||||
return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消');
|
||||
}
|
||||
$chatResult = $this->waitForChatCompletion($conversationId, $chatId);
|
||||
|
||||
if (!$chatResult['success']) {
|
||||
$this->clearRequestCache();
|
||||
return ResponseHelper::error($chatResult['error']);
|
||||
}
|
||||
|
||||
$chatResult = $chatResult['data'];
|
||||
|
||||
// 9. 扣除Tokens
|
||||
$this->updateRequestStep(9, $conversationId, $chatId);
|
||||
if ($this->isRequestCanceled($conversationId, $chatId)) {
|
||||
return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消');
|
||||
}
|
||||
$this->consumeTokens($chatResult, $params, $friendSettings);
|
||||
|
||||
// 10. 获取对话消息
|
||||
$this->updateRequestStep(10, $conversationId, $chatId);
|
||||
if ($this->isRequestCanceled($conversationId, $chatId)) {
|
||||
return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消');
|
||||
}
|
||||
$messages = $this->getChatMessages($conversationId, $chatId);
|
||||
|
||||
if (!$messages) {
|
||||
return ResponseHelper::error('获取对话消息失败');
|
||||
}
|
||||
|
||||
// 筛选type为answer的消息(AI回复的内容)
|
||||
$answerContent = '';
|
||||
foreach ($messages as $msg) {
|
||||
if (isset($msg['type']) && $msg['type'] === 'answer') {
|
||||
$answerContent = $msg['content'] ?? '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($answerContent)) {
|
||||
Log::warning('未找到AI回复内容,messages: ' . json_encode($messages));
|
||||
return ResponseHelper::error('未获取到AI回复内容');
|
||||
}
|
||||
|
||||
// 清理请求缓存
|
||||
$this->clearRequestCache();
|
||||
|
||||
// 返回结果
|
||||
return ResponseHelper::success(['content' => $answerContent], '对话成功');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('AI聊天异常:' . $e->getMessage());
|
||||
|
||||
// 清理请求缓存
|
||||
$this->clearRequestCache();
|
||||
|
||||
return ResponseHelper::error('系统异常:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消AI对话
|
||||
* 取消当前正在进行的AI对话请求
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function cancel()
|
||||
{
|
||||
try {
|
||||
// 获取参数
|
||||
$friendId = $this->request->param('friendId', '');
|
||||
$wechatAccountId = $this->request->param('wechatAccountId', '');
|
||||
|
||||
if (empty($wechatAccountId) || empty($friendId)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
// 生成缓存键
|
||||
$requestKey = "aichat_{$friendId}_{$wechatAccountId}";
|
||||
|
||||
// 获取缓存数据
|
||||
$cacheData = Cache::get($requestKey);
|
||||
|
||||
if (!$cacheData) {
|
||||
return ResponseHelper::error('当前没有正在进行的AI对话');
|
||||
}
|
||||
|
||||
$requestId = $cacheData['request_id'] ?? '';
|
||||
$step = $cacheData['step'] ?? 0;
|
||||
$conversationId = $cacheData['conversation_id'] ?? '';
|
||||
$chatId = $cacheData['chat_id'] ?? '';
|
||||
|
||||
Log::info("手动取消AI对话 - 请求ID: {$requestId}, 步骤: {$step}");
|
||||
|
||||
// 如果已经到达步骤7或之后,需要调用取消API
|
||||
if ($step >= 7 && !empty($conversationId) && !empty($chatId)) {
|
||||
try {
|
||||
$cozeAI = new CozeAI();
|
||||
$cancelResult = $cozeAI->cancelConversationChat([
|
||||
'conversation_id' => $conversationId,
|
||||
'chat_id' => $chatId,
|
||||
]);
|
||||
|
||||
$result = json_decode($cancelResult, true);
|
||||
if ($result['code'] != 200) {
|
||||
Log::error("调用取消API失败 - conversation_id: {$conversationId}, chat_id: {$chatId}, 错误: " . ($result['msg'] ?? '未知错误'));
|
||||
} else {
|
||||
Log::info("成功调用取消API - conversation_id: {$conversationId}, chat_id: {$chatId}");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error("调用取消API异常:" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
Cache::rm($requestKey);
|
||||
Log::info("已清理AI对话缓存 - 请求ID: {$requestId}");
|
||||
|
||||
return ResponseHelper::success([
|
||||
'canceled_request_id' => $requestId,
|
||||
'step' => $step
|
||||
], 'AI对话已取消');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('取消AI对话异常:' . $e->getMessage());
|
||||
return ResponseHelper::error('系统异常:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证和初始化参数
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
private function validateAndInitParams()
|
||||
{
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$friendId = $this->request->param('friendId', '');
|
||||
$wechatAccountId = $this->request->param('wechatAccountId', '');
|
||||
|
||||
if (empty($wechatAccountId) || empty($friendId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return [
|
||||
'userId' => $userId,
|
||||
'companyId' => $companyId,
|
||||
'friendId' => $friendId,
|
||||
'wechatAccountId' => $wechatAccountId
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Tokens余额
|
||||
*
|
||||
* @param int $companyId 公司ID
|
||||
* @return bool
|
||||
*/
|
||||
private function checkTokensBalance($companyId)
|
||||
{
|
||||
$tokens = TokensCompany::where(['companyId' => $companyId])->value('tokens');
|
||||
return !empty($tokens) && $tokens > 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AI配置
|
||||
*
|
||||
* @param int $companyId 公司ID
|
||||
* @return AiSettings|null
|
||||
*/
|
||||
private function getAiSettings($companyId)
|
||||
{
|
||||
return AiSettings::where(['companyId' => $companyId])->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取好友AI设置
|
||||
*
|
||||
* @param int $companyId 公司ID
|
||||
* @param string $friendId 好友ID
|
||||
* @return FriendSettings|null
|
||||
*/
|
||||
private function getFriendSettings($companyId, $friendId)
|
||||
{
|
||||
$friendSettings = FriendSettings::where([
|
||||
'companyId' => $companyId,
|
||||
'friendId' => $friendId
|
||||
])->find();
|
||||
|
||||
if (empty($friendSettings) || $friendSettings->type == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $friendSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保会话存在
|
||||
*
|
||||
* @param FriendSettings $friendSettings 好友设置
|
||||
* @param AiSettings $setting AI设置
|
||||
* @param array $params 参数
|
||||
* @return string|null 会话ID
|
||||
*/
|
||||
private function ensureConversation($friendSettings, $setting, $params)
|
||||
{
|
||||
if (!empty($friendSettings->conversationId)) {
|
||||
return $friendSettings->conversationId;
|
||||
}
|
||||
|
||||
// 创建新会话
|
||||
$cozeAI = new CozeAI();
|
||||
$data = [
|
||||
'bot_id' => $setting->botId,
|
||||
'name' => '与好友' . $params['friendId'] . '的对话',
|
||||
'meta_data' => [
|
||||
'friendId' => (string)$friendSettings->friendId,
|
||||
'wechatAccountId' => (string)$params['wechatAccountId'],
|
||||
],
|
||||
];
|
||||
|
||||
$res = $cozeAI->createConversation($data);
|
||||
$res = json_decode($res, true);
|
||||
|
||||
if ($res['code'] != 200) {
|
||||
Log::error('创建会话失败:' . ($res['msg'] ?? '未知错误'));
|
||||
return null;
|
||||
}
|
||||
|
||||
// 保存会话ID
|
||||
$conversationId = $res['data']['id'];
|
||||
$friendSettings->conversationId = $conversationId;
|
||||
$friendSettings->conversationTime = time();
|
||||
$friendSettings->save();
|
||||
return $conversationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取历史消息
|
||||
*
|
||||
* @param string $friendId 好友ID
|
||||
* @param FriendSettings $friendSettings 好友设置
|
||||
* @return array
|
||||
*/
|
||||
private function getHistoryMessages($friendId, $friendSettings)
|
||||
{
|
||||
$msgData = [];
|
||||
|
||||
// 会话创建时间小于1分钟,加载最近10条消息
|
||||
if ($friendSettings->conversationTime >= time() - 60) {
|
||||
$messages = Db::table('s2_wechat_message')
|
||||
->where('wechatFriendId', $friendId)
|
||||
->where('msgType', '<', 50)
|
||||
->order('wechatTime desc')
|
||||
->field('id,content,msgType,isSend,wechatTime')
|
||||
->limit(10)
|
||||
->select();
|
||||
|
||||
// 按时间正序排列
|
||||
usort($messages, function ($a, $b) {
|
||||
return $a['wechatTime'] <=> $b['wechatTime'];
|
||||
});
|
||||
|
||||
// 处理聊天数据
|
||||
foreach ($messages as $val) {
|
||||
if (empty($val['content'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$msg = [
|
||||
'role' => empty($val['isSend']) ? 'user' : 'assistant',
|
||||
'content' => $val['content'],
|
||||
'type' => empty($val['isSend']) ? 'question' : 'answer',
|
||||
'content_type' => 'text'
|
||||
];
|
||||
$msgData[] = $msg;
|
||||
}
|
||||
} else {
|
||||
// 只加载最新一条用户消息
|
||||
$message = Db::table('s2_wechat_message')
|
||||
->where('wechatFriendId', $friendId)
|
||||
->where('msgType', '<', 50)
|
||||
->where('isSend', 0)
|
||||
->order('wechatTime desc')
|
||||
->field('id,content,msgType,isSend,wechatTime')
|
||||
->find();
|
||||
|
||||
if (!empty($message) && !empty($message['content'])) {
|
||||
$msgData[] = [
|
||||
'role' => 'user',
|
||||
'content' => $message['content'],
|
||||
'type' => 'question',
|
||||
'content_type' => 'text'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $msgData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建AI对话
|
||||
*
|
||||
* @param AiSettings $setting AI设置
|
||||
* @param FriendSettings $friendSettings 好友设置
|
||||
* @param array $msgData 消息数据
|
||||
* @return string|null 对话ID
|
||||
*/
|
||||
private function createAiChat($setting, $friendSettings, $msgData)
|
||||
{
|
||||
$cozeAI = new CozeAI();
|
||||
$data = [
|
||||
'bot_id' => $setting->botId,
|
||||
'uid' => $friendSettings->friendId,
|
||||
'conversation_id' => $friendSettings->conversationId,
|
||||
'question' => $msgData,
|
||||
];
|
||||
|
||||
$res = $cozeAI->createChat($data);
|
||||
$res = json_decode($res, true);
|
||||
|
||||
if ($res['code'] != 200) {
|
||||
Log::error('创建对话失败:' . ($res['msg'] ?? '未知错误'));
|
||||
return null;
|
||||
}
|
||||
|
||||
return $res['data']['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待AI处理完成(轮询机制)
|
||||
*
|
||||
* @param string $conversationId 会话ID
|
||||
* @param string $chatId 对话ID
|
||||
* @return array ['success' => bool, 'data' => array|null, 'error' => string]
|
||||
*/
|
||||
private function waitForChatCompletion($conversationId, $chatId)
|
||||
{
|
||||
$cozeAI = new CozeAI();
|
||||
$retryCount = 0;
|
||||
|
||||
while ($retryCount < self::MAX_RETRY_TIMES) {
|
||||
// 获取对话状态
|
||||
$res = $cozeAI->getConversationChat([
|
||||
'conversation_id' => $conversationId,
|
||||
'chat_id' => $chatId,
|
||||
]);
|
||||
$res = json_decode($res, true);
|
||||
|
||||
if ($res['code'] != 200) {
|
||||
$errorMsg = 'AI接口调用失败:' . ($res['msg'] ?? '未知错误');
|
||||
Log::error($errorMsg);
|
||||
return ['success' => false, 'data' => null, 'error' => $errorMsg];
|
||||
}
|
||||
|
||||
$status = $res['data']['status'] ?? '';
|
||||
|
||||
// 处理不同的状态
|
||||
switch ($status) {
|
||||
case self::STATUS_COMPLETED:
|
||||
// 对话完成,返回结果
|
||||
return ['success' => true, 'data' => $res['data'], 'error' => ''];
|
||||
|
||||
case self::STATUS_IN_PROGRESS:
|
||||
case self::STATUS_CREATED:
|
||||
// 继续等待
|
||||
$retryCount++;
|
||||
usleep(self::RETRY_INTERVAL);
|
||||
break;
|
||||
|
||||
case self::STATUS_FAILED:
|
||||
$errorMsg = 'AI对话处理失败';
|
||||
Log::error($errorMsg . ',chat_id: ' . $chatId);
|
||||
return ['success' => false, 'data' => null, 'error' => $errorMsg];
|
||||
|
||||
case self::STATUS_CANCELED:
|
||||
$errorMsg = 'AI对话已被取消';
|
||||
Log::error($errorMsg . ',chat_id: ' . $chatId);
|
||||
return ['success' => false, 'data' => null, 'error' => $errorMsg];
|
||||
|
||||
case self::STATUS_REQUIRES_ACTION:
|
||||
$errorMsg = 'AI对话需要进一步处理';
|
||||
Log::warning($errorMsg . ',chat_id: ' . $chatId);
|
||||
return ['success' => false, 'data' => null, 'error' => $errorMsg];
|
||||
|
||||
default:
|
||||
$errorMsg = 'AI返回未知状态:' . $status;
|
||||
Log::error($errorMsg);
|
||||
return ['success' => false, 'data' => null, 'error' => $errorMsg];
|
||||
}
|
||||
}
|
||||
|
||||
// 超时
|
||||
$errorMsg = 'AI对话处理超时,已等待' . (self::MAX_RETRY_TIMES * self::RETRY_INTERVAL / 1000000) . '秒';
|
||||
Log::error($errorMsg . ',chat_id: ' . $chatId);
|
||||
return ['success' => false, 'data' => null, 'error' => $errorMsg];
|
||||
}
|
||||
|
||||
/**
|
||||
* 扣除Tokens
|
||||
*
|
||||
* @param array $chatResult 对话结果
|
||||
* @param array $params 参数
|
||||
* @param FriendSettings $friendSettings 好友设置
|
||||
*/
|
||||
private function consumeTokens($chatResult, $params, $friendSettings)
|
||||
{
|
||||
$tokenCount = $chatResult['usage']['token_count'] ?? 0;
|
||||
|
||||
if (empty($tokenCount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取好友昵称
|
||||
$nickname = WechatFriendModel::where('id', $friendSettings->friendId)->value('nickname');
|
||||
$remarks = !empty($nickname) ? '与好友【' . $nickname . '】聊天' : '与好友聊天';
|
||||
|
||||
// 扣除Tokens
|
||||
$tokensRecord = new tokensRecord();
|
||||
$data = [
|
||||
'tokens' => $tokenCount * 20,
|
||||
'type' => 0,
|
||||
'form' => 13,
|
||||
'wechatAccountId' => $params['wechatAccountId'],
|
||||
'friendIdOrGroupId' => $params['friendId'],
|
||||
'remarks' => $remarks,
|
||||
];
|
||||
|
||||
$tokensRecord->consumeTokens($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对话消息
|
||||
*
|
||||
* @param string $conversationId 会话ID
|
||||
* @param string $chatId 对话ID
|
||||
* @return array|null
|
||||
*/
|
||||
private function getChatMessages($conversationId, $chatId)
|
||||
{
|
||||
$cozeAI = new CozeAI();
|
||||
$res = $cozeAI->listConversationMessage([
|
||||
'conversation_id' => $conversationId,
|
||||
'chat_id' => $chatId,
|
||||
]);
|
||||
$res = json_decode($res, true);
|
||||
|
||||
if ($res['code'] != 200) {
|
||||
Log::error('获取对话消息失败:' . ($res['msg'] ?? '未知错误'));
|
||||
return null;
|
||||
}
|
||||
|
||||
return $res['data'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理并发请求
|
||||
* 检查是否有同一用户的旧请求正在处理,如果有则取消旧请求
|
||||
*
|
||||
* @param array $params 请求参数
|
||||
* @return true|\think\response\Json true表示可以继续,否则返回错误响应
|
||||
*/
|
||||
private function handleConcurrentRequest($params)
|
||||
{
|
||||
$cacheData = Cache::get($this->requestKey);
|
||||
|
||||
if ($cacheData) {
|
||||
// 有旧请求正在处理
|
||||
$oldRequestId = $cacheData['request_id'] ?? '';
|
||||
$oldStep = $cacheData['step'] ?? 0;
|
||||
$oldConversationId = $cacheData['conversation_id'] ?? '';
|
||||
$oldChatId = $cacheData['chat_id'] ?? '';
|
||||
|
||||
Log::info("检测到并发请求 - 旧请求: {$oldRequestId} (步骤{$oldStep}), 新请求: {$this->requestId}");
|
||||
|
||||
// 如果旧请求已经到达步骤7或之后,需要调用取消API
|
||||
if ($oldStep >= 7 && !empty($oldConversationId) && !empty($oldChatId)) {
|
||||
try {
|
||||
$cozeAI = new CozeAI();
|
||||
$cancelResult = $cozeAI->cancelConversationChat([
|
||||
'conversation_id' => $oldConversationId,
|
||||
'chat_id' => $oldChatId,
|
||||
]);
|
||||
Log::info("已调用取消API取消旧请求的对话 - conversation_id: {$oldConversationId}, chat_id: {$oldChatId}");
|
||||
} catch (\Exception $e) {
|
||||
Log::error("取消旧请求对话失败:" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 标记旧请求为已取消(通过更新缓存的 canceled 标志)
|
||||
$cacheData['canceled'] = true;
|
||||
$cacheData['canceled_by'] = $this->requestId;
|
||||
Cache::set($this->requestKey, $cacheData, self::CACHE_EXPIRE);
|
||||
}
|
||||
|
||||
// 设置当前请求为活动请求
|
||||
$newCacheData = [
|
||||
'request_id' => $this->requestId,
|
||||
'step' => 1,
|
||||
'start_time' => time(),
|
||||
'canceled' => false,
|
||||
'conversation_id' => '',
|
||||
'chat_id' => '',
|
||||
];
|
||||
Cache::set($this->requestKey, $newCacheData, self::CACHE_EXPIRE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前请求是否被新请求取消
|
||||
*
|
||||
* @param string $conversationId 会话ID(可选,用于取消对话)
|
||||
* @param string $chatId 对话ID(可选,用于取消对话)
|
||||
* @return bool
|
||||
*/
|
||||
private function isRequestCanceled($conversationId = '', $chatId = '')
|
||||
{
|
||||
$cacheData = Cache::get($this->requestKey);
|
||||
|
||||
if (!$cacheData) {
|
||||
// 缓存不存在,说明被清理或过期,视为被取消
|
||||
return true;
|
||||
}
|
||||
|
||||
$currentRequestId = $cacheData['request_id'] ?? '';
|
||||
$isCanceled = $cacheData['canceled'] ?? false;
|
||||
|
||||
// 如果缓存中的请求ID与当前请求ID不一致,或者被标记为取消
|
||||
if ($currentRequestId !== $this->requestId || $isCanceled) {
|
||||
Log::info("当前请求已被取消 - 请求ID: {$this->requestId}, 缓存请求ID: {$currentRequestId}, 取消标志: " . ($isCanceled ? 'true' : 'false'));
|
||||
|
||||
// 如果提供了对话ID,尝试取消对话
|
||||
if (!empty($conversationId) && !empty($chatId) && $this->currentStep >= 7) {
|
||||
try {
|
||||
$cozeAI = new CozeAI();
|
||||
$cancelResult = $cozeAI->cancelConversationChat([
|
||||
'conversation_id' => $conversationId,
|
||||
'chat_id' => $chatId,
|
||||
]);
|
||||
Log::info("已取消当前请求的对话 - conversation_id: {$conversationId}, chat_id: {$chatId}");
|
||||
} catch (\Exception $e) {
|
||||
Log::error("取消当前请求对话失败:" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新请求步骤
|
||||
*
|
||||
* @param int $step 当前步骤
|
||||
* @param string $conversationId 会话ID(可选)
|
||||
* @param string $chatId 对话ID(可选)
|
||||
*/
|
||||
private function updateRequestStep($step, $conversationId = '', $chatId = '')
|
||||
{
|
||||
$this->currentStep = $step;
|
||||
|
||||
$cacheData = Cache::get($this->requestKey);
|
||||
|
||||
if ($cacheData && $cacheData['request_id'] === $this->requestId) {
|
||||
$cacheData['step'] = $step;
|
||||
$cacheData['update_time'] = time();
|
||||
|
||||
if (!empty($conversationId)) {
|
||||
$cacheData['conversation_id'] = $conversationId;
|
||||
}
|
||||
if (!empty($chatId)) {
|
||||
$cacheData['chat_id'] = $chatId;
|
||||
}
|
||||
|
||||
Cache::set($this->requestKey, $cacheData, self::CACHE_EXPIRE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理请求缓存
|
||||
*/
|
||||
private function clearRequestCache()
|
||||
{
|
||||
if (!empty($this->requestKey)) {
|
||||
$cacheData = Cache::get($this->requestKey);
|
||||
|
||||
// 只有当前请求才能清理自己的缓存
|
||||
if ($cacheData && isset($cacheData['request_id']) && $cacheData['request_id'] === $this->requestId) {
|
||||
Cache::rm($this->requestKey);
|
||||
Log::info("已清理请求缓存 - 请求ID: {$this->requestId}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function index2222()
|
||||
{
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$friendId = $this->request->param('friendId', '');
|
||||
$wechatAccountId = $this->request->param('wechatAccountId', '');
|
||||
$content = $this->request->param('content', '');
|
||||
|
||||
if (empty($wechatAccountId) || empty($friendId)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$tokens = TokensCompany::where(['companyId' => $companyId])->value('tokens');
|
||||
if (empty($tokens) || $tokens <= 0) {
|
||||
return ResponseHelper::error('用户Tokens余额不足');
|
||||
}
|
||||
|
||||
|
||||
//读取AI配置
|
||||
$setting = Db::name('ai_settings')->where(['companyId' => $companyId, 'userId' => $userId])->find();
|
||||
if (empty($setting)) {
|
||||
return ResponseHelper::error('未找到配置信息,请先配置AI策略');
|
||||
}
|
||||
$config = json_decode($setting['config'], true);
|
||||
$modelSetting = $config['modelSetting'];
|
||||
$round = isset($config['round']) ? $config['round'] : 10;
|
||||
|
||||
|
||||
// 导出聊天
|
||||
$messages = Db::table('s2_wechat_message')
|
||||
->where('wechatFriendId', $friendId)
|
||||
->order('wechatTime desc')
|
||||
->field('id,content,msgType,isSend,wechatTime')
|
||||
->limit($round)
|
||||
->select();
|
||||
|
||||
usort($messages, function ($a, $b) {
|
||||
return $a['wechatTime'] <=> $b['wechatTime'];
|
||||
});
|
||||
|
||||
//处理聊天数据
|
||||
$msg = [];
|
||||
foreach ($messages as $val) {
|
||||
if (empty($val['content'])) {
|
||||
continue;
|
||||
}
|
||||
if (!empty($val['isSend'])) {
|
||||
$msg[] = '客服:' . $val['content'];
|
||||
} else {
|
||||
$msg[] = '用户:' . $val['content'];
|
||||
}
|
||||
}
|
||||
$content = implode("\n", $msg);
|
||||
|
||||
|
||||
$params = [
|
||||
'model' => 'doubao-1-5-pro-32k-250115',
|
||||
'messages' => [
|
||||
// ['role' => 'system', 'content' => '请完成跟客户的对话'],
|
||||
['role' => 'system', 'content' => '角色设定:' . $modelSetting['role']],
|
||||
['role' => 'system', 'content' => '公司背景:' . $modelSetting['businessBackground']],
|
||||
['role' => 'system', 'content' => '对话风格:' . $modelSetting['dialogueStyle']],
|
||||
['role' => 'user', 'content' => $content],
|
||||
],
|
||||
];
|
||||
|
||||
//AI处理
|
||||
$ai = new DouBaoAI();
|
||||
$res = $ai->text($params);
|
||||
$res = json_decode($res, true);
|
||||
|
||||
if ($res['code'] == 200) {
|
||||
//扣除Tokens
|
||||
$tokensRecord = new tokensRecord();
|
||||
$nickname = Db::table('s2_wechat_friend')->where(['id' => $friendId])->value('nickname');
|
||||
$remarks = !empty($nickname) ? '与好友【' . $nickname . '】聊天' : '与好友聊天';
|
||||
$data = [
|
||||
'tokens' => $res['data']['token'],
|
||||
'type' => 0,
|
||||
'form' => 13,
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'friendIdOrGroupId' => $friendId,
|
||||
'remarks' => $remarks,
|
||||
];
|
||||
$tokensRecord->consumeTokens($data);
|
||||
return ResponseHelper::success($res['data']['content']);
|
||||
} else {
|
||||
return ResponseHelper::error($res['msg']);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
505
application/chukebao/controller/AiPushController.php
Normal file
505
application/chukebao/controller/AiPushController.php
Normal file
@@ -0,0 +1,505 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\AiPush;
|
||||
use app\chukebao\model\AiPushRecord;
|
||||
use app\chukebao\model\AutoGreetings;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class AiPushController extends BaseController
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取推送列表
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getList()
|
||||
{
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
$where = [
|
||||
['companyId', '=', $companyId],
|
||||
['userId', '=', $userId],
|
||||
['isDel', '=', 0],
|
||||
];
|
||||
|
||||
if (!empty($keyword)) {
|
||||
$where[] = ['name', 'like', '%' . $keyword . '%'];
|
||||
}
|
||||
|
||||
$query = AiPush::where($where);
|
||||
$total = $query->count();
|
||||
$list = $query->where($where)->page($page, $limit)->order('id desc')->select();
|
||||
|
||||
// 处理数据
|
||||
$list = is_array($list) ? $list : $list->toArray();
|
||||
foreach ($list as &$item) {
|
||||
// 解析标签数组
|
||||
$item['tags'] = json_decode($item['tags'], true);
|
||||
if (!is_array($item['tags'])) {
|
||||
$item['tags'] = [];
|
||||
}
|
||||
// 格式化推送时机显示文本
|
||||
$timingTypes = [
|
||||
1 => '立即推送',
|
||||
2 => 'AI最佳时机',
|
||||
3 => '定时推送'
|
||||
];
|
||||
$item['timingText'] = $timingTypes[$item['pushTiming']] ?? '未知';
|
||||
// 处理定时推送时间
|
||||
if ($item['pushTiming'] == 3 && !empty($item['scheduledTime'])) {
|
||||
$item['scheduledTime'] = date('Y-m-d H:i:s', $item['scheduledTime']);
|
||||
} else {
|
||||
$item['scheduledTime'] = '';
|
||||
}
|
||||
// 从记录表计算实际成功率
|
||||
$pushId = $item['id'];
|
||||
$totalCount = Db::name('kf_ai_push_record')
|
||||
->where('pushId', $pushId)
|
||||
->count();
|
||||
$sendCount = Db::name('kf_ai_push_record')
|
||||
->where('pushId', $pushId)
|
||||
->where('isSend', 1)
|
||||
->count();
|
||||
$item['successRate'] = $totalCount > 0 ? round(($sendCount * 100) / $totalCount, 1) : 0;
|
||||
$item['totalPushCount'] = $totalCount; // 推送总数
|
||||
$item['sendCount'] = $sendCount; // 成功发送数
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return ResponseHelper::success(['list' => $list, 'total' => $total]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$name = $this->request->param('name', '');
|
||||
$tags = $this->request->param('tags', ''); // 标签,支持逗号分隔的字符串或数组
|
||||
$content = $this->request->param('content', '');
|
||||
$pushTiming = $this->request->param('pushTiming', 1); // 1=立即推送,2=最佳时机(AI决定),3=定时推送
|
||||
$scheduledTime = $this->request->param('scheduledTime', ''); // 定时推送的时间
|
||||
$status = $this->request->param('status', 1);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($name) || empty($content)) {
|
||||
return ResponseHelper::error('推送名称和推送内容不能为空');
|
||||
}
|
||||
|
||||
// 验证推送时机
|
||||
if (!in_array($pushTiming, [1, 2, 3])) {
|
||||
return ResponseHelper::error('无效的推送时机类型');
|
||||
}
|
||||
|
||||
// 如果是定时推送,需要验证时间
|
||||
if ($pushTiming == 3) {
|
||||
if (empty($scheduledTime)) {
|
||||
return ResponseHelper::error('定时推送需要设置推送时间');
|
||||
}
|
||||
// 验证时间格式
|
||||
$timestamp = strtotime($scheduledTime);
|
||||
if ($timestamp === false || $timestamp <= time()) {
|
||||
return ResponseHelper::error('定时推送时间格式不正确或必须大于当前时间');
|
||||
}
|
||||
} else {
|
||||
$scheduledTime = '';
|
||||
}
|
||||
|
||||
// 处理标签
|
||||
$tagsArray = [];
|
||||
if (!empty($tags)) {
|
||||
if (is_string($tags)) {
|
||||
// 如果是字符串,按逗号分割
|
||||
$tagsArray = array_filter(array_map('trim', explode(',', $tags)));
|
||||
} elseif (is_array($tags)) {
|
||||
$tagsArray = array_filter(array_map('trim', $tags));
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($tagsArray)) {
|
||||
return ResponseHelper::error('目标用户标签不能为空');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$aiPush = new AiPush();
|
||||
$aiPush->name = $name;
|
||||
$aiPush->tags = json_encode($tagsArray, JSON_UNESCAPED_UNICODE);
|
||||
$aiPush->content = $content;
|
||||
$aiPush->pushTiming = $pushTiming;
|
||||
$aiPush->scheduledTime = $pushTiming == 3 && !empty($scheduledTime) ? strtotime($scheduledTime) : 0;
|
||||
$aiPush->status = $status;
|
||||
$aiPush->successRate = 0; // 初始成功率为0
|
||||
$aiPush->userId = $userId;
|
||||
$aiPush->companyId = $companyId;
|
||||
$aiPush->createTime = time();
|
||||
$aiPush->updateTime = time();
|
||||
$aiPush->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(['id' => $aiPush->id], '创建成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('创建失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 详情
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function details()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($id)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$data = AiPush::where(['id' => $id, 'isDel' => 0, 'userId' => $userId, 'companyId' => $companyId])->find();
|
||||
if (empty($data)) {
|
||||
return ResponseHelper::error('该推送已被删除或者不存在');
|
||||
}
|
||||
|
||||
$data = $data->toArray();
|
||||
// 解析标签数组
|
||||
$data['tags'] = json_decode($data['tags'], true);
|
||||
if (!is_array($data['tags'])) {
|
||||
$data['tags'] = [];
|
||||
}
|
||||
// 标签转为逗号分隔的字符串(用于编辑时回显)
|
||||
$data['tagsString'] = implode(',', $data['tags']);
|
||||
|
||||
// 处理定时推送时间
|
||||
if ($data['pushTiming'] == 3 && !empty($data['scheduledTime'])) {
|
||||
$data['scheduledTime'] = date('Y-m-d H:i:s', $data['scheduledTime']);
|
||||
} else {
|
||||
$data['scheduledTime'] = '';
|
||||
}
|
||||
|
||||
// 成功率保留一位小数
|
||||
$data['successRate'] = isset($data['successRate']) ? round($data['successRate'], 1) : 0;
|
||||
|
||||
return ResponseHelper::success($data, '获取成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function del()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($id)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$data = AiPush::where(['id' => $id, 'isDel' => 0, 'userId' => $userId, 'companyId' => $companyId])->find();
|
||||
if (empty($data)) {
|
||||
return ResponseHelper::error('该推送已被删除或者不存在');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$data->isDel = 1;
|
||||
$data->delTime = time();
|
||||
$data->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success('', '删除成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('删除失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$name = $this->request->param('name', '');
|
||||
$tags = $this->request->param('tags', '');
|
||||
$content = $this->request->param('content', '');
|
||||
$pushTiming = $this->request->param('pushTiming', 1);
|
||||
$scheduledTime = $this->request->param('scheduledTime', '');
|
||||
$status = $this->request->param('status', 1);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($id) || empty($name) || empty($content)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
// 验证推送时机
|
||||
if (!in_array($pushTiming, [1, 2, 3])) {
|
||||
return ResponseHelper::error('无效的推送时机类型');
|
||||
}
|
||||
|
||||
// 如果是定时推送,需要验证时间
|
||||
if ($pushTiming == 3) {
|
||||
if (empty($scheduledTime)) {
|
||||
return ResponseHelper::error('定时推送需要设置推送时间');
|
||||
}
|
||||
// 验证时间格式
|
||||
$timestamp = strtotime($scheduledTime);
|
||||
if ($timestamp === false || $timestamp <= time()) {
|
||||
return ResponseHelper::error('定时推送时间格式不正确或必须大于当前时间');
|
||||
}
|
||||
} else {
|
||||
$scheduledTime = '';
|
||||
}
|
||||
|
||||
// 处理标签
|
||||
$tagsArray = [];
|
||||
if (!empty($tags)) {
|
||||
if (is_string($tags)) {
|
||||
$tagsArray = array_filter(array_map('trim', explode(',', $tags)));
|
||||
} elseif (is_array($tags)) {
|
||||
$tagsArray = array_filter(array_map('trim', $tags));
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($tagsArray)) {
|
||||
return ResponseHelper::error('目标用户标签不能为空');
|
||||
}
|
||||
|
||||
$query = AiPush::where(['id' => $id, 'isDel' => 0, 'userId' => $userId, 'companyId' => $companyId])->find();
|
||||
if (empty($query)) {
|
||||
return ResponseHelper::error('该推送已被删除或者不存在');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$query->name = $name;
|
||||
$query->tags = json_encode($tagsArray, JSON_UNESCAPED_UNICODE);
|
||||
$query->content = $content;
|
||||
$query->pushTiming = $pushTiming;
|
||||
$query->scheduledTime = $pushTiming == 3 && !empty($scheduledTime) ? strtotime($scheduledTime) : 0;
|
||||
$query->status = $status;
|
||||
$query->updateTime = time();
|
||||
$query->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success('', '修改成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('修改失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改状态
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setStatus()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$status = $this->request->param('status', 1);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($id)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
if (!in_array($status, [0, 1])) {
|
||||
return ResponseHelper::error('状态值无效');
|
||||
}
|
||||
|
||||
$data = AiPush::where(['id' => $id, 'isDel' => 0, 'userId' => $userId, 'companyId' => $companyId])->find();
|
||||
if (empty($data)) {
|
||||
return ResponseHelper::error('该推送已被删除或者不存在');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$data->status = $status;
|
||||
$data->updateTime = time();
|
||||
$data->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success('', $status == 1 ? '启用成功' : '禁用成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('操作失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计概览(整合自动问候和AI推送)
|
||||
* - 活跃规则(自动问候规则,近30天)
|
||||
* - 总触发次数(自动问候记录总数)
|
||||
* - AI推送成功率(AI推送的成功率)
|
||||
* - AI智能推送(AI推送规则,近30天活跃)
|
||||
* - 规则效果排行(自动问候规则,按使用次数排序)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function stats()
|
||||
{
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$userId = $this->getUserInfo('id');
|
||||
|
||||
$start30d = time() - 30 * 24 * 3600;
|
||||
|
||||
try {
|
||||
// 公司维度(用于除排行外的统计)
|
||||
$companyWhere = [
|
||||
['companyId', '=', $companyId],
|
||||
];
|
||||
// 排行维度(限定个人)
|
||||
$rankingWhere = [
|
||||
['companyId', '=', $companyId],
|
||||
['userId', '=', $userId],
|
||||
];
|
||||
|
||||
// ========== 自动问候统计 ==========
|
||||
|
||||
// 1) 活跃规则(自动问候规则,近30天有记录的)
|
||||
$activeRules = Db::name('kf_auto_greetings_record')
|
||||
->where($companyWhere)
|
||||
->where('createTime', '>=', $start30d)
|
||||
->distinct(true)
|
||||
->count('autoId');
|
||||
|
||||
// 2) 总触发次数(自动问候记录总数)
|
||||
$totalTriggers = Db::name('kf_auto_greetings_record')
|
||||
->where($companyWhere)
|
||||
->count();
|
||||
|
||||
// ========== AI推送统计 ==========
|
||||
|
||||
// 3) AI推送成功率
|
||||
$totalPushes = Db::name('kf_ai_push_record')
|
||||
->where($companyWhere)
|
||||
->count();
|
||||
$sendCount = Db::name('kf_ai_push_record')
|
||||
->where($companyWhere)
|
||||
->where('isSend', '=', 1)
|
||||
->count();
|
||||
// 成功率:百分比,保留整数(75%)
|
||||
$aiPushSuccessRate = $totalPushes > 0 ? round(($sendCount * 100) / $totalPushes, 0) : 0;
|
||||
|
||||
// 4) AI智能推送(AI推送规则,近30天活跃的)
|
||||
$aiPushCount = Db::name('kf_ai_push_record')
|
||||
->where($companyWhere)
|
||||
->where('createTime', '>=', $start30d)
|
||||
->distinct(true)
|
||||
->count('pushId');
|
||||
|
||||
// ========== 规则效果排行(自动问候规则,按使用次数排序)==========
|
||||
$ruleRanking = Db::name('kf_auto_greetings_record')
|
||||
->where($rankingWhere)
|
||||
->field([
|
||||
'autoId AS id',
|
||||
'COUNT(*) AS usageCount'
|
||||
])
|
||||
->group('autoId')
|
||||
->order('usageCount DESC')
|
||||
->limit(20)
|
||||
->select();
|
||||
|
||||
// 附加规则名称和触发类型
|
||||
$autoIds = array_values(array_unique(array_column($ruleRanking, 'id')));
|
||||
$autoIdToRule = [];
|
||||
if (!empty($autoIds)) {
|
||||
$rules = AutoGreetings::where([['id', 'in', $autoIds]])
|
||||
->field('id,name,trigger')
|
||||
->select();
|
||||
foreach ($rules as $rule) {
|
||||
$triggerTypes = [
|
||||
1 => '新好友',
|
||||
2 => '首次发消息',
|
||||
3 => '时间触发',
|
||||
4 => '关键词',
|
||||
5 => '生日触发',
|
||||
6 => '自定义'
|
||||
];
|
||||
$autoIdToRule[$rule['id']] = [
|
||||
'name' => $rule['name'],
|
||||
'trigger' => $rule['trigger'],
|
||||
'triggerText' => $triggerTypes[$rule['trigger']] ?? '未知',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($ruleRanking as &$row) {
|
||||
$row['usageCount'] = (int)($row['usageCount'] ?? 0);
|
||||
$row['name'] = $autoIdToRule[$row['id']]['name'] ?? '';
|
||||
$row['trigger'] = $autoIdToRule[$row['id']]['trigger'] ?? null;
|
||||
$row['triggerText'] = $autoIdToRule[$row['id']]['triggerText'] ?? '';
|
||||
// 格式化使用次数显示
|
||||
$row['usageCountText'] = $row['usageCount'] . ' 次';
|
||||
}
|
||||
unset($row);
|
||||
|
||||
// 更新主表中的成功率字段(异步或定期更新)
|
||||
$this->updatePushSuccessRate($companyId);
|
||||
|
||||
return ResponseHelper::success([
|
||||
'activeRules' => (int)$activeRules,
|
||||
'totalTriggers' => (int)$totalTriggers,
|
||||
'aiPushSuccessRate' => (int)$aiPushSuccessRate,
|
||||
'aiPushCount' => (int)$aiPushCount,
|
||||
'ruleRanking' => $ruleRanking,
|
||||
], '统计成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('统计失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新推送表的成功率字段
|
||||
* @param int $companyId
|
||||
* @return void
|
||||
*/
|
||||
private function updatePushSuccessRate($companyId)
|
||||
{
|
||||
try {
|
||||
// 获取所有启用的推送
|
||||
$pushes = AiPush::where([
|
||||
['companyId', '=', $companyId],
|
||||
['isDel', '=', 0]
|
||||
])->field('id')->select();
|
||||
|
||||
foreach ($pushes as $push) {
|
||||
$pushId = $push['id'];
|
||||
$totalCount = Db::name('kf_ai_push_record')
|
||||
->where('pushId', $pushId)
|
||||
->count();
|
||||
$sendCount = Db::name('kf_ai_push_record')
|
||||
->where('pushId', $pushId)
|
||||
->where('isSend', 1)
|
||||
->count();
|
||||
|
||||
$successRate = $totalCount > 0 ? round(($sendCount * 100) / $totalCount, 2) : 0.00;
|
||||
|
||||
AiPush::where('id', $pushId)->update([
|
||||
'successRate' => $successRate,
|
||||
'updateTime' => time()
|
||||
]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 静默失败,不影响主流程
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
266
application/chukebao/controller/AiSettingsController.php
Normal file
266
application/chukebao/controller/AiSettingsController.php
Normal file
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\FriendSettings;
|
||||
use app\chukebao\model\Questions;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class AiSettingsController extends BaseController
|
||||
{
|
||||
|
||||
|
||||
const SETTING_DEFAULT = [
|
||||
'audioSetting' => false,
|
||||
'round' => 10,
|
||||
'aiStopSetting' => [
|
||||
'status' => true,
|
||||
'key' => ['好', '不错', '好的', '下次', '可以']
|
||||
],
|
||||
'fileSetting' => [
|
||||
'type' => 1,
|
||||
'content' => ''
|
||||
],
|
||||
'modelSetting' => [
|
||||
'model' => 'GPT-4',
|
||||
'role' => '你是一名销售的AI助理,同时也是一个工智能技术专家,你的名字叫小灵,你是单身女性,出生于2003年10月10日,喜欢听音乐和看电影有着丰富的人生阅历,前成熟大方,分享用幽默风趣的语言和客户交流,顾客问起你的感情,回复内容中不要使用号,特别注意不要跟客户问题,不要更多选择发送的信息。',
|
||||
'businessBackground' => '灵销智能公司开发了多款AI营销智能技术产品,以提升销售GPT AI大模型为核心,接入打造的销售/营销/客服等AI智能应用,为企业AI办公,AI助理,AI销售,AI营销,AI直播等大AI应用产品。',
|
||||
'dialogueStyle' => '客户:你们的AI解决方案具体是怎么收费的?销售:嗯,朋友,我们的AI解决方案是根据项目需求来定的,这样吧,你能跟我说说你们的具体情况吗,不过这样一分钱,您看怎么样?我们可以给您做个详细的方案对比。',
|
||||
]
|
||||
];
|
||||
|
||||
const TYPE_DATA = ['audioSetting', 'round', 'aiStopSetting', 'fileSetting', 'modelSetting'];
|
||||
|
||||
/**
|
||||
* 获取配置信息
|
||||
* @return \think\response\Json
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function getSetting()
|
||||
{
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
$data = Db::name('ai_settings')->where(['userId' => $userId, 'companyId' => $companyId])->find();
|
||||
if (empty($data)) {
|
||||
$setting = self::SETTING_DEFAULT;
|
||||
$data = [
|
||||
'companyId' => $companyId,
|
||||
'userId' => $userId,
|
||||
'config' => json_encode($setting, 256),
|
||||
'createTime' => time(),
|
||||
'updateTime' => time()
|
||||
];
|
||||
Db::name('ai_settings')->insert($data);
|
||||
|
||||
} else {
|
||||
$setting = json_decode($data['config'], true);
|
||||
}
|
||||
|
||||
return ResponseHelper::success($setting, '获取成功');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 配置
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setSetting()
|
||||
{
|
||||
$key = $this->request->param('key', '');
|
||||
$value = $this->request->param('value', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($key) || empty($value)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
|
||||
if (!in_array($key, self::TYPE_DATA)) {
|
||||
return ResponseHelper::error('该类型不在配置项');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$data = Db::name('ai_settings')->where(['userId' => $userId, 'companyId' => $companyId])->find();
|
||||
if (empty($data)) {
|
||||
$setting = self::SETTING_DEFAULT;
|
||||
} else {
|
||||
$setting = json_decode($data['config'], true);
|
||||
}
|
||||
$setting[$key] = $value;
|
||||
$setting = json_encode($setting, 256);
|
||||
Db::name('ai_settings')->where(['id' => $data['id']])->update(['config' => $setting, 'updateTime' => time()]);
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ', '配置成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('配置失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getUserTokens()
|
||||
{
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$tokens = Db::name('users')
|
||||
->where('id', $userId)
|
||||
->where('companyId', $companyId)
|
||||
->value('tokens');
|
||||
|
||||
return ResponseHelper::success($tokens, '获取成功');
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getFriend()
|
||||
{
|
||||
$friendId = $this->request->param('friendId', '');
|
||||
$wechatAccountId = $this->request->param('wechatAccountId', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$aiType = FriendSettings::where(['userId' => $userId, 'companyId' => $companyId,'friendId' => $friendId,'wechatAccountId' => $wechatAccountId])->value('type');
|
||||
if (empty($aiType)) {
|
||||
$aiType = 0;
|
||||
}
|
||||
return ResponseHelper::success($aiType, '获取成功');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public function setFriend()
|
||||
{
|
||||
$friendId = $this->request->param('friendId', '');
|
||||
$wechatAccountId = $this->request->param('wechatAccountId', '');
|
||||
$type = $this->request->param('type', 0);
|
||||
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($friendId) || empty($wechatAccountId)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$friend = Db::table('s2_wechat_friend')->where(['id' => $friendId,'wechatAccountId' => $wechatAccountId])->find();
|
||||
|
||||
if (empty($friend)) {
|
||||
return ResponseHelper::error('该好友不存在');
|
||||
}
|
||||
|
||||
$friendSettings = FriendSettings::where(['userId' => $userId, 'companyId' => $companyId,'friendId' => $friendId,'wechatAccountId' => $wechatAccountId])->find();
|
||||
Db::startTrans();
|
||||
try {
|
||||
if (empty($friendSettings)) {
|
||||
$friendSettings = new FriendSettings();
|
||||
$friendSettings->companyId = $companyId;
|
||||
$friendSettings->userId = $userId;
|
||||
$friendSettings->type = $type;
|
||||
$friendSettings->wechatAccountId = $wechatAccountId;
|
||||
$friendSettings->friendId = $friendId;
|
||||
$friendSettings->createTime = time();
|
||||
$friendSettings->updateTime = time();
|
||||
}else{
|
||||
$friendSettings->type = $type;
|
||||
$friendSettings->updateTime = time();
|
||||
}
|
||||
$friendSettings->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ', '配置成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('配置失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function setAllFriend()
|
||||
{
|
||||
$packageId = $this->request->param('packageId', []);
|
||||
$type = $this->request->param('type', 0);
|
||||
$isUpdata = $this->request->param('isUpdata', 0);
|
||||
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($packageId)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
//列出所有好友
|
||||
$row = Db::name('traffic_source_package_item')->alias('a')
|
||||
->join('wechat_friendship f','a.identifier = f.wechatId and f.companyId = '.$companyId)
|
||||
->join(['s2_wechat_account' => 'wa'],'f.ownerWechatId = wa.wechatId')
|
||||
->whereIn('a.packageId' , $packageId)
|
||||
->field('f.id as friendId,wa.id as wechatAccountId')
|
||||
->group('f.id')
|
||||
->select();
|
||||
|
||||
if (empty($row)) {
|
||||
return ResponseHelper::error('`好友不存在');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 1000条为一组进行批量处理
|
||||
$batchSize = 1000;
|
||||
$totalRows = count($row);
|
||||
|
||||
for ($i = 0; $i < $totalRows; $i += $batchSize) {
|
||||
$batchRows = array_slice($row, $i, $batchSize);
|
||||
if (!empty($batchRows)) {
|
||||
// 1. 提取当前批次的phone
|
||||
$friendIds = array_column($batchRows, 'friendId');
|
||||
// 2. 批量查询已存在的phone
|
||||
$existingPhones = [];
|
||||
if (!empty($friendIds)) {
|
||||
//强制更新
|
||||
if(!empty($isUpdata)){
|
||||
FriendSettings::whereIn('friendId',$friendIds)->update(['type' => $type,'updateTime' => time()]);
|
||||
}
|
||||
|
||||
$existing = FriendSettings::where('companyId', $companyId)->where('friendId', 'in', $friendIds)->field('friendId')->select()->toArray();
|
||||
$existingPhones = array_column($existing, 'friendId');
|
||||
}
|
||||
|
||||
// 3. 过滤出新数据,批量插入
|
||||
$newData = [];
|
||||
foreach ($batchRows as $row) {
|
||||
if (!empty($friendIds) && !in_array($row['friendId'], $existingPhones)) {
|
||||
$newData[] = [
|
||||
'companyId' => $companyId,
|
||||
'userId' => $userId,
|
||||
'type' => $type,
|
||||
'wechatAccountId' => $row['wechatAccountId'],
|
||||
'friendId' => $row['friendId'],
|
||||
'createTime' => time(),
|
||||
'updateTime' => time(),
|
||||
];
|
||||
}
|
||||
}
|
||||
// 4. 批量插入新数据
|
||||
if (!empty($newData)) {
|
||||
FriendSettings::insertAll($newData);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
return ResponseHelper::success(' ', '配置成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('配置失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
754
application/chukebao/controller/AutoGreetingsController.php
Normal file
754
application/chukebao/controller/AutoGreetingsController.php
Normal file
@@ -0,0 +1,754 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\AutoGreetings;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class AutoGreetingsController extends BaseController
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取问候规则列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getList(){
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$is_template = $this->request->param('is_template', 0);
|
||||
$triggerType = $this->request->param('triggerType', ''); // 触发类型筛选
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if($is_template == 1){
|
||||
$where = [
|
||||
['is_template','=',1],
|
||||
['isDel' ,'=', 0],
|
||||
];
|
||||
}else{
|
||||
$where = [
|
||||
['companyId','=',$companyId],
|
||||
['userId' ,'=', $userId],
|
||||
['isDel' ,'=', 0],
|
||||
];
|
||||
}
|
||||
|
||||
if(!empty($keyword)){
|
||||
$where[] = ['name','like','%'.$keyword.'%'];
|
||||
}
|
||||
|
||||
if(!empty($triggerType)){
|
||||
$where[] = ['trigger','=',$triggerType];
|
||||
}
|
||||
|
||||
$query = AutoGreetings::where($where);
|
||||
$total = $query->count();
|
||||
$list = $query->where($where)->page($page,$limit)->order('level asc,id desc')->select();
|
||||
|
||||
// 获取使用次数
|
||||
$list = is_array($list) ? $list : $list->toArray();
|
||||
$ids = array_column($list, 'id');
|
||||
$usageCounts = [];
|
||||
if (!empty($ids)) {
|
||||
$counts = Db::name('kf_auto_greetings_record')
|
||||
->where('autoId', 'in', $ids)
|
||||
->field('autoId, COUNT(*) as count')
|
||||
->group('autoId')
|
||||
->select();
|
||||
foreach ($counts as $count) {
|
||||
$usageCounts[$count['autoId']] = (int)$count['count'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($list as &$item) {
|
||||
$item['condition'] = json_decode($item['condition'], true);
|
||||
$item['usageCount'] = $usageCounts[$item['id']] ?? 0;
|
||||
// 格式化触发类型显示文本
|
||||
$triggerTypes = [
|
||||
1 => '新好友',
|
||||
2 => '首次发消息',
|
||||
3 => '时间触发',
|
||||
4 => '关键词触发',
|
||||
5 => '生日触发',
|
||||
6 => '自定义'
|
||||
];
|
||||
$item['triggerText'] = $triggerTypes[$item['trigger']] ?? '未知';
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return ResponseHelper::success(['list'=>$list,'total'=>$total]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验trigger类型对应的condition
|
||||
* @param int $trigger 触发类型
|
||||
* @param mixed $condition 条件参数
|
||||
* @return array|string 返回处理后的condition数组,或错误信息字符串
|
||||
*/
|
||||
private function validateTriggerCondition($trigger, $condition)
|
||||
{
|
||||
// trigger类型:1=新好友,2=首次发消息,3=时间触发,4=关键词触发,5=生日触发,6=自定义
|
||||
switch ($trigger) {
|
||||
case 1: // 新好友
|
||||
// 不需要condition
|
||||
return [];
|
||||
|
||||
case 2: // 首次发消息
|
||||
// 不需要condition
|
||||
return [];
|
||||
|
||||
case 3: // 时间触发
|
||||
// 需要condition,格式为:{"type": "daily_time|yearly_datetime|fixed_range|workday", "value": "..."}
|
||||
if (empty($condition)) {
|
||||
return '时间触发类型需要配置具体的触发条件';
|
||||
}
|
||||
$condition = is_array($condition) ? $condition : json_decode($condition, true);
|
||||
if (empty($condition) || !is_array($condition)) {
|
||||
return '时间触发类型的条件格式不正确,应为数组格式';
|
||||
}
|
||||
|
||||
// 验证必须包含type字段
|
||||
if (!isset($condition['type']) || empty($condition['type'])) {
|
||||
return '时间触发类型必须指定触发方式:daily_time(每天固定时间)、yearly_datetime(每年固定日期时间)、fixed_range(固定时间段)、workday(工作日)';
|
||||
}
|
||||
|
||||
$timeType = $condition['type'];
|
||||
$allowedTypes = ['daily_time', 'yearly_datetime', 'fixed_range', 'workday'];
|
||||
// 兼容旧版本的 fixed_time,自动转换为 daily_time
|
||||
if ($timeType === 'fixed_time') {
|
||||
$timeType = 'daily_time';
|
||||
}
|
||||
if (!in_array($timeType, $allowedTypes)) {
|
||||
return '时间触发类型无效,必须为:daily_time(每天固定时间)、yearly_datetime(每年固定日期时间)、fixed_range(固定时间段)、workday(工作日)';
|
||||
}
|
||||
|
||||
// 根据不同的type验证value
|
||||
switch ($timeType) {
|
||||
case 'daily_time': // 每天固定时间(每天的几点几分)
|
||||
// value应该是时间字符串,格式:HH:mm,如 "14:30"
|
||||
if (!isset($condition['value']) || empty($condition['value'])) {
|
||||
return '每天固定时间类型需要配置具体时间,格式:HH:mm(如 14:30)';
|
||||
}
|
||||
$timeValue = $condition['value'];
|
||||
if (!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $timeValue)) {
|
||||
return '每天固定时间格式不正确,应为 HH:mm 格式(如 14:30)';
|
||||
}
|
||||
return [
|
||||
'type' => 'daily_time',
|
||||
'value' => $timeValue
|
||||
];
|
||||
|
||||
case 'yearly_datetime': // 每年固定日期时间(每年的几月几号几点几分)
|
||||
// value应该是日期时间字符串,格式:MM-dd HH:mm,如 "12-25 14:30"
|
||||
if (!isset($condition['value']) || empty($condition['value'])) {
|
||||
return '每年固定日期时间类型需要配置具体日期和时间,格式:MM-dd HH:mm(如 12-25 14:30)';
|
||||
}
|
||||
$datetimeValue = $condition['value'];
|
||||
// 验证格式:MM-dd HH:mm
|
||||
if (!preg_match('/^(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) ([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $datetimeValue)) {
|
||||
return '每年固定日期时间格式不正确,应为 MM-dd HH:mm 格式(如 12-25 14:30)';
|
||||
}
|
||||
// 进一步验证日期是否有效(例如2月30日不存在)
|
||||
list($datePart, $timePart) = explode(' ', $datetimeValue);
|
||||
list($month, $day) = explode('-', $datePart);
|
||||
if (!checkdate((int)$month, (int)$day, 2000)) { // 使用2000年作为参考年份验证日期有效性
|
||||
return '日期无效,请检查月份和日期是否正确(如2月不能有30日)';
|
||||
}
|
||||
return [
|
||||
'type' => 'yearly_datetime',
|
||||
'value' => $datetimeValue
|
||||
];
|
||||
|
||||
case 'fixed_range': // 固定时间段
|
||||
// value应该是时间段数组,格式:["09:00", "18:00"]
|
||||
if (!isset($condition['value']) || !is_array($condition['value'])) {
|
||||
return '固定时间段类型需要配置时间段,格式:["开始时间", "结束时间"](如 ["09:00", "18:00"])';
|
||||
}
|
||||
$rangeValue = $condition['value'];
|
||||
if (count($rangeValue) !== 2) {
|
||||
return '固定时间段应为包含两个时间点的数组,格式:["09:00", "18:00"]';
|
||||
}
|
||||
// 验证时间格式
|
||||
foreach ($rangeValue as $time) {
|
||||
if (!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $time)) {
|
||||
return '时间段格式不正确,应为 HH:mm 格式(如 09:00)';
|
||||
}
|
||||
}
|
||||
// 验证开始时间小于结束时间
|
||||
$startTime = strtotime('2000-01-01 ' . $rangeValue[0]);
|
||||
$endTime = strtotime('2000-01-01 ' . $rangeValue[1]);
|
||||
if ($startTime >= $endTime) {
|
||||
return '开始时间必须小于结束时间';
|
||||
}
|
||||
return [
|
||||
'type' => 'fixed_range',
|
||||
'value' => $rangeValue
|
||||
];
|
||||
|
||||
case 'workday': // 工作日
|
||||
// 工作日需要配置时间,格式:HH:mm(如 09:00)
|
||||
if (!isset($condition['value']) || empty($condition['value'])) {
|
||||
return '工作日触发类型需要配置时间,格式:HH:mm(如 09:00)';
|
||||
}
|
||||
$timeValue = trim($condition['value']);
|
||||
// 验证格式:HH:mm
|
||||
if (!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $timeValue)) {
|
||||
return '工作日时间格式不正确,应为 HH:mm 格式(如 09:00)';
|
||||
}
|
||||
return [
|
||||
'type' => 'workday',
|
||||
'value' => $timeValue
|
||||
];
|
||||
|
||||
default:
|
||||
return '时间触发类型无效';
|
||||
}
|
||||
|
||||
case 4: // 关键词触发
|
||||
// 需要condition,格式:{"keywords": ["关键词1", "关键词2"], "match_type": "exact|fuzzy"}
|
||||
if (empty($condition)) {
|
||||
return '关键词触发类型需要配置至少一个关键词';
|
||||
}
|
||||
|
||||
// 如果是字符串,尝试解析JSON
|
||||
if (is_string($condition)) {
|
||||
$decoded = json_decode($condition, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE) {
|
||||
$condition = $decoded;
|
||||
} else {
|
||||
return '关键词触发类型格式错误,应为对象格式:{"keywords": ["关键词1", "关键词2"], "match_type": "exact|fuzzy"}';
|
||||
}
|
||||
}
|
||||
|
||||
// 必须是对象格式
|
||||
if (!is_array($condition) || !isset($condition['keywords'])) {
|
||||
return '关键词触发类型格式错误,应为对象格式:{"keywords": ["关键词1", "关键词2"], "match_type": "exact|fuzzy"}';
|
||||
}
|
||||
|
||||
$keywords = $condition['keywords'];
|
||||
$matchType = isset($condition['match_type']) ? $condition['match_type'] : 'fuzzy';
|
||||
|
||||
// 验证match_type
|
||||
if (!in_array($matchType, ['exact', 'fuzzy'])) {
|
||||
return '匹配类型无效,必须为:exact(精准匹配)或 fuzzy(模糊匹配)';
|
||||
}
|
||||
|
||||
// 处理keywords
|
||||
if (is_string($keywords)) {
|
||||
$keywords = explode(',', $keywords);
|
||||
}
|
||||
if (!is_array($keywords)) {
|
||||
return '关键词格式不正确,应为数组格式';
|
||||
}
|
||||
|
||||
// 过滤空值并去重
|
||||
$keywords = array_filter(array_map('trim', $keywords));
|
||||
if (empty($keywords)) {
|
||||
return '关键词触发类型需要配置至少一个关键词';
|
||||
}
|
||||
|
||||
// 验证每个关键词不为空
|
||||
foreach ($keywords as $keyword) {
|
||||
if (empty($keyword)) {
|
||||
return '关键词不能为空';
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'keywords' => array_values($keywords),
|
||||
'match_type' => $matchType
|
||||
];
|
||||
|
||||
case 5: // 生日触发
|
||||
// 需要condition,格式支持:
|
||||
// 1. 月日字符串:'10-10' 或 '10-10 09:00'(MM-DD格式,不包含年份)
|
||||
// 2. 对象格式:{'month': 10, 'day': 10, 'time': '09:00'} 或 {'month': '10', 'day': '10', 'time_range': ['09:00', '10:00']}
|
||||
if (empty($condition)) {
|
||||
return '生日触发类型需要配置日期条件';
|
||||
}
|
||||
|
||||
// 如果是字符串,只接受 MM-DD 格式(不包含年份)
|
||||
if (is_string($condition)) {
|
||||
// 检查是否包含时间部分
|
||||
if (preg_match('/^(\d{1,2})-(\d{1,2})\s+(\d{2}:\d{2})$/', $condition, $matches)) {
|
||||
// 格式:'10-10 09:00'
|
||||
$month = (int)$matches[1];
|
||||
$day = (int)$matches[2];
|
||||
if ($month < 1 || $month > 12 || $day < 1 || $day > 31) {
|
||||
return '生日日期格式不正确,月份应为1-12,日期应为1-31';
|
||||
}
|
||||
return [
|
||||
'month' => $month,
|
||||
'day' => $day,
|
||||
'time' => $matches[3]
|
||||
];
|
||||
} elseif (preg_match('/^(\d{1,2})-(\d{1,2})$/', $condition, $matches)) {
|
||||
// 格式:'10-10'(不指定时间,当天任何时间都可以触发)
|
||||
$month = (int)$matches[1];
|
||||
$day = (int)$matches[2];
|
||||
if ($month < 1 || $month > 12 || $day < 1 || $day > 31) {
|
||||
return '生日日期格式不正确,月份应为1-12,日期应为1-31';
|
||||
}
|
||||
return [
|
||||
'month' => $month,
|
||||
'day' => $day
|
||||
];
|
||||
} else {
|
||||
return '生日日期格式不正确,应为 MM-DD 或 MM-DD HH:mm 格式(如 10-10 或 10-10 09:00),不包含年份';
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是数组,可能是对象格式或旧格式
|
||||
if (is_array($condition)) {
|
||||
// 检查是否是旧格式(仅兼容 MM-DD 格式的数组)
|
||||
if (isset($condition[0]) && is_string($condition[0])) {
|
||||
$dateStr = $condition[0];
|
||||
// 只接受 MM-DD 格式:'10-10' 或 '10-10 09:00'
|
||||
if (preg_match('/^(\d{1,2})-(\d{1,2})(?:\s+(\d{2}:\d{2}))?$/', $dateStr, $matches)) {
|
||||
$month = (int)$matches[1];
|
||||
$day = (int)$matches[2];
|
||||
if ($month < 1 || $month > 12 || $day < 1 || $day > 31) {
|
||||
return '生日日期格式不正确,月份应为1-12,日期应为1-31';
|
||||
}
|
||||
if (isset($matches[3])) {
|
||||
return [
|
||||
'month' => $month,
|
||||
'day' => $day,
|
||||
'time' => $matches[3]
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'month' => $month,
|
||||
'day' => $day
|
||||
];
|
||||
}
|
||||
} else {
|
||||
return '生日日期格式不正确,应为 MM-DD 格式(如 10-10),不包含年份';
|
||||
}
|
||||
}
|
||||
|
||||
// 新格式:{'month': 10, 'day': 10, 'time': '09:00'}
|
||||
if (isset($condition['month']) && isset($condition['day'])) {
|
||||
$month = (int)$condition['month'];
|
||||
$day = (int)$condition['day'];
|
||||
|
||||
if ($month < 1 || $month > 12) {
|
||||
return '生日月份格式不正确,应为1-12';
|
||||
}
|
||||
if ($day < 1 || $day > 31) {
|
||||
return '生日日期格式不正确,应为1-31';
|
||||
}
|
||||
|
||||
$result = [
|
||||
'month' => $month,
|
||||
'day' => $day
|
||||
];
|
||||
|
||||
// 检查是否配置了时间
|
||||
if (isset($condition['time']) && !empty($condition['time'])) {
|
||||
$time = trim($condition['time']);
|
||||
if (!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $time)) {
|
||||
return '生日时间格式不正确,应为 HH:mm 格式(如 09:00)';
|
||||
}
|
||||
$result['time'] = $time;
|
||||
}
|
||||
|
||||
// 检查是否配置了时间范围
|
||||
if (isset($condition['time_range']) && is_array($condition['time_range']) && count($condition['time_range']) === 2) {
|
||||
$startTime = trim($condition['time_range'][0]);
|
||||
$endTime = trim($condition['time_range'][1]);
|
||||
if (!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $startTime) ||
|
||||
!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $endTime)) {
|
||||
return '生日时间范围格式不正确,应为 ["HH:mm", "HH:mm"] 格式';
|
||||
}
|
||||
$result['time_range'] = [$startTime, $endTime];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return '生日触发条件格式不正确,需要提供month和day字段';
|
||||
}
|
||||
|
||||
return '生日触发条件格式不正确';
|
||||
|
||||
case 6: // 自定义
|
||||
// 自定义类型,condition可选,如果有则必须是数组格式
|
||||
if (!empty($condition)) {
|
||||
$condition = is_array($condition) ? $condition : json_decode($condition, true);
|
||||
if (!is_array($condition)) {
|
||||
return '自定义类型的条件格式不正确,应为数组格式';
|
||||
}
|
||||
return $condition;
|
||||
}
|
||||
return [];
|
||||
|
||||
default:
|
||||
return '无效的触发类型';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function create(){
|
||||
$name = $this->request->param('name', '');
|
||||
$trigger = $this->request->param('trigger', 0);
|
||||
$condition = $this->request->param('condition', '');
|
||||
$content = $this->request->param('content', '');
|
||||
$level = $this->request->param('level', 0);
|
||||
$status = $this->request->param('status', 1);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($name) || empty($trigger) || empty($content)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
// 校验trigger类型
|
||||
if (!in_array($trigger, [1, 2, 3, 4, 5, 6])) {
|
||||
return ResponseHelper::error('无效的触发类型');
|
||||
}
|
||||
|
||||
// 校验并处理condition
|
||||
$conditionResult = $this->validateTriggerCondition($trigger, $condition);
|
||||
if (is_string($conditionResult)) {
|
||||
// 返回的是错误信息
|
||||
return ResponseHelper::error($conditionResult);
|
||||
}
|
||||
$condition = $conditionResult;
|
||||
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$AutoGreetings = new AutoGreetings();
|
||||
$AutoGreetings->name = $name;
|
||||
$AutoGreetings->trigger = $trigger;
|
||||
$AutoGreetings->condition = json_encode($condition,256);
|
||||
$AutoGreetings->content = $content;
|
||||
$AutoGreetings->level = $level;
|
||||
$AutoGreetings->status = $status;
|
||||
$AutoGreetings->userId = $userId;
|
||||
$AutoGreetings->companyId = $companyId;
|
||||
$AutoGreetings->updateTime = time();
|
||||
$AutoGreetings->createTime = time();
|
||||
$AutoGreetings->usageCount = 0; // 初始化使用次数为0
|
||||
$AutoGreetings->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(['id' => $AutoGreetings->id],'创建成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('创建失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function details()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$data = AutoGreetings::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($data)){
|
||||
return ResponseHelper::error('该内容已被删除或者不存在');
|
||||
}
|
||||
|
||||
|
||||
$data['condition'] = json_decode($data['condition'],true);
|
||||
|
||||
// 获取使用次数
|
||||
$usageCount = Db::name('kf_auto_greetings_record')
|
||||
->where('autoId', $id)
|
||||
->count();
|
||||
$data['usageCount'] = (int)$usageCount;
|
||||
|
||||
return ResponseHelper::success($data,'获取成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function del()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$data = AutoGreetings::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($data)){
|
||||
return ResponseHelper::error('该已被删除或者不存在');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$data->isDel = 1;
|
||||
$data->delTime = time();
|
||||
$data->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success('','删除成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('删除失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function update(){
|
||||
$id = $this->request->param('id', '');
|
||||
$name = $this->request->param('name', '');
|
||||
$trigger = $this->request->param('trigger', 0);
|
||||
$condition = $this->request->param('condition', '');
|
||||
$content = $this->request->param('content', '');
|
||||
$level = $this->request->param('level', 0);
|
||||
$status = $this->request->param('status', 1);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($id) || empty($name) || empty($trigger) || empty($content)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
// 校验trigger类型
|
||||
if (!in_array($trigger, [1, 2, 3, 4, 5, 6])) {
|
||||
return ResponseHelper::error('无效的触发类型');
|
||||
}
|
||||
|
||||
// 校验并处理condition
|
||||
$conditionResult = $this->validateTriggerCondition($trigger, $condition);
|
||||
if (is_string($conditionResult)) {
|
||||
// 返回的是错误信息
|
||||
return ResponseHelper::error($conditionResult);
|
||||
}
|
||||
$condition = $conditionResult;
|
||||
|
||||
|
||||
$query = AutoGreetings::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($query)){
|
||||
return ResponseHelper::error('该内容已被删除或者不存在');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$query->name = $name;
|
||||
$query->trigger = $trigger;
|
||||
$query->condition = !empty($condition) ? json_encode($condition,256) : json_encode([]);
|
||||
$query->content = $content;
|
||||
$query->level = $level;
|
||||
$query->status = $status;
|
||||
$query->userId = $userId;
|
||||
$query->companyId = $companyId;
|
||||
$query->updateTime = time();
|
||||
$query->createTime = time();
|
||||
$query->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','修改成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('修改失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改状态
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setStatus(){
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$query = AutoGreetings::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($query)){
|
||||
return ResponseHelper::error('该内容已被删除或者不存在');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$status = $this->request->param('status', '');
|
||||
if ($status !== '') {
|
||||
$query->status = (int)$status;
|
||||
} else {
|
||||
$query->status = $query->status == 1 ? 0 : 1;
|
||||
}
|
||||
$query->updateTime = time();
|
||||
$query->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(['status' => $query->status],'修改成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('修改失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 拷贝
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function copy(){
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($id) ){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$data = AutoGreetings::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($data)){
|
||||
return ResponseHelper::error('该内容已被删除或者不存在');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$query = new AutoGreetings();
|
||||
$query->name = $data['name'] . '_copy';
|
||||
$query->trigger = $data['trigger'];
|
||||
$query->condition = $data['condition'];
|
||||
$query->content = $data['content'];
|
||||
$query->level = $data['level'];
|
||||
$query->status = $data['status'];
|
||||
$query->userId = $userId;
|
||||
$query->companyId = $companyId;
|
||||
$query->updateTime = time();
|
||||
$query->createTime = time();
|
||||
$query->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','拷贝成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('拷贝失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 统计概览
|
||||
* - 总触发次数
|
||||
* - 活跃规则(近一个月)
|
||||
* - 发送成功率
|
||||
* - 平均响应时间(秒)
|
||||
* - 规则效果排行(按发送次数降序、平均响应时间升序)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function stats()
|
||||
{
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$userId = $this->getUserInfo('id');
|
||||
|
||||
$start30d = time() - 30 * 24 * 3600;
|
||||
|
||||
try {
|
||||
// 公司维度(用于除排行外的统计)
|
||||
$companyWhere = [
|
||||
['companyId', '=', $companyId],
|
||||
];
|
||||
// 排行维度(限定个人)
|
||||
$rankingWhere = [
|
||||
['companyId', '=', $companyId],
|
||||
['userId', '=', $userId],
|
||||
];
|
||||
|
||||
// 1) 总触发次数
|
||||
$totalTriggers = Db::name('kf_auto_greetings_record')
|
||||
->where($companyWhere)
|
||||
->count();
|
||||
|
||||
// 2) 近30天活跃规则(仅返回数量,按公司维度,distinct autoId)
|
||||
$activeRulesCount = Db::name('kf_auto_greetings_record')
|
||||
->where($companyWhere)
|
||||
->where('createTime', '>=', $start30d)
|
||||
->distinct(true)
|
||||
->count('autoId');
|
||||
|
||||
// 3) 发送成功率
|
||||
$sendCount = Db::name('kf_auto_greetings_record')
|
||||
->where($companyWhere)
|
||||
->where('isSend', '=', 1)
|
||||
->count();
|
||||
// 成功率:百分比,保留两位小数
|
||||
$sendRate = $totalTriggers > 0 ? round(($sendCount * 100) / $totalTriggers, 2) : 0.00;
|
||||
|
||||
// 4) 平均响应时间(receiveTime - sendTime,单位秒)
|
||||
$avgResponse = Db::name('kf_auto_greetings_record')
|
||||
->where($companyWhere)
|
||||
->whereRaw('sendTime IS NOT NULL AND receiveTime IS NOT NULL AND receiveTime >= sendTime')
|
||||
->avg(Db::raw('(receiveTime - sendTime)'));
|
||||
$avgResponse = $avgResponse ? (int)round($avgResponse) : 0;
|
||||
|
||||
// 5) 规则效果排行(按发送次数降序、平均响应时间升序)
|
||||
$ranking = Db::name('kf_auto_greetings_record')
|
||||
->where($rankingWhere)
|
||||
->field([
|
||||
'autoId AS id',
|
||||
'COUNT(*) AS totalCount',
|
||||
'SUM(CASE WHEN isSend = 1 THEN 1 ELSE 0 END) AS sendCount',
|
||||
'AVG(CASE WHEN sendTime IS NOT NULL AND receiveTime IS NOT NULL AND receiveTime >= sendTime THEN (receiveTime - sendTime) END) AS avgResp'
|
||||
])
|
||||
->group('autoId')
|
||||
->orderRaw('sendCount DESC, avgResp ASC')
|
||||
->limit(20)
|
||||
->select();
|
||||
|
||||
// 附加规则名称(如存在)
|
||||
$autoIds = array_values(array_unique(array_column($ranking, 'id')));
|
||||
$autoIdToRule = [];
|
||||
if (!empty($autoIds)) {
|
||||
$rules = AutoGreetings::where([['id', 'in', $autoIds]])
|
||||
->field('id,name,trigger')
|
||||
->select();
|
||||
foreach ($rules as $rule) {
|
||||
$autoIdToRule[$rule['id']] = [
|
||||
'name' => $rule['name'],
|
||||
'trigger' => $rule['trigger'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($ranking as &$row) {
|
||||
$row['avgResp'] = isset($row['avgResp']) && $row['avgResp'] !== null ? (int)round($row['avgResp']) : 0;
|
||||
// 百分比,两位小数
|
||||
$row['sendRate'] = ($row['totalCount'] ?? 0) > 0 ? round((($row['sendCount'] ?? 0) * 100) / $row['totalCount'], 2) : 0.00;
|
||||
$row['name'] = $autoIdToRule[$row['id']]['name'] ?? '';
|
||||
$row['trigger'] = $autoIdToRule[$row['id']]['trigger'] ?? null;
|
||||
}
|
||||
unset($row);
|
||||
|
||||
return ResponseHelper::success([
|
||||
'totalTriggers' => (int)$totalTriggers,
|
||||
'activeRules' => (int)$activeRulesCount,
|
||||
'sendSuccessRate' => $sendRate,
|
||||
'avgResponseSeconds' => $avgResponse,
|
||||
'ruleRanking' => $ranking,
|
||||
], '统计成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('统计失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
29
application/chukebao/controller/BaseController.php
Normal file
29
application/chukebao/controller/BaseController.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use think\Controller;
|
||||
|
||||
/**
|
||||
* 基础控制器
|
||||
*/
|
||||
class BaseController extends Controller
|
||||
{
|
||||
/**
|
||||
* 获取用户信息
|
||||
*
|
||||
* @param string $column
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getUserInfo(?string $column = null)
|
||||
{
|
||||
$user = $this->request->userInfo;
|
||||
|
||||
if (!$user) {
|
||||
throw new \Exception('未授权访问,缺少有效的身份凭证', 401);
|
||||
}
|
||||
|
||||
return $column ? $user[$column] : $user;
|
||||
}
|
||||
}
|
||||
665
application/chukebao/controller/ContentController.php
Normal file
665
application/chukebao/controller/ContentController.php
Normal file
@@ -0,0 +1,665 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\Keywords;
|
||||
use app\chukebao\model\Material;
|
||||
use app\chukebao\model\SensitiveWord;
|
||||
use think\Db;
|
||||
use library\ResponseHelper;
|
||||
|
||||
class ContentController extends BaseController
|
||||
{
|
||||
//===================================================== 素材管理 =====================================================
|
||||
|
||||
public function getAllMaterial(){
|
||||
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
$query = Material::where(['userId' => $userId,'companyId' => $companyId,'isDel' => 0,'status' => 1])
|
||||
->field('id,title,cover')
|
||||
->order('id desc');
|
||||
|
||||
$list = $query->select()->toArray();
|
||||
|
||||
return ResponseHelper::success($list);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 素材列表
|
||||
* @return \think\response\Json
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function getMaterial(){
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
$query = Material::where(['userId' => $userId,'companyId' => $companyId,'isDel' => 0])
|
||||
->order('id desc');
|
||||
if (!empty($keyword)){
|
||||
$query->where('title', 'like', '%'.$keyword.'%');
|
||||
}
|
||||
$list = $query->page($page, $limit)->select()->toArray();
|
||||
$total = $query->count();
|
||||
|
||||
foreach ($list as $k => &$v){
|
||||
$user = Db::name('users')->where(['id' => $v['userId']])->field('username,account')->find();
|
||||
if (!empty($user)){
|
||||
$v['userName'] = !empty($user['username']) ? $user['username'] : $user['account'];
|
||||
}else{
|
||||
$v['userName'] = '';
|
||||
}
|
||||
}
|
||||
unset($v);
|
||||
return ResponseHelper::success(['list'=>$list,'total'=>$total]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 素材添加
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createMaterial(){
|
||||
$title = $this->request->param('title', '');
|
||||
$content = $this->request->param('content', []);
|
||||
$cover = $this->request->param('cover', '');
|
||||
$status = $this->request->param('status', 0);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($title) || empty($content) || empty($cover)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$newContent = [];
|
||||
foreach ($content as $k => $v){
|
||||
if (in_array($v['type'],['text','image','video','audio','file','link'])){
|
||||
$newContent[] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$query = new Material();
|
||||
$query->title = $title;
|
||||
$query->content = !empty($newContent) ? json_encode($newContent,256) : json_encode([],256);
|
||||
$query->cover = $cover;
|
||||
$query->status = $status;
|
||||
$query->userId = $userId;
|
||||
$query->companyId = $companyId;
|
||||
$query->createTime = time();
|
||||
$query->updateTime = time();
|
||||
$query->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','创建成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('创建失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 素材详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detailsMaterial()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$data = Material::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($data)){
|
||||
return ResponseHelper::error('该素材已被删除或者不存在');
|
||||
}
|
||||
$data['content'] = json_decode($data['content'],true);
|
||||
unset($data['createTime'],$data['updateTime'],$data['isDel'],$data['delTime']);
|
||||
return ResponseHelper::success($data,'获取成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除素材
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function delMaterial()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$data = Material::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($data)){
|
||||
return ResponseHelper::error('该素材已被删除或者不存在');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$data->isDel = 1;
|
||||
$data->delTime = time();
|
||||
$data->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success('','删除成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('删除失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修改素材
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function updateMaterial(){
|
||||
$id = $this->request->param('id', '');
|
||||
$title = $this->request->param('title', '');
|
||||
$content = $this->request->param('content', []);
|
||||
$cover = $this->request->param('cover', '');
|
||||
$status = $this->request->param('status', 0);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($id) || empty($title) || empty($content) || empty($cover)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$newContent = [];
|
||||
foreach ($content as $k => $v){
|
||||
if (in_array($v['type'],['text','image','video','audio','file','link'])){
|
||||
$newContent[] = $v;
|
||||
}
|
||||
}
|
||||
$query = Material::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($query)){
|
||||
return ResponseHelper::error('该素材已被删除或者不存在');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$query->title = $title;
|
||||
$query->content = !empty($newContent) ? json_encode($newContent,256) : json_encode([],256);
|
||||
$query->cover = $cover;
|
||||
$query->status = $status;
|
||||
$query->updateTime = time();
|
||||
$query->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','修改成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('修改失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
//===================================================== 素材管理 =====================================================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==================================================== 违禁词管理 ====================================================
|
||||
|
||||
/**
|
||||
* 违禁词列表
|
||||
* @return \think\response\Json
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function getSensitiveWord(){
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
$query = SensitiveWord::where(['userId' => $userId,'companyId' => $companyId,'isDel' => 0])
|
||||
->order('id desc');
|
||||
if (!empty($keyword)){
|
||||
$query->where('title', 'like', '%'.$keyword.'%');
|
||||
}
|
||||
$total = $query->count();
|
||||
$list = $query->page($page, $limit)->select()->toArray();
|
||||
|
||||
|
||||
foreach ($list as $k => &$v){
|
||||
$user = Db::name('users')->where(['id' => $v['userId']])->field('username,account')->find();
|
||||
if (!empty($user)){
|
||||
$v['userName'] = !empty($user['username']) ? $user['username'] : $user['account'];
|
||||
}else{
|
||||
$v['userName'] = '';
|
||||
}
|
||||
}
|
||||
unset($v);
|
||||
return ResponseHelper::success(['list'=>$list,'total'=>$total]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 违禁词添加
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createSensitiveWord(){
|
||||
$title = $this->request->param('title', '');
|
||||
$keywords = $this->request->param('keywords', '');
|
||||
$content = $this->request->param('content', '');
|
||||
$status = $this->request->param('status', 0);
|
||||
$operation = $this->request->param('operation', 0);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($title) || empty($keywords)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$keywords = explode(',',$keywords);
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$query = new SensitiveWord();
|
||||
$query->title = $title;
|
||||
$query->keywords = $keywords;
|
||||
$query->content = $content;
|
||||
$query->status = $status;
|
||||
$query->operation = $operation;
|
||||
$query->userId = $userId;
|
||||
$query->companyId = $companyId;
|
||||
$query->createTime = time();
|
||||
$query->updateTime = time();
|
||||
$query->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','创建成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('创建失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 违禁词详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detailsSensitiveWord()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$data = SensitiveWord::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($data)){
|
||||
return ResponseHelper::error('该素材已被删除或者不存在');
|
||||
}
|
||||
$data['keywords'] = json_decode($data['keywords'],true);
|
||||
$data['keywords'] = implode(',',$data['keywords']);
|
||||
unset($data['createTime'],$data['updateTime'],$data['isDel'],$data['delTime']);
|
||||
return ResponseHelper::success($data,'获取成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 违禁词删除
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function delSensitiveWord()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$data = SensitiveWord::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($data)){
|
||||
return ResponseHelper::error('该素材已被删除或者不存在');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$data->isDel = 1;
|
||||
$data->delTime = time();
|
||||
$data->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success('','删除成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('删除失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新违禁词
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function updateSensitiveWord(){
|
||||
$id = $this->request->param('id', '');
|
||||
$title = $this->request->param('title', '');
|
||||
$keywords = $this->request->param('keywords', '');
|
||||
$content = $this->request->param('content', '');
|
||||
$status = $this->request->param('status', 0);
|
||||
$operation = $this->request->param('operation', 0);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($id) || empty($title) || empty($keywords)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$keywords = explode(',',$keywords);
|
||||
|
||||
$query = SensitiveWord::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($query)){
|
||||
return ResponseHelper::error('该素材已被删除或者不存在');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$query->title = $title;
|
||||
$query->keywords = $keywords;
|
||||
$query->content = $content;
|
||||
$query->status = $status;
|
||||
$query->operation = $operation;
|
||||
$query->updateTime = time();
|
||||
$query->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','修改成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('修改失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改违禁词状态
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setSensitiveWordStatus(){
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$query = SensitiveWord::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($query)){
|
||||
return ResponseHelper::error('该素材已被删除或者不存在');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$query->status = !empty($query['status']) ? 0 : 1;;
|
||||
$query->updateTime = time();
|
||||
$query->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','修改成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('修改失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==================================================== 违禁词管理 ====================================================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=================================================== 关键词词管理 ====================================================
|
||||
|
||||
/**
|
||||
* 关键词列表
|
||||
* @return \think\response\Json
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function getKeywords(){
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
$query = Keywords::where(['userId' => $userId,'companyId' => $companyId,'isDel' => 0])
|
||||
->order('id desc');
|
||||
if (!empty($keyword)){
|
||||
$query->where('title', 'like', '%'.$keyword.'%');
|
||||
}
|
||||
$total = $query->count();
|
||||
$list = $query->page($page, $limit)->select()->toArray();
|
||||
|
||||
|
||||
foreach ($list as $k => &$v){
|
||||
$v['metailGroups'] = json_decode($v['metailGroups'],true);
|
||||
$v['content'] = json_decode($v['content'],true);
|
||||
$v['keywords'] = json_decode($v['keywords'],true);
|
||||
|
||||
$metailData = Material::where(['isDel' => 0,'userId' => $userId,'companyId' => $companyId])
|
||||
->whereIn('id',$v['metailGroups'])
|
||||
->select()->toArray();
|
||||
$v['metailGroupsOptions'] = $metailData;
|
||||
|
||||
|
||||
$user = Db::name('users')->where(['id' => $v['userId']])->field('username,account')->find();
|
||||
if (!empty($user)){
|
||||
$v['userName'] = !empty($user['username']) ? $user['username'] : $user['account'];
|
||||
}else{
|
||||
$v['userName'] = '';
|
||||
}
|
||||
}
|
||||
unset($v);
|
||||
return ResponseHelper::success(['list'=>$list,'total'=>$total]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 关键词添加
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createKeywords(){
|
||||
$title = $this->request->param('title', '');
|
||||
$type = $this->request->param('type', 0);
|
||||
$keywords = $this->request->param('keywords', '');
|
||||
$replyType = $this->request->param('replyType', 0);
|
||||
$content = $this->request->param('content','');
|
||||
$metailGroups = $this->request->param('metailGroups',[]);
|
||||
$status = $this->request->param('status', 0);
|
||||
$level = $this->request->param('level', 50);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($title) || empty($keywords) || (empty(metailGroups) && empty($content))){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$keywords = explode(',',$keywords);
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$query = new Keywords();
|
||||
$query->title = $title;
|
||||
$query->type = $type;
|
||||
$query->keywords = !empty($keywords) ? json_encode($keywords,256) : json_encode([]);
|
||||
$query->replyType = $replyType;
|
||||
$query->content = !empty($content) ? json_encode($content,256) : json_encode([]);;
|
||||
$query->metailGroups = !empty($metailGroups) ? json_encode($metailGroups,256) : json_encode([]);;
|
||||
$query->status = $status;
|
||||
$query->level = $level;
|
||||
$query->userId = $userId;
|
||||
$query->companyId = $companyId;
|
||||
$query->createTime = time();
|
||||
$query->updateTime = time();
|
||||
$query->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','创建成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('创建失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 关键词详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detailsKeywords()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$data = Keywords::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($data)){
|
||||
return ResponseHelper::error('该素材已被删除或者不存在');
|
||||
}
|
||||
|
||||
$data['metailGroups'] = json_decode($data['metailGroups'],true);
|
||||
$metailData = Material::where(['isDel' => 0,'userId' => $userId,'companyId' => $companyId])
|
||||
->whereIn('id',$data['metailGroups'])
|
||||
->select()->toArray();
|
||||
$data['metailGroupsOptions'] = $metailData;
|
||||
|
||||
$data['content'] = json_decode($data['content'],true);
|
||||
$data['keywords'] = json_decode($data['keywords'],true);
|
||||
$data['keywords'] = implode(',',$data['keywords']);
|
||||
unset($data['createTime'],$data['updateTime'],$data['isDel'],$data['delTime']);
|
||||
return ResponseHelper::success($data,'获取成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关键词删除
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function delKeywords()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$data = Keywords::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($data)){
|
||||
return ResponseHelper::error('该关键词已被删除或者不存在');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$data->isDel = 1;
|
||||
$data->delTime = time();
|
||||
$data->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success('','删除成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('删除失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新关键词
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function updateKeywords(){
|
||||
$id = $this->request->param('id', '');
|
||||
$title = $this->request->param('title', '');
|
||||
$type = $this->request->param('type', 0);
|
||||
$keywords = $this->request->param('keywords', '');
|
||||
$replyType = $this->request->param('replyType', 0);
|
||||
$content = $this->request->param('content','');
|
||||
$metailGroups = $this->request->param('metailGroups','');
|
||||
$status = $this->request->param('status', 0);
|
||||
$level = $this->request->param('level', 50);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($title) || empty($keywords) || (empty($metailGroups) && empty($content))){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$keywords = explode(',',$keywords);
|
||||
|
||||
$query = Keywords::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($query)){
|
||||
return ResponseHelper::error('该素材已被删除或者不存在');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$query->title = $title;
|
||||
$query->type = $type;
|
||||
$query->keywords = !empty($keywords) ? json_encode($keywords,256) : json_encode([]);
|
||||
$query->replyType = $replyType;
|
||||
$query->content = !empty($content) ? json_encode($content,256) : json_encode([]);;
|
||||
$query->metailGroups = !empty($metailGroups) ? json_encode($metailGroups,256) : json_encode([]);;;
|
||||
$query->status = $status;
|
||||
$query->level = $level;
|
||||
$query->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','修改成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('修改失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改关键词状态
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setKeywordStatus(){
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$query = Keywords::where(['id'=>$id,'isDel' => 0,'userId' => $userId,'companyId' => $companyId])->find();
|
||||
if (empty($query)){
|
||||
return ResponseHelper::error('该素材已被删除或者不存在');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$query->status = !empty($query['status']) ? 0 : 1;
|
||||
$query->updateTime = time();
|
||||
$query->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','修改成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('修改失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================================================== 关键词词管理 ====================================================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class CustomerServiceController extends BaseController
|
||||
{
|
||||
|
||||
public function getList(){
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
if (empty($accountId)){
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
$accountIds1= Db::table('s2_wechat_friend')->where(['accountId' => $accountId,'isDeleted' => 0])->group('wechatAccountId')->column('wechatAccountId');
|
||||
$accountIds2 = Db::table('s2_wechat_chatroom')->where(['accountId' => $accountId,'isDeleted' => 0])->group('wechatAccountId')->column('wechatAccountId');
|
||||
// 确保即使有空数组也不会报错,并且去除重复值
|
||||
$accountIds = array_unique(array_merge($accountIds1 ?: [], $accountIds2 ?: []));
|
||||
|
||||
|
||||
|
||||
$wechatAliveTime = time() - 86400 * 30;
|
||||
$list = Db::table('s2_wechat_account')
|
||||
->whereIn('id',$accountIds)
|
||||
->where('wechatAliveTime','>',$wechatAliveTime)
|
||||
->order('id desc')
|
||||
->group('id')
|
||||
->select();
|
||||
foreach ($list as $k=>&$v){
|
||||
$v['createTime'] = !empty($v['createTime']) ? date('Y-m-d H:i:s',$v['createTime']) : '';
|
||||
$v['updateTime'] = !empty($v['updateTime']) ? date('Y-m-d H:i:s',$v['updateTime']) : '';
|
||||
$v['labels'] = json_decode($v['labels'],true);
|
||||
$momentsSetting = Db::name('kf_moments_settings')->where(['userId' => $userId,'companyId' => $companyId,'wechatId' =>$v['id']])->find();
|
||||
$v['momentsMax'] = !empty($momentsSetting['max']) ? $momentsSetting['max'] : 5;
|
||||
$v['momentsNum'] = !empty($momentsSetting['sendNum']) ? $momentsSetting['sendNum'] : 0;
|
||||
|
||||
unset(
|
||||
$v['accountUserName'],
|
||||
$v['accountRealName'],
|
||||
$v['accountNickname'],
|
||||
);
|
||||
}
|
||||
unset($v);
|
||||
|
||||
return ResponseHelper::success($list);
|
||||
}
|
||||
}
|
||||
206
application/chukebao/controller/DataProcessing.php
Normal file
206
application/chukebao/controller/DataProcessing.php
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\api\model\WechatChatroomModel;
|
||||
use library\ResponseHelper;
|
||||
use app\api\model\WechatFriendModel;
|
||||
use app\api\model\WechatMessageModel;
|
||||
use app\api\controller\MessageController;
|
||||
|
||||
|
||||
class DataProcessing extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$type = $this->request->param('type', '');
|
||||
$wechatAccountId = $this->request->param('wechatAccountId', '');
|
||||
//微信好友
|
||||
$toAccountId = $this->request->param('toAccountId', '');
|
||||
$wechatFriendId = $this->request->param('wechatFriendId', '');
|
||||
$newRemark = $this->request->param('newRemark', '');
|
||||
$labels = $this->request->param('labels', []);
|
||||
//微信群
|
||||
$wechatChatroomId = $this->request->param('wechatChatroomId', '');
|
||||
|
||||
//新消息
|
||||
$friendMessage = $this->request->param('friendMessage', '');
|
||||
$chatroomMessage = $this->request->param('chatroomMessage', '');
|
||||
|
||||
$typeData = [
|
||||
'CmdModifyFriendRemark', //好友修改备注 {newRemark、wechatAccountId、wechatFriendId}
|
||||
'CmdModifyFriendLabel', //好友修改标签 {labels、wechatAccountId、wechatFriendId}
|
||||
'CmdAllotFriend', //转让好友 {labels、wechatAccountId、wechatFriendId}
|
||||
'CmdChatroomOperate', //修改群信息 {chatroomName(群名)、announce(公告)、extra(公告)、wechatAccountId、wechatChatroomId}
|
||||
'CmdNewMessage', //接收消息
|
||||
'CmdSendMessageResult', //更新消息状态
|
||||
'CmdPinToTop', //置顶
|
||||
];
|
||||
|
||||
if (empty($type) || empty($wechatAccountId)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
if (!in_array($type, $typeData)) {
|
||||
return ResponseHelper::error('类型错误');
|
||||
}
|
||||
$msg = '';
|
||||
$codee = 200;
|
||||
switch ($type) {
|
||||
case 'CmdModifyFriendRemark': //修改好友备注
|
||||
if(empty($wechatFriendId) || empty($newRemark)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$friend = WechatFriendModel::where(['id' => $wechatFriendId,'wechatAccountId' => $wechatAccountId])->find();
|
||||
if(empty($friend)){
|
||||
return ResponseHelper::error('好友不存在');
|
||||
}
|
||||
$friend->conRemark = $newRemark;
|
||||
$friend->updateTime = time();
|
||||
$friend->save();
|
||||
$msg = '修改备成功';
|
||||
break;
|
||||
case 'CmdModifyFriendLabel': //修改好友标签
|
||||
if(empty($wechatFriendId)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$friend = WechatFriendModel::where(['id' => $wechatFriendId,'wechatAccountId' => $wechatAccountId])->find();
|
||||
if(empty($friend)){
|
||||
return ResponseHelper::error('好友不存在');
|
||||
}
|
||||
$friend->labels = json_encode($labels,256);
|
||||
$friend->updateTime = time();
|
||||
$friend->save();
|
||||
$msg = '修标签成功';
|
||||
break;
|
||||
case 'CmdAllotFriend': //迁移好友
|
||||
if(empty($toAccountId)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
if(empty($wechatFriendId) && empty($wechatChatroomId)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
|
||||
if (!empty($wechatFriendId)){
|
||||
$data = WechatFriendModel::where(['id' => $wechatFriendId,'wechatAccountId' => $wechatAccountId])->find();
|
||||
$msg = '好友转移成功';
|
||||
if(empty($data)){
|
||||
return ResponseHelper::error('好友不存在');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!empty($wechatChatroomId)){
|
||||
$data = WechatChatroomModel::where(['id' => $wechatChatroomId,'wechatAccountId' => $wechatAccountId])->find();
|
||||
$msg = '群聊转移成功';
|
||||
if(empty($data)){
|
||||
return ResponseHelper::error('群聊不存在');
|
||||
}
|
||||
}
|
||||
|
||||
$data->accountId = $toAccountId;
|
||||
$data->updateTime = time();
|
||||
$data->save();
|
||||
break;
|
||||
case 'CmdNewMessage':
|
||||
if(empty($friendMessage) && empty($chatroomMessage)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
if(is_array($friendMessage) && is_array($chatroomMessage)){
|
||||
return ResponseHelper::error('数据类型错误');
|
||||
}
|
||||
|
||||
|
||||
$messageController = new MessageController();
|
||||
if (!empty($friendMessage)){
|
||||
$res = $messageController->saveMessage($friendMessage[0]);
|
||||
}else{
|
||||
$res = $messageController->saveChatroomMessage($chatroomMessage[0]);
|
||||
}
|
||||
if (!empty($res)){
|
||||
$msg = '消息记录成功';
|
||||
}else{
|
||||
$msg = '消息记录失败';
|
||||
$codee = 200;
|
||||
}
|
||||
break;
|
||||
case 'CmdSendMessageResult':
|
||||
$friendMessageId = $this->request->param('friendMessageId', 0);
|
||||
$chatroomMessageId = $this->request->param('chatroomMessageId', 0);
|
||||
$sendStatus = $this->request->param('sendStatus', null);
|
||||
$wechatTime = $this->request->param('wechatTime', 0);
|
||||
|
||||
if ($sendStatus === null) {
|
||||
return ResponseHelper::error('sendStatus不能为空');
|
||||
}
|
||||
|
||||
if (empty($friendMessageId) && empty($chatroomMessageId)) {
|
||||
return ResponseHelper::error('friendMessageId或chatroomMessageId至少提供一个');
|
||||
}
|
||||
|
||||
$messageId = $friendMessageId ?: $chatroomMessageId;
|
||||
$update = [
|
||||
'sendStatus' => (int)$sendStatus,
|
||||
];
|
||||
|
||||
if (!empty($wechatTime)) {
|
||||
$update['wechatTime'] = strlen((string)$wechatTime) > 10
|
||||
? intval($wechatTime / 1000)
|
||||
: (int)$wechatTime;
|
||||
}
|
||||
|
||||
$affected = WechatMessageModel::where('id', $messageId)->update($update);
|
||||
|
||||
if ($affected === false) {
|
||||
return ResponseHelper::success('','更新消息状态失败');
|
||||
}
|
||||
|
||||
if ($affected === 0) {
|
||||
return ResponseHelper::success('','消息不存在');
|
||||
}
|
||||
|
||||
$msg = '更新消息状态成功';
|
||||
break;
|
||||
case 'CmdPinToTop': //置顶
|
||||
$wechatFriendId = $this->request->param('wechatFriendId', 0);
|
||||
$wechatChatroomId = $this->request->param('wechatChatroomId', 0);
|
||||
$isTop = $this->request->param('isTop', null);
|
||||
|
||||
if ($isTop === null) {
|
||||
return ResponseHelper::error('isTop不能为空');
|
||||
}
|
||||
|
||||
if (empty($wechatFriendId) && empty($wechatChatroomId)) {
|
||||
return ResponseHelper::error('wechatFriendId或chatroomId至少提供一个');
|
||||
}
|
||||
|
||||
|
||||
if (!empty($wechatFriendId)){
|
||||
$data = WechatFriendModel::where(['id' => $wechatFriendId,'wechatAccountId' => $wechatAccountId])->find();
|
||||
$msg = $isTop == 1 ? '已置顶' : '取消置顶';
|
||||
if(empty($data)){
|
||||
return ResponseHelper::error('好友不存在');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!empty($wechatChatroomId)){
|
||||
$data = WechatChatroomModel::where(['id' => $wechatChatroomId,'wechatAccountId' => $wechatAccountId])->find();
|
||||
$msg = $isTop == 1 ? '已置顶' : '取消置顶';
|
||||
if(empty($data)){
|
||||
return ResponseHelper::error('群聊不存在');
|
||||
}
|
||||
}
|
||||
|
||||
$data->updateTime = time();
|
||||
$data->isTop = $isTop;
|
||||
$data->save();
|
||||
break;
|
||||
}
|
||||
return ResponseHelper::success('',$msg,$codee);
|
||||
}
|
||||
}
|
||||
144
application/chukebao/controller/FollowUpController.php
Normal file
144
application/chukebao/controller/FollowUpController.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\FollowUp;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class FollowUpController extends BaseController
|
||||
{
|
||||
|
||||
public function getList(){
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$isRemind = $this->request->param('isRemind', '');
|
||||
$isProcess = $this->request->param('isProcess', '');
|
||||
$type = $this->request->param('type', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
|
||||
$where = [
|
||||
['companyId','=',$companyId],
|
||||
['userId' ,'=', $userId]
|
||||
];
|
||||
|
||||
if ($isRemind != '') {
|
||||
$where[] = ['isRemind','=',$isRemind];
|
||||
}
|
||||
if ($type != '') {
|
||||
$where[] = ['type','=',$type];
|
||||
}
|
||||
if ($isProcess != '') {
|
||||
$where[] = ['isProcess','=',$isProcess];
|
||||
}
|
||||
|
||||
if(!empty($keyword)){
|
||||
$where[] = ['title|description','like','%'.$keyword.'%'];
|
||||
}
|
||||
|
||||
$query = FollowUp::where($where);
|
||||
|
||||
$total = $query->count();
|
||||
$list = $query->where($where)->page($page,$limit)->order('id desc')->select();
|
||||
|
||||
|
||||
foreach ($list as &$item) {
|
||||
$nickname = Db::table('s2_wechat_friend')->where(['id' => $item['friendId']])->value('nickname');
|
||||
$item['nickname'] = !empty($nickname) ? $nickname : '-';
|
||||
$item['reminderTime'] = date('Y-m-d H:i:s',$item['reminderTime']);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return ResponseHelper::success(['list'=>$list,'total'=>$total]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function create(){
|
||||
$type = $this->request->param('type', 0);
|
||||
$title = $this->request->param('title', '');
|
||||
$reminderTime = $this->request->param('reminderTime', '');
|
||||
$description = $this->request->param('description', '');
|
||||
$friendId = $this->request->param('friendId', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($title) || empty($reminderTime) || empty($description) || empty($friendId)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$friend = Db::table('s2_wechat_friend')->where(['id' => $friendId])->find();
|
||||
if (empty($friend)) {
|
||||
return ResponseHelper::error('好友不存在');
|
||||
}
|
||||
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$FollowUp = new FollowUp();
|
||||
$FollowUp->type = $type;
|
||||
$FollowUp->title = $title;
|
||||
$FollowUp->friendId = $friendId;
|
||||
$FollowUp->reminderTime = !empty($reminderTime) ? strtotime($reminderTime) : time();
|
||||
$FollowUp->description = $description;
|
||||
$FollowUp->userId = $userId;
|
||||
$FollowUp->companyId = $companyId;
|
||||
$FollowUp->updateTime = time();
|
||||
$FollowUp->createTime = time();
|
||||
$FollowUp->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','创建成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('创建失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理代办事项
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function process(){
|
||||
$ids = $this->request->param('ids','');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($ids)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$ids = explode(',',$ids);
|
||||
|
||||
if (!is_array($ids)){
|
||||
return ResponseHelper::error('格式错误');
|
||||
}
|
||||
|
||||
$FollowUpIds = FollowUp::where(['userId' => $userId,'companyId' => $companyId,'isProcess' => 0])->whereIn('id',$ids)->column('id');
|
||||
if (empty($FollowUpIds)){
|
||||
return ResponseHelper::error('代办事项不存在');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
FollowUp::whereIn('id',$FollowUpIds)->update(['isProcess' => 1,'isRemind' => 1,'updateTime' => time()]);
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','已处理');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('处理失败:'.$e->getMessage());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
141
application/chukebao/controller/LoginController.php
Normal file
141
application/chukebao/controller/LoginController.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\common\util\JwtUtil;
|
||||
use Exception;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
use think\Controller;
|
||||
use think\facade\Cache;
|
||||
|
||||
/**
|
||||
* 认证控制器
|
||||
* 处理用户登录和身份验证
|
||||
*/
|
||||
class LoginController extends Controller
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index($username = '', $password = '',$verifySessionId = '',$verifyCode = '')
|
||||
{
|
||||
|
||||
$username = !empty($username) ? $username : $this->request->param('account', '');
|
||||
$password = !empty($password) ? $password : $this->request->param('password', '');
|
||||
$verifySessionId =!empty($verifySessionId) ? $verifySessionId : $this->request->param('verifySessionId', '');
|
||||
$verifyCode = !empty($verifyCode) ? $verifyCode : $this->request->param('verifyCode', '');
|
||||
$token = JwtUtil::getRequestToken();
|
||||
$payload = '';
|
||||
if (!empty($token)){
|
||||
$payload = JwtUtil::verifyToken($token);
|
||||
}
|
||||
|
||||
if ((empty($username) || empty($password)) && empty($payload)){
|
||||
return ResponseHelper::error('请输入账号密码');
|
||||
}
|
||||
|
||||
// 验证账号是否存在(支持账号或手机号登录)
|
||||
if (empty($payload11)){
|
||||
$user = Db::name('users')
|
||||
->where(function ($query) use ($username) {
|
||||
$query->where('account', $username)->whereOr('phone', $username);
|
||||
})
|
||||
->where(function ($query2) use ($password) {
|
||||
$query2->where('passwordMd5', md5($password))->whereOr('passwordLocal', localEncrypt($password));
|
||||
})
|
||||
->find();
|
||||
}else{
|
||||
$user = $payload;
|
||||
}
|
||||
|
||||
if (empty($user)) {
|
||||
return ResponseHelper::error('账号不存在或密码错误');
|
||||
}
|
||||
|
||||
if($user['status'] != 1){
|
||||
return ResponseHelper::error('账号已禁用');
|
||||
}
|
||||
|
||||
//登录参数
|
||||
$params = [
|
||||
'grant_type' => 'password',
|
||||
'username' => $user['account'],
|
||||
'password' => !empty($user['passwordLocal']) ? localDecrypt($user['passwordLocal']) : $password
|
||||
];
|
||||
try {
|
||||
// 调用登录接口获取token
|
||||
$headerData = ['client:kefu-client'];
|
||||
if (!empty($verifySessionId) && !empty($verifyCode)){
|
||||
$headerData[] = 'verifysessionid:'.$verifySessionId;
|
||||
$headerData[] = 'verifycode:'.$verifyCode;
|
||||
}
|
||||
$header = setHeader($headerData, '', 'plain');
|
||||
$result = requestCurl('https://s2.siyuguanli.com:9991/token', $params, 'POST', $header);
|
||||
$result = handleApiResponse($result);
|
||||
if (isset($result['access_token']) && !empty($result['access_token'])) {
|
||||
$kefuData['token'] = $result;
|
||||
$headerData = ['client:kefu-client'];
|
||||
$header = setHeader($headerData, $result['access_token']);
|
||||
$result2 = requestCurl('https://s2.siyuguanli.com:9991/api/account/self', [], 'GET', $header, 'json');
|
||||
$self = handleApiResponse($result2);
|
||||
$kefuData['self'] = $self;
|
||||
Db::name('users')->where('id', $user['id'])->update(['passwordLocal' => localEncrypt($params['password']),'updateTime' => time()]);
|
||||
}else{
|
||||
$kefuData = [
|
||||
'token' => [
|
||||
"access_token"=> "27gINKZqGux6V4j9QLawOcTKlWXg-j4zxQjKvScvDTq-YlLcwIrDP2AFaNZKnOo9zLzepOBC8qrdXh4z9GxxkwE9TKGRQI1FjITRlMZzrim13IbSEbJUoywGs_BhDmIZnnPhfjqxDB1vjZgVtT2Kp4bxbUCV3i2uO_FTv_DT2G7NUFFLjq8oIuUrd_c1YXeYkH8m8Fw1AM4yPZJZyfdaHSSMOpJ2Bk2LAghnB6OaZCYWNFQcwWARsmh1BSAANUOAoadjkztZC7Fme-GGOm2sLo0WL6Mf26NfeLmnkluewTiPMyacD7RYclAR2LZ_8Mhwr3pwRg",
|
||||
"token_type"=> "bearer",
|
||||
"expires_in"=> 195519999,
|
||||
"refresh_token"=> "a9545daa-d1c4-4c87-8c4c-b713631d4f0d"
|
||||
],
|
||||
'self' => [
|
||||
'account' => [
|
||||
"id"=> 5538,
|
||||
"realName"=> "测试",
|
||||
"nickname"=> "",
|
||||
"memo"=> "",
|
||||
"avatar"=> "",
|
||||
"userName"=> "wz_02",
|
||||
"secret"=> "8f6f743395ad4198b6a4c0e6ca0e452f",
|
||||
"accountType"=> 10,
|
||||
"departmentId"=> 2130,
|
||||
"useGoogleSecretKey"=> false,
|
||||
"hasVerifyGoogleSecret"=> true
|
||||
],
|
||||
'tenant' => [
|
||||
"id" => 242,
|
||||
"name"=> "泉州市卡若网络技术有限公司",
|
||||
"guid"=> "5E2C38F5A275450D935F3ECEC076124E",
|
||||
"thirdParty"=> null,
|
||||
"tenantType"=> 0,
|
||||
"deployName"=> "deploy-s2"
|
||||
]
|
||||
]
|
||||
];
|
||||
//return ResponseHelper::error($result['error_description']);
|
||||
}
|
||||
|
||||
|
||||
unset($user['passwordMd5'],$user['deleteTime']);
|
||||
$userData['member'] = $user;
|
||||
|
||||
// 生成JWT令牌
|
||||
$expired = 86400 * 30;
|
||||
$token = JwtUtil::createToken($user, $expired);
|
||||
$token_expired = time() + $expired;
|
||||
|
||||
$userData['token'] = $token;
|
||||
$userData['token_expired'] = $token_expired;
|
||||
$userData['kefuData'] = $kefuData;
|
||||
|
||||
return ResponseHelper::success($userData, '登录成功');
|
||||
} catch (Exception $e) {
|
||||
return ResponseHelper::error($e->getMessage(), $e->getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
476
application/chukebao/controller/MessageController.php
Normal file
476
application/chukebao/controller/MessageController.php
Normal file
@@ -0,0 +1,476 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\api\model\WechatMessageModel;
|
||||
use app\chukebao\model\FriendSettings;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
use think\facade\Env;
|
||||
use app\common\service\AuthService;
|
||||
|
||||
class MessageController extends BaseController
|
||||
{
|
||||
protected $baseUrl;
|
||||
protected $authorization;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->baseUrl = Env::get('api.wechat_url');
|
||||
$this->authorization = AuthService::getSystemAuthorization();
|
||||
}
|
||||
|
||||
public function getList()
|
||||
{
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
if (empty($accountId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
$friends = Db::table('s2_wechat_friend')
|
||||
->where(['accountId' => $accountId, 'isDeleted' => 0])
|
||||
->column('id,nickname,avatar,conRemark,labels,groupId,wechatAccountId,wechatId,extendFields,phone,region,isTop');
|
||||
|
||||
|
||||
// 构建好友子查询
|
||||
$friendSubQuery = Db::table('s2_wechat_friend')
|
||||
->where(['accountId' => $accountId, 'isDeleted' => 0])
|
||||
->field('id')
|
||||
->buildSql();
|
||||
|
||||
// 优化后的查询:使用MySQL兼容的查询方式
|
||||
$unionQuery = "
|
||||
(SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime,m.wechatAccountId, 2 as msgType, wc.nickname, wc.chatroomAvatar as avatar, wc.chatroomId, wc.isTop
|
||||
FROM s2_wechat_chatroom wc
|
||||
INNER JOIN s2_wechat_message m ON wc.id = m.wechatChatroomId AND m.type = 2
|
||||
INNER JOIN (
|
||||
SELECT wechatChatroomId, MAX(wechatTime) as maxTime, MAX(id) as maxId
|
||||
FROM s2_wechat_message
|
||||
WHERE type = 2
|
||||
GROUP BY wechatChatroomId
|
||||
) latest ON m.wechatChatroomId = latest.wechatChatroomId AND m.wechatTime = latest.maxTime AND m.id = latest.maxId
|
||||
WHERE wc.accountId = {$accountId} AND wc.isDeleted = 0
|
||||
)
|
||||
UNION ALL
|
||||
(SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime, 1 as msgType, 1 as nickname, 1 as avatar, 1 as chatroomId, 1 as wechatAccountId, 0 as isTop
|
||||
FROM s2_wechat_message m
|
||||
INNER JOIN (
|
||||
SELECT wechatFriendId, MAX(wechatTime) as maxTime, MAX(id) as maxId
|
||||
FROM s2_wechat_message
|
||||
WHERE type = 1 AND wechatFriendId IN {$friendSubQuery}
|
||||
GROUP BY wechatFriendId
|
||||
) latest ON m.wechatFriendId = latest.wechatFriendId AND m.wechatTime = latest.maxTime AND m.id = latest.maxId
|
||||
WHERE m.type = 1 AND m.wechatFriendId IN {$friendSubQuery}
|
||||
)
|
||||
ORDER BY wechatTime DESC
|
||||
LIMIT " . (($page - 1) * $limit) . ", {$limit}
|
||||
";
|
||||
|
||||
$list = Db::query($unionQuery);
|
||||
|
||||
// 对分页后的结果进行排序(按wechatTime降序)
|
||||
usort($list, function ($a, $b) {
|
||||
return $b['wechatTime'] <=> $a['wechatTime'];
|
||||
});
|
||||
|
||||
// 批量统计未读数量(isRead=0),按好友/群聊分别聚合
|
||||
$friendIds = [];
|
||||
$chatroomIds = [];
|
||||
foreach ($list as $row) {
|
||||
if (!empty($row['wechatFriendId'])) {
|
||||
$friendIds[] = $row['wechatFriendId'];
|
||||
}
|
||||
if (!empty($row['wechatChatroomId'])) {
|
||||
$chatroomIds[] = $row['wechatChatroomId'];
|
||||
}
|
||||
}
|
||||
$friendIds = array_values(array_unique(array_filter($friendIds)));
|
||||
$chatroomIds = array_values(array_unique(array_filter($chatroomIds)));
|
||||
|
||||
$friendUnreadMap = [];
|
||||
if (!empty($friendIds)) {
|
||||
// 获取未读消息数量
|
||||
$friendUnreadMap = Db::table('s2_wechat_message')
|
||||
->where(['isRead' => 0])
|
||||
->whereIn('wechatFriendId', $friendIds)
|
||||
->group('wechatFriendId')
|
||||
->column('COUNT(*) AS cnt', 'wechatFriendId');
|
||||
}
|
||||
|
||||
$chatroomUnreadMap = [];
|
||||
if (!empty($chatroomIds)) {
|
||||
// 获取未读消息数量
|
||||
$chatroomUnreadMap = Db::table('s2_wechat_message')
|
||||
->where(['isRead' => 0])
|
||||
->whereIn('wechatChatroomId', $chatroomIds)
|
||||
->group('wechatChatroomId')
|
||||
->column('COUNT(*) AS cnt', 'wechatChatroomId');
|
||||
}
|
||||
|
||||
$aiTypeData = [];
|
||||
if (!empty($friendIds)) {
|
||||
$aiTypeData = FriendSettings::where('friendId', 'in', $friendIds)->column('friendId,type');
|
||||
}
|
||||
|
||||
foreach ($list as $k => &$v) {
|
||||
|
||||
$createTime = !empty($v['createTime']) ? date('Y-m-d H:i:s', $v['createTime']) : '';
|
||||
$wechatTime = !empty($v['wechatTime']) ? date('Y-m-d H:i:s', $v['wechatTime']) : '';
|
||||
|
||||
|
||||
$unreadCount = 0;
|
||||
$v['aiType'] = 0;
|
||||
if (!empty($v['wechatFriendId'])) {
|
||||
$v['nickname'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['nickname'] : '';
|
||||
$v['avatar'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['avatar'] : '';
|
||||
$v['conRemark'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['conRemark'] : '';
|
||||
$v['groupId'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['groupId'] : '';
|
||||
$v['wechatAccountId'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['wechatAccountId'] : '';
|
||||
$v['wechatId'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['wechatId'] : '';
|
||||
$v['extendFields'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['extendFields'] : [];
|
||||
$v['region'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['region'] : '';
|
||||
$v['phone'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['phone'] : '';
|
||||
$v['isTop'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['isTop'] : 0;
|
||||
$v['labels'] = !empty($friends[$v['wechatFriendId']]) ? json_decode($friends[$v['wechatFriendId']]['labels'], true) : [];
|
||||
|
||||
$unreadCount = isset($friendUnreadMap[$v['wechatFriendId']]) ? (int)$friendUnreadMap[$v['wechatFriendId']] : 0;
|
||||
$v['aiType'] = isset($aiTypeData[$v['wechatFriendId']]) ? $aiTypeData[$v['wechatFriendId']] : 0;
|
||||
unset($v['chatroomId']);
|
||||
}
|
||||
|
||||
if (!empty($v['wechatChatroomId'])) {
|
||||
$v['conRemark'] = '';
|
||||
$unreadCount = isset($chatroomUnreadMap[$v['wechatChatroomId']]) ? (int)$chatroomUnreadMap[$v['wechatChatroomId']] : 0;
|
||||
}
|
||||
|
||||
$v['id'] = !empty($v['wechatFriendId']) ? $v['wechatFriendId'] : $v['wechatChatroomId'];
|
||||
$v['config'] = [
|
||||
'top' => !empty($v['isTop']) ? true : false,
|
||||
'unreadCount' => $unreadCount,
|
||||
'chat' => true,
|
||||
'msgTime' => $v['wechatTime'],
|
||||
];
|
||||
$v['createTime'] = $createTime;
|
||||
$v['lastUpdateTime'] = $wechatTime;
|
||||
|
||||
// 最新消息内容已经在UNION查询中获取,直接使用
|
||||
$v['latestMessage'] = [
|
||||
'content' => $v['content'],
|
||||
'wechatTime' => $wechatTime
|
||||
];
|
||||
|
||||
unset($v['wechatFriendId'], $v['wechatChatroomId'],$v['isTop']);
|
||||
|
||||
}
|
||||
unset($v);
|
||||
return ResponseHelper::success($list);
|
||||
}
|
||||
|
||||
|
||||
public function readMessage()
|
||||
{
|
||||
$wechatFriendId = $this->request->param('wechatFriendId', '');
|
||||
$wechatChatroomId = $this->request->param('wechatChatroomId', '');
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
if (empty($accountId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
if (empty($wechatChatroomId) && empty($wechatFriendId)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$where = [];
|
||||
if (!empty($wechatChatroomId)) {
|
||||
$where[] = ['wechatChatroomId', '=', $wechatChatroomId];
|
||||
}
|
||||
|
||||
if (!empty($wechatFriendId)) {
|
||||
$where[] = ['wechatFriendId', '=', $wechatFriendId];
|
||||
}
|
||||
|
||||
Db::table('s2_wechat_message')->where($where)->update(['isRead' => 1]);
|
||||
return ResponseHelper::success([]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取单条消息发送状态(带轮询功能)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getMessageStatus()
|
||||
{
|
||||
$messageId = $this->request->param('messageId', 0);
|
||||
$wechatAccountId = $this->request->param('wechatAccountId', '');
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
$wechatFriendId = $this->request->param('wechatFriendId', '');
|
||||
$wechatChatroomId = $this->request->param('wechatChatroomId', '');
|
||||
|
||||
if (empty($accountId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
if (empty($messageId)) {
|
||||
return ResponseHelper::error('消息ID不能为空');
|
||||
}
|
||||
|
||||
if(empty($wechatFriendId) && empty($wechatChatroomId)) {
|
||||
return ResponseHelper::error('消息类型不能为空');
|
||||
}
|
||||
|
||||
// 查询单条消息的基本信息(只需要发送状态相关字段)
|
||||
$message = Db::table('s2_wechat_message')
|
||||
->where('id', $messageId)
|
||||
->field('id,wechatAccountId,wechatFriendId,wechatChatroomId,sendStatus')
|
||||
->find();
|
||||
|
||||
if (empty($message)) {
|
||||
$message = [
|
||||
'id' => $messageId,
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'wechatFriendId' => $wechatFriendId,
|
||||
'wechatChatroomId' => $wechatChatroomId,
|
||||
'sendStatus' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$sendStatus = isset($message['sendStatus']) ? (int)$message['sendStatus'] : 0;
|
||||
$isUpdated = false;
|
||||
$pollCount = 0;
|
||||
$maxPollCount = 10; // 最多轮询10次
|
||||
|
||||
// 如果sendStatus不为0,开始轮询
|
||||
if ($sendStatus != 0) {
|
||||
$messageRequest = [
|
||||
'id' => $message['id'],
|
||||
'wechatAccountId' => !empty($wechatAccountId) ? $wechatAccountId : $message['wechatAccountId'],
|
||||
'wechatFriendId' => !empty($message['wechatFriendId']) ? $message['wechatFriendId'] : '',
|
||||
'wechatChatroomId' => !empty($message['wechatChatroomId']) ? $message['wechatChatroomId'] : '',
|
||||
'from' => '',
|
||||
'to' => '',
|
||||
];
|
||||
|
||||
|
||||
// 轮询逻辑:最多10次
|
||||
while ($pollCount < $maxPollCount && $sendStatus != 0) {
|
||||
$pollCount++;
|
||||
|
||||
// 请求线上接口获取最新状态
|
||||
$newData = $this->fetchLatestMessageFromApi($messageRequest);
|
||||
|
||||
if (!empty($newData)) {
|
||||
// 重新查询消息状态(可能已更新)
|
||||
$updatedMessage = Db::table('s2_wechat_message')
|
||||
->where('id', $messageId)
|
||||
->field('sendStatus')
|
||||
->find();
|
||||
|
||||
if (!empty($updatedMessage)) {
|
||||
$newSendStatus = isset($updatedMessage['sendStatus']) ? (int)$updatedMessage['sendStatus'] : 0;
|
||||
|
||||
// 如果状态已更新为0(已发送),停止轮询
|
||||
if ($newSendStatus == 0) {
|
||||
$sendStatus = 0;
|
||||
$isUpdated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果状态仍然是1,继续轮询(但需要等待一下,避免请求过快)
|
||||
if ($newSendStatus != 0 && $pollCount < $maxPollCount) {
|
||||
// 每次轮询间隔500毫秒(0.5秒)
|
||||
usleep(500000);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果请求失败,等待后继续尝试
|
||||
if ($pollCount < $maxPollCount) {
|
||||
usleep(500000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 返回发送状态信息
|
||||
return ResponseHelper::success([
|
||||
'messageId' => $messageId,
|
||||
'sendStatus' => $sendStatus,
|
||||
'statusText' => $sendStatus == 0 ? '已发送' : '发送中'
|
||||
]);
|
||||
}
|
||||
|
||||
public function details()
|
||||
{
|
||||
$wechatFriendId = $this->request->param('wechatFriendId', '');
|
||||
$wechatChatroomId = $this->request->param('wechatChatroomId', '');
|
||||
$wechatAccountId = $this->request->param('wechatAccountId', '');
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$from = $this->request->param('From', '');
|
||||
$to = $this->request->param('To', '');
|
||||
$olderData = $this->request->param('olderData', false);
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
if (empty($accountId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
if (empty($wechatChatroomId) && empty($wechatFriendId)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$where = [];
|
||||
if (!empty($wechatChatroomId)) {
|
||||
$where[] = ['wechatChatroomId', '=', $wechatChatroomId];
|
||||
}
|
||||
|
||||
if (!empty($wechatFriendId)) {
|
||||
$where[] = ['wechatFriendId', '=', $wechatFriendId];
|
||||
}
|
||||
|
||||
if (!empty($From) && !empty($To)) {
|
||||
$where[] = ['wechatTime', 'between', [$from, $to]];
|
||||
}
|
||||
|
||||
$total = Db::table('s2_wechat_message')->where($where)->count();
|
||||
$list = Db::table('s2_wechat_message')->where($where)->page($page, $limit)->order('id DESC')->select();
|
||||
|
||||
// 检查消息是否有sendStatus字段,如果有且不为0,则请求线上最新接口
|
||||
foreach ($list as $k => &$item) {
|
||||
// 检查是否存在sendStatus字段且不为0(0表示已发送成功)
|
||||
if (isset($item['sendStatus']) && $item['sendStatus'] != 0) {
|
||||
// 需要请求新的数据
|
||||
$messageRequest = [
|
||||
'id' => $item['id'],
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'wechatFriendId' => $wechatFriendId,
|
||||
'wechatChatroomId' => $wechatChatroomId,
|
||||
'from' => '',
|
||||
'to' => '',
|
||||
];
|
||||
$newData = $this->fetchLatestMessageFromApi($messageRequest);
|
||||
if (!empty($newData)){
|
||||
$item['sendStatus'] = 0;
|
||||
}
|
||||
}
|
||||
// 格式化时间
|
||||
$item['wechatTime'] = !empty($item['wechatTime']) ? date('Y-m-d H:i:s', $item['wechatTime']) : '';
|
||||
}
|
||||
unset($item);
|
||||
|
||||
|
||||
return ResponseHelper::success(['total' => $total, 'list' => $list]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 从线上接口获取最新消息
|
||||
* @param array $messageRequest 消息项(包含wechatAccountId、wechatFriendId或wechatChatroomId、id等)
|
||||
* @return array|null 最新消息数据,失败返回null
|
||||
*/
|
||||
private function fetchLatestMessageFromApi($messageRequest)
|
||||
{
|
||||
if (empty($this->baseUrl) || empty($this->authorization)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// 设置请求头
|
||||
$headerData = ['client:system'];
|
||||
$header = setHeader($headerData, $this->authorization, 'json');
|
||||
|
||||
// 判断是好友消息还是群聊消息
|
||||
if (!empty($messageRequest['wechatFriendId'])) {
|
||||
// 好友消息接口
|
||||
$params = [
|
||||
'keyword' => '',
|
||||
'msgType' => '',
|
||||
'accountId' => '',
|
||||
'count' => 20, // 获取多条消息以便找到对应的消息
|
||||
'messageId' => isset($messageRequest['id']) ? $messageRequest['id'] : '',
|
||||
'olderData' => true,
|
||||
'wechatAccountId' => $messageRequest['wechatAccountId'],
|
||||
'wechatFriendId' => $messageRequest['wechatFriendId'],
|
||||
'from' => $messageRequest['from'],
|
||||
'to' => $messageRequest['to'],
|
||||
'searchFrom' => 'admin'
|
||||
];
|
||||
$result = requestCurl($this->baseUrl . 'api/FriendMessage/searchMessage', $params, 'GET', $header, 'json');
|
||||
$response = handleApiResponse($result);
|
||||
// 查找对应的消息
|
||||
if (!empty($response) && is_array($response)) {
|
||||
$data = $response[0];
|
||||
if ($data['sendStatus'] == 0){
|
||||
WechatMessageModel::where(['id' => $data['id']])->update(['sendStatus' => 0]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} elseif (!empty($messageRequest['wechatChatroomId'])) {
|
||||
// 群聊消息接口
|
||||
$params = [
|
||||
'keyword' => '',
|
||||
'msgType' => '',
|
||||
'accountId' => '',
|
||||
'count' => 20, // 获取多条消息以便找到对应的消息
|
||||
'messageId' => isset($messageRequest['id']) ? $messageRequest['id'] : '',
|
||||
'olderData' => true,
|
||||
'wechatId' => '',
|
||||
'wechatAccountId' => $messageRequest['wechatAccountId'],
|
||||
'wechatChatroomId' => $messageRequest['wechatChatroomId'],
|
||||
'from' => $messageRequest['from'],
|
||||
'to' => $messageRequest['to'],
|
||||
'searchFrom' => 'admin'
|
||||
];
|
||||
|
||||
$result = requestCurl($this->baseUrl . 'api/ChatroomMessage/searchMessage', $params, 'GET', $header, 'json');
|
||||
$response = handleApiResponse($result);
|
||||
|
||||
// 查找对应的消息
|
||||
if (!empty($response) && is_array($response)) {
|
||||
$data = $response[0];
|
||||
if ($data['sendStatus'] == 0){
|
||||
WechatMessageModel::where(['id' => $data['id']])->update(['sendStatus' => 0]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 记录错误日志,但不影响主流程
|
||||
\think\facade\Log::error('获取线上最新消息失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据库中的消息
|
||||
* @param array $latestMessage 线上获取的最新消息
|
||||
* @param array $oldMessage 旧消息数据
|
||||
*/
|
||||
private function updateMessageInDatabase($latestMessage, $oldMessage)
|
||||
{
|
||||
try {
|
||||
// 使用API模块的MessageController来保存消息
|
||||
$apiMessageController = new \app\api\controller\MessageController();
|
||||
|
||||
// 判断是好友消息还是群聊消息
|
||||
if (!empty($oldMessage['wechatFriendId'])) {
|
||||
// 保存好友消息
|
||||
$apiMessageController->saveMessage($latestMessage);
|
||||
} elseif (!empty($oldMessage['wechatChatroomId'])) {
|
||||
// 保存群聊消息
|
||||
$apiMessageController->saveChatroomMessage($latestMessage);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 记录错误日志,但不影响主流程
|
||||
\think\facade\Log::error('更新数据库消息失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
504
application/chukebao/controller/MomentsController.php
Normal file
504
application/chukebao/controller/MomentsController.php
Normal file
@@ -0,0 +1,504 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\KfMoments;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class MomentsController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 创建朋友圈
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
// 获取请求参数
|
||||
$text = $this->request->param('content', ''); // 朋友圈内容
|
||||
$picUrlList = $this->request->param('picUrlList', []); // 图片列表
|
||||
$videoUrl = $this->request->param('videoUrl', ''); // 视频链接
|
||||
$link = $this->request->param('link', []); // 链接信息
|
||||
$momentContentType = (int)$this->request->param('type', 1); // 内容类型 1文本 2图文 3视频 4链接
|
||||
$publicMode = (int)$this->request->param('publicMode', 0); // 公开模式
|
||||
$wechatIds = $this->request->param('wechatIds', []); // 微信账号ID列表
|
||||
$labels = $this->request->param('labels', []); // 标签列表
|
||||
$timingTime = $this->request->param('timingTime', date('Y-m-d H:i:s')); // 定时发布时间
|
||||
$immediately = $this->request->param('immediately', false); // 是否立即发布
|
||||
|
||||
// 格式化时间字符串为统一格式
|
||||
$timingTime = $this->normalizeTimingTime($timingTime);
|
||||
if ($timingTime === false) {
|
||||
return ResponseHelper::error('定时发布时间格式不正确');
|
||||
}
|
||||
|
||||
// 参数验证
|
||||
if (empty($text) && empty($picUrlList) && empty($videoUrl)) {
|
||||
return ResponseHelper::error('朋友圈内容不能为空');
|
||||
}
|
||||
|
||||
if (empty($wechatIds)) {
|
||||
return ResponseHelper::error('请选择发布账号');
|
||||
}
|
||||
|
||||
// 校验内容类型
|
||||
if (!in_array($momentContentType, [1, 2, 3, 4])) {
|
||||
return ResponseHelper::error('内容类型不合法,支持:1文本 2图文 3视频 4链接');
|
||||
}
|
||||
|
||||
if(!empty($labels)){
|
||||
$publicMode = 2;
|
||||
}
|
||||
|
||||
// 根据内容类型校验必要参数
|
||||
switch ($momentContentType) {
|
||||
case 1: // 文本
|
||||
if (empty($text)) {
|
||||
return ResponseHelper::error('文本类型必须填写内容');
|
||||
}
|
||||
break;
|
||||
case 2: // 图文
|
||||
if (empty($text) || empty($picUrlList)) {
|
||||
return ResponseHelper::error('图文类型必须填写内容和上传图片');
|
||||
}
|
||||
break;
|
||||
case 3: // 视频
|
||||
if (empty($videoUrl)) {
|
||||
return ResponseHelper::error('视频类型必须上传视频');
|
||||
}
|
||||
break;
|
||||
case 4: // 链接
|
||||
if (empty($link)) {
|
||||
return ResponseHelper::error('链接类型必须填写链接信息');
|
||||
}
|
||||
if (empty($link['url'])) {
|
||||
return ResponseHelper::error('链接类型必须填写链接地址');
|
||||
}
|
||||
if (empty($link['desc'])) {
|
||||
return ResponseHelper::error('链接类型必须填写链接描述');
|
||||
}
|
||||
if (empty($link['image'])) {
|
||||
return ResponseHelper::error('链接类型必须填写链接图片');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 处理链接信息 - 所有链接都必须验证
|
||||
if (!empty($link)) {
|
||||
$link = [
|
||||
'desc' => $link['desc'] ?? '',
|
||||
'image' => $link['image'] ?? '',
|
||||
'url' => $link['url'] ?? ''
|
||||
];
|
||||
|
||||
// 验证链接URL格式
|
||||
if (!empty($link['url']) && !filter_var($link['url'], FILTER_VALIDATE_URL)) {
|
||||
return ResponseHelper::error('链接地址格式不正确');
|
||||
}
|
||||
} else {
|
||||
$link = ['desc' => '', 'image' => '', 'url' => ''];
|
||||
}
|
||||
|
||||
// 构建发布账号列表
|
||||
$jobPublishWechatMomentsItems = $this->buildJobPublishWechatMomentsItems($wechatIds, $labels);
|
||||
if (empty($jobPublishWechatMomentsItems)) {
|
||||
return ResponseHelper::error('无法获取有效的发布账号信息');
|
||||
}
|
||||
|
||||
try {
|
||||
// 构建发送数据
|
||||
$sendData = [
|
||||
'altList' => '',
|
||||
'beginTime' => $timingTime,
|
||||
'endTime' => date('Y-m-d H:i:s', strtotime($timingTime) + 3600),
|
||||
'immediately' => $immediately,
|
||||
'isUseLocation' => false,
|
||||
'jobPublishWechatMomentsItems' => $jobPublishWechatMomentsItems,
|
||||
'lat' => 0,
|
||||
'lng' => 0,
|
||||
'link' => $link,
|
||||
'momentContentType' => $momentContentType,
|
||||
'picUrlList' => $picUrlList,
|
||||
'poiAddress' => '',
|
||||
'poiName' => '',
|
||||
'publicMode' => $publicMode,
|
||||
'text' => $text,
|
||||
'timingTime' => $timingTime ?: date('Y-m-d H:i:s'),
|
||||
'videoUrl' => $videoUrl
|
||||
];
|
||||
|
||||
// 保存到数据库
|
||||
$moments = new KfMoments();
|
||||
$moments->companyId = $companyId;
|
||||
$moments->userId = $userId;
|
||||
$moments->sendData = json_encode($sendData, 256);
|
||||
$nowTs = time();
|
||||
$moments->createTime = $nowTs;
|
||||
$moments->updateTime = $nowTs;
|
||||
$moments->isDel = 0;
|
||||
$moments->delTime = null;
|
||||
$moments->isSend = $immediately ? 1 : 0;
|
||||
$moments->sendTime = $immediately ? $nowTs : strtotime($timingTime);
|
||||
$moments->save();
|
||||
|
||||
// 如果立即发布,调用发布接口
|
||||
if ($immediately) {
|
||||
$this->publishMoments($sendData);
|
||||
}
|
||||
return ResponseHelper::success('', '朋友圈创建成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('创建失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑朋友圈
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$id = (int)$this->request->param('id', 0);
|
||||
if ($id <= 0) {
|
||||
return ResponseHelper::error('ID不合法');
|
||||
}
|
||||
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
// 获取请求参数(与创建一致的字段名)
|
||||
$text = $this->request->param('content', '');
|
||||
$picUrlList = $this->request->param('picUrlList', []);
|
||||
$videoUrl = $this->request->param('videoUrl', '');
|
||||
$link = $this->request->param('link', []);
|
||||
$momentContentType = (int)$this->request->param('type', 1);
|
||||
$publicMode = (int)$this->request->param('publicMode', 0);
|
||||
$wechatIds = $this->request->param('wechatIds', []);
|
||||
$labels = $this->request->param('labels', []);
|
||||
$timingTime = $this->request->param('timingTime', date('Y-m-d H:i:s'));
|
||||
$immediately = $this->request->param('immediately', false);
|
||||
|
||||
// 格式化时间字符串为统一格式
|
||||
$timingTime = $this->normalizeTimingTime($timingTime);
|
||||
if ($timingTime === false) {
|
||||
return ResponseHelper::error('定时发布时间格式不正确');
|
||||
}
|
||||
|
||||
// 读取待编辑记录
|
||||
/** @var KfMoments|null $moments */
|
||||
$moments = KfMoments::where(['id' => $id, 'companyId' => $companyId, 'userId' => $userId, 'isDel' => 0])->find();
|
||||
if (empty($moments)) {
|
||||
return ResponseHelper::error('朋友圈不存在');
|
||||
}
|
||||
|
||||
// 参数校验
|
||||
if (empty($text) && empty($picUrlList) && empty($videoUrl)) {
|
||||
return ResponseHelper::error('朋友圈内容不能为空');
|
||||
}
|
||||
if (empty($wechatIds)) {
|
||||
return ResponseHelper::error('请选择发布账号');
|
||||
}
|
||||
if (!in_array($momentContentType, [1, 2, 3, 4])) {
|
||||
return ResponseHelper::error('内容类型不合法,支持:1文本 2图文 3视频 4链接');
|
||||
}
|
||||
if (!empty($labels)) {
|
||||
$publicMode = 2;
|
||||
}
|
||||
switch ($momentContentType) {
|
||||
case 1:
|
||||
if (empty($text)) {
|
||||
return ResponseHelper::error('文本类型必须填写内容');
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (empty($text) || empty($picUrlList)) {
|
||||
return ResponseHelper::error('图文类型必须填写内容和上传图片');
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (empty($videoUrl)) {
|
||||
return ResponseHelper::error('视频类型必须上传视频');
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (empty($link)) {
|
||||
return ResponseHelper::error('链接类型必须填写链接信息');
|
||||
}
|
||||
if (empty($link['url'])) {
|
||||
return ResponseHelper::error('链接类型必须填写链接地址');
|
||||
}
|
||||
if (empty($link['desc'])) {
|
||||
return ResponseHelper::error('链接类型必须填写链接描述');
|
||||
}
|
||||
if (empty($link['image'])) {
|
||||
return ResponseHelper::error('链接类型必须填写链接图片');
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!empty($link)) {
|
||||
$link = [
|
||||
'desc' => $link['desc'] ?? '',
|
||||
'image' => $link['image'] ?? '',
|
||||
'url' => $link['url'] ?? ''
|
||||
];
|
||||
if (!empty($link['url']) && !filter_var($link['url'], FILTER_VALIDATE_URL)) {
|
||||
return ResponseHelper::error('链接地址格式不正确');
|
||||
}
|
||||
} else {
|
||||
$link = ['desc' => '', 'image' => '', 'url' => ''];
|
||||
}
|
||||
|
||||
// 构建账号列表
|
||||
$jobPublishWechatMomentsItems = $this->buildJobPublishWechatMomentsItems($wechatIds, $labels);
|
||||
if (empty($jobPublishWechatMomentsItems)) {
|
||||
return ResponseHelper::error('无法获取有效的发布账号信息');
|
||||
}
|
||||
|
||||
try {
|
||||
$sendData = [
|
||||
'altList' => '',
|
||||
'beginTime' => $timingTime,
|
||||
'endTime' => date('Y-m-d H:i:s', strtotime($timingTime) + 1800),
|
||||
'immediately' => $immediately,
|
||||
'isUseLocation' => false,
|
||||
'jobPublishWechatMomentsItems' => $jobPublishWechatMomentsItems,
|
||||
'lat' => 0,
|
||||
'lng' => 0,
|
||||
'link' => $link,
|
||||
'momentContentType' => $momentContentType,
|
||||
'picUrlList' => $picUrlList,
|
||||
'poiAddress' => '',
|
||||
'poiName' => '',
|
||||
'publicMode' => $publicMode,
|
||||
'text' => $text,
|
||||
'timingTime' => $timingTime ?: date('Y-m-d H:i:s'),
|
||||
'videoUrl' => $videoUrl
|
||||
];
|
||||
|
||||
$moments->sendData = json_encode($sendData, 256);
|
||||
$moments->isSend = $immediately ? 1 : 0;
|
||||
$moments->sendTime = $immediately ? time() : strtotime($timingTime);
|
||||
$moments->updateTime = time();
|
||||
$moments->save();
|
||||
|
||||
if ($immediately) {
|
||||
$this->publishMoments($sendData);
|
||||
}
|
||||
|
||||
return ResponseHelper::success('', '朋友圈更新成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('更新失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建发布账号列表
|
||||
* @param array $wechatIds 微信账号ID列表
|
||||
* @param array $labels 标签列表
|
||||
* @return array
|
||||
*/
|
||||
private function buildJobPublishWechatMomentsItems($wechatIds, $labels)
|
||||
{
|
||||
try {
|
||||
// 查询微信账号信息
|
||||
$wechatAccounts = Db::table('s2_wechat_account')
|
||||
->whereIn('id', $wechatIds)
|
||||
->field('id,labels')
|
||||
->select();
|
||||
if (empty($wechatAccounts)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($wechatAccounts as $account) {
|
||||
$accountLabels = [];
|
||||
|
||||
// 如果账号有标签,解析标签
|
||||
if (!empty($account['labels'])) {
|
||||
$accountLabels = is_string($account['labels'])
|
||||
? json_decode($account['labels'], true)
|
||||
: $account['labels'];
|
||||
}
|
||||
|
||||
// 取传入标签与账号标签的交集
|
||||
$finalLabels = array_intersect($labels, $accountLabels);
|
||||
|
||||
$result[] = [
|
||||
'wechatAccountId' => $account['id'],
|
||||
'labels' => array_values($finalLabels), // 重新索引数组
|
||||
'comments' => []
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('构建发布账号列表失败:' . $e->getMessage());
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布朋友圈到微信
|
||||
* @param array $sendData
|
||||
* @return bool
|
||||
*/
|
||||
private function publishMoments($sendData)
|
||||
{
|
||||
try {
|
||||
// 这里调用实际的朋友圈发布接口
|
||||
// 根据您的系统架构,可能需要调用 WebSocket 或其他服务
|
||||
// 示例:调用 MomentsController 的 addJob 方法
|
||||
$moments = new \app\api\controller\MomentsController();
|
||||
return $moments->addJob($sendData);
|
||||
} catch (\Exception $e) {
|
||||
// 记录错误日志
|
||||
\think\facade\Log::error('朋友圈发布失败:' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取朋友圈列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getList()
|
||||
{
|
||||
$page = (int)$this->request->param('page', 1);
|
||||
$limit = (int)$this->request->param('limit', 10);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
try {
|
||||
$list = KfMoments::where(['companyId' => $companyId, 'userId' => $userId, 'isDel' => 0])
|
||||
->order('createTime desc')
|
||||
->page($page, $limit)
|
||||
->select();
|
||||
|
||||
$total = KfMoments::where(['companyId' => $companyId, 'userId' => $userId, 'isDel' => 0])->count();
|
||||
|
||||
// 处理数据
|
||||
$data = [];
|
||||
foreach ($list as $item) {
|
||||
$sendData = json_decode($item->sendData,true);
|
||||
$data[] = [
|
||||
'id' => $item->id,
|
||||
'content' => $sendData['text'] ?? '',
|
||||
'momentContentType' => $sendData['momentContentType'] ?? 1,
|
||||
'picUrlList' => $sendData['picUrlList'] ?? [],
|
||||
'videoUrl' => $sendData['videoUrl'] ?? '',
|
||||
'link' => $sendData['link'] ?? [],
|
||||
'publicMode' => $sendData['publicMode'] ?? 2,
|
||||
'isSend' => $item->isSend,
|
||||
'sendTime' => date('Y-m-d H:i:s',$item->sendTime),
|
||||
'accountCount' => count($sendData['jobPublishWechatMomentsItems'] ?? [])
|
||||
];
|
||||
}
|
||||
|
||||
return ResponseHelper::success([
|
||||
'list' => $data,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'limit' => $limit
|
||||
], '获取成功');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('获取失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除朋友圈
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$id = (int)$this->request->param('id', 0);
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if ($id <= 0) {
|
||||
return ResponseHelper::error('ID不合法');
|
||||
}
|
||||
|
||||
try {
|
||||
$moments = KfMoments::where(['id' => $id, 'companyId' => $companyId, 'userId' => $userId, 'isDel' => 0])->find();
|
||||
if (empty($moments)) {
|
||||
return ResponseHelper::error('朋友圈不存在');
|
||||
}
|
||||
|
||||
$moments->isDel = 1;
|
||||
$moments->delTime = time();
|
||||
$moments->updateTime = time();
|
||||
$moments->save();
|
||||
return ResponseHelper::success([], '删除成功');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('删除失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化时间字符串为 Y-m-d H:i:s 格式
|
||||
* 支持多种时间格式:
|
||||
* - "2026年1月5日15:43:00"
|
||||
* - "2026-01-05 15:43:00"
|
||||
* - "2026/01/05 15:43:00"
|
||||
* - 时间戳
|
||||
* @param string|int $timingTime 时间字符串或时间戳
|
||||
* @return string|false 格式化后的时间字符串,失败返回false
|
||||
*/
|
||||
private function normalizeTimingTime($timingTime)
|
||||
{
|
||||
if (empty($timingTime)) {
|
||||
return date('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
// 如果是时间戳
|
||||
if (is_numeric($timingTime) && strlen($timingTime) == 10) {
|
||||
return date('Y-m-d H:i:s', $timingTime);
|
||||
}
|
||||
|
||||
// 如果是毫秒时间戳
|
||||
if (is_numeric($timingTime) && strlen($timingTime) == 13) {
|
||||
return date('Y-m-d H:i:s', intval($timingTime / 1000));
|
||||
}
|
||||
|
||||
// 如果已经是标准格式,直接返回
|
||||
if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $timingTime)) {
|
||||
return $timingTime;
|
||||
}
|
||||
|
||||
// 处理中文日期格式:2026年1月5日15:43:00 或 2026年01月05日15:43:00
|
||||
if (preg_match('/^(\d{4})年(\d{1,2})月(\d{1,2})日(\d{1,2}):(\d{1,2}):(\d{1,2})$/', $timingTime, $matches)) {
|
||||
$year = $matches[1];
|
||||
$month = str_pad($matches[2], 2, '0', STR_PAD_LEFT);
|
||||
$day = str_pad($matches[3], 2, '0', STR_PAD_LEFT);
|
||||
$hour = str_pad($matches[4], 2, '0', STR_PAD_LEFT);
|
||||
$minute = str_pad($matches[5], 2, '0', STR_PAD_LEFT);
|
||||
$second = str_pad($matches[6], 2, '0', STR_PAD_LEFT);
|
||||
return "{$year}-{$month}-{$day} {$hour}:{$minute}:{$second}";
|
||||
}
|
||||
|
||||
// 处理中文日期格式(无秒):2026年1月5日15:43
|
||||
if (preg_match('/^(\d{4})年(\d{1,2})月(\d{1,2})日(\d{1,2}):(\d{1,2})$/', $timingTime, $matches)) {
|
||||
$year = $matches[1];
|
||||
$month = str_pad($matches[2], 2, '0', STR_PAD_LEFT);
|
||||
$day = str_pad($matches[3], 2, '0', STR_PAD_LEFT);
|
||||
$hour = str_pad($matches[4], 2, '0', STR_PAD_LEFT);
|
||||
$minute = str_pad($matches[5], 2, '0', STR_PAD_LEFT);
|
||||
return "{$year}-{$month}-{$day} {$hour}:{$minute}:00";
|
||||
}
|
||||
|
||||
// 尝试使用 strtotime 解析其他格式
|
||||
$timestamp = strtotime($timingTime);
|
||||
if ($timestamp !== false) {
|
||||
return date('Y-m-d H:i:s', $timestamp);
|
||||
}
|
||||
|
||||
// 如果所有方法都失败,返回 false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
116
application/chukebao/controller/NoticeController.php
Normal file
116
application/chukebao/controller/NoticeController.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\FollowUp;
|
||||
use app\chukebao\model\NoticeModel;
|
||||
use app\chukebao\model\ToDo;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class NoticeController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 列表
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getList()
|
||||
{
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($accountId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
$noRead = NoticeModel::where(['userId' => $userId, 'companyId' => $companyId,'isRead' => 0])->count();
|
||||
|
||||
$query = NoticeModel::where(['userId' => $userId, 'companyId' => $companyId])
|
||||
->order('id desc');
|
||||
if (!empty($keyword)) {
|
||||
$query->where('title|message', 'like', '%' . $keyword . '%');
|
||||
}
|
||||
$total = $query->count();
|
||||
$list = $query->page($page, $limit)->order('isRead ASC,id DESC')->select()->toArray();
|
||||
|
||||
|
||||
foreach ($list as $k => &$v) {
|
||||
if ($v['type'] == 1) {
|
||||
$friendId = ToDo::where(['id' => $v['bindId']])->value('friendId');
|
||||
} elseif ($v['type'] == 2) {
|
||||
$friendId = FollowUp::where(['id' => $v['bindId']])->value('friendId');
|
||||
}
|
||||
if (!empty($friendId)) {
|
||||
$friend = Db::table('s2_wechat_friend')->where(['id' => $friendId])->field('nickname,avatar')->find();
|
||||
} else {
|
||||
$friend = ['nickname' => '', 'avatar' => ''];
|
||||
}
|
||||
$v['friendData'] = $friend;
|
||||
|
||||
$v['readTime'] = !empty($v['readTime']) ? date('Y-m-d H:i:s', $v['readTime']) : '';
|
||||
}
|
||||
unset($v);
|
||||
return ResponseHelper::success(['list' => $list, 'total' => $total,'noRead' => $noRead]);
|
||||
}
|
||||
|
||||
|
||||
public function readMessage()
|
||||
{
|
||||
$id = $this->request->param('id', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($id)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$notice = NoticeModel::where(['userId' => $userId, 'companyId' => $companyId, 'id' => $id, 'isRead' => 0])->find();
|
||||
if (empty($notice)) {
|
||||
return ResponseHelper::error('该消息不存在或标记已读');
|
||||
}
|
||||
$notice->isRead = 1;
|
||||
$notice->readTime = time();
|
||||
$notice->save();
|
||||
if ($notice->type == 1) {
|
||||
ToDo::where(['userId' => $userId, 'companyId' => $companyId, 'id' => $notice->bindId])->update(['isProcess' => 1, 'updateTime' => time()]);
|
||||
} elseif ($notice->type == 2) {
|
||||
FollowUp::where(['userId' => $userId, 'companyId' => $companyId, 'id' => $notice->bindId])->update(['isProcess' => 1, 'updateTime' => time()]);
|
||||
}
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ', '处理成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('处理失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function readAll()
|
||||
{
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$noticeData = NoticeModel::where(['userId' => $userId, 'companyId' => $companyId, 'isRead' => 0])->select()->toArray();
|
||||
if (empty($noticeData)) {
|
||||
return ResponseHelper::error('暂未有新消息');
|
||||
}
|
||||
NoticeModel::where(['userId' => $userId, 'companyId' => $companyId, 'isRead' => 0])->update(['isRead' => 1, 'readTime' => time()]);
|
||||
FollowUp::where(['userId' => $userId, 'companyId' => $companyId, 'isProcess' => 0])->update(['isProcess' => 1, 'updateTime' => time()]);
|
||||
ToDo::where(['userId' => $userId, 'companyId' => $companyId, 'isProcess' => 0])->update(['isProcess' => 1, 'updateTime' => time()]);
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ', '全部已读');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('处理失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
233
application/chukebao/controller/QuestionsController.php
Normal file
233
application/chukebao/controller/QuestionsController.php
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\Questions;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class QuestionsController extends BaseController
|
||||
{
|
||||
|
||||
/**
|
||||
* 列表
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getList(){
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($accountId)){
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
$query = Questions::where(['userId' => $userId,'companyId' => $companyId,'isDel' => 0])
|
||||
->order('id desc');
|
||||
if (!empty($keyword)){
|
||||
$query->where('questions|answers', 'like', '%'.$keyword.'%');
|
||||
}
|
||||
$total = $query->count();
|
||||
$list = $query->page($page, $limit)->select()->toArray();
|
||||
|
||||
|
||||
foreach ($list as $k => &$v){
|
||||
$user = Db::name('users')->where(['id' => $v['userId']])->field('username,account')->find();
|
||||
if (!empty($user)){
|
||||
$v['userName'] = !empty($user['username']) ? $user['username'] : $user['account'];
|
||||
}else{
|
||||
$v['userName'] = '';
|
||||
}
|
||||
$v['answers'] = json_decode($v['answers'],true);
|
||||
}
|
||||
unset($v);
|
||||
return ResponseHelper::success(['list'=>$list,'total'=>$total]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function create(){
|
||||
|
||||
$type = $this->request->param('type', 0);
|
||||
$questions = $this->request->param('questions', '');
|
||||
$answers = $this->request->param('answers', []);
|
||||
$status = $this->request->param('status', 0);
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($accountId)){
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
if (empty($questions) || empty($answers)){
|
||||
return ResponseHelper::error('问题和答案不能为空');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$questionsModel = new Questions();
|
||||
$questionsModel->type = $type;
|
||||
$questionsModel->questions = $questions;
|
||||
$questionsModel->answers = !empty($answers) ? json_encode($answers,256) : json_encode([],256);
|
||||
$questionsModel->status = $status;
|
||||
$questionsModel->accountId = $accountId;
|
||||
$questionsModel->userId = $userId;
|
||||
$questionsModel->companyId = $companyId;
|
||||
$questionsModel->createTime = time();
|
||||
$questionsModel->updateTime = time();
|
||||
$questionsModel->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','创建成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('创建失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function update(){
|
||||
|
||||
$id = $this->request->param('id', 0);
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$type = $this->request->param('type', 0);
|
||||
$questions = $this->request->param('questions', '');
|
||||
$answers = $this->request->param('answers', []);
|
||||
$status = $this->request->param('status', 0);
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($accountId)){
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
if (empty($questions) || empty($answers)){
|
||||
return ResponseHelper::error('问题和答案不能为空');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
$questionsData = Questions::where(['id' => $id,'userId' => $userId,'companyId' => $companyId,'isDel' => 0])->find();
|
||||
$questionsData->type = $type;
|
||||
$questionsData->questions = $questions;
|
||||
$questionsData->answers = !empty($answers) ? json_encode($answers,256) : json_encode([],256);
|
||||
$questionsData->status = $status;
|
||||
$questionsData->accountId = $accountId;
|
||||
$questionsData->userId = $userId;
|
||||
$questionsData->companyId = $companyId;
|
||||
$questionsData->updateTime = time();
|
||||
$questionsData->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','更新成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('更新失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete(){
|
||||
|
||||
$id = $this->request->param('id', 0);
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($accountId)){
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$questions = Questions::where(['id' => $id,'userId' => $userId,'companyId' => $companyId,'isDel' => 0])->find();
|
||||
|
||||
if (empty($questions)){
|
||||
return ResponseHelper::error('该问题不存在或者已删除');
|
||||
}
|
||||
$res = Questions::where(['id' => $id])->update(['isDel' => 1,'deleteTime' => time()]);
|
||||
|
||||
if (!empty($res)){
|
||||
return ResponseHelper::success('','已删除');
|
||||
}else{
|
||||
return ResponseHelper::error('删除失败');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 详情
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function detail(){
|
||||
|
||||
$id = $this->request->param('id', 0);
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($accountId)){
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
if (empty($id)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$questions = Questions::where(['id' => $id,'userId' => $userId,'companyId' => $companyId,'isDel' => 0])->find();
|
||||
|
||||
if (empty($questions)){
|
||||
return ResponseHelper::error('该问题不存在或者已删除');
|
||||
}
|
||||
|
||||
$questions['answers'] = json_decode($questions['answers'],true);
|
||||
$user = Db::name('users')->where(['id' => $questions['userId']])->field('username,account')->find();
|
||||
if (!empty($user)){
|
||||
$questions['userName'] = !empty($user['username']) ? $user['username'] : $user['account'];
|
||||
}else{
|
||||
$questions['userName'] = '';
|
||||
}
|
||||
|
||||
unset(
|
||||
$questions['isDel'],
|
||||
$questions['deleteTime'],
|
||||
$questions['createTime'],
|
||||
$questions['updateTime']
|
||||
);
|
||||
|
||||
return ResponseHelper::success($questions,'获取成功');
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
371
application/chukebao/controller/ReplyController.php
Normal file
371
application/chukebao/controller/ReplyController.php
Normal file
@@ -0,0 +1,371 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\Reply;
|
||||
use app\chukebao\model\ReplyGroup;
|
||||
use library\ResponseHelper;
|
||||
|
||||
class ReplyController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 列表
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getList()
|
||||
{
|
||||
$replyType = $this->request->param('replyType', 0);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
try {
|
||||
// 构建分组查询条件
|
||||
$groupWhere = [
|
||||
['isDel','=',0]
|
||||
];
|
||||
switch ($replyType) {
|
||||
case 0:
|
||||
//公共快捷语
|
||||
$groupWhere[] = ['replyType', '=', 0];
|
||||
break;
|
||||
case 1:
|
||||
//私有快捷语
|
||||
$groupWhere[] = ['companyId', '=', $companyId];
|
||||
$groupWhere[] = ['userId', '=', $userId];
|
||||
$groupWhere[] = ['replyType', '=', 1];
|
||||
break;
|
||||
case 2:
|
||||
//公司快捷语
|
||||
$groupWhere[] = ['companyId', '=', $companyId];
|
||||
$groupWhere[] = ['replyType', '=', 2];
|
||||
break;
|
||||
default:
|
||||
$groupWhere[] = ['replyType', '=', 0];
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!empty($keyword)) {
|
||||
$groupWhere[] = ['groupName','like', '%' . $keyword . '%'];
|
||||
}
|
||||
|
||||
// 获取所有分组
|
||||
$allGroups = ReplyGroup::where($groupWhere)
|
||||
->order('sortIndex asc,id DESC')
|
||||
->select();
|
||||
// 构建树形结构
|
||||
$result = $this->buildGroupTree($allGroups, $keyword);
|
||||
|
||||
return ResponseHelper::success($result, '获取成功');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('获取失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增快捷语分组
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function addGroup()
|
||||
{
|
||||
$groupName = $this->request->param('groupName', '');
|
||||
$parentId = (int)$this->request->param('parentId', 0);
|
||||
$replyType = (int)$this->request->param('replyType', 0); // 0公共 1私有 2公司
|
||||
$sortIndex = (string)$this->request->param('sortIndex', 50);
|
||||
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
|
||||
if ($groupName === '') {
|
||||
return ResponseHelper::error('分组名称不能为空');
|
||||
}
|
||||
|
||||
try {
|
||||
$data = [
|
||||
'groupName' => $groupName,
|
||||
'parentId' => $parentId,
|
||||
'replyType' => $replyType,
|
||||
'sortIndex' => $sortIndex,
|
||||
// 兼容现有程序中使用到的字段
|
||||
'companyId' => $companyId,
|
||||
'userId' => $userId,
|
||||
];
|
||||
|
||||
/** @var ReplyGroup $group */
|
||||
$group = new ReplyGroup();
|
||||
$group->save($data);
|
||||
|
||||
return ResponseHelper::success($group->toArray(), '创建成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('创建失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增快捷语
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function addReply()
|
||||
{
|
||||
$groupId = (int)$this->request->param('groupId', 0);
|
||||
$title = $this->request->param('title', '');
|
||||
$msgType = (int)$this->request->param('msgType', 1); // 1文本 3图片 43视频 49链接 等
|
||||
$content = $this->request->param('content', '');
|
||||
$sortIndex = (string)$this->request->param('sortIndex', 50);
|
||||
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$userId = $this->getUserInfo('id');
|
||||
|
||||
if ($groupId <= 0) {
|
||||
return ResponseHelper::error('分组ID不合法');
|
||||
}
|
||||
if ($title === '') {
|
||||
return ResponseHelper::error('标题不能为空');
|
||||
}
|
||||
|
||||
try {
|
||||
$now = time();
|
||||
$data = [
|
||||
'tenantId' => $companyId,
|
||||
'groupId' => $groupId,
|
||||
'accountId' => $accountId,
|
||||
'title' => $title,
|
||||
'msgType' => $msgType,
|
||||
'content' => $content,
|
||||
'sortIndex' => $sortIndex,
|
||||
'createTime' => $now,
|
||||
'lastUpdateTime' => $now,
|
||||
'userId' => $userId,
|
||||
];
|
||||
|
||||
/** @var Reply $reply */
|
||||
$reply = new Reply();
|
||||
$reply->save($data);
|
||||
|
||||
return ResponseHelper::success($reply->toArray(), '创建成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('创建失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑快捷语分组
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function updateGroup()
|
||||
{
|
||||
$id = (int)$this->request->param('id', 0);
|
||||
if ($id <= 0) {
|
||||
return ResponseHelper::error('分组ID不合法');
|
||||
}
|
||||
|
||||
$data = [];
|
||||
$groupName = $this->request->param('groupName', null);
|
||||
$parentId = $this->request->param('parentId', null);
|
||||
$replyType = $this->request->param('replyType', null);
|
||||
$sortIndex = $this->request->param('sortIndex', null);
|
||||
|
||||
if ($groupName !== null) $data['groupName'] = $groupName;
|
||||
if ($parentId !== null) $data['parentId'] = (int)$parentId;
|
||||
if ($replyType !== null) $data['replyType'] = (int)$replyType;
|
||||
if ($sortIndex !== null) $data['sortIndex'] = (string)$sortIndex;
|
||||
|
||||
if (empty($data)) {
|
||||
return ResponseHelper::error('无可更新字段');
|
||||
}
|
||||
|
||||
try {
|
||||
$group = ReplyGroup::where(['id' => $id,'isDel' => 0])->find();
|
||||
if (empty($group)) {
|
||||
return ResponseHelper::error('分组不存在');
|
||||
}
|
||||
$group->save($data);
|
||||
return ResponseHelper::success($group->toArray(), '更新成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('更新失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 假删除快捷语分组
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function deleteGroup()
|
||||
{
|
||||
$id = (int)$this->request->param('id', 0);
|
||||
if ($id <= 0) {
|
||||
return ResponseHelper::error('分组ID不合法');
|
||||
}
|
||||
try {
|
||||
$group = ReplyGroup::where(['id' => $id,'isDel' => 0])->find();
|
||||
if (empty($group)) {
|
||||
return ResponseHelper::error('分组不存在');
|
||||
}
|
||||
$group->save(['isDel' => 1,'delTime' => time()]);
|
||||
return ResponseHelper::success([], '删除成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('删除失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑快捷语
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function updateReply()
|
||||
{
|
||||
$id = (int)$this->request->param('id', 0);
|
||||
if ($id <= 0) {
|
||||
return ResponseHelper::error('快捷语ID不合法');
|
||||
}
|
||||
|
||||
$data = [];
|
||||
$groupId = $this->request->param('groupId', null);
|
||||
$title = $this->request->param('title', null);
|
||||
$msgType = $this->request->param('msgType', null);
|
||||
$content = $this->request->param('content', null);
|
||||
$sortIndex = $this->request->param('sortIndex', null);
|
||||
|
||||
if ($groupId !== null) $data['groupId'] = (int)$groupId;
|
||||
if ($title !== null) $data['title'] = $title;
|
||||
if ($msgType !== null) $data['msgType'] = (int)$msgType;
|
||||
if ($content !== null) $data['content'] = $content;
|
||||
if ($sortIndex !== null) $data['sortIndex'] = (string)$sortIndex;
|
||||
if (!empty($data)) {
|
||||
$data['lastUpdateTime'] = time();
|
||||
}
|
||||
|
||||
if (empty($data)) {
|
||||
return ResponseHelper::error('无可更新字段');
|
||||
}
|
||||
|
||||
try {
|
||||
$reply = Reply::where(['id' => $id,'isDel' => 0])->find();
|
||||
if (empty($reply)) {
|
||||
return ResponseHelper::error('快捷语不存在');
|
||||
}
|
||||
$reply->save($data);
|
||||
return ResponseHelper::success($reply->toArray(), '更新成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('更新失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 假删除快捷语
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function deleteReply()
|
||||
{
|
||||
$id = (int)$this->request->param('id', 0);
|
||||
if ($id <= 0) {
|
||||
return ResponseHelper::error('快捷语ID不合法');
|
||||
}
|
||||
try {
|
||||
$reply = Reply::where(['id' => $id,'isDel' => 0])->find();
|
||||
if (empty($reply)) {
|
||||
return ResponseHelper::error('快捷语不存在');
|
||||
}
|
||||
$reply->save(['isDel' => 1, 'delTime' => time()]);
|
||||
return ResponseHelper::success([], '删除成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('删除失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建分组树形结构
|
||||
* @param array $groups 所有分组数据
|
||||
* @param string $keyword 搜索关键词
|
||||
* @return array
|
||||
*/
|
||||
private function buildGroupTree($groups, $keyword = '')
|
||||
{
|
||||
$tree = [];
|
||||
$groupMap = [];
|
||||
|
||||
// 先构建分组映射
|
||||
foreach ($groups as $group) {
|
||||
$groupMap[$group->id] = $group->toArray();
|
||||
}
|
||||
|
||||
// 构建树形结构
|
||||
foreach ($groups as $group) {
|
||||
$groupData = $this->buildGroupData($group, $keyword);
|
||||
|
||||
if ($group->parentId == null || $group->parentId == 0) {
|
||||
// 顶级分组
|
||||
$tree[] = $groupData;
|
||||
} else {
|
||||
// 子分组,需要找到父分组并添加到其children中
|
||||
$this->addToParentGroup($tree, $group->parentId, $groupData);
|
||||
}
|
||||
}
|
||||
|
||||
return $tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建单个分组数据
|
||||
* @param object $group 分组对象
|
||||
* @param string $keyword 搜索关键词
|
||||
* @return array
|
||||
*/
|
||||
private function buildGroupData($group, $keyword = '')
|
||||
{
|
||||
// 构建快捷回复查询条件
|
||||
$replyWhere[] =[
|
||||
['groupId' ,'=', $group->id],
|
||||
['isDel','=',0]
|
||||
];
|
||||
if (!empty($keyword)) {
|
||||
$replyWhere[] = ['title','like', '%' . $keyword . '%'];
|
||||
}
|
||||
|
||||
// 获取该分组下的快捷回复
|
||||
$replies = Reply::where($replyWhere)
|
||||
->order('sortIndex asc, id desc
|
||||
')
|
||||
->select();
|
||||
|
||||
return [
|
||||
'id' => $group->id,
|
||||
'groupName' => $group->groupName,
|
||||
'sortIndex' => $group->sortIndex,
|
||||
'parentId' => $group->parentId,
|
||||
'replyType' => $group->replyType,
|
||||
'replys' => $group->replys,
|
||||
'companyId' => $group->companyId,
|
||||
'userId' => $group->userId,
|
||||
'replies' => $replies->toArray(),
|
||||
'children' => [] // 子分组
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将子分组添加到父分组中
|
||||
* @param array $tree 树形结构
|
||||
* @param int $parentId 父分组ID
|
||||
* @param array $childGroup 子分组数据
|
||||
*/
|
||||
private function addToParentGroup(&$tree, $parentId, $childGroup)
|
||||
{
|
||||
foreach ($tree as &$group) {
|
||||
if ($group['id'] == $parentId) {
|
||||
$group['children'][] = $childGroup;
|
||||
return;
|
||||
}
|
||||
|
||||
// 递归查找子分组
|
||||
if (!empty($group['children'])) {
|
||||
$this->addToParentGroup($group['children'], $parentId, $childGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
143
application/chukebao/controller/ToDoController.php
Normal file
143
application/chukebao/controller/ToDoController.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\ToDo;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class ToDoController extends BaseController
|
||||
{
|
||||
|
||||
public function getList(){
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$isRemind = $this->request->param('isRemind', '');
|
||||
$isProcess = $this->request->param('isProcess', '');
|
||||
$level = $this->request->param('level', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
|
||||
$where = [
|
||||
['companyId','=',$companyId],
|
||||
['userId' ,'=', $userId]
|
||||
];
|
||||
|
||||
if ($isRemind != '') {
|
||||
$where[] = ['isRemind','=',$isRemind];
|
||||
}
|
||||
if ($level != '') {
|
||||
$where[] = ['level','=',$level];
|
||||
}
|
||||
if ($isProcess != '') {
|
||||
$where[] = ['isProcess','=',$isProcess];
|
||||
}
|
||||
|
||||
if(!empty($keyword)){
|
||||
$where[] = ['title|description','like','%'.$keyword.'%'];
|
||||
}
|
||||
|
||||
$query = ToDo::where($where);
|
||||
$total = $query->count();
|
||||
$list = $query->where($where)->page($page,$limit)->order('id desc')->select();
|
||||
|
||||
|
||||
foreach ($list as &$item) {
|
||||
$nickname = Db::table('s2_wechat_friend')->where(['id' => $item['friendId']])->value('nickname');
|
||||
$item['nickname'] = !empty($nickname) ? $nickname : '-';
|
||||
$item['reminderTime'] = date('Y-m-d H:i:s',$item['reminderTime']);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return ResponseHelper::success(['list'=>$list,'total'=>$total]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function create(){
|
||||
$level = $this->request->param('level', 0);
|
||||
$title = $this->request->param('title', '');
|
||||
$reminderTime = $this->request->param('reminderTime', '');
|
||||
$description = $this->request->param('description', '');
|
||||
$friendId = $this->request->param('friendId', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($title) || empty($reminderTime) || empty($description) || empty($friendId)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$friend = Db::table('s2_wechat_friend')->where(['id' => $friendId])->find();
|
||||
if (empty($friend)) {
|
||||
return ResponseHelper::error('好友不存在');
|
||||
}
|
||||
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$todo = new ToDo();
|
||||
$todo->level = $level;
|
||||
$todo->title = $title;
|
||||
$todo->friendId = $friendId;
|
||||
$todo->reminderTime = !empty($reminderTime) ? strtotime($reminderTime) : time();
|
||||
$todo->description = $description;
|
||||
$todo->userId = $userId;
|
||||
$todo->companyId = $companyId;
|
||||
$todo->updateTime = time();
|
||||
$todo->createTime = time();
|
||||
$todo->save();
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','创建成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('创建失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理代办事项
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function process(){
|
||||
$ids = $this->request->param('ids','');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($ids)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
$ids = explode(',',$ids);
|
||||
|
||||
if (!is_array($ids)){
|
||||
return ResponseHelper::error('格式错误');
|
||||
}
|
||||
|
||||
$todoIds = ToDo::where(['userId' => $userId,'companyId' => $companyId,'isProcess' => 0])->whereIn('id',$ids)->column('id');
|
||||
if (empty($todoIds)){
|
||||
return ResponseHelper::error('代办事项不存在');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
ToDo::whereIn('id',$todoIds)->update(['isProcess' => 1,'isRemind' => 1,'updateTime' => time()]);
|
||||
Db::commit();
|
||||
return ResponseHelper::success(' ','已处理');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('处理失败:'.$e->getMessage());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
192
application/chukebao/controller/TokensRecordController.php
Normal file
192
application/chukebao/controller/TokensRecordController.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\TokensCompany;
|
||||
use app\chukebao\model\TokensRecord;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class TokensRecordController extends BaseController
|
||||
{
|
||||
|
||||
|
||||
public function getList(){
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$type = $this->request->param('type', '');
|
||||
$form = $this->request->param('form', '');
|
||||
$startTime = $this->request->param('startTime', '');
|
||||
$endTime = $this->request->param('endTime', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
|
||||
$where = [
|
||||
['companyId','=',$companyId],
|
||||
['userId' ,'=', $userId]
|
||||
];
|
||||
|
||||
if ($type != '') {
|
||||
$where[] = ['type','=',$type];
|
||||
}
|
||||
if ($form != '') {
|
||||
$where[] = ['form','=',$form];
|
||||
}
|
||||
|
||||
// 时间筛选
|
||||
if (!empty($startTime)) {
|
||||
// 支持时间戳或日期字符串格式
|
||||
$startTimestamp = is_numeric($startTime) ? intval($startTime) : strtotime($startTime);
|
||||
if ($startTimestamp !== false) {
|
||||
$where[] = ['createTime', '>=', $startTimestamp];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($endTime)) {
|
||||
// 支持时间戳或日期字符串格式
|
||||
$endTimestamp = is_numeric($endTime) ? intval($endTime) : strtotime($endTime);
|
||||
if ($endTimestamp !== false) {
|
||||
// 如果是日期字符串,自动设置为当天的23:59:59
|
||||
if (!is_numeric($endTime)) {
|
||||
$endTimestamp = strtotime(date('Y-m-d 23:59:59', $endTimestamp));
|
||||
}
|
||||
$where[] = ['createTime', '<=', $endTimestamp];
|
||||
}
|
||||
}
|
||||
|
||||
$query = TokensRecord::where($where);
|
||||
$total = $query->count();
|
||||
$list = $query->where($where)->page($page,$limit)->order('id desc')->select();
|
||||
|
||||
|
||||
foreach ($list as &$item) {
|
||||
if (in_array($item['type'],[1])){
|
||||
$nickname = Db::table('s2_wechat_friend')->where(['id' => $item['friendIdOrGroupId']])->value('nickname');
|
||||
$item['nickname'] = !empty($nickname) ? $nickname : '-';
|
||||
}
|
||||
if (in_array($item['type'],[2,3])){
|
||||
$nickname = Db::table('s2_wechat_chatroom')->where(['id' => $item['friendIdOrGroupId']])->value('nickname');
|
||||
$item['nickname'] = !empty($nickname) ? $nickname : '-';
|
||||
}
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return ResponseHelper::success(['list'=>$list,'total'=>$total]);
|
||||
}
|
||||
|
||||
|
||||
public function consumeTokens($data = [])
|
||||
{
|
||||
if (empty($data)){
|
||||
return ResponseHelper::error('数据缺失');
|
||||
}
|
||||
|
||||
$tokens = isset($data['tokens']) ? intval($data['tokens']) : 0;
|
||||
$type = isset($data['type']) ? intval($data['type']) : 0;
|
||||
$form = isset($data['form']) ? intval($data['form']) : 0;
|
||||
$wechatAccountId = isset($data['wechatAccountId']) ? intval($data['wechatAccountId']) : 0;
|
||||
$friendIdOrGroupId = isset($data['friendIdOrGroupId']) ? intval($data['friendIdOrGroupId']) : 0;
|
||||
$remarks = isset($data['remarks']) ? $data['remarks'] : '';
|
||||
$companyId = isset($data['companyId']) ? intval($data['companyId']) : $this->getUserInfo('companyId');
|
||||
$userId = isset($data['userId']) ? intval($data['userId']) : $this->getUserInfo('id');
|
||||
|
||||
// 验证必要参数
|
||||
if ($tokens <= 0) {
|
||||
return ResponseHelper::error('tokens数量必须大于0');
|
||||
}
|
||||
|
||||
if (!in_array($type, [0, 1])) {
|
||||
return ResponseHelper::error('类型参数错误,0为减少,1为增加');
|
||||
}
|
||||
|
||||
|
||||
// 重试机制,最多重试3次
|
||||
$maxRetries = 3;
|
||||
$retryCount = 0;
|
||||
while ($retryCount < $maxRetries) {
|
||||
try {
|
||||
return $this->doConsumeTokens($userId, $companyId, $tokens, $type, $form, $wechatAccountId, $friendIdOrGroupId, $remarks);
|
||||
} catch (\Exception $e) {
|
||||
$retryCount++;
|
||||
if ($retryCount >= $maxRetries) {
|
||||
return ResponseHelper::error('操作失败,请稍后重试:' . $e->getMessage());
|
||||
}
|
||||
// 短暂延迟后重试
|
||||
usleep(100000); // 100ms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行tokens消费的核心方法
|
||||
*/
|
||||
private function doConsumeTokens($userId, $companyId, $tokens, $type, $form, $wechatAccountId, $friendIdOrGroupId, $remarks)
|
||||
{
|
||||
// 开启数据库事务
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 使用悲观锁获取用户当前tokens余额,确保并发安全
|
||||
$userInfo = TokensCompany::where(['companyId'=> $companyId,'userId' => $userId])->lock(true)->find();
|
||||
if (!$userInfo) {
|
||||
throw new \Exception('用户不存在');
|
||||
}
|
||||
|
||||
$currentTokens = intval($userInfo['tokens']);
|
||||
|
||||
// 计算新的余额
|
||||
$newBalance = $type == 1 ? ($currentTokens + $tokens) : ($currentTokens - $tokens);
|
||||
|
||||
// 使用原子更新操作,基于当前值进行更新,防止并发覆盖
|
||||
$updateResult = TokensCompany::where('companyId', $companyId)
|
||||
->where('companyId', $companyId)
|
||||
->update([
|
||||
'tokens' => $newBalance,
|
||||
'updateTime' => time()
|
||||
]);
|
||||
|
||||
if (!$updateResult) {
|
||||
// 如果更新失败,说明tokens值已被其他事务修改,需要重新获取
|
||||
throw new \Exception('tokens余额已被其他操作修改,请重试');
|
||||
}
|
||||
|
||||
// 记录tokens变动
|
||||
$recordData = [
|
||||
'companyId' => $companyId,
|
||||
'userId' => $userId,
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'friendIdOrGroupId' => $friendIdOrGroupId,
|
||||
'form' => $form,
|
||||
'type' => $type,
|
||||
'tokens' => $tokens,
|
||||
'balanceTokens' => $newBalance,
|
||||
'remarks' => $remarks,
|
||||
'createTime' => time()
|
||||
];
|
||||
|
||||
$recordId = Db::name('tokens_record')->insertGetId($recordData);
|
||||
|
||||
if (!$recordId) {
|
||||
throw new \Exception('记录tokens变动失败');
|
||||
}
|
||||
|
||||
// 提交事务
|
||||
Db::commit();
|
||||
|
||||
return ResponseHelper::success([
|
||||
'recordId' => $recordId,
|
||||
'oldBalance' => $currentTokens,
|
||||
'newBalance' => $newBalance,
|
||||
'changeAmount' => $type == 1 ? $tokens : -$tokens
|
||||
], 'tokens变动记录成功');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// 回滚事务
|
||||
Db::rollback();
|
||||
throw $e; // 重新抛出异常,让重试机制处理
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
269
application/chukebao/controller/WechatChatroomController.php
Normal file
269
application/chukebao/controller/WechatChatroomController.php
Normal file
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\ai\controller\DouBaoAI;
|
||||
use app\chukebao\controller\TokensRecordController as tokensRecord;
|
||||
use app\chukebao\model\TokensCompany;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class WechatChatroomController extends BaseController
|
||||
{
|
||||
|
||||
public function getList(){
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$groupIds = $this->request->param('groupId', '');
|
||||
$ownerWechatId = $this->request->param('ownerWechatId', '');
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
if (empty($accountId)){
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
$query = Db::table('s2_wechat_chatroom')
|
||||
->where(['accountId' => $accountId,'isDeleted' => 0]);
|
||||
|
||||
// 关键字搜索:群昵称、微信号(这里使用chatroomId作为群标识)
|
||||
if ($keyword !== '' && $keyword !== null) {
|
||||
$query->where(function ($q) use ($keyword) {
|
||||
$like = '%' . $keyword . '%';
|
||||
$q->whereLike('nickname', $like)
|
||||
->whereOr('conRemark', 'like', $like);
|
||||
});
|
||||
}
|
||||
|
||||
// 分组筛选:groupIds(单个分组ID)
|
||||
if ($groupIds !== '' && $groupIds !== null) {
|
||||
$query->where('groupIds', $groupIds);
|
||||
}
|
||||
|
||||
if (!empty($ownerWechatId)) {
|
||||
$query->where('ownerWechatId', $ownerWechatId);
|
||||
}
|
||||
|
||||
$query->order('id desc');
|
||||
$total = $query->count();
|
||||
$list = $query->page($page, $limit)->select();
|
||||
|
||||
|
||||
|
||||
// 提取所有聊天室ID,用于批量查询
|
||||
$chatroomIds = array_column($list, 'id');
|
||||
|
||||
|
||||
// 一次性查询所有聊天室的未读消息数量
|
||||
$unreadCounts = [];
|
||||
if (!empty($chatroomIds)) {
|
||||
$unreadResults = Db::table('s2_wechat_message')
|
||||
->field('wechatChatroomId, COUNT(*) as count')
|
||||
->where('wechatChatroomId', 'in', $chatroomIds)
|
||||
->where('isRead', 0)
|
||||
->group('wechatChatroomId')
|
||||
->select();
|
||||
|
||||
foreach ($unreadResults as $result) {
|
||||
$unreadCounts[$result['wechatChatroomId']] = $result['count'];
|
||||
}
|
||||
}
|
||||
// 一次性查询所有聊天室的最新消息
|
||||
$latestMessages = [];
|
||||
if (!empty($chatroomIds)) {
|
||||
// 使用子查询获取每个聊天室的最新消息ID
|
||||
$subQuery = Db::table('s2_wechat_message')
|
||||
->field('MAX(id) as max_id, wechatChatroomId')
|
||||
->where('wechatChatroomId', 'in', $chatroomIds)
|
||||
->group('wechatChatroomId')
|
||||
->buildSql();
|
||||
|
||||
// 查询最新消息的详细信息
|
||||
$messageResults = Db::table('s2_wechat_message')
|
||||
->alias('m')
|
||||
->join([$subQuery => 'sub'], 'm.id = sub.max_id')
|
||||
->field('m.*, sub.wechatChatroomId')
|
||||
->select();
|
||||
|
||||
foreach ($messageResults as $message) {
|
||||
$latestMessages[$message['wechatChatroomId']] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理每个聊天室的数据
|
||||
foreach ($list as $k => &$v) {
|
||||
$v['createTime'] = !empty($v['createTime']) ? date('Y-m-d H:i:s', $v['createTime']) : '';
|
||||
$v['updateTime'] = !empty($v['updateTime']) ? date('Y-m-d H:i:s', $v['updateTime']) : '';
|
||||
|
||||
$config = [
|
||||
'unreadCount' => isset($unreadCounts[$v['id']]) ? $unreadCounts[$v['id']] : 0,
|
||||
'chat' => isset($latestMessages[$v['id']]),
|
||||
'msgTime' => isset($latestMessages[$v['id']]) ? $latestMessages[$v['id']]['wechatTime'] : 0
|
||||
];
|
||||
$v['config'] = $config;
|
||||
}
|
||||
unset($v);
|
||||
|
||||
return ResponseHelper::success(['list'=>$list,'total'=>$total]);
|
||||
}
|
||||
|
||||
public function getDetail(){
|
||||
$id = input('id', 0);
|
||||
|
||||
if (!$id) {
|
||||
return ResponseHelper::error('聊天室ID不能为空');
|
||||
}
|
||||
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
if (empty($accountId)){
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
$detail = Db::table('s2_wechat_chatroom')
|
||||
->where(['accountId' => $accountId, 'id' => $id, 'isDeleted' => 0])
|
||||
->find();
|
||||
|
||||
if (!$detail) {
|
||||
return ResponseHelper::error('聊天室不存在或无权限访问');
|
||||
}
|
||||
|
||||
// 处理时间格式
|
||||
$detail['createTime'] = !empty($detail['createTime']) ? date('Y-m-d H:i:s', $detail['createTime']) : '';
|
||||
$detail['updateTime'] = !empty($detail['updateTime']) ? date('Y-m-d H:i:s', $detail['updateTime']) : '';
|
||||
|
||||
// 查询未读消息数量
|
||||
$unreadCount = Db::table('s2_wechat_message')
|
||||
->where('wechatChatroomId', $id)
|
||||
->where('isRead', 0)
|
||||
->count();
|
||||
|
||||
// 查询最新消息
|
||||
$latestMessage = Db::table('s2_wechat_message')
|
||||
->where('wechatChatroomId', $id)
|
||||
->order('id desc')
|
||||
->find();
|
||||
|
||||
$config = [
|
||||
'unreadCount' => $unreadCount,
|
||||
'chat' => !empty($latestMessage),
|
||||
'msgTime' => isset($latestMessage['wechatTime']) ? $latestMessage['wechatTime'] : 0
|
||||
];
|
||||
$detail['config'] = $config;
|
||||
|
||||
return ResponseHelper::success($detail);
|
||||
}
|
||||
|
||||
public function getMembers()
|
||||
{
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$groupId = $this->request->param('groupId', '');
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
if (empty($accountId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
// 验证群组ID必填
|
||||
if (empty($groupId)) {
|
||||
return ResponseHelper::error('群组ID不能为空');
|
||||
}
|
||||
|
||||
// 验证群组是否属于当前账号
|
||||
$chatroom = Db::table('s2_wechat_chatroom')
|
||||
->where(['id' => $groupId, 'isDeleted' => 0])
|
||||
->find();
|
||||
|
||||
if (!$chatroom) {
|
||||
return ResponseHelper::error('群组不存在或无权限访问');
|
||||
}
|
||||
|
||||
// 获取群组的chatroomId(微信群聊ID)
|
||||
$chatroomId = $chatroom['chatroomId'] ?? $chatroom['id'];
|
||||
|
||||
// 如果chatroomId为空,使用id作为chatroomId
|
||||
if (empty($chatroomId)) {
|
||||
$chatroomId = $chatroom['id'];
|
||||
}
|
||||
|
||||
// 构建查询
|
||||
$query = Db::table('s2_wechat_chatroom_member')
|
||||
->where('chatroomId', $chatroomId);
|
||||
|
||||
// 关键字搜索:昵称、备注、别名
|
||||
if ($keyword !== '' && $keyword !== null) {
|
||||
$query->where(function ($q) use ($keyword) {
|
||||
$like = '%' . $keyword . '%';
|
||||
$q->whereLike('nickname', $like)
|
||||
->whereOr('conRemark', 'like', $like)
|
||||
->whereOr('alias', 'like', $like);
|
||||
});
|
||||
}
|
||||
|
||||
$query->order('id desc');
|
||||
$total = $query->count();
|
||||
$list = $query->page($page, $limit)->select();
|
||||
|
||||
// 处理时间格式
|
||||
foreach ($list as $k => &$v) {
|
||||
$v['createTime'] = !empty($v['createTime']) ? date('Y-m-d H:i:s', $v['createTime']) : '';
|
||||
$v['updateTime'] = !empty($v['updateTime']) ? date('Y-m-d H:i:s', $v['updateTime']) : '';
|
||||
}
|
||||
unset($v);
|
||||
|
||||
return ResponseHelper::success(['list' => $list, 'total' => $total]);
|
||||
}
|
||||
|
||||
public function aiAnnouncement()
|
||||
{
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$wechatAccountId = $this->request->param('wechatAccountId', '');
|
||||
$groupId = $this->request->param('groupId', '');
|
||||
$content = $this->request->param('content', '');
|
||||
|
||||
if (empty($groupId) || empty($content)|| empty($wechatAccountId)){
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
$tokens = TokensCompany::where(['companyId' => $companyId])->value('tokens');
|
||||
if (empty($tokens) || $tokens <= 0){
|
||||
return ResponseHelper::error('用户Tokens余额不足');
|
||||
}
|
||||
|
||||
$params = [
|
||||
'model' => 'doubao-1-5-pro-32k-250115',
|
||||
'messages' => [
|
||||
['role' => 'system', 'content' => '你现在是存客宝的AI助理,你精通中国大陆的法律'],
|
||||
['role' => 'user', 'content' => $content],
|
||||
],
|
||||
];
|
||||
|
||||
//AI处理
|
||||
$ai = new DouBaoAI();
|
||||
$res = $ai->text($params);
|
||||
$res = json_decode($res,true);
|
||||
|
||||
|
||||
if ($res['code'] == 200) {
|
||||
//扣除Tokens
|
||||
$tokensRecord = new tokensRecord();
|
||||
$nickname = Db::table('s2_wechat_chatroom')->where(['id' => $groupId])->value('nickname');
|
||||
$remarks = !empty($nickname) ? '生成【'.$nickname.'】群公告' : '生成群公告';
|
||||
$data = [
|
||||
'tokens' => $res['data']['token'],
|
||||
'type' => 0,
|
||||
'form' => 14,
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'friendIdOrGroupId' => $groupId,
|
||||
'remarks' => $remarks,
|
||||
];
|
||||
$tokensRecord->consumeTokens($data);
|
||||
return ResponseHelper::success($res['data']['content']);
|
||||
}else{
|
||||
return ResponseHelper::error($res['msg']);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
302
application/chukebao/controller/WechatFriendController.php
Normal file
302
application/chukebao/controller/WechatFriendController.php
Normal file
@@ -0,0 +1,302 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\FriendSettings;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class WechatFriendController extends BaseController
|
||||
{
|
||||
|
||||
public function getList()
|
||||
{
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$groupIds = $this->request->param('groupId', '');
|
||||
$ownerWechatId = $this->request->param('ownerWechatId', '');
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
if (empty($accountId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
$query = Db::table('s2_wechat_friend')
|
||||
->where(['accountId' => $accountId, 'isDeleted' => 0]);
|
||||
|
||||
// 关键字搜索:昵称、备注、微信号
|
||||
if ($keyword !== '' && $keyword !== null) {
|
||||
$query->where(function ($q) use ($keyword) {
|
||||
$like = '%' . $keyword . '%';
|
||||
$q->whereLike('nickname', $like)
|
||||
->whereOr('conRemark', 'like', $like)
|
||||
->whereOr('alias', 'like', $like)
|
||||
->whereOr('wechatId', 'like', $like);
|
||||
});
|
||||
}
|
||||
|
||||
// 分组筛选:groupIds(单个分组ID)
|
||||
if ($groupIds !== '' && $groupIds !== null) {
|
||||
$query->where('groupIds', $groupIds);
|
||||
}
|
||||
|
||||
if (!empty($ownerWechatId)) {
|
||||
$query->where('ownerWechatId', $ownerWechatId);
|
||||
}
|
||||
|
||||
$query->order('id desc');
|
||||
$total = $query->count();
|
||||
$list = $query->page($page, $limit)->select();
|
||||
|
||||
// 提取所有好友ID
|
||||
$friendIds = array_column($list, 'id');
|
||||
|
||||
$aiTypeData = [];
|
||||
if (!empty($friendIds)) {
|
||||
$aiTypeData = FriendSettings::where('friendId', 'in', $friendIds)->column('friendId,type');
|
||||
}
|
||||
|
||||
|
||||
// 处理每个好友的数据
|
||||
foreach ($list as $k => &$v) {
|
||||
$v['labels'] = json_decode($v['labels'], true);
|
||||
$v['siteLabels'] = json_decode($v['siteLabels'], true);
|
||||
$v['createTime'] = !empty($v['createTime']) ? date('Y-m-d H:i:s', $v['createTime']) : '';
|
||||
$v['updateTime'] = !empty($v['updateTime']) ? date('Y-m-d H:i:s', $v['updateTime']) : '';
|
||||
$v['passTime'] = !empty($v['passTime']) ? date('Y-m-d H:i:s', $v['passTime']) : '';
|
||||
$v['aiType'] = isset($aiTypeData[$v['id']]) ? $aiTypeData[$v['id']] : 0;
|
||||
}
|
||||
unset($v);
|
||||
|
||||
return ResponseHelper::success(['list' => $list, 'total' => $total]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个好友详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getDetail()
|
||||
{
|
||||
$friendId = $this->request->param('id');
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
|
||||
if (empty($accountId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
if (empty($friendId)) {
|
||||
return ResponseHelper::error('好友ID不能为空');
|
||||
}
|
||||
|
||||
// 查询好友详情
|
||||
$friend = Db::table('s2_wechat_friend')
|
||||
->where(['id' => $friendId, 'isDeleted' => 0])
|
||||
->find();
|
||||
|
||||
if (empty($friend)) {
|
||||
return ResponseHelper::error('好友不存在');
|
||||
}
|
||||
|
||||
// 处理好友数据
|
||||
$friend['labels'] = json_decode($friend['labels'], true);
|
||||
$friend['siteLabels'] = json_decode($friend['siteLabels'], true);
|
||||
$friend['createTime'] = !empty($friend['createTime']) ? date('Y-m-d H:i:s', $friend['createTime']) : '';
|
||||
$friend['updateTime'] = !empty($friend['updateTime']) ? date('Y-m-d H:i:s', $friend['updateTime']) : '';
|
||||
$friend['passTime'] = !empty($friend['passTime']) ? date('Y-m-d H:i:s', $friend['passTime']) : '';
|
||||
|
||||
// 获取AI类型设置
|
||||
$aiTypeSetting = FriendSettings::where('friendId', $friendId)->find();
|
||||
$friend['aiType'] = $aiTypeSetting ? $aiTypeSetting['type'] : 0;
|
||||
|
||||
return ResponseHelper::success(['detail' => $friend]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新好友资料(公司、姓名、手机号等字段可单独更新)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function updateFriendInfo()
|
||||
{
|
||||
$friendId = $this->request->param('id');
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
|
||||
if (empty($accountId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
if (empty($friendId)) {
|
||||
return ResponseHelper::error('好友ID不能为空');
|
||||
}
|
||||
|
||||
$friend = Db::table('s2_wechat_friend')
|
||||
->where(['id' => $friendId, 'accountId' => $accountId, 'isDeleted' => 0])
|
||||
->find();
|
||||
|
||||
if (empty($friend)) {
|
||||
return ResponseHelper::error('好友不存在或无权限操作');
|
||||
}
|
||||
|
||||
$requestData = $this->request->param();
|
||||
$updatableColumns = [
|
||||
'phone',
|
||||
'conRemark',
|
||||
];
|
||||
$columnUpdates = [];
|
||||
|
||||
foreach ($updatableColumns as $field) {
|
||||
if (array_key_exists($field, $requestData)) {
|
||||
$columnUpdates[$field] = $requestData[$field];
|
||||
}
|
||||
}
|
||||
|
||||
$extendFieldsData = [];
|
||||
if (!empty($friend['extendFields'])) {
|
||||
$decodedExtend = json_decode($friend['extendFields'], true);
|
||||
$extendFieldsData = is_array($decodedExtend) ? $decodedExtend : [];
|
||||
}
|
||||
|
||||
$extendFieldKeys = [
|
||||
'company',
|
||||
'name',
|
||||
'position',
|
||||
'email',
|
||||
'address',
|
||||
'wechat',
|
||||
'qq',
|
||||
'remark'
|
||||
];
|
||||
$extendFieldsUpdated = false;
|
||||
|
||||
foreach ($extendFieldKeys as $key) {
|
||||
if (array_key_exists($key, $requestData)) {
|
||||
$extendFieldsData[$key] = $requestData[$key];
|
||||
$extendFieldsUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($extendFieldsUpdated) {
|
||||
$columnUpdates['extendFields'] = json_encode($extendFieldsData, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
if (empty($columnUpdates)) {
|
||||
return ResponseHelper::error('没有可更新的字段');
|
||||
}
|
||||
|
||||
$columnUpdates['updateTime'] = time();
|
||||
|
||||
try {
|
||||
Db::table('s2_wechat_friend')->where('id', $friendId)->update($columnUpdates);
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('更新失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
return ResponseHelper::success(['id' => $friendId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取添加好友任务记录列表(全新功能)
|
||||
* 返回当前账号的所有添加好友任务记录,无论是否通过都展示
|
||||
* 包含:添加者头像、昵称、微信号、添加状态、添加时间、通过时间等信息
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getAddTaskList()
|
||||
{
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$status = $this->request->param('status', ''); // 可选:筛选状态 0执行中,1执行成功,2执行失败
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
|
||||
if (empty($accountId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
// 直接使用operatorAccountId查询添加好友任务记录
|
||||
$query = Db::table('s2_friend_task')
|
||||
->where('operatorAccountId', $accountId)
|
||||
->order('createTime desc');
|
||||
|
||||
// 如果指定了状态筛选
|
||||
if ($status !== '' && $status !== null) {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
|
||||
$total = $query->count();
|
||||
$tasks = $query->page($page, $limit)->select();
|
||||
|
||||
|
||||
// 处理任务数据
|
||||
$list = [];
|
||||
foreach ($tasks as $task) {
|
||||
// 提取所有任务的phone、wechatId,用于查询好友信息(获取通过时间)
|
||||
$friendInfo = Db::table('s2_wechat_friend')
|
||||
->where(['isDeleted' => 0, 'ownerWechatId' => $task['wechatId']])
|
||||
->where(function ($query) use ($task) {
|
||||
$query->whereLike('phone', '%'.$task['phone'].'%')->whereOr('alias', $task['phone'])->whereOr('wechatId', $task['phone']);
|
||||
})->field('phone,wechatId,alias,passTime,nickname')->find();
|
||||
|
||||
|
||||
|
||||
$item = [
|
||||
'taskId' => $task['id'] ?? 0,
|
||||
'phone' => $task['phone'] ?? '',
|
||||
'wechatId' => $task['wechatId'] ?? '',
|
||||
'alias' => $task['alias'] ?? '',
|
||||
// 添加者信息
|
||||
'adder' => [
|
||||
'avatar' => $task['wechatAvatar'] ?? '', // 添加者头像
|
||||
'nickname' => $task['wechatNickname'] ?? '', // 添加者昵称
|
||||
'username' => $task['accountUsername'] ?? '', // 添加者微信号
|
||||
'accountNickname' => $task['accountNickname'] ?? '', // 账号昵称
|
||||
'accountRealName' => $task['accountRealName'] ?? '', // 账号真实姓名
|
||||
],
|
||||
// 添加状态
|
||||
'status' => [
|
||||
'code' => $task['status'] ?? 0, // 状态码:0执行中,1执行成功,2执行失败
|
||||
'text' => $this->getTaskStatusText($task['status'] ?? 0), // 状态文本
|
||||
'extra' => ''
|
||||
],
|
||||
// 时间信息
|
||||
'time' => [
|
||||
'addTime' => !empty($task['createTime']) ? date('Y-m-d H:i:s', $task['createTime']) : '', // 添加时间
|
||||
'addTimeStamp' => $task['createTime'] ?? 0, // 添加时间戳
|
||||
'updateTime' => !empty($task['updateTime']) ? date('Y-m-d H:i:s', $task['updateTime']) : '', // 更新时间
|
||||
'updateTimeStamp' => $task['updateTime'] ?? 0, // 更新时间戳
|
||||
'passTime' => !empty($friendInfo['passTime']) ? date('Y-m-d H:i:s', $friendInfo['passTime']) : '', // 通过时间
|
||||
'passTimeStamp' => $friendInfo['passTime'] ?? 0, // 通过时间戳
|
||||
],
|
||||
// 好友信息(如果已通过)
|
||||
'friend' => [
|
||||
'nickname' => $friendInfo['nickname'] ?? '', // 好友昵称
|
||||
'isPassed' => !empty($friendInfo['passTime']), // 是否已通过
|
||||
],
|
||||
// 其他信息
|
||||
'other' => [
|
||||
'msgContent' => $task['msgContent'] ?? '', // 验证消息
|
||||
'remark' => $task['remark'] ?? '', // 备注
|
||||
'from' => $task['from'] ?? '', // 来源
|
||||
'labels' => !empty($task['labels']) ? explode(',', $task['labels']) : [], // 标签
|
||||
]
|
||||
];
|
||||
|
||||
$list[] = $item;
|
||||
}
|
||||
|
||||
return ResponseHelper::success(['list' => $list, 'total' => $total]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取任务状态文本
|
||||
* @param int $status 状态码
|
||||
* @return string 状态文本
|
||||
*/
|
||||
private function getTaskStatusText($status)
|
||||
{
|
||||
$statusMap = [
|
||||
0 => '执行中',
|
||||
1 => '执行成功',
|
||||
2 => '执行失败',
|
||||
];
|
||||
|
||||
return isset($statusMap[$status]) ? $statusMap[$status] : '未知状态';
|
||||
}
|
||||
}
|
||||
273
application/chukebao/controller/WechatGroupController.php
Normal file
273
application/chukebao/controller/WechatGroupController.php
Normal file
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
use app\chukebao\model\ChatGroups;
|
||||
|
||||
class WechatGroupController extends BaseController
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取分组列表
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getList()
|
||||
{
|
||||
// 公司维度分组,不强制校验 userId
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
$query = ChatGroups::where([
|
||||
'companyId' => $companyId,
|
||||
'isDel' => 0,
|
||||
])
|
||||
->order('groupType desc,sort desc,id desc');
|
||||
|
||||
$total = $query->count();
|
||||
$list = $query->select();
|
||||
|
||||
// 处理每个分组的数据
|
||||
$list = is_array($list) ? $list : $list->toArray();
|
||||
foreach ($list as $k => &$v) {
|
||||
$v['createTime'] = !empty($v['createTime']) ? date('Y-m-d H:i:s', $v['createTime']) : '';
|
||||
}
|
||||
unset($v);
|
||||
|
||||
return ResponseHelper::success(['list'=>$list,'total'=>$total]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增分组
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$groupName = $this->request->param('groupName', '');
|
||||
$groupMemo = $this->request->param('groupMemo', '');
|
||||
$groupType = $this->request->param('groupType', 1);
|
||||
$sort = $this->request->param('sort', 0);
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
// 只校验公司维度
|
||||
if (empty($companyId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
if (empty($groupName)) {
|
||||
return ResponseHelper::error('分组名称不能为空');
|
||||
}
|
||||
|
||||
// 验证分组类型
|
||||
if (!in_array($groupType, [1, 2])) {
|
||||
return ResponseHelper::error('无效的分组类型');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$chatGroup = new ChatGroups();
|
||||
$chatGroup->groupName = $groupName;
|
||||
$chatGroup->groupMemo = $groupMemo;
|
||||
$chatGroup->groupType = $groupType;
|
||||
$chatGroup->sort = $sort;
|
||||
$chatGroup->userId = $this->getUserInfo('id');
|
||||
$chatGroup->companyId = $companyId;
|
||||
$chatGroup->createTime = time();
|
||||
$chatGroup->isDel = 0;
|
||||
$chatGroup->save();
|
||||
|
||||
Db::commit();
|
||||
return ResponseHelper::success(['id' => $chatGroup->id], '创建成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('创建失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新分组
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$id = $this->request->param('id', 0);
|
||||
$groupName = $this->request->param('groupName', '');
|
||||
$groupMemo = $this->request->param('groupMemo', '');
|
||||
$groupType = $this->request->param('groupType', 1);
|
||||
$sort = $this->request->param('sort', 0);
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($companyId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
if (empty($id)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
if (empty($groupName)) {
|
||||
return ResponseHelper::error('分组名称不能为空');
|
||||
}
|
||||
|
||||
// 验证分组类型
|
||||
if (!in_array($groupType, [1, 2])) {
|
||||
return ResponseHelper::error('无效的分组类型');
|
||||
}
|
||||
|
||||
// 检查分组是否存在
|
||||
$chatGroup = ChatGroups::where([
|
||||
'id' => $id,
|
||||
'companyId' => $companyId,
|
||||
'isDel' => 0,
|
||||
])->find();
|
||||
|
||||
if (empty($chatGroup)) {
|
||||
return ResponseHelper::error('该分组不存在或已删除');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$chatGroup->groupName = $groupName;
|
||||
$chatGroup->groupMemo = $groupMemo;
|
||||
$chatGroup->groupType = $groupType;
|
||||
$chatGroup->sort = $sort;
|
||||
$chatGroup->save();
|
||||
|
||||
Db::commit();
|
||||
return ResponseHelper::success('', '更新成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('更新失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除分组(假删除)
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$id = $this->request->param('id', 0);
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($companyId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
if (empty($id)) {
|
||||
return ResponseHelper::error('参数缺失');
|
||||
}
|
||||
|
||||
// 检查分组是否存在
|
||||
$chatGroup = ChatGroups::where([
|
||||
'id' => $id,
|
||||
'companyId' => $companyId,
|
||||
'isDel' => 0,
|
||||
])->find();
|
||||
|
||||
if (empty($chatGroup)) {
|
||||
return ResponseHelper::error('该分组不存在或已删除');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 1. 假删除当前分组
|
||||
$chatGroup->isDel = 1;
|
||||
$chatGroup->deleteTime = time();
|
||||
$chatGroup->save();
|
||||
|
||||
// 2. 重置该分组下所有好友的分组ID(s2_wechat_friend.groupIds -> 0)
|
||||
Db::table('s2_wechat_friend')
|
||||
->where('groupIds', $id)
|
||||
->update(['groupIds' => 0]);
|
||||
|
||||
// 3. 重置该分组下所有微信群的分组ID(s2_wechat_chatroom.groupIds -> 0)
|
||||
Db::table('s2_wechat_chatroom')
|
||||
->where('groupIds', $id)
|
||||
->update(['groupIds' => 0]);
|
||||
|
||||
Db::commit();
|
||||
return ResponseHelper::success('', '删除成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('删除失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动分组(将好友或群移动到指定分组)
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function move()
|
||||
{
|
||||
// type: friend 好友, chatroom 群
|
||||
$type = $this->request->param('type', 'friend');
|
||||
$targetId = (int)$this->request->param('groupId', 0);
|
||||
// 仅支持单个ID移动
|
||||
$idParam = $this->request->param('id', 0);
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
if (empty($companyId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
if (empty($targetId)) {
|
||||
return ResponseHelper::error('目标分组ID不能为空');
|
||||
}
|
||||
|
||||
// 仅允许单个 ID,禁止批量
|
||||
$moveId = (int)$idParam;
|
||||
if (empty($moveId)) {
|
||||
return ResponseHelper::error('需要移动的ID不能为空');
|
||||
}
|
||||
|
||||
// 校验目标分组是否存在且属于当前公司
|
||||
$targetGroup = ChatGroups::where([
|
||||
'id' => $targetId,
|
||||
'companyId' => $companyId,
|
||||
'isDel' => 0,
|
||||
])->find();
|
||||
|
||||
if (empty($targetGroup)) {
|
||||
return ResponseHelper::error('目标分组不存在或已删除');
|
||||
}
|
||||
|
||||
// 校验分组类型与移动对象类型是否匹配
|
||||
// groupType: 1=好友分组, 2=群分组
|
||||
if ($type === 'friend' && (int)$targetGroup->groupType !== 1) {
|
||||
return ResponseHelper::error('目标分组类型错误(需要好友分组)');
|
||||
}
|
||||
if ($type === 'chatroom' && (int)$targetGroup->groupType !== 2) {
|
||||
return ResponseHelper::error('目标分组类型错误(需要群分组)');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
if ($type === 'friend') {
|
||||
// 移动单个好友到指定分组:更新 s2_wechat_friend.groupIds
|
||||
Db::table('s2_wechat_friend')
|
||||
->where('id', $moveId)
|
||||
->update(['groupIds' => $targetId]);
|
||||
} elseif ($type === 'chatroom') {
|
||||
// 移动单个群到指定分组:更新 s2_wechat_chatroom.groupIds
|
||||
Db::table('s2_wechat_chatroom')
|
||||
->where('id', $moveId)
|
||||
->update(['groupIds' => $targetId]);
|
||||
} else {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('无效的类型参数');
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
return ResponseHelper::success('', '移动成功');
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
return ResponseHelper::error('移动失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user