STAY HUNGRY , STAY FOOLISH.

求知若饥,虚心若愚。

       浏览:

全桟知识体系(二)

这是该系列文章的第二篇,我记得第一篇是在2017-04-23写的,离现在已经过去整整一年的时间。《全桟知识体系(一)》

在这整整一年的时间内,我对全栈有了更加清晰的认识和新的领悟。这篇文章可能会很长,我会把自己所做所学的东西毫无保留的分享给大家。该文章预估写作一个月,实际三个月。


1.什么是全栈?

全栈

套用知乎的话说,全栈工程师,也叫全端工程师(同时具备前端和后台能力),英文Full Stack developer。是指掌握多种技能,并能利用多种技能独立完成产品的人。
我认为全栈是指能把想法变现的一个人
说实话,全栈工程师要自己做一个产品出来,花费的时间是很长的。
因为做出一个能用的产品并上线,需要学习和了解的东西真的太多。
其次,全栈工程师是跨工种的,需求分析(想做什么)、原型设计(流程走通)、页面设计(展示前准备)、前端研发(展示效果)、数据库研发(业务建表)、后台研发(接口调用)、自测上线(给到用户)等等等等。
而且学习每个工种的知识点时没他们专一的人员熟悉,所以做起来比较慢。
最后,全栈工程师什么都做,需要串行地执行每一步操作,该操作有问题,会直接影响下一步,花费的时间多了去了。
当然成为全栈是痛苦且漫长的,但成为全栈后的好处是:知识面广,学起东西来灵感比较强


2.产品怎么出来的?

这个问题最佳的答案是一个字:
一个人能把自己想法实现出来是特别困难的,尤其是当你没任何资源(人力、财力)的时候。
唯一你能拥有的资源,就是时间(精力)。
在短短的9个月的时间里,我一人做出了真正意义上的产品,享健身于今年3月5日正式上线。
下面是其logo:

享健身


有兴趣的朋友可以点击下面链接进行下载:
http://a.app.qq.com/o/simple.jsp?pkgname=com.xjs

xjs_download


3.产品的由来及准备工作

话说无风不起浪,做享健身这个app想法的由来肯定是有一定道理的。
长话短说,理由是因为某一天,我和媳妇儿被健身会籍“强制”办了5年健身年卡。因为健身房有很多黑幕,自己又心疼钱,所以想找个东西发泄一下。
当时认为办健身卡是不值得的,因为我和我媳妇儿都还没做好准备,就强制去健身,对健身这个态度就是会籍为业绩,故意拉会员;教练心太黑,强制让你买课。好吧,我承认根本原因是没钱,请不起私教
为了发泄这一情绪,我想健身房为什么要有办年卡这个机制?难道我们需要天天去健身房锻炼吗?答案当然不是,因为**健身房实际上就是个对赌协议,赌的就是你不去**。
现在不是提倡共享经济吗?于是乎我想如果有能按次收费的健身房就好了,那里没有会籍的故意拉会员,无年卡会员制,没私人教练的打扰,即使有,也是针对有需要私人教练的人。
这样的健身房可以叫共享健身房、这样的教练可以叫共享健身教练。
这款app的诞生是为解决因种种问题而最终放弃健身的人


3.1.iOS产品取名

既然是产品当然需要一个好听的名字,当时第一反应就是先注册iOS产品名称,说干就干,当时凌晨3点创建了以下名字:

名称

最后选择“享健身”这个名字,因为它简单明了,同时也有三层含义:

1.我想健身
2.共享健身
3.分享健身


3.2.域名购买

既然名字想好了,下一步就需要域名购买,因为产品必须有自己的官网,推广和宣传都需要用到,除此之外,绑定阿里服务器主机,提供后台接口服务、七牛图片镜像储存等都离不开域名。
域名的取名也是个学问,名字太长,担心用户记不住;名字太短,早就被人注册。如果享健身,按照拼音来注册的话,那么就是xiangjianshen.com。我个人感觉13个英文字母确实太长,所以把享健身用缩写试试看,结果xjs.com早被别人注册,于是我最终选择缩略前两个字的xjshen.com作为自己的域名。
购买域名的时候建议将com和cn两个后缀的都购买,理由是方便后期分配和管理,各自有各自的用途。com是国际域名,cn是国内域名。
我一般将com域名用在官网,cn域名用在除官网之外的地方。

域名

为什么要先将iOS名称注册和域名名称购买?因为名称是无重复且宝贵的,早点注册和购买避免被别人使用。


4.需求分析及原型设计

拥有名称,接下面需要对产品的业务和流程进行分析和设计。产品的分析很重要,因为需求是给健身爱好者的,那么就需要明确分析该群体需要什么,如何便利于他们?分析完毕后,就需要对需求进行整理,并将其设计出来。业务是否清晰,产品是否可用,完全取决于设计。这里给大家推荐一款针对移动app的原型工具,叫“墨刀”。
使用起来很简单,最重要的功能是可以直接在手机上预览看效果和交互。
这一步骤得感谢我的媳妇儿丽姐,因为原型设计的页面都是她做的,需求分析是我和她共同完成。

原型设计


5.logo设计

5.1.logo设计

当原型设计感觉差不多的时候,就进入编码阶段。在进入编码前,需要将Android和iOS的logo定下来。
围绕享健身这个词语,在我看来,健身应该是阳光、乐观、温暖、高兴的事情。那么logo如何才能体现正能量的点呢?于是我想到奔跑,因为奔跑的感觉就是积极向上的。
logo想好后,就需要去填logo的颜色,下面图分别是红色、蓝色、黑色、绿色。最终我选择绿色,绿色代表健康,跟享健身观念是相符的。

LOGO

logo设计成功后,就需要针对不同设备进行切图。
首先是安卓app的图标,大致的尺寸有48x48、72x72、96x96、144x144、512x512,共5张。
接着是iOS app的图标,尺寸就更多了,29x29、40x40、60x60、76x76,这些都有1、2、3倍的区别,共3x4=12张logo,512x512、1024x1024、1536×1536,3张,总共15张。

例如:
ICONS


5.2.Launch页面的设计

Launch页面,指app启动页。
安卓和iOS手机各自的机型不同,屏幕尺寸也会不同,因此启动页的尺寸也必须不同,否则使用同一张图片来当充当启动页该图会看着畸形。
首先是安卓app的Launch图,大致的尺寸有720x1280、1080x1920、1440x2560,共3张。
接着是iOS app的Launch图,大致的尺寸有640×1136、750x1334、1242x2208,共3张。

例如:
Launch

这两步骤得感谢我们公司的爽姐,她是负责UI设计的,给我切的icon图标和launch图片都特别的专业。


6.漫长的准备阶段

这个阶段是最重要的,因为前面都是部分准备工作。我用思维导图画了下大致的流程图,如下:
极简版:
极简版


进阶版:
进阶版


最终版:
最终版


下面我会依据享健身这个思维导图,挨个说明其注意事项及具体流程。

享健身总共分成三类,分别是准备工作编码工作部署工作

该节只会讲解准备工作方面的东西,想看编码工作或部署工作可直接跳过该节。
准备工作需要做哪些事情:

  • 需求分析,明确自己要做什么
  • 产品设计、原型设计 (上一节已讲解)
  • 人事
  • 第三方服务
  • 其他准备工作(常用工具)

6.1 需求分析

想做一件事情,需求一定要明确。想做什么比怎么做更重要。因为需求才是你能一直坚持走下去的原因,需求就是目标,做没目标的事情,很难坚持到最后的。

以享健身举例,需求确定:要实现场地共享,教练共享,干货共享三个大功能。
如何实现场地共享:找健身房,定位离用户近的附近健身房,方便健身

如何实现教练共享:查看教练基本信息,在线聊天,方便联系

如何实现干货共享:查看文章,来源微信公众号;查看视频,来源Change Pro


6.2 设计

这块。。。。额,上面已经提到过了,就不说明了,还是得请专业设计帮忙才是最佳选择。


6.3 人事

这块内容主要目的是需要实现某些功能所必须的。比如:支付,想实现微信支付和支付宝支付是需要企业认证的,个人是没办法申请支付的。

