算法
回文链表
请判断一个链表是否为回文链表。
var isPalindrome = function(head) {
if(!head || !head.next) return true;
var prev = new ListNode(head.val);
var l1 = head;
while(l1.next){
var node = new ListNode(l1.next.val);
node.next = prev;
prev = node;
l1 = l1.next;
}
while(prev && head){
if(prev.val === head.val) {
prev = prev.next;
head = head.next;
}else{
return false
}
}
return true
};
判断环形链表
给定一个链表,判断链表中是否有环。
在当前指针上添加额外的参数来判断,算法必须要循环 N 次。
var hasCycle = function(head) {
if(!head) return false;
while(head){
if(head.name){
return true;
}
head.name = true;
head = head.next;
}
return false
};
两个指针比快慢,如果两个指针相遇说明有环
var hasCycle = function(head) {
let fast = head;
let slow = head;
while (fast && fast.next) {
fast = fast.next.next;
slow = slow.next;
if (fast === slow) {
return true;
}
}
return false;
};
二叉树的最大深度
给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
var maxDepth = function(root) {
var max = 0;
if(!root) return max;
const findRoot = (tree, len) => {
if(!tree.left && !tree.right){
max = Math.max(max, len);
}
tree.left && findRoot(tree.left, len + 1);
tree.right && findRoot(tree.right, len + 1);
}
findRoot(root, 1);
return max
};
自助机架构
整体技术机构
整体 ZZJ 系统是一套常规的 B/S 系统,但是由于 YY 环境是内网环境不接入公网,所以我们与 YY 通信是用专线通信,为了安全考虑我们公司服务器和 YY 服务器中间设有一个中心机房,中心机房主要是做防火墙和一套双向 IP 映射主要为了让 YY 隐藏自己的内网 IP,这样我们和 YY 存放的自助机就能通信了。
整体前端是由客户端和业务前端组成,客户端负责提供前端展示的宿主环境 chromium 内核,硬件交互比如读取硬件卡、YB 分解等。
前端基于 Vue 开发的单页面应用,页面量不大,单页面主要是因为自助机的前端宿主环境性能较差,使用多页面的话会有短暂的白屏,恰好客户端此时在做一些同步操作页面就会卡住发生白屏,体验会更加不好。
技术难点
前端和客户端之间是有一套通信机制,按照常规的桥的方式去设计的,客户端在 window 上提供一个对象,客户端所有提供的接口都存放在这个对象上,前端调用客户端大部分为异步操作,都是采用回调的方式,同步操作会占用整个 UI 线程,页面会出现卡顿。
自助机可能为 24 小时开机,并且前端又是单页面的展现形式,所以更新是一个问题,更新的要求可能是某一些 YY 的某一些自助机需要更新。所以这里前端自己写了一套简单的更新机制,前端业务和后端之间建立一个简单的 ws,当然会有常规的心跳,确认送达的逻辑。
前端代码发生更新会往 node 的服务器上请求一个接口,node 接到请求之后会根据约定格式解析,根据哪些 YY 和哪些自助机,对自助机进行 push,前端接到 push 之后判断当前自助机有没有人使用,没有使用就会刷新页面。
业务架构
整体业务架构由这么几个主要的系统构成:基础信息,用户中心,报价,订单中心,支付中心,清算中心等。
基础信息:存放 YY 的基础信息系统,主要是一些静态数据不太经常变动。
用户中心:存放用户信息的系统
报价:由于我们没有库存系统,库存是存放在各个 YY 的,我们的报价也不会常变,但是由于 YY 的承受并发量的能力极弱,所以我们需要有一个单纯的报价系统,主要是同步 YY 的报价信息和库存信息,
订单中心:由于库存不在我们这儿,我们没有真是的库存,所以我们下单之后会往医院发起一次锁号,锁定号源期限为 15 分钟,超时我们向 YY 发起号源释放,支付完成我们向医院进行结果确认,确认完成之后才算挂上号,失败发起退款。
支付中心:主要支付方式有银联、微信、卡支付。我们比常规的支付方式多了一种硬件卡支付,这是一个第三方的卡管系统,只负责写入金额和扣减金额,最后统一结算。银联也是前端和客户端之间进行交互。
清算中心:我们涉及到与卡管中心、YY、YGJ 进行分账和结算,这个对账不会涉及到供应商结算就稍微简单一些,我们还有一套为我们盈利业务结算系统。这套结算要复杂一些涉及到供应商。结算基本上是属于剥离结算,当订单支付以后对应会有多条流水,比如支付流水、附加服务流水、供应商流水等
业务难点
业务主要的难点在于订单支付,由于是单页面应用,所以期望达到最大程度的复用,所有自助机的业务生成订单和支付都在同一个页面。常规的支付中心只用完成收钱的功能。自助机的支付中心还有一套 YB 业务,并且这部分业务还需要根据订单状态进行。所以支付中心还揉入了订单的逻辑。
首先会根据订单状态进行轮询,此时会有一个拆单支付的业务,这块业务是需要调用客户端,客户端调用 YB 前置服务器完成,调用完成之后由前端通知客户端扣减多少,做一个类似动态扣减的过程,然后后端到支付中心生成支付表单…
由于可能从中途任何一个状态断掉,所以整个支付是由后端订单状态驱动,订单不同的状态策略映射前端一个不同的 action,不同的 action 会去执行不同的业务返回,轮询订单的状态并不会停,前端不保存状态机。
微信端
以 Vue2 + vuex + vue-router 单页面系统。涉及一百多个页面。自行设计了页面间的动画,达到 APP 的交互效果。
路由设计自己保存堆栈,判断新开、浏览器左上角返回、手势返回等。保证正常的动画效果。
我们每个页面启动前都要获取用户 openid,这个需要进行重定向,单页面获取 openid 会丢失 hash,所以在重定向时会把 hash 当做 query 词放在 URL 中,在主 hash 映射的业务中再次进行 replace 跳转到对应的业务。
API 层基于 axios 自己封装了一层请求挂在 Vue 的原型链上,定义一层 API 层,主要是做请求定义,字段过滤,字段类型校验等