From 6c86f914cce9dc946d186cc4d49c11b9648e47a2 Mon Sep 17 00:00:00 2001 From: koogua Date: Fri, 26 Mar 2021 15:15:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=9D=A2=E6=8E=88=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=EF=BC=8C=E9=87=8D=E6=9E=84=E7=BE=A4=E7=BB=84=E6=88=90?= =?UTF-8?q?=E5=91=98=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 10 +++ README.md | 14 +-- app/Builders/ImGroupList.php | 11 +++ app/Console/Tasks/DeliverTask.php | 11 ++- app/Console/Tasks/UpgradeTask.php | 2 +- .../Admin/Controllers/ChapterController.php | 19 +++- .../Admin/Controllers/ImGroupController.php | 16 ++++ .../Controllers/ImGroupUserController.php | 32 +++++++ app/Http/Admin/Services/AuthNode.php | 12 +++ app/Http/Admin/Services/Chapter.php | 18 ++-- app/Http/Admin/Services/ChapterContent.php | 86 +++++++++++++------ app/Http/Admin/Services/Course.php | 54 +++++++++--- app/Http/Admin/Services/ImGroup.php | 69 +++++++++++++-- app/Http/Admin/Services/ImGroupUser.php | 55 ++++++++++++ app/Http/Admin/Services/Package.php | 16 +++- app/Http/Admin/Services/User.php | 4 +- app/Http/Admin/Views/chapter/edit_lesson.volt | 4 + .../Views/chapter/edit_lesson_offline.volt | 24 ++++++ app/Http/Admin/Views/chapter/lessons.volt | 2 + .../Admin/Views/chapter/lessons_offline.volt | 64 ++++++++++++++ app/Http/Admin/Views/course/add.volt | 3 +- app/Http/Admin/Views/course/edit.volt | 38 ++++---- app/Http/Admin/Views/course/edit_offline.volt | 33 +++++++ app/Http/Admin/Views/course/edit_sale.volt | 54 +++++------- app/Http/Admin/Views/course/expiry_macro.volt | 31 ------- app/Http/Admin/Views/course/list.volt | 27 +----- app/Http/Admin/Views/im/group/list.volt | 45 ++++------ app/Http/Admin/Views/im/group/users.volt | 70 +++++++++++++++ app/Http/Admin/Views/macros/course.volt | 27 ++++++ app/Http/Admin/Views/macros/group.volt | 11 +++ app/Http/Admin/Views/macros/user.volt | 34 ++++++++ app/Http/Admin/Views/order/list.volt | 10 +-- app/Http/Admin/Views/order/macro.volt | 33 +++++-- app/Http/Admin/Views/user/list.volt | 60 ++++--------- .../Home/Controllers/ImGroupController.php | 40 +++++++++ .../Controllers/ImGroupManageController.php | 73 ---------------- .../Controllers/ImGroupUserController.php | 32 +++++++ app/Http/Home/Services/ImGroup.php | 53 +----------- app/Http/Home/Services/ImGroupTrait.php | 2 + app/Http/Home/Services/ImGroupUser.php | 58 +++++++++++++ app/Http/Home/Views/course/show_catalog.volt | 23 +++++ app/Http/Home/Views/course/show_meta.volt | 61 ++++++++++--- app/Http/Home/Views/flash_sale/index.volt | 13 +++ .../Views/im/group/{manage => }/edit.volt | 0 .../Home/Views/im/group/manage/users.volt | 32 ------- .../Home/Views/im/group/manage_users.volt | 55 ++++++++++++ app/Http/Home/Views/im/group/users.volt | 14 +-- app/Http/Home/Views/macros/course.volt | 16 ++-- app/Http/Home/Views/macros/order.volt | 49 +++++++---- app/Http/Home/Views/order/confirm.volt | 61 +++++++++---- app/Http/Home/Views/order/info.volt | 8 +- app/Http/Home/Views/package/courses.volt | 46 +++++----- app/Http/Home/Views/user/console/friends.volt | 15 ++-- .../Views/user/console/groups_joined.volt | 17 ++-- .../Home/Views/user/console/groups_owned.volt | 16 ++-- app/Library/AppInfo.php | 2 +- app/Models/Chapter.php | 19 +++- app/Models/ChapterOffline.php | 72 ++++++++++++++++ app/Models/Course.php | 40 +++++++-- app/Models/ImGroup.php | 2 +- app/Models/ImUser.php | 2 +- app/Repos/Chapter.php | 13 +++ app/Repos/Course.php | 12 ++- app/Services/CourseStat.php | 35 ++------ app/Services/Logic/Course/ChapterList.php | 8 +- app/Services/Logic/Order/OrderConfirm.php | 1 + app/Services/Logic/Order/OrderCreate.php | 2 + app/Services/Logic/Order/OrderInfo.php | 40 +++++++-- app/Validators/Chapter.php | 4 + app/Validators/ChapterLive.php | 8 -- app/Validators/ChapterOffline.php | 36 ++++++++ app/Validators/Course.php | 8 +- app/Validators/CourseOffline.php | 60 +++++++++++++ app/Validators/ImGroupUser.php | 9 ++ config/errors.php | 24 +++++- db/migrations/20210324064239.php | 86 +++++++++++++++++++ public/static/admin/css/common.css | 10 +++ public/static/home/css/common.css | 34 ++++++-- public/static/home/js/flashsale.js | 2 +- public/static/home/js/user.console.js | 4 +- 80 files changed, 1623 insertions(+), 593 deletions(-) create mode 100644 app/Http/Admin/Controllers/ImGroupUserController.php create mode 100644 app/Http/Admin/Services/ImGroupUser.php create mode 100644 app/Http/Admin/Views/chapter/edit_lesson_offline.volt create mode 100644 app/Http/Admin/Views/chapter/lessons_offline.volt create mode 100644 app/Http/Admin/Views/course/edit_offline.volt delete mode 100644 app/Http/Admin/Views/course/expiry_macro.volt create mode 100644 app/Http/Admin/Views/im/group/users.volt create mode 100644 app/Http/Admin/Views/macros/course.volt create mode 100644 app/Http/Admin/Views/macros/group.volt create mode 100644 app/Http/Admin/Views/macros/user.volt delete mode 100644 app/Http/Home/Controllers/ImGroupManageController.php create mode 100644 app/Http/Home/Controllers/ImGroupUserController.php create mode 100644 app/Http/Home/Services/ImGroupUser.php rename app/Http/Home/Views/im/group/{manage => }/edit.volt (100%) delete mode 100644 app/Http/Home/Views/im/group/manage/users.volt create mode 100644 app/Http/Home/Views/im/group/manage_users.volt create mode 100644 app/Models/ChapterOffline.php create mode 100644 app/Validators/ChapterOffline.php create mode 100644 app/Validators/CourseOffline.php create mode 100644 db/migrations/20210324064239.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dd4f0bf..cb5bebab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +### [v1.3.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.0)(2021-03-26) + +### 更新 + +- 课程增加面授模型 +- 重构前台群组成员管理 +- 后台增加群组成员管理 +- 重构订单存储商品详情数据结构 +- 调整用户和群组列表等UI + ### [v1.2.9](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.9)(2021-03-22) ### 更新 diff --git a/README.md b/README.md index b8019549..acae1239 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,12 @@ ### 系统功能 -实现了点播、直播、专栏、会员、微聊等,是一个完整的产品,具体功能我也不想写一大堆,自己体验吧! +实现了点播、直播、专栏、面授、会员、群组、积分商城、秒杀等,全功能无阉割,100%真开源在线教育解决方案。具体功能我也不想写一大堆,自己体验吧! 友情提示: -- 系统配置低(1核 1G 1M 跑多个容器),切莫压测 -- 课程数据来源于网络(无实质内容),切莫购买 +- 演示系统配置低(1Core,1G,1M 跑多个容器)切莫压测 +- 课程数据来源于网络(无实质内容)切莫购买 - 管理后台已禁止数据提交,私密配置已过滤 桌面端演示: @@ -27,13 +27,13 @@ - [前台演示](https://ctc.koogua.com) - [后台演示](https://ctc.koogua.com/admin) -演示帐号:100015@163.com / 123456 (前后台通用) +演示账号:100015@163.com / 123456 (前后台通用) 移动端演示: ![移动端二维码](https://images.gitee.com/uploads/images/2020/1127/093203_265221a2_23592.png) -演示帐号:13507083515 / 123456 +演示账号:13507083515 / 123456 支付流程演示: @@ -42,10 +42,10 @@ - [数据库与中间件的基础必修课(0.02元)](https://ctc.koogua.com/order/confirm?item_id=80&item_type=2) Tips: 测试支付请用手机号注册一个新账户,以便接收订单通知,以及避免课程无法购买 - + 即时通讯演示: -请使用以下两个帐号在不同终端或者浏览器登录,打开微聊界面 +请使用以下两个账号在不同终端或者浏览器登录,打开微聊界面 - 帐号A:100015@163.com / 123456 - 帐号B:100065@163.com / 123456 diff --git a/app/Builders/ImGroupList.php b/app/Builders/ImGroupList.php index a21b9648..f0bdd8d9 100644 --- a/app/Builders/ImGroupList.php +++ b/app/Builders/ImGroupList.php @@ -8,6 +8,17 @@ use App\Repos\User as UserRepo; class ImGroupList extends Builder { + public function handleGroups(array $groups) + { + $baseUrl = kg_cos_url(); + + foreach ($groups as $key => $group) { + $groups[$key]['avatar'] = $baseUrl . $group['avatar']; + } + + return $groups; + } + public function handleCourses(array $groups) { $courses = $this->getCourses($groups); diff --git a/app/Console/Tasks/DeliverTask.php b/app/Console/Tasks/DeliverTask.php index f37a12d0..7c57b30d 100644 --- a/app/Console/Tasks/DeliverTask.php +++ b/app/Console/Tasks/DeliverTask.php @@ -2,6 +2,7 @@ namespace App\Console\Tasks; +use App\Models\Course as CourseModel; use App\Models\CourseUser as CourseUserModel; use App\Models\ImGroupUser as ImGroupUserModel; use App\Models\Order as OrderModel; @@ -105,13 +106,19 @@ class DeliverTask extends Task protected function handleCourseOrder(OrderModel $order) { - $itemInfo = $order->item_info; + $course = $order->item_info['course']; + + if ($course['model'] == CourseModel::MODEL_OFFLINE) { + $expiryTime = strtotime($course['attrs']['end_date']); + } else { + $expiryTime = $course['study_expiry_time']; + } $courseUser = new CourseUserModel(); $courseUser->user_id = $order->owner_id; $courseUser->course_id = $order->item_id; - $courseUser->expiry_time = $itemInfo['course']['study_expiry_time']; + $courseUser->expiry_time = $expiryTime; $courseUser->role_type = CourseUserModel::ROLE_STUDENT; $courseUser->source_type = CourseUserModel::SOURCE_CHARGE; diff --git a/app/Console/Tasks/UpgradeTask.php b/app/Console/Tasks/UpgradeTask.php index 2667186d..521289f1 100644 --- a/app/Console/Tasks/UpgradeTask.php +++ b/app/Console/Tasks/UpgradeTask.php @@ -80,7 +80,7 @@ class UpgradeTask extends Task $redis->del($statsKey); } - echo "end reset metadata..." . PHP_EOL; + echo "------ end reset metadata ------" . PHP_EOL; } /** diff --git a/app/Http/Admin/Controllers/ChapterController.php b/app/Http/Admin/Controllers/ChapterController.php index 1c81ebbc..f1a40e5d 100644 --- a/app/Http/Admin/Controllers/ChapterController.php +++ b/app/Http/Admin/Controllers/ChapterController.php @@ -79,10 +79,17 @@ class ChapterController extends Controller $chapter = $chapterService->createChapter(); - $location = $this->url->get([ - 'for' => 'admin.course.chapters', - 'id' => $chapter->course_id, - ]); + if ($chapter->parent_id > 0) { + $location = $this->url->get([ + 'for' => 'admin.chapter.lessons', + 'id' => $chapter->parent_id, + ]); + } else { + $location = $this->url->get([ + 'for' => 'admin.course.chapters', + 'id' => $chapter->course_id, + ]); + } $content = [ 'location' => $location, @@ -131,6 +138,10 @@ class ChapterController extends Controller $read = $contentService->getChapterRead($chapter->id); $this->view->setVar('read', $read); break; + case CourseModel::MODEL_OFFLINE: + $offline = $contentService->getChapterOffline($chapter->id); + $this->view->setVar('offline', $offline); + break; } } diff --git a/app/Http/Admin/Controllers/ImGroupController.php b/app/Http/Admin/Controllers/ImGroupController.php index e5659778..fc715e73 100644 --- a/app/Http/Admin/Controllers/ImGroupController.php +++ b/app/Http/Admin/Controllers/ImGroupController.php @@ -10,6 +10,22 @@ use App\Http\Admin\Services\ImGroup as ImGroupService; class ImGroupController extends Controller { + /** + * @Get("/{id:[0-9]+}/users", name="admin.im_group.users") + */ + public function usersAction($id) + { + $service = new ImGroupService(); + + $group = $service->getGroup($id); + $pager = $service->getGroupUsers($id); + + $this->view->pick('im/group/users'); + + $this->view->setVar('group', $group); + $this->view->setVar('pager', $pager); + } + /** * @Get("/list", name="admin.im_group.list") */ diff --git a/app/Http/Admin/Controllers/ImGroupUserController.php b/app/Http/Admin/Controllers/ImGroupUserController.php new file mode 100644 index 00000000..f5de83f1 --- /dev/null +++ b/app/Http/Admin/Controllers/ImGroupUserController.php @@ -0,0 +1,32 @@ +deleteGroupUser(); + + $location = $this->request->getHTTPReferer(); + + $content = [ + 'location' => $location, + 'msg' => '删除成员成功', + ]; + + return $this->jsonSuccess($content); + } + +} diff --git a/app/Http/Admin/Services/AuthNode.php b/app/Http/Admin/Services/AuthNode.php index 236ae11b..646c0dec 100644 --- a/app/Http/Admin/Services/AuthNode.php +++ b/app/Http/Admin/Services/AuthNode.php @@ -381,6 +381,18 @@ class AuthNode extends Service 'type' => 'button', 'route' => 'admin.im_group.delete', ], + [ + 'id' => '2-4-6', + 'title' => '群员列表', + 'type' => 'button', + 'route' => 'admin.im_group.users', + ], + [ + 'id' => '2-4-7', + 'title' => '删除群员', + 'type' => 'button', + 'route' => 'admin.im_group_user.delete', + ], ], ], [ diff --git a/app/Http/Admin/Services/Chapter.php b/app/Http/Admin/Services/Chapter.php index 5a387d95..4d9b4efa 100644 --- a/app/Http/Admin/Services/Chapter.php +++ b/app/Http/Admin/Services/Chapter.php @@ -7,6 +7,7 @@ use App\Caches\Chapter as ChapterCache; use App\Caches\CourseChapterList as CatalogCache; use App\Models\Chapter as ChapterModel; use App\Models\ChapterLive as ChapterLiveModel; +use App\Models\ChapterOffline as ChapterOfflineModel; use App\Models\ChapterRead as ChapterReadModel; use App\Models\ChapterVod as ChapterVodModel; use App\Models\Course as CourseModel; @@ -25,9 +26,7 @@ class Chapter extends Service $resources = $resourceRepo->findByChapterId($id); - if ($resources->count() == 0) { - return []; - } + if ($resources->count() == 0) return []; $builder = new ResourceListBuilder(); @@ -118,6 +117,10 @@ class Chapter extends Service $chapterRead = new ChapterReadModel(); $attrs = $chapterRead->create($data); break; + case CourseModel::MODEL_OFFLINE: + $chapterOffline = new ChapterOfflineModel(); + $attrs = $chapterOffline->create($data); + break; } if ($attrs === false) { @@ -137,10 +140,11 @@ class Chapter extends Service $this->db->rollback(); - $logger = $this->getLogger(); + $logger = $this->getLogger('http'); $logger->error('Create Chapter Error ' . kg_json_encode([ - 'code' => $e->getCode(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), 'message' => $e->getMessage(), ])); @@ -176,7 +180,7 @@ class Chapter extends Service if (isset($post['published'])) { $data['published'] = $validator->checkPublishStatus($post['published']); - if ($chapter->published == 0 && $post['published'] == 1) { + if ($post['published'] == 1) { $validator->checkPublishAbility($chapter); } } @@ -259,6 +263,8 @@ class Chapter extends Service $courseStats->updateLiveAttrs($course->id); } elseif ($course->model == CourseModel::MODEL_READ) { $courseStats->updateReadAttrs($course->id); + } elseif ($course->model == CourseModel::MODEL_OFFLINE) { + $courseStats->updateOfflineAttrs($course->id); } } diff --git a/app/Http/Admin/Services/ChapterContent.php b/app/Http/Admin/Services/ChapterContent.php index 09fccf37..a4bb6235 100644 --- a/app/Http/Admin/Services/ChapterContent.php +++ b/app/Http/Admin/Services/ChapterContent.php @@ -12,6 +12,7 @@ use App\Services\ChapterVod as ChapterVodService; use App\Services\CourseStat as CourseStatService; use App\Services\Vod as VodService; use App\Validators\ChapterLive as ChapterLiveValidator; +use App\Validators\ChapterOffline as ChapterOfflineValidator; use App\Validators\ChapterRead as ChapterReadValidator; use App\Validators\ChapterVod as ChapterVodValidator; @@ -39,6 +40,13 @@ class ChapterContent extends Service return $chapterRepo->findChapterRead($chapterId); } + public function getChapterOffline($chapterId) + { + $chapterRepo = new ChapterRepo(); + + return $chapterRepo->findChapterOffline($chapterId); + } + public function getPlayUrls($chapterId) { $service = new ChapterVodService(); @@ -64,6 +72,9 @@ class ChapterContent extends Service case CourseModel::MODEL_READ: $this->updateChapterRead($chapter); break; + case CourseModel::MODEL_OFFLINE: + $this->updateChapterOffline($chapter); + break; } $this->rebuildCatalogCache($chapter); @@ -84,9 +95,7 @@ class ChapterContent extends Service /** * 无新文件上传 */ - if ($fileId == $vod->file_id) { - return; - } + if ($fileId == $vod->file_id) return; /** * 删除旧文件 @@ -95,21 +104,17 @@ class ChapterContent extends Service $this->deleteVodFile($vod->file_id); } - $vod->update([ - 'file_id' => $fileId, - 'file_transcode' => '', - ]); + $vod->file_id = $fileId; + $vod->file_transcode = []; + + $vod->update(); - /** - * @var array $attrs - */ $attrs = $chapter->attrs; - $attrs['duration'] = 0; - $attrs['file']['status'] = ChapterModel::FS_UPLOADED; + $chapter->attrs = $attrs; - $chapter->update(['attrs' => $attrs]); + $chapter->update(); $this->updateCourseVodAttrs($vod->course_id); } @@ -129,20 +134,17 @@ class ChapterContent extends Service $validator->checkTimeRange($startTime, $endTime); - $live->update([ - 'start_time' => $startTime, - 'end_time' => $endTime, - ]); + $live->start_time = $startTime; + $live->end_time = $endTime; + + $live->update(); - /** - * @var array $attrs - */ $attrs = $chapter->attrs; - $attrs['start_time'] = $startTime; $attrs['end_time'] = $endTime; + $chapter->attrs = $attrs; - $chapter->update(['attrs' => $attrs]); + $chapter->update(); $this->updateCourseLiveAttrs($live->course_id); } @@ -161,9 +163,6 @@ class ChapterContent extends Service $read->update(['content' => $content]); - /** - * @var array $attrs - */ $attrs = $chapter->attrs; $attrs['word_count'] = WordUtil::getWordCount($content); @@ -174,6 +173,36 @@ class ChapterContent extends Service $this->updateCourseReadAttrs($read->course_id); } + protected function updateChapterOffline(ChapterModel $chapter) + { + $post = $this->request->getPost(); + + $chapterRepo = new ChapterRepo(); + + $offline = $chapterRepo->findChapterOffline($chapter->id); + + $validator = new ChapterOfflineValidator(); + + $startTime = $validator->checkStartTime($post['start_time']); + $endTime = $validator->checkEndTime($post['end_time']); + + $validator->checkTimeRange($startTime, $endTime); + + $offline->start_time = $startTime; + $offline->end_time = $endTime; + + $offline->update(); + + $attrs = $chapter->attrs; + $attrs['start_time'] = $startTime; + $attrs['end_time'] = $endTime; + $chapter->attrs = $attrs; + + $chapter->update(); + + $this->updateCourseOfflineAttrs($offline->course_id); + } + protected function updateCourseVodAttrs($courseId) { $statService = new CourseStatService(); @@ -195,6 +224,13 @@ class ChapterContent extends Service $statService->updateReadAttrs($courseId); } + protected function updateCourseOfflineAttrs($courseId) + { + $statService = new CourseStatService(); + + $statService->updateOfflineAttrs($courseId); + } + protected function deleteVodFile($fileId) { $vodService = new VodService(); diff --git a/app/Http/Admin/Services/Course.php b/app/Http/Admin/Services/Course.php index 7ee7204d..12c858f4 100644 --- a/app/Http/Admin/Services/Course.php +++ b/app/Http/Admin/Services/Course.php @@ -26,6 +26,7 @@ use App\Repos\ImGroup as ImGroupRepo; use App\Repos\User as UserRepo; use App\Services\Sync\CourseIndex as CourseIndexSync; use App\Validators\Course as CourseValidator; +use App\Validators\CourseOffline as CourseOfflineValidator; class Course extends Service { @@ -114,7 +115,7 @@ class Course extends Service $this->db->rollback(); - $logger = $this->getLogger(); + $logger = $this->getLogger('http'); $logger->error('Create Course Error ' . kg_json_encode([ 'code' => $e->getCode(), @@ -159,17 +160,24 @@ class Course extends Service $data['level'] = $validator->checkLevel($post['level']); } - if (isset($post['price_mode'])) { - if ($post['price_mode'] == 'free') { - $data['market_price'] = 0; - $data['vip_price'] = 0; - } else { - $data['origin_price'] = $validator->checkOriginPrice($post['origin_price']); - $data['market_price'] = $validator->checkMarketPrice($post['market_price']); - $data['vip_price'] = $validator->checkVipPrice($post['vip_price']); - $data['study_expiry'] = $validator->checkStudyExpiry($post['study_expiry']); - $data['refund_expiry'] = $validator->checkRefundExpiry($post['refund_expiry']); - } + if (isset($post['study_expiry'])) { + $data['study_expiry'] = $validator->checkStudyExpiry($post['study_expiry']); + } + + if (isset($post['refund_expiry'])) { + $data['refund_expiry'] = $validator->checkRefundExpiry($post['refund_expiry']); + } + + if (isset($post['origin_price'])) { + $data['origin_price'] = $validator->checkOriginPrice($post['origin_price']); + } + + if (isset($post['market_price'])) { + $data['market_price'] = $validator->checkMarketPrice($post['market_price']); + } + + if (isset($post['vip_price'])) { + $data['vip_price'] = $validator->checkVipPrice($post['vip_price']); } if (isset($post['featured'])) { @@ -195,6 +203,28 @@ class Course extends Service $this->saveRelatedCourses($course, $post['xm_course_ids']); } + if ($course->model == CourseModel::MODEL_OFFLINE) { + + $validator = new CourseOfflineValidator(); + + $data['study_expiry'] = 0; + $data['refund_expiry'] = 0; + + if (isset($post['attrs']['start_date']) && isset($post['attrs']['end_date'])) { + $data['attrs']['start_date'] = $validator->checkStartDate($post['attrs']['start_date']); + $data['attrs']['end_date'] = $validator->checkEndDate($post['attrs']['end_date']); + $validator->checkDateRange($data['attrs']['start_date'], $data['attrs']['end_date']); + } + + if (isset($post['attrs']['user_limit'])) { + $data['attrs']['user_limit'] = $validator->checkUserLimit($post['attrs']['user_limit']); + } + + if (isset($post['attrs']['location'])) { + $data['attrs']['location'] = $validator->checkLocation($post['attrs']['location']); + } + } + $course->update($data); $this->updateImGroup($course); diff --git a/app/Http/Admin/Services/ImGroup.php b/app/Http/Admin/Services/ImGroup.php index 9f4af997..8cad2a93 100644 --- a/app/Http/Admin/Services/ImGroup.php +++ b/app/Http/Admin/Services/ImGroup.php @@ -3,13 +3,15 @@ namespace App\Http\Admin\Services; use App\Builders\ImGroupList as ImGroupListBuilder; +use App\Builders\ImGroupUserList as ImGroupUserListBuilder; use App\Library\Paginator\Query as PagerQuery; use App\Models\ImGroup as ImGroupModel; use App\Models\ImGroupUser as ImGroupUserModel; -use App\Models\User as UserModel; +use App\Models\ImUser as ImUserModel; use App\Repos\ImGroup as ImGroupRepo; use App\Repos\ImGroupUser as ImGroupUserRepo; use App\Validators\ImGroup as ImGroupValidator; +use App\Validators\ImGroupUser as ImGroupUserValidator; class ImGroup extends Service { @@ -89,9 +91,10 @@ class ImGroup extends Service } if (isset($post['owner_id'])) { - $owner = $validator->checkGroupOwner($post['owner_id']); - $data['owner_id'] = $owner->id; - $this->handleGroupOwner($group, $owner); + $validator = new ImGroupUserValidator(); + $user = $validator->checkUser($post['owner_id']); + $data['owner_id'] = $user->id; + $this->handleGroupOwner($group, $user); } $group->update($data); @@ -121,7 +124,42 @@ class ImGroup extends Service return $group; } - protected function handleGroupOwner(ImGroupModel $group, UserModel $user) + public function getGroupUsers($id) + { + $pagerQuery = new PagerQuery(); + + $params = $pagerQuery->getParams(); + + $params['group_id'] = $id; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $groupUserRepo = new ImGroupUserRepo(); + + $pager = $groupUserRepo->paginate($params, $sort, $page, $limit); + + return $this->handleGroupUsers($pager); + } + + protected function handleGroupUsers($pager) + { + if ($pager->total_items == 0) { + return $pager; + } + + $builder = new ImGroupUserListBuilder(); + + $stepA = $pager->items->toArray(); + $stepB = $builder->handleUsers($stepA); + + $pager->items = $stepB; + + return $pager; + } + + protected function handleGroupOwner(ImGroupModel $group, ImUserModel $user) { $repo = new ImGroupUserRepo(); @@ -130,21 +168,40 @@ class ImGroup extends Service if ($groupUser) return; $groupUser = new ImGroupUserModel(); + $groupUser->group_id = $group->id; $groupUser->user_id = $user->id; + $groupUser->create(); + $this->incrGroupUserCount($group); + + $this->incrUserGroupCount($user); + } + + protected function incrGroupUserCount(ImGroupModel $group) + { $group->user_count += 1; + $group->update(); } + protected function incrUserGroupCount(ImUserModel $user) + { + $user->group_count += 1; + + $user->update(); + } + protected function handleGroups($pager) { if ($pager->total_items > 0) { $builder = new ImGroupListBuilder(); - $pipeA = $pager->items->toArray(); + $items = $pager->items->toArray(); + + $pipeA = $builder->handleGroups($items); $pipeB = $builder->handleUsers($pipeA); $pipeC = $builder->objects($pipeB); diff --git a/app/Http/Admin/Services/ImGroupUser.php b/app/Http/Admin/Services/ImGroupUser.php new file mode 100644 index 00000000..22305ece --- /dev/null +++ b/app/Http/Admin/Services/ImGroupUser.php @@ -0,0 +1,55 @@ +request->getQuery('group_id', 'int', 0); + $userId = $this->request->getQuery('user_id', 'int', 0); + + $validator = new ImGroupUserValidator(); + + $group = $validator->checkGroup($groupId); + $user = $validator->checkUser($userId); + + $validator->checkIfAllowDelete($groupId, $userId); + + $groupUser = $this->findOrFail($groupId, $userId); + + $groupUser->delete(); + + $this->decrGroupUserCount($group); + $this->decrUserGroupCount($user); + } + + protected function decrGroupUserCount(ImGroupModel $group) + { + if ($group->user_count > 0) { + $group->user_count -= 1; + $group->update(); + } + } + + protected function decrUserGroupCount(ImUserModel $user) + { + if ($user->group_count > 0) { + $user->group_count -= 1; + $user->update(); + } + } + + protected function findOrFail($groupId, $userId) + { + $validator = new ImGroupUserValidator(); + + return $validator->checkGroupUser($groupId, $userId); + } + +} diff --git a/app/Http/Admin/Services/Package.php b/app/Http/Admin/Services/Package.php index f89a13ce..07171e7b 100644 --- a/app/Http/Admin/Services/Package.php +++ b/app/Http/Admin/Services/Package.php @@ -6,6 +6,7 @@ use App\Caches\CoursePackageList as CoursePackageListCache; use App\Caches\Package as PackageCache; use App\Caches\PackageCourseList as PackageCourseListCache; use App\Library\Paginator\Query as PagerQuery; +use App\Models\Course as CourseModel; use App\Models\CoursePackage as CoursePackageModel; use App\Models\Package as PackageModel; use App\Repos\Course as CourseRepo; @@ -32,7 +33,20 @@ class Package extends Service $courseRepo = new CourseRepo(); - $items = $courseRepo->findAll(['free' => 0, 'published' => 1]); + /** + * 面授课程不参与套餐计划,因为无法进行退款计算 + */ + $model = [ + CourseModel::MODEL_VOD, + CourseModel::MODEL_LIVE, + CourseModel::MODEL_READ, + ]; + + $items = $courseRepo->findAll([ + 'model' => $model, + 'free' => 0, + 'published' => 1, + ]); if ($items->count() == 0) return []; diff --git a/app/Http/Admin/Services/User.php b/app/Http/Admin/Services/User.php index 539fcb32..7ebf83c9 100644 --- a/app/Http/Admin/Services/User.php +++ b/app/Http/Admin/Services/User.php @@ -267,7 +267,9 @@ class User extends Service $builder = new UserListBuilder(); - $pipeA = $pager->items->toArray(); + $items = $pager->items->toArray(); + + $pipeA = $builder->handleUsers($items); $pipeB = $builder->handleAdminRoles($pipeA); $pipeC = $builder->handleEduRoles($pipeB); $pipeD = $builder->objects($pipeC); diff --git a/app/Http/Admin/Views/chapter/edit_lesson.volt b/app/Http/Admin/Views/chapter/edit_lesson.volt index 4f0c7dc6..9f4e98ab 100644 --- a/app/Http/Admin/Views/chapter/edit_lesson.volt +++ b/app/Http/Admin/Views/chapter/edit_lesson.volt @@ -9,6 +9,8 @@ 直播信息 {% elseif model == '3' %} 图文信息 + {% elseif model == '4' %} + 面授信息 {% endif %} {%- endmacro %} @@ -33,6 +35,8 @@ {{ partial('chapter/edit_lesson_live') }} {% elseif course.model == 3 %} {{ partial('chapter/edit_lesson_read') }} + {% elseif course.model == 4 %} + {{ partial('chapter/edit_lesson_offline') }} {% endif %}
diff --git a/app/Http/Admin/Views/chapter/edit_lesson_offline.volt b/app/Http/Admin/Views/chapter/edit_lesson_offline.volt new file mode 100644 index 00000000..9dfb9ce4 --- /dev/null +++ b/app/Http/Admin/Views/chapter/edit_lesson_offline.volt @@ -0,0 +1,24 @@ +{% set offline.start_time = offline.start_time > 0 ? date('Y-m-d H:i:s',offline.start_time) : '' %} +{% set offline.end_time = offline.end_time > 0 ? date('Y-m-d H:i:s',offline.end_time) : '' %} + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
\ No newline at end of file diff --git a/app/Http/Admin/Views/chapter/lessons.volt b/app/Http/Admin/Views/chapter/lessons.volt index 8ec5ab29..f4d22162 100644 --- a/app/Http/Admin/Views/chapter/lessons.volt +++ b/app/Http/Admin/Views/chapter/lessons.volt @@ -27,6 +27,8 @@ {{ partial('chapter/lessons_live') }} {% elseif course.model == 3 %} {{ partial('chapter/lessons_read') }} + {% elseif course.model == 4 %} + {{ partial('chapter/lessons_offline') }} {% endif %} {% endblock %} diff --git a/app/Http/Admin/Views/chapter/lessons_offline.volt b/app/Http/Admin/Views/chapter/lessons_offline.volt new file mode 100644 index 00000000..d7d899af --- /dev/null +++ b/app/Http/Admin/Views/chapter/lessons_offline.volt @@ -0,0 +1,64 @@ +{%- macro offline_time_info(attrs) %} + {% if attrs['start_time'] > 0 %} +