6.3.1 注册个体工商户还是公司

我注册的是个体工商户,理由很简单:运作成本降到最低,且报税简单
按专业说法讲,公司是根据你的收入每月自行申报,个体工商户是专管员给你核税,后面就直接从银行扣。


6.3.2 办营业执照

选好自己需要的公司名或个体户名,找淘宝去办理营业执照即可,费用400左右,耗时一周左右。
营业执照


6.3.3 刻章及开通对公账户

有了营业执照后,接下来需要去刻章,章共有3个,分别是公章,财务章,法人章。公章哪儿都可以用,财务章和法人章一般是银行才会用到。有了章之后才能继续下一步,也是最重要的一步申请对公账户。对公账号就是公司的开户许可证,有了它,你才能够有属于自己的公司银行账户,以后微信支付、支付宝支付的收入和支出都是通过该账号使用的。费用3000左右,耗时三个月。

刻章

对公账户


当你营业执照、三个章、对公账户都拿到后,恭喜你,流程才走了一半,惊不惊喜,意不意外…(卧槽,还有…)
所有


6.3.4 税务报到

前面说的都是准备工作,有了这些东西后,还需要到本地税务局去报到。去的目的可以简单理解成对公账户激活。耗时1天左右。
以我自己的为例,我申请的是成都市武侯区那边的个体工商户,所以需要去武侯区税务局报到。


1.到政务中心后,完成税务登记,说是个体户税务报到就行了。
税务局


2.税务登记完成后,去国税局进行纳税申报。
税务局

国税完成后,可以得到一个通知书,该书表明你国税已经申报成功。
税务局


3.国税局申报后,还没结束,继续去地税局进行纳税申报。
税务局

地税局完成后,同样也可以得到一个通知书,该书表明你地税已经申报成功。

税务局

到此为止,终于可以开始你的公司或工作室的创业之旅了~


6.3.5 每月银企对账

每月银企对账指的是银行客观记录企业资金流转情况的记录单,以防范金融风险。自己来操作,其实也很简单,打开电脑,打开你所办理的对公账户的所在银行官网,进行网上操作即可。以我的浙商银行为例:
1.打开浙商银行网银助手,点击企业网银登录

浙商银行


2.插上U盾,输入密码和验证码,点击登录

浙商银行


3.成功进入后,选择企业财务下的银企电子对账

浙商银行


4.选择对账年月

浙商银行


5.输入当月底银行账户的剩余金额,实现账户核对

浙商银行


6.核对后,输入浙商e密码,成功后点击完成对账

浙商银行


7.点击完成对账后,还没结束,还会继续弹出弹框,请求输入U盾密码

浙商银行


8.输入U盾密码成功后,让你核对交易信息,确认无误后点击U盾上的OK键

浙商银行


浙商银行


9.操作成功后,出现银企对账单,完成每月对账。
浙商银行


6.4 第三方服务

一款成熟的产品,当然离不开注册登录、分享、支付、推送等基本功能。由于业务所需,也有可能需要用到定位、查看地图、聊天等其他功能。想实现上面的功能,肯定不可能自己挨个去实现,于是乎第三方服务显得尤为重要了。

6.4.1 注册登录

app常见的注册登录无非两种,一是通过手机短信验证码实现注册登录,二是通过第三方登录,比如微信、QQ、微博等。
1.手机短信验证码
想用手机短信实现注册登录,那肯定需要购买短信条数按量计费。这两种方案,个人建议购买短信条数吧,除非你自己的产品有很多用户使用,不然没必要按量计费。
用百度搜索短信验证码,一搜一大堆,这里需要访问螺丝帽,地址:https://luosimao.com,1万条短信,550元。1万条起步,完全够用,我现在还剩余9000多条没用。

螺丝帽

2.微信登录
微信登录分为移动应用微信登录网站应用微信登录
享健身属于app产品,当然走移动应用微信登录这套流程。
申请地址:https://open.weixin.qq.com

微信登录

不认证,注册后自动有微信分享功能,但微信登录微信支付功能不可用,必须申请微信开发者资质认证,,认证标准必须是企业或个体户,且年费300元

微信认证

认证成功后,自动获得微信登录功能,但微信支付功能还得再次申请开通,如何申请微信支付,在下一节我会详细介绍。先看下获得微信分享、微信登录、微信支付后的效果:

微信认证成功


6.4.2 移动支付

app常见的支付方式有三种微信支付支付宝支付apple pay苹果支付
当然适应国内行情,只需要实现微信支付和支付宝支付这两种支付方式就OK。
1.微信支付
微信支付分为移动应用微信支付网站应用微信支付
申请地址:https://open.weixin.qq.com

微信支付认证

这块需要用到对公账户,填写完后,微信会让你支付一笔小于1元的指定金额到指定账户上去,必须使用企业账户来进行支付,支付成功后,签署协议就算开通成功了。

开通成功后,记住一个网址微信支付商户平台:https://pay.weixin.qq.com

微信支付成功


2.支付宝支付
申请地址:https://open.alipay.com
申请逻辑和微信支付一样,也是填写完,需要转入一笔金额到指定账户,必须使用企业账户来进行支付,支付成功后进行签约就行了,和微信支付不同的是它没300年费的说法,但每笔都有手续费

alipay

支付宝支付


6.4.3 定位和地图

目前常用的定位和地图是高德地图、百度地图
我使用的高德地图,申请地址:https://lbs.amap.com

高德地图

建议升级成企业开发者,接口服务调用量会很高。

高德地图企业


6.4.5 推送和聊天

目前常用的推送是极光友盟
我使用的极光,申请地址:https://www.jiguang.cn
极光推送:

极光推送

极光IM:

极光IM


6.5 准备工作

上面只是按常用功能介绍第三方服务,想系统的了解第三方服务,需要以公司分类来介绍。
常见的服务提供方有:

  • 百度
    • 百度云
    • 百度网盘
    • 百度地图开放平台
    • 百度统计
  • 阿里巴巴
    • 阿里云
    • 蚂蚁金服开放平台
    • 高德开放平台
  • 腾讯
    • 腾讯云
    • 微信公众平台
    • 微信开放平台
    • 微信支付商户平台
    • 应用宝APP+开放平台
  • 苹果
    • Apple Developer
    • iTunes Connect
  • 谷歌
    • Google Play
  • 其他
    • 七牛云
    • 友盟+
    • 极光推送
    • 螺丝帽
    • fir.im
    • ngrok
    • Easy HTTPs

简单总结下涉及的公司所给予的业务和功能。
大公司:
百度:一家全球最大的中文搜索引擎的公司。合作的点很多,建议推广和统计地图其实也不错。

推广:http://e.baidu.com
网站统计:https://tongji.baidu.com
APP统计:https://mtj.baidu.com
地图:http://lbsyun.baidu.com


阿里巴巴:按马云的说法,我们集团本质上是一家扩大数据价值的公司。合作的点很多,建议域名、服务器支付宝没得选,地图也不错。

阿里云:https://www.aliyun.com
蚂蚁金服:https://open.alipay.com
高德地图:https://lbs.amap.com


腾讯:是中国最大的互联网综合服务提供商之一,也是中国服务用户最多的互联网企业之一。合作的点很多,建议微信和应用宝

微信公众平台:https://mp.weixin.qq.com
微信开放平台:https://open.weixin.qq.com
微信支付商户平台:https://pay.weixin.qq.com
腾讯开放平台:http://open.qq.com


苹果:是美国一家高科技公司,自身研发硬件和软件。合作的点很多,iOS App没得选。

Apple Developer:https://developer.apple.com
iTunes Connect: https://itunesconnect.apple.com


谷歌:一家全球最大的搜索引擎公司。合作的点很多,Google Play没得选.

Android Developers:https://developers.google.com
Google Play Publish:https://play.google.com/apps/publish


七牛云:是国内领先的以视觉智能和数据智能为核心的企业级云计算服务商,同时也是国内最有特色的智能视频云服务商,合作的点很多,CDN、镜像存储蛮不错的。
地址:https://www.qiniu.com


