mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-06-26 20:52:44 +08:00
Merge branch 'koogua/v1.7.2' into demo
This commit is contained in:
commit
335f2fd5cf
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,3 +1,14 @@
|
|||||||
|
### [v1.7.2](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.2)(2024-07-31)
|
||||||
|
|
||||||
|
- 更新layui-v2.9.14
|
||||||
|
- 优化docker自动化脚本
|
||||||
|
- 修正教师直播通知
|
||||||
|
- 修正课程分类删选问题
|
||||||
|
- 后台增加客户服务入口
|
||||||
|
- redis增加expire方法
|
||||||
|
- 日志记录增加log.trace参数
|
||||||
|
- 精简代码
|
||||||
|
|
||||||
### [v1.7.1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.1)(2024-06-31)
|
### [v1.7.1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.1)(2024-06-31)
|
||||||
|
|
||||||
- 更新layui-v2.9.10
|
- 更新layui-v2.9.10
|
||||||
|
@ -7,11 +7,29 @@
|
|||||||
|
|
||||||
namespace App\Console\Migrations;
|
namespace App\Console\Migrations;
|
||||||
|
|
||||||
|
use App\Models\Setting as SettingModel;
|
||||||
|
use App\Repos\Setting as SettingRepo;
|
||||||
use App\Traits\Service as ServiceTrait;
|
use App\Traits\Service as ServiceTrait;
|
||||||
|
|
||||||
abstract class Migration
|
abstract class Migration
|
||||||
{
|
{
|
||||||
|
|
||||||
use ServiceTrait;
|
use ServiceTrait;
|
||||||
|
|
||||||
abstract public function run();
|
abstract public function run();
|
||||||
|
|
||||||
|
protected function saveSetting(array $setting)
|
||||||
|
{
|
||||||
|
$settingRepo = new SettingRepo();
|
||||||
|
|
||||||
|
$item = $settingRepo->findItem($setting['section'], $setting['item_key']);
|
||||||
|
|
||||||
|
if (!$item) {
|
||||||
|
$item = new SettingModel();
|
||||||
|
$item->create($setting);
|
||||||
|
} else {
|
||||||
|
$item->update($setting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -30,7 +30,7 @@ class TeacherLiveNoticeTask extends Task
|
|||||||
$keyName = $this->getCacheKeyName();
|
$keyName = $this->getCacheKeyName();
|
||||||
|
|
||||||
foreach ($lives as $live) {
|
foreach ($lives as $live) {
|
||||||
$redis->sAdd($keyName, $live->chapter_id);
|
$redis->sAdd($keyName, $live->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
$redis->expire($keyName, 86400);
|
$redis->expire($keyName, 86400);
|
||||||
|
41
app/Http/Admin/Controllers/KooguaController.php
Normal file
41
app/Http/Admin/Controllers/KooguaController.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
|
||||||
|
* @license https://opensource.org/licenses/GPL-2.0
|
||||||
|
* @link https://www.koogua.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Admin\Controllers;
|
||||||
|
|
||||||
|
use App\Traits\Response as ResponseTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @RoutePrefix("/admin/koogua")
|
||||||
|
*/
|
||||||
|
class KooguaController extends \Phalcon\Mvc\Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
use ResponseTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/wiki", name="admin.koogua.wiki")
|
||||||
|
*/
|
||||||
|
public function wikiAction()
|
||||||
|
{
|
||||||
|
$url = 'https://www.koogua.com/page/wiki';
|
||||||
|
|
||||||
|
$this->response->redirect($url, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/community", name="admin.koogua.community")
|
||||||
|
*/
|
||||||
|
public function communityAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
$url = 'https://www.koogua.com/question/list';
|
||||||
|
|
||||||
|
return $this->response->redirect($url, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,6 +28,16 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="layui-nav layui-layout-right">
|
<ul class="layui-nav layui-layout-right">
|
||||||
|
<li class="layui-nav-item">
|
||||||
|
<a href="{{ url({'for':'home.index'}) }}" target="_blank">前台首页</a>
|
||||||
|
</li>
|
||||||
|
<li class="layui-nav-item">
|
||||||
|
<a href="javascript:">用户服务</a>
|
||||||
|
<dl class="layui-nav-child">
|
||||||
|
<dd><a href="{{ url({'for':'admin.koogua.wiki'}) }}" target="_blank">系统文档</a></dd>
|
||||||
|
<dd><a href="{{ url({'for':'admin.koogua.community'}) }}" target="_blank">开源社区</a></dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
<li class="layui-nav-item">
|
<li class="layui-nav-item">
|
||||||
<a href="javascript:">{{ auth_user.name }}</a>
|
<a href="javascript:">{{ auth_user.name }}</a>
|
||||||
<dl class="layui-nav-child">
|
<dl class="layui-nav-child">
|
||||||
@ -36,9 +46,6 @@
|
|||||||
<dd><a href="{{ url({'for':'admin.logout'}) }}">退出登录</a></dd>
|
<dd><a href="{{ url({'for':'admin.logout'}) }}">退出登录</a></dd>
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
<li class="layui-nav-item">
|
|
||||||
<a href="{{ url({'for':'home.index'}) }}" target="_blank">前台</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-side layui-bg-black">
|
<div class="layui-side layui-bg-black">
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<div class="layui-tab layui-tab-brief">
|
<div class="layui-tab layui-tab-brief">
|
||||||
<ul class="layui-tab-title kg-tab-title">
|
<ul class="layui-tab-title kg-tab-title">
|
||||||
<li class="layui-this">注册设置</li>
|
<li class="layui-this">基本设置</li>
|
||||||
<li>QQ登录</li>
|
<li>QQ登录</li>
|
||||||
<li>微信登录</li>
|
<li>微信登录</li>
|
||||||
<li>微博登录</li>
|
<li>微博登录</li>
|
||||||
|
@ -16,13 +16,13 @@
|
|||||||
<div class="layui-form-item" style="margin-bottom:20px;">
|
<div class="layui-form-item" style="margin-bottom:20px;">
|
||||||
<label class="layui-form-label">用户协议</label>
|
<label class="layui-form-label">用户协议</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<a class="layui-btn layui-btn-normal" href="{{ url({'for':'admin.page.edit','id':'terms'}) }}">前往设置</a>
|
<a class="layui-btn layui-btn-normal" href="{{ url({'for':'admin.page.edit','id':'terms'}) }}">设置</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item" style="margin-bottom:20px;">
|
<div class="layui-form-item" style="margin-bottom:20px;">
|
||||||
<label class="layui-form-label">隐私政策</label>
|
<label class="layui-form-label">隐私政策</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<a class="layui-btn layui-btn-normal" href="{{ url({'for':'admin.page.edit','id':'privacy'}) }}">前往设置</a>
|
<a class="layui-btn layui-btn-normal" href="{{ url({'for':'admin.page.edit','id':'privacy'}) }}">设置</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
|
@ -16,7 +16,7 @@ class AppInfo
|
|||||||
|
|
||||||
protected $link = 'https://www.koogua.com';
|
protected $link = 'https://www.koogua.com';
|
||||||
|
|
||||||
protected $version = '1.7.1';
|
protected $version = '1.7.2';
|
||||||
|
|
||||||
public function __get($name)
|
public function __get($name)
|
||||||
{
|
{
|
||||||
|
@ -161,7 +161,7 @@ class Redis extends \Phalcon\Cache\Backend\Redis
|
|||||||
|
|
||||||
$redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY);
|
$redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY);
|
||||||
|
|
||||||
$it = null;
|
$it = 0;
|
||||||
|
|
||||||
while ($keys = $redis->scan($it, $pattern)) {
|
while ($keys = $redis->scan($it, $pattern)) {
|
||||||
if (count($result) > $limit) break;
|
if (count($result) > $limit) break;
|
||||||
@ -191,6 +191,35 @@ class Redis extends \Phalcon\Cache\Backend\Redis
|
|||||||
return (bool)$redis->exists($lastKey);
|
return (bool)$redis->exists($lastKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $keyName
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function expire($keyName = null, $lifetime = null): bool
|
||||||
|
{
|
||||||
|
$redis = $this->getRedis();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var FrontendInterface $frontend
|
||||||
|
*/
|
||||||
|
$frontend = $this->_frontend;
|
||||||
|
|
||||||
|
if ($keyName === null) {
|
||||||
|
$lastKey = $this->_lastKey;
|
||||||
|
} else {
|
||||||
|
$lastKey = $this->getKeyName($keyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($lifetime === null) {
|
||||||
|
$tmp = $this->_lastLifetime;
|
||||||
|
$ttl = $tmp ?: $frontend->getLifetime();
|
||||||
|
} else {
|
||||||
|
$ttl = $lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $redis->expire($lastKey, $ttl);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $keyName
|
* @param string $keyName
|
||||||
* @return int|bool
|
* @return int|bool
|
||||||
|
@ -26,19 +26,4 @@ class UserSession extends Repository
|
|||||||
->execute();
|
->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $userId
|
|
||||||
* @param int $minutes
|
|
||||||
* @return ResultsetInterface|Resultset|UserSessionModel[]
|
|
||||||
*/
|
|
||||||
public function findUserRecentSessions($userId, $minutes = 10)
|
|
||||||
{
|
|
||||||
$createTime = time() - $minutes * 60;
|
|
||||||
|
|
||||||
return UserSessionModel::query()
|
|
||||||
->where('user_id = :user_id:', ['user_id' => $userId])
|
|
||||||
->andWhere('create_time > :create_time:', ['create_time' => $createTime])
|
|
||||||
->execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,19 +25,4 @@ class UserToken extends Repository
|
|||||||
->execute();
|
->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $userId
|
|
||||||
* @param int $minutes
|
|
||||||
* @return ResultsetInterface|Resultset|UserTokenModel[]
|
|
||||||
*/
|
|
||||||
public function findUserRecentTokens($userId, $minutes = 10)
|
|
||||||
{
|
|
||||||
$createTime = time() - $minutes * 60;
|
|
||||||
|
|
||||||
return UserTokenModel::query()
|
|
||||||
->where('user_id = :user_id:', ['user_id' => $userId])
|
|
||||||
->andWhere('create_time > :create_time:', ['create_time' => $createTime])
|
|
||||||
->execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -56,19 +56,4 @@ class Admin extends AuthService
|
|||||||
return 'admin_auth_info';
|
return 'admin_auth_info';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasPermission($route)
|
|
||||||
{
|
|
||||||
$authUser = $this->getAuthInfo();
|
|
||||||
|
|
||||||
if ($authUser['root'] == 1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_array($route, $authUser['routes'])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -57,11 +57,6 @@ class Home extends AuthService
|
|||||||
return $authInfo ?: null;
|
return $authInfo ?: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAuthKey()
|
|
||||||
{
|
|
||||||
return 'home_auth_info';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function logoutClients($userId)
|
public function logoutClients($userId)
|
||||||
{
|
{
|
||||||
$cache = $this->getCache();
|
$cache = $this->getCache();
|
||||||
@ -104,4 +99,9 @@ class Home extends AuthService
|
|||||||
return "_PHCR_SESSION_:{$sessionId}";
|
return "_PHCR_SESSION_:{$sessionId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getAuthKey()
|
||||||
|
{
|
||||||
|
return 'home_auth_info';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,10 +38,11 @@ class CourseList extends LogicService
|
|||||||
|
|
||||||
$childCategoryIds = $categoryService->getChildCategoryIds($params['tc']);
|
$childCategoryIds = $categoryService->getChildCategoryIds($params['tc']);
|
||||||
|
|
||||||
/**
|
$parentCategoryIds = [$params['tc']];
|
||||||
* 构造空记录条件
|
|
||||||
*/
|
$allCategoryIds = array_merge($parentCategoryIds, $childCategoryIds);
|
||||||
$params['category_id'] = $childCategoryIds ?: -999;
|
|
||||||
|
$params['category_id'] = $allCategoryIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
$params['published'] = 1;
|
$params['published'] = 1;
|
||||||
|
@ -12,12 +12,9 @@ use App\Exceptions\Forbidden as ForbiddenException;
|
|||||||
use App\Library\Utils\Password as PasswordUtil;
|
use App\Library\Utils\Password as PasswordUtil;
|
||||||
use App\Library\Validators\Common as CommonValidator;
|
use App\Library\Validators\Common as CommonValidator;
|
||||||
use App\Models\Account as AccountModel;
|
use App\Models\Account as AccountModel;
|
||||||
use App\Models\Client as ClientModel;
|
|
||||||
use App\Models\User as UserModel;
|
use App\Models\User as UserModel;
|
||||||
use App\Repos\Account as AccountRepo;
|
use App\Repos\Account as AccountRepo;
|
||||||
use App\Repos\User as UserRepo;
|
use App\Repos\User as UserRepo;
|
||||||
use App\Repos\UserSession as UserSessionRepo;
|
|
||||||
use App\Repos\UserToken as UserTokenRepo;
|
|
||||||
use App\Traits\Client as ClientTrait;
|
use App\Traits\Client as ClientTrait;
|
||||||
|
|
||||||
class Account extends Validator
|
class Account extends Validator
|
||||||
@ -193,34 +190,6 @@ class Account extends Validator
|
|||||||
if ($case1 && $case2) {
|
if ($case1 && $case2) {
|
||||||
throw new ForbiddenException('account.locked');
|
throw new ForbiddenException('account.locked');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->checkFloodLogin($user->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function checkFloodLogin($userId)
|
|
||||||
{
|
|
||||||
$clientIp = $this->getClientIp();
|
|
||||||
$clientType = $this->getClientType();
|
|
||||||
|
|
||||||
if ($clientType == ClientModel::TYPE_PC) {
|
|
||||||
$repo = new UserSessionRepo();
|
|
||||||
$records = $repo->findUserRecentSessions($userId, 10);
|
|
||||||
} else {
|
|
||||||
$repo = new UserTokenRepo();
|
|
||||||
$records = $repo->findUserRecentTokens($userId, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($records->count() == 0) return;
|
|
||||||
|
|
||||||
$clientIps = array_column($records->toArray(), 'client_ip');
|
|
||||||
|
|
||||||
$countValues = array_count_values($clientIps);
|
|
||||||
|
|
||||||
foreach ($countValues as $ip => $count) {
|
|
||||||
if ($clientIp == $ip && $count > 4) {
|
|
||||||
throw new ForbiddenException('account.flood_login');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,12 @@ class ConsoleErrorHandler extends Injectable
|
|||||||
|
|
||||||
$config = $this->getConfig();
|
$config = $this->getConfig();
|
||||||
|
|
||||||
if ($config->get('env') == 'dev') {
|
if ($config->path('env') == 'dev' || $config->path('log.trace')) {
|
||||||
$trace = sprintf('%sTrace Content: %s', PHP_EOL, $e->getTraceAsString());
|
|
||||||
|
$trace = sprintf('Trace Content: %s', $e->getTraceAsString());
|
||||||
|
|
||||||
$logger->error($trace);
|
$logger->error($trace);
|
||||||
|
|
||||||
$content .= $trace;
|
$content .= $trace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,8 +77,10 @@ class HttpErrorHandler extends Injectable
|
|||||||
|
|
||||||
$config = $this->getConfig();
|
$config = $this->getConfig();
|
||||||
|
|
||||||
if ($config->get('env') == 'dev') {
|
if ($config->path('env') == 'dev' || $config->path('log.trace')) {
|
||||||
|
|
||||||
$content = sprintf('Trace Content: %s', $e->getTraceAsString());
|
$content = sprintf('Trace Content: %s', $e->getTraceAsString());
|
||||||
|
|
||||||
$logger->error($content);
|
$logger->error($content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,11 @@ $config['timezone'] = 'Asia/Shanghai';
|
|||||||
*/
|
*/
|
||||||
$config['log']['level'] = Phalcon\Logger::INFO;
|
$config['log']['level'] = Phalcon\Logger::INFO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志链路
|
||||||
|
*/
|
||||||
|
$config['log']['trace'] = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 网站根地址,必须以"/"结尾
|
* 网站根地址,必须以"/"结尾
|
||||||
*/
|
*/
|
||||||
|
@ -47,7 +47,6 @@ $error['captcha.invalid_code'] = '无效的验证码';
|
|||||||
*/
|
*/
|
||||||
$error['account.not_found'] = '账号不存在';
|
$error['account.not_found'] = '账号不存在';
|
||||||
$error['account.locked'] = '账号被锁定,无法登录';
|
$error['account.locked'] = '账号被锁定,无法登录';
|
||||||
$error['account.flood_login'] = '帐号泛滥登录';
|
|
||||||
$error['account.login_pwd_incorrect'] = '登录密码不正确';
|
$error['account.login_pwd_incorrect'] = '登录密码不正确';
|
||||||
$error['account.invalid_login_name'] = '无效的登录名';
|
$error['account.invalid_login_name'] = '无效的登录名';
|
||||||
$error['account.invalid_email'] = '无效的电子邮箱';
|
$error['account.invalid_email'] = '无效的电子邮箱';
|
||||||
|
@ -11,7 +11,7 @@ layui.use(['jquery', 'layer', 'helper'], function () {
|
|||||||
var url = '/verify/captcha?type=all&account=' + $account.val();
|
var url = '/verify/captcha?type=all&account=' + $account.val();
|
||||||
layer.open({
|
layer.open({
|
||||||
type: 2,
|
type: 2,
|
||||||
title: '获取验证码',
|
title: '验证码',
|
||||||
area: ['500px', '250px'],
|
area: ['500px', '250px'],
|
||||||
content: [url, 'no'],
|
content: [url, 'no'],
|
||||||
});
|
});
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user