diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php
index 1129aee4..e2c8ce05 100755
--- a/app/Http/Controllers/Api/DialogController.php
+++ b/app/Http/Controllers/Api/DialogController.php
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api;
use App\Models\User;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
+use App\Models\WebSocketDialogMsgRead;
use App\Models\WebSocketDialogUser;
use App\Module\Base;
use Request;
@@ -44,6 +45,10 @@ class DialogController extends AbstractController
}
//
$list = WebSocketDialogMsg::whereDialogId($dialog_id)->orderByDesc('id')->paginate(Base::getPaginate(100, 50));
+ $list->transform(function (WebSocketDialogMsg $item) use ($user) {
+ $item->r = $item->userid === $user->userid ? null : WebSocketDialogMsgRead::whereMsgId($item->id)->whereUserid($user->userid)->first();
+ return $item;
+ });
//
return Base::retSuccess('success', $list);
}
@@ -94,4 +99,128 @@ class DialogController extends AbstractController
return WebSocketDialogMsg::addUserMsg($dialog_id, 'text', $msg, $user->userid, $extra_int, $extra_str);
}
}
+
+ /**
+ * {post}文件上传
+ *
+ * @apiParam {Number} dialog_id 对话ID
+ * @apiParam {Number} [extra_int] 额外参数(数字)
+ * @apiParam {String} [extra_str] 额外参数(字符)
+ * @apiParam {String} [filename] post-文件名称
+ * @apiParam {String} [image64] post-base64图片(二选一)
+ * @apiParam {File} [files] post-文件对象(二选一)
+ */
+ public function msg__sendfile()
+ {
+ $user = User::authE();
+ if (Base::isError($user)) {
+ return $user;
+ } else {
+ $user = User::IDE($user['data']);
+ }
+ //
+ $dialog_id = Base::getPostInt('dialog_id');
+ $extra_int = Base::getPostInt('extra_int');
+ $extra_str = Base::getPostValue('extra_str');
+ //
+ if (!WebSocketDialogUser::whereDialogId($dialog_id)->whereUserid($user->userid)->exists()) {
+ return Base::retError('不在成员列表内');
+ }
+ $dialog = WebSocketDialog::whereId($dialog_id)->first();
+ if (empty($dialog)) {
+ return Base::retError('对话不存在或已被删除');
+ }
+ //
+ $path = "uploads/chat/" . $user->userid . "/";
+ $image64 = Base::getPostValue('image64');
+ $fileName = Base::getPostValue('filename');
+ if ($image64) {
+ $data = Base::image64save([
+ "image64" => $image64,
+ "path" => $path,
+ "fileName" => $fileName,
+ ]);
+ } else {
+ $data = Base::upload([
+ "file" => Request::file('files'),
+ "type" => 'file',
+ "path" => $path,
+ "fileName" => $fileName,
+ ]);
+ }
+ //
+ if (Base::isError($data)) {
+ return Base::retError($data['msg']);
+ } else {
+ $fileData = $data['data'];
+ $fileData['thumb'] = $fileData['thumb'] ?: 'images/ext/file.png';
+ switch ($fileData['ext']) {
+ case "docx":
+ $fileData['thumb'] = 'images/ext/doc.png';
+ break;
+ case "xlsx":
+ $fileData['thumb'] = 'images/ext/xls.png';
+ break;
+ case "pptx":
+ $fileData['thumb'] = 'images/ext/ppt.png';
+ break;
+ case "ai":
+ case "avi":
+ case "bmp":
+ case "cdr":
+ case "doc":
+ case "eps":
+ case "gif":
+ case "mov":
+ case "mp3":
+ case "mp4":
+ case "pdf":
+ case "ppt":
+ case "pr":
+ case "psd":
+ case "rar":
+ case "svg":
+ case "tif":
+ case "txt":
+ case "xls":
+ case "zip":
+ $fileData['thumb'] = 'images/ext/' . $fileData['ext'] . '.png';
+ break;
+ }
+ //
+ $msg = $fileData;
+ $msg['size'] *= 1024;
+ //
+ if ($dialog->type == 'group') {
+ return WebSocketDialogMsg::addGroupMsg($dialog_id, 'file', $msg, $user->userid, $extra_int, $extra_str);
+ } else {
+ return WebSocketDialogMsg::addUserMsg($dialog_id, 'file', $msg, $user->userid, $extra_int, $extra_str);
+ }
+ }
+ }
+
+ /**
+ * 获取消息阅读情况
+ *
+ * @apiParam {Number} msg_id 消息ID(需要是消息的发送人)
+ */
+ public function msg__readlist()
+ {
+ $user = User::authE();
+ if (Base::isError($user)) {
+ return $user;
+ } else {
+ $user = User::IDE($user['data']);
+ }
+ //
+ $msg_id = intval(Request::input('msg_id'));
+ //
+ $msg = WebSocketDialogMsg::whereId($msg_id)->whereUserid($user->userid)->first();
+ if (empty($msg)) {
+ return Base::retError('不是发送人');
+ }
+ //
+ $read = WebSocketDialogMsgRead::whereMsgId($msg_id)->get();
+ return Base::retSuccess('success', $read ?: []);
+ }
}
diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php
index cd3a95c8..5e0bb090 100644
--- a/app/Http/Middleware/VerifyCsrfToken.php
+++ b/app/Http/Middleware/VerifyCsrfToken.php
@@ -23,5 +23,8 @@ class VerifyCsrfToken extends Middleware
// 添加任务
'api/project/task/add/',
+
+ // 聊天发文件
+ 'api/dialog/msg/sendfile/',
];
}
diff --git a/app/Models/WebSocketDialogMsg.php b/app/Models/WebSocketDialogMsg.php
index 5b82717a..78a235d1 100644
--- a/app/Models/WebSocketDialogMsg.php
+++ b/app/Models/WebSocketDialogMsg.php
@@ -4,7 +4,9 @@ namespace App\Models;
use App\Module\Base;
use App\Tasks\PushTask;
+use App\Tasks\WebSocketDialogMsgTask;
use Carbon\Carbon;
+use Hhxsv5\LaravelS\Swoole\Task\Task;
/**
* Class WebSocketDialogMsg
@@ -15,11 +17,13 @@ use Carbon\Carbon;
* @property int|null $userid 发送会员ID
* @property string|null $type 消息类型
* @property array|mixed $msg 详细消息
- * @property int|null $read 是否已读
+ * @property int|null $read 已阅数量
+ * @property int|null $send 发送数量
* @property int|null $extra_int 额外数字参数
* @property string|null $extra_str 额外字符参数
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
+ * @property-read int|mixed $percentage
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg query()
@@ -30,6 +34,7 @@ use Carbon\Carbon;
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereMsg($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereRead($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereSend($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereType($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereUserid($value)
@@ -37,12 +42,32 @@ use Carbon\Carbon;
*/
class WebSocketDialogMsg extends AbstractModel
{
+ protected $appends = [
+ 'percentage',
+ ];
+
protected $hidden = [
'updated_at',
];
/**
- * 消息
+ * 阅读占比
+ * @return int|mixed
+ */
+ public function getPercentageAttribute()
+ {
+ if (!isset($this->attributes['percentage'])) {
+ if ($this->read > $this->send || empty($this->send)) {
+ $this->attributes['percentage'] = 100;
+ } else {
+ $this->attributes['percentage'] = intval($this->read / $this->send * 100);
+ }
+ }
+ return $this->attributes['percentage'];
+ }
+
+ /**
+ * 消息格式化
* @param $value
* @return array|mixed
*/
@@ -51,27 +76,54 @@ class WebSocketDialogMsg extends AbstractModel
if (is_array($value)) {
return $value;
}
- return Base::json2array($value);
+ $value = Base::json2array($value);
+ if ($this->type === 'file') {
+ $value['type'] = in_array($value['ext'], ['jpg', 'jpeg', 'png', 'gif']) ? 'img' : 'file';
+ $value['url'] = Base::fillUrl($value['path']);
+ $value['thumb'] = Base::fillUrl($value['thumb']);
+ }
+ return $value;
}
/**
* 标记已送达 同时 告诉发送人已送达
- * @return $this
+ * @param $userid
+ * @return bool
*/
- public function readSuccess()
+ public function readSuccess($userid)
{
- if (empty($this->read)) {
- $this->read = 1;
- $this->save();
- PushTask::push([
- 'userid' => $this->userid,
- 'msg' => [
- 'type' => 'dialog',
- 'data' => $this->toArray(),
- ]
- ]);
+ if (empty($userid)) {
+ return false;
}
- return $this;
+ $result = self::transaction(function() use ($userid) {
+ $msgRead = WebSocketDialogMsgRead::whereMsgId($this->id)->whereUserid($userid)->lockForUpdate()->first();
+ if (empty($msgRead)) {
+ $msgRead = WebSocketDialogMsgRead::createInstance([
+ 'msg_id' => $this->id,
+ 'userid' => $userid,
+ ]);
+ try {
+ $msgRead->saveOrFail();
+ $this->send = WebSocketDialogMsgRead::whereMsgId($this->id)->count();
+ $this->save();
+ } catch (\Throwable $e) {
+ $msgRead = $msgRead->first();
+ }
+ }
+ if ($msgRead && !$msgRead->read_at) {
+ $msgRead->read_at = Carbon::now();
+ $msgRead->save();
+ $this->increment('read');
+ PushTask::push([
+ 'userid' => $this->userid,
+ 'msg' => [
+ 'type' => 'dialog',
+ 'data' => $this->toArray(),
+ ]
+ ]);
+ }
+ });
+ return Base::isSuccess($result);
}
/**
@@ -101,19 +153,14 @@ class WebSocketDialogMsg extends AbstractModel
}
$dialog->last_at = Carbon::now();
$dialog->save();
+ $userids = WebSocketDialogUser::whereDialogId($dialog->id)->where('userid', '!=', $dialogMsg->userid)->pluck('userid')->toArray();
+ $dialogMsg->send = count($userids);
$dialogMsg->dialog_id = $dialog->id;
$dialogMsg->save();
//
- $userids = WebSocketDialogUser::whereDialogId($dialog->id)->where('userid', '!=', $dialogMsg->userid)->pluck('userid')->toArray();
- if ($userids) {
- PushTask::push([
- 'userid' => $userids,
- 'msg' => [
- 'type' => 'dialog',
- 'data' => $dialogMsg->toArray(),
- ]
- ]);
- }
+ $task = new WebSocketDialogMsgTask($userids, $dialogMsg->toArray());
+ Task::deliver($task);
+ //
return Base::retSuccess('发送成功', $dialogMsg);
});
}
@@ -146,16 +193,13 @@ class WebSocketDialogMsg extends AbstractModel
}
$dialog->last_at = Carbon::now();
$dialog->save();
+ $dialogMsg->send = 1;
$dialogMsg->dialog_id = $dialog->id;
$dialogMsg->save();
//
- PushTask::push([
- 'userid' => $userid,
- 'msg' => [
- 'type' => 'dialog',
- 'data' => $dialogMsg->toArray(),
- ]
- ]);
+ $task = new WebSocketDialogMsgTask($userid, $dialogMsg->toArray());
+ Task::deliver($task);
+ //
return Base::retSuccess('发送成功', $dialogMsg);
});
}
diff --git a/app/Models/WebSocketDialogMsgRead.php b/app/Models/WebSocketDialogMsgRead.php
new file mode 100644
index 00000000..caa4ff95
--- /dev/null
+++ b/app/Models/WebSocketDialogMsgRead.php
@@ -0,0 +1,29 @@
+timestamps = false;
+ }
+}
diff --git a/app/Module/Base.php b/app/Module/Base.php
index e60e6a1b..cb5ba5b3 100755
--- a/app/Module/Base.php
+++ b/app/Module/Base.php
@@ -744,7 +744,11 @@ class Base
) {
return $str;
} else {
- return url($str);
+ try {
+ return url($str);
+ } catch (\Throwable $e) {
+ return self::getSchemeAndHost() . "/" . $str;
+ }
}
}
@@ -761,7 +765,22 @@ class Base
}
return $str;
}
- return Base::leftDelete($str, url('') . '/');
+ try {
+ $find = url('');
+ } catch (\Throwable $e) {
+ $find = self::getSchemeAndHost();
+ }
+ return Base::leftDelete($str, $find . '/');
+ }
+
+ /**
+ * 获取主地址
+ * @return string 如:http://127.0.0.1:8080
+ */
+ public static function getSchemeAndHost()
+ {
+ $scheme = isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == '443' ? 'https://' : 'http://';
+ return $scheme.($_SERVER['HTTP_HOST'] ?? '');
}
/**
@@ -1922,12 +1941,16 @@ class Base
public static function getPostValue($key, $default = null)
{
- return self::newTrim(self::getContentValue($key, $default));
+ $value = self::newTrim(self::getContentValue($key, $default));
+ if (empty($value)) {
+ $value = self::newTrim(Request::post($key, $default));
+ }
+ return $value;
}
public static function getPostInt($key, $default = null)
{
- return intval(self::getContentValue($key, $default));
+ return intval(self::getPostValue($key, $default));
}
/**
@@ -2301,7 +2324,7 @@ class Base
$array['thumb'] = $array['path'];
if ($param['autoThumb'] === "false") $param['autoThumb'] = false;
if ($param['autoThumb'] !== false) {
- if (Base::imgThumb($array['file'], $array['file'] . "_thumb.jpg", 180, 0)) {
+ if (Base::imgThumb($array['file'], $array['file'] . "_thumb.jpg", 320, 0)) {
$array['thumb'] .= "_thumb.jpg";
}
}
diff --git a/app/Services/WebSocketService.php b/app/Services/WebSocketService.php
index f6e5daa8..8abcd4fd 100644
--- a/app/Services/WebSocketService.php
+++ b/app/Services/WebSocketService.php
@@ -122,7 +122,7 @@ class WebSocketService implements WebSocketHandlerInterface
*/
case 'readMsg':
$dialogMsg = WebSocketDialogMsg::whereId(intval($data['id']))->first();
- $dialogMsg && $dialogMsg->readSuccess();
+ $dialogMsg && $dialogMsg->readSuccess($this->getUserid($frame->fd));
return;
}
//
@@ -178,4 +178,14 @@ class WebSocketService implements WebSocketHandlerInterface
{
WebSocket::whereFd($fd)->delete();
}
+
+ /**
+ * 根据fd获取会员ID
+ * @param $fd
+ * @return int
+ */
+ private function getUserid($fd)
+ {
+ return intval(WebSocket::whereFd($fd)->value('userid'));
+ }
}
diff --git a/app/Tasks/WebSocketDialogMsgTask.php b/app/Tasks/WebSocketDialogMsgTask.php
new file mode 100644
index 00000000..43763185
--- /dev/null
+++ b/app/Tasks/WebSocketDialogMsgTask.php
@@ -0,0 +1,71 @@
+userid = $userid;
+ $this->dialogMsgArray = $dialogMsgArray;
+ }
+
+ /**
+ * @throws \Throwable
+ */
+ public function start()
+ {
+ $userids = is_array($this->userid) ? $this->userid : [$this->userid];
+ $msgId = intval($this->dialogMsgArray['id']);
+ $send = intval($this->dialogMsgArray['send']);
+ if (empty($userids) || empty($msgId)) {
+ return;
+ }
+ $pushIds = [];
+ foreach ($userids AS $userid) {
+ $msgRead = WebSocketDialogMsgRead::createInstance([
+ 'msg_id' => $msgId,
+ 'userid' => $userid,
+ ]);
+ try {
+ $msgRead->saveOrFail();
+ $pushIds[] = $userid;
+ } catch (\Throwable $e) {
+ //
+ }
+ }
+ // 更新已发送数量
+ if ($send != count($pushIds)) {
+ $send = WebSocketDialogMsgRead::whereMsgId($msgId)->count();
+ WebSocketDialogMsg::whereId($msgId)->update([ 'send' => $send ]);
+ $this->dialogMsgArray['send'] = $send;
+ }
+ // 开始推送消息
+ if ($pushIds) {
+ PushTask::push([
+ 'userid' => $pushIds,
+ 'msg' => [
+ 'type' => 'dialog',
+ 'data' => $this->dialogMsgArray,
+ ]
+ ]);
+ }
+ }
+}
diff --git a/resources/assets/js/components/UserAvatar.vue b/resources/assets/js/components/UserAvatar.vue
index 07fb1d6b..f9d0c689 100755
--- a/resources/assets/js/components/UserAvatar.vue
+++ b/resources/assets/js/components/UserAvatar.vue
@@ -1,14 +1,19 @@
{{$L('昵称')}}: {{user.nickname}} {{$L('职位/职称')}}: {{user.profession || '-'}}