1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-08-01 12:28:09 +08:00

Merge branch 'koogua/v1.6.5' into develop

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
xiaochong0302 2023-07-05 17:02:07 +08:00
commit 5454239ac7
161 changed files with 1895 additions and 1213 deletions

View File

@ -1,4 +1,28 @@
### [v1.6.5](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.5)(2023-07-15)
- 升级layui-v2.8.8
- 使用本地图像验证码
- 优化计划任务脚本
- 优化日志清理脚本
- 优化钉钉webhook
- 修正图文分享参数问题
### [v1.6.4](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.4)(2023-06-15)
- 增加推荐课程等Widget
- 更新Composer包
- 修正验证空口令问题
- 优化订单确认页样式
- 优化课程等Me相关信息
- 优化分享URL
- 优化用户课程查找
- 优化通知相关
- 优化Providers
- 优化课程章节权限
- 优化钉钉机器人
### [v1.6.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.3)(2023-05-08)
- 强化文章|提问|课程列表参数检查
- 优化HtmlPurifier内容过滤
- 优化排序条件和分页重复问题

View File

@ -55,7 +55,7 @@ Tips: 请用手机注册一个新账号,用户中心 -> 关注订阅,扫码
### 项目组件
- 后台框架:[phalcon 3.4.5](https://phalcon.io)
- 前端框架:[layui 2.7.6](https://layui.com)
- 前端框架:[layui 2.8.8](https://layui.com)
- 全文检索:[xunsearch 1.4.9](http://www.xunsearch.com)
- 即时通讯:[workerman 3.5.22](https://workerman.net)
- 基础依赖:[php7.3](https://php.net) [mysql5.7](https://mysql.com) [redis5.0](https://redis.io)

View File

@ -12,7 +12,7 @@ use App\Repos\Article as ArticleRepo;
class Article extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -12,7 +12,7 @@ use App\Repos\Chapter as ChapterRepo;
class Chapter extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -12,7 +12,7 @@ use App\Repos\Course as CourseRepo;
class Course extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -13,7 +13,7 @@ use App\Repos\Course as CourseRepo;
class CourseCategoryList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -13,7 +13,7 @@ use App\Repos\Course as CourseRepo;
class CoursePackageList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -14,7 +14,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class CourseRecommendedList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -13,7 +13,7 @@ use App\Repos\Course as CourseRepo;
class CourseRelatedList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -13,7 +13,7 @@ use App\Repos\Course as CourseRepo;
class CourseTeacherList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -14,7 +14,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class CourseTopicList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -0,0 +1,83 @@
<?php
/**
* @copyright Copyright (c) 2023 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Article as ArticleModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class FeaturedArticleList extends Cache
{
protected $lifetime = 86400;
public function getLifetime()
{
$tomorrow = strtotime('tomorrow');
return $tomorrow - time();
}
public function getKey($id = null)
{
return 'featured_article_list';
}
public function getContent($id = null)
{
$limit = 8;
$articles = $this->findArticles($limit);
if ($articles->count() == 0) {
return [];
}
$result = [];
foreach ($articles as $article) {
$userCount = $article->user_count;
if ($article->fake_user_count > $article->user_count) {
$userCount = $article->fake_user_count;
}
$result[] = [
'id' => $article->id,
'title' => $article->title,
'cover' => $article->cover,
'market_price' => (float)$article->market_price,
'vip_price' => (float)$article->vip_price,
'user_count' => $userCount,
'favorite_count' => $article->favorite_count,
'comment_count' => $article->comment_count,
'view_count' => $article->view_count,
'like_count' => $article->like_count,
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|ArticleModel[]
*/
protected function findArticles($limit = 8)
{
return ArticleModel::query()
->where('featured = 1')
->andWhere('published = 1')
->andWhere('deleted = 0')
->orderBy('id DESC')
->limit($limit)
->execute();
}
}

View File

@ -0,0 +1,85 @@
<?php
/**
* @copyright Copyright (c) 2023 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Course as CourseModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class FeaturedCourseList extends Cache
{
protected $lifetime = 86400;
public function getLifetime()
{
$tomorrow = strtotime('tomorrow');
return $tomorrow - time();
}
public function getKey($id = null)
{
return 'featured_course_list';
}
public function getContent($id = null)
{
$limit = 8;
$courses = $this->findCourses($limit);
if ($courses->count() == 0) {
return [];
}
$result = [];
foreach ($courses as $course) {
$userCount = $course->user_count;
if ($course->fake_user_count > $course->user_count) {
$userCount = $course->fake_user_count;
}
$result[] = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'model' => $course->model,
'level' => $course->level,
'rating' => round($course->rating, 1),
'market_price' => (float)$course->market_price,
'vip_price' => (float)$course->vip_price,
'user_count' => $userCount,
'lesson_count' => $course->lesson_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|CourseModel[]
*/
protected function findCourses($limit = 8)
{
return CourseModel::query()
->where('featured = 1')
->andWhere('published = 1')
->andWhere('deleted = 0')
->orderBy('id DESC')
->limit($limit)
->execute();
}
}

View File

@ -12,7 +12,7 @@ use App\Repos\FlashSale as FlashSaleRepo;
class FlashSale extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -14,7 +14,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class HotQuestionList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -19,7 +19,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexFeaturedCourseList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -12,7 +12,7 @@ use App\Services\Logic\FlashSale\SaleList;
class IndexFlashSaleList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -19,7 +19,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexFreeCourseList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -18,7 +18,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexLiveList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -19,7 +19,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexNewCourseList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -17,7 +17,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexSimpleFeaturedCourseList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -17,7 +17,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexSimpleFreeCourseList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -17,7 +17,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexSimpleNewCourseList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -17,7 +17,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexSimpleVipCourseList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -14,7 +14,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexTeacherList extends Cache
{
protected $lifetime = 1 * 3600;
protected $lifetime = 3600;
public function getLifetime()
{

View File

@ -19,7 +19,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexVipCourseList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -13,7 +13,7 @@ use App\Repos\Package as PackageRepo;
class PackageCourseList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -12,7 +12,7 @@ use App\Repos\PointGift as PointGiftRepo;
class PointGift extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -19,7 +19,7 @@ class PointHotGiftList extends Cache
*
* @var int
*/
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
/**
* 显示个数

View File

@ -12,7 +12,7 @@ use App\Repos\Question as QuestionRepo;
class Question extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -15,7 +15,7 @@ class TaggedArticleList extends Cache
protected $limit = 5;
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -15,7 +15,7 @@ class TaggedQuestionList extends Cache
protected $limit = 5;
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -16,7 +16,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class TopAnswererList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -16,7 +16,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class TopAuthorList extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -12,7 +12,7 @@ use App\Repos\User as UserRepo;
class User extends Cache
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -10,7 +10,7 @@ namespace App\Caches;
class UserDailyCounter extends Counter
{
protected $lifetime = 1 * 86400;
protected $lifetime = 86400;
public function getLifetime()
{

View File

@ -19,7 +19,6 @@ class CleanLogTask extends Task
$this->cleanHttpLog();
$this->cleanSqlLog();
$this->cleanListenLog();
$this->cleanCaptchaLog();
$this->cleanWeChatLog();
$this->cleanMailLog();
$this->cleanSmsLog();
@ -96,18 +95,6 @@ class CleanLogTask extends Task
$this->whitelist[] = $type;
}
/**
* 清理验证码服务日志
*/
protected function cleanCaptchaLog()
{
$type = 'captcha';
$this->cleanLog($type, 7);
$this->whitelist[] = $type;
}
/**
* 清理点播服务日志
*/
@ -268,13 +255,12 @@ class CleanLogTask extends Task
* 清理其它日志
*
* @param int $keepDays
* @return mixed
*/
protected function cleanOtherLog($keepDays = 7)
{
$files = glob(log_path() . "/*.log");
if (!$files) return false;
if (!$files) return;
foreach ($files as $file) {
$name = str_replace(log_path() . '/', '', $file);
@ -287,9 +273,9 @@ class CleanLogTask extends Task
if (strtotime($today) - strtotime($date) >= $keepDays * 86400) {
$deleted = unlink($file);
if ($deleted) {
echo "delete {$file} success" . PHP_EOL;
$this->successPrint("remove {$file} success");
} else {
echo "delete {$file} failed" . PHP_EOL;
$this->errorPrint("remove {$file} failed");
}
}
}
@ -300,13 +286,12 @@ class CleanLogTask extends Task
*
* @param string $prefix
* @param int $keepDays 保留天数
* @return mixed
*/
protected function cleanLog($prefix, $keepDays)
{
$files = glob(log_path() . "/{$prefix}-*.log");
if (!$files) return false;
if (!$files) return;
foreach ($files as $file) {
$date = substr($file, -14, 10);
@ -314,9 +299,9 @@ class CleanLogTask extends Task
if (strtotime($today) - strtotime($date) >= $keepDays * 86400) {
$deleted = unlink($file);
if ($deleted) {
echo "------ delete {$file} success ------" . PHP_EOL;
$this->successPrint("remove {$file} success");
} else {
echo "------ delete {$file} failed -------" . PHP_EOL;
$this->errorPrint("remove {$file} failed");
}
}
}

View File

@ -11,5 +11,17 @@ use App\Traits\Service as ServiceTrait;
class Task extends \Phalcon\Cli\Task
{
use ServiceTrait;
protected function successPrint($text)
{
echo "\033[32m {$text} \033[0m" . PHP_EOL;
}
protected function errorPrint($text)
{
echo "\033[31m {$text} \033[0m" . PHP_EOL;
}
}

View File

@ -223,36 +223,6 @@ class SettingController extends Controller
}
}
/**
* @Route("/captcha", name="admin.setting.captcha")
*/
public function captchaAction()
{
$section = 'captcha';
$settingService = new SettingService();
if ($this->request->isPost()) {
$data = $this->request->getPost();
$settingService->updateSettings($section, $data);
$content = [
'location' => $this->request->getHTTPReferer(),
'msg' => '更新配置成功',
];
return $this->jsonSuccess($content);
} else {
$captcha = $settingService->getSettings($section);
$this->view->setVar('captcha', $captcha);
}
}
/**
* @Route("/point", name="admin.setting.point")
*/

View File

@ -20,11 +20,13 @@ class StudentController extends Controller
*/
public function searchAction()
{
$courseId = $this->request->getQuery('course_id', 'int', 0);
$studentService = new StudentService();
$sourceTypes = $studentService->getSourceTypes();
$xmCourses = $studentService->getXmCourses('all');
$xmCourses = $studentService->getXmCourses('all', $courseId);
$this->view->setVar('source_types', $sourceTypes);
$this->view->setVar('xm_courses', $xmCourses);
@ -56,9 +58,11 @@ class StudentController extends Controller
*/
public function addAction()
{
$courseId = $this->request->getQuery('course_id', 'int', 0);
$studentService = new StudentService();
$xmCourses = $studentService->getXmCourses('charge');
$xmCourses = $studentService->getXmCourses('all', $courseId);
$this->view->setVar('xm_courses', $xmCourses);
}

View File

@ -1200,12 +1200,6 @@ class AuthNode extends Service
'type' => 'menu',
'route' => 'admin.setting.sms',
],
[
'id' => '5-2-3',
'title' => '验证码设置',
'type' => 'menu',
'route' => 'admin.setting.captcha',
],
[
'id' => '5-2-4',
'title' => '存储设置',

View File

@ -20,7 +20,7 @@ use App\Validators\CourseUser as CourseUserValidator;
class Student extends Service
{
public function getXmCourses($scope = 'all')
public function getXmCourses($scope = 'all', $courseId = 0)
{
$courseRepo = new CourseRepo();
@ -46,6 +46,7 @@ class Student extends Service
$result[] = [
'name' => sprintf('%s - %s¥%0.2f', $item->id, $item->title, $item->market_price),
'value' => $item->id,
'selected' => $item->id == $courseId,
];
}

View File

@ -12,22 +12,4 @@
{% elseif value == 6 %}
已失败
{% endif %}
{%- endmacro %}
{%- macro refund_status_history(items) %}
{% for item in items %}
{% if item.status == 1 %}
<p>创建时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% elseif item.status == 2 %}
<p>取消时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% elseif item.status == 3 %}
<p>过审时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% elseif item.status == 4 %}
<p>拒绝时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% elseif item.status == 5 %}
<p>完成时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% elseif item.status == 6 %}
<p>失败时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% endif %}
{% endfor %}
{%- endmacro %}

View File

@ -1,124 +0,0 @@
{% extends 'templates/main.volt' %}
{% block content %}
{% set captcha_display = captcha.enabled == 1 ? 'display:block' : 'display:none' %}
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.setting.captcha'}) }}">
<fieldset class="layui-elem-field layui-field-title">
<legend>验证码配置</legend>
</fieldset>
<div class="layui-form-item">
<label class="layui-form-label">开启服务</label>
<div class="layui-input-block">
<input type="radio" name="enabled" value="1" title="是" lay-filter="captcha_enabled" {% if captcha.enabled == 1 %}checked="checked"{% endif %}>
<input type="radio" name="enabled" value="0" title="否" lay-filter="captcha_enabled" {% if captcha.enabled == 0 %}checked="checked"{% endif %}>
</div>
</div>
<div id="captcha-block" style="{{ captcha_display }}">
<div class="layui-form-item">
<label class="layui-form-label">App Id</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="app_id" value="{{ captcha.app_id }}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">Secret Key</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="secret_key" value="{{ captcha.secret_key }}">
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
</div>
</div>
</form>
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.test.captcha'}) }}">
<fieldset class="layui-elem-field layui-field-title">
<legend>验证码测试</legend>
</fieldset>
<div class="layui-form-item">
<label class="layui-form-label"><i class="layui-icon layui-icon-vercode"></i></label>
<div class="layui-input-inline" style="width:200px;">
<button type="button" id="front-captcha-btn" class="layui-btn layui-btn-primary layui-btn-fluid" data-app-id="{{ captcha.app_id }}">前台验证</button>
<button type="button" id="front-verify-tips" class="kg-verify-btn layui-btn layui-btn-primary layui-btn-fluid layui-btn-disabled layui-hide"><i class="layui-icon layui-icon-ok"></i>前台验证成功</button>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i class="layui-icon layui-icon-vercode"></i></label>
<div class="layui-input-inline" style="width:200px;">
<button type="button" id="back-verify-btn" class="layui-btn layui-btn-primary layui-btn-fluid" disabled="disabled" lay-submit="true" lay-filter="back_verify">后台验证</button>
<button type="button" id="back-verify-tips" class="kg-verify-btn layui-btn layui-btn-primary layui-btn-fluid layui-btn-disabled layui-hide"><i class="layui-icon layui-icon-ok"></i>后台验证成功</button>
<input type="hidden" name="ticket">
<input type="hidden" name="rand">
</div>
</div>
</form>
{% endblock %}
{% block inline_js %}
<script src="https://ssl.captcha.qq.com/TCaptcha.js"></script>
<script>
layui.use(['jquery', 'form', 'layer'], function () {
var $ = layui.jquery;
var form = layui.form;
var layer = layui.layer;
var captcha = new TencentCaptcha(
$('#front-captcha-btn')[0],
$('#front-captcha-btn').data('app-id'),
function (res) {
if (res.ret === 0) {
$('input[name=ticket]').val(res.ticket);
$('input[name=rand]').val(res.randstr);
$('#front-captcha-btn').remove();
$('#back-verify-btn').removeAttr('disabled');
$('#front-verify-tips').removeClass('layui-hide');
}
}
);
form.on('radio(captcha_enabled)', function (data) {
var block = $('#captcha-block');
if (data.value === '1') {
block.show();
} else {
block.hide();
}
});
form.on('submit(back_verify)', function (data) {
$.ajax({
type: 'POST',
url: data.form.action,
data: data.field,
success: function (res) {
if (res.code === 0) {
$('#back-verify-btn').remove();
$('#back-verify-tips').removeClass('layui-hide');
}
layer.msg(res.msg, {icon: 1});
},
error: function (xhr) {
var json = JSON.parse(xhr.responseText);
layer.msg(json.msg, {icon: 2});
}
});
return false;
});
});
</script>
{% endblock %}

View File

@ -14,15 +14,15 @@
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">App Secret</label>
<label class="layui-form-label">Webhook</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="app_secret" value="{{ robot.app_secret }}" lay-verify="required">
<input class="layui-input" type="text" name="webhook_url" value="{{ robot.webhook_url }}" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">Access Token</label>
<label class="layui-form-label">加签密钥</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="app_token" value="{{ robot.app_token }}" lay-verify="required">
<input class="layui-input" type="text" name="app_secret" value="{{ robot.app_secret }}" lay-verify="required">
</div>
</div>
<div class="layui-form-item">

View File

@ -18,8 +18,13 @@
{% endif %}
{%- endmacro %}
{% set add_url = url({'for':'admin.student.add'}) %}
{% set search_url = url({'for':'admin.student.search'}) %}
{% if course %}
{% set add_url = url({'for':'admin.student.add'},{'course_id':course.id}) %}
{% set search_url = url({'for':'admin.student.search'},{'course_id':course.id}) %}
{% else %}
{% set add_url = url({'for':'admin.student.add'}) %}
{% set search_url = url({'for':'admin.student.search'}) %}
{% endif %}
<div class="kg-nav">
<div class="kg-nav-left">

View File

@ -16,18 +16,4 @@
{% elseif value == 4 %}
已退款
{% endif %}
{%- endmacro %}
{%- macro trade_status_history(items) %}
{% for item in items %}
{% if item.status == 1 %}
<p>创建时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% elseif item.status == 2 %}
<p>完成时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% elseif item.status == 3 %}
<p>关闭时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% elseif item.status == 4 %}
<p>退款时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% endif %}
{% endfor %}
{%- endmacro %}

