技术开发文档

来自技术开发小组内部wiki
跳转至: 导航搜索

主要用来汇总开发过程中涉及到的各种功能或问题处理的说明文档,方便查阅

目录

后台程序执行统一规范

[[详见:后台程序执行统一规范]]

Redis|sub/pub后台异步实时请求

[[详见:Redis sub/pub后台异步实时请求|

重构后的分页类

详见:重构后的分页类

版本库如何恢复

[[详见:版本库如何还原]]

MySQL闪回实现

[[详见:MySQL闪回实现]]

MySQL-explain

[[详见:MySQL-explain]]

sql防注入规则

[[详见:Sql防注入]]

xss攻击防范

[[详见:Xss攻击防范]]

关于建表规范

[[详见:建表规范]]

关于前端主从

[[详见:前端主从]]

父母帮DB故障切换

[[详见:父母帮DB故障切换]]

日志集中统一部署

[[详见:日志集中统一部署]]

强制重置用户密码

[[详见:重置用户密码]]

检查库存数和已卖出数

[[详见:检查库存数和已卖出数]]

实体票改为电子票对已卖出的老订单发码

[[详见:实体票改为电子票对已卖出的老订单发码]]

对支付宝h5订单掉单的处理

[[详见:对支付宝h5订单掉单的处理]]

代码安全加固处理规范

[[详见:代码安全加固处理规范]]

出现货到付款的订单修正

[[详见:出现货到付款的订单修正]]

遇到mysql数据库堵塞情况处理步骤

[[详见:遇到mysql数据库堵塞情况处理步骤]]

订单掉单及订单补发验证码处理方法

[[详见:订单掉单及订单补发验证码处理方法]]


订单补发合同处理方法

[[详见:订单补发合同处理方法]]

订单由一个商家移动到另外一个商家的处理方法

[[详见:订单由一个商家移动到另外一个商家的处理方法]]

结伴成功修改方法

[[详见:结伴成功修改方法]]

订单改签及POS支付无法下发验证码的原因

[[详见:订单改签及POS支付无法下发验证码的原因]]

MySQL非常规恢复

[[详见:MySQL非常规恢复]]

父母帮redis配置

[[详见:父母帮redis配置]]

后台改签后用户后悔怎么办

[[详见:改签悔改具体操作]]

订单有退货而且是退货成功又想改签怎么办

[[详见:订单有退货而且是退货成功又想改签怎么办]]

前后台汉字转拼音用法

[[详见:前后台汉字转拼音用法]]

更换商家工具

[[详见:更换商家工具]]

造活动报名数据

[[详见:造活动报名数据]]

批量退款退货

[[详见:在命令行下操作批量退款退货]]

数据监控PHP端使用说明

[[详见:数据监控使用说明]]

支付流程概述及代码位置

[[详见:支付流程概述及代码位置]]

Cacti安装配置

[[详见:Cacti安装配置]]

订单更改商户对应关系

[[详见:订单更改对应商户关系涉及到的表修改]]

第三方导码操作步骤

[[详见:第三方导码操作步骤]]

如何进入灰度发布环境

[[详见:灰度发布]]

后台发布中心使用指南

[[详见:后台发布中心使用指南]]

代码库push Web Hooks 创建过程

[[详见:代码库push Web Hooks 创建过程]]

跨境通相关紧急操作及命令

[[详见:跨境通相关紧急操作及命令]]

跨境通商品注意事项

[[详见:跨境通商品注意事项]]

Linux下查找CC攻击的方法

[[详见:Linux下查找CC攻击的方法]]


Linux下常用命令

[[详见:Linux下常用命令]]

线路游相关命令

[[详见:线路游相关命令]]

超全局变量

详见: 超全局变量及公共类库]]

一致性api算法

详见: 一致性api算法]]

结算系统常见问题以及解决方案

详见: 结算系统常见问题以及解决方案]]

微信批量退款操作处理

详见: 微信批量退款操作处理]]

周年庆处理

详见: 周年庆处理]]


微信公众号位置

详见: 微信公众号位置

