初始化

main
gzydong 2020-11-04 22:58:49 +08:00
parent 5ff02a48c7
commit bc29ea47a9
9 changed files with 592 additions and 14 deletions

View File

@ -2,8 +2,300 @@
namespace App\Controller\Api\V1;
use App\Service\ArticleService;
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;
/**
* Class ArticleController
*
* @Controller(path="/api/v1/article")
* @Middleware(JWTAuthMiddleware::class)
*
* @package App\Controller\Api\V1
*/
class ArticleController extends CController
{
/**
* @Inject
* @var ArticleService
*/
private $articleService;
/**
* 获取笔记分类列表
*
* @RequestMapping(path="article-class", methods="get")
*/
public function getArticleClass()
{
$user_id = $this->uid();
return $this->response->success(
$this->articleService->getUserClass($user_id)
);
}
/**
* 获取笔记标签列表
*
* @RequestMapping(path="article-tags", methods="get")
*/
public function getArticleTags()
{
$user_id = $this->uid();
return $this->response->success(
$this->articleService->getUserTags($user_id)
);
}
/**
* 获取笔记列表
*
* @RequestMapping(path="article-list", methods="get")
*/
public function getArticleList()
{
$this->validate($this->request->all(), [
'keyword' => "present",
'find_type' => 'required|in:0,1,2,3,4,5,6',
'cid' => 'present|integer|min:-1',
'page' => 'present|integer|min:1'
]);
// 查询类型 $findType 1:获取近期日记 2:获取星标日记 3:获取指定分类文章 4:获取指定标签文章 5:获取已删除文章 6:关键词搜索
$findType = $this->request->input('find_type', 0);
$keyword = $this->request->input('keyword', '');// 搜索关键词
$cid = $this->request->input('cid', -1);// 分类ID
$page = $this->request->input('page', 1);
$user_id = $this->uid();
$params = [];
$params['find_type'] = $findType;
if (in_array($findType, [3, 4])) {
$params['class_id'] = $cid;
}
if (!empty($keyword)) {
$params['keyword'] = $keyword;
}
return $this->response->success(
$this->articleService->getUserArticleList($user_id, $page, 10000, $params)
);
}
/**
* 获取笔记详情
*
* @RequestMapping(path="article-detail", methods="get")
*/
public function getArticleDetail()
{
$this->validate($this->request->all(), [
'article_id' => 'required|integer',
]);
$data = $this->articleService->getArticleDetail(
$this->request->input('article_id'),
$this->uid()
);
return $this->response->success($data);
}
/**
* 添加或编辑笔记分类
*
* @RequestMapping(path="edit-article-class", methods="post")
*/
public function editArticleClass()
{
}
/**
* 删除笔记分类
*
* @RequestMapping(path="del-article-class", methods="post")
*/
public function delArticleClass()
{
}
/**
* 笔记分类列表排序接口
*
* @RequestMapping(path="article-class-sort", methods="post")
*/
public function articleClassSort()
{
}
/**
* 笔记分类合并接口
*
* @RequestMapping(path="merge-article-class", methods="post")
*/
public function mergeArticleClass()
{
}
/**
* 添加或编辑笔记标签
*
* @RequestMapping(path="edit-article-tag", methods="post")
*/
public function editArticleTags()
{
}
/**
* 删除笔记标签
*
* @RequestMapping(path="del-article-tag", methods="post")
*/
public function delArticleTags()
{
}
/**
* 编辑笔记信息
*
* @RequestMapping(path="edit-article", methods="post")
*/
public function editArticle()
{
}
/**
* 删除笔记
*
* @RequestMapping(path="delete-article", methods="post")
*/
public function deleteArticle()
{
}
/**
* 恢复笔记
*
* @RequestMapping(path="recover-article", methods="post")
*/
public function recoverArticle()
{
}
/**
* 笔记图片上传接口
*
* @RequestMapping(path="upload-article-image", methods="post")
*/
public function uploadArticleImage()
{
}
/**
* 移动笔记至指定分类
*
* @RequestMapping(path="move-article", methods="post")
*/
public function moveArticle()
{
}
/**
* 笔记标记星号接口
*
* @RequestMapping(path="set-asterisk-article", methods="post")
*/
public function setAsteriskArticle()
{
}
/**
* 更新笔记关联标签ID
*
* @RequestMapping(path="update-article-tag", methods="post")
*/
public function updateArticleTag()
{
}
/**
* 永久删除笔记文章
*
* @RequestMapping(path="forever-delete-article", methods="post")
*/
public function foreverDelArticle()
{
}
/**
* 上传笔记附件
*
* @RequestMapping(path="upload-article-annex", methods="post")
*/
public function uploadArticleAnnex()
{
}
/**
* 删除笔记附件
*
* @RequestMapping(path="delete-article-annex", methods="post")
*/
public function deleteArticleAnnex()
{
}
/**
* 恢复笔记附件
*
* @RequestMapping(path="recover-article-annex", methods="post")
*/
public function recoverArticleAnnex()
{
}
/**
* 获取附件回收站列表
*
* @RequestMapping(path="recover-annex-list", methods="get")
*/
public function recoverAnnexList()
{
}
/**
* 永久删除笔记附件(从已删除附件中永久删除)
*
* @RequestMapping(path="forever-delete-annex", methods="get")
*/
public function foreverDelAnnex()
{
}
}

