[Oak 17] 团队项目:Beta 阶段项目展示
[Oak 17] 团队项目:Beta 阶段项目展示
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 2024年北航敏捷软件工程 |
| 这个作业的要求在哪里 | [T.17] 团队项目:Beta 阶段项目展示 |
| 我在这个课程的目标是 | 学习软件工程的方法论,强化成员团队合作能力,并通过实际开发产品进行实践。 |
| 这个作业在哪个具体方面帮助我实现目标 | 发布 Beta 阶段的相关说明,描述新的功能特性,运行环境要求,安装使用方法,系统已知的问题 |
Author: Oak团队
Date: 2024.06.13
目录
- [Oak 17] 团队项目:Beta 阶段项目展示
- 项目与团队亮点
- 一、团队成员分工与项目管理
- 1. 团队成员
- 2. 项目管理
- 二、典型用户场景
- 1. 项目开发前的目标,预期的典型用户场景,预期的功能描述
- 典型用户场景
- 典型应用场景
- 2. 项目发布的功能和发布地址
- 2.1 路径规划、锚点样式与用户引导(还有一个小彩蛋)
- 路径规划
- 用户引导
- 彩蛋
- 2.2 地图风格化与日记模块
- 新功能特性
- 应用场景(地图)
- 实现需求
- 应用场景(日记)
- 实现需求
- 应用场景(账单)
- 2.3 用户系统与地图导出(PDF or JPG)
- 注册页面
- 登录页面
- 修改密码
- 开始页面
- 个人主页
- 导出功能
- 发布地址
- 三、杀手级功能
- 地图搜索与收藏
- 地图样式自主风格化
- 马卡龙地图样式
- 酱籽地图样式
- 锚点样式自定义
- 地图导出功能
- 导出JPG示例
- 导出PDF示例
- 日记与记账
- 彩蛋
- 四、产品发布与用户情况
- 五、软件工程质量
- 项目与团队总结
- 一、项目管理
- 1. 团队成员简介与个人博客地址
- 2. 项目管理方式
- 3. 团队分工协作的方式
- 3.1Beta 开发计划概览
- 3.2 任务划分
- 3.3 团队分工
- 4. 团队成员沟通和对接的方式
- 5. 团队如何平衡 时间/质量/资源 争取如期完成任务的
- 6. 项目实际进展
- 7. 成员在Beta阶段的角色和具体贡献
- Alpha与Beta阶段贡献分平均得分
- 二、用户场景
- 1. 项目开发前的目标,预期的典型用户场景,预期的功能描述
- 典型用户场景
- 典型应用场景
- 预期功能描述
- 2. 项目发布的功能和发布地址
- 2.1 路径规划、锚点样式与用户引导(还有一个小彩蛋)
- 路径规划
- 用户引导
- 彩蛋
- 2.2 地图风格化与日记模块
- 新功能特性
- 应用场景(地图)
- 实现需求
- 应用场景(日记)
- 实现需求
- 应用场景(账单)
- 2.3 用户系统与地图导出(PDF or JPG)
- 注册页面
- 登录页面
- 修改密码
- 开始页面
- 个人主页
- 导出功能
- 发布地址
- 3. 项目发布后是否满足了全部典型场景
- 4. 项目发布后真正符合用户需求了吗
- 三、用户日活
- 1. 项目发布时团队做了哪些努力来推广项目
- 2. 软件的日活跃用户量是否达到了预先定义的数量
- 3. 用户在功能改进上有所反馈
- 4. 用户在Bug上有所反馈
- 四、特色功能
- 1. 项目的杀手级功能,与竞品相比最特色的功能展现。
- 地图搜索与收藏
- 地图样式自主风格化
- 马卡龙地图样式
- 酱籽地图样式
- 锚点样式自定义
- 地图导出功能
- 导出JPG示例
- 导出PDF示例
- 日记与记账
- 彩蛋
- 2. 思考一下竞品出于什么原因并没有囊括该特色功能,团队凭借什么样的优势实现了它?
- 风格化与导出
- 日记与记账
- 3. 团队成员对这些功能的自我评价如何,是否达到了预期目标,为什么?
- 4. 用户对这些功能的评价如何,是否认为这些功能富有特色,为什么?
- 五、软件工程质量
- 1. 项目文档与约定代码规范
- 项目文档
- 约定代码规范
- 2. 项目是否有出现代码混乱,没有注释,没有详细文档的问题?明年的同学继续开发这个项目,会不会出现以上抱怨?如果一个新学生在一台新机器上想编译并运行你的项目, 请问能顺利完成么?有什么样的文档能指导新学生?
- 3. 测试,测试用例数目,代码覆盖率
- 3.1 单元测试
- 类覆盖率
- 方法覆盖率
- 3.2 SauceLabs浏览器适配性测试
- firefox dev win11
- iphone12-ios16.7
- Safari17-Mac13
- 3.3 压力测试
- 添加地图压力测试 /api/graph/add
- 标点添加压力测试 /api/marker/add
- 地图修改压力测试 /api/graph/modify
- 标点修改压力测试 /api/marker/modify
- 路径添加压力测试 /api/path/add
- 标点添加压力测试 /api/marker/add
- 修改日记压力测试 /api/diary/modify
- 添加日记压力测试 /api/diary/add
- 添加账单压力测试 /api/account/add
- 修改账单压力测试 /api/account/modify
- 4. CI/CD
- 5. 经验教训
- 收获与经验教训
项目与团队亮点
一、团队成员分工与项目管理
1. 团队成员
Oak 团队共由 7 名成员组成,具体分工如下图所示

2. 项目管理
首先,使用金山文档——Oak 开发工作日志:进行任务分工展示、日常会议纪要、会议主题与后续安排确定、任务板等需要协作编辑的事务,工作日志字数已超过19000字

同时,使用微信群用于即时沟通,承载 Oak 团队的基本交流需求
并且使用腾讯会议于每周四晚 19:00 召开周例会,如果线上则采用腾讯会议的形式。同时 Beta 阶段每两天召开一次 Scrum-meeting,统一会议时间设置在晚上 22:00,具体会议内容可以参考每次的会议纪要。
必要的时候,采取线下面对面沟通,重要事项讨论时采用。在第四周测试与集成的过程中,全天全队线下进行网站的开发与测试,保证开发的敏捷性。
Github:拟采用前后端分仓库的形式进行代码管理,选用 Github 的原因如下。开源社区、用户基数庞大,GitHub拥有1亿以上的开发人员,400万以上组织机构和3.3亿以上资料库;强大的工具和功能,使用GitHub Actions可以自动化项目流程,并且模版众多;优秀的UI设计,充满着未来的科技赛博感和代码的强逻辑性与强创造力,同时坐拥了良好的用户反馈与动画
Github Actions:持续开发与持续部署
项目前后端仓库均实现了 CI/CD,实现理由:
- 自动化实现前后端项目的部署
- 每次代码修改后,能够迅速地到软件网址处查看修改的结果,不需要人工进行服务器上的代码同步
- 实施自动化测试,以检查代码有效性和程序功能
- 有效缩减发布时间,实现敏捷性开发
前端
前端的CI/CD,实现人员私以为比较优雅(嘿嘿),基本实现工具囊括了Github Actions、Docker、nginx,过程可以分为以下几个阶段:
- 登录至 DockerHub
- 依据 Dockerfile 针对前端文件调用 Nodejs 进行打包与构建
- 依据 Dockerfile 启动 nginx 进行反向代理
- 将构建的镜像容器上传至 Dockerhub
- 登录至腾讯云服务器
- 执行脚本文件,包括拉取镜像容器、停止此前运行的前端容器、删除已停止的容器、运行对应的容器并执行相应的端口映射
后端
后端的CI/CD比较丑陋,但怎么说呢,反正能用(),基本实现工具包括Github Actions、uwsgi,过程可以分为以下几个阶段:
- 登录至腾讯云服务器
- 执行脚本文件
- git拉取后端仓库dev分支
- tmux启动对应窗口,首先停止此前的uwsgi进程,然后依据uwsgi.ini的配置启动新的后端进程
领歌Leangoo:进行团队燃尽图的绘制

