LisenH 发布的文章

昨晚大脑里浮现出一个词语,并且自己被这个词语给震惊住,思考了一晚上,早上醒来也一直在不自觉地思考它,这个词语就是“习惯性输出”。

我特意百度了一下,全网还没人讨论过这个词。

为什么被它震惊。起因是不经意看到了蒋方舟的微头条,稍微翻阅了一下,感觉她跟普通女生没太大区别,无非就是多了一些签售和演讲的照片。从某种角度来讲,就是一个普通女孩,而非被外界捧上天的“才女”。

那她作为一个比“普通人”更“成功”的女孩,跟普通人又有什么区别呢?或者说,她“成功”的秘诀是什么?我想,就是这个词————“习惯性输出”。她的写作,演讲,都是一种“输出”,并且已经养成了习惯。

这个可不是我自己意淫出来的。蒋方舟有一次自己描述说,她小学时就被父母威胁,逼着出书,自从有了第一次出书经验,出书这种高大上的事情对于小学生的她来说就不再是高不可攀的事情,加上成就感带来的正反馈,出书和写作,便成为了她的习惯性动作。

有一次窦文涛问蒋方舟,如果没有人看你的作品,你还会写作吗?蒋方舟回答得很诚恳:“不会”。当时我的体会是,如果没人看我的朋友圈,我也不会去发,如果对方从来不回我微信,我也不会一直给对方发消息。可见,在这点上,人性是共通的。

因此,我从昨晚到今天上午,一直在思考,“习惯性输出”只是我臆想出来的词语吗?不对,这可能是区分一个普通人和“成功人士”的惊天大秘密!

大家都觉得宇航员是个高大上的工作,只有精英中的精英才能做到,但其实未必。之前在一个航天的纪录片里了解到,宇航员在正式升空之前,往往要经历好几年的训练,要把所有相关流程操作变成肌肉记忆,可以说,宇航员上天到回地球的整个过程,99%的动作都是在地球上经历了几千遍的演练。我没机会当宇航员,但是想想,这件事在逻辑上也是成立的,火箭,上空,这可是超高危险的事情,任何动作失误或者不及时都有可能带来灾难性后果,哪有时间给宇航员太多时间去思考。

写到这里,我意识到,“习惯性输出”的威力是多么大。事实上,生活中大部分人是“习惯性被输入”的,刷别人抖音上的优质作品,看别人写的优质书,用别人设计的优质软件,吃别人烹饪的优质菜肴,仰慕别人的优秀成就......普通人跟优秀的人最大的区别就在于,他们“习惯性输出”,而我们“习惯性被输入”,仅此而已。

说“仅此而已”并不是说我们可以很容易地改变自己,事实上,我的经历告诉我,一个“习惯性被输入”的人想变成“习惯性输出”的人,过程是很艰难的,只是这个道理简单而已。

记得以前看过一个观点,叫“让优秀成为习惯”,这或许是对的,但是,可操作性太低了,因为不知道如何才能优秀,更别提把优秀变成习惯了。而“习惯性输出”可操作性就很强,总结起来就是“主动+分享”。你觉得你有写作天赋,很棒,写起来,分享出去;你觉得你是做产品经理的料,很棒,设计出来,分享出去;你觉得你对小视频很有想法,很棒,拍出来,分享出去!只有主动地去产出,然后分享,获得正反馈,这样才能养成“习惯性输出”,当你养成“习惯性输出”,你离“成功人士”的距离也就不远了。

我听说,影响一个人的幸福感,很大的一个因素是“价值感”,价值感来自于“输出”,或者说付出。仔细回忆一下,你上次觉得自己很有价值感的时候是什么情景?那是不是跟你“输出”有关?

所以下次看到别人又出书了,抖音里的百万粉丝又侃侃而谈了,看到别人又做出了你认为了不起的成就,不用太羡慕,因为你已经知道了其中秘密————“习惯性输出”。

输出和输入对一个人来说有着非常本质的区别,因为输出就是创造价值,你对世界创造了价值,世界就会给你回报。

知道->做到->成为习惯,这是你需要经历的成长。

养成输出的习惯性,专注和坚持是必要条件,爱好可以让你的专注和坚持更加轻松。因此,不是看到别人发抖音你也跟着发,看到别人写书你也跟着写,看到别人朋友圈天天发英语你也去学英语。做你热爱的事情并且坚持输出,这才是符合你自己的路。

