1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-07-03 07:26:49 +08:00

删除api验证,app应用管理

This commit is contained in:
xiaochong0302 2020-11-23 21:03:00 +08:00
parent 70886569d1
commit ac352683dd
29 changed files with 168 additions and 1080 deletions

View File

@ -1,3 +1,7 @@
### [v1.2.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.0)(2020-11-25)
- 增加客户端api
- 代码优化以及问题修复
### [v1.1.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.1.0)(2020-10-08)
- 增加运营统计功能

View File

@ -36,7 +36,7 @@ class ImGroupUserList extends Builder
$userRepo = new UserRepo();
$columns = ['id', 'name', 'avatar', 'title', 'about', 'vip'];
$columns = ['id', 'name', 'avatar', 'title', 'about', 'vip', 'gender', 'area'];
$users = $userRepo->findByIds($ids, $columns);

View File

@ -4,6 +4,7 @@ namespace App\Builders;
use App\Repos\Chapter as ChapterRepo;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class LiveList extends Builder
{
@ -32,19 +33,38 @@ class LiveList extends Builder
public function getCourses(array $lives)
{
$ids = kg_array_column($lives, 'course_id');
$courseIds = kg_array_column($lives, 'course_id');
$courseRepo = new CourseRepo();
$courses = $courseRepo->findByIds($ids, ['id', 'title', 'cover']);
$courses = $courseRepo->findByIds($courseIds, ['id', 'title', 'cover', 'teacher_id']);
$teacherIds = kg_array_column($courses->toArray(), 'teacher_id');
$userRepo = new UserRepo();
$users = $userRepo->findByIds($teacherIds, ['id', 'name', 'title', 'avatar', 'about']);
$baseUrl = kg_cos_url();
$teachers = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$teachers[$user['id']] = $user;
}
$result = [];
foreach ($courses->toArray() as $course) {
$course['cover'] = $baseUrl . $course['cover'];
$result[$course['id']] = $course;
$course['teacher'] = $teachers[$course['teacher_id']] ?? new \stdClass();
$result[$course['id']] = [
'id' => $course['id'],
'title' => $course['title'],
'cover' => $course['cover'],
'teacher' => $course['teacher'],
];
}
return $result;

View File

@ -1,127 +0,0 @@
<?php
namespace App\Http\Admin\Controllers;
use App\Http\Admin\Services\App as AppService;
/**
* @RoutePrefix("/admin/app")
*/
class AppController extends Controller
{
/**
* @Get("/list", name="admin.app.list")
*/
public function listAction()
{
$appService = new AppService();
$pager = $appService->getApps();
$this->view->setVar('pager', $pager);
}
/**
* @Get("/add", name="admin.app.add")
*/
public function addAction()
{
$appService = new AppService();
$types = $appService->getAppTypes();
$this->view->setVar('types', $types);
}
/**
* @Post("/create", name="admin.app.create")
*/
public function createAction()
{
$appService = new AppService();
$appService->createApp();
$location = $this->url->get(['for' => 'admin.app.list']);
$content = [
'location' => $location,
'msg' => '创建应用成功',
];
return $this->jsonSuccess($content);
}
/**
* @Get("/{id:[0-9]+}/edit", name="admin.app.edit")
*/
public function editAction($id)
{
$appService = new AppService;
$app = $appService->getApp($id);
$types = $appService->getAppTypes();
$this->view->setVar('app', $app);
$this->view->setVar('types', $types);
}
/**
* @Post("/{id:[0-9]+}/update", name="admin.app.update")
*/
public function updateAction($id)
{
$appService = new AppService();
$appService->updateApp($id);
$location = $this->url->get(['for' => 'admin.app.list']);
$content = [
'location' => $location,
'msg' => '更新应用成功',
];
return $this->jsonSuccess($content);
}
/**
* @Post("/{id:[0-9]+}/delete", name="admin.app.delete")
*/
public function deleteAction($id)
{
$appService = new AppService();
$appService->deleteApp($id);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '删除应用成功',
];
return $this->jsonSuccess($content);
}
/**
* @Post("/{id:[0-9]+}/restore", name="admin.app.restore")
*/
public function restoreAction($id)
{
$appService = new AppService();
$appService->restoreApp($id);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '还原应用成功',
];
return $this->jsonSuccess($content);
}
}

