ElasticSearch 使用 – 传递数据规避重复数据

我们一般把mysql的数据,传递到ElasticSearch中,然后进行即使查询,但是ES有一个问题就是,刚刚插入的数据,在1秒内是查询不到的(大致一秒内,粗略的说法),也就是说在很短的时间内,插入一条数据,然后去查询这条数据是查询不到的,因此,我们必须用ES的_id来规避重复数据。

ES中一般除了_id, 再加一个id的字段,用来存储mysql中的id,当然es中的_id也要用mysql中的id来赋值,来规避重复数据。

#通过_id获取数据

$EsTraceDataOne = EsTraceData::findOne($one['_id']);
            if(!$EsTraceDataOne){
                $EsTraceDataOne = new EsTraceData;

# 给Es的表的_id设置值
                $EsTraceDataOne->setPrimaryKey($one['_id']);
            }

# 设置Id
            $EsTraceDataOne->id = $one['_id'];
            $attributes = $EsTraceDataOne->attributes();
            foreach($one as $k=>$v){
                if(in_array($k,$attributes)){

# 只取attributes()方法返回的数组中存在的数据
                    $EsTraceDataOne[$k] = $v;
                }
            }

# 保存
            $EsTraceDataOne->save();

 

通过上面的方法,基本可以规避数据多次传递重复插入数据的问题。

 

附:

<?php

namespace appadmin\code\Ta\models\elasticSearch;

use yii\elasticsearch\ActiveRecord;

class TraceData extends ActiveRecord
{
  public static $currentIndex;
  
  # 定义db链接
  public static function getDb()
  {
    return \Yii::$app->get('elasticsearch_TA');
  }
  
  # 不同的website 使用的是不同的db ,使用前需要先初始化
  # db的名字
  public static function initDb($website_id){
    //echo 888;
    if($website_id){
      //echo 999;
      self::$currentIndex = 'ta'."_".$website_id;
      //echo self::$currentIndex;
      //echo 3;
    }
  }
  
  
  
  # db
  public static function index()
  {
    return self::$currentIndex;
  }
  # table
  public static function type()
  {
    return 'trace_data';
  }
  
   public function attributes()
    {
        // path mapping for '_id' is setup to field 'id'
        return [
      'id',
      'ip',
      'service_date_str',
      'service_datetime',
      'service_timestamp',
      'devide',
      'user_agent',
      'browser_name',
      'browser_version',
      'browser_date',
      'browser_lang',
      'operate',
      'operate_relase',
      'domain',
      'url',
      'title',
      'refer_url',
      'first_referrer_domain',
      'is_return',
      'uuid',
      'device_pixel_ratio',
      'resolution',
      'color_depth',
      'website_id',
      'sku',
      'country_code',
      'country_name',
      
      'order_status',
      'cart',
      'order',
      'category',
      'login_email',
      'register_email',
      'search',
      'currency',
    ];
    }
  
  
  
}

 

安装ElasticSearch ,以及在yii2中的使用

ElasticSearch 是一款优秀的搜索引擎,用java编写,restful接口的方式进行对接。

1. 安装ElasticSearch

安装Java环境
首先检测是否安装java

java -version  
echo $JAVA_HOME

如果java的版本过低,建议安装高版本,下面安装的是java 1.8

cd /opt/    
wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-linux-x64.tar.gz"    
tar xzf jdk-8u66-linux-x64.tar.gz

上面的下载,如果失效,您可以在我的百度网盘中下载jdk: https://pan.baidu.com/s/1kVwRD2Z

 

cd /opt/jdk1.8.0_66/    
alternatives --install /usr/bin/java java /opt/jdk1.8.0_66/bin/java 2    
alternatives --config java

运行了上面的,会初选一个选择的地方,我的机器显示:

There are 3 programs which provide 'java'.    
    
  Selection    Command    
-----------------------------------------------    
*  1           /opt/jdk1.7.0_71/bin/java    
 + 2           /opt/jdk1.8.0_45/bin/java    
   3           /opt/jdk1.8.0_51/bin/java    
   4           /opt/jdk1.8.0_66/bin/java    
    
Enter to keep the current selection[+], or type selection number: 4

我们安装的是jdk1.8.0.66 所以,我选择的是4,这个看具体情况,jdk1.8.0.66 是第几个,就选择那个数字。

alternatives --install /usr/bin/jar jar /opt/jdk1.8.0_66/bin/jar 2    
alternatives --install /usr/bin/javac javac /opt/jdk1.8.0_66/bin/javac 2    
alternatives --set jar /opt/jdk1.8.0_66/bin/jar    
alternatives --set javac /opt/jdk1.8.0_66/bin/javac

安装完成,检查版本

java -version    
    
java version "1.8.0_66"    
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)    
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

保存到文件  /etc/environment中,当服务器重启的时候加载

vi /etc/profile  
  
export JAVA_HOME=/opt/jdk1.8.0_66    
export JRE_HOME=/opt/jdk1.8.0_66/jre    
export PATH=$PATH:/opt/jdk1.8.0_66/bin:/opt/jdk1.8.0_66/jre/bin

重启linux

reboot

查看是否安装成功

java -version  
echo $JAVA_HOME

 

2.

2.1安装ElasticSearch

cd /tools  
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.1.3.zip 
unzip elasticsearch-6.1.3.zip 
mv ./elasticsearch-6.1.3 /usr/local/elasticsearch  
cd /usr/local  
groupadd elasticsearch  
useradd -g elasticsearch  elasticsearch  
chown elasticsearch:elasticsearch -R elasticsearch

上面安装的是es6.

2.2 设置开机启动: 我是本地,直接把iptables 关掉了

vim /etc/rc.d/rc.local  

service iptables stop  
su elasticsearch  -c "/usr/local/elasticsearch/bin/elasticsearch -d"

