mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-08-10 00:11:38 +08:00
Merge branch 'koogua/I1XFCF' of https://gitee.com/koogua/course-tencent-cloud into develop
Conflicts: db/migrations/schema.php
This commit is contained in:
commit
cc1c663917
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,6 +6,7 @@
|
||||
/config/xs.user.ini
|
||||
/config/alipay/*.crt
|
||||
/config/wxpay/*.pem
|
||||
/db/migrations/schema.php
|
||||
/public/robots.txt
|
||||
/public/sitemap.xml
|
||||
*KgTest*
|
||||
|
40
app/Builders/ResourceList.php
Normal file
40
app/Builders/ResourceList.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Builders;
|
||||
|
||||
use App\Repos\Upload as UploadRepo;
|
||||
|
||||
class ResourceList extends Builder
|
||||
{
|
||||
|
||||
public function handleUploads($relations)
|
||||
{
|
||||
$uploads = $this->getUploads($relations);
|
||||
|
||||
foreach ($relations as $key => $value) {
|
||||
$relations[$key]['upload'] = $uploads[$value['upload_id']] ?? new \stdClass();
|
||||
}
|
||||
|
||||
return $relations;
|
||||
}
|
||||
|
||||
public function getUploads($relations)
|
||||
{
|
||||
$ids = kg_array_column($relations, 'upload_id');
|
||||
|
||||
$uploadRepo = new UploadRepo();
|
||||
|
||||
$columns = ['id', 'name', 'path', 'mime', 'md5', 'size'];
|
||||
|
||||
$uploads = $uploadRepo->findByIds($ids, $columns);
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($uploads->toArray() as $upload) {
|
||||
$result[$upload['id']] = $upload;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
29
app/Caches/MaxUploadId.php
Normal file
29
app/Caches/MaxUploadId.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Caches;
|
||||
|
||||
use App\Models\Upload as UploadModel;
|
||||
|
||||
class MaxUploadId extends Cache
|
||||
{
|
||||
|
||||
protected $lifetime = 365 * 86400;
|
||||
|
||||
public function getLifetime()
|
||||
{
|
||||
return $this->lifetime;
|
||||
}
|
||||
|
||||
public function getKey($id = null)
|
||||
{
|
||||
return 'max_upload_id';
|
||||
}
|
||||
|
||||
public function getContent($id = null)
|
||||
{
|
||||
$upload = UploadModel::findFirst(['order' => 'id DESC']);
|
||||
|
||||
return $upload->id ?? 0;
|
||||
}
|
||||
|
||||
}
|
@ -7,6 +7,7 @@ use App\Http\Admin\Services\ChapterContent as ChapterContentService;
|
||||
use App\Http\Admin\Services\Course as CourseService;
|
||||
use App\Models\ChapterLive as ChapterLiveModel;
|
||||
use App\Models\Course as CourseModel;
|
||||
use Phalcon\Mvc\View;
|
||||
|
||||
/**
|
||||
* @RoutePrefix("/admin/chapter")
|
||||
@ -14,6 +15,19 @@ use App\Models\Course as CourseModel;
|
||||
class ChapterController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @Get("/{id:[0-9]+}/resources", name="admin.chapter.resources")
|
||||
*/
|
||||
public function resourcesAction($id)
|
||||
{
|
||||
$chapterService = new ChapterService();
|
||||
|
||||
$resources = $chapterService->getResources($id);
|
||||
|
||||
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
||||
$this->view->setVar('resources', $resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/{id:[0-9]+}/lessons", name="admin.chapter.lessons")
|
||||
*/
|
||||
@ -96,6 +110,12 @@ class ChapterController extends Controller
|
||||
|
||||
$this->view->pick('chapter/edit_lesson');
|
||||
|
||||
$resources = $chapterService->getResources($chapter->id);
|
||||
|
||||
$cos = $chapterService->getSettings('cos');
|
||||
|
||||
$this->view->setVar('cos', $cos);
|
||||
|
||||
switch ($course->model) {
|
||||
case CourseModel::MODEL_VOD:
|
||||
$vod = $contentService->getChapterVod($chapter->id);
|
||||
|
49
app/Http/Admin/Controllers/ResourceController.php
Normal file
49
app/Http/Admin/Controllers/ResourceController.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Admin\Controllers;
|
||||
|
||||
use App\Http\Admin\Services\Resource as ResourceService;
|
||||
|
||||
/**
|
||||
* @RoutePrefix("/admin/resource")
|
||||
*/
|
||||
class ResourceController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @Post("/create", name="admin.resource.create")
|
||||
*/
|
||||
public function createAction()
|
||||
{
|
||||
$resourceService = new ResourceService();
|
||||
|
||||
$resourceService->createResource();
|
||||
|
||||
return $this->jsonSuccess(['msg' => '上传资源成功']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/update", name="admin.resource.update")
|
||||
*/
|
||||
public function updateAction($id)
|
||||
{
|
||||
$resourceService = new ResourceService();
|
||||
|
||||
$resourceService->updateResource($id);
|
||||
|
||||
return $this->jsonSuccess(['msg' => '更新资源成功']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/delete", name="admin.resource.delete")
|
||||
*/
|
||||
public function deleteAction($id)
|
||||
{
|
||||
$resourceService = new ResourceService();
|
||||
|
||||
$resourceService->deleteResource($id);
|
||||
|
||||
return $this->jsonSuccess(['msg' => '删除资源成功']);
|
||||
}
|
||||
|
||||
}
|
@ -73,4 +73,22 @@ class UploadController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/sign", name="admin.upload.sign")
|
||||
*/
|
||||
public function signatureAction()
|
||||
{
|
||||
$service = new StorageService();
|
||||
|
||||
$token = $service->getFederationToken();
|
||||
|
||||
$data = [
|
||||
'credentials' => $token->getCredentials(),
|
||||
'expiredTime' => $token->getExpiredTime(),
|
||||
'startTime' => time(),
|
||||
];
|
||||
|
||||
return $this->jsonSuccess($data);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Admin\Services;
|
||||
|
||||
use App\Builders\ResourceList as ResourceListBuilder;
|
||||
use App\Caches\Chapter as ChapterCache;
|
||||
use App\Caches\CourseChapterList as CatalogCache;
|
||||
use App\Models\Chapter as ChapterModel;
|
||||
@ -11,12 +12,32 @@ use App\Models\ChapterVod as ChapterVodModel;
|
||||
use App\Models\Course as CourseModel;
|
||||
use App\Repos\Chapter as ChapterRepo;
|
||||
use App\Repos\Course as CourseRepo;
|
||||
use App\Repos\Resource as ResourceRepo;
|
||||
use App\Services\CourseStat as CourseStatService;
|
||||
use App\Validators\Chapter as ChapterValidator;
|
||||
|
||||
class Chapter extends Service
|
||||
{
|
||||
|
||||
public function getResources($id)
|
||||
{
|
||||
$resourceRepo = new ResourceRepo();
|
||||
|
||||
$resources = $resourceRepo->findByChapterId($id);
|
||||
|
||||
if ($resources->count() == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$builder = new ResourceListBuilder();
|
||||
|
||||
$items = $resources->toArray();
|
||||
|
||||
$items = $builder->handleUploads($items);
|
||||
|
||||
return $builder->objects($items);
|
||||
}
|
||||
|
||||
public function getLessons($parentId)
|
||||
{
|
||||
$deleted = $this->request->getQuery('deleted', 'int', 0);
|
||||
|
119
app/Http/Admin/Services/Resource.php
Normal file
119
app/Http/Admin/Services/Resource.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Admin\Services;
|
||||
|
||||
use App\Models\Resource as ResourceModel;
|
||||
use App\Models\Upload as UploadModel;
|
||||
use App\Repos\Upload as UploadRepo;
|
||||
use App\Services\Storage as StorageService;
|
||||
use App\Validators\Chapter as ChapterValidator;
|
||||
use App\Validators\Resource as ResourceValidator;
|
||||
use App\Validators\Upload as UploadValidator;
|
||||
|
||||
class Resource extends Service
|
||||
{
|
||||
|
||||
public function createResource()
|
||||
{
|
||||
$post = $this->request->getPost();
|
||||
|
||||
$validator = new ChapterValidator();
|
||||
|
||||
$chapter = $validator->checkChapter($post['chapter_id']);
|
||||
|
||||
$course = $validator->checkCourse($chapter->course_id);
|
||||
|
||||
$uploadRepo = new UploadRepo();
|
||||
|
||||
$upload = $uploadRepo->findByMd5($post['upload']['md5']);
|
||||
|
||||
if (!$upload) {
|
||||
|
||||
$upload = new UploadModel();
|
||||
|
||||
$upload->type = UploadModel::TYPE_RESOURCE;
|
||||
$upload->name = $post['upload']['name'];
|
||||
$upload->size = $post['upload']['size'];
|
||||
$upload->path = $post['upload']['path'];
|
||||
$upload->md5 = $post['upload']['md5'];
|
||||
$upload->mime = $post['upload']['mime'];
|
||||
|
||||
$upload->create();
|
||||
}
|
||||
|
||||
$resource = new ResourceModel();
|
||||
|
||||
$resource->course_id = $course->id;
|
||||
$resource->chapter_id = $chapter->id;
|
||||
$resource->upload_id = $upload->id;
|
||||
|
||||
$resource->create();
|
||||
|
||||
$chapter->resource_count += 1;
|
||||
$chapter->update();
|
||||
|
||||
$course->resource_count += 1;
|
||||
$course->update();
|
||||
|
||||
return $upload;
|
||||
}
|
||||
|
||||
public function updateResource($id)
|
||||
{
|
||||
$post = $this->request->getPost();
|
||||
|
||||
$resource = $this->findOrFail($id);
|
||||
|
||||
$validator = new UploadValidator();
|
||||
|
||||
$upload = $validator->checkUpload($resource->upload_id);
|
||||
|
||||
$data = [];
|
||||
|
||||
if (isset($post['name'])) {
|
||||
$data['name'] = $validator->checkName($post['name']);
|
||||
}
|
||||
|
||||
$upload->update($data);
|
||||
|
||||
$resource->update();
|
||||
}
|
||||
|
||||
public function deleteResource($id)
|
||||
{
|
||||
$resource = $this->findOrFail($id);
|
||||
|
||||
$validator = new ResourceValidator();
|
||||
|
||||
$course = $validator->checkCourse($resource->course_id);
|
||||
$chapter = $validator->checkChapter($resource->chapter_id);
|
||||
|
||||
$validator = new UploadValidator();
|
||||
|
||||
$upload = $validator->checkUpload($resource->upload_id);
|
||||
|
||||
$storageService = new StorageService();
|
||||
|
||||
$storageService->deleteObject($upload->path);
|
||||
|
||||
$resource->delete();
|
||||
|
||||
if ($course->resource_count > 1) {
|
||||
$course->resource_count -= 1;
|
||||
$course->update();
|
||||
}
|
||||
|
||||
if ($chapter->resource_count > 1) {
|
||||
$chapter->resource_count -= 1;
|
||||
$chapter->update();
|
||||
}
|
||||
}
|
||||
|
||||
protected function findOrFail($id)
|
||||
{
|
||||
$validator = new ResourceValidator();
|
||||
|
||||
return $validator->checkResource($id);
|
||||
}
|
||||
|
||||
}
|
@ -148,6 +148,12 @@ class Role extends Service
|
||||
$list[] = 'admin.chapter.content';
|
||||
}
|
||||
|
||||
if (array_intersect(['admin.chapter.add', 'admin.chapter.edit'], $routes)) {
|
||||
$list[] = 'admin.resource.create';
|
||||
$list[] = 'admin.resource.update';
|
||||
$list[] = 'admin.resource.delete';
|
||||
}
|
||||
|
||||
if (in_array('admin.course.delete', $routes)) {
|
||||
$list[] = 'admin.chapter.delete';
|
||||
$list[] = 'admin.chapter.restore';
|
||||
|
@ -20,6 +20,7 @@
|
||||
<ul class="layui-tab-title kg-tab-title">
|
||||
<li class="layui-this">基本信息</li>
|
||||
<li>{{ content_title(course.model) }}</li>
|
||||
<li>学习资料</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
@ -34,6 +35,9 @@
|
||||
{{ partial('chapter/edit_lesson_read') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
{{ partial('chapter/edit_resource') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -64,6 +68,9 @@
|
||||
|
||||
{% endif %}
|
||||
|
||||
{{ js_include('lib/cos-js-sdk-v5.min.js') }}
|
||||
{{ js_include('admin/js/chapter.resource.js') }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block inline_js %}
|
||||
|
34
app/Http/Admin/Views/chapter/edit_resource.volt
Normal file
34
app/Http/Admin/Views/chapter/edit_resource.volt
Normal file
@ -0,0 +1,34 @@
|
||||
{% set res_list_url = url({'for':'admin.chapter.resources','id':chapter.id}) %}
|
||||
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>资料列表</legend>
|
||||
</fieldset>
|
||||
|
||||
<div id="res-list" data-url="{{ res_list_url }}"></div>
|
||||
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>上传资料</legend>
|
||||
</fieldset>
|
||||
|
||||
<form class="layui-form kg-form" id="res-form">
|
||||
<div class="layui-form-item" id="res-upload-block">
|
||||
<label class="layui-form-label">资源文件</label>
|
||||
<div class="layui-input-block">
|
||||
<span class="layui-btn" id="res-upload-btn">选择文件</span>
|
||||
<input class="layui-hide" type="file" name="res_file" accept="*/*">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-hide" id="res-progress-block">
|
||||
<label class="layui-form-label">上传进度</label>
|
||||
<div class="layui-input-block">
|
||||
<div class="layui-progress layui-progress-big" lay-showpercent="yes" lay-filter="res-upload-progress" style="top:10px;">
|
||||
<div class="layui-progress-bar" lay-percent="0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-hide">
|
||||
<input type="hidden" name="chapter_id" value="{{ chapter.id }}">
|
||||
<input type="hidden" name="bucket" value="{{ cos.bucket }}">
|
||||
<input type="hidden" name="region" value="{{ cos.region }}">
|
||||
</div>
|
||||
</form>
|
30
app/Http/Admin/Views/chapter/resources.volt
Normal file
30
app/Http/Admin/Views/chapter/resources.volt
Normal file
@ -0,0 +1,30 @@
|
||||
{% if resources %}
|
||||
<table class="kg-table layui-table">
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
<th>类型</th>
|
||||
<th>大小</th>
|
||||
<th>日期</th>
|
||||
<th width="15%">操作</th>
|
||||
</tr>
|
||||
{% for item in resources %}
|
||||
{% set update_url = url({'for':'admin.resource.update','id':item.id}) %}
|
||||
{% set delete_url = url({'for':'admin.resource.delete','id':item.id}) %}
|
||||
{% set download_url = url({'for':'home.download','md5':item.upload.md5}) %}
|
||||
<tr>
|
||||
<td><input class="layui-input res-name" type="text" value="{{ item.upload.name }}" data-url="{{ update_url }}"></td>
|
||||
<td>{{ item.upload.mime }}</td>
|
||||
<td>{{ item.upload.size|human_size }}</td>
|
||||
<td>{{ date('Y-m-d H:i:s',item.create_time) }}</td>
|
||||
<td>
|
||||
<a class="layui-btn layui-btn-sm layui-bg-red res-btn-delete" href="javascript:" data-url="{{ delete_url }}">删除</a>
|
||||
<a class="layui-btn layui-btn-sm" href="{{ download_url }}" target="_blank">下载</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<br>
|
||||
{% else %}
|
||||
<div class="kg-center">没有相关资料</div>
|
||||
<br>
|
||||
{% endif %}
|
@ -8,6 +8,7 @@ use App\Services\Logic\Chapter\ChapterInfo as ChapterInfoService;
|
||||
use App\Services\Logic\Chapter\ChapterLike as ChapterLikeService;
|
||||
use App\Services\Logic\Chapter\DanmuList as ChapterDanmuListService;
|
||||
use App\Services\Logic\Chapter\Learning as ChapterLearningService;
|
||||
use App\Services\Logic\Chapter\ResourceList as ChapterResourceListService;
|
||||
use App\Services\Logic\Course\ChapterList as CourseChapterListService;
|
||||
|
||||
/**
|
||||
@ -16,6 +17,18 @@ use App\Services\Logic\Course\ChapterList as CourseChapterListService;
|
||||
class ChapterController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @Get("/{id:[0-9]+}/resources", name="home.chapter.resources")
|
||||
*/
|
||||
public function resourcesAction($id)
|
||||
{
|
||||
$service = new ChapterResourceListService();
|
||||
|
||||
$items = $service->handle($id);
|
||||
|
||||
$this->view->setVar('items', $items);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/{id:[0-9]+}", name="home.chapter.show")
|
||||
*/
|
||||
|
@ -19,6 +19,31 @@ class PublicController extends \Phalcon\Mvc\Controller
|
||||
use ResponseTrait;
|
||||
use SecurityTrait;
|
||||
|
||||
/**
|
||||
* @Get("/download/{md5}", name="home.download")
|
||||
*/
|
||||
public function downloadAction($md5)
|
||||
{
|
||||
$repo = new UploadRepo();
|
||||
|
||||
$file = $repo->findByMd5($md5);
|
||||
|
||||
if ($file) {
|
||||
|
||||
$service = new StorageService();
|
||||
|
||||
$location = $service->getFileUrl($file->path);
|
||||
|
||||
$this->response->redirect($location, true);
|
||||
|
||||
} else {
|
||||
|
||||
$this->response->setStatusCode(404);
|
||||
|
||||
return $this->response;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/img/{id:[0-9]+}", name="home.img")
|
||||
*/
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
{% set full_chapter_url = full_url({'for':'home.chapter.show','id':chapter.id}) %}
|
||||
{% set course_url = url({'for':'home.course.show','id':chapter.course.id}) %}
|
||||
{% set resources_url = url({'for':'home.chapter.resources','id':chapter.id}) %}
|
||||
{% set learning_url = url({'for':'home.chapter.learning','id':chapter.id}) %}
|
||||
{% set live_chats_url = url({'for':'home.live.chats','id':chapter.id}) %}
|
||||
{% set live_stats_url = url({'for':'home.live.stats','id':chapter.id}) %}
|
||||
@ -19,8 +20,11 @@
|
||||
<a><cite>{{ chapter.title }}</cite></a>
|
||||
</span>
|
||||
<span class="share">
|
||||
<a href="javascript:" title="点赞" data-url="{{ like_url }}"><i class="layui-icon layui-icon-praise icon-praise"></i><em class="like-count">{{ chapter.like_count }}</em></a>
|
||||
<a href="javascript:" title="我要点赞" data-url="{{ like_url }}"><i class="layui-icon layui-icon-praise icon-praise"></i><em class="like-count">{{ chapter.like_count }}</em></a>
|
||||
<a href="javascript:" title="在线人数"><i class="layui-icon layui-icon-user"></i><em>0</em></a>
|
||||
{% if chapter.resource_count > 0 and chapter.me.owned == 1 %}
|
||||
<a href="javascript:" title="资料下载" data-url="{{ resources_url }}"><i class="layui-icon layui-icon-download-circle icon-resource"></i></a>
|
||||
{% endif %}
|
||||
<a href="javascript:" title="分享到微信" data-url=""><i class="layui-icon layui-icon-login-wechat icon-wechat"></i></a>
|
||||
<a href="javascript:" title="分享到QQ空间"><i class="layui-icon layui-icon-login-qq icon-qq"></i></a>
|
||||
<a href="javascript:" title="分享到微博"><i class="layui-icon layui-icon-login-weibo icon-weibo"></i></a>
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
{% set full_chapter_url = full_url({'for':'home.chapter.show','id':chapter.id}) %}
|
||||
{% set course_url = url({'for':'home.course.show','id':chapter.course.id}) %}
|
||||
{% set resources_url = url({'for':'home.chapter.resources','id':chapter.id}) %}
|
||||
{% set learning_url = url({'for':'home.chapter.learning','id':chapter.id}) %}
|
||||
{% set like_url = url({'for':'home.chapter.like','id':chapter.id}) %}
|
||||
{% set consult_url = url({'for':'home.consult.add'},{'chapter_id':chapter.id}) %}
|
||||
@ -16,9 +17,12 @@
|
||||
<a><cite>{{ chapter.title }}</cite></a>
|
||||
</span>
|
||||
<span class="share">
|
||||
<a href="javascript:" title="点赞" data-url="{{ like_url }}"><i class="layui-icon layui-icon-praise icon-praise"></i><em class="like-count">{{ chapter.like_count }}</em></a>
|
||||
<a href="javascript:" title="我要点赞" data-url="{{ like_url }}"><i class="layui-icon layui-icon-praise icon-praise"></i><em class="like-count">{{ chapter.like_count }}</em></a>
|
||||
<a href="javascript:" title="学习人次"><i class="layui-icon layui-icon-user"></i><em>{{ chapter.user_count }}</em></a>
|
||||
<a href="javascript:" title="我要提问" data-url="{{ consult_url }}"><i class="layui-icon layui-icon-help icon-help"></i></a>
|
||||
{% if chapter.resource_count > 0 and chapter.me.owned == 1 %}
|
||||
<a href="javascript:" title="资料下载" data-url="{{ resources_url }}"><i class="layui-icon layui-icon-download-circle icon-resource"></i></a>
|
||||
{% endif %}
|
||||
<a href="javascript:" title="分享到微信" data-url=""><i class="layui-icon layui-icon-login-wechat icon-wechat"></i></a>
|
||||
<a href="javascript:" title="分享到QQ空间"><i class="layui-icon layui-icon-login-qq icon-qq"></i></a>
|
||||
<a href="javascript:" title="分享到微博"><i class="layui-icon layui-icon-login-weibo icon-weibo"></i></a>
|
||||
|
32
app/Http/Home/Views/chapter/resources.volt
Normal file
32
app/Http/Home/Views/chapter/resources.volt
Normal file
@ -0,0 +1,32 @@
|
||||
{% extends 'templates/layer.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<table class="kg-table layui-table">
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
<th>类型</th>
|
||||
<th>大小</th>
|
||||
<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.mime }}</td>
|
||||
<td>{{ item.size|human_size }}</td>
|
||||
<td><a class="layui-btn layui-btn-sm" href="{{ download_url }}" target="_blank">下载</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block inline_js %}
|
||||
|
||||
<script>
|
||||
var index = parent.layer.getFrameIndex(window.name);
|
||||
parent.layer.iframeAuto(index);
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
@ -4,6 +4,7 @@
|
||||
|
||||
{% set full_chapter_url = full_url({'for':'home.chapter.show','id':chapter.id}) %}
|
||||
{% set course_url = url({'for':'home.course.show','id':chapter.course.id}) %}
|
||||
{% set resources_url = url({'for':'home.chapter.resources','id':chapter.id}) %}
|
||||
{% set learning_url = url({'for':'home.chapter.learning','id':chapter.id}) %}
|
||||
{% set like_url = url({'for':'home.chapter.like','id':chapter.id}) %}
|
||||
{% set qrcode_url = url({'for':'home.qrcode'},{'text':full_chapter_url}) %}
|
||||
@ -17,9 +18,12 @@
|
||||
<a><cite>{{ chapter.title }}</cite></a>
|
||||
</span>
|
||||
<span class="share">
|
||||
<a href="javascript:" title="学习人次"><i class="layui-icon layui-icon-user"></i><em>{{ chapter.user_count }}</em></a>
|
||||
<a href="javascript:" title="我要点赞" data-url="{{ like_url }}"><i class="layui-icon layui-icon-praise icon-praise {{ liked_class }}"></i><em class="like-count">{{ chapter.like_count }}</em></a>
|
||||
<a href="javascript:" title="学习人次"><i class="layui-icon layui-icon-user"></i><em>{{ chapter.user_count }}</em></a>
|
||||
<a href="javascript:" title="我要提问" data-url="{{ consult_url }}"><i class="layui-icon layui-icon-help icon-help"></i></a>
|
||||
{% if chapter.resource_count > 0 and chapter.me.owned == 1 %}
|
||||
<a href="javascript:" title="资料下载" data-url="{{ resources_url }}"><i class="layui-icon layui-icon-download-circle icon-resource"></i></a>
|
||||
{% endif %}
|
||||
<a href="javascript:" title="分享到微信"><i class="layui-icon layui-icon-login-wechat icon-wechat"></i></a>
|
||||
<a href="javascript:" title="分享到QQ空间"><i class="layui-icon layui-icon-login-qq icon-qq"></i></a>
|
||||
<a href="javascript:" title="分享到微博"><i class="layui-icon layui-icon-login-weibo icon-weibo"></i></a>
|
||||
|
@ -30,6 +30,7 @@
|
||||
<label class="layui-form-label"></label>
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -27,6 +27,7 @@
|
||||
<label class="layui-form-label"></label>
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
{% set consult.answer = consult.answer ? consult.answer : '请耐心等待回复吧' %}
|
||||
|
||||
<div class="consult-info">
|
||||
<div class="consult-info clearfix">
|
||||
{% if consult.course.id is defined %}
|
||||
<div class="item">
|
||||
<div class="label">课程:</div>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrfToken.getToken() }}">
|
||||
<title>即时通讯</title>
|
||||
<title>酷瓜云课堂</title>
|
||||
{{ icon_link('favicon.ico') }}
|
||||
{{ css_link('lib/layui/css/layui.css') }}
|
||||
{{ css_link('home/css/common.css') }}
|
||||
|
@ -137,7 +137,7 @@ function kg_site_url()
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
function kg_site_setting($section, $key = null)
|
||||
function kg_setting($section, $key = null)
|
||||
{
|
||||
$cache = new SettingCache();
|
||||
|
||||
@ -148,6 +148,24 @@ function kg_site_setting($section, $key = null)
|
||||
return $settings[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取站点配置
|
||||
*
|
||||
* @param string $path
|
||||
* @param mixed $defaultValue
|
||||
* @return mixed
|
||||
*/
|
||||
function kg_config($path, $defaultValue = null)
|
||||
{
|
||||
/**
|
||||
* @var Config $config
|
||||
*/
|
||||
$config = Di::getDefault()->getShared('config');
|
||||
|
||||
return $config->path($path, $defaultValue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取默认头像路径
|
||||
*
|
||||
@ -282,6 +300,23 @@ function kg_human_number($number)
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化大小
|
||||
*
|
||||
* @param int $bytes
|
||||
* @return string
|
||||
*/
|
||||
function kg_human_size($bytes)
|
||||
{
|
||||
if (!$bytes) return 0;
|
||||
|
||||
$symbols = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
|
||||
|
||||
$exp = floor(log($bytes) / log(1024));
|
||||
|
||||
return sprintf('%.2f ' . $symbols[$exp], ($bytes / pow(1024, floor($exp))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化之前时间
|
||||
*
|
||||
|
@ -72,6 +72,9 @@ class Audit extends Model
|
||||
|
||||
if (is_array($this->req_data) && !empty($this->req_data)) {
|
||||
foreach ($this->req_data as $key => $value) {
|
||||
if (!is_scalar($value)) {
|
||||
$value = kg_json_encode($value);
|
||||
}
|
||||
if (kg_strlen($value) > 255) {
|
||||
$this->req_data[$key] = kg_substr($value, 0, 255);
|
||||
}
|
||||
|
@ -132,6 +132,13 @@ class Chapter extends Model
|
||||
*/
|
||||
public $deleted;
|
||||
|
||||
/**
|
||||
* 资源数
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $resource_count;
|
||||
|
||||
/**
|
||||
* 课时数
|
||||
*
|
||||
@ -160,13 +167,6 @@ class Chapter extends Model
|
||||
*/
|
||||
public $like_count;
|
||||
|
||||
/**
|
||||
* 资源数
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $res_count;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*
|
||||
|
@ -179,6 +179,13 @@ class Course extends Model
|
||||
*/
|
||||
public $deleted;
|
||||
|
||||
/**
|
||||
* 资源数
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $resource_count;
|
||||
|
||||
/**
|
||||
* 学员数
|
||||
*
|
||||
@ -221,13 +228,6 @@ class Course extends Model
|
||||
*/
|
||||
public $favorite_count;
|
||||
|
||||
/**
|
||||
* 资源数
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $res_count;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*
|
||||
|
@ -2,9 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Phalcon\Mvc\Model\Behavior\SoftDelete;
|
||||
|
||||
class ChapterResource extends Model
|
||||
class Resource extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
@ -35,13 +33,6 @@ class ChapterResource extends Model
|
||||
*/
|
||||
public $upload_id;
|
||||
|
||||
/**
|
||||
* 删除标识
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $deleted;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*
|
||||
@ -58,19 +49,7 @@ class ChapterResource extends Model
|
||||
|
||||
public function getSource(): string
|
||||
{
|
||||
return 'kg_chapter_resource';
|
||||
}
|
||||
|
||||
public function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
$this->addBehavior(
|
||||
new SoftDelete([
|
||||
'field' => 'deleted',
|
||||
'value' => 1,
|
||||
])
|
||||
);
|
||||
return 'kg_resource';
|
||||
}
|
||||
|
||||
public function beforeCreate()
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Caches\MaxUploadId as MaxUploadIdCache;
|
||||
use Phalcon\Mvc\Model\Behavior\SoftDelete;
|
||||
|
||||
class Upload extends Model
|
||||
@ -13,7 +14,7 @@ class Upload extends Model
|
||||
const TYPE_COVER_IMG = 1; // 封面图
|
||||
const TYPE_CONTENT_IMG = 2; // 内容图
|
||||
const TYPE_AVATAR_IMG = 3; // 头像
|
||||
const TYPE_COURSE_RES = 4; // 课件资源
|
||||
const TYPE_RESOURCE = 4; // 课件资源
|
||||
const TYPE_IM_IMG = 5; // IM图片
|
||||
const TYPE_IM_FILE = 6; // IM文件
|
||||
|
||||
@ -114,4 +115,11 @@ class Upload extends Model
|
||||
$this->update_time = time();
|
||||
}
|
||||
|
||||
public function afterCreate()
|
||||
{
|
||||
$cache = new MaxUploadIdCache();
|
||||
|
||||
$cache->rebuild();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,8 +22,12 @@ class Volt extends Provider
|
||||
|
||||
$compiler = $volt->getCompiler();
|
||||
|
||||
$compiler->addFunction('site_setting', function ($resolvedArgs) {
|
||||
return 'kg_site_setting(' . $resolvedArgs . ')';
|
||||
$compiler->addFunction('config', function ($resolvedArgs) {
|
||||
return 'kg_config(' . $resolvedArgs . ')';
|
||||
});
|
||||
|
||||
$compiler->addFunction('setting', function ($resolvedArgs) {
|
||||
return 'kg_setting(' . $resolvedArgs . ')';
|
||||
});
|
||||
|
||||
$compiler->addFunction('full_url', function ($resolvedArgs) {
|
||||
@ -62,6 +66,10 @@ class Volt extends Provider
|
||||
return 'kg_human_number(' . $resolvedArgs . ')';
|
||||
});
|
||||
|
||||
$compiler->addFilter('human_size', function ($resolvedArgs) {
|
||||
return 'kg_human_size(' . $resolvedArgs . ')';
|
||||
});
|
||||
|
||||
$compiler->addFilter('time_ago', function ($resolvedArgs) {
|
||||
return 'kg_time_ago(' . $resolvedArgs . ')';
|
||||
});
|
||||
|
@ -8,7 +8,6 @@ use App\Models\ChapterLive as ChapterLiveModel;
|
||||
use App\Models\ChapterRead as ChapterReadModel;
|
||||
use App\Models\ChapterUser as ChapterUserModel;
|
||||
use App\Models\ChapterVod as ChapterVodModel;
|
||||
use App\Models\Comment as CommentModel;
|
||||
use Phalcon\Mvc\Model;
|
||||
use Phalcon\Mvc\Model\Resultset;
|
||||
use Phalcon\Mvc\Model\ResultsetInterface;
|
||||
@ -153,14 +152,6 @@ class Chapter extends Repository
|
||||
]);
|
||||
}
|
||||
|
||||
public function countComments($chapterId)
|
||||
{
|
||||
return (int)CommentModel::count([
|
||||
'conditions' => 'chapter_id = :chapter_id: AND deleted = 0',
|
||||
'bind' => ['chapter_id' => $chapterId],
|
||||
]);
|
||||
}
|
||||
|
||||
public function countLikes($chapterId)
|
||||
{
|
||||
return (int)ChapterLikeModel::count([
|
||||
|
57
app/Repos/Resource.php
Normal file
57
app/Repos/Resource.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repos;
|
||||
|
||||
use App\Models\Resource as ResourceModel;
|
||||
use Phalcon\Mvc\Model;
|
||||
use Phalcon\Mvc\Model\Resultset;
|
||||
use Phalcon\Mvc\Model\ResultsetInterface;
|
||||
|
||||
class Resource extends Repository
|
||||
{
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return ResourceModel|Model|bool
|
||||
*/
|
||||
public function findById($id)
|
||||
{
|
||||
return ResourceModel::findFirst($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $ids
|
||||
* @param array|string $columns
|
||||
* @return ResultsetInterface|Resultset|ResourceModel[]
|
||||
*/
|
||||
public function findByIds($ids, $columns = '*')
|
||||
{
|
||||
return ResourceModel::query()
|
||||
->columns($columns)
|
||||
->inWhere('id', $ids)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $courseId
|
||||
* @return ResultsetInterface|Resultset|ResourceModel[]
|
||||
*/
|
||||
public function findByCourseId($courseId)
|
||||
{
|
||||
return ResourceModel::query()
|
||||
->where('course_id = :course_id:', ['course_id' => $courseId])
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chapterId
|
||||
* @return ResultsetInterface|Resultset|ResourceModel[]
|
||||
*/
|
||||
public function findByChapterId($chapterId)
|
||||
{
|
||||
return ResourceModel::query()
|
||||
->where('chapter_id = :chapter_id:', ['chapter_id' => $chapterId])
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
@ -4,6 +4,8 @@ namespace App\Repos;
|
||||
|
||||
use App\Models\Upload as UploadModel;
|
||||
use Phalcon\Mvc\Model;
|
||||
use Phalcon\Mvc\Model\Resultset;
|
||||
use Phalcon\Mvc\Model\ResultsetInterface;
|
||||
|
||||
class Upload extends Repository
|
||||
{
|
||||
@ -29,4 +31,17 @@ class Upload extends Repository
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $ids
|
||||
* @param string|array $columns
|
||||
* @return ResultsetInterface|Resultset|UploadModel[]
|
||||
*/
|
||||
public function findByIds($ids, $columns = '*')
|
||||
{
|
||||
return UploadModel::query()
|
||||
->columns($columns)
|
||||
->inWhere('id', $ids)
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ class BasicInfo extends Service
|
||||
'summary' => $chapter->summary,
|
||||
'model' => $chapter->model,
|
||||
'play_urls' => $playUrls,
|
||||
'resource_count' => $chapter->resource_count,
|
||||
'user_count' => $chapter->user_count,
|
||||
'like_count' => $chapter->like_count,
|
||||
];
|
||||
@ -97,6 +98,7 @@ class BasicInfo extends Service
|
||||
'start_time' => $live->start_time,
|
||||
'end_time' => $live->end_time,
|
||||
'status' => $live->status,
|
||||
'resource_count' => $chapter->resource_count,
|
||||
'user_count' => $chapter->user_count,
|
||||
'like_count' => $chapter->like_count,
|
||||
];
|
||||
@ -114,6 +116,7 @@ class BasicInfo extends Service
|
||||
'summary' => $chapter->summary,
|
||||
'model' => $chapter->model,
|
||||
'content' => $read->content,
|
||||
'resource_count' => $chapter->resource_count,
|
||||
'user_count' => $chapter->user_count,
|
||||
'like_count' => $chapter->like_count,
|
||||
];
|
||||
|
34
app/Services/Logic/Chapter/ResourceList.php
Normal file
34
app/Services/Logic/Chapter/ResourceList.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Logic\Chapter;
|
||||
|
||||
use App\Builders\ResourceList as ResourceListBuilder;
|
||||
use App\Repos\Resource as ResourceRepo;
|
||||
use App\Services\Logic\ChapterTrait;
|
||||
use App\Services\Logic\Service;
|
||||
|
||||
class ResourceList extends Service
|
||||
{
|
||||
|
||||
use ChapterTrait;
|
||||
|
||||
public function handle($id)
|
||||
{
|
||||
$chapter = $this->checkChapter($id);
|
||||
|
||||
$resourceRepo = new ResourceRepo();
|
||||
|
||||
$resources = $resourceRepo->findByChapterId($chapter->id);
|
||||
|
||||
if ($resources->count() == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$builder = new ResourceListBuilder();
|
||||
|
||||
$relations = $resources->toArray();
|
||||
|
||||
return $builder->getUploads($relations);
|
||||
}
|
||||
|
||||
}
|
@ -78,9 +78,9 @@ class MyStorage extends Storage
|
||||
*
|
||||
* @return UploadModel|bool
|
||||
*/
|
||||
public function uploadCourseResource()
|
||||
public function uploadResource()
|
||||
{
|
||||
return $this->upload('/res/course/', self::MIME_FILE, UploadModel::TYPE_COURSE_RES);
|
||||
return $this->upload('/resource/', self::MIME_FILE, UploadModel::TYPE_RESOURCE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,6 +4,13 @@ namespace App\Services;
|
||||
|
||||
use Phalcon\Logger\Adapter\File as FileLogger;
|
||||
use Qcloud\Cos\Client as CosClient;
|
||||
use TencentCloud\Common\Credential;
|
||||
use TencentCloud\Common\Exception\TencentCloudSDKException;
|
||||
use TencentCloud\Common\Profile\ClientProfile;
|
||||
use TencentCloud\Common\Profile\HttpProfile;
|
||||
use TencentCloud\Sts\V20180813\Models\GetFederationTokenRequest;
|
||||
use TencentCloud\Sts\V20180813\Models\GetFederationTokenResponse;
|
||||
use TencentCloud\Sts\V20180813\StsClient;
|
||||
|
||||
class Storage extends Service
|
||||
{
|
||||
@ -32,6 +39,74 @@ class Storage extends Service
|
||||
$this->client = $this->getCosClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取临时凭证
|
||||
*
|
||||
* @return GetFederationTokenResponse
|
||||
*/
|
||||
public function getFederationToken()
|
||||
{
|
||||
$secret = $this->getSettings('secret');
|
||||
|
||||
$resource = sprintf('qcs::cos:%s:uid/%s:%s/*',
|
||||
$this->settings['region'],
|
||||
$secret['app_id'],
|
||||
$this->settings['bucket']
|
||||
);
|
||||
|
||||
$policy = json_encode([
|
||||
'version' => '2.0',
|
||||
'statement' => [
|
||||
'effect' => 'allow',
|
||||
'action' => [
|
||||
'name/cos:PutObject',
|
||||
'name/cos:PostObject',
|
||||
'name/cos:InitiateMultipartUpload',
|
||||
'name/cos:ListMultipartUploads',
|
||||
'name/cos:ListParts',
|
||||
'name/cos:UploadPart',
|
||||
'name/cos:CompleteMultipartUpload',
|
||||
],
|
||||
'resource' => [$resource],
|
||||
],
|
||||
]);
|
||||
|
||||
try {
|
||||
|
||||
$credential = new Credential($secret['secret_id'], $secret['secret_key']);
|
||||
|
||||
$httpProfile = new HttpProfile();
|
||||
|
||||
$httpProfile->setEndpoint('sts.tencentcloudapi.com');
|
||||
|
||||
$clientProfile = new ClientProfile();
|
||||
|
||||
$clientProfile->setHttpProfile($httpProfile);
|
||||
|
||||
$client = new StsClient($credential, $this->settings['region'], $clientProfile);
|
||||
|
||||
$request = new GetFederationTokenRequest();
|
||||
|
||||
$params = json_encode([
|
||||
'Name' => 'foo',
|
||||
'Policy' => urlencode($policy),
|
||||
]);
|
||||
|
||||
$request->fromJsonString($params);
|
||||
|
||||
return $client->GetFederationToken($request);
|
||||
|
||||
} catch (TencentCloudSDKException $e) {
|
||||
|
||||
$this->logger->error('Get Tmp Token Exception' . kg_json_encode([
|
||||
'code' => $e->getCode(),
|
||||
'message' => $e->getMessage(),
|
||||
]));
|
||||
|
||||
throw new \Exception('Get Tmp Token Exception');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传字符内容
|
||||
*
|
||||
|
45
app/Validators/Resource.php
Normal file
45
app/Validators/Resource.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Validators;
|
||||
|
||||
use App\Exceptions\BadRequest as BadRequestException;
|
||||
use App\Repos\Resource as ResourceRepo;
|
||||
|
||||
class Resource extends Validator
|
||||
{
|
||||
|
||||
public function checkResource($id)
|
||||
{
|
||||
$resourceRepo = new ResourceRepo();
|
||||
|
||||
$resource = $resourceRepo->findById($id);
|
||||
|
||||
if (!$resource) {
|
||||
throw new BadRequestException('resource.not_found');
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
public function checkCourse($id)
|
||||
{
|
||||
$validator = new Course();
|
||||
|
||||
return $validator->checkCourse($id);
|
||||
}
|
||||
|
||||
public function checkChapter($id)
|
||||
{
|
||||
$validator = new Chapter();
|
||||
|
||||
return $validator->checkChapter($id);
|
||||
}
|
||||
|
||||
public function checkUpload($id)
|
||||
{
|
||||
$validator = new Upload();
|
||||
|
||||
return $validator->checkUpload($id);
|
||||
}
|
||||
|
||||
}
|
57
app/Validators/Upload.php
Normal file
57
app/Validators/Upload.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Validators;
|
||||
|
||||
use App\Caches\MaxUploadId as MaxUploadIdCache;
|
||||
use App\Exceptions\BadRequest as BadRequestException;
|
||||
use App\Repos\Upload as UploadRepo;
|
||||
|
||||
class Upload extends Validator
|
||||
{
|
||||
|
||||
public function checkUpload($id)
|
||||
{
|
||||
$this->checkId($id);
|
||||
|
||||
$uploadRepo = new UploadRepo();
|
||||
|
||||
$upload = $uploadRepo->findById($id);
|
||||
|
||||
if (!$upload) {
|
||||
throw new BadRequestException('upload.not_found');
|
||||
}
|
||||
|
||||
return $upload;
|
||||
}
|
||||
|
||||
public function checkId($id)
|
||||
{
|
||||
$id = intval($id);
|
||||
|
||||
$maxIdCache = new MaxUploadIdCache();
|
||||
|
||||
$maxId = $maxIdCache->get();
|
||||
|
||||
if ($id < 1 || $id > $maxId) {
|
||||
throw new BadRequestException('upload.not_found');
|
||||
}
|
||||
}
|
||||
|
||||
public function checkName($name)
|
||||
{
|
||||
$value = $this->filter->sanitize($name, ['trim', 'string']);
|
||||
|
||||
$length = kg_strlen($value);
|
||||
|
||||
if ($length < 2) {
|
||||
throw new BadRequestException('upload.name_too_short');
|
||||
}
|
||||
|
||||
if ($length > 100) {
|
||||
throw new BadRequestException('upload.name_too_long');
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
@ -8,6 +8,7 @@ use App\Exceptions\NotFound as NotFoundException;
|
||||
use App\Exceptions\ServiceUnavailable as ServiceUnavailableException;
|
||||
use App\Exceptions\Unauthorized as UnauthorizedException;
|
||||
use App\Library\Logger as AppLogger;
|
||||
use Phalcon\Config;
|
||||
use Phalcon\Mvc\User\Component;
|
||||
|
||||
class HttpErrorHandler extends Component
|
||||
@ -127,6 +128,14 @@ class HttpErrorHandler extends Component
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Config
|
||||
*/
|
||||
protected function getConfig()
|
||||
{
|
||||
return $this->getDI()->getShared('config');
|
||||
}
|
||||
|
||||
protected function getLogger()
|
||||
{
|
||||
$logger = new AppLogger();
|
||||
|
@ -640,7 +640,7 @@ class InitTable extends Phinx\Migration\AbstractMigration
|
||||
'unique' => false,
|
||||
])
|
||||
->create();
|
||||
$this->table('kg_chapter_resource', [
|
||||
$this->table('kg_resource', [
|
||||
'id' => false,
|
||||
'primary_key' => ['id'],
|
||||
'engine' => 'InnoDB',
|
||||
@ -676,13 +676,6 @@ class InitTable extends Phinx\Migration\AbstractMigration
|
||||
'comment' => '上传编号',
|
||||
'after' => 'chapter_id',
|
||||
])
|
||||
->addColumn('deleted', 'integer', [
|
||||
'null' => false,
|
||||
'default' => '0',
|
||||
'limit' => MysqlAdapter::INT_REGULAR,
|
||||
'comment' => '删除标识',
|
||||
'after' => 'upload_id',
|
||||
])
|
||||
->addColumn('create_time', 'integer', [
|
||||
'null' => false,
|
||||
'default' => '0',
|
||||
|
15025
db/migrations/schema.php
15025
db/migrations/schema.php
File diff suppressed because it is too large
Load Diff
133
public/static/admin/js/chapter.resource.js
Normal file
133
public/static/admin/js/chapter.resource.js
Normal file
@ -0,0 +1,133 @@
|
||||
layui.use(['jquery', 'element', 'layer'], function () {
|
||||
|
||||
var $ = layui.jquery;
|
||||
var element = layui.element;
|
||||
var layer = layui.layer;
|
||||
|
||||
var $uploadBtn = $('#res-upload-btn');
|
||||
var $resFile = $('input[name=res_file]');
|
||||
var $uploadBlock = $('#res-upload-block');
|
||||
var $progressBlock = $('#res-progress-block');
|
||||
var chapterId = $('input[name=chapter_id]').val();
|
||||
|
||||
var myConfig = {
|
||||
bucket: $('input[name=bucket]').val(),
|
||||
region: $('input[name=region]').val(),
|
||||
storageClass: 'STANDARD'
|
||||
};
|
||||
|
||||
var cos = new COS({
|
||||
getAuthorization: function (options, callback) {
|
||||
$.get('/admin/upload/sign', {
|
||||
bucket: options.Bucket,
|
||||
region: options.Region,
|
||||
}, function (data) {
|
||||
console.log(data);
|
||||
var credentials = data && data.credentials;
|
||||
if (!data || !credentials) {
|
||||
layer.msg('获取临时凭证失败', {icon: 2});
|
||||
return console.error('invalid credentials');
|
||||
}
|
||||
callback({
|
||||
TmpSecretId: credentials.TmpSecretId,
|
||||
TmpSecretKey: credentials.TmpSecretKey,
|
||||
XCosSecurityToken: credentials.Token,
|
||||
ExpiredTime: data.expiredTime,
|
||||
StartTime: data.startTime
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
loadResourceList();
|
||||
|
||||
$uploadBtn.on('click', function () {
|
||||
$resFile.trigger('click');
|
||||
});
|
||||
|
||||
$resFile.on('change', function (e) {
|
||||
var file = this.files[0];
|
||||
var keyName = getKeyName(file.name);
|
||||
cos.putObject({
|
||||
StorageClass: myConfig.storageClass,
|
||||
Bucket: myConfig.bucket,
|
||||
Region: myConfig.region,
|
||||
Key: keyName,
|
||||
Body: file,
|
||||
onProgress: function (info) {
|
||||
if (!isNaN(info.percent)) {
|
||||
var percent = Math.ceil(100 * info.percent);
|
||||
element.progress('res-upload-progress', percent + '%');
|
||||
}
|
||||
console.log(info);
|
||||
}
|
||||
}, function (err, data) {
|
||||
if (data && data.statusCode === 200) {
|
||||
$.post('/admin/resource/create', {
|
||||
upload: {
|
||||
name: file.name,
|
||||
mime: file.type,
|
||||
size: file.size,
|
||||
path: keyName,
|
||||
md5: data.ETag.replace(/"/g, '')
|
||||
},
|
||||
chapter_id: chapterId,
|
||||
}, function () {
|
||||
$uploadBlock.removeClass('layui-hide');
|
||||
$progressBlock.addClass('layui-hide');
|
||||
loadResourceList();
|
||||
});
|
||||
}
|
||||
console.log(err || data);
|
||||
});
|
||||
$uploadBlock.addClass('layui-hide');
|
||||
$progressBlock.removeClass('layui-hide');
|
||||
});
|
||||
|
||||
$('body').on('change', '.res-name', function () {
|
||||
var url = $(this).data('url');
|
||||
$.post(url, {
|
||||
name: $(this).val()
|
||||
}, function (res) {
|
||||
layer.msg(res.msg, {icon: 1});
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('click', '.res-btn-delete', function () {
|
||||
var url = $(this).data('url');
|
||||
layer.confirm('确定要删除吗?', function () {
|
||||
$.post(url, function (res) {
|
||||
layer.msg(res.msg, {icon: 1});
|
||||
loadResourceList();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getKeyName(filename) {
|
||||
var ext = getFileExtension(filename);
|
||||
var date = new Date();
|
||||
var name = [
|
||||
date.getFullYear(),
|
||||
date.getMonth() + 1,
|
||||
date.getDate(),
|
||||
date.getHours(),
|
||||
date.getMinutes(),
|
||||
date.getSeconds(),
|
||||
Math.round(10000 * Math.random())
|
||||
].join('');
|
||||
return '/resource/' + name + '.' + ext;
|
||||
}
|
||||
|
||||
function getFileExtension(filename) {
|
||||
var index = filename.lastIndexOf('.');
|
||||
return filename.substr(index + 1);
|
||||
}
|
||||
|
||||
function loadResourceList() {
|
||||
var url = $('#res-list').data('url');
|
||||
$.get(url, function (html) {
|
||||
$('#res-list').html(html);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
@ -1500,14 +1500,15 @@
|
||||
}
|
||||
|
||||
.consult-info .item {
|
||||
clear: both;
|
||||
margin-bottom: 15px;
|
||||
line-height: 1.5em;
|
||||
height: 1.5em;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.consult-info .item .label {
|
||||
float: left;
|
||||
width: 50px;
|
||||
height: 32px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
@ -47,4 +47,19 @@ layui.use(['jquery', 'helper'], function () {
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 资料
|
||||
*/
|
||||
$('.icon-resource').on('click', function () {
|
||||
var url = $(this).parent().data('url');
|
||||
helper.checkLogin(function () {
|
||||
layer.open({
|
||||
type: 2,
|
||||
title: '资料下载',
|
||||
content: [url, 'no'],
|
||||
area: ['640px', '300px']
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user