初始化

main
gzydong 2020-11-05 17:40:51 +08:00
parent bc29ea47a9
commit 84e1dfc7a8
42 changed files with 2184 additions and 339 deletions

View File

@ -75,7 +75,7 @@ class ChatMessageConsumer extends ConsumerMessage
}
}
return Result::ACK;
return Result::NACK;
}
/**

View File

@ -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('笔记附件删除失败...');
}
}

View File

@ -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(

View File

@ -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;

View File

@ -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('公告删除失败...');
}
}

View File

@ -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,

29
app/Helper/Hash.php Normal file
View File

@ -0,0 +1,29 @@
<?php
namespace App\Helper;
class Hash
{
/**
* Hash the given value.
*
* @param string $value
* @return string
*/
public function make($value)
{
return password_hash($value, PASSWORD_DEFAULT);
}
/**
* Check the given plain value against a hash.
*
* @param string $value
* @param string $hashedValue
* @return bool
*/
public function check($value, $hashedValue)
{
return password_verify($value, $hashedValue);
}
}

View File

@ -1,44 +0,0 @@
<?php
declare (strict_types=1);
namespace App\Model;
/**
* @property int $id
* @property int $user_id
* @property int $class_id
* @property string $tags_id
* @property string $title
* @property string $abstract
* @property string $image
* @property int $is_asterisk
* @property int $status
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string $deleted_at
*/
class Article extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'article';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'class_id' => 'integer', 'is_asterisk' => 'integer', 'status' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime'];
}

View File

@ -0,0 +1,87 @@
<?php
declare (strict_types=1);
namespace App\Model\Article;
use App\Model\BaseModel;
/**
* 笔记数据表模型
*
* @property int $id 笔记ID
* @property int $user_id 用户ID
* @property int $class_id 分类ID
* @property string $tags_id 笔记标签ID
* @property string $title 笔记标题
* @property string $abstract 笔记摘要
* @property string $image 笔记头图
* @property int $is_asterisk 是否标记星号
* @property int $status 笔记状态
* @property string $created_at 创建时间
* @property string $updated_at 更新时间
* @property string $deleted_at 删除时间
*
* @package App\Model\Article
*/
class Article extends BaseModel
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'article';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'user_id',
'class_id',
'title',
'abstract',
'image',
'is_asterisk',
'status',
'created_at',
'updated_at',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'id' => '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');
}
}

View File

@ -0,0 +1,56 @@
<?php
declare (strict_types=1);
namespace App\Model\Article;
use App\Model\BaseModel;
/**
* 笔记附件数据表模型
*
* @property int $id 笔记附件ID
* @property int $user_id 用户ID
* @property int $article_id 笔记ID
* @property string $file_suffix 文件后缀名
* @property int $file_size 文件大小
* @property string $save_dir 文件相对路径
* @property string $original_name 文件原名
* @property integer $status 文件状态
* @property string $created_at 上传时间
* @property string $deleted_at 删除时间
*
* @package App\Model\Article
*/
class ArticleAnnex extends BaseModel
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'article_annex';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'user_id', 'article_id', 'file_size', 'status', 'created_at'
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'id' => 'integer',
'user_id' => 'integer',
'article_id' => 'integer',
'file_size' => 'integer',
'status' => 'integer',
'created_at' => 'datetime'
];
}

View File

@ -0,0 +1,55 @@
<?php
declare (strict_types=1);
namespace App\Model\Article;
use App\Model\BaseModel;
/**
* 笔记分类数据表模型
*
* @property int $id 分类ID
* @property int $user_id 用户ID
* @property string $class_name 分类名
* @property int $sort 排序[值越小越靠前]
* @property int $is_default 默认分类[1:;0:不是]
* @property string $created_at 创建时间
*
* @package App\Model\Article
*/
class ArticleClass extends BaseModel
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'article_class';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'user_id',
'class_name',
'sort',
'is_default',
'created_at',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'id' => 'integer',
'user_id' => 'integer',
'sort' => 'integer',
'is_default' => 'integer',
'created_at' => 'int'
];
}

View File

@ -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'
];
}

View File

@ -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'
];
}

View File