想想看,如果你有机会遇到一个在某方面很有成就的人,你问他做出成就的秘诀,我想他很大概率会说,因为热爱,所以主动去练习,时间久了成为习惯,因此一直输出,然后被你看见。

今天是2021年的第一天,从今天开始“输出”,让它成为你的习惯性动作吧,你会发现生活因此而不同。

哦,对了,回复别人的文章也是一种“输出”,你不回一下吗?

祝进步。

今天是2020年最后一天,买了7台华为云非大陆300Mbps带宽的服务器,用于对比

imgbed.cn图床

测试数据如下:

地区国内平均ping值下载速度
香港58ms8MB/s
曼谷119ms8MB/s
新加坡89ms8MB/s
非洲428ms6KB/s
墨西哥311ms9KB/s
圣保罗395ms8KB/s
圣地亚哥387ms7KB/s

ping值为电信、移动、联通三网的均值。

下载速度测法:

  • 服务器放个100MB的zip文件,本地浏览器下载,南方电信宽带,8MB/s应该是我带宽和笔记本wifi的限制,抛开限制,下载速度应该可以更大。
  • 用华为云贵阳服务器去wget测下载,曼谷和新加坡只有100KB/s左右,香港1MB/s,从这点来看,还是香港比较好。

如果只考虑下载速度,南方买曼谷、香港、新加坡都差不多,再考虑ping值和全国速度的话,香港首选。

国内使用的话,亚太地区以外强烈不建议入手。

2021.01.13增加脚本测试如下:
imgbed.cn图床

imgbed.cn图床

imgbed.cn图床

imgbed.cn图床

imgbed.cn图床

有个项目需要获取上个月的“年-月”,用了php的这个方法:

date( 'Y-m', strtotime('-1 month') )

今天2020年12月31日,发现上述函数给出的结果是2020-12,仔细研究了一下,原来是php的bug:

echo date('Y-m-d', strtotime('-1 month', strtotime('2019-03-31')));
// 输出2019-03-03,非闰年,扣28天

echo date('Y-m-d', strtotime('-1 month', strtotime('2020-03-31')));
// 输出2019-03-02,闰年,扣29天

echo date('Y-m-d', strtotime('-1 month', strtotime('2020-06-30')));
// 输出2020-05-30,扣30天,这个正常

echo date('Y-m-d', strtotime('-1 month', strtotime('2020-05-31')));
// 输出2020-05-01,遇到有31天的月份依然扣30天

这个bug总结起来就是:

  • 在3月份,如果当年是闰年,则执行'-1 month'一股脑减29天,非闰年一股脑减28天。
  • 在非3月份,全部一股脑减30天。

Rasmus Lerdorf,难道你不知道“一三五七八十腊,三十一天永不差”吗?

最后总结,正确地获取上个月“年-月”的姿势为:

date('Y-m', strtotime('-1 month', strtotime(date('Y-m'))))

也就是用每月1日为基准去扣天数。

支付宝网页支付的流程:前端请求支付宝支付表单参数->后端生成支付表单参数给前端->前端根据支付参数构建form表单对支付宝发起POST请求->支付宝支付成功POST异步通知开发者的服务器。

支付宝用到的是RSA加密,搞懂RSA加密的原理有助于理解支付宝支付流程。推荐李永乐老师讲非对称加密的视频。

这是已经写好的测试例子:https://imgbed.cn/static/alipay-demo.html

代码很简单,直接看代码就理解了,注意点写在注释里。

请求支付参数的服务端代码:

'use strict'
const NodeRSA = require('node-rsa') // 需要执行npm install node-rsa才能调用
const querystring = require('querystring') // node自带,无需安装,直接调用
exports.main = async (event, context) => {
    const appId = '支付宝的appId'
    let merchantPrivateKey = '支付宝商家公钥'
    
    let ua = ''
    try {
        ua = event.headers['user-agent']
    }catch(e){}
    let productCode = 'FAST_INSTANT_TRADE_PAY'
    let method = 'alipay.trade.page.pay'
    if (ua.indexOf('Mobile') > -1) {
        productCode = 'QUICK_WAP_WAY'
        method = 'alipay.trade.wap.pay'
    }
    let bodyObj = querystring.parse(event.body) // url请求参数字符串转object。uni-app云函数实例化后,POST的请求参数在body里
    if (Object.keys(bodyObj).length) {
        const passbackParams = JSON.stringify({mobile: '18888888888', sku: 'year'}) // 开发者想要传递的参数,字符串,支付宝异步通知会带上这个
        let bizContent = JSON.stringify({
            subject: '支付宝测试-'+bodyObj.price+'元',
            out_trade_no: (new Date()).getTime(),
            total_amount: bodyObj.price,
            product_code: productCode,
            quit_url: 'https://imgbed.cn/static/alipay-return.html', // 手机网页支付放弃支付时返回的网址
            passback_params: passbackParams
        })
        let queryObject = ksort({
            app_id: appId,
            biz_content: bizContent,
            charset: 'UTF-8',
            method: method,
            notify_url: '支付宝异步通知地址',
            return_url: 'https://imgbed.cn/static/alipay-return.html',
            sign_type: 'RSA2',
            timestamp: time2date((new Date()).getTime()), // Y-m-d H:i:s格式的字符串
            version: '1.0'
        })
        const query = querystring.unescape(querystring.stringify(queryObject)) // 要再套一层querystring.unescape,否则query被转义,会导致签名的字符串跟支付宝不一致
        const key = new NodeRSA(merchantPrivateKey, 'pkcs8-private')
        const sign = key.sign(Buffer.from(query)).toString('base64')
        queryObject.sign = sign
        return {code:0, alipayParams: queryObject}
    } else {
        return {code: 1, msg: 'event.body is empty'}
    }
    
    /****************************************************************************************************/
    // 毫秒时间戳转Y-m-d H:i:s或者Y-m-d
    function add0(m){return m<10?'0'+m:m }
    function time2date(shijianchuo, onlyDate) {
        var time = new Date(shijianchuo);
        var y = time.getFullYear();
        var m = time.getMonth()+1;
        var d = time.getDate();
        var h = time.getHours();
        var mm = time.getMinutes();
        var s = time.getSeconds();
        let returnStr = y+'-'+add0(m)+'-'+add0(d)
        if (!onlyDate) {
            returnStr += ' '+add0(h)+':'+add0(mm)+':'+add0(s)
        }
        return returnStr
    }
    
    // 对object的key进行排序
    function ksort(params) {
        let keys = Object.keys(params).sort();
        let newParams = {};
        keys.forEach((key) => {
            newParams[key] = params[key];
        });
        return newParams;
    }
}

前端支付代码,为了方便,我用了vue+vant:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0, viewport-fit=cover">
        <title>支付宝支付演示</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@2.11/lib/index.css"/>
    </head>
    <body>
        <div id='app' style="display: none; padding: 100px 15px 15px 15px;">
            <div style="max-width: 512px; margin: 0 auto;">
                <p><img height="44px" src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/dca221bb-2a37-4527-bea7-6fcb92945c18.png"></p>
                <p v-for="price in prices"><van-button @click="alipay(price)" type="info" block>{{price}}元</van-button></p>
            </div>
            <div id="form-pay"></div>
            
            <van-overlay :show="showLoading">
                <div style="display: flex; align-items: center; justify-content: center; height: 100%">
                    <van-loading size="24px" vertical>加载中...</van-loading>
                </div>
            </van-overlay>
        </div>
        <script src="https://cdn.bootcdn.net/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
        <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/vant@2.11/lib/vant.min.js"></script>
        <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"></script>
        <script src="https://cdn.bootcdn.net/ajax/libs/qs/6.9.4/qs.min.js"></script>
        <script>
            const vm = new Vue({
                el: '#app',
                data() {
                    return {
                        prices: [0.01, 1, 10, 100],
                        showLoading: false
                    }
                },
                methods: {
                    alipay(price) {
                        if (navigator.userAgent.indexOf('MicroMessenger')>-1) {
                            vant.Dialog({ message: '点击微信右上角···,选择“用浏览器打开”' })
                        } else {
                            this.showLoading = true
                            axios({
                                url: 'https://b1ebbd3c-ca49-405b-957b-effe60782276.bspapp.com/http/alipay-params-demo', // uni-app云函数URL实例化的api
                                data: Qs.stringify({price: price}),
                                method: 'POST'
                            }).then(res=>{
                                this.showLoading = false
                                if (0===res.data.code) {
                                    const obj = res.data.alipayParams
                                    const keys = Object.keys(obj)
                                    let formHtml = ''
                                    formHtml += '<meta charset="utf-8">'
                                    formHtml += '<form id="alipaysubmit" method="POST" name="alipaysubmit" action="https://openapi.alipay.com/gateway.do?charset=UTF-8">'
                                    for (i=0; i<keys.length; i++) {
                                        formHtml += '<input type="hidden" name="'+keys[i]+'" value=\''+obj[keys[i]]+'\'>'
                                    }
                                    // formHtml += '<input type="submit">' // 手动提交表单
                                    formHtml += '</form>'
                                    $('#form-pay').html(formHtml)
                                    document.forms["alipaysubmit"].submit() // 自动提交表单
                                } else {
                                    console.error(res.data.msg)
                                }
                            }).catch(err=>{
                                this.showLoading = false
                                console.error(err)
                            })
                        }
                    }
                }
            })
            $(document).ready(function(){
                $('#app').show()
            })
        </script>
    </body>
