no message

This commit is contained in:
hajimi
2026-06-11 12:15:29 +08:00
parent 10ebe39c30
commit 96efa1d905
5859 changed files with 815501 additions and 5 deletions
@@ -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;
}
}
+355
View File
@@ -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);
}
}
+160
View File
@@ -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,
];
}
}
+511
View File
@@ -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
]);
}
}
+277
View File
@@ -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;
}
}
+92
View File
@@ -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;
}
}
}
+85
View File
@@ -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,
];
}
}
+53
View File
@@ -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,
];
}
}
+60
View File
@@ -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;
}
}
}
+334
View File
@@ -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;
}
}
}
+73
View File
@@ -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;
}
}
}
+65
View File
@@ -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;
}
}
}