在项目管理中,由于课程组提供的基于Github Issue与Milestone绘制的工具不能使用,本次开发Oak主要采用 Leangoo领歌 来进行燃尽图的绘制,根据任务分配设置任务看板,并在每次 scrum-meeting 前针对此两日的工作进行已完成标注。如上图所示,该燃尽图表明团队整体开发基本符合规范,在开发阶段有合理的时间分配,不存在畸形的开发进度。
由于并未结合Issue,所以事实上该燃尽图是一定程度上美化了团队的实际开发进展的,主要可以从以下几个方面展开描述:
- 并不是每日进行已完成任务的勾选,有些任务完成但纳入领歌的时间不及时,所以可以在燃尽图中看到有些开发日没有开发记录存在
- 燃尽图的截止日期是6.3,但实际上此后一周的时间里在API对接、网站部署与测试等方面还花费了巨大的时间未被记录
二、典型用户场景
1. 项目开发前的目标,预期的典型用户场景,预期的功能描述
Slogan:把地图当作画布,记录美好生活
定位:以地图为基础的生活记录和分享平台
在快速发展的数字时代,人们的生活方式和记录习惯也在发生变化。传统的地图应用主要关注于导航和位置搜索,而忽视了个人生活记录与分享的需求。"吟游Map"旨在填补这一市场空缺,提供一个结合地图与生活记录的平台,不仅帮助用户规划行程,记录旅行中的点点滴滴,还能在日常生活中捕捉和分享那些与地点相关的特殊时刻。这种结合地图的生活记录方式,为用户带来全新的体验和记忆存储方式,增加了地理信息的情感价值。
典型用户场景
旅游博主
| 姓名 | Elena |
|---|---|
| 年龄 | 30 |
| 职业 | 社交平台博主 |
| 需求 | 记录自己的旅行,在小红书、B站、抖音上分享旅行日常、攻略 |
| 占比 | 15% |
| 旅行爱好者 |
| 姓名 | Billy |
|---|---|
| 年龄 | 23 |
| 职业 | 学生 |
| 需求 | 出行前制定旅行计划,出行过程中记录日常,在旅行后可以在微信朋友圈分享自己的旅行记录 |
| 占比 | 80% |
| 文旅相关工作人员 |
| 姓名 | Iris |
|---|---|
| 年龄 | 35 |
| 职业 | 文旅宣传工作负责人 |
| 需求 | 记录所在地区的旅行热点、在社交媒体及官方宣传渠道分享宣传,吸引游客前来旅行 |
| 占比 | 5% |
典型应用场景
计划
Billy 决定在即将到来的五一假期和四个同学前往天津旅行。出行前他查找了大量的攻略,但这些攻略分布在不同的平台,将这些攻略分享给一起出行的朋友以及在实际旅行时打开每一个查看都十分繁杂,于是他决定用”吟游Map“结合已搜索到的攻略制定自己的旅行计划并在出行前发送给每一个同学。
他首先创建新的地图画布,选定自己要前往的地区。
接着,他根据搜集来的攻略在地图上标记要旅行的景点,并选定参观景点的顺序,再通过路线规划功能自动生成推荐的旅行路线。
然后,他选择标定的景点,并添加备注文本。他可以将参观该景点的建议、景点开放时间、门票价格、景点亮点等信息均填入到备注文本中,便于在实地参观使用。
最后他使用导出地图画布,将制定好的攻略以 pdf 的形式发送给同行的同学们。
记录
Elena 决定前往欧洲旅行,以往她会在小红书等社交媒体平台同步更新一期自己的旅行 vlog,但上次视频下,有粉丝留言希望她能同时分享文图结合的旅行日志,于是在这次她决定使用”吟游Map“来完成粉丝的这一心愿。
第一站,她来到了巴黎,她在巴黎游览了埃菲尔铁塔,探索了世界上最大的艺术博物馆卢浮宫,在香榭丽舍大街上步行到凯旋门……下一站她决定前往米兰,在机场,她决定写一份巴黎的旅行日志。
她首先新建地图画布,将地区选在了巴黎,然后标记自己到访过的景点,并在选定景点插入了自己旅行时拍的照片,同时加入了文字介绍,接着她根据自己的旅行顺序,绘制了旅行线路图,并最后记录了自己在巴黎的账单。
完成地图画布的创作后,她根据自己的喜欢,风格化了地图,并将其以图文的方式分享在了小红书。
分享
Iris 是 D 市文化和旅游局的工作人员,她承担着 D 市传文旅的工作。以往她发布的宣传信息中,文字与图片分离,地图上的路线信息和景点的介绍彼此孤立,因此导致宣传效果大大折扣。她在了解到”吟游Map
“可以把地图当成画布,在地图上记录后,决定在最新的宣传中使用”吟游Map“来编辑文案,完成自己的宣传工作。
她首先新建地图画布,选择 D 市的范围,然后标记了特色、热门的景点,并在插入景区照片,加入了景点信息介绍,同时还描绘了推荐路线图。
她在地图画布的编辑完成后,选择要分享的平台,根据不同平台导出对应形式的文案,并分享在不同的平台上。
在使用了”吟游Map“后,这一次的浏览量大幅提升,高质量地完成了宣传工作,在过去了清明假期中,吸引了大量游客,为 D 市带来了大量的旅游收入。
2. 项目发布的功能和发布地址
2.1 路径规划、锚点样式与用户引导(还有一个小彩蛋)
小明是一位喜欢旅游的大学生,但他一直苦于没有合适的地方来记录每次经历。某次朋友给他推荐了「吟游Map」app,听说这款软件可以帮他在地图上记录生活,于是小明准备试试这款软件。
路径规划
首先小明通过搜索功能找到目标景点:

随后,小明点击目标景点以跳转到对应位置,手动缩放到合适大小:

点击地址搜索栏的 x 号关闭地址搜索组件。
随后,小明在地图上标注出值得一去的景。选择锚点工具,然后标注:

上图标注了来北航学院路校区必去的几个地方:大运村、新主楼、航空航天博物馆、晨兴音乐厅、绿园。
不过先去哪里?后去哪里?最佳的游玩路线是什么?
切换到路径规划工具,在右上方选择步行作为出行方式,然后按照期望的游玩顺序点击锚点,首先点击大运村和绿园的锚点:

然后点击绿园和航空航天博物馆的锚点:

重复这个操作,把剩下的锚点也加到路径里:

不小心点错了?没关系,右键锚点可以删除附着到该锚点上的路径。删除新主楼锚点上附着的路径:

锚点标记的位置不太理想?没关系,回到锚点工具下,拖动锚点,路径将会自动重新规划。移动航空航天博物馆以及晨兴音乐厅的锚点:

完善一下,一份北航学院路校区的旅游攻略就做好了:

用户也可以边旅行边记录走过的路径,比如在沈阳玩上几天,可能会得到下面这张地图:

用户引导
本次发布新增了用户引导的功能。点击地图页面右下角的 ? 按钮即可进入当前工具视图下的用户引导。
例如,目前没有选中任何工具,点击 ? 按钮则会依次介绍页面上所有元素的功能:

如果正在使用锚点工具,点击 ? 按钮则会介绍锚点工具的使用方法:

除此之外,本次发布也对危险行为添加了二次确认,例如删除地图时,会弹出二次确认的对话框:

彩蛋
在 raccoon 路由下,可以进入Oak开发团队的菜单页面,该桌面宠物本来想作为用户引导的一部分,大大提升该产品的趣味性。可以做到最后,本团队遗憾地发现该组件的背景色在Web端无法调整至透明。
Anyway,这也是一次伟大的尝试!!!最后我们将它作为一个彩蛋页面,或许你可以试试多点几次,点到100次说不定还有惊喜~

2.2 地图风格化与日记模块
新功能特性
- 地图相关
- 地图风格化
- 日记相关
- 以富文本形式书写日记
- 账单相关
- 以表格形式记录账单
- 账单相关信息自动统计
应用场景(地图)
实现需求
- 风格化的地图
小A在创建地图画布后,可以根据自己的喜好选择对应的地图风格,如标准、幻影黑、月光银、远山黛等等。


应用场景(日记)
实现需求
- 富文本形式的日记
小A在白天参观中国国家博物馆后,想把自己的见闻以日记的形式记录下来。
首先小A在地图上用"marker"标记出中国国家博物馆。

然后小A点击对应锚点,点击查看日记。

接着小A新增日记,输入标题和摘要,便点击查看。


此时的日记内容为空,小A点击编辑日记,用文字结合自己白天拍摄的图片,编辑了图文并茂的日记。

应用场景(账单)
小A想记录一下在北京旅游第一天的花费,TA同样先在地图上标记今天游览过的地方。

点击对应的"marker",创建并查看账单。

再点击编辑账单,进入编辑页面。

小A根据今天的花费,在账单中添加记录。

下方自动为小A统计出了每类花费的占比等数据。

2.3 用户系统与地图导出(PDF or JPG)
小明是一位喜欢旅游的大学生,但他一直苦于没有合适的地方来记录每次经历。某次朋友给他推荐了「吟游Map」app,听说这款软件可以帮他在地图上记录生活,于是小明准备试试这款软件。
注册页面
小明首先注册了一个账号。

该页面引导新用户进行注册。新用户在输入用户名、密码并再次输入相同的密码后点击注册按钮,待系统检测通过后即可用新账号进行登录。
登录页面
小明随即用刚刚注册的账号进行登录。

该页面引导用户进行登录。用户输入用户名以及密码后点击登录按钮,待系统验证通过后即可成功登录。
修改密码
小明觉得之前用的密码不够安全,所以决定修改密码。

该页面允许用户修改密码。用户在登录状态下输入原密码并输入两次新密码后点击修改按钮,等待系统验证通过后修改密码成功。
开始页面
小明登录成功后来到了开始页面,瞬间小明就被宣传语深深吸引住了,于是他迫不及待点击了开始按钮。

个人主页
小明的每一次旅行对他而言都是一次非常珍贵的回忆,他在主页创建了一张又一张地图来记录每一次旅行的点点滴滴。

用户可以在主页创建新地图,主页上会显示用户最近更新的地图以及其他地图的名字,标记数目,日记数目等信息。

用户可以收藏地图。点击地图右边的五角星即可收藏/取消收藏,收藏的地图会在收藏页展示。

用户可以搜索创建的地图。点击搜索框后在框中输入关键词即可查看相关的地图。
导出功能
小明想要把自己精心制作的地图保存下来或分享给其他人看看,于是他尝试了导出功能。

