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/PointGiftAwardTask.php b/app/Console/Tasks/PointGiftAwardTask.php
new file mode 100644
index 00000000..72eec301
--- /dev/null
+++ b/app/Console/Tasks/PointGiftAwardTask.php
@@ -0,0 +1,213 @@
+getLogger('point');
+
+ $tasks = $this->findTasks();
+
+ if ($tasks->count() == 0) {
+ return;
+ }
+
+ $redeemRepo = new PointRedeemRepo();
+
+ foreach ($tasks as $task) {
+
+ $redeem = $redeemRepo->findById($task->item_id);
+
+ if (!$redeem) continue;
+
+ try {
+
+ switch ($redeem->gift_type) {
+ case PointGiftModel::TYPE_COURSE:
+ $this->handleCourseAward($redeem);
+ break;
+ case PointGiftModel::TYPE_GOODS:
+ $this->handleCommodityAward($redeem);
+ break;
+ }
+
+ $this->finishRedeem($redeem);
+
+ $task->status = TaskModel::STATUS_FINISHED;
+
+ $task->update();
+
+ } catch (\Exception $e) {
+
+ $task->try_count += 1;
+ $task->priority += 1;
+
+ if ($task->try_count > self::TRY_COUNT) {
+ $task->status = TaskModel::STATUS_FAILED;
+ }
+
+ $task->update();
+
+ $logger->info('Point Gift Award Exception ' . kg_json_encode([
+ 'code' => $e->getCode(),
+ 'message' => $e->getMessage(),
+ ]));
+ }
+
+ if ($task->status == TaskModel::STATUS_FINISHED) {
+ $this->handleFinishNotice();
+ } elseif ($task->status == TaskModel::STATUS_FAILED) {
+ $this->handlePointRefund($redeem);
+ }
+ }
+ }
+
+ protected function finishRedeem(PointRedeemModel $redeem)
+ {
+ $redeem->status = PointRedeemModel::STATUS_FINISHED;
+
+ if ($redeem->update() === false) {
+ throw new \RuntimeException('Finish Point Redeem Failed');
+ }
+ }
+
+ protected function handleCourseAward(PointRedeemModel $redeem)
+ {
+ $giftRepo = new PointGiftRepo();
+
+ $gift = $giftRepo->findById($redeem->gift_id);
+
+ $courseUser = new CourseUserModel();
+
+ $courseUser->user_id = $redeem->user_id;
+ $courseUser->course_id = $gift->attrs['id'];
+ $courseUser->expiry_time = $gift->attrs['study_expiry_time'];
+ $courseUser->role_type = CourseUserModel::ROLE_STUDENT;
+ $courseUser->source_type = CourseUserModel::SOURCE_POINT_REDEEM;
+
+ if ($courseUser->create() === false) {
+ throw new \RuntimeException('Create Course User Failed');
+ }
+
+ $courseRepo = new CourseRepo();
+
+ $group = $courseRepo->findImGroup($gift->attrs['id']);
+
+ $groupUserRepo = new ImGroupUserRepo();
+
+ $groupUser = $groupUserRepo->findGroupUser($group->id, $redeem->user_id);
+
+ if ($groupUser) return;
+
+ $groupUser = new ImGroupUserModel();
+
+ $groupUser->group_id = $group->id;
+ $groupUser->user_id = $redeem->user_id;
+
+ if ($groupUser->create() === false) {
+ throw new \RuntimeException('Create Im Group User Failed');
+ }
+ }
+
+ protected function handleCommodityAward(PointRedeemModel $redeem)
+ {
+
+ }
+
+ protected function handleFinishNotice()
+ {
+
+ }
+
+ protected function handlePointRefund(PointRedeemModel $redeem)
+ {
+ $logger = $this->getLogger('point');
+
+ $userRepo = new UserRepo();
+
+ $balance = $userRepo->findUserBalance($redeem->user_id);
+
+ try {
+
+ $this->db->begin();
+
+ $history = new PointHistoryModel();
+
+ $eventInfo = [
+ 'gift' => [
+ 'id' => $redeem->gift_id,
+ 'name' => $redeem->gift_name,
+ ]
+ ];
+
+ $history->user_id = $redeem->user_id;
+ $history->event_id = $redeem->id;
+ $history->event_type = PointHistoryModel::EVENT_POINT_REFUND;
+ $history->event_info = $eventInfo;
+
+ $result = $history->create();
+
+ if ($result === false) {
+ throw new \RuntimeException('Create Point History Failed');
+ }
+
+ $balance->point += $redeem->gift_point;
+
+ $result = $balance->update();
+
+ if ($result === false) {
+ throw new \RuntimeException('Update User Balance Failed');
+ }
+
+ $this->db->commit();
+
+ } catch (\Exception $e) {
+
+ $this->db->rollback();
+
+ $logger->error('Point Refund Exception ' . kg_json_encode([
+ 'code' => $e->getCode(),
+ 'message' => $e->getMessage(),
+ ]));
+ }
+ }
+
+ /**
+ * @param int $limit
+ * @return ResultsetInterface|Resultset|TaskModel[]
+ */
+ protected function findTasks($limit = 30)
+ {
+ $itemType = TaskModel::TYPE_POINT_GIFT_AWARD;
+ $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/SyncLearningTask.php b/app/Console/Tasks/SyncLearningTask.php
index af1f41aa..98284dfd 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->handleLearningPoint($chapterUser);
}
}
@@ -174,4 +179,14 @@ class SyncLearningTask extends Task
$courseUser->update();
}
+ /**
+ * @param ChapterUserModel $chapterUser
+ */
+ protected function handleLearningPoint(ChapterUserModel $chapterUser)
+ {
+ $service = new PointHistoryService();
+
+ $service->handleChapterLearning($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..fafda241
--- /dev/null
+++ b/app/Http/Admin/Controllers/PointGiftController.php
@@ -0,0 +1,128 @@
+getGifts();
+
+ $this->view->pick('point/gift/list');
+
+ $this->view->setVar('pager', $pager);
+ }
+
+ /**
+ * @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..2457c5de
--- /dev/null
+++ b/app/Http/Admin/Controllers/PointRedeemController.php
@@ -0,0 +1,60 @@
+getGroups();
+
+ $this->view->pick('point/redeem/list');
+
+ $this->view->setVar('pager', $pager);
+ }
+
+ /**
+ * @Get("/{id:[0-9]+}/edit", name="admin.point_redeem.edit")
+ */
+ public function editAction($id)
+ {
+ $groupService = new PointGiftService();
+
+ $group = $groupService->getGroup($id);
+
+ $this->view->pick('point/redeem/edit');
+
+ $this->view->setVar('group', $group);
+ }
+
+ /**
+ * @Post("/{id:[0-9]+}/update", name="admin.point_redeem.update")
+ */
+ public function updateAction($id)
+ {
+ $groupService = new PointGiftService();
+
+ $groupService->updateGroup($id);
+
+ $location = $this->url->get(['for' => 'admin.point_redeem.list']);
+
+ $content = [
+ 'location' => $location,
+ 'msg' => '更新群组成功',
+ ];
+
+ return $this->jsonSuccess($content);
+ }
+
+}
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..f29c29dc 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-1',
+ 'title' => '兑换记录',
+ 'type' => 'menu',
+ 'route' => 'admin.point_redeem.list',
+ ],
+ [
+ 'id' => '2-8-2',
+ 'title' => '礼品列表',
+ 'type' => 'menu',
+ 'route' => 'admin.point_gift.list',
+ ],
+ [
+ 'id' => '2-8-3',
+ 'title' => '添加礼品',
+ 'type' => 'menu',
+ '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..735dbc52
--- /dev/null
+++ b/app/Http/Admin/Services/PointGift.php
@@ -0,0 +1,155 @@
+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['point'])) {
+ $data['point'] = $validator->checkPoint($post['point']);
+ }
+
+ if (isset($post['stock'])) {
+ $data['stock'] = $validator->checkStock($post['stock']);
+ }
+
+ 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/Session.php b/app/Http/Admin/Services/Session.php
index fae5222b..ba18fda1 100644
--- a/app/Http/Admin/Services/Session.php
+++ b/app/Http/Admin/Services/Session.php
@@ -4,9 +4,10 @@ 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;
+use Phalcon\Di as Di;
+use Phalcon\Events\Manager as EventsManager;
class Session extends Service
{
@@ -47,21 +48,38 @@ class Session extends Service
$captchaValidator->checkCode($post['ticket'], $post['rand']);
}
- $this->handleLoginNotice($user);
-
$this->auth->saveAuthInfo($user);
+
+ $this->fireAfterLoginEvent($user);
}
public function logout()
{
+ $user = $this->getLoginUser();
+
$this->auth->clearAuthInfo();
+
+ $this->fireAfterLogoutEvent($user);
}
- protected function handleLoginNotice(UserModel $user)
+ protected function fireAfterLoginEvent(UserModel $user)
{
- $service = new AccountLoginNoticeService();
+ /**
+ * @var EventsManager $eventsManager
+ */
+ $eventsManager = Di::getDefault()->getShared('eventsManager');
- $service->createTask($user);
+ $eventsManager->fire('account:afterLogin', $this, $user);
+ }
+
+ protected function fireAfterLogoutEvent(UserModel $user)
+ {
+ /**
+ * @var EventsManager $eventsManager
+ */
+ $eventsManager = Di::getDefault()->getShared('eventsManager');
+
+ $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/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..d7aa4d3d
--- /dev/null
+++ b/app/Http/Admin/Views/point/gift/edit.volt
@@ -0,0 +1,108 @@
+{% extends 'templates/main.volt' %}
+
+{% block content %}
+
+ {% set update_url = url({'for':'admin.point_gift.update','id':gift.id}) %}
+
+ {% if gift.type == 1 %}
+
+ {% endif %}
+
+ {% if gift.type == 2 %}
+
+ {% 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/list.volt b/app/Http/Admin/Views/point/gift/list.volt
new file mode 100644
index 00000000..2ef3c8f9
--- /dev/null
+++ b/app/Http/Admin/Views/point/gift/list.volt
@@ -0,0 +1,83 @@
+{% extends 'templates/main.volt' %}
+
+{% block content %}
+
+ {%- macro type_info(value) %}
+ {% if value == 1 %}
+ 课程
+ {% elseif value == 2 %}
+ 商品
+ {% elseif value == 3 %}
+ 现金
+ {% else %}
+ 未知
+ {% endif %}
+ {%- endmacro %}
+
+
+
+
+
+ {{ partial('partials/pager') }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/Http/Admin/Views/point/gift/search.volt b/app/Http/Admin/Views/point/gift/search.volt
new file mode 100644
index 00000000..9ce882e2
--- /dev/null
+++ b/app/Http/Admin/Views/point/gift/search.volt
@@ -0,0 +1,64 @@
+{% extends 'templates/main.volt' %}
+
+{% block content %}
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/Http/Admin/Views/point/redeem/list.volt b/app/Http/Admin/Views/point/redeem/list.volt
new file mode 100644
index 00000000..7934d5b4
--- /dev/null
+++ b/app/Http/Admin/Views/point/redeem/list.volt
@@ -0,0 +1,86 @@
+{% extends 'templates/main.volt' %}
+
+{% block content %}
+
+ {%- macro type_info(value) %}
+ {% if value == 1 %}
+ 课
+ {% elseif value == 2 %}
+ 聊
+ {% elseif value == 3 %}
+ 职
+ {% else %}
+ 未知
+ {% endif %}
+ {%- endmacro %}
+
+ {%- macro owner_info(owner) %}
+ {% if owner.id is defined %}
+ {{ owner.name }}({{ owner.id }})
+ {% else %}
+ 未设置
+ {% endif %}
+ {%- endmacro %}
+
+
+
+
+
+ {{ partial('partials/pager') }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/Http/Admin/Views/setting/point.volt b/app/Http/Admin/Views/setting/point.volt
new file mode 100644
index 00000000..35083b42
--- /dev/null
+++ b/app/Http/Admin/Views/setting/point.volt
@@ -0,0 +1,112 @@
+{% extends 'templates/main.volt' %}
+
+{% block content %}
+
+ {% set consume_rule = point.consume_rule|json_decode %}
+ {% set event_rule = point.event_rule|json_decode %}
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/Http/Api/Controllers/Controller.php b/app/Http/Api/Controllers/Controller.php
index d33b4eeb..adf2b937 100644
--- a/app/Http/Api/Controllers/Controller.php
+++ b/app/Http/Api/Controllers/Controller.php
@@ -2,14 +2,22 @@
namespace App\Http\Api\Controllers;
-use App\Services\Auth\Api as AppAuth;
+use App\Models\User as UserModel;
+use App\Services\Auth\Api as ApiAuth;
use App\Traits\Response as ResponseTrait;
use App\Traits\Security as SecurityTrait;
+use Phalcon\Di as Di;
+use Phalcon\Events\Manager as EventsManager;
use Phalcon\Mvc\Dispatcher;
class Controller extends \Phalcon\Mvc\Controller
{
+ /**
+ * @var UserModel
+ */
+ protected $authUser;
+
use ResponseTrait;
use SecurityTrait;
@@ -19,21 +27,36 @@ class Controller extends \Phalcon\Mvc\Controller
$this->setCors();
}
- if (!$this->request->isOptions()) {
- $this->checkRateLimit();
- }
+ $this->checkRateLimit();
return true;
}
+ public function initialize()
+ {
+ $this->authUser = $this->getAuthUser();
+
+ $this->fireSiteViewEvent($this->authUser);
+ }
+
protected function getAuthUser()
{
/**
- * @var AppAuth $auth
+ * @var ApiAuth $auth
*/
$auth = $this->getDI()->get('auth');
return $auth->getCurrentUser();
}
+ protected function fireSiteViewEvent(UserModel $user)
+ {
+ /**
+ * @var EventsManager $eventsManager
+ */
+ $eventsManager = Di::getDefault()->getShared('eventsManager');
+
+ $eventsManager->fire('site:view', $this, $user);
+ }
+
}
diff --git a/app/Http/Api/Services/Account.php b/app/Http/Api/Services/Account.php
index 9ce43137..b187683c 100644
--- a/app/Http/Api/Services/Account.php
+++ b/app/Http/Api/Services/Account.php
@@ -6,8 +6,9 @@ use App\Models\User as UserModel;
use App\Repos\User as UserRepo;
use App\Services\Auth\Api as AuthService;
use App\Services\Logic\Account\Register as RegisterService;
-use App\Services\Logic\Notice\AccountLogin as AccountLoginNoticeService;
use App\Validators\Account as AccountValidator;
+use Phalcon\Di as Di;
+use Phalcon\Events\Manager as EventsManager;
class Account extends Service
{
@@ -32,7 +33,11 @@ class Account extends Service
$user = $userRepo->findById($account->id);
- return $this->auth->saveAuthInfo($user);
+ $token = $this->auth->saveAuthInfo($user);
+
+ $this->fireAfterRegisterEvent($user);
+
+ return $token;
}
public function loginByPassword()
@@ -52,9 +57,11 @@ class Account extends Service
$user = $validator->checkUserLogin($post['account'], $post['password']);
- $this->handleLoginNotice($user);
+ $token = $this->auth->saveAuthInfo($user);
- return $this->auth->saveAuthInfo($user);
+ $this->fireAfterLoginEvent($user);
+
+ return $token;
}
public function loginByVerify()
@@ -74,21 +81,50 @@ class Account extends Service
$user = $validator->checkVerifyLogin($post['account'], $post['verify_code']);
- $this->handleLoginNotice($user);
+ $token = $this->auth->saveAuthInfo($user);
- return $this->auth->saveAuthInfo($user);
+ $this->fireAfterLoginEvent($user);
+
+ return $token;
}
public function logout()
{
+ $user = $this->getLoginUser();
+
$this->auth->clearAuthInfo();
+
+ $this->fireAfterLogoutEvent($user);
}
- protected function handleLoginNotice(UserModel $user)
+ protected function fireAfterRegisterEvent(UserModel $user)
{
- $service = new AccountLoginNoticeService();
+ /**
+ * @var EventsManager $eventsManager
+ */
+ $eventsManager = Di::getDefault()->getShared('eventsManager');
- $service->createTask($user);
+ $eventsManager->fire('account:afterRegister', $this, $user);
+ }
+
+ protected function fireAfterLoginEvent(UserModel $user)
+ {
+ /**
+ * @var EventsManager $eventsManager
+ */
+ $eventsManager = Di::getDefault()->getShared('eventsManager');
+
+ $eventsManager->fire('account:afterLogin', $this, $user);
+ }
+
+ protected function fireAfterLogoutEvent(UserModel $user)
+ {
+ /**
+ * @var EventsManager $eventsManager
+ */
+ $eventsManager = Di::getDefault()->getShared('eventsManager');
+
+ $eventsManager->fire('account:afterLogout', $this, $user);
}
}
diff --git a/app/Http/Home/Controllers/Controller.php b/app/Http/Home/Controllers/Controller.php
index df84d9d2..c4ee0821 100644
--- a/app/Http/Home/Controllers/Controller.php
+++ b/app/Http/Home/Controllers/Controller.php
@@ -10,6 +10,8 @@ use App\Services\Auth\Home as HomeAuth;
use App\Services\Service as AppService;
use App\Traits\Response as ResponseTrait;
use App\Traits\Security as SecurityTrait;
+use Phalcon\Di as Di;
+use Phalcon\Events\Manager as EventsManager;
use Phalcon\Mvc\Dispatcher;
class Controller extends \Phalcon\Mvc\Controller
@@ -66,11 +68,7 @@ class Controller extends \Phalcon\Mvc\Controller
$this->checkCsrfToken();
}
- $config = $this->getConfig();
-
- if ($config->path('throttle.enabled')) {
- $this->checkRateLimit();
- }
+ $this->checkRateLimit();
return true;
}
@@ -82,6 +80,11 @@ class Controller extends \Phalcon\Mvc\Controller
$this->appInfo = $this->getAppInfo();
$this->imInfo = $this->getImInfo();
+ /**
+ * @todo 内部操作会改变afterFetch()
+ */
+ $this->fireSiteViewEvent($this->authUser);
+
$this->seo->setTitle($this->siteInfo['title']);
$this->view->setVar('seo', $this->seo);
@@ -158,4 +161,14 @@ class Controller extends \Phalcon\Mvc\Controller
return $appService->getSettings($section);
}
+ protected function fireSiteViewEvent(UserModel $user)
+ {
+ /**
+ * @var EventsManager $eventsManager
+ */
+ $eventsManager = Di::getDefault()->getShared('eventsManager');
+
+ $eventsManager->fire('site:view', $this, $user);
+ }
+
}
diff --git a/app/Http/Home/Controllers/PointGiftController.php b/app/Http/Home/Controllers/PointGiftController.php
new file mode 100644
index 00000000..9233679e
--- /dev/null
+++ b/app/Http/Home/Controllers/PointGiftController.php
@@ -0,0 +1,80 @@
+seo->prependTitle('积分兑换');
+
+ $this->view->pick('point/gift/list');
+ }
+
+ /**
+ * @Get("/pager", name="home.point_gift.pager")
+ */
+ public function pagerAction()
+ {
+ $service = new GiftListService();
+
+ $pager = $service->handle();
+
+ $pager->target = 'gift-list';
+
+ $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
+ $this->view->pick('point/gift/pager');
+ $this->view->setVar('pager', $pager);
+ }
+
+ /**
+ * @Get("/{id:[0-9]+}", name="home.point_gift.show")
+ */
+ public function showAction($id)
+ {
+ $service = new GiftInfoService();
+
+ $gift = $service->handle($id);
+
+ $hotGifts = $this->getHotGifts();
+
+ $this->seo->prependTitle(['积分兑换', $gift['name']]);
+
+ $this->view->pick('point/gift/show');
+ $this->view->setVar('gift', $gift);
+ $this->view->setVar('hot_gifts', $hotGifts);
+ }
+
+ /**
+ * @Post("/redeem", name="home.point_gift.redeem")
+ */
+ public function redeemAction()
+ {
+ $service = new GiftRedeemService();
+
+ $service->handle();
+
+ return $this->jsonSuccess(['msg' => '兑换成功']);
+ }
+
+ protected function getHotGifts()
+ {
+ $service = new HotGiftListService();
+
+ return $service->handle();
+ }
+
+}
diff --git a/app/Http/Home/Controllers/PointRedeemController.php b/app/Http/Home/Controllers/PointRedeemController.php
new file mode 100644
index 00000000..ea8856e2
--- /dev/null
+++ b/app/Http/Home/Controllers/PointRedeemController.php
@@ -0,0 +1,35 @@
+handle();
+
+ return $this->jsonSuccess(['msg' => '兑换成功']);
+ }
+
+ /**
+ * @Get("/list", name="home.point_gift.list")
+ */
+ public function listAction()
+ {
+ $this->seo->prependTitle('积分兑换');
+
+ $this->view->pick('point/gift/list');
+ }
+
+}
diff --git a/app/Http/Home/Controllers/UserConsoleController.php b/app/Http/Home/Controllers/UserConsoleController.php
index 12ed2de5..aceb0dc1 100644
--- a/app/Http/Home/Controllers/UserConsoleController.php
+++ b/app/Http/Home/Controllers/UserConsoleController.php
@@ -8,11 +8,14 @@ use App\Services\Logic\User\Console\AccountInfo as AccountInfoService;
use App\Services\Logic\User\Console\ConnectDelete as ConnectDeleteService;
use App\Services\Logic\User\Console\ConnectList as ConnectListService;
use App\Services\Logic\User\Console\ConsultList as ConsultListService;
+use App\Services\Logic\User\Console\ContactInfo as ContactInfoService;
+use App\Services\Logic\User\Console\ContactUpdate as ContactUpdateService;
use App\Services\Logic\User\Console\CourseList as CourseListService;
use App\Services\Logic\User\Console\FavoriteList as FavoriteListService;
use App\Services\Logic\User\Console\FriendList as FriendListService;
use App\Services\Logic\User\Console\GroupList as GroupListService;
use App\Services\Logic\User\Console\OrderList as OrderListService;
+use App\Services\Logic\User\Console\PointRedeemList as PointRedeemListService;
use App\Services\Logic\User\Console\ProfileInfo as ProfileInfoService;
use App\Services\Logic\User\Console\ProfileUpdate as ProfileUpdateService;
use App\Services\Logic\User\Console\RefundList as RefundListService;
@@ -67,6 +70,19 @@ class UserConsoleController extends Controller
$this->view->setVar('user', $user);
}
+ /**
+ * @Get("/contact", name="home.uc.contact")
+ */
+ public function contactAction()
+ {
+ $service = new ContactInfoService();
+
+ $contact = $service->handle();
+
+ $this->view->pick('user/console/contact');
+ $this->view->setVar('contact', $contact);
+ }
+
/**
* @Get("/account", name="home.uc.account")
*/
@@ -182,6 +198,19 @@ class UserConsoleController extends Controller
$this->view->setVar('pager', $pager);
}
+ /**
+ * @Get("/point_redeems", name="home.uc.point_redeems")
+ */
+ public function pointRedeemsAction()
+ {
+ $service = new PointRedeemListService();
+
+ $pager = $service->handle();
+
+ $this->view->pick('user/console/point_redeems');
+ $this->view->setVar('pager', $pager);
+ }
+
/**
* @Get("/friends", name="home.uc.friends")
*/
@@ -249,6 +278,20 @@ class UserConsoleController extends Controller
return $this->jsonSuccess($content);
}
+ /**
+ * @Post("/contact/update", name="home.uc.update_contact")
+ */
+ public function updateContactAction()
+ {
+ $service = new ContactUpdateService();
+
+ $service->handle();
+
+ $content = ['msg' => '更新收货信息成功'];
+
+ return $this->jsonSuccess($content);
+ }
+
/**
* @Post("/connect/{id:[0-9]+}/delete", name="home.uc.unconnect")
*/
diff --git a/app/Http/Home/Services/Account.php b/app/Http/Home/Services/Account.php
index 7f7fb06f..2145feef 100644
--- a/app/Http/Home/Services/Account.php
+++ b/app/Http/Home/Services/Account.php
@@ -6,9 +6,10 @@ use App\Models\User as UserModel;
use App\Repos\User as UserRepo;
use App\Services\Auth\Home as AuthService;
use App\Services\Logic\Account\Register as RegisterService;
-use App\Services\Logic\Notice\AccountLogin as AccountLoginNoticeService;
use App\Validators\Account as AccountValidator;
use App\Validators\Captcha as CaptchaValidator;
+use Phalcon\Di as Di;
+use Phalcon\Events\Manager as EventsManager;
class Account extends Service
{
@@ -35,6 +36,8 @@ class Account extends Service
$this->auth->saveAuthInfo($user);
+ $this->fireAfterRegisterEvent($user);
+
return $user;
}
@@ -50,9 +53,9 @@ class Account extends Service
$validator->checkCode($post['ticket'], $post['rand']);
- $this->handleLoginNotice($user);
-
$this->auth->saveAuthInfo($user);
+
+ $this->fireAfterLoginEvent($user);
}
public function loginByVerify()
@@ -63,21 +66,48 @@ class Account extends Service
$user = $validator->checkVerifyLogin($post['account'], $post['verify_code']);
- $this->handleLoginNotice($user);
-
$this->auth->saveAuthInfo($user);
+
+ $this->fireAfterLoginEvent($user);
}
public function logout()
{
+ $user = $this->getLoginUser();
+
$this->auth->clearAuthInfo();
+
+ $this->fireAfterLogoutEvent($user);
}
- protected function handleLoginNotice(UserModel $user)
+ protected function fireAfterRegisterEvent(UserModel $user)
{
- $service = new AccountLoginNoticeService();
+ /**
+ * @var EventsManager $eventsManager
+ */
+ $eventsManager = Di::getDefault()->getShared('eventsManager');
- $service->createTask($user);
+ $eventsManager->fire('account:afterRegister', $this, $user);
+ }
+
+ protected function fireAfterLoginEvent(UserModel $user)
+ {
+ /**
+ * @var EventsManager $eventsManager
+ */
+ $eventsManager = Di::getDefault()->getShared('eventsManager');
+
+ $eventsManager->fire('account:afterLogin', $this, $user);
+ }
+
+ protected function fireAfterLogoutEvent(UserModel $user)
+ {
+ /**
+ * @var EventsManager $eventsManager
+ */
+ $eventsManager = Di::getDefault()->getShared('eventsManager');
+
+ $eventsManager->fire('account:afterLogout', $this, $user);
}
}
diff --git a/app/Http/Home/Views/macros/point.volt b/app/Http/Home/Views/macros/point.volt
new file mode 100644
index 00000000..76f53ac8
--- /dev/null
+++ b/app/Http/Home/Views/macros/point.volt
@@ -0,0 +1,9 @@
+{%- macro gift_type_info(value) %}
+ {% if value == 1 %}
+ 课程
+ {% elseif value == 2 %}
+ 商品
+ {% elseif value == 3 %}
+ 现金
+ {% endif %}
+{%- endmacro %}
\ No newline at end of file
diff --git a/app/Http/Home/Views/partials/header.volt b/app/Http/Home/Views/partials/header.volt
index 1222e867..10279917 100644
--- a/app/Http/Home/Views/partials/header.volt
+++ b/app/Http/Home/Views/partials/header.volt
@@ -32,6 +32,9 @@
搜索
+
+ 积分
+
会员
diff --git a/app/Http/Home/Views/point/gift/list.volt b/app/Http/Home/Views/point/gift/list.volt
new file mode 100644
index 00000000..6db04fc4
--- /dev/null
+++ b/app/Http/Home/Views/point/gift/list.volt
@@ -0,0 +1,20 @@
+{% extends 'templates/main.volt' %}
+
+{% block content %}
+
+ {% set pager_url = url({'for':'home.point_gift.pager'}) %}
+
+
+
+
+
+{% endblock %}
+
+{% block include_js %}
+
+ {{ js_include('home/js/point.gift.list.js') }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/Http/Home/Views/point/gift/pager.volt b/app/Http/Home/Views/point/gift/pager.volt
new file mode 100644
index 00000000..0a4ae098
--- /dev/null
+++ b/app/Http/Home/Views/point/gift/pager.volt
@@ -0,0 +1,31 @@
+{{ partial('macros/point') }}
+
+{% if pager.total_pages > 0 %}
+
+
+ {% for item in pager.items %}
+ {% set gift_url = url({'for':'home.point_gift.show','id':item.id}) %}
+
+
+
+
+
+
+ {{ gift_type_info(item.type) }}
+ {{ item.point }} 积分
+ {{ item.redeem_count }} 人兑换
+
+
+
+
+ {% endfor %}
+
+
+ {{ partial('partials/pager_ajax') }}
+{% endif %}
\ No newline at end of file
diff --git a/app/Http/Home/Views/point/gift/show.volt b/app/Http/Home/Views/point/gift/show.volt
new file mode 100644
index 00000000..c8aee4bb
--- /dev/null
+++ b/app/Http/Home/Views/point/gift/show.volt
@@ -0,0 +1,89 @@
+{% extends 'templates/main.volt' %}
+
+{% block content %}
+
+ {% set gift_redeem_url = url({'for':'home.point_redeem.create'}) %}
+ {% set gift_list_url = url({'for':'home.point_gift.list'}) %}
+
+
+
+
+
+{% endblock %}
+
+{% block link_css %}
+
+ {{ css_link('home/css/markdown.css') }}
+
+{% endblock %}
+
+{% block include_js %}
+
+ {{ js_include('home/js/point.gift.show.js') }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/Http/Home/Views/user/console/contact.volt b/app/Http/Home/Views/user/console/contact.volt
new file mode 100644
index 00000000..fa129d27
--- /dev/null
+++ b/app/Http/Home/Views/user/console/contact.volt
@@ -0,0 +1,69 @@
+{% extends 'templates/main.volt' %}
+
+{% block content %}
+
+ {% set update_url = url({'for':'home.uc.update_contact'}) %}
+
+
+
+
+
+
+ 收货信息
+
+
+
+
+
+
+{% endblock %}
+
+{% block include_js %}
+
+ {{ js_include('home/js/user.console.contact.js') }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/Http/Home/Views/user/console/menu.volt b/app/Http/Home/Views/user/console/menu.volt
index e670e951..e22925c3 100644
--- a/app/Http/Home/Views/user/console/menu.volt
+++ b/app/Http/Home/Views/user/console/menu.volt
@@ -51,6 +51,7 @@