Compare commits

..

10 Commits

Author SHA1 Message Date
gzydong b4ed7c69a6 fix:优化代码 2022-05-16 19:36:48 +08:00
gzydong 4c82c45324 fix:修改迁移文件 2022-05-05 21:04:07 +08:00
gzydong 5266b79971 fix:撤回消息 2022-05-01 23:56:00 +08:00
gzydong 7cea815888 feat:优化代码 2022-04-12 19:34:20 +08:00
gzydong 71bbfca2dc feat:修改默认配置文件 2022-03-02 20:05:29 +08:00
gzydong f70116e0bc feat:修改默认配置文件 2022-03-02 19:59:56 +08:00
gzydong 807acc480a feat:兼容开发 2022-03-01 21:46:04 +08:00
gzydong 7e9bec8a96 feat:兼容开发 2022-02-16 21:06:57 +08:00
gzydong ccfe1727b6 feat:兼容开发 2022-01-24 22:32:33 +08:00
gzydong 5f3c9d4ca7 feat:兼容开发 2022-01-23 15:13:58 +08:00
31 changed files with 82 additions and 83 deletions

View File

@ -23,6 +23,7 @@ REDIS_DB=0
# 务必改为你自己的字符串
SIMPLE_JWT_SECRET=lumneim
JWT_HEADER_NAME=Authorization
SIMPLE_JWT_TTL=3600
SIMPLE_JWT_PREFIX=jwt
# ---- 项目配置 ----

View File