友盟+:全球领先的第三方全域数据服务商。合作的点,建议统计、第三方分享、第三方登录
地址:https://www.umeng.com


极光推送:是独立的第三方云推送平台。,合作的点,建议消息推送、聊天IM
地址:https://www.jiguang.cn


小公司和个人:
螺丝帽:中国市场上首款短信邮件整合一体化的网络发送平台。合作的点,当然短信
地址:https://luosimao.com

fir.im:国内首家为移动开发者提供App免费托管分发服务的平台。合作的点,当然APP分发测试
地址:https://fir.im

ngrok:提供内网穿透服务,支持绑定自定义域名。合作的点,当然内网穿透服务
地址:https://www.ngrok.cc

Easy HTTPs:Easy HTTPs是专门用于管理你的网站HTTPs(SSL)安全证书的服务。合作的点,当然ssl证书
地址:https://easy.zhetao.com


6.6 自己常用工具

6.61 PC端

1.开发必备
安卓开发者工具:Android Studio
地址:https://developer.android.google.cn/studio

苹果开发者工具:Xcode
地址:https://developer.apple.com/xcode

前端轻量文本编辑器:Sublime Text
地址:https://www.sublimetext.com


2.测试调试必备
接口测试:Postman
地址:https://www.getpostman.com/apps

iOS模拟器:iOS Simulator
地址:Xcode自带,可自行下载不同版本

Android模拟器:Genymotion
地址:https://www.genymotion.com

安装apk包工具:HandShaker
地址:https://www.smartisan.com/apps/#/handshaker

安装ipa包工具:PP助手
地址:https://pro.25pp.com

抓包工具:Charles
地址:https://www.charlesproxy.com

抓网站工具:Site Sucker
地址:http://www.ricks-apps.com


3.运维翻墙必备
MongoDB可视化工具:Robo 3T
地址:https://robomongo.org

远程SSH工具:Termius
地址:http://www.termius.com

翻墙工具:ShadowsocksX-NG
地址:https://github.com/shadowsocks/ShadowsocksX-NG/releases

FTP上传工具:FileZilla
地址:https://filezilla-project.org


4.图片设计处理
PS:Photoshop CS6
地址:https://www.adobe.com/cn/creativecloud/business.html

屏幕录制工具:LICEcap
地址:https://www.cockos.com/licecap

移动端原型设计:墨刀
地址:https://modao.cc


6.62 app端

查看数据或紧急运维
查看iOS APP产品状态: Connect
查看阿里服务器状态:阿里云
远程SSH工具:Termius


7.漫长的编码阶段

终于到了最最重要的编码阶段,大致分成以下部分来讲解:

  • 前端展示
    • 官网
    • 安卓/iOS APP
    • 后台管理
    • 小程序
  • 数据库建模
  • 后台接口
  • 模拟数据

7.1 前端展示

前端可分为web端移动端,移动端可分安卓端iOS端。因此,只要涉及跟用户交互的,都算前端
做前端就是做交互,用户的交互体验直接反应了前端工程师的能力,除了交互还有就是精准力。比如按钮与图片不对齐,布局出现问题等都是精准力不好导致的,所有好的产品的诞生都离不开精准力,因为细节决定成败。
一个完整的产品,当然离不开官网的展示安卓、iOS App的使用后台管理的配置和分析统计

1.官网

APP的官网一般很简单,介绍下APP的功能和特色,提供下载地址即可。因此,我这里直接使用Bootstrap Template,改改文字和图片实现享健身官网。自己写也行,不过需要时间,只是展示商品而已,能快则快吧。而且Bootstrap Template支持响应式布局,PC和移动端浏览器都能产生很好的展示效果。
官网地址:https://xjshen.com

享健身官网

Bootstrap Template地址:https://startbootstrap.com

代码如下,其实没啥好说的,改改文字、下载链接、图片、ico即可。
官网代码


2.安卓、iOS App

App按理说至少需要两个端的人员,Android和iOS。如果就一人去做App的话,当然希望写一次,兼容两端,且原生的最好。于是乎React Native来到了我的眼前。
地址:https://facebook.github.io/react-native

React Native

按官网的说法:

A React Native app is a real mobile app
With React Native, you don’t build a “mobile web app”, an “HTML5 app”, or a “hybrid app”. You build a real mobile app that’s indistinguishable from an app built using Objective-C or Java. React Native uses the same fundamental UI building blocks as regular iOS and Android apps. You just put those building blocks together using JavaScript and React.

使用React Native开发应用程序是一块真正的移动应用程序,不像web应用程序、html5应用程序、hybird应用程序。它与使用Objective-C或Java构建的应用程序无法区分,真正意思上的原生app。

疑问1:React Native协议已换成MIT协议
BSD、MIT 和 Apache v2 是常用的三个开源软件协议,但 Facebook 使用的却是 BSD+Patents 协议,Patents 协议是 Facebook 的“特产”,称为专利附属条款,被视为 Facebook 用于解决开源代码中可能出现的专利纠纷的防御措施。

Patents 协议是 2015 年 Facebook 添加的,大致内容是使用基于 Facebook BSD+Patents 协议的开源项目的开发者,未来要是因为专利问题与 Facebook 产生纠纷,那么 Facebook 将有权停止你使用该开源项目,也就是说如果你起诉 Facebook,那么你所使用他们的开源技术开发的产品要么得停用,要么得用别的技术迁移重构,这对企业来说是一个重大的灾难。去年9月,WordPress、百度等大型公司宣布停用 React(Native) 开源项目以规避风险。
好消息是,2018年2月17日,Facebook终于让 React Native 采用 MIT 标准,这意味着不会有专利纠纷,实现了真正的开源,即使是大公司也能有自己的合法权利,不会被Facebook左右。

修改声明:https://github.com/facebook/react-native/commit/26684cf3adf4094eb6c405d345a75bf8c7c0bf88


疑问2:继 Airbnb 之后,Udacity 也宣布放弃使用 React Native

Airbnb 是硅谷创业公司中最早使用 React Native 的公司之一,他们从 2016 年就开始使用,而彼时距离 2015 年 React Native 发布才仅仅过去了一年。那时,Airbnb 有原生的 iOS 和 Android 应用,在 Web 上则使用 React。由于有着丰富的 React 经验,他们决定采用 React Native 来加速原生应用的开发。这次移植造成的影响就是带来了大量的额外工作,而这个影响刚开始时并不明显。他们花费了大量时间去研究如何编写辅助功能(如原生桥梁、封装代码等)以便让 JavaScript 支持已有的原生功能。同时,每当 iOS、Android 或 React Native 升级时,这些库都要进行维护。这不仅是技术上的难题,也造成了毫无必要的组织问题。

Udacity 的移动团队表示虽然 React Native 有许多优势,但是他们在开发过程中也遇到了不少困难,包括 UI/UX、性能、向后兼容性与后期维护等问题,这些问题最终导致了开发团队决定放弃使用 React Native,在尝试了 18 个月、做出 4 个功能之后完全将其代码删除。

作者还表示,删除的功能并没有使用替代方案重新开发,团队决定不再维护这几个功能。而至于将来会不会再用到 React Native,他表示 iOS 和 Android 团队有不同的考量,iOS 方面将来还是有可能使用 RN,但是 Android 上将不会再使用,它的体验和组件集成等问题过于严重。

两个大公司到达一定规模,人数和战略上都不需要依赖RN去磨合产品,反而原生来的更实在,APP产品的多样化也不适合使用RN,在我看来使用RN的绝佳场景:适合做统一两端UI和业务的产品;更适合快速迭代、急于试错的产品

最后一句话总结:
RN适用于快速迭代产品,当产品稳定后,转原生,这是很正常的事情;不叫弃用,叫战略转移。


RN开发需要特别多的时间,从我博客上看关于RN的文章就能略知一二,这里就不一一细化讲解了。

React Native Demo


开发RN必要工具及准备:

  • Xcode (iOS开发工具)
  • Android Studio(Andorid开发工具)
  • iPhone Simulator(iOS模拟器)
  • Genymotion Player(Android模拟器)
  • 安卓手机(真机调试用)
  • 苹果手机(真机调试用)

