1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-06-23 11:58:41 +08:00

账户,课程,章节关联表生成做成事务

This commit is contained in:
xiaochong0302 2020-07-23 20:05:08 +08:00
parent 8748b7d52f
commit 199845cfd0
41 changed files with 4859 additions and 249 deletions

View File

@ -4,7 +4,7 @@ namespace App\Builders;
use App\Repos\User as UserRepo;
class FriendUserList extends Builder
class ImFriendUserList extends Builder
{
public function handleFriends(array $relations)
@ -24,7 +24,12 @@ class FriendUserList extends Builder
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar', 'about', 'vip']);
$columns = [
'id', 'name', 'avatar', 'gender', 'vip',
'location', 'about', 'active_time',
];
$users = $userRepo->findByIds($ids, $columns);
$baseUrl = kg_ci_base_url();

View File

@ -0,0 +1,43 @@
<?php
namespace App\Builders;
use App\Repos\ImGroup as ImGroupRepo;
class ImGroupUserList extends Builder
{
public function handleGroups(array $relations)
{
$groups = $this->getGroups($relations);
foreach ($relations as $key => $value) {
$relations[$key]['group'] = $groups[$value['group_id']] ?? new \stdClass();
}
return $relations;
}
public function getGroups(array $relations)
{
$ids = kg_array_column($relations, 'group_id');
$groupRepo = new ImGroupRepo();
$columns = ['id', 'type', 'name', 'avatar', 'about', 'user_count'];
$groups = $groupRepo->findByIds($ids, $columns);
$baseUrl = kg_ci_base_url();
$result = [];
foreach ($groups->toArray() as $group) {
$group['group'] = $baseUrl . $group['avatar'];
$result[$group['id']] = $group;
}
return $result;
}
}

View File

@ -5,6 +5,9 @@ namespace App\Http\Admin\Services;
use App\Caches\Chapter as ChapterCache;
use App\Caches\CourseChapterList as CourseChapterListCache;
use App\Models\Chapter as ChapterModel;
use App\Models\ChapterLive as ChapterLiveModel;
use App\Models\ChapterRead as ChapterReadModel;
use App\Models\ChapterVod as ChapterVodModel;
use App\Models\Course as CourseModel;
use App\Repos\Chapter as ChapterRepo;
use App\Repos\Course as CourseRepo;
@ -47,6 +50,8 @@ class Chapter extends Service
$chapterRepo = new ChapterRepo();
$parentId = 0;
if (isset($post['parent_id'])) {
$parent = $validator->checkParent($post['parent_id']);
$data['parent_id'] = $parent->id;
@ -54,21 +59,72 @@ class Chapter extends Service
$data['priority'] = $chapterRepo->maxLessonPriority($post['parent_id']);
} else {
$data['priority'] = $chapterRepo->maxChapterPriority($post['course_id']);
$data['parent_id'] = $parentId;
}
$data['priority'] += 1;
$chapter = new ChapterModel();
try {
$chapter->create($data);
$this->db->begin();
$this->updateChapterStats($chapter);
$chapter = new ChapterModel();
$this->updateCourseStats($chapter);
if ($chapter->create($data) === false) {
throw new \RuntimeException('Create Chapter Failed');
}
$this->rebuildChapterCache($chapter);
$data = [
'course_id' => $course->id,
'chapter_id' => $chapter->id,
];
return $chapter;
if ($parentId > 0) {
$attrs = false;
switch ($course->model) {
case CourseMOdel::MODEL_VOD:
$chapterVod = new ChapterVodModel();
$attrs = $chapterVod->create($data);
break;
case CourseModel::MODEL_LIVE:
$chapterLive = new ChapterLiveModel();
$attrs = $chapterLive->create($data);
break;
case CourseModel::MODEL_READ:
$chapterRead = new ChapterReadModel();
$attrs = $chapterRead->create($data);
break;
}
if ($attrs === false) {
throw new \RuntimeException("Create Chapter {$course->model} Attrs Failed");
}
}
$this->db->commit();
$this->updateChapterStats($chapter);
$this->updateCourseStats($chapter);
return $chapter;
} catch (\Exception $e) {
$this->db->rollback();
$logger = $this->getLogger();
$logger->error('Create Chapter Error ' . kg_json_encode([
'line' => $e->getLine(),
'code' => $e->getCode(),
'message' => $e->getMessage(),
]));
throw new \RuntimeException('sys.trans_rollback');
}
}
public function updateChapter($id)
@ -110,8 +166,6 @@ class Chapter extends Service
$this->updateCourseStats($chapter);
$this->rebuildChapterCache($chapter);
return $chapter;
}
@ -131,8 +185,6 @@ class Chapter extends Service
$this->updateCourseStats($chapter);
$this->rebuildChapterCache($chapter);
return $chapter;
}
@ -148,8 +200,6 @@ class Chapter extends Service
$this->updateCourseStats($chapter);
$this->rebuildChapterCache($chapter);
return $chapter;
}

View File

