1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-07-16 13:16:14 +08:00

增加im_group后台管理,user_id->owner_id

This commit is contained in:
xiaochong0302 2020-07-26 20:32:52 +08:00
parent b7bd710cd9
commit 7f2d3b793e
106 changed files with 1429 additions and 414 deletions

View File

@ -25,7 +25,7 @@ class ConsultList extends Builder
$users = $this->getUsers($consults);
foreach ($consults as $key => $consult) {
$consults[$key]['user'] = $users[$consult['user_id']] ?? new \stdClass();
$consults[$key]['owner'] = $users[$consult['owner_id']] ?? new \stdClass();
}
return $consults;
@ -67,7 +67,7 @@ class ConsultList extends Builder
public function getUsers(array $consults)
{
$ids = kg_array_column($consults, 'user_id');
$ids = kg_array_column($consults, 'owner_id');
$userRepo = new UserRepo();

View File

@ -7,7 +7,7 @@ use App\Models\Course as CourseModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ChapterTreeList extends Builder
class CourseCatalog extends Builder
{
/**

View File

@ -36,7 +36,7 @@ class DanmuList extends Builder
$users = $this->getUsers($danmus);
foreach ($danmus as $key => $danmu) {
$danmus[$key]['user'] = $users[$danmu['user_id']] ?? new \stdClass();
$danmus[$key]['owner'] = $users[$danmu['owner_id']] ?? new \stdClass();
}
return $danmus;
@ -78,7 +78,7 @@ class DanmuList extends Builder
public function getUsers(array $danmus)
{
$ids = kg_array_column($danmus, 'user_id');
$ids = kg_array_column($danmus, 'owner_id');
$userRepo = new UserRepo();

View File

@ -0,0 +1,70 @@
<?php
namespace App\Builders;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class ImGroupList extends Builder
{
public function handleCourses(array $groups)
{
$courses = $this->getCourses($groups);
foreach ($groups as $key => $group) {
$groups[$key]['course'] = $courses[$group['course_id']] ?? new \stdClass();
}
return $groups;
}
public function handleUsers(array $groups)
{
$users = $this->getUsers($groups);
foreach ($groups as $key => $group) {
$groups[$key]['owner'] = $users[$group['owner_id']] ?? new \stdClass();
}
return $groups;
}
public function getCourses(array $groups)
{
$ids = kg_array_column($groups, '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(array $groups)
{
$ids = kg_array_column($groups, 'owner_id');
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$baseUrl = kg_ci_base_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -24,7 +24,7 @@ class ReviewList extends Builder
$users = $this->getUsers($reviews);
foreach ($reviews as $key => $review) {
$reviews[$key]['user'] = $users[$review['user_id']] ?? new \stdClass();
$reviews[$key]['owner'] = $users[$review['owner_id']] ?? new \stdClass();
}
return $reviews;
@ -49,7 +49,7 @@ class ReviewList extends Builder
public function getUsers(array $reviews)
{
$ids = kg_array_column($reviews, 'user_id');
$ids = kg_array_column($reviews, 'owner_id');
$userRepo = new UserRepo();

View File

@ -2,9 +2,9 @@
namespace App\Caches;
use App\Builders\ChapterTreeList as ChapterTreeListBuilder;
use App\Builders\CourseCatalog as CourseCatalogBuilder;
class CourseChapterList extends Cache
class CourseCatalog extends Cache
{
protected $lifetime = 7 * 86400;
@ -16,12 +16,12 @@ class CourseChapterList extends Cache
public function getKey($id = null)
{
return "course_chapter_list:{$id}";
return "course_catalog:{$id}";
}
public function getContent($id = null)
{
$builder = new ChapterTreeListBuilder();
$builder = new CourseCatalogBuilder();
$list = $builder->handle($id);

View File

@ -22,7 +22,6 @@ class UserDailyCounter extends Counter
public function getContent($id = null)
{
return [
'favorite_count' => 0,
'danmu_count' => 0,
'consult_count' => 0,
'order_count' => 0,

View File

@ -0,0 +1,132 @@
<?php
namespace App\Http\Admin\Controllers;
use App\Http\Admin\Services\ImGroup as ImGroupService;
/**
* @RoutePrefix("/admin/group")
*/
class GroupController extends Controller
{
/**
* @Get("/list", name="admin.group.list")
*/
public function listAction()
{
$groupService = new ImGroupService();
$pager = $groupService->getGroups();
$this->view->setVar('pager', $pager);
}
/**
* @Get("/search", name="admin.group.search")
*/
public function searchAction()
{
}
/**
* @Get("/add", name="admin.group.add")
*/
public function addAction()
{
}
/**
* @Post("/create", name="admin.group.create")
*/
public function createAction()
{
$groupService = new ImGroupService();
$group = $groupService->createGroup();
$location = $this->url->get([
'for' => 'admin.group.edit',
'id' => $group->id,
]);
$content = [
'location' => $location,
'msg' => '创建群组成功',
];
return $this->jsonSuccess($content);
}
/**
* @Get("/{id:[0-9]+}/edit", name="admin.group.edit")
*/
public function editAction($id)
{
$groupService = new ImGroupService();
$group = $groupService->getGroup($id);
$this->view->setVar('group', $group);
}
/**
* @Post("/{id:[0-9]+}/update", name="admin.group.update")
*/
public function updateAction($id)
{
$groupService = new ImGroupService();
$groupService->updateGroup($id);
$location = $this->url->get(['for' => 'admin.group.list']);
$content = [
'location' => $location,
'msg' => '更新群组成功',
];
return $this->jsonSuccess($content);
}
/**
* @Post("/{id:[0-9]+}/delete", name="admin.group.delete")
*/
public function deleteAction($id)
{
$groupService = new ImGroupService();
$groupService->deleteGroup($id);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '删除群组成功',
];
return $this->jsonSuccess($content);
}
/**
* @Post("/{id:[0-9]+}/restore", name="admin.group.restore")
*/
public function restoreAction($id)
{
$groupService = new ImGroupService();
$groupService->restoreGroup($id);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '还原群组成功',
];
return $this->jsonSuccess($content);
}
}

View File

@ -325,6 +325,43 @@ class AuthNode extends Service
],
],
],
[
'id' => '2-4',
'title' => '群组管理',
'type' => 'menu',
'children' => [
[
'id' => '2-4-1',
'title' => '群组列表',
'type' => 'menu',
'route' => 'admin.group.list',
],
[
'id' => '2-4-2',
'title' => '搜索群组',
'type' => 'menu',
'route' => 'admin.group.search',
],
[
'id' => '2-4-3',
'title' => '添加群组',
'type' => 'menu',
'route' => 'admin.group.add',
],
[
'id' => '2-4-4',
'title' => '编辑群组',
'type' => 'button',
'route' => 'admin.group.edit',
],
[
'id' => '2-4-5',
'title' => '删除群组',
'type' => 'button',
'route' => 'admin.group.delete',
],
],
],
[
'id' => '2-5',
'title' => '轮播管理',

View File

@ -3,7 +3,7 @@
namespace App\Http\Admin\Services;
use App\Caches\Chapter as ChapterCache;
use App\Caches\CourseChapterList as CourseChapterListCache;
use App\Caches\CourseCatalog as CourseCatalogCache;
use App\Models\Chapter as ChapterModel;
use App\Models\ChapterLive as ChapterLiveModel;
use App\Models\ChapterRead as ChapterReadModel;
@ -166,6 +166,8 @@ class Chapter extends Service
$this->updateCourseStats($chapter);
$this->rebuildCatalogCache($chapter);
return $chapter;
}
@ -185,6 +187,8 @@ class Chapter extends Service
$this->updateCourseStats($chapter);
$this->rebuildCatalogCache($chapter);
return $chapter;
}
@ -200,6 +204,8 @@ class Chapter extends Service
$this->updateCourseStats($chapter);
$this->rebuildCatalogCache($chapter);
return $chapter;
}
@ -240,8 +246,11 @@ class Chapter extends Service
$cache = new ChapterCache();
$cache->rebuild($chapter->id);
}
$cache = new CourseChapterListCache();
protected function rebuildCatalogCache(ChapterModel $chapter)
{
$cache = new CourseCatalogCache();
$cache->rebuild($chapter->course_id);
}

View File