@ -1,41 +0,0 @@
<?php
declare (strict_types=1);
namespace App\Model;
/**
* @property int $id
* @property int $user_id
* @property int $article_id
* @property string $file_suffix
* @property int $file_size
* @property string $save_dir
* @property string $original_name
* @property int $status
* @property \Carbon\Carbon $created_at
* @property string $deleted_at
*/
class ArticleAnnex extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'article_annex';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'article_id' => 'integer', 'file_size' => 'integer', 'status' => 'integer', 'created_at' => 'datetime'];
}

View File

@ -1,44 +0,0 @@
<?php
declare (strict_types=1);
namespace App\Model;
/**
* @property int $id
* @property int $user_id
* @property string $class_name
* @property int $sort
* @property int $is_default
* @property \Carbon\Carbon $created_at
*/
class ArticleClass extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'article_class';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'user_id',
'class_name',
'sort',
'is_default',
'created_at',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'sort' => 'integer', 'is_default' => 'integer', 'created_at' => 'int'];
}

View File

@ -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;
}

View File

@ -0,0 +1,61 @@
<?php
declare (strict_types=1);
namespace App\Model\Chat;
use App\Model\BaseModel;
/**
* 聊天记录数据表模型
*
* @property int $id 聊天消息ID
* @property int $source 消息来源[1:好友消息;2:群聊消息]
* @property int $msg_type 消息类型[1:文本消息;2:文件消息;3:入群消息/退群消息;4:会话记录消息;5:代码块消息]
* @property int $user_id 发送者ID[0:代表系统消息; >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'
];
}

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,52 @@
<?php
declare (strict_types=1);
namespace App\Model\Chat;
use App\Model\BaseModel;
/**
* 聊天记录(入群/退群消息)数据表模型
*
* @property int $id 入群或退群通知ID
* @property int $record_id 消息记录ID
* @property int $type 通知类型[1:入群通知;2:自动退群;3:管理员踢群]
* @property int $operate_user_id 操作人的用户ID[邀请人OR管理员ID]
* @property string $user_ids 用户ID(多个用 , 分割)
*
* @package App\Model\Chat
*/
class ChatRecordsInvite extends BaseModel
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'chat_records_invite';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'record_id',
'type',
'operate_user_id',
'user_ids',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'id' => 'integer',
'record_id' => 'integer',
'type' => 'integer',
'operate_user_id' => 'integer'
];
}

View File

@ -1,39 +0,0 @@
<?php
declare (strict_types=1);
namespace App\Model;
/**
* @property int $id
* @property int $source
* @property int $msg_type
* @property int $user_id
* @property int $receive_id
* @property string $content
* @property int $is_revoke
* @property \Carbon\Carbon $created_at
*/
class ChatRecord extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'chat_records';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [];
/**
* 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'];
}

View File

@ -1,36 +0,0 @@
<?php
declare (strict_types=1);
namespace App\Model;
/**
* @property int $id
* @property int $record_id
* @property int $type
* @property int $operate_user_id
* @property string $user_ids
*/
class ChatRecordsInvite extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'chat_records_invite';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = ['id' => 'integer', 'record_id' => 'integer', 'type' => 'integer', 'operate_user_id' => 'integer'];
}

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,87 @@
<?php
declare (strict_types=1);
namespace App\Model\Group;
use App\Model\BaseModel;
/**
* 用户群组数据表模型
*
* @property int $id 群ID
* @property int $user_id 群组ID
* @property string $group_name 群名称
* @property string $group_profile 群简介
* @property int $status 群状态
* @property string $avatar 群头像
* @property string $created_at 创建时间
*
* @package App\Model\Group
*/
class UsersGroup extends BaseModel
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'users_group';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'user_id',
'group_name',
'group_profile',
'status',
'avatar',
'created_at',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'id' => '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;
}
}

View File

@ -0,0 +1,70 @@
<?php
declare (strict_types=1);
namespace App\Model\Group;
use App\Model\BaseModel;
/**
* @property int $id
* @property int $group_id
* @property int $user_id
* @property int $group_owner
* @property int $status
* @property string $visit_card
* @property \Carbon\Carbon $created_at
*/
class UsersGroupMember extends BaseModel
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'users_group_member';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'id' => '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');
}
}

View File

@ -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.

View File

