1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-07-21 23:46:39 +08:00

整理索引和部分字段顺序

This commit is contained in:
xiaochong0302 2020-08-23 19:39:31 +08:00
parent 56c1c851fe
commit 335adbb050
33 changed files with 1332 additions and 1991 deletions

View File

@ -4,9 +4,8 @@
<div class="layui-tab layui-tab-brief">
<ul class="layui-tab-title kg-tab-title">
<li class="layui-this">基本信息</li>
<li class="layui-this">基本设置</li>
<li>在线客服</li>
<li>聊天机器人</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
@ -15,9 +14,6 @@
<div class="layui-tab-item">
{{ partial('setting/im_cs') }}
</div>
<div class="layui-tab-item">
{{ partial('setting/im_robot') }}
</div>
</div>
</div>

View File

@ -1,34 +0,0 @@
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.setting.im'}) }}">
<div class="layui-form-item">
<label class="layui-form-label">开启服务</label>
<div class="layui-input-block">
<input type="radio" name="robot_enabled" value="1" title="是" lay-filter="status" {% if im.robot_enabled == 1 %}checked{% endif %}>
<input type="radio" name="robot_enabled" value="0" title="否" lay-filter="status" {% if im.robot_enabled == 0 %}checked{% endif %}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">客服1用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="cs_user1_id" value="{{ im.cs_user1_id }}" layui-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">客服2用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="cs_user2_id" value="{{ im.cs_user2_id }}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">客服3用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="cs_user3_id" value="{{ im.cs_user3_id }}">
</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

@ -24,7 +24,7 @@ class UserController extends Controller
$user = $service->handle($id);
$this->seo->prependTitle("{$user['name']}的个人主页");
$this->seo->prependTitle([$user['name'], '个人主页']);
$this->view->setVar('user', $user);
}

View File

@ -2,7 +2,7 @@
{% block content %}
{% set chapter_full_url = full_url({'for':'desktop.chapter.show','id':chapter.id}) %}
{% set full_chapter_url = full_url({'for':'desktop.chapter.show','id':chapter.id}) %}
{% set course_url = url({'for':'desktop.course.show','id':chapter.course.id}) %}
{% set learning_url = url({'for':'desktop.chapter.learning','id':chapter.id}) %}
{% set live_chats_url = url({'for':'desktop.live.chats','id':chapter.id}) %}
@ -10,7 +10,7 @@
{% set send_msg_url = url({'for':'desktop.live.send_msg','id':chapter.id}) %}
{% set bind_user_url = url({'for':'desktop.live.bind_user','id':chapter.id}) %}
{% set like_url = url({'for':'desktop.chapter.like','id':chapter.id}) %}
{% set qrcode_url = url({'for':'desktop.qrcode'},{'text':chapter_full_url}) %}
{% set qrcode_url = url({'for':'desktop.qrcode'},{'text':full_chapter_url}) %}
<div class="breadcrumb">
<span class="layui-breadcrumb">
@ -62,7 +62,7 @@
<div class="layui-hide">
<input type="hidden" name="share.title" value="{{ chapter.course.title }}">
<input type="hidden" name="share.pic" value="{{ chapter.course.cover }}">
<input type="hidden" name="share.url" value="{{ chapter_full_url }}">
<input type="hidden" name="share.url" value="{{ full_chapter_url }}">
<input type="hidden" name="share.qrcode" value="{{ qrcode_url }}">
</div>

View File

@ -2,12 +2,12 @@
{% block content %}
{% set chapter_full_url = full_url({'for':'desktop.chapter.show','id':chapter.id}) %}
{% set full_chapter_url = full_url({'for':'desktop.chapter.show','id':chapter.id}) %}
{% set course_url = url({'for':'desktop.course.show','id':chapter.course.id}) %}
{% set learning_url = url({'for':'desktop.chapter.learning','id':chapter.id}) %}
{% set like_url = url({'for':'desktop.chapter.like','id':chapter.id}) %}
{% set consult_url = url({'for':'desktop.consult.add'},{'chapter_id':chapter.id}) %}
{% set qrcode_url = url({'for':'desktop.qrcode'},{'text':chapter_full_url}) %}
{% set qrcode_url = url({'for':'desktop.qrcode'},{'text':full_chapter_url}) %}
<div class="breadcrumb">
<span class="layui-breadcrumb">
@ -43,7 +43,7 @@
<div class="layui-hide">
<input type="hidden" name="share.title" value="{{ chapter.course.title }}">
<input type="hidden" name="share.pic" value="{{ chapter.course.cover }}">
<input type="hidden" name="share.url" value="{{ chapter_full_url }}">
<input type="hidden" name="share.url" value="{{ full_chapter_url }}">
<input type="hidden" name="share.qrcode" value="{{ qrcode_url }}">
</div>