@ -52,11 +52,11 @@ class Consult extends Service
$data = [];
if (isset($post['question'])) {
if (!empty($post['question'])) {
$data['question'] = $validator->checkQuestion($post['question']);
}
if (isset($post['answer'])) {
if (!empty($post['answer'])) {
$data['answer'] = $validator->checkAnswer($post['answer']);
}

View File

@ -96,6 +96,7 @@ class Course extends Service
$imGroup->course_id = $course->id;
$imGroup->name = $course->title;
$imGroup->about = $course->summary;
$imGroup->published = 1;
if ($imGroup->create() === false) {
throw new \RuntimeException('Create ImGroup Failed');

View File

@ -0,0 +1,159 @@
<?php
namespace App\Http\Admin\Services;
use App\Builders\ImGroupList as ImGroupListBuilder;
use App\Library\Paginator\Query as PagerQuery;
use App\Models\ImGroup as ImGroupModel;
use App\Models\ImGroupUser as ImGroupUserModel;
use App\Models\User as UserModel;
use App\Repos\ImGroup as ImGroupRepo;
use App\Repos\ImGroupUser as ImGroupUserRepo;
use App\Validators\ImGroup as ImGroupValidator;
class ImGroup extends Service
{
public function getGroups()
{
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['deleted'] = $params['deleted'] ?? 0;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$groupRepo = new ImGroupRepo();
$pager = $groupRepo->paginate($params, $sort, $page, $limit);
return $this->handleGroups($pager);
}
public function getGroup($id)
{
return $this->findOrFail($id);
}
public function createGroup()
{
$post = $this->request->getPost();
$validator = new ImGroupValidator();
$data = [];
$data['name'] = $validator->checkName($post['name']);
$data['about'] = $validator->checkAbout($post['about']);
$data['type'] = $validator->checkType($post['type']);
$group = new ImGroupModel();
$group->create($data);
return $group;
}
public function updateGroup($id)
{
$group = $this->findOrFail($id);
$post = $this->request->getPost();
$validator = new ImGroupValidator();
$data = [];
if (isset($post['name'])) {
$data['name'] = $validator->checkName($post['name']);
}
if (isset($post['about'])) {
$data['about'] = $validator->checkAbout($post['about']);
}
if (isset($post['avatar'])) {
$data['avatar'] = $validator->checkAvatar($post['avatar']);
}
if (isset($post['published'])) {
$data['published'] = $validator->checkPublishStatus($post['published']);
}
if (isset($post['owner_id'])) {
$owner = $validator->checkGroupOwner($post['owner_id']);
$data['owner_id'] = $owner->id;
$this->handleGroupOwner($group, $owner);
}
$group->update($data);
return $group;
}
public function deleteGroup($id)
{
$group = $this->findOrFail($id);
$group->deleted = 1;
$group->update();
return $group;
}
public function restoreGroup($id)
{
$group = $this->findOrFail($id);
$group->deleted = 0;
$group->update();
return $group;
}
protected function handleGroupOwner(ImGroupModel $group, UserModel $user)
{
$repo = new ImGroupUserRepo();
$groupUser = $repo->findGroupUser($group->id, $user->id);
if ($groupUser) return;
$groupUser = new ImGroupUserModel();
$groupUser->group_id = $group->id;
$groupUser->user_id = $user->id;
$groupUser->create();
$group->user_count += 1;
$group->update();
}
protected function handleGroups($pager)
{
if ($pager->total_items > 0) {
$builder = new ImGroupListBuilder();
$pipeA = $pager->items->toArray();
$pipeB = $builder->handleUsers($pipeA);
$pipeC = $builder->objects($pipeB);
$pager->items = $pipeC;
}
return $pager;
}
protected function findOrFail($id)
{
$validator = new ImGroupValidator();
return $validator->checkGroup($id);
}
}

View File

@ -52,7 +52,7 @@
<td><span class="layui-badge layui-bg-gray">{{ item.level }}</span></td>
<td><span class="layui-badge layui-bg-gray">{{ item.child_count }}</span></td>
<td><span class="layui-badge layui-bg-gray">{{ item.course_count }}</span></td>
<td><input class="layui-input kg-priority-input" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.category.update','id':item.id}) }}"></td>
<td><input class="layui-input kg-priority" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.category.update','id':item.id}) }}"></td>
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ url({'for':'admin.category.update','id':item.id}) }}" {% if item.published == 1 %}checked{% endif %}></td>
<td align="center">
<div class="layui-dropdown">

View File

@ -34,7 +34,7 @@
<label class="layui-form-label">免费</label>
<div class="layui-input-block">
<input type="radio" name="free" value="1" title="是">
<input type="radio" name="free" value="0" title="否" checked="true">
<input type="radio" name="free" value="0" title="否" checked="checked">
</div>
</div>

View File

@ -24,8 +24,8 @@
<div class="layui-form-item">
<label class="layui-form-label">免费</label>
<div class="layui-input-block">
<input type="radio" name="free" value="1" title="是" {% if chapter.free == 1 %}checked="true"{% endif %}>
<input type="radio" name="free" value="0" title="否" {% if chapter.free == 0 %}checked="true"{% endif %}>
<input type="radio" name="free" value="1" title="是" {% if chapter.free == 1 %}checked="checked"{% endif %}>
<input type="radio" name="free" value="0" title="否" {% if chapter.free == 0 %}checked="checked"{% endif %}>
</div>
</div>

View File

@ -14,7 +14,7 @@
{% for item in play_urls %}
<tr>
<td>{{ item.format }}</td>
<td>{{ item.duration|play_duration }}</td>
<td>{{ item.duration|duration }}</td>
<td>{{ item.width }} x {{ item.height }}</td>
<td>{{ item.rate }}kbps</td>
<td>{{ item.size }}M</td>

View File

@ -37,7 +37,7 @@
<span class="layui-badge layui-bg-green">课</span>
</td>
<td>{{ live_time_info(item.attrs) }}</td>
<td><input class="layui-input kg-priority-input" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}"></td>
<td><input class="layui-input kg-priority" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}"></td>
<td><input type="checkbox" name="free" value="1" lay-skin="switch" lay-text="是|否" lay-filter="free" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}" {% if item.free == 1 %}checked{% endif %}></td>
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}" {% if item.published == 1 %}checked{% endif %}></td>
<td align="center">

View File

@ -28,7 +28,7 @@
<span class="layui-badge layui-bg-green">课</span>
</td>
<td><span class="layui-badge layui-bg-gray">{{ item.attrs['word_count'] }}</span></td>
<td><input class="layui-input kg-priority-input" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}"></td>
<td><input class="layui-input kg-priority" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}"></td>
<td><input type="checkbox" name="free" value="1" lay-skin="switch" lay-text="是|否" lay-filter="free" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}" {% if item.free == 1 %}checked{% endif %}></td>
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}" {% if item.published == 1 %}checked{% endif %}></td>
<td align="center">

View File

@ -44,8 +44,8 @@
<span class="layui-badge layui-bg-green">课</span>
</td>
<td>{{ file_status(item.attrs['file_status']) }}</td>
<td>{{ item.attrs['duration']|play_duration }}</td>
<td><input class="layui-input kg-priority-input" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}"></td>
<td>{{ item.attrs['duration']|duration }}</td>
<td><input class="layui-input kg-priority" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}"></td>
<td><input type="checkbox" name="free" value="1" lay-skin="switch" lay-text="是|否" lay-filter="free" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}" {% if item.free == 1 %}checked{% endif %}></td>
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}" {% if item.published == 1 %}checked{% endif %}></td>
<td align="center">

View File

@ -14,23 +14,23 @@
<div class="layui-form-item">
<label class="layui-form-label">回复</label>
<div class="layui-input-block">
<textarea name="answer" class="layui-textarea" lay-verify="required">{{ consult.answer }}</textarea>
<textarea name="answer" class="layui-textarea">{{ consult.answer }}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">私密</label>
<div class="layui-input-block">
<input type="radio" name="private" value="1" title="是" {% if consult.private == 1 %}checked="true"{% endif %}>
<input type="radio" name="private" value="0" title="否" {% if consult.private == 0 %}checked="true"{% endif %}>
<input type="radio" name="private" value="1" title="是" {% if consult.private == 1 %}checked="checked"{% endif %}>
<input type="radio" name="private" value="0" title="否" {% if consult.private == 0 %}checked="checked"{% endif %}>
</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 consult.published == 1 %}checked="true"{% endif %}>
<input type="radio" name="published" value="0" title="否" {% if consult.published == 0 %}checked="true"{% endif %}>
<input type="radio" name="published" value="1" title="是" {% if consult.published == 1 %}checked="checked"{% endif %}>
<input type="radio" name="published" value="0" title="否" {% if consult.published == 0 %}checked="checked"{% endif %}>
</div>
</div>

View File

@ -49,8 +49,8 @@
{% endif %}
</td>
<td>
<p>昵称:{{ item.user.name }}</p>
<p>编号:{{ item.user.id }}</p>
<p>昵称:{{ item.owner.name }}</p>
<p>编号:{{ item.owner.id }}</p>
</td>
<td>{{ date('Y-m-d H:i:s',item.create_time) }}</td>
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ url({'for':'admin.consult.update','id':item.id}) }}" {% if item.published == 1 %}checked{% endif %}></td>

View File

@ -21,7 +21,7 @@
<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="用户编号精确匹配">
<input class="layui-input" type="text" name="owner_id" placeholder="用户编号精确匹配">
</div>
</div>

View File

@ -42,7 +42,7 @@
<span class="layui-badge layui-bg-green">{{ item.lesson_count }}</span>
</a>
</td>
<td><input class="layui-input kg-priority-input" type="text" name="priority" value="{{ item.priority }}" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}"></td>
<td><input class="layui-input kg-priority" type="text" name="priority" value="{{ item.priority }}" data-url="{{ url({'for':'admin.chapter.update','id':item.id}) }}"></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>

View File

@ -11,9 +11,9 @@
<label class="layui-form-label">封面</label>
<div class="layui-input-inline">
{% if course.cover %}
<img id="cover-img" class="kg-cover" src="{{ course.cover }}">
<img id="img-cover" class="kg-cover" src="{{ course.cover }}">
{% else %}
{{ image('id':'cover-img','class':'kg-cover','src':'admin/img/default_cover.png') }}
{{ image('id':'img-cover','class':'kg-cover','src':'admin/img/default_cover.png') }}
{% endif %}
<input type="hidden" name="cover" value="{{ course.cover }}">
</div>

View File

@ -0,0 +1,37 @@
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.group.create'}) }}">
<fieldset class="layui-elem-field layui-field-title">
<legend>添加群组</legend>
</fieldset>
<div class="layui-form-item">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="name" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea class="layui-textarea" name="about"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">类型</label>
<div class="layui-input-block">
<input type="radio" name="type" value="course" title="课程" disabled="disabled">
<input type="radio" name="type" value="chat" title="聊天" checked="checked">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
</div>
</div>
</form>

View File