一定要注意,elasticSearch不能用root账户启动,elasticSearch不能用root账户启动,elasticSearch不能用root账户启动,重要的说三遍,我这里用的是我新建的elasticsearch账户开机启动

报错:

$./bin/elasticsearch
Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000085330000, 2060255232, 0) failed; error='Cannot allocate memory' (errno=12)
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 2060255232 bytes for committing reserved memory.
# An error report file with more information is saved as:
# /data/elasticsearch-5.2.2/hs_err_pid26945.log

解决方案:调小启动内存

# vi /usr/local/elasticsearch/config/jvm.options

#-Xms2g
#-Xmx2g
-Xms256m
-Xmx64m

上面设置的分配的内存的最大值为256MB和最小值64mb,您可以根据自己的机器情况设置内存大小。

重新启动即可。

 

2.3 配置

vim /usr/local/elasticsearch/config/elasticsearch.yml

修改如下:

cluster.name: TA-application  
node.name: node-210  
network.host: 192.168.0.210

其中cluster.name 是集群名称,这个不要使用默认的,要修改,去掉注释,如果有多个机器,加入同一个集群,那么这个值必须一样

noide.name 是集群里面每个节点的值,也就是当前机器的节点的值,这个值,每个节点要不一样。

network host 改成当前的内网ip

 

下面的部分是elasticsearch 2 部分的插件,在es6中已经不可用,

es6的可视化GUI,请查看:http://www.fecshop.com/topic/668

 

 

2.3  #安装head 插件

su elasticsearch  
  
cd  /usr/local/elasticsearch  
  
bin/plugin install mobz/elasticsearch-head  
  
#启动:  
  
bin/elasticsearch -d

2.4 查看:

http://192.168.0.210:9200/

head插件地址:

http://192.168.0.210:9200/_plugin/head/

2.5 集群设置

如果想要建立一个elasticSearch集群,可以按照下面的步骤,非常的简单,首先,想说明的是:对于elasticSearch,他隐藏了分布式的复杂性,分片和复制集,都是他自动完成,你只需要配置好ip就可以了,下面是配置的步骤:

我有两台机器  192.169.0.210   192.168.0.199

我的两台机器都按照上面的步骤配置完成,下面配置集群

首先是192.168.0.210

vim /usr/local/elasticsearch/config/elasticsearch.yml

#找到行 , 修改如下:

discovery.zen.ping.unicast.hosts: ["192.168.0.199"]

上面的ip就是其他的节点的ip,如果我有5台机器,那么,这里需要把其他四台机器的ip写上。

同理,对于其他的节点,需要把其他的节点协商,用逗号隔开

elasticSearch会找到对应的节点,自动分片和做复制集。

3. 资料片

官方文档:

https://www.elastic.co/guide/en/elasticsearch/reference/5.0/_installation.html

入门教程

http://www.jianshu.com/p/f437b893502a

权威指南

http://es.xiaoleilu.com/

 

4.  Elasticsearch集群关闭节点

关闭节点

关闭节点的API允许关闭集群中的一个或多个(或者全部)节点。下面是一个关闭 _local 节点的例子:

$ curl -XPOST 'http://localhost:9200/_cluster/nodes/_local/_shutdown' 

也可以通过各自的节点ID来关闭指定的节点(或者像这里说明 的别的选项):

$ curl -XPOST 'http://localhost:9200/_cluster/nodes/nodeId1,nodeId2/_shutdown' 

集群的主节点也可以使用下面的方法来关闭:

$ curl -XPOST 'http://localhost:9200/_cluster/nodes/_master/_shutdown' 

最后,可以使用如下的任意一种方法来关闭所有的节点:

$ curl -XPOST 'http://localhost:9200/_shutdown' $ curl -XPOST 'http://localhost:9200/_cluster/nodes/_shutdown' $ curl -XPOST 'http://localhost:9200/_cluster/nodes/_all/_shutdown' 

延迟

默认情况下,关闭命令会延迟1秒(1s)之后执行。可以通过设置 delay 参数 来指定延迟的时间。比如:

$ curl -XPOST 'http://localhost:9200/_cluster/nodes/_local/_shutdown?delay=10s' 

禁用关闭命令

关闭的API可以通过设置节点里的 action.disable_shutdown 选项来禁用。

5. Yii2 使用elasticSearch

首先需要安装yii2的elasticSearch插件

https://github.com/yiisoft/yii2-elasticsearch

安装步骤,参看官网的下面的说明,用起来还是不错

里面有聚合功能,可以做一些小规模数据的快速统计。

yii2 elasticSearch的使用说明地址:https://github.com/yiisoft/yii2-elasticsearch/blob/master/docs/guide/README.md

 

 

 

 

 

Fecshop 功能添加部分

1.产品加入一个字段,收藏次数,客户收藏后,个数+1,客户取消后,收藏-1
2.后台加入一个产品收藏列表展示,只查看即可
3.在后台 , 展示前台的用户,在后台可以编辑前台用户信息
4.产品加入一个按照收藏个数排序的功能。
5.产品加入一个相关产品的列表,然后在前台产品页面可以调取出来
6.交叉产品,在购物车页面展示,也就是产品在加入购物车后,根据购物车的产品,展示对应的产品。

随着滚动条滚动的Tab切换 – js例子

在电商网站,我们有一些tab 想要他,随着下拉,tab自动切换的方式,