View File

@ -2,11 +2,11 @@
{% block content %}
{% set chapter_full_url = full_url({'for':'desktop.chapter.show','id':chapter.id}) %}
{% set full_chapter_url = full_url({'for':'desktop.chapter.show','id':chapter.id}) %}
{% set course_url = url({'for':'desktop.course.show','id':chapter.course.id}) %}
{% set learning_url = url({'for':'desktop.chapter.learning','id':chapter.id}) %}
{% set like_url = url({'for':'desktop.chapter.like','id':chapter.id}) %}
{% set qrcode_url = url({'for':'desktop.qrcode'},{'text':chapter_full_url}) %}
{% set qrcode_url = url({'for':'desktop.qrcode'},{'text':full_chapter_url}) %}
{% set consult_url = url({'for':'desktop.consult.add'},{'chapter_id':chapter.id}) %}
{% set liked_class = chapter.me.liked ? 'active' : '' %}
@ -48,7 +48,7 @@
<div class="layui-hide">
<input type="hidden" name="share.title" value="{{ chapter.course.title }}">
<input type="hidden" name="share.pic" value="{{ chapter.course.cover }}">
<input type="hidden" name="share.url" value="{{ chapter_full_url }}">
<input type="hidden" name="share.url" value="{{ full_chapter_url }}">
<input type="hidden" name="share.qrcode" value="{{ qrcode_url }}">
</div>

View File

@ -3,3 +3,11 @@
<span class="layui-badge layui-bg-orange vip">宾</span>
{% endif %}
{%- endmacro %}
{%- macro gender_info(value) %}
{% if value == 1 %}
<i class="layui-icon layui-icon-male"></i>
{% elseif value == 2 %}
<i class="layui-icon layui-icon-female"></i>
{% endif %}
{%- endmacro %}

View File

@ -3,15 +3,32 @@
{% block content %}
{{ partial('macros/course') }}
{{ partial('macros/user') }}
{% set vip_flag = user.vip ? '<i class="layui-icon layui-icon-diamond icon-vip"></i>' : '' %}
{% set full_user_url = full_url({'for':'desktop.user.show','id':user.id}) %}
{% set qrcode_url = url({'for':'desktop.qrcode'},{'text':full_user_url}) %}
<div class="breadcrumb">
<span class="layui-breadcrumb">
<a href="/">首页</a>
<a><cite>个人主页</cite></a>
<a><cite>{{ user.name }}</cite></a>
</span>
<span class="share">
<a href="javascript:" title="添加好友" class="apply-friend" data-id="{{ user.id }}" data-name="{{ user.name }}" data-avatar="{{ user.avatar }}"><i class="layui-icon layui-icon-user"></i></a>
<a href="javascript:" title="分享到微信"><i class="layui-icon layui-icon-login-wechat icon-wechat"></i></a>
<a href="javascript:" title="分享到QQ空间"><i class="layui-icon layui-icon-login-qq icon-qq"></i></a>
<a href="javascript:" title="分享到微博"><i class="layui-icon layui-icon-login-weibo icon-weibo"></i></a>
</span>
</div>
<div class="user-profile wrap clearfix">
{{ vip_info(user.vip) }}
<div class="avatar">
<img src="{{ user.avatar }}" alt="{{ user.name }}">
</div>
<div class="info">
<p><span class="name">{{ user.name }}</span> {{ vip_flag }}</p>
<p><span class="name">{{ user.name }}</span><span>{{ gender_info(user.gender) }}</span></p>
<p><span><i class="layui-icon layui-icon-location"></i></span><span>{{ user.location }}</span></p>
<p><span><i class="layui-icon layui-icon-time"></i></span><span>{{ date('Y-m-d H:i',user.active_time) }}</span></p>
</div>
@ -59,11 +76,19 @@
</div>
</div>
<div class="layui-hide">
<input type="hidden" name="share.title" value="{{ user.name }}">
<input type="hidden" name="share.pic" value="{{ user.avatar }}">
<input type="hidden" name="share.url" value="{{ full_user_url }}">
<input type="hidden" name="share.qrcode" value="{{ qrcode_url }}">
</div>
{% endblock %}
{% block include_js %}
{{ js_include('desktop/js/user.show.js') }}
{{ js_include('desktop/js/user.share.js') }}
{{ js_include('desktop/js/im.apply.js') }}
{% endblock %}