@ -0,0 +1,61 @@
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.group.update','id':group.id}) }}">
<fieldset class="layui-elem-field layui-field-title">
<legend>编辑群组</legend>
</fieldset>
<div class="layui-form-item">
<label class="layui-form-label">头像</label>
<div class="layui-input-inline" style="width: 110px;">
{% if group.avatar %}
<img id="img-cover" class="kg-avatar" src="{{ group.avatar }}">
{% else %}
{{ image('id':'img-cover','class':'kg-cover','src':'admin/img/default_cover.png') }}
{% endif %}
<input type="hidden" name="cover" value="{{ group.avatar }}">
</div>
<div class="layui-input-inline" style="padding-top:35px;">
<a href="javascript:" class="layui-btn layui-btn-sm" id="choose-cover">编辑</a>
</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="name" value="{{ group.name }}" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea class="layui-textarea" name="about">{{ group.about }}</textarea>
</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="owner_id" value="{{ group.owner_id }}" lay-verify="number">
</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 group.published == 1 %}checked{% endif %}>
<input type="radio" name="published" value="0" title="否" {% if group.published == 0 %}checked{% 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="true" lay-filter="go">提交</button>
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
</div>
</div>
</form>
{{ partial('partials/cover_uploader') }}

View File

@ -0,0 +1,63 @@
{%- macro owner_info(owner) %}
{% if owner %}
{{ owner.name }}{{ owner.id }}
{% else %}
未设置
{% endif %}
{%- endmacro %}
<div class="kg-nav">
<div class="kg-nav-left">
<span class="layui-breadcrumb">
<a><cite>群组管理</cite></a>
</span>
</div>
<div class="kg-nav-right">
<a class="layui-btn layui-btn-sm" href="{{ url({'for':'admin.group.add'}) }}">
<i class="layui-icon layui-icon-add-1"></i>添加群组
</a>
</div>
</div>
<table class="kg-table layui-table layui-form">
<colgroup>
<col>
<col>
<col>
<col>
<col>
<col width="12%">
</colgroup>
<thead>
<tr>
<th>编号</th>
<th>名称</th>
<th>群主</th>
<th>成员</th>
<th>发布</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in pager.items %}
<tr>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ owner_info(item.owner) }}</td>
<td><span class="layui-badge layui-bg-gray">{{ item.user_count }}</span></td>
<td><input type="checkbox" name="published" value="1" lay-filter="published" lay-skin="switch" lay-text="是|否" data-url="{{ url({'for':'admin.group.update','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.group.edit','id':item.id}) }}">编辑</a></li>
<li><a href="javascript:" class="kg-delete" data-url="{{ url({'for':'admin.group.delete','id':item.id}) }}">删除</a></li>
</ul>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ partial('partials/pager') }}

View File

@ -0,0 +1,67 @@
<form class="layui-form kg-form" method="GET" action="{{ url({'for':'admin.group.list'}) }}">
<fieldset class="layui-elem-field layui-field-title">
<legend>搜索群组</legend>
</fieldset>
<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="name" 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="user_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 type="radio" name="type" value="course" title="课程">
<input type="radio" name="type" value="chat" title="聊天">
</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="true">提交</button>
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
</div>
</div>
</form>

View File

@ -39,7 +39,7 @@
<td>{{ item.title }}</td>
<td>{{ date('Y-m-d H:i',item.create_time) }}</td>
<td>{{ date('Y-m-d H:i',item.update_time) }}</td>
<td><input class="layui-input kg-priority-input" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.help.update','id':item.id}) }}"></td>
<td><input class="layui-input kg-priority" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.help.update','id':item.id}) }}"></td>
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ url({'for':'admin.help.update','id':item.id}) }}" {% if item.published == 1 %}checked{% endif %}>
</td>
<td align="center">

View File

@ -71,7 +71,7 @@
<td><span class="layui-badge layui-bg-gray">{{ item.child_count }}</span></td>
<td>{{ position_info(item.position) }}</td>
<td>{{ target_info(item.target) }}</td>
<td><input class="layui-input kg-priority-input" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.nav.update','id':item.id}) }}"></td>
<td><input class="layui-input kg-priority" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.nav.update','id':item.id}) }}"></td>
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ url({'for':'admin.nav.update','id':item.id}) }}" {% if item.published == 1 %}checked{% endif %}></td>
<td align="center">
<div class="layui-dropdown">

View File

@ -15,7 +15,7 @@
layer.load();
},
done: function (res, index, upload) {
$('#cover-img').attr('src', res.data.src);
$('#img-cover').attr('src', res.data.src);
$('input[name=cover]').val(res.data.src);
layer.closeAll('loading');
},

View File

@ -1,8 +1,10 @@
{% if pager.total_pages > 1 %}
<div class="layui-box layui-laypage layui-laypage-default">
<a href="{{ pager.first }}">首页</a>
<a href="{{ pager.previous }}">上页</a>
<a href="{{ pager.next }}">下页</a>
<a href="{{ pager.last }}">尾页</a>
<div class="kg-pager">
<div class="layui-box layui-laypage layui-laypage-default">
<a href="{{ pager.first }}">首页</a>
<a href="{{ pager.previous }}">上页</a>
<a href="{{ pager.next }}">下页</a>
<a href="{{ pager.last }}">尾页</a>
</div>
</div>
{% endif %}

View File

@ -22,8 +22,8 @@
<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 review.published == 1 %}checked="true"{% endif %}>
<input type="radio" name="published" value="0" title="否" {% if review.published == 0 %}checked="true"{% endif %}>
<input type="radio" name="published" value="1" title="是" {% if review.published == 1 %}checked="checked"{% endif %}>
<input type="radio" name="published" value="0" title="否" {% if review.published == 0 %}checked="checked"{% endif %}>
</div>
</div>

View File

@ -41,8 +41,8 @@
<p>评价:<a href="javascript:" title="{{ item.content }}">{{ substr(item.content,0,30) }}</a></p>
</td>
<td>
<p>昵称:{{ item.user.name }}</p>
<p>编号:{{ item.user.id }}</p>
<p>昵称:{{ item.owner.name }}</p>
<p>编号:{{ item.owner.id }}</p>
</td>
<td>{{ date('Y-m-d H:i:s',item.create_time) }}</td>
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ url({'for':'admin.review.update','id':item.id}) }}" {% if item.published == 1 %}checked{% endif %}></td>

View File

@ -21,7 +21,7 @@
<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="用户编号精确匹配">
<input class="layui-input" type="text" name="owner_id" placeholder="用户编号精确匹配">
</div>
</div>

View File

@ -18,9 +18,9 @@
<label class="layui-form-label">封面</label>
<div class="layui-input-inline">
{% if slide.cover %}
<img id="cover-img" class="kg-cover" src="{{ slide.cover }}">
<img id="img-cover" class="kg-cover" src="{{ slide.cover }}">
{% else %}
{{ image('id':'cover-img','class':'kg-cover','src':'admin/img/default_cover.png') }}
{{ image('id':'img-cover','class':'kg-cover','src':'admin/img/default_cover.png') }}
{% endif %}
<input type="hidden" name="cover" value="{{ slide.cover }}">
</div>

View File

@ -46,8 +46,8 @@
<td>{{ item.id }}</td>
<td>{{ item.title }}</td>
<td>{{ target_info(item.target) }}</td>
<td><input class="layui-input kg-priority-input" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.slide.update','id':item.id}) }}"></td>
<td><input type="checkbox" name="published" value="1" lay-filter="switch-published" lay-skin="switch" lay-text="是|否" data-url="{{ url({'for':'admin.slide.update','id':item.id}) }}" {% if item.published == 1 %}checked{% endif %}></td>
<td><input class="layui-input kg-priority" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ url({'for':'admin.slide.update','id':item.id}) }}"></td>
<td><input type="checkbox" name="published" value="1" lay-filter="published" lay-skin="switch" lay-text="是|否" data-url="{{ url({'for':'admin.slide.update','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>

View File

@ -4,10 +4,9 @@ namespace App\Http\Web\Controllers;
use App\Services\Frontend\Chapter\ChapterInfo as ChapterInfoService;
use App\Services\Frontend\Chapter\ChapterLike as ChapterLikeService;
use App\Services\Frontend\Chapter\CommentList as ChapterCommentListService;
use App\Services\Frontend\Chapter\DanmuList as ChapterDanmuListService;
use App\Services\Frontend\Chapter\Learning as ChapterLearningService;
use App\Services\Frontend\Course\ChapterList as CourseChapterListService;
use App\Services\Frontend\Course\ChapterList as CourseCatalogService;
/**
* @RoutePrefix("/chapter")
@ -33,7 +32,7 @@ class ChapterController extends Controller
]);
}
$service = new CourseChapterListService();
$service = new CourseCatalogService();
$contents = $service->handle($chapter['course']['id']);
@ -65,18 +64,6 @@ class ChapterController extends Controller
return $this->jsonSuccess(['items' => $items]);
}
/**
* @Get("/{id:[0-9]+}/comments", name="web.chapter.comments")
*/
public function commentsAction($id)
{
$service = new ChapterCommentListService();
$comments = $service->handle($id);
return $this->jsonSuccess(['comments' => $comments]);
}
/**
* @Post("/{id:[0-9]+}/like", name="web.chapter.like")
*/
@ -84,9 +71,13 @@ class ChapterController extends Controller
{
$service = new ChapterLikeService();
$service->handle($id);
$like = $service->handle($id);
return $this->jsonSuccess();
$msg = $like->deleted == 0 ? '点赞成功' : '取消点赞成功';
$content = ['msg' => $msg];
return $this->jsonSuccess($content);
}
/**

View File

@ -6,6 +6,7 @@ use App\Services\Frontend\Consult\ConsultCreate as ConsultCreateService;
use App\Services\Frontend\Consult\ConsultDelete as ConsultDeleteService;
use App\Services\Frontend\Consult\ConsultInfo as ConsultInfoService;
use App\Services\Frontend\Consult\ConsultLike as ConsultLikeService;
use App\Services\Frontend\Consult\ConsultRating as ConsultRatingService;
use App\Services\Frontend\Consult\ConsultUpdate as ConsultUpdateService;
use Phalcon\Mvc\View;
@ -108,9 +109,27 @@ class ConsultController extends Controller
{
$service = new ConsultLikeService();
$like = $service->handle($id);
$msg = $like->deleted == 0 ? '点赞成功' : '取消点赞成功';
$content = ['msg' => $msg];
return $this->jsonSuccess($content);
}
/**
* @Post("/{id:[0-9]+}/rating", name="web.consult.rating")
*/
public function ratingAction($id)
{
$service = new ConsultRatingService();
$service->handle($id);
return $this->jsonSuccess();
$content = ['msg' => '评价成功'];
return $this->jsonSuccess($content);
}
}