开发RN总结大纲:
1.揭秘React生态体系
2.React基础知识
3.Redux基础知识
4.React-Redux基本用法
5.React Native基础知识
6.React Native搭建之旅
7.React Native的ES5/ES6写法对照表
8.教你如何用React-Native开发原生应用(一)
9.教你如何用React-Native开发原生应用(二)
10.教你如何用React-Native开发原生应用(三)
11.React Native常用组件+经验之谈


开发RN基本目录结构介绍:

React Native Dir

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
xjs
├── android //android 工程的源代码,可用android studio打开
├── img //静态图片,如:tabBar图标
├── ios // ios 工程的源代码,可用xcode打开
├── node_modules //react-native工程用到的模块
├── src //rn代码,实现业务及功能
│ ├── assets //字体图标
│ ├── components // rn公共组件
│ ├── config // 其他第三方配置
│ ├── renducers // redux状态管理
│ ├── screens // rn页面
│ └── utils // 公共工具类
│ └── app.js //android和ios的界面主入口
├── index.android.js //android主入口,引用到app.js
├── index.ios.js //ios主入口,引用到app.js
└── package.json //工程描述文件

3.后台管理

一个App的运营和生产当然离不开后台管理,后台管理的功能当然是更好的服务于App。比如能看到注册用户的信息,配置展示的内容等功能。
后台管理我使用了vue-element-admin。vue-element-admin 是一个后台集成解决方案,它基于vueelement-ui
前面rn的技术桟是react,为什么不使用react去研发管理后台,而是vue呢?因为vue是国人写的,当然支持国产。开个玩笑,重点还是时间问题,想快速搭建一个易上手的后台管理系统,vue肯定是不二之选。而且vue-element-admin里面实现的前端组件已经足够完善,完全符合我做后台管理项目的功能,够用而且代码写入成本低。

vue-element-admin


Element地址:http://element-cn.eleme.io/#/zh-CN
Vue.js地址:https://cn.vuejs.org

vue-element-admin基本目录结构介绍:

vue-element-source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
vue-element-admin
├── build // 构建相关
├── config // 配置相关
├── src // 源代码
│ ├── api // 所有请求
│ ├── assets // 主题 字体等静态资源
│ ├── components // 全局公用组件
│ ├── directive // 全局指令
│ ├── filtres // 全局filter
│ ├── mock // mock数据
│ ├── router // 路由
│ ├── store // 全局store管理
│ ├── styles // 全局样式
│ ├── utils // 全局公用方法
│ ├── view // view
│ ├── App.vue // 入口页面
│ └── main.js // 入口 加载组件 初始化等
├── static // 第三方不打包资源
│ └── Tinymce // 富文本
├── .babelrc // babel-loader 配置
├── eslintrc.js // eslint 配置项
├── .gitignore // git 忽略项
├── favicon.ico // favicon图标
├── index.html // html模板
└── package.json // package.json

4.小程序

xcx

小程序蛮简单的,没什么特别需要讲解的。
这里我使用的是别人的demo,和官网一样,改改就行,地址:
https://github.com/zce/weapp-demo

xcx-source

xjsXcx基本目录结构介绍:

1
2
3
4
5
xjs-xcx
├── images //静态图片
├── pages //小程序页面
├── utils //封装的常用工具
└── app.js //小程序入口

7.2 数据库建模

Java最出名的建模工具当属PowerDesigner,全栈当然是想只用一门语言(JavaScript)解决所有技术栈(前端、后台、数据库)。这里我使用的Node.js、MongoDB,具体的框架Koa2、Mongoose

node mongodb

基础知识可参考我之前写过的这两篇文章:
1. 初识Node.js
2. 初识MongoDB


Mongoose是支持Node.js的MongoDB对象建模框架,下面会以mongoose来实现数据库建模。
官网地址:http://mongoosejs.com
以享健身建模为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//引入mongoose,配置MongoDB数据库
const mongoose = require('mongoose');
const url = 'mongodb://localhost/xjs';

//连接数据库
mongoose.connect(url);

//监听数据库状态
const db = mongoose.connection;
db.on('error', ctx => console.log('连接异常:' + ctx));
db.on('connected', ctx => console.log('连接成功'));
db.on('disconnected', ctx => console.log('连接断开'));

//创建模型
mongoose.model('Gym', GymSchema);
mongoose.model('GymBrand', GymBrandSchema);
....

创建一个对象模型步骤:
1.创建Schema定义表结构,作用是定义表字段实现业务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//定义表结构(健身房)
const GymSchema = new mongoose.Schema({
brand: {
type: ObjectId,
ref: 'GymBrand'
}, //该品牌所对应的健身房
name: String, //名称
address: String, ////地址
headImg: {
//列表展示的图片
type: String,
default: 'https://images.vmartaw.com/xjshen/default/IMG_64592a.jpg'
},
pictureOriginal: [], //详情页预览的原始图片
picture: [], //详情页上的图片
mobile: String, //联系电话
// 设施 是否wifi 是否有泳池 是否有淋浴 是否有空调 是否有饮水机
facilities: [],
time: {
//营业时间
type: String,
default: '周一至周五8:00-22:30,周末9:00-21:00'
},
charge: Number, //收费标准
QRcode: String, //二维码
location: [], //经纬度
meta: {
createAt: {
//创建时间
type: Date,
default: Date.now()
},
updateAt: {
//更新时间
type: Date,
default: Date.now()
}
},
status: {
//是否显示 1准备运营 2正常运营
type: Number,
default: 1
},
show: {
//是否显示
type: Boolean,
default: true
}
});

Schema常见的类型有:String、Boolean、Number、Date、Array、ObjectId


2.创建Model定义该对象模型,作用是负责从底层创建和读取文档MongoDB数据库,和MongoDB的数据库表相对应

1
mongoose.model('Gym', GymSchema)

3.创建、删除、查询Document,作用是根据业务实现增删改查操作,和MongoDB的数据记录行相对应

1
2
3
4
5
6
7
8
9
10
11
12
13
//创建
let gym = new Gym(info);

//删除
let gym = await Gym.remove({ _id : { $in: info.ids } });

//查询
let gym = await Gym.findById(id);

//修改
let gym = await Gym.updateMany({ _id : { $in: info.ids } })

//gym就是docuemnet

需要注意的地方:
mongoose好玩的地方,就是实现业务的CRUD操作。
讲解的点可能不是很完善,但确实是经验之谈。

1. _id

MongoDB 中存储的文档必须有一个”_id” 键。这个键的值可以是任何类型的,默认类型是ObjectId,共12个字节,由时间戳+机器+PID+计数器构成,确保唯一性。
mongodb有个好玩的玩法是可以通过mongoimport命令导入json文件的数据到数据库,_id类型可换成String或Number来直接实现表之间的关联。
说的简单点,通过python进行数据爬取,将获取到的数据可直接导入到MongoDB数据库中进行接口调用。因此,在对数据进行爬取前,可以先定义好Schema,尤其是定义好_id
举例:如演员和电影,一名演员可能有多部电影。
1.定义Schema,且_id是String类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//定义表结构(电影表)
const MovieSchema = new mongoose.Schema({
_id: String,
title: String,
desc: String,
src: String,
date: Date
});

mongoose.model('Movie', MovieSchema)

//定义表结构(演员表)
const ActressSchema = new mongoose.Schema({
_id: String,
name: String,
movies: [{type: String, ref: 'Movie'}]
});

2.通过python爬数据,获得演员数据和电影数据信息,格式内容如下:

1
2
3
4
5
6
7
8
//actress.json
{"_id": "ayubu", "name": "ww1", "movies": ["javlii3j34"]}
{"_id": "aysvc", "name": "ww2", "movies": ["javli4yj4y", "javliiasqy"]}

//movies.json
{"_id": "javlii3j34","title": "test", "desc":"test","src": "xxx.jpg", "date": "2010-01-20"}
{"_id": "javli4yj4y","title": "test", "desc":"test2","src": "xxx2.jpg", "date": "2012-01-20"}
{"_id": "javliiasqy","title": "test", "desc":"test3","src": "xxx3.jpg", "date": "2014-01-20"}