View File

@ -1,524 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: zhangyajun <448901948@qq.com>
// +----------------------------------------------------------------------
namespace App\Library;
use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use JsonSerializable;
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
{
protected $items = [];
public function __construct($items = [])
{
$this->items = $this->convertToArray($items);
}
public static function make($items = [])
{
return new static($items);
}
public function isEmpty()
{
return empty($this->items);
}
public function toArray()
{
return array_map(function ($value) {
return ($value instanceof self) ? $value->toArray() : $value;
}, $this->items);
}
public function all()
{
return $this->items;
}
/**
* 合并数组
*
* @param mixed $items
* @return static
*/
public function merge($items)
{
return new static(array_merge($this->items, $this->convertToArray($items)));
}
/**
* 交换数组中的键和值
*
* @return static
*/
public function flip()
{
return new static(array_flip($this->items));
}
/**
* 按指定键整理数据
*
* @param mixed $items 数据
* @param string $indexKey 键名
* @return array
*/
public function dictionary($items = null, &$indexKey = null)
{
if ($items instanceof self) {
$items = $items->all();
}
$items = is_null($items) ? $this->items : $items;
if (isset($indexKey) && is_string($indexKey)) {
return array_column($items, null, $indexKey);
}
return $items;
}
/**
* 比较数组,返回差集
*
* @param mixed $items 数据
* @param string $indexKey 指定比较的键名
* @return static
*/
public function diff($items, $indexKey = null)
{
if ($this->isEmpty() || is_scalar($this->items[0])) {
return new static(array_diff($this->items, $this->convertToArray($items)));
}
$diff = [];
$dictionary = $this->dictionary($items, $indexKey);
if (is_string($indexKey)) {
foreach ($this->items as $item) {
if (!isset($dictionary[$item[$indexKey]])) {
$diff[] = $item;
}
}
}
return new static($diff);
}
/**
* 比较数组,返回交集
*
* @param mixed $items 数据
* @param string $indexKey 指定比较的键名
* @return static
*/
public function intersect($items, $indexKey = null)
{
if ($this->isEmpty() || is_scalar($this->items[0])) {
return new static(array_diff($this->items, $this->convertToArray($items)));
}
$intersect = [];
$dictionary = $this->dictionary($items, $indexKey);
if (is_string($indexKey)) {
foreach ($this->items as $item) {
if (isset($dictionary[$item[$indexKey]])) {
$intersect[] = $item;
}
}
}
return new static($intersect);
}
/**
* 返回数组中所有的键名
*
* @return array
*/
public function keys()
{
$current = current($this->items);
if (is_scalar($current)) {
$array = $this->items;
} elseif (is_array($current)) {
$array = $current;
} else {
$array = $current->toArray();
}
return array_keys($array);
}
/**
* 删除数组的最后一个元素(出栈)
*
* @return mixed
*/
public function pop()
{
return array_pop($this->items);
}
/**
* 通过使用用户自定义函数,以字符串返回数组
*
* @param callable $callback
* @param mixed $initial
* @return mixed
*/
public function reduce(callable $callback, $initial = null)
{
return array_reduce($this->items, $callback, $initial);
}
/**
* 以相反的顺序返回数组。
*
* @return static
*/
public function reverse()
{
return new static(array_reverse($this->items));
}
/**
* 删除数组中首个元素,并返回被删除元素的值
*
* @return mixed
*/
public function shift()
{
return array_shift($this->items);
}
/**
* 在数组结尾插入一个元素
*
* @param mixed $value
* @param mixed $key
* @return void
*/
public function push($value, $key = null)
{
if (is_null($key)) {
$this->items[] = $value;
} else {
$this->items[$key] = $value;
}
}
/**
* 把一个数组分割为新的数组块.
*
* @param int $size
* @param bool $preserveKeys
* @return static
*/
public function chunk($size, $preserveKeys = false)
{
$chunks = [];
foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
$chunks[] = new static($chunk);
}
return new static($chunks);
}
/**
* 在数组开头插入一个元素
*
* @param mixed $value
* @param mixed $key
* @return void
*/
public function unshift($value, $key = null)
{
if (is_null($key)) {
array_unshift($this->items, $value);
} else {
$this->items = [$key => $value] + $this->items;
}
}
/**
* 给每个元素执行个回调
*
* @param callable $callback
* @return $this
*/
public function each(callable $callback)
{
foreach ($this->items as $key => $item) {
$result = $callback($item, $key);
if (false === $result) {
break;
} elseif (!is_object($item)) {
$this->items[$key] = $result;
}
}
return $this;
}
/**
* 用回调函数处理数组中的元素
*
* @param callable|null $callback
* @return static
*/
public function map(callable $callback)
{
return new static(array_map($callback, $this->items));
}
/**
* 用回调函数过滤数组中的元素
*
* @param callable|null $callback
* @return static
*/
public function filter(callable $callback = null)
{
if ($callback) {
return new static(array_filter($this->items, $callback));
}
return new static(array_filter($this->items));
}
/**
* 根据字段条件过滤数组中的元素
*
* @param string $field 字段名
* @param mixed $operator 操作符
* @param mixed $value 数据
* @return static
*/
public function where($field, $operator, $value = null)
{
if (is_null($value)) {
$value = $operator;
$operator = '=';
}
return $this->filter(function ($data) use ($field, $operator, $value) {
if (strpos($field, '.')) {
list($field, $relation) = explode('.', $field);
$result = isset($data[$field][$relation]) ? $data[$field][$relation] : null;
} else {
$result = isset($data[$field]) ? $data[$field] : null;
}
switch ($operator) {
case '===':
return $result === $value;
case '!==':
return $result !== $value;
case '!=':
case '<>':
return $result != $value;
case '>':
return $result > $value;
case '>=':
return $result >= $value;
case '<':
return $result < $value;
case '<=':
return $result <= $value;
case 'like':
return is_string($result) && false !== strpos($result, $value);
case 'not_like':
return is_string($result) && false === strpos($result, $value);
case 'in':
return is_scalar($result) && in_array($result, $value, true);
case 'not_in':
return is_scalar($result) && !in_array($result, $value, true);
case 'between':
list($min, $max) = is_string($value) ? explode(',', $value) : $value;
return is_scalar($result) && $result >= $min && $result <= $max;
case 'not_between':
list($min, $max) = is_string($value) ? explode(',', $value) : $value;
return is_scalar($result) && $result > $max || $result < $min;
case '==':
case '=':
default:
return $result == $value;
}
});
}
/**
* 返回数据中指定的一列
*
* @param mixed $columnKey 键名
* @param mixed $indexKey 作为索引值的列
* @return array
*/
public function column($columnKey, $indexKey = null)
{
return array_column($this->items, $columnKey, $indexKey);
}
/**
* 对数组排序
*
* @access public
* @param callable|null $callback
* @return static
*/
public function sort(callable $callback = null)
{
$items = $this->items;
$callback = $callback ?: function ($a, $b) {
return $a == $b ? 0 : (($a < $b) ? -1 : 1);
};
uasort($items, $callback);
return new static($items);
}
/**
* 指定字段排序
*
* @param string $field 排序字段
* @param string $order 排序
* @param bool $intSort 是否为数字排序
* @return $this
*/
public function order($field, $order = null, $intSort = true)
{
return $this->sort(function ($a, $b) use ($field, $order, $intSort) {
$fieldA = isset($a[$field]) ? $a[$field] : null;
$fieldB = isset($b[$field]) ? $b[$field] : null;
if ($intSort) {
return 'desc' == strtolower($order) ? $fieldB >= $fieldA : $fieldA >= $fieldB;
} else {
return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB);
}
});
}
/**
* 将数组打乱
*
* @return static
*/
public function shuffle()
{
$items = $this->items;
shuffle($items);
return new static($items);
}
/**
* 截取数组
*
* @param int $offset
* @param int $length
* @param bool $preserveKeys
* @return static
*/
public function slice($offset, $length = null, $preserveKeys = false)
{
return new static(array_slice($this->items, $offset, $length, $preserveKeys));
}
public function offsetExists($offset)
{
return array_key_exists($offset, $this->items);
}
public function offsetGet($offset)
{
return $this->items[$offset];
}
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->items[] = $value;
} else {
$this->items[$offset] = $value;
}
}
public function offsetUnset($offset)
{
unset($this->items[$offset]);
}
public function count()
{
return count($this->items);
}
public function getIterator()
{
return new ArrayIterator($this->items);
}
public function jsonSerialize()
{
return $this->toArray();
}
/**
* 转换当前数据集为JSON字符串
*
* @param integer $options json参数
* @return string
*/
public function toJson($options = JSON_UNESCAPED_UNICODE)
{
return json_encode($this->toArray(), $options);
}
public function __toString()
{
return $this->toJson();
}
/**
* 转换成数组
*
* @param mixed $items
* @return array
*/
protected function convertToArray($items)
{
if ($items instanceof self) {
return $items->all();
}
return (array)$items;
}
}