@ -16,7 +16,7 @@ namespace App\Model;
*
* @package App\Model
*/
class User extends Model
class User extends BaseModel
{
/**
* The table associated with the model.

View File

@ -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'
];
}

View File

@ -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.

View File

@ -1,8 +1,11 @@
<?php
declare (strict_types=1);
namespace App\Model;
use Hyperf\DbConnection\Db;
/**
* @property int $id
* @property int $user1
@ -14,7 +17,7 @@ namespace App\Model;
* @property string $agree_time
* @property \Carbon\Carbon $created_at
*/
class UsersFriend extends Model
class UsersFriend extends BaseModel
{
/**
* The table associated with the model.
@ -36,4 +39,64 @@ class UsersFriend extends Model
* @var array
*/
protected $casts = ['id' => '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 = <<<SQL
SELECT users.id,users.nickname,users.avatar,users.motto,users.gender,tmp_table.friend_remark from {$prefix}users users
INNER join
(
SELECT id as rid,user2 as uid,user1_remark as friend_remark from {$prefix}users_friends where user1 = {$uid} and `status` = 1
UNION all
SELECT id as rid,user1 as uid,user2_remark as friend_remark from {$prefix}users_friends where user2 = {$uid} and `status` = 1
) tmp_table on tmp_table.uid = users.id order by tmp_table.rid desc
SQL;
$rows = Db::select($sql);
array_walk($rows, function (&$item) {
$item = (array)$item;
});
return $rows;
}
/**
* 判断用户之间是否存在好友关系
*
* @param int $user_id1 用户1
* @param int $user_id2 用户2
* @return bool
*/
public static function isFriend(int $user_id1, int $user_id2)
{
// 比较大小交换位置
if ($user_id1 > $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));
}
}

View File

@ -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.

View File

@ -1,38 +0,0 @@
<?php
declare (strict_types=1);
namespace App\Model;
use Hyperf\DbConnection\Model\Model;
/**
* @property int $id
* @property int $user_id
* @property string $group_name
* @property string $group_profile
* @property int $status
* @property string $avatar
* @property \Carbon\Carbon $created_at
*/
class UsersGroup extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'users_group';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'status' => 'integer', 'created_at' => 'datetime'];
}

View File

@ -1,37 +0,0 @@
<?php
declare (strict_types=1);
namespace App\Model;
/**
* @property int $id
* @property int $group_id
* @property int $user_id
* @property int $group_owner
* @property int $status
* @property string $visit_card
* @property \Carbon\Carbon $created_at
*/
class UsersGroupMember extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'users_group_member';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = ['id' => 'integer', 'group_id' => 'integer', 'user_id' => 'integer', 'group_owner' => 'integer', 'status' => 'integer', 'created_at' => 'datetime'];
}

View File

@ -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();
}
}

View File

@ -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];
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace App\Supports\Http;
namespace App\Support\Http;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\ResponseInterface;

109
app/Support/RedisLock.php Normal file
View File

@ -0,0 +1,109 @@
<?php
namespace App\Support;
/**
* Class RedisLock
* @package App\Support
*/
class RedisLock
{
/**
* 锁前缀标识
*/
const PREFIX = 'lock';
/**
* 获取Redis连接
*
* @return mixed|\Redis
*/
public static function getRedis()
{
return redis();
}
/**
* 获得锁,如果锁被占用,阻塞,直到获得锁或者超时。
* -- 1、如果 $timeout 参数为 0,则立即返回锁。
* -- 2、建议 timeout 设置为 0,避免 redis 因为阻塞导致性能下降。请根据实际需求进行设置。
*
* @param string $key 缓存KEY
* @param string $requestId 客户端请求唯一ID
* @param int $lockSecond 锁定时间 单位()
* @param int $timeout 取锁超时时间。单位()。等于0,如果当前锁被占用,则立即返回失败。如果大于0,则反复尝试获取锁直到达到该超时时间。
* @param int $sleep 取锁间隔时间 单位(微秒)。当锁为占用状态时。每隔多久尝试去取锁。默认 0.1 秒一次取锁。
* @return bool
* @throws \Exception
*/
public static function lock(string $key, string $requestId, $lockSecond = 20, $timeout = 0, $sleep = 100000)
{
if (empty($key)) {
throw new \Exception('获取锁的KEY值没有设置');
}
$start = self::getMicroTime();
$redis = self::getRedis();
do {
$acquired = $redis->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 = <<<LAU
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
LAU;
return self::getRedis()->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);
}
}

View File

@ -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 = "/<img.*?src=[\'|\"](.*?)[\'|\"].*?[\/]?>/";
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);
}