这样无论对seo还是客户体验都很不错,下面是实现的一个例子,代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
<html xmlns="http://www.w3.org/1999/xhtml">  
<head>  
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
<title>随着滚动条滚动的Tab切换</title>  
<style>  
*{  
    list-style:none;  
    margin:0;  
    padding:0;  
    text-decoration:none;  
    font-family:'Microsoft YaHei';  
  
}  
li{  
    width:100px;  
    height:50px;  
    line-height:50px;  
    float:left;  
    border-right:2px solid #eee;  
    text-align:center;  
    cursor:pointer;  
}  
ul{  
    width:1200px;     
    margin:0 auto;  
}  
.nav{  
    height:52px;  
    width:100%;  
    background:#f5f5f5;  
}  
.nav .cur{  
    background:#fff;  
    border-top:2px solid #1a92cf;  
    color:#1a92cf;  
}  
.fixed{  
    position:fixed;  
    top:0;  
    left:0;  
}  
a{  
    color:#505050;  
}  
</style>  
</head>  
<body>  
<br/><br/><br/><br/><br/><br/><br/>  
<div class="nav" id="nav-container">  
    <ul id="nav-box">
        <li class="cur">text1-nav</li>  
        <li>text2-nav</li>  
        <li>text3-nav</li>  
    </ul>  
</div>  
<div id="text">  
    <div style="width:100%;height:500px;background:green;text-align:center;">text1</div>  
    <div style="width:100%;height:500px;background:yellow;text-align:center;">text2</div>  
    <div style="width:100%;height:500px;background:blue;text-align:center;">text3</div>   
</div>  
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>  
  
<script>  
var navContainer = document.getElementById("nav-container");  
var navBox = document.getElementById("nav-box");  
var text = document.getElementById("text");  
var navBoxChild = navBox.children;  
var textChild = text.children;  
var num = navContainer.offsetTop;  
var a = navContainer.offsetHeight;  
window.onscroll = function(){  
    var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;  
    if(scrollTop >= num){  
        navContainer.className = "nav fixed";  
        text.style.paddingTop = a +"px";  
    }else{  
        navContainer.className = "nav";  
        text.style.paddingTop = "";  
    }  
    //当导航与相应文档接触的时候自动切换  
    //method1  
    for(var i=0;i<navBoxChild.length;i++){  
        if( scrollTop + a >= textChild[i].offsetTop){  
            for(var j=0;j<navBoxChild.length;j++){  
                navBoxChild[j].className = "";  
            }  
            navBoxChild[i].className = "cur";  
       }  
    }  
};  
for(var i=0;i<navBoxChild.length;i++){  
    var interval;  
    navBoxChild[i].index = i;  
    navBoxChild[i].onclick = function(){  
        var self = this;  
        clearInterval(interval);  
    if(document.body.scrollTop){
      scroll = document.body;
    }else if(document.documentElement.scrollTop){
      scroll = document.documentElement;
    }
        interval = setInterval(function(){  
      if(scroll.scrollTop + a<=textChild[self.index].offsetTop){  
        scroll.scrollTop += 40;  
        if(scroll.scrollTop + a>=textChild[self.index].offsetTop){  
          scroll.scrollTop = textChild[self.index].offsetTop-a;  
          clearInterval(interval);  
        }  
      }else{  
        scroll.scrollTop /= 1.1;  
        if(scroll.scrollTop + a<=textChild[self.index].offsetTop){  
          scroll.scrollTop = textChild[self.index].offsetTop-a;  
          clearInterval(interval);  
        }  
      }  
    },40); 
    };  
}  
</script>    
</body>  
</html>

 

Geoip geoip-api-php 库包使用 – 通过ip 找到国家

通过js 接收了用户的ip,我们需要通过ip得到用户的国家,可以通过一个库包来实现,下面是详细:
1.下载geoip的数据库:

cd geoip/  
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz  
gzip -d GeoIP.dat.gz  
ls  
GeoLiteCity.dat

 

其他库: 查看

GeoLite免费数据库

 

2. 下载php库包:

wget http://www.maxmind.com/download/geoip/api/php/php-latest.tar.gz  
unzip php-latest.tar.gz

3.使用:

include("/www/web/develop/marketsystem/common/lib/geoip-api-php-1.14/src/geoip.inc");  
  
//得到国家。通过ip  
function getCountryByIp($ip){  
    $gi = geoip_open("/www/web/develop/marketsystem/common/lib/geoip/GeoIP.dat", GEOIP_STANDARD);  
    if(strstr($ip,":")){  
        $country_code = geoip_country_code_by_addr_v6($gi,$ip);  
        $country_name = geoip_country_name_by_addr_v6($gi,$ip);  
    }else{  
        $country_code = geoip_country_code_by_addr($gi,$ip);  
        $country_name = geoip_country_name_by_addr($gi,$ip);  
    }  
    geoip_close($gi);  
    return [  
        "country_code" => $country_code,  
        "country_name" => $country_name,  
    ];  
}

 

类似谷歌,在网站里面嵌入js,接收各种浏览器信息以及网站信息

需要3个步骤:js 收集,调动接口, php接收,通过ip找到国家,然后存储

1.

需要在网页里面嵌入js代码:

<script type="text/javascript">
  var _maq = _maq || [];
  _maq.push(['website_id', '3']);
  (function() {
    var ma = document.createElement('script'); ma.type = 'text/javascript'; ma.async = true;
    ma.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'static.tomtop-cdn.com/tomtop/js/trace.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ma, s);
  })();
</script>

上下面的代码,是在每一个页面都需要加入,您可以加入到页面的底部

如果您想收集某些页面的单独的数据,譬如:产品页面的sku,你可以加入下面的代码,这个代码必须在上面的代码的前面:

<script type="text/javascript">
  var _maq = _maq || [];
  _maq.push(['sku', '$sku']);
</script>

一定要注意,这个代码一定要在上面

如果传递购物车信息,可以用json个数传递

2. 全局加入的js代码可以看到有个trace.js    这个js文件就是下面的内容