View File

@ -1,72 +0,0 @@
<?php
namespace App\Models;
class AccessToken extends Model
{
/**
* 主键编号
*
* @var string
*/
public $id;
/**
* 用户编号
*
* @var int
*/
public $user_id;
/**
* 回收标识
*
* @var int
*/
public $revoked;
/**
* 过期时间
*
* @var int
*/
public $expiry_time;
/**
* 创建时间
*
* @var int
*/
public $create_time;
/**
* 更新时间
*
* @var int
*/
public $update_time;
public function getSource(): string
{
return 'kg_access_token';
}
public function beforeCreate()
{
$this->id = $this->getRandId($this->user_id);
$this->create_time = time();
}
public function beforeUpdate()
{
$this->update_time = time();
}
protected function getRandId($userId, $prefix = 'AT')
{
return md5("{$prefix}-{$userId}" . time() . rand(1000, 9999));
}
}

View File

@ -8,9 +8,9 @@ class Area extends Model
/**
* 区域类型
*/
const TYPE_PROVINCE = 'province';
const TYPE_CITY = 'city';
const TYPE_COUNTY = 'county';
const TYPE_PROVINCE = 1; // 省
const TYPE_CITY = 2; // 市
const TYPE_COUNTY = 3; // 区
/**
* 主键
@ -22,7 +22,7 @@ class Area extends Model
/**
* 类型
*
* @var string
* @var int
*/
public $type;

