diff --git a/.gitignore b/.gitignore index 2f894c4f..0da71a39 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ /config/xs.user.ini /config/alipay/*.crt /config/wxpay/*.pem +/db/migrations/schema.php /public/robots.txt /public/sitemap.xml *KgTest* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..95530986 --- /dev/null +++ b/CHANGELOG.md @@ -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) + +前台功能: + +- 注册、登录、忘记密码 +- 首页:轮播、新上课程、免费课程、会员课程 +- 课程列表:多维度筛选,多维度排序 +- 课程详情:章节,咨询,评价,相关课程,推荐课程,课程套餐 +- 课时详情:点播,直播,图文 +- 购买支付:课程,套餐,赞赏,会员 +- 教师列表 +- 群组列表 +- 即时通讯 +- 在线客服 +- 全文检索:课程、群组、用户 +- 个人主页:我的课程,我的收藏,我的好友,我的群组 +- 会员中心:我的课程,我的收藏,我的咨询,我的评价,我的好友,我的群组,我的订单,我的退款,个人信息,账户安全 +- 教学中心 :我的课程,我的直播,我的咨询 + +后台功能: + +- 课程管理:课程列表,课程搜索,添加课程,编辑课程,删除课程,课程分类 +- 套餐管理:套餐列表,添加套餐,编辑套餐,删除套餐 +- 话题管理:话题列表,添加话题,编辑话题,删除话题 +- 单页管理:单页列表,添加单页,编辑单页,删除单页 +- 帮助管理:帮助列表,添加帮助,编辑帮助,删除帮助,帮助分类 +- 学员管理:学员列表,搜索学员,添加学员,编辑学员,学习记录 +- 咨询管理:咨询列表,搜索咨询,编辑咨询,删除咨询 +- 评价管理:评价列表,搜索评价,编辑评价,删除评价 +- 群组管理:群组列表,搜索群组,编辑群组,删除群组 +- 轮播管理:轮播列表,编辑轮播,删除轮播 +- 导航管理:导航列表,编辑导航,删除导航 +- 订单管理:订单列表,搜索订单,订单详情 +- 交易管理:交易列表,搜索交易,交易详情 +- 退款管理:退款列表,搜索退款,退款详情,退款审核 +- 用户管理:用户列表,编辑用户,添加用户 +- 角色管理:角色列表,编辑角色,删除角色 +- 操作记录:记录列表,搜索记录,记录详情 +- 系统配置:网站,密钥,存储,点播,直播,短信,邮件,验证码,支付,会员,微聊 diff --git a/app/Builders/ResourceList.php b/app/Builders/ResourceList.php new file mode 100644 index 00000000..ef7f78ec --- /dev/null +++ b/app/Builders/ResourceList.php @@ -0,0 +1,40 @@ +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; + } + +} diff --git a/app/Caches/MaxUploadId.php b/app/Caches/MaxUploadId.php new file mode 100644 index 00000000..1726ecea --- /dev/null +++ b/app/Caches/MaxUploadId.php @@ -0,0 +1,29 @@ +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; + } + +} diff --git a/app/Caches/SiteTodayStat.php b/app/Caches/SiteTodayStat.php index dde4824b..13c0ae66 100644 --- a/app/Caches/SiteTodayStat.php +++ b/app/Caches/SiteTodayStat.php @@ -2,8 +2,7 @@ namespace App\Caches; -use App\Models\Order as OrderModel; -use App\Models\User as UserModel; +use App\Repos\Stat as StatRepo; class SiteTodayStat extends Cache { @@ -22,42 +21,21 @@ class SiteTodayStat extends Cache 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 [ - 'user_count' => $this->countUsers(), - 'order_count' => $this->countOrders(), - 'sale_amount' => $this->sumSales(), + 'sale_count' => $saleCount, + 'sale_amount' => $saleAmount, + '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, - ], - ]); - } - } diff --git a/app/Http/Admin/Controllers/ChapterController.php b/app/Http/Admin/Controllers/ChapterController.php index d941641b..9b0ff8e7 100644 --- a/app/Http/Admin/Controllers/ChapterController.php +++ b/app/Http/Admin/Controllers/ChapterController.php @@ -7,6 +7,7 @@ use App\Http\Admin\Services\ChapterContent as ChapterContentService; use App\Http\Admin\Services\Course as CourseService; use App\Models\ChapterLive as ChapterLiveModel; use App\Models\Course as CourseModel; +use Phalcon\Mvc\View; /** * @RoutePrefix("/admin/chapter") @@ -14,6 +15,19 @@ use App\Models\Course as CourseModel; class ChapterController extends Controller { + /** + * @Get("/{id:[0-9]+}/resources", name="admin.chapter.resources") + */ + public function resourcesAction($id) + { + $chapterService = new ChapterService(); + + $resources = $chapterService->getResources($id); + + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + $this->view->setVar('resources', $resources); + } + /** * @Get("/{id:[0-9]+}/lessons", name="admin.chapter.lessons") */ @@ -96,6 +110,12 @@ class ChapterController extends Controller $this->view->pick('chapter/edit_lesson'); + $resources = $chapterService->getResources($chapter->id); + + $cos = $chapterService->getSettings('cos'); + + $this->view->setVar('cos', $cos); + switch ($course->model) { case CourseModel::MODEL_VOD: $vod = $contentService->getChapterVod($chapter->id); diff --git a/app/Http/Admin/Controllers/Controller.php b/app/Http/Admin/Controllers/Controller.php index b38fabe5..058bfa77 100644 --- a/app/Http/Admin/Controllers/Controller.php +++ b/app/Http/Admin/Controllers/Controller.php @@ -49,7 +49,7 @@ class Controller extends \Phalcon\Mvc\Controller * 特例白名单 */ $whitelist = [ - 'controllers' => ['public', 'index', 'vod', 'test', 'xm_course'], + 'controllers' => ['public', 'index', 'vod', 'upload', 'test', 'xm_course'], 'routes' => ['admin.package.guiding'], ]; diff --git a/app/Http/Admin/Controllers/ResourceController.php b/app/Http/Admin/Controllers/ResourceController.php new file mode 100644 index 00000000..c8422902 --- /dev/null +++ b/app/Http/Admin/Controllers/ResourceController.php @@ -0,0 +1,49 @@ +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' => '删除资源成功']); + } + +} diff --git a/app/Http/Admin/Controllers/StatController.php b/app/Http/Admin/Controllers/StatController.php new file mode 100644 index 00000000..cb80569b --- /dev/null +++ b/app/Http/Admin/Controllers/StatController.php @@ -0,0 +1,98 @@ +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); + } + +} diff --git a/app/Http/Admin/Controllers/UploadController.php b/app/Http/Admin/Controllers/UploadController.php index 86709468..d180c70a 100644 --- a/app/Http/Admin/Controllers/UploadController.php +++ b/app/Http/Admin/Controllers/UploadController.php @@ -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); + } + } diff --git a/app/Http/Admin/Controllers/UserController.php b/app/Http/Admin/Controllers/UserController.php index e90039d3..d1482164 100644 --- a/app/Http/Admin/Controllers/UserController.php +++ b/app/Http/Admin/Controllers/UserController.php @@ -84,7 +84,7 @@ class UserController extends Controller $roles = $userService->getRoles(); if ($user->admin_role == RoleModel::ROLE_ROOT) { - $this->response->redirect(['action' => 'list']); + $this->response->redirect(['for' => 'admin.user.list']); } $this->view->setVar('user', $user); diff --git a/app/Http/Admin/Controllers/VodController.php b/app/Http/Admin/Controllers/VodController.php index d2ffdfa5..4ee0f40b 100644 --- a/app/Http/Admin/Controllers/VodController.php +++ b/app/Http/Admin/Controllers/VodController.php @@ -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() { diff --git a/app/Http/Admin/Services/AuthNode.php b/app/Http/Admin/Services/AuthNode.php index 44482bb0..86b54761 100644 --- a/app/Http/Admin/Services/AuthNode.php +++ b/app/Http/Admin/Services/AuthNode.php @@ -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', + ], + ], + ], ], ]; } diff --git a/app/Http/Admin/Services/Chapter.php b/app/Http/Admin/Services/Chapter.php index 4eeb8264..de9372e4 100644 --- a/app/Http/Admin/Services/Chapter.php +++ b/app/Http/Admin/Services/Chapter.php @@ -2,6 +2,7 @@ namespace App\Http\Admin\Services; +use App\Builders\ResourceList as ResourceListBuilder; use App\Caches\Chapter as ChapterCache; use App\Caches\CourseChapterList as CatalogCache; use App\Models\Chapter as ChapterModel; @@ -11,12 +12,32 @@ use App\Models\ChapterVod as ChapterVodModel; use App\Models\Course as CourseModel; use App\Repos\Chapter as ChapterRepo; use App\Repos\Course as CourseRepo; +use App\Repos\Resource as ResourceRepo; use App\Services\CourseStat as CourseStatService; use App\Validators\Chapter as ChapterValidator; class Chapter extends Service { + public function getResources($id) + { + $resourceRepo = new ResourceRepo(); + + $resources = $resourceRepo->findByChapterId($id); + + if ($resources->count() == 0) { + return []; + } + + $builder = new ResourceListBuilder(); + + $items = $resources->toArray(); + + $items = $builder->handleUploads($items); + + return $builder->objects($items); + } + public function getLessons($parentId) { $deleted = $this->request->getQuery('deleted', 'int', 0); diff --git a/app/Http/Admin/Services/Resource.php b/app/Http/Admin/Services/Resource.php new file mode 100644 index 00000000..48aef8f8 --- /dev/null +++ b/app/Http/Admin/Services/Resource.php @@ -0,0 +1,119 @@ +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); + } + +} diff --git a/app/Http/Admin/Services/Role.php b/app/Http/Admin/Services/Role.php index 3cf97cd8..82fde39a 100644 --- a/app/Http/Admin/Services/Role.php +++ b/app/Http/Admin/Services/Role.php @@ -148,6 +148,12 @@ class Role extends Service $list[] = 'admin.chapter.content'; } + if (array_intersect(['admin.chapter.add', 'admin.chapter.edit'], $routes)) { + $list[] = 'admin.resource.create'; + $list[] = 'admin.resource.update'; + $list[] = 'admin.resource.delete'; + } + if (in_array('admin.course.delete', $routes)) { $list[] = 'admin.chapter.delete'; $list[] = 'admin.chapter.restore'; diff --git a/app/Http/Admin/Services/Stat.php b/app/Http/Admin/Services/Stat.php new file mode 100644 index 00000000..bacb6858 --- /dev/null +++ b/app/Http/Admin/Services/Stat.php @@ -0,0 +1,405 @@ +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; + } + +} diff --git a/app/Http/Admin/Views/chapter/edit_lesson.volt b/app/Http/Admin/Views/chapter/edit_lesson.volt index 69bfd8d1..4f0c7dc6 100644 --- a/app/Http/Admin/Views/chapter/edit_lesson.volt +++ b/app/Http/Admin/Views/chapter/edit_lesson.volt @@ -20,6 +20,7 @@
名称 | +类型 | +大小 | +日期 | +操作 | +
---|---|---|---|---|
+ | {{ item.upload.mime }} | +{{ item.upload.size|human_size }} | +{{ date('Y-m-d H:i:s',item.create_time) }} | ++ 删除 + 下载 + | +
排序 | +名称 | +数量 | +金额 | +
---|---|---|---|
{{ loop.index }} | +{{ sale.title }} | +{{ sale.total_count }} | +{{ '¥%0.2f'|format(sale.total_amount) }} | +
- 类型:{{ item.client_type }} +类型:{{ client_type_info(item.client_type) }} |
{{ item.duration|duration }} | diff --git a/app/Http/Admin/Views/student/list.volt b/app/Http/Admin/Views/student/list.volt index 453db91d..bc673b7b 100644 --- a/app/Http/Admin/Views/student/list.volt +++ b/app/Http/Admin/Views/student/list.volt @@ -4,11 +4,11 @@ {%- macro source_type_info(value) %} {% if value == 1 %} - 免费 + 免费 {% elseif value == 2 %} 付费 {% elseif value == 3 %} - 导入 + 导入 {% endif %} {%- endmacro %} diff --git a/app/Http/Admin/Views/templates/layer.volt b/app/Http/Admin/Views/templates/layer.volt index 96287252..529288fe 100644 --- a/app/Http/Admin/Views/templates/layer.volt +++ b/app/Http/Admin/Views/templates/layer.volt @@ -1,7 +1,7 @@ - + diff --git a/app/Http/Admin/Views/templates/main.volt b/app/Http/Admin/Views/templates/main.volt index 5e222058..3c1345f1 100644 --- a/app/Http/Admin/Views/templates/main.volt +++ b/app/Http/Admin/Views/templates/main.volt @@ -1,7 +1,7 @@ - + diff --git a/app/Http/Admin/Views/trade/macro.volt b/app/Http/Admin/Views/trade/macro.volt index df25e586..331f7ad3 100644 --- a/app/Http/Admin/Views/trade/macro.volt +++ b/app/Http/Admin/Views/trade/macro.volt @@ -1,20 +1,20 @@ {%- macro channel_type(value) %} {% if value == 1 %} - 支付宝 + 支付宝 {% elseif value == 2 %} - 微信 + 微信 {% endif %} {%- endmacro %} {%- macro trade_status(value) %} {% if value == 1 %} - 待支付 + 待支付 {% elseif value == 2 %} - 已完成 + 已完成 {% elseif value == 3 %} - 已关闭 + 已关闭 {% elseif value == 4 %} - 已退款 + 已退款 {% endif %} {%- endmacro %} diff --git a/app/Http/Admin/Views/user/list.volt b/app/Http/Admin/Views/user/list.volt index 837e94cb..19eac204 100644 --- a/app/Http/Admin/Views/user/list.volt +++ b/app/Http/Admin/Views/user/list.volt @@ -4,25 +4,25 @@ {%- macro gender_info(value) %} {% if value == 1 %} - 男 + 男 {% elseif value == 2 %} - 女 + 女 {% elseif value == 3 %} - 密 + 密 {% endif %} {%- endmacro %} {%- macro edu_role_info(user) %} {% if user.edu_role.id == 1 %} - 学员 + 学员 {% elseif user.edu_role.id == 2 %} - 讲师 + 讲师 {% endif %} {%- endmacro %} {%- macro admin_role_info(user) %} - {% if user.admin_role.id %} - {{ user.admin_role.name }} + {% if user.admin_role.id > 0 %} + {{ user.admin_role.name }} {% endif %} {%- endmacro %} diff --git a/app/Http/Home/Controllers/AccountController.php b/app/Http/Home/Controllers/AccountController.php index a0a22076..4e2fc3d0 100644 --- a/app/Http/Home/Controllers/AccountController.php +++ b/app/Http/Home/Controllers/AccountController.php @@ -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\PasswordUpdate as PasswordUpdateService; use App\Services\Logic\Account\PhoneUpdate as PhoneUpdateService; -use Phalcon\Mvc\View; -/** - * @RoutePrefix("/account") - */ class AccountController extends Controller { @@ -24,12 +20,12 @@ class AccountController extends Controller $this->response->redirect('/'); } + $returnUrl = $this->request->getHTTPReferer(); + $service = new AccountService(); $captcha = $service->getSettings('captcha'); - $returnUrl = $this->request->getHTTPReferer(); - $this->view->setVar('return_url', $returnUrl); $this->view->setVar('captcha', $captcha); } @@ -137,60 +133,6 @@ class AccountController extends Controller $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") */ @@ -219,7 +161,12 @@ class AccountController extends Controller $service->handle(); - $content = ['msg' => '更新手机成功']; + $location = $this->url->get(['for' => 'home.uc.account']); + + $content = [ + 'location' => $location, + 'msg' => '更新手机成功', + ]; return $this->jsonSuccess($content); } @@ -233,7 +180,12 @@ class AccountController extends Controller $service->handle(); - $content = ['msg' => '更新邮箱成功']; + $location = $this->url->get(['for' => 'home.uc.account']); + + $content = [ + 'location' => $location, + 'msg' => '更新邮箱成功', + ]; return $this->jsonSuccess($content); } @@ -247,7 +199,12 @@ class AccountController extends Controller $service->handle(); - $content = ['msg' => '更新密码成功']; + $location = $this->url->get(['for' => 'home.uc.account']); + + $content = [ + 'location' => $location, + 'msg' => '更新密码成功', + ]; return $this->jsonSuccess($content); } diff --git a/app/Http/Home/Controllers/ChapterController.php b/app/Http/Home/Controllers/ChapterController.php index 4e46138e..dace273d 100644 --- a/app/Http/Home/Controllers/ChapterController.php +++ b/app/Http/Home/Controllers/ChapterController.php @@ -8,6 +8,7 @@ use App\Services\Logic\Chapter\ChapterInfo as ChapterInfoService; use App\Services\Logic\Chapter\ChapterLike as ChapterLikeService; use App\Services\Logic\Chapter\DanmuList as ChapterDanmuListService; use App\Services\Logic\Chapter\Learning as ChapterLearningService; +use App\Services\Logic\Chapter\ResourceList as ChapterResourceListService; use App\Services\Logic\Course\ChapterList as CourseChapterListService; /** @@ -16,6 +17,18 @@ use App\Services\Logic\Course\ChapterList as CourseChapterListService; class ChapterController extends Controller { + /** + * @Get("/{id:[0-9]+}/resources", name="home.chapter.resources") + */ + public function resourcesAction($id) + { + $service = new ChapterResourceListService(); + + $items = $service->handle($id); + + $this->view->setVar('items', $items); + } + /** * @Get("/{id:[0-9]+}", name="home.chapter.show") */ diff --git a/app/Http/Home/Controllers/ImController.php b/app/Http/Home/Controllers/ImController.php index a42f4066..f768edb4 100644 --- a/app/Http/Home/Controllers/ImController.php +++ b/app/Http/Home/Controllers/ImController.php @@ -24,7 +24,7 @@ class ImController extends Controller } /** - * @Get("/index", name="home.im.index") + * @Get("/", name="home.im.index") */ public function indexAction() { diff --git a/app/Http/Home/Controllers/PublicController.php b/app/Http/Home/Controllers/PublicController.php index bab9e469..63eb1e37 100644 --- a/app/Http/Home/Controllers/PublicController.php +++ b/app/Http/Home/Controllers/PublicController.php @@ -19,6 +19,31 @@ class PublicController extends \Phalcon\Mvc\Controller use ResponseTrait; use SecurityTrait; + /** + * @Get("/download/{md5}", name="home.download") + */ + public function downloadAction($md5) + { + $repo = new UploadRepo(); + + $file = $repo->findByMd5($md5); + + if ($file) { + + $service = new StorageService(); + + $location = $service->getFileUrl($file->path); + + $this->response->redirect($location, true); + + } else { + + $this->response->setStatusCode(404); + + return $this->response; + } + } + /** * @Get("/img/{id:[0-9]+}", name="home.img") */ diff --git a/app/Http/Home/Controllers/TeacherConsoleController.php b/app/Http/Home/Controllers/TeacherConsoleController.php index d98b46c7..53164fe3 100644 --- a/app/Http/Home/Controllers/TeacherConsoleController.php +++ b/app/Http/Home/Controllers/TeacherConsoleController.php @@ -1,6 +1,5 @@ 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('obs', $obs); } diff --git a/app/Http/Home/Controllers/UserConsoleController.php b/app/Http/Home/Controllers/UserConsoleController.php index dd814a93..5e159e0e 100644 --- a/app/Http/Home/Controllers/UserConsoleController.php +++ b/app/Http/Home/Controllers/UserConsoleController.php @@ -34,7 +34,7 @@ class UserConsoleController extends Controller } /** - * @Get("/index", name="home.uc.index") + * @Get("/", name="home.uc.index") */ public function indexAction() { @@ -61,9 +61,23 @@ class UserConsoleController extends Controller { $service = new AccountInfoService(); + $captcha = $service->getSettings('captcha'); + $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); } @@ -183,8 +197,10 @@ class UserConsoleController extends Controller $service->handle(); + $location = $this->url->get(['for' => 'home.uc.profile']); + $content = [ - 'location' => $this->request->getHTTPReferer(), + 'location' => $location, 'msg' => '更新资料成功', ]; diff --git a/app/Http/Home/Views/account/edit_email.volt b/app/Http/Home/Views/account/edit_email.volt deleted file mode 100644 index 9a9b1b09..00000000 --- a/app/Http/Home/Views/account/edit_email.volt +++ /dev/null @@ -1,42 +0,0 @@ -{% extends 'templates/layer.volt' %} - -{% block content %} - - - -{% endblock %} - -{% block include_js %} - - {{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }} - {{ js_include('home/js/captcha.verify.js') }} - -{% endblock %} \ No newline at end of file diff --git a/app/Http/Home/Views/account/edit_password.volt b/app/Http/Home/Views/account/edit_password.volt deleted file mode 100644 index b13a77d8..00000000 --- a/app/Http/Home/Views/account/edit_password.volt +++ /dev/null @@ -1,29 +0,0 @@ -{% extends 'templates/layer.volt' %} - -{% block content %} - - - -{% endblock %} \ No newline at end of file diff --git a/app/Http/Home/Views/account/edit_phone.volt b/app/Http/Home/Views/account/edit_phone.volt deleted file mode 100644 index d24685ca..00000000 --- a/app/Http/Home/Views/account/edit_phone.volt +++ /dev/null @@ -1,42 +0,0 @@ -{% extends 'templates/layer.volt' %} - -{% block content %} - - - -{% endblock %} - -{% block include_js %} - - {{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }} - {{ js_include('home/js/captcha.verify.js') }} - -{% endblock %} \ No newline at end of file diff --git a/app/Http/Home/Views/account/forget_password.volt b/app/Http/Home/Views/account/forget_password.volt index d72ccd9d..e6db53df 100644 --- a/app/Http/Home/Views/account/forget_password.volt +++ b/app/Http/Home/Views/account/forget_password.volt @@ -16,20 +16,20 @@