需求方面:
上来就是上千页的英文需求文档,还不是我们公司写的,写完之后直接交付然后撤了,最后的实践表明50%的逻辑不通,心态爆炸。看似可能50%的功能返工,其实是60%多的功能返回,有人可能不理解,这么说吧,到目前为止没有一个人会使用整个系统,开发不会,产品不会(前期没产品),客户不会,没错就是这么夸张。
架构方面:服务端:spring boot+spring cloud
web前端:vue + electron
app: flutter
自动化部署: Jenkins + open shift + gitea
难点: 这篇文章只说遇到的难点和使用什么技术解决的,具体解决代码,有兴趣可以讨论
java: 服务端的其实还好,主要的就是业务复杂包括对接香港那边的支付,和内地区别很大;其次是my government Hong Kong对接与iamsmart对接了
app: 手机端采用了flutte开发,主要难点是一开始原生开发没做过需要时间去学习,熟悉之后问题不大,OCR这块都是采用第三方的,比较困难解决不了的就采用原生开发,flutter也支持。
web端:
- ST管理端:管理端具备的都具备,什么权限,多角色登录展示不同,导航栏等,主要他要支持打印机(A4),小票打印机,身份证读卡器,八达通支付,银行卡支付,二维码扫描,卡片打印.
(针对上面的实现采用了electron框架开发,然后使用webview加载H5页面,这样既支持浏览器访问也可以在exe中运行,不需要开发两边。至于上面原生功能则是采用ffi-napi调用自己编写好的DLL(C++编写的)来实现,有的没办法比如是面向对象开发的,这样ffi-napi没办法使用,于是直接写好EXE,然后node调用并与EXE直接通信来解决。) - IC端:这个比较麻烦的就是完全国际化和有的组件实现都需要自己写(有自己的UI设计方案,市面上的UI框架不适合,只是一小部分的)。然后就是这个框架是采用vue2+TS的写法,习惯js的开始可能不熟悉。下面是新增的特性
这是请求接口的封装,每一个和java controller一一对应的(熟悉java的看起来很舒服)
import { Controller, Get } from "../middleware/controller"
import request from "../utils/request"
import { MyResponseType } from "../interface/my-response-type"
@Controller({ path: "/cms/api/v1/publ", token: "no" })
export class CmsApi {
@Get("/contents/{modelCode}") //表明是一个get请求并且是路径传参
public async getByModelCodeApi(params: MyRequest) {
return request(params)
}
}
//这是VUEX的
import { VuexModule, Module, Action, getModule, Mutation } from "vuex-module-decorators"
import store from "@/store"
import { ParamApi } from "@web-app/common/api/param"
import Loading from "@web-app/common/middleware/loading"
interface ParamState {
district: any
param: any
}
const params = new ParamApi()
@Module({ dynamic: true, store, name: "param", namespaced: true })
class Param extends VuexModule implements ParamState {
public param: unknown = {}
public district: unknown = []
@Action
@Loading //这个装饰器表示这个具有loading的效果
@Throttle //这个装饰器表示这个具有截流的效果
async getParam(param: MyRequest) {
return await params.paramApi(param)
}
@Mutation
//这个装饰器表示这个使用vuex设值会自动缓存到session,刷新也不会丢失
@CacheSession({ key: "#programme.preConsentCacheData", value: "preConsentCacheData" })
public SET_PRE_CONSENT_CACHE_DATA(data: any) {
this.preConsentCacheData = data
}
}
- 3S自助机端:这个端是在windows上运行的EXE,所以也是采用electron开发的。
- 八达通,小票打印(和ST还不一样),二维码,灯带:采用ffi-napi+DLL+exe等方式实现,其中八达通最复杂,专门的八达通人员来测试,要支持所有异常场景,上传文件失败写系统日志然后发邮件等(这个来回一个月,主要没有各种异常的卡,也没文档,测试有问题记录统一改,下次来继续测,来测试还要预约他们)
- 断网检测,时钟和定时任务:采用electron中的多进程实现,专门起一个进程实现这个,这样不影响主页面卡顿
- 离线checkin功能:这就是在断网情况下也要支持签到。采用koa+sqplite3,具体实现就是使用electron的代理转发,当断网时转发我electron中启动的koa服务,然后使用sqplite3数据库作为存储,接口和java一模一样,业务也是,当网络重新连接后自动同步数据(说起来简单,实际操作起来问题也不少)
- windows虚拟键盘:这个是这里面最恶心的,其他的只是负责可以做,这个开始毫无头绪,要支持香港繁体,简体,英文输入法,然后还要切换成手写模式(香港不少老年人不会打字),主要是还要结合electron实现,困难重重。最终解决方式是采用C++开发IMM实现打字键盘,然后使用C#来开发手写键盘,最后使用electron调用EXE然后通信(不能是DLL不支持)
- windows系统:这个就是为了保持windows配置一致,有的场馆各种问题一看配置有问题,这是没办法一个个去看改,只能代码控制写注册表改配置
- 其他端:剩下的都没什么复杂的包含5端的开发没必要一一列举了
自动化部署:
如果只是简单的使用Jenkins+docker部署微服务是很简单的,这个轻车熟路。但是现在是Jenkins+docker+openshift部署这就很复杂了
1. 首先是我们自己的代码库gitlab到部署代码库gitea同步代码,然后每次提交都要和版本号一一对应,这样每次发布时我只需要看每次提交的版本号,然后直接拉取就行(至于git两边提交的冲突怎么解决暂时不提,包括是Gitflow 工作流还是什么)2. 然后就是Jenkins这边步骤了,先是配置Jenkinsfile放在gitea上管理,然后开出变量版本号分支等,这个都是简单的,复杂的是配置代理服务器然后结合open shift中的buildconfig来制作docker镜像,这步是最麻烦的,buildconfig分为4种模式,每种都不一样,还有结合本地编译结果上传到nexus镜像版本保留等,其次就是各种代理配置,因为是内网,所以很多jar,npm包都需要下载传到服务器上,极其麻烦。中间还有添加工作流,分为dev,ff,uat.sand.train,prod等环境。也要包含# SonarQube代码检测,镜像检测,漏掉检测等
3. 最后就是open shift的部署了,这就很复杂了,有篇专门说的到此为止好像难点没有了,其实最难的就是上线发布,很多UAT环境OK的,一道正式环境就各种稀奇古怪的问题,要么就是之前没有测试出来,最后香港居民反馈的问题,焦头烂额