mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-06-22 19:44:02 +08:00
Merge branch 'master' of https://gitee.com/koogua/course-tencent-cloud
This commit is contained in:
commit
034a6803f0
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,6 +6,7 @@
|
|||||||
/config/xs.user.ini
|
/config/xs.user.ini
|
||||||
/config/alipay/*.crt
|
/config/alipay/*.crt
|
||||||
/config/wxpay/*.pem
|
/config/wxpay/*.pem
|
||||||
|
/db/migrations/schema.php
|
||||||
/public/robots.txt
|
/public/robots.txt
|
||||||
/public/sitemap.xml
|
/public/sitemap.xml
|
||||||
*KgTest*
|
*KgTest*
|
||||||
|
49
CHANGELOG.md
Normal file
49
CHANGELOG.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
### [v1.1.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.1.0)(2020-10-08)
|
||||||
|
|
||||||
|
- 增加运营统计功能
|
||||||
|
- 增加课程资料功能
|
||||||
|
- 增加changelog
|
||||||
|
- 忽略schema
|
||||||
|
- 账户安全页面调整
|
||||||
|
- 简化部分路由
|
||||||
|
- 修复相关课程列表undefined问题
|
||||||
|
|
||||||
|
### [v1.0.0-beta1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.0.0-beta1)(2020-09-26)
|
||||||
|
|
||||||
|
前台功能:
|
||||||
|
|
||||||
|
- 注册、登录、忘记密码
|
||||||
|
- 首页:轮播、新上课程、免费课程、会员课程
|
||||||
|
- 课程列表:多维度筛选,多维度排序
|
||||||
|
- 课程详情:章节,咨询,评价,相关课程,推荐课程,课程套餐
|
||||||
|
- 课时详情:点播,直播,图文
|
||||||
|
- 购买支付:课程,套餐,赞赏,会员
|
||||||
|
- 教师列表
|
||||||
|
- 群组列表
|
||||||
|
- 即时通讯
|
||||||
|
- 在线客服
|
||||||
|
- 全文检索:课程、群组、用户
|
||||||
|
- 个人主页:我的课程,我的收藏,我的好友,我的群组
|
||||||
|
- 会员中心:我的课程,我的收藏,我的咨询,我的评价,我的好友,我的群组,我的订单,我的退款,个人信息,账户安全
|
||||||
|
- 教学中心 :我的课程,我的直播,我的咨询
|
||||||
|
|
||||||
|
后台功能:
|
||||||
|
|
||||||
|
- 课程管理:课程列表,课程搜索,添加课程,编辑课程,删除课程,课程分类
|
||||||
|
- 套餐管理:套餐列表,添加套餐,编辑套餐,删除套餐
|
||||||
|
- 话题管理:话题列表,添加话题,编辑话题,删除话题
|
||||||
|
- 单页管理:单页列表,添加单页,编辑单页,删除单页
|
||||||
|
- 帮助管理:帮助列表,添加帮助,编辑帮助,删除帮助,帮助分类
|
||||||
|
- 学员管理:学员列表,搜索学员,添加学员,编辑学员,学习记录
|
||||||
|
- 咨询管理:咨询列表,搜索咨询,编辑咨询,删除咨询
|
||||||
|
- 评价管理:评价列表,搜索评价,编辑评价,删除评价
|
||||||
|
- 群组管理:群组列表,搜索群组,编辑群组,删除群组
|
||||||
|
- 轮播管理:轮播列表,编辑轮播,删除轮播
|
||||||
|
- 导航管理:导航列表,编辑导航,删除导航
|
||||||
|
- 订单管理:订单列表,搜索订单,订单详情
|
||||||
|
- 交易管理:交易列表,搜索交易,交易详情
|
||||||
|
- 退款管理:退款列表,搜索退款,退款详情,退款审核
|
||||||
|
- 用户管理:用户列表,编辑用户,添加用户
|
||||||
|
- 角色管理:角色列表,编辑角色,删除角色
|
||||||
|
- 操作记录:记录列表,搜索记录,记录详情
|
||||||
|
- 系统配置:网站,密钥,存储,点播,直播,短信,邮件,验证码,支付,会员,微聊
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Caches;
|
namespace App\Caches;
|
||||||
|
|
||||||
use App\Models\Order as OrderModel;
|
use App\Repos\Stat as StatRepo;
|
||||||
use App\Models\User as UserModel;
|
|
||||||
|
|
||||||
class SiteTodayStat extends Cache
|
class SiteTodayStat extends Cache
|
||||||
{
|
{
|
||||||
@ -22,42 +21,21 @@ class SiteTodayStat extends Cache
|
|||||||
|
|
||||||
public function getContent($id = null)
|
public function getContent($id = null)
|
||||||
{
|
{
|
||||||
|
$statRepo = new StatRepo();
|
||||||
|
|
||||||
|
$date = date('Y-m-d');
|
||||||
|
|
||||||
|
$saleCount = $statRepo->countDailySales($date);
|
||||||
|
$saleAmount = $statRepo->sumDailySales($date);
|
||||||
|
$refundAmount = $statRepo->sumDailyRefunds($date);
|
||||||
|
$registerCount = $statRepo->countDailyRegisteredUser($date);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'user_count' => $this->countUsers(),
|
'sale_count' => $saleCount,
|
||||||
'order_count' => $this->countOrders(),
|
'sale_amount' => $saleAmount,
|
||||||
'sale_amount' => $this->sumSales(),
|
'refund_amount' => $refundAmount,
|
||||||
|
'register_count' => $registerCount,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function countUsers()
|
|
||||||
{
|
|
||||||
return (int)UserModel::count([
|
|
||||||
'conditions' => 'create_time > :time:',
|
|
||||||
'bind' => ['time' => strtotime('today')],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function countOrders()
|
|
||||||
{
|
|
||||||
return (int)OrderModel::count([
|
|
||||||
'conditions' => 'create_time > :time: AND status = :status:',
|
|
||||||
'bind' => [
|
|
||||||
'time' => strtotime('today'),
|
|
||||||
'status' => OrderModel::STATUS_FINISHED,
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function sumSales()
|
|
||||||
{
|
|
||||||
return (float)OrderModel::sum([
|
|
||||||
'column' => 'amount',
|
|
||||||
'conditions' => 'create_time > :time: AND status = :status:',
|
|
||||||
'bind' => [
|
|
||||||
'time' => strtotime('today'),
|
|
||||||
'status' => OrderModel::STATUS_FINISHED,
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ use App\Http\Admin\Services\ChapterContent as ChapterContentService;
|
|||||||
use App\Http\Admin\Services\Course as CourseService;
|
use App\Http\Admin\Services\Course as CourseService;
|
||||||
use App\Models\ChapterLive as ChapterLiveModel;
|
use App\Models\ChapterLive as ChapterLiveModel;
|
||||||
use App\Models\Course as CourseModel;
|
use App\Models\Course as CourseModel;
|
||||||
|
use Phalcon\Mvc\View;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @RoutePrefix("/admin/chapter")
|
* @RoutePrefix("/admin/chapter")
|
||||||
@ -14,6 +15,19 @@ use App\Models\Course as CourseModel;
|
|||||||
class ChapterController extends Controller
|
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")
|
* @Get("/{id:[0-9]+}/lessons", name="admin.chapter.lessons")
|
||||||
*/
|
*/
|
||||||
@ -96,6 +110,12 @@ class ChapterController extends Controller
|
|||||||
|
|
||||||
$this->view->pick('chapter/edit_lesson');
|
$this->view->pick('chapter/edit_lesson');
|
||||||
|
|
||||||
|
$resources = $chapterService->getResources($chapter->id);
|
||||||
|
|
||||||
|
$cos = $chapterService->getSettings('cos');
|
||||||
|
|
||||||
|
$this->view->setVar('cos', $cos);
|
||||||
|
|
||||||
switch ($course->model) {
|
switch ($course->model) {
|
||||||
case CourseModel::MODEL_VOD:
|
case CourseModel::MODEL_VOD:
|
||||||
$vod = $contentService->getChapterVod($chapter->id);
|
$vod = $contentService->getChapterVod($chapter->id);
|
||||||
|
@ -49,7 +49,7 @@ class Controller extends \Phalcon\Mvc\Controller
|
|||||||
* 特例白名单
|
* 特例白名单
|
||||||
*/
|
*/
|
||||||
$whitelist = [
|
$whitelist = [
|
||||||
'controllers' => ['public', 'index', 'vod', 'test', 'xm_course'],
|
'controllers' => ['public', 'index', 'vod', 'upload', 'test', 'xm_course'],
|
||||||
'routes' => ['admin.package.guiding'],
|
'routes' => ['admin.package.guiding'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
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' => '删除资源成功']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
98
app/Http/Admin/Controllers/StatController.php
Normal file
98
app/Http/Admin/Controllers/StatController.php
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Admin\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Admin\Services\Stat as StatService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @RoutePrefix("/admin/stat")
|
||||||
|
*/
|
||||||
|
class StatController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/sales/hot", name="admin.stat.hot_sales")
|
||||||
|
*/
|
||||||
|
public function hotSalesAction()
|
||||||
|
{
|
||||||
|
$statService = new StatService();
|
||||||
|
|
||||||
|
$years = $statService->getYearOptions();
|
||||||
|
$months = $statService->getMonthOptions();
|
||||||
|
$items = $statService->hotSales();
|
||||||
|
|
||||||
|
$this->view->pick('stat/hot_sales');
|
||||||
|
$this->view->setVar('years', $years);
|
||||||
|
$this->view->setVar('months', $months);
|
||||||
|
$this->view->setVar('items', $items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/sales", name="admin.stat.sales")
|
||||||
|
*/
|
||||||
|
public function salesAction()
|
||||||
|
{
|
||||||
|
$statService = new StatService();
|
||||||
|
|
||||||
|
$years = $statService->getYearOptions();
|
||||||
|
$months = $statService->getMonthOptions();
|
||||||
|
$data = $statService->sales();
|
||||||
|
|
||||||
|
$this->view->pick('stat/sales');
|
||||||
|
$this->view->setVar('years', $years);
|
||||||
|
$this->view->setVar('months', $months);
|
||||||
|
$this->view->setVar('data', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/refunds", name="admin.stat.refunds")
|
||||||
|
*/
|
||||||
|
public function refundsAction()
|
||||||
|
{
|
||||||
|
$statService = new StatService();
|
||||||
|
|
||||||
|
$years = $statService->getYearOptions();
|
||||||
|
$months = $statService->getMonthOptions();
|
||||||
|
$data = $statService->refunds();
|
||||||
|
|
||||||
|
$this->view->pick('stat/refunds');
|
||||||
|
$this->view->setVar('years', $years);
|
||||||
|
$this->view->setVar('months', $months);
|
||||||
|
$this->view->setVar('data', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/users/registered", name="admin.stat.reg_users")
|
||||||
|
*/
|
||||||
|
public function registeredUsersAction()
|
||||||
|
{
|
||||||
|
$statService = new StatService();
|
||||||
|
|
||||||
|
$years = $statService->getYearOptions();
|
||||||
|
$months = $statService->getMonthOptions();
|
||||||
|
$data = $statService->registeredUsers();
|
||||||
|
|
||||||
|
$this->view->pick('stat/registered_users');
|
||||||
|
$this->view->setVar('years', $years);
|
||||||
|
$this->view->setVar('months', $months);
|
||||||
|
$this->view->setVar('data', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/users/online", name="admin.stat.online_users")
|
||||||
|
*/
|
||||||
|
public function onlineUsersAction()
|
||||||
|
{
|
||||||
|
$statService = new StatService();
|
||||||
|
|
||||||
|
$years = $statService->getYearOptions();
|
||||||
|
$months = $statService->getMonthOptions();
|
||||||
|
$data = $statService->onlineUsers();
|
||||||
|
|
||||||
|
$this->view->pick('stat/online_users');
|
||||||
|
$this->view->setVar('years', $years);
|
||||||
|
$this->view->setVar('months', $months);
|
||||||
|
$this->view->setVar('data', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ class UserController extends Controller
|
|||||||
$roles = $userService->getRoles();
|
$roles = $userService->getRoles();
|
||||||
|
|
||||||
if ($user->admin_role == RoleModel::ROLE_ROOT) {
|
if ($user->admin_role == RoleModel::ROLE_ROOT) {
|
||||||
$this->response->redirect(['action' => 'list']);
|
$this->response->redirect(['for' => 'admin.user.list']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->setVar('user', $user);
|
$this->view->setVar('user', $user);
|
||||||
|
@ -11,7 +11,7 @@ class VodController extends Controller
|
|||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Post("/upload/sign", name="admin.vod.upload_sign")
|
* @Get("/upload/sign", name="admin.vod.upload_sign")
|
||||||
*/
|
*/
|
||||||
public function uploadSignatureAction()
|
public function uploadSignatureAction()
|
||||||
{
|
{
|
||||||
|
@ -438,6 +438,43 @@ class AuthNode extends Service
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'id' => '2-7',
|
||||||
|
'title' => '数据统计',
|
||||||
|
'type' => 'menu',
|
||||||
|
'children' => [
|
||||||
|
[
|
||||||
|
'id' => '2-7-1',
|
||||||
|
'title' => '热卖商品',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.stat.hot_sales',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '2-7-2',
|
||||||
|
'title' => '成交订单',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.stat.sales',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '2-7-3',
|
||||||
|
'title' => '售后退款',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.stat.refunds',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '2-7-4',
|
||||||
|
'title' => '注册用户',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.stat.reg_users',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '2-7-5',
|
||||||
|
'title' => '活跃用户',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.stat.online_users',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Admin\Services;
|
namespace App\Http\Admin\Services;
|
||||||
|
|
||||||
|
use App\Builders\ResourceList as ResourceListBuilder;
|
||||||
use App\Caches\Chapter as ChapterCache;
|
use App\Caches\Chapter as ChapterCache;
|
||||||
use App\Caches\CourseChapterList as CatalogCache;
|
use App\Caches\CourseChapterList as CatalogCache;
|
||||||
use App\Models\Chapter as ChapterModel;
|
use App\Models\Chapter as ChapterModel;
|
||||||
@ -11,12 +12,32 @@ use App\Models\ChapterVod as ChapterVodModel;
|
|||||||
use App\Models\Course as CourseModel;
|
use App\Models\Course as CourseModel;
|
||||||
use App\Repos\Chapter as ChapterRepo;
|
use App\Repos\Chapter as ChapterRepo;
|
||||||
use App\Repos\Course as CourseRepo;
|
use App\Repos\Course as CourseRepo;
|
||||||
|
use App\Repos\Resource as ResourceRepo;
|
||||||
use App\Services\CourseStat as CourseStatService;
|
use App\Services\CourseStat as CourseStatService;
|
||||||
use App\Validators\Chapter as ChapterValidator;
|
use App\Validators\Chapter as ChapterValidator;
|
||||||
|
|
||||||
class Chapter extends Service
|
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)
|
public function getLessons($parentId)
|
||||||
{
|
{
|
||||||
$deleted = $this->request->getQuery('deleted', 'int', 0);
|
$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';
|
$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)) {
|
if (in_array('admin.course.delete', $routes)) {
|
||||||
$list[] = 'admin.chapter.delete';
|
$list[] = 'admin.chapter.delete';
|
||||||
$list[] = 'admin.chapter.restore';
|
$list[] = 'admin.chapter.restore';
|
||||||
|
405
app/Http/Admin/Services/Stat.php
Normal file
405
app/Http/Admin/Services/Stat.php
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Admin\Services;
|
||||||
|
|
||||||
|
use App\Models\Order as OrderModel;
|
||||||
|
use App\Repos\Stat as StatRepo;
|
||||||
|
|
||||||
|
class Stat extends Service
|
||||||
|
{
|
||||||
|
|
||||||
|
public function hotSales()
|
||||||
|
{
|
||||||
|
$type = $this->request->getQuery('type', 'int', OrderModel::ITEM_COURSE);
|
||||||
|
$year = $this->request->getQuery('year', 'int', date('Y'));
|
||||||
|
$month = $this->request->getQuery('month', 'int', date('m'));
|
||||||
|
|
||||||
|
$prev = $this->getPrevMonth($year, $month);
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'title' => "{$year}-{$month}",
|
||||||
|
'sales' => $this->handleHotSales($type, $year, $month),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'title' => "{$prev['year']}-{$prev['month']}",
|
||||||
|
'sales' => $this->handleHotSales($type, $prev['year'], $prev['month']),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sales()
|
||||||
|
{
|
||||||
|
$year = $this->request->getQuery('year', 'int', date('Y'));
|
||||||
|
$month = $this->request->getQuery('month', 'int', date('m'));
|
||||||
|
|
||||||
|
$prev = $this->getPrevMonth($year, $month);
|
||||||
|
$currSales = $this->handleSales($year, $month);
|
||||||
|
$prevSales = $this->handleSales($prev['year'], $prev['month']);
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
foreach (range(1, 31) as $day) {
|
||||||
|
$date = sprintf('%02d', $day);
|
||||||
|
$prevMonth = "{$prev['year']}-{$prev['month']}";
|
||||||
|
$currMonth = "{$year}-{$month}";
|
||||||
|
$items[] = [
|
||||||
|
'date' => $date,
|
||||||
|
$currMonth => $currSales[$date] ?? 0,
|
||||||
|
$prevMonth => $prevSales[$date] ?? 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refunds()
|
||||||
|
{
|
||||||
|
$year = $this->request->getQuery('year', 'int', date('Y'));
|
||||||
|
$month = $this->request->getQuery('month', 'int', date('m'));
|
||||||
|
|
||||||
|
$prev = $this->getPrevMonth($year, $month);
|
||||||
|
$currRefunds = $this->handleRefunds($year, $month);
|
||||||
|
$prevRefunds = $this->handleRefunds($prev['year'], $prev['month']);
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
foreach (range(1, 31) as $day) {
|
||||||
|
$date = sprintf('%02d', $day);
|
||||||
|
$prevMonth = "{$prev['year']}-{$prev['month']}";
|
||||||
|
$currMonth = "{$year}-{$month}";
|
||||||
|
$items[] = [
|
||||||
|
'date' => $date,
|
||||||
|
$currMonth => $currRefunds[$date] ?? 0,
|
||||||
|
$prevMonth => $prevRefunds[$date] ?? 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registeredUsers()
|
||||||
|
{
|
||||||
|
$year = $this->request->getQuery('year', 'int', date('Y'));
|
||||||
|
$month = $this->request->getQuery('month', 'int', date('m'));
|
||||||
|
|
||||||
|
$prev = $this->getPrevMonth($year, $month);
|
||||||
|
$currUsers = $this->handleRegisteredUsers($year, $month);
|
||||||
|
$prevUsers = $this->handleRegisteredUsers($prev['year'], $prev['month']);
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
foreach (range(1, 31) as $day) {
|
||||||
|
$date = sprintf('%02d', $day);
|
||||||
|
$prevMonth = "{$prev['year']}-{$prev['month']}";
|
||||||
|
$currMonth = "{$year}-{$month}";
|
||||||
|
$items[] = [
|
||||||
|
'date' => $date,
|
||||||
|
$currMonth => $currUsers[$date] ?? 0,
|
||||||
|
$prevMonth => $prevUsers[$date] ?? 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onlineUsers()
|
||||||
|
{
|
||||||
|
$year = $this->request->getQuery('year', 'int', date('Y'));
|
||||||
|
$month = $this->request->getQuery('month', 'int', date('m'));
|
||||||
|
|
||||||
|
$prev = $this->getPrevMonth($year, $month);
|
||||||
|
$currUsers = $this->handleOnlineUsers($year, $month);
|
||||||
|
$prevUsers = $this->handleOnlineUsers($prev['year'], $prev['month']);
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
foreach (range(1, 31) as $day) {
|
||||||
|
$date = sprintf('%02d', $day);
|
||||||
|
$prevMonth = "{$prev['year']}-{$prev['month']}";
|
||||||
|
$currMonth = "{$year}-{$month}";
|
||||||
|
$items[] = [
|
||||||
|
'date' => $date,
|
||||||
|
$currMonth => $currUsers[$date] ?? 0,
|
||||||
|
$prevMonth => $prevUsers[$date] ?? 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getYearOptions()
|
||||||
|
{
|
||||||
|
$end = date('Y');
|
||||||
|
|
||||||
|
$start = $end - 3;
|
||||||
|
|
||||||
|
return range($start, $end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMonthOptions()
|
||||||
|
{
|
||||||
|
$options = [];
|
||||||
|
|
||||||
|
foreach (range(1, 12) as $value) {
|
||||||
|
$options[] = sprintf('%02d', $value);
|
||||||
|
}
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function isCurrMonth($year, $month)
|
||||||
|
{
|
||||||
|
return date('Y-m') == "{$year}-{$month}";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPrevMonth($year, $month)
|
||||||
|
{
|
||||||
|
$currentMonthTime = strtotime("{$year}-{$month}");
|
||||||
|
|
||||||
|
$prevMonthTime = strtotime('-1 month', $currentMonthTime);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'year' => date('Y', $prevMonthTime),
|
||||||
|
'month' => date('m', $prevMonthTime),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getMonthDates($year, $month)
|
||||||
|
{
|
||||||
|
$startTime = strtotime("{$year}-{$month}-01");
|
||||||
|
|
||||||
|
$days = date('t', $startTime);
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach (range(1, $days) as $day) {
|
||||||
|
$result[] = sprintf('%04d-%02d-%02d', $year, $month, $day);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleHotSales($type, $year, $month)
|
||||||
|
{
|
||||||
|
$keyName = "stat_hot_sales:{$type}_{$year}_{$month}";
|
||||||
|
|
||||||
|
$cache = $this->getCache();
|
||||||
|
|
||||||
|
$items = $cache->get($keyName);
|
||||||
|
|
||||||
|
if (!$items) {
|
||||||
|
|
||||||
|
$statRepo = new StatRepo();
|
||||||
|
|
||||||
|
$orders = $statRepo->findMonthlyOrders($type, $year, $month);
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
if ($orders->count() > 0) {
|
||||||
|
|
||||||
|
foreach ($orders as $order) {
|
||||||
|
$key = $order->item_id;
|
||||||
|
if (!isset($items[$key])) {
|
||||||
|
$items[$key] = [
|
||||||
|
'title' => $order->subject,
|
||||||
|
'total_count' => 1,
|
||||||
|
'total_amount' => $order->amount,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$items[$key]['total_count'] += 1;
|
||||||
|
$items[$key]['total_amount'] += $order->amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalCount = array_column($items, 'total_count');
|
||||||
|
|
||||||
|
array_multisort($totalCount, SORT_DESC, $items);
|
||||||
|
}
|
||||||
|
|
||||||
|
$queryMonth = "{$year}-{$month}";
|
||||||
|
|
||||||
|
$currMonth = date('Y-m');
|
||||||
|
|
||||||
|
if ($queryMonth < $currMonth) {
|
||||||
|
$cache->save($keyName, $items, 7 * 86400);
|
||||||
|
} else {
|
||||||
|
$cache->save($keyName, $items, 2 * 3600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleSales($year, $month)
|
||||||
|
{
|
||||||
|
$keyName = "stat_sales:{$year}_{$month}";
|
||||||
|
|
||||||
|
$redis = $this->getRedis();
|
||||||
|
|
||||||
|
$list = $redis->hGetAll($keyName);
|
||||||
|
|
||||||
|
$statRepo = new StatRepo();
|
||||||
|
|
||||||
|
$currDate = date('Y-m-d');
|
||||||
|
$currDay = date('d');
|
||||||
|
|
||||||
|
if (!$list) {
|
||||||
|
$dates = $this->getMonthDates($year, $month);
|
||||||
|
foreach ($dates as $date) {
|
||||||
|
$key = substr($date, -2);
|
||||||
|
if ($date < $currDate) {
|
||||||
|
$list[$key] = $statRepo->sumDailySales($date);
|
||||||
|
} elseif ($date == $currDate) {
|
||||||
|
$list[$key] = -999;
|
||||||
|
} else {
|
||||||
|
$list[$key] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$redis->hMSet($keyName, $list);
|
||||||
|
$redis->expire($keyName, 7 * 86400);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($list as $key => $value) {
|
||||||
|
if ($value < 0) {
|
||||||
|
$list[$key] = $statRepo->sumDailySales("{$year}-{$month}-{$key}");
|
||||||
|
$redis->hSet($keyName, $key, $list[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isCurrMonth($year, $month)) {
|
||||||
|
$list[$currDay] = $statRepo->sumDailySales($currDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleRefunds($year, $month)
|
||||||
|
{
|
||||||
|
$keyName = "stat_refunds:{$year}_{$month}";
|
||||||
|
|
||||||
|
$redis = $this->getRedis();
|
||||||
|
|
||||||
|
$list = $redis->hGetAll($keyName);
|
||||||
|
|
||||||
|
$statRepo = new StatRepo();
|
||||||
|
|
||||||
|
$currDate = date('Y-m-d');
|
||||||
|
$currDay = date('d');
|
||||||
|
|
||||||
|
if (!$list) {
|
||||||
|
$dates = $this->getMonthDates($year, $month);
|
||||||
|
foreach ($dates as $date) {
|
||||||
|
$key = substr($date, -2);
|
||||||
|
if ($date < $currDate) {
|
||||||
|
$list[$key] = $statRepo->sumDailyRefunds($date);
|
||||||
|
} elseif ($date == $currDate) {
|
||||||
|
$list[$key] = -999;
|
||||||
|
} else {
|
||||||
|
$list[$key] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$redis->hMSet($keyName, $list);
|
||||||
|
$redis->expire($keyName, 7 * 86400);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($list as $key => $value) {
|
||||||
|
if ($value < 0) {
|
||||||
|
$list[$key] = $statRepo->sumDailyRefunds("{$year}-{$month}-{$key}");
|
||||||
|
$redis->hSet($keyName, $key, $list[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isCurrMonth($year, $month)) {
|
||||||
|
$list[$currDay] = $statRepo->sumDailyRefunds($currDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleRegisteredUsers($year, $month)
|
||||||
|
{
|
||||||
|
$keyName = "stat_reg_users:{$year}_{$month}";
|
||||||
|
|
||||||
|
$redis = $this->getRedis();
|
||||||
|
|
||||||
|
$list = $redis->hGetAll($keyName);
|
||||||
|
|
||||||
|
$statRepo = new StatRepo();
|
||||||
|
|
||||||
|
$currDate = date('Y-m-d');
|
||||||
|
$currDay = date('d');
|
||||||
|
|
||||||
|
if (!$list) {
|
||||||
|
$dates = $this->getMonthDates($year, $month);
|
||||||
|
foreach ($dates as $date) {
|
||||||
|
$key = substr($date, -2);
|
||||||
|
if ($date < $currDate) {
|
||||||
|
$list[$key] = $statRepo->countDailyRegisteredUser($date);
|
||||||
|
} elseif ($date == $currDate) {
|
||||||
|
$list[$key] = -999;
|
||||||
|
} else {
|
||||||
|
$list[$key] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$redis->hMSet($keyName, $list);
|
||||||
|
$redis->expire($keyName, 7 * 86400);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($list as $key => $value) {
|
||||||
|
if ($value < 0) {
|
||||||
|
$list[$key] = $statRepo->countDailyRegisteredUser("{$year}-{$month}-{$key}");
|
||||||
|
$redis->hSet($keyName, $key, $list[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isCurrMonth($year, $month)) {
|
||||||
|
$list[$currDay] = $statRepo->countDailyRegisteredUser($currDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleOnlineUsers($year, $month)
|
||||||
|
{
|
||||||
|
$keyName = "stat_online_users:{$year}_{$month}";
|
||||||
|
|
||||||
|
$redis = $this->getRedis();
|
||||||
|
|
||||||
|
$list = $redis->hGetAll($keyName);
|
||||||
|
|
||||||
|
$statRepo = new StatRepo();
|
||||||
|
|
||||||
|
$currDate = date('Y-m-d');
|
||||||
|
$currDay = date('d');
|
||||||
|
|
||||||
|
if (!$list) {
|
||||||
|
$dates = $this->getMonthDates($year, $month);
|
||||||
|
foreach ($dates as $date) {
|
||||||
|
$key = substr($date, -2);
|
||||||
|
if ($date < $currDate) {
|
||||||
|
$list[$key] = $statRepo->countDailyOnlineUser($date);
|
||||||
|
} elseif ($date == $currDate) {
|
||||||
|
$list[$key] = -999;
|
||||||
|
} else {
|
||||||
|
$list[$key] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$redis->hMSet($keyName, $list);
|
||||||
|
$redis->expire($keyName, 7 * 86400);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($list as $key => $value) {
|
||||||
|
if ($value < 0) {
|
||||||
|
$list[$key] = $statRepo->countDailyOnlineUser("{$year}-{$month}-{$key}");
|
||||||
|
$redis->hSet($keyName, $key, $list[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isCurrMonth($year, $month)) {
|
||||||
|
$list[$currDay] = $statRepo->countDailyOnlineUser($currDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -20,6 +20,7 @@
|
|||||||
<ul class="layui-tab-title kg-tab-title">
|
<ul class="layui-tab-title kg-tab-title">
|
||||||
<li class="layui-this">基本信息</li>
|
<li class="layui-this">基本信息</li>
|
||||||
<li>{{ content_title(course.model) }}</li>
|
<li>{{ content_title(course.model) }}</li>
|
||||||
|
<li>学习资料</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="layui-tab-content">
|
<div class="layui-tab-content">
|
||||||
<div class="layui-tab-item layui-show">
|
<div class="layui-tab-item layui-show">
|
||||||
@ -34,6 +35,9 @@
|
|||||||
{{ partial('chapter/edit_lesson_read') }}
|
{{ partial('chapter/edit_lesson_read') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="layui-tab-item">
|
||||||
|
{{ partial('chapter/edit_resource') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -64,6 +68,9 @@
|
|||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{{ js_include('lib/cos-js-sdk-v5.min.js') }}
|
||||||
|
{{ js_include('admin/js/chapter.resource.js') }}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block inline_js %}
|
{% 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 %}
|
@ -13,11 +13,14 @@
|
|||||||
<body class="layui-layout-body">
|
<body class="layui-layout-body">
|
||||||
<div class="layui-layout layui-layout-admin">
|
<div class="layui-layout layui-layout-admin">
|
||||||
<div class="layui-header">
|
<div class="layui-header">
|
||||||
<div class="layui-logo">COURSE ADMIN</div>
|
<div class="layui-logo">酷瓜云课堂</div>
|
||||||
<div class="kg-side-menu-bar">
|
<div class="kg-side-menu-bar">
|
||||||
<a href="javascript:"><i class="layui-icon layui-icon-spread-left"></i></a>
|
<a href="javascript:"><i class="layui-icon layui-icon-spread-left"></i></a>
|
||||||
</div>
|
</div>
|
||||||
<ul class="layui-nav layui-layout-left kg-nav-module">
|
<ul class="layui-nav layui-layout-left kg-nav-module">
|
||||||
|
<li class="layui-nav-item">
|
||||||
|
<a href="/admin">首页</a>
|
||||||
|
</li>
|
||||||
{% for item in top_menus %}
|
{% for item in top_menus %}
|
||||||
<li data-module="module-{{ item.id }}" class="layui-nav-item">
|
<li data-module="module-{{ item.id }}" class="layui-nav-item">
|
||||||
<a href="javascript:">{{ item.title }}</a>
|
<a href="javascript:">{{ item.title }}</a>
|
||||||
|
@ -2,22 +2,28 @@
|
|||||||
<div class="layui-card-header">今日统计</div>
|
<div class="layui-card-header">今日统计</div>
|
||||||
<div class="layui-card-body">
|
<div class="layui-card-body">
|
||||||
<div class="layui-row layui-col-space10">
|
<div class="layui-row layui-col-space10">
|
||||||
<div class="layui-col-md4">
|
<div class="layui-col-md3">
|
||||||
<div class="kg-stat-card">
|
<div class="kg-stat-card">
|
||||||
<div class="name">用户注册</div>
|
<div class="name">用户注册</div>
|
||||||
<div class="count">{{ today_stat.user_count }}</div>
|
<div class="count">{{ today_stat.register_count }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-col-md4">
|
<div class="layui-col-md3">
|
||||||
<div class="kg-stat-card">
|
<div class="kg-stat-card">
|
||||||
<div class="name">成交订单</div>
|
<div class="name">成交订单</div>
|
||||||
<div class="count">{{ today_stat.order_count }}</div>
|
<div class="count">{{ today_stat.sale_count }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-col-md4">
|
<div class="layui-col-md3">
|
||||||
<div class="kg-stat-card">
|
<div class="kg-stat-card">
|
||||||
<div class="name">销售金额</div>
|
<div class="name">销售金额</div>
|
||||||
<div class="count">{{ today_stat.sale_amount }}</div>
|
<div class="count">{{ '¥%0.2f'|format(today_stat.sale_amount) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-col-md3">
|
||||||
|
<div class="kg-stat-card">
|
||||||
|
<div class="name">退款金额</div>
|
||||||
|
<div class="count">{{ '¥%0.2f'|format(today_stat.refund_amount) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,17 +4,17 @@
|
|||||||
|
|
||||||
{%- macro position_info(value) %}
|
{%- macro position_info(value) %}
|
||||||
{% if value == 1 %}
|
{% if value == 1 %}
|
||||||
<span class="layui-badge layui-bg-green">顶部</span>
|
顶部
|
||||||
{% elseif value == 2 %}
|
{% elseif value == 2 %}
|
||||||
<span class="layui-badge layui-bg-blue">底部</span>
|
底部
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{%- macro target_info(value) %}
|
{%- macro target_info(value) %}
|
||||||
{% if value == '_blank' %}
|
{% if value == '_blank' %}
|
||||||
<span class="layui-badge layui-bg-green">新窗口</span>
|
新窗口
|
||||||
{% elseif value == '_self' %}
|
{% elseif value == '_self' %}
|
||||||
<span class="layui-badge layui-bg-blue">原窗口</span>
|
原窗口
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
@ -38,28 +38,28 @@
|
|||||||
|
|
||||||
{%- macro item_type(value) %}
|
{%- macro item_type(value) %}
|
||||||
{% if value == 1 %}
|
{% if value == 1 %}
|
||||||
<span class="layui-badge layui-bg-green">课程</span>
|
课程
|
||||||
{% elseif value == 2 %}
|
{% elseif value == 2 %}
|
||||||
<span class="layui-badge layui-bg-blue">套餐</span>
|
套餐
|
||||||
{% elseif value == 3 %}
|
{% elseif value == 3 %}
|
||||||
<span class="layui-badge layui-bg-red">赞赏</span>
|
赞赏
|
||||||
{% elseif value == 4 %}
|
{% elseif value == 4 %}
|
||||||
<span class="layui-badge layui-bg-orange">会员</span>
|
会员
|
||||||
{% elseif value == 99 %}
|
{% elseif value == 99 %}
|
||||||
<span class="layui-badge layui-bg-black">测试</span>
|
测试
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{%- macro order_status(value) %}
|
{%- macro order_status(value) %}
|
||||||
{% if value == 1 %}
|
{% if value == 1 %}
|
||||||
<span class="layui-badge layui-bg-blue">待支付</span>
|
待支付
|
||||||
{% elseif value == 2 %}
|
{% elseif value == 2 %}
|
||||||
<span class="layui-badge layui-bg-gray">发货中</span>
|
发货中
|
||||||
{% elseif value == 3 %}
|
{% elseif value == 3 %}
|
||||||
<span class="layui-badge layui-bg-green">已完成</span>
|
已完成
|
||||||
{% elseif value == 4 %}
|
{% elseif value == 4 %}
|
||||||
<span class="layui-badge layui-bg-cyan">已关闭</span>
|
已关闭
|
||||||
{% elseif value == 5 %}
|
{% elseif value == 5 %}
|
||||||
<span class="layui-badge layui-bg-red">已退款</span>
|
已退款
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
@ -1,16 +1,16 @@
|
|||||||
{%- macro refund_status(value) %}
|
{%- macro refund_status(value) %}
|
||||||
{% if value == 1 %}
|
{% if value == 1 %}
|
||||||
<span class="layui-badge layui-bg-blue">待处理</span>
|
待处理
|
||||||
{% elseif value == 2 %}
|
{% elseif value == 2 %}
|
||||||
<span class="layui-badge layui-bg-gray">已取消</span>
|
已取消
|
||||||
{% elseif value == 3 %}
|
{% elseif value == 3 %}
|
||||||
<span class="layui-badge layui-bg-orange">已审核</span>
|
已审核
|
||||||
{% elseif value == 4 %}
|
{% elseif value == 4 %}
|
||||||
<span class="layui-badge layui-bg-red">已拒绝</span>
|
已拒绝
|
||||||
{% elseif value == 5 %}
|
{% elseif value == 5 %}
|
||||||
<span class="layui-badge layui-bg-green">已完成</span>
|
已完成
|
||||||
{% elseif value == 6 %}
|
{% elseif value == 6 %}
|
||||||
<span class="layui-badge layui-bg-cyan">已失败</span>
|
已失败
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{%- macro type_info(value) %}
|
{%- macro type_info(value) %}
|
||||||
{% if value == 'system' %}
|
{% if value == 1 %}
|
||||||
<span class="layui-badge layui-bg-green">内置</span>
|
内置
|
||||||
{% elseif value == 'custom' %}
|
{% elseif value == 2 %}
|
||||||
<span class="layui-badge layui-bg-blue">自定</span>
|
自定义
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
@ -43,7 +43,7 @@
|
|||||||
{% set restore_url = url({'for':'admin.role.restore','id':item.id}) %}
|
{% set restore_url = url({'for':'admin.role.restore','id':item.id}) %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ item.id }}</td>
|
<td>{{ item.id }}</td>
|
||||||
<td><a href="javascript:" title="{{ item.summary }}">{{ item.name }}</a></td>
|
<td><a href="{{ edit_url }}" title="{{ item.summary }}">{{ item.name }}</a></td>
|
||||||
<td>{{ type_info(item.type) }}</td>
|
<td>{{ type_info(item.type) }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ user_list_url }}">
|
<a href="{{ user_list_url }}">
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
{%- macro target_info(value) %}
|
{%- macro target_info(value) %}
|
||||||
{% if value == 1 %}
|
{% if value == 1 %}
|
||||||
<span class="layui-badge layui-bg-green">课程</span>
|
课程
|
||||||
{% elseif value == 2 %}
|
{% elseif value == 2 %}
|
||||||
<span class="layui-badge layui-bg-blue">单页</span>
|
单页
|
||||||
{% elseif value == 3 %}
|
{% elseif value == 3 %}
|
||||||
<span class="layui-badge layui-bg-gray">链接</span>
|
链接
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
101
app/Http/Admin/Views/stat/hot_sales.volt
Normal file
101
app/Http/Admin/Views/stat/hot_sales.volt
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{%- macro show_sales(sales) %}
|
||||||
|
<table class="layui-table kg-table">
|
||||||
|
<colgroup>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>排序</th>
|
||||||
|
<th>名称</th>
|
||||||
|
<th>数量</th>
|
||||||
|
<th>金额</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for sale in sales %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ loop.index }}</td>
|
||||||
|
<td>{{ sale.title }}</td>
|
||||||
|
<td>{{ sale.total_count }}</td>
|
||||||
|
<td>{{ '¥%0.2f'|format(sale.total_amount) }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% set types = {'1':'课程','2':'套餐','3':'赞赏','4':'会员'} %}
|
||||||
|
{% set year = request.get('year','int',date('Y')) %}
|
||||||
|
{% set month = request.get('month','int',date('m')) %}
|
||||||
|
{% set type = request.get('type','int',1) %}
|
||||||
|
|
||||||
|
<div class="kg-nav">
|
||||||
|
<div class="kg-nav-left">
|
||||||
|
<span class="layui-breadcrumb">
|
||||||
|
<a><cite>热卖商品统计</cite></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="layui-form kg-search-form" method="GET" action="{{ url({'for':'admin.stat.hot_sales'}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">选择类型</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="type">
|
||||||
|
{% for key,value in types %}
|
||||||
|
<option value="{{ key }}" {% if key == type %}selected{% endif %}>{{ value }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="layui-form-label">选择年份</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="year">
|
||||||
|
{% for value in years %}
|
||||||
|
<option value="{{ value }}" {% if value == year %}selected{% endif %}>{{ value }}年</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="layui-form-label">选择月份</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="month">
|
||||||
|
{% for value in months %}
|
||||||
|
<option value="{{ value }}" {% if value == month %}selected{% endif %}>{{ value }}月</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<button class="layui-btn" lay-submit="true">查询</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="kg-sale-list layui-row layui-col-space15">
|
||||||
|
{% for item in items %}
|
||||||
|
<div class="layui-col-md6">
|
||||||
|
<div class="layui-card">
|
||||||
|
<div class="layui-card-header">{{ item.title }}</div>
|
||||||
|
<div class="layui-card-body">{{ show_sales(item.sales) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block inline_css %}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.kg-sale-list {
|
||||||
|
padding: 10px;
|
||||||
|
background: #f2f2f2;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
{% endblock %}
|
74
app/Http/Admin/Views/stat/online_users.volt
Normal file
74
app/Http/Admin/Views/stat/online_users.volt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% set year = request.get('year','int',date('Y')) %}
|
||||||
|
{% set month = request.get('month','int',date('m')) %}
|
||||||
|
|
||||||
|
<div class="kg-nav">
|
||||||
|
<div class="kg-nav-left">
|
||||||
|
<span class="layui-breadcrumb">
|
||||||
|
<a><cite>活跃用户统计</cite></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="layui-form kg-search-form" method="GET" action="{{ url({'for':'admin.stat.online_users'}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">选择年份</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="year">
|
||||||
|
{% for value in years %}
|
||||||
|
<option value="{{ value }}" {% if value == year %}selected{% endif %}>{{ value }}年</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="layui-form-label">选择月份</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="month">
|
||||||
|
{% for value in months %}
|
||||||
|
<option value="{{ value }}" {% if value == month %}selected{% endif %}>{{ value }}月</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<button class="layui-btn" lay-submit="true">查询</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="kg-chart" id="chart"></div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('https://cdn.bootcdn.net/ajax/libs/echarts/4.8.0/echarts.min.js', false) }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block inline_js %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var myChart = echarts.init(document.getElementById('chart'));
|
||||||
|
|
||||||
|
var option = {
|
||||||
|
legend: {},
|
||||||
|
tooltip: {},
|
||||||
|
dataset: {
|
||||||
|
source: {{ data|json_encode }}
|
||||||
|
},
|
||||||
|
xAxis: {type: 'category'},
|
||||||
|
yAxis: {},
|
||||||
|
series: [
|
||||||
|
{type: 'line'},
|
||||||
|
{type: 'line'}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
myChart.setOption(option);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
74
app/Http/Admin/Views/stat/refunds.volt
Normal file
74
app/Http/Admin/Views/stat/refunds.volt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% set year = request.get('year','int',date('Y')) %}
|
||||||
|
{% set month = request.get('month','int',date('m')) %}
|
||||||
|
|
||||||
|
<div class="kg-nav">
|
||||||
|
<div class="kg-nav-left">
|
||||||
|
<span class="layui-breadcrumb">
|
||||||
|
<a><cite>售后退款统计</cite></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="layui-form kg-search-form" method="GET" action="{{ url({'for':'admin.stat.refunds'}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">选择年份</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="year">
|
||||||
|
{% for value in years %}
|
||||||
|
<option value="{{ value }}" {% if value == year %}selected{% endif %}>{{ value }}年</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="layui-form-label">选择月份</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="month">
|
||||||
|
{% for value in months %}
|
||||||
|
<option value="{{ value }}" {% if value == month %}selected{% endif %}>{{ value }}月</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<button class="layui-btn" lay-submit="true">查询</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="kg-chart" id="chart"></div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('https://cdn.bootcdn.net/ajax/libs/echarts/4.8.0/echarts.min.js', false) }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block inline_js %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var myChart = echarts.init(document.getElementById('chart'));
|
||||||
|
|
||||||
|
var option = {
|
||||||
|
legend: {},
|
||||||
|
tooltip: {},
|
||||||
|
dataset: {
|
||||||
|
source: {{ data|json_encode }}
|
||||||
|
},
|
||||||
|
xAxis: {type: 'category'},
|
||||||
|
yAxis: {},
|
||||||
|
series: [
|
||||||
|
{type: 'line'},
|
||||||
|
{type: 'line'}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
myChart.setOption(option);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
74
app/Http/Admin/Views/stat/registered_users.volt
Normal file
74
app/Http/Admin/Views/stat/registered_users.volt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% set year = request.get('year','int',date('Y')) %}
|
||||||
|
{% set month = request.get('month','int',date('m')) %}
|
||||||
|
|
||||||
|
<div class="kg-nav">
|
||||||
|
<div class="kg-nav-left">
|
||||||
|
<span class="layui-breadcrumb">
|
||||||
|
<a><cite>注册用户统计</cite></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="layui-form kg-search-form" method="GET" action="{{ url({'for':'admin.stat.reg_users'}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">选择年份</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="year">
|
||||||
|
{% for value in years %}
|
||||||
|
<option value="{{ value }}" {% if value == year %}selected{% endif %}>{{ value }}年</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="layui-form-label">选择月份</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="month">
|
||||||
|
{% for value in months %}
|
||||||
|
<option value="{{ value }}" {% if value == month %}selected{% endif %}>{{ value }}月</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<button class="layui-btn" lay-submit="true">查询</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="kg-chart" id="chart"></div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('https://cdn.bootcdn.net/ajax/libs/echarts/4.8.0/echarts.min.js', false) }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block inline_js %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var myChart = echarts.init(document.getElementById('chart'));
|
||||||
|
|
||||||
|
var option = {
|
||||||
|
legend: {},
|
||||||
|
tooltip: {},
|
||||||
|
dataset: {
|
||||||
|
source: {{ data|json_encode }}
|
||||||
|
},
|
||||||
|
xAxis: {type: 'category'},
|
||||||
|
yAxis: {},
|
||||||
|
series: [
|
||||||
|
{type: 'line'},
|
||||||
|
{type: 'line'}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
myChart.setOption(option);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
74
app/Http/Admin/Views/stat/sales.volt
Normal file
74
app/Http/Admin/Views/stat/sales.volt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% set year = request.get('year','int',date('Y')) %}
|
||||||
|
{% set month = request.get('month','int',date('m')) %}
|
||||||
|
|
||||||
|
<div class="kg-nav">
|
||||||
|
<div class="kg-nav-left">
|
||||||
|
<span class="layui-breadcrumb">
|
||||||
|
<a><cite>成交订单统计</cite></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="layui-form kg-search-form" method="GET" action="{{ url({'for':'admin.stat.sales'}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">选择年份</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="year">
|
||||||
|
{% for value in years %}
|
||||||
|
<option value="{{ value }}" {% if value == year %}selected{% endif %}>{{ value }}年</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="layui-form-label">选择月份</label>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<select name="month">
|
||||||
|
{% for value in months %}
|
||||||
|
<option value="{{ value }}" {% if value == month %}selected{% endif %}>{{ value }}月</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<button class="layui-btn" lay-submit="true">查询</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="kg-chart" id="chart"></div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('https://cdn.bootcdn.net/ajax/libs/echarts/4.8.0/echarts.min.js', false) }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block inline_js %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var myChart = echarts.init(document.getElementById('chart'));
|
||||||
|
|
||||||
|
var option = {
|
||||||
|
legend: {},
|
||||||
|
tooltip: {},
|
||||||
|
dataset: {
|
||||||
|
source: {{ data|json_encode }}
|
||||||
|
},
|
||||||
|
xAxis: {type: 'category'},
|
||||||
|
yAxis: {},
|
||||||
|
series: [
|
||||||
|
{type: 'line'},
|
||||||
|
{type: 'line'}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
myChart.setOption(option);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -2,6 +2,18 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
{%- macro client_type_info(value) %}
|
||||||
|
{% if value == 1 %}
|
||||||
|
desktop
|
||||||
|
{% elseif value == 2 %}
|
||||||
|
mobile
|
||||||
|
{% elseif value == 3 %}
|
||||||
|
app
|
||||||
|
{% elseif value == 4 %}
|
||||||
|
小程序
|
||||||
|
{% endif %}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
<table class="layui-table kg-table">
|
<table class="layui-table kg-table">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col>
|
<col>
|
||||||
@ -26,7 +38,7 @@
|
|||||||
<p class="layui-elip">章节:{{ item.chapter.title }}</p>
|
<p class="layui-elip">章节:{{ item.chapter.title }}</p>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<p>类型:{{ item.client_type }}</p>
|
<p>类型:{{ client_type_info(item.client_type) }}</p>
|
||||||
<p>地址:<a href="javascript:" class="kg-ip2region" title="查看位置" data-ip="{{ item.client_ip }}">{{ item.client_ip }}</a></p>
|
<p>地址:<a href="javascript:" class="kg-ip2region" title="查看位置" data-ip="{{ item.client_ip }}">{{ item.client_ip }}</a></p>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ item.duration|duration }}</td>
|
<td>{{ item.duration|duration }}</td>
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
{%- macro source_type_info(value) %}
|
{%- macro source_type_info(value) %}
|
||||||
{% if value == 1 %}
|
{% if value == 1 %}
|
||||||
<span class="layui-badge layui-bg-green">免费</span>
|
免费
|
||||||
{% elseif value == 2 %}
|
{% elseif value == 2 %}
|
||||||
<span class="layui-badge layui-bg-orange">付费</span>
|
<span class="layui-badge layui-bg-orange">付费</span>
|
||||||
{% elseif value == 3 %}
|
{% elseif value == 3 %}
|
||||||
<span class="layui-badge layui-bg-blue">导入</span>
|
导入
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN-Hans">
|
<html lang="zh-CN-Hans">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
<meta name="csrf-token" content="{{ csrfToken.getToken() }}">
|
<meta name="csrf-token" content="{{ csrfToken.getToken() }}">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN-Hans">
|
<html lang="zh-CN-Hans">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
<meta name="csrf-token" content="{{ csrfToken.getToken() }}">
|
<meta name="csrf-token" content="{{ csrfToken.getToken() }}">
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
{%- macro channel_type(value) %}
|
{%- macro channel_type(value) %}
|
||||||
{% if value == 1 %}
|
{% if value == 1 %}
|
||||||
<span class="layui-badge layui-bg-blue">支付宝</span>
|
支付宝
|
||||||
{% elseif value == 2 %}
|
{% elseif value == 2 %}
|
||||||
<span class="layui-badge layui-bg-green">微信</span>
|
微信
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{%- macro trade_status(value) %}
|
{%- macro trade_status(value) %}
|
||||||
{% if value == 1 %}
|
{% if value == 1 %}
|
||||||
<span class="layui-badge layui-bg-blue">待支付</span>
|
待支付
|
||||||
{% elseif value == 2 %}
|
{% elseif value == 2 %}
|
||||||
<span class="layui-badge layui-bg-green">已完成</span>
|
已完成
|
||||||
{% elseif value == 3 %}
|
{% elseif value == 3 %}
|
||||||
<span class="layui-badge layui-bg-cyan">已关闭</span>
|
已关闭
|
||||||
{% elseif value == 4 %}
|
{% elseif value == 4 %}
|
||||||
<span class="layui-badge layui-bg-red">已退款</span>
|
已退款
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
@ -4,25 +4,25 @@
|
|||||||
|
|
||||||
{%- macro gender_info(value) %}
|
{%- macro gender_info(value) %}
|
||||||
{% if value == 1 %}
|
{% if value == 1 %}
|
||||||
<span class="layui-badge layui-bg-gray">男</span>
|
男
|
||||||
{% elseif value == 2 %}
|
{% elseif value == 2 %}
|
||||||
<span class="layui-badge layui-bg-gray">女</span>
|
女
|
||||||
{% elseif value == 3 %}
|
{% elseif value == 3 %}
|
||||||
<span class="layui-badge layui-bg-gray">密</span>
|
密
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{%- macro edu_role_info(user) %}
|
{%- macro edu_role_info(user) %}
|
||||||
{% if user.edu_role.id == 1 %}
|
{% if user.edu_role.id == 1 %}
|
||||||
<span class="layui-badge layui-bg-gray">学员</span>
|
学员
|
||||||
{% elseif user.edu_role.id == 2 %}
|
{% elseif user.edu_role.id == 2 %}
|
||||||
<span class="layui-badge layui-bg-blue">讲师</span>
|
<a href="{{ url({'for':'admin.user.list'},{'edu_role':user.edu_role.id}) }}">讲师</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{%- macro admin_role_info(user) %}
|
{%- macro admin_role_info(user) %}
|
||||||
{% if user.admin_role.id %}
|
{% if user.admin_role.id > 0 %}
|
||||||
<span class="layui-badge layui-bg-gray">{{ user.admin_role.name }}</span>
|
<a href="{{ url({'for':'admin.user.list'},{'admin_role':user.admin_role.id}) }}">{{ user.admin_role.name }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
@ -7,11 +7,7 @@ use App\Services\Logic\Account\EmailUpdate as EmailUpdateService;
|
|||||||
use App\Services\Logic\Account\PasswordReset as PasswordResetService;
|
use App\Services\Logic\Account\PasswordReset as PasswordResetService;
|
||||||
use App\Services\Logic\Account\PasswordUpdate as PasswordUpdateService;
|
use App\Services\Logic\Account\PasswordUpdate as PasswordUpdateService;
|
||||||
use App\Services\Logic\Account\PhoneUpdate as PhoneUpdateService;
|
use App\Services\Logic\Account\PhoneUpdate as PhoneUpdateService;
|
||||||
use Phalcon\Mvc\View;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @RoutePrefix("/account")
|
|
||||||
*/
|
|
||||||
class AccountController extends Controller
|
class AccountController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -24,12 +20,12 @@ class AccountController extends Controller
|
|||||||
$this->response->redirect('/');
|
$this->response->redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$returnUrl = $this->request->getHTTPReferer();
|
||||||
|
|
||||||
$service = new AccountService();
|
$service = new AccountService();
|
||||||
|
|
||||||
$captcha = $service->getSettings('captcha');
|
$captcha = $service->getSettings('captcha');
|
||||||
|
|
||||||
$returnUrl = $this->request->getHTTPReferer();
|
|
||||||
|
|
||||||
$this->view->setVar('return_url', $returnUrl);
|
$this->view->setVar('return_url', $returnUrl);
|
||||||
$this->view->setVar('captcha', $captcha);
|
$this->view->setVar('captcha', $captcha);
|
||||||
}
|
}
|
||||||
@ -137,60 +133,6 @@ class AccountController extends Controller
|
|||||||
$this->view->setVar('captcha', $captcha);
|
$this->view->setVar('captcha', $captcha);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Get("/password/edit", name="home.account.edit_pwd")
|
|
||||||
*/
|
|
||||||
public function editPasswordAction()
|
|
||||||
{
|
|
||||||
if ($this->authUser->id == 0) {
|
|
||||||
$this->response->redirect(['for' => 'home.account.login']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$service = new AccountService();
|
|
||||||
|
|
||||||
$captcha = $service->getSettings('captcha');
|
|
||||||
|
|
||||||
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
|
||||||
$this->view->pick('account/edit_password');
|
|
||||||
$this->view->setVar('captcha', $captcha);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Get("/phone/edit", name="home.account.edit_phone")
|
|
||||||
*/
|
|
||||||
public function editPhoneAction()
|
|
||||||
{
|
|
||||||
if ($this->authUser->id == 0) {
|
|
||||||
$this->response->redirect(['for' => 'home.account.login']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$service = new AccountService();
|
|
||||||
|
|
||||||
$captcha = $service->getSettings('captcha');
|
|
||||||
|
|
||||||
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
|
||||||
$this->view->pick('account/edit_phone');
|
|
||||||
$this->view->setVar('captcha', $captcha);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Get("/email/edit", name="home.account.edit_email")
|
|
||||||
*/
|
|
||||||
public function editEmailAction()
|
|
||||||
{
|
|
||||||
if ($this->authUser->id == 0) {
|
|
||||||
$this->response->redirect(['for' => 'home.account.login']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$service = new AccountService();
|
|
||||||
|
|
||||||
$captcha = $service->getSettings('captcha');
|
|
||||||
|
|
||||||
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
|
||||||
$this->view->pick('account/edit_email');
|
|
||||||
$this->view->setVar('captcha', $captcha);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Post("/password/reset", name="home.account.reset_pwd")
|
* @Post("/password/reset", name="home.account.reset_pwd")
|
||||||
*/
|
*/
|
||||||
@ -219,7 +161,12 @@ class AccountController extends Controller
|
|||||||
|
|
||||||
$service->handle();
|
$service->handle();
|
||||||
|
|
||||||
$content = ['msg' => '更新手机成功'];
|
$location = $this->url->get(['for' => 'home.uc.account']);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '更新手机成功',
|
||||||
|
];
|
||||||
|
|
||||||
return $this->jsonSuccess($content);
|
return $this->jsonSuccess($content);
|
||||||
}
|
}
|
||||||
@ -233,7 +180,12 @@ class AccountController extends Controller
|
|||||||
|
|
||||||
$service->handle();
|
$service->handle();
|
||||||
|
|
||||||
$content = ['msg' => '更新邮箱成功'];
|
$location = $this->url->get(['for' => 'home.uc.account']);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '更新邮箱成功',
|
||||||
|
];
|
||||||
|
|
||||||
return $this->jsonSuccess($content);
|
return $this->jsonSuccess($content);
|
||||||
}
|
}
|
||||||
@ -247,7 +199,12 @@ class AccountController extends Controller
|
|||||||
|
|
||||||
$service->handle();
|
$service->handle();
|
||||||
|
|
||||||
$content = ['msg' => '更新密码成功'];
|
$location = $this->url->get(['for' => 'home.uc.account']);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '更新密码成功',
|
||||||
|
];
|
||||||
|
|
||||||
return $this->jsonSuccess($content);
|
return $this->jsonSuccess($content);
|
||||||
}
|
}
|
||||||
|
@ -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\ChapterLike as ChapterLikeService;
|
||||||
use App\Services\Logic\Chapter\DanmuList as ChapterDanmuListService;
|
use App\Services\Logic\Chapter\DanmuList as ChapterDanmuListService;
|
||||||
use App\Services\Logic\Chapter\Learning as ChapterLearningService;
|
use App\Services\Logic\Chapter\Learning as ChapterLearningService;
|
||||||
|
use App\Services\Logic\Chapter\ResourceList as ChapterResourceListService;
|
||||||
use App\Services\Logic\Course\ChapterList as CourseChapterListService;
|
use App\Services\Logic\Course\ChapterList as CourseChapterListService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,6 +17,18 @@ use App\Services\Logic\Course\ChapterList as CourseChapterListService;
|
|||||||
class ChapterController extends Controller
|
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")
|
* @Get("/{id:[0-9]+}", name="home.chapter.show")
|
||||||
*/
|
*/
|
||||||
|
@ -24,7 +24,7 @@ class ImController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Get("/index", name="home.im.index")
|
* @Get("/", name="home.im.index")
|
||||||
*/
|
*/
|
||||||
public function indexAction()
|
public function indexAction()
|
||||||
{
|
{
|
||||||
|
@ -19,6 +19,31 @@ class PublicController extends \Phalcon\Mvc\Controller
|
|||||||
use ResponseTrait;
|
use ResponseTrait;
|
||||||
use SecurityTrait;
|
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")
|
* @Get("/img/{id:[0-9]+}", name="home.img")
|
||||||
*/
|
*/
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace App\Http\Home\Controllers;
|
namespace App\Http\Home\Controllers;
|
||||||
|
|
||||||
use App\Services\Logic\Teacher\Console\ConsultList as ConsultListService;
|
use App\Services\Logic\Teacher\Console\ConsultList as ConsultListService;
|
||||||
@ -28,7 +27,7 @@ class TeacherConsoleController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Get("/index", name="home.tc.index")
|
* @Get("/", name="home.tc.index")
|
||||||
*/
|
*/
|
||||||
public function indexAction()
|
public function indexAction()
|
||||||
{
|
{
|
||||||
@ -95,7 +94,7 @@ class TeacherConsoleController extends Controller
|
|||||||
'stream_code' => substr($pushUrl, $pos + 1),
|
'stream_code' => substr($pushUrl, $pos + 1),
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->view->pick('teacher/console/live_push');
|
$this->view->pick('teacher/console/live');
|
||||||
$this->view->setVar('qrcode', $qrcode);
|
$this->view->setVar('qrcode', $qrcode);
|
||||||
$this->view->setVar('obs', $obs);
|
$this->view->setVar('obs', $obs);
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ class UserConsoleController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Get("/index", name="home.uc.index")
|
* @Get("/", name="home.uc.index")
|
||||||
*/
|
*/
|
||||||
public function indexAction()
|
public function indexAction()
|
||||||
{
|
{
|
||||||
@ -61,9 +61,23 @@ class UserConsoleController extends Controller
|
|||||||
{
|
{
|
||||||
$service = new AccountInfoService();
|
$service = new AccountInfoService();
|
||||||
|
|
||||||
|
$captcha = $service->getSettings('captcha');
|
||||||
|
|
||||||
$account = $service->handle();
|
$account = $service->handle();
|
||||||
|
|
||||||
$this->view->pick('user/console/account');
|
$type = $this->request->getQuery('type', 'string', 'info');
|
||||||
|
|
||||||
|
if ($type == 'info') {
|
||||||
|
$this->view->pick('user/console/account_info');
|
||||||
|
} elseif ($type == 'phone') {
|
||||||
|
$this->view->pick('user/console/account_phone');
|
||||||
|
} elseif ($type == 'email') {
|
||||||
|
$this->view->pick('user/console/account_email');
|
||||||
|
} elseif ($type == 'password') {
|
||||||
|
$this->view->pick('user/console/account_password');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->view->setVar('captcha', $captcha);
|
||||||
$this->view->setVar('account', $account);
|
$this->view->setVar('account', $account);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,8 +197,10 @@ class UserConsoleController extends Controller
|
|||||||
|
|
||||||
$service->handle();
|
$service->handle();
|
||||||
|
|
||||||
|
$location = $this->url->get(['for' => 'home.uc.profile']);
|
||||||
|
|
||||||
$content = [
|
$content = [
|
||||||
'location' => $this->request->getHTTPReferer(),
|
'location' => $location,
|
||||||
'msg' => '更新资料成功',
|
'msg' => '更新资料成功',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
{% extends 'templates/layer.volt' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<form class="layui-form account-form" method="POST" action="{{ url({'for':'home.account.update_email'}) }}">
|
|
||||||
<br><br>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<input class="layui-input" type="password" name="login_password" placeholder="登录密码" lay-verify="required">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<input id="cv-account" class="layui-input" type="text" name="email" placeholder="邮箱地址" data-type="email" lay-verify="required">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<div class="verify-input-inline">
|
|
||||||
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
|
|
||||||
</div>
|
|
||||||
<div class="verify-btn-inline">
|
|
||||||
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">立即绑定</button>
|
|
||||||
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
|
|
||||||
<input id="cv-ticket" type="hidden" name="ticket">
|
|
||||||
<input id="cv-rand" type="hidden" name="rand">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block include_js %}
|
|
||||||
|
|
||||||
{{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }}
|
|
||||||
{{ js_include('home/js/captcha.verify.js') }}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -1,29 +0,0 @@
|
|||||||
{% extends 'templates/layer.volt' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<form class="layui-form account-form" method="POST" action="{{ url({'for':'home.account.update_pwd'}) }}">
|
|
||||||
<br>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<input class="layui-input" type="password" name="origin_password" placeholder="原始密码" lay-verify="required">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<input class="layui-input" type="password" name="new_password" placeholder="新设密码" lay-verify="required">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<input class="layui-input" type="password" name="confirm_password" placeholder="确认密码" lay-verify="required">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<button class="layui-btn layui-btn-fluid" lay-submit="true" lay-filter="go">提交</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -1,42 +0,0 @@
|
|||||||
{% extends 'templates/layer.volt' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<form class="layui-form account-form" method="POST" action="{{ url({'for':'home.account.update_phone'}) }}">
|
|
||||||
<br><br>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<input class="layui-input" type="password" name="login_password" placeholder="登录密码" lay-verify="required">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<input id="cv-account" class="layui-input" type="text" name="phone" placeholder="手机号码" data-type="phone" lay-verify="required">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<div class="verify-input-inline">
|
|
||||||
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
|
|
||||||
</div>
|
|
||||||
<div class="verify-btn-inline">
|
|
||||||
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">立即绑定</button>
|
|
||||||
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
|
|
||||||
<input id="cv-ticket" type="hidden" name="ticket">
|
|
||||||
<input id="cv-rand" type="hidden" name="rand">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block include_js %}
|
|
||||||
|
|
||||||
{{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }}
|
|
||||||
{{ js_include('home/js/captcha.verify.js') }}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -16,20 +16,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" type="password" name="new_password" autocomplete="off" placeholder="新密码" lay-verify="required">
|
<input class="layui-input" type="password" name="new_password" autocomplete="off" placeholder="新密码(字母数字特殊字符6-16位)" lay-verify="required">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<div class="verify-input-inline">
|
<div class="layui-input-inline verify-input-inline">
|
||||||
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
|
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
|
||||||
</div>
|
</div>
|
||||||
<div class="verify-btn-inline">
|
<div class="layui-input-inline verify-btn-inline">
|
||||||
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
|
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">立即重置</button>
|
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">重置密码</button>
|
||||||
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
|
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
|
||||||
<input id="cv-ticket" type="hidden" name="ticket">
|
<input id="cv-ticket" type="hidden" name="ticket">
|
||||||
<input id="cv-rand" type="hidden" name="rand">
|
<input id="cv-rand" type="hidden" name="rand">
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<div class="verify-input-inline">
|
<div class="layui-input-inline verify-input-inline">
|
||||||
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
|
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
|
||||||
</div>
|
</div>
|
||||||
<div class="verify-btn-inline">
|
<div class="layui-input-inline verify-btn-inline">
|
||||||
<button id="cv-verify-emit" class="layui-btn layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
|
<button id="cv-verify-emit" class="layui-btn layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,20 +16,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码" lay-verify="required">
|
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码(字母数字特殊字符6-16位)" lay-verify="required">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<div class="verify-input-inline">
|
<div class="layui-input-inline verify-input-inline">
|
||||||
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
|
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
|
||||||
</div>
|
</div>
|
||||||
<div class="verify-btn-inline">
|
<div class="layui-input-inline verify-btn-inline">
|
||||||
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
|
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">立即注册</button>
|
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">注册帐号</button>
|
||||||
<input type="hidden" name="return_url" value="{{ return_url }}">
|
<input type="hidden" name="return_url" value="{{ return_url }}">
|
||||||
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
|
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
|
||||||
<input id="cv-ticket" type="hidden" name="ticket">
|
<input id="cv-ticket" type="hidden" name="ticket">
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
{% set full_chapter_url = full_url({'for':'home.chapter.show','id':chapter.id}) %}
|
{% 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 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 learning_url = url({'for':'home.chapter.learning','id':chapter.id}) %}
|
||||||
{% set live_chats_url = url({'for':'home.live.chats','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}) %}
|
{% set live_stats_url = url({'for':'home.live.stats','id':chapter.id}) %}
|
||||||
@ -19,8 +20,11 @@
|
|||||||
<a><cite>{{ chapter.title }}</cite></a>
|
<a><cite>{{ chapter.title }}</cite></a>
|
||||||
</span>
|
</span>
|
||||||
<span class="share">
|
<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>
|
<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="分享到微信" 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="分享到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>
|
<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 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 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 learning_url = url({'for':'home.chapter.learning','id':chapter.id}) %}
|
||||||
{% set like_url = url({'for':'home.chapter.like','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}) %}
|
{% set consult_url = url({'for':'home.consult.add'},{'chapter_id':chapter.id}) %}
|
||||||
@ -16,9 +17,12 @@
|
|||||||
<a><cite>{{ chapter.title }}</cite></a>
|
<a><cite>{{ chapter.title }}</cite></a>
|
||||||
</span>
|
</span>
|
||||||
<span class="share">
|
<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="学习人次"><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>
|
<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="分享到微信" 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="分享到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>
|
<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 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 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 learning_url = url({'for':'home.chapter.learning','id':chapter.id}) %}
|
||||||
{% set like_url = url({'for':'home.chapter.like','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}) %}
|
{% set qrcode_url = url({'for':'home.qrcode'},{'text':full_chapter_url}) %}
|
||||||
@ -17,9 +18,12 @@
|
|||||||
<a><cite>{{ chapter.title }}</cite></a>
|
<a><cite>{{ chapter.title }}</cite></a>
|
||||||
</span>
|
</span>
|
||||||
<span class="share">
|
<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="我要点赞" 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>
|
<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="分享到微信"><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="分享到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>
|
<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>
|
<label class="layui-form-label"></label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||||
|
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
<label class="layui-form-label"></label>
|
<label class="layui-form-label"></label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||||
|
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
{% set consult.answer = consult.answer ? consult.answer : '请耐心等待回复吧' %}
|
{% set consult.answer = consult.answer ? consult.answer : '请耐心等待回复吧' %}
|
||||||
|
|
||||||
<div class="consult-info">
|
<div class="consult-info clearfix">
|
||||||
{% if consult.course.id is defined %}
|
{% if consult.course.id is defined %}
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="label">课程:</div>
|
<div class="label">课程:</div>
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
|
|
||||||
<div class="breadcrumb">
|
<div class="breadcrumb">
|
||||||
<span class="layui-breadcrumb">
|
<span class="layui-breadcrumb">
|
||||||
<a href="{{ url({'for':'home.group.list'}) }}">群组列表</a>
|
<a href="/">首页</a>
|
||||||
|
<a href="{{ url({'for':'home.group.list'}) }}">群组</a>
|
||||||
<a><cite>{{ group.name }}</cite></a>
|
<a><cite>{{ group.name }}</cite></a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN-Hans">
|
<html lang="zh-CN-Hans">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
<title>出错啦</title>
|
<title>出错啦</title>
|
||||||
{{ icon_link("favicon.ico") }}
|
{{ icon_link("favicon.ico") }}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN-Hans">
|
<html lang="zh-CN-Hans">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
<meta name="csrf-token" content="{{ csrfToken.getToken() }}">
|
<meta name="csrf-token" content="{{ csrfToken.getToken() }}">
|
||||||
<title>即时通讯</title>
|
<title>酷瓜云课堂</title>
|
||||||
{{ icon_link('favicon.ico') }}
|
{{ icon_link('favicon.ico') }}
|
||||||
{{ css_link('lib/layui/css/layui.css') }}
|
{{ css_link('lib/layui/css/layui.css') }}
|
||||||
{{ css_link('home/css/common.css') }}
|
{{ css_link('home/css/common.css') }}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN-Hans">
|
<html lang="zh-CN-Hans">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
<meta name="keywords" content="{{ seo.getKeywords() }}">
|
<meta name="keywords" content="{{ seo.getKeywords() }}">
|
||||||
|
54
app/Http/Home/Views/user/console/account_email.volt
Normal file
54
app/Http/Home/Views/user/console/account_email.volt
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="layout-main clearfix">
|
||||||
|
<div class="my-sidebar">{{ partial('user/console/menu') }}</div>
|
||||||
|
<div class="my-content">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="my-nav">
|
||||||
|
<span class="title">账号安全 - 修改邮箱</span>
|
||||||
|
</div>
|
||||||
|
<form class="layui-form security-form" method="POST" action="{{ url({'for':'home.account.update_email'}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">登录密码</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input class="layui-input" type="password" name="login_password" autocomplete="off" lay-verify="required">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">邮箱地址</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input id="cv-account" class="layui-input" type="text" name="email" data-type="email" lay-verify="required">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">验证码</label>
|
||||||
|
<div class="layui-input-inline verify-input-inline">
|
||||||
|
<input class="layui-input" type="text" name="verify_code" lay-verify="required">
|
||||||
|
</div>
|
||||||
|
<div class="layui-input-inline verify-btn-inline">
|
||||||
|
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">提交修改</button>
|
||||||
|
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
|
||||||
|
<input id="cv-ticket" type="hidden" name="ticket">
|
||||||
|
<input id="cv-rand" type="hidden" name="rand">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }}
|
||||||
|
{{ js_include('home/js/captcha.verify.js') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% set edit_pwd_url = url({'for':'home.account.edit_pwd'}) %}
|
{% set edit_pwd_url = url({'for':'home.uc.account'},{'type':'password'}) %}
|
||||||
{% set edit_phone_url = url({'for':'home.account.edit_phone'}) %}
|
{% set edit_phone_url = url({'for':'home.uc.account'},{'type':'phone'}) %}
|
||||||
{% set edit_email_url = url({'for':'home.account.edit_email'}) %}
|
{% set edit_email_url = url({'for':'home.uc.account'},{'type':'email'}) %}
|
||||||
|
|
||||||
<div class="layout-main clearfix">
|
<div class="layout-main clearfix">
|
||||||
<div class="my-sidebar">{{ partial('user/console/menu') }}</div>
|
<div class="my-sidebar">{{ partial('user/console/menu') }}</div>
|
||||||
@ -18,17 +18,17 @@
|
|||||||
<span class="icon"><i class="layui-icon layui-icon-password"></i></span>
|
<span class="icon"><i class="layui-icon layui-icon-password"></i></span>
|
||||||
<span class="title">登录密码</span>
|
<span class="title">登录密码</span>
|
||||||
<span class="summary">经常更改密码有助于保护您的帐号安全</span>
|
<span class="summary">经常更改密码有助于保护您的帐号安全</span>
|
||||||
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-pwd" href="javascript:" data-url="{{ edit_pwd_url }}">修改</a></span>
|
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-pwd" href="{{ edit_pwd_url }}">修改</a></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="security-item">
|
<div class="security-item">
|
||||||
<span class="icon"><i class="layui-icon layui-icon-cellphone"></i></span>
|
<span class="icon"><i class="layui-icon layui-icon-cellphone"></i></span>
|
||||||
<span class="title">手机绑定</span>
|
<span class="title">手机绑定</span>
|
||||||
{% if account.phone %}
|
{% if account.phone %}
|
||||||
<span class="summary">已绑定手机:{{ account.phone|anonymous }}</span>
|
<span class="summary">已绑定手机:{{ account.phone|anonymous }}</span>
|
||||||
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-phone" href="javascript:" data-url="{{ edit_phone_url }}">修改</a></span>
|
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-phone" href="{{ edit_phone_url }}">修改</a></span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="summary">可用于登录和重置密码</span>
|
<span class="summary">可用于登录和重置密码</span>
|
||||||
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-phone" href="javascript:" data-url="{{ edit_phone_url }}">绑定</a></span>
|
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-phone" href="{{ edit_phone_url }}">绑定</a></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="security-item">
|
<div class="security-item">
|
||||||
@ -36,10 +36,10 @@
|
|||||||
<span class="title">邮箱绑定</span>
|
<span class="title">邮箱绑定</span>
|
||||||
{% if account.phone %}
|
{% if account.phone %}
|
||||||
<span class="summary">已绑定邮箱:{{ account.email|anonymous }}</span>
|
<span class="summary">已绑定邮箱:{{ account.email|anonymous }}</span>
|
||||||
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-email" href="javascript:" data-url="{{ edit_email_url }}">修改</a></span>
|
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-email" href="{{ edit_email_url }}">修改</a></span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="summary">可用于登录和重置密码</span>
|
<span class="summary">可用于登录和重置密码</span>
|
||||||
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-email" href="javascript:" data-url="{{ edit_email_url }}">绑定</a></span>
|
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-email" href="{{ edit_email_url }}">绑定</a></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -48,9 +48,3 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block include_js %}
|
|
||||||
|
|
||||||
{{ js_include('home/js/user.console.account.js') }}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
44
app/Http/Home/Views/user/console/account_password.volt
Normal file
44
app/Http/Home/Views/user/console/account_password.volt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="layout-main clearfix">
|
||||||
|
<div class="my-sidebar">{{ partial('user/console/menu') }}</div>
|
||||||
|
<div class="my-content">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="my-nav">
|
||||||
|
<span class="title">账号安全 - 修改密码</span>
|
||||||
|
</div>
|
||||||
|
<form class="layui-form security-form" method="POST" action="{{ url({'for':'home.account.update_pwd'}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">原始密码</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input class="layui-input" type="password" name="origin_password" autocomplete="off" lay-verify="required">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">新设密码</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input class="layui-input" type="password" name="new_password" placeholder="字母数字特殊字符6-16位" autocomplete="off" lay-verify="required">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">确认密码</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input class="layui-input" type="password" name="confirm_password" placeholder="字母数字特殊字符6-16位" autocomplete="off" lay-verify="required">
|
||||||
|
</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 class="layui-btn layui-btn-primary" type="reset">重置</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
54
app/Http/Home/Views/user/console/account_phone.volt
Normal file
54
app/Http/Home/Views/user/console/account_phone.volt
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="layout-main clearfix">
|
||||||
|
<div class="my-sidebar">{{ partial('user/console/menu') }}</div>
|
||||||
|
<div class="my-content">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="my-nav">
|
||||||
|
<span class="title">账号安全 - 修改手机</span>
|
||||||
|
</div>
|
||||||
|
<form class="layui-form security-form" method="POST" action="{{ url({'for':'home.account.update_phone'}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">登录密码</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input class="layui-input" type="password" name="login_password" autocomplete="off" lay-verify="required">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">手机号码</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input id="cv-account" class="layui-input" type="text" name="phone" data-type="phone" lay-verify="required">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">验证码</label>
|
||||||
|
<div class="layui-input-inline verify-input-inline">
|
||||||
|
<input class="layui-input" type="text" name="verify_code" lay-verify="required">
|
||||||
|
</div>
|
||||||
|
<div class="layui-input-inline verify-btn-inline">
|
||||||
|
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">提交修改</button>
|
||||||
|
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
|
||||||
|
<input id="cv-ticket" type="hidden" name="ticket">
|
||||||
|
<input id="cv-rand" type="hidden" name="rand">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }}
|
||||||
|
{{ js_include('home/js/captcha.verify.js') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -11,7 +11,7 @@ class AppInfo
|
|||||||
|
|
||||||
protected $link = 'https://gitee.com/koogua';
|
protected $link = 'https://gitee.com/koogua';
|
||||||
|
|
||||||
protected $version = '1.0.0';
|
protected $version = '1.1.0';
|
||||||
|
|
||||||
public function __get($name)
|
public function __get($name)
|
||||||
{
|
{
|
||||||
|
@ -137,7 +137,7 @@ function kg_site_url()
|
|||||||
* @param string $key
|
* @param string $key
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
function kg_site_setting($section, $key = null)
|
function kg_setting($section, $key = null)
|
||||||
{
|
{
|
||||||
$cache = new SettingCache();
|
$cache = new SettingCache();
|
||||||
|
|
||||||
@ -148,6 +148,24 @@ function kg_site_setting($section, $key = null)
|
|||||||
return $settings[$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;
|
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))));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化之前时间
|
* 格式化之前时间
|
||||||
*
|
*
|
||||||
|
@ -90,7 +90,7 @@ class Common
|
|||||||
|
|
||||||
public static function password($str)
|
public static function password($str)
|
||||||
{
|
{
|
||||||
$pattern = '/^[A-Za-z0-9]{6,16}$/';
|
$pattern = '/^[[:graph:]]{6,16}$/';
|
||||||
|
|
||||||
return preg_match($pattern, $str) ? true : false;
|
return preg_match($pattern, $str) ? true : false;
|
||||||
}
|
}
|
||||||
|
52
app/Listeners/User.php
Normal file
52
app/Listeners/User.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Listeners;
|
||||||
|
|
||||||
|
use App\Models\Online as OnlineModel;
|
||||||
|
use App\Models\User as UserModel;
|
||||||
|
use App\Repos\Online as OnlineRepo;
|
||||||
|
use App\Traits\Client as ClientTrait;
|
||||||
|
use Phalcon\Events\Event;
|
||||||
|
|
||||||
|
class User extends Listener
|
||||||
|
{
|
||||||
|
|
||||||
|
use ClientTrait;
|
||||||
|
|
||||||
|
public function online(Event $event, $source, UserModel $user)
|
||||||
|
{
|
||||||
|
$now = time();
|
||||||
|
|
||||||
|
if ($now - $user->active_time > 600) {
|
||||||
|
|
||||||
|
$user->active_time = $now;
|
||||||
|
|
||||||
|
$user->update();
|
||||||
|
|
||||||
|
$onlineRepo = new OnlineRepo();
|
||||||
|
|
||||||
|
$online = $onlineRepo->findByUserDate($user->id, date('Y-m-d'));
|
||||||
|
|
||||||
|
if ($online) {
|
||||||
|
|
||||||
|
$online->active_time = $now;
|
||||||
|
$online->client_type = $this->getClientType();
|
||||||
|
$online->client_ip = $this->getClientIp();
|
||||||
|
|
||||||
|
$online->update();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$online = new OnlineModel();
|
||||||
|
|
||||||
|
$online->user_id = $user->id;
|
||||||
|
$online->active_time = $now;
|
||||||
|
$online->client_type = $this->getClientType();
|
||||||
|
$online->client_ip = $this->getClientIp();
|
||||||
|
|
||||||
|
$online->create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -72,6 +72,9 @@ class Audit extends Model
|
|||||||
|
|
||||||
if (is_array($this->req_data) && !empty($this->req_data)) {
|
if (is_array($this->req_data) && !empty($this->req_data)) {
|
||||||
foreach ($this->req_data as $key => $value) {
|
foreach ($this->req_data as $key => $value) {
|
||||||
|
if (!is_scalar($value)) {
|
||||||
|
$value = kg_json_encode($value);
|
||||||
|
}
|
||||||
if (kg_strlen($value) > 255) {
|
if (kg_strlen($value) > 255) {
|
||||||
$this->req_data[$key] = kg_substr($value, 0, 255);
|
$this->req_data[$key] = kg_substr($value, 0, 255);
|
||||||
}
|
}
|
||||||
|
@ -132,6 +132,13 @@ class Chapter extends Model
|
|||||||
*/
|
*/
|
||||||
public $deleted;
|
public $deleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源数
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $resource_count;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 课时数
|
* 课时数
|
||||||
*
|
*
|
||||||
@ -160,13 +167,6 @@ class Chapter extends Model
|
|||||||
*/
|
*/
|
||||||
public $like_count;
|
public $like_count;
|
||||||
|
|
||||||
/**
|
|
||||||
* 资源数
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $res_count;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建时间
|
* 创建时间
|
||||||
*
|
*
|
||||||
|
@ -8,7 +8,7 @@ class Client
|
|||||||
/**
|
/**
|
||||||
* 类型
|
* 类型
|
||||||
*/
|
*/
|
||||||
const TYPE_DESKTOP = 1; // home
|
const TYPE_DESKTOP = 1; // desktop
|
||||||
const TYPE_MOBILE = 2; // mobile
|
const TYPE_MOBILE = 2; // mobile
|
||||||
const TYPE_APP = 3; // app
|
const TYPE_APP = 3; // app
|
||||||
const TYPE_MINI = 4; // 小程序
|
const TYPE_MINI = 4; // 小程序
|
||||||
@ -16,7 +16,7 @@ class Client
|
|||||||
public static function types()
|
public static function types()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
self::TYPE_DESKTOP => 'home',
|
self::TYPE_DESKTOP => 'desktop',
|
||||||
self::TYPE_MOBILE => 'mobile',
|
self::TYPE_MOBILE => 'mobile',
|
||||||
self::TYPE_APP => 'app',
|
self::TYPE_APP => 'app',
|
||||||
self::TYPE_MINI => 'mini',
|
self::TYPE_MINI => 'mini',
|
||||||
|
@ -179,6 +179,13 @@ class Course extends Model
|
|||||||
*/
|
*/
|
||||||
public $deleted;
|
public $deleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源数
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $resource_count;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 学员数
|
* 学员数
|
||||||
*
|
*
|
||||||
@ -221,13 +228,6 @@ class Course extends Model
|
|||||||
*/
|
*/
|
||||||
public $favorite_count;
|
public $favorite_count;
|
||||||
|
|
||||||
/**
|
|
||||||
* 资源数
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $res_count;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建时间
|
* 创建时间
|
||||||
*
|
*
|
||||||
|
79
app/Models/Online.php
Normal file
79
app/Models/Online.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
class Online extends Model
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键编号
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户编号
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $user_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计划编号
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端类型
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $client_type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端IP
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $client_ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 活跃时间
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $active_time;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $create_time;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $update_time;
|
||||||
|
|
||||||
|
public function getSource(): string
|
||||||
|
{
|
||||||
|
return 'kg_online';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beforeCreate()
|
||||||
|
{
|
||||||
|
$this->create_time = time();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beforeUpdate()
|
||||||
|
{
|
||||||
|
$this->update_time = time();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Phalcon\Mvc\Model\Behavior\SoftDelete;
|
class Resource extends Model
|
||||||
|
|
||||||
class ChapterResource extends Model
|
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,13 +33,6 @@ class ChapterResource extends Model
|
|||||||
*/
|
*/
|
||||||
public $upload_id;
|
public $upload_id;
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除标识
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $deleted;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建时间
|
* 创建时间
|
||||||
*
|
*
|
||||||
@ -58,19 +49,7 @@ class ChapterResource extends Model
|
|||||||
|
|
||||||
public function getSource(): string
|
public function getSource(): string
|
||||||
{
|
{
|
||||||
return 'kg_chapter_resource';
|
return 'kg_resource';
|
||||||
}
|
|
||||||
|
|
||||||
public function initialize()
|
|
||||||
{
|
|
||||||
parent::initialize();
|
|
||||||
|
|
||||||
$this->addBehavior(
|
|
||||||
new SoftDelete([
|
|
||||||
'field' => 'deleted',
|
|
||||||
'value' => 1,
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function beforeCreate()
|
public function beforeCreate()
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Caches\MaxUploadId as MaxUploadIdCache;
|
||||||
use Phalcon\Mvc\Model\Behavior\SoftDelete;
|
use Phalcon\Mvc\Model\Behavior\SoftDelete;
|
||||||
|
|
||||||
class Upload extends Model
|
class Upload extends Model
|
||||||
@ -13,7 +14,7 @@ class Upload extends Model
|
|||||||
const TYPE_COVER_IMG = 1; // 封面图
|
const TYPE_COVER_IMG = 1; // 封面图
|
||||||
const TYPE_CONTENT_IMG = 2; // 内容图
|
const TYPE_CONTENT_IMG = 2; // 内容图
|
||||||
const TYPE_AVATAR_IMG = 3; // 头像
|
const TYPE_AVATAR_IMG = 3; // 头像
|
||||||
const TYPE_COURSE_RES = 4; // 课件资源
|
const TYPE_RESOURCE = 4; // 课件资源
|
||||||
const TYPE_IM_IMG = 5; // IM图片
|
const TYPE_IM_IMG = 5; // IM图片
|
||||||
const TYPE_IM_FILE = 6; // IM文件
|
const TYPE_IM_FILE = 6; // IM文件
|
||||||
|
|
||||||
@ -114,4 +115,11 @@ class Upload extends Model
|
|||||||
$this->update_time = time();
|
$this->update_time = time();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function afterCreate()
|
||||||
|
{
|
||||||
|
$cache = new MaxUploadIdCache();
|
||||||
|
|
||||||
|
$cache->rebuild();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,12 @@ class Volt extends Provider
|
|||||||
|
|
||||||
$compiler = $volt->getCompiler();
|
$compiler = $volt->getCompiler();
|
||||||
|
|
||||||
$compiler->addFunction('site_setting', function ($resolvedArgs) {
|
$compiler->addFunction('config', function ($resolvedArgs) {
|
||||||
return 'kg_site_setting(' . $resolvedArgs . ')';
|
return 'kg_config(' . $resolvedArgs . ')';
|
||||||
|
});
|
||||||
|
|
||||||
|
$compiler->addFunction('setting', function ($resolvedArgs) {
|
||||||
|
return 'kg_setting(' . $resolvedArgs . ')';
|
||||||
});
|
});
|
||||||
|
|
||||||
$compiler->addFunction('full_url', function ($resolvedArgs) {
|
$compiler->addFunction('full_url', function ($resolvedArgs) {
|
||||||
@ -62,6 +66,10 @@ class Volt extends Provider
|
|||||||
return 'kg_human_number(' . $resolvedArgs . ')';
|
return 'kg_human_number(' . $resolvedArgs . ')';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$compiler->addFilter('human_size', function ($resolvedArgs) {
|
||||||
|
return 'kg_human_size(' . $resolvedArgs . ')';
|
||||||
|
});
|
||||||
|
|
||||||
$compiler->addFilter('time_ago', function ($resolvedArgs) {
|
$compiler->addFilter('time_ago', function ($resolvedArgs) {
|
||||||
return 'kg_time_ago(' . $resolvedArgs . ')';
|
return 'kg_time_ago(' . $resolvedArgs . ')';
|
||||||
});
|
});
|
||||||
|
@ -63,7 +63,7 @@ class Audit extends Repository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param int $id
|
||||||
* @return AuditModel|Model|bool
|
* @return AuditModel|Model|bool
|
||||||
*/
|
*/
|
||||||
public function findById($id)
|
public function findById($id)
|
||||||
|
@ -8,7 +8,6 @@ use App\Models\ChapterLive as ChapterLiveModel;
|
|||||||
use App\Models\ChapterRead as ChapterReadModel;
|
use App\Models\ChapterRead as ChapterReadModel;
|
||||||
use App\Models\ChapterUser as ChapterUserModel;
|
use App\Models\ChapterUser as ChapterUserModel;
|
||||||
use App\Models\ChapterVod as ChapterVodModel;
|
use App\Models\ChapterVod as ChapterVodModel;
|
||||||
use App\Models\Comment as CommentModel;
|
|
||||||
use Phalcon\Mvc\Model;
|
use Phalcon\Mvc\Model;
|
||||||
use Phalcon\Mvc\Model\Resultset;
|
use Phalcon\Mvc\Model\Resultset;
|
||||||
use Phalcon\Mvc\Model\ResultsetInterface;
|
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)
|
public function countLikes($chapterId)
|
||||||
{
|
{
|
||||||
return (int)ChapterLikeModel::count([
|
return (int)ChapterLikeModel::count([
|
||||||
|
30
app/Repos/Online.php
Normal file
30
app/Repos/Online.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repos;
|
||||||
|
|
||||||
|
use App\Models\Online as OnlineModel;
|
||||||
|
use Phalcon\Mvc\Model;
|
||||||
|
|
||||||
|
class Online extends Repository
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $userId
|
||||||
|
* @param string $activeDate
|
||||||
|
* @return OnlineModel|Model|bool
|
||||||
|
*/
|
||||||
|
public function findByUserDate($userId, $activeDate)
|
||||||
|
{
|
||||||
|
$activeTime = strtotime($activeDate);
|
||||||
|
|
||||||
|
return OnlineModel::findFirst([
|
||||||
|
'conditions' => 'user_id = ?1 AND active_time BETWEEN ?2 AND ?3',
|
||||||
|
'bind' => [
|
||||||
|
1 => $userId,
|
||||||
|
2 => $activeTime,
|
||||||
|
3 => $activeTime + 86400,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
139
app/Repos/Stat.php
Normal file
139
app/Repos/Stat.php
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repos;
|
||||||
|
|
||||||
|
use App\Models\Online as OnlineModel;
|
||||||
|
use App\Models\Order as OrderModel;
|
||||||
|
use App\Models\OrderStatus as OrderStatusModel;
|
||||||
|
use App\Models\Refund as RefundModel;
|
||||||
|
use App\Models\User as UserModel;
|
||||||
|
use Phalcon\Mvc\Model\Resultset;
|
||||||
|
use Phalcon\Mvc\Model\ResultsetInterface;
|
||||||
|
|
||||||
|
class Stat extends Repository
|
||||||
|
{
|
||||||
|
|
||||||
|
public function countDailyRegisteredUser($date)
|
||||||
|
{
|
||||||
|
$startTime = strtotime($date);
|
||||||
|
|
||||||
|
$endTime = $startTime + 86400;
|
||||||
|
|
||||||
|
return (int)UserModel::count([
|
||||||
|
'conditions' => 'create_time BETWEEN :start_time: AND :end_time:',
|
||||||
|
'bind' => ['start_time' => $startTime, 'end_time' => $endTime],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countDailyOnlineUser($date)
|
||||||
|
{
|
||||||
|
$startTime = strtotime($date);
|
||||||
|
|
||||||
|
$endTime = $startTime + 86400;
|
||||||
|
|
||||||
|
return (int)OnlineModel::count([
|
||||||
|
'conditions' => 'active_time BETWEEN :start_time: AND :end_time:',
|
||||||
|
'bind' => ['start_time' => $startTime, 'end_time' => $endTime],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countDailySales($date)
|
||||||
|
{
|
||||||
|
$sql = "SELECT count(*) AS total_count FROM %s AS os JOIN %s AS o ON os.order_id = o.id ";
|
||||||
|
|
||||||
|
$sql .= "WHERE os.status = ?1 AND o.create_time BETWEEN ?2 AND ?3";
|
||||||
|
|
||||||
|
$phql = sprintf($sql, OrderStatusModel::class, OrderModel::class);
|
||||||
|
|
||||||
|
$startTime = strtotime($date);
|
||||||
|
|
||||||
|
$endTime = $startTime + 86400;
|
||||||
|
|
||||||
|
$result = $this->modelsManager->executeQuery($phql, [
|
||||||
|
1 => OrderModel::STATUS_FINISHED,
|
||||||
|
2 => $startTime,
|
||||||
|
3 => $endTime,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (float)$result[0]['total_count'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countDailyRefunds($date)
|
||||||
|
{
|
||||||
|
$startTime = strtotime($date);
|
||||||
|
|
||||||
|
$endTime = $startTime + 86400;
|
||||||
|
|
||||||
|
return (int)RefundModel::count([
|
||||||
|
'conditions' => 'status = ?1 AND create_time BETWEEN ?2 AND ?3',
|
||||||
|
'bind' => [
|
||||||
|
1 => RefundModel::STATUS_FINISHED,
|
||||||
|
2 => $startTime,
|
||||||
|
3 => $endTime,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sumDailySales($date)
|
||||||
|
{
|
||||||
|
$sql = "SELECT sum(o.amount) AS total_amount FROM %s AS os JOIN %s AS o ON os.order_id = o.id ";
|
||||||
|
|
||||||
|
$sql .= "WHERE os.status = ?1 AND o.create_time BETWEEN ?2 AND ?3";
|
||||||
|
|
||||||
|
$phql = sprintf($sql, OrderStatusModel::class, OrderModel::class);
|
||||||
|
|
||||||
|
$startTime = strtotime($date);
|
||||||
|
|
||||||
|
$endTime = $startTime + 86400;
|
||||||
|
|
||||||
|
$result = $this->modelsManager->executeQuery($phql, [
|
||||||
|
1 => OrderModel::STATUS_FINISHED,
|
||||||
|
2 => $startTime,
|
||||||
|
3 => $endTime,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (float)$result[0]['total_amount'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sumDailyRefunds($date)
|
||||||
|
{
|
||||||
|
$startTime = strtotime($date);
|
||||||
|
|
||||||
|
$endTime = $startTime + 86400;
|
||||||
|
|
||||||
|
return (float)RefundModel::sum([
|
||||||
|
'column' => 'amount',
|
||||||
|
'conditions' => 'status = ?1 AND create_time BETWEEN ?2 AND ?3',
|
||||||
|
'bind' => [
|
||||||
|
1 => RefundModel::STATUS_FINISHED,
|
||||||
|
2 => $startTime,
|
||||||
|
3 => $endTime,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $type
|
||||||
|
* @param int $year
|
||||||
|
* @param int $month
|
||||||
|
* @return ResultsetInterface|Resultset|OrderModel[]
|
||||||
|
*/
|
||||||
|
public function findMonthlyOrders($type, $year, $month)
|
||||||
|
{
|
||||||
|
$startTime = strtotime("{$year}-{$month}");
|
||||||
|
|
||||||
|
$endTime = strtotime('+1 month', $startTime);
|
||||||
|
|
||||||
|
$status = OrderModel::STATUS_FINISHED;
|
||||||
|
|
||||||
|
return $this->modelsManager->createBuilder()
|
||||||
|
->addFrom(OrderStatusModel::class, 'os')
|
||||||
|
->join(OrderModel::class, 'os.order_id = o.id', 'o')
|
||||||
|
->columns('o.*')
|
||||||
|
->where('o.item_type = :type:', ['type' => $type])
|
||||||
|
->andWhere('os.status = :status:', ['status' => $status])
|
||||||
|
->betweenWhere('o.create_time', $startTime, $endTime)
|
||||||
|
->getQuery()->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,6 +4,8 @@ namespace App\Repos;
|
|||||||
|
|
||||||
use App\Models\Upload as UploadModel;
|
use App\Models\Upload as UploadModel;
|
||||||
use Phalcon\Mvc\Model;
|
use Phalcon\Mvc\Model;
|
||||||
|
use Phalcon\Mvc\Model\Resultset;
|
||||||
|
use Phalcon\Mvc\Model\ResultsetInterface;
|
||||||
|
|
||||||
class Upload extends Repository
|
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,
|
'summary' => $chapter->summary,
|
||||||
'model' => $chapter->model,
|
'model' => $chapter->model,
|
||||||
'play_urls' => $playUrls,
|
'play_urls' => $playUrls,
|
||||||
|
'resource_count' => $chapter->resource_count,
|
||||||
'user_count' => $chapter->user_count,
|
'user_count' => $chapter->user_count,
|
||||||
'like_count' => $chapter->like_count,
|
'like_count' => $chapter->like_count,
|
||||||
];
|
];
|
||||||
@ -97,6 +98,7 @@ class BasicInfo extends Service
|
|||||||
'start_time' => $live->start_time,
|
'start_time' => $live->start_time,
|
||||||
'end_time' => $live->end_time,
|
'end_time' => $live->end_time,
|
||||||
'status' => $live->status,
|
'status' => $live->status,
|
||||||
|
'resource_count' => $chapter->resource_count,
|
||||||
'user_count' => $chapter->user_count,
|
'user_count' => $chapter->user_count,
|
||||||
'like_count' => $chapter->like_count,
|
'like_count' => $chapter->like_count,
|
||||||
];
|
];
|
||||||
@ -114,6 +116,7 @@ class BasicInfo extends Service
|
|||||||
'summary' => $chapter->summary,
|
'summary' => $chapter->summary,
|
||||||
'model' => $chapter->model,
|
'model' => $chapter->model,
|
||||||
'content' => $read->content,
|
'content' => $read->content,
|
||||||
|
'resource_count' => $chapter->resource_count,
|
||||||
'user_count' => $chapter->user_count,
|
'user_count' => $chapter->user_count,
|
||||||
'like_count' => $chapter->like_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
|
* @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 Phalcon\Logger\Adapter\File as FileLogger;
|
||||||
use Qcloud\Cos\Client as CosClient;
|
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
|
class Storage extends Service
|
||||||
{
|
{
|
||||||
@ -32,6 +39,74 @@ class Storage extends Service
|
|||||||
$this->client = $this->getCosClient();
|
$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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传字符内容
|
* 上传字符内容
|
||||||
*
|
*
|
||||||
|
@ -6,7 +6,8 @@ use App\Models\User as UserModel;
|
|||||||
use App\Repos\User as UserRepo;
|
use App\Repos\User as UserRepo;
|
||||||
use App\Services\Auth as AuthService;
|
use App\Services\Auth as AuthService;
|
||||||
use App\Validators\Validator as AppValidator;
|
use App\Validators\Validator as AppValidator;
|
||||||
use Phalcon\Di;
|
use Phalcon\Di as Di;
|
||||||
|
use Phalcon\Events\Manager as EventsManager;
|
||||||
|
|
||||||
trait Auth
|
trait Auth
|
||||||
{
|
{
|
||||||
@ -24,7 +25,16 @@ trait Auth
|
|||||||
|
|
||||||
$userRepo = new UserRepo();
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
return $userRepo->findById($authUser['id']);
|
$user = $userRepo->findById($authUser['id']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var EventsManager $eventsManager
|
||||||
|
*/
|
||||||
|
$eventsManager = Di::getDefault()->getShared('eventsManager');
|
||||||
|
|
||||||
|
$eventsManager->fire('user:online', $this, $user);
|
||||||
|
|
||||||
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,13 +50,7 @@ trait Auth
|
|||||||
|
|
||||||
$userRepo = new UserRepo();
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
$user = $userRepo->findById($authUser['id']);
|
return $userRepo->findById($authUser['id']);
|
||||||
|
|
||||||
if (time() - $user->active_time > 600) {
|
|
||||||
$user->update(['active_time' => time()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
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\ServiceUnavailable as ServiceUnavailableException;
|
||||||
use App\Exceptions\Unauthorized as UnauthorizedException;
|
use App\Exceptions\Unauthorized as UnauthorizedException;
|
||||||
use App\Library\Logger as AppLogger;
|
use App\Library\Logger as AppLogger;
|
||||||
|
use Phalcon\Config;
|
||||||
use Phalcon\Mvc\User\Component;
|
use Phalcon\Mvc\User\Component;
|
||||||
|
|
||||||
class HttpErrorHandler extends 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()
|
protected function getLogger()
|
||||||
{
|
{
|
||||||
$logger = new AppLogger();
|
$logger = new AppLogger();
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Listeners\Pay;
|
use App\Listeners\Pay;
|
||||||
|
use App\Listeners\User;
|
||||||
use App\Listeners\UserDailyCounter;
|
use App\Listeners\UserDailyCounter;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'pay' => Pay::class,
|
'pay' => Pay::class,
|
||||||
|
'user' => User::class,
|
||||||
'userDailyCounter' => UserDailyCounter::class,
|
'userDailyCounter' => UserDailyCounter::class,
|
||||||
];
|
];
|
@ -640,7 +640,7 @@ class InitTable extends Phinx\Migration\AbstractMigration
|
|||||||
'unique' => false,
|
'unique' => false,
|
||||||
])
|
])
|
||||||
->create();
|
->create();
|
||||||
$this->table('kg_chapter_resource', [
|
$this->table('kg_resource', [
|
||||||
'id' => false,
|
'id' => false,
|
||||||
'primary_key' => ['id'],
|
'primary_key' => ['id'],
|
||||||
'engine' => 'InnoDB',
|
'engine' => 'InnoDB',
|
||||||
@ -676,13 +676,6 @@ class InitTable extends Phinx\Migration\AbstractMigration
|
|||||||
'comment' => '上传编号',
|
'comment' => '上传编号',
|
||||||
'after' => 'chapter_id',
|
'after' => 'chapter_id',
|
||||||
])
|
])
|
||||||
->addColumn('deleted', 'integer', [
|
|
||||||
'null' => false,
|
|
||||||
'default' => '0',
|
|
||||||
'limit' => MysqlAdapter::INT_REGULAR,
|
|
||||||
'comment' => '删除标识',
|
|
||||||
'after' => 'upload_id',
|
|
||||||
])
|
|
||||||
->addColumn('create_time', 'integer', [
|
->addColumn('create_time', 'integer', [
|
||||||
'null' => false,
|
'null' => false,
|
||||||
'default' => '0',
|
'default' => '0',
|
||||||
@ -2517,7 +2510,7 @@ class InitTable extends Phinx\Migration\AbstractMigration
|
|||||||
->addColumn('client_ip', 'string', [
|
->addColumn('client_ip', 'string', [
|
||||||
'null' => false,
|
'null' => false,
|
||||||
'default' => '',
|
'default' => '',
|
||||||
'limit' => 30,
|
'limit' => 64,
|
||||||
'collation' => 'utf8mb4_general_ci',
|
'collation' => 'utf8mb4_general_ci',
|
||||||
'encoding' => 'utf8mb4',
|
'encoding' => 'utf8mb4',
|
||||||
'comment' => '终端IP',
|
'comment' => '终端IP',
|
||||||
|
107
db/migrations/20201004095647_create_online_table.php
Normal file
107
db/migrations/20201004095647_create_online_table.php
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Phinx\Db\Adapter\MysqlAdapter;
|
||||||
|
|
||||||
|
class CreateOnlineTable extends Phinx\Migration\AbstractMigration
|
||||||
|
{
|
||||||
|
public function change()
|
||||||
|
{
|
||||||
|
$this->table('kg_online', [
|
||||||
|
'id' => false,
|
||||||
|
'primary_key' => ['id'],
|
||||||
|
'engine' => 'InnoDB',
|
||||||
|
'encoding' => 'utf8mb4',
|
||||||
|
'collation' => 'utf8mb4_general_ci',
|
||||||
|
'comment' => '',
|
||||||
|
'row_format' => 'DYNAMIC',
|
||||||
|
])
|
||||||
|
->addColumn('id', 'integer', [
|
||||||
|
'null' => false,
|
||||||
|
'limit' => MysqlAdapter::INT_REGULAR,
|
||||||
|
'identity' => 'enable',
|
||||||
|
'comment' => '主键编号',
|
||||||
|
])
|
||||||
|
->addColumn('user_id', 'integer', [
|
||||||
|
'null' => false,
|
||||||
|
'default' => '0',
|
||||||
|
'limit' => MysqlAdapter::INT_REGULAR,
|
||||||
|
'comment' => '用户编号',
|
||||||
|
'after' => 'id',
|
||||||
|
])
|
||||||
|
->addColumn('client_type', 'integer', [
|
||||||
|
'null' => false,
|
||||||
|
'default' => '1',
|
||||||
|
'limit' => MysqlAdapter::INT_REGULAR,
|
||||||
|
'comment' => '终端类型',
|
||||||
|
'after' => 'user_id',
|
||||||
|
])
|
||||||
|
->addColumn('client_ip', 'string', [
|
||||||
|
'null' => false,
|
||||||
|
'default' => '',
|
||||||
|
'limit' => 64,
|
||||||
|
'collation' => 'utf8mb4_general_ci',
|
||||||
|
'encoding' => 'utf8mb4',
|
||||||
|
'comment' => '终端IP',
|
||||||
|
'after' => 'client_type',
|
||||||
|
])
|
||||||
|
->addColumn('active_time', 'integer', [
|
||||||
|
'null' => false,
|
||||||
|
'default' => '0',
|
||||||
|
'limit' => MysqlAdapter::INT_REGULAR,
|
||||||
|
'comment' => '活跃时间',
|
||||||
|
'after' => 'client_ip',
|
||||||
|
])
|
||||||
|
->addColumn('create_time', 'integer', [
|
||||||
|
'null' => false,
|
||||||
|
'default' => '0',
|
||||||
|
'limit' => MysqlAdapter::INT_REGULAR,
|
||||||
|
'comment' => '创建时间',
|
||||||
|
'after' => 'active_time',
|
||||||
|
])
|
||||||
|
->addColumn('update_time', 'integer', [
|
||||||
|
'null' => false,
|
||||||
|
'default' => '0',
|
||||||
|
'limit' => MysqlAdapter::INT_REGULAR,
|
||||||
|
'comment' => '更新时间',
|
||||||
|
'after' => 'create_time',
|
||||||
|
])
|
||||||
|
->addIndex(['active_time'], [
|
||||||
|
'name' => 'active_time',
|
||||||
|
'unique' => false,
|
||||||
|
])
|
||||||
|
->addIndex(['user_id'], [
|
||||||
|
'name' => 'user_id',
|
||||||
|
'unique' => false,
|
||||||
|
])
|
||||||
|
->create();
|
||||||
|
$this->table('kg_task', [
|
||||||
|
'id' => false,
|
||||||
|
'primary_key' => ['id'],
|
||||||
|
'engine' => 'InnoDB',
|
||||||
|
'encoding' => 'utf8mb4',
|
||||||
|
'collation' => 'utf8mb4_general_ci',
|
||||||
|
'comment' => '',
|
||||||
|
'row_format' => 'DYNAMIC',
|
||||||
|
])
|
||||||
|
->save();
|
||||||
|
$this->table('kg_trade', [
|
||||||
|
'id' => false,
|
||||||
|
'primary_key' => ['id'],
|
||||||
|
'engine' => 'InnoDB',
|
||||||
|
'encoding' => 'utf8mb4',
|
||||||
|
'collation' => 'utf8mb4_general_ci',
|
||||||
|
'comment' => '',
|
||||||
|
'row_format' => 'DYNAMIC',
|
||||||
|
])
|
||||||
|
->changeColumn('channel_sn', 'string', [
|
||||||
|
'null' => false,
|
||||||
|
'default' => '',
|
||||||
|
'limit' => 64,
|
||||||
|
'collation' => 'utf8mb4_general_ci',
|
||||||
|
'encoding' => 'utf8mb4',
|
||||||
|
'comment' => '平台序号',
|
||||||
|
'after' => 'channel',
|
||||||
|
])
|
||||||
|
->save();
|
||||||
|
}
|
||||||
|
}
|
14784
db/migrations/schema.php
14784
db/migrations/schema.php
File diff suppressed because it is too large
Load Diff
@ -60,10 +60,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.kg-login-wrap {
|
.kg-login-wrap {
|
||||||
width: 400px;
|
width: 500px;
|
||||||
margin: 100px auto;
|
margin: 100px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.kg-login-wrap .layui-card-header {
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.kg-login-form {
|
.kg-login-form {
|
||||||
padding: 15px 10px 5px 10px;
|
padding: 15px 10px 5px 10px;
|
||||||
}
|
}
|
||||||
@ -258,3 +265,20 @@ img.kg-qrcode {
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: rgb(0, 150, 136);
|
color: rgb(0, 150, 136);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.kg-search-form {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-search-form .layui-form-label {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-search-form .layui-input-inline:last-child {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kg-chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 480px;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -6,7 +6,7 @@ layui.use(['jquery', 'element'], function () {
|
|||||||
var getSignature = function () {
|
var getSignature = function () {
|
||||||
var result = '';
|
var result = '';
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'GET',
|
||||||
url: '/admin/vod/upload/sign',
|
url: '/admin/vod/upload/sign',
|
||||||
async: false,
|
async: false,
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
|
@ -43,24 +43,24 @@ function xmCourse(data, url) {
|
|||||||
{field: 'title', title: '标题', width: 390},
|
{field: 'title', title: '标题', width: 390},
|
||||||
{
|
{
|
||||||
field: 'model', title: '类型', width: 50, templet: function (d) {
|
field: 'model', title: '类型', width: 50, templet: function (d) {
|
||||||
if (d.model === 'vod') {
|
if (d.model === 1) {
|
||||||
return '点播';
|
return '点播';
|
||||||
} else if (d.model === 'live') {
|
} else if (d.model === 2) {
|
||||||
return '直播';
|
return '直播';
|
||||||
} else if (d.model === 'read') {
|
} else if (d.model === 3) {
|
||||||
return '图文';
|
return '图文';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'level', title: '难度', width: 50, templet: function (d) {
|
field: 'level', title: '难度', width: 50, templet: function (d) {
|
||||||
if (d.level === 'entry') {
|
if (d.level === 1) {
|
||||||
return '入门';
|
return '入门';
|
||||||
} else if (d.level === 'junior') {
|
} else if (d.level === 2) {
|
||||||
return '初级';
|
return '初级';
|
||||||
} else if (d.level === 'medium') {
|
} else if (d.level === 3) {
|
||||||
return '中级';
|
return '中级';
|
||||||
} else if (d.level === 'senior') {
|
} else if (d.level === 4) {
|
||||||
return '高级';
|
return '高级';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@
|
|||||||
#main {
|
#main {
|
||||||
margin-top: 80px;
|
margin-top: 80px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
min-height: 560px;
|
min-height: 760px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#footer {
|
#footer {
|
||||||
@ -1161,18 +1161,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.account-form .verify-input-inline {
|
.account-form .verify-input-inline {
|
||||||
display: inline-block;
|
width: 280px;
|
||||||
width: 283px;
|
margin-right: 10px;
|
||||||
margin-right: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.account-form .verify-btn-inline {
|
.account-form .verify-btn-inline {
|
||||||
display: inline-block;
|
width: 110px;
|
||||||
vertical-align: top;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.verify-btn-inline button {
|
.verify-btn-inline button {
|
||||||
min-width: 108px;
|
min-width: 100px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-tab {
|
.login-tab {
|
||||||
@ -1500,14 +1500,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.consult-info .item {
|
.consult-info .item {
|
||||||
clear: both;
|
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
|
line-height: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.consult-info .item .label {
|
.consult-info .item .label {
|
||||||
float: left;
|
float: left;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 32px;
|
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1523,6 +1524,19 @@
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.security-form {
|
||||||
|
width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-form .verify-input-inline {
|
||||||
|
width: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-form .verify-btn-inline {
|
||||||
|
width: 120px;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.security-item {
|
.security-item {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user