Files
cunkebao_v3/Server/application/cunkebao/controller/ContentLibraryController.php
2025-11-19 15:02:10 +08:00

2114 lines
76 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\cunkebao\controller;
use app\cunkebao\model\ContentLibrary;
use app\cunkebao\model\ContentItem;
use library\s2\titleFavicon;
use think\Controller;
use think\Db;
use app\api\controller\WebSocketController;
use think\facade\Cache;
use think\facade\Env;
use app\api\controller\AutomaticAssign;
use think\facade\Request;
/**
* 内容库控制器
*/
class ContentLibraryController extends Controller
{
/************************************
* 内容库基础管理功能
************************************/
/**
* 创建内容库
* @return \think\response\Json
*/
public function create()
{
if (!$this->request->isPost()) {
return json(['code' => 400, 'msg' => '请求方式错误']);
}
// 获取请求参数
$param = $this->request->post();
// 验证参数
if (empty($param['name'])) {
return json(['code' => 400, 'msg' => '内容库名称不能为空']);
}
// 检查内容库名称是否已存在
$where = [
['name', '=', $param['name']],
['companyId', '=', $this->request->userInfo['companyId']],
['isDel', '=', 0]
];
if (empty($this->request->userInfo['isAdmin'])) {
$where[] = ['userId', '=', $this->request->userInfo['id']];
}
// 查询内容库是否存在
$exists = ContentLibrary::where($where)->find();
if ($exists) {
return json(['code' => 400, 'msg' => '内容库名称已存在']);
}
Db::startTrans();
try {
$keywordInclude = isset($param['keywordInclude']) ? json_encode($param['keywordInclude'], 256) : json_encode([]);
$keywordExclude = isset($param['keywordExclude']) ? json_encode($param['keywordExclude'], 256) : json_encode([]);
$sourceType = isset($param['sourceType']) ? $param['sourceType'] : 1;
// 构建数据
$data = [
'name' => $param['name'],
// 数据来源配置
'sourceFriends' => $sourceType == 1 && isset($param['friendsGroups']) ? json_encode($param['friendsGroups']) : json_encode([]), // 选择的微信好友
'sourceGroups' => $sourceType == 2 && isset($param['wechatGroups']) ? json_encode($param['wechatGroups']) : json_encode([]), // 选择的微信群
'groupMembers' => $sourceType == 2 && isset($param['groupMembers']) ? json_encode($param['groupMembers']) : json_encode([]), // 群组成员
'catchType' => isset($param['catchType']) ? json_encode($param['catchType']) : json_encode([]), // 采集类型
// 关键词配置
'keywordInclude' => $keywordInclude, // 包含的关键词
'keywordExclude' => $keywordExclude, // 排除的关键词
// AI配置
'aiEnabled' => isset($param['aiEnabled']) ? $param['aiEnabled'] : 0, // 是否启用AI
'aiPrompt' => isset($param['aiPrompt']) ? $param['aiPrompt'] : '', // AI提示词
// 时间配置
'timeEnabled' => isset($param['timeEnabled']) ? $param['timeEnabled'] : 0, // 是否启用时间限制
'timeStart' => isset($param['startTime']) ? strtotime($param['startTime']) : 0, // 开始时间(转换为时间戳)
'timeEnd' => isset($param['endTime']) ? strtotime($param['endTime']) : 0, // 结束时间(转换为时间戳)
// 来源类型
'sourceType' => $sourceType, // 1=好友2=群3=好友和群
// 基础信息
'status' => isset($param['status']) ? $param['status'] : 0, // 状态0=禁用1=启用
'userId' => $this->request->userInfo['id'],
'companyId' => $this->request->userInfo['companyId'],
'createTime' => time(),
'updateTime' => time()
];
// 创建内容库
$library = new ContentLibrary;
$result = $library->save($data);
if (!$result) {
Db::rollback();
return json(['code' => 500, 'msg' => '创建内容库失败']);
}
Db::commit();
return json(['code' => 200, 'msg' => '创建成功', 'data' => ['id' => $library->id]]);
} catch (\Exception $e) {
Db::rollback();
return json(['code' => 500, 'msg' => '创建失败:' . $e->getMessage()]);
}
}
/**
* 获取内容库列表
* @return \think\response\Json
*/
public function getList()
{
$page = $this->request->param('page', 1);
$limit = $this->request->param('limit', 10);
$keyword = $this->request->param('keyword', '');
$sourceType = $this->request->param('sourceType', ''); // 新增来源类型1=好友2=群
$where = [
['companyId', '=', $this->request->userInfo['companyId']],
['isDel', '=', 0] // 只查询未删除的记录
];
if (empty($this->request->userInfo['isAdmin'])) {
$where[] = ['userId', '=', $this->request->userInfo['id']];
}
// 添加名称模糊搜索
if ($keyword !== '') {
$where[] = ['name', 'like', '%' . $keyword . '%'];
}
// 添加名称模糊搜索
if (!empty($sourceType)) {
$where[] = ['sourceType', '=', $sourceType];
}
$list = ContentLibrary::where($where)
->field('id,name,sourceFriends,sourceGroups,keywordInclude,keywordExclude,aiEnabled,aiPrompt,timeEnabled,timeStart,timeEnd,status,sourceType,userId,createTime,updateTime')
->with(['user' => function ($query) {
$query->field('id,username');
}])
->order('id', 'desc')
->page($page, $limit)
->select();
// 处理JSON字段
foreach ($list as &$item) {
$item['sourceFriends'] = json_decode($item['sourceFriends'] ?: '[]', true);
$item['sourceGroups'] = json_decode($item['sourceGroups'] ?: '[]', true);
$item['keywordInclude'] = json_decode($item['keywordInclude'] ?: '[]', true);
$item['keywordExclude'] = json_decode($item['keywordExclude'] ?: '[]', true);
// 添加创建人名称
$item['creatorName'] = $item['user']['username'] ?? '';
$item['itemCount'] = Db::name('content_item')->where('libraryId', $item['id'])->count();
// 获取好友详细信息
if (!empty($item['sourceFriends'] && $item['sourceType'] == 1)) {
$friendIds = $item['sourceFriends'];
$friendsInfo = [];
if (!empty($friendIds)) {
// 查询好友信息使用wechat_friendship表
$friendsInfo = Db::name('wechat_friendship')->alias('wf')
->field('wf.id,wf.wechatId, wa.nickname, wa.avatar')
->join('wechat_account wa', 'wf.wechatId = wa.wechatId')
->whereIn('wf.id', $friendIds)
->select();
}
// 将好友信息添加到返回数据中
$item['selectedFriends'] = $friendsInfo;
}
if (!empty($item['sourceGroups']) && $item['sourceType'] == 2) {
$groupIds = $item['sourceGroups'];
$groupsInfo = [];
if (!empty($groupIds)) {
// 查询群组信息
$groupsInfo = Db::name('wechat_group')->alias('g')
->field('g.id, g.chatroomId, g.name, g.avatar, g.ownerWechatId')
->whereIn('g.id', $groupIds)
->select();
}
// 将群组信息添加到返回数据中
$item['selectedGroups'] = $groupsInfo;
}
unset($item['user']); // 移除关联数据
}
unset($item);
$total = ContentLibrary::where($where)->count();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'list' => $list,
'total' => $total,
'page' => $page,
]
]);
}
/**
* 获取内容库详情
* @return \think\response\Json
*/
public function detail()
{
$id = $this->request->param('id', 0);
if (empty($id)) {
return json(['code' => 400, 'msg' => '参数错误']);
}
$where = [
['companyId', '=', $this->request->userInfo['companyId']],
['isDel', '=', 0] // 只查询未删除的记录
];
if (empty($this->request->userInfo['isAdmin'])) {
$where[] = ['userId', '=', $this->request->userInfo['id']];
}
$library = ContentLibrary::where($where)
->field('id,name,sourceType,sourceFriends,sourceGroups,keywordInclude,keywordExclude,aiEnabled,aiPrompt,timeEnabled,timeStart,timeEnd,status,userId,companyId,createTime,updateTime,groupMembers,catchType')
->find();
if (empty($library)) {
return json(['code' => 500, 'msg' => '内容库不存在']);
}
// 处理JSON字段转数组
$library['friendsGroups'] = json_decode($library['sourceFriends'] ?: '[]', true);
$library['wechatGroups'] = json_decode($library['sourceGroups'] ?: '[]', true);
$library['keywordInclude'] = json_decode($library['keywordInclude'] ?: '[]', true);
$library['keywordExclude'] = json_decode($library['keywordExclude'] ?: '[]', true);
$library['groupMembers'] = json_decode($library['groupMembers'] ?: '[]', true);
$library['catchType'] = json_decode($library['catchType'] ?: '[]', true);
unset($library['sourceFriends'], $library['sourceGroups']);
// 将时间戳转换为日期格式(精确到日)
if (!empty($library['timeStart'])) {
$library['timeStart'] = date('Y-m-d', $library['timeStart']);
}
if (!empty($library['timeEnd'])) {
$library['timeEnd'] = date('Y-m-d', $library['timeEnd']);
}
// 获取好友详细信息
if (!empty($library['friendsGroups'])) {
$friendIds = $library['friendsGroups'];
$friendsInfo = [];
if (!empty($friendIds)) {
// 查询好友信息使用wechat_friendship表
$friendsInfo = Db::name('wechat_friendship')->alias('wf')
->field('wf.id,wf.wechatId, wa.nickname, wa.avatar')
->join('wechat_account wa', 'wf.wechatId = wa.wechatId')
->order('wa.id DESC')
->whereIn('wf.id', $friendIds)
->select();
}
// 将好友信息添加到返回数据中
$library['friendsGroupsOptions'] = $friendsInfo;
} else {
$library['friendsGroupsOptions'] = [];
}
// 获取群组详细信息
if (!empty($library['wechatGroups'])) {
$groupIds = $library['wechatGroups'];
$groupsInfo = [];
if (!empty($groupIds)) {
// 查询群组信息
$groupsInfo = Db::name('wechat_group')->alias('g')
->field('g.id, g.chatroomId, g.name, g.avatar, g.ownerWechatId,wa.nickname as ownerNickname,wa.avatar as ownerAvatar,wa.alias as ownerAlias')
->join('wechat_account wa', 'g.ownerWechatId = wa.wechatId')
->whereIn('g.id', $groupIds)
->select();
}
// 将群组信息添加到返回数据中
$library['wechatGroupsOptions'] = $groupsInfo;
} else {
$library['wechatGroupsOptions'] = [];
}
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $library
]);
}
/**
* 更新内容库
* @return \think\response\Json
*/
public function update()
{
if (!$this->request->isPost()) {
return json(['code' => 400, 'msg' => '请求方式错误']);
}
// 获取请求参数
$param = $this->request->post();
// 简单验证
if (empty($param['id'])) {
return json(['code' => 400, 'msg' => '参数错误']);
}
if (empty($param['name'])) {
return json(['code' => 400, 'msg' => '内容库名称不能为空']);
}
$where = [
['companyId', '=', $this->request->userInfo['companyId']],
['isDel', '=', 0] // 只查询未删除的记录
];
if (empty($this->request->userInfo['isAdmin'])) {
$where[] = ['userId', '=', $this->request->userInfo['id']];
}
// 查询内容库是否存在
$library = ContentLibrary::where($where)->find();
if (!$library) {
return json(['code' => 500, 'msg' => '内容库不存在']);
}
Db::startTrans();
try {
$keywordInclude = isset($param['keywordInclude']) ? json_encode($param['keywordInclude'], 256) : json_encode([]);
$keywordExclude = isset($param['keywordExclude']) ? json_encode($param['keywordExclude'], 256) : json_encode([]);
// 更新内容库基本信息
$library->name = $param['name'];
$library->sourceType = isset($param['sourceType']) ? $param['sourceType'] : 1;
$library->sourceFriends = $param['sourceType'] == 1 && isset($param['friendsGroups']) ? json_encode($param['friendsGroups']) : json_encode([]);
$library->sourceGroups = $param['sourceType'] == 2 && isset($param['wechatGroups']) ? json_encode($param['wechatGroups']) : json_encode([]);
$library->groupMembers = $param['sourceType'] == 2 && isset($param['groupMembers']) ? json_encode($param['groupMembers']) : json_encode([]);
$library->catchType = isset($param['catchType']) ? json_encode($param['catchType']) : json_encode([]);// 采集类型
$library->keywordInclude = $keywordInclude;
$library->keywordExclude = $keywordExclude;
$library->aiEnabled = isset($param['aiEnabled']) ? $param['aiEnabled'] : 0;
$library->aiPrompt = isset($param['aiPrompt']) ? $param['aiPrompt'] : '';
$library->timeEnabled = isset($param['timeEnabled']) ? $param['timeEnabled'] : 0;
$library->timeStart = isset($param['startTime']) ? strtotime($param['startTime']) : 0;
$library->timeEnd = isset($param['endTime']) ? strtotime($param['endTime']) : 0;
$library->status = isset($param['status']) ? $param['status'] : 0;
$library->updateTime = time();
$library->save();
Db::commit();
return json(['code' => 200, 'msg' => '更新成功']);
} catch (\Exception $e) {
Db::rollback();
return json(['code' => 500, 'msg' => '更新失败:' . $e->getMessage()]);
}
}
/**
* 删除内容库
* @return \think\response\Json
*/
public function delete()
{
$id = $this->request->param('id', 0);
if (empty($id)) {
return json(['code' => 400, 'msg' => '参数错误']);
}
$where = [
['id', '=', $id],
['companyId', '=', $this->request->userInfo['companyId']],
['isDel', '=', 0]
];
if (empty($this->request->userInfo['isAdmin'])) {
$where[] = ['userId', '=', $this->request->userInfo['id']];
}
$library = ContentLibrary::where($where)->find();
if (empty($library)) {
return json(['code' => 500, 'msg' => '内容库不存在']);
}
try {
// 软删除
$library->isDel = 1;
$library->deleteTime = time();
$library->save();
return json(['code' => 200, 'msg' => '删除成功']);
} catch (\Exception $e) {
return json(['code' => 500, 'msg' => '删除失败:' . $e->getMessage()]);
}
}
/************************************
* 内容项目管理功能
************************************/
/**
* 获取内容库素材列表
* @return \think\response\Json
*/
public function getItemList()
{
$page = $this->request->param('page', 1);
$limit = $this->request->param('limit', 10);
$libraryId = $this->request->param('libraryId', 0);
$keyword = $this->request->param('keyword', ''); // 搜索关键词
if (empty($libraryId)) {
return json(['code' => 400, 'msg' => '内容库ID不能为空']);
}
$where = [
['id', '=', $libraryId],
['companyId', '=', $this->request->userInfo['companyId']],
['isDel', '=', 0]
];
if (empty($this->request->userInfo['isAdmin'])) {
$where[] = ['userId', '=', $this->request->userInfo['id']];
}
// 验证内容库权限
$library = ContentLibrary::where($where)->find();
if (empty($library)) {
return json(['code' => 500, 'msg' => '内容库不存在或无权限访问']);
}
// 构建查询条件
$where = [
['libraryId', '=', $libraryId],
['isDel', '=', 0]
];
// 关键词搜索
if (!empty($keyword)) {
$where[] = ['content|title', 'like', '%' . $keyword . '%'];
}
// 查询数据
$list = ContentItem::where($where)
->order('createMomentTime DESC,createTime DESC')
->page($page, $limit)
->select();
// 处理数据
foreach ($list as &$item) {
$item['content'] = !empty($item['contentAi']) ? $item['contentAi'] : $item['content'];
// 处理资源URL
$item['resUrls'] = json_decode($item['resUrls'] ?: '[]', true);
$item['urls'] = json_decode($item['urls'] ?: '[]', true);
// 格式化时间
//$item['createTime'] = date('Y-m-d H:i:s', $item['createTime']);
if ($item['createMomentTime']) {
$item['time'] = date('Y-m-d H:i:s', $item['createMomentTime']);
}
if ($item['createMessageTime']) {
$item['time'] = date('Y-m-d H:i:s', $item['createMessageTime']);
}
// 获取发送者信息
if ($item['type'] == 'moment' && !empty($item['friendId'])) {
$friendInfo = Db::table('s2_wechat_friend')
->where('id', $item['friendId'])
->field('nickname, avatar')
->order('id desc')
->find();
$item['senderNickname'] = !empty($friendInfo['nickname']) ? $friendInfo['nickname'] : '';
$item['senderAvatar'] = !empty($friendInfo['avatar']) ? $friendInfo['avatar'] : '';
} else if ($item['type'] == 'group_message' && !empty($item['wechatChatroomId'])) {
$friendInfo = Db::table('s2_wechat_chatroom_member')
->field('nickname, avatar')
->where('wechatId', $item['wechatId'])
->find();
$item['senderNickname'] = !empty($friendInfo['nickname']) ? $friendInfo['nickname'] : '';
$item['senderAvatar'] = !empty($friendInfo['avatar']) ? $friendInfo['avatar'] : '';
}
unset($item['contentAi']);
}
unset($item);
// 获取总数
$total = ContentItem::where($where)->count();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'list' => $list,
'total' => $total,
'page' => $page,
'limit' => $limit
]
]);
}
/**
* 添加内容项目
* @return \think\response\Json
*/
public function addItem()
{
if (!$this->request->isPost()) {
return json(['code' => 400, 'msg' => '请求方式错误']);
}
// 获取请求参数
$param = $this->request->post();
// A简单验证
if (empty($param['libraryId'])) {
return json(['code' => 400, 'msg' => '内容库ID不能为空']);
}
if (empty($param['type'])) {
return json(['code' => 400, 'msg' => '内容类型不能为空']);
}
if (empty($param['content'])) {
return json(['code' => 400, 'msg' => '内容数据不能为空']);
}
// 当类型为群消息时,限制图片只能上传一张
if ($param['type'] == 'group_message') {
$images = isset($param['images']) ? $param['images'] : [];
if (is_string($images)) {
$images = json_decode($images, true);
}
if (count($images) > 1) {
return json(['code' => 400, 'msg' => '群消息类型只能上传一张图片']);
}
}
$where = [
['id', '=', $param['libraryId']],
['companyId', '=', $this->request->userInfo['companyId']],
['isDel', '=', 0]
];
if (empty($this->request->userInfo['isAdmin'])) {
$where[] = ['userId', '=', $this->request->userInfo['id']];
}
// 查询内容库是否存在
$library = ContentLibrary::where($where)->find();
if (!$library) {
return json(['code' => 500, 'msg' => '内容库不存在']);
}
try {
// 创建内容项目
$item = new ContentItem;
$item->libraryId = $param['libraryId'];
$item->contentType = $param['type'];
$item->type = 'diy';
$item->title = $param['title'] ?? '自定义内容';
$item->content = $param['content'];
$item->comment = $param['comment'] ?? '';
$item->sendTime = strtotime($param['sendTime']);
$item->resUrls = json_encode($param['resUrls'] ?? [], 256);
$item->urls = json_encode($param['urls'] ?? [], 256);
$item->senderNickname = '系统创建';
$item->coverImage = $param['coverImage'] ?? '';
$item->save();
return json(['code' => 200, 'msg' => '添加成功', 'data' => ['id' => $item->id]]);
} catch (\Exception $e) {
return json(['code' => 500, 'msg' => '添加失败:' . $e->getMessage()]);
}
}
/**
* 删除内容项目
* @param int $id 内容项目ID
* @return \think\response\Json
*/
public function deleteItem()
{
$id = $this->request->param('id', 0);
if (empty($id)) {
return json(['code' => 400, 'msg' => '参数错误']);
}
// 查询内容项目是否存在并检查权限
$item = ContentItem::alias('i')
->join('content_library l', 'i.libraryId = l.id')
->where([
['i.id', '=', $id],
['l.userId', '=', $this->request->userInfo['id']]
])
->find();
if (!$item) {
return json(['code' => 500, 'msg' => '内容项目不存在或无权限操作']);
}
try {
// 删除内容项目
$service = new \app\cunkebao\service\ContentItemService();
$result = $service->deleteItem($id);
if ($result['code'] != 200) {
return json($result);
}
return json(['code' => 200, 'msg' => '删除成功']);
} catch (\Exception $e) {
return json(['code' => 500, 'msg' => '删除失败:' . $e->getMessage()]);
}
}
/**
* 获取内容项目详情
* @return \think\response\Json
*/
public function getItemDetail()
{
$id = $this->request->param('id', 0);
if (empty($id)) {
return json(['code' => 400, 'msg' => '参数错误']);
}
// 查询内容项目是否存在并检查权限
$item = ContentItem::alias('i')
->join('content_library l', 'i.libraryId = l.id')
->where([
['i.id', '=', $id],
['l.userId', '=', $this->request->userInfo['id']],
['i.isDel', '=', 0]
])
->field('i.*')
->find();
if (empty($item)) {
return json(['code' => 500, 'msg' => '内容项目不存在或无权限访问']);
}
// 处理数据
// 处理资源URL
$item['resUrls'] = json_decode($item['resUrls'] ?: '[]', true);
$item['urls'] = json_decode($item['urls'] ?: '[]', true);
// 添加内容类型的文字描述
$contentTypeMap = [
0 => '未知',
1 => '图片',
2 => '链接',
3 => '视频',
4 => '文本',
5 => '小程序',
6 => '图文'
];
$item['contentTypeName'] = $contentTypeMap[$item['contentType'] ?? 0] ?? '未知';
// 格式化时间
if ($item['createMomentTime']) {
$item['createMomentTimeFormatted'] = date('Y-m-d H:i:s', $item['createMomentTime']);
}
if ($item['createMessageTime']) {
$item['createMessageTimeFormatted'] = date('Y-m-d H:i:s', $item['createMessageTime']);
}
// 格式化发送时间
if ($item['sendTime']) {
$item['sendTime'] = date('Y-m-d H:i:s', $item['sendTime']);
}
// 获取发送者信息
if ($item['type'] == 'moment' && $item['friendId']) {
$friendInfo = Db::name('wechat_friendship')
->alias('wf')
->join('wechat_account wa', 'wf.wechatId = wa.wechatId')
->where('wf.id', $item['friendId'])
->field('wa.nickname, wa.avatar')
->find();
$item['senderInfo'] = $friendInfo ?: [];
} elseif ($item['type'] == 'group_message' && $item['wechatChatroomId']) {
// 获取群组信息
$groupInfo = Db::name('wechat_group')
->where('id', $item['wechatChatroomId'])
->field('name, avatar')
->find();
$item['groupInfo'] = $groupInfo ?: [];
}
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $item
]);
}
/**
* 更新内容项目
* @return \think\response\Json
*/
public function updateItem()
{
if (!$this->request->isPost()) {
return json(['code' => 400, 'msg' => '请求方式错误']);
}
// 获取请求参数
$param = $this->request->post();
// 简单验证
if (empty($param['id'])) {
return json(['code' => 400, 'msg' => '参数错误']);
}
// 查询内容项目是否存在并检查权限
$item = ContentItem::where([
['id', '=', $param['id']],
['isDel', '=', 0]
])->find();
if (!$item) {
return json(['code' => 500, 'msg' => '内容项目不存在或无权限操作']);
}
try {
// 更新内容项目
$item->title = $param['title'] ?? $item->title;
$item->content = $param['content'] ?? $item->content;
$item->comment = $param['comment'] ?? $item->comment;
// 处理发送时间
if (!empty($param['sendTime'])) {
$item->sendTime = strtotime($param['sendTime']);
}
// 处理内容类型
if (isset($param['contentType'])) {
$item->contentType = $param['contentType'];
}
// 处理资源URL
if (isset($param['resUrls'])) {
$resUrls = is_string($param['resUrls']) ? json_decode($param['resUrls'], true) : $param['resUrls'];
$item->resUrls = json_encode($resUrls, JSON_UNESCAPED_UNICODE);
// 设置封面图片
if (!empty($resUrls[0])) {
$item->coverImage = $resUrls[0];
}
}
// 处理链接URL
if (isset($param['urls'])) {
$urls = is_string($param['urls']) ? json_decode($param['urls'], true) : $param['urls'];
$item->urls = json_encode($urls, JSON_UNESCAPED_UNICODE);
}
// 处理地理位置信息
if (isset($param['location'])) {
$item->location = $param['location'];
}
if (isset($param['lat'])) {
$item->lat = $param['lat'];
}
if (isset($param['lng'])) {
$item->lng = $param['lng'];
}
// 更新修改时间
$item->updateTime = time();
// 保存更新
$item->save();
return json(['code' => 200, 'msg' => '更新成功']);
} catch (\Exception $e) {
return json(['code' => 500, 'msg' => '更新失败:' . $e->getMessage()]);
}
}
public function aiEditContent()
{
$id = Request::param('id', '');
$aiPrompt = Request::param('aiPrompt', '');
$content = Request::param('content', '');
$companyId = $this->request->userInfo['companyId'];
// 简单验证
if (empty($id) && empty($content)) {
return json(['code' => 400, 'msg' => '参数错误']);
}
if (!empty($id)) {
// 查询内容项目是否存在并检查权限
$item = ContentItem::alias('ci')
->join('content_library cl', 'ci.libraryId = cl.id')
->where(['ci.id' => $id, 'ci.isDel' => 0, 'cl.isDel' => 0, 'cl.companyId' => $companyId])
->field('ci.*')
->find();
} else {
$item['content'] = $content;
}
if (empty($item)) {
return json(['code' => 500, 'msg' => '内容项目不存在或无权限操作']);
}
if (empty($item['content'])) {
return json(['code' => 500, 'msg' => '内容不能为空']);
}
$contentFront = !empty($item['contentAi']) ? $item['contentAi'] : $item['content'];
if (!$this->request->isPost()) {
try {
$contentAi = $this->aiRewrite(['aiEnabled' => true, 'aiPrompt' => $aiPrompt], $contentFront);
if (!empty($contentAi)) {
return json(['code' => 200, 'msg' => 'ai编写成功', 'data' => ['contentAfter' => $contentAi, 'contentFront' => $contentFront]]);
} else {
return json(['code' => 500, 'msg' => 'ai编写失败']);
}
} catch (\Exception $e) {
return json(['code' => 500, 'msg' => 'ai编写失败' . $e->getMessage()]);
}
} else {
if (empty($content)) {
return json(['code' => 500, 'msg' => '新内容不能为空']);
}
$res = ContentItem::where(['id' => $item['id']])->update(['contentAi' => $content, 'updateTime' => time()]);
if (!empty($res)) {
return json(['code' => 200, 'msg' => '更新成功']);
} else {
return json(['code' => 500, 'msg' => '更新失败']);
}
}
}
/************************************
* 数据采集相关功能
************************************/
function getExternalPageDetails($url)
{
$html = file_get_contents($url);
$dom = new \DOMDocument();
@$dom->loadHTML($html);
$xpath = new \DOMXPath($dom);
// 获取标题
$titleNode = $xpath->query('//title');
$title = $titleNode->length > 0 ? $titleNode->item(0)->nodeValue : '';
// 获取图标链接
$iconNode = $xpath->query('//link[@rel="shortcut icon"]/@href');
$icon = $iconNode->length > 0 ? $iconNode->item(0)->nodeValue : '';
return ['title' => $title, 'icon' => $icon];
}
/**
* 执行朋友圈采集任务
* @return \think\response\Json
*/
public function collectMoments()
{
// 查询条件:未删除且已开启的内容库
$where = [
['isDel', '=', 0], // 未删除
['status', '=', 1], // 已开启
// ['id', '=', 61], // 已开启
];
// 查询符合条件的内容库
$libraries = ContentLibrary::where($where)
->field('id,name,sourceType,sourceFriends,sourceGroups,keywordInclude,keywordExclude,aiEnabled,aiPrompt,timeEnabled,timeStart,timeEnd,status,userId,companyId,createTime,updateTime,groupMembers')
->order('id', 'desc')
->select()->toArray();
if (empty($libraries)) {
return json(['code' => 200, 'msg' => '没有可用的内容库配置']);
}
$successCount = 0;
$failCount = 0;
$results = [];
// 处理每个内容库的采集任务
foreach ($libraries as $library) {
try {
// 解析JSON字段
$library['sourceFriends'] = json_decode($library['sourceFriends'] ?: '[]', true);
$library['sourceGroups'] = json_decode($library['sourceGroups'] ?: '[]', true);
$library['keywordInclude'] = json_decode($library['keywordInclude'] ?: '[]', true);
$library['keywordExclude'] = json_decode($library['keywordExclude'] ?: '[]', true);
$library['groupMembers'] = json_decode($library['groupMembers'] ?: '[]', true);
// 根据数据来源类型执行不同的采集逻辑
$collectResult = [];
switch ($library['sourceType']) {
case 1: // 好友类型
if (!empty($library['sourceFriends'])) {
$collectResult = $this->collectFromFriends($library);
}
break;
case 2: // 群类型
if (!empty($library['sourceGroups'])) {
$collectResult = $this->collectFromGroups($library);
}
break;
default:
$collectResult = [
'status' => 'failed',
'message' => '不支持的数据来源类型'
];
}
if ($collectResult['status'] == 'success') {
$successCount++;
} else {
$failCount++;
}
$results[] = [
'library_id' => $library['id'],
'library_name' => $library['name'],
'status' => $collectResult['status'],
'message' => $collectResult['message'] ?? '',
'data' => $collectResult['data'] ?? []
];
} catch (\Exception $e) {
$failCount++;
$results[] = [
'library_id' => $library['id'],
'library_name' => $library['name'],
'status' => 'error',
'message' => $e->getMessage()
];
}
}
// 返回采集结果
return json_encode([
'code' => 200,
'msg' => '采集任务执行完成',
'data' => [
'total' => count($libraries),
'success' => $successCount,
'fail' => $failCount,
'results' => $results
]
]);
}
/**
* 从好友采集朋友圈内容
* @param array $library 内容库配置
* @return array 采集结果
*/
private function collectFromFriends($library)
{
$friendIds = $library['sourceFriends'];
if (empty($friendIds)) {
return [
'status' => 'failed',
'message' => '没有指定要采集的好友'
];
}
$friendData = [];
try {
$toAccountId = '';
$username = Env::get('api.username2', '');
$password = Env::get('api.password2', '');
if (!empty($username) || !empty($password)) {
$toAccountId = Db::name('users')->where('account', $username)->value('s2_accountId');
}
// 查询好友信息
$friends = Db::table('s2_wechat_friend')
->field('id, wechatAccountId, wechatId,accountId')
->whereIn('id', $friendIds)
->where('isDeleted', 0)
->select();
if (empty($friends)) {
return [
'status' => 'failed',
'message' => '未找到有效的好友信息'
];
}
// 从朋友圈采集内容
$collectedData = [];
$totalMomentsCount = 0;
foreach ($friends as $friend) {
$friendData = $friend;
if (!empty($username) && !empty($password)) {
//执行切换好友命令
$automaticAssign = new AutomaticAssign();
$automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['id'], 'toAccountId' => $toAccountId], true);
//存入缓存
$friendData['friendId'] = $friend['id'];
artificialAllotWechatFriend($friendData);
//执行采集朋友圈命令
$webSocket = new WebSocketController(['userName' => $username, 'password' => $password, 'accountId' => $toAccountId]);
$webSocket->getMoments(['wechatFriendId' => $friend['id'], 'wechatAccountId' => $friend['wechatAccountId']]);
//采集完毕切换
$automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['id'], 'toAccountId' => $friend['accountId']], true);
}
// 从s2_wechat_moments表获取朋友圈数据
$moments = Db::table('s2_wechat_moments')
->where([
'userName' => $friend['wechatId'],
'wechatAccountId' => $friend['wechatAccountId']
])
->order('createTime', 'desc')
//->where('create_time', '>=', time() - 86400)
->page(1, 20)
->select();
if (empty($moments)) {
continue;
}
// 获取好友详细信息
$friendInfo = Db::table('s2_wechat_friend')
->where('wechatId', $friend['wechatId'])
->field('nickname, avatar')
->find();
$nickname = $friendInfo['nickname'] ?? '未知好友';
$friendMomentsCount = 0;
// 处理每条朋友圈数据
foreach ($moments as $moment) {
// 处理关键词过滤
$content = $moment['content'] ?? '';
// 如果启用了关键词过滤
$includeKeywords = $library['keywordInclude'];
$excludeKeywords = $library['keywordExclude'];
// 检查是否包含必须关键词
$includeMatch = empty($includeKeywords);
if (!empty($includeKeywords)) {
foreach ($includeKeywords as $keyword) {
if (strpos($content, $keyword) !== false) {
$includeMatch = true;
break;
}
}
}
// 如果不满足包含条件,跳过
if (!$includeMatch) {
continue;
}
// 检查是否包含排除关键词
$excludeMatch = false;
if (!empty($excludeKeywords)) {
foreach ($excludeKeywords as $keyword) {
if (strpos($content, $keyword) !== false) {
$excludeMatch = true;
break;
}
}
}
// 如果满足排除条件,跳过
if ($excludeMatch) {
continue;
}
// 如果启用了AI处理
if (!empty($library['aiEnabled']) && !empty($content)) {
$contentAi = $this->aiRewrite($library, $content);
if (!empty($content)) {
$moment['contentAi'] = $contentAi;
} else {
$moment['contentAi'] = '';
}
}
// 保存到内容库的content_item表
$this->saveMomentToContentItem($moment, $library['id'], $friend, $nickname);
$friendMomentsCount++;
}
if ($friendMomentsCount > 0) {
// 记录采集结果
$collectedData[$friend['wechatId']] = [
'friendId' => $friend['id'],
'nickname' => $nickname,
'count' => $friendMomentsCount
];
$totalMomentsCount += $friendMomentsCount;
}
}
if (empty($collectedData)) {
return [
'status' => 'warning',
'message' => '未采集到任何朋友圈内容'
];
}
return [
'status' => 'success',
'message' => '成功采集到' . count($collectedData) . '位好友的' . $totalMomentsCount . '条朋友圈内容',
'data' => [
'friend_count' => count($collectedData),
'collected_count' => $totalMomentsCount,
'details' => $collectedData
]
];
} catch (\Exception $e) {
return [
'status' => 'error',
'message' => '采集过程发生错误: ' . $e->getMessage()
];
}
}
/**
* 从群组采集消息内容
* @param array $library 内容库配置
* @return array 采集结果
*/
private function collectFromGroups($library)
{
$groupIds = $library['sourceGroups'];
if (empty($groupIds)) {
return [
'status' => 'failed',
'message' => '没有指定要采集的群组'
];
}
try {
// 查询群组信息
$groups = Db::name('wechat_group')->alias('g')
->field('g.id, g.chatroomId, g.name, g.ownerWechatId')
->whereIn('g.id', $groupIds)
->where('g.deleteTime', 0)
->select();
if (empty($groups)) {
return [
'status' => 'failed',
'message' => '未找到有效的群组信息'
];
}
// 获取群成员信息
$groupMembers = $library['groupMembers'];
if (empty($groupMembers)) {
// 如果没有指定群成员,则尝试获取所有群成员
return [
'status' => 'failed',
'message' => '未找到有效的群成员信息'
];
}
// 从群组采集内容
$collectedData = [];
$totalMessagesCount = 0;
$chatroomIds = array_column($groups, 'id');
// 获取群消息 - 支持时间范围过滤
$messageWhere = [
['wechatChatroomId', 'in', $chatroomIds],
['type', '=', 2]
];
// 如果启用时间限制
if ($library['timeEnabled'] && $library['timeStart'] > 0 && $library['timeEnd'] > 0) {
$messageWhere[] = ['createTime', 'between', [$library['timeStart'], $library['timeEnd']]];
}
// 查询群消息
$groupMessages = Db::table('s2_wechat_message')
->where($messageWhere)
->order('createTime', 'desc')
->limit(500) // 限制最大消息数量
->select();
if (empty($groupMessages)) {
return [
'status' => 'warning',
'message' => '未找到符合条件的群消息'
];
}
// 按群组分组处理消息
$groupedMessages = [];
foreach ($groupMessages as $message) {
$chatroomId = $message['wechatChatroomId'];
if (!isset($groupedMessages[$chatroomId])) {
$groupedMessages[$chatroomId] = [
'count' => 0,
'messages' => []
];
}
// 处理消息内容
$content = $message['content'] ?? '';
// 如果启用了关键词过滤
$includeKeywords = $library['keywordInclude'];
$excludeKeywords = $library['keywordExclude'];
// 检查是否包含必须关键词
$includeMatch = empty($includeKeywords);
if (!empty($includeKeywords)) {
foreach ($includeKeywords as $keyword) {
if (strpos($content, $keyword) !== false) {
$includeMatch = true;
break;
}
}
}
// 如果不满足包含条件,跳过
if (!$includeMatch) {
continue;
}
// 检查是否包含排除关键词
$excludeMatch = false;
if (!empty($excludeKeywords)) {
foreach ($excludeKeywords as $keyword) {
if (strpos($content, $keyword) !== false) {
$excludeMatch = true;
break;
}
}
}
// 如果满足排除条件,跳过
if ($excludeMatch) {
continue;
}
// 找到对应的群组信息
$groupInfo = null;
foreach ($groups as $group) {
if ($group['id'] == $chatroomId) {
$groupInfo = $group;
break;
}
}
if (!$groupInfo) {
continue;
}
// 如果启用了AI处理
if (!empty($library['aiEnabled']) && !empty($content)) {
$contentAi = $this->aiRewrite($library, $content);
if (!empty($content)) {
$moment['contentAi'] = $contentAi;
} else {
$moment['contentAi'] = '';
}
}
// 保存消息到内容库
$this->saveMessageToContentItem($message, $library['id'], $groupInfo);
// 累计计数
$groupedMessages[$chatroomId]['count']++;
$groupedMessages[$chatroomId]['messages'][] = [
'id' => $message['id'],
'content' => mb_substr($content, 0, 50) . (mb_strlen($content) > 50 ? '...' : ''),
'sender' => $message['senderNickname'],
'time' => date('Y-m-d H:i:s', $message['createTime'])
];
$totalMessagesCount++;
}
// 构建结果数据
foreach ($groups as $group) {
$chatroomId = $group['chatroomId'];
if (isset($groupedMessages[$chatroomId]) && $groupedMessages[$chatroomId]['count'] > 0) {
$collectedData[$chatroomId] = [
'groupId' => $group['id'],
'groupName' => $group['name'],
'count' => $groupedMessages[$chatroomId]['count'],
'messages' => $groupedMessages[$chatroomId]['messages']
];
}
}
if (empty($collectedData)) {
return [
'status' => 'warning',
'message' => '未采集到符合条件的群消息内容'
];
}
return [
'status' => 'success',
'message' => '成功采集到' . count($collectedData) . '个群的' . $totalMessagesCount . '条消息',
'data' => [
'group_count' => count($collectedData),
'collected_count' => $totalMessagesCount,
'details' => $collectedData
]
];
} catch (\Exception $e) {
return [
'status' => 'error',
'message' => '采集过程发生错误: ' . $e->getMessage()
];
}
}
/**
* 判断内容类型
* @param string $content 内容文本
* @param array $resUrls 资源URL数组
* @param array $urls URL数组
* @return int 内容类型: 1=图片, 2=链接, 3=视频, 4=文本, 5=小程序
*/
private function determineContentType($content, $resUrls = [], $urls = [])
{
// 判断是否为空
if (empty($content) && empty($resUrls) && empty($urls)) {
return 0; // 未知类型
}
// 分析内容中可能包含的链接或图片地址
if (!empty($content)) {
// 检查内容中是否有链接
$urlPattern = '/https?:\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]/';
preg_match_all($urlPattern, $content, $contentUrlMatches);
if (!empty($contentUrlMatches[0])) {
// 将内容中的链接添加到urls数组中(去重)
foreach ($contentUrlMatches[0] as $url) {
if (!in_array($url, $urls)) {
$urls[] = $url;
}
}
}
// 检查内容中是否包含图片或视频链接
foreach ($contentUrlMatches[0] ?? [] as $url) {
// 检查是否为图片文件
if (stripos($url, '.jpg') !== false ||
stripos($url, '.jpeg') !== false ||
stripos($url, '.png') !== false ||
stripos($url, '.gif') !== false ||
stripos($url, '.webp') !== false ||
stripos($url, '.bmp') !== false ||
stripos($url, 'image') !== false) {
if (!in_array($url, $resUrls)) {
$resUrls[] = $url;
}
}
// 检查是否为视频文件
if (stripos($url, '.mp4') !== false ||
stripos($url, '.mov') !== false ||
stripos($url, '.avi') !== false ||
stripos($url, '.wmv') !== false ||
stripos($url, '.flv') !== false ||
stripos($url, 'video') !== false) {
if (!in_array($url, $resUrls)) {
$resUrls[] = $url;
}
}
}
}
// 判断是否有小程序信息
if (strpos($content, '小程序') !== false || strpos($content, 'appid') !== false) {
return 5; // 小程序
}
// 检查资源URL中是否有视频或图片
$hasVideo = false;
$hasImage = false;
if (!empty($resUrls)) {
foreach ($resUrls as $url) {
// 检查是否为视频文件
if (stripos($url, '.mp4') !== false ||
stripos($url, '.mov') !== false ||
stripos($url, '.avi') !== false ||
stripos($url, '.wmv') !== false ||
stripos($url, '.flv') !== false ||
stripos($url, 'video') !== false) {
$hasVideo = true;
break; // 一旦发现视频文件,立即退出循环
}
// 检查是否为图片文件
if (stripos($url, '.jpg') !== false ||
stripos($url, '.jpeg') !== false ||
stripos($url, '.png') !== false ||
stripos($url, '.gif') !== false ||
stripos($url, '.webp') !== false ||
stripos($url, '.bmp') !== false ||
stripos($url, 'image') !== false) {
$hasImage = true;
// 不退出循环,继续检查是否有视频(视频优先级更高)
}
}
}
// 如果发现视频文件,判定为视频类型
if ($hasVideo) {
return 3; // 视频
}
// 判断内容是否纯链接
$isPureLink = false;
if (!empty($content) && !empty($urls)) {
$contentWithoutUrls = $content;
foreach ($urls as $url) {
$contentWithoutUrls = str_replace($url, '', $contentWithoutUrls);
}
// 如果去除链接后内容为空,则认为是纯链接
if (empty(trim($contentWithoutUrls))) {
$isPureLink = true;
}
}
// 如果内容是纯链接,判定为链接类型
if ($isPureLink) {
return 2; // 链接
}
// 优先判断内容文本
// 如果有文本内容(不仅仅是链接)
if (!empty($content) && !$isPureLink) {
// 如果有图片,则为图文类型
if ($hasImage) {
return 1; // 图文
} else {
return 4; // 纯文本
}
}
// 判断是否为图片类型
if ($hasImage) {
return 1; // 图片
}
// 判断是否为链接类型
if (!empty($urls)) {
return 2; // 链接
}
// 默认为文本类型
return 4; // 文本
}
/**
* 保存朋友圈数据到内容项目表
* @param array $moment 朋友圈数据
* @param int $libraryId 内容库ID
* @param array $friend 好友信息
* @param string $nickname 好友昵称
* @return bool 是否保存成功
*/
private function saveMomentToContentItem($moment, $libraryId, $friend, $nickname)
{
if (empty($moment) || empty($libraryId)) {
return false;
}
try {
// 检查朋友圈数据是否已存在于内容项目中
$exists = ContentItem::where('libraryId', $libraryId)
->where('snsId', $moment['snsId'] ?? '')
->find();
if ($exists) {
return true;
}
// 解析资源URL (可能是JSON字符串)
$resUrls = $moment['resUrls'];
if (is_string($resUrls)) {
$resUrls = json_decode($resUrls, true);
}
// 处理urls字段
$urls = $moment['urls'] ?? [];
if (is_string($urls)) {
$urls = json_decode($urls, true);
}
// 构建封面图片
$coverImage = '';
if (!empty($resUrls) && is_array($resUrls) && count($resUrls) > 0) {
$coverImage = $resUrls[0];
}
// 判断内容类型 (0=未知, 1=图片, 2=链接, 3=视频, 4=文本, 5=小程序)
if ($moment['type'] == 1) {
//图文
$contentType = 1;
} elseif ($moment['type'] == 3) {
//链接
$contentType = 2;
$urls = [];
$url = is_string($moment['urls']) ? json_decode($moment['urls'], true) : $moment['urls'] ?? [];
$url = $url[0];
//兼容链接采集不到标题及图标
if (empty($moment['title'])) {
// 检查是否是飞书链接
if (strpos($url, 'feishu.cn') !== false) {
// 飞书文档需要登录,无法直接获取内容,返回默认信息
$urls[] = [
'url' => $url,
'image' => 'http://karuosiyujzk.oss-cn-shenzhen.aliyuncs.com/2025/07/09/3db2a5d7fe49011ab68175a42a5094ce.jpeg',
'desc' => '飞书文档'
];
} else {
$getUrlDetails = $this->getExternalPageDetails($url);
$icon = 'http://karuosiyujzk.oss-cn-shenzhen.aliyuncs.com/2025/07/09/ec039d96fad6eab1d960f207d3d9ca9f.jpeg';
if (!empty($getUrlDetails['title'])) {
$urls[] = [
'url' => $url,
'image' => $icon,
'desc' => '点击查看详情'
];
} else {
$urls[] = [
'url' => $url,
'image' => !empty($getUrlDetails['icon']) ? $getUrlDetails['icon'] : $icon,
'desc' => $getUrlDetails['title']
];
}
}
} else {
if (strpos($url, 'feishu.cn') !== false) {
$coverImage = 'http://karuosiyujzk.oss-cn-shenzhen.aliyuncs.com/2025/07/09/3db2a5d7fe49011ab68175a42a5094ce.jpeg';
} else {
$coverImage = 'http://karuosiyujzk.oss-cn-shenzhen.aliyuncs.com/2025/07/09/ec039d96fad6eab1d960f207d3d9ca9f.jpeg';
}
$urls[] = [
'url' => $url,
'image' => !empty($moment['coverImage']) ? $moment['coverImage'] : $coverImage,
'desc' => $moment['title']
];
}
$moment['urls'] = $urls;
} elseif ($moment['type'] == 15) {
//视频
$contentType = 3;
} elseif ($moment['type'] == 2) {
//纯文本
$contentType = 4;
} elseif ($moment['type'] == 30) {
//小程序
$contentType = 5;
} else {
$contentType = 1;
}
// 如果不存在,则创建新的内容项目
$item = new ContentItem();
$item->libraryId = $libraryId;
$item->type = 'moment'; // 朋友圈类型
$item->title = '来自 ' . $nickname . ' 的朋友圈';
$item->contentData = json_encode($moment, JSON_UNESCAPED_UNICODE);
$item->snsId = $moment['snsId'] ?? ''; // 存储snsId便于后续查询
$item->createTime = time();
$item->wechatId = $friend['wechatId'];
$item->friendId = $friend['id'];
$item->createMomentTime = $moment['createTime'] ?? 0;
$item->content = $moment['content'] ?? '';
$item->contentAi = $moment['contentAi'] ?? '';
$item->coverImage = $coverImage;
$item->contentType = $contentType; // 设置内容类型
// 独立存储resUrls和urls字段
$item->resUrls = is_string($moment['resUrls']) ? $moment['resUrls'] : json_encode($resUrls, JSON_UNESCAPED_UNICODE);
$item->urls = is_string($moment['urls']) ? $moment['urls'] : json_encode($urls, JSON_UNESCAPED_UNICODE);
// 保存地理位置信息
$item->location = $moment['location'] ?? '';
$item->lat = $moment['lat'] ?? 0;
$item->lng = $moment['lng'] ?? 0;
$item->save();
return true;
} catch (\Exception $e) {
// 记录错误日志
\think\facade\Log::error('保存朋友圈数据失败: ' . $e->getMessage());
return false;
}
}
/**
* 保存群聊消息到内容项目表
* @param array $message 消息数据
* @param int $libraryId 内容库ID
* @param array $group 群组信息
* @return bool 是否保存成功
*/
private function saveMessageToContentItem($message, $libraryId, $group)
{
if (empty($message) || empty($libraryId)) {
return false;
}
try {
// 检查消息是否已存在于内容项目中
$exists = ContentItem::where('libraryId', $libraryId)
->where('msgId', $message['msgSvrId'] ?? '')
->find();
if ($exists) {
return true;
}
// 提取消息内容中的链接
$content = $message['content'] ?? '';
$links = [];
$pattern = '/https?:\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]/';
preg_match_all($pattern, $content, $matches);
if (!empty($matches[0])) {
$links = $matches[0];
}
// 提取可能的图片URL
$resUrls = [];
if (isset($message['imageUrl']) && !empty($message['imageUrl'])) {
$resUrls[] = $message['imageUrl'];
}
// 判断内容类型 (0=未知, 1=图片, 2=链接, 3=视频, 4=文本, 5=小程序, 6=图文)
$contentType = $this->determineContentType($content, $resUrls, $links);
// 创建新的内容项目
$item = new ContentItem();
$item->libraryId = $libraryId;
$item->type = 'group_message'; // 群消息类型
$item->title = '来自 ' . ($group['name'] ?? '未知群组') . ' 的消息';
$item->contentData = json_encode($message, JSON_UNESCAPED_UNICODE);
$item->msgId = $message['msgId'] ?? ''; // 存储msgId便于后续查询
$item->createTime = time();
$item->content = $content;
$item->contentType = $contentType; // 设置内容类型
// 设置发送者信息
$item->wechatId = $message['senderWechatId'] ?? '';
$item->wechatChatroomId = $message['wechatChatroomId'] ?? '';
$item->senderNickname = $message['senderNickname'] ?? '';
$item->createMessageTime = $message['createTime'] ?? 0;
// 处理资源URL
if (!empty($resUrls)) {
$item->resUrls = json_encode($resUrls, JSON_UNESCAPED_UNICODE);
// 设置封面图片
if (!empty($resUrls[0])) {
$item->coverImage = $resUrls[0];
}
}
// 处理链接
if (!empty($links)) {
$item->urls = json_encode($links, JSON_UNESCAPED_UNICODE);
}
// 设置商品信息(需根据消息内容解析)
$this->extractProductInfo($item, $content);
$item->save();
return true;
} catch (\Exception $e) {
// 记录错误日志
\think\facade\Log::error('保存群消息数据失败: ' . $e->getMessage());
return false;
}
}
/**
* 从消息内容中提取商品信息
* @param ContentItem $item 内容项目对象
* @param string $content 消息内容
* @return void
*/
private function extractProductInfo($item, $content)
{
// 尝试提取商品名称
$titlePatterns = [
'/【(.+?)】/', // 匹配【】中的内容
'/《(.+?)》/', // 匹配《》中的内容
'/商品名称[:](.+?)[\r\n]/' // 匹配"商品名称:"后的内容
];
foreach ($titlePatterns as $pattern) {
preg_match($pattern, $content, $matches);
if (!empty($matches[1])) {
$item->productTitle = trim($matches[1]);
break;
}
}
// 如果没有找到商品名称,尝试使用内容的前部分作为标题
if (empty($item->productTitle)) {
// 获取第一行非空内容作为标题
$lines = explode("\n", $content);
foreach ($lines as $line) {
$line = trim($line);
if (!empty($line) && mb_strlen($line) > 2) {
$item->productTitle = mb_substr($line, 0, 30);
break;
}
}
}
}
/**
* 获取朋友圈数据
* @param string $wechatId 微信ID
* @return array 朋友圈数据
*/
private function getMomentsData($wechatId)
{
// 这里应该是实际从API或数据库获取朋友圈数据的逻辑
// 这里仅作示例返回
return [
// 示例数据
['id' => 1, 'content' => '今天天气真好!', 'createTime' => time() - 3600],
['id' => 2, 'content' => '分享一个有趣的项目', 'createTime' => time() - 7200],
];
}
/**
* 根据关键词过滤朋友圈内容
* @param array $moments 朋友圈内容
* @param array $includeKeywords 包含关键词
* @param array $excludeKeywords 排除关键词
* @return array 过滤后的内容
*/
private function filterMomentsByKeywords($moments, $includeKeywords, $excludeKeywords)
{
if (empty($moments)) {
return [];
}
$filtered = [];
foreach ($moments as $moment) {
$content = $moment['content'] ?? '';
// 如果内容为空,跳过
if (empty($content)) {
continue;
}
// 检查是否包含必须关键词
$includeMatch = empty($includeKeywords);
if (!empty($includeKeywords)) {
foreach ($includeKeywords as $keyword) {
if (strpos($content, $keyword) !== false) {
$includeMatch = true;
break;
}
}
}
// 如果不满足包含条件,跳过
if (!$includeMatch) {
continue;
}
// 检查是否包含排除关键词
$excludeMatch = false;
if (!empty($excludeKeywords)) {
foreach ($excludeKeywords as $keyword) {
if (strpos($content, $keyword) !== false) {
$excludeMatch = true;
break;
}
}
}
// 如果满足排除条件,跳过
if ($excludeMatch) {
continue;
}
// 通过所有过滤,添加到结果中
$filtered[] = $moment;
}
return $filtered;
}
/**
* 使用AI处理采集的数据
* @param array $data 采集的数据
* @param string $prompt AI提示词
* @return array 处理后的数据
*/
private function processWithAI($data, $prompt)
{
// 这里应该是调用AI处理数据的逻辑
// 实际实现需要根据具体的AI API
return $data;
}
/**
* 保存采集的数据到内容项目
* @param array $data 采集的数据
* @param int $libraryId 内容库ID
* @return bool 是否保存成功
*/
private function saveCollectedData($data, $libraryId)
{
if (empty($data) || empty($libraryId)) {
return false;
}
try {
foreach ($data as $wechatId => $userData) {
foreach ($userData['moments'] as $moment) {
// 创建内容项目
$item = new ContentItem;
$item->libraryId = $libraryId;
$item->type = 'moment'; // 朋友圈类型
$item->title = '来自 ' . $userData['nickname'] . ' 的朋友圈';
$item->contentData = json_encode($moment);
$item->createTime = time();
$item->save();
}
}
return true;
} catch (\Exception $e) {
// 记录错误日志
\think\facade\Log::error('保存采集数据失败: ' . $e->getMessage());
return false;
}
}
/**
* 获取所有群成员
* @param array $groupIds 群组ID列表
* @return array 群成员列表
*/
private function getAllGroupMembers($groupIds)
{
if (empty($groupIds)) {
return [];
}
try {
// 查询群成员信息
$members = Db::name('wechat_group_member')->alias('gm')
->field('gm.id, gm.memberId, gm.groupId, wa.nickname')
->join('wechat_account wa', 'gm.memberId = wa.wechatId')
->whereIn('gm.groupId', $groupIds)
->where('gm.isDel', 0)
->select();
return $members;
} catch (\Exception $e) {
\think\facade\Log::error('获取群成员失败: ' . $e->getMessage());
return [];
}
}
/**
* 解析URL获取网页信息内部调用
* @param string $url 要解析的URL
* @return array 包含title、icon的数组失败返回空数组
*/
public function parseUrl($url)
{
if (empty($url) || !filter_var($url, FILTER_VALIDATE_URL)) {
return [];
}
try {
// 设置请求头,模拟浏览器访问
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => [
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8',
'Accept-Encoding: gzip, deflate',
'Connection: keep-alive',
'Upgrade-Insecure-Requests: 1'
],
'timeout' => 10,
'follow_location' => true,
'max_redirects' => 3
]
]);
// 获取网页内容
$html = @file_get_contents($url, false, $context);
if ($html === false) {
return [];
}
// 检测编码并转换为UTF-8
$encoding = mb_detect_encoding($html, ['UTF-8', 'GBK', 'GB2312', 'BIG5', 'ASCII']);
if ($encoding && $encoding !== 'UTF-8') {
$html = mb_convert_encoding($html, 'UTF-8', $encoding);
}
// 解析HTML
$dom = new \DOMDocument();
@$dom->loadHTML($html, LIBXML_NOERROR | LIBXML_NOWARNING);
$xpath = new \DOMXPath($dom);
$result = [
'title' => '',
'icon' => '',
'url' => $url
];
// 提取标题
$titleNodes = $xpath->query('//title');
if ($titleNodes->length > 0) {
$result['title'] = trim($titleNodes->item(0)->textContent);
}
// 提取图标 - 优先获取favicon
$iconNodes = $xpath->query('//link[@rel="icon"]/@href | //link[@rel="shortcut icon"]/@href | //link[@rel="apple-touch-icon"]/@href');
if ($iconNodes->length > 0) {
$iconUrl = trim($iconNodes->item(0)->value);
$result['icon'] = $this->makeAbsoluteUrl($iconUrl, $url);
} else {
// 尝试获取Open Graph图片
$ogImageNodes = $xpath->query('//meta[@property="og:image"]/@content');
if ($ogImageNodes->length > 0) {
$result['icon'] = trim($ogImageNodes->item(0)->value);
} else {
// 默认favicon路径
$result['icon'] = $this->makeAbsoluteUrl('/favicon.ico', $url);
}
}
// 清理和验证数据
$result['title'] = $this->cleanText($result['title']);
return $result;
} catch (\Exception $e) {
// 记录错误日志但不抛出异常
\think\facade\Log::error('URL解析失败: ' . $e->getMessage() . ' URL: ' . $url);
return [];
}
}
/**
* 将相对URL转换为绝对URL
* @param string $relativeUrl 相对URL
* @param string $baseUrl 基础URL
* @return string 绝对URL
*/
private function makeAbsoluteUrl($relativeUrl, $baseUrl)
{
if (empty($relativeUrl)) {
return '';
}
// 如果已经是绝对URL直接返回
if (filter_var($relativeUrl, FILTER_VALIDATE_URL)) {
return $relativeUrl;
}
// 解析基础URL
$baseParts = parse_url($baseUrl);
if (!$baseParts) {
return $relativeUrl;
}
// 处理以/开头的绝对路径
if (strpos($relativeUrl, '/') === 0) {
return $baseParts['scheme'] . '://' . $baseParts['host'] .
(isset($baseParts['port']) ? ':' . $baseParts['port'] : '') .
$relativeUrl;
}
// 处理相对路径
$basePath = isset($baseParts['path']) ? dirname($baseParts['path']) : '/';
if ($basePath === '.') {
$basePath = '/';
}
return $baseParts['scheme'] . '://' . $baseParts['host'] .
(isset($baseParts['port']) ? ':' . $baseParts['port'] : '') .
$basePath . '/' . $relativeUrl;
}
/**
* 清理文本内容
* @param string $text 要清理的文本
* @return string 清理后的文本
*/
private function cleanText($text)
{
if (empty($text)) {
return '';
}
// 移除HTML实体
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// 移除多余的空白字符
$text = preg_replace('/\s+/', ' ', $text);
// 移除控制字符
$text = preg_replace('/[\x00-\x1F\x7F]/', '', $text);
return trim($text);
}
public function aiRewrite($library = [], $content = '')
{
if (empty($library['aiEnabled']) && empty($content)) {
return false;
}
// 此处实现AI处理逻辑暂未实现
$utl = Env::get('doubaoAi.api_url', '');
$apiKey = Env::get('doubaoAi.api_key', '');
$model = Env::get('doubaoAi.model', 'doubao-1-5-pro-32k-250115');
if (empty($apiKey)) {
return false;
}
if (!empty($library['aiPrompt'])) {
$aiPrompt = $library['aiPrompt'];
} else {
$aiPrompt = '重写这条朋友圈 要求:
1、原本的字数和意思不要修改超过10%
2、出现品牌名或个人名字就去除';
}
$content = $aiPrompt . ' ' . $content;
$headerData = ['Authorization:Bearer ' . $apiKey];
$header = setHeader($headerData);
// 发送请求
$params = [
'model' => $model,
'messages' => [
['role' => 'system', 'content' => '你是人工智能助手.'],
['role' => 'user', 'content' => $content],
]
];
$result = requestCurl($utl, $params, 'POST', $header, 'json');
$result = json_decode($result, true);
if (!empty($result['choices'])) {
$contentAI = $result['choices'][0]['message']['content'];
return $contentAI;
} else {
return false;
}
}
}