View File

@ -3,7 +3,7 @@
namespace App\Http\Web\Controllers;
use App\Http\Web\Services\CourseQuery as CourseQueryService;
use App\Services\Frontend\Course\ChapterList as CourseChapterListService;
use App\Services\Frontend\Course\ChapterList as CourseCatalogService;
use App\Services\Frontend\Course\ConsultList as CourseConsultListService;
use App\Services\Frontend\Course\CourseInfo as CourseInfoService;
use App\Services\Frontend\Course\CourseList as CourseListService;
@ -102,7 +102,7 @@ class CourseController extends Controller
*/
public function chaptersAction($id)
{
$service = new CourseChapterListService();
$service = new CourseCatalogService();
$chapters = $service->handle($id);
@ -206,9 +206,11 @@ class CourseController extends Controller
{
$service = new CourseFavoriteService();
$service->handle($id);
$favorite = $service->handle($id);
return $this->jsonSuccess(['msg' => '收藏课程成功']);
$msg = $favorite->deleted == 0 ? '收藏成功' : '取消收藏成功';
return $this->jsonSuccess(['msg' => $msg]);
}
}

View File

@ -5,6 +5,7 @@ namespace App\Http\Web\Controllers;
use App\Services\Frontend\My\AccountInfo as AccountInfoService;
use App\Services\Frontend\My\ConsultList as MyConsultListService;
use App\Services\Frontend\My\CourseList as MyCourseListService;
use App\Services\Frontend\My\FavoriteList as MyFavoriteListService;
use App\Services\Frontend\My\ImFriendDelete as MyFriendDeleteService;
use App\Services\Frontend\My\ImGroupDelete as MyGroupDeleteService;
use App\Services\Frontend\My\OrderList as MyOrderListService;
@ -84,10 +85,12 @@ class MyController extends Controller
*/
public function favoritesAction()
{
$service = new MyConsultListService();
$service = new MyFavoriteListService();
$pager = $service->handle();
$pager->items = kg_array_object($pager->items);
$this->view->setVar('pager', $pager);
}

View File

@ -111,9 +111,13 @@ class ReviewController extends Controller
{
$service = new ReviewLikeService();
$service->handle($id);
$like = $service->handle($id);
return $this->jsonSuccess();
$msg = $like->deleted == 0 ? '点赞成功' : '取消点赞成功';
$content = ['msg' => $msg];
return $this->jsonSuccess($content);
}
}

View File

@ -64,12 +64,16 @@ Trait ImFriendTrait
$friendUser = $friendUserRepo->findFriendUser($user->id, $sender->id);
if (!$friendUser) {
$friendUserModel = new ImFriendUserModel();
$friendUserModel->create([
'user_id' => $user->id,
'friend_id' => $sender->id,
'group_id' => $groupId,
]);
$this->incrUserFriendCount($user);
}
$friendUser = $friendUserRepo->findFriendUser($sender->id, $user->id);
@ -77,12 +81,16 @@ Trait ImFriendTrait
$groupId = $message->item_info['group']['id'] ?: 0;
if (!$friendUser) {
$friendUserModel = new ImFriendUserModel();
$friendUserModel->create([
'user_id' => $sender->id,
'friend_id' => $user->id,
'group_id' => $groupId,
]);
$this->incrUserFriendCount($sender);
}
$itemInfo = $message->item_info;
@ -245,4 +253,18 @@ Trait ImFriendTrait
}
}
protected function incrUserFriendCount(ImUserModel $user)
{
$user->friend_count += 1;
$user->update();
}
protected function decrUserFriendCount(ImUserModel $user)
{
if ($user->friend_count > 0) {
$user->friend_count -= 1;
$user->update();
}
}
}

View File

@ -67,13 +67,17 @@ Trait ImGroupTrait
$groupUser = $groupUserRepo->findGroupUser($group->id, $applicant->id);
if (!$groupUser) {
$groupUserModel = new ImGroupUserModel();
$groupUserModel->create([
'group_id' => $group->id,
'user_id' => $applicant->id,
]);
$group->user_count += 1;
$group->update();
$this->incrGroupUserCount($group);
$this->incrUserGroupCount($applicant);
}
$itemInfo = $message->item_info;
@ -264,4 +268,32 @@ Trait ImGroupTrait
}
}
protected function incrUserGroupCount(ImUserModel $user)
{
$user->group_count += 1;
$user->update();
}
protected function decrUserGroupCount(ImUserModel $user)
{
if ($user->group_count > 0) {
$user->group_count -= 1;
$user->update();
}
}
protected function incrGroupUserCount(ImGroupModel $group)
{
$group->user_count += 1;
$group->update();
}
protected function decrGroupUserCount(ImGroupModel $group)
{
if ($group->user_count > 0) {
$group->user_count -= 1;
$group->update();
}
}
}

View File

@ -3,7 +3,7 @@
{% block content %}
<form class="layui-form" method="post" action="{{ url({'for':'web.consult.create'}) }}">
<div class="layui-form-item">
<label class="layui-form-label">咨询内容</label>
<label class="layui-form-label">问题</label>
<div class="layui-input-block">
<textarea name="question" class="layui-textarea" placeholder="请详细描述问题,我们会尽快回复您" lay-verify="required"></textarea>
</div>

View File

@ -14,7 +14,7 @@
<div class="layui-form-mid">{{ consult.chapter.title }}</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">咨询内容</label>
<label class="layui-form-label">问题</label>
<div class="layui-input-block">
<textarea name="question" class="layui-textarea" lay-verify="required">{{ consult.question }}</textarea>
</div>
@ -34,4 +34,5 @@
</div>
</div>
</form>
{% endblock %}

View File

@ -1,27 +1,60 @@
{% extends 'templates/layer.volt' %}
{% block content %}
<form class="layui-form" method="post" action="{{ url({'for':'web.consult.create'}) }}">
<div class="layui-form-item">
<label class="layui-form-label">咨询内容</label>
<div class="layui-input-block">
<textarea name="question" class="layui-textarea" placeholder="请详细描述问题,我们会尽快回复您" lay-verify="required"></textarea>
</div>
{% set rating_url = url({'for':'web.consult.rating','id':consult.id}) %}
{% set answer = consult.answer ? consult.answer : '<span class="gray">稍安勿燥,请耐心等待回复吧</span>' %}
<form class="layui-form review-form">
<div class="layui-form-item mb0">
<label class="layui-form-label">课程:</label>
<div class="layui-form-mid">{{ consult.course.title }}</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">私密问题</label>
<div class="layui-input-block">
<input type="radio" name="private" value="1" title="是">
<input type="radio" name="private" value="0" title="否" checked="checked">
</div>
<div class="layui-form-item mb0">
<label class="layui-form-label">章节:</label>
<div class="layui-form-mid">{{ consult.chapter.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="true" lay-filter="go">提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
<input type="hidden" name="chapter_id" value="{{ request.get('chapter_id') }}">
</div>
<div class="layui-form-item mb0">
<label class="layui-form-label">提问:</label>
<div class="layui-form-mid">{{ consult.question }}</div>
</div>
<div class="layui-form-item mb0">
<label class="layui-form-label">回复:</label>
<div class="layui-form-mid">{{ answer }}</div>
</div>
{% if consult.answer %}
<div class="layui-form-item">
<label class="layui-form-label">评分:</label>
<div class="layui-input-block">
<input type="hidden" name="rating" value="{{ consult.rating }}">
<input type="hidden" name="rating_url" value="{{ rating_url }}">
<div id="rating"></div>
</div>
</div>
{% endif %}
</form>
{% endblock %}
{% block inline_js %}
<script>
layui.use(['jquery', 'layer', 'rate'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var rate = layui.rate;
var rating = $('input[name=rating]').val();
var ratingUrl = $('input[name=rating_url]').val();
rate.render({
elem: '#rating',
value: rating,
choose: function (value) {
$.post(ratingUrl, {rating: value}, function () {
layer.msg('评价成功');
});
}
});
});
</script>
{% endblock %}

View File

@ -1,17 +1,21 @@
{{ partial('partials/macro_course') }}
{% if pager.total_pages > 0 %}
<div class="review-list">
{% for item in pager.items %}
{% set user_url = url({'for':'web.user.show','id':item.id}) %}
{% set item.answer = item.answer ? item.answer : '稍安勿燥,请耐心等待回复吧' %}
{% set owner_url = url({'for':'web.user.show','id':item.id}) %}
{% set like_url = url({'for':'web.consult.like','id':item.id}) %}
<div class="review-card clearfix">
<div class="avatar">
<a href="{{ user_url }}">
<img src="{{ item.user.avatar }}" alt="{{ item.user.name }}" title="{{ item.user.name }}">
<a href="{{ owner_url }}">
<img src="{{ item.owner.avatar }}" alt="{{ item.owner.name }}" title="{{ item.owner.name }}">
</a>
</div>
<div class="info">
<div class="rating">{{ star_info(item.rating) }}</div>
<div class="title">{{ item.question }}</div>
<div class="content">{% if item.answer %} {{ item.answer }} {% else %} 稍安勿燥,请耐心等待我们的回复吧 {% endif %}</div>
<div class="content">{{ item.answer }}</div>
<div class="footer">
<span class="time">{{ item.create_time|time_ago }}</span>
<a href="javascript:" class="like" title="点赞" data-url="{{ like_url }}">

View File

@ -3,16 +3,16 @@
{% if pager.total_pages > 0 %}
<div class="review-list">
{% for item in pager.items %}
{% set user_url = url({'for':'web.user.show','id':item.user.id}) %}
{% set owner_url = url({'for':'web.user.show','id':item.owner.id}) %}
{% set like_url = url({'for':'web.review.like','id':item.id}) %}
<div class="review-card clearfix">
<div class="avatar">
<img src="{{ item.user.avatar }}" alt="{{ item.user.name }}">
<img src="{{ item.owner.avatar }}" alt="{{ item.owner.name }}">
</div>
<div class="info">
<div class="rating">{{ star_info(item.rating) }}</div>
<div class="user">
<a href="{{ user_url }}">{{ item.user.name }}</a>
<a href="{{ owner_url }}">{{ item.owner.name }}</a>
</div>
<div class="content">{{ item.content }}</div>
<div class="footer">

View File

@ -10,7 +10,7 @@
<div class="wrap">
<div class="my-nav-title">我的咨询</div>
{% if pager.total_pages > 0 %}
<table class="layui-table review-table">
<table class="layui-table consult-table">
<colgroup>
<col>
<col>
@ -25,14 +25,14 @@
</thead>
<tbody>
{% for item in pager.items %}
{% set item.answer = item.answer ? item.answer : '请耐心等待我们的回复吧' %}
{% set answer = item.answer ? item.answer : '<span class="gray">稍安勿燥,请耐心等待回复吧</span>' %}
{% set show_url = url({'for':'web.consult.show','id':item.id}) %}
{% set edit_url = url({'for':'web.consult.edit','id':item.id}) %}
{% set delete_url = url({'for':'web.consult.delete','id':item.id}) %}
<tr>
<td>
<p class="content layui-elip" title="{{ item.question|e }}">提问:{{ item.question }}</p>
<p class="content layui-elip" title="{{ item.answer|e }}">回复:{{ item.answer }}</p>
<p class="question layui-elip" title="{{ item.question }}">提问:{{ item.question }}</p>
<p class="answer layui-elip" title="{{ item.answer }}">回复:{{ answer }}</p>
</td>
<td>{{ date('Y-m-d',item.create_time) }}</td>
<td>

View File

@ -2,24 +2,24 @@
{% block content %}
{{ partial('partials/macro_course') }}
<div class="layout-main">
<div class="my-sidebar">{{ partial('my/menu') }}</div>
<div class="my-content">
<div class="wrap">
<div class="my-nav-title">我的课程</div>
{% if pager.total_pages > 0 %}
<table class="layui-table" lay-size="lg">
<table class="layui-table">
<colgroup>
<col>
<col>
<col>
<col width="15%">
<col width="12%">
</colgroup>
<thead>
<tr>
<th>课程</th>
<th>进度</th>
<th>过期时间</th>
<th>操作</th>
</tr>
</thead>
@ -28,13 +28,15 @@
{% set course_url = url({'for':'web.course.show','id':item.course.id}) %}
{% set review_url = url({'for':'web.review.add'},{'id':item.course.id}) %}
<tr>
<td><a href="{{ course_url }}">{{ item.course.title }}</a></td>
<td>
<p>标题:<a href="{{ course_url }}">{{ item.course.title }}</a> {{ model_info(item.course.model) }}</p>
<p>期限:{{ date('Y-m-d',item.expiry_time) }}</p>
</td>
<td>
<p>用时:{{ item.duration|duration }}</p>
<p>进度:{{ item.progress }}%</p>
</td>
<td>{{ date('Y-m-d', item.expiry_time) }}</td>
<td>
<td align="center">
<button class="layui-btn layui-btn-sm btn-add-review" data-url="{{ review_url }}">评价</button>
</td>
</tr>

View File

@ -0,0 +1,55 @@
{% extends 'templates/full.volt' %}
{% block content %}
{{ partial('partials/macro_course') }}
<div class="layout-main">
<div class="my-sidebar">{{ partial('my/menu') }}</div>
<div class="my-content">
<div class="wrap">
<div class="my-nav-title">我的收藏</div>
{% if pager.total_pages > 0 %}
<table class="layui-table" lay-size="lg">
<colgroup>
<col>
<col>
<col>
<col width="12%">
</colgroup>
<thead>
<tr>
<th>课程</th>
<th>人气</th>
<th>评分</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in pager.items %}
{% set course_url = url({'for':'web.course.show','id':item.id}) %}
{% set favorite_url = url({'for':'web.course.favorite','id':item.id}) %}
<tr>
<td><a href="{{ course_url }}">{{ item.title }}</a> {{ model_info(item.model) }}</td>
<td><span class="layui-badge-rim">{{ item.user_count }}</span></td>
<td>{{ star_info(item.rating) }}</td>
<td align="center">
<button class="layui-btn layui-btn-sm kg-delete" data-tips="确定要取消收藏吗?" data-url="{{ favorite_url }}">取消</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ partial('partials/pager') }}
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block include_js %}
{{ js_include('web/js/my.js') }}
{% endblock %}

