619 lines
19 KiB
PHP
619 lines
19 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Service;
|
|
|
|
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;
|
|
|
|
/**
|
|
* 笔记服务层
|
|
*
|
|
* @package App\Service
|
|
*/
|
|
class ArticleService extends BaseService
|
|
{
|
|
use PagingTrait;
|
|
|
|
/**
|
|
* 获取用户文章标签列表
|
|
*
|
|
* @param int $user_id 用户ID
|
|
* @return array
|
|
*/
|
|
public function getUserTags(int $user_id): array
|
|
{
|
|
$items = ArticleTag::where('user_id', $user_id)->orderBy('id', 'desc')->get(['id', 'tag_name', Db::raw('0 as count')])->toArray();
|
|
foreach ($items as $k => $item) {
|
|
$items[$k]['count'] = Article::where('user_id', $user_id)->whereRaw("FIND_IN_SET({$item['id']},tags_id)")->count();
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* 获取用户文章列表
|
|
*
|
|
* @param int $user_id 用户ID
|
|
* @param int $page 分页
|
|
* @param int $page_size 分页大小
|
|
* @param array $params 查询参数
|
|
* @return array
|
|
*/
|
|
public function getUserArticleList(int $user_id, int $page, int $page_size, $params = []): array
|
|
{
|
|
$filed = ['article.id', 'article.class_id', 'article.title', 'article.image', 'article.abstract', 'article.updated_at', 'article_class.class_name', 'article.status'];
|
|
|
|
$model = Article::select($filed)
|
|
->leftJoin('article_class', 'article_class.id', '=', 'article.class_id');
|
|
|
|
$model->where('article.user_id', $user_id);
|
|
|
|
if ($params['find_type'] == 3) {
|
|
$model->where('article.class_id', $params['class_id']);
|
|
} else if ($params['find_type'] == 4) {
|
|
$model->whereRaw("FIND_IN_SET({$params['class_id']},tags_id)");
|
|
} else if ($params['find_type'] == 2) {
|
|
$model->where('article.is_asterisk', 1);
|
|
}
|
|
|
|
$model->where('article.status', $params['find_type'] == 5 ? 2 : 1);
|
|
|
|
if (isset($params['keyword']) && !empty($params['keyword'])) {
|
|
$model->where('article.title', 'like', "%{$params['keyword']}%");
|
|
}
|
|
|
|
$count = $model->count();
|
|
$rows = [];
|
|
if ($count > 0) {
|
|
if ($params['find_type'] == 1) {
|
|
$model->orderBy('updated_at', 'desc');
|
|
$page_size = 20;
|
|
} else {
|
|
$model->orderBy('id', 'desc');
|
|
}
|
|
|
|
$rows = $model->forPage($page, $page_size)->get()->toArray();
|
|
}
|
|
|
|
return $this->getPagingRows($rows, $count, $page, $page_size);
|
|
}
|
|
|
|
/**
|
|
* 获取文章详情
|
|
*
|
|
* @param int $article_id 文章ID
|
|
* @param int $user_id 用户ID
|
|
* @return array
|
|
*/
|
|
public function getArticleDetail(int $article_id, $user_id = 0): array
|
|
{
|
|
$info = Article::where('id', $article_id)->where('user_id', $user_id)->first(['id', 'class_id', 'tags_id', 'title', 'status', 'is_asterisk', 'created_at', 'updated_at']);
|
|
|
|
if (!$info) return [];
|
|
|
|
// 关联文章详情
|
|
$detail = $info->detail;
|
|
if (!$detail) return [];
|
|
|
|
$tags = [];
|
|
if ($info->tags_id) {
|
|
$tags = ArticleTag::whereIn('id', explode(',', $info->tags_id))->get(['id', 'tag_name']);
|
|
}
|
|
|
|
return [
|
|
'id' => $article_id,
|
|
'class_id' => $info->class_id,
|
|
'title' => $info->title,
|
|
'md_content' => htmlspecialchars_decode($detail->md_content),
|
|
'content' => htmlspecialchars_decode($detail->content),
|
|
'is_asterisk' => $info->is_asterisk,
|
|
'status' => $info->status,
|
|
'created_at' => $info->created_at,
|
|
'updated_at' => $info->updated_at,
|
|
'tags' => $tags,
|
|
'files' => $this->findArticleAnnexAll($user_id, $article_id)
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 获取笔记附件
|
|
*
|
|
* @param int $user_id 用户ID
|
|
* @param int $article_id 笔记ID
|
|
* @return array
|
|
*/
|
|
public function findArticleAnnexAll(int $user_id, int $article_id): array
|
|
{
|
|
return ArticleAnnex::where([
|
|
['user_id', '=', $user_id],
|
|
['article_id', '=', $article_id],
|
|
['status', '=', 1]
|
|
])->get(['id', 'suffix', 'size', 'original_name', 'created_at'])->toArray();
|
|
}
|
|
|
|
/**
|
|
* 编辑笔记分类
|
|
*
|
|
* @param int $uid 用户ID
|
|
* @param int|string $class_id 分类ID
|
|
* @param string $class_name 分类名
|
|
* @return bool|int
|
|
*/
|
|
public function editArticleClass(int $uid, $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']]);
|
|
}
|
|
|
|
$res = ArticleClass::create(['user_id' => $uid, 'class_name' => $class_name, 'sort' => 1, 'created_at' => date("y-m-d H:i:s")]);
|
|
|
|
Db::commit();
|
|
return $res->id;
|
|
} catch (Exception $e) {
|
|
Db::rollBack();
|
|
|
|
logger()->info("编辑标签失败", [
|
|
"error" => $e->getMessage(),
|
|
"line" => $e->getLine(),
|
|
"file" => $e->getFile(),
|
|
]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除笔记分类
|
|
*
|
|
* @param int $user_id 用户ID
|
|
* @param int $class_id 分类ID
|
|
* @return bool
|
|
* @throws Exception
|
|
*/
|
|
public function delArticleClass(int $user_id, int $class_id): bool
|
|
{
|
|
$result = ArticleClass::where('id', $class_id)->where('user_id', $user_id)->first(['id', 'sort']);
|
|
if (!$result) return false;
|
|
|
|
$count = Article::where('user_id', $user_id)->where('class_id', $class_id)->count();
|
|
if ($count > 0) return false;
|
|
|
|
Db::transaction(function () use ($user_id, $class_id, $result) {
|
|
ArticleClass::where('id', $class_id)->where('user_id', $user_id)->where('is_default', 0)->delete();
|
|
ArticleClass::where('user_id', $user_id)->where('sort', '>', $result->sort)->decrement('sort');
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 文集分类排序
|
|
*
|
|
* @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): bool
|
|
{
|
|
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): bool
|
|
{
|
|
$count = ArticleClass::whereIn('id', [$class_id, $to_class_id])->where('user_id', $user_id)->count();
|
|
if ($count < 2) {
|
|
return false;
|
|
}
|
|
|
|
return (bool)Article::where('class_id', $class_id)->where('user_id', $user_id)->update([
|
|
'class_id' => $to_class_id,
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 编辑笔记标签
|
|
*
|
|
* @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]);
|
|
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): bool
|
|
{
|
|
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;
|
|
|
|
try {
|
|
return (bool)ArticleTag::where('id', $tag_id)->where('user_id', $uid)->delete();
|
|
} catch (Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 编辑文章信息
|
|
*
|
|
* @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 (!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();
|
|
}
|
|
|
|
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): bool
|
|
{
|
|
$data = [
|
|
'status' => $status,
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
];
|
|
|
|
if ($status == 2) {
|
|
$data['deleted_at'] = date('Y-m-d H:i:s');
|
|
}
|
|
|
|
return (bool)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): bool
|
|
{
|
|
return (bool)Article::where('id', $article_id)->where('user_id', $user_id)->update([
|
|
'class_id' => $class_id,
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 笔记标记星号
|
|
*
|
|
* @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): bool
|
|
{
|
|
return (bool)Article::where('id', $article_id)->where('user_id', $user_id)->update([
|
|
'is_asterisk' => $type == 1 ? 1 : 0,
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 更新笔记关联标签
|
|
*
|
|
* @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): bool
|
|
{
|
|
return (bool)Article::where('id', $article_id)->where('user_id', $uid)->update([
|
|
'tags_id' => implode(',', $tags),
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 永久删除回收站中的笔记
|
|
*
|
|
* @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', 'path'])->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): bool
|
|
{
|
|
$data = ['status' => $status];
|
|
if ($status == 2) {
|
|
$data['deleted_at'] = date('Y-m-d H:i:s');
|
|
}
|
|
|
|
return (bool)ArticleAnnex::where('id', $annex_id)->where('user_id', $user_id)->update($data);
|
|
}
|
|
|
|
/**
|
|
* 回收站附件列表
|
|
*
|
|
* @param int $uid 用户ID
|
|
* @return array
|
|
*/
|
|
public function recoverAnnexList(int $uid): array
|
|
{
|
|
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 mixed
|
|
* @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', 'path']);
|
|
if (!$info) {
|
|
return false;
|
|
}
|
|
|
|
return $info->delete();
|
|
}
|
|
|
|
/**
|
|
* 添加笔记附件
|
|
*
|
|
* @param int $user_id 用户id
|
|
* @param int $article_id 笔记ID
|
|
* @param array $annex 笔记附件信息
|
|
* @return bool|int
|
|
*/
|
|
public function insertArticleAnnex(int $user_id, int $article_id, array $annex)
|
|
{
|
|
if (!Article::where('id', $article_id)->where('user_id', $user_id)->exists()) {
|
|
return false;
|
|
}
|
|
|
|
$result = ArticleAnnex::create([
|
|
'user_id' => $user_id,
|
|
'article_id' => $article_id,
|
|
'suffix' => $annex['suffix'],
|
|
'size' => $annex['size'],
|
|
'drive' => $annex['drive'],
|
|
'path' => $annex['path'],
|
|
'original_name' => $annex['original_name'],
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
]);
|
|
|
|
return $result ? $result->id : false;
|
|
}
|
|
}
|