3.将爬取的数据,导入到MongoDB数据库中:

1
2
3
4
5
//导入演员数据
sudo mongoimport -h 127.0.0.1:27017 -d man -c actresses /Users/zhangyang/python/actress.json --mode upsert

//导入电影数据
sudo mongoimport -h 127.0.0.1:27017 -d man -c movies /Users/zhangyang/python/movies.json --mode upsert

至此,man数据库里已经自动创建了actressesmovies两张表,且演员和电影也实现了一对多的关系映射。


2.populate和deepPopulate

populate方法是mongoose自带的方法,可以实现关联查询,但只能关联一层
deepPopulate方法需要安装插件mongoose-deep-populate,可以实现多层关联查询
地址:https://github.com/buunguyen/mongoose-deep-populate
举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//获取演员列表,同时返回每个演员所有电影的标题和图片
let list = await Actress.find(condition,opt).populate('movies','-_id title src')

//演员和电影,一层关联

//获取该教练的昵称、工作经验及其所在的健身房的名称、地址、品牌名称
let coach = Coach.findById(id, 'nickname experience gym').
deepPopulate('gym.brand',
{
populate: {
gym: {
match: secondCondition,
select: 'address name brand -_id'
},
'gym.brand': {
select: 'name -_id'
}
}
})

//教练和健身房、健身房和健身品牌,两层关联

3.表的继承

业务有时会涉及到表的继承,比如说app用户和教练
每个APP都有自己的用户,用户可以是app用户,也可以是教练。如果设计成两张表,一个用户表、一个教练表,虽说也行,但是教练如果使用app的话,难道就不算app用户,还得进行关联?准确的设计应该是教练也是app用户,只是比较特殊而已,教练是继承app用户的基本字段的。那么在mongoose中,如何实现表的继承呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//引入Schema和util
const Schema = mongoose.Schema;
const util = require('util');

//声明个基础Schema
function BaseSchema() {
Schema.apply(this, arguments);

this.add({
name: String,
createdAt: Date
});
}
//BaseSchema继承Schema
util.inherits(BaseSchema, Schema);

//声明个用户Schema
const UserSchema = new BaseSchema({
accessToken: String, //用户唯一标识
nickname: {
type: String,
default: '健身小将'
}, //昵称
avatar: {
type: String,
default: 'https://image.xjshen.cn/no-head@2x.png'
}, //头像
number: String, //手机号
code: String, //验证码
});

//声明个教练Schema
const CoachSchema = new BaseSchema({
experience: {
type: Number,
default: 0
}, //工作经验
price: {
type: Number,
default: 0
}, //单次价格
gym: {
type: ObjectId,
ref: 'Gym'
} //所在健身房
});

//创建用户对象模型
const User = mongoose.model('User', UserSchema);

//将用户的属性合并到教练对象模型中
const Coach = User.discriminator('Coach', CoachSchema);

至此,表只会产生users一张表,教练比app用户多了工作经验、单次私教收费价格、所在健身房等额外字段


4.geoWithin和center范围查询

想实现一个业务,寻找附近的健身房
准确来说$geoWithin$center这是MongoDB自带的范围查询。
地址:https://docs.mongodb.com/manual/reference/operator/query/center/#op._S_center

想实现找附近健身房这个业务,原理就是获取用户当前的经纬度,与数据库的健身店铺的经纬度进行范围搜索,比如展示用户10km的健身房店铺。

1
2
3
4
5
6
7
8
9
10
11
12
13
//获取当前用户经度纬度
if(longitude && latitude){
radius = 0.1; //1公里 0.01 10公里 0.1 100公里1
condition['location'] = {
$geoWithin: {
$center: [ [longitude, latitude], radius ]
}
};
console.log(`用户经度:${longitude} 用户纬度:${latitude}`);
}

//这样的话就能返回10km内用户所在的健身房店铺信息
list = await Gym.find(condition,opt).populate('brand','-_id name')

但是上面代码有个问题,尽管我获取到了所有10km内的健身房,但是返回的数据是按插入数据的时间进行排序的,我想要实现从10km距离店铺由近到远进行展示,且想下拉进行分页展示,该怎么办呢?代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
list = await new Promise(function(resolve, reject) {
Gym.find(condition,opt).populate('brand','-_id name').exec((err,data) =>{
if(err){
reject(new Error(err))
}else{
//就近排序
data = data.sort((a,b)=>{return _getDistance(longitude,latitude,a.location[0],a.location[1]) - _getDistance(longitude,latitude,b.location[0],b.location[1])});
//数据分页
data = data.slice((currentPage-1)*pageSize,currentPage*pageSize);
resolve(data);
}
});
});

确实有些复杂,了解一下就行。
最后需要注意的是,使用$center时经度纬度类型必须是Number,如果是String类型,范围查询无效。


7.3 后台接口

既然想用JavaScript一门语言走到底,那么后台接口的实现我用的Koa2及其中间件
这里先说到几个关键词Express、Koa1、Koa2Eggjs、Thinkjs、

1.Express vs Koa

Express是一个简洁而灵活的node.js Web应用框架,开源于2009年6月。
Koa是由Express背后的团队设计的新Web应用框架,开源于2013年8月。

Express地址:https://expressjs.com
Koa地址:https://koajs.com

Express

Koa

关系:
父子关系,子已经离家出走

各自特点:
Express:大而全
Koa:小而精

主要模块:
Express:Application、Request、Response、Router
Koa:Application、Request、Response、Context


2.Koa1 vs Koa2

Koa1

Koa2

关系:
同一个框架,只是版本不同

各自特点:
Koa2语法糖升级,支持async/await功能

说到语法糖,不得不对比下Express、Koa1、Koa2对异步的处理。
Express:ES5、回调嵌套
Koa1:ES6、generator函数+yield语句+Promise
Koa2:ES7、async/await+Promise

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//ES5写法:
function ajaxs(callback){
$.get('a.html',function(dataa) {
$.get('b.html',function(datab) {
$.get('c.html',function(datac) {
callback();
});
});
});
}

ajaxs(function(){
console.log('OK');
});

//ES6写法:
function request(url) {
$.get(url, function(response){
it.next(response);
});
}
function* ajaxs() {
yield request('a.html');
yield request('b.html');
yield request('c.html');
return console.log('OK');
}
let it = ajaxs();
it.next();

//ES7写法:
async function request(url) {
await $.get(url, function(response){
console.log(response);
});
}
async function ajaxs() {
await request('a.html');
await request('b.html');
await request('c.html');
console.log('OK');
}
ajaxs();

就目前而言,使用async/await是解决异步的最佳方法。


3.Eggjs vs Thinkjs

Egg.js是阿里开源的基于Koa2的企业级Node.js框架
Thinkjs是360开源的基于Koa2的企业级Node.js框架

Egg.js地址:https://eggjs.org
Thinkjs地址:https://thinkjs.org

eggjs

thinkjs

按Github的Star数来看,Egg.js的火热程度是Thinkjs的两倍。
说实话,我没怎么用过这两个框架,不知道各自的特性及特点。
引用Egg.js官网的说法:

Koa 是一个非常优秀的框架,然而对于企业级应用来说,它还比较基础。而Egg选择了Koa作为其基础框架,在它的模型基础上,进一步对它进行了一些增强。

Egg的插件机制有很高的可扩展性,一个插件只做一件事。Egg 通过框架聚合这些插件,并根据自己的业务场景定制配置,这样应用的开发成本就变得很低。

目前公司的项目有开始使用Egg.js,等有机会再仔细学习学习Egg.js,深入的东西再慢慢了解。(话说,我是不是忽略了thinkjs,管他的)

xjs-server

xjsServer基本目录结构介绍:

1
2
3
4
5
6
7
8
9
10
xjs-server
├── app //node代码,实现业务及功能
│ ├── controllers //业务层
│ ├── models // 模型层
│ └── service //第三方服务
├── config //配置
│ ├── config.js //全局配置
│ └── routes.js //请求路由配置
├── app.js //主入口,启动服务器
└── package.json //工程描述文件