用户可以点击右上角的导出图片/pdf按钮将地图导出为图片/pdf。
发布地址
「吟游Map」是一款 Web 端应用,通过浏览器访问 吟游Map即可
三、杀手级功能
地图搜索与收藏
地图搜索

地图收藏

地图样式自主风格化

马卡龙地图样式

酱籽地图样式

锚点样式自定义

地图导出功能
支持PDF导出与JPG导出,分别对应攻略分享和朋友圈分享两个场景

导出JPG示例

导出PDF示例

日记与记账


彩蛋

剩余介绍详见“项目与团队总结——特色功能”部分
四、产品发布与用户情况
略,见“项目与团队总结——三、用户日活”部分
五、软件工程质量
团队 PR 情况


团队 CI/CD 触发情况


代码编写情况
前端


后端


其余略,见“项目与团队总结——五、软件工程质量”部分
项目与团队总结
一、项目管理
1. 团队成员简介与个人博客地址
以成员姓名首字母的字典序进行排列

我是练习时长两年半的北航练习生,喜欢吃、睡、code和群星。
在今后的节目中,我和我的朋友们还准备了很多我们自己做调查、做分析、编码的原创作品,期待的话,请多多为我们投票吧
在这次团队中主要担任后端开发的工作

我是胡诗杰
在本次团队中担任前端开发,架构设计的工作

“全世界的水都会相逢,北冰洋与尼罗河也会在湿云中相逢”
想做有意义的东西,讨厌当麻木的编程机器,更讨厌成为被工具化的码农
编码能力有待提升,等着各位大佬带着飞
在本次团队中主要担任后端开发、Project Manager的工作

希望跟大家能拥有一段有趣而独特的项目经历~
在本次团队中主要担任后端开发的工作

我是闵若涵
在本次团队中担任前端开发的工作

--<-<<+[+[<+>--->->->-<<<]>]<<--.<++++++.<<-..<<.<+.>>.>>.<<<.+++.>>.>>-.<<<+.
在团队中主要担任前端开发的工作

“to console the unconsoled”
希望和大家创造一个好的产品
在本次团队中主要担任前端开发的工作
2. 项目管理方式
金山文档——Oak 开发工作日志:用于任务分工展示、日常会议纪要、会议主题与后续安排确定、任务板等需要协作编辑的功能

微信群:用于即时沟通,承载 Oak 团队的基本交流需求
腾讯会议:每周四晚 19:00 召开周例会,如果线上则采用腾讯会议的形式
线下:面对面沟通,重要事项讨论时采用
Github:拟采用前后端分仓库的形式进行代码管理,选用 Github 的原因如下。开源社区、用户基数庞大,GitHub拥有1亿以上的开发人员,400万以上组织机构和3.3亿以上资料库;强大的工具和功能,使用GitHub Actions可以自动化项目流程,并且模版众多;优秀的UI设计,充满着未来的科技赛博感和代码的强逻辑性与强创造力,同时坐拥了良好的用户反馈与动画
Github Actions:持续开发与持续部署
领歌:进行团队燃尽图的绘制
3. 团队分工协作的方式
由PM按照功能模块,大致划分任务,然后经过会议的方式进行统一的表决,尽量保证任务分配均衡,在此基础上多劳多得。Alpha 阶段的任务分配主要采用成员认领,先到先得的方式进行分配。
3.1Beta 开发计划概览
总的开发更新点:
(1)用户提示(基于Element UI的导航组件,浮窗的形式显示,拖拽之类的实现存疑,非Element UI组件存疑,js捕获或许可以;悬浮在页面上的提示窗;做一个引导教程类型的东西)1人 wjl
(2)风格化(地图、路径、锚点) 1人 hsj
(3)导出功能(格式:图片、文本、html)2人 lnb mrh
(4)日记账单在地图上的显示、一个锚点多日记 1人 yyy
(5)添加锚点的形式 数字、emoji(搜索、中心打点)、锚点的有序化 1人 hrl
3.2 任务划分
| 任务名称 | 预计时长(小时) |
|---|---|
| 地图风格化方案敲定 | 8 |
| 地图图层的风格化 | 8 |
| 路径风格化 | 4 |
| 自定义样式的地图风格创建 | 8 |
| 用户提示方案敲定 | 6 |
| 用户提示浮窗的实现 | 8 |
| 引导教程 | 6 |
| 导出方案的敲定 | 4 |
| 导出模式的确定(图片、文本、html) | 4 |
| 图片导出的制作 | 8 |
| HTML导出功能的制作 | 8 |
| 文本导出功能的制作 | 8 |
| 日记在地图上的显示 | 4 |
| 账单在地图上的显示 | 4 |
| 导出日记与账单的适配 | 6 |
| 一个锚点多日记的实现 | 8 |
| 前后端交互API | 6 |
| Sauce Labs进行不同浏览器的适配测试 | 8 |
| 基于DRF进行单元测试 | 8 |
| 项目展示与总结 | 6 |
| 会议纪要与文档 | 6 |
| 导出方案的敲定 | 4 |
| 导出模式的确定(图片、文本、html) | 4 |
| 图片导出的制作 | 8 |
| HTML导出功能的制作 | 8 |
| 文本导出功能的制作 | 8 |
| 前端锚点的多样式功能(数字、emoji) | 8 |
| 后端锚点多样式的适配 | 4 |
| 锚点的有序化 | 4 |
| 中间添加锚点的功能实现 | 4 |
| 搜索添加锚点的功能 | 4 |
3.3 团队分工



4. 团队成员沟通和对接的方式
首先,使用金山文档——Oak 开发工作日志:进行任务分工展示、日常会议纪要、会议主题与后续安排确定、任务板等需要协作编辑的事务,工作日志字数已超过19000字

同时,使用微信群用于即时沟通,承载 Oak 团队的基本交流需求
并且使用腾讯会议于每周四晚 19:00 召开周例会,如果线上则采用腾讯会议的形式。同时Alpha 阶段每两天召开一次 Scrum-meeting,统一会议时间设置在晚上 22:00,具体会议内容可以参考每次的会议纪要。
必要的时候,采取线下面对面沟通,重要事项讨论时采用。在第四周测试与集成的过程中,全天全队线下进行网站的开发与测试,保证开发的敏捷性。
5. 团队如何平衡 时间/质量/资源 争取如期完成任务的
- 均衡的分配任务,每个任务有专门的成员负责
- 面对面敏捷性开发
- 保证覆盖软件的基本功能集,同时注重项目的精美化与进阶功能分配
- 尽量避免使用具有争议性的资源,如OpenMapStreet
- 尽量避免使用成本过高的资源
6. 项目实际进展
燃尽图

在项目管理中,由于课程组提供的基于Github Issue与Milestone绘制的工具不能使用,本次开发Oak主要采用 Leangoo领歌 来进行燃尽图的绘制,根据任务分配设置任务看板,并在每次 scrum-meeting 前针对此两日的工作进行已完成标注。如上图所示,该燃尽图表明团队整体开发基本符合规范,在开发阶段有合理的时间分配,不存在畸形的开发进度。
由于并未结合Issue,所以事实上该燃尽图是一定程度上美化了团队的实际开发进展的,主要可以从以下几个方面展开描述:
- 并不是每日进行已完成任务的勾选,有些任务完成但纳入领歌的时间不及时,所以可以在燃尽图中看到有些开发日没有开发记录存在
- 燃尽图的截止日期是6.3,但实际上此后一周的时间里在API对接、网站部署与测试等方面还花费了巨大的时间未被记录
7. 成员在Beta阶段的角色和具体贡献
| 名字 | 角色 | 团队基础分+转赠分+工作量互评分 | 具体的, 可衡量的, 可验证的贡献 |
|---|---|---|---|
| 柯棋严 | PM | 38+6+(2+2+1+4+1+2)=56 | 单元测试,浏览器匹配性测试,网站部署运维和CICD,后端PR审核,项目展示,团队博客(T13-14,T17-18共4篇博客撰写,余下两篇博客整合),7篇会议纪要,1次用户推广,1次用户调查 |
| 王继来 | 前端开发 | 38+4+(2+1+2+2)=49 | 用户手册,功能规格书部分内容 实现了用户引导,彩蛋页面,重构部分功能 |
| 尹悠阳 | 前端开发 | 38+2+(1+2)=43 | 用户手册,功能规格书,实现了单锚点多日记,地图上地日记帐单显示,日记帐单查看和编辑分离 |
| 胡诗杰 | 前端开发 | 38+2+(1+1+1+3)=46 | 测试报告,功能规格书,基础框架搭建,登录注册密码鉴权,主页面,导航栏,开发者名单,意见反馈模块 |
| 闵若涵 | 前端开发 | 38+4+(1+4+2)=49 | 共同实现了导出功能,包括导出地图图片,导出地图与日记为PDF,压缩导出PDF,html2canvas与地图日记的适应 用户手册、运行环境 |
| 黄融亮 | 后端开发 前端开发 | 38+6+(2+2+4+2+4+1)=59 | 团队博客(T14后端出口,前端marker出口) 1次用户调查(9个ux建议,完成5个) 后端API迭代与文档 数据库迁移工作 前端导出库的补丁 前端基础的日记弹窗基础适配 前端marker label拓展,提色器拓展,二级toolbar拓展 5次bug修改 ...... |
| 李南冰 | 后端开发 | 38+4+(1+4+1)=48 | 测试报告,技术规格书 共同实现了导出功能,包括导出地图图片,导出地图与日记为PDF,压缩导出PDF,html2canvas与地图日记的适应 完成了对新增多个接口的压力测试 |
注:团队贡献分严格按照以下公式计算得到,过程完全透明,保证多劳多得的原则,详细分配规则参考[Oak 08] 团队项目:团队贡献分分配规则
$$score = Base + Bonus_d + Bonus_w$$


