1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-07-31 04:02:18 +08:00

Merge branch 'koogua/v1.7.8'

# Conflicts:
#	public/static/admin/js/avatar.upload.js
This commit is contained in:
xiaochong0302 2025-06-15 16:51:20 +08:00
commit 478a2dcb9a
22 changed files with 190 additions and 49 deletions

View File

@ -1,3 +1,18 @@
### [v1.7.8](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.8)(2025-06-20)
- 移除ThrottleLimit
- 增加CloseLiveTask
- 增加搜索页图片alt属性striptags过滤
- 后台增加返回顶部快捷方式
- 前台fixbar增加联系电话
- 优化安装脚本
- 优化课时列表直播提示
- 优化后台返回链接
- 优化统计分析代码位置
- 直播回调后更新课时缓存
- 后台清空头像->上传头像
- sitemap.xml直接写入网站根目录
### [v1.7.7](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.7)(2025-04-20) ### [v1.7.7](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.7)(2025-04-20)
- 优化索引管理工具 - 优化索引管理工具

View File

@ -18,6 +18,13 @@ abstract class Migration
abstract public function run(); abstract public function run();
protected function saveSettings(array $settings)
{
foreach ($settings as $setting) {
$this->saveSetting($setting);
}
}
protected function saveSetting(array $setting) protected function saveSetting(array $setting)
{ {
$settingRepo = new SettingRepo(); $settingRepo = new SettingRepo();

View File

@ -0,0 +1,72 @@
<?php
/**
* @copyright Copyright (c) 2024 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Console\Tasks;
use App\Caches\CourseChapterList as CourseChapterListCache;
use App\Models\Chapter as ChapterModel;
use App\Models\ChapterLive as ChapterLiveModel;
use App\Repos\Chapter as ChapterRepo;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class CloseLiveTask extends Task
{
public function mainAction()
{
$chapterLives = $this->findChapterLives();
echo sprintf('pending lives: %s', $chapterLives->count()) . PHP_EOL;
if ($chapterLives->count() == 0) return;
echo '------ start close live task ------' . PHP_EOL;
foreach ($chapterLives as $chapterLive) {
$chapterLive->status = ChapterLiveModel::STATUS_INACTIVE;
$chapterLive->update();
$chapterRepo = new ChapterRepo();
$chapter = $chapterRepo->findById($chapterLive->chapter_id);
$attrs = $chapter->attrs;
$attrs['stream']['status'] = ChapterModel::SS_INACTIVE;
$chapter->attrs = $attrs;
$chapter->update();
$cache = new CourseChapterListCache();
$cache->rebuild($chapterLive->course_id);
}
echo '------ end close live task ------' . PHP_EOL;
}
/**
* 查找待关闭直播
*
* @param int $limit
* @return ResultsetInterface|Resultset|ChapterLiveModel[]
*/
protected function findChapterLives(int $limit = 100)
{
$status = ChapterLiveModel::STATUS_ACTIVE;
$endTime = time() - 3600;
return ChapterLiveModel::query()
->where('status = :status:', ['status' => $status])
->andWhere('end_time < :end_time:', ['end_time' => $endTime])
->limit($limit)
->execute();
}
}

View File

@ -37,7 +37,7 @@ class SitemapTask extends Task
$this->sitemap = new Sitemap(); $this->sitemap = new Sitemap();
$filename = tmp_path('sitemap.xml'); $filename = public_path('sitemap.xml');
echo '------ start sitemap task ------' . PHP_EOL; echo '------ start sitemap task ------' . PHP_EOL;

View File

@ -9,7 +9,7 @@
<div class="kg-nav-left"> <div class="kg-nav-left">
<span class="layui-breadcrumb"> <span class="layui-breadcrumb">
{% if parent.id > 0 %} {% if parent.id > 0 %}
<a class="kg-back" href="{{ back_url }}"><i class="layui-icon layui-icon-return"></i>返回</a> <a href="{{ back_url }}"><i class="layui-icon layui-icon-return"></i>返回</a>
<a><cite>{{ parent.name }}</cite></a> <a><cite>{{ parent.name }}</cite></a>
{% endif %} {% endif %}
<a><cite>分类管理</cite></a> <a><cite>分类管理</cite></a>

View File

@ -43,7 +43,7 @@
<div class="kg-nav"> <div class="kg-nav">
<div class="kg-nav-left"> <div class="kg-nav-left">
<span class="layui-breadcrumb"> <span class="layui-breadcrumb">
<a class="kg-back" href="{{ back_url }}"><i class="layui-icon layui-icon-return"></i>返回</a> <a href="{{ back_url }}"><i class="layui-icon layui-icon-return"></i>返回</a>
<a><cite>{{ course.title }}</cite></a> <a><cite>{{ course.title }}</cite></a>
<a><cite>{{ chapter.title }}</cite></a> <a><cite>{{ chapter.title }}</cite></a>
<a><cite>课时管理</cite></a> <a><cite>课时管理</cite></a>

View File

@ -9,7 +9,7 @@
<div class="kg-nav"> <div class="kg-nav">
<div class="kg-nav-left"> <div class="kg-nav-left">
<span class="layui-breadcrumb"> <span class="layui-breadcrumb">
<a class="kg-back" href="{{ back_url }}"><i class="layui-icon layui-icon-return"></i>返回</a> <a href="{{ back_url }}"><i class="layui-icon layui-icon-return"></i>返回</a>
<a><cite>{{ course.title }}</cite></a> <a><cite>{{ course.title }}</cite></a>
<a><cite>章节管理</cite></a> <a><cite>章节管理</cite></a>
</span> </span>

View File

@ -25,9 +25,7 @@
<div class="kg-nav-left"> <div class="kg-nav-left">
<span class="layui-breadcrumb"> <span class="layui-breadcrumb">
{% if parent.id > 0 %} {% if parent.id > 0 %}
<a class="kg-back" href="{{ back_url }}"> <a href="{{ back_url }}"><i class="layui-icon layui-icon-return"></i>返回</a>
<i class="layui-icon layui-icon-return"></i> 返回
</a>
<a><cite>{{ parent.name }}</cite></a> <a><cite>{{ parent.name }}</cite></a>
{% endif %} {% endif %}
<a><cite>导航管理</cite></a> <a><cite>导航管理</cite></a>

View File

@ -21,12 +21,11 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label" style="padding-top:30px;">头像</label> <label class="layui-form-label" style="padding-top:30px;">头像</label>
<div class="layui-input-inline" style="width:80px;"> <div class="layui-input-inline" style="width:80px;">
<img id="avatar" class="kg-avatar" src="{{ user.avatar }}"> <img id="img-avatar" class="kg-avatar" src="{{ user.avatar }}">
<input type="hidden" name="avatar" value="{{ user.avatar }}"> <input type="hidden" name="avatar" value="{{ user.avatar }}">
<input type="hidden" name="default_avatar" value="{{ default_avatar }}">
</div> </div>
<div class="layui-input-inline" style="padding-top:25px;"> <div class="layui-input-inline" style="padding-top:25px;">
<button id="clear-avatar" class="layui-btn layui-btn-sm" type="button">清空</button> <button id="change-avatar" class="layui-btn layui-btn-sm" type="button">更换</button>
</div> </div>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
@ -153,6 +152,12 @@
{% endblock %} {% endblock %}
{% block include_js %}
{{ js_include('admin/js/avatar.upload.js') }}
{% endblock %}
{% block inline_js %} {% block inline_js %}
<script> <script>
@ -163,12 +168,6 @@
var form = layui.form; var form = layui.form;
var laydate = layui.laydate; var laydate = layui.laydate;
$('#clear-avatar').on('click', function () {
var defaultAvatar = $('input[name=default_avatar]').val();
$('input[name=avatar]').val(defaultAvatar);
$('#avatar').attr('src', defaultAvatar);
});
laydate.render({ laydate.render({
elem: 'input[name=vip_expiry_time]', elem: 'input[name=vip_expiry_time]',
type: 'datetime' type: 'datetime'

View File

@ -36,6 +36,6 @@
</form> </form>
{% else %} {% else %}
<div class="register-close-tips"> <div class="register-close-tips">
<i class="layui-icon layui-icon-tips"></i> 邮箱注册已关闭 <i class="layui-icon layui-icon-lock"></i> 邮箱注册已关闭
</div> </div>
{% endif %} {% endif %}

View File

@ -36,6 +36,6 @@
</form> </form>
{% else %} {% else %}
<div class="register-close-tips"> <div class="register-close-tips">
<i class="layui-icon layui-icon-tips"></i> 手机注册已关闭 <i class="layui-icon layui-icon-lock"></i> 手机注册已关闭
</div> </div>
{% endif %} {% endif %}

View File

@ -98,7 +98,7 @@
{% if lesson.attrs.stream.status == 'active' %} {% if lesson.attrs.stream.status == 'active' %}
<span class="flag flag-active">直播中</span> <span class="flag flag-active">直播中</span>
{% elseif lesson.attrs.start_time > time() %} {% elseif lesson.attrs.start_time > time() %}
<span class="flag flag-pending">倒计时</span> <span class="flag flag-scheduled">倒计时</span>
{% elseif lesson.attrs.end_time < time() %} {% elseif lesson.attrs.end_time < time() %}
<span class="flag flag-ended">已结束</span> <span class="flag flag-ended">已结束</span>
{% elseif lesson.attrs.stream.status == 'inactive' %} {% elseif lesson.attrs.stream.status == 'inactive' %}
@ -110,7 +110,7 @@
{% if lesson.attrs.start_time < time() and lesson.attrs.end_time > time() %} {% if lesson.attrs.start_time < time() and lesson.attrs.end_time > time() %}
<span class="flag flag-active">授课中</span> <span class="flag flag-active">授课中</span>
{% elseif lesson.attrs.start_time > time() %} {% elseif lesson.attrs.start_time > time() %}
<span class="flag flag-pending">未开始</span> <span class="flag flag-scheduled">未开始</span>
{% elseif lesson.attrs.end_time < time() %} {% elseif lesson.attrs.end_time < time() %}
<span class="flag flag-ended">已结束</span> <span class="flag flag-ended">已结束</span>
{% endif %} {% endif %}

View File

@ -19,7 +19,7 @@
{% if item.cover %} {% if item.cover %}
<div class="cover"> <div class="cover">
<a href="{{ article_url }}" target="_blank"> <a href="{{ article_url }}" target="_blank">
<img src="{{ item.cover }}!cover_270" alt="{{ item.title }}"> <img src="{{ item.cover }}!cover_270" alt="{{ item.title|striptags }}">
</a> </a>
</div> </div>
{% endif %} {% endif %}

View File

@ -6,7 +6,7 @@
<div class="search-course-card"> <div class="search-course-card">
<div class="cover"> <div class="cover">
<a href="{{ course_url }}" target="_blank"> <a href="{{ course_url }}" target="_blank">
<img src="{{ item.cover }}!cover_270" alt="{{ item.title }}"> <img src="{{ item.cover }}!cover_270" alt="{{ item.title|striptags }}">
</a> </a>
</div> </div>
<div class="info"> <div class="info">

View File

@ -19,7 +19,7 @@
{% if item.cover %} {% if item.cover %}
<div class="cover"> <div class="cover">
<a href="{{ question_url }}" target="_blank"> <a href="{{ question_url }}" target="_blank">
<img src="{{ item.cover }}!cover_270" alt="{{ item.title }}"> <img src="{{ item.cover }}!cover_270" alt="{{ item.title|striptags }}">
</a> </a>
</div> </div>
{% endif %} {% endif %}

View File

@ -1,6 +1,9 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-CN-Hans"> <html lang="zh-CN-Hans">
<head> <head>
{% if site_info.analytics_enabled == 1 %}
{{ site_info.analytics_script }}
{% endif %}
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
@ -38,12 +41,5 @@
{% block include_js %}{% endblock %} {% block include_js %}{% endblock %}
{% block inline_js %}{% endblock %} {% block inline_js %}{% endblock %}
{% if site_info.analytics_enabled == 1 %}
<div class="layui-hide">
{{ site_info.analytics_script }}
</div>
{% endif %}
</body> </body>
</html> </html>

View File

@ -16,7 +16,7 @@ class AppInfo
protected $link = 'https://www.koogua.com'; protected $link = 'https://www.koogua.com';
protected $version = '1.7.7'; protected $version = '1.7.8';
public function __get($name) public function __get($name)
{ {

View File

@ -7,7 +7,7 @@
namespace App\Services; namespace App\Services;
use App\Caches\CourseChapterList as CatalogCache; use App\Caches\CourseChapterList as CourseChapterListCache;
use App\Models\Chapter as ChapterModel; use App\Models\Chapter as ChapterModel;
use App\Models\ChapterLive as ChapterLiveModel; use App\Models\ChapterLive as ChapterLiveModel;
use App\Repos\Chapter as ChapterRepo; use App\Repos\Chapter as ChapterRepo;
@ -175,7 +175,7 @@ class LiveNotify extends Service
protected function rebuildCatalogCache(ChapterModel $chapter) protected function rebuildCatalogCache(ChapterModel $chapter)
{ {
$cache = new CatalogCache(); $cache = new CourseChapterListCache();
$cache->rebuild($chapter->course_id); $cache->rebuild($chapter->course_id);
} }

View File

@ -0,0 +1,25 @@
layui.use(['jquery', 'layer', 'upload'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var upload = layui.upload;
upload.render({
elem: '#change-avatar',
url: '/admin/upload/avatar/img',
accept: 'images',
acceptMime: 'image/*',
before: function () {
layer.load();
},
done: function (res, index, upload) {
$('#img-avatar').attr('src', res.data.url);
$('input[name=avatar]').val(res.data.url);
layer.closeAll('loading');
},
error: function (index, upload) {
layer.msg('上传文件失败', {icon: 2});
}
});
});

View File

@ -1096,7 +1096,7 @@
color: orange; color: orange;
} }
.lesson-item .flag-pending { .lesson-item .flag-scheduled {
border: 1px solid green; border: 1px solid green;
color: green; color: green;
} }

View File

@ -1,4 +1,4 @@
layui.use(['jquery', 'helper', 'util'], function () { layui.use(['jquery', 'util'], function () {
var $ = layui.jquery; var $ = layui.jquery;
var util = layui.util; var util = layui.util;
@ -47,6 +47,19 @@ layui.use(['jquery', 'helper', 'util'], function () {
}); });
} }
var showPhoneCode = function () {
var content = '<div class="layui-font-32 layui-font-red layui-padding-5">';
content += '<i class="iconfont icon-phone layui-padding-1 layui-font-28"></i>' + window.contact.phone;
content += '</div>';
layer.open({
type: 1,
title: false,
closeBtn: 0,
shadeClose: true,
content: content,
});
}
var bars = []; var bars = [];
if (window.contact.wechat) { if (window.contact.wechat) {
@ -63,6 +76,13 @@ layui.use(['jquery', 'helper', 'util'], function () {
}); });
} }
if (window.contact.phone) {
bars.push({
type: 'phone',
content: '<i class="iconfont icon-phone layui-font-30"></i>',
});
}
util.fixbar({ util.fixbar({
bars: bars, bars: bars,
click: function (type) { click: function (type) {
@ -70,24 +90,30 @@ layui.use(['jquery', 'helper', 'util'], function () {
showWechatCode(); showWechatCode();
} else if (type === 'qq') { } else if (type === 'qq') {
showQQCode(); showQQCode();
} else if (type === 'phone') {
showPhoneCode();
} }
} }
}); });
$('.icon-wechat').on('click', function () { $('.contact > .wechat').on('click', function () {
showWechatCode(); showWechatCode();
}); });
$('.icon-qq').on('click', function () { $('.contact > .qq').on('click', function () {
showQQCode(); showQQCode();
}); });
$('.icon-toutiao').on('click', function () { $('.contact > .toutiao').on('click', function () {
showTouTiaoCode(); showTouTiaoCode();
}); });
$('.icon-douyin').on('click', function () { $('.contact > .douyin').on('click', function () {
showDouYinCode(); showDouYinCode();
}); });
$('.contact > .phone').on('click', function () {
showPhoneCode();
});
}); });

View File

@ -63,6 +63,9 @@ $scheduler->php($script, $bin, ['--task' => 'sync_article_score', '--action' =>
$scheduler->php($script, $bin, ['--task' => 'sync_question_score', '--action' => 'main']) $scheduler->php($script, $bin, ['--task' => 'sync_question_score', '--action' => 'main'])
->hourly(29); ->hourly(29);
$scheduler->php($script, $bin, ['--task' => 'close_live', '--action' => 'main'])
->hourly(31);
$scheduler->php($script, $bin, ['--task' => 'clean_log', '--action' => 'main']) $scheduler->php($script, $bin, ['--task' => 'clean_log', '--action' => 'main'])
->daily(3, 3); ->daily(3, 3);