View File

@ -111,6 +111,20 @@ class Chapter extends Model
*/
public $attrs;
/**
* 发布标识
*
* @var int
*/
public $published;
/**
* 删除标识
*
* @var int
*/
public $deleted;
/**
* 课时数
*
@ -139,20 +153,6 @@ class Chapter extends Model
*/
public $like_count;
/**
* 发布标识
*
* @var int
*/
public $published;
/**
* 删除标识
*
* @var int
*/
public $deleted;
/**
* 创建时间
*

View File

@ -56,13 +56,6 @@ class Consult extends Model
*/
public $answer;
/**
* 赞成数
*
* @var int
*/
public $like_count;
/**
* 优先级
*
@ -91,6 +84,13 @@ class Consult extends Model
*/
public $deleted;
/**
* 赞成数
*
* @var int
*/
public $like_count;
/**
* 回复时间
*

View File

@ -165,6 +165,20 @@ class Course extends Model
*/
public $attrs;
/**
* 发布标识
*
* @var int
*/
public $published;
/**
* 删除标识
*
* @var int
*/
public $deleted;
/**
* 学员数
*
@ -207,20 +221,6 @@ class Course extends Model
*/
public $favorite_count;
/**
* 发布标识
*
* @var int
*/
public $published;
/**
* 删除标识
*
* @var int
*/
public $deleted;
/**
* 创建时间
*

View File

@ -69,6 +69,13 @@ class Learning extends Model
*/
public $position;
/**
* 删除标识
*
* @var int
*/
public $deleted;
/**
* 客户端类型
*
@ -83,13 +90,6 @@ class Learning extends Model
*/
public $client_ip;
/**
* 删除标识
*
* @var int
*/
public $deleted;
/**
* 活跃时间
*

View File

@ -166,8 +166,6 @@ class Order extends Model
if (is_array($this->item_info)) {
$this->item_info = kg_json_encode($this->item_info);
} else {
$this->item_info = ''; // text类型不会自动填充默认值
}
}

View File

@ -1,72 +0,0 @@
<?php
namespace App\Models;
class RefreshToken extends Model
{
/**
* 主键编号
*
* @var string
*/
public $id;
/**
* 用户编号
*
* @var int
*/
public $user_id;
/**
* 回收标识
*
* @var int
*/
public $revoked;
/**
* 过期时间
*
* @var int
*/
public $expiry_time;
/**
* 创建时间
*
* @var int
*/
public $create_time;
/**
* 更新时间
*
* @var int
*/
public $update_time;
public function getSource(): string
{
return 'kg_refresh_token';
}
public function beforeCreate()
{
$this->id = $this->getRandId($this->user_id);
$this->create_time = time();
}
public function beforeUpdate()
{
$this->update_time = time();
}
protected function getRandId($userId, $prefix = 'RT')
{
return md5("{$prefix}-{$userId}" . time() . rand(1000, 9999));
}
}