关于用Koa2实现享健身后台业务的具体功能,请参考我之前写过的文章:
教你用Koa发送手机验证码实现注册登录


7.5 模拟数据

数据这块需要后台管理的支持,比如视频、文章数据的添加,健身房店铺的管理及教练的管理等。
除了用后台管理配置数据外,这里尤其说下图片,因为图片这个资源是巨大的。比如下面这个图,健身房列表里的图应该是缩略图,然后点进去的健身房滑动的图也应该是稍大的缩略图,最后点击进去的图才是真正意思上的原图

xjsData

这样做的好处当然是提高用户体验、减少用户流量,尽管5G时代即将到来,但是处在当前4G的我们,流量还是显得珍贵,试想一下,如果用户进入一个列表,每张图加载的都是2M多的原图,是种什么样的体验?

如何将原图分割成三种尺寸大小不同的图?这里我用到了基于node的图片处理工具gm,地址:https://github.com/aheckmann/gm。使用node来操作IO流、二进制文件是迅速的,代码如下。

1.删除.DS_Store,避免执行node报错

1
sudo find ./ -name ".DS_Store" -depth -exec rm {} \; 

2.将resource目录里面的所有图片处理后到imgs目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
const fs = require('fs')
const gm = require('gm')
const dir = __dirname + '/imgs' //处理后的目录
const beforeDir = __dirname + '/resource' //处理前的目录
//转移文件目录
async function changeDirectory(opath,npath) {
await fs.renameSync(opath,npath);
console.log(`转移目录${opath}成功`);
}

//单张处理
function copySingleImg(opath,npath,img,minWidth,minHeight){
let image = gm(`${opath}/${img}`);
image.size((err,rst)=>{
if(err){
console.log(err);
return;
}
//读取图片大小,然后等比例缩放
let width = rst.width;
let height = rst.height;
//宽高比 3024/4032 算出缩放的宽度
//假设已高度为准,宽度小于最小宽度,则已最小宽度为准,算缩放的高度
let calWidth = width/height * minHeight;
if(calWidth <= minWidth){
let calHeight = minWidth / (width/height);
minHeight = calHeight;
}else{
minWidth = calWidth;
}
//生成目录
mkDir(`${npath}`);
//重新调整其大小,并生成出来
image.resize(minWidth,minHeight).write(`${npath}/${img}`,(err,rst)=>{
if (err) return console.dir(arguments);
console.log(`生成图片${img}成功`);
});
})
}
//多张处理
function copyMultipleImg(imgs,opath,npath,minWidth,minHeight){
imgs.map(img=>copySingleImg(opath,npath,img,minWidth,minHeight));
}
//生成目录
async function mkDir(path){
let isExist = await fs.existsSync(path);
if(!isExist){
let mkdir = await fs.mkdirSync(path);
console.log(`生成目录${path}成功`);
}
}
//删文件
async function removeFile(path){
let isExist = await fs.existsSync(path);
if(isExist){
let mkdir = await fs.unlinkSync(path);
console.log(`删除文件${path}成功`);
}
}
//是否有目录
async function isHasDirectory() {
let dirs = await fs.readdirSync(beforeDir);
let isDirectory = 0;
await dirs.map(async (file)=> {
let stats = await fs.lstatSync(`${beforeDir}/${file}`);
if(stats.isDirectory()){
isDirectory ++;
}
});
if(isDirectory==0){
console.log('还没有图片哟');
return
};
//新建imgs目录
mkDir(dir);
//读取文件目录,处理后放imgs里
readDir();
}
//读取目录
async function readDir() {
let dirs = await fs.readdirSync(beforeDir);
dirs.map(async (file)=> {
let stats = await fs.lstatSync(`${beforeDir}/${file}`);
if(stats.isDirectory()){
//如果是目录,新建对应的目录到imgs
mkDir(`${dir}/${file}`);

//读取目录下面的head目录
let headOPath = `${beforeDir}/${file}/head`;
let headNPath = `${dir}/${file}/head`;
let headDirs = await fs.readdirSync(headOPath);
let imgName = `${headDirs[0]}`;
//处理head下面的图片
await headDirs.map((img)=>{
if(img == '.DS_Store'){
//如果存在.DS_Store,则删除
removeFile(`${headOPath}/.DS_Store`);
}
})
copySingleImg(headOPath,headNPath,imgName,120,108);

//读取目录下面的body目录
let bodyOPath = `${beforeDir}/${file}/body`;
let bodyNPath = `${dir}/${file}/bodyOriginal`;
let bodyN2Path = `${dir}/${file}/body`;
//转移body的图片到bodyO
await changeDirectory(bodyOPath,bodyNPath);
let bodyNDirs = await fs.readdirSync(bodyNPath);
//处理bodyO下面的图片
await bodyNDirs.map((img)=>{
if(img == '.DS_Store'){
//如果存在.DS_Store,则删除
removeFile(`${bodyNPath}/.DS_Store`);
}
})
copyMultipleImg(bodyNDirs,bodyNPath,bodyN2Path,400,300);
}
})
}
//是否有文件目录
isHasDirectory();

3.效果如下:

xjsDataDemo

将43kb和341kb的图,转换成5kb、35kb、341kb的图。
这样能为用户省下很多流量,提高用户体验。


8.漫长的部署阶段

撸完代码,终于可以见人了。其实按理说撸完代码,该进行测试阶段。测试的阶段只有自测和我媳妇儿测,她刚好又是测试,所以能把大bug改掉,我改享健身bug的阶段持续了一个月。

本节分为后端篇和前端篇,跳过测试,假如你的APP已经测试通过,那么会进行下一个阶段,部署阶段。一个APP产品的发布离不开:自己搭建或购买数据库、服务器,上传代码部署接口、上架APP到相应市场
首先介绍后端篇的内容:

8.1.1 Linux常用命令、常见配置文件目录、常见Service

为什么要学Linux?答:服务器需要它。阿里云服务器ECS版本分为Centos和Ubuntu两种操作系统,它们都是基于Linux的。
下面为列出我常用的Linux命令,仅供参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/******************网络管理******************/
// 远程登录服务器
ssh ww@101.132.111.255

// 删除所有的规则
iptables -F

// 查看防火墙规则
iptables -L -n

// 还原备份的防火墙规则
iptables-restore < /etc/iptables.up.rules

// 查看防火墙状态(Ubuntu独有)
ufw status

// 启动、关闭、重启防火墙
ufw enable/disable/reload

/******************文件和目录管理******************/
// 显示目录列表
ls -lht

// 切换工作目录
cd xjs/xjsServer/xjsServer

// 显示用户当前工作目录
pwd

// 查看文本里的内容
cat job.txt -n

// 创建、修改文本里的内容
vi job.txt

// 创建目录
mkdir xjs

// 删除文件或目录
rm -rf xjs

// 更换文件或目录名
mv xjs xjsNew

// 文件移动到指定目录
mv job.txt xjs

// 变更文件或目录的权限
sudo chmod -R 777 xjs

// 设置服务脚本有执行权限
sudo chmod +x a.sh

// 压缩成gz包
tar -zcvf xjs.tar.gz xjs

// 解压gz包
tar -zxvf xjs.tar.gz

// 远程拷贝文件
scp -P 33333 ./gyms.json ww@101.132.111.255:/home/ww/xjs/xjsData/

/******************硬件·内核·Shell·监测******************/
// 查看磁盘使用情况
df -h

// 观察硬盘分区情况、硬盘分区
fdisk -l

打印字符串
echo "Hello,World!"

/******************系统管理******************/
// 创建新的系统用户
adduser ww

// 增删用户到指定组
gpasswd -a/-d ww sudo

// 查看服务状态
service ssh status

// 启动、关闭、重启系统服务
service ssh start/stop/restart

// 查看定时任务
crontab -l

// 编辑定时任务
crontab -e

// 当前系统的进程状态
ps -ef|grep cron

/******************软件·打印·开发·工具******************/
// 安装包
apt-get install vim

