自助机架构

博客分类: 江河计划

自助机架构

算法

回文链表

请判断一个链表是否为回文链表。

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 层,主要是做请求定义,字段过滤,字段类型校验等