(function () {
  
  var nVer = navigator.appVersion;
  var nAgt = navigator.userAgent;
  var browserName  = navigator.appName;
  var fullVersion  = ''+parseFloat(navigator.appVersion); 
  var majorVersion = parseInt(navigator.appVersion,10);
  var nameOffset,verOffset,ix;

  // In Opera, the true version is after "Opera" or after "Version"
  if ((verOffset=nAgt.indexOf("Opera"))!=-1) {
   browserName = "Opera";
   fullVersion = nAgt.substring(verOffset+6);
   if ((verOffset=nAgt.indexOf("Version"))!=-1) 
     fullVersion = nAgt.substring(verOffset+8);
  }
  // In MSIE, the true version is after "MSIE" in userAgent
  else if ((verOffset=nAgt.indexOf("MSIE"))!=-1) {
   browserName = "Microsoft Internet Explorer";
   fullVersion = nAgt.substring(verOffset+5);
  }
  // In Chrome, the true version is after "Chrome" 
  else if ((verOffset=nAgt.indexOf("Chrome"))!=-1) {
   browserName = "Chrome";
   fullVersion = nAgt.substring(verOffset+7);
  }
  // In Safari, the true version is after "Safari" or after "Version" 
  else if ((verOffset=nAgt.indexOf("Safari"))!=-1) {
   browserName = "Safari";
   fullVersion = nAgt.substring(verOffset+7);
   if ((verOffset=nAgt.indexOf("Version"))!=-1) 
     fullVersion = nAgt.substring(verOffset+8);
  }
  // In Firefox, the true version is after "Firefox" 
  else if ((verOffset=nAgt.indexOf("Firefox"))!=-1) {
   browserName = "Firefox";
   fullVersion = nAgt.substring(verOffset+8);
  }
  // In most other browsers, "name/version" is at the end of userAgent 
  else if ( (nameOffset=nAgt.lastIndexOf(' ')+1) < 
        (verOffset=nAgt.lastIndexOf('/')) ) 
  {
   browserName = nAgt.substring(nameOffset,verOffset);
   fullVersion = nAgt.substring(verOffset+1);
   if (browserName.toLowerCase()==browserName.toUpperCase()) {
    browserName = navigator.appName;
   }
  }
  // trim the fullVersion string at semicolon/space if present
  if ((ix=fullVersion.indexOf(";"))!=-1)
     fullVersion=fullVersion.substring(0,ix);
  if ((ix=fullVersion.indexOf(" "))!=-1)
     fullVersion=fullVersion.substring(0,ix);

  majorVersion = parseInt(''+fullVersion,10);
  if (isNaN(majorVersion)) {
   fullVersion  = ''+parseFloat(navigator.appVersion); 
   majorVersion = parseInt(navigator.appVersion,10);
  }

//////////////////////得到手机的设备名称
  operate_relase = "";
  var OS_Name = navigator.appVersion;
          if (OS_Name.indexOf("Win") != -1) {	 
        operate = "Windows";
        
        if ((OS_Name.indexOf("Windows 95") != -1)||
        (OS_Name.indexOf("Win95") != -1) ||
        (OS_Name.indexOf("Windows_95") != -1)
        ) {	 
          operate_relase = "Windows 95";
        }else if ((OS_Name.indexOf("Windows 98") != -1)||
        (OS_Name.indexOf("Win98") != -1)) {	
          operate_relase = "Win98";
          
        }else if ((OS_Name.indexOf("Windows NT 5.0") != -1)||
        (OS_Name.indexOf("Windows 2000") != -1)) {	
          operate_relase = "Windows 2000";
          
        }else if ((OS_Name.indexOf("Windows NT 5.1") != -1)||
        (OS_Name.indexOf("Windows XP") != -1)) {	
          operate_relase = "Windows XP";
          
        }else if (OS_Name.indexOf("Win16") != -1) {	
          operate_relase = "Windows 3.11";
          
        }else if (OS_Name.indexOf("Windows NT 5.2") != -1) {	
          operate_relase = "Windows Server 2003";
          
        }else if (OS_Name.indexOf("Windows NT 6.0") != -1) {	
          operate_relase = "Windows Vista";
          
        }else if (OS_Name.indexOf("Windows NT 6.1") != -1) {	
          operate_relase = "Windows 7";
          
        }else if ((OS_Name.indexOf("Windows NT 4.0") != -1)||
        (OS_Name.indexOf("WinNT4.0") != -1) ||
        (OS_Name.indexOf("WinNT") != -1)||
        (OS_Name.indexOf("Windows NT") != -1)) {	
          operate_relase = "Windows NT 4.0";
        }else if (OS_Name.indexOf("Windows ME") != -1) {	
          operate_relase = "Windows ME";
        }

        
          } else if (OS_Name.indexOf("Mac") != -1) {
        operate = "Mac OS";	
          } else if (OS_Name.indexOf("X11") != -1) {
        operate = "Unix";	
          } else if (OS_Name.indexOf("Linux") != -1) {
        operate = "Linux";
          } else if (OS_Name.indexOf("SunOS") != -1) {
        operate = "Sun OS";
          } else if (OS_Name.indexOf("OpenBSD") != -1) {
        operate = "Open BSD";
        
      } else if (OS_Name.indexOf("QNX") != -1) {
        operate = "QNX";
      } else if (OS_Name.indexOf("BeOS") != -1) {
        operate = "BeOS";
      } else if (OS_Name.indexOf("OS/2") != -1) {
        operate = "OS/2";
      } else if ((OS_Name.indexOf("nuhk") != -1) 
      || (OS_Name.indexOf("Googlebot") != -1)
      || (OS_Name.indexOf("Yammybot") != -1)
      || (OS_Name.indexOf("Openbot") != -1)
      || (OS_Name.indexOf("Slurp") != -1)
      || (OS_Name.indexOf("MSNBot") != -1)
      || (OS_Name.indexOf("Ask Jeeves/Teoma") != -1)
      || (OS_Name.indexOf("ia_archiver") != -1)
      ) {
        operate = "Search Bot";
      }else{
        operate = "unknow";
      }
  /////////////////////

  //通过参数,得到url中参数的值
  function getParameterByName(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
      results = regex.exec(location.search);
    return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
  }
  //得到当前的时间
  function getDate(){
    var currentdate = new Date(); 
    
    month = currentdate.getMonth();if(month<10){month = "0"+month;}
    day = currentdate.getDate();if(day<10){day = "0"+day;}
    hours = currentdate.getHours();if(hours<10){hours = "0"+hours;}
    minutes = currentdate.getMinutes();if(minutes<10){minutes = "0"+minutes;}
    second = currentdate.getSeconds();if(second<10){second = "0"+second;}
    var datetime =  currentdate.getFullYear() + "-"  
        + month  + "-" 
        + day + " "
                + hours + ":"  
                + minutes + ":" 
                + second;
    return datetime ;
  }
  
  
  
  //得到设备名称
  function getDevice(){
    var isMobile = {
      Android: function() {
      return navigator.userAgent.match(/Android/i);
      },
      webOS:function() {
      return navigator.userAgent.match(/webOS/i);
      },
      BlackBerry: function() {
      return navigator.userAgent.match(/BlackBerry/i);
      },
      iPhone: function() {
      return navigator.userAgent.match(/iPhone/i);
      },
      iPad: function() {
      return navigator.userAgent.match(/iPad/i);
      },
      iPod: function() {
      return navigator.userAgent.match(/iPod/i);
      },
      Opera: function() {
      return navigator.userAgent.match(/Opera Mini/i);
      },
      Windows: function() {
      return navigator.userAgent.match(/IEMobile/i);
      },
      any: function() {
      return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iPad() ||isMobile.iPod() || isMobile.iPhone() || isMobile.Opera() || isMobile.Windows());
      }
    };
    var str = "";
    if( isMobile.Android() ) { str = "Android";  }
    if( isMobile.webOS() ) { str = "webOS";  }
    if( isMobile.BlackBerry() ) { str = "BlackBerry";  }
    if( isMobile.iPhone() ) { str =  "iPhone";  }
    if( isMobile.iPad() ) { str =  "iPad";  }
    if( isMobile.iPod() ) { str =  "iPod";  }
    if( isMobile.Opera() ) { str =  "Opera";  }
    if( isMobile.Windows() ) { str =  "Windows";  }
    if(str){
      return "Mobile:"+str;
    }else{
      return "PC";
    }
    
  }
  
  //////////////
  function s4() {
    return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
  };
   
  function guid() {
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
  }
  ////得到唯一标示码uuid 
  var uuid = guid();
  ////设置cookie,默认是设置天  expires代表的是天数。
  function Set_Cookie( name, value, expires, path, domain, secure )
  {
    // set time, it's in milliseconds
    
    domain = document.domain;
    
    domain = domain.replace("www.","");
    
    var today = new Date();
    today.setTime( today.getTime() );

    /*
    if the expires variable is set, make the correct
    expires time, the current script below will set
    it for x number of days, to make it for hours,
    delete * 24, for minutes, delete * 60 * 24
    */
    if ( expires )
    {
      expires = expires * 1000 * 60 * 60 * 24;
    }else{
      expires = expires * 20 * 365 * 1000 * 60 * 60 * 24;
    }
    var expires_date = new Date( today.getTime() + (expires) );
    
    this_expires = expires/1000;
    //document.write("thisdomain:"+domain+"<br/>");
    document.cookie = name + "=" +escape( value ) +
    ( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) +
    ( ( path ) ? ";path=" + path : "" ) +
    ( ( domain ) ? ";domain=" + domain : "" ) +
    ( ( secure ) ? ";secure" : "" );
    
    
  }
  //得到cookie
  function Get_Cookie( check_name ) {
    // first we'll split this cookie up into name/value pairs
    // note: document.cookie only returns name=value, not the other components
    var a_all_cookies = document.cookie.split( ';' );
    var a_temp_cookie = '';
    var cookie_name = '';
    var cookie_value = '';
    var b_cookie_found = false; // set boolean t/f default f

    for ( i = 0; i < a_all_cookies.length; i++ )
    {
      // now we'll split apart each name=value pair
      a_temp_cookie = a_all_cookies[i].split( '=' );


      // and trim left/right whitespace while we're at it
      cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');

      // if the extracted name matches passed check_name
      if ( cookie_name == check_name )
      {
        b_cookie_found = true;
        // we need to handle case where cookie has no value but exists (no = sign, that is):
        if ( a_temp_cookie.length > 1 )
        {
          cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
        }
        // note that in cases where cookie is initialized but no value, null is returned
        return cookie_value;
        break;
      }
      a_temp_cookie = null;
      cookie_name = '';
    }
    if ( !b_cookie_found )
    {
      return null;
    }
  }
  //删除cookie
  function Delete_Cookie( name, path, domain ) {
    if ( Get_Cookie( name ) ) document.cookie = name + "=" +
    ( ( path ) ? ";path=" + path : "") +
    ( ( domain ) ? ";domain=" + domain : "" ) +
    ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
  }
  
  
    var params = {};
  
  // 设置  cid   如果新的cid访问,那么cid将会被覆盖。 
  cid = getParameterByName("cid");
  var cookie_cid = Get_Cookie("cid");
  
  if(cid){
    //set cid cookie.
    params.cid = cid;
    Set_Cookie( "cid", cid, 7, "/", '', '' );
  }else if(cookie_cid){
    params.cid = cookie_cid;
  }
  
  //设备
  params.devide = getDevice();
  params.user_agent = navigator.userAgent;
  var userLang = navigator.language || navigator.userLanguage; 
  params.browser_name 	= browserName;  //浏览器名称
  params.browser_version 	= majorVersion;  //浏览器版本
  params.browser_date 	= getDate();  //浏览器时间
  params.browser_lang 	= userLang;   //浏览器语言
  params.operate 			= operate;     //操作系统
  params.operate_relase 	= operate_relase;  //操作系统详细
  
  
  
    //Document对象数据
  //通过加入下面的代码,来辨别是否是老客户
  //标准为:refer domain为空,或者不包含  && _fta 这个cookie存在
  //在数据分析中,客户中间客户点击刷新页面,也会被标示成return:1,因此,在分析的时候,需要找UID
  //最小的那个,查看refer。
    if(document) {
    params.domain = document.domain || '';   //域名
    params.url = document.URL || '';    //当前url
    params.title = document.title || '';   //当前title
    params.refer_url = document.referrer || '';  //来源referrer
    
    
    //thisrefer =  document.referrer || '';
    //refer_fta_cookie = Get_Cookie( '_fta' );
    
    //此cookie用来判断客户是否是持续的访问网站,如果设置的是6个小时,如果客户在6个小时内第二次访问网站
    //系统会认为这是客户的一次连贯的访问。第二次访问后,此cookie会更新超时时间
    _fto = Get_Cookie( '_fto' );  
    // //永久cookie,这个cookie存在,那么说明是老客户了
    _fta = Get_Cookie( '_fta' );   
    
    if(_fto){
      //继续的访问,访问的延伸。无所谓refer,和return
      //更新超时时间:
      Set_Cookie( '_fto',1, 0.25, '/', '', '' );   //online - one day
    }else{
      //相当于第一次访问。
      
      thisreferrer = document.referrer || '';
      // 存在refer 则记录refer  ,如果不存在,则设置redirect
      if(!thisreferrer){
        thisreferrer_domain = "redirect";
      }else{
        thisreferrer_domain = thisreferrer.replace('http://','').replace('https://','').split(/[/?#]/)[0];
      }
      // 第一次访问网站的时候会记录访问来源。
      Set_Cookie( '_ftreferdomain',thisreferrer_domain, 1, '/', '', '' );   //refer  - one day
      // 第一次访问的时候会设置_fto为6个小时。
      Set_Cookie( '_fto',1, 0.25, '/', '', '' );   //online - one day
          
        
      //如果存在_fta 则代表这个客户肯定访问过网站信息。
      if(_fta){
        //老客户,设置的cookie:来源域名,是否在线,是否是老客户
        Set_Cookie( '_ftreturn',1, 36500, '/', '', '' );   // is return  - one day
      }else{
        //新客户
        Set_Cookie( '_ftreturn',0, 36500, '/', '', '' );   // is return  - one day
      }
    }
    //referrer 域名,是否是老客户,是否是在线状态
    params.first_referrer_domain = Get_Cookie('_ftreferdomain');
    params.is_return = Get_Cookie('_ftreturn');
    //params.online = Get_Cookie('_fto');

    
    //设置uuid,如果 _fta存在,那么设置uuid为它的值,如果不存在,那么重新获取uuid。
    if(cookie_uuid = Get_Cookie('_fta') ){
      params.uuid = cookie_uuid;
    }else{
      params.uuid = uuid;
      Set_Cookie( '_fta', uuid, 36500, '/', '', '' );
    }
    
    
    
    }
    //Window对象数据
    if(window && window.screen) {
    //params.sh = window.screen.height || 0;
    //屏幕分辨率和屏幕的画质
    if(window.devicePixelRatio){
      devicePixelRatio = window.devicePixelRatio;
      params.device_pixel_ratio = devicePixelRatio;
      params.resolution = (window.screen.width*devicePixelRatio || 0) +"x"+ (window.screen.height*devicePixelRatio || 0);
    }else{
      params.resolution = (window.screen.width || 0) +"x"+ (window.screen.height || 0);
    }
    params.color_depth = window.screen.colorDepth || 0;
    }
  
    
  
  
    //解析_maq配置
  if(_maq) {
    for(var i in _maq) {
      x = _maq[i][0];
      if(x){
        //email
        //当前的customer email如果和cookie一样,那么使用customer email
        //如果不一样,那么保存customer email到cookie _fte
        //对于customer name同样也是这样
        if(x == 'login_email'){
          current_customer_email = _maq[i][1];
          //存在cookie 
          if(cookie_customer_email = Get_Cookie( '_fte' )){
            //当前cookie存在,并且与传递过来的相同
            if(current_customer_email != cookie_customer_email){
              Set_Cookie( '_fte', current_customer_email, '36500', '/', '', '' );
            }
          //不存在cookie
          }else{
            Set_Cookie( '_fte', current_customer_email, '36500', '/', '', '' );
          }
          params[x] = current_customer_email;
        
        }else{
          params[x] = _maq[i][1];
        }
      }
    }
  }
  //如果cookie中存在客户邮箱,那么从cookie中获取
  if(!params.login_email){
    if(Get_Cookie( '_fte' )){
      params.login_email = Get_Cookie( '_fte' )
    }
  }
  
    //拼接参数串,形成url,然后通过图片的方式传递数据
    var args = '';
    for(var i in params) {
    if(args != '') {
      args += '&';
    }
    args += i + '=' + encodeURIComponent(params[i]);
    }
     
    //通过Image对象请求后端脚本
    var img = new Image(1, 1);
    img.src = 'http://trace.tomtop.com/trace.php?' + args;
 })();

原理:js手机的数据,通过参数的方式拼成一个url  :http://trace.tomtop.com/trace.php? xxxxx ,然后把数据传递给trace.php

 

2. ip找到国家(穿插部分)

Geoip geoip-api-php 库包使用 – 通过ip 找到国家

 

3. php 存储(market.php)这里的geoip就是第二部分的通过ip找到国家

<?php
//exit;
//date_default_timezone_set('Asia/Shanghai');
# 时区
ini_set('date.timezone','UTC');
# ip 库
include("/www/web/market/common/lib/geoip-api-php-1.14/src/geoip.inc");

# log 测试函数
function logdd($info){
  if(is_array($info) || is_object($info)){
    $info = var_export($info,true);
  }
  //echo $info;exit;
  $logfile = './logs/my.log';
  $handle = fopen($logfile,"a+");/*根据需要更改这里的参数*/ 
  $contents = fwrite($handle,$info."\n"); 
  fclose($handle);
      
}


#得到IP地址。
function get_client_ip() {
  $ipaddress = '';
  if (getenv('HTTP_CLIENT_IP'))
    $ipaddress = getenv('HTTP_CLIENT_IP');
  else if(getenv('HTTP_X_FORWARDED_FOR'))
    $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
  else if(getenv('HTTP_X_FORWARDED'))
    $ipaddress = getenv('HTTP_X_FORWARDED');
  else if(getenv('HTTP_FORWARDED_FOR'))
    $ipaddress = getenv('HTTP_FORWARDED_FOR');
  else if(getenv('HTTP_FORWARDED'))
     $ipaddress = getenv('HTTP_FORWARDED');
  else if(getenv('REMOTE_ADDR'))
    $ipaddress = getenv('REMOTE_ADDR');
  else
    $ipaddress = 'UNKNOWN';
  return $ipaddress;
}

//得到国家。通过ip 支持ipv6  已经测试。
function getCountryByIp($ip){
  $gi = geoip_open("/www/web/market/common/lib/geoip/GeoIP.dat", GEOIP_STANDARD);
  if(strstr($ip,":")){
    $country_code = geoip_country_code_by_addr_v6($gi,$ip);
    $country_name = geoip_country_name_by_addr_v6($gi,$ip);
  }else{
    $country_code = geoip_country_code_by_addr($gi,$ip);
    $country_name = geoip_country_name_by_addr($gi,$ip);
  }
  geoip_close($gi);
  return [
    "country_code" => $country_code,
    "country_name" => $country_name,
  ];
}

#得到递增id
/*
function increament($m,$db_name,$tablename){
  
  $ids = "ids";
  $db_name = "tracedb";
  $db = $m->selectDB($db_name);
  $col = $m->selectDB($db_name)->$ids;
  
  $query = array('name'=>'trace_2014_11_24');
  $update = array('?inc'=>array('id'=>1));
  $result = $db->command(
            array(
            "findandmodify" => "ids",
            "query" => $query,
            "update" => $update,
            )
          );

  return $result['value']['id'];
} 
*/

$delay_time = " -0 hours";	
$insertTimeStamp = strtotime(date('Y-m-d H:i:s').' '.$delay_time);
$insertDateTime = date('Y-m-d H:i:s',$insertTimeStamp);
$insertDate = date('Y-m-d',$insertTimeStamp);
$get 						= array();
$get['ip'] 					= get_client_ip();
#$get['server_datetime']	= date('Y-m-d H:i:s',strtotime('-12 hours'));    //+3 hours  延后12个小时,美国时间
$get['service_date_str']	= $insertDate;
$get['service_datetime'] 	=  new MongoDate($insertTimeStamp);   #北京时间
$get['service_timestamp'] 	=  (float)$insertTimeStamp;
foreach($_GET as $k=>$v){
  $get[$k] = $v;
}
  # ip6 example
  #$get['ip'] = "2001:0DB8:0:0:0:0:1428:0000";
$ip 					= $get['ip'];
$website_id 			= $get['website_id'];
$countInfo 				= getCountryByIp($ip);
$get['country_code'] 	= $countInfo['country_code'];
$get['country_name'] 	= $countInfo['country_name'];


#连接到mongodb
$m = new MongoClient('mongodb://localhost:27017');
$database_name 		= "tracedb";  
#trace_网站编号_年份_月_initial_data
$collection_name 	= "trace_".$website_id."_".date("Y_m",strtotime($delay_time))."_initial_data";
$db 		= $m->$database_name;
$collection = $db->$collection_name;
#得到递增id。
#$id = increament($m,$database_name,$collection_name);

#$get["_id"] = $id; 
#var_dump($get);
#订单信息
if($get['order']){
  $get['order'] = json_decode($get['order'],true);
  $get['order_status'] = 'fail';
}
#购物车信息。
if($get['cart']){
  $get['cart'] = json_decode($get['cart']);
}

#购物车信息。
if($get['search']){
  $get['search'] = json_decode($get['search']);
}


#插入数据到mongodb
if(!empty($get) && is_array($get)){
  $collection->insert($get);
  echo "Insert succsessfully";
}

   
   
   
   
   
   
   
   
   

这样就把数据保存到mongodb里面去了。

然后就可以通过mongodb的mapreduce进行统计分析数据了。

mongodb 导出csv文件

/usr/bin/mongoexport  --csv  -d erp -f _id,item_no,gross_default,profit,product_cost,order_gross_default,order_profit,order_product_cost,profit_difference,gross_difference,product_cost_difference -c tmp_erp_order_check_problem_profit -o  /backup/erp/tmp_erp_order_check_problem_profit-07-09.csv

/usr/bin/mongoexport  --csv  -d erp  -f _id,item_no,gross_default,profit,product_cost,order_gross_default,order_profit,order_product_cost,profit_difference,gross_difference,product_cost_difference  -c tmp_erp_order_check_problem_gross -o  /backup/erp/tmp_erp_order_check_problem_gross-07-09.csv

/usr/bin/mongoexport  --csv  -d erp  -f _id,item_no,gross_default,profit,product_cost,order_gross_default,order_profit,order_product_cost,profit_difference,gross_difference,product_cost_difference  -c tmp_erp_order_check_problem_product_cost -o  /backup/erp/tmp_erp_order_check_problem_product_cost-07-09.csv

上面是mongodb 表  导出 csv 的一个例子

通过配置的方式重写某个Yii2 文件 或第三方扩展文件

1.我需要重写某个Yii的类方法,譬如:yii\helpers\ArrayHelper

我需要新建一个类,继承,然后覆盖这个类的方法。
如果我的系统都成型了,然后我在调用这个类的地方,需要将

use yii\helpers\ArrayHelper

改成

use xxxxxx\yii\helpers\ArrayHelper

2.现在用classMap

Yii::$classMap['yii\helpers\ArrayHelper'] = '@xxxxxx/yii/helpers/ArrayHelper.php';

直接就行了,对yii的文件不需要改动,调用的地方也不用改动。

可能是我在做fecshop考虑重写的事情,看到这个,真的豁然开朗的感觉,
这样可以在不改动yii2文件,和不改动我的fecshop文件的前提下,重写任何文件了

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

下面是代码举例说明的详细步骤:

  1. 下面是我写的一个类,内容如下:
<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\app\appfront\helper\test;
use Yii;
use fec\helpers\CConfig;
use fec\controllers\FecController;
use yii\base\InvalidValueException;
/**
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class My
{
  
  public static function test(){
    echo 'this is my first test php file';
  }
  
}

2. 然后我在controller中对这个类的静态方法进行了调用:

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\app\appfront\modules\Customer\controllers;
use Yii;
use fec\helpers\CModule;
use fec\helpers\CRequest;
use fecshop\app\appfront\modules\AppfrontController;
use fecshop\app\appfront\helper\test\My;
/**
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class AccountController extends AppfrontController
{
  
  public function actionLogin()
    {
    My::test();
    exit;
  }
}

然后我在很多地方对My::test进行了调用,然后我想对这个My类的test的内容进行重写,但是前提是My这个文件是库包文件,我不能直接进行修改,否则,以后的升级会出现问题,那么我需要用一个类继承这个My类,然后重写test()方法,然后在各个调用My::test()的地方修改use部分,改成新的类的namespaces,这种方式的坏处是修改量大,对于维护起来很费劲,下面介绍另外一种方法,通过在Yii::classMap中配置:

Yii::$classMap['yii\helpers\ArrayHelper'] = '@app/components/ArrayHelper.php';

官网部分的介绍为:

http://www.yiiframework.com/doc-2.0/guide-helper-overview.html#customizing-helper-classes

下面是代码步骤:

1.原来的类的内容为:

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\app\appfront\helper\test;
use Yii;
use fec\helpers\CConfig;
use fec\controllers\FecController;
use yii\base\InvalidValueException;
/**
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class My
{
  
  public static function test(){
    echo 'this is my first test php file';
  }
  
}

2.我写一个新类: 文件路径为: appfront/helper/My.php ,我想让controller调用的类为下面的类

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\app\appfront\helper\test;
use Yii;
use fec\helpers\CConfig;
use fec\controllers\FecController;
use yii\base\InvalidValueException;
/**
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class My{
  
  public static function test(){
    echo 'this is my appfront test php file';
  }
  
}

注意:namespace和上面的那个My类的要一样,而不是按照 appfront/helper/My.php 写成 namespace appfront\helper  ,这样会报错的。

3. 我添加Yii::classMap  数组的值的新的My类的文件路径

Yii::$classMap['fecshop\app\appfront\helper\test\My'] = ['@appfront/helper/My.php'];

然后调用后,发现调用的是新的My类。

4. 需要注意的是,新的类的名字必须和之前的类的名字一样,否则会出错,另外,namespace要一致,一样。

5. 我们希望通过配置文件的方式,这样比较方面,我们可以这样做。

5.1 在app/config/下面添加文件 YiiClassMap.php ,内容如下:

<?php
return [
  'fecshop\app\appfront\helper\test\My' => '@appfront/helper/My.php',   
  
];

在web/index.php的代码

$application = new yii\web\Application($config);   上面添加代码:

/**
 * yii class Map Custom
 * 
 */ 
$yiiClassMap = require(__DIR__ . '/../config/YiiClassMap.php');
if(is_array($yiiClassMap) && !empty($yiiClassMap)){
  foreach($yiiClassMap as $namespace => $filePath){
    Yii::$classMap[$namespace] = $filePath;
  }
}

这样,通过上面的配置文件,就可以把classMap执行了,以后如果添加classMap,直接在文件

app/config/YiiClassMap.php 文件里面的数组中添加一条数据就可以了。

您也可以把yii2的库包文件,yii2的某个扩展库包里面的几个文件,通过这种方式进行重写。这个是非常非常非常方便的,尤其对于你写了一个扩展,让大家用,你的扩展需要升级,因此别人不能直接动你的库包文件,不然升级后,修改的会被覆盖,通过这种方式就可以解决这个问题。

总之,这个功能是更好的进行文件重写。

 

对于Yii2的自动加载的原理,可以参看:http://www.digpage.com/autoload.html,这里不多写了。

XunSearch 安装,使用

  1. 安装:
wget http://www.xunsearch.com/download/xunsearch-full-latest.tar.bz2
tar -xjf xunsearch-full-latest.tar.bz2
cd xunsearch-full-1.4.10/
sh setup.sh

第一次安装的话,过程可能会稍显漫长,请不必着急,您大可泡杯茶一边喝一边等待即可。

待命令运行结束后,如果没有出错中断,则表示顺利安装完成,然后就可以启动/重新启动 xunsearch 的后台服务,下面命令中的 $prefix 请务必替换为您的安装目录,而不是照抄。

cd $prefix ; bin/xs-ctl.sh restart

强烈建议您将此命令添加到开机启动脚本中,以便每次服务器重启后能自动启动搜索服务程序, 在 Linux 系统中您可以将脚本指令写进 /etc/rc.local 即可。

其他更加详细的资料参看:

http://www.xunsearch.com/doc/php/guide/start.installation

 

2.