@ -10,8 +10,10 @@ use App\Caches\CourseTeacherList as CourseTeacherListCache;
use App\Library\Paginator\Query as PagerQuery;
use App\Models\Course as CourseModel;
use App\Models\CourseCategory as CourseCategoryModel;
use App\Models\CourseRating as CourseRatingModel;
use App\Models\CourseRelated as CourseRelatedModel;
use App\Models\CourseUser as CourseUserModel;
use App\Models\ImGroup as ImGroupModel;
use App\Repos\Category as CategoryRepo;
use App\Repos\Chapter as ChapterRepo;
use App\Repos\Course as CourseRepo;
@ -65,20 +67,57 @@ class Course extends Service
$validator = new CourseValidator();
$data = [];
$model = $validator->checkModel($post['model']);
$title = $validator->checkTitle($post['title']);
$data['model'] = $validator->checkModel($post['model']);
$data['title'] = $validator->checkTitle($post['title']);
try {
$course = new CourseModel();
$this->db->begin();
$course->create($data);
$course = new CourseModel();
$this->rebuildCourseCache($course);
$course->model = $model;
$course->title = $title;
$this->rebuildCourseIndex($course);
if ($course->create() === false) {
throw new \RuntimeException('Create Course Failed');
}
return $course;
$courseRating = new CourseRatingModel();
$courseRating->course_id = $course->id;
if ($courseRating->create() === false) {
throw new \RuntimeException('Create CourseRating Failed');
}
$imGroup = new ImGroupModel();
$imGroup->course_id = $course->id;
$imGroup->name = $course->title;
$imGroup->about = $course->summary;
if ($imGroup->create() === false) {
throw new \RuntimeException('Create ImGroup Failed');
}
$this->db->commit();
return $course;
} catch (\Exception $e) {
$this->db->rollback();
$logger = $this->getLogger();
$logger->error('Create Course Error ' . kg_json_encode([
'code' => $e->getCode(),
'message' => $e->getMessage(),
]));
throw new \RuntimeException('sys.trans_rollback');
}
}
public function updateCourse($id)
@ -149,8 +188,6 @@ class Course extends Service
$course->update($data);
$this->rebuildCourseCache($course);
$this->rebuildCourseIndex($course);
return $course;
@ -164,8 +201,6 @@ class Course extends Service
$course->update();
$this->rebuildCourseCache($course);
$this->rebuildCourseIndex($course);
return $course;
@ -179,8 +214,6 @@ class Course extends Service
$course->update();
$this->rebuildCourseCache($course);
$this->rebuildCourseIndex($course);
return $course;

View File

@ -7,6 +7,7 @@ use App\Services\Frontend\Account\EmailUpdate as EmailUpdateService;
use App\Services\Frontend\Account\PasswordReset as PasswordResetService;
use App\Services\Frontend\Account\PasswordUpdate as PasswordUpdateService;
use App\Services\Frontend\Account\PhoneUpdate as PhoneUpdateService;
use Phalcon\Mvc\View;
/**
* @RoutePrefix("/account")
@ -149,6 +150,7 @@ class AccountController extends Controller
$captcha = $service->getSectionSettings('captcha');
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('account/edit_password');
$this->view->setVar('captcha', $captcha);
}
@ -166,6 +168,7 @@ class AccountController extends Controller
$captcha = $service->getSectionSettings('captcha');
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('account/edit_phone');
$this->view->setVar('captcha', $captcha);
}
@ -183,6 +186,7 @@ class AccountController extends Controller
$captcha = $service->getSectionSettings('captcha');
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('account/edit_email');
$this->view->setVar('captcha', $captcha);
}
@ -215,10 +219,7 @@ class AccountController extends Controller
$service->handle();
$content = [
'location' => $this->url->get(['for' => 'web.my.account']),
'msg' => '更新手机成功',
];
$content = ['msg' => '更新手机成功'];
return $this->jsonSuccess($content);
}
@ -232,10 +233,7 @@ class AccountController extends Controller
$service->handle();
$content = [
'location' => $this->url->get(['for' => 'web.my.account']),
'msg' => '更新邮箱成功',
];
$content = ['msg' => '更新邮箱成功'];
return $this->jsonSuccess($content);
}
@ -249,10 +247,7 @@ class AccountController extends Controller
$service->handle();
$content = [
'location' => $this->url->get(['for' => 'web.my.account']),
'msg' => '更新密码成功',
];
$content = ['msg' => '更新密码成功'];
return $this->jsonSuccess($content);
}

View File

@ -9,6 +9,8 @@ use App\Services\Frontend\My\RefundList as MyRefundListService;
use App\Services\Frontend\My\ReviewList as MyReviewListService;
use App\Services\Frontend\My\UserInfo as UserInfoService;
use App\Services\Frontend\My\UserUpdate as UserUpdateService;
use App\Services\Frontend\User\FriendList as UserFriendListService;
use App\Services\Frontend\User\GroupList as UserGroupListService;
/**
* @RoutePrefix("/my")
@ -16,6 +18,15 @@ use App\Services\Frontend\My\UserUpdate as UserUpdateService;
class MyController extends Controller
{
public function initialize()
{
parent::initialize();
if ($this->authUser->id == 0) {
$this->response->redirect(['for' => 'web.account.login']);
}
}
/**
* @Get("/home", name="web.my.home")
*/
@ -127,6 +138,34 @@ class MyController extends Controller
$this->view->setVar('pager', $pager);
}
/**
* @Get("/friends", name="web.my.friends")
*/
public function friendsAction()
{
$service = new UserFriendListService();
$pager = $service->handle($this->authUser->id);
$pager->items = kg_array_object($pager->items);
$this->view->setVar('pager', $pager);
}
/**
* @Get("/groups", name="web.my.groups")
*/
public function groupsAction()
{
$service = new UserGroupListService();
$pager = $service->handle($this->authUser->id);
$pager->items = kg_array_object($pager->items);
$this->view->setVar('pager', $pager);
}
/**
* @Post("/profile/update", name="web.my.update_profile")
*/
@ -141,4 +180,20 @@ class MyController extends Controller
return $this->jsonSuccess($content);
}
/**
* @Post("/friend/delete", name="web.my.delete_friend")
*/
public function deleteFriendAction()
{
}
/**
* @Post("/group/delete", name="web.my.delete_group")
*/
public function deleteGroupAction()
{
}
}

