530 lines
16 KiB
PHP
530 lines
16 KiB
PHP
<?php
|
||
|
||
ini_set('display_errors', 'On');
|
||
error_reporting(E_ERROR);
|
||
/**
|
||
* Created by PhpStorm.
|
||
* User: xuxb
|
||
* Date: 2019/12/4
|
||
* Time: 19:35
|
||
*/
|
||
abstract class Wxapp extends HD_Controller{
|
||
const STATUS_NOR = 1;//状态正常
|
||
const STATUS_USED = 1;//被使用
|
||
const STATUS_DEL = -1;//删除
|
||
const STATUS_WAIT = 0;//待使用
|
||
|
||
const FOND_ZAN = 2;//点赞
|
||
const FOND_SHARE = 3;//分享
|
||
|
||
protected $log_file;//日志文件
|
||
protected $log_dir;//日志目录
|
||
protected $session;//登录状态密钥
|
||
|
||
protected $login_white;//登录白名单每个ct单独管控
|
||
protected $majia_white;//超级管理员披上马甲可操作权限
|
||
protected $check_status;//需校验用户正常请求列表,每个ct单独管控
|
||
protected $check_mobile;//需要校验手机号,每个ct单独管控
|
||
protected $check_headimg;//校验使用授权用户头像
|
||
|
||
protected $wx_config;//微信配置
|
||
protected $cf_title;
|
||
protected $cf_platform = 'wx_app';
|
||
protected $app_key;//应用key
|
||
protected $app_id;//应用id,hd_app.id
|
||
protected $redis_login;
|
||
protected $redis_mcode;//手机验证码
|
||
protected $redis_hxcode;//核销码
|
||
protected $model_app_user;
|
||
protected $u_entity;
|
||
protected $app_redis;
|
||
protected $ukey;
|
||
protected $env;//网络环境 d开发, t测试, p生产
|
||
protected $myuid;
|
||
protected $plugin_img = 'https://qs.haodian.cn/wechat_app';
|
||
|
||
private $inputs;
|
||
private $app;
|
||
private $city_id;
|
||
|
||
/**
|
||
* Wxapp constructor.
|
||
* @param $inputs (请求参数)
|
||
* @param string $app_key (应用key)
|
||
*/
|
||
function __construct($inputs, $app_key){
|
||
parent::__construct();
|
||
|
||
$this->app_key = $app_key;
|
||
$this->redis_login = "wxapp_{$app_key}_login_";
|
||
$this->redis_mcode = "wxapp_{$app_key}_mcode_";
|
||
$this->redis_hxcode = "wxapp_{$app_key}_hxcode_";
|
||
|
||
if (false !== strpos($_SERVER['HTTP_HOST'], 'dev')) { //dev 测试
|
||
$this->env = 'd';
|
||
} elseif (false !== strpos($_SERVER['HTTP_HOST'], 'test')) {//test 测试
|
||
$this->env = 't';
|
||
} else { // 正式
|
||
$this->env = 'p';
|
||
}
|
||
|
||
//app配置
|
||
$config = $this->get_config($app_key);
|
||
$this->wx_config = $config['wx'];
|
||
$this->cf_title = $config['name'];
|
||
$this->app_id = $config['app_id'];
|
||
$this->model_app_user = $config['model']['user_model'];//应用用户model
|
||
|
||
//根据app_id重载model
|
||
$this->load->rebuild_model($this->app_id);
|
||
|
||
//日志文件
|
||
$class_name = lcfirst(get_class($this));//调用类的名称,子类或者当前类名称
|
||
|
||
$this->log_file = "wxapp_{$app_key}_{$class_name}.log";
|
||
$this->log_dir = "wxapp_{$app_key}_{$class_name}";
|
||
|
||
//设置input
|
||
$this->set_input($inputs);
|
||
|
||
//加载library
|
||
$this->load->library('entity/user_entity');
|
||
$this->load->library('hd_exception');
|
||
|
||
$this->u_entity = new User_entity($this->app_id);
|
||
$this->app_redis = &load_cache('redis');
|
||
|
||
//加载model
|
||
if($this->model_app_user){
|
||
$this->load->model($this->model_app_user, 'app_user_model');
|
||
}
|
||
|
||
//获取session
|
||
$this->fetch_session();
|
||
}
|
||
|
||
/**
|
||
* 获取参数
|
||
* @param string $key
|
||
* @return array|mixed
|
||
*/
|
||
function input_param($key = ''){
|
||
if($key){
|
||
return $this->inputs[$key];
|
||
}
|
||
|
||
return $this->inputs;
|
||
}
|
||
|
||
/**
|
||
* @param $name "请求方法"
|
||
* @param $arguments (第一个参数默认版本号)
|
||
* @return mixed
|
||
* @throws Exception
|
||
*/
|
||
function __call($name, $arguments){
|
||
$version = $arguments[0];
|
||
$sversion = $arguments[1];
|
||
//一些接口的分支需要校验,如biz里的rec_list,是需要登录的
|
||
$pre_call = 'call__';
|
||
if(0 === strpos($name, $pre_call)){
|
||
$name = substr($name, strlen($pre_call));
|
||
}
|
||
$method = $name;
|
||
|
||
if($version){//版本号的方法存在用版本号的,否则继续用默认的方法
|
||
if(method_exists($this, $method . '__' . $version)){
|
||
$method .= '__' . $version;
|
||
}
|
||
}
|
||
|
||
if($sversion){//小版本,设置某个方法的版本
|
||
$sversion = str_replace('.', '_', $sversion);
|
||
if(method_exists($this, $method . '__' . $sversion)){
|
||
$method .= '__' . $sversion;
|
||
}
|
||
}
|
||
|
||
if(!method_exists($this, $method)){
|
||
debug_log("[fail]". __FUNCTION__ . ": request not allow; method:{$method}", $this->log_file);
|
||
throw new Exception('非法请求', API_CODE_NONE);
|
||
}
|
||
|
||
//某个方法或者整个ct在白名单里无需校验
|
||
$session = $this->session;
|
||
if(!in_array($name, $this->login_white) && 'all' != $this->login_white){
|
||
if(!$session){
|
||
throw new Exception('还未登录', API_CODE_LOGOUT);
|
||
}
|
||
|
||
$user = $this->u_entity->get(array('id' => $session['uid']));
|
||
if(!$user || -1 == $user['status']){
|
||
$this->logout($this->ukey);
|
||
debug_log("[error]# user is delete, sql=". $this->u_entity->last_query(), __FUNCTION__, $this->log_dir);
|
||
throw new Exception('登录超时', API_CODE_LOGOUT);
|
||
}
|
||
|
||
// 校验用户状态
|
||
if(in_array($name, $this->check_status) || 'all' == $this->check_status){
|
||
if(self::STATUS_NOR != $session['status']){
|
||
throw new Exception('用户被禁用', API_CODE_FORB);
|
||
}
|
||
}
|
||
//是否绑定手机号
|
||
if(in_array($name, $this->check_mobile) || 'all' == $this->check_mobile){
|
||
if(!$session['mobile'] || !mobile_valid($session['mobile'])){
|
||
throw new Exception('请绑定正确的手机号', API_CODE_FORB);
|
||
}
|
||
}
|
||
|
||
// 校验用户头像
|
||
if(in_array($name, $this->check_headimg) || 'all' == $this->check_headimg){
|
||
if(!$session['headimg']){
|
||
throw new Exception('获取头像信息失败', API_CODE_FORB);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
//超级管理员登录其他用户操作权限控制
|
||
/* not limit again edit by xxb 20200529 */
|
||
/*if($session['is_majia']){
|
||
if(!in_array($name, $this->majia_white) && 'all' != $this->majia_white){
|
||
throw new Exception('无权限', API_CODE_FORB);
|
||
}
|
||
}*/
|
||
|
||
return $this->$method();
|
||
}
|
||
|
||
/**
|
||
* 刷新登录有效时间
|
||
* @param $data
|
||
* @return string
|
||
*/
|
||
protected function refresh_login($data){
|
||
$redis = $this->app_redis;
|
||
|
||
$ukey = md5("{$data['uid']}_{$data['session_key']}");
|
||
|
||
$redis->save($this->redis_login.$ukey, json_encode($data, JSON_UNESCAPED_UNICODE), 30 * 24 * 3600);
|
||
|
||
$this->ukey = $ukey;
|
||
return $ukey;
|
||
}
|
||
|
||
/**
|
||
* 删除登录记录
|
||
* @param $ukey
|
||
*/
|
||
protected function logout($ukey = ''){
|
||
!$ukey && $ukey = $this->input_param('ukey');
|
||
|
||
if($ukey){
|
||
$redis = $this->app_redis;
|
||
$redis->delete($this->redis_login.$ukey);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 校验用户是否黑名单
|
||
* @return bool
|
||
*/
|
||
protected function is_black(){
|
||
$mobile = $this->session['mobile'];
|
||
if(!$mobile){
|
||
return false;
|
||
}
|
||
|
||
return $this->u_entity->is_black($mobile);
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @return array|mixed
|
||
*/
|
||
private function fetch_session(){
|
||
$ukey = $this->input_param('ukey');
|
||
$this->ukey = $ukey;
|
||
if(!$ukey){
|
||
return array();
|
||
}
|
||
|
||
$redis = $this->app_redis;
|
||
|
||
//data:{"uid":"用户ID", "session_key":"微信session_key"}
|
||
$data = json_decode($redis->get($this->redis_login.$ukey), true);
|
||
|
||
if($data){
|
||
$session = $data;
|
||
$source_uid = $uid = $session['uid'];
|
||
$user = $this->u_entity->get(array('id' => $uid));
|
||
if($user){
|
||
//判断是否超级管理员批马甲
|
||
$json = $user['jsondata'] ? json_decode($user['jsondata'], true) : array();
|
||
if($json['majia']){//披上马甲
|
||
$muid = $json['majia']['uid'];
|
||
$row = $this->u_entity->get(array('id' => $muid));
|
||
if($row){
|
||
$uid = $muid;
|
||
$session['is_majia'] = 1;
|
||
$session['source_uid'] = $source_uid;
|
||
$session['uid'] = $uid;
|
||
$user = $row;
|
||
}
|
||
}
|
||
}
|
||
if($user){
|
||
$session = array_merge($session, $user);
|
||
$this->session = $session;
|
||
//更新登录有效时间
|
||
$this->refresh_login($data);
|
||
} else {
|
||
$this->logout($ukey);
|
||
}
|
||
}
|
||
$this->myuid = $this->session['uid'];
|
||
return $this->session;
|
||
}
|
||
|
||
/**
|
||
* 设置
|
||
* @param array $arr
|
||
* @param int $type "-1删除 0重置 1新增"
|
||
* @return mixed
|
||
*/
|
||
protected function set_session($arr = array(), $type=0){
|
||
$redis = $this->app_redis;
|
||
|
||
$ukey = $this->ukey;
|
||
|
||
$data = json_decode($redis->get($this->redis_login.$ukey), true);
|
||
if(-1 == $type){//删除
|
||
foreach($arr as $k){
|
||
unset($data[$k]);
|
||
}
|
||
} elseif(0 == $type){//重置
|
||
$data = $arr;
|
||
} elseif(1 == $type){//新增
|
||
foreach($arr as $k => $v){
|
||
$data[$k] = $v;
|
||
}
|
||
}
|
||
|
||
$redis->save($this->redis_login.$ukey, json_encode($data, JSON_UNESCAPED_UNICODE), 30 * 24 * 3600);
|
||
|
||
$this->ukey = $ukey;
|
||
return $ukey;
|
||
}
|
||
|
||
/**
|
||
* 小程序开关
|
||
* @param string $key (name, logo, biz_cate)
|
||
* @return mixed
|
||
*/
|
||
protected function app_config($key = ''){
|
||
$this->load->model('app/app_model');
|
||
$this->load->model("app/appusual/app_config_model");
|
||
|
||
if($this->app){
|
||
$app = $this->app;
|
||
} else {
|
||
$where = array('id' => $this->app_id);
|
||
$app = $this->app_model->get($where);
|
||
$json = $app['jsondata'] ? json_decode($app['jsondata'], true) : array();
|
||
|
||
$where = array('app_id' => $this->app_id);
|
||
$select = "k,v";
|
||
$map_config = $this->app_config_model->map('k', 'v', $where, '', 0, 0, $select);
|
||
if($map_config){
|
||
foreach($map_config as $k => $v){
|
||
$v && $json[$k] = json_decode($v, true);
|
||
}
|
||
}
|
||
$app['json'] = $json;
|
||
$this->app = $app;
|
||
}
|
||
|
||
if(!$key){
|
||
return $app;
|
||
}elseif($app[$key]){
|
||
return $app[$key];
|
||
}else{
|
||
$json = $app['json'];
|
||
return $json[$key];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取app的配置
|
||
* @param $app_key
|
||
* @param $app_id
|
||
* @return null
|
||
*/
|
||
protected function get_config($app_key = '', $app_id = ''){
|
||
$this->config->load('app', true, true);
|
||
$configs = $this->config->item('app');
|
||
if($app_key){
|
||
return $configs[$app_key];
|
||
} elseif($app_id) {
|
||
foreach($configs as $k => $v){
|
||
if($app_id == $v['app_id']){
|
||
return $v;
|
||
}
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* @return string
|
||
*/
|
||
protected function cityid(){
|
||
|
||
if(!$this->city_id){
|
||
$city_id = $this->app_config('city_id');
|
||
!$city_id && $city_id = '350200';
|
||
$this->city_id = $city_id;
|
||
}
|
||
|
||
return $this->city_id;
|
||
}
|
||
|
||
/**
|
||
* 获取用户当前访问的城市ID
|
||
* @return mixed
|
||
*/
|
||
protected function ucityid(){
|
||
$json = $this->session['jsondata'];
|
||
!is_array($json) && $json = json_decode($json, true);
|
||
return $json['city_id'];
|
||
}
|
||
|
||
/**
|
||
* 用户访问城市配置
|
||
* @param string $k
|
||
* @param string $cityid 取默认城市id
|
||
* @return array
|
||
*/
|
||
protected function config_ucity($k = '',$cityid=''){
|
||
if($cityid){
|
||
$city_id = $this->cityid();
|
||
}else{
|
||
$city_id = $this->ucityid();
|
||
}
|
||
$citys = $this->app_config("citys");
|
||
$config_city = $citys[$city_id];
|
||
1 == $config_city && $config_city = array();
|
||
|
||
return $k ? $config_city[$k]:$config_city;
|
||
}
|
||
|
||
protected function get_pager()
|
||
{
|
||
$size = $this->input_param('size');
|
||
$page = $this->input_param('page');
|
||
$page = $page ? $page : '1';
|
||
$size = $size ? $size : '10';
|
||
return array('page' => $page, 'size' => $size);
|
||
}
|
||
|
||
/**
|
||
* 解析获取微信
|
||
* @param $code
|
||
* @return mixed|string
|
||
*/
|
||
protected function wx_session($code){
|
||
$appid = $this->wx_config['appid'];
|
||
$secret = $this->wx_config['secret'];
|
||
$url = "https://api.weixin.qq.com/sns/jscode2session?appid={$appid}&secret={$secret}&js_code={$code}&grant_type=authorization_code";
|
||
debug_log("[info] ". __FUNCTION__ . "微信授权:\n{$url}", $this->log_file);
|
||
// $ch = curl_init($url);
|
||
// curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
|
||
// //关闭https验证
|
||
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||
// curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||
// $res = curl_exec($ch);
|
||
$res = file_get_contents($url);
|
||
//存日志
|
||
debug_log("[info] ". __FUNCTION__ . "res={$res}", $this->log_file);
|
||
$ret = json_decode($res, true);
|
||
// {
|
||
// "session_key": "会话密钥",
|
||
// "openid": "用户唯一标识",
|
||
// "unionid": "用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。",
|
||
// "errcode": "错误码",
|
||
// "errmsg": "错误信息"
|
||
// }
|
||
|
||
if(!$ret['session_key']){
|
||
debug_log("[fail] ". __FUNCTION__ . ": session_key is null", $this->log_file);
|
||
}
|
||
|
||
return $ret;
|
||
}
|
||
|
||
/**
|
||
* 解析微信数据
|
||
* @param $encrypted
|
||
* @param $iv
|
||
* @return array|mixed|string
|
||
*/
|
||
protected function wx_data($encrypted, $iv){
|
||
require_once APPPATH."third_party/WeChat/wxBizDataCrypt.php";
|
||
$pc = new WXBizDataCrypt($this->wx_config['appid'], $this->session['session_key']);
|
||
$wx_data = '';
|
||
$errCode = $pc->decryptData($encrypted, $iv, $wx_data);
|
||
|
||
debug_log("[info] ". __FUNCTION__ . ":code={$errCode}; wxdata:{$wx_data}", $this->log_file);
|
||
|
||
if ($errCode == 0) {
|
||
$wx_data = json_decode($wx_data, true);
|
||
return $wx_data;
|
||
}
|
||
|
||
debug_log("[warning] ". __FUNCTION__ . ":appid=" . $this->wx_config['appid'] . "; session_key=" . $this->session['session_key'] . "; encrypted={$encrypted}; iv={$iv}; wxdata:{$wx_data}", $this->log_file);
|
||
return array();
|
||
}
|
||
|
||
/**
|
||
* 输入参数处理
|
||
* @param $inputs
|
||
* @return array
|
||
*/
|
||
private function set_input($inputs){
|
||
if(!$inputs){
|
||
return array();
|
||
}
|
||
foreach($inputs as $k => $v){
|
||
if('undefined' === $v){//前端空参数过滤
|
||
$inputs[$k] = '';
|
||
}
|
||
}
|
||
|
||
$this->inputs = $inputs;
|
||
|
||
return $this->inputs;
|
||
}
|
||
|
||
/**
|
||
* h5地址
|
||
* @return string
|
||
*/
|
||
protected function h5_host(){
|
||
if (false !== strpos($_SERVER['HTTP_HOST'], 'dev')) { //dev 测试
|
||
$h5_host = 'https://hd-wxdev.xiaoyu.com';
|
||
} elseif (false !== strpos($_SERVER['HTTP_HOST'], 'test')) {//test 测试
|
||
$h5_host = 'https://www.test.haodian.cn';
|
||
} else { // 正式
|
||
$h5_host = 'https://www.haodian.cn';
|
||
}
|
||
return $h5_host;
|
||
}
|
||
|
||
//获取当前门店id
|
||
protected function get_biz_id(){
|
||
return $this->session['new_biz_id'] ? $this->session['new_biz_id'] : intval($this->session['biz_id']);
|
||
}
|
||
}
|