开始:{{ date('Y-m-d H:i',attrs['start_time']) }}

+

结束:{{ date('Y-m-d H:i',attrs['end_time']) }}

+ {% else %} + N/A + {% endif %} +{%- endmacro %} + + + + + + + + + + + + + + + + + + + + + + + + + {% for item in lessons %} + {% set edit_url = url({'for':'admin.chapter.edit','id':item.id}) %} + {% set update_url = url({'for':'admin.chapter.update','id':item.id}) %} + {% set delete_url = url({'for':'admin.chapter.delete','id':item.id}) %} + {% set restore_url = url({'for':'admin.chapter.restore','id':item.id}) %} + + + + + + + + + + {% endfor %} + +
编号名称时间排序免费发布操作
{{ item.id }} + {{ item.title }} + + {{ offline_time_info(item.attrs) }} +
+ + +
+
\ No newline at end of file diff --git a/app/Http/Admin/Views/course/add.volt b/app/Http/Admin/Views/course/add.volt index f457c85c..e0f00957 100644 --- a/app/Http/Admin/Views/course/add.volt +++ b/app/Http/Admin/Views/course/add.volt @@ -50,7 +50,8 @@ var modelTips = { '1': '通过音视频呈现课程内容,内容可视化,有图像有声音,适合大部分场景', '2': '通过直播呈现课程内容,交互性强,适合需要交互反馈、情绪表达的场景', - '3': '通过图文呈现课程内容,简单直接,适合撰写文档、书籍、教程的场景' + '3': '通过图文呈现课程内容,简单直接,适合撰写文档、书籍、教程的场景', + '4': '面对面讲授课程内容,传统教学,适合有条件开展线下教学的场景', }; var modelTipsBlock = $('#model-tips'); diff --git a/app/Http/Admin/Views/course/edit.volt b/app/Http/Admin/Views/course/edit.volt index 2fac4899..4104faa6 100644 --- a/app/Http/Admin/Views/course/edit.volt +++ b/app/Http/Admin/Views/course/edit.volt @@ -9,6 +9,9 @@
  • 基本信息
  • + {% if course.model == 4 %} +
  • 面授信息
  • + {% endif %}
  • 课程介绍
  • 营销设置
  • 相关课程
  • @@ -17,6 +20,11 @@
    {{ partial('course/edit_basic') }}
    + {% if course.model == 4 %} +
    + {{ partial('course/edit_offline') }} +
    + {% endif %}
    {{ partial('course/edit_desc') }}
    @@ -53,23 +61,16 @@ xmSelect.render({ el: '#xm-category-ids', name: 'xm_category_ids', + filterable: true, max: 5, - prop: { - name: 'name', - value: 'id' - }, data: {{ xm_categories|json_encode }} }); xmSelect.render({ el: '#xm-teacher-ids', name: 'xm_teacher_ids', - paging: true, + filterable: true, max: 5, - prop: { - name: 'name', - value: 'id' - }, data: {{ xm_teachers|json_encode }} }); @@ -86,19 +87,20 @@