各种场景唤起 App

唤起 App 应该是很常见的问题了。我们在开发 H5 的时候,有一些链路上的功能在 H5 不支持,只能去 App 才能完成。比如,下单、支付等功能。那么在更多的场景能够唤起 App 就显得很重要了。

几步走

  1. 判断浏览器,动态加载对应浏览器的下载逻辑
  2. 通过 universal link、URL Scheme、a 标签、iframe 几种方式找出最适合这个浏览器的唤起方式。
  3. 如果下载了 App,就会走打开逻辑,如果没有下载则走下载逻辑。
  4. 如果已知不能唤起的浏览器引导其它浏览器打开

流程

各个唤起方法对比

没有哪种方式是完美的,每种唤起方式都有它的优势跟劣势,只有将所有的唤起方法在不同浏览器上尝试过才能择优使用。

通用链接(universal link)

通用链接 universal link 是 IOS 9.0 版本以上才可以使用的新的特性。此特性类似于深层链接,并能够方便地通过打开一个 https 链接来直接启动您的客户端应用(手机有安装 App)。对比起以往所使用的 URL Scheme, 这种新特性在实现 web-app 的无缝链接时能够提供极佳的用户体验。

体验一下通用链接

我们可以来体验一下,左边的是通用链接 universal link方式,右边是 URL Scheme 方式。

左边可以直接将 App 打开,而右边需要确认是否打开才可以。

解决的bug:在 ios 12.3 版本之后,Safari 会有一个 bug,就是 URL Scheme 跟 download App 先后一起使用的话,会同时唤起 App 然后再进入 App store,体验极差。使用 universal link 可以完美解决这个问题。

通用链接配置

必须在根目录下或者 域名/.well-known 下面。注意 域名不要使用业务域名添加配置文件,必须要跨域使用,否则会会不能唤起App,直接打开页面。

  • 访问链接 我们假设有三个 App,分别是 appa、appb、appc,我们通过访问 https://jump.test.com/appa/index.html 就可以唤起 appa 了。
  • 配置下载页 如果没有安装 appa 的话就会跳转到 一个 404 页面,因为我们这个页面 https://jump.test.com/appa/index.html 其实并不存在,所以我们需要将这个页面在 nginx 代理到一个下载页面。
  • 疑问 那么你可能有一个疑问,我如果唤起来 app 之后这个页面会不会跳转到下载页呢?答案是不会的,如果唤起了 app 就不会继续跳转页面了,只有在唤不起来的时候才会进入下载页。
apple-app-site-association 配置
{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "ABCD.com.aaa.appa",
        "paths": ["appa/*"]
      },
      {
        "appID": "ABCD.com.aaa.appb",
        "paths": ["appb/*"]
      },
      {
        "appID": "ABCD.com.aaa.appc",
        "paths": ["appc/*"]
      },
      {
        "appID": "ABCD.com.aaa.*",
        "paths": ["*"]
      }
    ]
  }
}
通用链接的优缺点:
  • 优点:提供极佳的用户体验,使用起来也非常方便,解决了同时唤起 app 跟 app store 的问题
  • 缺点:只有在 ios 9 版本之上才能使用,个别浏览器不支持有兼容性问题

URL Scheme 方式

用法

根据协议来判断是哪个 app 唤起。

location.href = "testa://test.aaa.com/home";
判断是否唤起 app

因为唤起 app 并没有回调让我们知道唤起了 app,所以我们只能通过监听页面是否隐藏来实现判断是否唤起 app 的逻辑。当然这种做法还是有很大缺陷的,比如低端机型唤起速度很慢,高端机型要快得多,所以 delay 的时间要设置一个可以接受的值,我们暂时定的是 2500。还有如果用户没有安装 app,也会有 2500 的延时才能安装。

const timer = setTimeout(() => {
  this.__download(options);
}, options.delay);

const visibilitychange = function() {
  const tag = document.hidden || document.webkitHidden;
  tag && clearTimeout(timer);
};