View File

@ -70,13 +70,6 @@ class Review extends Model
*/
public $rating3;
/**
* 点赞数量
*
* @var int
*/
public $like_count;
/**
* 匿名标识
*
@ -98,6 +91,13 @@ class Review extends Model
*/
public $deleted;
/**
* 点赞数量
*
* @var int
*/
public $like_count;
/**
* 创建时间
*

View File

@ -102,8 +102,6 @@ class Task extends Model
if (!empty($this->item_info)) {
$this->item_info = kg_json_encode($this->item_info);
} else {
$this->item_info = ''; // text类型不会自动填充默认值
}
}

View File

@ -1,45 +0,0 @@
<?php
namespace App\Repos;
use App\Models\AccessToken as AccessTokenModel;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class AccessToken extends Repository
{
/**
* @param string $id
* @return AccessTokenModel|Model|bool
*/
public function findById($id)
{
return AccessTokenModel::findFirst([
'conditions' => 'id = :id:',
'bind' => ['id' => $id],
]);
}
/**
* @param int $userId
* @return ResultsetInterface|Resultset|AccessTokenModel[]
*/
public function findByUserId($userId)
{
return AccessTokenModel::query()
->where('user_id = :user_id:', ['user_id' => $userId])
->andWhere('deleted = 0')
->execute();
}
public function countByUserId($userId)
{
return AccessTokenModel::count([
'conditions' => 'user_id = :user_id: AND deleted = 0',
'bind' => ['user_id' => $userId],
]);
}
}

View File

@ -1,45 +0,0 @@
<?php
namespace App\Repos;
use App\Models\RefreshToken as RefreshTokenModel;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class RefreshToken extends Repository
{
/**
* @param string $id
* @return RefreshTokenModel|Model|bool
*/
public function findById($id)
{
return RefreshTokenModel::findFirst([
'conditions' => 'id = :id:',
'bind' => ['id' => $id],
]);
}
/**
* @param int $userId
* @return ResultsetInterface|Resultset|RefreshTokenModel[]
*/
public function findByUserId($userId)
{
return RefreshTokenModel::query()
->where('user_id = :user_id:', ['user_id' => $userId])
->andWhere('deleted = 0')
->execute();
}
public function countByUserId($userId)
{
return (int)RefreshTokenModel::count([
'conditions' => 'user_id = :user_id: AND deleted = 0',
'bind' => ['user_id' => $userId],
]);
}
}

View File

@ -9,9 +9,6 @@ abstract class Auth extends Service
abstract function saveAuthInfo(UserModel $user);
/**
* @return array|null
*/
abstract function getAuthInfo();
abstract function clearAuthInfo();

View File

@ -2,56 +2,41 @@
namespace App\Services\Auth;
use App\Library\Cache\Backend\Redis as RedisCache;
use App\Models\AccessToken as AccessTokenModel;
use App\Models\RefreshToken as RefreshTokenModel;
use App\Models\User as UserModel;
use App\Services\Auth as AuthService;
use Lcobucci\JWT\Builder as JwtBuilder;
use Lcobucci\JWT\Parser as JwtParser;
use Lcobucci\JWT\Signer\Hmac\Sha256 as JwtSingerSha256;
use Lcobucci\JWT\Signer\Key as JwtSingerKey;
use Lcobucci\JWT\ValidationData as JwtValidationData;
class Api extends AuthService
{
public function saveAuthInfo(UserModel $user)
{
$config = $this->getDI()->get('config');
$builder = new JwtBuilder();
$accessToken = new AccessTokenModel();
$accessToken->user_id = $user->id;
$accessToken->expiry_time = time() + $config->access_token->lifetime;
$accessToken->create();
$config = $this->getConfig();
$refreshToken = new RefreshTokenModel();
$refreshToken->user_id = $user->id;
$refreshToken->expiry_time = time() + $config->refresh_token->lifetime;
$refreshToken->create();
$expireTime = time() + $config->jwt->lifetime;
$authInfo = [
'id' => $user->id,
'name' => $user->name,
];
$builder->expiresAt($expireTime);
$builder->withClaim('user_id', $user->id);
$builder->withClaim('user_name', $user->name);
$cache = $this->getCache();
$singer = new JwtSingerSha256();
$key = $this->getCacheKey($accessToken->id);
$key = new JwtSingerKey($config->jwt->key);
$cache->save($key, $authInfo, $config->access_token->lifetime);
$token = $builder->getToken($singer, $key);
return [
'access_token' => $accessToken->id,
'refresh_token' => $refreshToken->id,
'expiry_time' => $accessToken->expiry_time,
];
return $token->__toString();
}
public function clearAuthInfo()
{
$authToken = $this->getAuthToken();
$cache = $this->getCache();
$key = $this->getCacheKey($authToken);
$cache->delete($key);
}
public function getAuthInfo()
@ -60,31 +45,40 @@ class Api extends AuthService
if (!$authToken) return null;
$cache = $this->getCache();
$config = $this->getConfig();
$key = $this->getCacheKey($authToken);
$parser = new JWTParser();
$authInfo = $cache->get($key);
$token = $parser->parse($authToken);
return $authInfo ?: null;
}
$data = new JWTValidationData(time(), $config->jwt->leeway);
/**
* @return RedisCache
*/
protected function getCache()
{
return $this->getDI()->get('cache');
if (!$token->validate($data)) {
return null;
}
$singer = new JwtSingerSha256();
if (!$token->verify($singer, $config->jwt->key)) {
return null;
}
return [
'id' => $token->getClaim('user_id'),
'name' => $token->getClaim('user_name'),
];
}
protected function getAuthToken()
{
return $this->request->getHeader('Authorization');
$authorization = $this->request->getHeader('Authorization');
return trim(str_ireplace('Bearer', '', $authorization));
}
protected function getCacheKey($token)
protected function getConfig()
{
return "access_token:{$token}";
return $this->getDI()->get('config');
}
}