Alpha与Beta阶段贡献分平均得分
| 名字 | Alpha得分 | Beta得分 | 总分(平均后微调) |
|---|---|---|---|
| 柯棋严 | 60 | 56 | 57 |
| 王继来 | 53 | 49 | 51 |
| 尹悠阳 | 45 | 43 | 45 |
| 胡诗杰 | 45 | 46 | 46 |
| 闵若涵 | 46 | 49 | 48 |
| 黄融亮 | 52 | 59 | 55 |
| 李南冰 | 49 | 48 | 48 |
二、用户场景
1. 项目开发前的目标,预期的典型用户场景,预期的功能描述
Slogan:把地图当作画布,记录美好生活
定位:以地图为基础的生活记录和分享平台
在快速发展的数字时代,人们的生活方式和记录习惯也在发生变化。传统的地图应用主要关注于导航和位置搜索,而忽视了个人生活记录与分享的需求。"吟游Map"旨在填补这一市场空缺,提供一个结合地图与生活记录的平台,不仅帮助用户规划行程,记录旅行中的点点滴滴,还能在日常生活中捕捉和分享那些与地点相关的特殊时刻。这种结合地图的生活记录方式,为用户带来全新的体验和记忆存储方式,增加了地理信息的情感价值。
典型用户场景
旅游博主
| 姓名 | Elena |
|---|---|
| 年龄 | 30 |
| 职业 | 社交平台博主 |
| 需求 | 记录自己的旅行,在小红书、B站、抖音上分享旅行日常、攻略 |
| 占比 | 15% |
| 旅行爱好者 |
| 姓名 | Billy |
|---|---|
| 年龄 | 23 |
| 职业 | 学生 |
| 需求 | 出行前制定旅行计划,出行过程中记录日常,在旅行后可以在微信朋友圈分享自己的旅行记录 |
| 占比 | 80% |
| 文旅相关工作人员 |
| 姓名 | Iris |
|---|---|
| 年龄 | 35 |
| 职业 | 文旅宣传工作负责人 |
| 需求 | 记录所在地区的旅行热点、在社交媒体及官方宣传渠道分享宣传,吸引游客前来旅行 |
| 占比 | 5% |
典型应用场景
计划
Billy 决定在即将到来的五一假期和四个同学前往天津旅行。出行前他查找了大量的攻略,但这些攻略分布在不同的平台,将这些攻略分享给一起出行的朋友以及在实际旅行时打开每一个查看都十分繁杂,于是他决定用”吟游Map“结合已搜索到的攻略制定自己的旅行计划并在出行前发送给每一个同学。
他首先创建新的地图画布,选定自己要前往的地区。
接着,他根据搜集来的攻略在地图上标记要旅行的景点,并选定参观景点的顺序,再通过路线规划功能自动生成推荐的旅行路线。
然后,他选择标定的景点,并添加备注文本。他可以将参观该景点的建议、景点开放时间、门票价格、景点亮点等信息均填入到备注文本中,便于在实地参观使用。
最后他使用导出地图画布,将制定好的攻略以 pdf 的形式发送给同行的同学们。
记录
Elena 决定前往欧洲旅行,以往她会在小红书等社交媒体平台同步更新一期自己的旅行 vlog,但上次视频下,有粉丝留言希望她能同时分享文图结合的旅行日志,于是在这次她决定使用”吟游Map“来完成粉丝的这一心愿。
第一站,她来到了巴黎,她在巴黎游览了埃菲尔铁塔,探索了世界上最大的艺术博物馆卢浮宫,在香榭丽舍大街上步行到凯旋门……下一站她决定前往米兰,在机场,她决定写一份巴黎的旅行日志。
她首先新建地图画布,将地区选在了巴黎,然后标记自己到访过的景点,并在选定景点插入了自己旅行时拍的照片,同时加入了文字介绍,接着她根据自己的旅行顺序,绘制了旅行线路图,并最后记录了自己在巴黎的账单。
完成地图画布的创作后,她根据自己的喜欢,风格化了地图,并将其以图文的方式分享在了小红书。
分享
Iris 是 D 市文化和旅游局的工作人员,她承担着 D 市传文旅的工作。以往她发布的宣传信息中,文字与图片分离,地图上的路线信息和景点的介绍彼此孤立,因此导致宣传效果大大折扣。她在了解到”吟游Map
“可以把地图当成画布,在地图上记录后,决定在最新的宣传中使用”吟游Map“来编辑文案,完成自己的宣传工作。
她首先新建地图画布,选择 D 市的范围,然后标记了特色、热门的景点,并在插入景区照片,加入了景点信息介绍,同时还描绘了推荐路线图。
她在地图画布的编辑完成后,选择要分享的平台,根据不同平台导出对应形式的文案,并分享在不同的平台上。
在使用了”吟游Map“后,这一次的浏览量大幅提升,高质量地完成了宣传工作,在过去了清明假期中,吸引了大量游客,为 D 市带来了大量的旅游收入。
预期功能描述
本团队此处采用 Axure 进行原型设计,下文主要展示了产品的十个构成页面,从而涵盖用户功能模块、地图功能模块、日记功能模块的基本功能。此处仅用作示意功能,后续开发可能会动态调整以适应用户需求。
注册与登录页面

集成了登录与注册功能的页面。
开始页

- A:侧边导航栏,可以导航到最近创建的地图作品或是收藏的地图作品
- B:用户信息栏,收起状态下显示为用户头像,可以用来放修改信息、登出等操作
- C:搜索框,可以按照文件名搜索创建的地图作品
- D:新建地图
- E:地图作品预览,用于展示地图作品的基本信息以及相关操作
- F:删除该地图作品
- G:更多,可以集成导出分享等功能
地图页——日记预览

- A:文件名,点击可修改
- B:位置搜索框,用于快速定位到某个位置
- C:操作栏,用于查看当前作品的大纲以及导出分享该作品
- D:日记预览,用于浏览编写好的日记
- E:marker,左键单击可以查看详情(日记/记账/其他),拖拽可移动,右键可编辑
- F:功能栏,从左到右依次是放置marker,更改样式,规划路径
- G:缩放,用于缩放地图
- H:定位,用于定位到当前所在位置
- J:返回,用于返回开始页
地图页——marker 编辑

- A:marker编辑,可以更改marker的类型以及删除该marker
地图页——日记编辑

- A:富文本编辑器,用于编辑日记
地图页——记账

- A:记账
地图页——样式与风格化

- A:样式,可以选择不同风格的地图画布
地图页——路径规划

- A:路径规划,为框选起来的Marker规划路径,规划方式可自定义
地图页——分享

- A:导出模式,用于选择导出的图像所包含的内容以及样式
- B:预览,用于预览所选模式下的导出效果
地图页——大纲

- A:大纲,用于汇总当前地图作品包含的所有内容
2. 项目发布的功能和发布地址
2.1 路径规划、锚点样式与用户引导(还有一个小彩蛋)
小明是一位喜欢旅游的大学生,但他一直苦于没有合适的地方来记录每次经历。某次朋友给他推荐了「吟游Map」app,听说这款软件可以帮他在地图上记录生活,于是小明准备试试这款软件。
路径规划
首先小明通过搜索功能找到目标景点:

随后,小明点击目标景点以跳转到对应位置,手动缩放到合适大小:

点击地址搜索栏的 x 号关闭地址搜索组件。
随后,小明在地图上标注出值得一去的景。选择锚点工具,然后标注:

上图标注了来北航学院路校区必去的几个地方:大运村、新主楼、航空航天博物馆、晨兴音乐厅、绿园。
不过先去哪里?后去哪里?最佳的游玩路线是什么?
切换到路径规划工具,在右上方选择步行作为出行方式,然后按照期望的游玩顺序点击锚点,首先点击大运村和绿园的锚点:

然后点击绿园和航空航天博物馆的锚点:

重复这个操作,把剩下的锚点也加到路径里:

不小心点错了?没关系,右键锚点可以删除附着到该锚点上的路径。删除新主楼锚点上附着的路径:

锚点标记的位置不太理想?没关系,回到锚点工具下,拖动锚点,路径将会自动重新规划。移动航空航天博物馆以及晨兴音乐厅的锚点:

完善一下,一份北航学院路校区的旅游攻略就做好了:

用户也可以边旅行边记录走过的路径,比如在沈阳玩上几天,可能会得到下面这张地图:

用户引导
本次发布新增了用户引导的功能。点击地图页面右下角的 ? 按钮即可进入当前工具视图下的用户引导。
例如,目前没有选中任何工具,点击 ? 按钮则会依次介绍页面上所有元素的功能:

如果正在使用锚点工具,点击 ? 按钮则会介绍锚点工具的使用方法:

除此之外,本次发布也对危险行为添加了二次确认,例如删除地图时,会弹出二次确认的对话框:

彩蛋
在 raccoon 路由下,可以进入Oak开发团队的菜单页面,该桌面宠物本来想作为用户引导的一部分,大大提升该产品的趣味性。可以做到最后,本团队遗憾地发现该组件的背景色在Web端无法调整至透明。
Anyway,这也是一次伟大的尝试!!!最后我们将它作为一个彩蛋页面,或许你可以试试多点几次,点到100次说不定还有惊喜~

2.2 地图风格化与日记模块
新功能特性
- 地图相关
- 地图风格化
- 日记相关
- 以富文本形式书写日记
- 账单相关
- 以表格形式记录账单
- 账单相关信息自动统计
应用场景(地图)
实现需求
- 风格化的地图
小A在创建地图画布后,可以根据自己的喜好选择对应的地图风格,如标准、幻影黑、月光银、远山黛等等。


应用场景(日记)
实现需求
- 富文本形式的日记
小A在白天参观中国国家博物馆后,想把自己的见闻以日记的形式记录下来。
首先小A在地图上用"marker"标记出中国国家博物馆。

然后小A点击对应锚点,点击查看日记。

接着小A新增日记,输入标题和摘要,便点击查看。


此时的日记内容为空,小A点击编辑日记,用文字结合自己白天拍摄的图片,编辑了图文并茂的日记。

应用场景(账单)
小A想记录一下在北京旅游第一天的花费,TA同样先在地图上标记今天游览过的地方。

点击对应的"marker",创建并查看账单。

再点击编辑账单,进入编辑页面。

小A根据今天的花费,在账单中添加记录。

下方自动为小A统计出了每类花费的占比等数据。

2.3 用户系统与地图导出(PDF or JPG)
小明是一位喜欢旅游的大学生,但他一直苦于没有合适的地方来记录每次经历。某次朋友给他推荐了「吟游Map」app,听说这款软件可以帮他在地图上记录生活,于是小明准备试试这款软件。
注册页面
小明首先注册了一个账号。

该页面引导新用户进行注册。新用户在输入用户名、密码并再次输入相同的密码后点击注册按钮,待系统检测通过后即可用新账号进行登录。
登录页面
小明随即用刚刚注册的账号进行登录。

该页面引导用户进行登录。用户输入用户名以及密码后点击登录按钮,待系统验证通过后即可成功登录。
修改密码
小明觉得之前用的密码不够安全,所以决定修改密码。

该页面允许用户修改密码。用户在登录状态下输入原密码并输入两次新密码后点击修改按钮,等待系统验证通过后修改密码成功。
开始页面
小明登录成功后来到了开始页面,瞬间小明就被宣传语深深吸引住了,于是他迫不及待点击了开始按钮。

个人主页
小明的每一次旅行对他而言都是一次非常珍贵的回忆,他在主页创建了一张又一张地图来记录每一次旅行的点点滴滴。

用户可以在主页创建新地图,主页上会显示用户最近更新的地图以及其他地图的名字,标记数目,日记数目等信息。

用户可以收藏地图。点击地图右边的五角星即可收藏/取消收藏,收藏的地图会在收藏页展示。

用户可以搜索创建的地图。点击搜索框后在框中输入关键词即可查看相关的地图。
导出功能
小明想要把自己精心制作的地图保存下来或分享给其他人看看,于是他尝试了导出功能。

用户可以点击右上角的导出图片/pdf按钮将地图导出为图片/pdf。
发布地址
「吟游Map」是一款 Web 端应用,通过浏览器访问 吟游Map即可
3. 项目发布后是否满足了全部典型场景
Beta阶段完成后,典型场景基本满足
Beta阶段新增的功能:
- 地图风格样式选择
- 锚点样式更改与附属文字添加
- 用户引导
- 单锚点多日记绑定
- 地图导出(PDF/JPG)
- 某功能中道崩殂而诞生的彩蛋
4. 项目发布后真正符合用户需求了吗
基本符合用户需求,以下是一位目标用户使用产品的过程和评价。
背景:该目标用户使用 吟游Map 进行了北航校园行程规划和北京旅游攻略制作。
过程与评价:这个新版本的软件已经能够满足我的需求,比如说用来进行攻略的制作,旅游或者日常出行的路径规划,还有记账日记一些用来记录生活的功能。新版本里内置的风格化与导出功能我觉得很好地弥补了上一个版本的缺失,也从某种意义上符合了我试用这个软件的初衷,能够较为美观且清晰地展示我的规划。但是美中不足的是,导出的图片我觉得不太有特点,还是局限在了一个地图载体之上。
三、用户日活
1. 项目发布时团队做了哪些努力来推广项目
在 吟游Map 用户群进行内部交流,发布版本更新;同时建立了用户反馈问卷,并在用户群和网站上发布

2. 软件的日活跃用户量是否达到了预先定义的数量
目标
- 发布后一周的总用户量(精确到百):200人次
- 每日活跃用户量(活跃包括查看历史记录与创作新地图):20人次
实际结果
基本满足预先定义的数量
用户数目:155个用户

实际地图数目:1000+部地图(其余地图用于压力测试)

实际路径数目:5000+条路径(其余路径用于压力测试)

实际标记点数目:2000+个标记点(其余标记点用于压力测试)

3. 用户在功能改进上有所反馈
有用户进行功能上的反馈,但是数量较为有限

4. 用户在Bug上有所反馈
用户反馈的 bug 主要有以下几个:
问题:锚点文字标识功能存在问题
在部分浏览器上,在编辑锚点中央的文字标识时可能出不能顺利输入文字的问题。为了输入文字,用户可以选择将剪切板中的内容复制到文字标识输入框内,可缓解该问题的影响。本功能的初衷是让用户输入数字,输入数字以及英文字母均不会触发该问题。
限制:缩略图显示的区域缺乏实用性
当前的缩略图显示区域为地图上所有锚点的几何中心所在位置。如果锚点分布分散,则会导致该区域没有实际内容。不过,进入地图后会自动移动到上一次离开时的位置,实际使用过程中不会有明显感知。
限制:移动端操作不够便捷
当前版本在开发时优先适配桌面端浏览器环境,部分操作如右键删除锚点在移动端只能通过长按代替,操作不够便捷。
限制:部分功能的使用次数有限
由于高德部分 API 的调用有次数限制,因此次数用完之后部分功能将无法使用。其中受到影响最大的是位置搜索功能,该功能的使用上限为 100 次,因此很快就会用完。不过,本应用的核心功能(锚点标注、日记、记账)均不会依赖这些 API ,因此不会影响用户的核心体验。
限制:缺少快捷键
当前版本不支持快捷键,强依赖于鼠标点击与拖拽,部分场景如登录、添加锚点、移动地图、删除锚点等可能更加适合用键盘操作。不过当前版本尽可能优化了鼠标操作的便捷程度,使之符合直觉。
四、特色功能
Beta阶段完成了该软件所有功能的研发,Oak团队认为,本产品具有四大杀手功能外加一个菜单,这三个功能分别是地图搜索与收藏、地图样式自主风格化、日记记账与地图导出,
1. 项目的杀手级功能,与竞品相比最特色的功能展现。
地图搜索与收藏
地图搜索

地图收藏

地图样式自主风格化

马卡龙地图样式

酱籽地图样式

锚点样式自定义

地图导出功能
支持PDF导出与JPG导出,分别对应攻略分享和朋友圈分享两个场景

导出JPG示例

导出PDF示例

日记与记账


彩蛋

2. 思考一下竞品出于什么原因并没有囊括该特色功能,团队凭借什么样的优势实现了它?
风格化与导出
竞品Exping事实上在地图风格化方面做得较好,预设了多种样式的地图,但是绝大多数都是收费体验,且Exping完全不支持地图导出功能。我们认为主要原因在于,地图渲染的成本高,作为一个企业组织需要有效平衡产出比;其次,其实很难找到一个平衡美观和地图导出的方案,日记的文字数目,锚点数目都是一些未知且动态范围大的变量。
团队的优势是,以实现该功能为出发点,舍弃掉了一些美工上的特性,尝试以html2canvas为基础进行功能的实现(例如该组件不支持SVG,故放弃了锚点多样式的功能)
日记与记账
竞品 Exping 的简介模块采用了简介和照片分离的模式,且简介部分仅支持纯文本的编辑,同时,Exping 中并未加入记账这样一个功能。

本团队认为,竞品 Exping 没有加入记账功能是因为其忽视了记账功能在典型场景中的重要性。吟游Map和Exping的典型应用场景都是旅游攻略或者日常记录生活,在这样的场景中,加入记账模块能够有效地辅助用户完成旅游计划或日常开销的记录。另一方面,Exping 可能受限于开发规模,认为记账功能可以作为简介模块的一部分。而Exping之所以没有采用富文本编辑器,是因为单独从头开发实现一个富文本编辑器的成本过高,而Exping的核心功能并不在简介模块,并且Exping没有加入以图片形式导出地图的功能,或许其开发团队认为图片与纯文本分开展示的形式已经足够支撑这个功能。
团队的优势是结合 Froala 编辑器实现了富文本编辑日记的功能,从而实现图片与文字夹杂同时单独开辟日记模块,生成日常开销的相关统计数据。
3. 团队成员对这些功能的自我评价如何,是否达到了预期目标,为什么?
日记和记账模块基本达到预期,其能够有效地辅助用户完成旅游计划或日常开销的记录,并且允许用户以富文本的形式撰写日记记录生活,允许文字与图片相结合的方式进行呈现。
地图样式自主风格化与地图导出功能基本达到预期,但是其实为了导出,在美工方面还是妥协了不少。但是作为一个工具,还是能够支持用户利用这个工具制作出比较精美的地图记录。
4. 用户对这些功能的评价如何,是否认为这些功能富有特色,为什么?