document.addEventListener("visibilitychange", visibilitychange, false);
document.addEventListener("webkitvisibilitychange", visibilitychange, false);
window.addEventListener(
  "pagehide",
  function() {
    clearTimeout(timer);
  },
  false
);
URL Scheme 的优缺点:
  • 优点 1. 兼容性好,大部分机型都能兼容 2. 使用方便
  • 缺点 1. 不能判断是否已经下载了 app,需要自己写逻辑判断 2. 用户体验不如通用链接的方式。

a 标签跟 iframe 方式

这两种方式都是通过创建元素,通过元素属性的特性来做的

a 标签
const a = document.createElement("a");
a.setAttribute("href", __SCHEMA_PATH);
a.click();
iframe
const ifr = document.createElement("iframe");
iframe.src = path;
ifr.src = nativeUrl;
ifr.style.cssText = "display:none;border:0;width:0;height:0;";
document.body.appendChild(ifr);
setTimeout(function() {
  document.body.removeChild(ifr);
}, 1000);
优缺点
  • 优点 在个别场景中如果发现唤起 app 有问题,可以尝试这两种。
  • 缺点 iframe 基本上已经被废弃了,a 标签测试结果只在个别机型中可用。

总结

  1. 微信 必须配置白名单,或者使用 universal link 也可以在 IOS 唤起,表现形式有一点差别
const wxconfig = {
  debug: false,
  appId: conf.appId, // 公众号的 appid
  timestamp: conf.timestamp,
  nonceStr: conf.noncestr,
  signature: conf.signature,
  beta: true,
  jsApiList: ["launchApplication", "getInstallState"]
};
(window.wx && window.wx.config(wxconfig)) || (window.wxconfig = wxconfig);
  1. 百度浏览器必须用 universal link 方式才能在 IOS 唤起
  2. QQ 在 IOS 要使用 a 标签 的方式,即使通用链接的方式也不能将其唤起
if (isIOS) {
  const a = document.createElement("a");
  a.setAttribute("href", __SCHEMA_PATH);
  return a.click();
}
location.href = __SCHEMA_PATH;
  1. 微博,测试京东跟淘宝都可以调起,猜测是设置了白名单,目前没有找到好的办法,只能引导到其它浏览器中打开

测试结果

目前只有微博不能唤起,其它都可以正常唤起

场景 IOS 安卓 是否需要配置白名单
微信 ✔️ ✔️
qq ✔️ ✔️
chrome ✔️ ✔️
小米 browser ✔️ ✔️
uc ✔️ ✔️
qq browser ✔️ ✔️
360 browser ✔️ ✔️
猎豹 ✔️ ✔️
sogou ✔️ ✔️
baidu browser ✔️ ✔️
safari ✔️ ✔️
微博 X X

[1]各种场景唤起 App 骚操作

Linux crontab curl突然运行没成功访问

今天,在执行crontab curl时,访问不成功了,问题是之前完全没有出现过这样的情况。
首先是排查crontab是否有执行
通过命令查看定时任务是否正常执行

service crond restart

执行结果显示是正常执行的,然后进一步查看日志
发现有报错提示
curl:(6) Could not resolve host: www.baidu,cim; Unknown error

上网了解了一下,都说是DNS的问题,因此修改一下DNS配置

[root@localhost ~]# vim /etc/resolv.conf

// 原先的DNS
# Generated by NetworkManager
nameserver 8.8.8.8

// 修改后的DNS
# Generated by NetworkManager
nameserver 114.114.114.114
nameserver 8.8.8.8

结果只是加上了nameserver 114.114.114.114就可以了,网上了解到的可能是电信DNS有调整的缘故,但是具体的原因还是不得而知。

Mysql查询某个字段包含某个值

查询字段某一字段中包含某一ID的记录我们可以用以下的方法:

首先创建表:

CREATE TABLE users(id int(6) NOT NULL AUTO_INCREMENT,PRIMARY KEY (id),name VARCHAR(20) NOT NULL,limits VARCHAR(50) NOT NULL);