View File

@ -4,11 +4,11 @@
{%- macro gender_info(value) %}
{% if value == 1 %}
<span class="layui-badge layui-bg-red"></span>
{% elseif value == 2 %}
<span class="layui-badge layui-bg-green"></span>
{% elseif value == 3 %}
保密
<span class="layui-badge layui-bg-gray">密</span>
{% endif %}
{%- endmacro %}

View File

@ -2,12 +2,14 @@
{% block content %}
{% set update_profile_url = url({'for':'web.my.update_profile'}) %}
<div class="layout-main">
<div class="my-sidebar">{{ partial('my/menu') }}</div>
<div class="my-content">
<div class="wrap">
<div class="my-nav-title">个人信息</div>
<form class="layui-form my-form" method="post" action="{{ url({'for':'web.my.update_profile'}) }}">
<form class="layui-form my-form" method="post" action="{{ update_profile_url }}">
<div class="layui-form-item">
<label class="layui-form-label">昵称</label>
<div class="layui-input-block">
@ -38,7 +40,7 @@
</select>
</div>
<div class="layui-input-inline" style="width: 200px;">
<select name="area[county]" class="county-selector" data-value="{{ user.area.county }}" lay-verify="required">
<select name="area[county]" class="county-selector" data-value="{{ user.area.county }}">
<option value="">请选择区</option>
</select>
</div>

View File