View File

@ -1,135 +0,0 @@
<?php
namespace App\Http\Admin\Services;
use App\Caches\App as AppCache;
use App\Library\Paginator\Query as PagerQuery;
use App\Models\App as AppModel;
use App\Repos\App as AppRepo;
use App\Validators\App as AppValidator;
class App extends Service
{
public function getApps()
{
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['deleted'] = $params['deleted'] ?? 0;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$appRepo = new AppRepo();
return $appRepo->paginate($params, $sort, $page, $limit);
}
public function getApp($id)
{
return $this->findOrFail($id);
}
public function createApp()
{
$post = $this->request->getPost();
$validator = new AppValidator();
$data = [];
$data['type'] = $validator->checkType($post['type']);
$data['name'] = $validator->checkName($post['name']);
$data['remark'] = $validator->checkRemark($post['remark']);
$page = new AppModel();
$page->create($data);
$this->rebuildAppCache($page);
return $page;
}
public function updateApp($id)
{
$app = $this->findOrFail($id);
$post = $this->request->getPost();
$validator = new AppValidator();
$data = [];
if (isset($post['type'])) {
$data['type'] = $validator->checkType($post['type']);
}
if (isset($post['name'])) {
$data['name'] = $validator->checkName($post['name']);
}
if (isset($post['remark'])) {
$data['remark'] = $validator->checkRemark($post['remark']);
}
if (isset($post['published'])) {
$data['published'] = $validator->checkPublishStatus($post['published']);
}
$app->update($data);
$this->rebuildAppCache($app);
return $app;
}
public function deleteApp($id)
{
$app = $this->findOrFail($id);
$app->deleted = 1;
$app->update();
$this->rebuildAppCache($app);
return $app;
}
public function restoreApp($id)
{
$app = $this->findOrFail($id);
$app->deleted = 0;
$app->update();
$this->rebuildAppCache($app);
return $app;
}
public function getAppTypes()
{
return AppModel::types();
}
protected function rebuildAppCache(AppModel $app)
{
$cache = new AppCache();
$cache->rebuild($app->key);
}
protected function findOrFail($id)
{
$validator = new AppValidator();
return $validator->checkApp($id);
}
}

View File

@ -746,37 +746,6 @@ class AuthNode extends Service
],
],
],
[
'id' => '5-2',
'title' => '应用管理',
'type' => 'menu',
'children' => [
[
'id' => '5-2-1',
'title' => '应用列表',
'type' => 'menu',
'route' => 'admin.app.list',
],
[
'id' => '5-2-2',
'title' => '添加应用',
'type' => 'menu',
'route' => 'admin.app.add',
],
[
'id' => '5-2-3',
'title' => '编辑应用',
'type' => 'button',
'route' => 'admin.app.edit',
],
[
'id' => '5-2-4',
'title' => '删除应用',
'type' => 'button',
'route' => 'admin.app.delete',
],
],
],
],
];
}

View File

@ -1,41 +0,0 @@
{% extends 'templates/main.volt' %}
{% block content %}
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.app.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">
<select name="type" lay-verify="required">
<option value="">选择类型</option>
{% for key,name in types %}
<option value="{{ key }}">{{ name }}</option>
{% endfor %}
</select>
</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" lay-verify="required">
</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="remark">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="kg-submit layui-btn" lay-submit="true" lay-filter="go">提交</button>
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
</div>
</div>
</form>
{% endblock %}

View File