后台系统日志不更新处理方法

详见: 后台系统日志不更新处理方法]]

活动id&&产品id相关

 get_mobile_url_by_id 根据id 返回相应的移动售卖url地址
 get_table_by_id 根据id 返回相应的表
 详见 fmb.dianping/ application/helpers/fmb_baseinfo_helper.php
 未来详情页数据可以在此基础上统一获取 ,如果有必要的话

长线游打包产品命令行更新最低价格

cd /data2/local-dev/192.168.27.12/fmb.dianping/public_html
批量更新最低价格
php index.php test cmdrun longline deal_status
2 更新单个产品最低价格及状态
php index.php test cmdrun longline check_status_cmd <打包产品id>
3 更新单个产品最低价格
php index.php test cmdrun longline set_min_price_with_big <打包产品id>
4 检查单品状态 <机票>
php index.php test cmdrun longline check_plane <单品id>
5 检查单品状态 <酒店>
php index.php test cmdrun longline check_hotel <单品id>

长线游打包搜索库更新

导入库new_activity,base_product与product_index的差异或完整插入 单条记录
 php index.php test cmdrun longline import_product_index_by_id 309
 
 

批量发邮件组件说明

//本次调整修改邮件发送时 不自动清空附件
//建议以后发邮件时注意下
//原有系统每次发邮件时候 不初始化邮件附件信息
//现在已经调整
$this->load->library('email'); //加载组件
$this->email->initialize($config); //设定邮件信息
//如果初始化邮件后仍要批量发附件,注意清空附件信息
foreach($data as k=>$v){
$this->email->attach($v['real_path']);
....
$this->email->send();
$this->email->initialize($config); //清空相关信息 并重新初始化邮件
}

ELK安装调试查询

详见: ELK安装调试查询]]

css js 压缩规范

详见: Minify压缩规范]]

前后台单独分支测试

1.首先在/data2/fmb_preview/下面建立对应的fmb.admin.test建立对应的分支

git clone git@code.fumubang.net:repositories/root/admin001.git -b 20151215-zeus-fajiangjin fmb.admin.test