@ -40,7 +40,7 @@ class Consult extends Model
*
* @var int
*/
public $user_id;
public $owner_id;
/**
* 提问
@ -56,6 +56,13 @@ class Consult extends Model
*/
public $answer;
/**
* 评分
*
* @var int
*/
public $rating;
/**
* 赞成数
*
@ -91,6 +98,13 @@ class Consult extends Model
*/
public $deleted;
/**
* 回复时间
*
* @var int
*/
public $answer_time;
/**
* 创建时间
*
@ -131,6 +145,10 @@ class Consult extends Model
{
$this->update_time = time();
if (!empty($this->answer) && $this->answer_time == 0) {
$this->answer_time = time();
}
if ($this->deleted == 1) {
$this->published = 0;
}

View File

@ -42,6 +42,13 @@ class ConsultLike extends Model
*/
public $create_time;
/**
* 更新时间
*
* @var int
*/
public $update_time;
public function getSource(): string
{
return 'kg_consult_like';

View File

@ -269,10 +269,10 @@ class Course extends Model
break;
}
if (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
} elseif (empty($this->cover)) {
if (empty($this->cover)) {
$this->cover = kg_default_cover_path();
} elseif (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}
if (!empty($attrs)) {

View File

@ -55,7 +55,7 @@ class Danmu extends Model
*
* @var int
*/
public $user_id;
public $owner_id;
/**
* 内容

View File

@ -33,7 +33,7 @@ class ImGroup extends Model
*
* @var int
*/
public $user_id;
public $owner_id;
/**
* 群组类型
@ -119,10 +119,10 @@ class ImGroup extends Model
{
$this->create_time = time();
if (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
} elseif (empty($this->avatar)) {
if (empty($this->avatar)) {
$this->avatar = kg_default_avatar_path();
} elseif (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
}
}
@ -151,5 +151,13 @@ class ImGroup extends Model
return $url;
}
public static function types()
{
return [
self::TYPE_COURSE => '课程',
self::TYPE_CHAT => '聊天',
];
}
}

View File

@ -65,6 +65,20 @@ class ImUser extends Model
*/
public $deleted;
/**
* 好友数
*
* @var int
*/
public $friend_count;
/**
* 群组数
*
* @var int
*/
public $group_count;
/**
* 创建时间
*
@ -100,10 +114,10 @@ class ImUser extends Model
{
$this->create_time = time();
if (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
} elseif (empty($this->avatar)) {
if (empty($this->avatar)) {
$this->avatar = kg_default_avatar_path();
} elseif (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
}
}

View File

@ -26,7 +26,7 @@ class Review extends Model
*
* @var int
*/
public $user_id;
public $owner_id;
/**
* 评价内容

View File

@ -42,6 +42,13 @@ class ReviewLike extends Model
*/
public $create_time;
/**
* 更新时间
*
* @var int
*/
public $update_time;
public function getSource(): string
{
return 'kg_review_like';

View File

@ -120,10 +120,10 @@ class Slide extends Model
{
$this->create_time = time();
if (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
} elseif (empty($this->cover)) {
if (empty($this->cover)) {
$this->cover = kg_default_cover_path();
} elseif (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}
if (is_array($this->style) && !empty($this->style)) {

View File

@ -107,7 +107,21 @@ class User extends Model
public $deleted;
/**
* VIP期限
* 课程数
*
* @var int
*/
public $course_count;
/**
* 收藏数
*
* @var int
*/
public $favorite_count;
/**
* 会员期限
*
* @var int
*/
@ -162,12 +176,10 @@ class User extends Model
{
$this->create_time = time();
$this->im = kg_json_encode($this->_im);
if (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
} elseif (empty($this->avatar)) {
if (empty($this->avatar)) {
$this->avatar = kg_default_avatar_path();
} elseif (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
}
}

View File

@ -28,8 +28,8 @@ class Consult extends Repository
$builder->andWhere('course_id = :course_id:', ['course_id' => $where['course_id']]);
}
if (!empty($where['user_id'])) {
$builder->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]);
if (!empty($where['owner_id'])) {
$builder->andWhere('owner_id = :owner_id:', ['owner_id' => $where['owner_id']]);
}
if (isset($where['private'])) {
@ -94,7 +94,7 @@ class Consult extends Repository
public function findUserLastChapterConsult($chapterId, $userId)
{
return ConsultModel::findFirst([
'conditions' => 'chapter_id = ?1 AND user_id = ?2 AND deleted = 0',
'conditions' => 'chapter_id = ?1 AND owner_id = ?2 AND deleted = 0',
'bind' => [1 => $chapterId, 2 => $userId],
'order' => 'id DESC',
]);

View File

@ -6,7 +6,6 @@ use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder;
use App\Models\Category as CategoryModel;
use App\Models\Chapter as ChapterModel;
use App\Models\ChapterUser as ChapterUserModel;
use App\Models\Comment as CommentModel;
use App\Models\Consult as ConsultModel;
use App\Models\Course as CourseModel;
use App\Models\CourseCategory as CourseCategoryModel;
@ -290,14 +289,6 @@ class Course extends Repository
]);
}
public function countComments($courseId)
{
return (int)CommentModel::count([
'conditions' => 'course_id = :course_id: AND published = 1',
'bind' => ['course_id' => $courseId],
]);
}
public function countFavorites($courseId)
{
return (int)CourseFavoriteModel::count([

View File

@ -31,8 +31,8 @@ class Danmu extends Repository
$builder->andWhere('chapter_id = :chapter_id:', ['chapter_id' => $where['chapter_id']]);
}
if (!empty($where['user_id'])) {
$builder->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]);
if (!empty($where['owner_id'])) {
$builder->andWhere('owner_id = :owner_id:', ['owner_id' => $where['owner_id']]);
}
if (isset($where['published'])) {
@ -100,8 +100,8 @@ class Danmu extends Repository
$query->andWhere('chapter_id = :chapter_id:', ['chapter_id' => $where['chapter_id']]);
}
if (!empty($where['user_id'])) {
$query->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]);
if (!empty($where['owner_id'])) {
$query->andWhere('owner_id = :owner_id:', ['owner_id' => $where['owner_id']]);
}
if (!empty($where['start_time']) && !empty($where['end_time'])) {
@ -123,7 +123,7 @@ class Danmu extends Repository
public function countDanmus()
{
return (int)DanmuModel::count(['conditions' => 'deleted = 0']);
return (int)DanmuModel::count(['conditions' => 'published = 1']);
}
}

View File

@ -21,14 +21,26 @@ class ImGroup extends Repository
$builder->where('1 = 1');
if (!empty($where['course_id'])) {
$builder->andWhere('sender_id = :sender_id:', ['sender_id' => $where['sender_id']]);
if (!empty($where['id'])) {
$builder->andWhere('id = :id:', ['id' => $where['id']]);
}
if (!empty($where['name'])) {
$builder->andWhere('name LIKE :name:', ['name' => "%{$where['name']}%"]);
}
if (!empty($where['course_id'])) {
$builder->andWhere('course_id = :course_id:', ['course_id' => $where['course_id']]);
}
if (!empty($where['owner_id'])) {
$builder->andWhere('owner_id = :owner_id:', ['owner_id' => $where['owner_id']]);
}
if (isset($where['published'])) {
$builder->andWhere('published = :published:', ['published' => $where['published']]);
}
if (isset($where['deleted'])) {
$builder->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]);
}

View File

@ -28,8 +28,8 @@ class Review extends Repository
$builder->andWhere('course_id = :course_id:', ['course_id' => $where['course_id']]);
}
if (!empty($where['user_id'])) {
$builder->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]);
if (!empty($where['owner_id'])) {
$builder->andWhere('owner_id = :owner_id:', ['owner_id' => $where['owner_id']]);
}
if (isset($where['published'])) {
@ -88,8 +88,8 @@ class Review extends Repository
public function findReview($courseId, $userId)
{
return ReviewModel::findFirst([
'conditions' => 'course_id = :course_id: AND user_id = :user_id:',
'bind' => ['course_id' => $courseId, 'user_id' => $userId],
'conditions' => 'course_id = :course_id: AND owner_id = :owner_id:',
'bind' => ['course_id' => $courseId, 'owner_id' => $userId],
]);
}

View File

@ -114,6 +114,8 @@ class ChapterInfo extends FrontendService
$this->joinedCourse = true;
$this->incrCourseUserCount($course);
$this->incrUserCourseCount($course);
}
protected function handleChapterUser(ChapterModel $chapter, UserModel $user)
@ -142,6 +144,12 @@ class ChapterInfo extends FrontendService
$this->incrChapterUserCount($chapter);
}
protected function incrUserCourseCount(UserModel $user)
{
$user->course_count += 1;
$user->update();
}
protected function incrCourseUserCount(CourseModel $course)
{
$course->user_count += 1;

View File

@ -8,7 +8,7 @@ use App\Models\User as UserModel;
use App\Repos\ChapterLike as ChapterLikeRepo;
use App\Services\Frontend\ChapterTrait;
use App\Services\Frontend\Service as FrontendService;
use App\Validators\UserDailyLimit as UserDailyLimitValidator;
use App\Validators\UserLimit as UserLimitValidator;
class ChapterLike extends FrontendService
{
@ -21,9 +21,9 @@ class ChapterLike extends FrontendService
$user = $this->getLoginUser();
$validator = new UserDailyLimitValidator();
$validator = new UserLimitValidator();
$validator->checkChapterLikeLimit($user);
$validator->checkDailyChapterLikeLimit($user);
$likeRepo = new ChapterLikeRepo();
@ -58,7 +58,7 @@ class ChapterLike extends FrontendService
$this->incrUserDailyChapterLikeCount($user);
return $chapter;
return $chapterLike;
}
protected function incrLikeCount(ChapterModel $chapter)

View File

@ -48,7 +48,7 @@ class DanmuList extends FrontendService
foreach ($items as $item) {
$user = $users[$item['user_id']] ?? new \stdClass();
$owner = $users[$item['owner_id']] ?? new \stdClass();
$result[] = [
'id' => $item['id'],
@ -57,7 +57,7 @@ class DanmuList extends FrontendService
'size' => $item['size'],
'time' => $item['time'],
'position' => $item['position'],
'user' => $user,
'owner' => $owner,
];
}

View File

@ -10,7 +10,7 @@ use App\Services\Frontend\ChapterTrait;
use App\Services\Frontend\CourseTrait;
use App\Services\Frontend\Service as FrontendService;
use App\Validators\Consult as ConsultValidator;
use App\Validators\UserDailyLimit as UserDailyLimitValidator;
use App\Validators\UserLimit as UserLimitValidator;
class ConsultCreate extends FrontendService
{
@ -28,15 +28,15 @@ class ConsultCreate extends FrontendService
$course = $this->checkCourse($chapter->course_id);
$validator = new UserDailyLimitValidator();
$validator = new UserLimitValidator();
$validator->checkConsultLimit($user);
$validator->checkDailyConsultLimit($user);
$validator = new ConsultValidator();
$question = $validator->checkQuestion($post['question']);
$validator->checkIfDuplicated($chapter->id, $user->id, $question);
$validator->checkIfDuplicated($question, $chapter->id, $user->id);
$priority = $this->getPriority($course, $user);
@ -46,7 +46,7 @@ class ConsultCreate extends FrontendService
$consult->priority = $priority;
$consult->course_id = $course->id;
$consult->chapter_id = $chapter->id;
$consult->user_id = $user->id;
$consult->owner_id = $user->id;
$consult->published = 1;
$consult->create();

View File

@ -29,7 +29,7 @@ class ConsultDelete extends FrontendService
$validator = new ConsultValidator();
$validator->checkOwner($user->id, $consult->user_id);
$validator->checkOwner($user->id, $consult->owner_id);
$consult->update(['deleted' => 1]);

View File

@ -28,6 +28,7 @@ class ConsultInfo extends FrontendService
'question' => $consult->question,
'answer' => $consult->answer,
'private' => $consult->private,
'rating' => $consult->rating,
'like_count' => $consult->like_count,
'create_time' => $consult->create_time,
'update_time' => $consult->update_time,
@ -53,12 +54,12 @@ class ConsultInfo extends FrontendService
$userRepo = new UserRepo();
$user = $userRepo->findById($consult->user_id);
$owner = $userRepo->findById($consult->owner_id);
$result['user'] = [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
$result['owner'] = [
'id' => $owner->id,
'name' => $owner->name,
'avatar' => $owner->avatar,
];
return $result;

View File

@ -8,7 +8,7 @@ use App\Models\User as UserModel;
use App\Services\Frontend\ConsultTrait;
use App\Services\Frontend\Service as FrontendService;
use App\Validators\Consult as ConsultValidator;
use App\Validators\UserDailyLimit as UserDailyLimitValidator;
use App\Validators\UserLimit as UserLimitValidator;
class ConsultLike extends FrontendService
{
@ -21,9 +21,9 @@ class ConsultLike extends FrontendService
$user = $this->getLoginUser();
$validator = new UserDailyLimitValidator();
$validator = new UserLimitValidator();
$validator->checkConsultLikeLimit($user);
$validator->checkDailyConsultLikeLimit($user);
$validator = new ConsultValidator();
@ -58,7 +58,7 @@ class ConsultLike extends FrontendService
$this->incrUserDailyConsultLikeCount($user);
return $consult;
return $consultLike;
}
protected function incrLikeCount(ConsultModel $consult)

View File

@ -0,0 +1,37 @@
<?php
namespace App\Services\Frontend\Consult;
use App\Services\Frontend\ConsultTrait;
use App\Services\Frontend\Service as FrontendService;
use App\Validators\Consult as ConsultValidator;
class ConsultRating extends FrontendService
{
use ConsultTrait;
public function handle($id)
{
$post = $this->request->getPost();
$user = $this->getLoginUser();
$consult = $this->checkConsult($id);
$validator = new ConsultValidator();
$validator->checkOwner($user->id, $consult->user_id);
$validator->checkIfAllowRate($consult);
$rating = $validator->checkRating($post['rating']);
$consult->rating = $rating;
$consult->update();
return $consult;
}
}

View File

@ -21,7 +21,9 @@ class ConsultUpdate extends FrontendService
$validator = new ConsultValidator();
$validator->checkOwner($user->id, $consult->user_id);
$validator->checkOwner($user->id, $consult->owner_id);
$validator->checkIfAllowEdit($consult);
$question = $validator->checkQuestion($post['question']);

View File

@ -2,7 +2,7 @@
namespace App\Services\Frontend\Course;
use App\Caches\CourseChapterList as CourseChapterListCache;
use App\Caches\CourseCatalog as CourseCatalogCache;
use App\Models\Course as CourseModel;
use App\Models\User as UserModel;
use App\Repos\Course as CourseRepo;
@ -27,7 +27,7 @@ class ChapterList extends FrontendService
protected function getChapters(CourseModel $course, UserModel $user)
{
$cache = new CourseChapterListCache();
$cache = new CourseCatalogCache();
$chapters = $cache->get($course->id);

View File

@ -52,16 +52,17 @@ class ConsultList extends FrontendService
foreach ($consults as $consult) {
$user = $users[$consult['user_id']] ?? new \stdClass();
$owner = $users[$consult['owner_id']] ?? new \stdClass();
$items[] = [
'id' => $consult['id'],
'question' => $consult['question'],
'answer' => $consult['answer'],
'rating' => $consult['rating'],
'like_count' => $consult['like_count'],
'create_time' => $consult['create_time'],
'update_time' => $consult['update_time'],
'user' => $user,
'owner' => $owner,
];
}

View File

@ -8,7 +8,7 @@ use App\Models\User as UserModel;
use App\Repos\CourseFavorite as CourseFavoriteRepo;
use App\Services\Frontend\CourseTrait;
use App\Services\Frontend\Service as FrontendService;
use App\Validators\UserDailyLimit as UserDailyLimitValidator;
use App\Validators\UserLimit as UserLimitValidator;
class Favorite extends FrontendService
{
@ -21,7 +21,7 @@ class Favorite extends FrontendService
$user = $this->getLoginUser();
$validator = new UserDailyLimitValidator();
$validator = new UserLimitValidator();
$validator->checkFavoriteLimit($user);
@ -48,15 +48,19 @@ class Favorite extends FrontendService
$this->decrCourseFavoriteCount($course);
$this->decrUserFavoriteCount($user);
} else {
$favorite->update(['deleted' => 0]);
$this->incrCourseFavoriteCount($course);
$this->incrUserFavoriteCount($user);
}
}
$this->incrUserDailyFavoriteCount($user);
return $favorite;
}
protected function incrCourseFavoriteCount(CourseModel $course)
@ -73,9 +77,18 @@ class Favorite extends FrontendService
}
}
protected function incrUserDailyFavoriteCount(UserModel $user)
protected function incrUserFavoriteCount(UserModel $user)
{
$this->eventsManager->fire('userDailyCounter:incrFavoriteCount', $this, $user);
$user->favorite_count += 1;
$user->update();
}
protected function decrUserFavoriteCount(UserModel $user)
{
if ($user->favorite_count > 0) {
$user->favorite_count -= 1;
$user->update();
}
}
}