View File

@ -3,6 +3,7 @@
namespace App\Controller\Api\V1;
use App\Constants\ResponseCode;
use App\Model\User;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\RequestMapping;
@ -31,6 +32,12 @@ class AuthController extends CController
*/
private $jwt;
/**
* @Inject
* @var SmsCodeService
*/
private $smsCodeService;
/**
* 授权登录接口
*
@ -54,6 +61,7 @@ class AuthController extends CController
$this->request->input('mobile'),
$this->request->input('password')
);
if (!$userInfo) {
return $this->response->fail('账号不存在或密码填写错误...', ResponseCode::FAIL);
}
@ -100,10 +108,9 @@ class AuthController extends CController
*
* @RequestMapping(path="register", methods="post")
*
* @param SmsCodeService $smsCodeService
* @return \Psr\Http\Message\ResponseInterface
*/
public function register(SmsCodeService $smsCodeService)
public function register()
{
$params = $this->request->all();
$this->validate($params, [
@ -114,8 +121,8 @@ class AuthController extends CController
'platform' => 'required|in:h5,ios,windows,mac',
]);
if (!$smsCodeService->check('user_register', $params['mobile'], $params['sms_code'])) {
return $this->response->fail('验证码填写错误...');
if (!$this->smsCodeService->check('user_register', $params['mobile'], $params['sms_code'])) {
//return $this->response->fail('验证码填写错误...');
}
$isTrue = $this->userService->register([
@ -124,11 +131,12 @@ class AuthController extends CController
'nickname' => strip_tags($params['nickname']),
]);
if ($isTrue) {
$smsCodeService->delCode('user_register', $params['mobile']);
if (!$isTrue) {
return $this->response->fail('账号注册失败...');
}
return $this->response->success([], 'Successfully logged out');
$this->smsCodeService->delCode('user_register', $params['mobile']);
return $this->response->success([], '账号注册成功...');
}
/**
@ -138,7 +146,24 @@ class AuthController extends CController
*/
public function forget()
{
$params = $this->request->all();
$this->validate($params, [
'mobile' => "required|regex:/^1[345789][0-9]{9}$/",
'password' => 'required',
'sms_code' => 'required|integer|max:999999',
]);
if (!$this->smsCodeService->check('forget_password', $params['mobile'], $params['sms_code'])) {
return $this->response->fail('验证码填写错误...', ResponseCode::FAIL);
}
$isTrue = $this->userService->resetPassword($params['mobile'], $params['password']);
if ($isTrue) {
$this->smsCodeService->delCode('forget_password', $params['mobile']);
return $this->response->success([], '账号注册成功...');
}
return $this->response->fail('重置密码失败...', ResponseCode::FAIL);
}
/**
@ -156,4 +181,45 @@ class AuthController extends CController
]
], '刷新 Token 成功...');
}
/**
* 发送验证码
*
* @RequestMapping(path="send-code", methods="post")
*
* @return \Psr\Http\Message\ResponseInterface
*/
public function sendVerifyCode()
{
$params = $this->request->all();
$this->validate($params, [
'type' => "required",
'mobile' => "required|regex:/^1[345789][0-9]{9}$/"
]);
if (!$this->smsCodeService->isUsages($params['type'])) {
return $this->response->fail('验证码发送失败...');
}
if ($params['type'] == 'forget_password') {
if (!User::where('mobile', $params['mobile'])->value('id')) {
return $this->response->fail('手机号未被注册使用...');
}
} else if ($params['type'] == 'change_mobile' || $params['type'] == 'user_register') {
if (User::where('mobile', $params['mobile'])->value('id')) {
return $this->response->fail('手机号已被他(她)人注册...');
}
}
$data = ['is_debug' => true];
[$isTrue, $result] = $this->smsCodeService->send($params['type'], $params['mobile']);
if ($isTrue) {
// 测试环境下直接返回验证码
$data['sms_code'] = $result['data']['code'];
} else {
// ... 处理发送失败逻辑,当前默认发送成功
}
return $this->response->success($data, '验证码发送成功...');
}
}

View File

@ -5,6 +5,7 @@ namespace App\Controller\Api\V1;
use App\Controller\AbstractController;
use App\Supports\Http\Response;
use Hyperf\Di\Annotation\Inject;
use Phper666\JWTAuth\JWT;
/**
* 基类控制器
@ -19,4 +20,14 @@ class CController extends AbstractController
* @var Response
*/
protected $response;
/**
* 获取当前登录用户ID
*
* @return int
*/
public function uid(){
$data = container()->get(JWT::class)->getParserData();
return $data['user_id'];
}
}