添加数据:

1、 INSERT INTO users(name, limits) VALUES('小张','1,2,12');

2、 INSERT INTO users(name, limits) VALUES('小王','11,22,32');

如何查询出limits字段中含有ID=2的记录呢,我们可以用以下的方法:

第一种方法:

SELECT * FROM users WHERE limits like "%2%";

执行后您会发现两条数据都被查询了出来,而第2条数据是我们不想要的,所以我们需要对查询进行优化,考虑所有情况:

SELECT * FROM users WHERE limits like "%,2,%," or "2,%" or "%,2" or "2";

另一种方法就是利用mysql 字符串函数 find_in_set()。

第二种方法:
SELECT * FROM users WHERE find_in_set('2', limits);

这一句即可解决。。

另需注意:mysql字符串函数 find_in_set(str1,str2)函数是返回str2中str1所在的位置索引,str2必须以”,”分割开。

Linux中空间不足的解决办法

在Linux的使用过程中,如果/var单独分区,但是指定的空间不大,在更新系统或者安装大型软件(如搭建Discourse论坛平台)的时候,使用到了该目录。或开启了cron定时任务时/var/spool/clientmqueue里会不停收集错误信息,长年累积下来动辄就有几十个G的空间,就可能导致提示空间不足的情况本文介绍部分解决办法。

使用软链接指向富足的空间区块

首先了解下Linux链接的概念。
链接是为某一个文件在另外一个位置建立一个不同的链接,分为硬链接和软链接。

Linux软链接:它只会在你选定的位置上生成一个文件的镜像,不会占用磁盘空间,命令:ln -s xxx
Linux硬链接:它会在你选定的位置上生成一个和源文件大小相同的文件,命令:ln xxx

无论是软链接还是硬链接,文件都保持同步变化。
因此,使用软链接可以将/var目录下占用空间较大的目录移动到富足的空间区块(如/home)下,使得/var下不再占用空间。
具体实现(/var/spool/clientmqueue目录为例):

mv /var/spool/clientmqueue /home  #将var下的clientmqueue目录移动到home或者其他空间富足的区块中
ln -s /home/clientmqueue /var/spool/clientmqueue  #/var/spool/clientmqueue 指向/home/clientmqueue,这样clientmqueue目录将不再占用/var目录的空间

Linux系统/var/spool/clientmqueue目录下存在大量文件的原因及解决方法

问题现象:linux操作系统中的/var/spool/clientmqueue/目录下存在大量文件。原因分析:

系统中有用户开启了cron,而cron中执行的程序有输出内容,输出内容会以邮件形式发给cron的用户,而sendmail没有启动所以就产生了这些文件;

解决办法:
1、 将crontab里面的命令后面加上 > /dev/null 2>&1
例:
4 3 * * * /usr/bin/w > /dev/null 2>&1
2、知识点:
2>:重定向错误。
2>&1:把错误重定向到输出要送到的地方。即把上述命令的执行结果重定向到/dev/null,即抛弃,同时,把产生的错误也抛弃。
3、清除/var/spool/clientmqueue/目录下的文件:
cd /var/spool/clientmqueue
rm -rf *
如果文件太多,占用空间太大,用上面命令删除慢的话,就执行下面的命令:
cd /var/spool/clientmqueue
ls | xargs rm -f

Mysql中存储过程或函数使用select into语句给变量赋值没有匹配记录时的结果

前言

对select into语句感兴趣是因为看了项目中的一个存储过程引起的,在程序运行之前看了存储过程的逻辑,本以为没有数据时会报错,结果程序却正常运行,这说明我对select into语句理解的问题,同时也暴露了一个知识盲点,所以写了个小例子测试一下,并把测试的过程记录方便日后查找。

创建测试表格

为了更清楚的表明问题,我们创建的表格尽可能的简单,同时为了测试空值的情况,数据列我们不设置默认值,表格命名为’intotest’,创建语句如下:

CREATE TABLE `intotest` (
  `id` int(4) NOT NULL AUTO_INCREMENT,
  `number` int(4),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=binary ROW_FORMAT=DYNAMIC;

插入测试数据

表格建立完成可以使用可视化工具或者insert语句插入测试数据,插入测试数据后查询结果如下:

mysql> select * from intotest;
+----+--------+
| id | number |
+----+--------+
|  1 |      1 |
|  2 |      2 |
|  3 |   NULL |
+----+--------+
3 rows in set (0.00 sec)

建立一个存储过程

我们建立一个用于测试的存储过程,主要的逻辑就是看看当select into语句找不到匹配记录时,被赋值的变量会怎么样,建立存储过程的代码如下:

CREATE PROCEDURE `select_into_value2`()
BEGIN
    DECLARE _value INT DEFAULT 0;

    SELECT number FROM intotest WHERE id=1 INTO _value;
    SELECT _value;
END

这个存储过程运行正常,配合刚才我们插入表格的记录可以知道,运行后的结果为1:

mysql> call select_into_value();
+--------+
| _value |
+--------+
|      1 |
+--------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

测试过程

当查询结果中不存在符合条件的记录时会怎样,修改存储过程定义,然后查看运行结果:

CREATE PROCEDURE `select_into_value2`()
BEGIN
    DECLARE _value INT DEFAULT 0;

    SELECT number FROM intotest WHERE id=5 INTO _value;
    SELECT _value;
END
mysql> call select_into_value();
+--------+
| _value |
+--------+
|      0 |
+--------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

结果为0,也就是说当查不到匹配结果时,不会执行select into的赋值效果。

当匹配到查询结果但是查询出来的数值为null会怎样,修改存储过程定义,然后查看运行结果:

CREATE PROCEDURE `select_into_value2`()
BEGIN
    DECLARE _value INT DEFAULT 0;

    SELECT number FROM intotest WHERE id=3 INTO _value;
    SELECT _value;
END
mysql> call select_into_value();
+--------+
| _value |
+--------+
|   NULL |
+--------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

结果为NULL,也就是说当查到匹配结果时,不管结果时什么都会赋值到指定的变量中(类型不匹配的sql错误除外)。

当连续查询赋值中间出现不匹配会怎样,修改存储过程定义,然后查看运行结果:

CREATE PROCEDURE `select_into_value2`()
BEGIN
    DECLARE _value INT DEFAULT 0;

    SELECT number FROM intotest WHERE id=2 INTO _value;
    SELECT number FROM intotest WHERE id=5 INTO _value;
    SELECT _value;
END
mysql> call select_into_value();
+--------+
| _value |
+--------+
|      2 |
+--------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

想必明白了前两种情况,这第三种也应该明白了,两条语句顺序执行,找到匹配的就赋值,找不到就放弃操作,结果就保留了上一次成功赋值的结果。

总结

关于select into语句赋值的规则就一句话,找到了符合条件的记录就赋值,找不到就算了。
在找到记录的前提下,如果类型不匹配会导致赋值失败并报错,比如查询到字符串赋值给整型变量。
————————————————
版权声明:本文为CSDN博主「AlbertS」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/albertsh/article/details/84176421

MySQL的 FOUND_ROWS() 函数和 ROW_COUNT() 函数

1.先填个坑
如果你要测试 FOUND_ROWS() 和 ROW_COUNT() 这两个函数,最好就不要用那些MySQL的图形化管理工具软件了(例如,SQLYog)。因为当你使用些工具软件执行某条SQL语句时,可能实际上并不仅仅是执行了这条SQL,这些软件同时会在后台自己执行一些其他SQL语句。所以有时你可能会发现这两个函数返回的结果和你预期的并不一样。所以呢,最好还是用 cmd 窗口来执行SQL进行测试。

2.FOUND_ROWS() 函数
(1) FOUND_ROWS()函数返回的是上一条 SELECT 语句(或 SHOW语句等)查询结果集的记录数。

注意,是上一条 SELECT 语句(即执行该函数前的最近一条SELECT语句),而不是上一条 SQL 语句;因为上一条SQL语句不一定是 SELECT 语句。

且,像 SELECT ROW_COUNT() 这种语句也是 SELECT 语句,它们的结果集也会被 FOUND_ROWS() 函数查出来。

(2)如果上一条 SELECT 语句查询结果为空,则返回 0。

(3)SHOW XXX(例如,show tables、show databases、show status)语句也会被 FOUND_ROWS() 函数查出来。

  1. ROW_COUNT() 函数
    (1)FOUND_ROWS()函数返回的是上一条SQL语句,对表数据进行修改操作后影响的记录数。

如果上一条SQL语句不是修改操作语句(INSERT/UPDATE/DELETE 等),而是查询语句(SELECT/SHOW 等)则返回-1。如果是修改操作语句,则返回修改(增/删/该)影响的记录数。

注意,这里是上一条SQL语句(即执行该函数前的上一条SQL语句),和上面有所区别。

(2)如果上一条SQL语句是UPDATE语句,但是UPDATE后所有数据的值并没有改变,则返回 0。

(3)如果上一条SQL语句是建表语句(创建表或临时表),但创建的是空表,则返回 0。

如果是删除表(DROP语句),则返回的还是 0。

(4)如果是创建临时表,但使用的是 AS 关键字直接将查询出来的值赋值给新建的临时表的话(其实就相当于新建了一个空表,紧接着使用了一条INSERT语句而已),则返回插入的记录数。

CREATE TEMPORARY TABLE tmp_sal AS
SELECT 
    *
FROM 
    employee 
WHERE 
    salary < 12000;

————————————————

原文链接:https://blog.csdn.net/zhou520yue520/article/details/81155248

MySql基础回顾之表定义

1.创建表

CREATE [TEMPORARY] TABLE tbl_name
(
字段名1 数据类型 [列级完整性约束条件] [默认值],
[字段名2 数据类型 [列级完整性约束条件] [默认值]]
[,…]
[表级完整性约束]
)[ENGINE=引擎类型]

CREATE TABLE  user
(
    `uid` int(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `name` varchar(10) NOT NULL DEFAULT '' COMMENT '姓名',
    `age` int(4) NOT NULL DEFAULT 0 COMMENT '年龄'
)

注:其中“TEMPORARY”为临时表选项,临时表不永久保留,断开连接即自动删除

2.更新表

在MySQL中,通过ALTER TABLE语句来更改原有表结构,常见操作有增加或删减列,创建或取消索引,更改原有列的数据类型、默认值,重命名表名或列名,更改标记注释或引擎类型,更改索引、外键等等

(1)ADD[COLUMN]子句

用于向表中增加列,可同时增加多列

ALTER TABLE USER
ADD `sex` CHAR(1) DEFAULT 1 COMMENT '1为男,0为女' AFTER 'age',
ADD `idcard` VARCHAR(20) COMMENT '身份证'

注:可用关键字“FIRST”将新列置于第一列,也可用关键字“AFTER” 使新列置于某列之后,如“AFTER ”

(2)CHANGE[COLUMN]子句

用于修改列的名称或数据类型

ALTER TABLE user
CHANGE age ages int(4)

注:使用change子句必须写上修改后的数据类型和数据长度,修改列的数据类型可能导致数据丢失或字符串长度不足被截断

(3)ALTER[COLUMN]子句

用于修改或删除列的默认值

ALTER TABLE user
ALTER age SET DEFAULT '1'

(4)MODIFY[COLUMN]子句

与CHANGE子句类型,但MODIFY只修改列数据类型,不修改列名;且可通过关键字“FIRST”,”AFTER”修改列的位置

ALTER TABLE user
MODIFY sex char(2)

(5)DROP[COLUMN]子句

用于删除列

ALTER TABLE user
DROP sex

(6)RENAME[TO]子句

用于为表重新命名

ALTER TABLE user
RENAME TO userinfo

3重命名表

(1)RENAME TABLE

除了使用上面ALTER TABLE语句,还可直接使用RENAME TABLE语句重命名表

RENAME TABLE tbl_name TO new_tbl_name
[,tbl_name2 TO new_tbl_name2]

4删除表

DROP[TEMPOPARY] TABLE[IF EXISTS]
tbl_name[,tal_name2][,...]

5查看表

(1)显示表名

SHOW[FULL] TABLES [{FROM|IN} db_name]
[LIKE 'pattern'|WHERE expr]

(2)显示表结构

SHOW [FULL] COLUMN {FROM|IN} tbl_name [{FROM|IN} db_name]
[LIKE 'pattern'|WHERE expr]
#或者
{DESCRIBE|DESC} tbl_name [col_name|wild]
#例如
DESC user;
DESC user name

通过IP获取IP内含信息

class Taobaoip {

/**
 * 淘宝IP地址库 Reset API
 * @author 
 * @param  [string] ip [IP地址]
 * @return [type]     [只返回获取成功的ip数据]
 */
    public function getLocation(ip) {
        if (empty(ip))ip = get_client_ip();
        taobaoUrl = "http://ip.taobao.com/service/getIpInfo.php?ip=";url       = taobaoUrl .ip;
        data      = self::httpRequest(url);
        data      = preg_replace("#\\\u([0-9a-f]{4})#ie", "iconv('UCS-2BE', 'UTF-8', pack('H4', '\\1'))",data);
        data      = json_decode(data, true);
        return data[data];
    }

    Static Private function httpRequest(url) {
        ch = curl_init();
        curl_setopt(ch, CURLOPT_URL, url);
        curl_setopt(ch, CURLOPT_RETURNTRANSFER, 1);
        output = curl_exec(ch);
        curl_close(ch);
        if (output === FALSE) {
            return "cURL Error: " . curl_error(ch);
        }
        returnoutput;
    }


}
#成功返回
#{"code":0,"data":{"ip":"113.70.24.1","country":"中国","area":"","region":"广东","city":"佛山","county":"XX","isp":"电信","country_id":"CN","area_id":"","region_id":"440000","city_id":"440600","county_id":"xx","isp_id":"100017"}}

Mysql中IF()函数使用

mysql中if()函数具体语法如下:

IF(expr1,expr2,expr3)
#如果expr1的值为true,则返回expr2的值。
#如果expr1的值为false,则返回expr3的值。

其经常判断查询出来的值,示例;

mysql> select name,if(sex=0,'女','男') as sex from student;
+-------+-----+
| name  | sex |
+-------+-----+
| name1 | 女  |
| name2 | 女  |
| name3 | 男  |
| name4 | 女  |
+-------+-----+

其也经常用到判断的关联条件中,其示例如下:

SELECT  s.SCHOOL_CITY as schoolCity,
        s.SCHOOL_COUNTY as schoolCounty,
        count(DISTINCT `s`.`SCHOOL_ID`) as schoolNum,
        sum(m.duration) as sumDuration,
        sum(`m`.`VIEWERCOUNT`) as viewLiveSum,
        sum(m.replayViewerCount)as reViewSum,
        sum(m.praisecount) as sumpraise,
        sum(`m`.`VIEWERCOUNT`+m.replayViewerCount) as viewSum
         from ((tbl_hbb_mobile_live_statistics m join tbl_hbb_resource_visit_map v) join tbl_school_info s)
    where`m`.`RESOURCEID`= `v`.`RESOURCEID` 
        and if((`v`.`rangeTYPE`= '2'),(`v`.`rangeID`= `s`.`SCHOOL_ID`),(`v`.`parentId`= `s`.`SCHOOL_ID`))

转载至:https://www.cnblogs.com/zjdxr-up/p/8383609.html