View File

@ -58,7 +58,6 @@ trait Auth
$user->id = 0;
$user->name = 'guest';
$user->avatar = kg_ci_avatar_img_url(null);
return $user;
}

View File

@ -19,7 +19,8 @@
"hightman/xunsearch": "^1.4.14",
"aferrandini/phpqrcode": "1.0.1",
"xiaochong0302/ip2region": "^1.0",
"robmorgan/phinx": "^0.12"
"robmorgan/phinx": "^0.12",
"lcobucci/jwt": "^3.3"
},
"require-dev": {
"phalcon/ide-stubs": "^3.4.3",

2129
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -103,14 +103,19 @@ $config['session']['db'] = 0;
$config['session']['lifetime'] = 24 * 3600;
/**
* 访问令牌有效期(秒)
* 加密密钥
*/
$config['access_token']['lifetime'] = 2 * 3600;
$config['jwt']['key'] = 'fu6ckEc8pv8k5K7m';
/**
* 刷新令牌有效期(秒)
* 有效期(秒)
*/
$config['refresh_token']['lifetime'] = 30 * 86400;
$config['jwt']['lifetime'] = 7 * 86400;
/**
* 回旋时间(秒)
*/
$config['jwt']['leeway'] = 30;
/**
* 限流开启

View File

@ -61,11 +61,15 @@
.kg-login-copyright {
margin-top: -80px;
color: #999;
color: #666;
font-size: 12px;
text-align: center;
}
.kg-login-copyright a {
color: #666;
}
.kg-input-inline {
float: left;
width: 250px;

View File

@ -1206,12 +1206,18 @@ body {
border-radius: 100px;
}
.user-profile .icon-vip {
color: orange;
.user-profile .vip {
position: absolute;
top: 10px;
left: 10px;
height: 16px;
padding: 0 3px;
font-size: 10px;
}
.user-profile .info {
float: left;
padding-top: 10px;
}
.user-profile .info h3 {

View File

@ -1,7 +1,7 @@
layui.use(['jquery', 'layer'], function () {
layui.use(['jquery', 'helper'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var helper = layui.helper;
var myShare = {
title: $('input[name="share.title"]').val(),
@ -11,40 +11,17 @@ layui.use(['jquery', 'layer'], function () {
};
$('.icon-wechat').on('click', function () {
var content = '<div class="qrcode"><img src="' + myShare.qrcode + '" alt="分享到微信"></div>';
layer.open({
type: 1,
title: false,
closeBtn: 0,
shadeClose: true,
content: content
});
helper.wechatShare(myShare.qrcode);
});
$('.icon-qq').on('click', function () {
var title = '推荐一门好课:' + myShare.title + ',快来和我一起学习吧!';
qqShare(title, myShare.url, myShare.pic);
helper.qqShare(title, myShare.url, myShare.pic);
});
$('.icon-weibo').on('click', function () {
var title = '推荐一门好课:' + myShare.title + ',快来和我一起学习吧!';
weiboShare(title, myShare.url, myShare.pic);
helper.weiboShare(title, myShare.url, myShare.pic);
});
function qqShare(title, url, pic) {
var shareUrl = 'http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?';
shareUrl += 'title=' + encodeURIComponent(title || document.title);
shareUrl += '&url=' + encodeURIComponent(url || document.location);
shareUrl += '&pics=' + pic;
window.open(shareUrl, '_blank');
}
function weiboShare(title, url, pic) {
var shareUrl = 'http://v.t.sina.com.cn/share/share.php?';
shareUrl += 'title=' + encodeURIComponent(title || document.title);
shareUrl += '&url=' + encodeURIComponent(url || document.location);
shareUrl += '&pic=' + encodeURIComponent(pic || '');
window.open(shareUrl, '_blank');
}
});

View File

@ -0,0 +1,27 @@
layui.use(['jquery', 'helper'], function () {
var $ = layui.jquery;
var helper = layui.helper;
var myShare = {
title: $('input[name="share.title"]').val(),
pic: $('input[name="share.pic"]').val(),
url: $('input[name="share.url"]').val(),
qrcode: $('input[name="share.qrcode"]').val()
};
$('.icon-wechat').on('click', function () {
helper.wechatShare(myShare.qrcode);
});
$('.icon-qq').on('click', function () {
var title = '推荐一个有趣的朋友:' + myShare.title + '快来和Ta一起学习吧';
helper.qqShare(title, myShare.url, myShare.pic);
});
$('.icon-weibo').on('click', function () {
var title = '推荐一个有趣的朋友:' + myShare.title + '快来和Ta一起学习吧';
helper.weiboShare(title, myShare.url, myShare.pic);
});
});

View File

@ -6,6 +6,12 @@ layui.define(['jquery', 'layer'], function (exports) {
var helper = {};
helper.getRequestId = function () {
var id = Date.now().toString(36);
id += Math.random().toString(36).substr(3);
return id;
};
helper.ajaxLoadHtml = function (url, target) {
var $target = $('#' + target);
var html = '<div class="loading"><i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i></div>';
@ -32,10 +38,31 @@ layui.define(['jquery', 'layer'], function (exports) {
});
};
helper.getRequestId = function () {
var id = Date.now().toString(36);
id += Math.random().toString(36).substr(3);
return id;
helper.wechatShare = function (qrcode) {
var content = '<div class="qrcode"><img src="' + qrcode + '" alt="分享到微信"></div>';
layer.open({
type: 1,
title: false,
closeBtn: 0,
shadeClose: true,
content: content
});
};
helper.qqShare = function (title, url, pic) {
var shareUrl = 'http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?';
shareUrl += 'title=' + encodeURIComponent(title || document.title);
shareUrl += '&url=' + encodeURIComponent(url || document.location);
shareUrl += '&pics=' + pic;
window.open(shareUrl, '_blank');
};
helper.weiboShare = function (title, url, pic) {
var shareUrl = 'http://v.t.sina.com.cn/share/share.php?';
shareUrl += 'title=' + encodeURIComponent(title || document.title);
shareUrl += '&url=' + encodeURIComponent(url || document.location);
shareUrl += '&pic=' + encodeURIComponent(pic || '');
window.open(shareUrl, '_blank');
};
exports(MOD_NAME, helper);

View File

@ -36,7 +36,7 @@ layui.define(['layer', 'form', 'laytpl'], function (exports) {
120000: '天津市',
130000: '河北省',
140000: '山西省',
150000: '内蒙古自治区',
150000: '内蒙古',
210000: '辽宁省',
220000: '吉林省',
230000: '黑龙江省',
@ -51,21 +51,21 @@ layui.define(['layer', 'form', 'laytpl'], function (exports) {
420000: '湖北省',
430000: '湖南省',
440000: '广东省',
450000: '广西壮族自治区',
450000: '广西',
460000: '海南省',
500000: '重庆市',
510000: '四川省',
520000: '贵州省',
530000: '云南省',
540000: '西藏自治区',
540000: '西藏',
610000: '陕西省',
620000: '甘肃省',
630000: '青海省',
640000: '宁夏回族自治区',
650000: '新疆维吾尔自治区',
710000: '台湾',
810000: '香港特别行政区',
820000: '澳门特别行政区',
640000: '宁夏',
650000: '新疆',
710000: '台湾',
810000: '香港',
820000: '澳门',
900000: '海外'
},
city_list: {