View File

@ -51,7 +51,7 @@ class ReviewList extends FrontendService
foreach ($reviews as $review) {
$user = $users[$review['user_id']] ?? new \stdClass();
$owner = $users[$review['owner_id']] ?? new \stdClass();
$items[] = [
'id' => $review['id'],
@ -59,7 +59,7 @@ class ReviewList extends FrontendService
'content' => $review['content'],
'like_count' => $review['like_count'],
'create_time' => $review['create_time'],
'user' => $user,
'owner' => $owner,
];
}

View File

@ -7,7 +7,7 @@ use App\Models\User as UserModel;
use App\Services\Frontend\ChapterTrait;
use App\Services\Frontend\Service as FrontendService;
use App\Validators\Danmu as DanmuValidator;
use App\Validators\UserDailyLimit as UserDailyLimitValidator;
use App\Validators\UserLimit as UserLimitValidator;
class DanmuCreate extends FrontendService
{
@ -22,9 +22,9 @@ class DanmuCreate extends FrontendService
$chapter = $this->checkChapter($post['chapter_id']);
$validator = new UserDailyLimitValidator();
$validator = new UserLimitValidator();
$validator->checkDanmuLimit($user);
$validator->checkDailyDanmuLimit($user);
$validator = new DanmuValidator();
@ -40,7 +40,7 @@ class DanmuCreate extends FrontendService
$data['course_id'] = $chapter->course_id;
$data['chapter_id'] = $chapter->id;
$data['user_id'] = $user->id;
$data['owner_id'] = $user->id;
$data['published'] = 1;

View File

@ -32,12 +32,12 @@ class DanmuInfo extends FrontendService
$userRepo = new UserRepo();
$user = $userRepo->findById($danmu->user_id);
$owner = $userRepo->findById($danmu->user_id);
$result['user'] = [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
$result['owner'] = [
'id' => $owner->id,
'name' => $owner->name,
'avatar' => $owner->avatar,
];
return $result;

View File

@ -56,6 +56,7 @@ class ConsultList extends FrontendService
'id' => $consult['id'],
'question' => $consult['question'],
'answer' => $consult['answer'],
'rating' => $consult['rating'],
'like_count' => $consult['like_count'],
'create_time' => $consult['create_time'],
'update_time' => $consult['update_time'],

View File

@ -2,63 +2,19 @@
namespace App\Services\Frontend\My;
use App\Builders\CourseFavoriteList as CourseFavoriteListBuilder;
use App\Library\Paginator\Query as PagerQuery;
use App\Repos\CourseFavorite as CourseFavoriteRepo;
use App\Services\Frontend\Service as FrontendService;
use App\Services\Frontend\UserTrait;
use App\Services\Frontend\User\FavoriteList as UserFavoriteListService;
class FavoriteList extends FrontendService
{
use UserTrait;
public function handle()
{
$user = $this->getLoginUser();
$pagerQuery = new PagerQuery();
$service = new UserFavoriteListService();
$params = $pagerQuery->getParams();
$params['user_id'] = $user->id;
$params['deleted'] = 0;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$favoriteRepo = new CourseFavoriteRepo();
$pager = $favoriteRepo->paginate($params, $sort, $page, $limit);
return $this->handleCourses($pager);
}
protected function handleCourses($pager)
{
if ($pager->total_items == 0) {
return $pager;
}
$builder = new CourseFavoriteListBuilder();
$relations = $pager->items->toArray();
$courses = $builder->getCourses($relations);
$items = [];
foreach ($relations as $relation) {
$course = $courses[$relation['course_id']] ?? new \stdClass();
$items[] = $course;
}
$pager->items = $items;
return $pager;
return $service->handle($user->id);
}
}

View File

@ -12,7 +12,7 @@ use App\Repos\Order as OrderRepo;
use App\Repos\Package as PackageRepo;
use App\Services\Frontend\Service as FrontendService;
use App\Validators\Order as OrderValidator;
use App\Validators\UserDailyLimit as UserDailyLimitValidator;
use App\Validators\UserLimit as UserLimitValidator;
class OrderCreate extends FrontendService
{
@ -23,9 +23,9 @@ class OrderCreate extends FrontendService
$user = $this->getLoginUser();
$validator = new UserDailyLimitValidator();
$validator = new UserLimitValidator();
$validator->checkOrderLimit($user);
$validator->checkDailyOrderLimit($user);
$validator = new OrderValidator();

View File

@ -4,13 +4,11 @@ namespace App\Services\Frontend\Review;
use App\Models\Course as CourseModel;
use App\Models\Review as ReviewModel;
use App\Models\User as UserModel;
use App\Services\Frontend\CourseTrait;
use App\Services\Frontend\ReviewTrait;
use App\Services\Frontend\Service as FrontendService;
use App\Validators\CourseUser as CourseUserValidator;
use App\Validators\Review as ReviewValidator;
use App\Validators\UserDailyLimit as UserDailyLimitValidator;
class ReviewCreate extends FrontendService
{
@ -26,10 +24,6 @@ class ReviewCreate extends FrontendService
$user = $this->getLoginUser();
$validator = new UserDailyLimitValidator();
$validator->checkReviewLimit($user);
$validator = new CourseUserValidator();
$validator->checkCourseUser($course->id, $user->id);
@ -39,7 +33,7 @@ class ReviewCreate extends FrontendService
$data = [
'course_id' => $course->id,
'user_id' => $user->id,
'owner_id' => $user->id,
];
$data['content'] = $validator->checkContent($post['content']);
@ -55,8 +49,6 @@ class ReviewCreate extends FrontendService
$this->incrCourseReviewCount($course);
$this->incrUserDailyReviewCount($user);
return $review;
}
@ -66,9 +58,4 @@ class ReviewCreate extends FrontendService
$course->update();
}
protected function incrUserDailyReviewCount(UserModel $user)
{
$this->eventsManager->fire('userDailyCounter:incrReviewCount', $this, $user);
}
}

View File

@ -24,9 +24,9 @@ class ReviewDelete extends FrontendService
$validator = new ReviewValidator();
$validator->checkOwner($user->id, $review->user_id);
$validator->checkOwner($user->id, $review->owner_id);
$review->delete();
$review->update(['deleted' => 1]);
$this->decrCourseReviewCount($course);

View File

@ -36,9 +36,9 @@ class ReviewInfo extends FrontendService
$userRepo = new UserRepo();
$owner = $userRepo->findById($review->user_id);
$owner = $userRepo->findById($review->owner_id);
$result['user'] = [
$result['owner'] = [
'id' => $owner->id,
'name' => $owner->name,
'avatar' => $owner->avatar,

View File

@ -8,7 +8,7 @@ use App\Models\User as UserModel;
use App\Services\Frontend\ReviewTrait;
use App\Services\Frontend\Service as FrontendService;
use App\Validators\Review as ReviewValidator;
use App\Validators\UserDailyLimit as UserDailyLimitValidator;
use App\Validators\UserLimit as UserLimitValidator;
class ReviewLike extends FrontendService
{
@ -21,9 +21,9 @@ class ReviewLike extends FrontendService
$user = $this->getLoginUser();
$validator = new UserDailyLimitValidator();
$validator = new UserLimitValidator();
$validator->checkReviewLikeLimit($user);
$validator->checkDailyReviewLikeLimit($user);
$validator = new ReviewValidator();
@ -58,7 +58,7 @@ class ReviewLike extends FrontendService
$this->incrUserDailyReviewLikeCount($user);
return $review;
return $reviewLike;
}
protected function incrLikeCount(ReviewModel $review)

View File

@ -25,7 +25,9 @@ class ReviewUpdate extends FrontendService
$validator = new ReviewValidator();
$validator->checkOwner($user->id, $review->user_id);
$validator->checkOwner($user->id, $review->owner_id);
$validator->checkIfAllowEdit($review);
$data = [];

View File

@ -3,6 +3,7 @@
namespace App\Validators;
use App\Exceptions\BadRequest as BadRequestException;
use App\Models\Consult as ConsultModel;
use App\Repos\Consult as ConsultRepo;
use App\Repos\ConsultLike as ConsultLikeRepo;
@ -70,6 +71,15 @@ class Consult extends Validator
return $value;
}
public function checkRating($rating)
{
if (!in_array($rating, [1, 2, 3, 4, 5])) {
throw new BadRequestException('consult.invalid_rating');
}
return $rating;
}
public function checkPrivateStatus($status)
{
if (!in_array($status, [0, 1])) {
@ -88,7 +98,41 @@ class Consult extends Validator
return $status;
}
public function checkIfDuplicated($chapterId, $userId, $question)
public function checkIfAllowEdit(ConsultModel $consult)
{
/**
* (1)已回复不允许修改提问
* (2)发表三天以后不能修改提问
*/
$case1 = !empty($consult->answer);
$case2 = time() - $consult->create_time > 3 * 86400;
if ($case1 || $case2) {
throw new BadRequestException('consult.edit_not_allowed');
}
}
public function checkIfAllowRate(ConsultModel $consult)
{
/**
* 未回复不允许评价
*/
if (empty($consult->answer)) {
throw new BadRequestException('consult.rate_not_allowed');
}
/**
* 已评价,三天后不能更改评价
*/
$case1 = $consult->rating > 0;
$case2 = time() - $consult->answer_time > 3 * 86400;
if ($case1 && $case2) {
throw new BadRequestException('consult.rate_not_allowed');
}
}
public function checkIfDuplicated($question, $chapterId, $userId)
{
$repo = new ConsultRepo();

View File

@ -5,6 +5,7 @@ namespace App\Validators;
use App\Caches\ImGroup as ImGroupCache;
use App\Caches\MaxImGroupId as MaxImGroupIdCache;
use App\Exceptions\BadRequest as BadRequestException;
use App\Library\Validators\Common as CommonValidator;
use App\Models\ImGroup as ImGroupModel;
use App\Repos\ImGroup as ImGroupRepo;
@ -28,7 +29,7 @@ class ImGroup extends Validator
* 防止缓存穿透
*/
if ($id < 1 || $id > $maxGroupId) {
throw new BadRequestException('im_chat_group.not_found');
throw new BadRequestException('im_group.not_found');
}
$groupCache = new ImGroupCache();
@ -36,7 +37,7 @@ class ImGroup extends Validator
$group = $groupCache->get($id);
if (!$group) {
throw new BadRequestException('im_chat_group.not_found');
throw new BadRequestException('im_group.not_found');
}
return $group;
@ -49,7 +50,7 @@ class ImGroup extends Validator
$group = $groupRepo->findById($id);
if (!$group) {
throw new BadRequestException('im_chat_group.not_found');
throw new BadRequestException('im_group.not_found');
}
return $group;
@ -62,11 +63,11 @@ class ImGroup extends Validator
$length = kg_strlen($value);
if ($length < 2) {
throw new BadRequestException('im_chat_group.name_too_short');
throw new BadRequestException('im_group.name_too_short');
}
if ($length > 30) {
throw new BadRequestException('im_chat_group.name_too_long');
throw new BadRequestException('im_group.name_too_long');
}
return $value;
@ -79,10 +80,48 @@ class ImGroup extends Validator
$length = kg_strlen($value);
if ($length > 255) {
throw new BadRequestException('im_chat_group.about_too_long');
throw new BadRequestException('im_group.about_too_long');
}
return $value;
}
public function checkAvatar($avatar)
{
$value = $this->filter->sanitize($avatar, ['trim', 'string']);
if (!CommonValidator::url($value)) {
throw new BadRequestException('im_group.invalid_avatar');
}
return $value;
}
public function checkType($type)
{
$list = ImGroupModel::types();
if (!isset($list[$type])) {
throw new BadRequestException('im_group.invalid_type');
}
return $type;
}
public function checkPublishStatus($status)
{
if (!in_array($status, [0, 1])) {
throw new BadRequestException('im_group.invalid_publish_status');
}
return $status;
}
public function checkGroupOwner($userId)
{
$validator = new User();
return $validator->checkUser($userId);
}
}

View File

@ -4,6 +4,7 @@ namespace App\Validators;
use App\Exceptions\BadRequest;
use App\Exceptions\BadRequest as BadRequestException;
use App\Models\Review as ReviewModel;
use App\Repos\Review as ReviewRepo;
use App\Repos\ReviewLike as ReviewLikeRepo;
@ -65,6 +66,15 @@ class Review extends Validator
return $status;
}
public function checkIfAllowEdit(ReviewModel $review)
{
$case = time() - $review->create_time > 7 * 86400;
if ($case) {
throw new BadRequestException('review.edit_not_allowed');
}
}
public function checkIfLiked($reviewId, $userId)
{
$repo = new ReviewLikeRepo();

View File

@ -114,10 +114,14 @@ class User extends Validator
public function checkArea($area)
{
if (empty($area['province'] || empty($area['city']) || empty($area['county']))) {
if (empty($area['province'] || empty($area['city']))) {
throw new BadRequestException('user.invalid_area');
}
if (empty($area['county'])) {
$area['county'] = '***';
}
return join('/', $area);
}

View File

@ -1,103 +0,0 @@
<?php
namespace App\Validators;
use App\Caches\UserDailyCounter as CacheUserDailyCounter;
use App\Exceptions\BadRequest as BadRequestException;
use App\Models\User as UserModel;
class UserDailyLimit extends Validator
{
protected $counter;
public function __construct()
{
$this->counter = new CacheUserDailyCounter();
}
public function checkFavoriteLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'favorite_count');
$limit = $user->vip ? 100 : 50;
if ($count > $limit) {
throw new BadRequestException('user_daily_limit.reach_favorite_limit');
}
}
public function checkDanmuLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'danmu_count');
$limit = $user->vip ? 100 : 50;
if ($count > $limit) {
throw new BadRequestException('user_daily_limit.reach_danmu_limit');
}
}
public function checkConsultLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'consult_count');
$limit = $user->vip ? 20 : 10;
if ($count > $limit) {
throw new BadRequestException('user_daily_limit.reach_consult_limit');
}
}
public function checkReviewLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'review_count');
if ($count > 10) {
throw new BadRequestException('user_daily_limit.reach_review_limit');
}
}
public function checkOrderLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'order_count');
if ($count > 10) {
throw new BadRequestException('user_daily_limit.reach_order_limit');
}
}
public function checkChapterLikeLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'chapter_like_count');
$limit = $user->vip ? 200 : 100;
if ($count > $limit) {
throw new BadRequestException('user_daily_limit.reach_like_limit');
}
}
public function checkConsultLikeLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'consult_like_count');
$limit = $user->vip ? 200 : 100;
if ($count > $limit) {
throw new BadRequestException('user_daily_limit.reach_like_limit');
}
}
public function checkReviewLikeLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'review_like_count');
$limit = $user->vip ? 200 : 100;
if ($count > $limit) {
throw new BadRequestException('user_daily_limit.reach_like_limit');
}
}
}

