diff --git a/app/Amqp/Consumer/ChatMessageConsumer.php b/app/Amqp/Consumer/ChatMessageConsumer.php index d380c14..ce12af7 100644 --- a/app/Amqp/Consumer/ChatMessageConsumer.php +++ b/app/Amqp/Consumer/ChatMessageConsumer.php @@ -75,7 +75,7 @@ class ChatMessageConsumer extends ConsumerMessage } } - return Result::ACK; + return Result::NACK; } /** diff --git a/app/Controller/Api/V1/ArticleController.php b/app/Controller/Api/V1/ArticleController.php index 5fad594..51f43c7 100644 --- a/app/Controller/Api/V1/ArticleController.php +++ b/app/Controller/Api/V1/ArticleController.php @@ -3,6 +3,7 @@ namespace App\Controller\Api\V1; use App\Service\ArticleService; +use App\Support\RedisLock; use Hyperf\Di\Annotation\Inject; use Hyperf\HttpServer\Annotation\Controller; use Hyperf\HttpServer\Annotation\RequestMapping; @@ -32,10 +33,8 @@ class ArticleController extends CController */ public function getArticleClass() { - $user_id = $this->uid(); - return $this->response->success( - $this->articleService->getUserClass($user_id) + $this->articleService->getUserClass($this->uid()) ); } @@ -46,10 +45,8 @@ class ArticleController extends CController */ public function getArticleTags() { - $user_id = $this->uid(); - return $this->response->success( - $this->articleService->getUserTags($user_id) + $this->articleService->getUserTags($this->uid()) ); } @@ -100,12 +97,12 @@ class ArticleController extends CController 'article_id' => 'required|integer', ]); - $data = $this->articleService->getArticleDetail( - $this->request->input('article_id'), - $this->uid() + return $this->response->success( + $this->articleService->getArticleDetail( + $this->request->input('article_id'), + $this->uid() + ) ); - - return $this->response->success($data); } /** @@ -115,7 +112,18 @@ class ArticleController extends CController */ public function editArticleClass() { + $params = $this->request->all(); + $this->validate($params, [ + 'class_id' => 'required|integer', + 'class_name' => 'required', + ]); + $class_id = $this->articleService->editArticleClass($this->uid(), $params['class_id'], $params['class_name']); + if (!$class_id) { + return $this->response->fail('笔记分类编辑失败...'); + } + + return $this->response->success(['id' => $class_id]); } /** @@ -125,7 +133,16 @@ class ArticleController extends CController */ public function delArticleClass() { + $params = $this->request->all(); + $this->validate($params, [ + 'class_id' => 'required|integer' + ]); + if (!$this->articleService->delArticleClass($this->uid(), $params['class_id'])) { + return $this->response->fail('笔记分类删除失败...'); + } + + return $this->response->success([], '笔记分类删除成功...'); } /** @@ -135,7 +152,27 @@ class ArticleController extends CController */ public function articleClassSort() { + $params = $this->request->all(); + $this->validate($params, [ + 'class_id' => 'required|integer', + 'sort_type' => 'required|in:1,2' + ]); + $lockKey = "article_class_sort:{$params['class_id']}_{$params['sort_type']}"; + + // 获取Redis锁 + if (RedisLock::lock($lockKey, 0, 5)) { + $isTrue = $this->articleService->articleClassSort($this->uid(), $params['class_id'], $params['sort_type']); + + // 释放Redis锁 + RedisLock::release($lockKey, 0); + } else { + $isTrue = false; + } + + return $isTrue + ? $this->response->success([], '排序完成...') + : $this->response->fail('排序失败...'); } /** @@ -145,7 +182,17 @@ class ArticleController extends CController */ public function mergeArticleClass() { + $params = $this->request->all(); + $this->validate($params, [ + 'class_id' => 'required|integer', + 'toid' => 'required|integer' + ]); + $isTrue = $this->articleService->mergeArticleClass($this->uid(), $params['class_id'], $params['toid']); + + return $isTrue + ? $this->response->success([], '合并完成...') + : $this->response->fail('合并完成...'); } /** @@ -155,7 +202,21 @@ class ArticleController extends CController */ public function editArticleTags() { + $params = $this->request->all(); + $this->validate($params, [ + 'tag_id' => 'required|integer|min:0', + 'tag_name' => 'required' + ]); + $id = $this->articleService->editArticleTag( + $this->uid(), + $this->request->post('tag_id', 0), + $this->request->post('tag_name', '') + ); + + return $id + ? $this->response->success(['id' => $id]) + : $this->response->fail('笔记标签编辑失败...'); } /** @@ -165,17 +226,46 @@ class ArticleController extends CController */ public function delArticleTags() { + $params = $this->request->all(); + $this->validate($params, [ + 'tag_id' => 'required|integer|min:0' + ]); + $isTrue = $this->articleService->delArticleTags($this->uid(), $params['tag_id']); + + return $isTrue + ? $this->response->success([], '笔记标签删除完成...') + : $this->response->fail('笔记标签删除失败...'); } /** - * 编辑笔记信息 + * 添加或编辑笔记 * * @RequestMapping(path="edit-article", methods="post") */ public function editArticle() { + $params = $this->request->all(); + $this->validate($params, [ + 'article_id' => 'required|integer|min:0', + 'class_id' => 'required|integer|min:0', + 'title' => 'required|max:255', + 'content' => 'required', + 'md_content' => 'required', + ]); + $id = $this->articleService->editArticle($this->uid(), $params['article_id'], [ + 'title' => $params['title'], + 'abstract' => mb_substr(strip_tags($params['content']), 0, 200), + 'class_id' => $params['class_id'], + 'image' => get_html_images($params['content']), + 'md_content' => htmlspecialchars($params['md_content']), + 'content' => htmlspecialchars($params['content']) + ]); + + return $id + ? $this->response->success(['aid' => $id], '笔记编辑成功...') + : $this->response->fail('笔记编辑失败...', ['id' => null]); } /** @@ -185,17 +275,34 @@ class ArticleController extends CController */ public function deleteArticle() { + $params = $this->request->all(); + $this->validate($params, [ + 'article_id' => 'required|integer|min:0' + ]); + $isTrue = $this->articleService->updateArticleStatus($this->uid(), $params['article_id'], 2); + + return $isTrue + ? $this->response->success([], '笔记删除成功...') + : $this->response->fail('笔记删除失败...'); } /** - * 恢复笔记 + * 恢复删除笔记 * * @RequestMapping(path="recover-article", methods="post") */ public function recoverArticle() { + $params = $this->request->all(); + $this->validate($params, [ + 'article_id' => 'required|integer|min:0' + ]); + $isTrue = $this->articleService->updateArticleStatus($this->uid(), $params['article_id'], 1); + return $isTrue + ? $this->response->success([], '笔记恢复成功...') + : $this->response->fail('笔记恢复失败...'); } /** @@ -208,7 +315,6 @@ class ArticleController extends CController } - /** * 移动笔记至指定分类 * @@ -216,7 +322,21 @@ class ArticleController extends CController */ public function moveArticle() { + $params = $this->request->all(); + $this->validate($params, [ + 'article_id' => 'required|integer|min:0', + 'class_id' => 'required|integer|min:0' + ]); + $isTrue = $this->articleService->moveArticle( + $this->uid(), + $params['article_id'], + $params['class_id'] + ); + + return $isTrue + ? $this->response->success([], '笔记移动成功...') + : $this->response->fail('笔记移动失败...'); } /** @@ -226,7 +346,21 @@ class ArticleController extends CController */ public function setAsteriskArticle() { + $params = $this->request->all(); + $this->validate($params, [ + 'article_id' => 'required|integer|min:0', + 'type' => 'required|in:1,2' + ]); + $isTrue = $this->articleService->setAsteriskArticle( + $this->uid(), + $params['article_id'], + $params['type'] + ); + + return $isTrue + ? $this->response->success([], '笔记标记成功...') + : $this->response->fail('笔记标记失败...'); } /** @@ -236,7 +370,16 @@ class ArticleController extends CController */ public function updateArticleTag() { + $params = $this->request->all(); + $this->validate($params, [ + 'article_id' => 'required|integer|min:0', + 'tags' => 'required|array' + ]); + $isTrue = $this->articleService->updateArticleTag($this->uid(), $params['article_id'], $params['tags']); + return $isTrue + ? $this->response->success([], 'success...') + : $this->response->fail('编辑失败...'); } /** @@ -246,7 +389,16 @@ class ArticleController extends CController */ public function foreverDelArticle() { + $params = $this->request->all(); + $this->validate($params, [ + 'article_id' => 'required|integer|min:0' + ]); + $isTrue = $this->articleService->foreverDelArticle($this->uid(), $params['article_id']); + + return $isTrue + ? $this->response->success([], '笔记删除成功...') + : $this->response->fail('笔记删除失败...'); } /** @@ -266,7 +418,16 @@ class ArticleController extends CController */ public function deleteArticleAnnex() { + $params = $this->request->all(); + $this->validate($params, [ + 'annex_id' => 'required|integer|min:0' + ]); + $isTrue = $this->articleService->updateArticleAnnexStatus($this->uid(), $params['annex_id'], 2); + + return $isTrue + ? $this->response->success([], '笔记附件删除成功...') + : $this->response->fail('笔记附件删除失败...'); } /** @@ -276,7 +437,16 @@ class ArticleController extends CController */ public function recoverArticleAnnex() { + $params = $this->request->all(); + $this->validate($params, [ + 'annex_id' => 'required|integer|min:0' + ]); + $isTrue = $this->articleService->updateArticleAnnexStatus($this->uid(), $params['annex_id'], 1); + + return $isTrue + ? $this->response->success([], '笔记附件恢复成功...') + : $this->response->fail('笔记附件恢复失败...'); } /** @@ -286,16 +456,39 @@ class ArticleController extends CController */ public function recoverAnnexList() { + $rows = $this->articleService->recoverAnnexList($this->uid()); + if ($rows) { + $getDay = function ($delete_at) { + $last_time = strtotime('+30 days', strtotime($delete_at)); + return (time() > $last_time) ? 0 : diff_date(date('Y-m-d', $last_time), date('Y-m-d')); + }; + + array_walk($rows, function (&$item) use ($getDay) { + $item['day'] = $getDay($item['deleted_at']); + $item['visible'] = false; + }); + } + + return $this->response->success(['rows' => $rows]); } /** * 永久删除笔记附件(从已删除附件中永久删除) * - * @RequestMapping(path="forever-delete-annex", methods="get") + * @RequestMapping(path="forever-delete-annex", methods="post") */ public function foreverDelAnnex() { + $params = $this->request->all(); + $this->validate($params, [ + 'annex_id' => 'required|integer|min:0' + ]); + $isTrue = $this->articleService->foreverDelAnnex($this->uid(), $params['annex_id']); + + return $isTrue + ? $this->response->success([], '笔记附件删除成功...') + : $this->response->fail('笔记附件删除失败...'); } } diff --git a/app/Controller/Api/V1/AuthController.php b/app/Controller/Api/V1/AuthController.php index cef786a..1a2e200 100644 --- a/app/Controller/Api/V1/AuthController.php +++ b/app/Controller/Api/V1/AuthController.php @@ -53,8 +53,6 @@ class AuthController extends CController 'mobile' => "required|regex:/^1[345789][0-9]{9}$/", 'password' => 'required', 'platform' => 'required|in:h5,ios,windows,mac', - ], [ - 'mobile.regex' => 'mobile 格式不正确' ]); $userInfo = $this->userService->login( diff --git a/app/Controller/Api/V1/CController.php b/app/Controller/Api/V1/CController.php index 55b7e04..1bf6dcf 100644 --- a/app/Controller/Api/V1/CController.php +++ b/app/Controller/Api/V1/CController.php @@ -3,7 +3,7 @@ namespace App\Controller\Api\V1; use App\Controller\AbstractController; -use App\Supports\Http\Response; +use App\Support\Http\Response; use Hyperf\Di\Annotation\Inject; use Phper666\JWTAuth\JWT; diff --git a/app/Controller/Api/V1/GroupController.php b/app/Controller/Api/V1/GroupController.php index 930cff4..f133e4c 100644 --- a/app/Controller/Api/V1/GroupController.php +++ b/app/Controller/Api/V1/GroupController.php @@ -2,8 +2,425 @@ namespace App\Controller\Api\V1; +use App\Model\UsersFriend; +use Hyperf\Di\Annotation\Inject; +use Hyperf\HttpServer\Annotation\Controller; +use Hyperf\HttpServer\Annotation\RequestMapping; +use Hyperf\HttpServer\Annotation\Middleware; +use Phper666\JWTAuth\Middleware\JWTAuthMiddleware; +use App\Service\GroupService; +use App\Model\UsersChatList; +use App\Model\Group\UsersGroup; +use App\Model\Group\UsersGroupMember; +use App\Model\Group\UsersGroupNotice; +/** + * Class GroupController + * + * @Controller(path="/api/v1/group") + * @Middleware(JWTAuthMiddleware::class) + * + * @package App\Controller\Api\V1 + */ class GroupController extends CController { + /** + * @Inject + * @var GroupService + */ + public $groupService; + /** + * 创建群组 + * + * @RequestMapping(path="create", methods="post") + * + * @return mixed + */ + public function create() + { + $params = $this->request->all(); + $this->validate($params, [ + 'group_name' => 'required', + 'group_profile' => 'required', + 'uids' => 'required', + ]); + + $friend_ids = array_filter(explode(',', $params['uids'])); + + [$isTrue, $data] = $this->groupService->create($this->uid(), [ + 'name' => $params['group_name'], + 'avatar' => $params['avatar'] ?? '', + 'profile' => $params['group_profile'] + ], array_unique($friend_ids)); + + if (!$isTrue) { + return $this->response->fail('创建群聊失败,请稍后再试...'); + } + + //群聊创建成功后需要创建聊天室并发送消息通知 + // ... 包装消息推送到队列 + + return $this->response->success([ + 'group_id' => $data['group_id'] + ], '群聊创建成功...'); + } + + /** + * 解散群组接口 + * + * @RequestMapping(path="dismiss", methods="post") + */ + public function dismiss() + { + $params = $this->request->inputs(['group_id']); + $this->validate($params, [ + 'group_id' => 'required|integer', + ]); + + $isTrue = $this->groupService->dismiss($params['group_id'], $this->uid()); + if (!$isTrue) { + return $this->response->fail('群组解散失败...'); + } + + // ... 推送群消息 + + return $this->response->success([], '群组解散成功...'); + } + + /** + * 邀请好友加入群组接口 + * + * @RequestMapping(path="invite", methods="post") + */ + public function invite() + { + $params = $this->request->inputs(['group_id', 'uids']); + $this->validate($params, [ + 'group_id' => 'required|integer', + 'uids' => 'required', + ]); + + $uids = array_filter(explode(',', $params['uids'])); + + [$isTrue, $record_id] = $this->groupService->invite($this->uid(), $params['group_id'], array_unique($uids)); + if (!$isTrue) { + return $this->response->fail('邀请好友加入群聊失败...'); + } + + // 推送入群消息 + // ... + + return $this->response->success([], '好友已成功加入群聊...'); + } + + /** + * 退出群组接口 + * + * @RequestMapping(path="secede", methods="post") + */ + public function secede() + { + $params = $this->request->inputs(['group_id']); + $this->validate($params, [ + 'group_id' => 'required|integer' + ]); + + [$isTrue, $record_id] = $this->groupService->quit($this->uid(), $params['group_id']); + if (!$isTrue) { + return $this->response->fail('退出群组失败...'); + } + + // 推送消息通知 + // ... + + return $this->response->success([], '已成功退出群组...'); + } + + /** + * 编辑群组信息 + * + * @RequestMapping(path="edit", methods="post") + */ + public function editDetail() + { + $params = $this->request->inputs(['group_id', 'group_name', 'group_profile', 'avatar']); + $this->validate($params, [ + 'group_id' => 'required|integer', + 'group_name' => 'required', + 'group_profile' => 'required', + 'avatar' => 'required', + ]); + + $result = UsersGroup::where('id', $params['group_id'])->where('user_id', $this->uid())->update([ + 'group_name' => $params['group_name'], + 'group_profile' => $params['group_profile'], + 'avatar' => $params['avatar'] + ]); + + // 推送消息通知 + // ... + + return $result + ? $this->response->success([], '群组信息修改成功...') + : $this->response->fail('群组信息修改失败...'); + } + + /** + * 移除指定成员(管理员权限) + * + * @RequestMapping(path="remove-members", methods="post") + */ + public function removeMembers() + { + $params = $this->request->inputs(['group_id', 'members_ids']); + $this->validate($params, [ + 'group_id' => 'required|integer', + 'members_ids' => 'required|array' + ]); + + [$isTrue, $record_id] = $this->groupService->removeMember($params['group_id'], $this->uid(), $params['members_ids']); + if (!$isTrue) { + return $this->response->fail('群聊用户移除失败...'); + } + + // 推送消息通知 + // ... + + return $this->response->success([], '已成功退出群组...'); + } + + /** + * 获取群信息接口 + * + * @RequestMapping(path="detail", methods="get") + */ + public function detail() + { + $group_id = $this->request->input('group_id', 0); + + $user_id = $this->uid(); + $groupInfo = UsersGroup::leftJoin('users', 'users.id', '=', 'users_group.user_id') + ->where('users_group.id', $group_id)->where('users_group.status', 0)->first([ + 'users_group.id', 'users_group.user_id', + 'users_group.group_name', + 'users_group.group_profile', 'users_group.avatar', + 'users_group.created_at', + 'users.nickname' + ]); + + if (!$groupInfo) { + return $this->response->success([]); + } + + $notice = UsersGroupNotice::where('group_id', $group_id) + ->where('is_delete', 0) + ->orderBy('id', 'desc') + ->first(['title', 'content']); + + return $this->response->success([ + 'group_id' => $groupInfo->id, + 'group_name' => $groupInfo->group_name, + 'group_profile' => $groupInfo->group_profile, + 'avatar' => $groupInfo->avatar, + 'created_at' => $groupInfo->created_at, + 'is_manager' => $groupInfo->user_id == $user_id, + 'manager_nickname' => $groupInfo->nickname, + 'visit_card' => UsersGroupMember::visitCard($user_id, $group_id), + 'not_disturb' => UsersChatList::where('uid', $user_id)->where('group_id', $group_id)->where('type', 2)->value('not_disturb') ?? 0, + 'notice' => $notice ? $notice->toArray() : [] + ]); + } + + /** + * 设置用户群名片 + * + * @RequestMapping(path="set-group-card", methods="post") + */ + public function setGroupCard() + { + $params = $this->request->inputs(['group_id', 'visit_card']); + $this->validate($params, [ + 'group_id' => 'required|integer', + 'visit_card' => 'required' + ]); + + $isTrue = UsersGroupMember::where('group_id', $params['group_id']) + ->where('user_id', $this->uid()) + ->where('status', 0) + ->update(['visit_card' => $params['visit_card']]); + + return $isTrue + ? $this->response->success([], '群名片修改成功...') + : $this->response->error('群名片修改失败...'); + } + + /** + * 获取用户可邀请加入群组的好友列表 + * + * @RequestMapping(path="invite-friends", methods="get") + */ + public function getInviteFriends() + { + $group_id = $this->request->input('group_id', 0); + $friends = UsersFriend::getUserFriends($this->uid()); + if ($group_id > 0 && $friends) { + if ($ids = UsersGroupMember::getGroupMemberIds($group_id)) { + foreach ($friends as $k => $item) { + if (in_array($item['id'], $ids)) unset($friends[$k]); + } + } + + $friends = array_values($friends); + } + + return $this->response->success($friends); + } + + /** + * 获取群组成员列表 + * + * @RequestMapping(path="members", methods="get") + */ + public function getGroupMembers() + { + $user_id = $this->uid(); + $group_id = $this->request->input('group_id', 0); + + // 判断用户是否是群成员 + if (!UsersGroup::isMember($group_id, $user_id)) { + return $this->response->fail('非法操作...'); + } + + $members = UsersGroupMember::select([ + 'users_group_member.id', 'users_group_member.group_owner as is_manager', 'users_group_member.visit_card', + 'users_group_member.user_id', 'users.avatar', 'users.nickname', 'users.gender', + 'users.motto', + ]) + ->leftJoin('users', 'users.id', '=', 'users_group_member.user_id') + ->where([ + ['users_group_member.group_id', '=', $group_id], + ['users_group_member.status', '=', 0], + ])->orderBy('is_manager', 'desc')->get()->toArray(); + + return $this->response->success($members); + } + + /** + * 获取群组公告列表 + * + * @RequestMapping(path="notices", methods="get") + */ + public function getGroupNotices() + { + $user_id = $this->uid(); + $group_id = $this->request->input('group_id', 0); + + // 判断用户是否是群成员 + if (!UsersGroup::isMember($group_id, $user_id)) { + return $this->response->fail('非管理员禁止操作...'); + } + + $rows = UsersGroupNotice::leftJoin('users', 'users.id', '=', 'users_group_notice.user_id') + ->where([ + ['users_group_notice.group_id', '=', $group_id], + ['users_group_notice.is_delete', '=', 0] + ]) + ->orderBy('users_group_notice.id', 'desc') + ->get([ + 'users_group_notice.id', + 'users_group_notice.user_id', + 'users_group_notice.title', + 'users_group_notice.content', + 'users_group_notice.created_at', + 'users_group_notice.updated_at', + 'users.avatar', 'users.nickname', + ])->toArray(); + + return $this->response->success($rows); + } + + /** + * 创建/编辑群公告 + * + * @RequestMapping(path="edit-notice", methods="post") + */ + public function editNotice() + { + $params = $this->request->inputs(['group_id', 'notice_id', 'title', 'content']); + $this->validate($params, [ + 'group_id' => 'required|integer', + 'notice_id' => 'required|integer', + 'title' => 'required', + 'content' => 'required', + ]); + + $user_id = $this->uid(); + + // 判断用户是否是管理员 + if (!UsersGroup::isManager($user_id, $params['group_id'])) { + return $this->response->fail('非管理员禁止操作...'); + } + + // 判断是否是新增数据 + if (empty($data['notice_id'])) { + $result = UsersGroupNotice::create([ + 'group_id' => $data['group_id'], + 'title' => $data['title'], + 'content' => $data['content'], + 'user_id' => $user_id, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s') + ]); + + if (!$result) { + return $this->response->fail('添加群公告信息失败...'); + } + + // ... 推送群消息 + return $this->response->success([], '添加群公告信息成功...'); + } + + $result = UsersGroupNotice::where('id', $data['notice_id'])->update([ + 'title' => $data['title'], + 'content' => $data['content'], + 'updated_at' => date('Y-m-d H:i:s') + ]); + + return $result + ? $this->response->success('修改群公告信息成功...') + : $this->response->fail('修改群公告信息成功...'); + } + + /** + * 删除群公告(软删除) + * + * @RequestMapping(path="delete-notice", methods="post") + */ + public function deleteNotice() + { + $params = $this->request->inputs(['group_id', 'notice_id']); + $this->validate($params, [ + 'group_id' => 'required|integer', + 'notice_id' => 'required|integer' + ]); + + $user_id = $this->uid(); + + // 判断用户是否是管理员 + if (!UsersGroup::isManager($user_id, $params['group_id'])) { + return $this->response->fail('非法操作...'); + } + + $result = UsersGroupNotice::where('id', $params['group_id']) + ->where('group_id', $params['group_id']) + ->update([ + 'is_delete' => 1, + 'deleted_at' => date('Y-m-d H:i:s') + ]); + + return $result + ? $this->response->success('公告删除成功...') + : $this->response->fail('公告删除失败...'); + } } diff --git a/app/Controller/IndexController.php b/app/Controller/IndexController.php index 7787770..cc33603 100644 --- a/app/Controller/IndexController.php +++ b/app/Controller/IndexController.php @@ -13,7 +13,9 @@ declare(strict_types=1); namespace App\Controller; +use App\Amqp\Producer\ChatMessageProducer; use Hyperf\HttpServer\Contract\ResponseInterface; +use Hyperf\Amqp\Producer; class IndexController extends AbstractController { @@ -22,10 +24,12 @@ class IndexController extends AbstractController $user = $this->request->input('user', 'Hyperf'); $method = $this->request->getMethod(); - $this->validate($this->request->all(), [ - 'username' => 'required', - 'password' => 'required', - ]); + $producer = container()->get(Producer::class); + + $ip = config('ip_address'); + + $string = time(); + $producer->produce(new ChatMessageProducer("我是来自[{$ip} 服务器的消息],{$string}")); return [ 'method' => $method, diff --git a/app/Helper/Hash.php b/app/Helper/Hash.php new file mode 100644 index 0000000..0d0df84 --- /dev/null +++ b/app/Helper/Hash.php @@ -0,0 +1,29 @@ + 'integer', 'user_id' => 'integer', 'class_id' => 'integer', 'is_asterisk' => 'integer', 'status' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; -} diff --git a/app/Model/Article/Article.php b/app/Model/Article/Article.php new file mode 100644 index 0000000..fdd44f5 --- /dev/null +++ b/app/Model/Article/Article.php @@ -0,0 +1,87 @@ + 'integer', + 'user_id' => 'integer', + 'class_id' => 'integer', + 'is_asterisk' => 'integer', + 'status' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime' + ]; + + /** + * 关联笔记详细表(一对一关系) + * + * @return \Hyperf\Database\Model\Relations\HasOne + */ + public function detail() + { + return $this->hasOne(ArticleDetail::class, 'article_id', 'id'); + } + + /** + * 关联笔记附件信息表(一对多关系) + * + * @return \Hyperf\Database\Model\Relations\HasMany + */ + public function annexs() + { + return $this->hasMany(ArticleAnnex::class, 'article_id', 'id'); + } +} diff --git a/app/Model/Article/ArticleAnnex.php b/app/Model/Article/ArticleAnnex.php new file mode 100644 index 0000000..9c59eb5 --- /dev/null +++ b/app/Model/Article/ArticleAnnex.php @@ -0,0 +1,56 @@ + 'integer', + 'user_id' => 'integer', + 'article_id' => 'integer', + 'file_size' => 'integer', + 'status' => 'integer', + 'created_at' => 'datetime' + ]; +} diff --git a/app/Model/Article/ArticleClass.php b/app/Model/Article/ArticleClass.php new file mode 100644 index 0000000..462896c --- /dev/null +++ b/app/Model/Article/ArticleClass.php @@ -0,0 +1,55 @@ + 'integer', + 'user_id' => 'integer', + 'sort' => 'integer', + 'is_default' => 'integer', + 'created_at' => 'int' + ]; +} diff --git a/app/Model/ArticleDetail.php b/app/Model/Article/ArticleDetail.php similarity index 57% rename from app/Model/ArticleDetail.php rename to app/Model/Article/ArticleDetail.php index a6899e4..90c17d5 100644 --- a/app/Model/ArticleDetail.php +++ b/app/Model/Article/ArticleDetail.php @@ -2,15 +2,21 @@ declare (strict_types=1); -namespace App\Model; +namespace App\Model\Article; + +use App\Model\BaseModel; /** + * 笔记详情数据表模型 + * * @property int $id * @property int $article_id * @property string $md_content * @property string $content + * + * @package App\Model\Article */ -class ArticleDetail extends Model +class ArticleDetail extends BaseModel { /** * The table associated with the model. @@ -24,12 +30,19 @@ class ArticleDetail extends Model * * @var array */ - protected $fillable = []; + protected $fillable = [ + 'article_id', + 'md_content', + 'content', + ]; /** * The attributes that should be cast to native types. * * @var array */ - protected $casts = ['id' => 'integer', 'article_id' => 'integer']; + protected $casts = [ + 'id' => 'integer', + 'article_id' => 'integer' + ]; } diff --git a/app/Model/ArticleTag.php b/app/Model/Article/ArticleTag.php similarity index 68% rename from app/Model/ArticleTag.php rename to app/Model/Article/ArticleTag.php index daf04cc..fd84694 100644 --- a/app/Model/ArticleTag.php +++ b/app/Model/Article/ArticleTag.php @@ -2,8 +2,9 @@ declare (strict_types=1); -namespace App\Model; +namespace App\Model\Article; +use App\Model\BaseModel; /** * @property int $id @@ -12,7 +13,7 @@ namespace App\Model; * @property int $sort * @property \Carbon\Carbon $created_at */ -class ArticleTag extends Model +class ArticleTag extends BaseModel { /** * The table associated with the model. @@ -33,5 +34,10 @@ class ArticleTag extends Model * * @var array */ - protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'sort' => 'integer', 'created_at' => 'datetime']; + protected $casts = [ + 'id' => 'integer', + 'user_id' => 'integer', + 'sort' => 'integer', + 'created_at' => 'datetime' + ]; } diff --git a/app/Model/ArticleAnnex.php b/app/Model/ArticleAnnex.php deleted file mode 100644 index 2768cb6..0000000 --- a/app/Model/ArticleAnnex.php +++ /dev/null @@ -1,41 +0,0 @@ - 'integer', 'user_id' => 'integer', 'article_id' => 'integer', 'file_size' => 'integer', 'status' => 'integer', 'created_at' => 'datetime']; -} diff --git a/app/Model/ArticleClass.php b/app/Model/ArticleClass.php deleted file mode 100644 index 2e8098f..0000000 --- a/app/Model/ArticleClass.php +++ /dev/null @@ -1,44 +0,0 @@ - 'integer', 'user_id' => 'integer', 'sort' => 'integer', 'is_default' => 'integer', 'created_at' => 'int']; -} diff --git a/app/Model/Model.php b/app/Model/BaseModel.php similarity index 77% rename from app/Model/Model.php rename to app/Model/BaseModel.php index ab0c466..ebf98a4 100644 --- a/app/Model/Model.php +++ b/app/Model/BaseModel.php @@ -13,9 +13,9 @@ declare(strict_types=1); namespace App\Model; -use Hyperf\DbConnection\Model\Model as BaseModel; +use Hyperf\DbConnection\Model\Model as CModel; -abstract class Model extends BaseModel +abstract class BaseModel extends CModel { public $timestamps = false; } diff --git a/app/Model/Chat/ChatRecord.php b/app/Model/Chat/ChatRecord.php new file mode 100644 index 0000000..c86f7f9 --- /dev/null +++ b/app/Model/Chat/ChatRecord.php @@ -0,0 +1,61 @@ +0: 用户ID] + * @property int $receive_id 接收者ID[用户ID 或 群ID] + * @property string $content 文本消息 + * @property int $is_revoke 是否撤回消息[0:否;1:是] + * @property string $created_at 创建时间 + * + * @package App\Model\Chat + */ +class ChatRecord extends BaseModel +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'chat_records'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'source', + 'msg_type', + 'user_id', + 'receive_id', + 'content', + 'is_revoke', + 'created_at', + ]; + + /** + * The attributes that should be cast to native types. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'source' => 'integer', + 'msg_type' => 'integer', + 'user_id' => 'integer', + 'receive_id' => 'integer', + 'is_revoke' => 'integer', + 'created_at' => 'datetime' + ]; +} diff --git a/app/Model/ChatRecordsCode.php b/app/Model/Chat/ChatRecordsCode.php similarity index 88% rename from app/Model/ChatRecordsCode.php rename to app/Model/Chat/ChatRecordsCode.php index e16f62d..f78e23a 100644 --- a/app/Model/ChatRecordsCode.php +++ b/app/Model/Chat/ChatRecordsCode.php @@ -2,7 +2,9 @@ declare (strict_types=1); -namespace App\Model; +namespace App\Model\Chat; + +use App\Model\BaseModel; /** * @property int $id @@ -12,7 +14,7 @@ namespace App\Model; * @property string $code * @property \Carbon\Carbon $created_at */ -class ChatRecordsCode extends Model +class ChatRecordsCode extends BaseModel { /** * The table associated with the model. diff --git a/app/Model/ChatRecordsDelete.php b/app/Model/Chat/ChatRecordsDelete.php similarity index 87% rename from app/Model/ChatRecordsDelete.php rename to app/Model/Chat/ChatRecordsDelete.php index 630a0a5..5874ea3 100644 --- a/app/Model/ChatRecordsDelete.php +++ b/app/Model/Chat/ChatRecordsDelete.php @@ -2,7 +2,9 @@ declare (strict_types=1); -namespace App\Model; +namespace App\Model\Chat; + +use App\Model\BaseModel; /** * @property int $id @@ -10,7 +12,7 @@ namespace App\Model; * @property int $user_id * @property \Carbon\Carbon $created_at */ -class ChatRecordsDelete extends Model +class ChatRecordsDelete extends BaseModel { /** * The table associated with the model. diff --git a/app/Model/ChatRecordsFile.php b/app/Model/Chat/ChatRecordsFile.php similarity index 91% rename from app/Model/ChatRecordsFile.php rename to app/Model/Chat/ChatRecordsFile.php index b20b3e3..43e4dfd 100644 --- a/app/Model/ChatRecordsFile.php +++ b/app/Model/Chat/ChatRecordsFile.php @@ -2,7 +2,9 @@ declare (strict_types=1); -namespace App\Model; +namespace App\Model\Chat; + +use App\Model\BaseModel; /** * @property int $id @@ -18,7 +20,7 @@ namespace App\Model; * @property int $is_delete * @property \Carbon\Carbon $created_at */ -class ChatRecordsFile extends Model +class ChatRecordsFile extends BaseModel { /** * The table associated with the model. diff --git a/app/Model/ChatRecordsForward.php b/app/Model/Chat/ChatRecordsForward.php similarity index 79% rename from app/Model/ChatRecordsForward.php rename to app/Model/Chat/ChatRecordsForward.php index 85262cb..2720f81 100644 --- a/app/Model/ChatRecordsForward.php +++ b/app/Model/Chat/ChatRecordsForward.php @@ -2,7 +2,9 @@ declare (strict_types=1); -namespace App\Model; +namespace App\Model\Chat; + +use App\Model\BaseModel; /** * @property int $id @@ -12,7 +14,7 @@ namespace App\Model; * @property string $text * @property \Carbon\Carbon $created_at */ -class ChatRecordsForward extends Model +class ChatRecordsForward extends BaseModel { /** * The table associated with the model. @@ -26,7 +28,9 @@ class ChatRecordsForward extends Model * * @var array */ - protected $fillable = []; + protected $fillable = [ + 'record_id', 'user_id', 'created_at' + ]; /** * The attributes that should be cast to native types. diff --git a/app/Model/Chat/ChatRecordsInvite.php b/app/Model/Chat/ChatRecordsInvite.php new file mode 100644 index 0000000..0ed8d78 --- /dev/null +++ b/app/Model/Chat/ChatRecordsInvite.php @@ -0,0 +1,52 @@ + 'integer', + 'record_id' => 'integer', + 'type' => 'integer', + 'operate_user_id' => 'integer' + ]; +} diff --git a/app/Model/ChatRecord.php b/app/Model/ChatRecord.php deleted file mode 100644 index d9f82be..0000000 --- a/app/Model/ChatRecord.php +++ /dev/null @@ -1,39 +0,0 @@ - 'integer', 'source' => 'integer', 'msg_type' => 'integer', 'user_id' => 'integer', 'receive_id' => 'integer', 'is_revoke' => 'integer', 'created_at' => 'datetime']; -} diff --git a/app/Model/ChatRecordsInvite.php b/app/Model/ChatRecordsInvite.php deleted file mode 100644 index a554f1c..0000000 --- a/app/Model/ChatRecordsInvite.php +++ /dev/null @@ -1,36 +0,0 @@ - 'integer', 'record_id' => 'integer', 'type' => 'integer', 'operate_user_id' => 'integer']; -} diff --git a/app/Model/Emoticon.php b/app/Model/Emoticon.php index f5f91d7..b32afe4 100644 --- a/app/Model/Emoticon.php +++ b/app/Model/Emoticon.php @@ -10,7 +10,7 @@ namespace App\Model; * @property string $url * @property \Carbon\Carbon $created_at */ -class Emoticon extends Model +class Emoticon extends BaseModel { /** * The table associated with the model. diff --git a/app/Model/EmoticonDetail.php b/app/Model/EmoticonDetail.php index 7b7b64c..94ea42d 100644 --- a/app/Model/EmoticonDetail.php +++ b/app/Model/EmoticonDetail.php @@ -14,7 +14,7 @@ namespace App\Model; * @property int $file_size * @property \Carbon\Carbon $created_at */ -class EmoticonDetail extends Model +class EmoticonDetail extends BaseModel { /** * The table associated with the model. diff --git a/app/Model/FileSplitUpload.php b/app/Model/FileSplitUpload.php index 74d670c..d27ae57 100644 --- a/app/Model/FileSplitUpload.php +++ b/app/Model/FileSplitUpload.php @@ -18,7 +18,7 @@ namespace App\Model; * @property int $is_delete * @property int $upload_at */ -class FileSplitUpload extends Model +class FileSplitUpload extends BaseModel { /** * The table associated with the model. diff --git a/app/Model/Group/UsersGroup.php b/app/Model/Group/UsersGroup.php new file mode 100644 index 0000000..7008ea1 --- /dev/null +++ b/app/Model/Group/UsersGroup.php @@ -0,0 +1,87 @@ + 'integer', + 'user_id' => 'integer', + 'status' => 'integer', + 'created_at' => 'datetime' + ]; + + /** + * 获取群聊成员 + */ + public function members() + { + return $this->hasMany(UsersGroupMember::class, 'group_id', 'id'); + } + + /** + * 判断用户是否是管理员 + * + * @param int $user_id 用户ID + * @param int $group_id 群ID + * @return mixed + */ + public static function isManager(int $user_id,int $group_id){ + return self::where('id', $group_id)->where('user_id', $user_id)->exists(); + } + + /** + * 判断用户是否是群成员 + * + * @param int $group_id 群ID + * @param int $user_id 用户ID + * @return bool + */ + public static function isMember(int $group_id, int $user_id) + { + return UsersGroupMember::where('group_id', $group_id)->where('user_id', $user_id)->where('status', 0)->exists() ? true : false; + } +} diff --git a/app/Model/Group/UsersGroupMember.php b/app/Model/Group/UsersGroupMember.php new file mode 100644 index 0000000..9ba3999 --- /dev/null +++ b/app/Model/Group/UsersGroupMember.php @@ -0,0 +1,70 @@ + 'integer', + 'group_id' => 'integer', + 'user_id' => 'integer', + 'group_owner' => 'integer', + 'status' => 'integer', + 'created_at' => 'datetime' + ]; + + /** + * 获取聊天群成员ID + * + * @param int $group_id 群聊ID + * @return mixed + */ + public static function getGroupMemberIds(int $group_id) + { + return self::where('group_id', $group_id)->where('status', 0)->pluck('user_id')->toArray(); + } + + /** + * 获取用户的群名片 + * + * @param int $user_id 用户ID + * @param int $group_id 群ID + * @return mixed + */ + public static function visitCard(int $user_id, int $group_id) + { + return self::where('group_id', $group_id)->where('user_id', $user_id)->value('visit_card'); + } +} diff --git a/app/Model/UsersGroupNotice.php b/app/Model/Group/UsersGroupNotice.php similarity index 89% rename from app/Model/UsersGroupNotice.php rename to app/Model/Group/UsersGroupNotice.php index a2b6ea3..1dcd0d7 100644 --- a/app/Model/UsersGroupNotice.php +++ b/app/Model/Group/UsersGroupNotice.php @@ -2,7 +2,9 @@ declare (strict_types=1); -namespace App\Model; +namespace App\Model\Group; + +use App\Model\BaseModel; /** * @property int $id @@ -15,7 +17,7 @@ namespace App\Model; * @property \Carbon\Carbon $updated_at * @property string $deleted_at */ -class UsersGroupNotice extends Model +class UsersGroupNotice extends BaseModel { /** * The table associated with the model. diff --git a/app/Model/User.php b/app/Model/User.php index f9ec367..44c6b3d 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -16,7 +16,7 @@ namespace App\Model; * * @package App\Model */ -class User extends Model +class User extends BaseModel { /** * The table associated with the model. diff --git a/app/Model/UsersChatList.php b/app/Model/UsersChatList.php index 4f4eb7f..9192128 100644 --- a/app/Model/UsersChatList.php +++ b/app/Model/UsersChatList.php @@ -15,7 +15,7 @@ namespace App\Model; * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at */ -class UsersChatList extends Model +class UsersChatList extends BaseModel { /** * The table associated with the model. @@ -36,5 +36,16 @@ class UsersChatList extends Model * * @var array */ - protected $casts = ['id' => 'integer', 'type' => 'integer', 'uid' => 'integer', 'friend_id' => 'integer', 'group_id' => 'integer', 'status' => 'integer', 'is_top' => 'integer', 'not_disturb' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + protected $casts = [ + 'id' => 'integer', + 'type' => 'integer', + 'uid' => 'integer', + 'friend_id' => 'integer', + 'group_id' => 'integer', + 'status' => 'integer', + 'is_top' => 'integer', + 'not_disturb' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime' + ]; } diff --git a/app/Model/UsersEmoticon.php b/app/Model/UsersEmoticon.php index aabf4a5..d80c05d 100644 --- a/app/Model/UsersEmoticon.php +++ b/app/Model/UsersEmoticon.php @@ -8,7 +8,7 @@ namespace App\Model; * @property int $user_id * @property string $emoticon_ids */ -class UsersEmoticon extends Model +class UsersEmoticon extends BaseModel { /** * The table associated with the model. diff --git a/app/Model/UsersFriend.php b/app/Model/UsersFriend.php index e6036d3..90507db 100644 --- a/app/Model/UsersFriend.php +++ b/app/Model/UsersFriend.php @@ -1,8 +1,11 @@ 'integer', 'user1' => 'integer', 'user2' => 'integer', 'active' => 'integer', 'status' => 'integer', 'created_at' => 'datetime']; + + /** + * 获取用户所有好友 + * + * @param int $uid 用户ID + * @return mixed + */ + public static function getUserFriends(int $uid) + { + $prefix = config('databases.default.prefix'); + $sql = << $user_id2) { + [$user_id1, $user_id2] = [$user_id2, $user_id1]; + } + + return self::where('user1', $user_id1)->where('user2', $user_id2)->where('status', 1)->exists(); + } + + /** + * 获取指定用户的所有朋友的用户ID + * + * @param int $user_id 指定用户ID + * @return array + */ + public static function getFriendIds(int $user_id) + { + $prefix = config('databases.default.prefix'); + $sql = "SELECT user2 as uid from {$prefix}users_friends where user1 = {$user_id} and `status` = 1 UNION all SELECT user1 as uid from {$prefix}users_friends where user2 = {$user_id} and `status` = 1"; + return array_map(function ($item) { + return $item->uid; + }, Db::select($sql)); + } } diff --git a/app/Model/UsersFriendsApply.php b/app/Model/UsersFriendsApply.php index f15c603..15eaf96 100644 --- a/app/Model/UsersFriendsApply.php +++ b/app/Model/UsersFriendsApply.php @@ -12,7 +12,7 @@ namespace App\Model; * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at */ -class UsersFriendsApply extends Model +class UsersFriendsApply extends BaseModel { /** * The table associated with the model. diff --git a/app/Model/UsersGroup.php b/app/Model/UsersGroup.php deleted file mode 100644 index d9079d5..0000000 --- a/app/Model/UsersGroup.php +++ /dev/null @@ -1,38 +0,0 @@ - 'integer', 'user_id' => 'integer', 'status' => 'integer', 'created_at' => 'datetime']; -} diff --git a/app/Model/UsersGroupMember.php b/app/Model/UsersGroupMember.php deleted file mode 100644 index 2d8848e..0000000 --- a/app/Model/UsersGroupMember.php +++ /dev/null @@ -1,37 +0,0 @@ - 'integer', 'group_id' => 'integer', 'user_id' => 'integer', 'group_owner' => 'integer', 'status' => 'integer', 'created_at' => 'datetime']; -} diff --git a/app/Service/ArticleService.php b/app/Service/ArticleService.php index 208826f..1bf2a9a 100644 --- a/app/Service/ArticleService.php +++ b/app/Service/ArticleService.php @@ -2,13 +2,14 @@ namespace App\Service; - -use App\Model\Article; -use App\Model\ArticleClass; -use App\Model\ArticleTag; -use App\Model\ArticleAnnex; +use App\Model\Article\Article; +use App\Model\Article\ArticleClass; +use App\Model\Article\ArticleDetail; +use App\Model\Article\ArticleTag; +use App\Model\Article\ArticleAnnex; use App\Traits\PagingTrait; use Hyperf\DbConnection\Db; +use Exception; class ArticleService extends BaseService { @@ -159,4 +160,442 @@ class ArticleService extends BaseService ])->get(['id', 'file_suffix', 'file_size', 'original_name', 'created_at'])->toArray(); } + /** + * 编辑笔记分类 + * + * @param int $uid 用户ID + * @param int $class_id 分类ID + * @param string $class_name 分类名 + * @return bool|int + */ + public function editArticleClass(int $uid, int $class_id, string $class_name) + { + if ($class_id) { + if (!ArticleClass::where('id', $class_id)->where('user_id', $uid)->where('is_default', 0)->update(['class_name' => $class_name])) { + return false; + } + + return $class_id; + } + + $arr = []; + $items = ArticleClass::where('user_id', $uid)->get(['id', 'sort']); + foreach ($items as $key => $item) { + $arr[] = ['id' => $item->id, 'sort' => $key + 2]; + } + + unset($items); + + Db::beginTransaction(); + try { + foreach ($arr as $val) { + ArticleClass::where('id', $val['id'])->update(['sort' => $val['sort']]); + } + + $insRes = ArticleClass::create(['user_id' => $uid, 'class_name' => $class_name, 'sort' => 1, 'created_at' => time()]); + if (!$insRes) { + throw new Exception('笔记分类添加失败..,.'); + } + + Db::commit(); + } catch (Exception $e) { + Db::rollBack(); + return false; + } + + return $insRes->id; + } + + /** + * 删除笔记分类 + * + * @param int $uid 用户ID + * @param int $class_id 分类ID + * @return bool + */ + public function delArticleClass(int $uid, int $class_id) + { + if (!ArticleClass::where('id', $class_id)->where('user_id', $uid)->exists()) { + return false; + } + + $count = Article::where('user_id', $uid)->where('class_id', $class_id)->count(); + if ($count > 0) { + return false; + } + + return (bool)ArticleClass::where('id', $class_id)->where('user_id', $uid)->where('is_default', 0)->delete(); + } + + /** + * 文集分类排序 + * + * @param int $user_id 用户ID + * @param int $class_id 文集分类ID + * @param int $sort_type 排序方式 + * @return bool + */ + public function articleClassSort(int $user_id, int $class_id, int $sort_type) + { + if (!$info = ArticleClass::select(['id', 'sort'])->where('id', $class_id)->where('user_id', $user_id)->first()) { + return false; + } + + //向下排序 + if ($sort_type == 1) { + $maxSort = ArticleClass::where('user_id', $user_id)->max('sort'); + if ($maxSort == $info->sort) { + return false; + } + + DB::beginTransaction(); + try { + ArticleClass::where('user_id', $user_id)->where('sort', $info->sort + 1)->update([ + 'sort' => $info->sort + ]); + + ArticleClass::where('id', $class_id)->update([ + 'sort' => $info->sort + 1 + ]); + + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + return false; + } + + return true; + } else if ($sort_type == 2) {//向上排序 + $minSort = ArticleClass::where('user_id', $user_id)->min('sort'); + if ($minSort == $info->sort) { + return false; + } + + DB::beginTransaction(); + try { + ArticleClass::where('user_id', $user_id)->where('sort', $info->sort - 1)->update([ + 'sort' => $info->sort + ]); + + ArticleClass::where('id', $class_id)->where('user_id', $user_id)->update([ + 'sort' => $info->sort - 1 + ]); + + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + return false; + } + + return true; + } + } + + /** + * 笔记分类合并 + * + * @param int $user_id 用户ID + * @param int $class_id 笔记分类ID + * @param int $to_class_id 笔记分类ID + * @return bool + */ + public function mergeArticleClass(int $user_id, int $class_id, int $to_class_id) + { + $count = ArticleClass::whereIn('id', [$class_id, $to_class_id])->where('user_id', $user_id)->count(); + if ($count < 2) { + return false; + } + + return (boolean)Article::where('class_id', $class_id)->where('user_id', $user_id)->update([ + 'class_id' => $to_class_id + ]); + } + + /** + * 编辑笔记标签 + * + * @param int $uid 用户ID + * @param int $tag_id 标签ID + * @param string $tag_name 标签名 + * @return bool|int + */ + public function editArticleTag(int $uid, int $tag_id, string $tag_name) + { + $id = ArticleTag::where('user_id', $uid)->where('tag_name', $tag_name)->value('id'); + if ($tag_id) { + if ($id && $id != $tag_id) { + return false; + } + + return ArticleTag::where('id', $tag_id)->where('user_id', $uid)->update(['tag_name' => $tag_name]) ? $tag_id : false; + } else { + //判断新添加的标签名是否存在 + if ($id) { + return false; + } + + $insRes = ArticleTag::create(['user_id' => $uid, 'tag_name' => $tag_name, 'sort' => 1, 'created_at' => time()]); + if (!$insRes) { + return false; + } + + return $insRes->id; + } + } + + /** + * 删除笔记标签 + * + * @param int $uid 用户ID + * @param int $tag_id 标签ID + * @return bool + */ + public function delArticleTags(int $uid, int $tag_id) + { + if (!ArticleTag::where('id', $tag_id)->where('user_id', $uid)->exists()) { + return false; + } + + $count = Article::where('user_id', $uid)->whereRaw("FIND_IN_SET({$tag_id},tags_id)")->count(); + if ($count > 0) { + return false; + } + + return (bool)ArticleTag::where('id', $tag_id)->where('user_id', $uid)->delete(); + } + + /** + * 编辑文章信息 + * + * @param int $user_id 用户ID + * @param int $article_id 文章ID + * @param array $data 文章数据 + * @return bool + */ + public function editArticle(int $user_id, int $article_id, $data = []) + { + if ($article_id) { + if (!$info = Article::where('id', $article_id)->where('user_id', $user_id)->first()) { + return false; + } + + Db::beginTransaction(); + try { + Article::where('id', $article_id)->where('user_id', $user_id)->update([ + 'class_id' => $data['class_id'], + 'title' => $data['title'], + 'abstract' => $data['abstract'], + 'image' => $data['image'] ? $data['image'][0] : '', + 'updated_at' => date('Y-m-d H:i:s') + ]); + + ArticleDetail::where('article_id', $article_id)->update([ + 'md_content' => $data['md_content'], + 'content' => $data['content'] + ]); + + Db::commit(); + return $article_id; + } catch (Exception $e) { + Db::rollBack(); + } + + return false; + } + + Db::beginTransaction(); + try { + $res = Article::create([ + 'user_id' => $user_id, + 'class_id' => $data['class_id'], + 'title' => $data['title'], + 'abstract' => $data['abstract'], + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s') + ]); + + ArticleDetail::create([ + 'article_id' => $res->id, + 'md_content' => $data['md_content'], + 'content' => $data['content'] + ]); + + Db::commit(); + return $res->id; + } catch (Exception $e) { + Db::rollBack(); + + var_dump($e->getMessage()); + } + + return false; + } + + /** + * 更新笔记状态 + * + * @param int $user_id 用户ID + * @param int $article_id 笔记ID + * @param int $status 笔记状态 1:正常 2:已删除 + * @return bool + */ + public function updateArticleStatus(int $user_id, int $article_id, int $status) + { + $data = ['status' => $status]; + if ($status == 2) { + $data['deleted_at'] = date('Y-m-d H:i:s'); + } + + return Article::where('id', $article_id)->where('user_id', $user_id)->update($data); + } + + /** + * 笔记移动至指定分类 + * + * @param int $user_id 用户ID + * @param int $article_id 笔记ID + * @param int $class_id 笔记分类ID + * @return bool + */ + public function moveArticle(int $user_id, int $article_id, int $class_id) + { + return (boolean)Article::where('id', $article_id)->where('user_id', $user_id)->update(['class_id' => $class_id]); + } + + /** + * 笔记标记星号 + * + * @param int $user_id 用户ID + * @param int $article_id 笔记ID + * @param int $type 1:标记星号 2:取消星号标记 + * @return bool + */ + public function setAsteriskArticle(int $user_id, int $article_id, int $type) + { + return (boolean)Article::where('id', $article_id)->where('user_id', $user_id)->update([ + 'is_asterisk' => $type == 1 ? 1 : 0 + ]); + } + + /** + * 更新笔记关联标签 + * + * @param int $uid 用户ID + * @param int $article_id 笔记ID + * @param array $tags 关联标签ID + * @return bool + */ + public function updateArticleTag(int $uid, int $article_id, array $tags) + { + return (bool)Article::where('id', $article_id)->where('user_id', $uid)->update(['tags_id' => implode(',', $tags)]); + } + + /** + * 永久删除回收站中的笔记 + * + * @param int $uid 用户ID + * @param int $article_id 笔记ID + * @return bool|int|mixed|null + * @throws Exception + */ + public function foreverDelArticle(int $uid, int $article_id) + { + $info = Article::where('id', $article_id)->where('user_id', $uid)->where('status', 2)->first(['id', 'title']); + if (!$info) { + return false; + } + + $annex_files = $info->annexs()->get(['id', 'article_id', 'save_dir'])->toArray(); + + //判断笔记是否存在附件,不存在直接删除 + if (count($annex_files) == 0) { + return $info->delete(); + } + + Db::beginTransaction(); + try { + $info->detail->delete(); + + if (!$info->delete()) { + throw new Exception('删除笔记失败...'); + } + + if (!ArticleAnnex::whereIn('id', array_column($annex_files, 'id'))->delete()) { + throw new Exception('删除笔记附件失败...'); + } + + Db::commit(); + } catch (\Exception $e) { + Db::rollBack(); + return false; + } + + // 从磁盘中永久删除文件附件 + foreach ($annex_files as $item) { + //Storage::disk('uploads')->delete($item['save_dir']); + } + + return true; + } + + /** + * 更新笔记附件状态 + * + * @param int $user_id 用户ID + * @param int $annex_id 附件ID + * @param int $status 附件状态 1:正常 2:已删除 + * @return bool + */ + public function updateArticleAnnexStatus(int $user_id, int $annex_id, int $status) + { + $data = ['status' => $status]; + if ($status == 2) { + $data['deleted_at'] = date('Y-m-d H:i:s'); + } + + return ArticleAnnex::where('id', $annex_id)->where('user_id', $user_id)->update($data) ? true : false; + } + + /** + * 回收站附件列表 + * + * @param int $uid 用户ID + * @return array + */ + public function recoverAnnexList(int $uid) + { + return ArticleAnnex::join('article', 'article.id', '=', 'article_annex.article_id') + ->where('article_annex.user_id', $uid) + ->where('article_annex.status', 2) + ->get([ + 'article_annex.id', + 'article_annex.article_id', + 'article.title', + 'article_annex.original_name', + 'article_annex.deleted_at' + ])->toArray(); + } + + /** + * 永久删除笔记附件(从磁盘中永久删除) + * + * @param int $uid 用户ID + * @param int $annex_id 笔记附件ID + * @return bool|int|mixed|null + * @throws Exception + */ + public function foreverDelAnnex(int $uid, int $annex_id) + { + $info = ArticleAnnex::where('id', $annex_id)->where('user_id', $uid)->where('status', 2)->first(['id', 'save_dir']); + if (!$info) { + return false; + } + + // 将文件从磁盘中删除 +// if (!Storage::disk('uploads')->delete($info->save_dir)) { +// return false; +// } + + return $info->delete(); + } } diff --git a/app/Service/GroupService.php b/app/Service/GroupService.php index b8c4201..57fe6eb 100644 --- a/app/Service/GroupService.php +++ b/app/Service/GroupService.php @@ -2,8 +2,331 @@ namespace App\Service; +use App\Model\Chat\ChatRecord; +use App\Model\Chat\ChatRecordsInvite; +use App\Model\Group\UsersGroup; +use App\Model\Group\UsersGroupMember; +use App\Model\UsersChatList; +use Hyperf\DbConnection\Db; +use Exception; +/** + * Class GroupService + * @package App\Service + */ class GroupService extends BaseService { + /** + * 创建群组 + * + * @param int $user_id 用户ID + * @param array $group_info 群聊名称 + * @param array $friend_ids 好友的用户ID + * @return array + */ + public function create(int $user_id, array $group_info, $friend_ids = []) + { + $friend_ids[] = $user_id; + $groupMember = []; + $chatList = []; + Db::beginTransaction(); + try { + $insRes = UsersGroup::create([ + 'user_id' => $user_id, + 'group_name' => $group_info['name'], + 'avatar' => $group_info['avatar'], + 'group_profile' => $group_info['profile'], + 'status' => 0, + 'created_at' => date('Y-m-d H:i:s') + ]); + + if (!$insRes) { + throw new Exception('创建群失败'); + } + + foreach ($friend_ids as $k => $uid) { + $groupMember[] = [ + 'group_id' => $insRes->id, + 'user_id' => $uid, + 'group_owner' => ($k == 0) ? 1 : 0, + 'status' => 0, + 'created_at' => date('Y-m-d H:i:s'), + ]; + + $chatList[] = [ + 'type' => 2, + 'uid' => $uid, + 'friend_id' => 0, + 'group_id' => $insRes->id, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s') + ]; + } + + if (!Db::table('users_group_member')->insert($groupMember)) { + throw new Exception('创建群成员信息失败'); + } + + if (!Db::table('users_chat_list')->insert($chatList)) { + throw new Exception('创建群成员的聊天列表失败'); + } + + $result = ChatRecord::create([ + 'msg_type' => 3, + 'source' => 2, + 'user_id' => 0, + 'receive_id' => $insRes->id, + 'created_at' => date('Y-m-d H:i:s') + ]); + + if (!$result) { + throw new Exception('创建群成员的聊天列表失败'); + } + + ChatRecordsInvite::create([ + 'record_id' => $result->id, + 'type' => 1, + 'operate_user_id' => $user_id, + 'user_ids' => implode(',', $friend_ids) + ]); + + Db::commit(); + } catch (Exception $e) { + Db::rollBack(); + logger()->error($e); + return [false, 0]; + } + + // 设置群聊消息缓存 + //LastMsgCache::set(['created_at' => date('Y-m-d H:i:s'), 'text' => '入群通知'], $insRes->id, 0); + + return [true, ['record_id' => $result->id, 'group_id' => $insRes->id]]; + } + + /** + * 解散群组 + * + * @param int $group_id 群ID + * @param int $user_id 用户ID + * @return bool + */ + public function dismiss(int $group_id, int $user_id) + { + if (!UsersGroup::where('id', $group_id)->where('status', 0)->exists()) { + return false; + } + + //判断执行者是否属于群主 + if (!UsersGroup::isManager($user_id, $group_id)) { + return false; + } + + Db::beginTransaction(); + try { + + UsersGroup::where('id', $group_id)->update(['status' => 1]); + UsersGroupMember::where('group_id', $group_id)->update(['status' => 1]); + Db::commit(); + } catch (Exception $e) { + Db::rollBack(); + return false; + } + + return true; + } + + /** + * 邀请加入群组 + * + * @param int $user_id 用户ID + * @param int $group_id 聊天群ID + * @param array $friend_ids 被邀请的用户ID + * @return array + */ + public function invite(int $user_id, int $group_id, $friend_ids = []) + { + $info = UsersGroupMember::select(['id', 'status'])->where('group_id', $group_id)->where('user_id', $user_id)->first(); + + //判断主动邀请方是否属于聊天群成员 + if (!$info && $info->status == 1) { + return [false, 0]; + } + + if (empty($friend_ids)) { + return [false, 0]; + } + + $updateArr = $insertArr = $updateArr1 = $insertArr1 = []; + + $members = UsersGroupMember::where('group_id', $group_id)->whereIn('user_id', $friend_ids)->get(['id', 'user_id', 'status'])->keyBy('user_id')->toArray(); + $chatArr = UsersChatList::where('group_id', $group_id)->whereIn('uid', $friend_ids)->get(['id', 'uid', 'status'])->keyBy('uid')->toArray(); + + foreach ($friend_ids as $uid) { + if (!isset($members[$uid])) {//存在聊天群成员记录 + $insertArr[] = ['group_id' => $group_id, 'user_id' => $uid, 'group_owner' => 0, 'status' => 0, 'created_at' => date('Y-m-d H:i:s')]; + } else if ($members[$uid]['status'] == 1) { + $updateArr[] = $members[$uid]['id']; + } + + if (!isset($chatArr[$uid])) { + $insertArr1[] = ['type' => 2, 'uid' => $uid, 'friend_id' => 0, 'group_id' => $group_id, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')]; + } else if ($chatArr[$uid]['status'] == 0) { + $updateArr1[] = $chatArr[$uid]['id']; + } + } + + try { + if ($updateArr) { + UsersGroupMember::whereIn('id', $updateArr)->update(['status' => 0]); + } + + if ($insertArr) { + Db::table('users_group_member')->insert($insertArr); + } + + if ($updateArr1) { + UsersChatList::whereIn('id', $updateArr1)->update(['status' => 1, 'created_at' => date('Y-m-d H:i:s')]); + } + + if ($insertArr1) { + Db::table('users_chat_list')->insert($insertArr1); + } + + $result = ChatRecord::create([ + 'msg_type' => 3, + 'source' => 2, + 'user_id' => 0, + 'receive_id' => $group_id, + 'created_at' => date('Y-m-d H:i:s') + ]); + + if (!$result) throw new Exception('添加群通知记录失败1'); + + $result2 = ChatRecordsInvite::create([ + 'record_id' => $result->id, + 'type' => 1, + 'operate_user_id' => $user_id, + 'user_ids' => implode(',', $friend_ids) + ]); + + if (!$result2) throw new Exception('添加群通知记录失败2'); + + Db::commit(); + } catch (\Exception $e) { + Db::rollBack(); + + logger()->error($e); + return [false, 0]; + } + + //LastMsgCache::set(['created_at' => date('Y-m-d H:i:s'), 'text' => '入群通知'], $group_id, 0); + return [true, $result->id]; + } + + /** + * 退出群组 + * + * @param int $user_id 用户ID + * @param int $group_id 群组ID + * @return array + */ + public function quit(int $user_id, int $group_id) + { + $record_id = 0; + Db::beginTransaction(); + try { + $res = UsersGroupMember::where('group_id', $group_id)->where('user_id', $user_id)->where('group_owner', 0)->update(['status' => 1]); + if ($res) { + UsersChatList::where('uid', $user_id)->where('type', 2)->where('group_id', $group_id)->update(['status' => 0]); + + $result = ChatRecord::create([ + 'msg_type' => 3, + 'source' => 2, + 'user_id' => 0, + 'receive_id' => $group_id, + 'content' => $user_id, + 'created_at' => date('Y-m-d H:i:s') + ]); + + if (!$result) { + throw new Exception('添加群通知记录失败 : quitGroupChat'); + } + + $result2 = ChatRecordsInvite::create([ + 'record_id' => $result->id, + 'type' => 2, + 'operate_user_id' => $user_id, + 'user_ids' => $user_id + ]); + + if (!$result2) { + throw new Exception('添加群通知记录失败2 : quitGroupChat'); + } + + $record_id = $result->id; + } + + Db::commit(); + } catch (Exception $e) { + Db::rollBack(); + return [false, 0]; + } + + return [true, $record_id]; + } + + /** + * 踢出群组(管理员特殊权限) + * + * @param int $group_id 群ID + * @param int $user_id 操作用户ID + * @param array $member_ids 群成员ID + * @return array + */ + public function removeMember(int $group_id, int $user_id, array $member_ids) + { + if (!UsersGroup::isManager($user_id, $group_id)) { + return [false, 0]; + } + + Db::beginTransaction(); + try { + //更新用户状态 + if (!UsersGroupMember::where('group_id', $group_id)->whereIn('user_id', $member_ids)->where('group_owner', 0)->update(['status' => 1])) { + throw new Exception('修改群成员状态失败'); + } + + $result = ChatRecord::create([ + 'msg_type' => 3, + 'source' => 2, + 'user_id' => 0, + 'receive_id' => $group_id, + 'created_at' => date('Y-m-d H:i:s') + ]); + + if (!$result) { + throw new Exception('添加群通知记录失败1'); + } + + $result2 = ChatRecordsInvite::create([ + 'record_id' => $result->id, + 'type' => 3, + 'operate_user_id' => $user_id, + 'user_ids' => implode(',', $member_ids) + ]); + + if (!$result2) { + throw new Exception('添加群通知记录失败2'); + } + + Db::commit(); + } catch (Exception $e) { + Db::rollBack(); + return [false, 0]; + } + + return [true, $result->id]; + } } diff --git a/app/Supports/Http/Response.php b/app/Support/Http/Response.php similarity index 98% rename from app/Supports/Http/Response.php rename to app/Support/Http/Response.php index 45621ba..30d711f 100644 --- a/app/Supports/Http/Response.php +++ b/app/Support/Http/Response.php @@ -1,6 +1,6 @@ set(self::getLockKey($key), $requestId, 'NX', 'EX', $lockSecond); + if ($acquired) { + break; + } + + if ($timeout === 0) { + break; + } + + usleep($sleep); + } while (!is_numeric($timeout) || (self::getMicroTime()) < ($start + ($timeout * 1000000))); + + return $acquired ? true : false; + } + + /** + * 释放锁 + * + * @param string $key 被加锁的KEY + * @param string $requestId 客户端请求唯一ID + * @return bool + */ + public static function release(string $key, string $requestId) + { + if (strlen($key) === 0) { + return false; + } + + $lua = <<eval($lua, 1, self::getLockKey($key), $requestId); + } + + /** + * 获取锁 Key + * + * @param string $key 需要加锁的KEY + * @return string + */ + public static function getLockKey(string $key) + { + return self::PREFIX . ':' . $key; + } + + /** + * 获取当前微秒 + * + * @return string + */ + protected static function getMicroTime() + { + return bcmul(microtime(true), 1000000); + } +} diff --git a/app/helper.php b/app/helper.php index c8c058b..566736d 100644 --- a/app/helper.php +++ b/app/helper.php @@ -86,9 +86,9 @@ if (!function_exists('stdLog')) { * 文件日志 */ if (!function_exists('logger')) { - function logger() + function logger(string $name = 'APP') { - return container()->get(LoggerFactory::class)->make(); + return container()->get(LoggerFactory::class)->get($name); } } @@ -121,3 +121,42 @@ if (!function_exists('response')) { function create_password(string $password){ return password_hash($password, PASSWORD_DEFAULT); } + +/** + * 从HTML文本中提取所有图片 + * @param $content + * @return array + */ +function get_html_images($content) +{ + $pattern = "//"; + preg_match_all($pattern, htmlspecialchars_decode($content), $match); + $data = []; + if (!empty($match[1])) { + foreach ($match[1] as $img) { + if (!empty($img)) $data[] = $img; + } + return $data; + } + + return $data; +} + +/** + * 获取两个日期相差多少天 + * + * @param $day1 + * @param $day2 + * @return float|int + */ +function diff_date($day1, $day2) +{ + $second1 = strtotime($day1); + $second2 = strtotime($day2); + + if ($second1 < $second2) { + [$second1, $second2] = [$second2, $second1]; + } + + return ceil(($second1 - $second2) / 86400); +}