View File

@ -5,6 +5,7 @@ namespace App\Http\Web\Controllers;
use App\Services\Frontend\User\CourseList as UserCourseListService;
use App\Services\Frontend\User\FavoriteList as UserFavoriteListService;
use App\Services\Frontend\User\FriendList as UserFriendListService;
use App\Services\Frontend\User\GroupList as UserGroupListService;
use App\Services\Frontend\User\UserInfo as UserInfoService;
use Phalcon\Mvc\View;
@ -74,4 +75,20 @@ class UserController extends Controller
$this->view->setVar('pager', $pager);
}
/**
* @Get("/{id:[0-9]+}/groups", name="web.user.groups")
*/
public function groupsAction($id)
{
$service = new UserGroupListService();
$pager = $service->handle($id);
$pager->items = kg_array_object($pager->items);
$pager->target = 'tab-groups';
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('user/groups');
$this->view->setVar('pager', $pager);
}
}

View File

@ -1,43 +1,36 @@
{% extends 'templates/full.volt' %}
{% extends 'templates/layer.volt' %}
{% block content %}
<div class="layui-breadcrumb breadcrumb">
<a href="/">首页</a>
<a href="{{ url({'for':'web.my.account'}) }}">帐号安全</a>
<a><cite>绑定邮箱</cite></a>
</div>
<div class="account-wrap wrap">
<form class="layui-form account-form" method="POST" action="{{ url({'for':'web.account.update_email'}) }}">
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="password" name="login_password" placeholder="登录密码" lay-verify="required">
</div>
<form class="layui-form account-form" method="POST" action="{{ url({'for':'web.account.update_email'}) }}">
<br><br>
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="password" name="login_password" placeholder="登录密码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<input id="cv-account" class="layui-input" type="text" name="email" placeholder="邮箱地址" data-type="email" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<input id="cv-account" class="layui-input" type="text" name="email" placeholder="邮箱地址" data-type="email" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="verify-input-inline">
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
</div>
<div class="verify-btn-inline">
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
</div>
</div>
<div class="layui-form-item">
<div class="verify-input-inline">
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">立即绑定</button>
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
<input id="cv-ticket" type="hidden" name="ticket">
<input id="cv-rand" type="hidden" name="rand">
</div>
<div class="verify-btn-inline">
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
</div>
</form>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">立即绑定</button>
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
<input id="cv-ticket" type="hidden" name="ticket">
<input id="cv-rand" type="hidden" name="rand">
</div>
</div>
</form>
{% endblock %}

View File

@ -1,36 +1,29 @@
{% extends 'templates/full.volt' %}
{% extends 'templates/layer.volt' %}
{% block content %}
<div class="layui-breadcrumb breadcrumb">
<a href="/">首页</a>
<a href="{{ url({'for':'web.my.account'}) }}">帐号安全</a>
<a><cite>修改密码</cite></a>
</div>
<div class="account-wrap wrap">
<form class="layui-form account-form" method="POST" action="{{ url({'for':'web.account.update_pwd'}) }}">
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="password" name="origin_password" placeholder="原始密码" lay-verify="required">
</div>
<form class="layui-form account-form" method="POST" action="{{ url({'for':'web.account.update_pwd'}) }}">
<br>
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="password" name="origin_password" placeholder="原始密码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="password" name="new_password" placeholder="新设密码" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="password" name="new_password" placeholder="新设密码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="password" name="confirm_password" placeholder="确认密码" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="password" name="confirm_password" placeholder="确认密码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-fluid" lay-submit="true" lay-filter="go">提交</button>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-fluid" lay-submit="true" lay-filter="go">提交</button>
</div>
</form>
</div>
</div>
</form>
{% endblock %}

View File

