diff --git a/.gitignore b/.gitignore index bf6d78e4..aff1e894 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 /public/h5 diff --git a/CHANGELOG.md b/CHANGELOG.md index 26b077a2..2d08a790 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +### [v1.2.6](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.6)(2021-02-20) + +### 新增 + +- 积分兑换机制 +- 课程增加原价属性 +- gitee提交webhooks自动化部署脚本 + +### 更新 + +- course和chapter数据迁移文件中遗漏了recourse_count字段 +- app/Caches/TopicCourseList不存在 +- Model文件属性定义默认值 +- 隐藏非付费课程的咨询服务 +- 教学中心教师直播推流按钮无反应 +- 用户中心部分样式调整 +- 播放器清晰度标签和实际的清晰度不对应 +- CNZZ统计代码会显示出站长统计图标 +- 自动安装后访问站点500错误 +- 自动更新脚本可更新css和js版本号 + ### [v1.2.5](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.5)(2021-01-20) ### 新增 diff --git a/app/Caches/MaxPointGiftId.php b/app/Caches/MaxPointGiftId.php new file mode 100644 index 00000000..6c85c10a --- /dev/null +++ b/app/Caches/MaxPointGiftId.php @@ -0,0 +1,29 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'max_point_gift_id'; + } + + public function getContent($id = null) + { + $gift = PointGiftModel::findFirst(['order' => 'id DESC']); + + return $gift->id ?? 0; + } + +} diff --git a/app/Caches/PointGift.php b/app/Caches/PointGift.php new file mode 100644 index 00000000..6a027a71 --- /dev/null +++ b/app/Caches/PointGift.php @@ -0,0 +1,31 @@ +lifetime; + } + + public function getKey($id = null) + { + return "point_gift:{$id}"; + } + + public function getContent($id = null) + { + $giftRepo = new PointGiftRepo(); + + $gift = $giftRepo->findById($id); + + return $gift ?: null; + } + +} diff --git a/app/Caches/PointHotGiftList.php b/app/Caches/PointHotGiftList.php new file mode 100644 index 00000000..30ae8aa6 --- /dev/null +++ b/app/Caches/PointHotGiftList.php @@ -0,0 +1,79 @@ +limit = $limit; + } + + public function getLifetime() + { + return $this->lifetime; + } + + public function getKey($id = null) + { + return 'point_hot_gift_list'; + } + + public function getContent($id = null) + { + $gifts = $this->findGifts($this->limit); + + if (count($gifts) == 0) { + return []; + } + + $result = []; + + foreach ($gifts as $gift) { + $result[] = [ + 'id' => $gift->id, + 'name' => $gift->name, + 'cover' => $gift->cover, + 'details' => $gift->details, + 'type' => $gift->type, + 'point' => $gift->point, + 'redeem_count' => $gift->redeem_count, + ]; + } + + return $result; + } + + /** + * @param int $limit + * @return ResultsetInterface|Resultset|PointGiftModel[] + */ + protected function findGifts($limit = 5) + { + return PointGiftModel::query() + ->where('published = 1') + ->orderBy('redeem_count DESC') + ->limit($limit) + ->execute(); + } + +} diff --git a/app/Console/Tasks/CleanLogTask.php b/app/Console/Tasks/CleanLogTask.php index 725dfa6c..e198d702 100644 --- a/app/Console/Tasks/CleanLogTask.php +++ b/app/Console/Tasks/CleanLogTask.php @@ -25,6 +25,7 @@ class CleanLogTask extends Task $this->cleanWxpayLog(); $this->cleanOrderLog(); $this->cleanRefundLog(); + $this->cleanPointLog(); $this->cleanNoticeLog(); $this->cleanOtherLog(); } @@ -221,6 +222,18 @@ class CleanLogTask extends Task $this->whitelist[] = $type; } + /** + * 清理积分日志 + */ + protected function cleanPointLog() + { + $type = 'point'; + + $this->cleanLog($type, 7); + + $this->whitelist[] = $type; + } + /** * 清理通知日志 */ diff --git a/app/Console/Tasks/CloseTradeTask.php b/app/Console/Tasks/CloseTradeTask.php index 3918d40c..efe84b3a 100644 --- a/app/Console/Tasks/CloseTradeTask.php +++ b/app/Console/Tasks/CloseTradeTask.php @@ -48,7 +48,7 @@ class CloseTradeTask extends Task */ if ($alipayTrade->trade_status == 'TRADE_SUCCESS') { - $this->eventsManager->fire('pay:afterPay', $this, $trade); + $this->eventsManager->fire('Trade:afterPay', $this, $trade); $allowClosed = false; @@ -85,7 +85,7 @@ class CloseTradeTask extends Task */ if ($wxpayTrade->trade_state == 'SUCCESS') { - $this->eventsManager->fire('pay:afterPay', $this, $trade); + $this->eventsManager->fire('Trade:afterPay', $this, $trade); $allowClosed = false; diff --git a/app/Console/Tasks/DeliverTask.php b/app/Console/Tasks/DeliverTask.php index 874401a4..d41a22b7 100644 --- a/app/Console/Tasks/DeliverTask.php +++ b/app/Console/Tasks/DeliverTask.php @@ -13,6 +13,7 @@ use App\Repos\ImGroupUser as ImGroupUserRepo; use App\Repos\Order as OrderRepo; use App\Repos\User as UserRepo; use App\Services\Logic\Notice\OrderFinish as OrderFinishNotice; +use App\Services\Logic\Point\PointHistory as PointHistoryService; use Phalcon\Mvc\Model; use Phalcon\Mvc\Model\Resultset; use Phalcon\Mvc\Model\ResultsetInterface; @@ -36,17 +37,20 @@ class DeliverTask extends Task foreach ($tasks as $task) { - /** - * @var array $itemInfo - */ - $itemInfo = $task->item_info; + $orderId = $task->item_info['order']['id'] ?? 0; - $order = $orderRepo->findById($itemInfo['order']['id']); + $order = $orderRepo->findById($orderId); - if (!$order) continue; + if (!$order) { + $task->status = TaskModel::STATUS_FAILED; + $task->update(); + continue; + } try { + $this->db->begin(); + switch ($order->item_type) { case OrderModel::ITEM_COURSE: $this->handleCourseOrder($order); @@ -59,14 +63,24 @@ class DeliverTask extends Task break; } - $this->finishOrder($order); + $order->status = OrderModel::STATUS_FINISHED; + + if ($order->update() === false) { + throw new \RuntimeException('Update Order Status Failed'); + } $task->status = TaskModel::STATUS_FINISHED; - $task->update(); + if ($task->update() === false) { + throw new \RuntimeException('Update Task Status Failed'); + } + + $this->db->commit(); } catch (\Exception $e) { + $this->db->rollback(); + $task->try_count += 1; $task->priority += 1; @@ -76,14 +90,16 @@ class DeliverTask extends Task $task->update(); - $logger->info('Order Process Exception ' . kg_json_encode([ - 'code' => $e->getCode(), + $logger->error('Order Process Exception ' . kg_json_encode([ + 'file' => $e->getFile(), + 'line' => $e->getLine(), 'message' => $e->getMessage(), 'task' => $task->toArray(), ])); } if ($task->status == TaskModel::STATUS_FINISHED) { + $this->handleOrderConsumePoint($order); $this->handleOrderFinishNotice($order); } elseif ($task->status == TaskModel::STATUS_FAILED) { $this->handleOrderRefund($order); @@ -91,20 +107,8 @@ class DeliverTask extends Task } } - protected function finishOrder(OrderModel $order) - { - $order->status = OrderModel::STATUS_FINISHED; - - if ($order->update() === false) { - throw new \RuntimeException('Finish Order Failed'); - } - } - protected function handleCourseOrder(OrderModel $order) { - /** - * @var array $itemInfo - */ $itemInfo = $order->item_info; $courseUser = new CourseUserModel(); @@ -127,23 +131,21 @@ class DeliverTask extends Task $groupUser = $groupUserRepo->findGroupUser($group->id, $order->owner_id); - if ($groupUser) return; + if (!$groupUser) { - $groupUser = new ImGroupUserModel(); + $groupUser = new ImGroupUserModel(); - $groupUser->group_id = $group->id; - $groupUser->user_id = $order->owner_id; + $groupUser->group_id = $group->id; + $groupUser->user_id = $order->owner_id; - if ($groupUser->create() === false) { - throw new \RuntimeException('Create Group User Failed'); + if ($groupUser->create() === false) { + throw new \RuntimeException('Create Group User Failed'); + } } } protected function handlePackageOrder(OrderModel $order) { - /** - * @var array $itemInfo - */ $itemInfo = $order->item_info; foreach ($itemInfo['courses'] as $course) { @@ -168,24 +170,24 @@ class DeliverTask extends Task $groupUser = $groupUserRepo->findGroupUser($group->id, $order->owner_id); - if ($groupUser) continue; + if (!$groupUser) { - $groupUser = new ImGroupUserModel(); + $groupUser = new ImGroupUserModel(); - $groupUser->group_id = $group->id; - $groupUser->user_id = $order->owner_id; + $groupUser->group_id = $group->id; + $groupUser->user_id = $order->owner_id; - if ($groupUser->create() === false) { - throw new \RuntimeException('Create Group User Failed'); + if ($groupUser->create() === false) { + throw new \RuntimeException('Create Group User Failed'); + } + + continue; } } } protected function handleVipOrder(OrderModel $order) { - /** - * @var array $itemInfo - */ $itemInfo = $order->item_info; $userRepo = new UserRepo(); @@ -199,6 +201,13 @@ class DeliverTask extends Task } } + protected function handleOrderConsumePoint(OrderModel $order) + { + $service = new PointHistoryService(); + + $service->handleOrderConsume($order); + } + protected function handleOrderFinishNotice(OrderModel $order) { $notice = new OrderFinishNotice(); diff --git a/app/Console/Tasks/NoticeTask.php b/app/Console/Tasks/NoticeTask.php index b736b80c..7d17318d 100644 --- a/app/Console/Tasks/NoticeTask.php +++ b/app/Console/Tasks/NoticeTask.php @@ -66,7 +66,6 @@ class NoticeTask extends Task $logger->info('Notice Process Exception ' . kg_json_encode([ 'file' => $e->getFile(), 'line' => $e->getLine(), - 'code' => $e->getCode(), 'message' => $e->getMessage(), 'task' => $task->toArray(), ])); diff --git a/app/Console/Tasks/PointGiftDeliverTask.php b/app/Console/Tasks/PointGiftDeliverTask.php new file mode 100644 index 00000000..0f8baac9 --- /dev/null +++ b/app/Console/Tasks/PointGiftDeliverTask.php @@ -0,0 +1,209 @@ +getLogger('point'); + + $tasks = $this->findTasks(30); + + if ($tasks->count() == 0) { + return; + } + + $redeemRepo = new PointRedeemRepo(); + + foreach ($tasks as $task) { + + $redeemId = $task->item_info['point_redeem']['id'] ?? 0; + + $redeem = $redeemRepo->findById($redeemId); + + if (!$redeem) { + $task->status = TaskModel::STATUS_FAILED; + $task->update(); + break; + } + + try { + + $this->db->begin(); + + switch ($redeem->gift_type) { + case PointGiftModel::TYPE_COURSE: + $this->handleCourseRedeem($redeem); + break; + case PointGiftModel::TYPE_GOODS: + $this->handleGoodsRedeem($redeem); + break; + case PointGiftModel::TYPE_CASH: + $this->handleCashRedeem($redeem); + break; + } + + $task->status = TaskModel::STATUS_FINISHED; + + if ($task->update() === false) { + throw new \RuntimeException('Update Task Status Failed'); + } + + $this->db->commit(); + + } catch (\Exception $e) { + + $this->db->rollback(); + + $task->try_count += 1; + $task->priority += 1; + + if ($task->try_count > self::TRY_COUNT) { + $task->status = TaskModel::STATUS_FAILED; + } + + $task->update(); + + $logger->error('Point Gift Deliver Exception ' . kg_json_encode([ + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'message' => $e->getMessage(), + 'task' => $task->toArray(), + ])); + } + + if ($task->status == TaskModel::STATUS_FAILED) { + $this->handlePointRefund($redeem); + } + } + } + + protected function handleCourseRedeem(PointRedeemModel $redeem) + { + $giftRepo = new PointGiftRepo(); + + $gift = $giftRepo->findById($redeem->gift_id); + + if (!$gift) { + throw new \RuntimeException('Gift Not Found'); + } + + $courseRepo = new CourseRepo(); + + $course = $courseRepo->findById($gift->attrs['id']); + + if (!$course) { + throw new \RuntimeException('Course Not Found'); + } + + $groupRepo = new ImGroupRepo(); + + $group = $groupRepo->findByCourseId($course->id); + + if (!$group) { + throw new \RuntimeException('Im Group Not Found'); + } + + $courseUserRepo = new CourseUserRepo(); + + $courseUser = $courseUserRepo->findCourseUser($course->id, $redeem->user_id); + + if (!$courseUser) { + + $courseUser = new CourseUserModel(); + + $courseUser->user_id = $redeem->user_id; + $courseUser->course_id = $course->id; + $courseUser->expiry_time = strtotime("+{$course->study_expiry} months"); + $courseUser->role_type = CourseUserModel::ROLE_STUDENT; + $courseUser->source_type = CourseUserModel::SOURCE_POINT_REDEEM; + + if ($courseUser->create() === false) { + throw new \RuntimeException('Create Course User Failed'); + } + } + + $groupUserRepo = new ImGroupUserRepo(); + + $groupUser = $groupUserRepo->findGroupUser($group->id, $redeem->user_id); + + if (!$groupUser) { + + $groupUser = new ImGroupUserModel(); + + $groupUser->group_id = $group->id; + $groupUser->user_id = $redeem->user_id; + + if ($groupUser->create() === false) { + throw new \RuntimeException('Create Group User Failed'); + } + } + + $redeem->status = PointRedeemModel::STATUS_FINISHED; + + if ($redeem->update() === false) { + throw new \RuntimeException('Update Redeem Status Failed'); + } + } + + protected function handleGoodsRedeem(PointRedeemModel $redeem) + { + + } + + protected function handleCashRedeem(PointRedeemModel $redeem) + { + + } + + protected function handlePointRefund(PointRedeemModel $redeem) + { + $service = new PointHistoryService(); + + $service->handlePointRefund($redeem); + } + + protected function handleRedeemFinishNotice(PointRedeemModel $redeem) + { + + } + + /** + * @param int $limit + * @return ResultsetInterface|Resultset|TaskModel[] + */ + protected function findTasks($limit = 30) + { + $itemType = TaskModel::TYPE_POINT_GIFT_DELIVER; + $status = TaskModel::STATUS_PENDING; + $createTime = strtotime('-3 days'); + + return TaskModel::query() + ->where('item_type = :item_type:', ['item_type' => $itemType]) + ->andWhere('status = :status:', ['status' => $status]) + ->andWhere('create_time > :create_time:', ['create_time' => $createTime]) + ->orderBy('priority ASC') + ->limit($limit) + ->execute(); + } + +} diff --git a/app/Console/Tasks/RefundTask.php b/app/Console/Tasks/RefundTask.php index 7529c242..41e04a51 100644 --- a/app/Console/Tasks/RefundTask.php +++ b/app/Console/Tasks/RefundTask.php @@ -41,9 +41,6 @@ class RefundTask extends Task foreach ($tasks as $task) { - /** - * @var array $itemInfo - */ $itemInfo = $task->item_info; $refund = $refundRepo->findById($itemInfo['refund']['id']); @@ -51,13 +48,8 @@ class RefundTask extends Task $order = $orderRepo->findById($itemInfo['refund']['order_id']); if (!$refund || !$trade || !$order) { - continue; - } - - /** - * 退款存在延迟,给取消退款调解机会 - */ - if (isset($itemInfo['deadline']) && $itemInfo['deadline'] > time()) { + $task->status = TaskModel::STATUS_FAILED; + $task->update(); continue; } @@ -111,7 +103,8 @@ class RefundTask extends Task $task->update(); $logger->info('Refund Task Exception ' . kg_json_encode([ - 'code' => $e->getCode(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), 'message' => $e->getMessage(), 'task' => $task->toArray(), ])); @@ -148,7 +141,7 @@ class RefundTask extends Task } if (!$response) { - throw new \RuntimeException('Pay Refund Failed'); + throw new \RuntimeException('Trade Refund Failed'); } } @@ -208,9 +201,6 @@ class RefundTask extends Task { $courseUserRepo = new CourseUserRepo(); - /** - * @var array $itemInfo - */ $itemInfo = $order->item_info; foreach ($itemInfo['courses'] as $course) { @@ -239,9 +229,6 @@ class RefundTask extends Task $user = $userRepo->findById($order->owner_id); - /** - * @var array $itemInfo - */ $itemInfo = $order->item_info; $diffTime = "-{$itemInfo['vip']['expiry']} months"; diff --git a/app/Console/Tasks/SyncLearningTask.php b/app/Console/Tasks/SyncLearningTask.php index af1f41aa..2ba7a0e8 100644 --- a/app/Console/Tasks/SyncLearningTask.php +++ b/app/Console/Tasks/SyncLearningTask.php @@ -2,6 +2,7 @@ namespace App\Console\Tasks; +use App\Models\ChapterUser as ChapterUserModel; use App\Models\Course as CourseModel; use App\Models\Learning as LearningModel; use App\Repos\Chapter as ChapterRepo; @@ -9,7 +10,8 @@ use App\Repos\ChapterUser as ChapterUserRepo; use App\Repos\Course as CourseRepo; use App\Repos\CourseUser as CourseUserRepo; use App\Repos\Learning as LearningRepo; -use App\Services\Sync\Learning as LearningSync; +use App\Services\Logic\Point\PointHistory as PointHistoryService; +use App\Services\Sync\Learning as LearningSyncService; class SyncLearningTask extends Task { @@ -18,7 +20,7 @@ class SyncLearningTask extends Task { $redis = $this->getRedis(); - $sync = new LearningSync(); + $sync = new LearningSyncService(); $syncKey = $sync->getSyncKey(); @@ -120,7 +122,10 @@ class SyncLearningTask extends Task $chapterUser->update(); if ($chapterUser->consumed == 1) { + $this->updateCourseUser($learning); + + $this->handleStudyPoint($chapterUser); } } @@ -174,4 +179,14 @@ class SyncLearningTask extends Task $courseUser->update(); } + /** + * @param ChapterUserModel $chapterUser + */ + protected function handleStudyPoint(ChapterUserModel $chapterUser) + { + $service = new PointHistoryService(); + + $service->handleChapterStudy($chapterUser); + } + } diff --git a/app/Http/Admin/Controllers/Controller.php b/app/Http/Admin/Controllers/Controller.php index 058bfa77..724497a0 100644 --- a/app/Http/Admin/Controllers/Controller.php +++ b/app/Http/Admin/Controllers/Controller.php @@ -26,8 +26,6 @@ class Controller extends \Phalcon\Mvc\Controller $this->checkCsrfToken(); } - $this->checkRateLimit(); - $this->authInfo = $this->getAuthInfo(); if (!$this->authInfo) { diff --git a/app/Http/Admin/Controllers/PointGiftController.php b/app/Http/Admin/Controllers/PointGiftController.php new file mode 100644 index 00000000..1135eb9d --- /dev/null +++ b/app/Http/Admin/Controllers/PointGiftController.php @@ -0,0 +1,136 @@ +getGifts(); + + $this->view->pick('point/gift/list'); + + $this->view->setVar('pager', $pager); + } + + /** + * @Get("/search", name="admin.point_gift.search") + */ + public function searchAction() + { + $this->view->pick('point/gift/search'); + } + + /** + * @Get("/add", name="admin.point_gift.add") + */ + public function addAction() + { + $this->view->pick('point/gift/add'); + } + + /** + * @Get("/{id:[0-9]+}/edit", name="admin.point_gift.edit") + */ + public function editAction($id) + { + $giftService = new PointGiftService(); + + $gift = $giftService->getGift($id); + + $this->view->pick('point/gift/edit'); + + $this->view->setVar('gift', $gift); + } + + /** + * @Post("/create", name="admin.point_gift.create") + */ + public function createAction() + { + $giftService = new PointGiftService(); + + $gift = $giftService->createGift(); + + $location = $this->url->get([ + 'for' => 'admin.point_gift.edit', + 'id' => $gift->id, + ]); + + $content = [ + 'location' => $location, + 'msg' => '添加礼品成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/update", name="admin.point_gift.update") + */ + public function updateAction($id) + { + $giftService = new PointGiftService(); + + $giftService->updateGift($id); + + $location = $this->url->get(['for' => 'admin.point_gift.list']); + + $content = [ + 'location' => $location, + 'msg' => '更新礼品成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/delete", name="admin.point_gift.delete") + */ + public function deleteAction($id) + { + $giftService = new PointGiftService(); + + $giftService->deleteGift($id); + + $location = $this->request->getHTTPReferer(); + + $content = [ + 'location' => $location, + 'msg' => '删除礼品成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/restore", name="admin.point_gift.restore") + */ + public function restoreAction($id) + { + $giftService = new PointGiftService(); + + $giftService->restoreGift($id); + + $location = $this->request->getHTTPReferer(); + + $content = [ + 'location' => $location, + 'msg' => '还原礼品成功', + ]; + + return $this->jsonSuccess($content); + } + +} diff --git a/app/Http/Admin/Controllers/PointRedeemController.php b/app/Http/Admin/Controllers/PointRedeemController.php new file mode 100644 index 00000000..707403d3 --- /dev/null +++ b/app/Http/Admin/Controllers/PointRedeemController.php @@ -0,0 +1,47 @@ +view->pick('point/redeem/search'); + } + + /** + * @Get("/list", name="admin.point_redeem.list") + */ + public function listAction() + { + $redeemService = new PointRedeemService(); + + $pager = $redeemService->getRedeems(); + + $this->view->pick('point/redeem/list'); + + $this->view->setVar('pager', $pager); + } + + /** + * @Post("/{id:[0-9]+}/deliver", name="admin.point_redeem.deliver") + */ + public function deliverAction($id) + { + $redeemService = new PointRedeemService(); + + $redeemService->deliver($id); + + return $this->jsonSuccess(['msg' => '发货成功']); + } + +} diff --git a/app/Http/Admin/Controllers/SettingController.php b/app/Http/Admin/Controllers/SettingController.php index 7d949a67..76cff636 100644 --- a/app/Http/Admin/Controllers/SettingController.php +++ b/app/Http/Admin/Controllers/SettingController.php @@ -50,7 +50,7 @@ class SettingController extends Controller $data = $this->request->getPost(); - $settingService->updateStorageSettings($section, $data); + $settingService->updateSettings($section, $data); return $this->jsonSuccess(['msg' => '更新配置成功']); @@ -248,6 +248,31 @@ class SettingController extends Controller } } + /** + * @Route("/point", name="admin.setting.point") + */ + public function pointAction() + { + $section = 'point'; + + $settingService = new SettingService(); + + if ($this->request->isPost()) { + + $data = $this->request->getPost(); + + $settingService->updatePointSettings($section, $data); + + return $this->jsonSuccess(['msg' => '更新配置成功']); + + } else { + + $point = $settingService->getSettings($section); + + $this->view->setVar('point', $point); + } + } + /** * @Route("/vip", name="admin.setting.vip") */ diff --git a/app/Http/Admin/Services/AuthNode.php b/app/Http/Admin/Services/AuthNode.php index 77104b1d..20dedbb5 100644 --- a/app/Http/Admin/Services/AuthNode.php +++ b/app/Http/Admin/Services/AuthNode.php @@ -476,6 +476,43 @@ class AuthNode extends Service ], ], ], + [ + 'id' => '2-8', + 'title' => '积分商城', + 'type' => 'menu', + 'children' => [ + [ + 'id' => '2-8-2', + 'title' => '礼品列表', + 'type' => 'menu', + 'route' => 'admin.point_gift.list', + ], + [ + 'id' => '2-8-1', + 'title' => '兑换记录', + 'type' => 'menu', + 'route' => 'admin.point_redeem.list', + ], + [ + 'id' => '2-8-3', + 'title' => '添加礼品', + 'type' => 'button', + 'route' => 'admin.point_gift.add', + ], + [ + 'id' => '2-8-4', + 'title' => '编辑礼品', + 'type' => 'button', + 'route' => 'admin.point_gift.edit', + ], + [ + 'id' => '2-8-5', + 'title' => '删除礼品', + 'type' => 'button', + 'route' => 'admin.point_gift.delete', + ], + ], + ], ], ]; } @@ -763,6 +800,12 @@ class AuthNode extends Service 'type' => 'menu', 'route' => 'admin.setting.wechat_oa', ], + [ + 'id' => '5-1-14', + 'title' => '积分设置', + 'type' => 'menu', + 'route' => 'admin.setting.point', + ], ], ], ], diff --git a/app/Http/Admin/Services/PointGift.php b/app/Http/Admin/Services/PointGift.php new file mode 100644 index 00000000..b87211fe --- /dev/null +++ b/app/Http/Admin/Services/PointGift.php @@ -0,0 +1,163 @@ +getParams(); + + $params['deleted'] = $params['deleted'] ?? 0; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $giftRepo = new PointGiftRepo(); + + return $giftRepo->paginate($params, $sort, $page, $limit); + } + + public function getGift($id) + { + return $this->findOrFail($id); + } + + public function createGift() + { + $post = $this->request->getPost(); + + $validator = new PointGiftValidator(); + + $post['type'] = $validator->checkType($post['type']); + + $gift = new PointGiftModel(); + + switch ($post['type']) { + case PointGiftModel::TYPE_COURSE: + $gift = $this->createCourseGift($post); + break; + case PointGiftModel::TYPE_GOODS: + $gift = $this->createCommodityGift($post); + break; + } + + return $gift; + } + + public function updateGift($id) + { + $gift = $this->findOrFail($id); + + $post = $this->request->getPost(); + + $validator = new PointGiftValidator(); + + $data = []; + + if (isset($post['cover'])) { + $data['cover'] = $validator->checkCover($post['cover']); + } + + if (isset($post['name'])) { + $data['name'] = $validator->checkName($post['name']); + } + + if (isset($post['details'])) { + $data['details'] = $validator->checkDetails($post['details']); + } + + if (isset($post['attrs'])) { + $data['attrs'] = $validator->checkAttrs($gift, $post['attrs']); + } + + if (isset($post['point'])) { + $data['point'] = $validator->checkPoint($post['point']); + } + + if (isset($post['stock'])) { + $data['stock'] = $validator->checkStock($post['stock']); + } + + if (isset($post['redeem_limit'])) { + $data['redeem_limit'] = $validator->checkRedeemLimit($post['redeem_limit']); + } + + if (isset($post['published'])) { + $data['published'] = $validator->checkPublishStatus($post['published']); + } + + $gift->update($data); + + return $gift; + } + + public function deleteGift($id) + { + $gift = $this->findOrFail($id); + + $gift->deleted = 1; + + $gift->update(); + + return $gift; + } + + public function restoreGift($id) + { + $gift = $this->findOrFail($id); + + $gift->deleted = 0; + + $gift->update(); + + return $gift; + } + + protected function createCourseGift($post) + { + $validator = new PointGiftValidator(); + + $course = $validator->checkCourse($post['course_id']); + + $gift = new PointGiftModel(); + + $gift->type = PointGiftModel::TYPE_COURSE; + $gift->name = $course->title; + + $gift->create(); + + return $gift; + } + + protected function createCommodityGift($post) + { + $validator = new PointGiftValidator(); + + $gift = new PointGiftModel(); + + $gift->type = PointGiftModel::TYPE_GOODS; + $gift->name = $validator->checkName($post['name']); + + $gift->create(); + + return $gift; + } + + protected function findOrFail($id) + { + $validator = new PointGiftValidator(); + + return $validator->checkGift($id); + } + +} diff --git a/app/Http/Admin/Services/PointRedeem.php b/app/Http/Admin/Services/PointRedeem.php new file mode 100644 index 00000000..f3812807 --- /dev/null +++ b/app/Http/Admin/Services/PointRedeem.php @@ -0,0 +1,58 @@ +getParams(); + + $params['deleted'] = $params['deleted'] ?? 0; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $redeemRepo = new PointRedeemRepo(); + + return $redeemRepo->paginate($params, $sort, $page, $limit); + } + + public function getRedeem($id) + { + return $this->findOrFail($id); + } + + public function deliver($id) + { + $redeem = $this->findOrFail($id); + + if ($redeem->gift_type != PointGiftModel::TYPE_GOODS) { + return $redeem; + } + + $redeem->status = PointRedeemModel::STATUS_FINISHED; + + $redeem->update(); + + return $redeem; + } + + protected function findOrFail($id) + { + $validator = new PointRedeemValidator(); + + return $validator->checkRedeem($id); + } + +} diff --git a/app/Http/Admin/Services/Refund.php b/app/Http/Admin/Services/Refund.php index e0df2bc9..89efeb70 100644 --- a/app/Http/Admin/Services/Refund.php +++ b/app/Http/Admin/Services/Refund.php @@ -103,9 +103,17 @@ class Refund extends Service $task = new TaskModel(); + $itemInfo = [ + 'refund' => [ + 'id' => $refund->id, + 'order_id' => $refund->order_id, + 'trade_id' => $refund->trade_id, + ], + ]; + $task->item_id = $refund->id; $task->item_type = TaskModel::TYPE_REFUND; - $task->item_info = ['refund' => $refund->toArray()]; + $task->item_info = $itemInfo; $task->priority = TaskModel::PRIORITY_HIGH; $task->status = TaskModel::STATUS_PENDING; diff --git a/app/Http/Admin/Services/Session.php b/app/Http/Admin/Services/Session.php index fae5222b..bd64ad6e 100644 --- a/app/Http/Admin/Services/Session.php +++ b/app/Http/Admin/Services/Session.php @@ -2,9 +2,7 @@ namespace App\Http\Admin\Services; -use App\Models\User as UserModel; use App\Services\Auth\Admin as AdminAuth; -use App\Services\Logic\Notice\AccountLogin as AccountLoginNoticeService; use App\Validators\Account as AccountValidator; use App\Validators\Captcha as CaptchaValidator; @@ -47,21 +45,18 @@ class Session extends Service $captchaValidator->checkCode($post['ticket'], $post['rand']); } - $this->handleLoginNotice($user); - $this->auth->saveAuthInfo($user); + + $this->eventsManager->fire('Account:afterLogin', $this, $user); } public function logout() { + $user = $this->getLoginUser(); + $this->auth->clearAuthInfo(); - } - protected function handleLoginNotice(UserModel $user) - { - $service = new AccountLoginNoticeService(); - - $service->createTask($user); + $this->eventsManager->fire('Account:afterLogout', $this, $user); } } diff --git a/app/Http/Admin/Services/Setting.php b/app/Http/Admin/Services/Setting.php index 87e2b6a4..c076eb4e 100644 --- a/app/Http/Admin/Services/Setting.php +++ b/app/Http/Admin/Services/Setting.php @@ -181,6 +181,19 @@ class Setting extends Service $this->updateSettings($section, $settings); } + public function updatePointSettings($section, $settings) + { + if (isset($settings['event_rule'])) { + $settings['event_rule'] = kg_json_encode($settings['event_rule']); + } + + if (isset($settings['consume_rule'])) { + $settings['consume_rule'] = kg_json_encode($settings['consume_rule']); + } + + $this->updateSettings($section, $settings); + } + public function updateVipSettings($items) { $vipRepo = new VipRepo(); diff --git a/app/Http/Admin/Services/Topic.php b/app/Http/Admin/Services/Topic.php index ac8289bf..7b0f2544 100644 --- a/app/Http/Admin/Services/Topic.php +++ b/app/Http/Admin/Services/Topic.php @@ -3,7 +3,6 @@ namespace App\Http\Admin\Services; use App\Caches\Topic as TopicCache; -use App\Caches\TopicCourseList as TopicCourseListCache; use App\Library\Paginator\Query as PagerQuery; use App\Models\CourseTopic as CourseTopicModel; use App\Models\Topic as TopicModel; @@ -66,8 +65,8 @@ class Topic extends Service $data = []; - if (isset($post['name'])) { - $data['name'] = $validator->checkName($post['name']); + if (isset($post['title'])) { + $data['title'] = $validator->checkTitle($post['title']); } if (isset($post['summary'])) { @@ -194,10 +193,6 @@ class Topic extends Service $cache = new TopicCache(); $cache->rebuild($topic->id); - - $cache = new TopicCourseListCache(); - - $cache->rebuild($topic->id); } protected function findOrFail($id) diff --git a/app/Http/Admin/Views/macros/point.volt b/app/Http/Admin/Views/macros/point.volt new file mode 100644 index 00000000..8b2d6093 --- /dev/null +++ b/app/Http/Admin/Views/macros/point.volt @@ -0,0 +1,61 @@ +{%- macro redeem_status_info(value) %} + {% if value == 1 %} + 处理中 + {% elseif value == 2 %} + 已完成 + {% elseif value == 3 %} + 已失败 + {% endif %} +{%- endmacro %} + +{%- macro gift_type_info(value) %} + {% if value == 1 %} + 课程 + {% elseif value == 2 %} + 商品 + {% elseif value == 3 %} + 现金 + {% endif %} +{%- endmacro %} + +{%- macro event_type_info(value) %} + {% if value == 1 %} + 订单消费 + {% elseif value == 2 %} + 积分兑换 + {% elseif value == 3 %} + 积分退款 + {% elseif value == 4 %} + 帐号注册 + {% elseif value == 5 %} + 站点访问 + {% elseif value == 6 %} + 课时学习 + {% elseif value == 7 %} + 课程评价 + {% elseif value == 8 %} + 微聊讨论 + {% endif %} +{%- endmacro %} + +{%- macro event_detail_info(history) %} + {% set event_info = history.event_info %} + {% if history.event_type == 1 %} +
{{ event_info.order.subject }}
+ {% elseif history.event_type == 2 %} +{{ event_info.point_redeem.gift_name }}
+ {% elseif history.event_type == 3 %} + {{ event_info.point_redeem.gift_name }} + {% elseif history.event_type == 4 %} + N/A + {% elseif history.event_type == 5 %} + N/A + {% elseif history.event_type == 6 %} +课程:{{ event_info.course.title }}
+章节:{{ event_info.chapter.title }}
+ {% elseif history.event_type == 7 %} +{{ event_info.course.title }}
+ {% elseif history.event_type == 8 %} + N/A + {% endif %} +{%- endmacro %} \ No newline at end of file diff --git a/app/Http/Admin/Views/point/gift/add.volt b/app/Http/Admin/Views/point/gift/add.volt new file mode 100644 index 00000000..f4ea046a --- /dev/null +++ b/app/Http/Admin/Views/point/gift/add.volt @@ -0,0 +1,62 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + + + +{% endblock %} + +{% block inline_js %} + + + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/point/gift/edit.volt b/app/Http/Admin/Views/point/gift/edit.volt new file mode 100644 index 00000000..9e43021f --- /dev/null +++ b/app/Http/Admin/Views/point/gift/edit.volt @@ -0,0 +1,27 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + + {% set update_url = url({'for':'admin.point_gift.update','id':gift.id}) %} + + {% if gift.type == 1 %} + {{ partial('point/gift/edit_course') }} + {% elseif gift.type == 2 %} + {{ partial('point/gift/edit_goods') }} + {% endif %} + +{% endblock %} + +{% block link_css %} + + {{ css_link('https://cdn.jsdelivr.net/npm/vditor/dist/index.css', false) }} + +{% endblock %} + +{% block include_js %} + + {{ js_include('https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js', false) }} + {{ js_include('admin/js/cover.upload.js') }} + {{ js_include('admin/js/vditor.js') }} + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/point/gift/edit_course.volt b/app/Http/Admin/Views/point/gift/edit_course.volt new file mode 100644 index 00000000..e6c7fcd6 --- /dev/null +++ b/app/Http/Admin/Views/point/gift/edit_course.volt @@ -0,0 +1,34 @@ + \ No newline at end of file diff --git a/app/Http/Admin/Views/point/gift/edit_goods.volt b/app/Http/Admin/Views/point/gift/edit_goods.volt new file mode 100644 index 00000000..ecfbc919 --- /dev/null +++ b/app/Http/Admin/Views/point/gift/edit_goods.volt @@ -0,0 +1,59 @@ + \ No newline at end of file diff --git a/app/Http/Admin/Views/point/gift/list.volt b/app/Http/Admin/Views/point/gift/list.volt new file mode 100644 index 00000000..eba95255 --- /dev/null +++ b/app/Http/Admin/Views/point/gift/list.volt @@ -0,0 +1,87 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + + {{ partial('macros/point') }} + + {% set add_url = url({'for':'admin.point_gift.add'}) %} + {% set search_url = url({'for':'admin.point_gift.search'}) %} + + + +编号 | +物品名称 | +所需积分 | +库存数量 | +兑换限额 | +兑换人次 | +发布 | +操作 | +
---|---|---|---|---|---|---|---|
{{ item.id }} | +{{ item.name }} {{ gift_type_info(item.type) }} | +{{ item.point }} | +{{ item.stock }} | +{{ item.redeem_limit }} | +{{ item.redeem_count }} | ++ | + + | +
物品名称 | +消耗积分 | +兑换状态 | +兑换时间 | +操作 | +
---|---|---|---|---|
+ {{ item.gift_name }}({{ item.gift_id }}){{ gift_type_info(item.gift_type) }} +用户名称:{{ item.user_name }} ({{ item.user_id }}) 联系方式: + + + |
+ {{ item.gift_point }} | +{{ redeem_status_info(item.status) }} | +{{ date('Y-m-d H:i',item.create_time) }} | ++ {% if item.gift_type == 2 %} + + {% else %} + N/A + {% endif %} + | +
开始:{{ date('Y-m-d H:i:s',item.create_time) }}
-结束:{{ date('Y-m-d H:i:s',item.expiry_time) }}
+开始:{{ date('Y-m-d H:i',item.create_time) }}
+结束:{{ date('Y-m-d H:i',item.expiry_time) }}