用户对这些功能的评价为基本满意,其特别赞赏“吟游Map”在旅行和日常生活记录方面的实用性。应用的地图集成和标记功能使得记录具体地点的经历变得非常直观和方便。此外,用户还提到,该应用的界面友好且易于导航,新用户可以迅速上手并开始记录他们的旅程。另外,记账功能也被认为是一个亮点,它不仅帮助用户跟踪旅行开支,还使得日常财务管理更加有序。用户们认为这个功能帮助他们更好地理解和控制旅行期间的个人财务。
地图样式的个性化定制和地图数据的导出功能同样也是一大亮点。用户对能够改变地图的视觉样式表示出较大兴趣,表示能基于此实现一个美观简洁的地图记录。此外,能够导出地图为不同格式,如PDF或图片,极大地增加其实用性,用户能够更方便地分享和打印他们的旅行和生活记录。
五、软件工程质量
1. 项目文档与约定代码规范
项目文档
首先,项目有完善的前后端对接文档,详情可见工作日志中前后端对接文档与后端仓库中的 api.md 文档,以下使用部分对接文档作为展示



约定代码规范
其次,Oak团队约定了代码规范
API 规约

统一格式
- 字段名: 均用匈牙利命名(下划线小写)
- id: 不直接写id, 以
<对象>_id进行区分 - msg:
成功信息为
"<行为><对象>成功"
失败信息为
"没有对应的<对象>" --> <对象>id不存在
"用户权限错误" --> 操作别人的地图、marker、日记、账目
"请求缺少数据" --> 请求缺少必要数据
"数据库错误" --> 数据库sql执行错误
"未知错误"
.......
后端代码规约
参考 Django 代码规范:https://blog.csdn.net/zhouruifu2015/article/details/129647979?ops_request_misc=&request_id=&biz_id=102&utm_term=Django%E4%BB%A3%E7%A0%81%E7%BC%96%E5%86%99%E8%A7%84%E8%8C%83&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-129647979.142^v100^pc_search_result_base7&spm=1018.2226.3001.4187
环境规范:Python 3.10.6;Django4.2.3;mysql Ver 8.0.11 for Win64
前端代码规范
参考 Vue 前端代码开发规范:
https://blog.csdn.net/sjsja/article/details/123480376
环境规范:Vue3; Node Its 20.12.2
2. 项目是否有出现代码混乱,没有注释,没有详细文档的问题?明年的同学继续开发这个项目,会不会出现以上抱怨?如果一个新学生在一台新机器上想编译并运行你的项目, 请问能顺利完成么?有什么样的文档能指导新学生?
项目基本没有出现代码混乱,没有注释,没有详细文档的问题。我们认为明年的同学继续开发这个项目,不会出现以上抱怨
如果一个新学生在一台新机器上想编译并运行你的项目, 我们认为能顺利完成
首先关于环境配置,我们提供了相关的配置文件 requirements.txt

同时提供了相关的部署文件,例如nginx.conf, dev.yml, Dockerfile, uwsgi.ini

此外,前后端都有关于配置项目与启动项目的简单说明 README.md