// 常见的安装包有:
vim openssl build-essential libssl-dev wget curl git

// 更新软件支持列表
apt-get update

// 更新软件包
apt-get upgrade

// 卸载包
apt-get remove vim

// 查询包
apt-cache show vim

// 搜索包
apt-cache search vim

Linux常见配置文件目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// sudo配置
/etc/sudoers

// 服务器端
/etc/ssh/sshd_config

// 自定义iptables规则
/etc/iptables.up.rules

// 自定义nginx规则
/etc/nginx/conf.d

// mongodb配置
/etc/mongod.conf

// 系统crontab配置
/etc/crontab

Linux常用Service命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 定时任务
sudo service cron start/stop/restart

// ssh协议
sudo service ssh start/stop/restart

// mongodb数据库
sudo service mongod start/stop/restart

// fail2ban防火墙
sudo service fail2ban start/stop/restart

// nginx代理
sudo service nginx start/stop/restart

8.1.2 配置Fail2ban防火墙

Linux管理员的一个重要任务是保护服务器免受非法攻击或访问。上面提到过两个命令iptablesufw
默认情况下,Linux系统带有配置良好的防火墙,比如iptables、Uncomplicated Firewall(UFW),ConfigServer Security Firewall(CSF)等,可以防止多种攻击。

1.那么iptablesufw之间有什么关系呢?
首先iptables可以灵活的定义防火墙规则,功能非常强大。但是由此产生的副作用便是配置过于复杂。一向以简单易用著称Ubuntu在它的发行版中,附带了一个相对iptables简单很多的防火墙配置工具:ufw。
iptables是Linux系统都有的,ufw是Ubuntu系统独有的,比iptables更简单。

2.有了它们俩,为什么还需要fail2ban呢?
Fail2Ban是一款入侵防御软件,可以保护服务器免受暴力攻击。比如能在指定的时间内拒绝特定的IP地址,防止暴力请求。使用也蛮简单的。

1
2
3
4
5
6
7
8
9
10
11
// 1.下载fail2ban
apt-get install fail2ban

// 2.更改配置,具体请参考官网:
http://www.fail2ban.org

// 3.启动、关闭、重启fail2ban防火墙
service fail2ban stop/start/restart

// 4.查看fail2ban防火墙状态
service fail2ban status

8.1.3 安装Nginx

Nginx的常见使用场景有端口转发、反向代理、负载均衡
比如通过一个域名来访问阿里云服务器里面的特定端口服务就能实现接口的调用。

1
2
3
4
5
6
7
8
9
10
11
// 1.安装nginx
apt-get install nginx
nginx -v

// 2.自定义nginx规则
vi /etc/nginx/conf.d/xjs-com-2000.conf
nginx -t

// 3.启动nginx
service nginx start/stop/reload
service nginx status

可以设置一下阿里云的安全组,将端口占用情况做下整理,比如我的:

1
2
3
4
5
6
80 默认端口 ngnix入口
22 登录阿里云端口 可用可不用
33333 登录阿里云端口 正常使用
33334 MongoDB端口
2000 node服务端口
通过ngnix,将m.xjshen.cn域名80443端口转发到2000端口,享健身接口

8.1.4 安装Node.js环境

安装node.js,有点类似java安装jdk,就是提供一整套环境,供你使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 1.安装wget或curl
apt-get install wget curl

// 2.通过wget或curl安装nvm
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
//或者
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash

// 3.安装node.js指定版本
nvm install v8.9.4

// 4.使用该版本
nvm use v8.9.4

// 5.默认使用该版本
nvm alias default v8.9.4

// 6.是否安装成功
node -v

// 7.更新npm包
npm --registry=https://registry.npm.taobao.org install -g npm
npm -v

8.1.5 安装PM2

pm2 是一个带有负载均衡功能的node应用的进程管理器,负责启动、停止、重启node服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1.安装pm2
npm install pm2 -g

// 2.pm2常用命令
// 查看node应用运行状态列表
pm2 list

// 启动、停止、重启、删除第一个node服务器
pm2 start/stop/restart/delete 0

// 查看该node服务器更多信息
pm2 describe 0

// 查看错误日志
pm2 logs 0

将之前本地写好的后端node应用代码通过scp上传到阿里云服务器,然后解压node应用,执行pm2 start实现接口服务的启动。
当然在启动接口服务前,需要有数据库,可以直接购买阿里的云数据库,也可以自己安装,我选的是后者。


8.1.6 MongoDB数据库的安装与配置

1.安装mongodb数据库

参考官方文档:https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu

1
2
3
4
5
6
7
8
// 启动、关闭、重启mongod
service mongod start/stop/restart

// 查看mongodb状态
service mongod status

// 打开mongodb命令行
mongo --port 33334

2.将本地数据库导入到线上

数据库导入线上可以分成整库导入单表导入流程。
首先是整库导入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/******************本地操作******************/
// 1.导出本地数据库
mongodump -h 192.168.1.101:27017 -d xjs -o /Users/wuwei/Desktop/xjs/xjsData/

// 2.导出数据打成gz包
tar -zcvf xjs.tar.gz xjs

// 3.将gz包上传到服务器
scp -P 33333 ./xjs.tar.gz ww@101.132.111.255:/home/ww/xjs/xjsData/

/******************服务器操作******************/
// 4.解压从本地来的gz包
tar -zxvf xjs.tar.gz

// 5.将本地数据导入到服务器数据库
mongorestore -h 127.0.0.1:33334 -d xjs --dir /home/ww/xjs/xjsData/xjs

或者是单表导入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/******************本地操作******************/
// 1.导出本地单表
mongoexport -d xjs -c gyms -o /Users/wuwei/desktop/xjs/xjsData/gyms.json

// 2.将表上传到服务器
scp -P 33333 ./gyms.json ww@101.132.111.255:/home/ww/xjs/xjsData/

/******************服务器操作******************/
// 3.删掉线上数据库表
// 3.1 打开mongodb命令行
mongo 127.0.0.1:33334/xjs -u xjs_admin -p \!QAZ1qaz
use xjs

// 3.2.删除相应表
db.gyms.remove({})

// 4.将表导入到服务器表
mongoimport -h 127.0.0.1:33334 -d xjs -c gyms /home/ww/xjs/xjsData/gyms.json

3.权限控制

MongoDB权限特点:
1.没有默认管理员账号
2.admin管理员账号
3.用户只能在所在数据库登录
4.管理员可以管理所有的数据库,但不能直接管理其他数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 1.添加超级管理员
use admin/db.createUser({user:'ww',pwd:'!QAZ1qaz',roles:[{role:'userAdminAnyDatabase',db:'admin'}]})

// 2.授权超级管理员
db.auth('ww','!QAZ1qaz')

// 3.通过超级管理员授权某数据库管理员

// 3.1.使用该数据库
use xjs

// 3.2.创建该数据库只可读管理员
db.createUser({user:'xjs_read',pwd:'!QAZ1qaz',roles:[{role:'read',db:'xjs'}]})

// 3.3.创建该数据库可读写管理员
db.createUser({user:'xjs_admin',pwd:'!QAZ1qaz',roles:[{role:'readWrite',db:'xjs'}]})

// 3.4.授权该数据库管理员
db.auth('xjs_read','!QAZ1qaz')/db.auth('xjs_admin','!QAZ1qaz')

// 3.5.修改mongodb配置文件
authorization: 'enabled'

// 3.6.重启mongodb生效
service mongod restart

常用MonogDB数据库方面的命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 打开mongodb命令行h
show dbs //展示数据库列表
use xjs //使用数据库
show tables //展示数据库表
db.users.find({}) //展示数据
exit //退出

// 用账号登录到数据库
use admin
db.auth('ww','!QAZ1qaz')

// 直接登录某个数据库
mongo 127.0.0.1:33334/xjs -u xjs_admin -p \!QAZ1qaz

//删除数据库
mongo --host 127.0.0.1:33334 xjs --eval "db.dropDatabase()"

8.1.7 写个定时任务,实现MongoDB数据库的自动备份