@ -1,43 +1,36 @@
{% extends 'templates/full.volt' %}
{% extends 'templates/layer.volt' %}
{% block content %}
<div class="layui-breadcrumb breadcrumb">
<a href="/">首页</a>
<a href="{{ url({'for':'web.my.account'}) }}">帐号安全</a>
<a><cite>绑定手机</cite></a>
</div>
<div class="account-wrap wrap">
<form class="layui-form account-form" method="POST" action="{{ url({'for':'web.account.update_phone'}) }}">
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="password" name="login_password" placeholder="登录密码" lay-verify="required">
</div>
<form class="layui-form account-form" method="POST" action="{{ url({'for':'web.account.update_phone'}) }}">
<br><br>
<div class="layui-form-item">
<div class="layui-input-block">
<input class="layui-input" type="password" name="login_password" placeholder="登录密码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<input id="cv-account" class="layui-input" type="text" name="phone" placeholder="手机号码" data-type="phone" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<input id="cv-account" class="layui-input" type="text" name="phone" placeholder="手机号码" data-type="phone" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="verify-input-inline">
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
</div>
<div class="verify-btn-inline">
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
</div>
</div>
<div class="layui-form-item">
<div class="verify-input-inline">
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">立即绑定</button>
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
<input id="cv-ticket" type="hidden" name="ticket">
<input id="cv-rand" type="hidden" name="rand">
</div>
<div class="verify-btn-inline">
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
</div>
</form>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">立即绑定</button>
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
<input id="cv-ticket" type="hidden" name="ticket">
<input id="cv-rand" type="hidden" name="rand">
</div>
</div>
</form>
{% endblock %}

View File

@ -2,13 +2,13 @@
{% block content %}
{% set act_pwd_url = url({'for':'web.account.edit_pwd'}) %}
{% set act_phone_url = url({'for':'web.account.edit_phone'}) %}
{% set act_email_url = url({'for':'web.account.edit_email'}) %}
{% set edit_pwd_url = url({'for':'web.account.edit_pwd'}) %}
{% set edit_phone_url = url({'for':'web.account.edit_phone'}) %}
{% set edit_email_url = url({'for':'web.account.edit_email'}) %}
<div class="layout-main">
<div class="layout-sidebar">{{ partial('my/menu') }}</div>
<div class="layout-content">
<div class="my-sidebar">{{ partial('my/menu') }}</div>
<div class="my-content">
<div class="wrap">
<div class="my-nav-title">账号安全</div>
<div class="security-item-list">
@ -16,17 +16,17 @@
<span class="icon"><i class="layui-icon layui-icon-password"></i></span>
<span class="title">登录密码</span>
<span class="summary">经常更改密码有助于保护您的帐号安全</span>
<span class="action"><a class="layui-btn layui-btn-sm" href="{{ act_pwd_url }}">修改</a></span>
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-pwd" href="javascript:" data-url="{{ edit_pwd_url }}">修改</a></span>
</div>
<div class="security-item">
<span class="icon"><i class="layui-icon layui-icon-cellphone"></i></span>
<span class="title">手机绑定</span>
{% if account.phone %}
<span class="summary">已绑定手机:{{ account.phone|anonymous }}</span>
<span class="action"><a class="layui-btn layui-btn-sm" href="{{ act_phone_url }}">修改</a></span>
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-phone" href="javascript:" data-url="{{ edit_phone_url }}">修改</a></span>
{% else %}
<span class="summary">可用于登录和重置密码</span>
<span class="action"><a class="layui-btn layui-btn-sm" href="{{ act_phone_url }}">绑定</a></span>
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-phone" href="javascript:" data-url="{{ edit_phone_url }}">绑定</a></span>
{% endif %}
</div>
<div class="security-item">
@ -34,10 +34,10 @@
<span class="title">邮箱绑定</span>
{% if account.phone %}
<span class="summary">已绑定邮箱:{{ account.email|anonymous }}</span>
<span class="action"><a class="layui-btn layui-btn-sm" href="{{ act_email_url }}">修改</a></span>
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-email" href="javascript:" data-url="{{ edit_email_url }}">修改</a></span>
{% else %}
<span class="summary">可用于登录和重置密码</span>
<span class="action"><a class="layui-btn layui-btn-sm" href="{{ act_email_url }}">绑定</a></span>
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-email" href="javascript:" data-url="{{ edit_email_url }}">绑定</a></span>
{% endif %}
</div>
</div>
@ -45,4 +45,10 @@
</div>
</div>
{% endblock %}
{% block include_js %}
{{ js_include('web/js/my.account.js') }}
{% endblock %}

View File

@ -0,0 +1,64 @@
{% extends 'templates/full.volt' %}
{% block content %}
{%- macro gender_info(value) %}
{% if value == 1 %}
{% elseif value == 2 %}
{% elseif value == 3 %}
保密
{% endif %}
{%- endmacro %}
<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>
<col width="15%">
</colgroup>
<thead>
<tr>
<th>昵称</th>
<th>性别</th>
<th>地区</th>
<th>最后活跃</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in pager.items %}
{% set user_url = url({'for':'web.user.show','id':item.id}) %}
{% set delete_friend_url = url({'for':'web.my.delete_friend'}) %}
<tr>
<td><a href="{{ user_url }}" title="{{ item.about|e }}">{{ item.name }}</a></td>
<td>{{ gender_info(item.gender) }}</td>
<td>{{ item.location }}</td>
<td>{{ item.active_time|time_ago }}</td>
<td><a class="layui-btn layui-btn-sm btn-delete-friend" href="javascript:" data-friendId="{{ item.id }}" data-url="#">删除好友</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{{ partial('partials/pager') }}
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block include_js %}
{{ js_include('web/js/my.im.js') }}
{% endblock %}

