no message
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\article\Article;
|
||||
use app\common\model\article\ArticleComment;
|
||||
use app\common\model\article\ArticleVote;
|
||||
use app\common\model\user\User;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 文章互动逻辑(评论/点赞/踩)
|
||||
* Class ArticleInteractLogic
|
||||
* @package app\api\logic
|
||||
*/
|
||||
class ArticleInteractLogic extends BaseLogic
|
||||
{
|
||||
/**
|
||||
* @notes 评论列表
|
||||
*/
|
||||
public static function commentList($articleId, $pageNo = 1, $pageSize = 15)
|
||||
{
|
||||
$where = [
|
||||
'article_id' => $articleId,
|
||||
'parent_id' => 0,
|
||||
'is_show' => 1,
|
||||
];
|
||||
|
||||
$count = ArticleComment::where($where)->count();
|
||||
|
||||
$list = ArticleComment::where($where)
|
||||
->order('id', 'desc')
|
||||
->page($pageNo, $pageSize)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
$userIds = array_column($list, 'user_id');
|
||||
|
||||
// 取子评论的user_id
|
||||
$commentIds = array_column($list, 'id');
|
||||
$replies = [];
|
||||
if (!empty($commentIds)) {
|
||||
$repliesRaw = ArticleComment::where('parent_id', 'in', $commentIds)
|
||||
->where('is_show', 1)
|
||||
->order('id', 'asc')
|
||||
->select()
|
||||
->toArray();
|
||||
$replyUserIds = array_column($repliesRaw, 'user_id');
|
||||
$replyTargetUserIds = array_column($repliesRaw, 'reply_user_id');
|
||||
$userIds = array_merge($userIds, $replyUserIds, $replyTargetUserIds);
|
||||
|
||||
foreach ($repliesRaw as $r) {
|
||||
$replies[$r['parent_id']][] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
// 批量获取用户信息
|
||||
$userIds = array_unique(array_filter($userIds));
|
||||
$users = [];
|
||||
if (!empty($userIds)) {
|
||||
$usersRaw = User::whereIn('id', $userIds)
|
||||
->field('id,nickname,avatar')
|
||||
->select()
|
||||
->toArray();
|
||||
foreach ($usersRaw as $u) {
|
||||
$users[$u['id']] = $u;
|
||||
}
|
||||
}
|
||||
|
||||
$defaultUser = ['nickname' => '匿名用户', 'avatar' => ''];
|
||||
|
||||
foreach ($list as &$item) {
|
||||
$u = $users[$item['user_id']] ?? $defaultUser;
|
||||
$item['nickname'] = $u['nickname'];
|
||||
$item['avatar'] = $u['avatar'];
|
||||
$item['reply_list'] = [];
|
||||
|
||||
if (isset($replies[$item['id']])) {
|
||||
foreach ($replies[$item['id']] as $reply) {
|
||||
$ru = $users[$reply['user_id']] ?? $defaultUser;
|
||||
$reply['nickname'] = $ru['nickname'];
|
||||
$reply['avatar'] = $ru['avatar'];
|
||||
$reply['reply_nickname'] = '';
|
||||
if ($reply['reply_user_id'] > 0) {
|
||||
$tu = $users[$reply['reply_user_id']] ?? $defaultUser;
|
||||
$reply['reply_nickname'] = $tu['nickname'];
|
||||
}
|
||||
$item['reply_list'][] = $reply;
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return [
|
||||
'count' => $count,
|
||||
'lists' => $list,
|
||||
'page_no' => $pageNo,
|
||||
'page_size' => $pageSize,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 发布评论
|
||||
*/
|
||||
public static function addComment($userId, $params)
|
||||
{
|
||||
Db::startTrans();
|
||||
try {
|
||||
$comment = ArticleComment::create([
|
||||
'article_id' => $params['article_id'],
|
||||
'user_id' => $userId,
|
||||
'parent_id' => $params['parent_id'] ?? 0,
|
||||
'reply_user_id' => $params['reply_user_id'] ?? 0,
|
||||
'content' => $params['content'],
|
||||
]);
|
||||
|
||||
Article::where('id', $params['article_id'])
|
||||
->inc('comment_count')
|
||||
->update();
|
||||
|
||||
Db::commit();
|
||||
return $comment->id;
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 点赞/踩
|
||||
*/
|
||||
public static function vote($userId, $articleId, $voteType)
|
||||
{
|
||||
Db::startTrans();
|
||||
try {
|
||||
$existing = ArticleVote::where([
|
||||
'article_id' => $articleId,
|
||||
'user_id' => $userId,
|
||||
])->findOrEmpty();
|
||||
|
||||
if (!$existing->isEmpty()) {
|
||||
if ($existing->vote_type == $voteType) {
|
||||
// 取消
|
||||
$field = $voteType == ArticleVote::VOTE_LIKE ? 'like_count' : 'dislike_count';
|
||||
$existing->delete();
|
||||
Article::where('id', $articleId)->where($field, '>', 0)->dec($field)->update();
|
||||
Db::commit();
|
||||
return ['action' => 'cancel', 'vote_type' => $voteType];
|
||||
} else {
|
||||
// 切换: 旧的减,新的加
|
||||
$oldField = $existing->vote_type == ArticleVote::VOTE_LIKE ? 'like_count' : 'dislike_count';
|
||||
$newField = $voteType == ArticleVote::VOTE_LIKE ? 'like_count' : 'dislike_count';
|
||||
$existing->vote_type = $voteType;
|
||||
$existing->save();
|
||||
Article::where('id', $articleId)->where($oldField, '>', 0)->dec($oldField)->update();
|
||||
Article::where('id', $articleId)->inc($newField)->update();
|
||||
Db::commit();
|
||||
return ['action' => 'switch', 'vote_type' => $voteType];
|
||||
}
|
||||
} else {
|
||||
// 新增
|
||||
ArticleVote::create([
|
||||
'article_id' => $articleId,
|
||||
'user_id' => $userId,
|
||||
'vote_type' => $voteType,
|
||||
]);
|
||||
$field = $voteType == ArticleVote::VOTE_LIKE ? 'like_count' : 'dislike_count';
|
||||
Article::where('id', $articleId)->inc($field)->update();
|
||||
Db::commit();
|
||||
return ['action' => 'add', 'vote_type' => $voteType];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取用户对文章的投票状态
|
||||
*/
|
||||
public static function getVoteStatus($userId, $articleId)
|
||||
{
|
||||
if (empty($userId)) {
|
||||
return 0;
|
||||
}
|
||||
$vote = ArticleVote::where([
|
||||
'article_id' => $articleId,
|
||||
'user_id' => $userId,
|
||||
])->findOrEmpty();
|
||||
|
||||
return $vote->isEmpty() ? 0 : $vote->vote_type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
use app\common\enum\user\AccountLogEnum;
|
||||
use app\common\enum\YesNoEnum;
|
||||
use app\common\logic\AccountLogLogic;
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\article\Article;
|
||||
use app\common\model\article\ArticleCate;
|
||||
use app\common\model\article\ArticleCollect;
|
||||
use app\api\logic\ArticleInteractLogic;
|
||||
use app\common\service\article\ArticleImageProxyService;
|
||||
use app\common\service\ai\AiService;
|
||||
use app\common\service\ai\KbSyncService;
|
||||
use app\common\model\user\User;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 文章逻辑
|
||||
* Class ArticleLogic
|
||||
* @package app\api\logic
|
||||
*/
|
||||
class ArticleLogic extends BaseLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* @notes 文章详情
|
||||
* @param $articleId
|
||||
* @param $userId
|
||||
* @return array
|
||||
* @author 段誉
|
||||
* @date 2022/9/20 17:09
|
||||
*/
|
||||
public static function detail($articleId, $userId)
|
||||
{
|
||||
$article = Article::getArticleDetailArr($articleId);
|
||||
if (empty($article)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ext = self::decodeExt($article['ext'] ?? []);
|
||||
$translatedContent = self::getTranslatedContentForUser($articleId, $userId, $ext);
|
||||
if ($translatedContent === '' && (int) ($article['cid'] ?? 0) === 22) {
|
||||
$translatedContent = self::autoTranslateArticle($article);
|
||||
}
|
||||
if ($translatedContent !== '') {
|
||||
$article['translated_content'] = $translatedContent;
|
||||
} elseif (!empty($ext['translated_content'])) {
|
||||
$article['translated_content'] = (string) $ext['translated_content'];
|
||||
}
|
||||
$article['collect'] = ArticleCollect::isCollectArticle($userId, $articleId);
|
||||
$article['vote_status'] = ArticleInteractLogic::getVoteStatus($userId, $articleId);
|
||||
$article = ArticleImageProxyService::rewriteArticlePayload($article);
|
||||
|
||||
return $article;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 加入收藏
|
||||
* @param $userId
|
||||
* @param $articleId
|
||||
* @author 段誉
|
||||
* @date 2022/9/20 16:52
|
||||
*/
|
||||
public static function addCollect($articleId, $userId)
|
||||
{
|
||||
$where = ['user_id' => $userId, 'article_id' => $articleId];
|
||||
$collect = ArticleCollect::where($where)->findOrEmpty();
|
||||
if ($collect->isEmpty()) {
|
||||
ArticleCollect::create([
|
||||
'user_id' => $userId,
|
||||
'article_id' => $articleId,
|
||||
'status' => YesNoEnum::YES
|
||||
]);
|
||||
} else {
|
||||
ArticleCollect::update([
|
||||
'id' => $collect['id'],
|
||||
'status' => YesNoEnum::YES
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 取消收藏
|
||||
* @param $articleId
|
||||
* @param $userId
|
||||
* @author 段誉
|
||||
* @date 2022/9/20 16:59
|
||||
*/
|
||||
public static function cancelCollect($articleId, $userId)
|
||||
{
|
||||
ArticleCollect::update(['status' => YesNoEnum::NO], [
|
||||
'user_id' => $userId,
|
||||
'article_id' => $articleId,
|
||||
'status' => YesNoEnum::YES
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 文章分类
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @author 段誉
|
||||
* @date 2022/9/23 14:11
|
||||
*/
|
||||
public static function cate()
|
||||
{
|
||||
return ArticleCate::field('id,name')
|
||||
->where('is_show', '=', 1)
|
||||
->order(['sort' => 'desc', 'id' => 'desc'])
|
||||
->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 文章翻译
|
||||
* @param int $articleId
|
||||
* @return array
|
||||
*/
|
||||
public static function translate(int $articleId, int $userId = 0, int $confirm = 0): array
|
||||
{
|
||||
$article = Article::where(['id' => $articleId, 'is_show' => YesNoEnum::YES])->findOrEmpty();
|
||||
if ($article->isEmpty()) {
|
||||
return ['success' => false, 'error' => '文章不存在'];
|
||||
}
|
||||
|
||||
$articleData = $article->toArray();
|
||||
$ext = self::decodeExt($articleData['ext'] ?? []);
|
||||
$translatedContent = self::getTranslatedContentForUser($articleId, $userId, $ext);
|
||||
$translatePoints = 5;
|
||||
$remark = 'AI翻译资讯#' . $articleId;
|
||||
|
||||
$hasPaid = false;
|
||||
if ($userId) {
|
||||
$hasPaid = Db::name('user_account_log')
|
||||
->where('user_id', $userId)
|
||||
->where('change_type', AccountLogEnum::UP_DEC_TRANSLATE_POST)
|
||||
->where('remark', $remark)
|
||||
->count() > 0;
|
||||
}
|
||||
|
||||
if ($hasPaid && $translatedContent !== '') {
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'translated_content' => $translatedContent,
|
||||
'from_cache' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
if ($hasPaid) {
|
||||
$confirm = 1;
|
||||
}
|
||||
|
||||
if (!$confirm) {
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'needs_payment' => true,
|
||||
'cost' => $translatePoints,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
if (!$userId) {
|
||||
return ['success' => false, 'error' => '请先登录'];
|
||||
}
|
||||
|
||||
if (!$hasPaid) {
|
||||
$user = User::findOrEmpty($userId);
|
||||
if ($user->isEmpty()) {
|
||||
return ['success' => false, 'error' => '用户不存在'];
|
||||
}
|
||||
if (($user->user_points ?? 0) < $translatePoints) {
|
||||
return ['success' => false, 'error' => '积分不足,当前积分' . ($user->user_points ?? 0) . ',翻译需要' . $translatePoints . '积分'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($translatedContent !== '') {
|
||||
if (!$hasPaid) {
|
||||
User::where('id', $userId)->dec('user_points', $translatePoints)->update();
|
||||
AccountLogLogic::add(
|
||||
$userId,
|
||||
AccountLogEnum::UP_DEC_TRANSLATE_POST,
|
||||
AccountLogEnum::DEC,
|
||||
$translatePoints,
|
||||
'',
|
||||
$remark
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'translated_content' => $translatedContent,
|
||||
'from_cache' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$title = trim((string) ($articleData['title'] ?? ''));
|
||||
$abstract = trim((string) ($articleData['abstract'] ?? $articleData['desc'] ?? ''));
|
||||
$content = self::normalizeContent((string) ($articleData['content'] ?? ''));
|
||||
|
||||
$result = AiService::translateArticle($title, $abstract, $content);
|
||||
if (!$result['success']) {
|
||||
return ['success' => false, 'error' => $result['error'] ?? '翻译失败'];
|
||||
}
|
||||
|
||||
$translatedContent = trim((string) ($result['content'] ?? ''));
|
||||
if ($translatedContent === '') {
|
||||
return ['success' => false, 'error' => '翻译结果为空'];
|
||||
}
|
||||
|
||||
$ext['translated_content'] = $translatedContent;
|
||||
Article::where('id', $articleId)->update([
|
||||
'ext' => json_encode($ext, JSON_UNESCAPED_UNICODE),
|
||||
'update_time' => time(),
|
||||
]);
|
||||
KbSyncService::enqueue('article', 'article', $articleId, 'upsert', 45);
|
||||
|
||||
if (!$hasPaid) {
|
||||
User::where('id', $userId)->dec('user_points', $translatePoints)->update();
|
||||
AccountLogLogic::add(
|
||||
$userId,
|
||||
AccountLogEnum::UP_DEC_TRANSLATE_POST,
|
||||
AccountLogEnum::DEC,
|
||||
$translatePoints,
|
||||
'',
|
||||
$remark
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'translated_content' => $translatedContent,
|
||||
'from_cache' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 自动翻译并落库文章译文缓存
|
||||
* @param array $articleData
|
||||
* @return string
|
||||
*/
|
||||
public static function autoTranslateArticle(array $articleData): string
|
||||
{
|
||||
$articleId = (int) ($articleData['id'] ?? 0);
|
||||
if ($articleId <= 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$ext = self::decodeExt($articleData['ext'] ?? []);
|
||||
if (!empty($ext['translated_content'])) {
|
||||
return (string) $ext['translated_content'];
|
||||
}
|
||||
|
||||
$title = trim((string) ($articleData['title'] ?? ''));
|
||||
$abstract = trim((string) ($articleData['abstract'] ?? $articleData['desc'] ?? ''));
|
||||
$content = self::normalizeContent((string) ($articleData['content'] ?? ''));
|
||||
if ($title === '' && $abstract === '' && $content === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$result = AiService::translateArticle($title, $abstract, $content);
|
||||
if (empty($result['success'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$translatedContent = trim((string) ($result['content'] ?? ''));
|
||||
if ($translatedContent === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$ext['translated_content'] = $translatedContent;
|
||||
Article::where('id', $articleId)->update([
|
||||
'ext' => json_encode($ext, JSON_UNESCAPED_UNICODE),
|
||||
'update_time' => time(),
|
||||
]);
|
||||
KbSyncService::enqueue('article', 'article', $articleId, 'upsert', 45);
|
||||
|
||||
return $translatedContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 仅读取已缓存的文章翻译内容
|
||||
* @param array $articleData
|
||||
* @return string
|
||||
*/
|
||||
public static function getCachedTranslatedContent(array $articleData): string
|
||||
{
|
||||
$ext = self::decodeExt($articleData['ext'] ?? []);
|
||||
return !empty($ext['translated_content']) ? (string) $ext['translated_content'] : '';
|
||||
}
|
||||
|
||||
protected static function getTranslatedContentForUser(int $articleId, int $userId, array $ext): string
|
||||
{
|
||||
if (empty($ext['translated_content']) || !$userId) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$hasPaid = Db::name('user_account_log')
|
||||
->where('user_id', $userId)
|
||||
->where('change_type', AccountLogEnum::UP_DEC_TRANSLATE_POST)
|
||||
->where('remark', 'AI翻译资讯#' . $articleId)
|
||||
->count() > 0;
|
||||
|
||||
return $hasPaid ? (string) $ext['translated_content'] : '';
|
||||
}
|
||||
|
||||
protected static function decodeExt($ext): array
|
||||
{
|
||||
if (is_array($ext)) {
|
||||
return $ext;
|
||||
}
|
||||
if (is_string($ext) && $ext !== '') {
|
||||
$data = json_decode($ext, true);
|
||||
return is_array($data) ? $data : [];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
protected static function normalizeContent(string $content): string
|
||||
{
|
||||
if ($content === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$content = str_ireplace(['<br>', '<br/>', '<br />'], "\n", $content);
|
||||
$content = str_ireplace(['</p>', '</div>', '</li>', '</h1>', '</h2>', '</h3>'], "\n", $content);
|
||||
$content = strip_tags($content);
|
||||
$content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$content = preg_replace('/\r\n|\r/u', "\n", $content);
|
||||
$content = preg_replace('/\n{3,}/u', "\n\n", $content);
|
||||
|
||||
return trim($content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\article\Article;
|
||||
use app\common\model\decorate\DecoratePage;
|
||||
use app\common\model\decorate\DecorateTabbar;
|
||||
use app\common\service\ConfigService;
|
||||
use app\common\service\FileService;
|
||||
|
||||
|
||||
/**
|
||||
* index
|
||||
* Class IndexLogic
|
||||
* @package app\api\logic
|
||||
*/
|
||||
class IndexLogic extends BaseLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* @notes 首页数据
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @author 段誉
|
||||
* @date 2022/9/21 19:15
|
||||
*/
|
||||
public static function getIndexData()
|
||||
{
|
||||
// 装修配置
|
||||
$decoratePage = DecoratePage::findOrEmpty(1);
|
||||
|
||||
// 首页文章
|
||||
$field = [
|
||||
'id', 'title', 'desc', 'abstract', 'image',
|
||||
'author', 'click_actual', 'click_virtual', 'create_time'
|
||||
];
|
||||
|
||||
$article = Article::field($field)
|
||||
->where(['is_show' => 1])
|
||||
->order(['id' => 'desc'])
|
||||
->limit(20)->append(['click'])
|
||||
->hidden(['click_actual', 'click_virtual'])
|
||||
->select()->toArray();
|
||||
|
||||
return [
|
||||
'page' => $decoratePage,
|
||||
'article' => $article
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 获取政策协议
|
||||
* @param string $type
|
||||
* @return array
|
||||
* @author 段誉
|
||||
* @date 2022/9/20 20:00
|
||||
*/
|
||||
public static function getPolicyByType(string $type)
|
||||
{
|
||||
return [
|
||||
'title' => ConfigService::get('agreement', $type . '_title', ''),
|
||||
'content' => ConfigService::get('agreement', $type . '_content', ''),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 装修信息
|
||||
* @param $id
|
||||
* @return array
|
||||
* @author 段誉
|
||||
* @date 2022/9/21 18:37
|
||||
*/
|
||||
public static function getDecorate($id)
|
||||
{
|
||||
return DecoratePage::field(['type', 'name', 'data', 'meta'])
|
||||
->findOrEmpty($id)->toArray();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 获取配置
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @author 段誉
|
||||
* @date 2022/9/21 19:38
|
||||
*/
|
||||
public static function getConfigData()
|
||||
{
|
||||
// 底部导航
|
||||
$tabbar = DecorateTabbar::getTabbarLists();
|
||||
// 导航颜色
|
||||
$style = ConfigService::get('tabbar', 'style', config('project.decorate.tabbar_style'));
|
||||
// 登录配置
|
||||
$loginConfig = [
|
||||
// 登录方式
|
||||
'login_way' => ConfigService::get('login', 'login_way', config('project.login.login_way')),
|
||||
// 注册强制绑定手机
|
||||
'coerce_mobile' => ConfigService::get('login', 'coerce_mobile', config('project.login.coerce_mobile')),
|
||||
// 政策协议
|
||||
'login_agreement' => ConfigService::get('login', 'login_agreement', config('project.login.login_agreement')),
|
||||
// 第三方登录 开关
|
||||
'third_auth' => ConfigService::get('login', 'third_auth', config('project.login.third_auth')),
|
||||
// 微信授权登录
|
||||
'wechat_auth' => ConfigService::get('login', 'wechat_auth', config('project.login.wechat_auth')),
|
||||
// qq授权登录
|
||||
'qq_auth' => ConfigService::get('login', 'qq_auth', config('project.login.qq_auth')),
|
||||
];
|
||||
// 网址信息
|
||||
$website = [
|
||||
'h5_favicon' => FileService::getFileUrl(ConfigService::get('website', 'h5_favicon')),
|
||||
'shop_name' => ConfigService::get('website', 'shop_name'),
|
||||
'shop_logo' => FileService::getFileUrl(ConfigService::get('website', 'shop_logo')),
|
||||
];
|
||||
// H5配置
|
||||
$webPage = [
|
||||
// 渠道状态 0-关闭 1-开启
|
||||
'status' => ConfigService::get('web_page', 'status', 1),
|
||||
// 关闭后渠道后访问页面 0-空页面 1-自定义链接
|
||||
'page_status' => ConfigService::get('web_page', 'page_status', 0),
|
||||
// 自定义链接
|
||||
'page_url' => ConfigService::get('web_page', 'page_url', ''),
|
||||
'url' => request()->domain() . '/mobile'
|
||||
];
|
||||
|
||||
// 备案信息
|
||||
$copyright = ConfigService::get('copyright', 'config', []);
|
||||
|
||||
return [
|
||||
'domain' => FileService::getFileUrl(),
|
||||
'style' => $style,
|
||||
'tabbar' => $tabbar,
|
||||
'login' => $loginConfig,
|
||||
'website' => $website,
|
||||
'webPage' => $webPage,
|
||||
'version'=> config('project.version'),
|
||||
'copyright' => $copyright,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,511 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
use app\common\cache\WebScanLoginCache;
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\api\service\{UserTokenService, WechatUserService};
|
||||
use app\common\enum\{LoginEnum, user\UserTerminalEnum, YesNoEnum};
|
||||
use app\common\service\{
|
||||
ConfigService,
|
||||
FileService,
|
||||
wechat\WeChatConfigService,
|
||||
wechat\WeChatMnpService,
|
||||
wechat\WeChatOaService,
|
||||
wechat\WeChatRequestService
|
||||
};
|
||||
use app\common\model\user\{User, UserAuth};
|
||||
use think\facade\{Db, Config};
|
||||
|
||||
/**
|
||||
* 登录逻辑
|
||||
* Class LoginLogic
|
||||
* @package app\api\logic
|
||||
*/
|
||||
class LoginLogic extends BaseLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* @notes 账号密码注册
|
||||
* @param array $params
|
||||
* @return bool
|
||||
* @author 段誉
|
||||
* @date 2022/9/7 15:37
|
||||
*/
|
||||
public static function register(array $params)
|
||||
{
|
||||
try {
|
||||
$userSn = User::createUserSn();
|
||||
$passwordSalt = Config::get('project.unique_identification');
|
||||
$password = create_password($params['password'], $passwordSalt);
|
||||
$avatar = ConfigService::get('default_image', 'user_avatar');
|
||||
$inviteCode = User::createInviteCode();
|
||||
|
||||
$inviterId = 0;
|
||||
if (!empty($params['inviter_code'])) {
|
||||
$inviter = User::where('invite_code', $params['inviter_code'])->find();
|
||||
if ($inviter) {
|
||||
$inviterId = $inviter->id;
|
||||
}
|
||||
}
|
||||
|
||||
User::create([
|
||||
'sn' => $userSn,
|
||||
'avatar' => $avatar,
|
||||
'nickname' => '用户' . $userSn,
|
||||
'account' => $params['account'],
|
||||
'password' => $password,
|
||||
'channel' => $params['channel'],
|
||||
'invite_code' => $inviteCode,
|
||||
'inviter_id' => $inviterId,
|
||||
]);
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 账号/手机号登录,手机号验证码
|
||||
* @param $params
|
||||
* @return array|false
|
||||
* @author 段誉
|
||||
* @date 2022/9/6 19:26
|
||||
*/
|
||||
public static function login($params)
|
||||
{
|
||||
try {
|
||||
// 账号/手机号 密码登录
|
||||
$where = ['account|mobile' => $params['account']];
|
||||
if ($params['scene'] == LoginEnum::MOBILE_CAPTCHA) {
|
||||
//手机验证码登录
|
||||
$where = ['mobile' => $params['account']];
|
||||
}
|
||||
|
||||
// 验证码登录:只要上传表里存在该手机号记录即可视为通过
|
||||
$verifyScene = '';
|
||||
$smsMatched = false;
|
||||
if ($params['scene'] == LoginEnum::MOBILE_CAPTCHA) {
|
||||
$verifyInfo = \think\facade\Cache::get('sms_verify_info:' . $params['account']);
|
||||
if ($verifyInfo && is_array($verifyInfo)) {
|
||||
$verifyScene = $verifyInfo['scene'] ?? 'login';
|
||||
}
|
||||
|
||||
$userMobile = $params['account'];
|
||||
$record = \app\common\model\SmsLog::where(function ($query) use ($userMobile) {
|
||||
$query->where('phone', $userMobile)
|
||||
->whereOr('phone', '+86' . $userMobile)
|
||||
->whereOr('phone', '86' . $userMobile);
|
||||
})
|
||||
->order('sms_time', 'desc')
|
||||
->findOrEmpty();
|
||||
$smsMatched = !$record->isEmpty();
|
||||
if ($smsMatched && (int)$record->status === 0) {
|
||||
$record->status = 1;
|
||||
$record->save();
|
||||
}
|
||||
}
|
||||
|
||||
$user = User::where($where)->findOrEmpty();
|
||||
if ($user->isEmpty()) {
|
||||
// 验证码登录场景:用户不存在则自动注册,无论验证码是否匹配都允许注册
|
||||
if ($params['scene'] == LoginEnum::MOBILE_CAPTCHA) {
|
||||
// is_bind_mobile: sms_upload匹配=1,缓存码匹配但sms_upload不匹配=0,都不匹配=0
|
||||
$isPhoneReal = $smsMatched ? 1 : 0;
|
||||
$userSn = User::createUserSn();
|
||||
$avatar = ConfigService::get('default_image', 'user_avatar');
|
||||
$inviteCode = User::createInviteCode();
|
||||
$inviterId = 0;
|
||||
if (!empty($params['inviter_code'])) {
|
||||
$inviter = User::where('invite_code', $params['inviter_code'])->find();
|
||||
if ($inviter) {
|
||||
$inviterId = $inviter->id;
|
||||
}
|
||||
}
|
||||
$user = User::create([
|
||||
'sn' => $userSn,
|
||||
'avatar' => $avatar,
|
||||
'nickname' => '用户' . $userSn,
|
||||
'account' => $params['account'],
|
||||
'mobile' => $params['account'],
|
||||
'channel' => $params['terminal'],
|
||||
'invite_code' => $inviteCode,
|
||||
'inviter_id' => $inviterId,
|
||||
'is_bind_mobile' => $isPhoneReal,
|
||||
]);
|
||||
} else {
|
||||
throw new \Exception('用户不存在');
|
||||
}
|
||||
} else {
|
||||
// 已有账号:验证码登录必须通过 sms_upload 验证
|
||||
if ($params['scene'] == LoginEnum::MOBILE_CAPTCHA && !$smsMatched) {
|
||||
throw new \Exception('验证码错误,请使用密码登录');
|
||||
}
|
||||
if ($params['scene'] == LoginEnum::MOBILE_CAPTCHA && $smsMatched) {
|
||||
$user->is_bind_mobile = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 验证码验证成功才清除缓存
|
||||
if ($params['scene'] == LoginEnum::MOBILE_CAPTCHA && $smsMatched) {
|
||||
\think\facade\Cache::delete('sms_verify_code:' . $params['account']);
|
||||
\think\facade\Cache::delete('sms_verify_info:' . $params['account']);
|
||||
}
|
||||
|
||||
//更新登录信息
|
||||
$user->login_time = time();
|
||||
$user->login_ip = request()->ip();
|
||||
$user->save();
|
||||
|
||||
//设置token
|
||||
$userInfo = UserTokenService::setToken($user->id, $params['terminal']);
|
||||
|
||||
//返回登录信息
|
||||
$avatar = $user->avatar ?: Config::get('project.default_image.user_avatar');
|
||||
$avatar = FileService::getFileUrl($avatar);
|
||||
|
||||
return [
|
||||
'nickname' => $userInfo['nickname'],
|
||||
'sn' => $userInfo['sn'],
|
||||
'mobile' => $userInfo['mobile'],
|
||||
'avatar' => $avatar,
|
||||
'token' => $userInfo['token'],
|
||||
'is_real_phone' => $user->is_bind_mobile ?? 0,
|
||||
'verify_scene' => $verifyScene,
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 退出登录
|
||||
* @param $userInfo
|
||||
* @return bool
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @author 段誉
|
||||
* @date 2022/9/16 17:56
|
||||
*/
|
||||
public static function logout($userInfo)
|
||||
{
|
||||
//token不存在,不注销
|
||||
if (!isset($userInfo['token'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//设置token过期
|
||||
return UserTokenService::expireToken($userInfo['token']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 获取微信请求code的链接
|
||||
* @param string $url
|
||||
* @return string
|
||||
* @author 段誉
|
||||
* @date 2022/9/20 19:47
|
||||
*/
|
||||
public static function codeUrl(string $url)
|
||||
{
|
||||
return (new WeChatOaService())->getCodeUrl($url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 公众号登录
|
||||
* @param array $params
|
||||
* @return array|false
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
* @author 段誉
|
||||
* @date 2022/9/20 19:47
|
||||
*/
|
||||
public static function oaLogin(array $params)
|
||||
{
|
||||
Db::startTrans();
|
||||
try {
|
||||
//通过code获取微信 openid
|
||||
$response = (new WeChatOaService())->getOaResByCode($params['code']);
|
||||
$userServer = new WechatUserService($response, UserTerminalEnum::WECHAT_OA);
|
||||
$userInfo = $userServer->getResopnseByUserInfo()->authUserLogin()->getUserInfo();
|
||||
|
||||
// 更新登录信息
|
||||
self::updateLoginInfo($userInfo['id']);
|
||||
|
||||
Db::commit();
|
||||
return $userInfo;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::$error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 小程序-静默登录
|
||||
* @param array $params
|
||||
* @return array|false
|
||||
* @author 段誉
|
||||
* @date 2022/9/20 19:47
|
||||
*/
|
||||
public static function silentLogin(array $params)
|
||||
{
|
||||
try {
|
||||
//通过code获取微信 openid
|
||||
$response = (new WeChatMnpService())->getMnpResByCode($params['code']);
|
||||
$userServer = new WechatUserService($response, UserTerminalEnum::WECHAT_MMP);
|
||||
$userInfo = $userServer->getResopnseByUserInfo('silent')->getUserInfo();
|
||||
|
||||
if (!empty($userInfo)) {
|
||||
// 更新登录信息
|
||||
self::updateLoginInfo($userInfo['id']);
|
||||
}
|
||||
|
||||
return $userInfo;
|
||||
} catch (\Exception $e) {
|
||||
self::$error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 小程序-授权登录
|
||||
* @param array $params
|
||||
* @return array|false
|
||||
* @author 段誉
|
||||
* @date 2022/9/20 19:47
|
||||
*/
|
||||
public static function mnpLogin(array $params)
|
||||
{
|
||||
Db::startTrans();
|
||||
try {
|
||||
//通过code获取微信 openid
|
||||
$response = (new WeChatMnpService())->getMnpResByCode($params['code']);
|
||||
$userServer = new WechatUserService($response, UserTerminalEnum::WECHAT_MMP);
|
||||
$userInfo = $userServer->getResopnseByUserInfo()->authUserLogin()->getUserInfo();
|
||||
|
||||
// 更新登录信息
|
||||
self::updateLoginInfo($userInfo['id']);
|
||||
|
||||
Db::commit();
|
||||
return $userInfo;
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::$error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 更新登录信息
|
||||
* @param $userId
|
||||
* @throws \Exception
|
||||
* @author 段誉
|
||||
* @date 2022/9/20 19:46
|
||||
*/
|
||||
public static function updateLoginInfo($userId)
|
||||
{
|
||||
$user = User::findOrEmpty($userId);
|
||||
if ($user->isEmpty()) {
|
||||
throw new \Exception('用户不存在');
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$user->login_time = $time;
|
||||
$user->login_ip = request()->ip();
|
||||
$user->update_time = $time;
|
||||
$user->save();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 小程序端绑定微信
|
||||
* @param array $params
|
||||
* @return bool
|
||||
* @author 段誉
|
||||
* @date 2022/9/20 19:46
|
||||
*/
|
||||
public static function mnpAuthLogin(array $params)
|
||||
{
|
||||
try {
|
||||
//通过code获取微信openid
|
||||
$response = (new WeChatMnpService())->getMnpResByCode($params['code']);
|
||||
$response['user_id'] = $params['user_id'];
|
||||
$response['terminal'] = UserTerminalEnum::WECHAT_MMP;
|
||||
|
||||
return self::createAuth($response);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
self::$error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 公众号端绑定微信
|
||||
* @param array $params
|
||||
* @return bool
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
* @author 段誉
|
||||
* @date 2022/9/16 10:43
|
||||
*/
|
||||
public static function oaAuthLogin(array $params)
|
||||
{
|
||||
try {
|
||||
//通过code获取微信openid
|
||||
$response = (new WeChatOaService())->getOaResByCode($params['code']);
|
||||
$response['user_id'] = $params['user_id'];
|
||||
$response['terminal'] = UserTerminalEnum::WECHAT_OA;
|
||||
|
||||
return self::createAuth($response);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
self::$error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 生成授权记录
|
||||
* @param $response
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
* @author 段誉
|
||||
* @date 2022/9/16 10:43
|
||||
*/
|
||||
public static function createAuth($response)
|
||||
{
|
||||
//先检查openid是否有记录
|
||||
$isAuth = UserAuth::where('openid', '=', $response['openid'])->findOrEmpty();
|
||||
if (!$isAuth->isEmpty()) {
|
||||
throw new \Exception('该微信已被绑定');
|
||||
}
|
||||
|
||||
if (isset($response['unionid']) && !empty($response['unionid'])) {
|
||||
//在用unionid找记录,防止生成两个账号,同个unionid的问题
|
||||
$userAuth = UserAuth::where(['unionid' => $response['unionid']])
|
||||
->findOrEmpty();
|
||||
if (!$userAuth->isEmpty() && $userAuth->user_id != $response['user_id']) {
|
||||
throw new \Exception('该微信已被绑定');
|
||||
}
|
||||
}
|
||||
|
||||
//如果没有授权,直接生成一条微信授权记录
|
||||
UserAuth::create([
|
||||
'user_id' => $response['user_id'],
|
||||
'openid' => $response['openid'],
|
||||
'unionid' => $response['unionid'] ?? '',
|
||||
'terminal' => $response['terminal'],
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 获取扫码登录地址
|
||||
* @return array|false
|
||||
* @author 段誉
|
||||
* @date 2022/10/20 18:23
|
||||
*/
|
||||
public static function getScanCode($redirectUri)
|
||||
{
|
||||
try {
|
||||
$config = WeChatConfigService::getOpConfig();
|
||||
$appId = $config['app_id'];
|
||||
$redirectUri = UrlEncode($redirectUri);
|
||||
|
||||
// 设置有效时间标记状态, 超时扫码不可登录
|
||||
$state = MD5(time() . rand(10000, 99999));
|
||||
(new WebScanLoginCache())->setScanLoginState($state);
|
||||
|
||||
// 扫码地址
|
||||
$url = WeChatRequestService::getScanCodeUrl($appId, $redirectUri, $state);
|
||||
return ['url' => $url];
|
||||
|
||||
} catch (\Exception $e) {
|
||||
self::$error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 网站扫码登录
|
||||
* @param $params
|
||||
* @return array|false
|
||||
* @author 段誉
|
||||
* @date 2022/10/21 10:28
|
||||
*/
|
||||
public static function scanLogin($params)
|
||||
{
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 通过code 获取 access_token,openid,unionid等信息
|
||||
$userAuth = WeChatRequestService::getUserAuthByCode($params['code']);
|
||||
|
||||
if (empty($userAuth['openid']) || empty($userAuth['access_token'])) {
|
||||
throw new \Exception('获取用户授权信息失败');
|
||||
}
|
||||
|
||||
// 获取微信用户信息
|
||||
$response = WeChatRequestService::getUserInfoByAuth($userAuth['access_token'], $userAuth['openid']);
|
||||
|
||||
// 生成用户或更新用户信息
|
||||
$userServer = new WechatUserService($response, UserTerminalEnum::PC);
|
||||
$userInfo = $userServer->getResopnseByUserInfo()->authUserLogin()->getUserInfo();
|
||||
|
||||
// 更新登录信息
|
||||
self::updateLoginInfo($userInfo['id']);
|
||||
|
||||
Db::commit();
|
||||
return $userInfo;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::$error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 更新用户信息
|
||||
* @param $params
|
||||
* @param $userId
|
||||
* @return User
|
||||
* @author 段誉
|
||||
* @date 2023/2/22 11:19
|
||||
*/
|
||||
public static function updateUser($params, $userId)
|
||||
{
|
||||
return User::where(['id' => $userId])->update([
|
||||
'nickname' => $params['nickname'],
|
||||
'avatar' => FileService::setFileUrl($params['avatar']),
|
||||
'is_new_user' => YesNoEnum::NO
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
|
||||
use app\common\enum\YesNoEnum;
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\article\Article;
|
||||
use app\common\model\article\ArticleCate;
|
||||
use app\common\model\article\ArticleCollect;
|
||||
use app\common\model\article\ArticleComment;
|
||||
use app\common\model\decorate\DecoratePage;
|
||||
use app\common\service\ConfigService;
|
||||
use app\common\service\FileService;
|
||||
|
||||
|
||||
/**
|
||||
* index
|
||||
* Class IndexLogic
|
||||
* @package app\api\logic
|
||||
*/
|
||||
class PcLogic extends BaseLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* @notes 首页数据
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @author 段誉
|
||||
* @date 2022/9/21 19:15
|
||||
*/
|
||||
public static function getIndexData()
|
||||
{
|
||||
// 装修配置
|
||||
$decoratePage = DecoratePage::findOrEmpty(4);
|
||||
// 最新资讯
|
||||
$newArticle = self::getLimitArticle('new', 7);
|
||||
// 全部资讯
|
||||
$allArticle = self::getLimitArticle('all', 5);
|
||||
// 热门资讯
|
||||
$hotArticle = self::getLimitArticle('hot', 8);
|
||||
|
||||
return [
|
||||
'page' => $decoratePage,
|
||||
'all' => $allArticle,
|
||||
'new' => $newArticle,
|
||||
'hot' => $hotArticle
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 获取文章
|
||||
* @param string $sortType
|
||||
* @param int $limit
|
||||
* @return mixed
|
||||
* @author 段誉
|
||||
* @date 2022/10/19 9:53
|
||||
*/
|
||||
public static function getLimitArticle(string $sortType, int $limit = 0, int $cate = 0, int $excludeId = 0)
|
||||
{
|
||||
// 查询字段
|
||||
$field = [
|
||||
'id',
|
||||
'cid',
|
||||
'title',
|
||||
'desc',
|
||||
'abstract',
|
||||
'image',
|
||||
'author',
|
||||
'click_actual',
|
||||
'click_virtual',
|
||||
'create_time'
|
||||
];
|
||||
|
||||
// 排序条件
|
||||
$orderRaw = 'published_at desc, id desc';
|
||||
if ($sortType == 'new') {
|
||||
$orderRaw = 'published_at desc, id desc';
|
||||
}
|
||||
if ($sortType == 'hot') {
|
||||
$orderRaw = 'click_actual + click_virtual desc, id desc';
|
||||
}
|
||||
|
||||
// 查询条件
|
||||
$where[] = ['is_show', '=', YesNoEnum::YES];
|
||||
if (!empty($cate)) {
|
||||
$where[] = ['cid', '=', $cate];
|
||||
}
|
||||
if (!empty($excludeId)) {
|
||||
$where[] = ['id', '<>', $excludeId];
|
||||
}
|
||||
|
||||
$article = Article::field($field)
|
||||
->withCount(['comments' => 'comment_count'])
|
||||
->where($where)
|
||||
->append(['click'])
|
||||
->orderRaw($orderRaw)
|
||||
->hidden(['click_actual', 'click_virtual']);
|
||||
|
||||
if ($limit) {
|
||||
$article->limit($limit);
|
||||
}
|
||||
|
||||
return $article->select()->toArray();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 获取配置
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @author 段誉
|
||||
* @date 2022/9/21 19:38
|
||||
*/
|
||||
public static function getConfigData()
|
||||
{
|
||||
// 登录配置
|
||||
$loginConfig = [
|
||||
// 登录方式
|
||||
'login_way' => ConfigService::get('login', 'login_way', config('project.login.login_way')),
|
||||
// 注册强制绑定手机
|
||||
'coerce_mobile' => ConfigService::get('login', 'coerce_mobile', config('project.login.coerce_mobile')),
|
||||
// 政策协议
|
||||
'login_agreement' => ConfigService::get('login', 'login_agreement', config('project.login.login_agreement')),
|
||||
// 第三方登录 开关
|
||||
'third_auth' => ConfigService::get('login', 'third_auth', config('project.login.third_auth')),
|
||||
// 微信授权登录
|
||||
'wechat_auth' => ConfigService::get('login', 'wechat_auth', config('project.login.wechat_auth')),
|
||||
// qq授权登录
|
||||
'qq_auth' => ConfigService::get('login', 'qq_auth', config('project.login.qq_auth')),
|
||||
];
|
||||
|
||||
// 网站信息
|
||||
$website = [
|
||||
'shop_name' => ConfigService::get('website', 'shop_name'),
|
||||
'shop_logo' => FileService::getFileUrl(ConfigService::get('website', 'shop_logo')),
|
||||
'pc_logo' => FileService::getFileUrl(ConfigService::get('website', 'pc_logo')),
|
||||
'pc_title' => ConfigService::get('website', 'pc_title'),
|
||||
'pc_ico' => FileService::getFileUrl(ConfigService::get('website', 'pc_ico')),
|
||||
'pc_desc' => ConfigService::get('website', 'pc_desc'),
|
||||
'pc_keywords' => ConfigService::get('website', 'pc_keywords'),
|
||||
];
|
||||
|
||||
// 站点统计
|
||||
$siteStatistics = [
|
||||
'clarity_code' => ConfigService::get('siteStatistics', 'clarity_code'),
|
||||
];
|
||||
|
||||
// 备案信息
|
||||
$copyright = ConfigService::get('copyright', 'config', []);
|
||||
|
||||
// 公众号二维码
|
||||
$oaQrCode = ConfigService::get('oa_setting', 'qr_code', '');
|
||||
$oaQrCode = empty($oaQrCode) ? $oaQrCode : FileService::getFileUrl($oaQrCode);
|
||||
// 小程序二维码
|
||||
$mnpQrCode = ConfigService::get('mnp_setting', 'qr_code', '');
|
||||
$mnpQrCode = empty($mnpQrCode) ? $mnpQrCode : FileService::getFileUrl($mnpQrCode);
|
||||
|
||||
return [
|
||||
'domain' => FileService::getFileUrl(),
|
||||
'login' => $loginConfig,
|
||||
'website' => $website,
|
||||
'siteStatistics' => $siteStatistics,
|
||||
'version' => config('project.version'),
|
||||
'copyright' => $copyright,
|
||||
'admin_url' => request()->domain() . '/admin',
|
||||
'qrcode' => [
|
||||
'oa' => $oaQrCode,
|
||||
'mnp' => $mnpQrCode,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 资讯中心
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @author 段誉
|
||||
* @date 2022/10/19 16:55
|
||||
*/
|
||||
public static function getInfoCenter()
|
||||
{
|
||||
$data = ArticleCate::field(['id', 'name'])
|
||||
->with([
|
||||
'article' => function ($query) {
|
||||
$query->hidden(['content', 'click_virtual', 'click_actual'])
|
||||
->orderRaw('published_at desc, id desc')
|
||||
->append(['click'])
|
||||
->limit(10);
|
||||
}
|
||||
])
|
||||
->where(['is_show' => YesNoEnum::YES])
|
||||
->order(['sort' => 'desc', 'id' => 'desc'])
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 收集所有文章ID,批量统计评论数
|
||||
$allIds = [];
|
||||
foreach ($data as $cate) {
|
||||
foreach ($cate['article'] as $article) {
|
||||
$allIds[] = $article['id'];
|
||||
}
|
||||
}
|
||||
if ($allIds) {
|
||||
$commentCounts = ArticleComment::where('delete_time', null)
|
||||
->whereIn('article_id', $allIds)
|
||||
->group('article_id')
|
||||
->column('COUNT(*)', 'article_id');
|
||||
foreach ($data as &$cate) {
|
||||
foreach ($cate['article'] as &$article) {
|
||||
$article['comment_count'] = $commentCounts[$article['id']] ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 获取文章详情
|
||||
* @param $userId
|
||||
* @param $articleId
|
||||
* @param string $source
|
||||
* @return array
|
||||
* @author 段誉
|
||||
* @date 2022/10/20 15:18
|
||||
*/
|
||||
public static function getArticleDetail($userId, $articleId, $source = 'default')
|
||||
{
|
||||
// 文章详情
|
||||
$detail = Article::getArticleDetailArr($articleId);
|
||||
|
||||
// 根据来源列表查找对应列表
|
||||
$nowIndex = 0;
|
||||
$lists = self::getLimitArticle($source, 0, $detail['cid']);
|
||||
foreach ($lists as $key => $item) {
|
||||
if ($item['id'] == $articleId) {
|
||||
$nowIndex = $key;
|
||||
}
|
||||
}
|
||||
// 上一篇
|
||||
$detail['last'] = $lists[$nowIndex - 1] ?? [];
|
||||
// 下一篇
|
||||
$detail['next'] = $lists[$nowIndex + 1] ?? [];
|
||||
|
||||
// 最新资讯
|
||||
$detail['new'] = self::getLimitArticle('new', 8, $detail['cid'], $detail['id']);
|
||||
// 关注状态
|
||||
$detail['collect'] = ArticleCollect::isCollectArticle($userId, $articleId);
|
||||
// 分类名
|
||||
$detail['cate_name'] = ArticleCate::where('id', $detail['cid'])->value('name');
|
||||
|
||||
return $detail;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
use app\common\enum\PayEnum;
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\points\PointsOrder;
|
||||
use app\common\model\points\PointsProduct;
|
||||
|
||||
class PointsOrderLogic extends BaseLogic
|
||||
{
|
||||
public static function products(): array
|
||||
{
|
||||
return PointsProduct::where('status', 1)
|
||||
->order('sort', 'desc')
|
||||
->order('id', 'asc')
|
||||
->select()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
private static function makeSign(int $productId, int $points, string $amount): string
|
||||
{
|
||||
$secret = env('APP_KEY', 'likeadmin');
|
||||
return md5("pid={$productId}&pts={$points}&amt={$amount}&key={$secret}");
|
||||
}
|
||||
|
||||
public static function preOrder(array $params)
|
||||
{
|
||||
try {
|
||||
$product = PointsProduct::where(['id' => $params['product_id'], 'status' => 1])->findOrEmpty();
|
||||
if ($product->isEmpty()) {
|
||||
throw new \Exception('积分套餐不存在或已下架');
|
||||
}
|
||||
$amount = (string) $product['amount'];
|
||||
return [
|
||||
'product_id' => (int) $product['id'],
|
||||
'name' => $product['name'],
|
||||
'points' => (int) $product['points'],
|
||||
'amount' => $amount,
|
||||
'sign' => self::makeSign((int) $product['id'], (int) $product['points'], $amount),
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function createOrder(array $params)
|
||||
{
|
||||
try {
|
||||
$product = PointsProduct::where(['id' => $params['product_id'], 'status' => 1])->findOrEmpty();
|
||||
if ($product->isEmpty()) {
|
||||
throw new \Exception('积分套餐不存在或已下架');
|
||||
}
|
||||
$order = PointsOrder::create([
|
||||
'sn' => generate_sn(PointsOrder::class, 'sn'),
|
||||
'user_id' => $params['user_id'],
|
||||
'product_id' => $product['id'],
|
||||
'product_name' => $product['name'],
|
||||
'points' => $product['points'],
|
||||
'order_amount' => $product['amount'],
|
||||
'pay_status' => PayEnum::UNPAID,
|
||||
'order_terminal' => $params['terminal'],
|
||||
]);
|
||||
return [
|
||||
'order_id' => (int) $order['id'],
|
||||
'from' => 'points_order'
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function detail(array $params)
|
||||
{
|
||||
try {
|
||||
$order = PointsOrder::where(['id' => $params['order_id'], 'user_id' => $params['user_id']])
|
||||
->append(['pay_status_text', 'pay_way_text'])
|
||||
->findOrEmpty();
|
||||
if ($order->isEmpty()) {
|
||||
throw new \Exception('订单不存在');
|
||||
}
|
||||
$data = $order->toArray();
|
||||
$data['pay_time'] = empty($data['pay_time']) ? '' : date('Y-m-d H:i:s', $data['pay_time']);
|
||||
return $data;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
use app\common\enum\PayEnum;
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\recharge\RechargeOrder;
|
||||
use app\common\model\user\User;
|
||||
use app\common\service\ConfigService;
|
||||
|
||||
|
||||
/**
|
||||
* 充值逻辑层
|
||||
* Class RechargeLogic
|
||||
* @package app\shopapi\logic
|
||||
*/
|
||||
class RechargeLogic extends BaseLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* @notes 充值
|
||||
* @param array $params
|
||||
* @return array|false
|
||||
* @author 段誉
|
||||
* @date 2023/2/24 10:43
|
||||
*/
|
||||
public static function recharge(array $params)
|
||||
{
|
||||
try {
|
||||
$data = [
|
||||
'sn' => generate_sn(RechargeOrder::class, 'sn'),
|
||||
'order_terminal' => $params['terminal'],
|
||||
'user_id' => $params['user_id'],
|
||||
'pay_status' => PayEnum::UNPAID,
|
||||
'order_amount' => $params['money'],
|
||||
];
|
||||
$order = RechargeOrder::create($data);
|
||||
|
||||
return [
|
||||
'order_id' => (int)$order['id'],
|
||||
'from' => 'recharge'
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 充值配置
|
||||
* @param $userId
|
||||
* @return array
|
||||
* @author 段誉
|
||||
* @date 2023/2/24 16:56
|
||||
*/
|
||||
public static function config($userId)
|
||||
{
|
||||
$userMoney = User::where(['id' => $userId])->value('user_money');
|
||||
$minAmount = ConfigService::get('recharge', 'min_amount', 0);
|
||||
$status = ConfigService::get('recharge', 'status', 0);
|
||||
|
||||
return [
|
||||
'status' => $status,
|
||||
'min_amount' => $minAmount,
|
||||
'user_money' => $userMoney,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\HotSearch;
|
||||
use app\common\service\ConfigService;
|
||||
|
||||
/**
|
||||
* 搜索逻辑
|
||||
* Class SearchLogic
|
||||
* @package app\api\logic
|
||||
*/
|
||||
class SearchLogic extends BaseLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* @notes 热搜列表
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @author 段誉
|
||||
* @date 2022/9/23 14:34
|
||||
*/
|
||||
public static function hotLists()
|
||||
{
|
||||
$data = HotSearch::field(['name', 'sort'])
|
||||
->order(['sort' => 'desc', 'id' => 'desc'])
|
||||
->select()->toArray();
|
||||
|
||||
return [
|
||||
// 功能状态 0-关闭 1-开启
|
||||
'status' => ConfigService::get('hot_search', 'status', 0),
|
||||
// 热门搜索数据
|
||||
'data' => $data,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
use app\common\enum\notice\NoticeEnum;
|
||||
use app\common\logic\BaseLogic;
|
||||
|
||||
|
||||
/**
|
||||
* 短信逻辑
|
||||
* Class SmsLogic
|
||||
* @package app\api\logic
|
||||
*/
|
||||
class SmsLogic extends BaseLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* @notes 发送验证码
|
||||
* @param $params
|
||||
* @return false|mixed
|
||||
* @author 段誉
|
||||
* @date 2022/9/15 16:17
|
||||
*/
|
||||
public static function sendCode($params)
|
||||
{
|
||||
try {
|
||||
$scene = NoticeEnum::getSceneByTag($params['scene']);
|
||||
if (empty($scene)) {
|
||||
throw new \Exception('场景值异常');
|
||||
}
|
||||
|
||||
$result = event('Notice', [
|
||||
'scene_id' => $scene,
|
||||
'params' => [
|
||||
'mobile' => $params['mobile'],
|
||||
'code' => mt_rand(1000, 9999),
|
||||
]
|
||||
]);
|
||||
|
||||
return $result[0];
|
||||
|
||||
} catch (\Exception $e) {
|
||||
self::$error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin蹇€熷紑鍙戝墠鍚庣鍒嗙绠$悊鍚庡彴锛圥HP鐗堬級
|
||||
// +----------------------------------------------------------------------
|
||||
// | 娆㈣繋闃呰瀛︿範绯荤粺绋嬪簭浠g爜锛屽缓璁弽棣堟槸鎴戜滑鍓嶈繘鐨勫姩鍔?
|
||||
// | 寮€婧愮増鏈彲鑷敱鍟嗙敤锛屽彲鍘婚櫎鐣岄潰鐗堟潈logo
|
||||
// | gitee涓嬭浇锛歨ttps://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github涓嬭浇锛歨ttps://github.com/likeshop-github/likeadmin
|
||||
// | 璁块棶瀹樼綉锛歨ttps://www.likeadmin.cn
|
||||
// | likeadmin鍥㈤槦 鐗堟潈鎵€鏈?鎷ユ湁鏈€缁堣В閲婃潈
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
|
||||
use app\common\{
|
||||
enum\notice\NoticeEnum,
|
||||
enum\user\UserTerminalEnum,
|
||||
enum\YesNoEnum,
|
||||
logic\BaseLogic,
|
||||
model\SmsLog,
|
||||
model\user\User,
|
||||
model\user\UserAuth,
|
||||
service\FileService,
|
||||
service\sms\SmsDriver,
|
||||
service\wechat\WeChatMnpService
|
||||
};
|
||||
use think\facade\Config;
|
||||
|
||||
/**
|
||||
* 浼氬憳閫昏緫灞?
|
||||
* Class UserLogic
|
||||
* @package app\shopapi\logic
|
||||
*/
|
||||
class UserLogic extends BaseLogic
|
||||
{
|
||||
|
||||
private static function findLatestUploadRecord(string $mobile)
|
||||
{
|
||||
return SmsLog::where(function ($query) use ($mobile) {
|
||||
$query->where('phone', $mobile)
|
||||
->whereOr('phone', '+86' . $mobile)
|
||||
->whereOr('phone', '86' . $mobile);
|
||||
})
|
||||
->order('sms_time', 'desc')
|
||||
->findOrEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 涓汉涓績
|
||||
* @param array $userInfo
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @author 娈佃獕
|
||||
* @date 2022/9/16 18:04
|
||||
*/
|
||||
public static function center(array $userInfo): array
|
||||
{
|
||||
if (empty($userInfo['user_id'])) {
|
||||
return [];
|
||||
}
|
||||
$user = User::where(['id' => $userInfo['user_id']])
|
||||
->field('id,sn,sex,account,nickname,real_name,avatar,mobile,create_time,is_new_user,user_money,password,is_vip,vip_level,vip_expire_time,ai_free_count,ai_free_date,is_bind_mobile')
|
||||
->findOrEmpty();
|
||||
if (!$user->isEmpty() && !empty($user->mobile) && ((int) $user->is_bind_mobile !== 1 || (int) $user->is_new_user !== 1)) {
|
||||
$uploadRecord = self::findLatestUploadRecord((string) $user->mobile);
|
||||
if (!$uploadRecord->isEmpty()) {
|
||||
$user->is_bind_mobile = 1;
|
||||
$user->is_new_user = 1;
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($userInfo['terminal'], [UserTerminalEnum::WECHAT_MMP, UserTerminalEnum::WECHAT_OA])) {
|
||||
$auth = UserAuth::where(['user_id' => $userInfo['user_id'], 'terminal' => $userInfo['terminal']])->find();
|
||||
$user['is_auth'] = $auth ? YesNoEnum::YES : YesNoEnum::NO;
|
||||
}
|
||||
|
||||
$user['has_password'] = !empty($user['password']);
|
||||
$vipInfo = $user->getVipInfo();
|
||||
$user->hidden(['password', 'is_vip', 'vip_level', 'vip_expire_time', 'ai_free_count', 'ai_free_date']);
|
||||
$result = $user->toArray();
|
||||
$result['vip_info'] = $vipInfo;
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 涓汉淇℃伅
|
||||
* @param $userId
|
||||
* @return array
|
||||
* @author 娈佃獕
|
||||
* @date 2022/9/20 19:45
|
||||
*/
|
||||
public static function info(int $userId)
|
||||
{
|
||||
$user = User::where(['id' => $userId])
|
||||
->field('id,sn,sex,account,password,nickname,real_name,avatar,mobile,create_time,user_money,is_vip,vip_level,vip_expire_time,ai_free_count,ai_free_date')
|
||||
->findOrEmpty();
|
||||
$user['has_password'] = !empty($user['password']);
|
||||
$user['has_auth'] = self::hasWechatAuth($userId);
|
||||
$user['version'] = config('project.version');
|
||||
$vipInfo = $user->getVipInfo();
|
||||
$user->hidden(['password', 'is_vip', 'vip_level', 'vip_expire_time', 'ai_free_count', 'ai_free_date']);
|
||||
$result = $user->toArray();
|
||||
$result['vip_info'] = $vipInfo;
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 璁剧疆鐢ㄦ埛淇℃伅
|
||||
* @param int $userId
|
||||
* @param array $params
|
||||
* @return User|false
|
||||
* @author 娈佃獕
|
||||
* @date 2022/9/21 16:53
|
||||
*/
|
||||
public static function setInfo(int $userId, array $params)
|
||||
{
|
||||
try {
|
||||
if ($params['field'] == "avatar") {
|
||||
$params['value'] = FileService::setFileUrl($params['value']);
|
||||
}
|
||||
|
||||
return User::update(
|
||||
[
|
||||
'id' => $userId,
|
||||
$params['field'] => $params['value']
|
||||
]
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
self::$error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 鏄惁鏈夊井淇℃巿鏉冧俊鎭?
|
||||
* @param $userId
|
||||
* @return bool
|
||||
* @author 娈佃獕
|
||||
* @date 2022/9/20 19:36
|
||||
*/
|
||||
public static function hasWechatAuth(int $userId)
|
||||
{
|
||||
//鏄惁鏈夊井淇℃巿鏉冪櫥褰?
|
||||
$terminal = [UserTerminalEnum::WECHAT_MMP, UserTerminalEnum::WECHAT_OA, UserTerminalEnum::PC];
|
||||
$auth = UserAuth::where(['user_id' => $userId])
|
||||
->whereIn('terminal', $terminal)
|
||||
->findOrEmpty();
|
||||
return !$auth->isEmpty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 閲嶇疆鐧诲綍瀵嗙爜
|
||||
* @param $params
|
||||
* @return bool
|
||||
* @author 娈佃獕
|
||||
* @date 2022/9/16 18:06
|
||||
*/
|
||||
public static function resetPassword(array $params)
|
||||
{
|
||||
try {
|
||||
// 鏍¢獙楠岃瘉鐮?
|
||||
$smsDriver = new SmsDriver();
|
||||
if (!$smsDriver->verify($params['mobile'], $params['code'], NoticeEnum::FIND_LOGIN_PASSWORD_CAPTCHA)) {
|
||||
throw new \Exception('verification code error');
|
||||
}
|
||||
|
||||
// 閲嶇疆瀵嗙爜
|
||||
$passwordSalt = Config::get('project.unique_identification');
|
||||
$password = create_password($params['password'], $passwordSalt);
|
||||
|
||||
// 鏇存柊
|
||||
User::where('mobile', $params['mobile'])->update([
|
||||
'password' => $password
|
||||
]);
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 淇瀵嗙爜
|
||||
* @param $params
|
||||
* @param $userId
|
||||
* @return bool
|
||||
* @author 娈佃獕
|
||||
* @date 2022/9/20 19:13
|
||||
*/
|
||||
public static function changePassword(array $params, int $userId)
|
||||
{
|
||||
try {
|
||||
$user = User::findOrEmpty($userId);
|
||||
if ($user->isEmpty()) {
|
||||
throw new \Exception('user not found');
|
||||
}
|
||||
|
||||
// 瀵嗙爜鐩?
|
||||
$passwordSalt = Config::get('project.unique_identification');
|
||||
|
||||
if (!empty($user['password'])) {
|
||||
if (empty($params['old_password'])) {
|
||||
throw new \Exception('璇峰~鍐欐棫瀵嗙爜');
|
||||
}
|
||||
$oldPassword = create_password($params['old_password'], $passwordSalt);
|
||||
if ($oldPassword != $user['password']) {
|
||||
throw new \Exception('鍘熷瘑鐮佷笉姝g‘');
|
||||
}
|
||||
}
|
||||
|
||||
// 淇濆瓨瀵嗙爜
|
||||
$password = create_password($params['password'], $passwordSalt);
|
||||
$user->password = $password;
|
||||
$user->save();
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 鑾峰彇灏忕▼搴忔墜鏈哄彿
|
||||
* @param array $params
|
||||
* @return bool
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
|
||||
* @author 娈佃獕
|
||||
* @date 2023/2/27 11:49
|
||||
*/
|
||||
public static function getMobileByMnp(array $params)
|
||||
{
|
||||
try {
|
||||
$response = (new WeChatMnpService())->getUserPhoneNumber($params['code']);
|
||||
$phoneNumber = $response['phone_info']['purePhoneNumber'] ?? '';
|
||||
if (empty($phoneNumber)) {
|
||||
throw new \Exception('鑾峰彇鎵嬫満鍙风爜澶辫触');
|
||||
}
|
||||
|
||||
$user = User::where([
|
||||
['mobile', '=', $phoneNumber],
|
||||
['id', '<>', $params['user_id']]
|
||||
])->findOrEmpty();
|
||||
|
||||
if (!$user->isEmpty()) {
|
||||
throw new \Exception('mobile number already bound to another account');
|
||||
}
|
||||
|
||||
// 缁戝畾鎵嬫満鍙?
|
||||
User::update([
|
||||
'id' => $params['user_id'],
|
||||
'mobile' => $phoneNumber
|
||||
]);
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 缁戝畾鎵嬫満鍙?
|
||||
* @param $params
|
||||
* @return bool
|
||||
* @author 娈佃獕
|
||||
* @date 2022/9/21 17:28
|
||||
*/
|
||||
public static function bindMobile(array $params)
|
||||
{
|
||||
try {
|
||||
// 妫€鏌ユ墜鏈哄彿鏄惁宸茶鍏朵粬鐢ㄦ埛鍗犵敤
|
||||
$existUser = User::where('mobile', $params['mobile'])
|
||||
->where('id', '<>', $params['user_id'])
|
||||
->findOrEmpty();
|
||||
if (!$existUser->isEmpty()) {
|
||||
throw new \Exception('璇ユ墜鏈哄彿宸茶鍏朵粬鐢ㄦ埛浣跨敤');
|
||||
}
|
||||
|
||||
// 鏍¢獙鐭俊锛氫粠缂撳瓨鏍¢獙楠岃瘉鐮侊紝鐒跺悗鍙涓婁紶琛ㄥ瓨鍦ㄨ鎵嬫満鍙疯褰曞嵆瑙嗕负閫氳繃
|
||||
$cacheKey = 'sms_verify_code:' . $params['mobile'];
|
||||
$cached = \think\facade\Cache::get($cacheKey);
|
||||
if (!$cached || $cached['code'] != $params['code']) {
|
||||
throw new \Exception('verification code error');
|
||||
}
|
||||
|
||||
// sms_upload.phone 鏄彂閫佹柟锛堢敤鎴锋墜鏈哄彿锛夛紝鐢ㄧ敤鎴锋墜鏈哄彿鏌ヨ
|
||||
$userMobile = $params['mobile'];
|
||||
$record = \app\common\model\SmsLog::where(function ($query) use ($userMobile) {
|
||||
$query->where('phone', $userMobile)
|
||||
->whereOr('phone', '+86' . $userMobile)
|
||||
->whereOr('phone', '86' . $userMobile);
|
||||
})
|
||||
->order('sms_time', 'desc')
|
||||
->findOrEmpty();
|
||||
|
||||
if ($record->isEmpty()) {
|
||||
throw new \Exception('鐭俊鍔╂墜璁板綍涓湭鎵惧埌璇ユ墜鏈哄彿');
|
||||
}
|
||||
|
||||
if ((int) $record->status === 0) {
|
||||
$record->status = 1;
|
||||
$record->save();
|
||||
}
|
||||
\think\facade\Cache::delete($cacheKey);
|
||||
|
||||
User::update([
|
||||
'id' => $params['user_id'],
|
||||
'mobile' => $params['mobile'],
|
||||
'is_bind_mobile' => 1
|
||||
]);
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
use app\common\enum\PayEnum;
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\user\User;
|
||||
use app\common\model\vip\VipLevel;
|
||||
use app\common\model\vip\VipOrder;
|
||||
|
||||
class VipOrderLogic extends BaseLogic
|
||||
{
|
||||
public static function levels(): array
|
||||
{
|
||||
return VipLevel::where('status', 1)
|
||||
->order('sort', 'desc')
|
||||
->order('id', 'asc')
|
||||
->select()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public static function createOrder(array $params)
|
||||
{
|
||||
try {
|
||||
$level = VipLevel::where(['id' => $params['level_id'], 'status' => 1])->findOrEmpty();
|
||||
if ($level->isEmpty()) {
|
||||
throw new \Exception('VIP套餐不存在或已下架');
|
||||
}
|
||||
|
||||
// 有效期内不能重复购买同一等级
|
||||
$user = User::findOrEmpty($params['user_id']);
|
||||
if ($user->is_vip == 1 && $user->vip_expire_time > time() && $user->vip_level == $level['level']) {
|
||||
throw new \Exception('您当前已是' . $level['name'] . ',有效期内无需重复购买');
|
||||
}
|
||||
|
||||
$order = VipOrder::create([
|
||||
'sn' => generate_sn(VipOrder::class, 'sn'),
|
||||
'user_id' => $params['user_id'],
|
||||
'level_id' => $level['id'],
|
||||
'level_name' => $level['name'],
|
||||
'duration' => $level['duration'],
|
||||
'order_amount' => $level['price'],
|
||||
'pay_status' => PayEnum::UNPAID,
|
||||
'order_terminal' => $params['terminal'],
|
||||
]);
|
||||
return [
|
||||
'order_id' => (int) $order['id'],
|
||||
'from' => 'vip_order'
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function detail(array $params)
|
||||
{
|
||||
try {
|
||||
$order = VipOrder::where(['id' => $params['order_id'], 'user_id' => $params['user_id']])
|
||||
->append(['pay_status_text', 'pay_way_text'])
|
||||
->findOrEmpty();
|
||||
if ($order->isEmpty()) {
|
||||
throw new \Exception('订单不存在');
|
||||
}
|
||||
$data = $order->toArray();
|
||||
$data['pay_time'] = empty($data['pay_time']) ? '' : date('Y-m-d H:i:s', $data['pay_time']);
|
||||
return $data;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\service\wechat\WeChatOaService;
|
||||
use EasyWeChat\Kernel\Exceptions\Exception;
|
||||
|
||||
/**
|
||||
* 微信
|
||||
* Class WechatLogic
|
||||
* @package app\api\logic
|
||||
*/
|
||||
class WechatLogic extends BaseLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* @notes 微信JSSDK授权接口
|
||||
* @param $params
|
||||
* @return false|mixed[]
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
|
||||
* @author 段誉
|
||||
* @date 2023/3/1 11:49
|
||||
*/
|
||||
public static function jsConfig($params)
|
||||
{
|
||||
try {
|
||||
$url = urldecode($params['url']);
|
||||
return (new WeChatOaService())->getJsConfig($url, [
|
||||
'onMenuShareTimeline',
|
||||
'onMenuShareAppMessage',
|
||||
'onMenuShareQQ',
|
||||
'onMenuShareWeibo',
|
||||
'onMenuShareQZone',
|
||||
'openLocation',
|
||||
'getLocation',
|
||||
'chooseWXPay',
|
||||
'updateAppMessageShareData',
|
||||
'updateTimelineShareData',
|
||||
'openAddress',
|
||||
'scanQRCode'
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
self::setError('获取jssdk失败:' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user