@ -1,49 +0,0 @@
{% extends 'templates/main.volt' %}
{% block content %}
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.app.update','id':app.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-block">
<select name="type" lay-verify="required">
<option value="">选择类型</option>
{% for key,name in types %}
{% set selected = key == app.type ? 'selected="selected"' : '' %}
<option value="{{ key }}" {{ selected }}>{{ name }}</option>
{% endfor %}
</select>
</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="{{ app.name }}" lay-verify="required">
</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="remark" value="{{ app.remark }}">
</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 app.published == 1 %}checked="checked"{% endif %}>
<input type="radio" name="published" value="0" title="否" {% if app.published == 0 %}checked="checked"{% endif %}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="kg-submit layui-btn" lay-submit="true" lay-filter="go">提交</button>
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
</div>
</div>
</form>
{% endblock %}

View File

@ -1,83 +0,0 @@
{% extends 'templates/main.volt' %}
{% block content %}
{%- macro type_info(value) %}
{% if value == 'pc' %}
PC客户端
{% elseif value == 'h5' %}
H5客户端
{% elseif value == 'ios' %}
IOS客户端
{% elseif value == 'android' %}
Android客户端
{% elseif value == 'mp_weixin' %}
微信小程序
{% elseif value == 'mp_alipay' %}
支付宝小程序
{% else %}
未知
{% endif %}
{%- endmacro %}
<div class="kg-nav">
<div class="kg-nav-left">
<span class="layui-breadcrumb">
<a><cite>应用管理</cite></a>
</span>
</div>
</div>
<table class="kg-table layui-table layui-form">
<colgroup>
<col>
<col>
<col>
<col>
<col>
<col width="10%">
<col width="12%">
</colgroup>
<thead>
<tr>
<th>编号</th>
<th>名称</th>
<th>类型</th>
<th>Key / Secret</th>
<th>创建时间</th>
<th>发布</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in pager.items %}
{% set edit_url = url({'for':'admin.app.edit','id':item.id}) %}
{% set update_url = url({'for':'admin.app.update','id':item.id}) %}
{% set delete_url = url({'for':'admin.app.delete','id':item.id}) %}
{% set restore_url = url({'for':'admin.app.restore','id':item.id}) %}
<tr>
<td>{{ item.id }}</td>
<td><a href="{{ edit_url }}" title="{{ item.remark }}">{{ item.name }}</a></td>
<td>{{ type_info(item.type) }}</td>
<td>{{ item.key }} / {{ item.secret }}</td>
<td>{{ date('Y-m-d H:i:s',item.create_time) }}</td>
<td class="center"><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ update_url }}" {% if item.published == 1 %}checked="checked"{% endif %}></td>
<td class="center">
<div class="layui-dropdown">
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
<ul>
<li><a href="{{ edit_url }}">编辑</a></li>
{% if item.deleted == 0 %}
<li><a href="javascript:" class="kg-delete" data-url="{{ delete_url }}">删除</a></li>
{% else %}
<li><a href="javascript:" class="kg-restore" data-url="{{ restore_url }}">还原</a></li>
{% endif %}
</ul>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -15,19 +15,12 @@ class Controller extends \Phalcon\Mvc\Controller
public function beforeExecuteRoute(Dispatcher $dispatcher)
{
/**
* 存在Origin头信息才设置跨域
*/
if ($this->request->getHeader('Origin')) {
$this->setCors();
}
/**
* Options请求不验证签名和限流
*/
if (!$this->request->isOptions()) {
//$this->checkApiSignature();
//$this->checkRateLimit();
$this->checkRateLimit();
}
return true;

View File

@ -24,7 +24,7 @@ class HelpController extends Controller
}
/**
* @Get("/{id:[0-9]+}", name="api.help.info")
* @Get("/{id:[0-9]+}/info", name="api.help.info")
*/
public function infoAction($id)
{

View File

@ -11,7 +11,7 @@ class PageController extends Controller
{
/**
* @Get("/{id:[0-9]+}", name="api.page.info")
* @Get("/{id:[0-9]+}/info", name="api.page.info")
*/
public function infoAction($id)
{

View File

@ -1,12 +1,12 @@
{%- macro item_info(order) %}
{% if order.item_type == '1' %}
{% if order.item_type == 1 %}
{% set course = order.item_info.course %}
<div class="order-item">
<p>课程名称:<span>{{ course.title }}</span></p>
<p>市场价格:<span class="price">{{ '¥%0.2f'|format(course.market_price) }}</span>会员价格:<span class="price">{{ '¥%0.2f'|format(course.vip_price) }}</span></p>
<p>学习期限:<span>{{ date('Y-m-d',course.study_expiry_time) }}</span>退款期限:<span>{{ date('Y-m-d',course.refund_expiry_time) }}</span></p>
</div>
{% elseif order.item_type == '2' %}
{% elseif order.item_type == 2 %}
{% set courses = order.item_info.courses %}
{% for course in courses %}
<div class="order-item">
@ -15,20 +15,20 @@
<p>学习期限:<span>{{ date('Y-m-d',course.study_expiry_time) }}</span>退款期限:<span>{{ date('Y-m-d',course.refund_expiry_time) }}</span></p>
</div>
{% endfor %}
{% elseif order.item_type == '3' %}
{% elseif order.item_type == 3 %}
{% set course = order.item_info.course %}
{% set reward = order.item_info.reward %}
<div class="order-item">
<p>课程名称:<span>{{ course.title }}</span></p>
<p>赞赏金额:<span class="price">{{ '¥%0.2f'|format(reward.price) }}</span></p>
</div>
{% elseif order.item_type == '4' %}
{% elseif order.item_type == 4 %}
{% set vip = order.item_info.vip %}
<div class="order-item">
<p>商品名称:<span>{{ order.subject }}</span></p>
<p>商品价格:<span class="price">{{ '¥%0.2f'|format(order.amount) }}</span></p>
</div>
{% elseif order.item_type == '99' %}
{% elseif order.item_type == 99 %}
<div class="order-item">
<p>商品名称:<span>{{ order.subject }}</span></p>
<p>商品价格:<span class="price">{{ '¥%0.2f'|format(order.amount) }}</span></p>
@ -38,30 +38,30 @@
{%- macro status_history(items) %}
{% for item in items %}
{% if item.status == '1' %}
{% if item.status == 1 %}
<p>创建时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% elseif item.status == '2' %}
{% elseif item.status == 2 %}
<p>支付时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% elseif item.status == '3' %}
{% elseif item.status == 3 %}
<p>完成时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% elseif item.status == '4' %}
{% elseif item.status == 4 %}
<p>关闭时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% elseif item.status == '5' %}
{% elseif item.status == 5 %}
<p>退款时间:{{ date('Y-m-d H:i:s',item.create_time) }}</p>
{% endif %}
{% endfor %}
{%- endmacro %}
{%- macro order_status(value) %}
{% if value == '1' %}
{% if value == 1 %}
待支付
{% elseif value == '2' %}
{% elseif value == 2 %}
发货中
{% elseif value == '3' %}
{% elseif value == 3 %}
已完成
{% elseif value == '4' %}
{% elseif value == 4 %}
已关闭
{% elseif value == '5' %}
{% elseif value == 5 %}
已退款
{% endif %}
{%- endmacro %}