3. 测试,测试用例数目,代码覆盖率
团队基于Jmeter对主要API进行压力测试,基于DRF架构针对路由进行了覆盖率为76%的单元测试,基于SauceLabs对软件进行了浏览器适配的测试。
3.1 单元测试
Oak 团队以 DRF 框架为基础进行针对后端 API 的单元测试,总文件测试类覆盖率达到90%,方法覆盖率达到76%
类覆盖率
| File | class | statements | missing | excluded | coverage |
|---|---|---|---|---|---|
| path\views.py | (no class) | 16 | 0 | 0 | 100% |
| path\urls.py | (no class) | 3 | 0 | 0 | 100% |
| path\tests.py | PathAPITests | 31 | 0 | 0 | 100% |
| path\tests.py | (no class) | 21 | 1 | 0 | 95% |
| path\models.py | Path | 1 | 0 | 0 | 100% |
| path\models.py | Path.Meta | 0 | 0 | 0 | 100% |
| path\models.py | (no class) | 12 | 0 | 0 | 100% |
| path\apps.py | PathConfig | 0 | 0 | 0 | 100% |
| path\apps.py | (no class) | 4 | 0 | 0 | 100% |
| path\admin.py | (no class) | 3 | 0 | 0 | 100% |
| myapp\views\index\user.py | LoginView | 22 | 4 | 0 | 82% |
| myapp\views\index\user.py | ChangePasswordView | 21 | 21 | 0 | 0% |
| myapp\views\index\user.py | RegisterView | 22 | 4 | 0 | 82% |
| myapp\views\index\user.py | (no class) | 26 | 0 | 0 | 100% |
| myapp\views\index\secure.py | EncryptedService | 4 | 0 | 0 | 100% |
| myapp\views\index\secure.py | (no class) | 16 | 0 | 0 | 100% |
| myapp\urls.py | (no class) | 3 | 0 | 0 | 100% |
| myapp\tests.py | EncryptionTests | 39 | 0 | 0 | 100% |
| myapp\tests.py | (no class) | 14 | 0 | 0 | 100% |
| myapp\models\user.py | MapUser | 0 | 0 | 0 | 100% |
| myapp\models\user.py | MapUser.Meta | 0 | 0 | 0 | 100% |
| myapp\models\user.py | (no class) | 16 | 0 | 0 | 100% |
| myapp\apps.py | MyappConfig | 0 | 0 | 0 | 100% |
| myapp\apps.py | (no class) | 4 | 0 | 0 | 100% |
| myapp\admin.py | (no class) | 3 | 0 | 0 | 100% |
| marker\views.py | (no class) | 17 | 0 | 0 | 100% |
| marker\urls.py | (no class) | 3 | 0 | 0 | 100% |
| marker\tests.py | MarkerAPITests | 33 | 0 | 0 | 100% |
| marker\tests.py | (no class) | 20 | 1 | 0 | 95% |
| marker\models.py | Marker | 23 | 10 | 0 | 57% |
| marker\models.py | Marker.Meta | 0 | 0 | 0 | 100% |
| marker\models.py | (no class) | 23 | 0 | 0 | 100% |
| marker\apps.py | MarkerConfig | 0 | 0 | 0 | 100% |
| marker\apps.py | (no class) | 4 | 0 | 0 | 100% |
| marker\admin.py | (no class) | 3 | 0 | 0 | 100% |
| manage.py | (no class) | 5 | 0 | 0 | 100% |
| graph\views.py | (no class) | 18 | 0 | 0 | 100% |
| graph\urls.py | (no class) | 3 | 0 | 0 | 100% |
| graph\tests.py | GraphAPITests | 36 | 0 | 0 | 100% |
| graph\tests.py | (no class) | 19 | 1 | 0 | 95% |
| graph\signals.py | (no class) | 5 | 0 | 0 | 100% |
| graph\models.py | Graph | 1 | 0 | 0 | 100% |
| graph\models.py | Graph.Meta | 0 | 0 | 0 | 100% |
| graph\models.py | (no class) | 22 | 0 | 0 | 100% |
| graph\apps.py | GraphConfig | 21 | 19 | 0 | 10% |
| graph\apps.py | (no class) | 6 | 0 | 0 | 100% |
| graph\admin.py | (no class) | 3 | 0 | 0 | 100% |
| diary\views.py | (no class) | 26 | 0 | 0 | 100% |
| diary\urls.py | (no class) | 3 | 0 | 0 | 100% |
| diary\tests.py | DiaryAPITests | 49 | 0 | 0 | 100% |
| diary\tests.py | (no class) | 24 | 1 | 0 | 96% |
| diary\signals.py | (no class) | 9 | 0 | 0 | 100% |
| diary\models.py | Diary | 18 | 0 | 0 | 100% |
| diary\models.py | Diary.Meta | 0 | 0 | 0 | 100% |
| diary\models.py | DiaryImg | 7 | 3 | 0 | 57% |
| diary\models.py | DiaryImg.Meta | 0 | 0 | 0 | 100% |
| diary\models.py | (no class) | 33 | 0 | 0 | 100% |
| diary\apps.py | DiaryConfig | 30 | 28 | 0 | 7% |
| diary\apps.py | (no class) | 6 | 0 | 0 | 100% |
| diary\admin.py | (no class) | 4 | 0 | 0 | 100% |
| backEnd\urls.py | (no class) | 6 | 0 | 0 | 100% |
| backEnd\tools.py | (no class) | 3 | 0 | 0 | 100% |
| backEnd\settings.py | (no class) | 29 | 0 | 0 | 100% |
| account\views.py | (no class) | 18 | 0 | 0 | 100% |
| account\urls.py | (no class) | 3 | 0 | 0 | 100% |
| account\tests.py | AccountAPITests | 36 | 0 | 0 | 100% |
| account\tests.py | (no class) | 22 | 1 | 0 | 95% |
| account\models.py | Account | 1 | 0 | 0 | 100% |
| account\models.py | Account.Meta | 0 | 0 | 0 | 100% |
| account\models.py | (no class) | 15 | 0 | 0 | 100% |
| account\migrations\0002_initial.py | Migration | 0 | 0 | 0 | 100% |
| account\migrations\0002_initial.py | (no class) | 6 | 0 | 0 | 100% |
| account\migrations\0001_initial.py | Migration | 0 | 0 | 0 | 100% |
| account\migrations\0001_initial.py | (no class) | 6 | 0 | 0 | 100% |
| account\apps.py | AccountConfig | 0 | 0 | 0 | 100% |
| account\apps.py | (no class) | 4 | 0 | 0 | 100% |
| account\admin.py | (no class) | 3 | 0 | 0 | 100% |
| Total | 954 | 94 | 0 | 90% |
方法覆盖率
| File | function | statements | missing | excluded | coverage |
|---|---|---|---|---|---|
| path\views.py | get | 19 | 11 | 0 | 42% |
| path\views.py | add | 29 | 14 | 0 | 52% |
| path\views.py | delete | 20 | 11 | 0 | 45% |
| path\views.py | (no function) | 16 | 0 | 0 | 100% |
| path\urls.py | (no function) | 3 | 0 | 0 | 100% |
| path\tests.py | PathAPITests.setUp | 8 | 0 | 0 | 100% |
| path\tests.py | PathAPITests.get_token | 1 | 0 | 0 | 100% |
| path\tests.py | PathAPITests.test_get_paths | 5 | 0 | 0 | 100% |
| path\tests.py | PathAPITests.test_add_path | 6 | 0 | 0 | 100% |
| path\tests.py | PathAPITests.test_add_path_invalid | 5 | 0 | 0 | 100% |
| path\tests.py | PathAPITests.test_delete_path | 6 | 0 | 0 | 100% |
| path\tests.py | (no function) | 21 | 1 | 0 | 95% |
| path\models.py | Path.dict_type | 1 | 0 | 0 | 100% |
| path\models.py | (no function) | 12 | 0 | 0 | 100% |
| path\migrations\0001_initial.py | (no function) | 6 | 0 | 0 | 100% |
| path\apps.py | (no function) | 4 | 0 | 0 | 100% |
| path\admin.py | (no function) | 3 | 0 | 0 | 100% |
| myapp\views\index\user.py | LoginView.post | 22 | 4 | 0 | 82% |
| myapp\views\index\user.py | changePassword | 21 | 4 | 0 | 81% |
| myapp\views\index\user.py | ChangePasswordView.post | 21 | 21 | 0 | 0% |
| myapp\views\index\user.py | RegisterView.post | 22 | 4 | 0 | 82% |
| myapp\views\index\user.py | generate_token | 4 | 0 | 0 | 100% |
| myapp\views\index\user.py | mod_power | 8 | 0 | 0 | 100% |
| myapp\views\index\user.py | get_user_by_token | 10 | 3 | 0 | 70% |
| myapp\views\index\user.py | (no function) | 26 | 0 | 0 | 100% |
| myapp\views\index\secure.py | EncryptedService.post | 4 | 0 | 0 | 100% |
| myapp\views\index\secure.py | get_server_private_key | 1 | 0 | 0 | 100% |
| myapp\views\index\secure.py | store_server_private_key | 1 | 0 | 0 | 100% |
| myapp\views\index\secure.py | decrypt_password | 5 | 0 | 0 | 100% |
| myapp\views\index\secure.py | mod_power | 8 | 0 | 0 | 100% |
| myapp\views\index\secure.py | (no function) | 16 | 0 | 0 | 100% |
| myapp\urls.py | (no function) | 3 | 0 | 0 | 100% |
| myapp\tests.py | EncryptionTests.setUp | 1 | 0 | 0 | 100% |
| myapp\tests.py | EncryptionTests.mod_power | 8 | 0 | 0 | 100% |
| myapp\tests.py | EncryptionTests.encrypt_password | 4 | 0 | 0 | 100% |
| myapp\tests.py | EncryptionTests.test_register_and_login | 26 | 0 | 0 | 100% |
| myapp\tests.py | (no function) | 14 | 0 | 0 | 100% |
| myapp\models\user.py | (no function) | 16 | 0 | 0 | 100% |
| myapp\apps.py | (no function) | 4 | 0 | 0 | 100% |
| myapp\admin.py | (no function) | 3 | 0 | 0 | 100% |
| marker\views.py | get | 24 | 14 | 0 | 42% |
| marker\views.py | add | 19 | 10 | 0 | 47% |
| marker\views.py | modify | 30 | 11 | 0 | 63% |
| marker\views.py | delete | 20 | 11 | 0 | 45% |
| marker\views.py | (no function) | 17 | 0 | 0 | 100% |
| marker\urls.py | (no function) | 3 | 0 | 0 | 100% |
| marker\tests.py | MarkerAPITests.setUp | 6 | 0 | 0 | 100% |
| marker\tests.py | MarkerAPITests.get_token | 1 | 0 | 0 | 100% |
| marker\tests.py | MarkerAPITests.test_get_markers | 5 | 0 | 0 | 100% |
| marker\tests.py | MarkerAPITests.test_add_marker | 6 | 0 | 0 | 100% |
| marker\tests.py | MarkerAPITests.test_modify_marker | 9 | 0 | 0 | 100% |
| marker\tests.py | MarkerAPITests.test_delete_marker | 6 | 0 | 0 | 100% |
| marker\tests.py | (no function) | 20 | 1 | 0 | 95% |
| marker\models.py | Marker.dict_type | 1 | 0 | 0 | 100% |
| marker\models.py | Marker.save | 6 | 0 | 0 | 100% |
| marker\models.py | Marker.delete | 6 | 0 | 0 | 100% |
| marker\models.py | Marker.colorEncode | 8 | 8 | 0 | 0% |
| marker\models.py | Marker.colorDecode | 2 | 2 | 0 | 0% |
| marker\models.py | (no function) | 23 | 0 | 0 | 100% |
| marker\migrations\0001_initial.py | (no function) | 6 | 0 | 0 | 100% |
| marker\apps.py | (no function) | 4 | 0 | 0 | 100% |
| marker\admin.py | (no function) | 3 | 0 | 0 | 100% |
| manage.py | main | 6 | 2 | 0 | 67% |
| manage.py | (no function) | 5 | 0 | 0 | 100% |
| graph\views.py | get | 25 | 15 | 0 | 40% |
| graph\views.py | add | 27 | 8 | 0 | 70% |
| graph\views.py | modify | 63 | 28 | 0 | 56% |
| graph\views.py | delete | 18 | 9 | 0 | 50% |
| graph\views.py | (no function) | 18 | 0 | 0 | 100% |
| graph\urls.py | (no function) | 3 | 0 | 0 | 100% |
| graph\tests.py | GraphAPITests.setUp | 5 | 0 | 0 | 100% |
| graph\tests.py | GraphAPITests.get_token | 1 | 0 | 0 | 100% |
| graph\tests.py | GraphAPITests.test_get_graphs | 5 | 0 | 0 | 100% |
| graph\tests.py | GraphAPITests.test_add_graph | 6 | 0 | 0 | 100% |
| graph\tests.py | GraphAPITests.test_modify_graph | 13 | 0 | 0 | 100% |
| graph\tests.py | GraphAPITests.test_delete_graph | 6 | 0 | 0 | 100% |
| graph\tests.py | (no function) | 19 | 1 | 0 | 95% |
| graph\signals.py | post_graph_delete | 1 | 0 | 0 | 100% |
| graph\signals.py | (no function) | 5 | 0 | 0 | 100% |
| graph\models.py | Graph.dict_type | 1 | 0 | 0 | 100% |
| graph\models.py | (no function) | 22 | 0 | 0 | 100% |
| graph\migrations\0001_initial.py | (no function) | 6 | 0 | 0 | 100% |
| graph\apps.py | GraphConfig.ready | 21 | 19 | 0 | 10% |
| graph\apps.py | (no function) | 6 | 0 | 0 | 100% |
| graph\admin.py | (no function) | 3 | 0 | 0 | 100% |
| diary\views.py | get | 23 | 14 | 0 | 39% |
| diary\views.py | add | 24 | 11 | 0 | 54% |
| diary\views.py | modify | 40 | 15 | 0 | 62% |
| diary\views.py | delete | 20 | 11 | 0 | 45% |
| diary\views.py | upload_image | 31 | 16 | 0 | 48% |
| diary\views.py | get_image | 15 | 15 | 0 | 0% |
| diary\views.py | delete_image | 22 | 11 | 0 | 50% |
| diary\views.py | (no function) | 26 | 0 | 0 | 100% |
| diary\urls.py | (no function) | 3 | 0 | 0 | 100% |
| diary\tests.py | DiaryAPITests.setUp | 7 | 0 | 0 | 100% |
| diary\tests.py | DiaryAPITests.get_token | 1 | 0 | 0 | 100% |
| diary\tests.py | DiaryAPITests.test_get_diaries | 5 | 0 | 0 | 100% |
| diary\tests.py | DiaryAPITests.test_add_diary | 6 | 0 | 0 | 100% |
| diary\tests.py | DiaryAPITests.test_modify_diary | 9 | 0 | 0 | 100% |
| diary\tests.py | DiaryAPITests.test_delete_diary | 6 | 0 | 0 | 100% |
| diary\tests.py | DiaryAPITests.test_upload_image | 6 | 0 | 0 | 100% |
| diary\tests.py | DiaryAPITests.test_delete_image | 9 | 0 | 0 | 100% |
| diary\tests.py | (no function) | 24 | 1 | 0 | 96% |
| diary\signals.py | post_diary_delete | 2 | 0 | 0 | 100% |
| diary\signals.py | post_diaryImg_delete | 1 | 0 | 0 | 100% |
| diary\signals.py | (no function) | 9 | 0 | 0 | 100% |
| diary\models.py | Diary.dict_type | 1 | 0 | 0 | 100% |
| diary\models.py | Diary.save | 9 | 0 | 0 | 100% |
| diary\models.py | Diary.delete | 8 | 0 | 0 | 100% |
| diary\models.py | DiaryImg.save | 3 | 0 | 0 | 100% |
| diary\models.py | DiaryImg.img | 3 | 3 | 0 | 0% |
| diary\models.py | DiaryImg.get_new_random_name | 1 | 0 | 0 | 100% |
| diary\models.py | (no function) | 33 | 0 | 0 | 100% |
| diary\migrations\0002_initial.py | (no function) | 6 | 0 | 0 | 100% |
| diary\migrations\0001_initial.py | (no function) | 6 | 0 | 0 | 100% |
| diary\apps.py | DiaryConfig.ready | 30 | 28 | 0 | 7% |
| diary\apps.py | (no function) | 6 | 0 | 0 | 100% |
| diary\admin.py | (no function) | 4 | 0 | 0 | 100% |
| backEnd\urls.py | (no function) | 6 | 0 | 0 | 100% |
| backEnd\tools.py | stringToDate | 6 | 1 | 0 | 83% |
| backEnd\tools.py | (no function) | 3 | 0 | 0 | 100% |
| backEnd\settings.py | (no function) | 29 | 0 | 0 | 100% |
| account\views.py | get | 20 | 11 | 0 | 45% |
| account\views.py | add | 22 | 13 | 0 | 41% |
| account\views.py | modify | 41 | 13 | 0 | 68% |
| account\views.py | delete | 20 | 11 | 0 | 45% |
| account\views.py | (no function) | 18 | 0 | 0 | 100% |
| account\urls.py | (no function) | 3 | 0 | 0 | 100% |
| account\tests.py | AccountAPITests.setUp | 7 | 0 | 0 | 100% |
| account\tests.py | AccountAPITests.get_token | 1 | 0 | 0 | 100% |
| account\tests.py | AccountAPITests.test_get_accounts | 5 | 0 | 0 | 100% |
| account\tests.py | AccountAPITests.test_add_account | 6 | 0 | 0 | 100% |
| account\tests.py | AccountAPITests.test_modify_account | 11 | 0 | 0 | 100% |
| account\tests.py | AccountAPITests.test_delete_account | 6 | 0 | 0 | 100% |
| account\tests.py | (no function) | 22 | 1 | 0 | 95% |
| account\models.py | Account.dict_type | 1 | 0 | 0 | 100% |
| account\models.py | (no function) | 15 | 0 | 0 | 100% |
| account\apps.py | (no function) | 4 | 0 | 0 | 100% |
| account\admin.py | (no function) | 3 | 0 | 0 | 100% |
| Total | 1600 | 387 | 0 | 76% |
3.2 SauceLabs浏览器适配性测试
firefox dev win11


