@sewise-dev
2016-12-26T02:07:13.000000Z
字数 12751
阅读 605
VPaas
编号 | 版本 | 说明 | 作者 | 日期 |
---|---|---|---|---|
1 | v1.0 | 1.创建文档 | Seven | 2016-06-30 |
本框架基于Phalcon框架,对Phalcon框架进行了包装,使他可以自动通过配置来初始化Phalcon的系统组件
┌───────┬──── app 应用程序目录
│ │
│ ├──── Controllers 控制器程序目录
│ │
│ ├──── Models 数据库模型类目录
│ │
│ └──── Views 视图/模板文件目录
│
├──────────── config 配置文件目录
│
├──────────── docs 程序相关文档目录
│
├──────────── public web访问入口即index.php
│
├──────────── runtime 日志文件、文件缓存目录,要求可写,虽然以后日志可能不会写在这里,但框架本身的日志默认是在定个目录,所以需要可写权限
系统正式的配置文件请不要提交到版本库里,提交到版本库内的必须是配置文件的实例文件
nginx配置
try_files $uri $uri/ @rewrite;
location @rewrite {
rewrite ^/(.*)$ /index.php?_url=/$1;
}
支持纯``php``文件和``volt``格式的视图文件,如果需要使用smarty,请自行重载``Joy\Application``类中的``setView``方法。
Smarty的支持请使用``Phalcon\Mvc\View\Engine\Smarty``类;系统还支持``MustCache``和``Twig``模板引擎,你可以同时混用多种模板引擎。
注意:如果你同时混用多种模板引擎时,不同模板引擎的模板文件是不能够互相包含和继承的。
通过配置 ningx 域名 account.sewise.com 至本项目 wechat/public 目录
本项目的配置文件根据环境的不同设置环境变量,分别为:localhost(本地环境)、development(测试环境)、production(生产环境)
复制config/web.config.php 文件为 config/web.+对应的环境变量+.php,修改缓存配置至对应服务器
<?php
// 注意,本配置文件中所有关于组件名的设置均区分大小写
$runTimePath = ROOT_PATH . '/runtime';
return [
'basePath' => ROOT_PATH . '/app', // 应用程序 的根目录
'render' => 'json', //定义错误信息输出的页面,debug模式下此参数无效,如果为非debug模式,当设置为非json时会输出html;注意,请不要屏蔽此条配置
'runtimePath' => $runTimePath, // 运行时生成的文件的目录,主要是用于保存日志
'defaultNamespace' => 'Joy\Account', // 默认的命名空间
'errorPage' => null, // 错误页面;允许使用PHP代码;系统提供了两个变量$code,$message,前者表示当前的页面的状态码,后者表示系统输出的错误内容。你可以根据自己的实际需要来显示错误页面
'components' => [ // 设置系统需要加载的组件的属性
'router' => [],
'logger' => [ // 日志,允许同时开启多个日志
'stream' => [
'compress.zlib://' . $runTimePath . '/application.log.gz',
''
]
]
// 'database'=>[''],
// 'mongo'=>[],
,
'database' => [ // 数据库设置,可以设置多个数据库,每个数据库配置数组的键名为程序调用时使用的组件名
'db' => [
'adapter' => 'Mysql',
'host' => 'localhost',
'username' => 'DatabaseUser',
'password' => 'DatabasePassword',
'dbname' => 'DatabaseName',
'options' => [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
]
]
],
'cache' => [
// Msgpack暂时无法成功编译到php5.6,所以php5.6下暂时无法启用Msgpack支持
'frontend' => 'Json', // 为支持与其他语言数据交换,请使用Json和Msgpack;支持Json、Msgpack、Data(php serialized),Output(PHP输出),None(不处理),Base64,IgBinary
'backend' => [
[
'adapter' => 'Redis',
'host' => '127.0.0.1',
'port' => 6379
]
]
],
'metadata' => [
'adapter' => 'Memory',
// 支持 Memory、Memcache、Redis
// 公共参数:lifetime prefix
// Memcache参数:host(主机) port(端口)persistent(是否长连接)
// Redis参数:Redis(Redis链接url)
'metaDataDir' => $runTimePath . '/metadata/'
]
],
'application' => [
'controllersDir' => APP_PATH . '/controllers/',
'modelsDir' => APP_PATH . '/models/',
'views' => [
'dir' => APP_PATH . '/views/',
'compiledPath' => $runTimePath . '/views/',
'compiledExtension' => $runTimePath . '/views/',
'compiledSeparator' => '%%',
'compileAlways' => $runTimePath . '/views/'
],
'baseUri' => '/'
],
'modules' => []
];
<?php
/**
* 程序入口
*
* @author seven <seven@sewise.com>
* @date 2016-07-05
*/
$stage = getenv('APPLICATION_STAGE') ? getenv('APPLICATION_STAGE') : 'production';
if ($stage == 'production') {
defined('JOY_DEBUG') or define('JOY_DEBUG', false);
} else {
defined('JOY_DEBUG') or define('JOY_DEBUG', true);
}
defined('JOY_DB_DEBUG') or define('JOY_DB_DEBUG', false);
defined('ROOT_PATH') or define('ROOT_PATH', dirname(__DIR__));
defined('APP_PATH') or define('APP_PATH', ROOT_PATH . '/app');
include ROOT_PATH . '/../framework/Joy.php';
(new \Joy\Web\Application())->run();
<?php
namespace Joy\Web\Controllers;
/**
* 默认控制器目录的路由配置信息
*
* @author seven
* @creat_time 2016-07-05
*/
class Routes extends \Phalcon\Mvc\Router\Group
{
public function initialize($paths)
{
// Default paths(必要)
$this->setPaths(array(
'namespace' => 'Joy\Web\Controllers'
));
// $this->add('/?([a-zA-Z0-9_-]*)\/?([a-zA-Z0-9_]*)(/.*)*',array("controller"=>1,"action"=>2,"params"=>3));
//设置路由前缀
$this->setPrefix('/v1/');
//默认入口
$this->add('', [
'controller' => 'main',
'action' => 'index'
]);
//清缓存
$this->add('main/clearCache', array(
'controller' => 'main',
'action' => 'clearCache'
))->setName('main-clearCache');
/***********************************************************************/
/**
* 未登陆用户接口
*/
//用户注册API接口
$this->addPost('user/register', [
'controller' => 'normal',
'action' => 'register'
]);
//用户登录API接口
$this->addPost('user/auth', [
'controller' => 'normal',
'action' => 'login'
]);
//短信发送
$this->addPost('sms/send', [
'controller' => 'sms',
'action' => 'sendMsg'
]);
/***********************************************************************/
// $this->setPrefix('/v2/');
/**
* 已登陆用户信息操作
*/
//只获取基本账号信息
$this->add('user/accountInfo', [
'controller' => 'user',
'action' => 'accountInfo'
]);
//取得一个用户的资料
$this->add('user/userInfo', [
'controller' => 'user',
'action' => 'userInfo'
]);
//基本信息修改
$this->addPost('user/edit', [
'controller' => 'user',
'action' => 'editAccountInfo'
]);
//验证已登陆账号密码
$this->addPost('user/authPass', [
'controller' => 'user',
'action' => 'authPass'
]);
//重置密码
$this->addPost('user/reset', [
'controller' => 'user',
'action' => 'resetPass'
]);
//登录记录
$this->addPost('user/record', [
'controller' => 'user',
'action' => 'loginLog'
]);
//更改套餐
$this->addPost('user/rank', [
'controller' => 'user',
'action' => 'editRank'
]);
}
}
<?php
namespace Joy\Web\Controllers;
use Joy\Web\Models\Account;
use Joy\Web\Models\Tokens;
/**
1. 普通接口,处理用户的登录、注册行为等
2. * @author seven
3.
*/
class NormalController extends BaseController
{
/**
* 校验用户权限,根据控制器名和动作名来校验用户的权限
* $accessToken系统根据控制器名和动作名来自动生成
* ```
* $accessToken = substr(md5($module .
*
* $controller . $action), 0, 6);
* ```
*
* @see \Joy\Web\Controller::beforeExecuteRoute()
* @see \Joy\Web\Controller::checkAccess()
4.
列表项
---
*/
public function checkAccess($accessToken)
{
return true;
}
/**
* 用户登录并返回登录令牌;出错时会输出错误提示
*
* @param string $username
* @param string $password
* @return string | \Phalcon\Http\Response
*/
public function loginAction()
{
$user = $this->data;
// 查找用户资料
$account = Account::findFirst([
'mobile = :mobile:',
'bind' => [
'mobile' => $user['mobile']
]
]);
// 账号不存在
if ($account === false) {
return $this->sendError(402, 'Invalid user');
}
// 非免密码登陆,则需要检测密码
if ( ! $account->checkPassword($user['password'])) {
return $this->sendError(402, 'Invalid password');
}
$token = new Tokens();
$token->userId = $account->userId;
// 有效时间为24小时
$token->expire = date('Y-m-d H:i:s', time() + 24 * 3600);
if ($token->save() === false)
return $this->catchModelMessage($token);
// 更新最后登陆时间
$account->lastLogin = date('Y-m-d H:i:s');
$account->save();
// 返回结果
return [
'status' => 200,
'data' => [
'userId' => $account->userId,
'access_token' => $token->token,
'expire'=> $token->expire
]];
}
/**
* 保存一个用户
* ```
* {
* "mobile": "13632517742",
* "password": "b",
* }
* ```
* @return \Phalcon\Http\Response|multitype:number
*/
public function registerAction()
{
$user = $this->data;
$user['mobile'] =isset($user['mobile']) ? filter_var($user['mobile'], FILTER_SANITIZE_NUMBER_INT) : '';
$user['password'] = isset($user['password']) ? filter_var($user['password'], FILTER_SANITIZE_STRING) : '';
$account = new Account();
if($user['password']){
$account->password($user['password']);
$user['password'] = $account->password;
}
unset($user['userId']);
$this->db->begin();
$account->assign($user);
if($account->save() === false){
$this->db->rollback();
return $this->catchModelMessage($account);
}else{
$this->db->commit();
return [
'status' => 200,
'data' => [
'userId' => $account->userId,
]];
}
}
}
<?php
namespace Joy\Web\Models;
use Phalcon\Mvc\Model\Validator\Uniqueness;
use Phalcon\Mvc\Model\Message;
/**
* 用户资料表,保存帐号相关的资料。仅用于注册用户的资料显示,发送验证短信、邮件等
* PS:请面向对象编程
* @author seven
*/
class Account extends \Phalcon\Mvc\Model
{
/**
* 数据的正常标记
* @var string
*/
const NOT_DELETE = 'N';
/**
* 数据的删除标记
* @var string
*/
const DELETE = 'D';
/**
* 用户状态: 正常状态
* @var int
*/
const STATUS_NORMAL = 1;
/**
* 用户状态:禁止状态
* @var int
*/
const STATUS_FORBIDDEN = 2;
/**
* 普通用户
* @var int
*/
const USER_NORMAL = 1;
/**
* 管理用户
* @var int
*/
const USER_MANAGER = 2;
/**
* 用户帐号表的唯一ID
* @Primary
* @Identity
* @Column(type="integer",nullable=true,column="uid")
* @var integer
*/
public $userId;
/**
* 用户手机号
* @Column(type="integer",nullable=true,length="11",column="mobile")
* @var string
*/
public $mobile;
/**
* 用户密码,使用crypt函数进行加密
* @Column(type="string",nullable=true,length="64",column="password"))
* @see AccountInfo::checkPassword 或使用以下方法验证:
* ```
* if(crypt($password,$passwordHash)===$passwordHash)
* {
* return true;
* }
* ```
* 注意:请不要直接通过此属性来设置密码;直接调用此属性设置密码将不会进行加密操作。
* @see \Joy\Account\Models\Account::password
* @var string
*/
public $password;
/**
* 密码干扰码
* @Column(type="string",nullable=true,length="10",column="salt")
* @var string
*/
public $salt;
/**
* 用户类型;管理用户,普通用户
*
* @Column(type="integer",nullable=true,column="user_type")
* @var int
*/
public $userType;
/**
* 用户帐号建立时间
*
* @Column(type="datetime",nullable=true, column="creation_date")
* @var string
*/
public $creationDate;
/**
* 用户的状态,如待激活,禁用
*
* @Column(type="integer",nullable=false, column="account_status")
*
* @var int
*/
public $accountStatus;
/**
* 最后登录时间
*
* @Column(type="datetime",nullable=true, column="last_login")
* @var string
*/
public $lastLogin;
/**
* 软删除标记,N表示正常状态,D表示已经删除
*
* @Column(type="char",nullable=false,length="1",column="is_delete")
* @var string
*/
public $isDelete;
/**
* 原始密码
*/
private $originPassword = null;
/**
* 数据表表名定义
* @return string
*/
public function getSource()
{
return 'account';
}
/**
* 初始化软删除标记
* 初始化数据表外键关联
*/
public function initialize()
{
// $this->belongsTo ( 'userId', '\Joy\Account\Models\AccountInfo', 'userId', [
// 'alias' => 'AccountInfo'
// ] );
$this->addBehavior(new \Phalcon\Mvc\Model\Behavior\Timestampable([
'beforeCreate' => [
'field' => [
'creationDate',
'lastLogin'
],
'format' => 'Y-m-d H:i:s'
],
'beforeUpdate' => [
'field' => 'lastLogin',
'format' => 'Y-m-d H:i:s'
]
]));
$this->addBehavior(new \Phalcon\Mvc\Model\Behavior\SoftDelete([
'field' => 'isDelete',
'value' => self::DELETE
]));
$this->keepSnapshots ( true );
$this->useDynamicUpdate ( true );
}
/**
* 设置用户密码;执行此操作后,系统会根据提供的密码使用bcrypt算法进行加密。
*
* @see \Phalcon\Security::hash
* @link https://github.com/phalcon/cphalcon/blob/2.0.0/phalcon/security.zep#L139
* @param string $password
*/
public function password($password)
{
$this->originPassword = $password;
$this->salt = $this->genSalt();
$this->password = $this->encrypt($password, $this->salt);
//$this->password = \Joy::$di->get('security')->hash($password);
}
/**
* 检测密码的有效性
*
* @param string $password
* @return boolean
*/
public function checkPassword($password)
{
$password = $this->encrypt($password, $this->salt);
return $password === $this->password;
//return \Joy::$di->get('security')->checkHash($password, $this->password);
}
/**
* 对字符串进行加密处理
*
* @param string $str 预加密字符串
*/
private function encrypt($password, $salt)
{
return strtoupper(md5(strtoupper(md5($password . $salt))));
}
/**
* 生成随机干扰码
*
*/
private function genSalt()
{
$hash = md5(time().rand(1, 9999999));
return substr($hash, 0, 5);
}
/**
* 数据校验,使用预定义的规则对提交到数据库的数据进行校验
* 并对文本字段进行过滤处理
*
* @return boolean
*/
public function validation()
{
//手机号格式检测
if($this->mobile){
if(!preg_match ( '/^1[3|4|5|7|8]\d{9}$/', $this->mobile, $match )){
$message = new Message('手机号不正确', null, 'Error');
$this->appendMessage($message);
return false;
}
// 校验数据的唯一性
$this->validate(new Uniqueness(array(
"field" => [
"mobile"
],
'message' => '手机号已经存在'
)));
if($this->validationHasFailed() ==true){
return false;
}
}
// 验证用户类别参数
$this->validate(new \Phalcon\Mvc\Model\Validator\Inclusionin([
"field" => "userType",
"domain" => [
self::USER_NORMAL,
self::USER_MANAGER
],
'message' => '用户类型有误'
]));
if ($this->validationHasFailed() == true) {
return false;
}
// 验证删除状态
$this->validate(new \Phalcon\Mvc\Model\Validator\Inclusionin([
"field" => "isDelete",
"domain" => [
self::DELETE,
self::NOT_DELETE
],
'message' => '删除状态有误'
]));
if ($this->validationHasFailed() == true) {
return false;
}
// 验证未经过加密的密码的格式
if($this->originPassword){
$pattern = '/[\x{30A0}-\x{30FF}\x{3040}-\x{309F}\x{4E00}-\x{9FBF}]+/u';
if(preg_match ( $pattern, $this->originPassword, $match )){
$message = new Message('密码不能为中文', null, 'Error');
$this->appendMessage($message);
return false;
}
if(preg_match ( '/\s+/', $this->originPassword, $match )){
$message = new Message('密码不能含有空格', null, 'Error');
$this->appendMessage($message);
return false;
}
if (mb_strlen($this->originPassword, 'utf-8') != strlen($this->originPassword) || strlen($this->originPassword) < 6 || strlen($this->originPassword) > 20) {
$message = new Message('密码仅限于6-20位的英文字符', null, 'Error');
$this->appendMessage($message);
return false;
}
}
return true;
}
/**
* 在数据验证前初始化所有不允许为null的字段
*/
public function beforeValidationOnCreate()
{
// 设置删除状态
if($this->userType==null)
$this->userType = self::USER_NORMAL;
// 设置删除状态
$this->isDelete = self::NOT_DELETE;
//默认账号状态
$this->accountStatus = self::STATUS_NORMAL;
}
/**
* 添加前的额外数据校验
*
* @return boolean
*/
public function afterValidationOnCreate()
{
// 检测帐号是否已经存在
// 验证未经过加密的密码的格式
if($this->originPassword==null && $this->password){
$message = new Message('不允许直接设置密码', null, 'Error');
$this->appendMessage($message);
return false;
}
if($this->mobile==null){
$message = new Message('手机号不能为空', null, 'Error');
$this->appendMessage($message);
return false;
}
if($this->password==null){
$message = new Message('密码不能为空', null, 'Error');
$this->appendMessage($message);
return false;
}
}
}
基于RESTful架构原则
public function todo()
{
$url = "http://account.sewise.com/v1/user/accountInfo";
$data = array('userId'=>22);
$time = time();
$accessHeaders = array('time'=>$time, 'token'=>'09e05eafd740fc13116c962d1a6872e0412d1ecd','version'=>'V1');
try {
$ret = Request::post($url,$data)->addHeaders($accessHeaders)->send();
} catch (Exception $e) {
return new Error('访问接口失败:'.$e->getMessage());
}
$ret = json_decode($ret,true);
if(isset($ret['error'])) {
return false;
}
print_r($ret);exit;
}
HTTP请求状态返回格式
返回字段 | 字段类型 | 说明 |
---|---|---|
status | Integer | 请求返回码(详细请见状态表) |
message | Array | 错误描述(请求错误时返回) |
data | Array | 返回结果(请求成功时返回) |
错误样例
{
"status": 40013,
"message": {"0":"invalid appid"}
}
成功样例
{
"status": 200,
"data": {"access_token":"ACCESS_TOKEN","expires_in":7200}
}