Files
liche/api/controllers/wxapp/Wxapp.php
T
2021-07-05 09:56:27 +08:00

525 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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;//应用idhd_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;
}
}