View File

@ -0,0 +1,92 @@
<?php
namespace App\Validators;
use App\Caches\UserDailyCounter as CacheUserDailyCounter;
use App\Exceptions\BadRequest as BadRequestException;
use App\Models\User as UserModel;
class UserLimit extends Validator
{
protected $counter;
public function __construct()
{
$this->counter = new CacheUserDailyCounter();
}
public function checkFavoriteLimit(UserModel $user)
{
$limit = $user->vip ? 1000 : 500;
if ($user->favorite_count > $limit) {
throw new BadRequestException('user_limit.reach_favorite_limit');
}
}
public function checkDailyDanmuLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'danmu_count');
$limit = $user->vip ? 100 : 50;
if ($count > $limit) {
throw new BadRequestException('user_limit.reach_daily_danmu_limit');
}
}
public function checkDailyConsultLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'consult_count');
$limit = $user->vip ? 20 : 10;
if ($count > $limit) {
throw new BadRequestException('user_limit.reach_daily_consult_limit');
}
}
public function checkDailyOrderLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'order_count');
if ($count > 10) {
throw new BadRequestException('user_limit.reach_daily_order_limit');
}
}
public function checkDailyChapterLikeLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'chapter_like_count');
$limit = $user->vip ? 200 : 100;
if ($count > $limit) {
throw new BadRequestException('user_limit.reach_daily_like_limit');
}
}
public function checkDailyConsultLikeLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'consult_like_count');
$limit = $user->vip ? 200 : 100;
if ($count > $limit) {
throw new BadRequestException('user_limit.reach_daily_like_limit');
}
}
public function checkDailyReviewLikeLimit(UserModel $user)
{
$count = $this->counter->hGet($user->id, 'review_like_count');
$limit = $user->vip ? 200 : 100;
if ($count > $limit) {
throw new BadRequestException('user_limit.reach_daily_like_limit');
}
}
}

View File

@ -30,6 +30,10 @@ class ConsoleErrorHandler extends Component
$logger = $this->getLogger();
$logger->error($content);
if ($this->config->env == ENV_DEV) {
echo $content;
}
}
protected function getLogger()

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