diff --git a/app/Constants/TalkMessageType.php b/app/Constants/TalkMessageType.php index b6d5365..7c5cd29 100644 --- a/app/Constants/TalkMessageType.php +++ b/app/Constants/TalkMessageType.php @@ -1,4 +1,5 @@ request->inputs(['talk_type', 'receiver_id', 'records_ids', 'forward_mode', 'receive_user_ids', 'receive_group_ids']); $this->validate($params, [ @@ -310,26 +311,36 @@ class TalkMessageController extends CController 'forward_mode' => 'required|in:1,2',// 转发方方式[1:逐条转发;2:合并转发;] ]); + $user_id = $this->uid(); + + // 判断好友或者群关系 + if (!UserRelation::isFriendOrGroupMember($user_id, (int)$params['receiver_id'], (int)$params['talk_type'])) { + return $this->response->fail('暂不属于好友关系或群聊成员,无法发送聊天消息!'); + } + $receive_user_ids = $receive_group_ids = []; + + $func = function (array $ids, int $talk_type) { + return array_map(function ($id) use ($talk_type) { + return ['talk_type' => $talk_type, 'id' => (int)$id]; + }, $ids); + }; + if (isset($params['receive_user_ids']) && !empty($params['receive_user_ids'])) { - $receive_user_ids = array_map(function ($friend_id) { - return ['talk_type' => TalkModeConstant::PRIVATE_CHAT, 'id' => (int)$friend_id]; - }, $params['receive_user_ids']); + $receive_user_ids = $func($params['receive_user_ids'], TalkModeConstant::PRIVATE_CHAT); } if (isset($params['receive_group_ids']) && !empty($params['receive_group_ids'])) { - $receive_group_ids = array_map(function ($group_id) { - return ['talk_type' => TalkModeConstant::GROUP_CHAT, 'id' => (int)$group_id]; - }, $params['receive_group_ids']); + $receive_group_ids = $func($params['receive_group_ids'], TalkModeConstant::GROUP_CHAT); } + // 需要转发的好友或者群组 $items = array_merge($receive_user_ids, $receive_group_ids); - $user_id = $this->uid(); - if ($params['forward_mode'] == 1) {// 单条转发 - $ids = $this->talkService->forwardRecords($user_id, $params['receiver_id'], $params['records_ids']); + if ($params['forward_mode'] == 1) {// 逐条转发 + $ids = $forwardService->multiSplitForward($user_id, (int)$params['receiver_id'], (int)$params['talk_type'], $params['records_ids'], $items); } else {// 合并转发 - $ids = $this->talkService->mergeForwardRecords($user_id, (int)$params['receiver_id'], (int)$params['talk_type'], $params['records_ids'], $items); + $ids = $forwardService->multiMergeForward($user_id, (int)$params['receiver_id'], (int)$params['talk_type'], $params['records_ids'], $items); } if (!$ids) return $this->response->fail('转发失败!'); @@ -364,7 +375,7 @@ class TalkMessageController extends CController 'record_id' => 'required|integer' ]); - [$isTrue, $data] = $service->collect($this->uid(), $params['record_id']); + [$isTrue, $data] = $service->collect($this->uid(), (int)$params['record_id']); if (!$isTrue) return $this->response->fail('添加表情失败!'); @@ -384,7 +395,7 @@ class TalkMessageController extends CController 'record_id' => 'required|integer|min:1' ]); - [$isTrue, $message,] = $this->talkService->revokeRecord($this->uid(), $params['record_id']); + [$isTrue, $message,] = $this->talkService->revokeRecord($this->uid(), (int)$params['record_id']); if (!$isTrue) return $this->response->fail($message); return $this->response->success([], $message); @@ -405,8 +416,8 @@ class TalkMessageController extends CController $isTrue = $this->talkService->removeRecords( $this->uid(), - $params['talk_type'], - $params['receiver_id'], + (int)$params['talk_type'], + (int)$params['receiver_id'], parse_ids($params['record_id']) ); diff --git a/app/Service/Message/FormatMessageService.php b/app/Service/Message/FormatMessageService.php index 5a7319f..b67479a 100644 --- a/app/Service/Message/FormatMessageService.php +++ b/app/Service/Message/FormatMessageService.php @@ -62,7 +62,7 @@ class FormatMessageService * @param array $rows 聊天记录 * @return array */ - public function handleChatRecords(array $rows) + public function handleChatRecords(array $rows): array { if (!$rows) return []; diff --git a/app/Service/TalkForwardService.php b/app/Service/TalkForwardService.php new file mode 100644 index 0000000..fde2dfb --- /dev/null +++ b/app/Service/TalkForwardService.php @@ -0,0 +1,228 @@ +where(function ($query) use ($user_id, $receiver_id) { + $query->where([ + ['user_id', '=', $user_id], + ['receiver_id', '=', $receiver_id] + ])->orWhere([ + ['user_id', '=', $receiver_id], + ['receiver_id', '=', $user_id] + ]); + }); + } + + $result = $sqlObj->where('talk_type', $talk_type)->whereIn('msg_type', $msg_type)->where('is_revoke', 0)->get(); + + return count($result) == count($records_ids); + } + + /** + * 转发消息(多条合并转发) + * + * @param int $user_id 转发的用户ID + * @param int $receiver_id 当前转发消息的所属者(好友ID或者群聊ID) + * @param int $talk_type 消息来源 1:好友消息 2:群聊消息 + * @param array $records_ids 转发消息的记录ID + * @param array $receives 接受者数组 例如:[['talk_type' => 1,'id' => 3045]...] 二维数组 + * @return array + */ + public function multiMergeForward(int $user_id, int $receiver_id, int $talk_type, array $records_ids, array $receives): array + { + $isTrue = $this->verifyForward($user_id, $receiver_id, $talk_type, $records_ids); + if (!$isTrue) return []; + + // 默认取前3条聊天记录 + $rows = TalkRecords::leftJoin('users', 'users.id', '=', 'talk_records.user_id') + ->whereIn('talk_records.id', array_slice($records_ids, 0, 3)) + ->get(['talk_records.msg_type', 'talk_records.content', 'users.nickname']); + + $jsonText = []; + foreach ($rows as $row) { + switch ($row->msg_type) { + case TalkMessageType::TEXT_MESSAGE: + $jsonText[] = [ + 'nickname' => $row->nickname, + 'text' => mb_substr(str_replace(PHP_EOL, "", $row->content), 0, 30) + ]; + break; + case TalkMessageType::FILE_MESSAGE: + $jsonText[] = [ + 'nickname' => $row->nickname, + 'text' => '【文件消息】' + ]; + break; + case TalkMessageType::CODE_MESSAGE: + $jsonText[] = [ + 'nickname' => $row->nickname, + 'text' => '【代码消息】' + ]; + break; + } + } + + $insRecordIds = []; + Db::beginTransaction(); + try { + foreach ($receives as $item) { + $res = TalkRecords::create([ + 'talk_type' => $item['talk_type'], + 'user_id' => $user_id, + 'receiver_id' => $item['id'], + 'msg_type' => TalkMessageType::FORWARD_MESSAGE, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]); + + $insRecordIds[] = [ + 'record_id' => $res->id, + 'receiver_id' => $res->receiver_id, + 'talk_type' => $res->talk_type + ]; + + TalkRecordsForward::create([ + 'record_id' => $res->id, + 'user_id' => $user_id, + 'records_id' => implode(',', $records_ids), + 'text' => json_encode($jsonText), + 'created_at' => date('Y-m-d H:i:s'), + ]); + } + + Db::commit(); + } catch (Exception $e) { + Db::rollBack(); + return []; + } + + return $insRecordIds; + } + + /** + * 转发消息(多条拆分转发) + * @param int $user_id 转发的用户ID + * @param int $receiver_id 当前转发消息的所属者(好友ID或者群聊ID) + * @param int $talk_type 消息来源 1:好友消息 2:群聊消息 + * @param array $records_ids 转发消息的记录ID + * @param array $receives 接受者数组 例如:[['talk_type' => 1,'id' => 3045]...] 二维数组 + * @return array + */ + public function multiSplitForward(int $user_id, int $receiver_id, int $talk_type, array $records_ids, array $receives): array + { + $isTrue = $this->verifyForward($user_id, $receiver_id, $talk_type, $records_ids); + if (!$isTrue) return []; + + $rows = TalkRecords::whereIn('talk_records.id', $records_ids) + ->get(['talk_records.id', 'talk_records.msg_type', 'talk_records.content']); + + if (empty($rows)) return []; + + $fileArray = $codeArray = []; + foreach ($rows as $val) { + if ($val['msg_type'] == TalkMessageType::FILE_MESSAGE) { + $fileArray[] = $val['id']; + } else if ($val['msg_type'] == TalkMessageType::CODE_MESSAGE) { + $codeArray[] = $val['id']; + } + } + + if (!empty($fileArray)) { + $fileArray = TalkRecordsFile::where('record_id', $fileArray)->get()->keyBy('record_id')->toArray(); + } + + if (!empty($codeArray)) { + $codeArray = TalkRecordsCode::where('record_id', $codeArray)->get()->keyBy('record_id')->toArray(); + } + + $insRecordIds = []; + Db::beginTransaction(); + try { + $file = $code = []; + foreach ($rows as $row) { + foreach ($receives as $receive) { + $res = TalkRecords::create([ + 'talk_type' => $receive['talk_type'], + 'user_id' => $user_id, + 'receiver_id' => $receive['id'], + 'msg_type' => $row['msg_type'], + 'content' => $row['content'], + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]); + + $insRecordIds[] = [ + 'record_id' => $res->id, + 'receiver_id' => $res->receiver_id, + 'talk_type' => $res->talk_type + ]; + + switch ($row['msg_type']) { + case TalkMessageType::FILE_MESSAGE: + $fileInfo = $fileArray[$row['id']]; + $file[] = [ + 'record_id' => $res->id, + 'user_id' => $fileInfo['user_id'], + 'file_source' => $fileInfo['file_source'], + 'file_type' => $fileInfo['file_type'], + 'save_type' => $fileInfo['save_type'], + 'original_name' => $fileInfo['original_name'], + 'file_suffix' => $fileInfo['file_suffix'], + 'file_size' => $fileInfo['file_size'], + 'save_dir' => $fileInfo['save_dir'], + 'created_at' => date('Y-m-d H:i:s') + ]; + break; + case TalkMessageType::CODE_MESSAGE: + $codeInfo = $codeArray[$row['id']]; + $code[] = [ + 'record_id' => $res->id, + 'user_id' => $user_id, + 'code_lang' => $codeInfo['code_lang'], + 'code' => $codeInfo['code'], + 'created_at' => date('Y-m-d H:i:s') + ]; + break; + } + } + } + + $code && TalkRecordsCode::insert($code); + $file && TalkRecordsFile::insert($file); + Db::commit(); + } catch (\Exception $e) { + Db::rollBack(); + return []; + } + + return $insRecordIds; + } +} diff --git a/app/Service/TalkService.php b/app/Service/TalkService.php index ce05726..758fe3d 100644 --- a/app/Service/TalkService.php +++ b/app/Service/TalkService.php @@ -8,10 +8,7 @@ use App\Constants\TalkModeConstant; use App\Event\TalkEvent; use App\Service\Group\GroupMemberService; use App\Service\Message\FormatMessageService; -use Exception; use App\Model\Talk\TalkRecords; -use App\Model\Talk\TalkRecordsCode; -use App\Model\Talk\TalkRecordsFile; use App\Model\Talk\TalkRecordsForward; use App\Traits\PagingTrait; use Hyperf\DbConnection\Db; @@ -31,7 +28,7 @@ class TalkService extends BaseService * @param array $msg_type 消息类型 * @return array */ - public function getChatRecords(int $user_id, int $receiver_id, int $talk_type, int $record_id, $limit = 30, $msg_type = []) + public function getChatRecords(int $user_id, int $receiver_id, int $talk_type, int $record_id, $limit = 30, $msg_type = []): array { $fields = [ 'talk_records.id', @@ -65,9 +62,10 @@ class TalkService extends BaseService }); } else { $rowsSqlObj->where('talk_records.receiver_id', $receiver_id); - $rowsSqlObj->where('talk_records.talk_type', $talk_type); } + $rowsSqlObj->where('talk_records.talk_type', $talk_type); + if ($msg_type) { $rowsSqlObj->whereIn('talk_records.msg_type', $msg_type); } @@ -106,7 +104,7 @@ class TalkService extends BaseService * @param int $record_id 聊天记录ID * @return array */ - public function getForwardRecords(int $user_id, int $record_id) + public function getForwardRecords(int $user_id, int $record_id): array { $result = TalkRecords::where('id', $record_id)->first([ 'id', 'talk_type', 'msg_type', 'user_id', 'receiver_id', 'content', 'is_revoke', 'created_at' @@ -151,7 +149,7 @@ class TalkService extends BaseService * @param array $record_ids 聊天记录ID * @return bool */ - public function removeRecords(int $user_id, int $talk_type, int $receiver_id, array $record_ids) + public function removeRecords(int $user_id, int $talk_type, int $receiver_id, array $record_ids): bool { if ($talk_type == TalkModeConstant::PRIVATE_CHAT) {// 私聊信息 $ids = TalkRecords::whereIn('id', $record_ids)->where(function ($query) use ($user_id, $receiver_id) { @@ -190,7 +188,7 @@ class TalkService extends BaseService * @param int $record_id 聊天记录ID * @return array */ - public function revokeRecord(int $user_id, int $record_id) + public function revokeRecord(int $user_id, int $record_id): array { $result = TalkRecords::where('id', $record_id)->first(['id', 'talk_type', 'user_id', 'receiver_id', 'created_at']); if (!$result) return [false, '消息记录不存在']; @@ -220,221 +218,6 @@ class TalkService extends BaseService return [true, '消息已撤回', $result->toArray()]; } - /** - * 转发消息(单条转发) - * - * @param int $user_id 转发的用户ID - * @param int $record_id 转发消息的记录ID - * @param array $receiver_ids 接受者数组 例如:[['talk_type' => 1,'id' => 3045]...] 二维数组 - * @return array - */ - public function forwardRecords(int $user_id, int $record_id, array $receiver_ids) - { - $msgTypeArray = [ - TalkMessageType::TEXT_MESSAGE, - TalkMessageType::FILE_MESSAGE, - TalkMessageType::CODE_MESSAGE - ]; - - $result = TalkRecords::where('id', $record_id)->whereIn('msg_type', $msgTypeArray)->first(); - if (!$result) return []; - - // 根据消息类型判断用户是否有转发权限 - if ($result->talk_type == TalkModeConstant::PRIVATE_CHAT) { - if ($result->user_id != $user_id && $result->receiver_id != $user_id) { - return []; - } - } else if ($result->talk_type == TalkModeConstant::GROUP_CHAT) { - if (!di()->get(GroupMemberService::class)->isMember($result->receiver_id, $user_id)) { - return []; - } - } - - $fileInfo = $codeBlock = null; - if ($result->msg_type == TalkMessageType::FILE_MESSAGE) { - $fileInfo = TalkRecordsFile::where('record_id', $record_id)->first(); - } else if ($result->msg_type == TalkMessageType::CODE_MESSAGE) { - $codeBlock = TalkRecordsCode::where('record_id', $record_id)->first(); - } - - $insRecordIds = []; - Db::beginTransaction(); - try { - foreach ($receiver_ids as $item) { - $res = TalkRecords::create([ - 'talk_type' => $item['talk_type'], - 'msg_type' => $result->msg_type, - 'user_id' => $user_id, - 'receiver_id' => $item['id'], - 'content' => $result->content, - 'created_at' => date('Y-m-d H:i:s'), - 'updated_at' => date('Y-m-d H:i:s'), - ]); - - if (!$res) { - throw new Exception('插入消息记录失败'); - } - - $insRecordIds[] = [ - 'record_id' => $res->id, - 'receiver_id' => $res->receiver_id, - 'talk_type' => $res->talk_type - ]; - - if ($result->msg_type == TalkMessageType::FILE_MESSAGE) { - if (!TalkRecordsFile::create([ - 'record_id' => $res->id, - 'user_id' => $fileInfo->user_id, - 'file_source' => $fileInfo->file_source, - 'file_type' => $fileInfo->file_type, - 'save_type' => $fileInfo->save_type, - 'original_name' => $fileInfo->original_name, - 'file_suffix' => $fileInfo->file_suffix, - 'file_size' => $fileInfo->file_size, - 'save_dir' => $fileInfo->save_dir, - 'created_at' => date('Y-m-d H:i:s') - ])) { - throw new Exception('插入文件消息记录失败'); - } - } else if ($result->msg_type == TalkMessageType::CODE_MESSAGE) { - if (!TalkRecordsCode::create([ - 'record_id' => $res->id, - 'user_id' => $user_id, - 'code_lang' => $codeBlock->code_lang, - 'code' => $codeBlock->code, - 'created_at' => date('Y-m-d H:i:s') - ])) { - throw new Exception('插入代码消息记录失败'); - } - } - } - - Db::commit(); - } catch (Exception $e) { - Db::rollBack(); - return []; - } - - return $insRecordIds; - } - - /** - * 转发消息(多条合并转发) - * - * @param int $user_id 转发的用户ID - * @param int $receiver_id 当前转发消息的所属者(好友ID或者群聊ID) - * @param int $talk_type 消息来源 1:好友消息 2:群聊消息 - * @param array $records_ids 转发消息的记录ID - * @param array $receive_ids 接受者数组 例如:[['talk_type' => 1,'id' => 3045]...] 二维数组 - * @return array - */ - public function mergeForwardRecords(int $user_id, int $receiver_id, int $talk_type, array $records_ids, array $receive_ids) - { - // 支持转发的消息类型 - $msg_type = [ - TalkMessageType::TEXT_MESSAGE, - TalkMessageType::FILE_MESSAGE, - TalkMessageType::CODE_MESSAGE - ]; - - $sqlObj = TalkRecords::whereIn('id', $records_ids); - - if ($talk_type == TalkModeConstant::PRIVATE_CHAT) { - if (!di()->get(UserFriendService::class)->isFriend($user_id, $receiver_id, true)) return []; - - $sqlObj = $sqlObj->where(function ($query) use ($user_id, $receiver_id) { - $query->where([ - ['user_id', '=', $user_id], - ['receiver_id', '=', $receiver_id] - ])->orWhere([ - ['user_id', '=', $receiver_id], - ['receiver_id', '=', $user_id] - ]); - })->whereIn('msg_type', $msg_type)->where('talk_type', $talk_type)->where('is_revoke', 0); - } else { - if (!di()->get(GroupMemberService::class)->isMember($receiver_id, $user_id)) return []; - - $sqlObj = $sqlObj->where('receiver_id', $receiver_id)->whereIn('msg_type', $msg_type)->where('talk_type', TalkModeConstant::GROUP_CHAT)->where('is_revoke', 0); - } - - $result = $sqlObj->get(); - - // 判断消息记录是否存在 - if (count($result) != count($records_ids)) { - return []; - } - - $rows = TalkRecords::leftJoin('users', 'users.id', '=', 'talk_records.user_id') - ->whereIn('talk_records.id', array_slice($records_ids, 0, 3)) - ->get(['talk_records.msg_type', 'talk_records.content', 'users.nickname']); - - $jsonText = []; - foreach ($rows as $row) { - switch ($row->msg_type) { - case TalkMessageType::TEXT_MESSAGE: - $jsonText[] = [ - 'nickname' => $row->nickname, - 'text' => mb_substr(str_replace(PHP_EOL, "", $row->content), 0, 30) - ]; - break; - case TalkMessageType::FILE_MESSAGE: - $jsonText[] = [ - 'nickname' => $row->nickname, - 'text' => '【文件消息】' - ]; - break; - case TalkMessageType::CODE_MESSAGE: - $jsonText[] = [ - 'nickname' => $row->nickname, - 'text' => '【代码消息】' - ]; - break; - } - } - - $insRecordIds = []; - Db::beginTransaction(); - try { - foreach ($receive_ids as $item) { - $res = TalkRecords::create([ - 'talk_type' => $item['talk_type'], - 'user_id' => $user_id, - 'receiver_id' => $item['id'], - 'msg_type' => TalkMessageType::FORWARD_MESSAGE, - 'created_at' => date('Y-m-d H:i:s'), - 'updated_at' => date('Y-m-d H:i:s'), - ]); - - if (!$res) { - throw new Exception('插入消息失败'); - } - - $insRecordIds[] = [ - 'record_id' => $res->id, - 'receiver_id' => $res->receiver_id, - 'talk_type' => $res->talk_type - ]; - - if (!TalkRecordsForward::create([ - 'record_id' => $res->id, - 'user_id' => $user_id, - 'records_id' => implode(',', $records_ids), - 'text' => json_encode($jsonText), - 'created_at' => date('Y-m-d H:i:s'), - ])) { - throw new Exception('插入转发消息失败'); - } - } - - Db::commit(); - } catch (Exception $e) { - Db::rollBack(); - return []; - } - - return $insRecordIds; - } - /** * 关键词搜索聊天记录 * @@ -446,7 +229,7 @@ class TalkService extends BaseService * @param array $params 查询参数 * @return array */ - public function searchRecords(int $user_id, int $receiver_id, int $talk_type, int $page, int $page_size, array $params) + public function searchRecords(int $user_id, int $receiver_id, int $talk_type, int $page, int $page_size, array $params): array { $fields = [ 'talk_records.id', @@ -474,9 +257,10 @@ class TalkService extends BaseService }); } else { $rowsSqlObj->where('talk_records.receiver_id', $receiver_id); - $rowsSqlObj->where('talk_records.talk_type', $talk_type); } + $rowsSqlObj->where('talk_records.talk_type', $talk_type); + if (isset($params['keywords'])) { $rowsSqlObj->where('talk_records.content', 'like', "%{$params['keywords']}%"); } diff --git a/migrations/2020_11_04_153453_create_talk_records_delete_table.php b/migrations/2020_11_04_153453_create_talk_records_delete_table.php index 29a47a1..eaeb4ab 100644 --- a/migrations/2020_11_04_153453_create_talk_records_delete_table.php +++ b/migrations/2020_11_04_153453_create_talk_records_delete_table.php @@ -22,7 +22,7 @@ class CreateTalkRecordsDeleteTable extends Migration $table->collation = 'utf8_general_ci'; $table->engine = 'InnoDB'; - $table->index(['record_id', 'user_id'], 'idx_record_user_id'); + $table->unique(['record_id', 'user_id'], 'uk_record_id_user_id'); }); $prefix = config('databases.default.prefix');