View File

@ -0,0 +1,55 @@
{% extends 'templates/full.volt' %}
{% block content %}
{%- macro type_info(value) %}
{% if value == 'course' %}
<span class="layui-badge layui-bg-green">课</span>
{% elseif value == 'chat' %}
<span class="layui-badge layui-bg-blue">聊</span>
{% endif %}
{%- endmacro %}
<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 width="15%">
</colgroup>
<thead>
<tr>
<th>名称</th>
<th>成员数</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in pager.items %}
{% set delete_group_url = url({'for':'web.my.delete_group'}) %}
<tr>
<td><span title="{{ item.about|e }}">{{ item.name }}</span> {{ type_info(item.type) }}</td>
<td><span class="layui-badge-rim">{{ item.user_count }}</span></td>
<td><a class="layui-btn layui-btn-sm btn-delete-group" href="javascript:" data-groupId="{{ item.id }}" data-url="{{ delete_group_url }}">退出群组</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{{ partial('partials/pager') }}
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block include_js %}
{{ js_include('web/js/my.im.js') }}
{% endblock %}

View File

@ -40,8 +40,8 @@
<div class="layui-card-header">聊天设置</div>
<div class="layui-card-body">
<ul class="my-menu">
<li><a href="{{ url({'for':'web.my.profile'}) }}">我的好友</a></li>
<li><a href="{{ url({'for':'web.my.account'}) }}">我的群组</a></li>
<li><a href="{{ url({'for':'web.my.friends'}) }}">我的好友</a></li>
<li><a href="{{ url({'for':'web.my.groups'}) }}">我的群组</a></li>
</ul>
</div>
</div>

View File

@ -25,6 +25,24 @@
<input type="radio" name="gender" value="3" title="保密" {{ none_checked }}>
</div>
</div>
<div class="layui-form-item" id="area-picker" style="margin-bottom: 25px;">
<div class="layui-form-label">所在地</div>
<div class="layui-input-inline" style="width: 200px;">
<select name="area[province]" class="province-selector" data-value="{{ user.area.province }}" lay-verify="required">
<option value="">请选择省</option>
</select>
</div>
<div class="layui-input-inline" style="width: 200px;">
<select name="area[city]" class="city-selector" data-value="{{ user.area.city }}" lay-verify="required">
<option value="">请选择市</option>
</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">
<option value="">请选择区</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
@ -43,4 +61,10 @@
</div>
</div>
{% endblock %}
{% block include_js %}
{{ js_include('web/js/my.profile.js') }}
{% endblock %}

View File

@ -272,8 +272,12 @@ function kg_time_ago($time)
{
$diff = time() - $time;
if ($diff > 7 * 86400) {
if ($diff > 365 * 86400) {
return date('Y-m-d', $time);
} elseif ($diff > 30 * 86400) {
return floor($diff / 30 / 86400) . '月前';
} elseif ($diff > 7 * 86400) {
return floor($diff / 7 / 86400) . '周前';
} elseif ($diff > 86400) {
return floor($diff / 86400) . '天前';
} elseif ($diff > 3600) {

View File

@ -64,9 +64,10 @@ class Pay extends Listener
$this->db->rollback();
$this->logger->error('After Pay Event Exception {msg}',
['msg' => $e->getMessage()]
);
$this->logger->error('After Pay Event Error ' . kg_json_encode([
'code' => $e->getCode(),
'message' => $e->getMessage(),
]));
}
$this->logger->debug('Event: {event}, Source: {source}, Data: {data}', [
@ -74,6 +75,8 @@ class Pay extends Listener
'source' => get_class($source),
'data' => kg_json_encode($trade),
]);
throw new \RuntimeException('sys.trans_rollback');
}
}

View File

@ -90,17 +90,4 @@ class Account extends Model
$this->update_time = time();
}
public function afterCreate()
{
$user = new User();
$user->id = $this->id;
$user->name = "user_{$this->id}";
$user->create();
$imUser = new ImUser();
$imUser->id = $this->id;
$imUser->name = "user_{$this->id}";
$imUser->create();
}
}

View File