View File

@ -76,20 +76,6 @@ class PublicController extends Controller
return $this->jsonSuccess(['site' => $site]);
}
/**
* @Get("/captcha/info", name="api.public.captcha_info")
*/
public function captchaInfoAction()
{
$service = new AppService();
$captcha = $service->getSettings('captcha');
unset($captcha['secret_key']);
return $this->jsonSuccess(['captcha' => $captcha]);
}
/**
* @Get("/payment/info", name="api.public.payment_info")
*/

View File

@ -7,9 +7,10 @@
namespace App\Http\Api\Controllers;
use App\Services\Logic\Verify\MailCode as MailCodeService;
use App\Services\Logic\Verify\SmsCode as SmsCodeService;
use App\Services\Logic\Verify\Ticket as TicketService;
use App\Services\Logic\Verify\Captcha as VerifyCaptchaService;
use App\Services\Logic\Verify\Code as VerifyCodeService;
use App\Services\Logic\Verify\MailCode as VerifyMailCodeService;
use App\Services\Logic\Verify\SmsCode as VerifySmsCodeService;
/**
* @RoutePrefix("/api/verify")
@ -18,15 +19,27 @@ class VerifyController extends Controller
{
/**
* @Post("/ticket", name="api.verify.ticket")
* @Get("/captcha", name="api.verify.captcha")
*/
public function ticketAction()
public function captchaAction()
{
$service = new TicketService();
$service = new VerifyCaptchaService();
$ticket = $service->handle();
$captcha = $service->handle();
return $this->jsonSuccess(['ticket' => $ticket]);
return $this->jsonSuccess(['captcha' => $captcha]);
}
/**
* @Post("/code", name="api.verify.code")
*/
public function codeAction()
{
$service = new VerifyCodeService();
$service->handle();
return $this->jsonSuccess();
}
/**
@ -34,7 +47,7 @@ class VerifyController extends Controller
*/
public function smsCodeAction()
{
$service = new SmsCodeService();
$service = new VerifySmsCodeService();
$service->handle();
@ -46,7 +59,7 @@ class VerifyController extends Controller
*/
public function mailCodeAction()
{
$service = new MailCodeService();
$service = new VerifyMailCodeService();
$service->handle();

View File

@ -15,6 +15,9 @@ use App\Services\Logic\Account\PasswordReset as PasswordResetService;
use App\Services\Logic\Account\PasswordUpdate as PasswordUpdateService;
use App\Services\Logic\Account\PhoneUpdate as PhoneUpdateService;
/**
* @RoutePrefix("/account")
*/
class AccountController extends Controller
{
@ -40,15 +43,10 @@ class AccountController extends Controller
$oauthProvider = $service->handle();
$service = new AccountService();
$captcha = $service->getSettings('captcha');
$this->seo->prependTitle('用户注册');
$this->view->setVar('return_url', $returnUrl);
$this->view->setVar('local_oauth', $oauthProvider['local']);
$this->view->setVar('captcha', $captcha);
}
/**
@ -67,10 +65,6 @@ class AccountController extends Controller
return $this->response->redirect('/');
}
$service = new AccountService();
$captcha = $service->getSettings('captcha');
$service = new OAuthProviderService();
$oauthProvider = $service->handle();
@ -81,7 +75,6 @@ class AccountController extends Controller
$this->view->setVar('oauth_provider', $oauthProvider);
$this->view->setVar('return_url', $returnUrl);
$this->view->setVar('captcha', $captcha);
}
/**
@ -112,13 +105,7 @@ class AccountController extends Controller
return $this->response->redirect(['for' => 'home.index']);
}
$service = new AccountService();
$captcha = $service->getSettings('captcha');
$this->seo->prependTitle('重置密码');
$this->view->setVar('captcha', $captcha);
}
/**

View File

@ -8,7 +8,6 @@
namespace App\Http\Home\Controllers;
use App\Http\Home\Services\FullH5Url as FullH5UrlService;
use App\Http\Home\Services\Index as IndexService;
use App\Services\Logic\Help\HelpInfo as HelpInfoService;
use App\Services\Logic\Help\HelpList as HelpListService;
@ -63,19 +62,9 @@ class HelpController extends Controller
$this->notFound();
}
$featuredCourses = $this->getFeaturedCourses();
$this->seo->prependTitle(['帮助', $help['title']]);
$this->view->setVar('help', $help);
$this->view->setVar('featured_courses', $featuredCourses);
}
protected function getFeaturedCourses()
{
$service = new IndexService();
return $service->getSimpleFeaturedCourses();
}
}

View File

@ -7,40 +7,22 @@
namespace App\Http\Home\Controllers;
use App\Services\Logic\Verify\MailCode as MailCodeService;
use App\Services\Logic\Verify\SmsCode as SmsCodeService;
use App\Traits\Response as ResponseTrait;
/**
* @RoutePrefix("/verify")
*/
class VerifyController extends \Phalcon\Mvc\Controller
class VerifyController extends Controller
{
use ResponseTrait;
/**
* @Post("/sms/code", name="home.verify.sms_code")
* @Get("/captcha", name="home.verify.captcha")
*/
public function smsCodeAction()
public function captchaAction()
{
$service = new SmsCodeService();
$service->handle();
return $this->jsonSuccess();
}
/**
* @Post("/mail/code", name="home.verify.mail_code")
*/
public function mailCodeAction()
{
$service = new MailCodeService();
$service->handle();
return $this->jsonSuccess();
$this->view->pick('verify/captcha');
}
}

View File

@ -7,6 +7,8 @@
namespace App\Http\Home\Controllers;
use App\Caches\FeaturedArticleList as FeaturedArticleListCache;
use App\Caches\FeaturedCourseList as FeaturedCourseListCache;
use App\Services\Logic\Article\TopAuthorList as TopAuthorListService;
use App\Services\Logic\Question\HotQuestionList as HotQuestionListService;
use App\Services\Logic\Question\TopAnswererList as TopAnswererListService;
@ -33,6 +35,34 @@ class WidgetController extends Controller
$this->view->setVar('tags', $pager->items);
}
/**
* @Get("/featured/courses", name="home.widget.featured_courses")
*/
public function featuredCoursesAction()
{
$cache = new FeaturedCourseListCache();
$courses = $cache->get();
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('widget/featured_courses');
$this->view->setVar('courses', $courses);
}
/**
* @Get("/featured/articles", name="home.widget.featured_articles")
*/
public function featuredArticlesAction()
{
$cache = new FeaturedArticleListCache();
$articles = $cache->get();
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('widget/featured_articles');
$this->view->setVar('articles', $articles);
}
/**
* @Get("/hot/questions", name="home.widget.hot_questions")
*/

View File

@ -62,18 +62,6 @@ class Account extends Service
$validator->checkIfAllowLogin($user);
$captcha = $this->getSettings('captcha');
/**
* 验证码是一次性的,放到最后检查,减少第三方调用
*/
if ($captcha['enabled'] == 1) {
$validator = new CaptchaValidator();
$validator->checkCode($post['captcha']['ticket'], $post['captcha']['rand']);
}
$this->auth->saveAuthInfo($user);
$this->eventsManager->fire('Account:afterLogin', $this, $user);

View File

@ -35,7 +35,6 @@
{% block include_js %}
{{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }}
{{ js_include('home/js/captcha.verify.phone.js') }}
{{ js_include('home/js/captcha.verify.email.js') }}

View File

@ -5,7 +5,7 @@
</div>
<div class="layui-form-item">
<label class="layui-icon layui-icon-password"></label>
<input class="layui-input" type="password" name="new_password" autocomplete="off" placeholder="新密码字母数字特殊字符6-16位" lay-verify="required">
<input class="layui-input" type="password" name="new_password" autocomplete="off" placeholder="新密码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-inline verify-input-inline">
@ -19,8 +19,6 @@
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cv-email-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">重置密码</button>
<input id="cv-email-captcha-enabled" type="hidden" value="{{ captcha.enabled }}">
<input id="cv-email-captcha-appId" type="hidden" value="{{ captcha.app_id }}">
<input id="cv-email-captcha-ticket" type="hidden" name="captcha[ticket]">
<input id="cv-email-captcha-rand" type="hidden" name="captcha[rand]">
</div>

View File

@ -5,7 +5,7 @@
</div>
<div class="layui-form-item">
<label class="layui-icon layui-icon-password"></label>
<input class="layui-input" type="password" name="new_password" autocomplete="off" placeholder="新密码字母数字特殊字符6-16位" lay-verify="required">
<input class="layui-input" type="password" name="new_password" autocomplete="off" placeholder="新密码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-inline verify-input-inline">
@ -19,8 +19,6 @@
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cv-phone-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">重置密码</button>
<input id="cv-phone-captcha-enabled" type="hidden" value="{{ captcha.enabled }}">
<input id="cv-phone-captcha-appId" type="hidden" value="{{ captcha.app_id }}">
<input id="cv-phone-captcha-ticket" type="hidden" name="captcha[ticket]">
<input id="cv-phone-captcha-rand" type="hidden" name="captcha[rand]">
</div>

View File

@ -44,8 +44,6 @@
{% block include_js %}
{{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }}
{{ js_include('home/js/captcha.login.js') }}
{{ js_include('home/js/captcha.verify.js') }}
{% endblock %}

View File

@ -1,6 +1,3 @@
{% set disabled_submit = captcha.enabled == 1 ? 'disabled="disabled"' : '' %}
{% set disabled_class = captcha.enabled == 1 ? 'layui-btn-disabled' : '' %}
<form class="layui-form account-form" method="POST" action="{{ url({'for':'home.account.pwd_login'}) }}">
<div class="layui-form-item">
<label class="layui-icon layui-icon-username"></label>
@ -10,21 +7,10 @@
<label class="layui-icon layui-icon-password"></label>
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码" lay-verify="required">
</div>
{% if captcha.enabled == 1 %}
<div id="captcha-block" class="layui-form-item">
<div class="layui-input-block">
<button id="cl-emit-btn" class="layui-btn layui-btn-fluid" type="button">点击完成验证</button>
</div>
</div>
{% endif %}
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cl-submit-btn" class="layui-btn layui-btn-fluid {{ disabled_class }}" {{ disabled_submit }} lay-submit="true" lay-filter="go">立即登录</button>
<button id="cl-submit-btn" class="layui-btn layui-btn-fluid" lay-submit="true" lay-filter="go">立即登录</button>
<input type="hidden" name="return_url" value="{{ return_url }}">
<input id="cl-captcha-enabled" type="hidden" value="{{ captcha.enabled }}">
<input id="cl-captcha-appId" type="hidden" value="{{ captcha.app_id }}">
<input id="cl-captcha-ticket" type="hidden" name="captcha[ticket]">
<input id="cl-captcha-rand" type="hidden" name="captcha[rand]">
</div>
</div>
</form>

View File

@ -15,11 +15,9 @@
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">立即登录</button>
<input type="hidden" name="return_url" value="{{ return_url }}">
<input id="cv-captcha-enabled" type="hidden" value="{{ captcha.enabled }}">
<input id="cv-captcha-appId" type="hidden" value="{{ captcha.app_id }}">
<input id="cv-captcha-ticket" type="hidden" name="captcha[ticket]">
<input id="cv-captcha-rand" type="hidden" name="captcha[rand]">
<input type="hidden" name="return_url" value="{{ return_url }}">
</div>
</div>
</form>

View File

@ -39,7 +39,6 @@
{% block include_js %}
{{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }}
{{ js_include('home/js/account.register.js') }}
{{ js_include('home/js/captcha.verify.phone.js') }}
{{ js_include('home/js/captcha.verify.email.js') }}

View File

@ -2,11 +2,11 @@
<form class="layui-form account-form" method="POST" action="{{ action_url }}">
<div class="layui-form-item">
<label class="layui-icon layui-icon-email"></label>
<input id="cv-email" class="layui-input" type="text" name="email" autocomplete="off" placeholder="邮箱" lay-verify="email">
<input id="cv-email" class="layui-input" type="text" name="email" autocomplete="off" placeholder="邮箱" lay-verify="required|email">
</div>
<div class="layui-form-item">
<label class="layui-icon layui-icon-password"></label>
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码字母数字特殊字符6-16位" lay-verify="required">
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-inline verify-input-inline">
@ -20,7 +20,7 @@
<div class="layui-form-item">
<div class="layui-input-block">
<div class="agree">
<div class="left"><input id="cv-email-agree" type="checkbox" name="agree" lay-skin="primary"></div>
<div class="left"><input id="cv-email-agree" type="checkbox" name="agree" checked="checked" lay-skin="primary"></div>
<div class="right">我已阅读并同意<a href="{{ terms_url }}" target="_blank">《用户协议》</a>和<a href="{{ privacy_url }}" target="_blank">《隐私政策》</a></div>
</div>
</div>
@ -28,11 +28,9 @@
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cv-email-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">注册帐号</button>
<input type="hidden" name="return_url" value="{{ return_url }}">
<input id="cv-email-captcha-enabled" type="hidden" value="{{ captcha.enabled }}">
<input id="cv-email-captcha-appId" type="hidden" value="{{ captcha.app_id }}">
<input id="cv-email-captcha-ticket" type="hidden" name="captcha[ticket]">
<input id="cv-email-captcha-rand" type="hidden" name="captcha[rand]">
<input type="hidden" name="return_url" value="{{ return_url }}">
</div>
</div>
</form>

View File

@ -2,11 +2,11 @@
<form class="layui-form account-form" method="POST" action="{{ action_url }}">
<div class="layui-form-item">
<label class="layui-icon layui-icon-cellphone"></label>
<input id="cv-phone" class="layui-input" type="text" name="phone" autocomplete="off" placeholder="手机" lay-verify="phone">
<input id="cv-phone" class="layui-input" type="text" name="phone" autocomplete="off" placeholder="手机" lay-verify="required|phone">
</div>
<div class="layui-form-item">
<label class="layui-icon layui-icon-password"></label>
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码字母数字特殊字符6-16位" lay-verify="required">
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-inline verify-input-inline">
@ -20,7 +20,7 @@
<div class="layui-form-item">
<div class="layui-input-block">
<div class="agree">
<div class="left"><input id="cv-phone-agree" type="checkbox" name="agree" lay-skin="primary"></div>
<div class="left"><input id="cv-phone-agree" type="checkbox" name="agree" checked="checked" lay-skin="primary"></div>
<div class="right">我已阅读并同意<a href="{{ terms_url }}" target="_blank">《用户协议》</a>和<a href="{{ privacy_url }}" target="_blank">《隐私政策》</a></div>
</div>
</div>
@ -28,11 +28,9 @@
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cv-phone-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">注册帐号</button>
<input type="hidden" name="return_url" value="{{ return_url }}">
<input id="cv-phone-captcha-enabled" type="hidden" value="{{ captcha.enabled }}">
<input id="cv-phone-captcha-appId" type="hidden" value="{{ captcha.app_id }}">
<input id="cv-phone-captcha-ticket" type="hidden" name="captcha[ticket]">
<input id="cv-phone-captcha-rand" type="hidden" name="captcha[rand]">
<input type="hidden" name="return_url" value="{{ return_url }}">
</div>
</div>
</form>

View File

@ -4,6 +4,8 @@
{{ partial('macros/article') }}
{% set share_url = share_url('article',article.id,auth_user.id) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
{% set article_edit_url = url({'for':'home.article.edit','id':article.id}) %}
{% set article_delete_url = url({'for':'home.article.delete','id':article.id}) %}
{% set article_private_url = url({'for':'home.article.private','id':article.id}) %}
@ -102,9 +104,6 @@
{{ partial('article/sticky') }}
</div>
{% set share_url = full_url({'for':'home.share'},{'id':article.id,'type':'article'}) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
<div class="layui-hide">
<input type="hidden" name="share.title" value="{{ article.title }}">
<input type="hidden" name="share.pic" value="{{ article.cover }}">

View File

@ -2,6 +2,8 @@
{% block content %}
{% set share_url = full_url('chapter',chapter.id,auth_user.id) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
{% set course_url = url({'for':'home.course.show','id':chapter.course.id}) %}
{% set learning_url = url({'for':'home.chapter.learning','id':chapter.id}) %}
{% set live_chats_url = url({'for':'home.live.chats','id':chapter.id}) %}
@ -58,9 +60,6 @@
<input type="hidden" name="bind_user_url" value='{{ bind_user_url }}'>
</div>
{% set share_url = full_url({'for':'home.share'},{'id':chapter.id,'type':'chapter'}) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
<div class="layui-hide">
<input type="hidden" name="share.title" value="{{ chapter.course.title }}">
<input type="hidden" name="share.pic" value="{{ chapter.course.cover }}">

View File

@ -2,6 +2,8 @@
{% block content %}
{% set share_url = share_url('chapter',chapter.id,auth_user.id) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
{% set course_url = url({'for':'home.course.show','id':chapter.course.id}) %}
{% set learning_url = url({'for':'home.chapter.learning','id':chapter.id}) %}
@ -44,9 +46,6 @@
<input type="hidden" name="chapter.learning_url" value="{{ learning_url }}">
</div>
{% set share_url = full_url({'for':'home.share'},{'id':chapter.id,'type':'chapter'}) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
<div class="layui-hide">
<input type="hidden" name="share.title" value="{{ chapter.course.title }}">
<input type="hidden" name="share.pic" value="{{ chapter.course.cover }}">

View File

@ -2,6 +2,8 @@
{% block content %}
{% set share_url = share_url('chapter',chapter.id,auth_user.id) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
{% set course_url = url({'for':'home.course.show','id':chapter.course.id}) %}
{% set learning_url = url({'for':'home.chapter.learning','id':chapter.id}) %}
@ -47,9 +49,6 @@
<input type="hidden" name="chapter.play_urls" value='{{ chapter.play_urls|json_encode }}'>
</div>
{% set share_url = full_url({'for':'home.share'},{'id':chapter.id,'type':'chapter'}) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
<div class="layui-hide">
<input type="hidden" name="share.title" value="{{ chapter.course.title }}">
<input type="hidden" name="share.pic" value="{{ chapter.course.cover }}">

View File

@ -31,7 +31,6 @@
{% block include_js %}
{{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }}
{{ js_include('home/js/connect.bind.js') }}
{{ js_include('home/js/captcha.verify.js') }}

View File

@ -1,18 +1,16 @@
<form class="layui-form account-form" method="POST" action="{{ url({'for':'home.oauth.bind_login'}) }}">
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="text" name="account" autocomplete="off" placeholder="手机 / 邮箱" lay-verify="required">
</div>
<label class="layui-icon layui-icon-username"></label>
<input class="layui-input" type="text" name="account" autocomplete="off" placeholder="手机 / 邮箱" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码" lay-verify="required">
</div>
<label class="layui-icon layui-icon-password"></label>
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<div class="agree">
<div class="left"><input id="login-agree" type="checkbox" name="agree" lay-skin="primary"></div>
<div class="left"><input id="login-agree" type="checkbox" name="agree" checked="checked" lay-skin="primary"></div>
<div class="right">我已阅读并同意<a href="{{ terms_url }}" target="_blank">《用户协议》</a>和<a href="{{ privacy_url }}" target="_blank">《隐私政策》</a></div>
</div>
</div>

View File

@ -1,16 +1,15 @@
<form class="layui-form account-form" method="POST" action="{{ url({'for':'home.oauth.bind_register'}) }}">
<div class="layui-form-item">
<div class="layui-input-block">
<input id="cv-account" class="layui-input" type="text" name="account" autocomplete="off" placeholder="手机 / 邮箱" lay-verify="required">
</div>
<label class="layui-icon layui-icon-username"></label>
<input id="cv-account" class="layui-input" type="text" name="account" autocomplete="off" placeholder="手机 / 邮箱" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码字母数字特殊字符6-16位" lay-verify="required">
</div>
<label class="layui-icon layui-icon-password"></label>
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-inline verify-input-inline">
<label class="layui-icon layui-icon-vercode"></label>
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
</div>
<div class="layui-input-inline verify-btn-inline">
@ -20,7 +19,7 @@
<div class="layui-form-item">
<div class="layui-input-block">
<div class="agree">
<div class="left"><input id="register-agree" type="checkbox" name="agree" lay-skin="primary"></div>
<div class="left"><input id="register-agree" type="checkbox" name="agree" checked="checked" lay-skin="primary"></div>
<div class="right">我已阅读并同意<a href="{{ terms_url }}" target="_blank">《用户协议》</a>和<a href="{{ privacy_url }}" target="_blank">《隐私政策》</a></div>
</div>
</div>
@ -28,14 +27,12 @@
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">注册并绑定帐号</button>
<input id="cv-captcha-ticket" type="hidden" name="ticket">
<input id="cv-captcha-rand" type="hidden" name="rand">
<input type="hidden" name="provider" value="{{ provider }}">
<input type="hidden" name="code" value="{{ request.get('code') }}">
<input type="hidden" name="state" value="{{ request.get('state') }}">
<input type="hidden" name="open_user" value='{{ open_user|json_encode }}'>
<input id="cv-enabled" type="hidden" value="{{ captcha.enabled }}">
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
<input id="cv-ticket" type="hidden" name="ticket">
<input id="cv-rand" type="hidden" name="rand">
</div>
</div>
</form>

View File

@ -4,6 +4,9 @@
{{ partial('macros/course') }}
{% set share_url = share_url('course',course.id,auth_user.id) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
<div class="breadcrumb">
<span class="layui-breadcrumb">
<a href="/">首页</a>
@ -93,9 +96,6 @@
{{ partial('course/sticky') }}
</div>
{% set share_url = full_url({'for':'home.share'},{'id':course.id,'type':'course'}) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
<div class="layui-hide">
<input type="hidden" name="share.title" value="{{ course.title }}">
<input type="hidden" name="share.pic" value="{{ course.cover }}">

View File

@ -65,7 +65,7 @@
<span class="key">市场价格</span>
<span class="value price">{{ '¥%0.2f'|format(course.market_price) }}</span>
{% else %}
<span class="key">优惠价格</span>
<span class="key">市场价格</span>
<span class="value free">免费</span>
{% endif %}
{% if course.vip_price > 0 %}

View File

@ -1,4 +1,9 @@
{% if course.me.allow_order == 1 %}
{% if course.me.logged == 0 %}
{% set login_url = url({'for':'home.account.login'}) %}
<div class="sidebar wrap">
<a class="layui-btn layui-btn-fluid" href="{{ login_url }}">用户登录</a>
</div>
{% elseif course.me.allow_order == 1 %}
{% set order_url = url({'for':'home.order.confirm'},{'item_id':course.id,'item_type':1}) %}
<div class="sidebar wrap">
<button class="layui-btn layui-btn-fluid layui-bg-red btn-buy" data-url="{{ order_url }}">立即购买</button>

View File

@ -2,6 +2,8 @@
{% block content %}
{% set courses_url = url({'for':'home.widget.featured_courses'}) %}
<div class="breadcrumb">
<span class="layui-breadcrumb">
<a href="/">首页</a>
@ -30,25 +32,23 @@
</div>
</div>
<div class="layout-sidebar">
<div class="layui-card cs-sidebar">
<div class="layui-card-header">客户服务</div>
<div class="layui-card-body">
<p>没解决你的疑问?试试联系客服吧!</p>
{% if contact_info.qq %}
{% set link_url = 'https://wpa.qq.com/msgrd?v=3&uin=%s&site=qq&menu=yes'|format(contact_info.qq) %}
<p class="center">
<a href="{{ link_url }}" class="layui-btn layui-btn-sm">联系客服</a>
</p>
{% endif %}
</div>
</div>
<div class="sidebar" id="course-list" data-url="{{ courses_url }}"></div>
</div>
</div>
{% endblock %}
{% block include_js %}
{% block inline_js %}
{{ js_include('home/js/help.js') }}
<script>
layui.use(['jquery', 'helper'], function () {
var $ = layui.jquery;
var helper = layui.helper;
var $courseList = $('#course-list');
if ($courseList.length > 0) {
helper.ajaxLoadHtml($courseList.data('url'), $courseList.attr('id'));
}
});
</script>
{% endblock %}

View File

@ -2,7 +2,7 @@
{% block content %}
{{ partial('macros/course') }}
{% set courses_url = url({'for':'home.widget.featured_courses'}) %}
<div class="breadcrumb">
<span class="layui-breadcrumb">
@ -19,18 +19,7 @@
</div>
</div>
<div class="layout-sidebar">
{% if featured_courses %}
<div class="sidebar">
<div class="layui-card">
<div class="layui-card-header">推荐课程</div>
<div class="layui-card-body">
{% for course in featured_courses %}
{{ sidebar_course_card(course) }}
{% endfor %}
</div>
</div>
</div>
{% endif %}
<div class="sidebar" id="course-list" data-url="{{ courses_url }}"></div>
</div>
</div>
@ -40,4 +29,19 @@
{{ css_link('home/css/content.css') }}
{% endblock %}
{% block inline_js %}
<script>
layui.use(['jquery', 'helper'], function () {
var $ = layui.jquery;
var helper = layui.helper;
var $courseList = $('#course-list');
if ($courseList.length > 0) {
helper.ajaxLoadHtml($courseList.data('url'), $courseList.attr('id'));
}
});
</script>
{% endblock %}

View File

@ -4,7 +4,7 @@
<div class="order-item">
<p>课程名称:{{ course.title }}</p>
<p>
<span>优惠价格:<em class="price">{{ '¥%0.2f'|format(course.market_price) }}</em></span>
<span>市场价格:<em class="price">{{ '¥%0.2f'|format(course.market_price) }}</em></span>
<span>会员价格:<em class="price">{{ '¥%0.2f'|format(course.vip_price) }}</em></span>
</p>
{% if course.model in [1,2,3] %}
@ -23,7 +23,7 @@
<div class="order-item">
<p>课程名称:{{ course.title }}</p>
<p>
<span>优惠价格:{{ '¥%0.2f'|format(course.market_price) }}</span>
<span>市场价格:{{ '¥%0.2f'|format(course.market_price) }}</span>
<span>会员价格:<em class="price">{{ '¥%0.2f'|format(course.vip_price) }}</em></span>
</p>
{% if course.model in [1,2,3] %}

View File

@ -6,7 +6,7 @@
{%- macro cart_course_card(course, user) %}
{% set course_url = url({'for':'home.course.show','id':course.id}) %}
<div class="cart-course-card">
<div class="cart-item-card">
<div class="cover">
<img src="{{ course.cover }}!cover_270" alt="{{ course.title }}">
</div>
@ -46,7 +46,7 @@
{% set course = item_info.course %}
{% set reward = item_info.reward %}
{% set course_url = url({'for':'home.course.show','id':course.id}) %}
<div class="cart-course-card">
<div class="cart-item-card">
<div class="cover">
<img src="{{ course.cover }}!cover_270" alt="{{ course.title }}">
</div>
@ -70,7 +70,7 @@
{%- macro cart_vip_card(item_info) %}
{% set vip = item_info.vip %}
<div class="cart-course-card">
<div class="cart-item-card">
<div class="cover">
<img src="{{ vip.cover }}!cover_270" alt="{{ vip.title }}">
</div>

View File

@ -2,7 +2,7 @@
{% block content %}
{{ partial('macros/course') }}
{% set courses_url = url({'for':'home.widget.featured_courses'}) %}
<div class="breadcrumb">
<span class="layui-breadcrumb">
@ -18,18 +18,7 @@
</div>
</div>
<div class="layout-sidebar">
{% if featured_courses %}
<div class="sidebar">
<div class="layui-card">
<div class="layui-card-header">推荐课程</div>
<div class="layui-card-body">
{% for course in featured_courses %}
{{ sidebar_course_card(course) }}
{% endfor %}
</div>
</div>
</div>
{% endif %}
<div class="sidebar" id="course-list" data-url="{{ courses_url }}"></div>
</div>
</div>
@ -39,4 +28,19 @@
{{ css_link('home/css/content.css') }}
{% endblock %}
{% block inline_js %}
<script>
layui.use(['jquery', 'helper'], function () {
var $ = layui.jquery;
var helper = layui.helper;
var $courseList = $('#course-list');
if ($courseList.length > 0) {
helper.ajaxLoadHtml($courseList.data('url'), $courseList.attr('id'));
}
});
</script>
{% endblock %}

View File

@ -4,6 +4,8 @@
{{ partial('macros/question') }}
{% set share_url = share_url('question',question.id,auth_user.id) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
{% set question_report_url = url({'for':'home.report.add'},{'item_id':question.id,'item_type':107}) %}
{% set question_edit_url = url({'for':'home.question.edit','id':question.id}) %}
{% set question_delete_url = url({'for':'home.question.delete','id':question.id}) %}
@ -98,9 +100,6 @@
{{ partial('question/sticky') }}
</div>
{% set share_url = full_url({'for':'home.share'},{'id':question.id,'type':'question'}) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
<div class="layui-hide">
<input type="hidden" name="share.title" value="{{ question.title }}">
<input type="hidden" name="share.pic" value="">

View File

@ -10,16 +10,18 @@
<span class="title">账号安全 - 修改邮箱</span>
</div>
<form class="layui-form security-form" method="POST" action="{{ url({'for':'home.account.update_email'}) }}">
<div class="layui-form-item">
<label class="layui-form-label">登录密码</label>
<div class="layui-input-block">
<input class="layui-input" type="password" name="login_password" autocomplete="off" lay-verify="required">
{% if account.password|length > 0 %}
<div class="layui-form-item">
<label class="layui-form-label">登录密码</label>
<div class="layui-input-block">
<input class="layui-input" type="password" name="login_password" autocomplete="off" placeholder="请输入当前登录密码" lay-verify="required">
</div>
</div>
</div>
{% endif %}
<div class="layui-form-item">
<label class="layui-form-label">邮箱地址</label>
<div class="layui-input-block">
<input id="cv-email" class="layui-input" type="text" name="email" lay-verify="required">
<input id="cv-email" class="layui-input" type="text" name="email" placeholder="请输入新设邮箱地址" lay-verify="required|email">
</div>
</div>
<div class="layui-form-item">
@ -34,8 +36,6 @@
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cv-email-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">提交修改</button>
<input id="cv-email-captcha-enabled" type="hidden" value="{{ captcha.enabled }}">
<input id="cv-email-captcha-appId" type="hidden" value="{{ captcha.app_id }}">
<input id="cv-email-captcha-ticket" type="hidden" name="captcha[ticket]">
<input id="cv-email-captcha-rand" type="hidden" name="captcha[rand]">
</div>
@ -49,7 +49,6 @@
{% block include_js %}
{{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }}
{{ js_include('home/js/captcha.verify.email.js') }}
{% endblock %}

View File

@ -10,16 +10,18 @@
<span class="title">账号安全 - 修改手机</span>
</div>
<form class="layui-form security-form" method="POST" action="{{ url({'for':'home.account.update_phone'}) }}">
<div class="layui-form-item">
<label class="layui-form-label">登录密码</label>
<div class="layui-input-block">
<input class="layui-input" type="password" name="login_password" autocomplete="off" lay-verify="required">
{% if account.password|length > 0 %}
<div class="layui-form-item">
<label class="layui-form-label">登录密码</label>
<div class="layui-input-block">
<input class="layui-input" type="password" name="login_password" autocomplete="off" placeholder="请输入当前登录密码" lay-verify="required">
</div>
</div>
</div>
{% endif %}
<div class="layui-form-item">
<label class="layui-form-label">手机号码</label>
<div class="layui-input-block">
<input id="cv-phone" class="layui-input" type="text" name="phone" lay-verify="required">
<input id="cv-phone" class="layui-input" type="text" name="phone" placeholder="请输入新设手机号码" lay-verify="required|phone">
</div>
</div>
<div class="layui-form-item">
@ -34,8 +36,6 @@
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cv-phone-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">提交修改</button>
<input id="cv-phone-captcha-enabled" type="hidden" value="{{ captcha.enabled }}">
<input id="cv-phone-captcha-appId" type="hidden" value="{{ captcha.app_id }}">
<input id="cv-phone-captcha-ticket" type="hidden" name="captcha[ticket]">
<input id="cv-phone-captcha-rand" type="hidden" name="captcha[rand]">
</div>
@ -49,7 +49,6 @@
{% block include_js %}
{{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }}
{{ js_include('home/js/captcha.verify.phone.js') }}
{% endblock %}

View File

@ -4,6 +4,8 @@
{{ partial('macros/user') }}
{% set share_url = share_url('user',user.id,auth_user.id) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
{% set avatar_class = user.vip == 1 ? 'avatar vip' : 'avatar' %}
<div class="breadcrumb">
@ -13,7 +15,6 @@
<a><cite>{{ user.name }}</cite></a>
</span>
<span class="share">
<a href="javascript:" title="添加好友" class="apply-friend" data-id="{{ user.id }}" data-name="{{ user.name }}" data-avatar="{{ user.avatar }}"><i class="layui-icon layui-icon-user"></i></a>
<a href="javascript:" title="分享到微信"><i class="layui-icon layui-icon-login-wechat share-wechat"></i></a>
<a href="javascript:" title="分享到QQ空间"><i class="layui-icon layui-icon-login-qq share-qq"></i></a>
<a href="javascript:" title="分享到微博"><i class="layui-icon layui-icon-login-weibo share-weibo"></i></a>
@ -80,9 +81,6 @@
</div>
</div>
{% set share_url = full_url({'for':'home.share'},{'id':user.id,'type':'user'}) %}
{% set qrcode_url = url({'for':'home.qrcode'},{'text':share_url}) %}
<div class="layui-hide">
<input type="hidden" name="share.title" value="{{ user.name }}">
<input type="hidden" name="share.pic" value="{{ user.avatar }}">

View File

@ -0,0 +1,105 @@
{% extends 'templates/layer.volt' %}
{% block content %}
<form class="layui-form">
<div class="layui-form-item">
<label class="layui-form-label">验证算式</label>
<div class="layui-input-block">
<img id="img-captcha" class="pointer" title="刷新表达式" alt="验证表达式" width="200" height="50">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">计算结果</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="rand">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn" lay-submit="true" lay-filter="captcha">提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
<div class="layui-hide">
<input type="hidden" name="account" value="{{ request.get('account') }}">
<input type="hidden" name="type" value="{{ request.get('type') }}">
<input type="hidden" name="ticket">
</div>
{% endblock %}
{% block include_js %}
<script>
layui.use(['jquery', 'form', 'layer', 'helper'], function () {
var $ = layui.jquery;
var form = layui.form;
var layer = layui.layer;
var index = parent.layer.getFrameIndex(window.name);
var verify = {
account: $('input[name=account]').val(),
type: $('input[name=type]').val(),
}
var showCaptchaImage = function () {
$.get('/api/verify/captcha', function (res) {
$('#img-captcha').attr('src', res.captcha.content);
$('input[name=ticket]').val(res.captcha.ticket);
});
};
$('#img-captcha').on('click', function () {
showCaptchaImage();
});
form.on('submit(captcha)', function (data) {
var submit = $(this);
var account = $('input[name=account]').val();
var ticket = $('input[name=ticket]').val();
var rand = $('input[name=rand]').val();
submit.attr('disabled', 'disabled').addClass('layui-btn-disabled');
if (verify.type === 'phone') {
parent.layui.$('#cv-phone-submit-btn').removeAttr('disabled').removeClass('layui-btn-disabled');
parent.layui.$('#cv-phone-captcha-ticket').val(ticket);
parent.layui.$('#cv-phone-captcha-rand').val(rand);
} else if (verify.type === 'mail') {
parent.layui.$('#cv-mail-submit-btn').removeAttr('disabled').removeClass('layui-btn-disabled');
parent.layui.$('#cv-mail-captcha-ticket').val(ticket);
parent.layui.$('#cv-mail-captcha-rand').val(rand);
} else {
parent.layui.$('#cv-submit-btn').removeAttr('disabled').removeClass('layui-btn-disabled');
parent.layui.$('#cv-captcha-ticket').val(ticket);
parent.layui.$('#cv-captcha-rand').val(rand);
}
$.ajax({
type: 'POST',
url: '/api/verify/code',
data: {
account: account,
ticket: ticket,
rand: rand,
},
success: function () {
layer.msg('发送验证码成功', {icon: 1});
setTimeout(function () {
parent.layer.close(index);
}, 1500);
}, error: function () {
submit.removeAttr('disabled').removeClass('layui-btn-disabled');
}
});
return false;
});
showCaptchaImage();
});
</script>
{% endblock %}

View File

@ -0,0 +1,20 @@
{% if articles|length > 0 %}
<div class="layui-card">
<div class="layui-card-header">推荐文章</div>
<div class="layui-card-body">
<div class="sidebar-article-list">
{% for article in articles %}
{% set article_url = url({'for':'home.article.show','id':article.id}) %}
<div class="title">
<a href="{{ article_url }}" target="_blank">{{ article.title }}</a>
</div>
<div class="meta">
<span class="view">{{ article.view_count }} 浏览</span>
<span class="like">{{ article.like_count }} 点赞</span>
<span class="comment">{{ article.comment_count }} 评论</span>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}

View File

@ -0,0 +1,12 @@
{{ partial('macros/course') }}
{% if courses|length > 0 %}
<div class="layui-card">
<div class="layui-card-header">推荐课程</div>
<div class="layui-card-body">
{% for course in courses %}
{{ sidebar_course_card(course) }}
{% endfor %}
</div>
</div>
{% endif %}

View File

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

View File

@ -99,7 +99,7 @@ class Redis extends \Phalcon\Cache\Backend\Redis
if ($lifetime === null) {
$tmp = $this->_lastLifetime;
$ttl = $tmp ? $tmp : $frontend->getLifetime();
$ttl = $tmp ?: $frontend->getLifetime();
} else {
$ttl = $lifetime;
}
@ -157,7 +157,7 @@ class Redis extends \Phalcon\Cache\Backend\Redis
$redis = $this->getRedis();
$pattern = "{$this->_prefix}" . ($prefix ? $prefix : '') . '*';
$pattern = "{$this->_prefix}" . ($prefix ?: '') . '*';
$redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY);
@ -255,7 +255,7 @@ class Redis extends \Phalcon\Cache\Backend\Redis
*/
public function flush(): bool
{
return true;
}
/**

139
app/Library/Captcha.php Normal file
View File

@ -0,0 +1,139 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Library;
use Phalcon\Di;
use Phalcon\Text;
class Captcha
{
/**
* @var Cache\Backend\Redis
*/
protected $cache;
public function __construct()
{
$this->cache = Di::getDefault()->get('cache');
}
public function generate()
{
$ticket = $this->getRandTicket();
$cacheKey = $this->getCacheKey($ticket);
$expression = $this->getExpression();
$this->cache->save($cacheKey, $expression['result'], 600);
$width = 100;
$height = 25;
$im = imagecreate($width, $height);
$white = imagecolorallocate($im, 255, 255, 255);
$gray = imagecolorallocate($im, 118, 151, 199);
$bgColor = imagecolorallocate($im, rand(0, 100), rand(0, 100), rand(0, 100));
imagefilledrectangle($im, 0, 0, $width, $height, $bgColor);
for ($i = 0; $i < 200; $i++) {
imagesetpixel($im, rand(0, $width), rand(0, $height), $gray);
}
imagestring($im, 5, 5, 4, $expression['num1'], $white);
imagestring($im, 5, 30, 3, $expression['operator'], $white);
imagestring($im, 5, 45, 4, $expression['num2'], $white);
imagestring($im, 5, 70, 3, '=', $white);
imagestring($im, 5, 85, 3, '?', $white);
ob_start();
imagepng($im);
$content = ob_get_clean();
imagedestroy($im);
return [
'ticket' => $ticket,
'content' => $this->base64Encode($content),
];
}
public function check($ticket, $rand)
{
if (!$ticket) return false;
if (!$rand) return false;
$key = $this->getCacheKey($ticket);
$content = $this->cache->get($key);
return $content == $rand;
}
protected function getExpression()
{
$operators = ['+', '-', '*', '/'];
$index = array_rand($operators);
$operator = $operators[$index];
switch ($operator) {
case '+':
$num1 = rand(10, 50);
$num2 = rand(10, 50);
$result = $num1 + $num2;
break;
case '-':
$num1 = rand(50, 100);
$num2 = rand(10, 50);
$result = $num1 - $num2;
break;
case '*':
$num1 = rand(1, 10);
$num2 = rand(1, 10);
$result = $num1 * $num2;
break;
default:
$multiple = rand(2, 10);
$num1 = $multiple * rand(1, 10);
$num2 = $multiple;
$result = $num1 / $num2;
break;
}
return [
'num1' => $num1,
'num2' => $num2,
'operator' => $operator,
'result' => $result,
];
}
protected function base64Encode($content)
{
return sprintf('data:image/png;base64,%s', base64_encode($content));
}
protected function getRandTicket()
{
return Text::random(Text::RANDOM_ALNUM, 16);
}
protected function getCacheKey($key)
{
return "captcha:{$key}";
}
}

View File

@ -8,8 +8,8 @@
use App\Caches\Setting as SettingCache;
use App\Library\Purifier as HtmlPurifier;
use App\Library\Validators\Common as CommonValidator;
use App\Services\Logic\Url\ShareUrl as ShareUrlService;
use App\Services\Storage as StorageService;
use Koogua\Ip2Region\Searcher as Ip2RegionSearcher;
use Phalcon\Config;
use Phalcon\Di;
use Phalcon\Text;
@ -727,6 +727,21 @@ function kg_full_url($uri, $args = null)
return $baseUrl . $url->get($uri, $args);
}
/**
* 构造分享url
*
* @param string $type
* @param int $id
* @param int $referer
* @return string
*/
function kg_share_url($type, $id, $referer = 0)
{
$service = new ShareUrlService();
return $service->handle($type, $id, $referer);
}
/**
* 获取H5首页地址
*

View File

@ -46,7 +46,7 @@ class Sitemap
public function build($filename = null)
{
$xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
$xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
$xml .= '<urlset xmlns="https://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
foreach ($this->items as $item) {
$item['loc'] = htmlentities($item['loc'], ENT_QUOTES);

View File

@ -258,7 +258,7 @@ class Order extends Model
if (!$order) return $sn;
$this->getOrderSn();
return $this->getOrderSn();
}
}

View File

@ -182,7 +182,7 @@ class Refund extends Model
if (!$order) return $sn;
$this->getRefundSn();
return $this->getRefundSn();
}
}

View File

@ -185,7 +185,7 @@ class Trade extends Model
if (!$order) return $sn;
$this->getTradeSn();
return $this->getTradeSn();
}
}

View File

@ -26,15 +26,19 @@ class Annotation extends Provider
$this->di->setShared($this->serviceName, function () use ($config) {
if ($config->get('env') == ENV_DEV) {
$annotations = new MemoryAnnotations();
} else {
$statsKey = '_ANNOTATION_';
$annotations = new RedisAnnotations([
'host' => $config->path('redis.host'),
'port' => $config->path('redis.port'),
'auth' => $config->path('redis.auth'),
'index' => $config->path('redis.index') ?: 0,
'lifetime' => $config->path('annotation.lifetime') ?: 30 * 86400,
'index' => $config->path('redis.index'),
'lifetime' => $config->path('annotation.lifetime'),
'prefix' => $statsKey . ':',
'statsKey' => $statsKey,
]);

View File

@ -33,7 +33,7 @@ class Cache extends Provider
'host' => $config->path('redis.host'),
'port' => $config->path('redis.port'),
'auth' => $config->path('redis.auth'),
'index' => $config->path('redis.index') ?: 0,
'index' => $config->path('redis.index'),
]);
});
}

View File

@ -42,8 +42,11 @@ class Database extends Provider
$connection = new MySqlAdapter($options);
if ($config->get('env') == ENV_DEV) {
$eventsManager = new EventsManager();
$eventsManager->attach('db', new DbListener());
$connection->setEventsManager($eventsManager);
}

View File

@ -26,15 +26,19 @@ class MetaData extends Provider
$this->di->setShared($this->serviceName, function () use ($config) {
if ($config->get('env') == ENV_DEV) {
$metaData = new MemoryMetaData();
} else {
$statsKey = '_METADATA_';
$metaData = new RedisMetaData([
'host' => $config->path('redis.host'),
'port' => $config->path('redis.port'),
'auth' => $config->path('redis.auth'),
'index' => $config->path('redis.index') ?: 0,
'lifetime' => $config->path('metadata.lifetime') ?: 30 * 86400,
'index' => $config->path('redis.index'),
'lifetime' => $config->path('metadata.lifetime'),
'prefix' => $statsKey . ':',
'statsKey' => $statsKey,
]);

View File

@ -28,8 +28,8 @@ class Session extends Provider
'host' => $config->path('redis.host'),
'port' => $config->path('redis.port'),
'auth' => $config->path('redis.auth'),
'index' => $config->path('redis.index') ?: 0,
'lifetime' => $config->path('session.lifetime') ?: 24 * 3600,
'index' => $config->path('redis.index'),
'lifetime' => $config->path('session.lifetime'),
'prefix' => '_SESSION_:',
]);

View File

@ -39,6 +39,10 @@ class Volt extends Provider
return 'kg_full_url(' . $resolvedArgs . ')';
});
$compiler->addFunction('share_url', function ($resolvedArgs) {
return 'kg_share_url(' . $resolvedArgs . ')';
});
$compiler->addFunction('static_url', function ($resolvedArgs) {
return 'kg_static_url(' . $resolvedArgs . ')';
});
@ -95,6 +99,16 @@ class Volt extends Provider
return 'kg_anonymous(' . $resolvedArgs . ')';
});
$compiler->addFilter('split', function ($resolvedArgs, $exprArgs) use ($compiler) {
$firstArgument = $compiler->expression($exprArgs[0]['expr']);
if (isset($exprArgs[1])) {
$secondArgument = $compiler->expression($exprArgs[1]['expr']);
} else {
$secondArgument = '';
}
return sprintf('explode(%s,%s)', $secondArgument, $firstArgument);
});
return $volt;
});
}

View File

@ -1,131 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Services;
use Phalcon\Logger\Adapter\File as FileLogger;
use TencentCloud\Captcha\V20190722\CaptchaClient;
use TencentCloud\Captcha\V20190722\Models\DescribeCaptchaResultRequest;
use TencentCloud\Common\Credential;
use TencentCloud\Common\Exception\TencentCloudSDKException;
use TencentCloud\Common\Profile\ClientProfile;
use TencentCloud\Common\Profile\HttpProfile;
class Captcha extends Service
{
const END_POINT = 'captcha.tencentcloudapi.com';
/**
* @var array
*/
protected $settings;
/**
* @var FileLogger
*/
protected $logger;
/**
* @var CaptchaClient
*/
protected $client;
public function __construct()
{
$this->settings = $this->getSettings('captcha');
$this->logger = $this->getLogger('captcha');
$this->client = $this->getCaptchaClient();
}
/**
* 校验验证码
*
* @param string $ticket
* @param string $rand
* @return bool
*/
function verify($ticket, $rand)
{
$userIp = $this->request->getClientAddress();
$appId = $this->settings['app_id'];
$secretKey = $this->settings['secret_key'];
$captchaType = 9;
try {
$request = new DescribeCaptchaResultRequest();
/**
* 注意CaptchaType和CaptchaAppId强类型要求
*/
$params = json_encode([
'Ticket' => $ticket,
'Randstr' => $rand,
'UserIp' => $userIp,
'CaptchaType' => (int)$captchaType,
'CaptchaAppId' => (int)$appId,
'AppSecretKey' => $secretKey,
]);
$request->fromJsonString($params);
$this->logger->debug('Describe Captcha Result Request ' . $params);
$response = $this->client->DescribeCaptchaResult($request);
$this->logger->debug('Describe Captcha Result Response ' . $response->toJsonString());
$data = json_decode($response->toJsonString(), true);
$result = $data['CaptchaCode'] == 1;
} catch (TencentCloudSDKException $e) {
$this->logger->error('Describe Captcha Result Exception ' . kg_json_encode([
'code' => $e->getErrorCode(),
'message' => $e->getMessage(),
'requestId' => $e->getRequestId(),
]));
$result = false;
}
return $result;
}
/**
* 获取CaptchaClient
*
* @return CaptchaClient
*/
public function getCaptchaClient()
{
$secret = $this->getSettings('secret');
$secretId = $secret['secret_id'];
$secretKey = $secret['secret_key'];
$region = $this->settings['region'] ?? 'ap-guangzhou';
$credential = new Credential($secretId, $secretKey);
$httpProfile = new HttpProfile();
$httpProfile->setEndpoint(self::END_POINT);
$clientProfile = new ClientProfile();
$clientProfile->setHttpProfile($httpProfile);
return new CaptchaClient($credential, $region, $clientProfile);
}
}

Some files were not shown because too many files have changed in this diff Show More