1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-06-17 15:55:31 +08:00

后台学习资料部分完成

This commit is contained in:
xiaochong0302 2020-10-07 18:45:50 +08:00
parent b987ddf083
commit ebb2c36f9f
33 changed files with 876 additions and 14847 deletions

1
.gitignore vendored
View File

@ -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*

View 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;
}
}

View 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;
}
}

View File

@ -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);

View 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' => '删除资源成功']);
}
}

View File

@ -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);
}
}

View File

@ -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);

View 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);
}
}

View File

@ -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';

View File

@ -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 %}

View 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>

View 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 %}

View File

@ -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")
*/

View File

@ -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))));
}
/**
* 格式化之前时间
*

View File

@ -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);
}

View File

@ -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;
/**
* 创建时间
*

View File

@ -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;
/**
* 创建时间
*

View File

@ -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()

View File

@ -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();
}
}

View File

@ -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 . ')';
});

View File

@ -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
View 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();
}
}

View File

@ -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();
}
}

View File

@ -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);
}
/**

View File

@ -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');
}
}
/**
* 上传字符内容
*

View 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
View 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;
}
}

View File

@ -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
@ -70,7 +71,13 @@ class HttpErrorHandler extends Component
*/
protected function report($e)
{
$content = sprintf('%s(%d): %s', $e->getFile(), $e->getLine(), $e->getMessage());
$config = $this->getConfig();
if ($config->get('env') == ENV_DEV) {
$content = $e->getTraceAsString();
} else {
$content = sprintf('%s(%d): %s', $e->getFile(), $e->getLine(), $e->getMessage());
}
$logger = $this->getLogger();
@ -127,6 +134,14 @@ class HttpErrorHandler extends Component
];
}
/**
* @return Config
*/
protected function getConfig()
{
return $this->getDI()->getShared('config');
}
protected function getLogger()
{
$logger = new AppLogger();

View File

@ -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',

File diff suppressed because it is too large Load Diff

View 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);
});
}
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long