@ -227,31 +227,6 @@ class Chapter extends Model
public function afterCreate()
{
if ($this->parent_id > 0) {
$course = Course::findFirst($this->course_id);
$data = [
'course_id' => $course->id,
'chapter_id' => $this->id,
];
switch ($course->model) {
case Course::MODEL_VOD:
$chapterVod = new ChapterVod();
$chapterVod->create($data);
break;
case Course::MODEL_LIVE:
$chapterLive = new ChapterLive();
$chapterLive->create($data);
break;
case Course::MODEL_READ:
$chapterRead = new ChapterRead();
$chapterRead->create($data);
break;
}
}
$cache = new MaxChapterIdCache();
$cache->rebuild();

View File

@ -299,10 +299,6 @@ class Course extends Model
public function afterCreate()
{
$courseRating = new CourseRating();
$courseRating->create(['course_id' => $this->id]);
$cache = new MaxCourseIdCache();
$cache->rebuild();

View File

@ -10,7 +10,7 @@ class ImFriendGroup extends Model
/**
* 主键编号
*
* @var integer
* @var int
*/
public $id;
@ -24,35 +24,35 @@ class ImFriendGroup extends Model
/**
* 优先级
*
* @var integer
* @var int
*/
public $priority;
/**
* 状态
*
* @var integer
* @var int
*/
public $deleted;
/**
* 成员数
*
* @var integer
* @var int
*/
public $user_count;
/**
* 创建时间
*
* @var integer
* @var int
*/
public $create_time;
/**
* 更新时间
*
* @var integer
* @var int
*/
public $update_time;

View File

@ -10,7 +10,7 @@ class ImFriendMessage extends Model
/**
* 主键编号
*
* @var integer
* @var int
*/
public $id;
@ -24,14 +24,14 @@ class ImFriendMessage extends Model
/**
* 发送方编号
*
* @var integer
* @var int
*/
public $sender_id;
/**
* 接收方编号
*
* @var integer
* @var int
*/
public $receiver_id;
@ -45,28 +45,28 @@ class ImFriendMessage extends Model
/**
* 阅读标识
*
* @var integer
* @var int
*/
public $viewed;
/**
* 删除标识
*
* @var integer
* @var int
*/
public $deleted;
/**
* 创建时间
*
* @var integer
* @var int
*/
public $create_time;
/**
* 更新时间
*
* @var integer
* @var int
*/
public $update_time;

View File

@ -8,56 +8,56 @@ class ImFriendUser extends Model
/**
* 主键编号
*
* @var integer
* @var int
*/
public $id;
/**
* 用户编号
*
* @var integer
* @var int
*/
public $user_id;
/**
* 好友编号
*
* @var integer
* @var int
*/
public $friend_id;
/**
* 分组编号
*
* @var integer
* @var int
*/
public $group_id;
/**
* 消息数量
*
* @var integer
* @var int
*/
public $msg_count;
/**
* 屏蔽标识
*
* @var integer
* @var int
*/
public $blocked;
/**
* 创建时间
*
* @var integer
* @var int
*/
public $create_time;
/**
* 更新时间
*
* @var integer
* @var int
*/
public $update_time;

View File

@ -8,27 +8,40 @@ use Phalcon\Text;
class ImGroup extends Model
{
/**
* 群组类型
*/
const TYPE_COURSE = 'course'; // 课程
const TYPE_CHAT = 'chat'; // 聊天
/**
* 主键编号
*
* @var integer
* @var int
*/
public $id;
/**
* 课程编号
*
* @var integer
* @var int
*/
public $course_id;
/**
* 群主编号
*
* @var string
* @var int
*/
public $user_id;
/**
* 群组类型
*
* @var string
*/
public $type;
/**
* 名称
*
@ -53,35 +66,35 @@ class ImGroup extends Model
/**
* 发布状态
*
* @var integer
* @var int
*/
public $published;
/**
* 删除状态
*
* @var integer
* @var int
*/
public $deleted;
/**
* 成员数
*
* @var integer
* @var int
*/
public $user_count;
/**
* 创建时间
*
* @var integer
* @var int
*/
public $create_time;
/**
* 更新时间
*
* @var integer
* @var int
*/
public $update_time;

View File

@ -10,21 +10,21 @@ class ImGroupMessage extends Model
/**
* 主键编号
*
* @var integer
* @var int
*/
public $id;
/**
* 群组编号
*
* @var integer
* @var int
*/
public $group_id;
/**
* 发送方编号
*
* @var integer
* @var int
*/
public $sender_id;
@ -38,21 +38,21 @@ class ImGroupMessage extends Model
/**
* 删除标识
*
* @var integer
* @var int
*/
public $deleted;
/**
* 创建时间
*
* @var integer
* @var int
*/
public $create_time;
/**
* 更新时间
*
* @var integer
* @var int
*/
public $update_time;

View File

@ -8,49 +8,49 @@ class ImGroupUser extends Model
/**
* 主键编号
*
* @var integer
* @var int
*/
public $id;
/**
* 群组编号
*
* @var integer
* @var int
*/
public $group_id;
/**
* 用户编号
*
* @var integer
* @var int
*/
public $user_id;
/**
* 优先级
*
* @var integer
* @var int
*/
public $priority;
/**
* 屏蔽标识
*
* @var integer
* @var int
*/
public $blocked;
/**
* 创建时间
*
* @var integer
* @var int
*/
public $create_time;
/**
* 更新时间
*
* @var integer
* @var int
*/
public $update_time;

View File

@ -27,21 +27,21 @@ class ImSystemMessage extends Model
/**
* 主键编号
*
* @var integer
* @var int
*/
public $id;
/**
* 发送方编号
*
* @var integer
* @var int
*/
public $sender_id;
/**
* 接收方编号
*
* @var integer
* @var int
*/
public $receiver_id;
@ -62,35 +62,35 @@ class ImSystemMessage extends Model
/**
* 优先级
*
* @var integer
* @var int
*/
public $priority;
/**
* 阅读标识
*
* @var integer
* @var int
*/
public $viewed;
/**
* 删除标识
*
* @var integer
* @var int
*/
public $deleted;
/**
* 创建时间
*
* @var integer
* @var int
*/
public $create_time;
/**
* 更新时间
*
* @var integer
* @var int
*/
public $update_time;

View File

@ -2,12 +2,53 @@
namespace App\Repos;
use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder;
use App\Models\ImGroupUser as ImGroupUserModel;
use Phalcon\Mvc\Model;
class ImGroupUser extends Repository
{
public function paginate($where = [], $sort = 'latest', $page = 1, $limit = 15)
{
$builder = $this->modelsManager->createBuilder();
$builder->from(ImGroupUserModel::class);
$builder->where('1 = 1');
if (!empty($where['group_id'])) {
$builder->andWhere('group_id = :group_id:', ['group_id' => $where['group_id']]);
}
if (!empty($where['user_id'])) {
$builder->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]);
}
if (isset($where['blocked'])) {
$builder->andWhere('blocked = :blocked:', ['blocked' => $where['blocked']]);
}
switch ($sort) {
case 'oldest':
$orderBy = 'id ASC';
break;
default:
$orderBy = 'id DESC';
break;
}
$builder->orderBy($orderBy);
$pager = new PagerQueryBuilder([
'builder' => $builder,
'page' => $page,
'limit' => $limit,
]);
return $pager->paginate();
}
/**
* @param int $groupId
* @param int $userId

View File

@ -4,6 +4,8 @@ namespace App\Services\Frontend\Account;
use App\Library\Validators\Common as CommonValidator;
use App\Models\Account as AccountModel;
use App\Models\ImUser as ImUserModel;
use App\Models\User as UserModel;
use App\Services\Frontend\Service as FrontendService;
use App\Validators\Account as AccountValidator;
use App\Validators\Verify as VerifyValidator;
@ -40,11 +42,49 @@ class Register extends FrontendService
$data['password'] = $accountValidator->checkPassword($post['password']);
$account = new AccountModel();
try {
$account->create($data);
$this->db->begin();
return $account;
$account = new AccountModel();
if ($account->create($data) === false) {
throw new \RuntimeException('Create Account Failed');
}
$user = new UserModel();
$user->id = $this->id;
$user->name = "user_{$this->id}";
if ($user->create() === false) {
throw new \RuntimeException('Create User Failed');
}
$imUser = new ImUserModel();
$imUser->id = $this->id;
$imUser->name = "user_{$this->id}";
if ($imUser->create() === false) {
throw new \RuntimeException('Create ImUser Failed');
}
$this->db->commit();
return $account;
} catch (\Exception $e) {
$this->db->rollback();
$logger = $this->getLogger();
$logger->error('Register Error ' . kg_json_encode([
'code' => $e->getCode(),
'message' => $e->getMessage(),
]));
throw new \RuntimeException('sys.trans_rollback');
}
}
}

View File

@ -19,14 +19,17 @@ class UserInfo extends FrontendService
{
$user->avatar = kg_ci_img_url($user->avatar);
$area = $this->handleArea($user->location);
return [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
'title' => $user->title,
'about' => $user->about,
'location' => $user->location,
'gender' => $user->gender,
'location' => $user->location,
'area' => $area,
'vip' => $user->vip,
'locked' => $user->locked,
'vip_expiry_time' => $user->vip_expiry_time,
@ -36,4 +39,15 @@ class UserInfo extends FrontendService
];
}
protected function handleArea($location)
{
$area = explode('/', $location);
return [
'province' => $area[0] ?? '',
'city' => $area[1] ?? '',
'county' => $area[2] ?? '',
];
}
}

View File

@ -30,6 +30,10 @@ class UserUpdate extends FrontendService
$data['gender'] = $validator->checkGender($post['gender']);
}
if (!empty($post['area'])) {
$data['location'] = $validator->checkArea($post['area']);
}
if (!empty($post['about'])) {
$data['about'] = $validator->checkAbout($post['about']);
}

View File

@ -15,7 +15,7 @@ class FavoriteList extends FrontendService
public function handle($id)
{
$user = $this->checkUserCache($id);
$user = $this->checkUser($id);
$pagerQuery = new PagerQuery();

View File

@ -2,7 +2,7 @@
namespace App\Services\Frontend\User;
use App\Builders\FriendUserList as FriendUserListBuilder;
use App\Builders\ImFriendUserList as ImFriendUserListBuilder;
use App\Library\Paginator\Query as PagerQuery;
use App\Repos\ImFriendUser as ImFriendUserRepo;
use App\Services\Frontend\Service as FrontendService;
@ -15,7 +15,7 @@ class FriendList extends FrontendService
public function handle($id)
{
$user = $this->checkUserCache($id);
$user = $this->checkUser($id);
$pagerQuery = new PagerQuery();
@ -40,7 +40,7 @@ class FriendList extends FrontendService
return $pager;
}
$builder = new FriendUserListBuilder();
$builder = new ImFriendUserListBuilder();
$relations = $pager->items->toArray();

View File

@ -0,0 +1,61 @@
<?php
namespace App\Services\Frontend\User;
use App\Builders\ImGroupUserList as ImGroupUserListBuilder;
use App\Library\Paginator\Query as PagerQuery;
use App\Repos\ImGroupUser as ImGroupUserRepo;
use App\Services\Frontend\Service as FrontendService;
use App\Services\Frontend\UserTrait;
class GroupList extends FrontendService
{
use UserTrait;
public function handle($id)
{
$user = $this->checkUser($id);
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['user_id'] = $user->id;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$repo = new ImGroupUserRepo();
$pager = $repo->paginate($params, $sort, $page, $limit);
return $this->handleGroups($pager);
}
protected function handleGroups($pager)
{
if ($pager->total_items == 0) {
return $pager;
}
$builder = new ImGroupUserListBuilder();
$relations = $pager->items->toArray();
$groups = $builder->getGroups($relations);
$items = [];
foreach ($relations as $relation) {
$group = $groups[$relation['group_id']] ?? new \stdClass();
$items[] = $group;
}
$pager->items = $items;
return $pager;
}
}

View File

@ -112,6 +112,15 @@ class User extends Validator
return $value;
}
public function checkArea($area)
{
if (empty($area['province'] || empty($area['city']) || empty($area['county']))) {
throw new BadRequestException('user.invalid_area');
}
return join('/', $area);
}
public function checkEduRole($value)
{
$list = UserModel::eduRoleTypes();

View File

@ -11,6 +11,7 @@ $error['sys.bad_request'] = '无效的请求';
$error['sys.not_found'] = '资源不存在';
$error['sys.internal_server_error'] = '内部错误';
$error['sys.service_unavailable'] = '服务不可用';
$error['sys.trans_rollback'] = '事务回滚';
$error['sys.unknown_error'] = '未知错误';
/**
@ -60,6 +61,7 @@ $error['user.title_too_long'] = '头衔过长超过30个字符';
$error['user.sign_too_long'] = '签名过长超过50个字符';
$error['user.about_too_long'] = '简介过长超过255个字符';
$error['user.invalid_gender'] = '无效的性别类型';
$error['user.invalid_area'] = '无效的省市地区';
$error['user.invalid_edu_role'] = '无效的教学角色';
$error['user.invalid_admin_role'] = '无效的后台角色';
$error['user.invalid_vip_status'] = '无效的会员状态';

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
layui.use(['jquery', 'layer'], function () {
var $ = layui.jquery;
var layer = layui.layer;
$('.btn-edit-pwd').on('click', function () {
var url = $(this).data('url');
layer.open({
type: 2,
title: '修改密码',
content: [url, 'no'],
area: ['640px', '320px'],
cancel: function () {
window.location.reload();
}
});
});
$('.btn-edit-phone').on('click', function () {
var url = $(this).data('url');
layer.open({
type: 2,
title: '修改手机',
content: [url, 'no'],
area: ['640px', '420px'],
cancel: function () {
window.location.reload();
}
});
});
$('.btn-edit-email').on('click', function () {
var url = $(this).data('url');
layer.open({
type: 2,
title: '修改邮箱',
content: [url, 'no'],
area: ['640px', '420px'],
cancel: function () {
window.location.reload();
}
});
});
});

View File

@ -0,0 +1,36 @@
layui.use(['jquery', 'layer'], function () {
var $ = layui.jquery;
var layer = layui.layer;
$('.btn-delete-friend').on('click', function () {
var url = $(this).data('url');
var friendId = $(this).data('friendId');
layer.confirm('确定要删除好友吗?', function () {
$.ajax({
type: 'POST',
url: url,
data: {friend_id: friendId},
success: function (res) {
}
});
});
});
$('.btn-delete-group').on('click', function () {
var url = $(this).data('url');
var groupId = $(this).data('groupId');
layer.confirm('确定要退出群组吗?', function () {
$.ajax({
type: 'POST',
url: url,
data: {group_id: groupId},
success: function (res) {
}
});
});
});
});

View File

@ -32,6 +32,7 @@ layui.use(['jquery', 'layer', 'helper'], function () {
});
});
if ($('#tab-courses').length > 0) {
var $tabCourses = $('#tab-courses');
helper.ajaxLoadHtml($tabCourses.data('url'), $tabCourses.attr('id'));

View File

@ -0,0 +1,12 @@
layui.use(['layarea'], function () {
var layarea = layui.layarea;
layarea.render({
elem: '#area-picker',
change: function (res) {
console.log(res);
}
});
});