View File

@ -26,12 +26,19 @@ class ArticleClass extends Model
*
* @var array
*/
protected $fillable = [];
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' => 'datetime'];
protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'sort' => 'integer', 'is_default' => 'integer', 'created_at' => 'int'];
}

View File

@ -30,7 +30,9 @@ class User extends Model
*
* @var array
*/
protected $fillable = [];
protected $fillable = [
'mobile','nickname','avatar','gender','password','motto','email','created_at'
];
/**
* The attributes that should be cast to native types.

View File

@ -3,7 +3,160 @@
namespace App\Service;
use App\Model\Article;
use App\Model\ArticleClass;
use App\Model\ArticleTag;
use App\Model\ArticleAnnex;
use App\Traits\PagingTrait;
use Hyperf\DbConnection\Db;
class ArticleService extends BaseService
{
use PagingTrait;
/**
* 获取用户文章分类列表
*
* @param int $user_id 用户ID
* @return array
*/
public function getUserClass(int $user_id)
{
$subJoin = Article::select('class_id', Db::raw('count(class_id) as count'))->where('user_id', $user_id)->where('status', 1)->groupBy('class_id');
return ArticleClass::leftJoinSub($subJoin, 'sub_join', function ($join) {
$join->on('article_class.id', '=', Db::raw('sub_join.class_id'));
})->where('article_class.user_id', $user_id)
->orderBy('article_class.sort', 'asc')
->get(['article_class.id', 'article_class.class_name', 'article_class.is_default', Db::raw('sub_join.count')])
->toArray();
}
/**
* 获取用户文章标签列表
*
* @param int $user_id 用户ID
* @return mixed
*/
public function getUserTags(int $user_id)
{
$items = ArticleTag::where('user_id', $user_id)->orderBy('id', 'desc')->get(['id', 'tag_name'])->toArray();
array_walk($items, function ($item) use ($user_id) {
$item['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 = [])
{
$filed = ['article.id', 'article.class_id', 'article.title', 'article.image', 'article.abstract', 'article.updated_at', 'article_class.class_name', 'article.status'];
$countSqlObj = Article::select();
$rowsSqlObj = Article::select($filed)
->leftJoin('article_class', 'article_class.id', '=', 'article.class_id');
$countSqlObj->where('article.user_id', $user_id);
$rowsSqlObj->where('article.user_id', $user_id);
if ($params['find_type'] == 3) {
$countSqlObj->where('article.class_id', $params['class_id']);
$rowsSqlObj->where('article.class_id', $params['class_id']);
} else if ($params['find_type'] == 4) {
$countSqlObj->whereRaw("FIND_IN_SET({$params['class_id']},tags_id)");
$rowsSqlObj->whereRaw("FIND_IN_SET({$params['class_id']},tags_id)");
} else if ($params['find_type'] == 2) {
$countSqlObj->where('article.is_asterisk', 1);
$rowsSqlObj->where('article.is_asterisk', 1);
}
$countSqlObj->where('article.status', $params['find_type'] == 5 ? 2 : 1);
$rowsSqlObj->where('article.status', $params['find_type'] == 5 ? 2 : 1);
if (isset($params['keyword'])) {
$countSqlObj->where('article.title', 'like', "%{$params['keyword']}%");
$rowsSqlObj->where('article.title', 'like', "%{$params['keyword']}%");
}
$count = $countSqlObj->count();
$rows = [];
if ($count > 0) {
if ($params['find_type'] == 1) {
$rowsSqlObj->orderBy('updated_at', 'desc');
$page_size = 20;
} else {
$rowsSqlObj->orderBy('id', 'desc');
}
$rows = $rowsSqlObj->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)
{
$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 mixed
*/
public function findArticleAnnexAll(int $user_id, int $article_id)
{
return ArticleAnnex::where([
['user_id', '=', $user_id],
['article_id', '=', $article_id],
['status', '=', 1]
])->get(['id', 'file_suffix', 'file_size', 'original_name', 'created_at'])->toArray();
}
}

View File

@ -4,6 +4,7 @@ namespace App\Service;
use App\Model\User;
use App\Model\ArticleClass;
use Hyperf\DbConnection\Db;
class UserService extends BaseService
{
@ -37,8 +38,9 @@ class UserService extends BaseService
public function register(array $data)
{
try {
$data['password'] = Hash::make($data['password']);
$data['password'] = create_password($data['password']);
$data['created_at'] = date('Y-m-d H:i:s');
$result = User::create($data);
// 创建用户的默认笔记分类
@ -49,9 +51,8 @@ class UserService extends BaseService
'sort' => 1,
'created_at' => time()
]);
} catch (Exception $e) {
} catch (\Exception $e) {
$result = false;
DB::rollBack();
}
return $result ? true : false;

View File

@ -0,0 +1,46 @@
<?php
namespace App\Traits;
/**
* Trait PagingTrait 分页处理
*
* @package App\Traits
*/
trait PagingTrait
{
/**
* 计算分页总数
*
* @param int $total 总记录数
* @param int $page_size 分页大小
*
* @return int 分页总数
*/
protected function getPagingTotal(int $total, int $page_size)
{
return ($total === 0) ? 0 : (int)ceil((int)$total / (int)$page_size);
}
/**
* 获取分页数据
*
* @param array $rows 列表数据
* @param int $total 数据总记录数
* @param int $page 当前分页
* @param int $page_size 分页大小
* @param array $params 额外参数
*
* @return array
*/
protected function getPagingRows(array $rows, int $total, int $page, int $page_size, array $params = [])
{
return array_merge([
'rows' => $rows,
'page' => $page,
'page_size' => $page_size,
'page_total' => ($page_size == 0) ? 1 : $this->getPagingTotal($total, $page_size),
'total' => $total,
], $params);
}
}

View File

@ -40,7 +40,7 @@ return [
],
'settings' => [
'enable_coroutine' => true,
'worker_num' => swoole_cpu_num(),
'worker_num' => 1,
'pid_file' => BASE_PATH . '/runtime/hyperf.pid',
'open_tcp_nodelay' => true,
'max_coroutine' => 100000,