mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-06-25 04:07:17 +08:00
Merge branch 'koogua/v1.4.4' into develop
This commit is contained in:
commit
61b4e7696e
27
CHANGELOG.md
27
CHANGELOG.md
@ -1,3 +1,30 @@
|
||||
### [v1.4.4](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.4)(2021-09-17)
|
||||
|
||||
- 后台增加邮件手机登录选择配置
|
||||
- 增加移动端支付选项配置
|
||||
- 首页增加秒杀,直播,提问,文章接口
|
||||
- 增加秒杀列表列表接口
|
||||
- 调整markdown解析安全级别
|
||||
- 精简取消点赞以及取消收藏逻辑
|
||||
- 修复浮点转整型精度丢失造成的支付回调失败
|
||||
- 修复竖屏直播时造成的位置错乱
|
||||
- 修复视频清晰度配置序列化问题
|
||||
- 修复评论取消点赞数量不变问题
|
||||
- 修复章节资源数量问题
|
||||
- 修复删除课程后引发的用户课程列表错误问题
|
||||
- 修正课程咨询列表查询条件
|
||||
- 修正回答,兑换礼品说明重复转译的问题
|
||||
- 资源下载查询主键由md5改为加密的ID
|
||||
- 去除上传文件md5唯一索引
|
||||
- 去除课程发布对章节的要求
|
||||
- 去除点播回调中的处理数量限制
|
||||
- 优化文章,课程,提问,群组全文搜索
|
||||
- 优化直播列表数据结构
|
||||
- 优化章节目录交互呈现
|
||||
- 优化后台添加学员重复检查
|
||||
- 优化订单发货逻辑
|
||||
- 优化公众号订阅逻辑
|
||||
|
||||
### [v1.4.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.3)(2021-08-23)
|
||||
|
||||
- 优化邮件验证码
|
||||
|
@ -55,12 +55,10 @@ class CourseUserList extends Builder
|
||||
$result = [];
|
||||
|
||||
foreach ($courses->toArray() as $course) {
|
||||
if ($course['deleted'] == 0) {
|
||||
$course['cover'] = $baseUrl . $course['cover'];
|
||||
$course['attrs'] = json_decode($course['attrs'], true);
|
||||
$result[$course['id']] = $course;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
@ -36,6 +36,11 @@ class ResourceList extends Builder
|
||||
$result = [];
|
||||
|
||||
foreach ($uploads->toArray() as $upload) {
|
||||
|
||||
$id = $this->crypt->encryptBase64($upload['id'], null, true);
|
||||
|
||||
$upload['url'] = $this->url->get(['for' => 'home.download', 'id' => $id]);
|
||||
|
||||
$result[$upload['id']] = $upload;
|
||||
}
|
||||
|
||||
|
44
app/Caches/IndexArticleList.php
Normal file
44
app/Caches/IndexArticleList.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
|
||||
* @license https://opensource.org/licenses/GPL-2.0
|
||||
* @link https://www.koogua.com
|
||||
*/
|
||||
|
||||
namespace App\Caches;
|
||||
|
||||
use App\Models\Article as ArticleModel;
|
||||
use App\Repos\Article as ArticleRepo;
|
||||
use App\Services\Logic\Article\ArticleList as ArticleListService;
|
||||
|
||||
class IndexArticleList extends Cache
|
||||
{
|
||||
|
||||
protected $lifetime = 1 * 86400;
|
||||
|
||||
public function getLifetime()
|
||||
{
|
||||
return $this->lifetime;
|
||||
}
|
||||
|
||||
public function getKey($id = null)
|
||||
{
|
||||
return 'index_article_list';
|
||||
}
|
||||
|
||||
public function getContent($id = null)
|
||||
{
|
||||
$articleRepo = new ArticleRepo();
|
||||
|
||||
$where = ['published' => ArticleModel::PUBLISH_APPROVED];
|
||||
|
||||
$pager = $articleRepo->paginate($where, 'latest', 1, 10);
|
||||
|
||||
$service = new ArticleListService();
|
||||
|
||||
$pager = $service->handleArticles($pager);
|
||||
|
||||
return $pager->items ?: [];
|
||||
}
|
||||
|
||||
}
|
@ -7,14 +7,14 @@
|
||||
|
||||
namespace App\Caches;
|
||||
|
||||
use App\Models\Chapter as ChapterModel;
|
||||
use App\Models\ChapterLive as ChapterLiveModel;
|
||||
use App\Repos\Chapter as ChapterRepo;
|
||||
use App\Repos\Course as CourseRepo;
|
||||
use App\Repos\User as UserRepo;
|
||||
use Phalcon\Mvc\Model\Resultset;
|
||||
use Phalcon\Mvc\Model\ResultsetInterface;
|
||||
|
||||
/**
|
||||
* 直播课程
|
||||
*/
|
||||
class IndexLiveList extends Cache
|
||||
{
|
||||
|
||||
@ -32,32 +32,11 @@ class IndexLiveList extends Cache
|
||||
|
||||
public function getContent($id = null)
|
||||
{
|
||||
/**
|
||||
* 限制输出多少天数(一维限额)
|
||||
*/
|
||||
$dayLimit = 3;
|
||||
$limit = 8;
|
||||
|
||||
/**
|
||||
* 限制每天维度下的输出数(二维限额)
|
||||
*/
|
||||
$perDayLimit = 10;
|
||||
$lives = $this->findChapterLives();
|
||||
|
||||
$beginTime = strtotime('today');
|
||||
$endTime = strtotime("+30 days");
|
||||
|
||||
/**
|
||||
* @var Resultset|ChapterLiveModel[] $lives
|
||||
*/
|
||||
$lives = ChapterLiveModel::query()
|
||||
->betweenWhere('start_time', $beginTime, $endTime)
|
||||
->orderBy('start_time ASC')
|
||||
->execute();
|
||||
|
||||
if ($lives->count() == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
if ($lives->count() == 0) return [];
|
||||
|
||||
$chapterIds = kg_array_column($lives->toArray(), 'chapter_id');
|
||||
|
||||
@ -77,53 +56,85 @@ class IndexLiveList extends Cache
|
||||
|
||||
$courses = $courseRepo->findByIds($courseIds);
|
||||
|
||||
$teacherIds = kg_array_column($courses->toArray(), 'teacher_id');
|
||||
|
||||
$userRepo = new UserRepo();
|
||||
|
||||
$users = $userRepo->findByIds($teacherIds);
|
||||
|
||||
$courseMapping = [];
|
||||
|
||||
foreach ($courses as $course) {
|
||||
$courseMapping[$course->id] = $course;
|
||||
}
|
||||
|
||||
$userMapping = [];
|
||||
|
||||
foreach ($users as $user) {
|
||||
$userMapping[$user->id] = $user;
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
$flag = [];
|
||||
|
||||
foreach ($lives as $live) {
|
||||
|
||||
if (count($result) >= $dayLimit) {
|
||||
break;
|
||||
}
|
||||
|
||||
$day = date('y-m-d', $live->start_time);
|
||||
|
||||
if (isset($result[$day]) && count($result[$day]) >= $perDayLimit) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$chapter = $chapterMapping[$live->chapter_id];
|
||||
$course = $courseMapping[$chapter->course_id];
|
||||
$teacher = $userMapping[$course->teacher_id];
|
||||
|
||||
$teacherInfo = [
|
||||
'id' => $teacher->id,
|
||||
'name' => $teacher->name,
|
||||
'title' => $teacher->title,
|
||||
'avatar' => $teacher->avatar,
|
||||
];
|
||||
|
||||
$chapterInfo = [
|
||||
'id' => $chapter->id,
|
||||
'title' => $chapter->title,
|
||||
'start_time' => $live->start_time,
|
||||
'end_time' => $live->end_time,
|
||||
];
|
||||
|
||||
$courseInfo = [
|
||||
'id' => $course->id,
|
||||
'title' => $course->title,
|
||||
'cover' => $course->cover,
|
||||
'market_price' => $course->market_price,
|
||||
'vip_price' => $course->vip_price,
|
||||
'model' => $course->model,
|
||||
'level' => $course->level,
|
||||
'user_count' => $course->user_count,
|
||||
'lesson_count' => $course->lesson_count,
|
||||
'teacher' => $teacherInfo,
|
||||
];
|
||||
|
||||
$result[$day][] = [
|
||||
if (!isset($flag[$course->id]) && count($flag) < $limit) {
|
||||
$flag[$course->id] = 1;
|
||||
$result[] = [
|
||||
'id' => $live->id,
|
||||
'status' => $live->status,
|
||||
'start_time' => $live->start_time,
|
||||
'end_time' => $live->end_time,
|
||||
'course' => $courseInfo,
|
||||
'chapter' => $chapterInfo,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ResultsetInterface|Resultset|ChapterLiveModel[]
|
||||
*/
|
||||
protected function findChapterLives()
|
||||
{
|
||||
$startTime = strtotime('today');
|
||||
$endTime = strtotime('+30 days');
|
||||
|
||||
return $this->modelsManager->createBuilder()
|
||||
->columns('cl.*')
|
||||
->addFrom(ChapterLiveModel::class, 'cl')
|
||||
->join(ChapterModel::class, 'cl.chapter_id = c.id', 'c')
|
||||
->betweenWhere('start_time', $startTime, $endTime)
|
||||
->orderBy('start_time ASC')
|
||||
->getQuery()
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
||||
|
44
app/Caches/IndexQuestionList.php
Normal file
44
app/Caches/IndexQuestionList.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
|
||||
* @license https://opensource.org/licenses/GPL-2.0
|
||||
* @link https://www.koogua.com
|
||||
*/
|
||||
|
||||
namespace App\Caches;
|
||||
|
||||
use App\Models\Question as QuestionModel;
|
||||
use App\Repos\Question as QuestionRepo;
|
||||
use App\Services\Logic\Question\QuestionList as QuestionListService;
|
||||
|
||||
class IndexQuestionList extends Cache
|
||||
{
|
||||
|
||||
protected $lifetime = 1 * 86400;
|
||||
|
||||
public function getLifetime()
|
||||
{
|
||||
return $this->lifetime;
|
||||
}
|
||||
|
||||
public function getKey($id = null)
|
||||
{
|
||||
return 'index_question_list';
|
||||
}
|
||||
|
||||
public function getContent($id = null)
|
||||
{
|
||||
$questionRepo = new QuestionRepo();
|
||||
|
||||
$where = ['published' => QuestionModel::PUBLISH_APPROVED];
|
||||
|
||||
$pager = $questionRepo->paginate($where, 'latest', 1, 10);
|
||||
|
||||
$service = new QuestionListService();
|
||||
|
||||
$pager = $service->handleQuestions($pager);
|
||||
|
||||
return $pager->items ?: [];
|
||||
}
|
||||
|
||||
}
|
@ -66,6 +66,7 @@ class IndexSimpleVipCourseList extends Cache
|
||||
{
|
||||
return CourseModel::query()
|
||||
->where('published = 1')
|
||||
->andWhere('market_price > vip_price')
|
||||
->andWhere('vip_price >= 0')
|
||||
->orderBy('score DESC')
|
||||
->limit($limit)
|
||||
|
@ -114,6 +114,7 @@ class IndexVipCourseList extends Cache
|
||||
return CourseModel::query()
|
||||
->inWhere('category_id', $categoryIds)
|
||||
->andWhere('published = 1')
|
||||
->andWhere('market_price > vip_price')
|
||||
->andWhere('vip_price >= 0')
|
||||
->orderBy('score DESC')
|
||||
->limit($limit)
|
||||
|
@ -17,6 +17,7 @@ use App\Models\Trade as TradeModel;
|
||||
use App\Repos\Course as CourseRepo;
|
||||
use App\Repos\ImGroup as ImGroupRepo;
|
||||
use App\Repos\ImGroupUser as ImGroupUserRepo;
|
||||
use App\Repos\ImUser as ImUserRepo;
|
||||
use App\Repos\Order as OrderRepo;
|
||||
use App\Repos\User as UserRepo;
|
||||
use App\Services\Logic\Notice\OrderFinish as OrderFinishNotice;
|
||||
@ -134,9 +135,7 @@ class DeliverTask extends Task
|
||||
$courseUser->role_type = CourseUserModel::ROLE_STUDENT;
|
||||
$courseUser->source_type = CourseUserModel::SOURCE_CHARGE;
|
||||
|
||||
if ($courseUser->create() === false) {
|
||||
throw new \RuntimeException('Create Course User Failed');
|
||||
}
|
||||
$courseUser->create();
|
||||
|
||||
$courseRepo = new CourseRepo();
|
||||
|
||||
@ -150,6 +149,10 @@ class DeliverTask extends Task
|
||||
|
||||
$group = $groupRepo->findByCourseId($course->id);
|
||||
|
||||
$imUserRepo = new ImUserRepo();
|
||||
|
||||
$imUser = $imUserRepo->findById($order->owner_id);
|
||||
|
||||
$groupUserRepo = new ImGroupUserRepo();
|
||||
|
||||
$groupUser = $groupUserRepo->findGroupUser($group->id, $order->owner_id);
|
||||
@ -160,10 +163,13 @@ class DeliverTask extends Task
|
||||
|
||||
$groupUser->group_id = $group->id;
|
||||
$groupUser->user_id = $order->owner_id;
|
||||
$groupUser->create();
|
||||
|
||||
if ($groupUser->create() === false) {
|
||||
throw new \RuntimeException('Create Group User Failed');
|
||||
}
|
||||
$imUser->group_count += 1;
|
||||
$imUser->update();
|
||||
|
||||
$group->user_count += 1;
|
||||
$group->update();
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,9 +187,7 @@ class DeliverTask extends Task
|
||||
$courseUser->role_type = CourseUserModel::ROLE_STUDENT;
|
||||
$courseUser->source_type = CourseUserModel::SOURCE_CHARGE;
|
||||
|
||||
if ($courseUser->create() === false) {
|
||||
throw new \RuntimeException('Create Course User Failed');
|
||||
}
|
||||
$courseUser->create();
|
||||
|
||||
$courseRepo = new CourseRepo();
|
||||
|
||||
@ -197,6 +201,10 @@ class DeliverTask extends Task
|
||||
|
||||
$group = $groupRepo->findByCourseId($course->id);
|
||||
|
||||
$imUserRepo = new ImUserRepo();
|
||||
|
||||
$imUser = $imUserRepo->findById($order->owner_id);
|
||||
|
||||
$groupUserRepo = new ImGroupUserRepo();
|
||||
|
||||
$groupUser = $groupUserRepo->findGroupUser($group->id, $order->owner_id);
|
||||
@ -207,10 +215,13 @@ class DeliverTask extends Task
|
||||
|
||||
$groupUser->group_id = $group->id;
|
||||
$groupUser->user_id = $order->owner_id;
|
||||
$groupUser->create();
|
||||
|
||||
if ($groupUser->create() === false) {
|
||||
throw new \RuntimeException('Create Group User Failed');
|
||||
}
|
||||
$imUser->group_count += 1;
|
||||
$imUser->update();
|
||||
|
||||
$group->user_count += 1;
|
||||
$group->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -224,11 +235,10 @@ class DeliverTask extends Task
|
||||
$user = $userRepo->findById($order->owner_id);
|
||||
|
||||
$user->vip_expiry_time = $itemInfo['vip']['expiry_time'];
|
||||
|
||||
$user->vip = 1;
|
||||
|
||||
if ($user->update() === false) {
|
||||
throw new \RuntimeException('Update Vip Expiry Failed');
|
||||
}
|
||||
$user->update();
|
||||
}
|
||||
|
||||
protected function handleOrderConsumePoint(OrderModel $order)
|
||||
|
@ -23,8 +23,6 @@ class VodEventTask extends Task
|
||||
|
||||
$handles = [];
|
||||
|
||||
$count = 0;
|
||||
|
||||
foreach ($events as $event) {
|
||||
|
||||
$handles[] = $event['EventHandle'];
|
||||
@ -36,10 +34,6 @@ class VodEventTask extends Task
|
||||
} elseif ($event['EventType'] == 'FileDeleted') {
|
||||
$this->handleFileDeletedEvent($event);
|
||||
}
|
||||
|
||||
$count++;
|
||||
|
||||
if ($count >= 12) break;
|
||||
}
|
||||
|
||||
$this->confirmEvents($handles);
|
||||
|
@ -350,10 +350,12 @@ class SettingController extends Controller
|
||||
$qqAuth = $settingService->getQQAuthSettings();
|
||||
$weixinAuth = $settingService->getWeixinAuthSettings();
|
||||
$weiboAuth = $settingService->getWeiboAuthSettings();
|
||||
$localAuth = $settingService->getLocalAuthSettings();
|
||||
|
||||
$this->view->setVar('qq_auth', $qqAuth);
|
||||
$this->view->setVar('weixin_auth', $weixinAuth);
|
||||
$this->view->setVar('weibo_auth', $weiboAuth);
|
||||
$this->view->setVar('local_auth', $localAuth);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1205,7 +1205,7 @@ class AuthNode extends Service
|
||||
],
|
||||
[
|
||||
'id' => '5-1-12',
|
||||
'title' => '开放登录',
|
||||
'title' => '登录设置',
|
||||
'type' => 'menu',
|
||||
'route' => 'admin.setting.oauth',
|
||||
],
|
||||
|
@ -124,6 +124,20 @@ class Resource extends Service
|
||||
$chapter->resource_count = $chapterRepo->countResources($chapter->id);
|
||||
|
||||
$chapter->update();
|
||||
|
||||
$parent = $chapterRepo->findById($chapter->parent_id);
|
||||
|
||||
$lessons = $chapterRepo->findLessons($parent->id);
|
||||
|
||||
$resourceCount = 0;
|
||||
|
||||
foreach ($lessons as $lesson) {
|
||||
$resourceCount += $chapterRepo->countResources($lesson->id);
|
||||
}
|
||||
|
||||
$parent->resource_count = $resourceCount;
|
||||
|
||||
$parent->update();
|
||||
}
|
||||
|
||||
protected function recountCourseResources(CourseModel $course)
|
||||
|
@ -15,6 +15,11 @@ use App\Services\WeChat as WeChatService;
|
||||
class Setting extends Service
|
||||
{
|
||||
|
||||
public function getLocalAuthSettings()
|
||||
{
|
||||
return $this->getSettings('oauth.local');
|
||||
}
|
||||
|
||||
public function getQQAuthSettings()
|
||||
{
|
||||
$oauth = $this->getSettings('oauth.qq');
|
||||
|
@ -103,7 +103,7 @@ class Student extends Service
|
||||
$data['user_id'] = $user->id;
|
||||
$data['expiry_time'] = $expiryTime;
|
||||
|
||||
$validator->checkIfJoined($post['course_id'], $post['user_id']);
|
||||
$validator->checkIfImported($post['course_id'], $post['user_id']);
|
||||
|
||||
$courseUser = new CourseUserModel();
|
||||
|
||||
|
@ -4,12 +4,16 @@
|
||||
|
||||
<div class="layui-tab layui-tab-brief">
|
||||
<ul class="layui-tab-title kg-tab-title">
|
||||
<li class="layui-this">QQ登录</li>
|
||||
<li class="layui-this">本地登录</li>
|
||||
<li>QQ登录</li>
|
||||
<li>微信登录</li>
|
||||
<li>新浪微博</li>
|
||||
<li>微博登录</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
{{ partial('setting/oauth_local') }}
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
{{ partial('setting/oauth_qq') }}
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
|
24
app/Http/Admin/Views/setting/oauth_local.volt
Normal file
24
app/Http/Admin/Views/setting/oauth_local.volt
Normal file
@ -0,0 +1,24 @@
|
||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.setting.oauth'}) }}">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">开启手机登录</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="login_with_phone" value="1" title="是" {% if local_auth.login_with_phone == "1" %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="login_with_phone" value="0" title="否" {% if local_auth.login_with_phone == "0" %}checked="checked"{% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">开启邮箱登录</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="login_with_email" value="1" title="是" {% if local_auth.login_with_email == "1" %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="login_with_email" value="0" title="否" {% if local_auth.login_with_email == "0" %}checked="checked"{% endif %}>
|
||||
</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>
|
||||
<input type="hidden" name="section" value="oauth.local">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -54,12 +54,11 @@ class AnswerController extends Controller
|
||||
|
||||
$answer = $service->handle();
|
||||
|
||||
$content = [
|
||||
'answer' => $answer,
|
||||
'msg' => '创建回答成功',
|
||||
];
|
||||
$service = new AnswerInfoService();
|
||||
|
||||
return $this->jsonSuccess($content);
|
||||
$answer = $service->handle($answer->id);
|
||||
|
||||
return $this->jsonSuccess(['answer' => $answer]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,12 +70,7 @@ class AnswerController extends Controller
|
||||
|
||||
$answer = $service->handle($id);
|
||||
|
||||
$content = [
|
||||
'answer' => $answer,
|
||||
'msg' => '更新回答成功',
|
||||
];
|
||||
|
||||
return $this->jsonSuccess($content);
|
||||
return $this->jsonSuccess(['answer' => $answer]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,20 +99,6 @@ class AnswerController extends Controller
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/unaccept", name="api.answer.unaccept")
|
||||
*/
|
||||
public function unacceptAction($id)
|
||||
{
|
||||
$service = new AnswerAcceptService();
|
||||
|
||||
$data = $service->handle($id);
|
||||
|
||||
$msg = $data['action'] == 'do' ? '采纳成功' : '取消采纳成功';
|
||||
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/like", name="api.answer.like")
|
||||
*/
|
||||
@ -133,18 +113,4 @@ class AnswerController extends Controller
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/like", name="api.answer.unlike")
|
||||
*/
|
||||
public function unlikeAction($id)
|
||||
{
|
||||
$service = new AnswerLikeService();
|
||||
|
||||
$data = $service->handle($id);
|
||||
|
||||
$msg = $data['action'] == 'do' ? '点赞成功' : '取消点赞成功';
|
||||
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -82,20 +82,6 @@ class ArticleController extends Controller
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/unfavorite", name="api.article.unfavorite")
|
||||
*/
|
||||
public function unfavoriteAction($id)
|
||||
{
|
||||
$service = new ArticleFavoriteService();
|
||||
|
||||
$data = $service->handle($id);
|
||||
|
||||
$msg = $data['action'] == 'do' ? '收藏成功' : '取消收藏成功';
|
||||
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/like", name="api.article.like")
|
||||
*/
|
||||
@ -110,18 +96,4 @@ class ArticleController extends Controller
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/like", name="api.article.unlike")
|
||||
*/
|
||||
public function unlikeAction($id)
|
||||
{
|
||||
$service = new ArticleLikeService();
|
||||
|
||||
$data = $service->handle($id);
|
||||
|
||||
$msg = $data['action'] == 'do' ? '点赞成功' : '取消点赞成功';
|
||||
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,12 +7,12 @@
|
||||
|
||||
namespace App\Http\Api\Controllers;
|
||||
|
||||
use App\Services\Logic\Article\CommentList as CommentListService;
|
||||
use App\Services\Logic\Chapter\ChapterInfo as ChapterInfoService;
|
||||
use App\Services\Logic\Chapter\ChapterLike as ChapterLikeService;
|
||||
use App\Services\Logic\Chapter\ConsultList as ChapterConsultListService;
|
||||
use App\Services\Logic\Chapter\Learning as ChapterLearningService;
|
||||
use App\Services\Logic\Chapter\ResourceList as ChapterResourceListService;
|
||||
use App\Services\Logic\Chapter\CommentList as CommentListService;
|
||||
use App\Services\Logic\Chapter\ConsultList as ConsultListService;
|
||||
use App\Services\Logic\Chapter\Learning as LearningService;
|
||||
use App\Services\Logic\Chapter\ResourceList as ResourceListService;
|
||||
|
||||
/**
|
||||
* @RoutePrefix("/api/chapter")
|
||||
@ -37,7 +37,7 @@ class ChapterController extends Controller
|
||||
*/
|
||||
public function consultsAction($id)
|
||||
{
|
||||
$service = new ChapterConsultListService();
|
||||
$service = new ConsultListService();
|
||||
|
||||
$pager = $service->handle($id);
|
||||
|
||||
@ -49,7 +49,7 @@ class ChapterController extends Controller
|
||||
*/
|
||||
public function resourcesAction($id)
|
||||
{
|
||||
$service = new ChapterResourceListService();
|
||||
$service = new ResourceListService();
|
||||
|
||||
$resources = $service->handle($id);
|
||||
|
||||
@ -91,7 +91,7 @@ class ChapterController extends Controller
|
||||
*/
|
||||
public function learningAction($id)
|
||||
{
|
||||
$service = new ChapterLearningService();
|
||||
$service = new LearningService();
|
||||
|
||||
$service->handle($id);
|
||||
|
||||
|
@ -102,18 +102,4 @@ class CommentController extends Controller
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/like", name="home.comment.like")
|
||||
*/
|
||||
public function unlikeAction($id)
|
||||
{
|
||||
$service = new CommentLikeService();
|
||||
|
||||
$data = $service->handle($id);
|
||||
|
||||
$msg = $data['action'] == 'do' ? '点赞成功' : '取消点赞成功';
|
||||
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -89,18 +89,4 @@ class ConsultController extends Controller
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/unlike", name="api.consult.unlike")
|
||||
*/
|
||||
public function unlikeAction($id)
|
||||
{
|
||||
$service = new ConsultLikeService();
|
||||
|
||||
$data = $service->handle($id);
|
||||
|
||||
$msg = $data['action'] == 'do' ? '点赞成功' : '取消点赞成功';
|
||||
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -120,18 +120,4 @@ class CourseController extends Controller
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/unfavorite", name="api.course.unfavorite")
|
||||
*/
|
||||
public function unfavoriteAction($id)
|
||||
{
|
||||
$service = new CourseFavoriteService();
|
||||
|
||||
$data = $service->handle($id);
|
||||
|
||||
$msg = $data['action'] == 'do' ? '收藏成功' : '取消收藏成功';
|
||||
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
}
|
||||
|
48
app/Http/Api/Controllers/FlashSaleController.php
Normal file
48
app/Http/Api/Controllers/FlashSaleController.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
|
||||
* @license https://opensource.org/licenses/GPL-2.0
|
||||
* @link https://www.koogua.com
|
||||
*/
|
||||
|
||||
namespace App\Http\Api\Controllers;
|
||||
|
||||
use App\Services\Logic\FlashSale\OrderCreate as OrderCreateService;
|
||||
use App\Services\Logic\FlashSale\SaleList as SaleListService;
|
||||
use App\Services\Logic\Order\OrderInfo as OrderInfoService;
|
||||
|
||||
/**
|
||||
* @RoutePrefix("/api/flash/sale")
|
||||
*/
|
||||
class FlashSaleController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @Get("/list", name="api.flash_sale.list")
|
||||
*/
|
||||
public function listAction()
|
||||
{
|
||||
$service = new SaleListService();
|
||||
|
||||
$sales = $service->handle();
|
||||
|
||||
return $this->jsonSuccess(['sales' => $sales]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/order", name="api.flash_sale.order")
|
||||
*/
|
||||
public function orderAction()
|
||||
{
|
||||
$service = new OrderCreateService();
|
||||
|
||||
$order = $service->handle();
|
||||
|
||||
$service = new OrderInfoService();
|
||||
|
||||
$order = $service->handle($order->sn);
|
||||
|
||||
return $this->jsonSuccess(['order' => $order]);
|
||||
}
|
||||
|
||||
}
|
@ -7,6 +7,9 @@
|
||||
|
||||
namespace App\Http\Api\Controllers;
|
||||
|
||||
use App\Caches\IndexArticleList;
|
||||
use App\Caches\IndexLiveList;
|
||||
use App\Caches\IndexQuestionList;
|
||||
use App\Caches\IndexSimpleFeaturedCourseList;
|
||||
use App\Caches\IndexSimpleFreeCourseList;
|
||||
use App\Caches\IndexSimpleNewCourseList;
|
||||
@ -31,6 +34,42 @@ class IndexController extends Controller
|
||||
return $this->jsonSuccess(['slides' => $slides]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/articles", name="api.index.articles")
|
||||
*/
|
||||
public function articlesAction()
|
||||
{
|
||||
$cache = new IndexArticleList();
|
||||
|
||||
$articles = $cache->get();
|
||||
|
||||
return $this->jsonSuccess(['articles' => $articles]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/questions", name="api.index.questions")
|
||||
*/
|
||||
public function questionsAction()
|
||||
{
|
||||
$cache = new IndexQuestionList();
|
||||
|
||||
$questions = $cache->get();
|
||||
|
||||
return $this->jsonSuccess(['questions' => $questions]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/lives", name="api.index.lives")
|
||||
*/
|
||||
public function livesAction()
|
||||
{
|
||||
$cache = new IndexLiveList();
|
||||
|
||||
$lives = $cache->get();
|
||||
|
||||
return $this->jsonSuccess(['lives' => $lives]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/courses/featured", name="api.index.featured_courses")
|
||||
*/
|
||||
|
@ -86,6 +86,24 @@ class PublicController extends Controller
|
||||
return $this->jsonSuccess(['captcha' => $captcha]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/payment/info", name="api.public.payment_info")
|
||||
*/
|
||||
public function paymentInfoAction()
|
||||
{
|
||||
$service = new AppService();
|
||||
|
||||
$alipay = $service->getSettings('pay.alipay');
|
||||
$wxpay = $service->getSettings('pay.wxpay');
|
||||
|
||||
$content = [
|
||||
'alipay' => ['enabled' => $alipay['enabled']],
|
||||
'wxpay' => ['enabled' => $wxpay['enabled']],
|
||||
];
|
||||
|
||||
return $this->jsonSuccess($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/reward/options", name="api.public.reward_options")
|
||||
*/
|
||||
|
@ -95,20 +95,6 @@ class QuestionController extends Controller
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/unfavorite", name="api.question.unfavorite")
|
||||
*/
|
||||
public function unfavoriteAction($id)
|
||||
{
|
||||
$service = new QuestionFavoriteService();
|
||||
|
||||
$data = $service->handle($id);
|
||||
|
||||
$msg = $data['action'] == 'do' ? '收藏成功' : '取消收藏成功';
|
||||
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/like", name="api.question.like")
|
||||
*/
|
||||
@ -123,18 +109,4 @@ class QuestionController extends Controller
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/like", name="api.question.unlike")
|
||||
*/
|
||||
public function unlikeAction($id)
|
||||
{
|
||||
$service = new QuestionLikeService();
|
||||
|
||||
$data = $service->handle($id);
|
||||
|
||||
$msg = $data['action'] == 'do' ? '点赞成功' : '取消点赞成功';
|
||||
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -89,18 +89,4 @@ class ReviewController extends Controller
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/unlike", name="api.review.unlike")
|
||||
*/
|
||||
public function unlikeAction($id)
|
||||
{
|
||||
$service = new ReviewLikeService();
|
||||
|
||||
$data = $service->handle($id);
|
||||
|
||||
$msg = $data['action'] == 'do' ? '点赞成功' : '取消点赞成功';
|
||||
|
||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,10 +7,11 @@
|
||||
|
||||
namespace App\Http\Api\Controllers;
|
||||
|
||||
use App\Services\Logic\Search\Article as ArticleSearchService;
|
||||
use App\Services\Logic\Search\Course as CourseSearchService;
|
||||
use App\Services\Logic\Search\Group as GroupSearchService;
|
||||
use App\Services\Logic\Search\User as UserSearchService;
|
||||
use App\Services\Logic\Search\Article as ArticleSearch;
|
||||
use App\Services\Logic\Search\Course as CourseSearch;
|
||||
use App\Services\Logic\Search\Group as GroupSearch;
|
||||
use App\Services\Logic\Search\Question as QuestionSearch;
|
||||
use App\Services\Logic\Search\User as UserSearch;
|
||||
|
||||
/**
|
||||
* @RoutePrefix("/api/search")
|
||||
@ -45,22 +46,25 @@ class SearchController extends Controller
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @return ArticleSearchService|CourseSearchService|GroupSearchService|UserSearchService
|
||||
* @return ArticleSearch|QuestionSearch|CourseSearch|GroupSearch|UserSearch
|
||||
*/
|
||||
protected function getSearchService($type)
|
||||
{
|
||||
switch ($type) {
|
||||
case 'article':
|
||||
$service = new ArticleSearchService();
|
||||
$service = new ArticleSearch();
|
||||
break;
|
||||
case 'question':
|
||||
$service = new QuestionSearch();
|
||||
break;
|
||||
case 'group':
|
||||
$service = new GroupSearchService();
|
||||
$service = new GroupSearch();
|
||||
break;
|
||||
case 'user':
|
||||
$service = new UserSearchService();
|
||||
$service = new UserSearch();
|
||||
break;
|
||||
default:
|
||||
$service = new CourseSearchService();
|
||||
$service = new CourseSearch();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -25,13 +25,15 @@ class PublicController extends \Phalcon\Mvc\Controller
|
||||
use SecurityTrait;
|
||||
|
||||
/**
|
||||
* @Get("/download/{md5}", name="home.download")
|
||||
* @Get("/download/{id}", name="home.download")
|
||||
*/
|
||||
public function downloadAction($md5)
|
||||
public function downloadAction($id)
|
||||
{
|
||||
$id = $this->crypt->decryptBase64($id, null, true);
|
||||
|
||||
$repo = new UploadRepo();
|
||||
|
||||
$file = $repo->findByMd5($md5);
|
||||
$file = $repo->findById($id);
|
||||
|
||||
if ($file) {
|
||||
|
||||
|
@ -90,6 +90,18 @@ class WeChatOfficialAccount extends Service
|
||||
|
||||
protected function handleSubscribeEvent($message)
|
||||
{
|
||||
$openId = $message['FromUserName'] ?? '';
|
||||
$eventKey = $message['EventKey'] ?? '';
|
||||
|
||||
/**
|
||||
* 带场景值的关注事件
|
||||
*/
|
||||
$userId = str_replace('qrscene_', '', $eventKey);
|
||||
|
||||
if ($userId && $openId) {
|
||||
$this->saveWechatSubscribe($userId, $openId);
|
||||
}
|
||||
|
||||
return new TextMessage('开心呀,我们又多了一个小伙伴!');
|
||||
}
|
||||
|
||||
@ -115,26 +127,8 @@ class WeChatOfficialAccount extends Service
|
||||
|
||||
$userId = str_replace('qrscene_', '', $eventKey);
|
||||
|
||||
$userRepo = new UserRepo();
|
||||
|
||||
$user = $userRepo->findById($userId);
|
||||
|
||||
if (!$user) return $this->emptyReply();
|
||||
|
||||
$subscribeRepo = new WeChatSubscribeRepo();
|
||||
|
||||
$subscribe = $subscribeRepo->findByOpenId($openId);
|
||||
|
||||
if ($subscribe) {
|
||||
if ($subscribe->user_id != $userId) {
|
||||
$subscribe->user_id = $userId;
|
||||
}
|
||||
$subscribe->update();
|
||||
} else {
|
||||
$subscribe = new WeChatSubscribeModel();
|
||||
$subscribe->user_id = $userId;
|
||||
$subscribe->open_id = $openId;
|
||||
$subscribe->create();
|
||||
if ($userId && $openId) {
|
||||
$this->saveWechatSubscribe($userId, $openId);
|
||||
}
|
||||
|
||||
return $this->emptyReply();
|
||||
@ -200,4 +194,31 @@ class WeChatOfficialAccount extends Service
|
||||
return new TextMessage('没有匹配的服务哦!');
|
||||
}
|
||||
|
||||
protected function saveWechatSubscribe($userId, $openId)
|
||||
{
|
||||
if (!$userId || !$openId) return;
|
||||
|
||||
$userRepo = new UserRepo();
|
||||
|
||||
$user = $userRepo->findById($userId);
|
||||
|
||||
if (!$user) return;
|
||||
|
||||
$subscribeRepo = new WeChatSubscribeRepo();
|
||||
|
||||
$subscribe = $subscribeRepo->findByOpenId($openId);
|
||||
|
||||
if ($subscribe) {
|
||||
if ($subscribe->user_id != $userId) {
|
||||
$subscribe->user_id = $userId;
|
||||
$subscribe->update();
|
||||
}
|
||||
} else {
|
||||
$subscribe = new WeChatSubscribeModel();
|
||||
$subscribe->user_id = $userId;
|
||||
$subscribe->open_id = $openId;
|
||||
$subscribe->create();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% set login_with_phone = oauth_provider.local.login_with_phone == 1 %}
|
||||
{% set login_with_email = oauth_provider.local.login_with_email == 1 %}
|
||||
|
||||
<div class="layui-breadcrumb breadcrumb">
|
||||
<a href="/">首页</a>
|
||||
<a><cite>登录</cite></a>
|
||||
|
@ -1,8 +1,20 @@
|
||||
<form class="layui-form account-form" method="POST" action="{{ url({'for':'home.account.pwd_login'}) }}">
|
||||
{% if login_with_phone and login_with_email %}
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-icon layui-icon-username"></label>
|
||||
<input class="layui-input" type="text" name="account" autocomplete="off" placeholder="手机 / 邮箱" lay-verify="required">
|
||||
</div>
|
||||
{% elseif login_with_email %}
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-icon layui-icon-email"></label>
|
||||
<input class="layui-input" type="text" name="account" autocomplete="off" placeholder="邮箱" lay-verify="email">
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-icon layui-icon-cellphone"></label>
|
||||
<input class="layui-input" type="text" name="account" autocomplete="off" placeholder="手机" lay-verify="phone">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-icon layui-icon-password"></label>
|
||||
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码" lay-verify="required">
|
||||
|
@ -9,11 +9,10 @@
|
||||
<th width="15%">操作</th>
|
||||
</tr>
|
||||
{% for item in items %}
|
||||
{% set download_url = url({'for':'home.download','md5':item.md5}) %}
|
||||
<tr>
|
||||
<td>{{ item.name }}</td>
|
||||
<td>{{ item.size|human_size }}</td>
|
||||
<td><a class="layui-btn layui-btn-sm" href="{{ download_url }}" target="_blank">下载</a></td>
|
||||
<td><a class="layui-btn layui-btn-sm" href="{{ item.url }}" target="_blank">下载</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
@ -34,8 +34,8 @@
|
||||
<div class="course-tab-wrap wrap">
|
||||
<div class="layui-tab layui-tab-brief course-tab">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">目录</li>
|
||||
<li>详情</li>
|
||||
<li class="layui-this">详情</li>
|
||||
<li>目录</li>
|
||||
{% if show_tab_packages %}
|
||||
<li>套餐<span class="tab-count">{{ course.package_count }}</span></li>
|
||||
{% endif %}
|
||||
@ -48,10 +48,10 @@
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
{{ partial('course/show_catalog') }}
|
||||
<div class="course-details markdown-body">{{ course.details }}</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<div class="course-details markdown-body">{{ course.details }}</div>
|
||||
{{ partial('course/show_catalog') }}
|
||||
</div>
|
||||
{% if show_tab_packages %}
|
||||
{% set packages_url = url({'for':'home.course.packages','id':course.id}) %}
|
||||
|
@ -5,11 +5,14 @@
|
||||
<i class="layui-icon layui-icon-play"></i>
|
||||
<span class="title">{{ lesson.title }}</span>
|
||||
{% if lesson.free == 1 %}
|
||||
<span class="layui-badge free-badge">试听</span>
|
||||
<span class="iconfont icon-trial"></span>
|
||||
{% endif %}
|
||||
{% if lesson.me.duration > 0 %}
|
||||
<span class="study-time" title="学习时长:{{ lesson.me.duration|duration }}"><i class="layui-icon layui-icon-time"></i></span>
|
||||
{% endif %}
|
||||
{% if priv == 'deny' %}
|
||||
<span class="iconfont icon-lock"></span>
|
||||
{% endif %}
|
||||
<span class="duration">{{ lesson.attrs.duration|duration }}</span>
|
||||
</a>
|
||||
{%- endmacro %}
|
||||
@ -21,11 +24,14 @@
|
||||
<i class="layui-icon layui-icon-video"></i>
|
||||
<span class="title">{{ lesson.title }}</span>
|
||||
{% if lesson.free == 1 %}
|
||||
<span class="layui-badge free-badge">试听</span>
|
||||
<span class="iconfont icon-trial"></span>
|
||||
{% endif %}
|
||||
{% if lesson.me.duration > 0 %}
|
||||
<span class="study-time" title="学习时长:{{ lesson.me.duration|duration }}"><i class="layui-icon layui-icon-time"></i></span>
|
||||
{% endif %}
|
||||
{% if priv == 'deny' %}
|
||||
<span class="iconfont icon-lock"></span>
|
||||
{% endif %}
|
||||
<span class="live" title="{{ date('Y-m-d H:i',lesson.attrs.start_time) }}">{{ live_status_info(lesson) }}</span>
|
||||
</a>
|
||||
{%- endmacro %}
|
||||
@ -37,11 +43,14 @@
|
||||
<i class="layui-icon layui-icon-read"></i>
|
||||
<span class="title">{{ lesson.title }}</span>
|
||||
{% if lesson.free == 1 %}
|
||||
<span class="layui-badge free-badge">试读</span>
|
||||
<span class="iconfont icon-trial"></span>
|
||||
{% endif %}
|
||||
{% if lesson.me.duration > 0 %}
|
||||
<span class="study-time" title="学习时长:{{ lesson.me.duration|duration }}"><i class="layui-icon layui-icon-time"></i></span>
|
||||
{% endif %}
|
||||
{% if priv == 'deny' %}
|
||||
<span class="iconfont icon-lock"></span>
|
||||
{% endif %}
|
||||
</a>
|
||||
{%- endmacro %}
|
||||
|
||||
@ -57,7 +66,7 @@
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro live_status_info(lesson) %}
|
||||
{% if lesson.attrs.stream.status == 'active' %}
|
||||
{% if lesson.attrs.start_time < time() and lesson.attrs.end_time > time() %}
|
||||
<span class="active">{{ date('m月d日 H:i',lesson.attrs.start_time) }} 直播中</span>
|
||||
{% elseif lesson.attrs.start_time > time() %}
|
||||
<span class="pending">{{ date('m月d日 H:i',lesson.attrs.start_time) }} 倒计时</span>
|
||||
|
@ -42,7 +42,7 @@
|
||||
{% endif %}
|
||||
{% if auth_user.id > 0 %}
|
||||
<li class="layui-nav-item">
|
||||
<a href="javascript:">创建</a>
|
||||
<a href="javascript:"><i class="layui-icon layui-icon-add-circle"></i> 发布</a>
|
||||
<dl class="layui-nav-child">
|
||||
<dd><a href="{{ url({'for':'home.question.add'}) }}" target="_blank">提问题</a></dd>
|
||||
<dd><a href="{{ url({'for':'home.article.add'}) }}" target="_blank">写文章</a></dd>
|
||||
|
@ -13,8 +13,8 @@
|
||||
{% else %}
|
||||
{{ icon_link('favicon.ico') }}
|
||||
{% endif %}
|
||||
<link rel="preload" href="//at.alicdn.com/t/font_2760791_c83l29k7bz.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="//at.alicdn.com/t/font_2760791_c83l29k7bz.css">
|
||||
<link rel="preload" href="//at.alicdn.com/t/font_2760791_mj6x0o9n15s.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="//at.alicdn.com/t/font_2760791_mj6x0o9n15s.css">
|
||||
{{ css_link('lib/layui/css/layui.css') }}
|
||||
{{ css_link('home/css/common.css') }}
|
||||
{% block link_css %}{% endblock %}
|
||||
|
@ -16,7 +16,7 @@ class AppInfo
|
||||
|
||||
protected $link = 'https://koogua.com';
|
||||
|
||||
protected $version = '1.4.3';
|
||||
protected $version = '1.4.4';
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
|
@ -413,7 +413,7 @@ function kg_cos_icon_url($path, $style = null)
|
||||
/**
|
||||
* 清除存储图片处理样式
|
||||
*
|
||||
* @param $path
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function kg_cos_img_style_trim($path)
|
||||
@ -424,16 +424,18 @@ function kg_cos_img_style_trim($path)
|
||||
/**
|
||||
* 解析markdown内容
|
||||
*
|
||||
* @param $content
|
||||
* @param string $content
|
||||
* @param string $htmlInput (escape|strip)
|
||||
* @param bool $allowUnsafeLinks
|
||||
* @return string
|
||||
*/
|
||||
function kg_parse_markdown($content)
|
||||
function kg_parse_markdown($content, $htmlInput = 'escape', $allowUnsafeLinks = false)
|
||||
{
|
||||
$content = str_replace('!content_800', '', $content);
|
||||
|
||||
$parser = new League\CommonMark\GithubFlavoredMarkdownConverter([
|
||||
'html_input' => 'strip',
|
||||
'allow_unsafe_links' => false,
|
||||
'html_input' => $htmlInput,
|
||||
'allow_unsafe_links' => $allowUnsafeLinks,
|
||||
]);
|
||||
|
||||
return $parser->convertToHtml($content);
|
||||
@ -442,7 +444,7 @@ function kg_parse_markdown($content)
|
||||
/**
|
||||
* 解析内容摘要
|
||||
*
|
||||
* @param $content
|
||||
* @param string $content
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
|
@ -78,6 +78,18 @@ class Chapter extends Repository
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return ResultsetInterface|Resultset|ChapterModel[]
|
||||
*/
|
||||
public function findLessons($id)
|
||||
{
|
||||
return ChapterModel::query()
|
||||
->where('parent_id = :parent_id:', ['parent_id' => $id])
|
||||
->andWhere('deleted = 0')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileId
|
||||
* @return ChapterModel|Model|bool
|
||||
|
@ -14,11 +14,16 @@ class OAuthProvider extends LogicService
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$local = $this->getSettings('oauth.local');
|
||||
$weixin = $this->getSettings('oauth.weixin');
|
||||
$weibo = $this->getSettings('oauth.weibo');
|
||||
$qq = $this->getSettings('oauth.qq');
|
||||
|
||||
return [
|
||||
'local' => [
|
||||
'login_with_phone' => $local['login_with_phone'],
|
||||
'login_with_email' => $local['login_with_email'],
|
||||
],
|
||||
'weixin' => ['enabled' => $weixin['enabled']],
|
||||
'weibo' => ['enabled' => $weibo['enabled']],
|
||||
'qq' => ['enabled' => $qq['enabled']],
|
||||
|
@ -72,8 +72,6 @@ class ArticleList extends LogicService
|
||||
'source_type' => $article['source_type'],
|
||||
'source_url' => $article['source_url'],
|
||||
'tags' => $article['tags'],
|
||||
'category' => $category,
|
||||
'owner' => $owner,
|
||||
'private' => $article['private'],
|
||||
'published' => $article['published'],
|
||||
'closed' => $article['closed'],
|
||||
@ -83,6 +81,8 @@ class ArticleList extends LogicService
|
||||
'favorite_count' => $article['favorite_count'],
|
||||
'create_time' => $article['create_time'],
|
||||
'update_time' => $article['update_time'],
|
||||
'category' => $category,
|
||||
'owner' => $owner,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
namespace App\Services\Logic\Chapter;
|
||||
|
||||
use App\Library\Paginator\Query as PagerQuery;
|
||||
use App\Models\Consult as ConsultModel;
|
||||
use App\Repos\Consult as ConsultRepo;
|
||||
use App\Services\Logic\ChapterTrait;
|
||||
use App\Services\Logic\Course\ConsultListTrait;
|
||||
@ -31,8 +32,9 @@ class ConsultList extends LogicService
|
||||
|
||||
$params = [
|
||||
'chapter_id' => $chapter->id,
|
||||
'published' => ConsultModel::PUBLISH_APPROVED,
|
||||
'deleted' => 0,
|
||||
'private' => 0,
|
||||
'published' => 1,
|
||||
];
|
||||
|
||||
$consultRepo = new ConsultRepo();
|
||||
|
@ -50,7 +50,7 @@ class CommentLike extends LogicService
|
||||
|
||||
$isFirstTime = false;
|
||||
|
||||
$commentLike->comment_id = $commentLike->deleted == 1 ? 0 : 1;
|
||||
$commentLike->deleted = $commentLike->deleted == 1 ? 0 : 1;
|
||||
|
||||
$commentLike->update();
|
||||
}
|
||||
|
@ -64,7 +64,12 @@ class ConsultCreate extends LogicService
|
||||
$validator = new ConsultValidator();
|
||||
|
||||
$question = $validator->checkQuestion($post['question']);
|
||||
|
||||
$private = 0;
|
||||
|
||||
if (isset($post['private'])) {
|
||||
$private = $validator->checkPrivateStatus($post['private']);
|
||||
}
|
||||
|
||||
$validator->checkIfDuplicated($course->id, $user->id, $question);
|
||||
|
||||
@ -101,7 +106,12 @@ class ConsultCreate extends LogicService
|
||||
$validator = new ConsultValidator();
|
||||
|
||||
$question = $validator->checkQuestion($post['question']);
|
||||
|
||||
$private = 0;
|
||||
|
||||
if (isset($post['private'])) {
|
||||
$private = $validator->checkPrivateStatus($post['private']);
|
||||
}
|
||||
|
||||
$validator->checkIfDuplicated($course->id, $user->id, $question);
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
namespace App\Services\Logic\Course;
|
||||
|
||||
use App\Library\Paginator\Query as PagerQuery;
|
||||
use App\Models\Consult as ConsultModel;
|
||||
use App\Repos\Consult as ConsultRepo;
|
||||
use App\Services\Logic\CourseTrait;
|
||||
use App\Services\Logic\Service as LogicService;
|
||||
@ -30,9 +31,9 @@ class ConsultList extends LogicService
|
||||
|
||||
$params = [
|
||||
'course_id' => $course->id,
|
||||
'private' => 0,
|
||||
'published' => 1,
|
||||
'published' => ConsultModel::PUBLISH_APPROVED,
|
||||
'deleted' => 0,
|
||||
'private' => 0,
|
||||
];
|
||||
|
||||
$consultRepo = new ConsultRepo();
|
||||
|
@ -52,12 +52,17 @@ class LiveList extends LogicService
|
||||
$items = [];
|
||||
|
||||
foreach ($lives as $live) {
|
||||
|
||||
$course = $courses[$live['course_id']] ?? new \stdClass();
|
||||
$chapter = $chapters[$live['chapter_id']] ?? new \stdClass();
|
||||
|
||||
$items[] = [
|
||||
'course' => $courses[$live['course_id']] ?? new \stdClass(),
|
||||
'chapter' => $chapters[$live['chapter_id']] ?? new \stdClass(),
|
||||
'id' => $live['id'],
|
||||
'status' => $live['status'],
|
||||
'start_time' => $live['start_time'],
|
||||
'end_time' => $live['end_time'],
|
||||
'course' => $course,
|
||||
'chapter' => $chapter,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,8 @@ class QuestionList extends LogicService
|
||||
|
||||
$builder = new QuestionListBuilder();
|
||||
|
||||
$categories = $builder->getCategories();
|
||||
|
||||
$questions = $pager->items->toArray();
|
||||
|
||||
$users = $builder->getUsers($questions);
|
||||
@ -57,6 +59,8 @@ class QuestionList extends LogicService
|
||||
|
||||
$question['tags'] = json_decode($question['tags'], true);
|
||||
|
||||
$category = $categories[$question['category_id']] ?? new \stdClass();
|
||||
|
||||
$owner = $users[$question['owner_id']] ?? new \stdClass();
|
||||
|
||||
$lastReplier = $users[$question['last_replier_id']] ?? new \stdClass();
|
||||
@ -81,6 +85,7 @@ class QuestionList extends LogicService
|
||||
'create_time' => $question['create_time'],
|
||||
'update_time' => $question['update_time'],
|
||||
'last_replier' => $lastReplier,
|
||||
'category' => $category,
|
||||
'owner' => $owner,
|
||||
];
|
||||
}
|
||||
|
@ -64,6 +64,16 @@ class Article extends Handler
|
||||
|
||||
foreach ($pager->items as $item) {
|
||||
|
||||
$category = json_decode($item['category'], true);
|
||||
$owner = json_decode($item['owner'], true);
|
||||
$tags = json_decode($item['tags'], true);
|
||||
|
||||
$owner['avatar'] = $owner['avatar'] ?: kg_default_user_avatar_path();
|
||||
|
||||
if (!empty($owner['avatar']) && !Text::startsWith($owner['avatar'], 'http')) {
|
||||
$owner['avatar'] = $baseUrl . $owner['avatar'];
|
||||
}
|
||||
|
||||
if (!empty($item['cover']) && !Text::startsWith($item['cover'], 'http')) {
|
||||
$item['cover'] = $baseUrl . $item['cover'];
|
||||
}
|
||||
@ -78,9 +88,9 @@ class Article extends Handler
|
||||
'like_count' => (int)$item['like_count'],
|
||||
'favorite_count' => (int)$item['favorite_count'],
|
||||
'comment_count' => (int)$item['comment_count'],
|
||||
'tags' => json_decode($item['tags'], true),
|
||||
'owner' => json_decode($item['owner'], true),
|
||||
'category' => json_decode($item['category'], true),
|
||||
'category' => $category,
|
||||
'owner' => $owner,
|
||||
'tags' => $tags,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ namespace App\Services\Logic\Search;
|
||||
use App\Library\Paginator\Adapter\XunSearch as XunSearchPaginator;
|
||||
use App\Library\Paginator\Query as PagerQuery;
|
||||
use App\Services\Search\CourseSearcher as CourseSearcherService;
|
||||
use Phalcon\Text;
|
||||
|
||||
class Course extends Handler
|
||||
{
|
||||
@ -63,7 +64,24 @@ class Course extends Handler
|
||||
|
||||
foreach ($pager->items as $item) {
|
||||
|
||||
/**
|
||||
* 后补的字段,给默认值防止出错
|
||||
*/
|
||||
$item['tags'] = $item['tags'] ?: '[]';
|
||||
|
||||
$category = json_decode($item['category'], true);
|
||||
$teacher = json_decode($item['teacher'], true);
|
||||
$tags = json_decode($item['tags'], true);
|
||||
|
||||
$teacher['avatar'] = $teacher['avatar'] ?: kg_default_user_avatar_path();
|
||||
|
||||
if (!empty($teacher['avatar']) && !Text::startsWith($teacher['avatar'], 'http')) {
|
||||
$teacher['avatar'] = $baseUrl . $teacher['avatar'];
|
||||
}
|
||||
|
||||
if (!empty($item['cover']) && !Text::startsWith($item['cover'], 'http')) {
|
||||
$item['cover'] = $baseUrl . $item['cover'];
|
||||
}
|
||||
|
||||
$items[] = [
|
||||
'id' => (int)$item['id'],
|
||||
@ -78,8 +96,9 @@ class Course extends Handler
|
||||
'lesson_count' => (int)$item['lesson_count'],
|
||||
'review_count' => (int)$item['review_count'],
|
||||
'favorite_count' => (int)$item['favorite_count'],
|
||||
'teacher' => json_decode($item['teacher'], true),
|
||||
'category' => json_decode($item['category'], true),
|
||||
'category' => $category,
|
||||
'teacher' => $teacher,
|
||||
'tags' => $tags,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ namespace App\Services\Logic\Search;
|
||||
use App\Library\Paginator\Adapter\XunSearch as XunSearchPaginator;
|
||||
use App\Library\Paginator\Query as PagerQuery;
|
||||
use App\Services\Search\GroupSearcher as GroupSearcherService;
|
||||
use Phalcon\Text;
|
||||
|
||||
class Group extends Handler
|
||||
{
|
||||
@ -63,7 +64,11 @@ class Group extends Handler
|
||||
|
||||
foreach ($pager->items as $item) {
|
||||
|
||||
$owner = json_decode($item['owner'], true);
|
||||
|
||||
if (!empty($item['avatar']) && !Text::startsWith($item['avatar'], 'http')) {
|
||||
$item['avatar'] = $baseUrl . $item['avatar'];
|
||||
}
|
||||
|
||||
$items[] = [
|
||||
'id' => (int)$item['id'],
|
||||
@ -73,7 +78,7 @@ class Group extends Handler
|
||||
'about' => (string)$item['about'],
|
||||
'user_count' => (int)$item['user_count'],
|
||||
'msg_count' => (int)$item['msg_count'],
|
||||
'owner' => json_decode($item['owner'], true),
|
||||
'owner' => $owner,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -64,6 +64,17 @@ class Question extends Handler
|
||||
|
||||
foreach ($pager->items as $item) {
|
||||
|
||||
$lastReplier = json_decode($item['last_replier'], true);
|
||||
$category = json_decode($item['category'], true);
|
||||
$owner = json_decode($item['owner'], true);
|
||||
$tags = json_decode($item['tags'], true);
|
||||
|
||||
$owner['avatar'] = $owner['avatar'] ?: kg_default_user_avatar_path();
|
||||
|
||||
if (!empty($owner['avatar']) && !Text::startsWith($owner['avatar'], 'http')) {
|
||||
$owner['avatar'] = $baseUrl . $owner['avatar'];
|
||||
}
|
||||
|
||||
if (!empty($item['cover']) && !Text::startsWith($item['cover'], 'http')) {
|
||||
$item['cover'] = $baseUrl . $item['cover'];
|
||||
}
|
||||
@ -71,13 +82,13 @@ class Question extends Handler
|
||||
$lastAnswer = json_decode($item['last_answer'], true);
|
||||
|
||||
if (!empty($lastAnswer['cover']) && !Text::startsWith($lastAnswer['cover'], 'http')) {
|
||||
$item['last_answer'] = $baseUrl . $lastAnswer['cover'];
|
||||
$lastAnswer['cover'] = $baseUrl . $lastAnswer['cover'];
|
||||
}
|
||||
|
||||
$acceptAnswer = json_decode($item['accept_answer'], true);
|
||||
|
||||
if (!empty($acceptAnswer['cover']) && !Text::startsWith($acceptAnswer['cover'], 'http')) {
|
||||
$item['accept_answer'] = $baseUrl . $acceptAnswer['cover'];
|
||||
$acceptAnswer['cover'] = $baseUrl . $acceptAnswer['cover'];
|
||||
}
|
||||
|
||||
$items[] = [
|
||||
@ -95,12 +106,12 @@ class Question extends Handler
|
||||
'answer_count' => (int)$item['answer_count'],
|
||||
'comment_count' => (int)$item['comment_count'],
|
||||
'favorite_count' => (int)$item['favorite_count'],
|
||||
'category' => json_decode($item['category'], true),
|
||||
'tags' => json_decode($item['tags'], true),
|
||||
'owner' => json_decode($item['owner'], true),
|
||||
'last_replier' => json_decode($item['last_replier'], true),
|
||||
'last_answer' => $item['last_answer'],
|
||||
'accept_answer' => $item['accept_answer'],
|
||||
'accept_answer' => $acceptAnswer,
|
||||
'last_answer' => $lastAnswer,
|
||||
'last_replier' => $lastReplier,
|
||||
'category' => $category,
|
||||
'owner' => $owner,
|
||||
'tags' => $tags,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
namespace App\Services\Search;
|
||||
|
||||
use App\Models\Article as ArticleModel;
|
||||
use App\Models\User as UserModel;
|
||||
use App\Repos\Category as CategoryRepo;
|
||||
use App\Repos\User as UserRepo;
|
||||
use Phalcon\Di\Injectable;
|
||||
@ -80,9 +81,12 @@ class ArticleDocument extends Injectable
|
||||
|
||||
$user = $userRepo->findById($id);
|
||||
|
||||
$user->avatar = UserModel::getAvatarPath($user->avatar);
|
||||
|
||||
return kg_json_encode([
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
'avatar' => $user->avatar,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,10 @@
|
||||
|
||||
namespace App\Services\Search;
|
||||
|
||||
use App\Models\Category as CategoryModel;
|
||||
use App\Models\Course as CourseModel;
|
||||
use App\Models\User as UserModel;
|
||||
use App\Repos\Category as CategoryRepo;
|
||||
use App\Repos\User as UserRepo;
|
||||
use Phalcon\Di\Injectable;
|
||||
|
||||
class CourseDocument extends Injectable
|
||||
@ -44,24 +45,20 @@ class CourseDocument extends Injectable
|
||||
$course->attrs = kg_json_encode($course->attrs);
|
||||
}
|
||||
|
||||
if (is_array($course->tags) || is_object($course->tags)) {
|
||||
$course->tags = kg_json_encode($course->tags);
|
||||
}
|
||||
|
||||
$teacher = '{}';
|
||||
|
||||
if ($course->teacher_id > 0) {
|
||||
$record = UserModel::findFirst($course->teacher_id);
|
||||
$teacher = kg_json_encode([
|
||||
'id' => $record->id,
|
||||
'name' => $record->name,
|
||||
]);
|
||||
$teacher = $this->handleUser($course->teacher_id);
|
||||
}
|
||||
|
||||
$category = '{}';
|
||||
|
||||
if ($course->category_id > 0) {
|
||||
$record = CategoryModel::findFirst($course->category_id);
|
||||
$category = kg_json_encode([
|
||||
'id' => $record->id,
|
||||
'name' => $record->name,
|
||||
]);
|
||||
$category = $this->handleCategory($course->category_id);
|
||||
}
|
||||
|
||||
$course->cover = CourseModel::getCoverPath($course->cover);
|
||||
@ -83,6 +80,7 @@ class CourseDocument extends Injectable
|
||||
'model' => $course->model,
|
||||
'level' => $course->level,
|
||||
'attrs' => $course->attrs,
|
||||
'tags' => $course->tags,
|
||||
'category' => $category,
|
||||
'teacher' => $teacher,
|
||||
'user_count' => $course->user_count,
|
||||
@ -92,4 +90,31 @@ class CourseDocument extends Injectable
|
||||
];
|
||||
}
|
||||
|
||||
protected function handleUser($id)
|
||||
{
|
||||
$userRepo = new UserRepo();
|
||||
|
||||
$user = $userRepo->findById($id);
|
||||
|
||||
$user->avatar = UserModel::getAvatarPath($user->avatar);
|
||||
|
||||
return kg_json_encode([
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
'avatar' => $user->avatar,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function handleCategory($id)
|
||||
{
|
||||
$categoryRepo = new CategoryRepo();
|
||||
|
||||
$category = $categoryRepo->findById($id);
|
||||
|
||||
return kg_json_encode([
|
||||
'id' => $category->id,
|
||||
'name' => $category->name,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ namespace App\Services\Search;
|
||||
|
||||
use App\Models\ImGroup as GroupModel;
|
||||
use App\Models\User as UserModel;
|
||||
use App\Repos\User as UserRepo;
|
||||
use Phalcon\Di\Injectable;
|
||||
|
||||
class GroupDocument extends Injectable
|
||||
@ -42,11 +43,7 @@ class GroupDocument extends Injectable
|
||||
$owner = '{}';
|
||||
|
||||
if ($group->owner_id > 0) {
|
||||
$record = UserModel::findFirst($group->owner_id);
|
||||
$owner = kg_json_encode([
|
||||
'id' => $record->id,
|
||||
'name' => $record->name,
|
||||
]);
|
||||
$owner = $this->handleUser($group->owner_id);
|
||||
}
|
||||
|
||||
$group->avatar = GroupModel::getAvatarPath($group->avatar);
|
||||
@ -62,4 +59,19 @@ class GroupDocument extends Injectable
|
||||
];
|
||||
}
|
||||
|
||||
protected function handleUser($id)
|
||||
{
|
||||
$userRepo = new UserRepo();
|
||||
|
||||
$user = $userRepo->findById($id);
|
||||
|
||||
$user->avatar = UserModel::getAvatarPath($user->avatar);
|
||||
|
||||
return kg_json_encode([
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
'avatar' => $user->avatar,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
namespace App\Services\Search;
|
||||
|
||||
use App\Models\Question as QuestionModel;
|
||||
use App\Models\User as UserModel;
|
||||
use App\Repos\Answer as AnswerRepo;
|
||||
use App\Repos\Category as CategoryRepo;
|
||||
use App\Repos\User as UserRepo;
|
||||
@ -93,11 +94,11 @@ class QuestionDocument extends Injectable
|
||||
'answer_count' => $question->answer_count,
|
||||
'comment_count' => $question->comment_count,
|
||||
'favorite_count' => $question->favorite_count,
|
||||
'accept_answer' => $acceptAnswer,
|
||||
'last_answer' => $lastAnswer,
|
||||
'last_replier' => $lastReplier,
|
||||
'category' => $category,
|
||||
'owner' => $owner,
|
||||
'last_replier' => $lastReplier,
|
||||
'last_answer' => $lastAnswer,
|
||||
'accept_answer' => $acceptAnswer,
|
||||
];
|
||||
}
|
||||
|
||||
@ -107,9 +108,12 @@ class QuestionDocument extends Injectable
|
||||
|
||||
$user = $userRepo->findById($id);
|
||||
|
||||
$user->avatar = UserModel::getAvatarPath($user->avatar);
|
||||
|
||||
return kg_json_encode([
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
'avatar' => $user->avatar,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ class Answer extends Validator
|
||||
|
||||
public function checkContent($content)
|
||||
{
|
||||
$value = $this->filter->sanitize($content, ['trim', 'string']);
|
||||
$value = $this->filter->sanitize($content, ['trim']);
|
||||
|
||||
$length = kg_strlen($value);
|
||||
|
||||
|
@ -205,7 +205,7 @@ class Chapter extends Validator
|
||||
if ($attrs['word_count'] == 0) {
|
||||
throw new BadRequestException('chapter.read_not_ready');
|
||||
}
|
||||
} elseif ($chapter->model == CourseModel::MODEL_READ) {
|
||||
} elseif ($chapter->model == CourseModel::MODEL_OFFLINE) {
|
||||
if ($attrs['start_time'] == 0) {
|
||||
throw new BadRequestException('chapter.offline_time_empty');
|
||||
}
|
||||
|
@ -246,29 +246,6 @@ class Course extends Validator
|
||||
if ($course->teacher_id == 0) {
|
||||
throw new BadRequestException('course.teacher_not_assigned');
|
||||
}
|
||||
|
||||
$courseRepo = new CourseRepo();
|
||||
|
||||
$chapters = $courseRepo->findChapters($course->id);
|
||||
|
||||
$totalCount = $publishedCount = 0;
|
||||
|
||||
foreach ($chapters as $chapter) {
|
||||
if ($chapter->parent_id > 0 && $chapter->published == 1) {
|
||||
$publishedCount++;
|
||||
}
|
||||
if ($chapter->parent_id > 0) {
|
||||
$totalCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($publishedCount == 0) {
|
||||
throw new BadRequestException('course.pub_chapter_not_found');
|
||||
}
|
||||
|
||||
if ($publishedCount / $totalCount < 0.2) {
|
||||
throw new BadRequestException('course.pub_chapter_not_enough');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ namespace App\Validators;
|
||||
|
||||
use App\Exceptions\BadRequest as BadRequestException;
|
||||
use App\Library\Validators\Common as CommonValidator;
|
||||
use App\Models\CourseUser as CourseUserModel;
|
||||
use App\Repos\CourseUser as CourseUserRepo;
|
||||
|
||||
class CourseUser extends Validator
|
||||
@ -65,14 +66,14 @@ class CourseUser extends Validator
|
||||
return strtotime($value);
|
||||
}
|
||||
|
||||
public function checkIfJoined($courseId, $userId)
|
||||
public function checkIfImported($courseId, $userId)
|
||||
{
|
||||
$repo = new CourseUserRepo();
|
||||
|
||||
$courseUser = $repo->findCourseStudent($courseId, $userId);
|
||||
|
||||
if ($courseUser) {
|
||||
throw new BadRequestException('course_user.has_joined');
|
||||
if ($courseUser && $courseUser->source_type == CourseUserModel::SOURCE_IMPORT) {
|
||||
throw new BadRequestException('course_user.has_imported');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ class PointGift extends Validator
|
||||
|
||||
public function checkDetails($details)
|
||||
{
|
||||
$value = $this->filter->sanitize($details, ['trim', 'string']);
|
||||
$value = $this->filter->sanitize($details, ['trim']);
|
||||
|
||||
$length = kg_strlen($value);
|
||||
|
||||
|
@ -184,8 +184,6 @@ $error['course.invalid_refund_expiry'] = '无效的退款期限';
|
||||
$error['course.invalid_feature_status'] = '无效的推荐状态';
|
||||
$error['course.invalid_publish_status'] = '无效的发布状态';
|
||||
$error['course.teacher_not_assigned'] = '尚未指定授课教师';
|
||||
$error['course.pub_chapter_not_found'] = '尚未发现已发布的课时';
|
||||
$error['course.pub_chapter_not_enough'] = '已发布的课时太少(小于20%)';
|
||||
|
||||
/**
|
||||
* 面授课程相关
|
||||
@ -240,7 +238,7 @@ $error['package.invalid_expiry'] = '无效的期限(范围:1~60)';
|
||||
$error['course_user.not_found'] = '课程学员关系不存在';
|
||||
$error['course_user.invalid_expiry_time'] = '无效的过期时间';
|
||||
$error['course_user.review_not_allowed'] = '当前不允许评价课程';
|
||||
$error['course_user.has_joined'] = '已经加入过该课程';
|
||||
$error['course_user.has_imported'] = '已经加入过该课程';
|
||||
$error['course_user.has_reviewed'] = '已经评价过该课程';
|
||||
|
||||
/**
|
||||
|
@ -61,6 +61,9 @@ tokenizer = full
|
||||
[attrs]
|
||||
type = string
|
||||
|
||||
[tags]
|
||||
type = string
|
||||
|
||||
[category]
|
||||
type = string
|
||||
|
||||
|
27
db/migrations/20210825111618.php
Normal file
27
db/migrations/20210825111618.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
|
||||
* @license https://opensource.org/licenses/GPL-2.0
|
||||
* @link https://www.koogua.com
|
||||
*/
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class V20210825111618 extends AbstractMigration
|
||||
{
|
||||
|
||||
public function up()
|
||||
{
|
||||
$this->alterUploadTable();
|
||||
}
|
||||
|
||||
protected function alterUploadTable()
|
||||
{
|
||||
$table = $this->table('kg_upload');
|
||||
|
||||
$table->removeIndexByName('md5')->save();
|
||||
|
||||
$table->addIndex('md5')->save();
|
||||
}
|
||||
|
||||
}
|
36
db/migrations/20210916072842.php
Normal file
36
db/migrations/20210916072842.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
|
||||
* @license https://opensource.org/licenses/GPL-2.0
|
||||
* @link https://www.koogua.com
|
||||
*/
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class V20210916072842 extends AbstractMigration
|
||||
{
|
||||
|
||||
public function up()
|
||||
{
|
||||
$this->handleLocalAuthSetting();
|
||||
}
|
||||
|
||||
protected function handleLocalAuthSetting()
|
||||
{
|
||||
$rows = [
|
||||
[
|
||||
'section' => 'oauth.local',
|
||||
'item_key' => 'login_with_phone',
|
||||
'item_value' => '1',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.local',
|
||||
'item_key' => 'login_with_email',
|
||||
'item_value' => '1',
|
||||
]
|
||||
];
|
||||
|
||||
$this->table('kg_setting')->insert($rows)->save();
|
||||
}
|
||||
|
||||
}
|
@ -1029,7 +1029,9 @@
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.lesson-item .free-badge {
|
||||
.lesson-item .icon-trial {
|
||||
color: red;
|
||||
font-size: 24px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
@ -1347,12 +1349,16 @@
|
||||
}
|
||||
|
||||
.player-wrap {
|
||||
position: relative;
|
||||
width: 760px;
|
||||
height: 428px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.dplayer {
|
||||
width: 760px;
|
||||
height: 428px;
|
||||
}
|
||||
|
||||
.chat-wrap .layui-card-header {
|
||||
text-align: center;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user