2.需要在code.fumubang.net中配置对应的代码库的web-hooks:记得在上线完成之后需要进行对应的删除

 [http://192.168.30.225:7777?repos=admin&branch=20151215-zeus-fajiangjin http://192.168.30.225:7777?repos=admin&branch=20151215-zeus-fajiangjin]
 
 3.修改/home/www/hook下面的config.inc.php

 增加对应的配置

 4.需要修改/home/fumubang/deploy/

 对应的分支fmb.admin下面的build.xml文件配置
6.最后需要修改nginx.conf文件(一次性修改的)

cygwin 环境部署

网址 [http://www.cygwin.com/ http://www.cygwin.com/] 根据系统 提示 可选择操作系统位数 选择32或64位

基本安装

组件wget tar bzip2 gawk 必装

 安装 apt-cyg

wget [https://raw.githubusercontent.com/transcode-open/apt-cyg/master/apt-cyg 

 chmod +x apt-cyg

 mv apt-cyg /bin

 #cygwin 后台服务

 apt-cyg install cygrunsrv

 #定时任务

 apt-cyg install cron

 #openssh

 apt-cyg install openssl openssh

 #python

 apt-cyg install python apt-cyg install python-setuptools

 mkpasswd -l > /etc/passwd mkgroup -l > /etc/group chmod +r /etc/passwd chmod +r /etc/group chmod +rwx /var

 #生成sshkey

 ssh-keygen -t dsa -P -f ~/.ssh/id_dsa

新版成团统计相关命令操作

[[详见:新版成团统计相关命令操作]]

CI框架服务层SERVICE开发规范

为了更加清晰的表达业务流程层级,在现有CI框架三层MVC结构上引入服务层SERVICE概念,形成controller->service->model->view四层结构 
 新的结构形成之后,需要达成以下代码编写共识 

  1. controller层只处理参数验证,流程跳转,数据格式化,异常判断,其代码中不应(禁止)含有直接sql语句操作
  2. service层负责处理数据获取输出,通用共用数据业务处理逻辑,其代码应该是通用复用,不应(禁止)含有状态数据,比如会话,global全局,cookie,get,post参数等
  3. model层负责处理数据底层存储持久化,包含基本的单一查询,列表查询,单一更新,删除 等操作,其代码应该最简(一个模型理论上最少可以只有6行代码即可)
  4. view层负责页面元素结构渲染
  5. 服务层service存放规范,在根目录下application/services下面不能存放单一服务文件,必须有对应的文件夹(基于业务分类),类似services/user/xxx_service.php
  6. 模型层model也可以根据业务分类,组织管理模型文件,类似models/user/xxx_model.php
  7. 基本的业务分类:订单(order),商品(product),商家(shop),用户(user),购物车(cart),搜索(search),详情页面(deail),结算(settle),支付(pay),工具(util)可以根据需要增加但要规范统一
  8. 复杂关联数据表查询,可以在服务层service进行方法封装实现,也可以考虑放置到模型中处理
  9. 服务层代码文件行数控制在2000行左右,超出后对于后续需要增加的功能,通过继承方式另建文件处理,形如User_xxx_service.php
  10. 具体代码中的函数封装代码行数以最大100行为宜,尽量将功能微型化,即一个函数只处理最基本的功能,复杂功能函数通过调用其他基本函数来组装
  11. 代码注释需要依照PHP的标注注释写法进行处理,逻辑也中的代码注释也需要酌情增加以提高可阅读性
  12. libraries负责处理引入的第三方库,比如支付相关的,第三方接口的,及与具体业务无关的代码库,如生成pdf文件,验证码生成,ip地址判断等
  13. helper负责处理一些通用的函数库,一般函数功能单一简单,比如字符串处理的,地址格式化处理的等,但个人一般建议可以通过上述的libraries来组织管理,具体如何界定是个思考题
  14. 新的框架分层结构启用后,会加强针对service层的代码审查,以周为单位由组内一位同事进行审核检查,对于不符合编写规范要求的,需要进行整改力求保证代码结构清晰
  15. service层的方法编写实行准入制,所有加入的方法是经过讨论确定(高频重点使用),不能随意增加以减少破坏性

具体的服务层实例:

<?php 
// /////////////////////////////////////////////////// 
// Copyright(c) 2016,父母邦,帮父母 
// 日 期:2016年4月11日 
// 作 者:卢少锦 
// E-mail :shaojin.lu@fumubang.com 
// 文件名 :user_service.php 
// 创建时间:下午7:12:00 
// 编 码:UTF-8 
// 摘 要:用户服务 
// /////////////////////////////////////////////////// 
class User_service extends MY_Service 
{ 
 
 /** 
 * 构造函数 
 */ 
 public function __construct() 
 { 
 parent::__construct(); 
 $this->load->model("user_model"); // 加载用户模型 
 } 
 
 /** 
 * 获取用户的基本信息 
 * 
 * @param number $uid 
 * @return array 
 */ 
 public function getUserBasicInfo($uid = 0) 
 { 
 $uid = intval($uid); 
 if ($uid <= 0) { 
 return array(); // 返回空数组 
 } 
 // 查找单一用户数据 
 return $this->user_model->find($uid); 
 } 
 
 /** 
 * 判断用户是否登录 
 * @return boolean 
 */ 
 public function isLogin() 
 { 
 $info = $this->getCurrentUser(); 
 if (! isset($info['uid'])) { 
 return FALSE; 
 } 
 return TRUE; 
 } 
 
 /** 
 * 获取当前登录用户信息 
 * @param boolean $force 
 * @return array 
 */ 
 public function getCurrentUser($force = FALSE) 
 { 
 static $cache; 
 if ($cache == FALSE || $force == TRUE) { 
 $this->load->library('Session'); 
 $cache = $this->session->userdata('user_info'); 
 } 
 return $cache; 
 } 
} 

<?php // /////////////////////////////////////////////////// // Copyright(c) 2016,父母邦,帮父母 // 日 期:2016年4月11日 // 作 者:卢少锦 // E-mail :shaojin.lu@fumubang.com // 文件名 :Order_service.php // 创建时间:下午7:12:00 // 编 码:UTF-8 // 摘 要:订单服务 // /////////////////////////////////////////////////// class Order_service extends MY_Service {

/** 
* 构造函数 
*/ 
public function __construct() 
{ 
parent::__construct(); 
$this->load->model("tickets/orders_model","order_info_model"); // 加载订单模型 
$this->load->model("tickets/orders_goods_model","order_goods_model"); 
$this->load->model("tickets/tickets_model","tickets_model"); 
} 

/** 
* 获取订单的基本详情 
* @param string $order_sn 订单号 
* @return array 
*/ 
public function getOrderInfo($order_sn=""){ 
if(empty($order_sn)){ 
return array(); 
} 
//调用其中的方法进行处理 
$order_sn=addslashes($order_sn); 
return $this->order_info_model->get_orderby_order_sn($order_sn); 
} 

/** 
* 获取订单商品详情 
* @param string $order_sn 
* @return array 
*/ 
public function getOrderGoods($order_sn=){ 
if(empty($order_sn)){ 
return array(); 
} 
$order_sn=addslashes($order_sn); 
$goodsList=$this->order_goods_model->get_goods("*","order_sn='".$order_sn."'"); 
//需要针对其中的具体商品进行获取 
foreach ($goodsList as &$each){ 
$temp = $this->tickets_model->find($each['goods_id']); 
$each['goods_name'] = $temp['tname']; 
$each['tid']=$temp['ticket_id']; 
} 
return $goodsList; 
} 

}


class Test extends MY_Controller {

public function __construct() { 
parent::__construct (); 
error_reporting(E_ALL); 
ini_set("display_error","On"); 
$this->load->model("activity_model","act"); 
// $this->load->model("activity/activity_state","act2"); 
} 
public function testservice(){ 
$this->load->service("user/user_service"); 
$userinfo=$this->user_service->getUserBasicInfo(494); 
print_r($userinfo); 
var_dump($this->user_service->isLogin()); 
} 

}

最简单的model: class Xxxx_model extends MY_Model{

public function __construct(){ 
parent::__construct(); 
$this->setTable('xxxx', 'id');//手动指定模型的主键 
} 

}

具体的控制层引用:注意引用服务名的方式优化了下, $this->load->service("user/user_service");带有文件夹的服务名引用, 默认会实例化为具体的服务名,即:user_service  

服务层错误码定义

服务层框架改造流程

打开application下对应的core目录
(1.core下增加MY_Service.php:
class MY_Service
{

 public function __construct()
 {
 log_message('debug', "Service Class Initialized");
 }

 function __get($key)
 {
 $CI = & get_instance();
 return $CI->$key;
 }
}
(2.application/core/MY_Loader.php中增加以下代码:
主要是为了增加增加服务层的钩子
 protected $_ci_services = array();
 protected $_ci_service_paths = array();
 /**
 * Service Loader
 *
 * This function lets users load and instantiate classes.
 * It is designed to be called from a user's app controllers.
 *
 * @param
 * string the name of the class
 * @param
 * mixed the optional parameters
 * @param
 * string an optional object name
 * @return void
 */
 public function service($service = '', $params = NULL, $object_name = NULL)
 {
 if (is_array($service)) {
 foreach ($service as $class) {
 $this->service($class, $params);
 }
 return;
 }
 if ($service == '') {
 return FALSE;
 }
 $subdir = '';
 // Is the service in a sub-folder? If so, parse out the filename and path.
 if (($last_slash = strrpos($service, '/')) !== FALSE) {
 // The path is in front of the last slash
 $subdir = substr($service, 0, $last_slash + 1);
 // And the service name behind it
 $service = substr($service, $last_slash + 1);
 }
 //上面可以获取到$service变量
 if(is_null($object_name) || $object_name==''){
 $object_name=$service;//用service名称替代
 }

 if (in_array($object_name, $this->_ci_services, TRUE))
 {
 return;//已经存在了就不用再实例化
 }
 if (! is_null($params) && ! is_array($params)) {
 $params = NULL;
 }
 foreach ($this->_ci_service_paths as $path) {
 $filepath = $path . 'services/' . $subdir . $service . '.php';
 if (! file_exists($filepath)) {
 continue;
 }
 include_once ($filepath); 
 $service = strtolower($service);
 if (empty($object_name)) {
 $object_name = $service;
 }
 $service = ucfirst($service);
 $CI = &get_instance();
 if ($params !== NULL) {
 $CI->$object_name = new $service($params);
 } else {
 $CI->$object_name = new $service();
 }
 $this->_ci_services[] = $object_name;
 return;
 }
 }

(3.最后在system/core/CodeIgniter.php中增加以下代码:
 /*
 * ------------------------------------------------------
 * Load the service class
 * ------------------------------------------------------
 */
 $SERVICE =& load_class('Service', 'core');

最后,在services下下建立user_service.php
class User_sevice extends MY_Service,建立玩以后,$this->load->model导入对应的模型,像controller用法一样修改去修改对应的数据模型。

调用服务层API接口

(1.本地hosts增加:
192.168.30.110 dev-service.fumubang.com
192.168.30.110 service.fumubang.com

(2.curl发送header头
$headers[] = "Host: ".$real_host;
$headers[] = "X-Real-IP: ".$_SERVER['REMOTE_ADDR'];
(设置headers头)
curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);

这样做的是为了保证能够在fmb.dianping调用指向本机的host,同时调用对应API。(本地开发测试需要绑定域名才能生效)

如:http://dev-service.fumubang.com/web_service/user/get_base_user_info?uid=332&sign=9361ec4eec08628ac0ae2f7683645c67 此时会通过发送curl请求去获取admin对应的(对应admin)的接口数据,接口定义了统一的返回规范和标准,具体的错误码参考fmb_enum_info表。

需要说明的是:

接口设置了二级域:采用dev-service访问,对应的接口在/controller/web_service下实现


protected function call_api($url , $params = array(), $method = 'GET' , $multi = FALSE, $extheaders = array(), $sslv = FALSE)
{
$is_local=false;
$is_test=false;
$is_product=false;
if(ENVIRONMENT==='local'){
$is_local=true;
}else if(ENVIRONMENT==='test'){
$is_test=true;
}else{
$is_product=true;
}
$host=parse_url($url,PHP_URL_HOST);
$port=parse_url($url,PHP_URL_PORT);
$method = strtoupper($method);
$ci = curl_init();
$headers=array();
if($is_local){
$real_host=str_replace('.net','.com',$host);
$headers[] = "Host: ".$real_host;
$headers[] = "X-Real-IP: ".$_SERVER['REMOTE_ADDR'];
}elseif($is_test){
$real_host=str_replace('.net','.com',$host);
$headers[] = "Host: ".$real_host;
$headers[] = "X-Real-IP: ".$_SERVER['REMOTE_ADDR'];
}
curl_setopt($ci, CURLOPT_URL, $url);
curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ci, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36');
curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 3);
curl_setopt($ci,CURLOPT_TIMEOUT,3);
curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ci, CURLOPT_HEADER, FALSE);
curl_setopt($ci, CURLOPT_FOLLOWLOCATION, 1); 
if(stripos($url,"https://")!==FALSE){
if($sslv){
curl_setopt($ci, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
}
curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, FALSE);
}
curl_setopt($ci, CURLINFO_HEADER_OUT, TRUE );
curl_setopt($ci, CURLOPT_URL, $url);
if($headers)
{
curl_setopt($ci, CURLOPT_HTTPHEADER, $headers );
}
$response = curl_exec($ci);
if(curl_errno($ci))
{
echo 'Curl error: ' . curl_error($ci);
echo '<hr />';
}
curl_close ($ci);
return $response;
}

daemon守护进程

详见 Daemon守护进程

PC H5 列表

详见:PCH5列表

H5redis缓存计算

详见:H5redis缓存计算

长线游系统

项目文档 [[1]]

演出展览上架

详见:演出展览上架

通用数据格式说明

详见:通用产品数据格式说明


子进程挂载失败解决方案

详见: 子进程挂载失败解决方案

第三方导码配置流程

详见: 第三方导码配置流程

APP接口开发注意事项

详见: App开发注意事项

process_daemon异步消息处理

详见:Process daemon异步消息处理

成团统计后台

    后台地址
admin001.fumubang.com/tickets/tickets_tuan/lists 

后台目录:控制器
\fmb.admin\application\controllers\tickets\tickets_tuan.php
模型  
\fmb.admin\application\\models\tickets\tickets_tuan_model_new.php
模板
\fmb.admin\application\views\tickets\tickets_tuan_add.php
\fmb.admin\application\views\tickets\tickets_tuan_lists.php
\fmb.admin\application\views\tickets\tickets_tuan_search_activity.php
     相关表
fmb_tickets_tuan,fmb_tickets_tuan_infos,fmb_tickets_tuan_rule
     redmine,一期9719,二期9802,补充9847,9820
计划任务相关咨询艳民


结伴行后台

   后台地址
admin001.fumubang.com/partner/partner

后台目录:控制器
\fmb.admin\application\controllers\partner
模型  
\fmb.admin\application\models\partner
模板
\fmb.admin\application\views\partner
   相关表
fmb_partner_config,fmb_partner_info,fmb_partner_order
   redmine:9909,9945,9968
计划任务、前台相关咨询艳民



新地区管理后台

   后台地址
admin001.fumubang.com/region/region

后台目录:控制器
\fmb.admin\application\controllers\region
模型  
\fmb.admin\application\models\region_model.php
模板
\fmb.admin\application\views\region
   相关表
fmb_province,fmb_city,fmb_area,fmb_business


短信平台切换

 后台地址
admin001.fumubang.com/tickets/fixed_sms/sms_list

后台目录:控制器
\fmb.admin\application\controllers\tickets\fixed_sms.php
 模型  
\fmb.admin\application\models\tickets\sms_template_model.php

模板
\fmb.admin\application\views\tickets\fixed_sms_add.php
\fmb.admin\application\views\tickets\fixed_sms_edit.php
\fmb.admin\application\views\tickets\fixed_sms_list.php

libraries
\fmb.dianping\application\libraries\AliDayu.php
\fmb.dianping\application\libraries\Alidayu
\fmb.dianping\application\libraries\Sms.php
\fmb.dianping\application\libraries\Redis_codes_sms.php
   相关表
fmb_sms_template
   注意事项
1.切换平台开关的redis_key为"redis_sms_plat" 该key有值为大鱼平台,反之为默认平台
2.所有验证码发送部分调用sms下的send方法第二个参数改为数组格式,格式对应后台短信模板。
3.所有验证码发送部分调用sms下的send方法第三个参数需要为"passport.ajax.sendMessages.code"或者"dianping.api_login.send_messages.code"。


有房活动

   文件位置
1.控制器
\fmb.dianping\application\controllers\mobile\subject.php
2.模组
\fmb.dianping\application\models\activity\activity_state_model.php
   注意事项
带地区选择的有房活动,参照s20161001方法
无地区选择的有房活动,参照s20160915方法
   因范围较窄,需求变化较少。建议后期工具化。


前后台统一的Redis操作类

详见参阅 统一Redis操作类

预付款流水操作错误怎么办

详见参阅预付款悔改操作流程


php-binlog触发处理

详见参阅Php-binlog触发处理

结算无验证码处理

详见参阅结算无验证码处理

结算单解除已结算状态

详见参阅结算单解除已结算状态

后台系统日志不更新处理方法

详见参阅后台系统日志不更新处理方法

目前redis异步处理方式

详见参阅目前redis异步处理方式


新版导出功能处理【支持过万级数据】

详见参阅新版导出功能说明


导出下载中心异步处理流程

详见参阅异步下载中心处理方式


订单批量生成退款单处理方式

详见订单批量生成退款单