背景
我司的项目管理比较乱,核心数据存在了外部购买的系统上"teambition",比如项目排期、测试用例、bug等数据。PMO同学在做数据统计的时候,先要从teambition倒出一份原始数据,再按照PMO约定的规则计算人效,计算的效率极低。
项目管理系统主要目的最开始是为了解决提高PMO手动出人效的效率,核心是计算产研人员效能。
当时PMO计算人效的公式是内部协商出来的: 1、当研发阶段的时候,开发人效 1 2、当测试阶段的时候,开发人效 0.5、测试人效 1 3、当前上线阶段,开发人效 0.5、测试人效 1 等等还有一些公式。
基于上面的痛点,我们决定开发一条可视化项目管理平台,直观展示研发效能数据。
实现方案
1、经过调研在teambition中,如果达成pmo的效果,需要给teambition提需求,经过沟通比较难实现。
2、自研项目管理系统,通过同步teambition把数据存储到研项目管理系统,把计算人效逻辑封装到后端服务中。
自研系统好处: 1)、锻炼后端开发技术 2)、PMO、产品、研发共建系统,拉起目标 3)、向上管理工具
自研系统缺点: 1)、产品缺乏架构设计 2)、项目质量一般 3)、服务架构单一
业务架构
如下图是当时PMO计得产品原型图,包含从项目管理角度出发覆盖研发效能到质量分报告,涉及功能模块比较全。
首页图表
需求维度
质量分报告
如下图是,思维导图梳理的产品架构:
开发架构
前端
参考vue admin平台,隐藏不需要代码即可,开箱即用。
代码语言:javascript复制https://github.com/PanJiaChen/vue-element-admin
依赖如下库
代码语言:javascript复制vue2.x、axios、echarts、element-ui、stylus等库
后端
后端系统主要使用django全家桶开发,类库比较多、并且ORM功能强大、部署方便。
代码语言:javascript复制django全家桶、 mysql 、redis 、request 、gunicorn等库
经过几个迭代以后,代码越来越多,所有功能模块都写到一个服务上,变得越来越重。应该考虑微服务架构解耦代码问题。
定时任务
1、jenkins
jenkins主要做同步数据脚本使用,配置灵活方便。
2、apscheduler
apscheduler库,为了实现内部方法做定时任务使用。
登录
接入公司的单点sso登录
每个公司为了统一账号密码登录控制,都会由运维部门统一认证后,才能登录各个子系统。目的在于安全、统一管理账号密码。
下图是单点登录的首页,支持账号密码登录或者扫描二维码登录。
接入后,登录后可以拿到姓名、岗位等信息,方便后续权限管理模块开发。
接单点登录系统,一般需要和单点登录系统注册,注册后会把给clientid和clientsecret两个参数,为了安全性考虑。
需要在前端启动首页的时候,如果没有登录就去访问登录首页。
在router.beforeEach函数中,从cookie中获取token,如果没有token跳转到登录页面。
代码语言:javascript复制var hasToken = getCookie('token')
var myDate = new Date()
const logs = {}
const newDate = formatDate(myDate, 'yyyy-M-d h:m:s')
logs.username = hasToken
logs.content = newDate ' ' hasToken ' ' '登录'
logs.type = "logs"
createLogs(logs).then(res => {
})
if (hasToken) {
if (to.path === '/login') {
next({ path: '/' })
NProgress.done()
} else {
const hasPerms = store.getters.perms && store.getters.perms.length > 0
if (hasPerms) {
next()
} else {
try {
const { perms } = await store.dispatch('user/getInfo')
const accessRoutes = await store.dispatch('permission/generateRoutes', perms)
router.addRoutes(accessRoutes)
history record
next({ ...to, replace: true })
} catch (error) {
NProgress.done()
}
}
}
登陆页面的逻辑,首页会判断是有code,没有的话调用接口获取code,code参数通过前端页面拦截获取,所以这样是很安全的.拿到code再去换token,最后获取用户信息的接口携带token参数,跳转到首页。
代码语言:javascript复制mounted() {
this.getTokenByCode()
},
created() {
this.goSSO()
},
goSSO() {
if (!this.code) {
var sso_url =
'https://sso.xxxx.com/oauth/authorize/?client_id='
client_id
'&redirect_uri='
redirect_uri
'&response_type=code'
// console.log("sso_url is " sso_url);
window.location.href = sso_url
}
},
getTokenByCode() {
if (this.code) {
// SSO登录回调
const params = {
code: this.code,
client_id: client_id,
client_secret: constclient_secret,
grant_type: 'authorization_code',
redirect_uri: redirect_uri
}
// console.log(params);
const axiospost = axios.create({
timeout: 500000,
headers: { 'Content-Type': 'multipart/form-data' },
withCredentials: true
})
const formData = new FormData()
for (const k in params) {
formData.append(k, params[k])
}
axiospost.post(tokenURL, formData).then(res => {)
const access_token = res.data['access_token']
this.getTokenByUserName(access_token)
})
}
},
getTokenByUserName(access_token) {
console.log('getTokenByUserName')
const axiospost = axios.create({
timeout: 500000,
headers: {
'Content-Type': 'multipart/form-data',
Authorization: 'Bearer ' access_token
},
withCredentials: true
})
axiospost.get(infoURL, null).then(res => {
const username = res.data['name'])
if (username) {
setCookie('userName', username, 7)
this.$router.push('/dashboard')
} else {
this.$router.push('/login')
}
})
}
共享登录状态
内部有很多系统都接入了单点登陆系统,为了能登陆一次系统后,再登陆其他系统就不需要再登陆,需要共享登陆状态。
如果设置cookie的话,把cookie种在根域下就可以,这样就通过cookie完成了共享登录状态。
代码语言:javascript复制export function setCookie(cname, cvalue, exdays) {
var d = new Date()
d.setTime(d.getTime() (exdays * 24 * 60 * 60 * 1000))
var expires = 'expires=' d.toUTCString()
document.cookie = cname '=' cvalue '; ' expires ';' 'domain=.xxxx.com;'
}
首页
数据来源
由于我们和"teambition"第三方公司签约到很久,需求数据、bug、测试用例都存在了 "teambition"平台。所以需要使用对接"teambition"的api完成数据同步。
teambition接口鉴权这款还是比较复杂的,首先有管理员账号并且生成对应的token。
使用jwt对参数进行编码解码等一系列操作生成token,放在请求的header中。
代码语言:javascript复制def get_token():
"""
生成token
:return:
"""
payload = {}
iat = math.floor(int(time.time()) / 3600 * 3600)
payload['iat'] = iat
payload['_appId'] = appId
headers = {
"alg": "xxx",
"typ": "JWT"
}
token = jwt.encode(payload, appSecret, headers=headers).decode('utf-8')
return token
teambition的官方接口文档,如果对接第三方系统,没有好的接口文档,开发的过程中会很痛苦。
代码语言:javascript复制https://open.teambition.com/help
查询任务,只需要把task id传过去就行了。
还支持tql查询,扩展性比较强。
增量同步
因为增量同步可以减少同步时间,但是可能会存在同步数据存在误差。
主要有几个原因,需要保持新增、删除、变更的数据一样,这样就需要同步脚本有大量的逻辑判断。所以写完脚本,需要测试各种场景保障数据的一致性。
全量同步
这种用法比较少,暴力做法就是删库重新同步。
webhook方式同步
当"teambition"的数据发生变化,主动给我们服务发送请求告知数据变化。这种可以满足数据实时更新,但是需要服务端有逻辑判断。
数据管理
迭代填报
迭代填报主要是从"teambition"同步过来数据,这里只做展示,一般不会手动改数据。
人事管理
人事管理对接了"薪人薪事"系统,可以拿到人员状态。
请假管理
请假管理需要手动填报,具体因为很多人请假不走"薪人薪事"系统,每周五时候pmo手动同级。
需求管理
需求管理也是同步"teambition"空间的数据,需求有几种流转状态,当状态变更时候需要记录。
数据展示
业务线人效统计
从业务线唯独可以清晰的知道,每个业务线的数据。
按职能人效统计
因为我们公司前端在业务线干活,但是隶属统一前端leader管理,职能人效可以发现谁不忙就让谁补其他人得需求开发。
人员饱和度看板
人员饱和度主要是想细化到每人身上,关注每个人得产出。
项目投入统计
App发版日历
App发版日历主要想看本次发版的需求,共涉及多少个需求。
PMO项目人员统计
人事数据统计
周报分析统计
项目总结统计
项目管理甘特图
项目各阶段周期
系统设置
当时有个OKR是,想看看我们得系统使用率,所以做了埋点数据统计。
权限管理
权限管理是通过vue的动态路由实现和并且结合自定义指令控制按钮权限。
菜单控制
项目迭代的心得
1、每次版本开发前,PMO会做立项会。
2、每次版本发布前,PMO会做产品发布会。
3、内部系统一样做好技术文档沉淀。
4、项目完成后做复盘。
5、测试环境和线上环境隔离。
6、内部系统一样提bug,标明优先级。
落地效果
有几点用: 1、老板的确很喜欢这种系统。
2、通过数字化、图表化,反映研发效能。
3、产品也喜欢看谁有排期。
不好的点: 1、开发很diss这个系统,也能明白。
2、有一定的维持成本。
总结
研发效能开发会是未来趋势,大厂都开始自研内部效能系统了并且开始商业化了,为啥大家不计成本开始做研发效能呢,我觉得可能有几点思考:
1、随着自动化测试体系搭建,需要有一套流水线串联自动化测试。
2、需求迭代过程产生的数据需要被存储归档。
3、项目过程和结果需要被度量。
4、通过效能平台打通各个内部系统,串联研发流程。
回到项目管理系统本身,在小公司可能测开就去完成了,但是是否能成一个比较好得产品还是比较难,在大厂会是研发效能去当一个产品迭代开发,服务整体研发团队.从另外一个角度来开,QA应该从这个研发效能去看问题,这样才能看到更多团队内得问题。