View File

@ -11,7 +11,7 @@ class AppInfo
protected $link = 'https://gitee.com/koogua';
protected $version = '1.1.0';
protected $version = '1.2.0';
public function __get($name)
{

View File

@ -1,136 +0,0 @@
<?php
namespace App\Models;
use Phalcon\Mvc\Model\Behavior\SoftDelete;
use Phalcon\Text;
class App extends Model
{
/**
* 应用类型
*/
const TYPE_PC = 'pc';
const TYPE_H5 = 'h5';
const TYPE_IOS = 'ios';
const TYPE_ANDROID = 'android';
const TYPE_MP_WEIXIN = 'mp_weixin';
const TYPE_MP_ALIPAY = 'mp_alipay';
/**
* 主键编号
*
* @var int
*/
public $id;
/**
* 名称
*
* @var string
*/
public $name;
/**
* key
*
* @var string
*/
public $key;
/**
* secret
*
* @var string
*/
public $secret;
/**
* 类型
*
* @var string
*/
public $type;
/**
* 备注
*
* @var string
*/
public $remark;
/**
* 发布标识
*
* @var int
*/
public $published;
/**
* 删除标识
*
* @var int
*/
public $deleted;
/**
* 创建时间
*
* @var int
*/
public $create_time;
/**
* 更新时间
*
* @var int
*/
public $update_time;
public function getSource(): string
{
return 'kg_app';
}
public function initialize()
{
parent::initialize();
$this->addBehavior(
new SoftDelete([
'field' => 'deleted',
'value' => 1,
])
);
}
public function beforeCreate()
{
$this->key = Text::random(Text::RANDOM_ALNUM, 16);
$this->secret = Text::random(Text::RANDOM_ALNUM, 16);
$this->create_time = time();
}
public function beforeUpdate()
{
if ($this->deleted == 1) {
$this->published = 0;
}
$this->update_time = time();
}
public static function types()
{
return [
self::TYPE_PC => 'PC客户端',
self::TYPE_H5 => 'H5客户端',
self::TYPE_IOS => 'IOS客户端',
self::TYPE_ANDROID => 'Android客户端',
self::TYPE_MP_WEIXIN => '微信小程序',
self::TYPE_MP_ALIPAY => '支付宝小程序',
];
}
}

View File

@ -1,126 +0,0 @@
<?php
namespace App\Models;
use App\Caches\MaxCategoryId as MaxCategoryIdCache;
use Phalcon\Mvc\Model\Behavior\SoftDelete;
class AppVersion extends Model
{
/**
* 平台类型
*/
const PLATFORM_IOS = 1;
const PLATFORM_ANDROID = 2;
/**
* 主键编号
*
* @var int
*/
public $id;
/**
* 平台类型
*
* @var int
*/
public $platform;
/**
* 版本名例如v1.0.0
*
* @var string
*/
public $version_name;
/**
* 版本号例如100
*
* @var string
*/
public $version_code;
/**
* 下载地址
*
* @var string
*/
public $download_url;
/**
* 发布标识
*
* @var int
*/
public $published;
/**
* 删除标识
*
* @var int
*/
public $deleted;
/**
* 创建时间
*
* @var int
*/
public $create_time;
/**
* 更新时间
*
* @var int
*/
public $update_time;
public function getSource(): string
{
return 'kg_category';
}
public function initialize()
{
parent::initialize();
$this->addBehavior(
new SoftDelete([
'field' => 'deleted',
'value' => 1,
])
);
}
public function beforeCreate()
{
$this->create_time = time();
}
public function beforeUpdate()
{
if ($this->deleted == 1) {
$this->published = 0;
}
$this->update_time = time();
}
public function afterCreate()
{
$cache = new MaxCategoryIdCache();
$cache->rebuild();
}
public static function types()
{
return [
self::TYPE_COURSE => '课程',
self::TYPE_HELP => '帮助',
];
}
}

View File

@ -1,78 +0,0 @@
<?php
namespace App\Repos;
use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder;
use App\Models\App as AppModel;
use Phalcon\Mvc\Model;
class App extends Repository
{
public function paginate($where = [], $sort = 'latest', $page = 1, $limit = 15)
{
$builder = $this->modelsManager->createBuilder();
$builder->from(AppModel::class);
$builder->where('1 = 1');
if (!empty($where['id'])) {
$builder->andWhere('id = :id:', ['id' => $where['id']]);
}
if (!empty($where['key'])) {
$builder->andWhere('key = :key:', ['key' => $where['key']]);
}
if (!empty($where['type'])) {
$builder->andWhere('type = :type:', ['type' => $where['type']]);
}
if (!empty($where['published'])) {
$builder->andWhere('published = :published:', ['published' => $where['published']]);
}
if (isset($where['deleted'])) {
$builder->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]);
}
switch ($sort) {
default:
$orderBy = 'id DESC';
break;
}
$builder->orderBy($orderBy);
$pager = new PagerQueryBuilder([
'builder' => $builder,
'page' => $page,
'limit' => $limit,
]);
return $pager->paginate();
}
/**
* @param int $id
* @return AppModel|Model|bool
*/
public function findById($id)
{
return AppModel::findFirst($id);
}
/**
* @param string $appKey
* @return AppModel|Model|bool
*/
public function findByAppKey($appKey)
{
return AppModel::findFirst([
'conditions' => 'key = :key:',
'bind' => ['key' => $appKey],
]);
}
}

