英雄联盟线下赛事背后的技术实践
近几年,随着电子竞技的赛事火热,越来越多的游戏推出了自己的电竞赛事,玩家们也更多的走进线下,为喜欢的战队打call,体验线下观赛的魅力。于是提升玩家现场的观赛体验也就成为了我们新的研究课题。 有赖于以往对线下活动的支持经验,我们认为基于用户手机去做O2O的全场运营活动同样能为大型赛事活动提供较好的用户体验。 那么如何实现一个O2O的运营活动呢? 技术载体研发 在O2O 活动运营中,存在两类目标受众,一是用户端,也就是现场观众;一是管理端,即现场工作人员。所以我们需要基于两个目标受众来研发对应程序。 面向现场观众,我们连接了基于小程序为起点的互动场景: ① 小程序互动 小程序免下载特性,在线下活动场合非常适用。所以小程序是我们首选的用户端交互载体。基于互动小程序 ,玩家可以在小程序内进行拍照分享留念、获取官方摄影师拍摄的现场精美图片、进入现场观赛聊天室内聊天以及参与现场答题互动赢大奖。 ② 现场大屏互动 为了促进玩家的现场参与度,我们在现场的大屏幕展示了一些东西来促进现场玩家的互动,包含现场玩家签到头像上大屏、玩家在小程序分享的内容上大屏、聊天室弹幕上现场环屏和答题排行榜前十名上大屏播报。 面向工作人员,我们提供了大而全的WEB管理端: WEB管理端的基础功能配置包含小程序首页的Banner图配置以及小程序内的文案标题和一些互动的开关配置。在八周年活动的三天时间里,每天的互动奖品信息和参与规则都会有变化,通过服务端的配置可以节省小程序审核的所需要的时间,大大提高了现场运营的灵活性。 此外,在WEB管理端,我们还提供内容审核、互动抽奖等功能。 现场运营工作部署 在O2O活动运营这个场景下,技术人员不能仅是完成技术载体研发,在强技术的线下运营活动中,由于技术信息的不对称可能会导致现场运营方案运行的不流畅,为了避免这种状况发生,技术人员除了能研发出“技术载体”外,还需要承担一部分的线下客服或工作人员的调配工作,包括现场设备调试、软件使用培训等。是的,你不仅仅是开发工程师。 技术选型:云开发 我们选择了小程序云来搭建我们的后端服务,原因有以下几点: ① 弱化了后端运维概念,节省开发人力和时间 用小程序云最大的好处就是弱化了后端运维的概念,并在提供了云函数、数据库以及云存储(文件存储)的基础上,增加了完善的日志管理、服务端监控等一系列的后端服务,让开发者专注在业务逻辑上的开发上,这对于我们这种小步快跑的业务是非常合适的,也大大的节省了我们的开发时间。 ② 云开发调用,提升开发效率 小程序云在云函数中封装了一些小程序服务端API,可以很方便的通过云调用直接调用,且不需要自己维护access_token这套机制,例如模板消息发送、生成小程序码等,小程序云也在原先小程序服务端API的基础上,增加了OCR识别等通用能力,大大加强了小程序的通用处理能力。 const cloud = require('wx-server-sdk')
cloud.init()
exports.main = async (event, context) => {
try {
const result = await cloud.openapi.templateMessage.send({
touser: 'test_openid',
page: '/pages/test/test',
data: {
keyword1: {
value: '一等奖'
}
},
templateId: 'template_id',
formId: 'form_id'
})
} catch (err) {
console.log(err.message)
}
}
小程序云开发的开发实践小结 在开发的过程中遇到的一些问题和开发实践在这边做个小总结: Q:如何在WEB端调用小程序云中的数据? 上文提到的WEB大屏投放端和WEB管理端是在WEB页面上搭建的,这就需要我们在WEB端去调用小程序云上存储的数据了。那么如何在WEB端调用到存储在小程序云数据库中的数据呢? 1)云开发HTTP API调用 首先想到的就是利用云开发HTTP invokeCloudFunction API去触发云函数调用,将数据读取和写入的逻辑通过云函数先写好,再通过HTTP接口调用。但是这种方式有几个问题: 云开发HTTP API需要在小程序云之外维护一套access_token机制和HTTP服务,会带来额外的开发成本; 云开发HTTP API触发的云函数不能携带调用者的登录态信息,我们无法判断调用者的权限信息。 所以云开发HTTP API并不合适我们这个场景。 2)TCB WEB端SDK 小程序云是由腾讯云云开发(以下简称TCB)团队为小程序这个场景开发的,现在除了小程序端,已经支持了WED端的SDK接入。这里只需要通过小程序账号登陆腾讯云官网,进入云开发控制台,就可以在WEB端通过TCB提供的SDK获取到小程序云上的数据。 Q:实时的弹幕我们是如何实现的? 除了现场大屏之外,赛事现场还设置了三条长短不一的环形屏来投放玩家在小程序聊天室的聊天内容来作为弹幕展示。 环形屏的内容也是投放WEB页面来实现的。这个场景需要比较好的实时性,我们采用了小程序云开发推出的实时数据推送能力去推送审核完的弹幕显示到环形屏幕上。 db.collection("sceneDanmu").where({
checkStatus: 2 // 已经通过审核的弹幕
}).watch({
onChange(snapshot) {
if (snapshot.docChanges[0]) {
if (snapshot.docChanges[0]['dataType'] === 'update') {
addDanmu(snapshot.docChanges[0]["doc"]);
}
}
},
onError(err) {
// err handle
}
});
需要注意的事项: 因为是在网页端通过SDK直接对数据库collection进行监听,这里的对数据的访问权限控制方式是和小程序端发起访问的一致,在这个场景下(WEB端访问),需要将被监听的文档访问权限改成所有用户可读,这样才能在web端监听到数据的变化。 数据库文档的监听条数上限是5000条,这里要对监听的文档数量做一定的评估和优化。 Q:有什么更好的云函数路由优化方式? 小程序云开发的云函数都是运行在不同的开发环境中,每个云函数都是一个功能模块,那么有的时候,为了方便维护管理和公用代码块复用,我们需要将具有相似的处理逻辑云函数合并成一个云函数,也就是路由控制,较为初级的玩法是利用switch....case语法去操作: const userAction = require('./action')
exports.main = async (event, context) => {
let action = event.action
switch (action) {
case 'actionA':
return userAction.actionA(event)
case 'actionB':
return userAction.actionB(event)
case 'actionC':
return userAction.actionC(event)
default:
return userActiion.noAction(event)
}
}
但是switch....case的处理方式较为简单粗暴,更加理想化的方式是采用TCB开发的tcb-router。 tcb-router的路由控制方式是借鉴了koa2的中间件机制来实现的路由控制,通过这个机制,我们将一些权限控制、请求参数校验、错误处理等都通过中间件去实现,示例: const cloud = require('wx-server-sdk')
const TcbRouter = require('tcb-router')
cloud.init()
exports.main = async (event, context) => {
const app = new TcbRouter({
event
})
app.router('add', paramsValidatorMiddleware(event, {
a: paramsValidatorMiddleware.isRequire().number(),
b: paramsValidatorMiddleware.isRequire().number(),
}), async (ctx, next) => {
let { a, b } = event
ctx.body = {
ret: 0,
data: a + b,
errmsg: 'request:ok'
}
}) ...... return app.serve()
}
Q:如何自动切换云函数中的云环境? 一个小程序账号可以创建两个云开发环境,假如云函数内的初始化函数没有显式指定云环境ID的话,并不会默认使用云函数所部署的环境,而是默认的云开发环境,也就是开通小程序云后第一个创建的云环境。如果我们第一个创建的环境为测试环境的话,那么为了在线上环境能够调用到线上环境的数据库和云存储文件,就需要在代码中显式的声明云环境ID。就像这样: const cloud = require('wx-server-sdk')
cloud.init({ env: 'release-envid'})
这样每次在测试环境测试完成后,我们想要直接部署在正式环境,还需要手动指定每个云函数的环境ID,势必会带来一定的维护成本,所以为了优化这部分的逻辑,我们可以这样写: const cloud = require('wx-server-sdk')cloud.init()
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
if (wxContext.ENV !== 'local') { // 本地测试获取到的上下文云环境为local,这里需要判断下
cloud.updateConfig({
env: wxContext.ENV
})
} else {
cloud.updateConfig({
env: '开发环境的环境Id'
})
}
......
}
Q:如何在云环境中使用身份证OCR完成验票? LPL赛事小程序在部分功能上的使用是需要验证玩家的购票信息,因为玩家的购票信息都是实名制的,所以小程序上会用身份证验票。因为小程序云调用支持了身份证OCR识别,所以这块很方便就可以通过云调用API实现(这里也是为小程序云团队点个赞): app.router('getIdCardNo', paramsValidatorMiddleware(event, {
fileId: paramsValidatorMiddleware.isRequire().string()
}), async (ctx, next) => {
let getTempFileURLResult = null
try {
getTempFileURLResult = await cloud.getTempFileURL({
fileList: [event.fileId]
})
const result = await cloud.openapi.ocr.idcard({
type: 'photo',
imgUrl: getTempFileURLResult.fileList[0]['tempFileURL']
})
if (result.errcode === 0 || result.id) {
ctx.body = {
ret: 0,
msg: '请求成功',
data: {
idCardNo: result.id
}
}
} else {
// err handle
}
} catch (err) {
// err handle
}
})