@ -2,8 +2,6 @@
## 1、简介
这是一个使用Hyperf框架的开发的IM后端应用程序。此项目是 [LumenIM-Serve](https://github.com/gzydong/LumenIM-Serve) 的重构版本。
Lumen-IM 是一个网页版在线即时聊天项目,前端使用 Element-ui + Vue ,后端使用 PHP + Swoole 进行开发。项目后端采用 Hyperf 框架。
- 基于 Swoole WebSocket 服务做消息即时推送
@ -136,6 +134,10 @@ server {
}
```
### 前端地址
https://github.com/gzydong/LumenIM
### 注意事项
1. 请确保 PHP 安装 openssl、redis 扩展

View File

@ -41,13 +41,5 @@ class TestCommand extends HyperfCommand
{
// $repository = di()->get(ExampleRepository::class);
// $repository->where_case2();
// di()->get(RobotService::class)->create([
// 'robot_name' => "登录助手",
// 'describe' => "异地登录助手",
// 'logo' => '',
// 'is_talk' => 0,
// 'type' => 1,
// ]);
}
}

View File

@ -18,22 +18,22 @@ class TalkEventConstant
/**
* 键盘输入事件通知 - 事件名
*/
const EVENT_KEYBOARD = 'event_keyboard';
const EVENT_TALK_KEYBOARD = 'event_talk_keyboard';
/**
* 用户在线状态通知 - 事件名
*/
const EVENT_ONLINE_STATUS = 'event_online_status';
const EVENT_LOGIN = 'event_login';
/**
* 聊天消息撤销通知 - 事件名
*/
const EVENT_REVOKE_TALK = 'event_revoke_talk';
const EVENT_TALK_REVOKE = 'event_talk_revoke';
/**
* 好友申请消息通知 - 事件名
*/
const EVENT_FRIEND_APPLY = 'event_friend_apply';
const EVENT_CONTACT_APPLY = 'event_contact_apply';
/**
* @return array
@ -42,10 +42,10 @@ class TalkEventConstant
{
return [
self::EVENT_TALK => '对话消息通知',
self::EVENT_KEYBOARD => '键盘输入事件通知',
self::EVENT_ONLINE_STATUS => '用户在线状态通知',
self::EVENT_REVOKE_TALK => '聊天消息撤销通知',
self::EVENT_FRIEND_APPLY => '好友申请消息通知'
self::EVENT_TALK_KEYBOARD => '键盘输入事件通知',
self::EVENT_LOGIN => '用户在线状态通知',
self::EVENT_TALK_REVOKE => '聊天消息撤销通知',
self::EVENT_CONTACT_APPLY => '好友申请消息通知'
];
}
}

View File

@ -186,8 +186,7 @@ class AnnexController extends CController
case FileDriveConstant::Local:
return $response->download($this->getFilePath($info->path), $info->original_name);
case FileDriveConstant::Cos:
// 后期添加
break;
return $this->response->fail('文件驱动不存在!');
default:
break;
}

View File

@ -113,7 +113,7 @@ class ArticleController extends CController
'content' => htmlspecialchars($params['content'])
]);
return $id ? $this->response->success(['aid' => $id], '笔记编辑成功...') : $this->response->fail();
return $id ? $this->response->success(['id' => $id], '笔记编辑成功...') : $this->response->fail();
}
/**

View File

@ -84,6 +84,7 @@ class CommonController extends CController
public function EmailCode(SendEmailCode $sendEmailCode)
{
$params = $this->request->inputs(['email']);
$this->validate($params, [
'email' => "required|email"
]);

View File

@ -109,6 +109,10 @@ class ContactController extends CController
'id', 'nickname', 'mobile', 'avatar', 'gender'
]);
if (empty($result)) {
return $this->response->fail("手机号不存在!");
}
return $this->response->success($result);
}

View File

@ -15,6 +15,7 @@ use Psr\Http\Message\ResponseInterface;
/**
* Class EmoticonController
*
* @Controller(prefix="/api/v1/emoticon")
* @Middleware(JWTAuthMiddleware::class)
*

View File

@ -176,12 +176,12 @@ class MessageController extends CController
*/
public function file(Filesystem $filesystem): ResponseInterface
{
$params = $this->request->inputs(['talk_type', 'receiver_id', 'hash_name']);
$params = $this->request->inputs(['talk_type', 'receiver_id', 'upload_id']);
$this->validate($params, [
'talk_type' => 'required|in:1,2',
'receiver_id' => 'required|integer|min:1',
'hash_name' => 'required',
'upload_id' => 'required',
]);
$user_id = $this->uid();
@ -189,7 +189,7 @@ class MessageController extends CController
return $this->response->fail('暂不属于好友关系或群聊成员,无法发送聊天消息!');
}
$file = SplitUpload::where('user_id', $user_id)->where('upload_id', $params['hash_name'])->where('type', 1)->first();
$file = SplitUpload::where('user_id', $user_id)->where('upload_id', $params['upload_id'])->where('type', 1)->first();
if (!$file || empty($file->path)) {
return $this->response->fail('文件不存在...');
}

View File

@ -33,7 +33,6 @@ class RecordsController extends CController
*/
private $talkService;
public function __construct(TalkService $talkService)
{
parent::__construct();
@ -191,8 +190,7 @@ class RecordsController extends CController
return $response->download($this->getFilePath($info->path), $info->original_name);
case FileDriveConstant::Cos:
// 后期添加
break;
return $this->response->fail('文件驱动不存在!');
default:
break;
}

View File

@ -72,10 +72,14 @@ class UploadController extends CController
]);
$data = $this->splitUploadService->create($this->uid(), $params['file_name'], $params['file_size']);
if (empty($data)) {
return $this->response->fail('获取文件拆分信息失败!');
}
$data['hash_name'] = $data["upload_id"];
return $data ? $this->response->success($data) : $this->response->fail('获取文件拆分信息失败!');
return $this->response->success([
"upload_id" => $data["upload_id"],
"split_size" => $data["split_size"],
]);
}
/**
@ -111,11 +115,11 @@ class UploadController extends CController
}
return $this->response->success([
'is_file_merge' => true,
'hash' => $params['upload_id']
'is_merge' => true,
'upload_id' => $params['upload_id']
]);
}
return $this->response->success(['is_file_merge' => false]);
return $this->response->success(['is_merge' => false]);
}
}

View File

@ -54,6 +54,14 @@ class WebSocketController implements OnMessageInterface, OnOpenInterface, OnClos
*/
public function onOpen($server, Request $request): void
{
$server->push($request->fd, json_encode([
"event" => "connect",
"content" => [
"ping_interval" => 20,
"ping_timeout" => 20 * 3,
],
]));
// 当前连接的用户
$user_id = auth('jwt')->user()->getId();
@ -62,11 +70,6 @@ class WebSocketController implements OnMessageInterface, OnOpenInterface, OnClos
// 判断是否存在异地登录
$isOnline = $this->client->isOnlineAll($user_id);
// 若开启单点登录,则主动关闭之前登录的连接
if ($isOnline) {
// todo 预留
}
// 绑定fd与用户关系
$this->client->bind($request->fd, $user_id);
@ -77,7 +80,7 @@ class WebSocketController implements OnMessageInterface, OnOpenInterface, OnClos
}
if (!$isOnline) {
event()->dispatch(new TalkEvent(TalkEventConstant::EVENT_ONLINE_STATUS, [
event()->dispatch(new TalkEvent(TalkEventConstant::EVENT_LOGIN, [
'user_id' => $user_id,
'status' => 1,
]));
@ -130,7 +133,7 @@ class WebSocketController implements OnMessageInterface, OnOpenInterface, OnClos
$isOnline = $this->client->isOnlineAll($user_id);
if ($isOnline) return;
event()->dispatch(new TalkEvent(TalkEventConstant::EVENT_ONLINE_STATUS, [
event()->dispatch(new TalkEvent(TalkEventConstant::EVENT_LOGIN, [
'user_id' => $user_id,
'status' => 0,
]));

View File

@ -5,7 +5,7 @@ namespace App\Exception\Handler;
use App\Cache\Repository\LockRedis;
use App\Constant\ResponseCode;
use App\Templates\MailerTemplate;
use App\Template\MailerTemplate;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;

View File

@ -28,6 +28,8 @@ class SplitUpload extends BaseModel
{
protected $table = 'split_upload';
public $timestamps = true;
protected $fillable = [
'type',
'drive',

View File

@ -50,8 +50,6 @@ class RedisWebsocketSubscribe extends AbstractProcess
//echo PHP_EOL . "chan : $chan , msg : $message";
$data = json_decode($message, true);
var_dump("subscribe ====> {$message}");
$this->handleService->handle($data);
}
}

View File

@ -498,7 +498,7 @@ class ArticleService extends BaseService
return false;
}
$annex_files = $info->annexs()->get(['id', 'article_id', 'save_dir'])->toArray();
$annex_files = $info->annexs()->get(['id', 'article_id', 'path'])->toArray();
//判断笔记是否存在附件,不存在直接删除
if (count($annex_files) == 0) {

View File

@ -52,7 +52,7 @@ class ContactApplyService
// 判断对方是否在线。如果在线发送消息通知
$isOnline = di()->get(SocketClientService::class)->isOnlineAll($friend_id);
if ($isOnline) {
event()->dispatch(new TalkEvent(TalkEventConstant::EVENT_FRIEND_APPLY, [
event()->dispatch(new TalkEvent(TalkEventConstant::EVENT_CONTACT_APPLY, [
'apply_id' => $result->id,
'type' => 1,
]));
@ -103,7 +103,7 @@ class ContactApplyService
// 判断对方是否在线。如果在线发送消息通知
$isOnline = di()->get(SocketClientService::class)->isOnlineAll($info->user_id);
if ($isOnline) {
event()->dispatch(new TalkEvent(TalkEventConstant::EVENT_FRIEND_APPLY, [
event()->dispatch(new TalkEvent(TalkEventConstant::EVENT_CONTACT_APPLY, [
'apply_id' => $apply_id,
'type' => 2,
]));

View File

@ -282,7 +282,6 @@ class GroupService extends BaseService
Db::commit();
} catch (Exception $e) {
Db::rollBack();
var_dump($e->getMessage());
return false;
}

View File

@ -22,8 +22,8 @@ class ReceiveHandleService
// 消息事件绑定
const EVENTS = [
TalkEventConstant::EVENT_TALK => 'onTalk',
TalkEventConstant::EVENT_KEYBOARD => 'onKeyboard',
TalkEventConstant::EVENT_TALK => 'onTalk',
TalkEventConstant::EVENT_TALK_KEYBOARD => 'onKeyboard',
];
/**
@ -79,7 +79,7 @@ class ReceiveHandleService
*/
public function onKeyboard($server, Frame $frame, $data)
{
event()->dispatch(new TalkEvent(TalkEventConstant::EVENT_KEYBOARD, [
event()->dispatch(new TalkEvent(TalkEventConstant::EVENT_TALK_KEYBOARD, [
'sender_id' => (int)$data['sender_id'],
'receiver_id' => (int)$data['receiver_id'],
]));

View File

@ -12,7 +12,6 @@ use App\Model\User;
use App\Model\Contact\ContactApply;
use App\Repository\Contact\ContactRepository;
use App\Service\SocketClientService;
use App\Service\UserService;
class SubscribeHandleService
{
@ -26,16 +25,16 @@ class SubscribeHandleService
TalkEventConstant::EVENT_TALK => 'onConsumeTalk',
// 键盘输入事件
TalkEventConstant::EVENT_KEYBOARD => 'onConsumeKeyboard',
TalkEventConstant::EVENT_TALK_KEYBOARD => 'onConsumeKeyboard',
// 用户在线状态事件
TalkEventConstant::EVENT_ONLINE_STATUS => 'onConsumeOnlineStatus',
TalkEventConstant::EVENT_LOGIN => 'onConsumeOnlineStatus',
// 聊天消息推送事件
TalkEventConstant::EVENT_REVOKE_TALK => 'onConsumeRevokeTalk',
TalkEventConstant::EVENT_TALK_REVOKE => 'onConsumeRevokeTalk',
// 好友申请相关事件
TalkEventConstant::EVENT_FRIEND_APPLY => 'onConsumeFriendApply'
TalkEventConstant::EVENT_CONTACT_APPLY => 'onConsumeFriendApply'
];
/**
@ -49,15 +48,7 @@ class SubscribeHandleService
}
/**
* @param array $data 数据
* <pre>
* [
* 'uuid' => '',
* 'event' => '',
* 'data' => '',
* 'options' => ''
* ];
* </pre>
* @param array $data 数据 ['uuid' => '','event' => '','data' => '','options' => ''];
*/
public function handle(array $data)
{
@ -142,7 +133,7 @@ class SubscribeHandleService
{
$fds = $this->clientService->findUserFds($data['data']['receiver_id']);
$this->push($fds, $this->toJson(TalkEventConstant::EVENT_KEYBOARD, $data['data']));
$this->push($fds, $this->toJson(TalkEventConstant::EVENT_TALK_KEYBOARD, $data['data']));
}
/**
@ -166,7 +157,7 @@ class SubscribeHandleService
$fds = array_unique(array_merge(...$fds));
$this->push($fds, $this->toJson(TalkEventConstant::EVENT_ONLINE_STATUS, [
$this->push($fds, $this->toJson(TalkEventConstant::EVENT_LOGIN, [
'user_id' => $user_id,
'status' => $status
]));
@ -197,7 +188,7 @@ class SubscribeHandleService
if (!$fds) return;
$this->push($fds, $this->toJson(TalkEventConstant::EVENT_REVOKE_TALK, [
$this->push($fds, $this->toJson(TalkEventConstant::EVENT_TALK_REVOKE, [
'talk_type' => $record->talk_type,
'sender_id' => $record->user_id,
'receiver_id' => $record->receiver_id,
@ -243,7 +234,7 @@ class SubscribeHandleService
'mobile' => $friendInfo->mobile,
];
$this->push(array_unique($fds), $this->toJson(TalkEventConstant::EVENT_FRIEND_APPLY, $msg));
$this->push(array_unique($fds), $this->toJson(TalkEventConstant::EVENT_CONTACT_APPLY, $msg));
}
private function toJson(string $event, array $data): string

View File

@ -23,6 +23,7 @@ class RobotService
try {
$user = User::create([
'mobile' => '100' . mt_rand(1000, 9999) . mt_rand(1000, 9999),
"nickname" => "登录助手",
'password' => HashHelper::make(Str::random(10)),
'is_robot' => 1
]);

View File

@ -196,7 +196,7 @@ class TalkService extends BaseService
if (!$result) return [false, '消息记录不存在'];
// 判断是否在两分钟之内撤回消息超过2分钟不能撤回消息
if ((time() - strtotime($result->created_at) > 120)) {
if ((time() - $result->created_at->timestamp > 120)) {
return [false, '已超过有效的撤回时间', []];
}
@ -213,7 +213,7 @@ class TalkService extends BaseService
$result->is_revoke = 1;
$result->save();
event()->dispatch(new TalkEvent(TalkEventConstant::EVENT_REVOKE_TALK, [
event()->dispatch(new TalkEvent(TalkEventConstant::EVENT_TALK_REVOKE, [
'record_id' => $result->id
]));

View File

@ -39,10 +39,9 @@ class Response
$resp = [
"code" => ResponseCode::SUCCESS,
"message" => $message,
"data" => $data,
];
if ($data) $resp["data"] = $data;
return $this->response->json($resp);
}

View File

@ -3,7 +3,7 @@ declare(strict_types=1);
namespace App\Support;
use App\Templates\MailerTemplate;
use App\Template\MailerTemplate;
class SendEmailCode
{

View File

@ -1,7 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Templates;
namespace App\Template;
abstract class BaseTemplate
{

View File

@ -1,7 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Templates;
namespace App\Template;
use Throwable;

View File

@ -15,6 +15,7 @@ class CreateArticleAnnexTable extends Migration
$table->unsignedBigInteger('id', true)->comment('文件ID');
$table->unsignedInteger('user_id')->unsigned()->comment('上传文件的用户ID');
$table->unsignedInteger('article_id')->default(0)->comment('笔记ID');
$table->unsignedInteger('drive')->unsigned()->default(1)->comment('文件驱动[1:local;2:cos;]');
$table->string('suffix', 10)->default('')->comment('文件后缀名');
$table->unsignedBigInteger('size')->default(0)->comment('文件大小(单位字节)');
$table->string('path', 500)->nullable(false)->default('')->comment('文件保存地址(相对地址)');

View File

@ -14,8 +14,9 @@ class CreateSplitUploadTable extends Migration
Schema::create('split_upload', function (Blueprint $table) {
$table->unsignedInteger('id', true)->comment('临时文件ID');
$table->unsignedTinyInteger('type')->default(2)->comment('数据类型[1:合并文件;2:拆分文件]');
$table->unsignedInteger('drive')->unsigned()->default(1)->comment('文件驱动[1:local;2:cos;]');
$table->unsignedInteger('user_id')->default(0)->comment('上传的用户ID');
$table->string('upload_id', 32)->default('')->comment('上传文件ID');
$table->string('upload_id', 100)->default('')->comment('上传文件ID');
$table->string('original_name', 100)->default('')->comment('原文件名');
$table->unsignedTinyInteger('split_index')->default(0)->comment('当前索引块');
$table->unsignedTinyInteger('split_num')->default(0)->comment('总上传索引块');
@ -23,8 +24,9 @@ class CreateSplitUploadTable extends Migration
$table->string('file_ext', 10)->default('')->comment('文件后缀名');
$table->unsignedInteger('file_size')->default(0)->comment('临时文件大小');
$table->unsignedTinyInteger('is_delete')->default(0)->comment('文件是否已被删除[0:否;1:是]');
$table->unsignedInteger('created_at')->nullable(true)->comment('创建时间');
$table->unsignedInteger('updated_at')->nullable(true)->comment('更新时间');
$table->json('attr')->nullable(false)->comment('额外参数Json');
$table->dateTime('created_at')->nullable(true)->comment('创建时间');
$table->dateTime('updated_at')->nullable(true)->comment('更新时间');
$table->charset = 'utf8';
$table->collation = 'utf8_general_ci';

View File

@ -20,6 +20,7 @@ class CreateGroupMemberTable extends Migration
$table->tinyInteger('is_quit')->default(0)->comment('是否退群[0:否;1:是;]');
$table->string('user_card', 20)->default('')->comment('群名片');
$table->dateTime('created_at')->nullable()->comment('入群时间');
$table->dateTime('updated_at')->nullable()->comment('更新时间');
$table->dateTime('deleted_at')->nullable()->comment('退群时间');
$table->charset = 'utf8';

View File

@ -18,7 +18,7 @@ class Initialize extends Seeder
*/
public function run()
{
if (User::count() > 0) {
if (User::count() > 1) {
echo "数据库已存在数据,不能执行初始化数据脚本...\n";
return;
}
@ -37,14 +37,14 @@ class Initialize extends Seeder
User::insert($users);
$defaultArticleClass = [];
$usersFriends = [];
foreach (User::all() as $user) {
$defaultArticleClass[] = [
'user_id' => $user->id,
'class_name' => '我的笔记',
'sort' => 1,
'is_default' => 1,
'created_at' => time(),
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
];
}
@ -67,7 +67,7 @@ class Initialize extends Seeder
Contact::insert($friends);
$service = new \App\Service\TalkSessionService();
$service = di()->get(\App\Service\TalkSessionService::class);
foreach ($list as $item) {
$service->create($item->user_id, $item->friend_id, \App\Constant\TalkModeConstant::PRIVATE_CHAT);
}