View File

@ -95,15 +95,15 @@ class Consult extends Repository
}
/**
* @param int $chapterId
* @param int $courseId
* @param int $userId
* @return ConsultModel|Model|bool
*/
public function findUserLastChapterConsult($chapterId, $userId)
public function findUserLastCourseConsult($courseId, $userId)
{
return ConsultModel::findFirst([
'conditions' => 'chapter_id = ?1 AND owner_id = ?2 AND deleted = 0',
'bind' => [1 => $chapterId, 2 => $userId],
'conditions' => 'course_id = ?1 AND owner_id = ?2 AND deleted = 0',
'bind' => [1 => $courseId, 2 => $userId],
'order' => 'id DESC',
]);
}

View File

@ -20,29 +20,83 @@ class ConsultCreate extends Service
public function handle()
{
$post = $this->request->getPost();
$chapterId = $this->request->getPost('chapter_id', 'int', 0);
$courseId = $this->request->getPost('course_id', 'int', 0);
$user = $this->getLoginUser();
$chapter = $this->checkChapter($post['chapter_id']);
$course = $this->checkCourse($chapter->course_id);
$validator = new UserLimitValidator();
$validator->checkDailyConsultLimit($user);
$validator = new UserLimitValidator();
$validator->checkDailyConsultLimit($user);
if ($chapterId > 0) {
$chapter = $this->checkChapter($chapterId);
return $this->handleChapterConsult($chapter, $user);
} else {
$course = $this->checkCourse($courseId);
return $this->handleCourseConsult($course, $user);
}
}
protected function handleCourseConsult(CourseModel $course, UserModel $user)
{
$post = $this->request->getPost();
$validator = new ConsultValidator();
$question = $validator->checkQuestion($post['question']);
$private = $validator->checkPrivateStatus($post['private']);
$validator->checkIfDuplicated($question, $chapter->id, $user->id);
$validator->checkIfDuplicated($course->id, $user->id, $question);
$priority = $this->getPriority($course, $user);
$consult = new ConsultModel();
$consult->question = $question;
$consult->private = $private;
$consult->priority = $priority;
$consult->course_id = $course->id;
$consult->owner_id = $user->id;
$consult->published = 1;
$consult->create();
$this->incrCourseConsultCount($course);
$this->incrUserDailyConsultCount($user);
return $consult;
}
protected function handleChapterConsult(ChapterModel $chapter, UserModel $user)
{
$course = $this->checkCourse($chapter->course_id);
$post = $this->request->getPost();
$validator = new ConsultValidator();
$question = $validator->checkQuestion($post['question']);
$private = $validator->checkPrivateStatus($post['private']);
$validator->checkIfDuplicated($course->id, $user->id, $question);
$priority = $this->getPriority($course, $user);
$consult = new ConsultModel();
$consult->question = $question;
$consult->private = $private;
$consult->priority = $priority;
$consult->course_id = $course->id;
$consult->chapter_id = $chapter->id;

View File

@ -4,6 +4,7 @@ namespace App\Services\Logic\Live;
use App\Services\Logic\ChapterTrait;
use App\Services\Logic\Service;
use App\Validators\Live as LiveValidator;
use GatewayClient\Gateway;
class LiveChapter extends Service
@ -97,7 +98,9 @@ class LiveChapter extends Service
$content = $this->request->getPost('content', ['trim', 'striptags']);
$content = kg_substr($content, 0, 80);
$validator = new LiveValidator();
$validator->checkMessage($content);
Gateway::$registerAddress = $this->getRegisterAddress();

View File

@ -62,11 +62,11 @@ class Course extends Handler
$items[] = [
'id' => (int)$item['id'],
'title' => $item['title'],
'cover' => $item['cover'],
'summary' => $item['summary'],
'model' => $item['model'],
'level' => $item['level'],
'title' => (string)$item['title'],
'cover' => (string)$item['cover'],
'summary' => (string)$item['summary'],
'model' => (int)$item['model'],
'level' => (int)$item['level'],
'market_price' => (float)$item['market_price'],
'vip_price' => (float)$item['vip_price'],
'user_count' => (int)$item['user_count'],

View File

@ -62,10 +62,10 @@ class Group extends Handler
$items[] = [
'id' => (int)$item['id'],
'type' => $item['type'],
'name' => $item['name'],
'avatar' => $item['avatar'],
'about' => $item['about'],
'type' => (int)$item['type'],
'name' => (string)$item['name'],
'avatar' => (string)$item['avatar'],
'about' => (string)$item['about'],
'user_count' => (int)$item['user_count'],
'msg_count' => (int)$item['msg_count'],
'owner' => json_decode($item['owner'], true),

View File

@ -62,13 +62,13 @@ class User extends Handler
$items[] = [
'id' => (int)$item['id'],
'name' => $item['name'],
'avatar' => $item['avatar'],
'title' => $item['title'],
'about' => $item['about'],
'name' => (string)$item['name'],
'avatar' => (string)$item['avatar'],
'title' => (string)$item['title'],
'about' => (string)$item['about'],
'vip' => (int)$item['vip'],
'gender' => (int)$item['gender'],
'area' => $item['area'],
'area' => (string)$item['area'],
];
}

View File

@ -1,133 +0,0 @@
<?php
namespace App\Validators;
use App\Caches\App as AppCache;
use App\Exceptions\BadRequest as BadRequestException;
use App\Models\App as AppModel;
class ApiSecurity extends Validator
{
public function check()
{
$query = $this->request->getQuery();
if (isset($query['_url'])) {
unset($query['_url']);
}
$extra = [
'_timestamp' => $this->checkTimestamp(),
'_nonce' => $this->checkNonce(),
];
$appKey = $this->checkAppKey();
$app = $this->getApp($appKey);
if (!$app || $app->published == 0) {
throw new BadRequestException('api.invalid_app_key');
}
$url = $this->getRequestUrl();
if ($this->request->getMethod() == 'POST') {
$mySignature = $this->httpPostSignature($url, $extra, $app->secret);
} else {
$params = array_merge($query, $extra);
$mySignature = $this->httpGetSignature($url, $params, $app->secret);
}
$signature = $this->request->getHeader('X-Signature');
if ($signature != $mySignature) {
throw new BadRequestException('api.invalid_signature');
}
return $signature;
}
protected function checkTimestamp()
{
$timestamp = $this->request->getHeader('X-Timestamp');
$timestamp = $timestamp > 0 ? $timestamp : 0;
if (abs(time() - $timestamp) > 300) {
throw new BadRequestException('api.invalid_timestamp');
}
return $timestamp;
}
protected function checkNonce()
{
$nonce = $this->request->getHeader('X-Nonce');
if (!$nonce) {
throw new BadRequestException('api.invalid_nonce');
}
return $nonce;
}
protected function checkAppKey()
{
$appKey = $this->request->getHeader('X-App-Key');
if (!$appKey) {
throw new BadRequestException('api.invalid_app_key');
}
return $appKey;
}
protected function checkPlatform()
{
$platform = $this->request->getHeader('X-Platform');
if (!array_key_exists($platform, AppModel::types())) {
throw new BadRequestException('api.invalid_platform');
}
return $platform;
}
protected function getRequestUrl()
{
return sprintf('%s://%s%s',
$this->request->getScheme(),
$this->request->getHttpHost(),
$this->request->getURI()
);
}
protected function getApp($appKey)
{
$cache = new AppCache();
return $cache->get($appKey);
}
protected function httpGetSignature($url, $params, $appSecret)
{
ksort($params);
$query = http_build_query($params);
return md5($url . $query . $appSecret);
}
protected function httpPostSignature($url, $params, $appSecret)
{
ksort($params);
$query = http_build_query($params);
$body = $this->request->getRawBody();
return md5($url . $query . $body . $appSecret);
}
}

View File

@ -1,73 +0,0 @@
<?php
namespace App\Validators;
use App\Exceptions\BadRequest as BadRequestException;
use App\Models\App as AppModel;
use App\Repos\App as AppRepo;
class App extends Validator
{
public function checkApp($id)
{
$appRepo = new AppRepo();
$app = $appRepo->findById($id);
if (!$app) {
throw new BadRequestException('app.not_found');
}
return $app;
}
public function checkName($name)
{
$value = $this->filter->sanitize($name, ['trim', 'string']);
$length = kg_strlen($value);
if ($length < 2) {
throw new BadRequestException('app.name_too_short');
}
if ($length > 50) {
throw new BadRequestException('app.name_too_long');
}
return $value;
}
public function checkType($type)
{
if (!array_key_exists($type, AppModel::types())) {
throw new BadRequestException('app.invalid_type');
}
return $type;
}
public function checkRemark($remark)
{
$value = $this->filter->sanitize($remark, ['trim', 'striptags']);
$length = kg_strlen($value);
if ($length > 255) {
throw new BadRequestException('app.remark_too_long');
}
return $value;
}
public function checkPublishStatus($status)
{
if (!in_array($status, [0, 1])) {
throw new BadRequestException('app.invalid_publish_status');
}
return $status;
}
}

View File

@ -126,11 +126,11 @@ class Consult extends Validator
}
}
public function checkIfDuplicated($question, $chapterId, $userId)
public function checkIfDuplicated($chapterId, $userId, $question)
{
$repo = new ConsultRepo();
$consult = $repo->findUserLastChapterConsult($chapterId, $userId);
$consult = $repo->findUserLastCourseConsult($chapterId, $userId);
if (!$consult) return;

27
app/Validators/Live.php Normal file
View File

@ -0,0 +1,27 @@
<?php
namespace App\Validators;
use App\Exceptions\BadRequest as BadRequestException;
class Live extends Validator
{
public function checkMessage($content)
{
$value = $this->filter->sanitize($content, ['trim', 'striptags']);
$length = kg_strlen($value);
if ($length < 1) {
throw new BadRequestException('live.msg_too_short');
}
if ($length > 255) {
throw new BadRequestException('live.msg_too_long');
}
return $value;
}
}

View File

@ -45,15 +45,4 @@ class Security extends Validator
}
}
public function checkApiSignature()
{
$validator = new ApiSecurity();
$result = $validator->check();
if (!$result) {
throw new BadRequestException('security.invalid_api_signature');
}
}
}

View File

@ -157,6 +157,16 @@ $config['cors']['enabled'] = true;
*/
$config['cors']['allow_origin'] = '*';
/**
* 允许跨域字段string|array
*/
$config['cors']['allow_headers'] = '*';
/**
* 允许跨域方法
*/
$config['cors']['allow_methods'] = ['GET', 'POST', 'OPTIONS'];
/**
* 限流开启
*/