iphone12-ios16.7


Safari17-Mac13


3.3 压力测试
添加地图压力测试 /api/graph/add
- 压力测试配置如下:10s 1000次请求
- 测试结果如下:

- 图如下:

标点添加压力测试 /api/marker/add
- 压力测试配置如下:10s 1000次请求
- 测试结果如下:

- 图如下:

地图修改压力测试 /api/graph/modify
- 压力测试配置如下:10s 1000次请求
- 测试结果如下:

- 图如下:

标点修改压力测试 /api/marker/modify
- 压力测试配置如下:10s 1000次请求
- 测试结果如下:

- 图如下:

路径添加压力测试 /api/path/add
- 压力测试配置如下:10s 2000次请求
- 测试结果如下:

- 图如下:

标点添加压力测试 /api/marker/add
- 压力测试配置如下:10s 1000次请求
- 测试结果如下:

- 图如下:

修改日记压力测试 /api/diary/modify
- 压力测试配置如下:10s 1000次请求
- 测试结果如下:

- 图如下:

添加日记压力测试 /api/diary/add
- 压力测试配置如下:10s 1000次请求
- 测试结果如下:

- 图如下:

添加账单压力测试 /api/account/add
- 压力测试配置如下:10s 1000次请求
- 测试结果如下:

- 图如下:

修改账单压力测试 /api/account/modify
- 压力测试配置如下:10s 1000次请求
- 测试结果如下:

- 图如下:

4. CI/CD
项目前后端仓库均实现了 CI/CD,实现理由:
- 自动化实现前后端项目的部署
- 每次代码修改后,能够迅速地到软件网址处查看修改的结果,不需要人工进行服务器上的代码同步
- 实施自动化测试,以检查代码有效性和程序功能
- 有效缩减发布时间,实现敏捷性开发
前端
前端的CI/CD,实现人员私以为比较优雅(嘿嘿),基本实现工具囊括了Github Actions、Docker、nginx,过程可以分为以下几个阶段:
- 登录至 DockerHub
- 依据 Dockerfile 针对前端文件调用 Nodejs 进行打包与构建
- 依据 Dockerfile 启动 nginx 进行反向代理
- 将构建的镜像容器上传至 Dockerhub
- 登录至腾讯云服务器
- 执行脚本文件,包括拉取镜像容器、停止此前运行的前端容器、删除已停止的容器、运行对应的容器并执行相应的端口映射
后端
后端的CI/CD比较丑陋,但怎么说呢,反正能用(),基本实现工具包括Github Actions、uwsgi,过程可以分为以下几个阶段:
- 运行单元测试进行正确性检验
- 登录至腾讯云服务器
- 执行脚本文件
- git拉取后端仓库dev分支
- tmux启动对应窗口,首先停止此前的uwsgi进程,然后依据uwsgi.ini的配置启动新的后端进程
5. 经验教训
收获与经验教训
在Beta阶段中,团队成员收获良多,无论是从团队开发协作过程中的收获还是测试对接过程中的教训,都可以成为成员此后适应团队开发的宝贵财富。
- 详略得当的文档有助于团队协作:前期开发过程中,部分阶段由于没进行明确的规约导致开发过程中出现了较大的漏铜,浪费了一些时间
- 明确的角色划分和责任分配:有助于增强团队成员间的协同工作。每个成员都有明确的职责和期望,这有助于提高效率和责任感
- 使用多种工具(如金山文档、微信群、腾讯会议等)来辅助开发:使用多种工具(如金山文档、微信群、腾讯会议等)来支持团队沟通和任务管理。这些工具帮助团队保持同步并快速响应项目需求变化。
- 多沟通有助于减少无用功:通过定期的Scrum会议和全天候的线下开发会议,增加了面对面的交流,有助于解决复杂或紧急的项目问题。
- 将项目分为多个阶段,每个阶段有具体的目标和截止时间:这种分阶段的方法有助于团队集中精力完成当前最重要的任务,同时也为后续阶段留出缓冲时间。
- 在技术选择和系统架构方面,团队采用了前后端分离的开发模式,并利用GitHub和Docker等现代软件工程工具来支持持续集成和部署。不得不说,CI/CD真的是个好玩意。
相关推荐
Oak3D Demo
Oak3D本身也是一套很傻瓜的图形引擎,它本身可以自动完成大量具体的图形渲染的细节工作,开发者只需要编写上层逻辑即可。所以这两点应该是非常贴切的,希望大家能够喜欢这套范例。
OpenAI DevDay新工具实操指南:AgentKit、Sora-2与gpt-realtime-mini工程落地解析
大模型应用已从单点调用迈入系统化工程阶段,核心挑战在于调试不可见、依赖难统一、演进不自动。OpenAI DevDay发布的AgentKit、Sora-2和gpt-realtime-mini并非简单功能叠加,而是围绕可追溯性(provenance_hash)、状态机驱动(state-driven workflow)和物理约束建模(physics_constraints)三大技术原语重构AI基建。其价值体现在降低语音流延迟至290ms、支持视频生成中重力/粘度等物理参数控制、以及通过MCP协议实现跨平台App无
DALL·E 3语义理解革命:从提示词工程到视觉协作范式
AI图像生成已超越‘文本到图像’的简单映射,进入以语义理解为核心的视觉生成新阶段。其底层逻辑是通过跨模态对齐、指代消解与人类反馈强化学习(RLHF),实现对自然语言意图的深度解析与上下文补全。这种技术价值不仅提升生成准确性与鲁棒性,更重塑创意工作流——设计师不再调试参数,而是以自然语言与模型协同迭代。典型应用场景涵盖广告视觉化、教育插图生成、UI/UX快速原型及工业设计草图等需‘抽象→具象’转化的领域。DALL·E 3代表的不是工具升级,而是视觉语言交互范式的根本跃迁。
mcs:使用 jcr 和 Oak 的 mcs 原型
微信 使用 jcr 和 Oak 的 mcs 原型
开源项目-oakmound-oak.zip
开源项目-oakmound-oak.zip,Oak: A game engine built on exp/shiny


