1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-06-18 01:58:25 +08:00

疫情期间更新

This commit is contained in:
xiaochong0302 2020-03-16 15:33:36 +08:00
parent 57957f19eb
commit 7a4aa88218
343 changed files with 9583 additions and 7306 deletions

View File

@ -2,9 +2,9 @@
namespace App\Builders; namespace App\Builders;
use Phalcon\Mvc\User\Component as UserComponent; use Phalcon\Mvc\User\Component;
class Builder extends UserComponent class Builder extends Component
{ {
public function arrayToObject($array) public function arrayToObject($array)

View File

@ -2,7 +2,7 @@
namespace App\Builders; namespace App\Builders;
class CategoryList extends Builder class CategoryTreeList extends Builder
{ {
public function handleTreeList($categories) public function handleTreeList($categories)

View File

@ -4,7 +4,7 @@ namespace App\Builders;
use App\Models\Course as CourseModel; use App\Models\Course as CourseModel;
class ChapterList extends Builder class ChapterTreeList extends Builder
{ {
/** /**

View File

@ -0,0 +1,99 @@
<?php
namespace App\Builders;
use App\Repos\Chapter as ChapterRepo;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class CommentList extends Builder
{
public function handleCourses($comments)
{
$courses = $this->getCourses($comments);
foreach ($comments as $key => $comment) {
$comments[$key]['course'] = $courses[$comment['course_id']] ?? [];
}
return $comments;
}
public function handleChapters($comments)
{
$chapters = $this->getChapters($comments);
foreach ($comments as $key => $comment) {
$comments[$key]['chapter'] = $chapters[$comment['chapter_id']] ?? [];
}
return $comments;
}
public function handleUsers($comments)
{
$users = $this->getUsers($comments);
foreach ($comments as $key => $comment) {
$comments[$key]['user'] = $users[$comment['user_id']] ?? [];
}
return $comments;
}
public function getCourses($comments)
{
$ids = kg_array_column($comments, 'course_id');
$courseRepo = new CourseRepo();
$courses = $courseRepo->findByIds($ids, ['id', 'title']);
$result = [];
foreach ($courses->toArray() as $course) {
$result[$course['id']] = $course;
}
return $result;
}
public function getChapters($comments)
{
$ids = kg_array_column($comments, 'chapter_id');
$chapterRepo = new ChapterRepo();
$chapters = $chapterRepo->findByIds($ids, ['id', 'title']);
$result = [];
foreach ($chapters->toArray() as $chapter) {
$result[$chapter['id']] = $chapter;
}
return $result;
}
public function getUsers($comments)
{
$ids = kg_array_column($comments, 'user_id');
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$imgBaseUrl = kg_img_base_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $imgBaseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -0,0 +1,70 @@
<?php
namespace App\Builders;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class ConsultList extends Builder
{
public function handleCourses($consults)
{
$courses = $this->getCourses($consults);
foreach ($consults as $key => $consult) {
$consults[$key]['course'] = $courses[$consult['course_id']] ?? [];
}
return $consults;
}
public function handleUsers($consults)
{
$users = $this->getUsers($consults);
foreach ($consults as $key => $consult) {
$consults[$key]['user'] = $users[$consult['user_id']] ?? [];
}
return $consults;
}
public function getCourses($consults)
{
$ids = kg_array_column($consults, 'course_id');
$courseRepo = new CourseRepo();
$courses = $courseRepo->findByIds($ids, ['id', 'title']);
$result = [];
foreach ($courses->toArray() as $course) {
$result[$course['id']] = $course;
}
return $result;
}
public function getUsers($consults)
{
$ids = kg_array_column($consults, 'user_id');
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$imgBaseUrl = kg_img_base_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $imgBaseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace App\Builders;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class CourseFavoriteList extends Builder
{
public function handleCourses($relations)
{
$courses = $this->getCourses($relations);
foreach ($relations as $key => $value) {
$relations[$key]['course'] = $courses[$value['course_id']];
}
return $relations;
}
public function handleUsers($relations)
{
$users = $this->getUsers($relations);
foreach ($relations as $key => $value) {
$relations[$key]['user'] = $users[$value['user_id']];
}
return $relations;
}
public function getCourses($relations)
{
$ids = kg_array_column($relations, 'course_id');
$courseRepo = new CourseRepo();
$columns = [
'id', 'title', 'cover', 'summary',
'market_price', 'vip_price', 'model', 'level', 'attrs',
'user_count', 'lesson_count', 'review_count', 'favorite_count',
];
$courses = $courseRepo->findByIds($ids, $columns);
$imgBaseUrl = kg_img_base_url();
$result = [];
foreach ($courses->toArray() as $course) {
$course['cover'] = $imgBaseUrl . $course['cover'];
$course['attrs'] = json_decode($course['attrs'], true);
$result[$course['id']] = $course;
}
return $result;
}
public function getUsers($relations)
{
$ids = kg_array_column($relations, 'user_id');
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$imgBaseUrl = kg_img_base_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $imgBaseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -13,7 +13,7 @@ class CourseList extends Builder
{ {
$imgBaseUrl = kg_img_base_url(); $imgBaseUrl = kg_img_base_url();
$result = []; $list = [];
foreach ($courses as $course) { foreach ($courses as $course) {
@ -36,13 +36,13 @@ class CourseList extends Builder
'categories' => $course['categories'], 'categories' => $course['categories'],
'user_count' => $course['user_count'], 'user_count' => $course['user_count'],
'lesson_count' => $course['lesson_count'], 'lesson_count' => $course['lesson_count'],
'thread_count' => $course['thread_count'], 'comment_count' => $course['comment_count'],
'review_count' => $course['review_count'], 'review_count' => $course['review_count'],
'favorite_count' => $course['favorite_count'], 'favorite_count' => $course['favorite_count'],
]; ];
} }
return $result; return $list;
} }
public function handleCategories($courses) public function handleCategories($courses)

View File

@ -30,34 +30,47 @@ class CourseUserList extends Builder
return $relations; return $relations;
} }
protected function getCourses($relations) public function getCourses($relations)
{ {
$ids = kg_array_column($relations, 'course_id'); $ids = kg_array_column($relations, 'course_id');
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
$courses = $courseRepo->findByIds($ids, ['id', 'title', 'cover'])->toArray(); $columns = [
'id', 'title', 'cover', 'summary',
'market_price', 'vip_price', 'model', 'level', 'attrs',
'user_count', 'lesson_count', 'review_count', 'favorite_count',
];
$courses = $courseRepo->findByIds($ids, $columns);
$imgBaseUrl = kg_img_base_url();
$result = []; $result = [];
foreach ($courses as $course) { foreach ($courses->toArray() as $course) {
$course['cover'] = $imgBaseUrl . $course['cover'];
$course['attrs'] = json_decode($course['attrs'], true);
$result[$course['id']] = $course; $result[$course['id']] = $course;
} }
return $result; return $result;
} }
protected function getUsers($relations) public function getUsers($relations)
{ {
$ids = kg_array_column($relations, 'user_id'); $ids = kg_array_column($relations, 'user_id');
$userRepo = new UserRepo(); $userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar'])->toArray(); $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$imgBaseUrl = kg_img_base_url();
$result = []; $result = [];
foreach ($users as $user) { foreach ($users->toArray() as $user) {
$user['avatar'] = $imgBaseUrl . $user['avatar'];
$result[$user['id']] = $user; $result[$user['id']] = $user;
} }

View File

@ -48,11 +48,11 @@ class LearningList extends Builder
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
$courses = $courseRepo->findByIds($ids, ['id', 'title', 'cover'])->toArray(); $courses = $courseRepo->findByIds($ids, ['id', 'title', 'cover']);
$result = []; $result = [];
foreach ($courses as $course) { foreach ($courses->toArray() as $course) {
$result[$course['id']] = $course; $result[$course['id']] = $course;
} }
@ -65,11 +65,11 @@ class LearningList extends Builder
$chapterRepo = new ChapterRepo(); $chapterRepo = new ChapterRepo();
$chapters = $chapterRepo->findByIds($ids, ['id', 'title'])->toArray(); $chapters = $chapterRepo->findByIds($ids, ['id', 'title']);
$result = []; $result = [];
foreach ($chapters as $chapter) { foreach ($chapters->toArray() as $chapter) {
$result[$chapter['id']] = $chapter; $result[$chapter['id']] = $chapter;
} }
@ -82,11 +82,11 @@ class LearningList extends Builder
$userRepo = new UserRepo(); $userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar'])->toArray(); $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$result = []; $result = [];
foreach ($users as $user) { foreach ($users->toArray() as $user) {
$result[$user['id']] = $user; $result[$user['id']] = $user;
} }

View File

@ -2,7 +2,7 @@
namespace App\Builders; namespace App\Builders;
class NavList extends Builder class NavTreeList extends Builder
{ {
public function handleTreeList($navs) public function handleTreeList($navs)

View File

@ -7,6 +7,7 @@ use App\Repos\User as UserRepo;
class OrderList extends Builder class OrderList extends Builder
{ {
protected $imgBaseUrl; protected $imgBaseUrl;
public function __construct() public function __construct()
@ -14,28 +15,64 @@ class OrderList extends Builder
$this->imgBaseUrl = kg_img_base_url(); $this->imgBaseUrl = kg_img_base_url();
} }
public function handleItems($orders) /**
* @param array $orders
* @return array
*/
public function handleUsers(array $orders)
{ {
$itemInfo = []; $users = $this->getUsers($orders);
foreach ($orders as $key => $order) { foreach ($orders as $key => $order) {
switch ($order['item_type']) { $orders[$key]['user'] = $users[$order['user_id']];
case OrderModel::TYPE_COURSE: }
$itemInfo = $this->handleCourseInfo($order['item_info']);
break; return $orders;
case OrderModel::TYPE_PACKAGE: }
$itemInfo = $this->handlePackageInfo($order['item_info']);
break; /**
case OrderModel::TYPE_REWARD: * @param array $orders
$itemInfo = $this->handleRewardInfo($order['item_info']); * @return array
break; */
} public function handleItems(array $orders)
{
foreach ($orders as $key => $order) {
$itemInfo = $this->handleItem($order);
$orders[$key]['item_info'] = $itemInfo; $orders[$key]['item_info'] = $itemInfo;
} }
return $orders; return $orders;
} }
/**
* @param array $order
* @return array|mixed
*/
public function handleItem(array $order)
{
$itemInfo = [];
switch ($order['item_type']) {
case OrderModel::ITEM_COURSE:
$itemInfo = $this->handleCourseInfo($order['item_info']);
break;
case OrderModel::ITEM_PACKAGE:
$itemInfo = $this->handlePackageInfo($order['item_info']);
break;
case OrderModel::ITEM_VIP:
$itemInfo = $this->handleVipInfo($order['item_info']);
break;
}
return $itemInfo;
}
/**
* @param string $itemInfo
* @return mixed
*/
protected function handleCourseInfo($itemInfo) protected function handleCourseInfo($itemInfo)
{ {
if (!empty($itemInfo) && is_string($itemInfo)) { if (!empty($itemInfo) && is_string($itemInfo)) {
@ -46,6 +83,10 @@ class OrderList extends Builder
return $itemInfo; return $itemInfo;
} }
/**
* @param string $itemInfo
* @return mixed
*/
protected function handlePackageInfo($itemInfo) protected function handlePackageInfo($itemInfo)
{ {
if (!empty($itemInfo) && is_string($itemInfo)) { if (!empty($itemInfo) && is_string($itemInfo)) {
@ -58,6 +99,10 @@ class OrderList extends Builder
return $itemInfo; return $itemInfo;
} }
/**
* @param string $itemInfo
* @return mixed
*/
protected function handleRewardInfo($itemInfo) protected function handleRewardInfo($itemInfo)
{ {
if (!empty($itemInfo) && is_string($itemInfo)) { if (!empty($itemInfo) && is_string($itemInfo)) {
@ -68,28 +113,34 @@ class OrderList extends Builder
return $itemInfo; return $itemInfo;
} }
public function handleUsers($orders) /**
* @param string $itemInfo
* @return mixed
*/
protected function handleVipInfo($itemInfo)
{ {
$users = $this->getUsers($orders); if (!empty($itemInfo) && is_string($itemInfo)) {
$itemInfo = json_decode($itemInfo, true);
foreach ($orders as $key => $order) {
$orders[$key]['user'] = $users[$order['user_id']];
} }
return $orders; return $itemInfo;
} }
protected function getUsers($orders) /**
* @param array $orders
* @return array
*/
protected function getUsers(array $orders)
{ {
$ids = kg_array_column($orders, 'user_id'); $ids = kg_array_column($orders, 'user_id');
$userRepo = new UserRepo(); $userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar'])->toArray(); $users = $userRepo->findByIds($ids, ['id', 'name']);
$result = []; $result = [];
foreach ($users as $user) { foreach ($users->toArray() as $user) {
$result[$user['id']] = $user; $result[$user['id']] = $user;
} }

View File

@ -2,11 +2,23 @@
namespace App\Builders; namespace App\Builders;
use App\Repos\Order as OrderRepo;
use App\Repos\User as UserRepo; use App\Repos\User as UserRepo;
class RefundList extends Builder class RefundList extends Builder
{ {
public function handleOrders($trades)
{
$orders = $this->getOrders($trades);
foreach ($trades as $key => $trade) {
$trades[$key]['order'] = $orders[$trade['order_id']];
}
return $trades;
}
public function handleUsers($refunds) public function handleUsers($refunds)
{ {
$users = $this->getUsers($refunds); $users = $this->getUsers($refunds);
@ -18,22 +30,34 @@ class RefundList extends Builder
return $refunds; return $refunds;
} }
protected function getUsers($refunds) public function getOrders($trades)
{
$ids = kg_array_column($trades, 'order_id');
$orderRepo = new OrderRepo();
$orders = $orderRepo->findByIds($ids, ['id', 'sn', 'subject', 'amount']);
$result = [];
foreach ($orders->toArray() as $order) {
$result[$order['id']] = $order;
}
return $result;
}
public function getUsers($refunds)
{ {
$ids = kg_array_column($refunds, 'user_id'); $ids = kg_array_column($refunds, 'user_id');
$userRepo = new UserRepo(); $userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']); $users = $userRepo->findByIds($ids, ['id', 'name']);
$result = []; $result = [];
$imgBaseUrl = kg_img_base_url();
foreach ($users->toArray() as $user) { foreach ($users->toArray() as $user) {
$user['avatar'] = $imgBaseUrl . $user['avatar'];
$result[$user['id']] = $user; $result[$user['id']] = $user;
} }

View File

@ -30,7 +30,7 @@ class ReviewList extends Builder
return $reviews; return $reviews;
} }
protected function getCourses($reviews) public function getCourses($reviews)
{ {
$ids = kg_array_column($reviews, 'course_id'); $ids = kg_array_column($reviews, 'course_id');
@ -47,7 +47,7 @@ class ReviewList extends Builder
return $result; return $result;
} }
protected function getUsers($reviews) public function getUsers($reviews)
{ {
$ids = kg_array_column($reviews, 'user_id'); $ids = kg_array_column($reviews, 'user_id');
@ -55,10 +55,10 @@ class ReviewList extends Builder
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']); $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$result = [];
$imgBaseUrl = kg_img_base_url(); $imgBaseUrl = kg_img_base_url();
$result = [];
foreach ($users->toArray() as $user) { foreach ($users->toArray() as $user) {
$user['avatar'] = $imgBaseUrl . $user['avatar']; $user['avatar'] = $imgBaseUrl . $user['avatar'];
$result[$user['id']] = $user; $result[$user['id']] = $user;

View File

@ -2,11 +2,23 @@
namespace App\Builders; namespace App\Builders;
use App\Repos\Order as OrderRepo;
use App\Repos\User as UserRepo; use App\Repos\User as UserRepo;
class TradeList extends Builder class TradeList extends Builder
{ {
public function handleOrders($trades)
{
$orders = $this->getOrders($trades);
foreach ($trades as $key => $trade) {
$trades[$key]['order'] = $orders[$trade['order_id']];
}
return $trades;
}
public function handleUsers($trades) public function handleUsers($trades)
{ {
$users = $this->getUsers($trades); $users = $this->getUsers($trades);
@ -18,17 +30,34 @@ class TradeList extends Builder
return $trades; return $trades;
} }
protected function getUsers($trades) public function getOrders($trades)
{
$ids = kg_array_column($trades, 'order_id');
$orderRepo = new OrderRepo();
$orders = $orderRepo->findByIds($ids, ['id', 'sn', 'subject', 'amount']);
$result = [];
foreach ($orders->toArray() as $order) {
$result[$order['id']] = $order;
}
return $result;
}
public function getUsers($trades)
{ {
$ids = kg_array_column($trades, 'user_id'); $ids = kg_array_column($trades, 'user_id');
$userRepo = new UserRepo(); $userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar'])->toArray(); $users = $userRepo->findByIds($ids, ['id', 'name']);
$result = []; $result = [];
foreach ($users as $user) { foreach ($users->toArray() as $user) {
$result[$user['id']] = $user; $result[$user['id']] = $user;
} }

View File

@ -32,7 +32,7 @@ class UserList extends Builder
public function handleEduRoles($users) public function handleEduRoles($users)
{ {
$roles = $this->getEduRoles($users); $roles = $this->getEduRoles();
foreach ($users as $key => $user) { foreach ($users as $key => $user) {
$users[$key]['edu_role'] = $roles[$user['edu_role']] ?? ['id' => 0, 'name' => 'N/A']; $users[$key]['edu_role'] = $roles[$user['edu_role']] ?? ['id' => 0, 'name' => 'N/A'];
@ -41,24 +41,24 @@ class UserList extends Builder
return $users; return $users;
} }
private function getAdminRoles($users) protected function getAdminRoles($users)
{ {
$ids = kg_array_column($users, 'admin_role'); $ids = kg_array_column($users, 'admin_role');
$roleRepo = new RoleRepo(); $roleRepo = new RoleRepo();
$roles = $roleRepo->findByIds($ids, ['id', 'name'])->toArray(); $roles = $roleRepo->findByIds($ids, ['id', 'name']);
$result = []; $result = [];
foreach ($roles as $role) { foreach ($roles->toArray() as $role) {
$result[$role['id']] = $role; $result[$role['id']] = $role;
} }
return $result; return $result;
} }
private function getEduRoles() protected function getEduRoles()
{ {
$result = [ $result = [
UserModel::EDU_ROLE_STUDENT => [ UserModel::EDU_ROLE_STUDENT => [

View File

@ -2,11 +2,14 @@
namespace App\Caches; namespace App\Caches;
abstract class Cache extends \Phalcon\Mvc\User\Component use Phalcon\Cache\Backend\Redis as RedisCache;
use Phalcon\Mvc\User\Component;
abstract class Cache extends Component
{ {
/** /**
* @var \Phalcon\Cache\Backend\Redis * @var RedisCache
*/ */
protected $cache; protected $cache;

View File

@ -3,6 +3,7 @@
namespace App\Caches; namespace App\Caches;
use App\Models\Category as CategoryModel; use App\Models\Category as CategoryModel;
use Phalcon\Mvc\Model\Resultset;
class CategoryList extends Cache class CategoryList extends Cache
{ {
@ -25,6 +26,9 @@ class CategoryList extends Cache
*/ */
public function getContent($id = null) public function getContent($id = null)
{ {
/**
* @var Resultset $categories
*/
$categories = CategoryModel::query() $categories = CategoryModel::query()
->columns(['id', 'parent_id', 'name', 'priority', 'level', 'path']) ->columns(['id', 'parent_id', 'name', 'priority', 'level', 'path'])
->where('published = 1 AND deleted = 0') ->where('published = 1 AND deleted = 0')

View File

@ -2,8 +2,9 @@
namespace App\Caches; namespace App\Caches;
use App\Builders\CategoryList as CategoryTreeListBuilder; use App\Builders\CategoryTreeList as CategoryTreeListBuilder;
use App\Models\Category as CategoryModel; use App\Models\Category as CategoryModel;
use Phalcon\Mvc\Model\Resultset;
class CategoryTreeList extends Cache class CategoryTreeList extends Cache
{ {
@ -17,11 +18,14 @@ class CategoryTreeList extends Cache
public function getKey($id = null) public function getKey($id = null)
{ {
return 'category_tree'; return 'category_tree_list';
} }
public function getContent($id = null) public function getContent($id = null)
{ {
/**
* @var Resultset $categories
*/
$categories = CategoryModel::query() $categories = CategoryModel::query()
->where('published = 1 AND deleted = 0') ->where('published = 1 AND deleted = 0')
->execute(); ->execute();
@ -34,7 +38,7 @@ class CategoryTreeList extends Cache
} }
/** /**
* @param \App\Models\Category[] $categories * @param Resultset $categories
* @return array * @return array
*/ */
protected function handleContent($categories) protected function handleContent($categories)

View File

@ -1,40 +0,0 @@
<?php
namespace App\Caches;
use App\Repos\Chapter as ChapterRepo;
class ChapterCounter extends Counter
{
protected $lifetime = 7 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "chapter_counter:{$id}";
}
public function getContent($id = null)
{
$chapterRepo = new ChapterRepo();
$chapter = $chapterRepo->findById($id);
if (!$chapter) return [];
$content = [
'lesson_count' => $chapter->lesson_count,
'user_count' => $chapter->user_count,
'comment_count' => $chapter->comment_count,
'like_count' => $chapter->like_count,
];
return $content;
}
}

View File

@ -2,10 +2,11 @@
namespace App\Caches; namespace App\Caches;
use App\Builders\ChapterList as ChapterListBuilder; use App\Builders\ChapterTreeList as ChapterTreeListBuilder;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use Phalcon\Mvc\Model\Resultset;
class CourseChapterList extends Cache class ChapterTreeList extends Cache
{ {
protected $lifetime = 7 * 86400; protected $lifetime = 7 * 86400;
@ -17,13 +18,16 @@ class CourseChapterList extends Cache
public function getKey($id = null) public function getKey($id = null)
{ {
return "course_chapter_list:{$id}"; return "chapter_tree_list:{$id}";
} }
public function getContent($id = null) public function getContent($id = null)
{ {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset $chapters
*/
$chapters = $courseRepo->findChapters($id); $chapters = $courseRepo->findChapters($id);
if ($chapters->count() == 0) { if ($chapters->count() == 0) {
@ -34,14 +38,14 @@ class CourseChapterList extends Cache
} }
/** /**
* @param \App\Models\Chapter[] $chapters * @param Resultset $chapters
* @return array * @return array
*/ */
protected function handleContent($chapters) protected function handleContent($chapters)
{ {
$items = $chapters->toArray(); $items = $chapters->toArray();
$builder = new ChapterListBuilder(); $builder = new ChapterTreeListBuilder();
$content = $builder->handleTreeList($items); $content = $builder->handleTreeList($items);

View File

@ -2,11 +2,14 @@
namespace App\Caches; namespace App\Caches;
abstract class Counter extends \Phalcon\Mvc\User\Component use App\Library\Cache\Backend\Redis as RedisCache;
use Phalcon\Mvc\User\Component;
abstract class Counter extends Component
{ {
/** /**
* @var \App\Library\Cache\Backend\Redis * @var RedisCache
*/ */
protected $cache; protected $cache;
@ -40,16 +43,6 @@ abstract class Counter extends \Phalcon\Mvc\User\Component
$lifetime = $this->getLifetime(); $lifetime = $this->getLifetime();
/**
* 原始内容为空,设置较短的生存时间,简单防止穿透
*/
if (!$content) {
$lifetime = 5 * 60;
$content = ['default' => 0];
}
$this->redis->hMSet($key, $content); $this->redis->hMSet($key, $content);
$this->redis->expire($key, $lifetime); $this->redis->expire($key, $lifetime);
@ -82,17 +75,47 @@ abstract class Counter extends \Phalcon\Mvc\User\Component
$this->get($id); $this->get($id);
} }
public function increment($id, $hashKey, $value = 1) public function hGet($id, $hashKey)
{ {
$key = $this->getKey($id); $key = $this->getKey($id);
if (!$this->redis->exists($key)) {
$this->get($id);
}
$value = $this->redis->hGet($key, $hashKey);
return $value;
}
public function hDel($id, $hashKey)
{
$key = $this->getKey($id);
$value = $this->redis->hDel($key, $hashKey);
return $value;
}
public function hIncrBy($id, $hashKey, $value = 1)
{
$key = $this->getKey($id);
if (!$this->redis->exists($key)) {
$this->get($id);
}
$this->redis->hIncrBy($key, $hashKey, $value); $this->redis->hIncrBy($key, $hashKey, $value);
} }
public function decrement($id, $hashKey, $value = 1) public function hDecrBy($id, $hashKey, $value = 1)
{ {
$key = $this->getKey($id); $key = $this->getKey($id);
if (!$this->redis->exists($key)) {
$this->get($id);
}
$this->redis->hIncrBy($key, $hashKey, 0 - $value); $this->redis->hIncrBy($key, $hashKey, 0 - $value);
} }

View File

@ -25,6 +25,8 @@ class Course extends Cache
$course = $courseRepo->findById($id); $course = $courseRepo->findById($id);
$course->cover = kg_img_url($course->cover);
if (!$course) { if (!$course) {
return new \stdClass(); return new \stdClass();
} }

View File

@ -3,6 +3,7 @@
namespace App\Caches; namespace App\Caches;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use Phalcon\Mvc\Model\Resultset;
class CourseCategoryList extends Cache class CourseCategoryList extends Cache
{ {
@ -23,6 +24,9 @@ class CourseCategoryList extends Cache
{ {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset $categories
*/
$categories = $courseRepo->findCategories($id); $categories = $courseRepo->findCategories($id);
if ($categories->count() == 0) { if ($categories->count() == 0) {
@ -33,7 +37,7 @@ class CourseCategoryList extends Cache
} }
/** /**
* @param \App\Models\Category[] $categories * @param Resultset $categories
* @return array * @return array
*/ */
public function handleContent($categories) public function handleContent($categories)

View File

@ -1,41 +0,0 @@
<?php
namespace App\Caches;
use App\Repos\Course as CourseRepo;
class CourseCounter extends Counter
{
protected $lifetime = 7 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "course_counter:{$id}";
}
public function getContent($id = null)
{
$courseRepo = new CourseRepo();
$course = $courseRepo->findById($id);
if (!$course) return [];
$content = [
'user_count' => $course->user_count,
'lesson_count' => $course->lesson_count,
'comment_count' => $course->comment_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
return $content;
}
}

View File

@ -2,7 +2,10 @@
namespace App\Caches; namespace App\Caches;
use App\Models\Package as PackageModel;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use App\Repos\Package as PackageRepo;
use Phalcon\Mvc\Model\Resultset;
class CoursePackageList extends Cache class CoursePackageList extends Cache
{ {
@ -23,6 +26,9 @@ class CoursePackageList extends Cache
{ {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset $packages
*/
$packages = $courseRepo->findPackages($id); $packages = $courseRepo->findPackages($id);
if ($packages->count() == 0) { if ($packages->count() == 0) {
@ -33,7 +39,7 @@ class CoursePackageList extends Cache
} }
/** /**
* @param \App\Models\Package[] $packages * @param PackageModel[] $packages
* @return array * @return array
*/ */
protected function handleContent($packages) protected function handleContent($packages)
@ -64,15 +70,21 @@ class CoursePackageList extends Cache
$result = []; $result = [];
$imgBaseUrl = kg_img_base_url();
foreach ($courses as $course) { foreach ($courses as $course) {
$course->cover = $imgBaseUrl . $course->cover;
$result[] = [ $result[] = [
'id' => $course->id, 'id' => $course->id,
'model' => $course->model,
'title' => $course->title, 'title' => $course->title,
'summary' => $course->summary,
'cover' => $course->cover, 'cover' => $course->cover,
'summary' => $course->summary,
'market_price' => $course->market_price, 'market_price' => $course->market_price,
'vip_price' => $course->vip_price, 'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
]; ];
} }

View File

@ -2,7 +2,9 @@
namespace App\Caches; namespace App\Caches;
use App\Models\Course as CourseModel;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use Phalcon\Mvc\Model\Resultset;
class CourseRelatedList extends Cache class CourseRelatedList extends Cache
{ {
@ -23,6 +25,9 @@ class CourseRelatedList extends Cache
{ {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset $courses
*/
$courses = $courseRepo->findRelatedCourses($id); $courses = $courseRepo->findRelatedCourses($id);
if ($courses->count() == 0) { if ($courses->count() == 0) {
@ -34,22 +39,28 @@ class CourseRelatedList extends Cache
/** /**
* @param \App\Models\Course[] $courses * @param CourseModel[] $courses
* @return array * @return array
*/ */
public function handleContent($courses) public function handleContent($courses)
{ {
$result = []; $result = [];
$imgBaseUrl = kg_img_base_url();
foreach ($courses as $course) { foreach ($courses as $course) {
$course->cover = $imgBaseUrl . $course->cover;
$result[] = [ $result[] = [
'id' => $course->id, 'id' => $course->id,
'model' => $course->model,
'title' => $course->title, 'title' => $course->title,
'cover' => $course->cover, 'cover' => $course->cover,
'summary' => $course->summary, 'summary' => $course->summary,
'market_price' => $course->market_price, 'market_price' => $course->market_price,
'vip_price' => $course->vip_price, 'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
]; ];
} }

View File

@ -2,7 +2,9 @@
namespace App\Caches; namespace App\Caches;
use App\Models\User as UserModel;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use Phalcon\Mvc\Model\Resultset;
class CourseTeacherList extends Cache class CourseTeacherList extends Cache
{ {
@ -23,6 +25,9 @@ class CourseTeacherList extends Cache
{ {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset $users
*/
$users = $courseRepo->findTeachers($id); $users = $courseRepo->findTeachers($id);
if ($users->count() == 0) { if ($users->count() == 0) {
@ -33,14 +38,19 @@ class CourseTeacherList extends Cache
} }
/** /**
* @param \App\Models\User[] $users * @param UserModel[] $users
* @return array * @return array
*/ */
public function handleContent($users) public function handleContent($users)
{ {
$result = []; $result = [];
$imgBaseUrl = kg_img_base_url();
foreach ($users as $user) { foreach ($users as $user) {
$user->avatar = $imgBaseUrl . $user->avatar;
$result[] = [ $result[] = [
'id' => $user->id, 'id' => $user->id,
'name' => $user->name, 'name' => $user->name,

View File

@ -2,12 +2,14 @@
namespace App\Caches; namespace App\Caches;
use App\Models\Course as CourseModel;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use Phalcon\Mvc\Model\Resultset;
class HotCourseList extends Cache class HotCourseList extends Cache
{ {
protected $lifetime = 7 * 86400; protected $lifetime = 86400;
public function getLifetime() public function getLifetime()
{ {
@ -23,6 +25,9 @@ class HotCourseList extends Cache
{ {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset $courses
*/
$courses = $courseRepo->findRelatedCourses($id); $courses = $courseRepo->findRelatedCourses($id);
if ($courses->count() == 0) { if ($courses->count() == 0) {
@ -32,9 +37,8 @@ class HotCourseList extends Cache
return $this->handleContent($courses); return $this->handleContent($courses);
} }
/** /**
* @param \App\Models\Course[] $courses * @param CourseModel[] $courses
* @return array * @return array
*/ */
public function handleContent($courses) public function handleContent($courses)
@ -44,12 +48,13 @@ class HotCourseList extends Cache
foreach ($courses as $course) { foreach ($courses as $course) {
$result[] = [ $result[] = [
'id' => $course->id, 'id' => $course->id,
'model' => $course->model,
'title' => $course->title, 'title' => $course->title,
'summary' => $course->summary, 'summary' => $course->summary,
'cover' => $course->cover, 'cover' => $course->cover,
'market_price' => $course->market_price, 'market_price' => $course->market_price,
'vip_price' => $course->vip_price, 'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
]; ];
} }

View File

@ -2,7 +2,9 @@
namespace App\Caches; namespace App\Caches;
use App\Models\Course as CourseModel;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use Phalcon\Mvc\Model\Resultset;
class LatestCourseList extends Cache class LatestCourseList extends Cache
{ {
@ -23,6 +25,9 @@ class LatestCourseList extends Cache
{ {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset $courses
*/
$courses = $courseRepo->findRelatedCourses($id); $courses = $courseRepo->findRelatedCourses($id);
if ($courses->count() == 0) { if ($courses->count() == 0) {
@ -34,22 +39,28 @@ class LatestCourseList extends Cache
/** /**
* @param \App\Models\Course[] $courses * @param CourseModel[] $courses
* @return array * @return array
*/ */
public function handleContent($courses) public function handleContent($courses)
{ {
$result = []; $result = [];
$imgBaseUrl = kg_img_base_url();
foreach ($courses as $course) { foreach ($courses as $course) {
$course->cover = $imgBaseUrl . $course->cover;
$result[] = [ $result[] = [
'id' => $course->id, 'id' => $course->id,
'model' => $course->model,
'title' => $course->title, 'title' => $course->title,
'summary' => $course->summary, 'summary' => $course->summary,
'cover' => $course->cover, 'cover' => $course->cover,
'market_price' => $course->market_price, 'market_price' => $course->market_price,
'vip_price' => $course->vip_price, 'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
]; ];
} }

View File

@ -2,8 +2,9 @@
namespace App\Caches; namespace App\Caches;
use App\Builders\NavList as NavListBuilder; use App\Builders\NavTreeList as NavTreeListBuilder;
use App\Models\Nav as NavModel; use App\Models\Nav as NavModel;
use Phalcon\Mvc\Model\Resultset;
class NavTreeList extends Cache class NavTreeList extends Cache
{ {
@ -22,6 +23,9 @@ class NavTreeList extends Cache
public function getContent($id = null) public function getContent($id = null)
{ {
/**
* @var Resultset $navs
*/
$navs = NavModel::query() $navs = NavModel::query()
->where('published = 1 AND deleted = 0') ->where('published = 1 AND deleted = 0')
->orderBy('position ASC, priority ASC') ->orderBy('position ASC, priority ASC')
@ -35,7 +39,7 @@ class NavTreeList extends Cache
} }
/** /**
* @param \App\Models\Nav[] $navs * @param Resultset $navs
* @return array * @return array
*/ */
protected function handleContent($navs) protected function handleContent($navs)
@ -53,7 +57,7 @@ class NavTreeList extends Cache
} }
} }
$builder = new NavListBuilder(); $builder = new NavTreeListBuilder();
$content = [ $content = [
'top' => $builder->handleTreeList($list['top']), 'top' => $builder->handleTreeList($list['top']),

View File

@ -3,6 +3,7 @@
namespace App\Caches; namespace App\Caches;
use App\Models\Slide as SlideModel; use App\Models\Slide as SlideModel;
use Phalcon\Mvc\Model\Resultset;
class SlideList extends Cache class SlideList extends Cache
{ {
@ -21,6 +22,9 @@ class SlideList extends Cache
public function getContent($id = null) public function getContent($id = null)
{ {
/**
* @var Resultset $slides
*/
$slides = SlideModel::query() $slides = SlideModel::query()
->columns(['id', 'title', 'cover', 'summary', 'target', 'content']) ->columns(['id', 'title', 'cover', 'summary', 'target', 'content'])
->where('published = 1 AND deleted = 0') ->where('published = 1 AND deleted = 0')
@ -35,7 +39,7 @@ class SlideList extends Cache
} }
/** /**
* @param \App\Models\Slide[] $slides * @param SlideModel[] $slides
* @return array * @return array
*/ */
protected function handleContent($slides) protected function handleContent($slides)

View File

@ -1,38 +0,0 @@
<?php
namespace App\Caches;
use App\Repos\User as UserRepo;
class UserCounter extends Counter
{
protected $lifetime = 7 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "user_counter:{$id}";
}
public function getContent($id = null)
{
$userRepo = new UserRepo();
$user = $userRepo->findById($id);
if (!$user) return [];
$content = [
'notice_count' => $user->notice_count,
'msg_count' => $user->msg_count,
];
return $content;
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace App\Caches;
class UserDailyCounter extends Counter
{
protected $lifetime = 86400;
public function getLifetime()
{
$tomorrow = strtotime('tomorrow');
$lifetime = $tomorrow - time();
return $lifetime;
}
public function getKey($id = null)
{
return "user_daily_counter:{$id}";
}
public function getContent($id = null)
{
$content = [
'favorite_count' => 0,
'comment_count' => 0,
'consult_count' => 0,
'order_count' => 0,
'chapter_vote_count' => 0,
'comment_vote_count' => 0,
'consult_vote_count' => 0,
'review_vote_count' => 0,
];
return $content;
}
}

View File

@ -1,91 +0,0 @@
<?php
namespace App\Console\Tasks;
use App\Models\User;
use App\Services\Storage;
use Phalcon\Cli\Task;
use Phalcon\Text;
class AvatarSyncTask extends Task
{
public function mainAction()
{
$limit = 250;
foreach (range(1, 2) as $page) {
$offset = ($page - 1) * $limit;
$users = User::query()
->where('edu_role = 1')
->limit($limit, $offset)
->execute();
if ($users->count() > 0) {
$this->handleUsers($users);
}
}
}
protected function handleUsers($users)
{
$storage = new Storage();
foreach ($users as $user) {
$avatar = $user->avatar;
if (!$avatar) {
continue;
}
if (Text::startsWith($avatar, '/img/avatar')) {
continue;
}
if (Text::startsWith($avatar, '//')) {
$avatar = 'http:' . $avatar;
}
$url = str_replace(['-40-40', '-80-80', '-140-140', '-160-160'], '-200-200', $avatar);
$fileName = parse_url($url, PHP_URL_PATH);
$filePath = tmp_path('avatar') . $fileName;
$content = file_get_contents($url);
if ($content === false) {
echo "get user {$user->id} avatar failed" . PHP_EOL;
continue;
}
$put = file_put_contents($filePath, $content);
if ($put === false) {
echo "put user {$user->id} cover failed" . PHP_EOL;
continue;
}
$keyName = $this->getKeyName($filePath);
$remoteUrl = $storage->putFile($keyName, $filePath);
if ($remoteUrl) {
$user->avatar = $keyName;
$user->deleted = 2;
$user->update();
echo "upload avatar of user {$user->id} success" . PHP_EOL;
} else {
echo "upload avatar of user {$user->id} failed" . PHP_EOL;
}
}
}
protected function getKeyName($filePath)
{
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
return '/img/avatar/' . date('YmdHis') . rand(1000, 9999) . '.' . $ext;
}
}

View File

@ -1,511 +0,0 @@
<?php
namespace App\Console\Tasks;
use App\Models\Chapter as ChapterModel;
use App\Models\Consult as ConsultModel;
use App\Models\Course as CourseModel;
use App\Models\CoursePackage as CoursePackageModel;
use App\Models\CourseUser as CourseUserModel;
use App\Models\Package as PackageModel;
use App\Models\Review as ReviewModel;
use App\Models\User as UserModel;
use App\Repos\CoursePackage as CoursePackageRepo;
use Phalcon\Cli\Task;
use QL\QueryList;
class ClassSpiderTask extends Task
{
public function listAction()
{
$ql = QueryList::rules([
'course_link' => ['div.shizhan-course-wrap > a', 'href'],
'course_cover' => ['div.img-box > img.shizhan-course-img', 'src'],
'course_title' => ['div.img-box > img.shizhan-course-img', 'alt'],
]);
$this->handleList($ql);
}
protected function handleList($ql)
{
foreach (range(1, 6) as $page) {
$url = "https://coding.imooc.com/?sort=0&unlearn=0&page={$page}";
echo "============== Page {$page} =================" . PHP_EOL;
$data = $ql->get($url)->query()->getData();
if ($data->count() == 0) {
break;
}
foreach ($data->all() as $item) {
$courseData = [
'class_id' => $this->getCourseId($item['course_link']),
'title' => $item['course_title'],
'cover' => $item['course_cover'],
];
//print_r($courseData);
if ($courseData['class_id']) {
$course = CourseModel::findFirstByClassId($courseData['class_id']);
if (!$course) {
$course = new CourseModel();
$course->create($courseData);
}
}
}
}
$ql->destruct();
}
public function courseAction()
{
$courses = CourseModel::query()
->where('class_id > 114')
->orderBy('class_id ASC')
->execute();
foreach ($courses as $course) {
$this->handleCourse($course);
sleep(5);
}
}
public function chapterAction()
{
$ql = QueryList::rules([
'chapter_title' => ['.chapter-bd > h5.name', 'text'],
'chapter_summary' => ['.chapter-bd > p.desc', 'text'],
'lesson_html' => ['.chapter-bd > ul', 'html'],
]);
$courses = CourseModel::query()
->where('class_id > 114')
->orderBy('class_id ASC')
->execute();
foreach ($courses as $course) {
$this->handleChapter($ql, $course);
sleep(5);
}
}
protected function handleChapter(QueryList $ql, $course)
{
echo " course id: {$course->id} , class_id :{$course->class_id} " . PHP_EOL;
$url = "https://coding.imooc.com/class/chapter/{$course->class_id}.html";
$data = $ql->get($url)->query()->getData();
if ($data->count() == 0) {
return;
}
$lesson_ql = QueryList::rules([
'lesson_title' => ['span.title_info', 'text'],
'lesson_free' => ['span.watch-free', 'text'],
]);
foreach ($data->all() as $item) {
$chapterData = [
'course_id' => $course->id,
'title' => trim($item['chapter_title']),
'summary' => trim($item['chapter_summary']),
];
$chapter = new ChapterModel();
$chapter->create($chapterData);
$this->handleLesson($chapter, $lesson_ql, $item['lesson_html']);
}
$ql->destruct();
}
protected function handleLesson($chapter, QueryList $lesson_ql, $html)
{
$lessons = $lesson_ql->html($html)->query()->getData();
if ($lessons->count() == 0) {
return;
}
foreach ($lessons->all() as $item) {
$data = [
'course_id' => $chapter->course_id,
'parent_id' => $chapter->id,
'title' => $item['lesson_title'],
'free' => $item['lesson_free'] ? 1 : 0,
];
$model = new ChapterModel();
$model->create($data);
}
$lesson_ql->destruct();
}
public function consultAction()
{
$courses = CourseModel::query()
->where('class_id > 0')
->orderBy('class_id ASC')
->execute();
foreach ($courses as $course) {
$this->handleConsult($course);
sleep(5);
}
}
protected function handleConsult($course)
{
foreach (range(1, 20) as $page) {
echo "course {$course->id}, page {$page}" . PHP_EOL;
$url = "https://coding.imooc.com/class/ajaxconsultsearch?cid={$course->class_id}&page={$page}&pagesize=15";
$content = file_get_contents($url);
$json = json_decode($content, true);
$consults = $json['data']['data_adv'];
if (empty($consults)) {
break;
}
foreach ($consults as $item) {
$data = [
'question' => $item['content'],
'answer' => $item['answer'],
'like_count' => $item['praise'],
'created_at' => strtotime($item['create_time']),
];
$consult = new ConsultModel();
$consult->create($data);
}
}
}
public function reviewAction()
{
$ql = QueryList::rules([
'review_content' => ['p.cmt-txt', 'text'],
'review_rating' => ['div.stars > span', 'text'],
]);
$courses = CourseModel::query()
->where('class_id > 0')
->orderBy('class_id ASC')
->execute();
foreach ($courses as $course) {
$this->handleReview($ql, $course);
sleep(5);
}
}
protected function handleReview($ql, $course)
{
foreach (range(1, 10) as $page) {
$url = "https://coding.imooc.com/class/evaluation/{$course->class_id}.html?page={$page}";
echo "============== Course {$course->id}, Page {$page} =================" . PHP_EOL;
$data = $ql->get($url)->query()->getData();
if ($data->count() == 0) {
break;
}
foreach ($data->all() as $item) {
$reviewData = [
'course_id' => $course->id,
'content' => $item['review_content'],
'rating' => $this->getReviewRating($item['review_rating']),
];
$review = new ReviewModel();
$review->create($reviewData);
}
}
$ql->destruct();
}
public function packageAction()
{
$ql = QueryList::rules([
'id' => ['a.js-buy-package', 'data-cid'],
'title' => ['p.package-title', 'text'],
'price' => ['p.package-price', 'text'],
'other_html' => ['div.other-course-wrap', 'html'],
]);
$courses = CourseModel::query()
->where('class_id > 0')
->orderBy('class_id ASC')
->execute();
foreach ($courses as $course) {
$this->handlePackage($ql, $course);
sleep(5);
}
}
protected function handlePackage(QueryList $ql, $course)
{
echo " course id: {$course->id} , class_id :{$course->class_id} " . PHP_EOL;
$url = "https://coding.imooc.com/class/package/{$course->class_id}.html";
$data = $ql->get($url)->query()->getData();
if ($data->count() == 0) {
return;
}
$other_ql = QueryList::rules([
'href' => ['a.course-item', 'href'],
]);
foreach ($data->all() as $item) {
$packageData = [
'id' => trim($item['id']),
'title' => trim($item['title']),
'market_price' => $this->getMarketPrice($item['price']),
];
$package = PackageModel::findFirst($packageData['id']);
if (!$package) {
$package = new PackageModel();
$package->create($packageData);
}
$cpRepo = new CoursePackageRepo();
$cp = $cpRepo->findCoursePackage($course->id, $package->id);
if (!$cp) {
$cp = new CoursePackageModel();
$cp->course_id = $course->id;
$cp->package_id = $package->id;
$cp->create();
}
$this->handleOtherPackageCourse($package, $other_ql, $item['other_html']);
}
$ql->destruct();
}
protected function handleOtherPackageCourse($package, QueryList $other_ql, $html)
{
$courses = $other_ql->html($html)->query()->getData();
if ($courses->count() == 0) {
return;
}
foreach ($courses->all() as $item) {
$courseId = str_replace(['//coding.imooc.com/class/', '.html'], '', $item['href']);
$cpRepo = new CoursePackageRepo();
$cp = $cpRepo->findCoursePackage($courseId, $package->id);
if (!$cp) {
$cp = new CoursePackageModel();
$cp->course_id = (int)$courseId;
$cp->package_id = $package->id;
$cp->create();
}
}
$other_ql->destruct();
}
protected function handleCourse($course)
{
echo " =============== class id {$course->class_id} ============" . PHP_EOL;
$url = "https://coding.imooc.com/class/{$course->class_id}.html";
$ql = QueryList::getInstance()->get($url);
$summary = $ql->find('div.info-desc')->text();
$userLink = $ql->find('div.teacher > a')->attr('href');
$marketPrice = $ql->find('div.ori-price')->text();
$level = $ql->find('div.info-bar > span:eq(1)')->text();
$duration = $ql->find('div.info-bar > span:eq(3)')->text();
$userCount = $ql->find('div.info-bar > span:eq(5)')->text();
$score = $ql->find('div.info-bar > span:eq(7)')->text();
$courseData = [
'summary' => trim($summary),
'user_count' => intval($userCount),
'market_price' => $this->getMarketPrice($marketPrice),
'level' => $this->getLevel($level),
'score' => $this->getScore($score),
'attrs' => [
'duration' => $this->getCourseDuration($duration),
],
];
$course->update($courseData);
$ql->destruct();
$userId = $this->getUserId($userLink);
$user = UserModel::findFirst($userId);
if ($user) {
$user->edu_role = UserModel::EDU_ROLE_TEACHER;
$user->update();
$cuRepo = new \App\Repos\CourseUser();
$row = $cuRepo->findCourseTeacher($course->id, $user->id);
if (!$row) {
$courseUser = new CourseUserModel();
$courseUser->course_id = $course->id;
$courseUser->user_id = $user->id;
$courseUser->role_type = CourseUserModel::ROLE_TEACHER;
$courseUser->expire_time = strtotime('+15 years');
$courseUser->create();
}
}
$this->handleTeacherInfo($userId);
}
protected function handleTeacherInfo($id)
{
$url = 'http://www.imooc.com/t/' . $id;
$ql = QueryList::getInstance()->get($url);
$data = [];
$data['id'] = $id;
$data['avatar'] = $ql->find('img.tea-header')->attr('src');
$data['name'] = $ql->find('p.tea-nickname')->text();
$data['title'] = $ql->find('p.tea-professional')->text();
$data['about'] = $ql->find('p.tea-desc')->text();
$user = UserModel::findFirst($id);
if (!$user) {
$user = new UserModel();
$user->create($data);
}
$ql->destruct();
}
protected function getUserId($userLink)
{
$result = str_replace(['http://www.imooc.com/u/'], '', $userLink);
return trim($result);
}
protected function getCourseId($courseLink)
{
if (!strpos($courseLink, '.html')) {
return false;
}
$result = str_replace(['/class/', '.html'], '', $courseLink);
return trim($result);
}
protected function getMarketPrice($price)
{
$price = str_replace('¥', '', $price);
return floatval(trim($price));
}
protected function getScore($score)
{
return floatval(trim($score) * 10);
}
protected function getCourseDuration($duration)
{
$hours = 0;
$minutes = 0;
if (preg_match('/(.*?)小时(.*?)分/s', $duration, $matches)) {
$hours = trim($matches[1]);
$minutes = trim($matches[2]);
} elseif (preg_match('/(.*?)小时/s', $duration, $matches)) {
$hours = trim($matches[1]);
} elseif (preg_match('/(.*?)分/s', $duration, $matches)) {
$minutes = trim($matches[1]);
}
return 3600 * $hours + 60 * $minutes;
}
protected function getChapterDuration($duration)
{
if (strpos($duration, ':') === false) {
return 0;
}
list($minutes, $seconds) = explode(':', trim($duration));
return 60 * $minutes + $seconds;
}
protected function getLevel($type)
{
$mapping = [
'入门' => CourseModel::LEVEL_ENTRY,
'初级' => CourseModel::LEVEL_JUNIOR,
'中级' => CourseModel::LEVEL_MEDIUM,
'高级' => CourseModel::LEVEL_SENIOR,
];
return $mapping[$type] ?? CourseModel::LEVEL_ENTRY;
}
protected function getReviewRating($type)
{
$mapping = [
'好评' => 10,
'中评' => 8,
'差评' => 6,
];
return $mapping[$type] ?? 8;
}
}

View File

@ -11,6 +11,7 @@ class CleanLogTask extends Task
{ {
$this->cleanCommonLog(); $this->cleanCommonLog();
$this->cleanConsoleLog(); $this->cleanConsoleLog();
$this->cleanHttpLog();
$this->cleanSqlLog(); $this->cleanSqlLog();
$this->cleanListenerLog(); $this->cleanListenerLog();
$this->cleanCaptchaLog(); $this->cleanCaptchaLog();
@ -19,7 +20,7 @@ class CleanLogTask extends Task
$this->cleanVodLog(); $this->cleanVodLog();
$this->cleanStorageLog(); $this->cleanStorageLog();
$this->cleanAlipayLog(); $this->cleanAlipayLog();
$this->cleanWxpayLog(); $this->cleanWechatLog();
$this->cleanRefundLog(); $this->cleanRefundLog();
} }
@ -31,6 +32,14 @@ class CleanLogTask extends Task
$this->cleanLog('common', 7); $this->cleanLog('common', 7);
} }
/**
* 清理Http日志
*/
protected function cleanHttpLog()
{
$this->cleanLog('http', 7);
}
/** /**
* 清理Console日志 * 清理Console日志
*/ */
@ -106,9 +115,9 @@ class CleanLogTask extends Task
/** /**
* 清理微信支付服务日志 * 清理微信支付服务日志
*/ */
protected function cleanWxpayLog() protected function cleanWechatLog()
{ {
$this->cleanLog('wxpay', 30); $this->cleanLog('wechat', 30);
} }
/** /**

View File

@ -4,6 +4,8 @@ namespace App\Console\Tasks;
use App\Models\Order as OrderModel; use App\Models\Order as OrderModel;
use Phalcon\Cli\Task; use Phalcon\Cli\Task;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class CloseOrderTask extends Task class CloseOrderTask extends Task
{ {
@ -26,7 +28,7 @@ class CloseOrderTask extends Task
* 查找待关闭订单 * 查找待关闭订单
* *
* @param int $limit * @param int $limit
* @return \Phalcon\Mvc\Model\ResultsetInterface * @return ResultsetInterface|Resultset|OrderModel[]
*/ */
protected function findOrders($limit = 1000) protected function findOrders($limit = 1000)
{ {

View File

@ -1,40 +0,0 @@
<?php
namespace App\Console\Tasks;
use App\Models\Slide as SlideModel;
use Phalcon\Cli\Task;
class CloseSlideTask extends Task
{
public function mainAction()
{
$slides = $this->findSlides();
if ($slides->count() == 0) {
return;
}
foreach ($slides as $slide) {
$slide->published = 0;
$slide->update();
}
}
/**
* 查找待关闭轮播
*
* @return \Phalcon\Mvc\Model\ResultsetInterface
*/
protected function findSlides()
{
$Slides = SlideModel::query()
->where('published = 1')
->andWhere('end_time < :end_time:', ['end_time' => time()])
->execute();
return $Slides;
}
}

View File

@ -4,7 +4,10 @@ namespace App\Console\Tasks;
use App\Models\Trade as TradeModel; use App\Models\Trade as TradeModel;
use App\Services\Alipay as AlipayService; use App\Services\Alipay as AlipayService;
use App\Services\Wechat as WechatService;
use Phalcon\Cli\Task; use Phalcon\Cli\Task;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class CloseTradeTask extends Task class CloseTradeTask extends Task
{ {
@ -20,8 +23,8 @@ class CloseTradeTask extends Task
foreach ($trades as $trade) { foreach ($trades as $trade) {
if ($trade->channel == TradeModel::CHANNEL_ALIPAY) { if ($trade->channel == TradeModel::CHANNEL_ALIPAY) {
$this->closeAlipayTrade($trade); $this->closeAlipayTrade($trade);
} elseif ($trade->channel == TradeModel::CHANNEL_WXPAY) { } elseif ($trade->channel == TradeModel::CHANNEL_WECHAT) {
$this->closeWxpayTrade($trade); $this->closeWechatTrade($trade);
} }
} }
} }
@ -53,9 +56,9 @@ class CloseTradeTask extends Task
* *
* @param TradeModel $trade * @param TradeModel $trade
*/ */
protected function closeWxpayTrade($trade) protected function closeWechatTrade($trade)
{ {
$service = new WxpayService(); $service = new WechatService();
$wxOrder = $service->findOrder($trade->sn); $wxOrder = $service->findOrder($trade->sn);
@ -74,7 +77,7 @@ class CloseTradeTask extends Task
* 查找待关闭交易 * 查找待关闭交易
* *
* @param int $limit * @param int $limit
* @return \Phalcon\Mvc\Model\ResultsetInterface * @return Resultset|ResultsetInterface
*/ */
protected function findTrades($limit = 5) protected function findTrades($limit = 5)
{ {

View File

@ -2,6 +2,7 @@
namespace App\Console\Tasks; namespace App\Console\Tasks;
use App\Models\Category as CategoryModel;
use App\Repos\Category as CategoryRepo; use App\Repos\Category as CategoryRepo;
use Phalcon\Cli\Task; use Phalcon\Cli\Task;
@ -14,6 +15,9 @@ class CountCourseTask extends Task
$mapping = []; $mapping = [];
/**
* @var CategoryModel[] $subCategories
*/
$subCategories = $categoryRepo->findAll(['level' => 2, 'deleted' => 0]); $subCategories = $categoryRepo->findAll(['level' => 2, 'deleted' => 0]);
foreach ($subCategories as $category) { foreach ($subCategories as $category) {
@ -31,6 +35,9 @@ class CountCourseTask extends Task
} }
} }
/**
* @var CategoryModel[] $topCategories
*/
$topCategories = $categoryRepo->findAll(['level' => 1, 'deleted' => 0]); $topCategories = $categoryRepo->findAll(['level' => 1, 'deleted' => 0]);
foreach ($topCategories as $category) { foreach ($topCategories as $category) {

View File

@ -1,66 +0,0 @@
<?php
namespace App\Console\Tasks;
use App\Models\Course;
use App\Services\Storage;
use Phalcon\Cli\Task;
use Phalcon\Text;
class ImageSyncTask extends Task
{
public function mainAction()
{
$courses = Course::query()
->where('id > 1155')
->execute();
$storage = new Storage();
foreach ($courses as $course) {
$cover = $course->cover;
if (Text::startsWith($cover, '//')) {
$cover = 'http:' . $cover;
}
$url = str_replace('-360-202', '', $cover);
$fileName = parse_url($url, PHP_URL_PATH);
$filePath = tmp_path() . $fileName;
$content = file_get_contents($url);
if ($content === false) {
echo "get course {$course->id} cover failed" . PHP_EOL;
return;
}
$put = file_put_contents($filePath, $content);
if ($put === false) {
echo "put course {$course->id} cover failed" . PHP_EOL;
return;
}
$keyName = $this->getKeyName($filePath);
$remoteUrl = $storage->putFile($keyName, $filePath);
if ($remoteUrl) {
$course->cover = $keyName;
$course->update();
echo "upload cover of course {$course->id} success" . PHP_EOL;
} else {
echo "upload cover of course {$course->id} failed" . PHP_EOL;
}
}
}
protected function getKeyName($filePath)
{
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
return '/img/cover/' . date('YmdHis') . rand(1000, 9999) . '.' . $ext;
}
}

View File

@ -2,6 +2,7 @@
namespace App\Console\Tasks; namespace App\Console\Tasks;
use App\Library\Cache\Backend\Redis as RedisCache;
use App\Models\Course as CourseModel; use App\Models\Course as CourseModel;
use App\Models\Learning as LearningModel; use App\Models\Learning as LearningModel;
use App\Repos\Chapter as ChapterRepo; use App\Repos\Chapter as ChapterRepo;
@ -9,88 +10,109 @@ use App\Repos\ChapterUser as ChapterUserRepo;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use App\Repos\CourseUser as CourseUserRepo; use App\Repos\CourseUser as CourseUserRepo;
use App\Repos\Learning as LearningRepo; use App\Repos\Learning as LearningRepo;
use App\Services\LearningSyncer;
use Phalcon\Cli\Task; use Phalcon\Cli\Task;
class LearningTask extends Task class LearningTask extends Task
{ {
/** /**
* @var \App\Library\Cache\Backend\Redis * @var RedisCache
*/ */
protected $cache; protected $cache;
/**
* @var \Redis
*/
protected $redis;
public function mainAction() public function mainAction()
{ {
$this->cache = $this->getDI()->get('cache'); $this->cache = $this->getDI()->get('cache');
$keys = $this->cache->queryKeys('learning:'); $this->redis = $this->cache->getRedis();
if (!$keys) return; $syncer = new LearningSyncer();
$keys = array_slice($keys, 0, 500); $syncKey = $syncer->getSyncKey();
foreach ($keys as $key) { $requestIds = $this->redis->sMembers($syncKey);
$lastKey = $this->cache->getRawKeyName($key);
$this->handleLearning($lastKey); if (!$requestIds) return;
foreach ($requestIds as $requestId) {
$itemKey = $syncer->getItemKey($requestId);
$this->handleLearning($itemKey);
} }
$this->redis->sRem($syncKey, ...$requestIds);
} }
protected function handleLearning($key) /**
* @param string $itemKey
*/
protected function handleLearning($itemKey)
{ {
$content = $this->cache->get($key); /**
* @var LearningModel $cacheLearning
*/
$cacheLearning = $this->cache->get($itemKey);
if (!$content) return; if (!$cacheLearning) return;
$learningRepo = new LearningRepo(); $learningRepo = new LearningRepo();
$learning = $learningRepo->findByRequestId($content['request_id']); $dbLearning = $learningRepo->findByRequestId($cacheLearning->request_id);
if (!$learning) { if (!$dbLearning) {
$learning = new LearningModel(); $cacheLearning->create();
$learning->create($content);
} else { } else {
$learning->duration += $content['duration']; $dbLearning->duration += $cacheLearning->duration;
$learning->update(); $dbLearning->update();
} }
$this->updateChapterUser($content['chapter_id'], $content['user_id'], $content['duration'], $content['position']); $this->updateChapterUser($dbLearning);
$this->cache->delete($key); $this->cache->delete($itemKey);
} }
protected function updateChapterUser($chapterId, $userId, $duration = 0, $position = 0) /**
* @param LearningModel $learning
*/
protected function updateChapterUser(LearningModel $learning)
{ {
$chapterUserRepo = new ChapterUserRepo(); $chapterUserRepo = new ChapterUserRepo();
$chapterUser = $chapterUserRepo->findChapterUser($chapterId, $userId); $chapterUser = $chapterUserRepo->findChapterUser($learning->chapter_id, $learning->user_id);
if (!$chapterUser) return; if (!$chapterUser) return;
$chapterRepo = new ChapterRepo(); $chapterRepo = new ChapterRepo();
$chapter = $chapterRepo->findById($chapterId); $chapter = $chapterRepo->findById($learning->chapter_id);
if (!$chapter) return; if (!$chapter) return;
$chapterModel = $chapter->attrs['model']; $chapterModel = $chapter->attrs['model'];
$chapterUser->duration += $duration; $chapterUser->duration += $learning->duration;
/** /**
* 消费规则 * 消费规则
*
* 1.点播观看时间大于时长30% * 1.点播观看时间大于时长30%
* 2.直播观看时间超过10分钟 * 2.直播观看时间超过10分钟
* 3.图文浏览即消费 * 3.图文浏览即消费
*/ */
if ($chapterModel == CourseModel::MODEL_VOD) { if ($chapterModel == CourseModel::MODEL_VOD) {
$chapterDuration = $chapter->attrs['duration'] ?: 300; $duration = $chapter->attrs['duration'] ?: 300;
$progress = floor(100 * $chapterUser->duration / $chapterDuration); $progress = floor(100 * $chapterUser->duration / $duration);
$chapterUser->position = floor($position); $chapterUser->position = floor($learning->position);
$chapterUser->progress = $progress < 100 ? $progress : 100; $chapterUser->progress = $progress < 100 ? $progress : 100;
$chapterUser->consumed = $chapterUser->duration > 0.3 * $chapterDuration ? 1 : 0; $chapterUser->consumed = $chapterUser->duration > 0.3 * $duration ? 1 : 0;
} elseif ($chapterModel == CourseModel::MODEL_LIVE) { } elseif ($chapterModel == CourseModel::MODEL_LIVE) {
@ -108,6 +130,10 @@ class LearningTask extends Task
} }
} }
/**
* @param int $courseId
* @param int $userId
*/
protected function updateCourseUser($courseId, $userId) protected function updateCourseUser($courseId, $userId)
{ {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
@ -142,9 +168,11 @@ class LearningTask extends Task
$courseUser = $courseUserRepo->findCourseUser($courseId, $userId); $courseUser = $courseUserRepo->findCourseUser($courseId, $userId);
$courseUser->progress = $progress; if ($courseUser) {
$courseUser->duration = $duration; $courseUser->progress = $progress;
$courseUser->update(); $courseUser->duration = $duration;
$courseUser->update();
}
} }
} }

View File

@ -3,9 +3,11 @@
namespace App\Console\Tasks; namespace App\Console\Tasks;
use App\Models\Course as CourseModel; use App\Models\Course as CourseModel;
use App\Searchers\CourseDocumenter; use App\Searchers\CourseDocument;
use App\Searchers\CourseSearcher; use App\Searchers\CourseSearch;
use Phalcon\Cli\Task; use Phalcon\Cli\Task;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ManageCourseIndexTask extends Task class ManageCourseIndexTask extends Task
{ {
@ -55,7 +57,7 @@ class ManageCourseIndexTask extends Task
*/ */
protected function cleanCourseIndex() protected function cleanCourseIndex()
{ {
$searcher = new CourseSearcher(); $searcher = new CourseSearch();
$index = $searcher->getXS()->getIndex(); $index = $searcher->getXS()->getIndex();
@ -77,9 +79,9 @@ class ManageCourseIndexTask extends Task
return; return;
} }
$searcher = new CourseSearcher(); $searcher = new CourseSearch();
$documenter = new CourseDocumenter(); $documenter = new CourseDocument();
$index = $searcher->getXS()->getIndex(); $index = $searcher->getXS()->getIndex();
@ -106,7 +108,7 @@ class ManageCourseIndexTask extends Task
*/ */
protected function searchCourses($query) protected function searchCourses($query)
{ {
$searcher = new CourseSearcher(); $searcher = new CourseSearch();
$result = $searcher->search($query); $result = $searcher->search($query);
@ -116,7 +118,7 @@ class ManageCourseIndexTask extends Task
/** /**
* 查找课程 * 查找课程
* *
* @return \Phalcon\Mvc\Model\ResultsetInterface * @return Resultset|ResultsetInterface
*/ */
protected function findCourses() protected function findCourses()
{ {

View File

@ -0,0 +1,190 @@
<?php
namespace App\Console\Tasks;
use App\Models\CourseUser as CourseUserModel;
use App\Models\Order as OrderModel;
use App\Models\Task as TaskModel;
use App\Repos\Course as CourseRepo;
use App\Repos\CourseUser as CourseUserRepo;
use App\Repos\Order as OrderRepo;
use App\Repos\User as UserRepo;
use Phalcon\Cli\Task;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ProcessOrderTask extends Task
{
const TRY_COUNT = 3;
public function mainAction()
{
$tasks = $this->findTasks();
if ($tasks->count() == 0) {
return;
}
$orderRepo = new OrderRepo();
foreach ($tasks as $task) {
try {
/**
* @var array $itemInfo
*/
$itemInfo = $task->item_info;
$order = $orderRepo->findById($itemInfo['order']['id']);
switch ($order->item_type) {
case OrderModel::ITEM_COURSE:
$this->handleCourseOrder($order);
break;
case OrderModel::ITEM_PACKAGE:
$this->handlePackageOrder($order);
break;
case OrderModel::ITEM_VIP:
$this->handleVipOrder($order);
break;
}
} catch (\Exception $e) {
$task->try_count += 1;
$task->priority += 1;
if ($task->try_count > self::TRY_COUNT) {
$task->status = TaskModel::STATUS_FAILED;
}
$task->update();
}
}
}
/**
* @param OrderModel $order
*/
protected function handleCourseOrder(OrderModel $order)
{
/**
* @var array $itemInfo
*/
$itemInfo = $order->item_info;
$data = [
'user_id' => $order->user_id,
'course_id' => $order->item_id,
'expiry_time' => $itemInfo['course']['expiry_time'],
'role_type' => CourseUserModel::ROLE_STUDENT,
'source_type' => CourseUserModel::SOURCE_CHARGE,
];
$courseUser = new CourseUserModel();
if ($courseUser->create($data) === false) {
throw new \RuntimeException('Create Course User Failed');
}
$this->handleCourseHistory($data['course_id'], $data['user_id']);
}
/**
* @param OrderModel $order
*/
protected function handlePackageOrder(OrderModel $order)
{
/**
* @var array $itemInfo
*/
$itemInfo = $order->item_info;
foreach ($itemInfo['courses'] as $course) {
$data = [
'user_id' => $order->user_id,
'course_id' => $course['id'],
'expiry_time' => $course['expiry_time'],
'role_type' => CourseUserModel::ROLE_STUDENT,
'source_type' => CourseUserModel::SOURCE_CHARGE,
];
$courseUser = new CourseUserModel();
if ($courseUser->create($data) === false) {
throw new \RuntimeException('Create Course User Failed');
}
$this->handleCourseHistory($data['course_id'], $data['user_id']);
}
}
/**
* @param OrderModel $order
*/
protected function handleVipOrder(OrderModel $order)
{
$userRepo = new UserRepo();
$user = $userRepo->findById($order->user_id);
/**
* @var array $itemInfo
*/
$itemInfo = $order->item_info;
$user->vip_expiry_time = $itemInfo['vip']['expiry_time'];
if ($user->update() === false) {
throw new \RuntimeException('Update Vip Expiry Failed');
}
}
/**
* @param int $courseId
* @param int $userId
*/
protected function handleCourseHistory($courseId, $userId)
{
$courseUserRepo = new CourseUserRepo();
$courseUser = $courseUserRepo->findCourseStudent($courseId, $userId);
if ($courseUser) {
$courseUser->update(['deleted' => 1]);
}
$courseRepo = new CourseRepo();
$userLearnings = $courseRepo->findUserLearnings($courseId, $userId);
if ($userLearnings->count() > 0) {
$userLearnings->update(['deleted' => 1]);
}
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|TaskModel[]
*/
protected function findTasks($limit = 100)
{
$itemType = TaskModel::TYPE_PROCESS_ORDER;
$status = TaskModel::STATUS_PENDING;
$tryCount = self::TRY_COUNT;
$tasks = TaskModel::query()
->where('item_type = :item_type:', ['item_type' => $itemType])
->andWhere('status = :status:', ['status' => $status])
->andWhere('try_count < :try_count:', ['try_count' => $tryCount])
->orderBy('priority ASC')
->limit($limit)
->execute();
return $tasks;
}
}

View File

@ -6,6 +6,7 @@ use App\Caches\Chapter as ChapterCache;
use App\Caches\ChapterCounter as ChapterCounterCache; use App\Caches\ChapterCounter as ChapterCounterCache;
use App\Repos\Chapter as ChapterRepo; use App\Repos\Chapter as ChapterRepo;
use App\Services\ChapterCacheSyncer; use App\Services\ChapterCacheSyncer;
use Phalcon\Mvc\Model\Resultset;
class RebuildChapterCacheTask extends Task class RebuildChapterCacheTask extends Task
{ {
@ -39,6 +40,9 @@ class RebuildChapterCacheTask extends Task
$chapterRepo = new ChapterRepo(); $chapterRepo = new ChapterRepo();
/**
* @var Resultset $chapters
*/
$chapters = $chapterRepo->findByIds($chapterIds); $chapters = $chapterRepo->findByIds($chapterIds);
if ($chapters->count() == 0) { if ($chapters->count() == 0) {

View File

@ -4,14 +4,17 @@ namespace App\Console\Tasks;
use App\Caches\Course as CourseCache; use App\Caches\Course as CourseCache;
use App\Caches\CourseCounter as CourseCounterCache; use App\Caches\CourseCounter as CourseCounterCache;
use App\Library\Cache\Backend\Redis as RedisCache;
use App\Models\Course as CourseModel;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use App\Services\CourseCacheSyncer; use App\Services\CourseCacheSyncer;
use Phalcon\Mvc\Model\Resultset;
class RebuildCourseCacheTask extends Task class RebuildCourseCacheTask extends Task
{ {
/** /**
* @var \App\Library\Cache\Backend\Redis * @var RedisCache
*/ */
protected $cache; protected $cache;
@ -39,6 +42,9 @@ class RebuildCourseCacheTask extends Task
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset|CourseModel[] $courses
*/
$courses = $courseRepo->findByIds($courseIds); $courses = $courseRepo->findByIds($courseIds);
if ($courses->count() == 0) { if ($courses->count() == 0) {
@ -49,10 +55,12 @@ class RebuildCourseCacheTask extends Task
$counterCache = new CourseCounterCache(); $counterCache = new CourseCounterCache();
foreach ($courses as $course) { foreach ($courses as $course) {
$course->user_count = $courseRepo->countUsers($course->id); $course->user_count = $courseRepo->countUsers($course->id);
$course->comment_count = $courseRepo->countComments($course->id); $course->comment_count = $courseRepo->countComments($course->id);
$course->review_count = $courseRepo->countReviews($course->id); $course->review_count = $courseRepo->countReviews($course->id);
$course->favorite_count = $courseRepo->countFavorites($course->id); $course->favorite_count = $courseRepo->countFavorites($course->id);
$course->update(); $course->update();
$courseCache->rebuild($course->id); $courseCache->rebuild($course->id);

View File

@ -2,16 +2,17 @@
namespace App\Console\Tasks; namespace App\Console\Tasks;
use App\Library\Cache\Backend\Redis as RedisCache;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use App\Searchers\CourseDocumenter; use App\Searchers\CourseDocument;
use App\Searchers\CourseSearcher; use App\Searchers\CourseSearch;
use App\Services\CourseIndexSyncer; use App\Services\CourseIndexSyncer;
class RebuildCourseIndexTask extends Task class RebuildCourseIndexTask extends Task
{ {
/** /**
* @var \App\Library\Cache\Backend\Redis * @var RedisCache
*/ */
protected $cache; protected $cache;
@ -31,7 +32,7 @@ class RebuildCourseIndexTask extends Task
protected function rebuild() protected function rebuild()
{ {
$key = $this->getCacheKey(); $key = $this->getSyncKey();
$courseIds = $this->redis->sRandMember($key, 100); $courseIds = $this->redis->sRandMember($key, 100);
@ -45,16 +46,18 @@ class RebuildCourseIndexTask extends Task
return; return;
} }
$document = new CourseDocumenter(); $document = new CourseDocument();
$searcher = new CourseSearcher(); $searcher = new CourseSearch();
$index = $searcher->getXS()->getIndex(); $index = $searcher->getXS()->getIndex();
$index->openBuffer(); $index->openBuffer();
foreach ($courses as $course) { foreach ($courses as $course) {
$doc = $document->setDocument($course); $doc = $document->setDocument($course);
if ($course->published == 1) { if ($course->published == 1) {
$index->update($doc); $index->update($doc);
} else { } else {
@ -67,11 +70,11 @@ class RebuildCourseIndexTask extends Task
$this->redis->sRem($key, ...$courseIds); $this->redis->sRem($key, ...$courseIds);
} }
protected function getCacheKey() protected function getSyncKey()
{ {
$syncer = new CourseIndexSyncer(); $syncer = new CourseIndexSyncer();
return $syncer->getCacheKey(); return $syncer->getSyncKey();
} }
} }

View File

@ -10,13 +10,19 @@ use App\Repos\CourseUser as CourseUserRepo;
use App\Repos\Order as OrderRepo; use App\Repos\Order as OrderRepo;
use App\Repos\Refund as RefundRepo; use App\Repos\Refund as RefundRepo;
use App\Repos\Trade as TradeRepo; use App\Repos\Trade as TradeRepo;
use App\Repos\User as UserRepo;
use App\Services\Alipay as AlipayService; use App\Services\Alipay as AlipayService;
use App\Services\Wxpay as WxpayService; use App\Services\Wechat as WechatService;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class RefundTask extends Task class RefundTask extends Task
{ {
const TRY_COUNT = 5; /**
* 重试次数
*/
const TRY_COUNT = 3;
public function mainAction() public function mainAction()
{ {
@ -34,15 +40,21 @@ class RefundTask extends Task
foreach ($tasks as $task) { foreach ($tasks as $task) {
$refund = $refundRepo->findBySn($task->item_info['refund']['sn']); /**
$trade = $tradeRepo->findBySn($task->item_info['refund']['trade_sn']); * @var array $itemInfo
$order = $orderRepo->findBySn($task->item_info['refund']['order_sn']); */
$itemInfo = $task->item_info;
$refund = $refundRepo->findById($itemInfo['refund']['id']);
$trade = $tradeRepo->findById($itemInfo['refund']['trade_id']);
$order = $orderRepo->findById($itemInfo['refund']['order_id']);
try { try {
$this->db->begin(); $this->db->begin();
$this->handleTradeRefund($trade, $refund); $this->handleTradeRefund($trade, $refund);
$this->handleOrderRefund($order); $this->handleOrderRefund($order);
$refund->status = RefundModel::STATUS_FINISHED; $refund->status = RefundModel::STATUS_FINISHED;
@ -76,6 +88,7 @@ class RefundTask extends Task
$this->db->rollback(); $this->db->rollback();
$task->try_count += 1; $task->try_count += 1;
$task->priority += 1;
if ($task->try_count > self::TRY_COUNT) { if ($task->try_count > self::TRY_COUNT) {
$task->status = TaskModel::STATUS_FAILED; $task->status = TaskModel::STATUS_FAILED;
@ -104,18 +117,23 @@ class RefundTask extends Task
$response = false; $response = false;
if ($trade->channel == TradeModel::CHANNEL_ALIPAY) { if ($trade->channel == TradeModel::CHANNEL_ALIPAY) {
$alipay = new AlipayService(); $alipay = new AlipayService();
$response = $alipay->refundOrder([ $response = $alipay->refundOrder([
'out_trade_no' => $trade->sn, 'out_trade_no' => $trade->sn,
'out_request_no' => $refund->sn, 'out_request_no' => $refund->sn,
'refund_amount' => $refund->amount, 'refund_amount' => $refund->amount,
]); ]);
} elseif ($trade->channel == TradeModel::CHANNEL_WXPAY) {
$wxpay = new WxpayService(); } elseif ($trade->channel == TradeModel::CHANNEL_WECHAT) {
$response = $wxpay->refundOrder([
$wechat = new WechatService();
$response = $wechat->refundOrder([
'out_trade_no' => $trade->sn, 'out_trade_no' => $trade->sn,
'out_refund_no' => $refund->sn, 'out_refund_no' => $refund->sn,
'total_fee' => 100 * $trade->order_amount, 'total_fee' => 100 * $trade->amount,
'refund_fee' => 100 * $refund->amount, 'refund_fee' => 100 * $refund->amount,
]); ]);
} }
@ -133,19 +151,16 @@ class RefundTask extends Task
protected function handleOrderRefund(OrderModel $order) protected function handleOrderRefund(OrderModel $order)
{ {
switch ($order->item_type) { switch ($order->item_type) {
case OrderModel::TYPE_COURSE: case OrderModel::ITEM_COURSE:
$this->handleCourseOrderRefund($order); $this->handleCourseOrderRefund($order);
break; break;
case OrderModel::TYPE_PACKAGE: case OrderModel::ITEM_PACKAGE:
$this->handlePackageOrderRefund($order); $this->handlePackageOrderRefund($order);
break; break;
case OrderModel::TYPE_REWARD: case OrderModel::ITEM_VIP:
$this->handleRewardOrderRefund($order);
break;
case OrderModel::TYPE_VIP:
$this->handleVipOrderRefund($order); $this->handleVipOrderRefund($order);
break; break;
case OrderModel::TYPE_TEST: case OrderModel::ITEM_TEST:
$this->handleTestOrderRefund($order); $this->handleTestOrderRefund($order);
break; break;
} }
@ -179,8 +194,15 @@ class RefundTask extends Task
{ {
$courseUserRepo = new CourseUserRepo(); $courseUserRepo = new CourseUserRepo();
foreach ($order->item_info['courses'] as $course) { /**
* @var array $itemInfo
*/
$itemInfo = $order->item_info;
foreach ($itemInfo['courses'] as $course) {
$courseUser = $courseUserRepo->findCourseStudent($course['id'], $order->user_id); $courseUser = $courseUserRepo->findCourseStudent($course['id'], $order->user_id);
if ($courseUser) { if ($courseUser) {
$courseUser->deleted = 1; $courseUser->deleted = 1;
if ($courseUser->update() === false) { if ($courseUser->update() === false) {
@ -201,24 +223,17 @@ class RefundTask extends Task
$user = $userRepo->findById($order->user_id); $user = $userRepo->findById($order->user_id);
$baseTime = $user->vip_expiry; /**
* @var array $itemInfo
*/
$itemInfo = $order->item_info;
switch ($order->item_info['vip']['duration']) { $diffTime = "-{$itemInfo['vip']['expiry']} months";
case 'one_month': $baseTime = $itemInfo['vip']['expiry_time'];
$user->vip_expiry = strtotime('-1 months', $baseTime);
break;
case 'three_month':
$user->vip_expiry = strtotime('-3 months', $baseTime);
break;
case 'six_month':
$user->vip_expiry = strtotime('-6 months', $baseTime);
break;
case 'twelve_month':
$user->vip_expiry = strtotime('-12 months', $baseTime);
break;
}
if ($user->vip_expiry < time()) { $user->vip_expiry_time = strtotime($diffTime, $baseTime);
if ($user->vip_expiry_time < time()) {
$user->vip = 0; $user->vip = 0;
} }
@ -227,16 +242,6 @@ class RefundTask extends Task
} }
} }
/**
* 处理打赏订单退款
*
* @param OrderModel $order
*/
protected function handleRewardOrderRefund(OrderModel $order)
{
}
/** /**
* 处理测试订单退款 * 处理测试订单退款
* *
@ -247,6 +252,10 @@ class RefundTask extends Task
} }
/**
* @param int $limit
* @return ResultsetInterface|Resultset|TaskModel[]
*/
protected function findTasks($limit = 5) protected function findTasks($limit = 5)
{ {
$itemType = TaskModel::TYPE_REFUND; $itemType = TaskModel::TYPE_REFUND;
@ -257,7 +266,7 @@ class RefundTask extends Task
->where('item_type = :item_type:', ['item_type' => $itemType]) ->where('item_type = :item_type:', ['item_type' => $itemType])
->andWhere('status = :status:', ['status' => $status]) ->andWhere('status = :status:', ['status' => $status])
->andWhere('try_count < :try_count:', ['try_count' => $tryCount]) ->andWhere('try_count < :try_count:', ['try_count' => $tryCount])
->orderBy('priority ASC,try_count DESC') ->orderBy('priority ASC')
->limit($limit) ->limit($limit)
->execute(); ->execute();

View File

@ -1,156 +0,0 @@
<?php
namespace App\Console\Tasks;
use App\Models\Reply as ReplyModel;
use App\Models\Thread as ThreadModel;
use App\Models\User as UserModel;
use Phalcon\Cli\Task;
use QL\QueryList;
class ReplySpiderTask extends Task
{
public function mainAction()
{
$threads = ThreadModel::query()
->columns(['id'])
->where('id > 59429')
->orderBy('id ASC')
->execute();
$ql = $this->getRules();
foreach ($threads as $thread) {
$this->handleList($ql, $thread->id);
sleep(5);
}
}
protected function getRules()
{
$ql = QueryList::getInstance()->rules([
'thread_content' => ['div.qa-disscus', 'html'],
'user_link' => ['div.qa-comment-author > a', 'href'],
'user_img' => ['div.qa-comment-author > a > img', 'src'],
'user_name' => ['span.qa-comment-nick', 'text'],
'reply_id' => ['div.qa-comment', 'data-cid'],
'reply_content' => ['div.qa-comment-c > div.rich-text', 'html'],
'reply_time' => ['span.qa-comment-time', 'text'],
]);
return $ql;
}
protected function handleList($ql, $threadId)
{
$thread = ThreadModel::findFirst($threadId);
$first = true;
foreach (range(1, 10) as $page) {
$url = "https://www.imooc.com/qadetail/{$threadId}?page={$page}";
echo "============== Thread {$threadId}, Page {$page} =================" . PHP_EOL;
$data = $ql->get($url)->query()->getData();
if ($data->count() == 0) {
break;
}
foreach ($data->all() as $item) {
if ($first) {
$threadContent = $this->getThreadContent($item['thread_content']);
if ($threadContent) {
$thread->update(['content' => $threadContent]);
}
$first = false;
}
$userData = [
'id' => $this->getUserId($item['user_link']),
'name' => $this->getUserName($item['user_name']),
'avatar' => $item['user_img'],
];
$user = UserModel::findFirst($userData['id']);
if (!$user) {
$user = new UserModel();
$user->create($userData);
}
$replyData = [
'thread_id' => $threadId,
'author_id' => $user->id,
'id' => $item['reply_id'],
'content' => $this->getReplyContent($item['reply_content']),
'created_at' => $this->getReplyTime($item['reply_time']),
];
$reply = ReplyModel::findFirst($replyData['id']);
if (!$reply && $replyData['content']) {
$reply = new ReplyModel();
$reply->create($replyData);
}
}
}
$ql->destruct();
}
protected function getUserId($userLink)
{
$result = str_replace(['/u/', '/bbs'], '', $userLink);
return trim($result);
}
protected function getUserName($userName)
{
$result = mb_substr($userName, 0, 30);
return $result;
}
protected function getThreadContent($content)
{
$content = str_replace('&nbsp;&nbsp;', '&nbsp;', $content);
if (mb_strlen($content) > 3000) {
return false;
}
$result = mb_substr($content, 0, 3000);
return $result;
}
protected function getReplyContent($content)
{
$content = str_replace('&nbsp;&nbsp;', '&nbsp;', $content);
if (mb_strlen($content) > 1500) {
return false;
}
$result = mb_substr($content, 0, 1500);
return $result;
}
protected function getReplyTime($time)
{
$date = $this->filter->sanitize($time, ['trim', 'string']);
if (strpos($date, '天')) {
$days = str_replace(['天前'], '', $date);
$days = intval($days);
$result = strtotime("-{$days} days");
} else {
$result = strtotime(trim($date));
}
return $result;
}
}

View File

@ -1,117 +0,0 @@
<?php
namespace App\Console\Tasks;
use App\Models\Course as CourseModel;
use App\Models\Review as ReviewModel;
use App\Models\User as UserModel;
use App\Repos\Review as ReviewRepo;
use Phalcon\Cli\Task;
use QL\QueryList;
class ReviewSpiderTask extends Task
{
const BASE_URL = 'https://www.imooc.com';
public function mainAction()
{
$courses = CourseModel::query()
->columns(['id'])
->where('id > 778')
->orderBy('id ASC')
->execute();
$ql = $this->getRules();
foreach ($courses as $course) {
$this->handleList($ql, $course->id);
sleep(5);
}
}
protected function getRules()
{
$ql = QueryList::rules([
'user_link' => ['a.img-box', 'href'],
'user_img' => ['a.img-box > img', 'src'],
'user_name' => ['a.img-box > img', 'alt'],
'review_content' => ['p.content', 'text'],
'review_rating' => ['div.star-box > span', 'text'],
]);
return $ql;
}
protected function handleList($ql, $courseId)
{
foreach (range(1, 7) as $page) {
$url = "https://www.imooc.com/course/coursescore/id/{$courseId}?page={$page}";
echo "============== Course {$courseId}, Page {$page} =================" . PHP_EOL;
$data = $ql->get($url)->query()->getData();
if ($data->count() == 0) {
continue;
}
foreach ($data->all() as $item) {
$userData = [
'id' => $this->getUserId($item['user_link']),
'name' => $item['user_name'],
'avatar' => $item['user_img'],
];
$user = UserModel::findFirst($userData['id']);
if (!$user) {
$user = new UserModel();
$user->create($userData);
}
$reviewData = [
'user_id' => $user->id,
'course_id' => $courseId,
'content' => $this->getReviewContent($item['review_content']),
'rating' => $this->getReviewRating($item['review_rating']),
];
$reviewRepo = new ReviewRepo();
$reviewExist = $reviewRepo->findReview($courseId, $user->id);
if (!$reviewExist) {
$review = new ReviewModel();
$review->create($reviewData);
}
}
}
$ql->destruct();
}
protected function getUserId($userLink)
{
$result = str_replace(['/u/', '/courses'], '', $userLink);
return trim($result);
}
protected function getReviewRating($rating)
{
$result = str_replace(['分'], '', $rating);
return intval($result);
}
protected function getReviewContent($content)
{
$result = $this->filter->sanitize($content, ['trim', 'string']);
return $result;
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace App\Console\Tasks;
use App\Models\User as UserModel;
use Phalcon\Cli\Task;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class RevokeVipTask extends Task
{
public function mainAction()
{
$users = $this->findUsers();
if ($users->count() == 0) {
return;
}
foreach ($users as $user) {
$user->vip = 0;
$user->update();
}
}
/**
* 查找待解锁用户
*
* @param int $limit
* @return UserModel[]|Resultset|ResultsetInterface
*/
protected function findUsers($limit = 1000)
{
$time = time();
$users = UserModel::query()
->where('vip = 1')
->andWhere('vip_expiry < :time:', ['time' => $time])
->limit($limit)
->execute();
return $users;
}
}

View File

@ -1,438 +0,0 @@
<?php
namespace App\Console\Tasks;
use App\Models\Chapter as ChapterModel;
use App\Models\Course as CourseModel;
use App\Models\CourseUser as CourseUserModel;
use App\Models\User as UserModel;
use App\Repos\CourseUser as CourseUserRepo;
use Phalcon\Cli\Task;
use QL\QueryList;
class SpiderTask extends Task
{
public function ctAction()
{
$courses = CourseModel::query()
->where('class_id = 0')
->execute();
foreach ($courses as $course) {
$url = "http://www.imooc.com/learn/{$course->id}";
$ql = QueryList::getInstance()->get($url);
$userId = $ql->find('img.js-usercard-dialog')->attr('data-userid');
if ($userId) {
$user = UserModel::findFirst($userId);
if (!$user || !$user->avatar) {
$this->handleUserInfo2($course->id, $userId);
}
}
$ql->destruct();
echo "finished course " . $course->id . PHP_EOL;
}
}
public function user2Action()
{
$users = UserModel::query()
->where('edu_role = 2')
->andWhere('name = :name:', ['name' => ''])
->execute();
foreach ($users as $user) {
$this->handleUserInfo($user->id);
echo "finished user: {$user->id}" . PHP_EOL;
}
}
public function courseListAction($params)
{
$category = $params[0] ?? 'html';
$page = $params[1] ?? 1;
$categoryId = $this->getCategoryId($category);
if (empty($categoryId)) {
throw new \Exception('invalid category');
}
$url = "http://www.imooc.com/course/list?c={$category}&page={$page}";
$data = QueryList::get($url)->rules([
'link' => ['a.course-card', 'href'],
'title' => ['h3.course-card-name', 'text'],
'cover' => ['img.course-banner', 'data-original'],
'summary' => ['p.course-card-desc', 'text'],
'level' => ['.course-card-info>span:even', 'text'],
'user_count' => ['.course-card-info>span:odd', 'text'],
])->query()->getData();
if ($data->count() == 0) {
return false;
}
foreach ($data->all() as $item) {
$course = [
'id' => substr($item['link'], 7),
'category_id' => $categoryId,
'title' => $item['title'],
'cover' => $item['cover'],
'summary' => $item['summary'],
'user_count' => $item['user_count'],
'level' => $this->getLevel($item['level']),
];
$model = new CourseModel();
$model->save($course);
}
echo sprintf("saved: %d course", $data->count());
}
public function courseAction()
{
$courses = CourseModel::query()
->where('id = 762')
->orderBy('id asc')
->execute();
$instance = QueryList::getInstance();
foreach ($courses as $course) {
$url = "http://www.imooc.com/learn/{$course->id}";
$ql = $instance->get($url);
//$this->handleCourseInfo($course, $ql);
$this->handleCourseChapters($course, $ql);
//$ql->destruct();
echo "finished course " . $course->id . PHP_EOL;
}
}
public function teacherAction()
{
$users = UserModel::query()
->where('edu_role = 2')
->execute();
foreach ($users as $user) {
try {
$this->handleTeacherInfo2($user);
echo "finished teacher: {$user->id}" . PHP_EOL;
} catch (\Exception $e) {
echo $e->getMessage() . PHP_EOL;
}
}
}
protected function handleTeacherInfo2(UserModel $user)
{
$url = "http://www.imooc.com/t/{$user->id}";
$ql = QueryList::getInstance()->get($url);
$data = [];
$data['avatar'] = $ql->find('img.tea-header')->attr('src');
$data['name'] = $ql->find('p.tea-nickname')->text();
$data['title'] = $ql->find('p.tea-professional')->text();
$data['about'] = $ql->find('p.tea-desc')->text();
$user->update($data);
}
public function userAction()
{
$users = UserModel::query()
->where('edu_role = 1')
->execute();
foreach ($users as $user) {
$this->handleUserInfo($user->id);
echo "finished user: {$user->id}" . PHP_EOL;
}
}
protected function handleUserInfo($id)
{
$url = 'https://www.imooc.com/u/' . $id;
$user = UserModel::findFirst($id);
try {
$ql = QueryList::getInstance()->get($url);
$data = [];
$data['avatar'] = $ql->find('.user-pic-bg>img')->attr('src');
$data['name'] = $ql->find('h3.user-name>span')->text();
$data['about'] = $ql->find('p.user-desc')->text();
print_r($data);
$user->update($data);
$ql->destruct();
} catch (\Exception $e) {
$user->update(['deleted' => 1]);
echo "user {$id} not found" . PHP_EOL;
}
}
protected function handleUserInfo2($courseId, $userId)
{
$url = 'https://www.imooc.com/u/' . $userId;
$user = UserModel::findFirst($userId);
try {
$ql = QueryList::getInstance()->get($url);
$data = [];
$data['avatar'] = $ql->find('.user-pic-bg>img')->attr('src');
$data['name'] = $ql->find('h3.user-name>span')->text();
$data['about'] = $ql->find('p.user-desc')->text();
$data['edu_role'] = UserModel::EDU_ROLE_TEACHER;
if ($user) {
$user->update($data);
} else {
$user = new UserModel();
$user->create($data);
}
$cuRepo = new CourseUserRepo();
$courseUser = $cuRepo->findCourseUser($courseId, $userId);
if (!$courseUser) {
$courseUser = new CourseUserModel();
$courseUser->course_id = $courseId;
$courseUser->user_id = $userId;
$courseUser->role_type = CourseUserModel::ROLE_TEACHER;
$courseUser->expire_time = strtotime('+15 years');
$courseUser->create();
}
echo "teacher {$userId} off " . PHP_EOL;
$ql->destruct();
} catch (\Exception $e) {
$user->update(['deleted' => 1]);
echo "user {$userId} not found" . PHP_EOL;
}
}
protected function handleTeacherInfo($courseId, $userId)
{
$url = "http://www.imooc.com/t/{$userId}";
$ql = QueryList::getInstance()->get($url);
$data = [];
$data['id'] = $userId;
$data['avatar'] = $ql->find('img.tea-header')->attr('src');
$data['name'] = $ql->find('p.tea-nickname')->text();
$data['title'] = $ql->find('p.tea-professional')->text();
$data['about'] = $ql->find('p.tea-desc')->text();
$data['edu_role'] = UserModel::EDU_ROLE_TEACHER;
$user = UserModel::findFirst($userId);
if ($user) {
$user->update($data);
} else {
$user = new UserModel();
$user->create($data);
}
$cuRepo = new CourseUserRepo();
$courseUser = $cuRepo->findCourseUser($courseId, $userId);
if (!$courseUser) {
$courseUser = new CourseUserModel();
$courseUser->course_id = $courseId;
$courseUser->user_id = $userId;
$courseUser->role_type = CourseUserModel::ROLE_TEACHER;
$courseUser->expire_time = strtotime('+15 years');
$courseUser->create();
}
$ql->destruct();
echo "teacher ok" . PHP_EOL;
}
protected function handleCourseInfo(CourseModel $course, QueryList $ql)
{
$data = [];
$data['user_id'] = $ql->find('img.js-usercard-dialog')->attr('data-userid');
$data['description'] = $ql->find('.course-description')->text();
$data['duration'] = $ql->find('.static-item:eq(1)>.meta-value')->text();
$data['score'] = $ql->find('.score-btn>.meta-value')->text();
$data['attrs']['duration'] = $this->getCourseDuration($data['duration']);
$course->update($data);
if ($data['user_id']) {
$this->handleTeacherInfo($course->id, $data['user_id']);
}
echo "course info ok" . PHP_EOL;
}
protected function handleCourseChapters(CourseModel $course, QueryList $ql)
{
echo "top chapter" . PHP_EOL;
$topChapters = $ql->rules([
'title' => ['.chapter > h3', 'text'],
'sub_chapter_html' => ['.chapter > .video', 'html'],
])->query()->getData();
if ($topChapters->count() == 0) {
return false;
}
foreach ($topChapters->all() as $item) {
$data = [
'course_id' => $course->id,
'title' => $item['title'],
];
// create top chapter
$chapter = new ChapterModel();
$chapter->create($data);
// create sub chapter
if (!empty($item['sub_chapter_html'])) {
$this->handleSubChapters($chapter, $item['sub_chapter_html']);
}
}
}
protected function handleSubChapters(ChapterModel $topChapter, $subChapterHtml)
{
$ql = QueryList::html($subChapterHtml);
$chapters = $ql->find('li')->texts();
if ($chapters->count() == 0) {
return false;
}
foreach ($chapters->all() as $item) {
/**
*
* preg_match('/(\d{1,}-\d{1,})\s{1,}(.*?)\((.*?)\)/s', $item, $matches);
*
* if (!isset($matches[3]) || empty($matches[3])) {
* continue;
* }
*
* $data = [
* 'title' => $matches[2],
* 'duration' => $this->getChapterDuration($matches[3]),
* ];
*/
$title = str_replace(["开始学习", "\r", "\n", "\t", " "], "", $item);
$title = preg_replace('/\(\d{2}:\d{2}\)/', '', $title);
$data = [];
$data['course_id'] = $topChapter->course_id;
$data['parent_id'] = $topChapter->id;
$data['title'] = trim($title);
$model = new ChapterModel();
$model->create($data);
}
}
protected function getCourseDuration($duration)
{
$hours = 0;
$minutes = 0;
if (preg_match('/(.*?)小时(.*?)分/s', $duration, $matches)) {
$hours = trim($matches[1]);
$minutes = trim($matches[2]);
} elseif (preg_match('/(.*?)小时/s', $duration, $matches)) {
$hours = trim($matches[1]);
} elseif (preg_match('/(.*?)分/s', $duration, $matches)) {
$minutes = trim($matches[1]);
}
return 3600 * $hours + 60 * $minutes;
}
protected function getChapterDuration($duration)
{
if (strpos($duration, ':') === false) {
return 0;
}
list($minutes, $seconds) = explode(':', trim($duration));
return 60 * $minutes + $seconds;
}
protected function getLevel($type)
{
$mapping = [
'入门' => CourseModel::LEVEL_ENTRY,
'初级' => CourseModel::LEVEL_JUNIOR,
'中级' => CourseModel::LEVEL_MEDIUM,
'高级' => CourseModel::LEVEL_SENIOR,
];
return $mapping[$type] ?? CourseModel::LEVEL_ENTRY;
}
protected function getCategoryId($type)
{
$mapping = [
'html' => 1, 'javascript' => 2, 'vuejs' => 10, 'reactjs' => 19,
'angular' => 18, 'nodejs' => 16, 'jquery' => 15,
'bootstrap' => 17, 'sassless' => 21, 'webapp' => 22, 'fetool' => 23,
'html5' => 13, 'css3' => 14,
'php' => 24, 'java' => 25, 'python' => 26, 'c' => 27, 'cplusplus' => 28, 'ruby' => 29, 'go' => 30, 'csharp' => 31,
'android' => 32, 'ios' => 33,
'mysql' => 36, 'mongodb' => 37, 'redis' => 38, 'oracle' => 39, 'pgsql' => 40,
'cloudcomputing' => 42, 'bigdata' => 43,
'unity3d' => 34, 'cocos2dx' => 35,
'dxdh' => 46, 'uitool' => 47, 'uijc' => 48,
];
return $mapping[$type] ?? 0;
}
}

View File

@ -1,145 +0,0 @@
<?php
namespace App\Console\Tasks;
use App\Models\Course as CourseModel;
use App\Models\Thread as ThreadModel;
use App\Models\User as UserModel;
use Phalcon\Cli\Task;
use QL\QueryList;
class ThreadSpiderTask extends Task
{
public function mainAction()
{
$courses = CourseModel::query()
->columns(['id'])
->where('id > 494')
->orderBy('id ASC')
->execute();
$ql = $this->getRules();
foreach ($courses as $course) {
$this->handleList($ql, $course->id);
sleep(5);
}
}
protected function getRules()
{
$ql = QueryList::rules([
'user_link' => ['a.media', 'href'],
'user_img' => ['a.media > img', 'src'],
'user_name' => ['a.media', 'title'],
//'chapter_link' => ['div.l-box > a:eq(1)', 'href'],
'thread_link' => ['a.qa-tit', 'href'],
'thread_title' => ['a.qa-tit', 'text'],
'thread_time' => ['em.r', 'text'],
]);
return $ql;
}
protected function handleList($ql, $courseId)
{
foreach (range(1, 10) as $page) {
$url = "https://www.imooc.com/course/qa/id/{$courseId}/t/2?page={$page}";
echo "============== Course {$courseId}, Page {$page} =================" . PHP_EOL;
$data = $ql->get($url)->query()->getData();
if ($data->count() == 0) {
break;
}
foreach ($data->all() as $item) {
$userData = [
'id' => $this->getUserId($item['user_link']),
'name' => $this->getUserName($item['user_name']),
'avatar' => $item['user_img'],
];
$user = UserModel::findFirst($userData['id']);
if (!$user) {
$user = new UserModel();
$user->create($userData);
}
$threadData = [
'course_id' => $courseId,
'author_id' => $user->id,
'id' => $this->getThreadId($item['thread_link']),
'title' => $this->getThreadTitle($item['thread_title']),
'created_at' => $this->getThreadTime($item['thread_time']),
];
$thread = ThreadModel::findFirst($threadData['id']);
if (!$thread) {
$thread = new ThreadModel();
$thread->create($threadData);
}
}
}
$ql->destruct();
}
protected function getUserId($userLink)
{
$result = str_replace(['/u/', '/courses'], '', $userLink);
return trim($result);
}
protected function getUserName($userName)
{
$result = mb_substr($userName, 0, 30);
return $result;
}
protected function getChapterId($chapterLink)
{
$result = str_replace(['/video/'], '', $chapterLink);
return trim($result);
}
protected function getThreadId($threadLink)
{
$result = str_replace(['/qadetail/'], '', $threadLink);
return trim($result);
}
protected function getThreadTitle($title)
{
$title = $this->filter->sanitize($title, ['trim']);
$result = mb_substr($title, 0, 120);
return $result;
}
protected function getThreadTime($time)
{
$date = $this->filter->sanitize($time, ['trim', 'string']);
if (strpos($date, '天')) {
$days = str_replace(['天前'], '', $date);
$days = intval($days);
$result = strtotime("-{$days} days");
} else {
$result = strtotime(trim($date));
}
return $result;
}
}

View File

@ -1,67 +0,0 @@
<?php
namespace App\Console\Tasks;
use App\Models\Course as CourseModel;
use App\Models\Topic as TopicModel;
use Phalcon\Cli\Task;
class TopicSpiderTask extends Task
{
public function mainAction()
{
$courses = CourseModel::query()
->columns(['id'])
->where('id > 810')
->orderBy('id ASC')
->execute();
foreach ($courses as $course) {
$this->handleList($course->id);
sleep(5);
}
}
protected function handleList($courseId)
{
$url = "https://www.imooc.com/course/ajaxskillcourse?cid={$courseId}";
$content = file_get_contents($url);
$result = json_decode($content, true);
$topics = $result['data'];
echo "============== Course {$courseId} =================" . PHP_EOL;
if (empty($topics)) {
return;
}
foreach ($topics as $item) {
$topicData = [
'id' => $item['subject_id'],
'title' => $item['title'],
'alias' => $this->getAlias($item['url']),
];
$topic = TopicModel::findFirst($topicData['id']);
if (!$topic) {
$topic = new TopicModel();
$topic->create($topicData);
}
}
}
protected function getAlias($url)
{
$result = str_replace('//www.imooc.com/topic/', '', $url);
return trim($result);
}
}

View File

@ -4,6 +4,8 @@ namespace App\Console\Tasks;
use App\Models\User as UserModel; use App\Models\User as UserModel;
use Phalcon\Cli\Task; use Phalcon\Cli\Task;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class UnlockUserTask extends Task class UnlockUserTask extends Task
{ {
@ -18,7 +20,6 @@ class UnlockUserTask extends Task
foreach ($users as $user) { foreach ($users as $user) {
$user->locked = 0; $user->locked = 0;
$user->lock_expiry = 0;
$user->update(); $user->update();
} }
} }
@ -27,7 +28,7 @@ class UnlockUserTask extends Task
* 查找待解锁用户 * 查找待解锁用户
* *
* @param int $limit * @param int $limit
* @return \Phalcon\Mvc\Model\ResultsetInterface * @return UserModel[]|Resultset|ResultsetInterface
*/ */
protected function findUsers($limit = 1000) protected function findUsers($limit = 1000)
{ {

View File

@ -101,9 +101,9 @@ class ChapterController extends Controller
switch ($course->model) { switch ($course->model) {
case CourseModel::MODEL_VOD: case CourseModel::MODEL_VOD:
$vod = $contentService->getChapterVod($chapter->id); $vod = $contentService->getChapterVod($chapter->id);
$vodFiles = $contentService->getVodFiles($chapter->id); $playUrls = $contentService->getPlayUrls($chapter->id);
$this->view->setVar('vod', $vod); $this->view->setVar('vod', $vod);
$this->view->setVar('vod_files', $vodFiles); $this->view->setVar('play_urls', $playUrls);
break; break;
case CourseModel::MODEL_LIVE: case CourseModel::MODEL_LIVE:
$live = $contentService->getChapterLive($chapter->id); $live = $contentService->getChapterLive($chapter->id);

View File

@ -0,0 +1,121 @@
<?php
namespace App\Http\Admin\Controllers;
use App\Http\Admin\Services\Comment as CommentService;
/**
* @RoutePrefix("/admin/comment")
*/
class CommentController extends Controller
{
/**
* @Get("/search", name="admin.comment.search")
*/
public function searchAction()
{
}
/**
* @Get("/list", name="admin.comment.list")
*/
public function listAction()
{
$courseId = $this->request->getQuery('course_id', 'int', 0);
$chapterId = $this->request->getQuery('chapter_id', 'int', 0);
$commentService = new CommentService();
$pager = $commentService->getComments();
$chapter = null;
if ($chapterId > 0) {
$chapter = $commentService->getChapter($chapterId);
$courseId = $chapter->course_id;
}
$course = null;
if ($courseId > 0) {
$course = $commentService->getCourse($courseId);
}
$this->view->setVar('pager', $pager);
$this->view->setVar('course', $course);
$this->view->setVar('chapter', $chapter);
}
/**
* @Get("/{id:[0-9]+}/edit", name="admin.comment.edit")
*/
public function editAction($id)
{
$commentService = new CommentService();
$comment = $commentService->getComment($id);
$this->view->setVar('comment', $comment);
}
/**
* @Post("/{id:[0-9]+}/update", name="admin.comment.update")
*/
public function updateAction($id)
{
$commentService = new CommentService();
$commentService->update($id);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '更新评论成功',
];
return $this->ajaxSuccess($content);
}
/**
* @Post("/{id:[0-9]+}/delete", name="admin.comment.delete")
*/
public function deleteAction($id)
{
$commentService = new CommentService();
$commentService->deleteComment($id);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '删除评论成功',
];
return $this->ajaxSuccess($content);
}
/**
* @Post("/{id:[0-9]+}/restore", name="admin.comment.restore")
*/
public function restoreAction($id)
{
$commentService = new CommentService();
$commentService->restoreComment($id);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '还原评论成功',
];
return $this->ajaxSuccess($content);
}
}

View File

@ -157,10 +157,10 @@ class ConfigController extends Controller
} else { } else {
$alipay = $configService->getSectionConfig('payment.alipay'); $alipay = $configService->getSectionConfig('payment.alipay');
$wxpay = $configService->getSectionConfig('payment.wxpay'); $wechat = $configService->getSectionConfig('payment.wechat');
$this->view->setVar('alipay', $alipay); $this->view->setVar('alipay', $alipay);
$this->view->setVar('wxpay', $wxpay); $this->view->setVar('wechat', $wechat);
} }
} }
@ -252,23 +252,21 @@ class ConfigController extends Controller
*/ */
public function vipAction() public function vipAction()
{ {
$section = 'vip';
$configService = new ConfigService(); $configService = new ConfigService();
if ($this->request->isPost()) { if ($this->request->isPost()) {
$data = $this->request->getPost(); $data = $this->request->getPost('vip');
$configService->updateSectionConfig($section, $data); $configService->updateVipConfig($data);
return $this->ajaxSuccess(['msg' => '更新配置成功']); return $this->ajaxSuccess(['msg' => '更新配置成功']);
} else { } else {
$vip = $configService->getSectionConfig($section); $vips = $configService->getVipConfig();
$this->view->setVar('vip', $vip); $this->view->setVar('vips', $vips);
} }
} }

View File

@ -0,0 +1,108 @@
<?php
namespace App\Http\Admin\Controllers;
use App\Http\Admin\Services\Consult as ConsultService;
/**
* @RoutePrefix("/admin/consult")
*/
class ConsultController extends Controller
{
/**
* @Get("/search", name="admin.consult.search")
*/
public function searchAction()
{
}
/**
* @Get("/list", name="admin.consult.list")
*/
public function listAction()
{
$courseId = $this->request->getQuery('course_id', 'int', 0);
$consultService = new ConsultService();
$pager = $consultService->getConsults();
$course = null;
if ($courseId > 0) {
$course = $consultService->getCourse($courseId);
}
$this->view->setVar('pager', $pager);
$this->view->setVar('course', $course);
}
/**
* @Get("/{id:[0-9]+}/edit", name="admin.consult.edit")
*/
public function editAction($id)
{
$consultService = new ConsultService();
$consult = $consultService->getConsult($id);
$this->view->setVar('consult', $consult);
}
/**
* @Post("/{id:[0-9]+}/update", name="admin.consult.update")
*/
public function updateAction($id)
{
$consultService = new ConsultService();
$consultService->updateConsult($id);
$content = [
'msg' => '更新咨询成功',
];
return $this->ajaxSuccess($content);
}
/**
* @Post("/{id:[0-9]+}/delete", name="admin.consult.delete")
*/
public function deleteAction($id)
{
$consultService = new ConsultService();
$consultService->deleteConsult($id);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '删除咨询成功',
];
return $this->ajaxSuccess($content);
}
/**
* @Post("/{id:[0-9]+}/restore", name="admin.consult.restore")
*/
public function restoreAction($id)
{
$consultService = new ConsultService();
$consultService->restoreConsult($id);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '还原咨询成功',
];
return $this->ajaxSuccess($content);
}
}

View File

@ -75,11 +75,15 @@ class CourseController extends Controller
$xmTeachers = $courseService->getXmTeachers($id); $xmTeachers = $courseService->getXmTeachers($id);
$xmCategories = $courseService->getXmCategories($id); $xmCategories = $courseService->getXmCategories($id);
$xmCourses = $courseService->getXmCourses($id); $xmCourses = $courseService->getXmCourses($id);
$studyExpiryOptions = $courseService->getStudyExpiryOptions();
$refundExpiryOptions = $courseService->getRefundExpiryOptions();
$this->view->setVar('course', $course); $this->view->setVar('course', $course);
$this->view->setVar('xm_teachers', $xmTeachers); $this->view->setVar('xm_teachers', $xmTeachers);
$this->view->setVar('xm_categories', $xmCategories); $this->view->setVar('xm_categories', $xmCategories);
$this->view->setVar('xm_courses', $xmCourses); $this->view->setVar('xm_courses', $xmCourses);
$this->view->setVar('study_expiry_options', $studyExpiryOptions);
$this->view->setVar('refund_expiry_options', $refundExpiryOptions);
} }
/** /**

View File

@ -38,8 +38,8 @@ class OrderController extends Controller
$orderService = new OrderService(); $orderService = new OrderService();
$order = $orderService->getOrder($id); $order = $orderService->getOrder($id);
$trades = $orderService->getTrades($order->sn); $trades = $orderService->getTrades($order->id);
$refunds = $orderService->getRefunds($order->sn); $refunds = $orderService->getRefunds($order->id);
$account = $orderService->getAccount($order->user_id); $account = $orderService->getAccount($order->user_id);
$user = $orderService->getUser($order->user_id); $user = $orderService->getUser($order->user_id);

View File

@ -38,8 +38,8 @@ class RefundController extends Controller
$refundService = new RefundService(); $refundService = new RefundService();
$refund = $refundService->getRefund($id); $refund = $refundService->getRefund($id);
$order = $refundService->getOrder($refund->order_sn); $order = $refundService->getOrder($refund->order_id);
$trade = $refundService->getTrade($refund->trade_sn); $trade = $refundService->getTrade($refund->trade_id);
$account = $refundService->getAccount($trade->user_id); $account = $refundService->getAccount($trade->user_id);
$user = $refundService->getUser($trade->user_id); $user = $refundService->getUser($trade->user_id);

View File

@ -2,7 +2,7 @@
namespace App\Http\Admin\Controllers; namespace App\Http\Admin\Controllers;
use App\Http\Admin\Services\CourseStudent as CourseStudentService; use App\Http\Admin\Services\Student as StudentService;
/** /**
* @RoutePrefix("/admin/student") * @RoutePrefix("/admin/student")
@ -23,14 +23,20 @@ class StudentController extends Controller
*/ */
public function listAction() public function listAction()
{ {
$courseId = $this->request->getQuery('course_id', 'int', ''); $courseId = $this->request->getQuery('course_id', 'int', 0);
$courseStudentService = new CourseStudentService(); $studentService = new StudentService();
$pager = $courseStudentService->getCourseStudents(); $pager = $studentService->getPlans();
$course = null;
if ($courseId > 0) {
$course = $studentService->getCourse($courseId);
}
$this->view->setVar('pager', $pager); $this->view->setVar('pager', $pager);
$this->view->setVar('course_id', $courseId); $this->view->setVar('course', $course);
} }
/** /**
@ -38,9 +44,17 @@ class StudentController extends Controller
*/ */
public function addAction() public function addAction()
{ {
$courseId = $this->request->getQuery('course_id', 'int', ''); $courseId = $this->request->getQuery('course_id', 'int', 0);
$this->view->setVar('course_id', $courseId); $studentService = new StudentService();
$course = null;
if ($courseId > 0) {
$course = $studentService->getCourse($courseId);
}
$this->view->setVar('course', $course);
} }
/** /**
@ -48,9 +62,9 @@ class StudentController extends Controller
*/ */
public function createAction() public function createAction()
{ {
$courseStudentService = new CourseStudentService(); $studentService = new StudentService();
$student = $courseStudentService->createCourseStudent(); $student = $studentService->createPlan();
$location = $this->url->get( $location = $this->url->get(
['for' => 'admin.student.list'], ['for' => 'admin.student.list'],
@ -70,16 +84,15 @@ class StudentController extends Controller
*/ */
public function editAction() public function editAction()
{ {
$courseId = $this->request->getQuery('course_id', 'int'); $planId = $this->request->getQuery('plan_id');
$userId = $this->request->getQuery('user_id', 'int');
$courseStudentService = new CourseStudentService(); $studentService = new StudentService();
$courseStudent = $courseStudentService->getCourseStudent($courseId, $userId); $plan = $studentService->getPlan($planId);
$course = $courseStudentService->getCourse($courseId); $course = $studentService->getCourse($plan->course_id);
$student = $courseStudentService->getStudent($userId); $student = $studentService->getStudent($plan->user_id);
$this->view->setVar('course_student', $courseStudent); $this->view->setVar('plan', $plan);
$this->view->setVar('course', $course); $this->view->setVar('course', $course);
$this->view->setVar('student', $student); $this->view->setVar('student', $student);
} }
@ -89,14 +102,11 @@ class StudentController extends Controller
*/ */
public function updateAction() public function updateAction()
{ {
$courseStudentService = new CourseStudentService(); $studentService = new StudentService();
$student = $courseStudentService->updateCourseStudent(); $studentService->updatePlan();
$location = $this->url->get( $location = $this->url->get(['for' => 'admin.student.list']);
['for' => 'admin.student.list'],
['course_id' => $student->course_id]
);
$content = [ $content = [
'location' => $location, 'location' => $location,
@ -111,9 +121,9 @@ class StudentController extends Controller
*/ */
public function learningAction() public function learningAction()
{ {
$courseStudentService = new CourseStudentService(); $studentService = new StudentService();
$pager = $courseStudentService->getCourseLearnings(); $pager = $studentService->getLearnings();
$this->view->setVar('pager', $pager); $this->view->setVar('pager', $pager);
} }

View File

@ -165,7 +165,7 @@ class TestController extends Controller
$this->db->rollback(); $this->db->rollback();
} }
$this->view->pick('config/alipay_test'); $this->view->pick('config/payment_alipay_test');
$this->view->setVar('trade', $trade); $this->view->setVar('trade', $trade);
$this->view->setVar('qrcode', $qrcode); $this->view->setVar('qrcode', $qrcode);
} }

View File

@ -38,8 +38,8 @@ class TradeController extends Controller
$tradeService = new TradeService(); $tradeService = new TradeService();
$trade = $tradeService->getTrade($id); $trade = $tradeService->getTrade($id);
$refunds = $tradeService->getRefunds($trade->sn); $refunds = $tradeService->getRefunds($trade->id);
$order = $tradeService->getOrder($trade->order_sn); $order = $tradeService->getOrder($trade->order_id);
$account = $tradeService->getAccount($trade->user_id); $account = $tradeService->getAccount($trade->user_id);
$user = $tradeService->getUser($trade->user_id); $user = $tradeService->getUser($trade->user_id);

View File

@ -5,13 +5,13 @@ namespace App\Http\Admin\Controllers;
use App\Services\Storage as StorageService; use App\Services\Storage as StorageService;
/** /**
* @RoutePrefix("/admin/storage") * @RoutePrefix("/admin/upload")
*/ */
class StorageController extends Controller class UploadController extends Controller
{ {
/** /**
* @Post("/cover/img/upload", name="admin.storage.cover.img.upload") * @Post("/cover/img", name="admin.upload.cover.img")
*/ */
public function uploadCoverImageAction() public function uploadCoverImageAction()
{ {
@ -29,7 +29,7 @@ class StorageController extends Controller
} }
/** /**
* @Post("/content/img/upload", name="admin.content.img.upload") * @Post("/content/img", name="admin.upload.content.img")
*/ */
public function uploadContentImageAction() public function uploadContentImageAction()
{ {

View File

@ -81,9 +81,11 @@ class UserController extends Controller
$userService = new UserService(); $userService = new UserService();
$user = $userService->getUser($id); $user = $userService->getUser($id);
$account = $userService->getAccount($id);
$roles = $userService->getRoles(); $roles = $userService->getRoles();
$this->view->setVar('user', $user); $this->view->setVar('user', $user);
$this->view->setVar('account', $account);
$this->view->setVar('roles', $roles); $this->view->setVar('roles', $roles);
} }
@ -92,9 +94,15 @@ class UserController extends Controller
*/ */
public function updateAction($id) public function updateAction($id)
{ {
$type = $this->request->getPost('type');
$userService = new UserService(); $userService = new UserService();
$userService->updateUser($id); if ($type == 'user') {
$userService->updateUser($id);
} else {
$userService->updateAccount($id);
}
$location = $this->url->get(['for' => 'admin.user.list']); $location = $this->url->get(['for' => 'admin.user.list']);

View File

@ -18,9 +18,9 @@ class VodController extends Controller
*/ */
public function uploadSignatureAction() public function uploadSignatureAction()
{ {
$service = new VodService(); $vodService = new VodService();
$signature = $service->getUploadSignature(); $signature = $vodService->getUploadSignature();
return $this->ajaxSuccess(['signature' => $signature]); return $this->ajaxSuccess(['signature' => $signature]);
} }
@ -30,7 +30,6 @@ class VodController extends Controller
*/ */
public function playerAction() public function playerAction()
{ {
$courseId = $this->request->getQuery('course_id');
$chapterId = $this->request->getQuery('chapter_id'); $chapterId = $this->request->getQuery('chapter_id');
$playUrl = $this->request->getQuery('play_url'); $playUrl = $this->request->getQuery('play_url');
@ -38,7 +37,6 @@ class VodController extends Controller
$this->view->pick('public/vod_player'); $this->view->pick('public/vod_player');
$this->view->setVar('course_id', $courseId);
$this->view->setVar('chapter_id', $chapterId); $this->view->setVar('chapter_id', $chapterId);
$this->view->setVar('play_url', urldecode($playUrl)); $this->view->setVar('play_url', urldecode($playUrl));
} }
@ -54,13 +52,12 @@ class VodController extends Controller
$learning->user_id = $this->authUser->id; $learning->user_id = $this->authUser->id;
$learning->request_id = $query['request_id']; $learning->request_id = $query['request_id'];
$learning->course_id = $query['course_id'];
$learning->chapter_id = $query['chapter_id']; $learning->chapter_id = $query['chapter_id'];
$learning->position = $query['position']; $learning->position = $query['position'];
$syncerService = new LearningSyncerService(); $syncerService = new LearningSyncerService();
$syncerService->save($learning, $query['timeout']); $syncerService->addItem($learning, $query['timeout']);
return $this->ajaxSuccess(); return $this->ajaxSuccess();
} }

View File

@ -26,7 +26,9 @@ class AlipayTest extends PaymentTest
]; ];
$alipayService = new AlipayService(); $alipayService = new AlipayService();
$qrcode = $alipayService->getQrCode($outOrder); $qrcode = $alipayService->getQrCode($outOrder);
$result = $qrcode ?: false; $result = $qrcode ?: false;
return $result; return $result;
@ -40,12 +42,15 @@ class AlipayTest extends PaymentTest
public function cancelTestOrder($sn) public function cancelTestOrder($sn)
{ {
$tradeRepo = new TradeRepo(); $tradeRepo = new TradeRepo();
$trade = $tradeRepo->findBySn($sn); $trade = $tradeRepo->findBySn($sn);
$orderRepo = new OrderRepo(); $orderRepo = new OrderRepo();
$order = $orderRepo->findBySn($trade->order_sn);
$order = $orderRepo->findById($trade->order_id);
$alipayService = new AlipayService(); $alipayService = new AlipayService();
$response = $alipayService->cancelOrder($trade->sn); $response = $alipayService->cancelOrder($trade->sn);
if ($response) { if ($response) {

View File

@ -2,9 +2,9 @@
namespace App\Http\Admin\Services; namespace App\Http\Admin\Services;
use Phalcon\Mvc\User\Component as UserComponent; use Phalcon\Mvc\User\Component;
class AuthMenu extends UserComponent class AuthMenu extends Component
{ {

View File

@ -154,6 +154,68 @@ class AuthNode extends Service
], ],
], ],
], ],
[
'id' => '1-5',
'label' => '单页管理',
'type' => 'menu',
'child' => [
[
'id' => '1-5-1',
'label' => '单页列表',
'type' => 'menu',
'route' => 'admin.page.list',
],
[
'id' => '1-5-2',
'label' => '添加单页',
'type' => 'menu',
'route' => 'admin.page.add',
],
[
'id' => '1-5-3',
'label' => '编辑单页',
'type' => 'button',
'route' => 'admin.page.edit',
],
[
'id' => '1-5-4',
'label' => '删除单页',
'type' => 'button',
'route' => 'admin.page.delete',
],
],
],
[
'id' => '1-6',
'label' => '帮助管理',
'type' => 'menu',
'child' => [
[
'id' => '1-6-1',
'label' => '帮助列表',
'type' => 'menu',
'route' => 'admin.help.list',
],
[
'id' => '1-6-2',
'label' => '添加帮助',
'type' => 'menu',
'route' => 'admin.help.add',
],
[
'id' => '1-6-3',
'label' => '编辑帮助',
'type' => 'button',
'route' => 'admin.help.edit',
],
[
'id' => '1-6-4',
'label' => '删除帮助',
'type' => 'button',
'route' => 'admin.help.delete',
],
],
],
], ],
]; ];
@ -167,30 +229,30 @@ class AuthNode extends Service
'label' => '运营管理', 'label' => '运营管理',
'child' => [ 'child' => [
[ [
'id' => '2-5', 'id' => '2-1',
'label' => '学员管理', 'label' => '学员管理',
'type' => 'menu', 'type' => 'menu',
'child' => [ 'child' => [
[ [
'id' => '2-5-1', 'id' => '2-1-1',
'label' => '学员列表', 'label' => '学员列表',
'type' => 'menu', 'type' => 'menu',
'route' => 'admin.student.list', 'route' => 'admin.student.list',
], ],
[ [
'id' => '2-5-2', 'id' => '2-1-2',
'label' => '搜索学员', 'label' => '搜索学员',
'type' => 'menu', 'type' => 'menu',
'route' => 'admin.student.search', 'route' => 'admin.student.search',
], ],
[ [
'id' => '2-5-3', 'id' => '2-1-3',
'label' => '添加学员', 'label' => '添加学员',
'type' => 'menu', 'type' => 'menu',
'route' => 'admin.student.add', 'route' => 'admin.student.add',
], ],
[ [
'id' => '2-5-4', 'id' => '2-1-4',
'label' => '编辑学员', 'label' => '编辑学员',
'type' => 'button', 'type' => 'button',
'route' => 'admin.student.edit', 'route' => 'admin.student.edit',
@ -198,30 +260,61 @@ class AuthNode extends Service
], ],
], ],
[ [
'id' => '2-1', 'id' => '2-2',
'label' => '咨询管理',
'type' => 'menu',
'child' => [
[
'id' => '2-2-1',
'label' => '咨询列表',
'type' => 'menu',
'route' => 'admin.consult.list',
],
[
'id' => '2-2-2',
'label' => '搜索咨询',
'type' => 'menu',
'route' => 'admin.consult.search',
],
[
'id' => '2-2-3',
'label' => '编辑咨询',
'type' => 'button',
'route' => 'admin.consult.edit',
],
[
'id' => '2-2-4',
'label' => '删除咨询',
'type' => 'button',
'route' => 'admin.consult.delete',
],
],
],
[
'id' => '2-3',
'label' => '评价管理', 'label' => '评价管理',
'type' => 'menu', 'type' => 'menu',
'child' => [ 'child' => [
[ [
'id' => '2-1-1', 'id' => '2-3-1',
'label' => '评价列表', 'label' => '评价列表',
'type' => 'menu', 'type' => 'menu',
'route' => 'admin.review.list', 'route' => 'admin.review.list',
], ],
[ [
'id' => '2-1-2', 'id' => '2-3-2',
'label' => '搜索评价', 'label' => '搜索评价',
'type' => 'menu', 'type' => 'menu',
'route' => 'admin.review.search', 'route' => 'admin.review.search',
], ],
[ [
'id' => '2-1-3', 'id' => '2-3-3',
'label' => '编辑评价', 'label' => '编辑评价',
'type' => 'button', 'type' => 'button',
'route' => 'admin.review.edit', 'route' => 'admin.review.edit',
], ],
[ [
'id' => '2-1-4', 'id' => '2-3-4',
'label' => '删除评价', 'label' => '删除评价',
'type' => 'button', 'type' => 'button',
'route' => 'admin.review.delete', 'route' => 'admin.review.delete',
@ -229,67 +322,67 @@ class AuthNode extends Service
], ],
], ],
[ [
'id' => '2-3', 'id' => '2-4',
'label' => '评论管理',
'type' => 'menu',
'child' => [
[
'id' => '2-4-1',
'label' => '评论列表',
'type' => 'menu',
'route' => 'admin.comment.list',
],
[
'id' => '2-4-2',
'label' => '搜索评论',
'type' => 'menu',
'route' => 'admin.comment.search',
],
[
'id' => '2-4-3',
'label' => '编辑评论',
'type' => 'button',
'route' => 'admin.comment.edit',
],
[
'id' => '2-4-4',
'label' => '删除评论',
'type' => 'button',
'route' => 'admin.comment.delete',
],
],
],
[
'id' => '2-5',
'label' => '轮播管理', 'label' => '轮播管理',
'type' => 'menu', 'type' => 'menu',
'child' => [ 'child' => [
[ [
'id' => '2-3-1', 'id' => '2-5-1',
'label' => '轮播列表', 'label' => '轮播列表',
'type' => 'menu', 'type' => 'menu',
'route' => 'admin.slide.list', 'route' => 'admin.slide.list',
], ],
[ [
'id' => '2-3-2', 'id' => '2-5-2',
'label' => '添加轮播', 'label' => '添加轮播',
'type' => 'menu', 'type' => 'menu',
'route' => 'admin.slide.add', 'route' => 'admin.slide.add',
], ],
[ [
'id' => '2-3-3', 'id' => '2-5-3',
'label' => '编辑轮播', 'label' => '编辑轮播',
'type' => 'button', 'type' => 'button',
'route' => 'admin.slide.edit', 'route' => 'admin.slide.edit',
], ],
[ [
'id' => '2-3-4', 'id' => '2-5-4',
'label' => '删除轮播', 'label' => '删除轮播',
'type' => 'button', 'type' => 'button',
'route' => 'admin.slide.delete', 'route' => 'admin.slide.delete',
], ],
], ],
], ],
[
'id' => '2-4',
'label' => '单页管理',
'type' => 'menu',
'child' => [
[
'id' => '2-4-1',
'label' => '单页列表',
'type' => 'menu',
'route' => 'admin.page.list',
],
[
'id' => '2-4-2',
'label' => '添加单页',
'type' => 'menu',
'route' => 'admin.page.add',
],
[
'id' => '2-4-3',
'label' => '编辑单页',
'type' => 'button',
'route' => 'admin.page.edit',
],
[
'id' => '2-4-4',
'label' => '删除单页',
'type' => 'button',
'route' => 'admin.page.delete',
],
],
],
[ [
'id' => '2-6', 'id' => '2-6',
'label' => '导航管理', 'label' => '导航管理',
@ -321,37 +414,6 @@ class AuthNode extends Service
], ],
], ],
], ],
[
'id' => '2-7',
'label' => '帮助管理',
'type' => 'menu',
'child' => [
[
'id' => '2-7-1',
'label' => '帮助列表',
'type' => 'menu',
'route' => 'admin.help.list',
],
[
'id' => '2-7-2',
'label' => '添加帮助',
'type' => 'menu',
'route' => 'admin.help.add',
],
[
'id' => '2-7-3',
'label' => '编辑帮助',
'type' => 'button',
'route' => 'admin.help.edit',
],
[
'id' => '2-7-4',
'label' => '删除帮助',
'type' => 'button',
'route' => 'admin.help.delete',
],
],
],
], ],
]; ];

View File

@ -5,8 +5,9 @@ namespace App\Http\Admin\Services;
use App\Models\Role as RoleModel; use App\Models\Role as RoleModel;
use App\Models\User as UserModel; use App\Models\User as UserModel;
use App\Repos\Role as RoleRepo; use App\Repos\Role as RoleRepo;
use Phalcon\Mvc\User\Component;
class AuthUser extends \Phalcon\Mvc\User\Component class AuthUser extends Component
{ {
/** /**

View File

@ -5,6 +5,7 @@ namespace App\Http\Admin\Services;
use App\Models\Category as CategoryModel; use App\Models\Category as CategoryModel;
use App\Repos\Category as CategoryRepo; use App\Repos\Category as CategoryRepo;
use App\Validators\Category as CategoryValidator; use App\Validators\Category as CategoryValidator;
use Phalcon\Mvc\Model\Resultset;
class Category extends Service class Category extends Service
{ {
@ -179,6 +180,9 @@ class Category extends Service
{ {
$categoryRepo = new CategoryRepo(); $categoryRepo = new CategoryRepo();
/**
* @var Resultset|CategoryModel[] $categories
*/
$categories = $categoryRepo->findAll(['parent_id' => $parentId]); $categories = $categoryRepo->findAll(['parent_id' => $parentId]);
if ($categories->count() == 0) { if ($categories->count() == 0) {
@ -195,6 +199,9 @@ class Category extends Service
{ {
$categoryRepo = new CategoryRepo(); $categoryRepo = new CategoryRepo();
/**
* @var Resultset|CategoryModel[] $categories
*/
$categories = $categoryRepo->findAll(['parent_id' => $parentId]); $categories = $categoryRepo->findAll(['parent_id' => $parentId]);
if ($categories->count() == 0) { if ($categories->count() == 0) {

View File

@ -64,6 +64,8 @@ class Chapter extends Service
$this->updateChapterStats($chapter); $this->updateChapterStats($chapter);
$this->updateCourseStats($chapter); $this->updateCourseStats($chapter);
$this->eventsManager->fire('chapterAdmin:afterCreate', $this, $chapter);
return $chapter; return $chapter;
} }
@ -105,6 +107,8 @@ class Chapter extends Service
$this->updateChapterStats($chapter); $this->updateChapterStats($chapter);
$this->updateCourseStats($chapter); $this->updateCourseStats($chapter);
$this->eventsManager->fire('chapterAdmin:afterUpdate', $this, $chapter);
return $chapter; return $chapter;
} }
@ -123,6 +127,8 @@ class Chapter extends Service
$this->updateChapterStats($chapter); $this->updateChapterStats($chapter);
$this->updateCourseStats($chapter); $this->updateCourseStats($chapter);
$this->eventsManager->fire('chapterAdmin:afterDelete', $this, $chapter);
return $chapter; return $chapter;
} }
@ -137,6 +143,8 @@ class Chapter extends Service
$this->updateChapterStats($chapter); $this->updateChapterStats($chapter);
$this->updateCourseStats($chapter); $this->updateCourseStats($chapter);
$this->eventsManager->fire('chapterAdmin:afterRestore', $this, $chapter);
return $chapter; return $chapter;
} }

View File

@ -43,13 +43,13 @@ class ChapterContent extends Service
return $result; return $result;
} }
public function getVodFiles($chapterId) public function getPlayUrls($chapterId)
{ {
$chapterVodService = new ChapterVodService(); $chapterVodService = new ChapterVodService();
$vodFiles = $chapterVodService->getVodFiles($chapterId); $playUrls = $chapterVodService->getPlayUrls($chapterId);
return kg_array_object($vodFiles); return kg_array_object($playUrls);
} }
public function updateChapterContent($chapterId) public function updateChapterContent($chapterId)

View File

@ -0,0 +1,161 @@
<?php
namespace App\Http\Admin\Services;
use App\Builders\CommentList as CommentListBuilder;
use App\Library\Paginator\Query as PagerQuery;
use App\Repos\Chapter as ChapterRepo;
use App\Repos\Comment as CommentRepo;
use App\Repos\Course as CourseRepo;
use App\Validators\Comment as CommentValidator;
class Comment extends Service
{
public function getComments()
{
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['deleted'] = $params['deleted'] ?? 0;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$commentRepo = new CommentRepo();
$pager = $commentRepo->paginate($params, $sort, $page, $limit);
$result = $this->handleComments($pager);
return $result;
}
public function getCourse($courseId)
{
$courseRepo = new CourseRepo();
$result = $courseRepo->findById($courseId);
return $result;
}
public function getChapter($chapterId)
{
$chapterRepo = new ChapterRepo();
$result = $chapterRepo->findById($chapterId);
return $result;
}
public function getComment($id)
{
$comment = $this->findOrFail($id);
return $comment;
}
public function updateComment($id)
{
$comment = $this->findOrFail($id);
$post = $this->request->getPost();
$validator = new CommentValidator();
$data = [];
if (isset($post['content'])) {
$data['content'] = $validator->checkContent($post['content']);
}
if (isset($post['published'])) {
$data['published'] = $validator->checkPublishStatus($post['published']);
}
$comment->update($data);
return $comment;
}
public function deleteComment($id)
{
$comment = $this->findOrFail($id);
$comment->deleted = 1;
$comment->update();
$chapterRepo = new ChapterRepo();
$chapter = $chapterRepo->findById($comment->chapter_id);
$chapter->comment_count -= 1;
$chapter->update();
$courseRepo = new CourseRepo();
$course = $courseRepo->findById($comment->course_id);
$course->comment_count -= 1;
$course->update();
}
public function restoreComment($id)
{
$comment = $this->findOrFail($id);
$comment->deleted = 0;
$comment->update();
$chapterRepo = new ChapterRepo();
$chapter = $chapterRepo->findById($comment->chapter_id);
$chapter->comment_count += 1;
$chapter->update();
$courseRepo = new CourseRepo();
$course = $courseRepo->findById($comment->course_id);
$course->comment_count += 1;
$course->update();
}
private function findOrFail($id)
{
$validator = new CommentValidator();
$result = $validator->checkComment($id);
return $result;
}
private function handleComments($pager)
{
if ($pager->total_items > 0) {
$builder = new CommentListBuilder();
$pipeA = $pager->items->toArray();
$pipeB = $builder->handleCourses($pipeA);
$pipeC = $builder->handleChapters($pipeB);
$pipeD = $builder->handleUsers($pipeC);
$pipeE = $builder->arrayToObject($pipeD);
$pager->items = $pipeE;
}
return $pager;
}
}

View File

@ -3,6 +3,7 @@
namespace App\Http\Admin\Services; namespace App\Http\Admin\Services;
use App\Repos\Config as ConfigRepo; use App\Repos\Config as ConfigRepo;
use App\Repos\Vip as VipRepo;
class Config extends Service class Config extends Service
{ {
@ -108,4 +109,24 @@ class Config extends Service
$this->updateSectionConfig($section, $config); $this->updateSectionConfig($section, $config);
} }
public function getVipConfig()
{
$vipRepo = new VipRepo();
$config = $vipRepo->findAll(['deleted' => 0]);
return $config;
}
public function updateVipConfig($items)
{
$vipRepo = new VipRepo();
foreach ($items as $id => $price) {
$vip = $vipRepo->findById($id);
$vip->price = $price;
$vip->update();
}
}
} }

View File

@ -0,0 +1,136 @@
<?php
namespace App\Http\Admin\Services;
use App\Builders\ConsultList as ConsultListBuilder;
use App\Library\Paginator\Query as PagerQuery;
use App\Repos\Consult as ConsultRepo;
use App\Repos\Course as CourseRepo;
use App\Validators\Consult as ConsultValidator;
class Consult extends Service
{
public function getConsults()
{
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['deleted'] = $params['deleted'] ?? 0;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$consultRepo = new ConsultRepo();
$pager = $consultRepo->paginate($params, $sort, $page, $limit);
return $this->handleConsults($pager);
}
public function getCourse($courseId)
{
$courseRepo = new CourseRepo();
$result = $courseRepo->findById($courseId);
return $result;
}
public function getConsult($id)
{
$result = $this->findOrFail($id);
return $result;
}
public function updateConsult($id)
{
$consult = $this->findOrFail($id);
$post = $this->request->getPost();
$validator = new ConsultValidator();
$data = [];
if (isset($post['question'])) {
$data['question'] = $validator->checkQuestion($post['question']);
}
if (isset($post['answer'])) {
$data['answer'] = $validator->checkAnswer($post['answer']);
}
if (isset($post['published'])) {
$data['published'] = $validator->checkPublishStatus($post['published']);
}
$consult->update($data);
return $consult;
}
public function deleteConsult($id)
{
$consult = $this->findOrFail($id);
$consult->deleted = 1;
$consult->update();
$courseRepo = new CourseRepo();
$course = $courseRepo->findById($consult->course_id);
$course->consult_count -= 1;
$course->update();
}
public function restoreConsult($id)
{
$consult = $this->findOrFail($id);
$consult->deleted = 0;
$consult->update();
$courseRepo = new CourseRepo();
$course = $courseRepo->findById($consult->course_id);
$course->consult_count += 1;
$course->update();
}
protected function findOrFail($id)
{
$validator = new ConsultValidator();
$result = $validator->checkConsult($id);
return $result;
}
protected function handleConsults($pager)
{
if ($pager->total_items > 0) {
$builder = new ConsultListBuilder();
$pipeA = $pager->items->toArray();
$pipeB = $builder->handleCourses($pipeA);
$pipeC = $builder->handleUsers($pipeB);
$pipeD = $builder->arrayToObject($pipeC);
$pager->items = $pipeD;
}
return $pager;
}
}

View File

@ -4,10 +4,12 @@ namespace App\Http\Admin\Services;
use App\Builders\CourseList as CourseListBuilder; use App\Builders\CourseList as CourseListBuilder;
use App\Library\Paginator\Query as PagerQuery; use App\Library\Paginator\Query as PagerQuery;
use App\Models\Category as CategoryModel;
use App\Models\Course as CourseModel; use App\Models\Course as CourseModel;
use App\Models\CourseCategory as CourseCategoryModel; use App\Models\CourseCategory as CourseCategoryModel;
use App\Models\CourseRelated as CourseRelatedModel; use App\Models\CourseRelated as CourseRelatedModel;
use App\Models\CourseUser as CourseUserModel; use App\Models\CourseUser as CourseUserModel;
use App\Models\User as UserModel;
use App\Repos\Category as CategoryRepo; use App\Repos\Category as CategoryRepo;
use App\Repos\Chapter as ChapterRepo; use App\Repos\Chapter as ChapterRepo;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
@ -16,6 +18,7 @@ use App\Repos\CourseRelated as CourseRelatedRepo;
use App\Repos\CourseUser as CourseUserRepo; use App\Repos\CourseUser as CourseUserRepo;
use App\Repos\User as UserRepo; use App\Repos\User as UserRepo;
use App\Validators\Course as CourseValidator; use App\Validators\Course as CourseValidator;
use Phalcon\Mvc\Model\Resultset;
class Course extends Service class Course extends Service
{ {
@ -61,12 +64,13 @@ class Course extends Service
$data['model'] = $validator->checkModel($post['model']); $data['model'] = $validator->checkModel($post['model']);
$data['title'] = $validator->checkTitle($post['title']); $data['title'] = $validator->checkTitle($post['title']);
$data['published'] = 0;
$course = new CourseModel(); $course = new CourseModel();
$course->create($data); $course->create($data);
$this->eventsManager->fire('courseAdmin:afterCreate', $this, $course);
return $course; return $course;
} }
@ -111,7 +115,8 @@ class Course extends Service
} else { } else {
$data['market_price'] = $validator->checkMarketPrice($post['market_price']); $data['market_price'] = $validator->checkMarketPrice($post['market_price']);
$data['vip_price'] = $validator->checkVipPrice($post['vip_price']); $data['vip_price'] = $validator->checkVipPrice($post['vip_price']);
$data['expiry'] = $validator->checkExpiry($post['expiry']); $data['study_expiry'] = $validator->checkStudyExpiry($post['study_expiry']);
$data['refund_expiry'] = $validator->checkRefundExpiry($post['refund_expiry']);
} }
} }
@ -136,6 +141,8 @@ class Course extends Service
$course->update($data); $course->update($data);
$this->eventsManager->fire('courseAdmin:afterUpdate', $this, $course);
return $course; return $course;
} }
@ -147,6 +154,8 @@ class Course extends Service
$course->update(); $course->update();
$this->eventsManager->fire('courseAdmin:afterDelete', $this, $course);
return $course; return $course;
} }
@ -158,13 +167,32 @@ class Course extends Service
$course->update(); $course->update();
$this->eventsManager->fire('courseAdmin:afterRestore', $this, $course);
return $course; return $course;
} }
public function getStudyExpiryOptions()
{
$options = CourseModel::studyExpiryOptions();
return kg_array_object($options);
}
public function getRefundExpiryOptions()
{
$options = CourseModel::refundExpiryOptions();
return kg_array_object($options);
}
public function getXmCategories($id) public function getXmCategories($id)
{ {
$categoryRepo = new CategoryRepo(); $categoryRepo = new CategoryRepo();
/**
* @var Resultset|CategoryModel[] $allCategories
*/
$allCategories = $categoryRepo->findAll(['deleted' => 0]); $allCategories = $categoryRepo->findAll(['deleted' => 0]);
if ($allCategories->count() == 0) { if ($allCategories->count() == 0) {
@ -174,8 +202,14 @@ class Course extends Service
$courseCategoryIds = []; $courseCategoryIds = [];
if ($id > 0) { if ($id > 0) {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset|CategoryModel[] $courseCategories
*/
$courseCategories = $courseRepo->findCategories($id); $courseCategories = $courseRepo->findCategories($id);
if ($courseCategories->count() > 0) { if ($courseCategories->count() > 0) {
foreach ($courseCategories as $category) { foreach ($courseCategories as $category) {
$courseCategoryIds[] = $category->id; $courseCategoryIds[] = $category->id;
@ -214,6 +248,9 @@ class Course extends Service
{ {
$userRepo = new UserRepo(); $userRepo = new UserRepo();
/**
* @var Resultset|UserModel[] $allTeachers
*/
$allTeachers = $userRepo->findTeachers(); $allTeachers = $userRepo->findTeachers();
if ($allTeachers->count() == 0) { if ($allTeachers->count() == 0) {
@ -223,8 +260,14 @@ class Course extends Service
$courseTeacherIds = []; $courseTeacherIds = [];
if ($id > 0) { if ($id > 0) {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset|UserModel[] $courseTeachers
*/
$courseTeachers = $courseRepo->findTeachers($id); $courseTeachers = $courseRepo->findTeachers($id);
if ($courseTeachers->count() > 0) { if ($courseTeachers->count() > 0) {
foreach ($courseTeachers as $teacher) { foreach ($courseTeachers as $teacher) {
$courseTeacherIds[] = $teacher->id; $courseTeacherIds[] = $teacher->id;
@ -250,6 +293,9 @@ class Course extends Service
{ {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset|CourseModel[] $courses
*/
$courses = $courseRepo->findRelatedCourses($id); $courses = $courseRepo->findRelatedCourses($id);
$list = []; $list = [];
@ -297,6 +343,9 @@ class Course extends Service
{ {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset|UserModel[] $courseTeachers
*/
$courseTeachers = $courseRepo->findTeachers($course->id); $courseTeachers = $courseRepo->findTeachers($course->id);
$originTeacherIds = []; $originTeacherIds = [];
@ -318,7 +367,7 @@ class Course extends Service
'user_id' => $teacherId, 'user_id' => $teacherId,
'role_type' => CourseUserModel::ROLE_TEACHER, 'role_type' => CourseUserModel::ROLE_TEACHER,
'source_type' => CourseUserModel::SOURCE_IMPORT, 'source_type' => CourseUserModel::SOURCE_IMPORT,
'expire_time' => strtotime('+10 years'), 'expiry_time' => strtotime('+10 years'),
]); ]);
} }
} }
@ -340,6 +389,9 @@ class Course extends Service
{ {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset|CategoryModel[] $courseCategories
*/
$courseCategories = $courseRepo->findCategories($course->id); $courseCategories = $courseRepo->findCategories($course->id);
$originCategoryIds = []; $originCategoryIds = [];
@ -380,6 +432,9 @@ class Course extends Service
{ {
$courseRepo = new CourseRepo(); $courseRepo = new CourseRepo();
/**
* @var Resultset|CourseModel[] $relatedCourses
*/
$relatedCourses = $courseRepo->findRelatedCourses($course->id); $relatedCourses = $courseRepo->findRelatedCourses($course->id);
$originRelatedIds = []; $originRelatedIds = [];

View File

@ -2,10 +2,11 @@
namespace App\Http\Admin\Services; namespace App\Http\Admin\Services;
use App\Caches\NavList as NavListCache; use App\Caches\NavTreeList as NavTreeListCache;
use App\Models\Nav as NavModel; use App\Models\Nav as NavModel;
use App\Repos\Nav as NavRepo; use App\Repos\Nav as NavRepo;
use App\Validators\Nav as NavValidator; use App\Validators\Nav as NavValidator;
use Phalcon\Mvc\Model\Resultset;
class Nav extends Service class Nav extends Service
{ {
@ -191,7 +192,7 @@ class Nav extends Service
$nav->child_count = $childCount; $nav->child_count = $childCount;
$nav->update(); $nav->update();
$cache = new NavListCache(); $cache = new NavTreeListCache();
$cache->rebuild(); $cache->rebuild();
} }
@ -199,6 +200,9 @@ class Nav extends Service
{ {
$navRepo = new NavRepo(); $navRepo = new NavRepo();
/**
* @var Resultset $navs
*/
$navs = $navRepo->findAll(['parent_id' => $parentId]); $navs = $navRepo->findAll(['parent_id' => $parentId]);
if ($navs->count() == 0) { if ($navs->count() == 0) {
@ -215,6 +219,9 @@ class Nav extends Service
{ {
$navRepo = new NavRepo(); $navRepo = new NavRepo();
/**
* @var Resultset $navs
*/
$navs = $navRepo->findAll(['parent_id' => $parentId]); $navs = $navRepo->findAll(['parent_id' => $parentId]);
if ($navs->count() == 0) { if ($navs->count() == 0) {

View File

@ -88,7 +88,7 @@ class Order extends Service
{ {
$validator = new OrderValidator(); $validator = new OrderValidator();
$result = $validator->checkOrder($id); $result = $validator->checkOrderById($id);
return $result; return $result;
} }

View File

@ -23,7 +23,8 @@ abstract class PaymentTest extends Service
$order->subject = '测试 - 支付测试0.01元'; $order->subject = '测试 - 支付测试0.01元';
$order->amount = 0.01; $order->amount = 0.01;
$order->user_id = $authUser->id; $order->user_id = $authUser->id;
$order->item_type = OrderModel::TYPE_TEST; $order->item_type = OrderModel::ITEM_TEST;
$order->create(); $order->create();
return $order; return $order;
@ -40,10 +41,11 @@ abstract class PaymentTest extends Service
$trade = new TradeModel(); $trade = new TradeModel();
$trade->user_id = $order->user_id; $trade->user_id = $order->user_id;
$trade->order_sn = $order->sn; $trade->order_id = $order->id;
$trade->subject = $order->subject; $trade->subject = $order->subject;
$trade->amount = $order->amount; $trade->amount = $order->amount;
$trade->channel = TradeModel::CHANNEL_ALIPAY; $trade->channel = TradeModel::CHANNEL_ALIPAY;
$trade->create(); $trade->create();
return $trade; return $trade;

View File

@ -20,6 +20,16 @@ class Refund extends Service
$pageQuery = new PaginateQuery(); $pageQuery = new PaginateQuery();
$params = $pageQuery->getParams(); $params = $pageQuery->getParams();
/**
* 兼容订单编号或订单序号查询
*/
if (isset($params['order_id']) && strlen($params['order_id']) > 10) {
$orderRepo = new OrderRepo();
$order = $orderRepo->findBySn($params['order_id']);
$params['order_id'] = $order ? $order->id : -1000;
}
$sort = $pageQuery->getSort(); $sort = $pageQuery->getSort();
$page = $pageQuery->getPage(); $page = $pageQuery->getPage();
$limit = $pageQuery->getLimit(); $limit = $pageQuery->getLimit();
@ -38,29 +48,29 @@ class Refund extends Service
return $refund; return $refund;
} }
public function getTrade($sn) public function getTrade($tradeId)
{ {
$tradeRepo = new TradeRepo(); $tradeRepo = new TradeRepo();
$trade = $tradeRepo->findBySn($sn); $trade = $tradeRepo->findById($tradeId);
return $trade; return $trade;
} }
public function getOrder($sn) public function getOrder($orderId)
{ {
$orderRepo = new OrderRepo(); $orderRepo = new OrderRepo();
$order = $orderRepo->findBySn($sn); $order = $orderRepo->findById($orderId);
return $order; return $order;
} }
public function getUser($id) public function getUser($userId)
{ {
$userRepo = new UserRepo(); $userRepo = new UserRepo();
$user = $userRepo->findById($id); $user = $userRepo->findById($userId);
return $user; return $user;
} }
@ -121,9 +131,10 @@ class Refund extends Service
$pipeA = $pager->items->toArray(); $pipeA = $pager->items->toArray();
$pipeB = $builder->handleUsers($pipeA); $pipeB = $builder->handleUsers($pipeA);
$pipeC = $builder->arrayToObject($pipeB); $pipeC = $builder->handleOrders($pipeB);
$pipeD = $builder->arrayToObject($pipeC);
$pager->items = $pipeC; $pager->items = $pipeD;
} }
return $pager; return $pager;

View File

@ -2,7 +2,9 @@
namespace App\Http\Admin\Services; namespace App\Http\Admin\Services;
class Service extends \Phalcon\Mvc\User\Component use Phalcon\Mvc\User\Component;
class Service extends Component
{ {
} }

View File

@ -9,7 +9,7 @@ class Session extends Service
{ {
/** /**
* @var \App\Http\Admin\Services\AuthUser * @var AuthUser
*/ */
protected $auth; protected $auth;

View File

@ -57,10 +57,7 @@ class Slide extends Service
$data['content'] = $validator->checkLink($post['content']); $data['content'] = $validator->checkLink($post['content']);
} }
$data['start_time'] = strtotime(date('Y-m-d'));
$data['end_time'] = strtotime('+15 days', $data['start_time']);
$data['priority'] = 10; $data['priority'] = 10;
$data['published'] = 0;
$slide = new SlideModel(); $slide = new SlideModel();
@ -107,12 +104,6 @@ class Slide extends Service
$data['priority'] = $validator->checkPriority($post['priority']); $data['priority'] = $validator->checkPriority($post['priority']);
} }
if (isset($post['start_time']) || isset($post['end_time'])) {
$data['start_time'] = $validator->checkStartTime($post['start_time']);
$data['end_time'] = $validator->checkEndTime($post['end_time']);
$validator->checkTimeRange($post['start_time'], $post['end_time']);
}
if (isset($post['published'])) { if (isset($post['published'])) {
$data['published'] = $validator->checkPublishStatus($post['published']); $data['published'] = $validator->checkPublishStatus($post['published']);
} }

View File

@ -10,9 +10,10 @@ use App\Repos\Course as CourseRepo;
use App\Repos\CourseUser as CourseUserRepo; use App\Repos\CourseUser as CourseUserRepo;
use App\Repos\Learning as LearningRepo; use App\Repos\Learning as LearningRepo;
use App\Repos\User as UserRepo; use App\Repos\User as UserRepo;
use App\Services\CourseStats as CourseStatsUpdater;
use App\Validators\CourseUser as CourseUserValidator; use App\Validators\CourseUser as CourseUserValidator;
class CourseStudent extends Service class Student extends Service
{ {
public function getCourse($courseId) public function getCourse($courseId)
@ -29,7 +30,7 @@ class CourseStudent extends Service
return $repo->findById($userId); return $repo->findById($userId);
} }
public function getCourseStudents() public function getPlans()
{ {
$pagerQuery = new PagerQuery(); $pagerQuery = new PagerQuery();
@ -46,10 +47,10 @@ class CourseStudent extends Service
$pager = $courseUserRepo->paginate($params, $sort, $page, $limit); $pager = $courseUserRepo->paginate($params, $sort, $page, $limit);
return $this->handleCourseStudents($pager); return $this->handlePlans($pager);
} }
public function getCourseLearnings() public function getLearnings()
{ {
$pagerQuery = new PagerQuery(); $pagerQuery = new PagerQuery();
@ -63,17 +64,17 @@ class CourseStudent extends Service
$pager = $learningRepo->paginate($params, $sort, $page, $limit); $pager = $learningRepo->paginate($params, $sort, $page, $limit);
return $this->handleCourseLearnings($pager); return $this->handleLearnings($pager);
} }
public function getCourseStudent($courseId, $userId) public function getPlan($id)
{ {
$result = $this->findOrFail($courseId, $userId); $result = $this->findOrFail($id);
return $result; return $result;
} }
public function createCourseStudent() public function createPlan()
{ {
$post = $this->request->getPost(); $post = $this->request->getPost();
@ -86,7 +87,7 @@ class CourseStudent extends Service
$data['course_id'] = $validator->checkCourseId($post['course_id']); $data['course_id'] = $validator->checkCourseId($post['course_id']);
$data['user_id'] = $validator->checkUserId($post['user_id']); $data['user_id'] = $validator->checkUserId($post['user_id']);
$data['expire_time'] = $validator->checkExpireTime($post['expire_time']); $data['expiry_time'] = $validator->checkExpiryTime($post['expiry_time']);
$validator->checkIfJoined($post['course_id'], $post['user_id']); $validator->checkIfJoined($post['course_id'], $post['user_id']);
@ -99,27 +100,23 @@ class CourseStudent extends Service
return $courseUser; return $courseUser;
} }
public function updateCourseStudent() public function updatePlan()
{ {
$post = $this->request->getPost(); $post = $this->request->getPost();
$courseStudent = $this->findOrFail($post['course_id'], $post['user_id']); $plan = $this->findOrFail($post['plan_id']);
$validator = new CourseUserValidator(); $validator = new CourseUserValidator();
$data = []; $data = [];
if (isset($post['expire_time'])) { if (isset($post['expiry_time'])) {
$data['expire_time'] = $validator->checkExpireTime($post['expire_time']); $data['expiry_time'] = $validator->checkExpiryTime($post['expiry_time']);
} }
if (isset($post['locked'])) { $plan->update($data);
$data['locked'] = $validator->checkLockStatus($post['locked']);
}
$courseStudent->update($data); return $plan;
return $courseStudent;
} }
protected function updateUserCount($courseId) protected function updateUserCount($courseId)
@ -130,19 +127,19 @@ class CourseStudent extends Service
$updater = new CourseStatsUpdater(); $updater = new CourseStatsUpdater();
$updater->updateUserCount($course); $updater->updateUserCount($course->id);
} }
protected function findOrFail($courseId, $userId) protected function findOrFail($id)
{ {
$validator = new CourseUserValidator(); $validator = new CourseUserValidator();
$result = $validator->checkCourseStudent($courseId, $userId); $result = $validator->checkCourseUser($id);
return $result; return $result;
} }
protected function handleCourseStudents($pager) protected function handlePlans($pager)
{ {
if ($pager->total_items > 0) { if ($pager->total_items > 0) {
@ -159,7 +156,7 @@ class CourseStudent extends Service
return $pager; return $pager;
} }
protected function handleCourseLearnings($pager) protected function handleLearnings($pager)
{ {
if ($pager->total_items > 0) { if ($pager->total_items > 0) {

View File

@ -20,6 +20,16 @@ class Trade extends Service
$pageQuery = new PaginateQuery(); $pageQuery = new PaginateQuery();
$params = $pageQuery->getParams(); $params = $pageQuery->getParams();
/**
* 兼容订单编号或订单序号查询
*/
if (isset($params['order_id']) && strlen($params['order_id']) > 10) {
$orderRepo = new OrderRepo();
$order = $orderRepo->findBySn($params['order_id']);
$params['order_id'] = $order ? $order->id : -1000;
}
$sort = $pageQuery->getSort(); $sort = $pageQuery->getSort();
$page = $pageQuery->getPage(); $page = $pageQuery->getPage();
$limit = $pageQuery->getLimit(); $limit = $pageQuery->getLimit();
@ -40,29 +50,29 @@ class Trade extends Service
return $trade; return $trade;
} }
public function getOrder($sn) public function getOrder($orderId)
{ {
$orderRepo = new OrderRepo(); $orderRepo = new OrderRepo();
$order = $orderRepo->findBySn($sn); $order = $orderRepo->findById($orderId);
return $order; return $order;
} }
public function getRefunds($sn) public function getRefunds($tradeId)
{ {
$tradeRepo = new TradeRepo(); $tradeRepo = new TradeRepo();
$refunds = $tradeRepo->findRefunds($sn); $refunds = $tradeRepo->findRefunds($tradeId);
return $refunds; return $refunds;
} }
public function getUser($id) public function getUser($userId)
{ {
$userRepo = new UserRepo(); $userRepo = new UserRepo();
$user = $userRepo->findById($id); $user = $userRepo->findById($userId);
return $user; return $user;
} }
@ -103,9 +113,9 @@ class Trade extends Service
$refund->subject = $trade->subject; $refund->subject = $trade->subject;
$refund->amount = $trade->amount; $refund->amount = $trade->amount;
$refund->user_id = $trade->user_id; $refund->user_id = $trade->user_id;
$refund->order_sn = $trade->order_sn; $refund->order_id = $trade->order_id;
$refund->trade_sn = $trade->sn; $refund->trade_id = $trade->sn;
$refund->apply_reason = '后台人工申请退款'; $refund->apply_note = '后台人工申请退款';
$refund->create(); $refund->create();
@ -129,9 +139,10 @@ class Trade extends Service
$pipeA = $pager->items->toArray(); $pipeA = $pager->items->toArray();
$pipeB = $builder->handleUsers($pipeA); $pipeB = $builder->handleUsers($pipeA);
$pipeC = $builder->arrayToObject($pipeB); $pipeC = $builder->handleOrders($pipeB);
$pipeD = $builder->arrayToObject($pipeC);
$pager->items = $pipeC; $pager->items = $pipeD;
} }
return $pager; return $pager;

View File

@ -4,10 +4,11 @@ namespace App\Http\Admin\Services;
use App\Builders\UserList as UserListBuilder; use App\Builders\UserList as UserListBuilder;
use App\Library\Paginator\Query as PaginateQuery; use App\Library\Paginator\Query as PaginateQuery;
use App\Library\Util\Password as PasswordUtil; use App\Models\Account as AccountModel;
use App\Models\User as UserModel; use App\Repos\Account as AccountRepo;
use App\Repos\Role as RoleRepo; use App\Repos\Role as RoleRepo;
use App\Repos\User as UserRepo; use App\Repos\User as UserRepo;
use App\Validators\Account as AccountValidator;
use App\Validators\User as UserValidator; use App\Validators\User as UserValidator;
class User extends Service class User extends Service
@ -47,33 +48,49 @@ class User extends Service
return $user; return $user;
} }
public function getAccount($id)
{
$accountRepo = new AccountRepo();
$account = $accountRepo->findById($id);
return $account;
}
public function createUser() public function createUser()
{ {
$post = $this->request->getPost(); $post = $this->request->getPost();
$validator = new UserValidator(); $accountValidator = new AccountValidator();
$name = $validator->checkName($post['name']); $phone = $accountValidator->checkPhone($post['phone']);
$password = $validator->checkPassword($post['password']); $password = $accountValidator->checkPassword($post['password']);
$eduRole = $validator->checkEduRole($post['edu_role']);
$adminRole = $validator->checkAdminRole($post['admin_role']);
$validator->checkIfNameTaken($name); $accountValidator->checkIfPhoneTaken($post['phone']);
$data = []; $userValidator = new UserValidator();
$data['name'] = $name; $eduRole = $userValidator->checkEduRole($post['edu_role']);
$data['salt'] = PasswordUtil::salt(); $adminRole = $userValidator->checkAdminRole($post['admin_role']);
$data['password'] = PasswordUtil::hash($password, $data['salt']);
$data['edu_role'] = $eduRole;
$data['admin_role'] = $adminRole;
$user = new UserModel(); $account = new AccountModel();
$user->create($data); $account->phone = $phone;
$account->password = $password;
if ($user->admin_role > 0) { $account->create();
$this->updateAdminUserCount($user->admin_role);
$userRepo = new UserRepo();
$user = $userRepo->findById($account->id);
$user->edu_role = $eduRole;
$user->admin_role = $adminRole;
$user->update();
if ($adminRole > 0) {
$this->updateAdminUserCount($adminRole);
} }
return $user; return $user;
@ -89,6 +106,13 @@ class User extends Service
$data = []; $data = [];
if (isset($post['name'])) {
$data['name'] = $validator->checkName($post['name']);
if ($post['name'] != $user->name) {
$validator->checkIfNameTaken($post['name']);
}
}
if (isset($post['title'])) { if (isset($post['title'])) {
$data['title'] = $validator->checkTitle($post['title']); $data['title'] = $validator->checkTitle($post['title']);
} }
@ -105,13 +129,24 @@ class User extends Service
$data['admin_role'] = $validator->checkAdminRole($post['admin_role']); $data['admin_role'] = $validator->checkAdminRole($post['admin_role']);
} }
if (isset($post['vip'])) {
$data['vip'] = $validator->checkVipStatus($post['vip']);
}
if (isset($post['vip_expiry_time'])) {
$data['vip_expiry_time'] = $validator->checkVipExpiryTime($post['vip_expiry_time']);
if ($data['vip_expiry_time'] < time()) {
$data['vip'] = 0;
}
}
if (isset($post['locked'])) { if (isset($post['locked'])) {
$data['locked'] = $validator->checkLockStatus($post['locked']); $data['locked'] = $validator->checkLockStatus($post['locked']);
} }
if (isset($post['lock_expiry'])) { if (isset($post['lock_expiry_time'])) {
$data['lock_expiry'] = $validator->checkLockExpiry($post['lock_expiry']); $data['lock_expiry_time'] = $validator->checkLockExpiryTime($post['lock_expiry_time']);
if ($data['lock_expiry'] < time()) { if ($data['lock_expiry_time'] < time()) {
$data['locked'] = 0; $data['locked'] = 0;
} }
} }
@ -131,6 +166,41 @@ class User extends Service
return $user; return $user;
} }
public function updateAccount($id)
{
$post = $this->request->getPost();
$accountRepo = new AccountRepo();
$account = $accountRepo->findById($id);
$validator = new AccountValidator();
$data = [];
if (!empty($post['phone'])) {
$data['phone'] = $validator->checkPhone($post['phone']);
if ($post['phone'] != $account->phone) {
$validator->checkIfPhoneTaken($post['phone']);
}
}
if (!empty($post['email'])) {
$data['email'] = $validator->checkEmail($post['email']);
if ($post['email'] != $account->email) {
$validator->checkIfEmailTaken($post['email']);
}
}
if (!empty($post['password'])) {
$data['password'] = $validator->checkPassword($post['password']);
}
$account->update($data);
return $account;
}
protected function findOrFail($id) protected function findOrFail($id)
{ {
$validator = new UserValidator(); $validator = new UserValidator();
@ -146,9 +216,7 @@ class User extends Service
$role = $roleRepo->findById($roleId); $role = $roleRepo->findById($roleId);
if (!$role) { if (!$role) return;
return false;
}
$userCount = $roleRepo->countUsers($roleId); $userCount = $roleRepo->countUsers($roleId);

View File

@ -0,0 +1,66 @@
<?php
namespace App\Http\Admin\Services;
use App\Models\Order as OrderModel;
use App\Models\Trade as TradeModel;
use App\Repos\Order as OrderRepo;
use App\Repos\Trade as TradeRepo;
use App\Services\Wechat as WechatService;
class WechatTest extends PaymentTest
{
/**
* 获取测试二维码
*
* @param TradeModel $trade
* @return mixed
*/
public function getTestQrcode($trade)
{
$outOrder = [
'out_trade_no' => $trade->sn,
'total_fee' => 100 * $trade->amount,
'body' => $trade->subject,
];
$wechatService = new WechatService();
$qrcode = $wechatService->getQrCode($outOrder);
$result = $qrcode ?: false;
return $result;
}
/**
* 取消测试订单
*
* @param string $sn
*/
public function cancelTestOrder($sn)
{
$tradeRepo = new TradeRepo();
$trade = $tradeRepo->findBySn($sn);
$orderRepo = new OrderRepo();
$order = $orderRepo->findById($trade->order_id);
$wechatService = new WechatService();
$response = $wechatService->closeOrder($trade->sn);
if ($response) {
$trade->status = TradeModel::STATUS_CLOSED;
$trade->update();
if ($order->status != OrderModel::STATUS_PENDING) {
$order->status = OrderModel::STATUS_PENDING;
$order->update();
}
}
}
}

View File

@ -1,41 +0,0 @@
<?php
namespace App\Http\Admin\Services;
use App\Models\Trade as TradeModel;
class WxpayTest extends PaymentTest
{
/**
* 获取测试二维码
*
* @param TradeModel $trade
* @return mixed
*/
public function getTestQrcode($trade)
{
$outOrder = [
'out_trade_no' => $trade->sn,
'total_fee' => 100 * $trade->amount,
'body' => $trade->subject,
];
$wxpayService = new WxpayService();
$qrcode = $wxpayService->qrcode($outOrder);
$result = $qrcode ?: false;
return $result;
}
/**
* 取消测试订单
*
* @param string $sn
*/
public function cancelTestOrder($sn)
{
}
}

View File

@ -101,7 +101,7 @@
var categoryId = $(this).attr('category-id'); var categoryId = $(this).attr('category-id');
var checked = $(this).is(':checked'); var checked = $(this).is(':checked');
var published = checked ? 1 : 0; var published = checked ? 1 : 0;
var tips = published == 1 ? '确定要发布分类?' : '确定要下线分类?'; var tips = published === 1 ? '确定要发布分类?' : '确定要下线分类?';
layer.confirm(tips, function () { layer.confirm(tips, function () {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',

View File

@ -1,4 +1,4 @@
{% if vod_files %} {% if play_urls %}
<fieldset class="layui-elem-field layui-field-title"> <fieldset class="layui-elem-field layui-field-title">
<legend>视频信息</legend> <legend>视频信息</legend>
</fieldset> </fieldset>
@ -11,7 +11,7 @@
<th>大小</th> <th>大小</th>
<th width="16%">操作</th> <th width="16%">操作</th>
</tr> </tr>
{% for item in vod_files %} {% for item in play_urls %}
<tr> <tr>
<td>{{ item.format }}</td> <td>{{ item.format }}</td>
<td>{{ item.duration|play_duration }}</td> <td>{{ item.duration|play_duration }}</td>
@ -19,7 +19,7 @@
<td>{{ item.rate }}kbps</td> <td>{{ item.rate }}kbps</td>
<td>{{ item.size }}M</td> <td>{{ item.size }}M</td>
<td> <td>
<span class="layui-btn layui-btn-sm kg-preview" course-id="{{ chapter.course_id }}" chapter-id="{{ chapter.id }}" play-url="{{ item.url|url_encode }}">预览</span> <span class="layui-btn layui-btn-sm kg-preview" chapter-id="{{ chapter.id }}" play-url="{{ item.url|url_encode }}">预览</span>
<span class="layui-btn layui-btn-sm kg-copy" data-clipboard-text="{{ item.url }}">复制</span> <span class="layui-btn layui-btn-sm kg-copy" data-clipboard-text="{{ item.url }}">复制</span>
</td> </td>
</tr> </tr>

View File

@ -46,8 +46,10 @@
<td>{{ file_status(item.attrs['file_status']) }}</td> <td>{{ file_status(item.attrs['file_status']) }}</td>
<td>{{ item.attrs['duration']|play_duration }}</td> <td>{{ item.attrs['duration']|play_duration }}</td>
<td><input class="layui-input kg-priority-input" type="text" name="priority" value="{{ item.priority }}" chapter-id="{{ item.id }}"></td> <td><input class="layui-input kg-priority-input" type="text" name="priority" value="{{ item.priority }}" chapter-id="{{ item.id }}"></td>
<td><input type="checkbox" name="free" value="1" lay-skin="switch" lay-text="是|否" lay-filter="switch-free" chapter-id="{{ item.id }}" {% if item.free == 1 %}checked{% endif %}></td> <td><input type="checkbox" name="free" value="1" lay-skin="switch" lay-text="是|否" lay-filter="switch-free" chapter-id="{{ item.id }}"
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="switch-published" chapter-id="{{ item.id }}" {% if item.published == 1 %}checked{% endif %}></td> {% if item.free == 1 %}checked{% endif %}></td>
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="switch-published" chapter-id="{{ item.id }}"
{% if item.published == 1 %}checked{% endif %}></td>
<td align="center"> <td align="center">
<div class="layui-dropdown"> <div class="layui-dropdown">
<button class="layui-btn layui-btn-sm">操作 <span class="layui-icon layui-icon-triangle-d"></span></button> <button class="layui-btn layui-btn-sm">操作 <span class="layui-icon layui-icon-triangle-d"></span></button>

View File

@ -0,0 +1,30 @@
<fieldset class="layui-elem-field layui-field-title">
<legend>编辑评论</legend>
</fieldset>
<form class="layui-form" method="POST" action="{{ url({'for':'admin.comment.update','id':comment.id}) }}">
<div class="layui-form-item">
<label class="layui-form-label">内容</label>
<div class="layui-input-block">
<textarea name="content" lay-verify="required" class="layui-textarea">{{ comment.content }}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">开启</label>
<div class="layui-input-block">
<input type="radio" name="published" value="1" title="是" {% if comment.published == 1 %}checked="true"{% endif %}>
<input type="radio" name="published" value="0" title="否" {% if comment.published == 0 %}checked="true"{% endif %}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="go">提交</button>
<button type="reset" class="kg-back layui-btn layui-btn-primary">返回</button>
</div>
</div>
</form>

View File

@ -0,0 +1,114 @@
<div class="kg-nav">
<div class="kg-nav-left">
<span class="layui-breadcrumb">
<a class="kg-back"><i class="layui-icon layui-icon-return"></i> 返回</a>
{% if course %}
<a><cite>{{ course.title }}</cite></a>
{% endif %}
{% if chapter %}
<a><cite>{{ chapter.title }}</cite></a>
{% endif %}
<a><cite>评论管理</cite></a>
</span>
</div>
<div class="kg-nav-right">
<a class="layui-btn layui-btn-sm" href="{{ url({'for':'admin.comment.search'}) }}">
<i class="layui-icon layui-icon-search"></i>搜索评论
</a>
</div>
</div>
<table class="kg-table layui-table layui-form">
<colgroup>
<col>
<col>
<col>
<col width="10%">
<col width="10%">
</colgroup>
<thead>
<tr>
<th>评论</th>
<th>用户</th>
<th>时间</th>
<th>发布</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in pager.items %}
<tr>
<td>
<p>课程:<a href="{{ url({'for':'admin.comment.list'},{'course_id':item.course.id}) }}">{{ item.course.title }}</a></p>
{% if item.chapter %}
<p>章节:<a href="{{ url({'for':'admin.comment.list'},{'chapter_id':item.chapter.id}) }}">{{ item.chapter.title }}</a></p>
{% endif %}
<p>评论:<a href="javascript:" title="{{ item.content }}">{{ substr(item.content,0,25) }}</a></p>
</td>
<td>
<p>昵称:{{ item.user.name }}</p>
<p>编号:{{ item.user.id }}</p>
</td>
<td>{{ date('Y-m-d H:i',item.created_at) }}</td>
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否"
lay-filter="switch-published" comment-id="{{ item.id }}"
{% if item.published == 1 %}checked{% endif %}></td>
<td align="center">
<div class="layui-dropdown">
<button class="layui-btn layui-btn-sm">操作 <span class="layui-icon layui-icon-triangle-d"></span>
</button>
<ul>
<li><a href="{{ url({'for':'admin.comment.edit','id':item.id}) }}">编辑</a></li>
{% if item.deleted == 0 %}
<li><a href="javascript:" url="{{ url({'for':'admin.comment.delete','id':item.id}) }}"
class="kg-delete">删除</a></li>
{% else %}
<li><a href="javascript:" url="{{ url({'for':'admin.comment.restore','id':item.id}) }}"
class="kg-delete">还原</a></li>
{% endif %}
</ul>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ partial('partials/pager') }}
<script>
layui.use(['jquery', 'form'], function () {
var $ = layui.jquery;
var form = layui.form;
form.on('switch(switch-published)', function (data) {
var commentId = $(this).attr('comment-id');
var checked = $(this).is(':checked');
var published = checked ? 1 : 0;
var tips = published === 1 ? '确定要上线评论?' : '确定要下线评论?';
layer.confirm(tips, function () {
$.ajax({
type: 'POST',
url: '/admin/comment/' + commentId + '/update',
data: {published: published},
success: function (res) {
layer.msg(res.msg, {icon: 1});
},
error: function (xhr) {
var json = JSON.parse(xhr.responseText);
layer.msg(json.msg, {icon: 2});
data.elem.checked = !checked;
form.render();
}
});
}, function () {
data.elem.checked = !checked;
form.render();
});
});
});
</script>

View File

@ -0,0 +1,67 @@
<fieldset class="layui-elem-field layui-field-title">
<legend>搜索评论</legend>
</fieldset>
<form class="layui-form" method="GET" action="{{ url({'for':'admin.comment.list'}) }}">
<div class="layui-form-item">
<label class="layui-form-label">评论编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="id" placeholder="评论编号精确匹配">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">课程编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="course_id" placeholder="课程编号精确匹配">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">章节编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="chapter_id" placeholder="章节编号精确匹配">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="author_id" placeholder="用户编号精确匹配">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">发布</label>
<div class="layui-input-block">
<input type="radio" name="published" value="1" title="是">
<input type="radio" name="published" value="0" title="否">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">删除</label>
<div class="layui-input-block">
<input type="radio" name="deleted" value="1" title="是">
<input type="radio" name="deleted" value="0" title="否">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn" lay-submit>提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
<script>
layui.use('form', function () {
var form = layui.form;
});
</script>

View File

@ -2,8 +2,8 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label"></label> <label class="layui-form-label"></label>
<div class="layui-input-inline" style="width:150px;margin:10px 0px 5px 110px;"> <div class="layui-input-inline" style="width:150px;margin-left:110px;">
<div id="qrcode" class="qrcode" qrcode-text="{{ push_url }}"></div> <img src="/qrcode/img?text={{ push_url|url_encode }}">
</div> </div>
</div> </div>
@ -38,23 +38,4 @@
</form> </form>
{{ partial('partials/clipboard_tips') }} {{ partial('partials/clipboard_tips') }}
{{ javascript_include('lib/jquery.min.js') }}
{{ javascript_include('lib/jquery.qrcode.min.js') }}
<script>
layui.use(['jquery'], function () {
var $ = layui.jquery;
$('#qrcode').qrcode({
text: $('#qrcode').attr('qrcode-text'),
width: 120,
height: 120
});
});
</script>

View File

@ -8,7 +8,7 @@
{{ partial('config/payment_alipay') }} {{ partial('config/payment_alipay') }}
</div> </div>
<div class="layui-tab-item"> <div class="layui-tab-item">
{{ partial('config/payment_wxpay') }} {{ partial('config/payment_wechat') }}
</div> </div>
</div> </div>
</div> </div>

Some files were not shown because too many files have changed in this diff Show More