</html>

支付成功后,支付宝会异步通知,开发者接收异步通知,验签,代码如下:

'use strict'
const querystring = require('querystring')
const NodeRSA = require('node-rsa')
exports.main = async (event, context) => {
    const alipayPublicKey = '支付宝公钥'
    
    let bodyObj = ksort(querystring.parse(event.body))
    if ('TRADE_SUCCESS'===bodyObj.trade_status) { // 支付失败也有可能会收到异步通知,所以这里要判断TRADE_SUCCESS
        const outTradeNo = bodyObj.out_trade_no
        if (outTradeNo是否已经存在于数据库) { // 异步通知可能会收到多次,判断out_trade_no是否存过数据库来判断重复通知
            const sign = bodyObj.sign
            delete bodyObj.sign
            delete bodyObj.sign_type
            const body = querystring.unescape(querystring.stringify(bodyObj, '&', '='))
            const key = new NodeRSA(alipayPublicKey, 'pkcs8-public')
            if (key.verify(Buffer.from(body), sign, 'utf8', 'base64')) { // 验签通过,继续执行业务代码
                // 用户处理订单的代码
            } else {
                console.error('验签失败')
            }
        } else {
            console.error('订单号已存在')
        }
    } else {
        console.error('trade_status', bodyObj.trade_status)
    }
    
    return 'success' // 一定要返回'success'给支付宝,否则会重复多次通知
}

// 对object的key进行排序
function ksort(params) {
    let keys = Object.keys(params).sort();
    let newParams = {};
    keys.forEach((key) => {
        newParams[key] = params[key];
    });
    return newParams;
}

  1. 下载nw.js的NORMAL版:https://nwjs.io/
  2. 解压后在文件夹里新建package.json文件,编辑内容:

    {
      "main": "http://h5apk.cn",
      "name": "网页转安卓apk工具",
      "user-agent": "Mozilla/5.0 (%osinfo) AppleWebKit/%webkit_ver Chrome/77.0.3865.90 Safari/%webkit_ver pc_exe/1.0.0",
      "window": {
        "width": 1280,
        "height": 688,
        "icon": "logo.png"
      }
    }
    

main字段必填,你的网址。
name字段必填,应用名称,可以空字符串。
height写688,因为顶部工具栏高度32,加起来刚好 720px。
其它字段选填。
icon图标会显示在窗口左上角小图标位置,以及windows工具栏的地方。

  1. exe图标替换工具,resource_hacker,建议下载zip版本:http://www.angusj.com/resourcehacker/
  2. 打包成安装包,innosetup:https://jrsoftware.org/isinfo.php
    innosetup中文语言包:https://jrsoftware.org/files/istrans/,下载语言包时如果网页直接显示isl文件内容,则在网页右击选择另存为,如果通过复制黏贴文本去创建isl文件可能会导致乱码。

imgbed.cn图床

imgbed.cn图床

其它注意事项:因为窗体没有返回按钮,所以返回功能得在网页里实现。最好不要删除任何其它文件,否则可能导致无法运行。

经验:当你制作好exe安装包,发布到网上供其它人下载时,如果直接下载的是.exe文件,可能无法运行安装,并且在查看文件属性时提示“此文件来自其他计算机,可能被阻止以帮助保护该计算机。”,最直接的解决方法是先将.exe文件压缩成.zip再供用户下载。这个我也没有好的解决方法,如果你有的话,请指导我一下。