1.新建一个mongod_bak.sh文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/sh
DUMP=mongodump
OUT_DIR=/home/ww/xjs/xjsData/xjs_server/tmp
TAR_DIR=/home/ww/xjs/xjsData/xjs_server
DATE=`date +%Y_%m_%d_%H_%M_%S`
DB_USER="xjs_admin"
DB_PASS="!QAZ1qaz"
DAYS=14
TAR_BAK="mongod_bak_$DATE.tar.gz"
cd $OUT_DIR
rm -rf $OUT_DIR/*
mkdir -p $OUT_DIR/$DATE
$DUMP -u $DB_USER -p $DB_PASS -h 127.0.0.1:33334 -d xjs -o $OUT_DIR/$DATE
tar -zcvf $TAR_DIR/$TAR_BAK $DATE
find $TAR_DIR/ -mtime +$DAYS -delete

2.提高mongod_bak.sh该文件的权限,设定它为可执行

1
chmod +x mongod_bak.sh

3.测试一下命令是否没问题,看是否备份成功

1
./mongod_bak.sh

4.如果成功,下一步编辑crontab

1
2
3
4
5
6
7
8
9
10
// 查看定时任务
crontab -l

// 编辑定时任务
crontab -e

// 增加如下代码,表示每天凌晨3点执行数据库备份的sh脚本

0 3 * * * sh ~/crontab/mongod_bak.sh

5.编辑完成后重启crontab使其生效

1
2
3
4
5
// 启动crontab
sudo service cron start

// 查看状态,如果Active是active (running),则生效
sudo service cron status

介绍后端篇的内容后,现在介绍前端篇的内容,前端按理说没有后端的部署复杂,毕竟能做的就是发布到市场或服务器。

8.2.1 上线前端官网

官网因为用的Bootstrap Template实现,就是很简单的静态文件,html、js、css等。
发布这种前端代码是最简单的,不需要后台的任何支持,放到服务器上即可。
因为跟接口无任何挂钩,且只需要放这种静态资源,因此我选择了阿里云虚拟主机
通过FileZilla工具实现代码上传,FileZilla是方便高效的FTP客户端工具。

website

温馨提示:云虚拟主机(包括共享,独享,轻云)不支持SSL,OpenSSL,HTTPS

那如何用让云虚拟主机支持HTTPS呢?答案是使用cdn
通过cdn加速,在cdn上上传的ssl证书即可以实现https。
设置的时候,请注意,需要将动态跟随协议回源打开,然后设置为http回源

HTTP


8.2.2 iOS发布上线到APP Store

发布苹果应用app需要用到两个工具,一个Xcode,另一个iTunes Connect
Xcode能创建构建版本,然后将源码包上传到美国服务器那边,准备在iTunes Connect使用。
iTunes Connect能够管理多个应用app,且管理每个app的新包发布及
修改logo、简介、描述、价格等。


1.打开Xcode,找到上面菜单栏Windows选项,选中organizer。能看到之前打包好的app,点击右边Upload App Store实现构建版本的上传。

APP Store


2.登录https://itunesconnect.apple.com,进入享健身应用,做好相应的编辑即可,点击构建版本处,能看到之前从Xcode上上传的构建版本,选择好后,点击右上角的提交以供审核

myapp

myxjsapp


3.app第一次进行审核的时间应该在3天内,同时也特别的严格。我都被拒了两次才真正的审核通过,每次被拒,都要等7天后才能进行下一次的审核,审核时间挺漫长的。
出现问题比如说你使用了热更新代码;定位的时候没允许什么服务;不能强制用户使用第三方登录等都会被拒。

appstore0


下面是被拒时苹果发的邮件:
appstore1


appstore2


appstore3


8.2.3 安卓发布上线到应用宝

发布安卓应用到应用宝需要用到gradle软件著作权和乐固加固
配置好本地的gradle,即可通过gradlew assembleRelease实现apk打包。
1.打包apk

1
2
// 执行打包命令
cd android && ./gradlew assembleRelease

apk


2.使用腾讯乐固软件进行apk应用加固
为什么应用需要加固?
若应用不做任何安全防护,极易被病毒植入、广告替换、支付渠道篡改、钓鱼、信息劫持等,严重侵害开发者的利益。

legu

操作的具体文档可参考:https://cloud.tencent.com/developer/article/1119450


3.登录应用宝开放平台提交审核
上传使用乐固加固后的apk包到应用宝,然后需要软件著作权才能通过审核,因为需要版权认证。

version


4.审核通过后,增加微下载功能
应用宝审核通过很快,一天左右就能通过。通过后,可以使用微下载功能,即一个链接自动判断是安卓还是iOS手机进行相应下载的安装

download


9.软件著作权和商标权

软件著作权是什么?
软件的开发者或者其他权利人依据有关著作权法律的规定,对于软件作品所享有的各项专有权利。软件著作权分为安卓版和iOS版,安卓上架应用市场必须拥有安卓的软件著作权。

商标权是什么?
注册商标是将您的商标在商标局进行注册,以获得在本地区(大陆)内独家使用该商标标识的权利。比如:享健身app这三个字要想放心大胆地使用,且不会被别人找麻烦的话,建议申请商标第九大类0901

这里说个小插曲:
享健身这三个字,说实话对于做共享健身这类产品,名字上有很强的吸引力,因此在我做这个app的过程中,遇到了一个竞争对手,他让我把享健身在App Store上的这个称号让给他。
想想怎么可能转让给他,这是属于我自己的产品凭什么
他还给我打电话,说谈合作,合作的点就是让我技术入股,如果不行,他说会走法律途径得到享健身这个名称,而且还拿软件著作权吓唬我,说他已经注册享健身这个软件著作权了,已经在手里了。

首先要明白一点商标权才是名字的最终保护权,软件著作权只是软件的保护权,比如保护自己的源码,被别人使用,可以控告他,并非名字的保护权。

为了享健身这个名字能真正意思上的属于我,而且软件也能真正意思上的属于我,我立马注册了享健身安卓的软件著作权和iOS的软件著作权,及享健身商标,共花了我2400大洋,真心贵,因为当时担心享健身在App Store的名字和应用宝的名字怕被他抢走,所以都是加急注册的。

xjszj


注册软件著作权费用:

xjsprice


安卓软件著作权:

xjsAndroid


iOS软件著作权:

xjsiOS


商标:
因为申请商标从递交申请到拿证一般需要 13-18个月,也就1年的时间。所以证还没拿到,但是已经在审核当中了。
可以通过中国商标网,去查询需要的商标名称,看是否被注册过。
地址:http://sbj.saic.gov.cn/sbcx

xjssb


xjssb

最后的结果是:他最终放弃享健身这个名称,改名为"享健身平台",我赢得了此次竞争的胜利。


很高兴你能看到最后,且把准备工作编码工作部署工作三大部分都过来一遍,在这里我总结一下什么是全栈:

全栈工程师本身不应该仅仅局限于前端和后台的开发,而可以尝试去开拓更广泛的领域——因为全栈本身是依赖于工程师本身的学习能力,正是这种优秀的学习能力可以让他们接触更广泛的知识。

目前的我在享健身这个APP中学到的东西不仅仅是技术,还有非技术上的知识。
技术上跨的工种有:产品经理UE交互设计师web前端开发工程师安卓开发工程师ios开发工程师node.js后台开发工程师测试工程师运维工程师python爬虫工程师。(或多或少的有接触和了解)
非技术上跨的工种有:享健身创始人、人事行政专员。(或多或少的有接触和了解)

我想努力成为一个有思想的创业家,但目前的我看来,能力还不够,还有很多东西值得学习和深入,比如:沟通管理财务金融经济等等等。

前面说到的,全是通过JavaScript一门语言就能实现全栈的所有开发,其实还有一门语言也特别适合全栈,它就是Python。未来的路还很长,不能停滞在编程开发的一门语言上,也可以试试第二门编程语言。我会选择Python,明年的目标就是通过Python去做自己想做的事情

最后以乔布斯的经典话语作为本篇文章的结束语:
stay hungry,stay foolish!

保持饥饿,保持愚蠢,大家一起加油!