1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-06-25 04:07:17 +08:00

整理代码

This commit is contained in:
xiaochong0302 2020-05-13 21:20:13 +08:00
parent f772c206d0
commit 3014ef2260
25 changed files with 294 additions and 98 deletions

View File

@ -2,12 +2,13 @@
namespace App\Console\Tasks;
use App\Models\ChapterUser as ChapterUserModel;
use App\Models\CourseUser as CourseUserModel;
use App\Models\Learning as LearningModel;
use App\Models\Order as OrderModel;
use App\Models\Refund as RefundModel;
use App\Models\Task as TaskModel;
use App\Models\Trade as TradeModel;
use App\Repos\Course as CourseRepo;
use App\Repos\CourseUser as CourseUserRepo;
use App\Repos\Order as OrderRepo;
use App\Repos\User as UserRepo;
@ -87,9 +88,6 @@ class OrderTask extends Task
}
}
/**
* @param OrderModel $order
*/
protected function handleCourseOrder(OrderModel $order)
{
/**
@ -114,9 +112,6 @@ class OrderTask extends Task
$this->handleCourseHistory($data['course_id'], $data['user_id']);
}
/**
* @param OrderModel $order
*/
protected function handlePackageOrder(OrderModel $order)
{
/**
@ -144,9 +139,6 @@ class OrderTask extends Task
}
}
/**
* @param OrderModel $order
*/
protected function handleVipOrder(OrderModel $order)
{
$userRepo = new UserRepo();
@ -165,17 +157,11 @@ class OrderTask extends Task
}
}
/**
* @param OrderModel $order
*/
protected function handleRewardOrder(OrderModel $order)
{
}
/**
* @param OrderModel $order
*/
protected function handleOrderNotice(OrderModel $order)
{
$smser = new OrderSmser();
@ -183,9 +169,6 @@ class OrderTask extends Task
$smser->handle($order);
}
/**
* @param OrderModel $order
*/
protected function handleOrderRefund(OrderModel $order)
{
$trade = $this->findFinishedTrade($order->id);
@ -205,10 +188,6 @@ class OrderTask extends Task
$refund->create();
}
/**
* @param int $courseId
* @param int $userId
*/
protected function handleCourseHistory($courseId, $userId)
{
$courseUserRepo = new CourseUserRepo();
@ -219,17 +198,49 @@ class OrderTask extends Task
$courseUser->update(['deleted' => 1]);
}
$courseRepo = new CourseRepo();
$chapterUsers = $this->findPlanChapterUsers($courseId, $userId);
$userLearnings = $courseRepo->findUserLearnings($courseId, $userId);
if ($chapterUsers->count() > 0) {
$chapterUsers->update(['deleted' => 1]);
}
if ($userLearnings->count() > 0) {
$userLearnings->update(['deleted' => 1]);
$learnings = $this->findPlanLearnings($courseId, $userId);
if ($learnings->count() > 0) {
$learnings->update(['deleted' => 1]);
}
}
/**
* @param $orderId
* @param int $courseId
* @param int $userId
* @return ResultsetInterface|Resultset|TaskModel[]
*/
protected function findPlanChapterUsers($courseId, $userId)
{
return ChapterUserModel::query()
->where('course_id = :course_id:', ['course_id' => $courseId])
->andWhere('user_id = :user_id:', ['user_id' => $userId])
->andWhere('deleted = 0')
->execute();
}
/**
* @param int $courseId
* @param int $userId
* @return ResultsetInterface|Resultset|TaskModel[]
*/
protected function findPlanLearnings($courseId, $userId)
{
return LearningModel::query()
->where('course_id = :course_id:', ['course_id' => $courseId])
->andWhere('user_id = :user_id:', ['user_id' => $userId])
->andWhere('deleted = 0')
->execute();
}
/**
* @param int $orderId
* @return Model|TradeModel
*/
protected function findFinishedTrade($orderId)

View File

@ -56,6 +56,8 @@ class Student extends Service
$params = $pagerQuery->getParams();
$params['deleted'] = 0;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();

View File

@ -3,8 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="csrf-token-key" content="{{ security.getTokenKey() }}">
<meta name="csrf-token-value" content="{{ security.getTokenValue() }}">
<meta name="csrf-token" content="{{ csrfToken.getToken() }}">
<title>管理后台</title>
{{ icon_link("favicon.ico") }}
{{ css_link('lib/layui/css/layui.css') }}

View File

@ -95,14 +95,14 @@
<tr>
<th>编号</th>
<th>昵称</th>
<th>邮箱</th>
<th>手机</th>
<th>邮箱</th>
</tr>
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{% if account.phone %}{{ account.phone }}{% else %}N/A{% endif %}</td>
<td>{% if account.email %}{{ account.email }}{% else %}N/A{% endif %}</td>
<td>{% if account.phone %} {{ account.phone }} {% else %} 未知 {% endif %}</td>
<td>{% if account.email %} {{ account.email }} {% else %} 未知 {% endif %}</td>
</tr>
</table>

View File

@ -111,13 +111,13 @@
<tr>
<th>编号</th>
<th>昵称</th>
<th>邮箱</th>
<th>手机</th>
<th>邮箱</th>
</tr>
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{% if account.phone %}{{ account.phone }}{% else %}N/A{% endif %}</td>
<td>{% if account.email %}{{ account.email }}{% else %}N/A{% endif %}</td>
<td>{% if account.phone %} {{ account.phone }} {% else %} 未知 {% endif %}</td>
<td>{% if account.email %} {{ account.email }} {% else %} 未知 {% endif %}</td>
</tr>
</table>

View File

@ -1,9 +1,9 @@
{%- macro source_type_info(value) %}
{% if value == 'free' %}
{% if value == 1 %}
<span class="layui-badge layui-bg-green">免费</span>
{% elseif value == 'charge' %}
{% elseif value == 2 %}
<span class="layui-badge layui-bg-orange">付费</span>
{% elseif value == 'import' %}
{% elseif value == 3 %}
<span class="layui-badge layui-bg-blue">导入</span>
{% endif %}
{%- endmacro %}
@ -72,7 +72,7 @@
<button class="layui-btn layui-btn-sm">操作 <span class="layui-icon layui-icon-triangle-d"></span></button>
<ul>
<li><a href="{{ url({'for':'admin.student.edit'},{'plan_id':item.id}) }}">编辑学员</a></li>
<li><a href="javascript:" class="kg-learning" data-url="{{ url({'for':'admin.student.learning'},{'plan_id':item.id}) }}">学习记录</a></li>
<li><a href="javascript:" class="kg-learning" data-url="{{ url({'for':'admin.student.learning'},{'course_id':item.course_id,'user_id':item.user_id}) }}">学习记录</a></li>
</ul>
</div>
</td>

View File

@ -23,9 +23,9 @@
<div class="layui-form-item">
<label class="layui-form-label">加入方式</label>
<div class="layui-input-block">
<input type="radio" name="source" value="free" title="免费课程">
<input type="radio" name="source" value="charge" title="付费课程">
<input type="radio" name="source" value="import" title="后台导入">
<input type="radio" name="source" value="1" title="免费课程">
<input type="radio" name="source" value="2" title="付费课程">
<input type="radio" name="source" value="3" title="后台导入">
</div>
</div>

View File

@ -27,10 +27,10 @@
<div style="text-align: center">
{% if trade.status == 'pending' %}
<button class="kg-close layui-btn layui-bg-green" data-url="{{ url('for':'admin.trade.close','id':trade.id}) }}">关闭交易</button>
<button class="kg-close layui-btn layui-bg-green" data-url="{{ url({'for':'admin.trade.close','id':trade.id}) }}">关闭交易</button>
{% endif %}
{% if trade.status == 'finished' %}
<button class="kg-refund layui-btn layui-bg-green" data-url="{{ url('for':'admin.trade.refund','id':trade.id}) }}">申请退款</button>
<button class="kg-refund layui-btn layui-bg-green" data-url="{{ url({'for':'admin.trade.refund','id':trade.id}) }}">申请退款</button>
{% endif %}
<button class="kg-back layui-btn layui-bg-gray">返回上页</button>
</div>
@ -92,14 +92,14 @@
<tr>
<th>编号</th>
<th>昵称</th>
<th>邮箱</th>
<th>手机</th>
<th>邮箱</th>
</tr>
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{% if account.phone %}{{ account.phone }}{% else %}N/A{% endif %}</td>
<td>{% if account.email %}{{ account.email }}{% else %}N/A{% endif %}</td>
<td>{% if account.phone %} {{ account.phone }} {% else %} 未知 {% endif %}</td>
<td>{% if account.email %} {{ account.email }} {% else %} 未知 {% endif %}</td>
</tr>
</table>
@ -116,7 +116,7 @@
$.ajax({
type: 'POST',
url: url,
finished: function (res) {
success: function (res) {
layer.msg(res.msg, {icon: 1});
setTimeout(function () {
window.location.reload();

View File

@ -1,25 +1,26 @@
{%- macro location_info(value) %}
{% if value %}
{{ value }}
{% else %}
N/A
{%- macro last_login_info(user) %}
{% if user.last_login_ip %}
<span class="layui-badge layui-bg-gray">学员</span>
{% endif %}
{% if user.last_login_time %}
<span class="layui-badge layui-bg-gray">学员</span>
{% endif %}
{%- endmacro %}
{%- macro gender_info(value) %}
{% if value == 'male' %}
{% if value == 1 %}
<span class="layui-badge layui-bg-red">男</span>
{% elseif value == 'female' %}
{% elseif value == 2 %}
<span class="layui-badge layui-bg-green">女</span>
{% elseif value == 'none' %}
{% elseif value == 3 %}
<span class="layui-badge layui-bg-gray">密</span>
{% endif %}
{%- endmacro %}
{%- macro edu_role_info(user) %}
{% if user.edu_role.id == 'student' %}
{% if user.edu_role.id == 1 %}
<span class="layui-badge layui-bg-gray">学员</span>
{% elseif user.edu_role.id == 'teacher' %}
{% elseif user.edu_role.id == 2 %}
<span class="layui-badge layui-bg-blue">讲师</span>
{% endif %}
{%- endmacro %}
@ -70,8 +71,8 @@
<tr>
<th>编号</th>
<th>昵称</th>
<th>地区</th>
<th>性别</th>
<th>最后登录</th>
<th>教学角色</th>
<th>后台角色</th>
<th>注册时间</th>
@ -83,8 +84,8 @@
<tr>
<td>{{ item.id }}</td>
<td><span title="{{ item.about }}">{{ item.name }}</span>{{ status_info(item) }}</td>
<td>{{ location_info(item.location) }}</td>
<td>{{ gender_info(item.gender) }}</td>
<td>{{ last_login_info(item) }}</td>
<td>{{ edu_role_info(item) }}</td>
<td>{{ admin_role_info(item) }}</td>
<td>{{ date('Y-m-d H:i',item.create_time) }}</td>

View File

@ -21,8 +21,8 @@
<div class="layui-form-item">
<label class="layui-form-label">教学角色</label>
<div class="layui-input-block">
<input type="radio" name="edu_role" value="student" title="学员">
<input type="radio" name="edu_role" value="teacher" title="讲师">
<input type="radio" name="edu_role" value="1" title="学员">
<input type="radio" name="edu_role" value="2" title="讲师">
</div>
</div>

View File

@ -18,9 +18,12 @@ class OrderController extends Controller
*/
public function confirmAction()
{
$itemId = $this->request->getQuery('item_id');
$itemType = $this->request->getQuery('item_type');
$service = new OrderConfirmService();
$info = $service->handle();
$info = $service->handle($itemId, $itemType);
$this->view->setVar('info', $info);
}

View File

@ -18,9 +18,13 @@ class RefundController extends Controller
*/
public function confirmAction()
{
$sn = $this->request->getQuery('order_sn');
$service = new RefundConfirmService();
$info = $service->handle();
$info = $service->handle($sn);
return $this->jsonSuccess(['info' => $info]);
$this->view->setVar('info', $info);
}

52
app/Library/CsrfToken.php Normal file
View File

@ -0,0 +1,52 @@
<?php
namespace App\Library;
use Phalcon\Crypt;
use Phalcon\Di;
use Phalcon\Text;
class CsrfToken
{
/**
* @var Crypt
*/
protected $crypt;
protected $lifetime = 60 * 60;
protected $delimiter = '@@';
protected $fixed = 'KG';
public function __construct()
{
$this->crypt = Di::getDefault()->get('crypt');
}
public function getToken()
{
$text = implode($this->delimiter, [time(), $this->fixed, Text::random(8)]);
return $this->crypt->encryptBase64($text);
}
public function checkToken($token)
{
$text = $this->crypt->decryptBase64($token);
list($time, $fixed, $random) = explode($this->delimiter, $text);
if ($time != intval($time) || $fixed != $this->fixed || strlen($random) != 8) {
return false;
}
if (time() - $time > $this->lifetime) {
return false;
}
return true;
}
}

View File

@ -10,15 +10,15 @@ class CourseUser extends Model
/**
* 角色类型
*/
const ROLE_STUDENT = 'student'; // 学员
const ROLE_TEACHER = 'teacher'; // 讲师
const ROLE_STUDENT = 1; // 学员
const ROLE_TEACHER = 2; // 讲师
/**
* 来源类型
*/
const SOURCE_FREE = 'free'; // 免费
const SOURCE_CHARGE = 'charge'; // 付费
const SOURCE_IMPORT = 'import'; // 导入
const SOURCE_FREE = 1; // 免费
const SOURCE_CHARGE = 2; // 付费
const SOURCE_IMPORT = 3; // 导入
/**
* 主键编号

View File

@ -74,6 +74,13 @@ class Learning extends Model
*/
public $client_ip;
/**
* 删除标识
*
* @var int
*/
public $deleted;
/**
* 创建时间
*

View File

@ -2,17 +2,17 @@
namespace App\Providers;
use App\Library\Security as AppSecurity;
use App\Library\CsrfToken as MyCsrfToken;
class Security extends Provider
class CsrfToken extends Provider
{
protected $serviceName = 'security';
protected $serviceName = 'csrfToken';
public function register()
{
$this->di->setShared($this->serviceName, function () {
return new AppSecurity();
return new MyCsrfToken();
});
}

View File

@ -29,6 +29,10 @@ class Learning extends Repository
$builder->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]);
}
if (isset($where['deleted'])) {
$builder->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]);
}
switch ($sort) {
default:
$orderBy = 'id DESC';

View File

@ -14,11 +14,8 @@ use App\Validators\Order as OrderValidator;
class OrderConfirm extends Service
{
public function handle()
public function handle($itemId, $itemType)
{
$itemId = $this->request->getQuery('item_id');
$itemType = $this->request->getQuery('item_type');
$user = $this->getLoginUser();
$validator = new OrderValidator();

View File

@ -5,19 +5,20 @@ namespace App\Services\Frontend\Refund;
use App\Models\Refund as RefundModel;
use App\Services\Frontend\OrderTrait;
use App\Services\Frontend\Service;
use App\Services\RefundCalculator;
class RefundConfirm extends Service
{
use OrderTrait;
public function handle()
public function handle($sn)
{
$sn = $this->request->getQuery('order_sn');
$order = $this->checkOrderBySn($sn);
$service = new RefundCalculator();
return $service->handle($order);
}
protected function handleRefund(RefundModel $refund)

View File

@ -0,0 +1,120 @@
<?php
namespace App\Services;
use App\Models\Order as OrderModel;
use App\Repos\Course as CourseRepo;
class RefundCalculator extends Service
{
public function handle(OrderModel $order)
{
$result = [];
switch ($order->item_type) {
case OrderModel::ITEM_COURSE:
$result = $this->handleCourseRefund($order);
break;
case OrderModel::ITEM_PACKAGE:
$result = $this->handlePackageRefund($order);
break;
}
return $result;
}
protected function handleCourseRefund(OrderModel $order)
{
/**
* @var array $itemInfo
*/
$itemInfo = $order->item_info;
$refundPercent = 0.00;
$refundAmount = 0.00;
if ($itemInfo['course']['refund_expiry_time'] > time()) {
$refundPercent = $this->getCourseRefundPercent($order->item_id, $order->user_id);
$refundAmount = $order->amount * $refundPercent;
}
$itemInfo['course']['refund_percent'] = $refundPercent;
$itemInfo['course']['refund_amount'] = $refundAmount;
return [
'item_type' => $order->item_type,
'item_info' => $itemInfo,
'refund_amount' => $refundAmount,
];
}
protected function handlePackageRefund(OrderModel $order)
{
/**
* @var array $itemInfo
*/
$itemInfo = $order->item_info;
$totalMarketPrice = 0.00;
foreach ($itemInfo['courses'] as $course) {
$totalMarketPrice += $course['market_price'];
}
$totalRefundAmount = 0.00;
/**
* 按照占比方式计算退款
*/
foreach ($itemInfo['courses'] as &$course) {
$refundPercent = 0.00;
$refundAmount = 0.00;
if ($course['refund_expiry_time'] > time()) {
$pricePercent = round($course['market_price'] / $totalMarketPrice, 4);
$refundPercent = $this->getCourseRefundPercent($order->user_id, $course['id']);
$refundAmount = round($order->amount * $pricePercent * $refundPercent, 2);
$totalRefundAmount += $refundAmount;
}
$course['item_info']['refund_percent'] = $refundPercent;
$course['item_info']['refund_amount'] = $refundAmount;
}
return [
'item_type' => $order->item_type,
'item_info' => $itemInfo,
'refund_amount' => $totalRefundAmount,
];
}
protected function getCourseRefundPercent($courseId, $userId)
{
$courseRepo = new CourseRepo();
$courseLessons = $courseRepo->findLessons($courseId);
if ($courseLessons->count() == 0) {
return 1.00;
}
$userLearnings = $courseRepo->findConsumedUserLearnings($courseId, $userId);
if ($userLearnings->count() == 0) {
return 1.00;
}
$courseLessonIds = kg_array_column($courseLessons->toArray(), 'id');
$userLessonIds = kg_array_column($userLearnings->toArray(), 'chapter_id');
$consumedLessonIds = array_intersect($courseLessonIds, $userLessonIds);
$totalCount = count($courseLessonIds);
$consumedCount = count($consumedLessonIds);
$refundCount = $totalCount - $consumedCount;
return round($refundCount / $totalCount, 4);
}
}

View File

@ -11,10 +11,9 @@ class Security extends Validator
public function checkCsrfToken()
{
$tokenKey = $this->request->getHeader('X-Csrf-Token-Key');
$tokenValue = $this->request->getHeader('X-Csrf-Token-Value');
$token = $this->request->getHeader('X-Csrf-Token');
$result = $this->security->checkToken($tokenKey, $tokenValue);
$result = $this->csrfToken->checkToken($token);
if (!$result) {
throw new BadRequestException('security.invalid_csrf_token');

View File

@ -50,14 +50,14 @@ class Trade extends Validator
}
}
public function checkIfAllowClose($trade)
public function checkIfAllowClose(TradeModel $trade)
{
if ($trade->status != TradeModel::STATUS_PENDING) {
throw new BadRequestException('trade.close_not_allowed');
}
}
public function checkIfAllowRefund($trade)
public function checkIfAllowRefund(TradeModel $trade)
{
if ($trade->status != TradeModel::STATUS_FINISHED) {
throw new BadRequestException('trade.refund_not_allowed');

View File

@ -7,6 +7,7 @@ use App\Providers\Cache as CacheProvider;
use App\Providers\Config as ConfigProvider;
use App\Providers\Cookie as CookieProvider;
use App\Providers\Crypt as CryptProvider;
use App\Providers\CsrfToken as CsrfTokenProvider;
use App\Providers\Database as DatabaseProvider;
use App\Providers\EventsManager as EventsManagerProvider;
use App\Providers\Logger as LoggerProvider;
@ -15,7 +16,6 @@ use App\Providers\Provider as AppProvider;
use App\Providers\Request as RequestProvider;
use App\Providers\Response as ResponseProvider;
use App\Providers\Router as RouterProvider;
use App\Providers\Security as SecurityProvider;
use App\Providers\Session as SessionProvider;
use App\Providers\Url as UrlProvider;
use App\Providers\View as ViewProvider;
@ -71,6 +71,7 @@ class HttpKernel extends Kernel
CookieProvider::class,
ConfigProvider::class,
CryptProvider::class,
CsrfTokenProvider::class,
DatabaseProvider::class,
EventsManagerProvider::class,
LoggerProvider::class,
@ -78,7 +79,6 @@ class HttpKernel extends Kernel
RequestProvider::class,
ResponseProvider::class,
RouterProvider::class,
SecurityProvider::class,
SessionProvider::class,
UrlProvider::class,
ViewProvider::class,

View File

@ -38,7 +38,7 @@ $error['captcha.invalid_code'] = '无效的验证码';
* 帐号相关
*/
$error['account.not_found'] = '账号不存在';
$error['account.login_locked'] = '账号被锁定,无法登录';
$error['account.login_block'] = '账号被锁定,无法登录';
$error['account.login_name_incorrect'] = '登录账号不正确';
$error['account.login_password_incorrect'] = '登录密码不正确';
$error['account.invalid_email'] = '无效的电子邮箱';
@ -60,7 +60,7 @@ $error['user.invalid_edu_role'] = '无效的教学角色';
$error['user.invalid_admin_role'] = '无效的后台角色';
$error['user.invalid_vip_status'] = '无效的会员状态';
$error['user.invalid_vip_expiry_time'] = '无效的会员期限';
$error['user.invalid_lock_status'] = '无效的锁定状态';
$error['user.invalid_block_status'] = '无效的锁定状态';
$error['user.invalid_lock_expiry_time'] = '无效的锁定期限';
/**
@ -266,8 +266,8 @@ $error['order.trade_expired'] = '交易已过期';
$error['trade.not_found'] = '交易不存在';
$error['trade.create_failed'] = '创建交易失败';
$error['trade.invalid_channel'] = '无效的平台类型';
$error['trade.invalid_close_action'] = '当前不允许关闭交易';
$error['trade.invalid_refund_action'] = '当前不允许交易退款';
$error['trade.close_not_allowed'] = '当前不允许关闭交易';
$error['trade.refund_not_allowed'] = '当前不允许交易退款';
$error['trade.refund_existed'] = '退款申请已经存在';
/**

View File

@ -12,11 +12,7 @@ layui.use(['jquery', 'form', 'element', 'layer', 'dropdown'], function () {
$.ajaxSetup({
beforeSend: function (xhr) {
var csrfTokenKey = $('meta[name="csrf-token-key"]').attr('content');
var csrfTokenValue = $('meta[name="csrf-token-value"]').attr('content');
xhr.setRequestHeader('X-Csrf-Token-Key', csrfTokenKey);
xhr.setRequestHeader('X-Csrf-Token-Value', csrfTokenValue);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-Csrf-Token', $('meta[name="csrf-token"]').attr('content'));
}
});