2024秋软件工程iman现场编程作业
作业所属课程 | 班级的链接 |
---|---|
作业要求 | 2024秋软件工程现场编程作业 - 作业 - 软件工程2024 - 班级博客 - 博客园 |
作业目标 | 现场极限编程,制作一个个人记账本程序。 |
团队名称 | iman |
团队成员 | 102202146 - 蓝敏龙, 102201225 - 陈碧煌, 102202105 - 王梓铭, 102202124 - 阿依娜孜, 102202135 - 施宇翔, 102202134 - 承宇豪, 102202117 - 杨邑豪, 102202122 - 张诚坤, 102201506 - 刘宇杰, 102201137 - 郭剑敏 |
一、职责分工
PM-产品经理
102202146 - 蓝敏龙 | 进行任务拆分,统筹组员分工。整体功能设计。 |
---|
前端设计小组
102201225 - 陈碧煌 | 负责总体界面规划,账单收支统计功能设计 |
---|---|
102202105 - 王梓铭 | 负责前端界面绘制和美工设计 |
102202124 - 阿依娜孜 | 负责基础记账功能设计 |
102201506 - 刘宇杰 | 负责用户个人界面设计 |
102201137 - 郭剑敏 | 负责调试测试前端功能 |
后端开发与测试小组
102202135 - 施宇翔 | 负责前后端接口调用与数据处理,账单收支统计与GitHub仓库创建与管理 |
---|---|
102202134 - 承宇豪 | 编写账单添加和记录模块和调试账单添加模块 |
102202117 - 杨邑豪 | 编写账单查询模块,调试账单查询功能 |
102202122 - 张诚坤 | 调试记账整体功能,测试功能完备性 |
二、程序运行环境
前端:VUE2
后端:Python3.9 Django3.1.5
三、运行截图与视频


四、关键代码展示
读取和保存每条账单记录
# 初始化 JSON 文件
def initialize_json_file():
if not os.path.exists(JSON_FILE_PATH):
with open(JSON_FILE_PATH, mode='w', encoding='utf-8') as file:
json.dump([], file) # 创建一个空的 JSON 数组
# 读取所有记录
def read_records():
initialize_json_file() # 确保文件存在
with open(JSON_FILE_PATH, mode='r', encoding='utf-8') as file:
records = json.load(file) # 从 JSON 文件加载记录
return records
# 保存新记录
def save_record(year, month, day, amount, category, remark):
initialize_json_file() # 确保文件存在
records = read_records() # 读取现有记录以获取最新的 ID
record_id = len(records) # 新记录的 ID 为现有记录的数量
new_record = {
"id": record_id,
"year": year,
"month": month,
"day": day,
"amount": amount,
"category": category,
"remark": remark
}
records.append(new_record) # 将新记录添加到现有记录中
# 将更新后的记录写回 JSON 文件
with open(JSON_FILE_PATH, mode='w', encoding='utf-8') as file:
json.dump(records, file, ensure_ascii=False, indent=4) # 写入所有记录
# 写入所有记录(用于更新)
def write_all_records(records):
with open(JSON_FILE_PATH, mode='w', encoding='utf-8') as file:
json.dump(records, file, ensure_ascii=False, indent=4) # 写入所有记录
账单展示界面:
使用 v-for
循环遍历 dataList
,动态生成每一条记录的展示。
<view class="item" v-for="(item, index) in dataList" :key="index">
<view class="count">
<view class="sa-flex-x">
<view>日期: {{item.create_time}}</view>
<view class="week">({{item.create_time | weekFormat}})</view>
</view>
</view>
<view class="sa-flex-x space content">
<view class="sa-flex-x center cover" :class="item.type==1 ? 'income':'expend'">
<image :src="'../../static/icon/'+item.icon+'.png'" mode="aspectFill"></image>
</view>
<view class="info">
<view class="name">{{item.content}}</view>
<view class="desc" v-if="item.remarks">{{item.remarks}}</view>
</view>
<view class="sa-flex-x money" :class="item.type==1 ? 'income':'expend'">
<text class="symbol">{{item.type==1 ? '+':'-'}}</text>
<text>{{item.money}}</text>
</view>
</view>
</view>
weekFormat
过滤器用于格式化日期以显示星期几。
filters: {
weekFormat(val) {
return format.getWeekDay(val);
}
},
getData()
从本地存储中获取记录并解析。total()
方法计算总支出、总收入、笔数和结余,并更新状态。
getData() {
uni.getStorage({
key: 'sa_storage_bill',
success: res => {
const data = JSON.parse(res.data);
this.dataList = JSON.parse(res.data);
this.total();
}
})
},
total() {
const data = this.dataList;
const group = format.arrayGroup(data, 'type');
if(Object.keys(group).length > 0) {
const expendMoney = group[0].reduce((sum, item) => sum + Number(item.money), 0);
this.totalExpend = expendMoney.toFixed(2);
this.sumExpend = group[0].length;
}
if(Object.keys(group).length > 1) {
const incomeMoney = group[1].reduce((sum, item) => sum + Number(item.money), 0);
this.totalIncome = incomeMoney.toFixed(2);
this.sumIncome = group[1].length;
}
this.sumCount = data.length;
this.totalbalance = (this.totalIncome - this.totalExpend).toFixed(2);
}
五、亮点功能
用图表展示账单收支统计
将按天聚合的金额按日期排序,使用 Matplotlib 绘制折线图,设置图表的大小、标题、X 轴和 Y 轴标签,并添加网格。构造图像的绝对 URL,并将其作为响应返回给客户端。
@api_view(['POST'])
def get_records_image(request):
records = read_records()
# 获取查询参数
year = request.data.get('year')
month = request.data.get('month')
category = request.data.get('category')
# 根据查询条件过滤记录
if year is not None:
records = [record for record in records if record['year'] == int(year)]
if month is not None:
records = [record for record in records if record['month'] == int(month)]
if category is not None:
records = [record for record in records if record['category'] == category]
# 如果没有记录,返回错误信息
if not records:
return Response({"error": "No records found"}, status=404)
# 准备数据用于聚合
daily_totals = defaultdict(int)
for record in records:
day = record['day']
amount = record['amount']
daily_totals[day] += amount
# 将聚合后的数据分开为天和对应的总金额
days = sorted(daily_totals.keys())
amounts = [daily_totals[day] for day in days]
# 绘制折线图
plt.figure(figsize=(10, 5))
plt.plot(days, amounts, marker='o')
plt.title(f'Records for {year}-{month}')
plt.xlabel('Days')
plt.ylabel('Total Amount')
plt.grid()
# 保存图像到本地
image_path = os.path.join(settings.MEDIA_ROOT, 'charts', f'records_{year}_{month}_{category}.png')
plt.savefig(image_path)
plt.close()
# 返回图像的 URL
image_url = f"{request.build_absolute_uri(settings.MEDIA_URL)}charts/records_{year}_{month}_{category}.png"
return Response({"image_url": image_url})
六、在编码、争论、复审等活动中花费时间较长,给你们较大收获的事件:
-
学习GitHub的协作编程
在项目的初期阶段,我们对GitHub的协作操作还不够熟悉,出现了一些操作失误。一次因为版本合并问题,不得不重建仓库,已经上传的代码需要重新提交。虽然这个过程有些繁琐,但我们对GitHub协作有了更深的理解,认识到它在版本管理和团队协作中的巨大优势。(栋:不要使用QQ传文件!)
-
前后端接口的联调测试
在开发过程中,前后端接口联调耗费了大量时间。尤其是在数据格式和接口调用方面,前后端经常出现对不上的情况。这里非常感谢我们的后端小伙伴,他们的“可以接可以接”让人心里暖暖嘟。
-
功能需求与技术实现的取舍
因为三小时时间非常有限,而团队大家想法非常多想实现的功能也不少,但是考虑到整体框架的实现,我们还是做了很多取舍,最重要的是实现整体快速迭代的开发。
-
使用Matplotlib展示数据
在设计账单收支统计的图表功能时,我们第一次尝试用Matplotlib来展示数据。这个地方我们碰了非常多壁,大家一起学习,最后搞定了图表展示这一问题。
七、团队成员感悟:
-
蓝敏龙 (产品经理)
作为产品经理,我的任务是统筹全局,确保每个成员都能在各自的岗位上发挥最大效能。在任务拆分的过程中,我意识到良好的沟通是成功的关键。这个极限编程对我们一个小团队来说非常具有挑战性,但是最后还是成功的做出了一个相对成熟的man记账!
-
陈碧煌 (前端设计)
这次编程给我最大的挑战其实是协作编程,一开始我觉得记账本很简单,还乐呵呵的跟队友说man记账可以用到我们的man游中,但是协作编程却给我带来了很大的挑战。在最开始pull和merge产生了一些失误,幸亏有队友的帮助,我也能在完成自己的工作部分的同时,也协作到团队的整体工作中,整体来说是收获满满的一次体验。
-
王梓铭 (前端设计)
在这次团队编程中我学到了很多新东西,虽然写的代码量不多,但是我通过与队友的讨论以及实际编程学到了很多前端的注意事项,在和团队的讨论中深刻地体会到了和大家讨论的时候效率确实会变高,希望以后还有更多的机会参加团队编程。
-
阿依娜孜 (前端设计)
负责基础记账功能设计的过程中,我深入学习了数据结构和逻辑实现。实现一个高效的记账功能不仅需要考虑用户的输入体验,还要考虑数据的准确性和存储效率。通过这个项目,我对前端技术有了更深入的认识,特别是在VUE框架下如何实现数据绑定和事件处理,让我的编程能力得到了提升。
-
施宇翔 (后端开发)
作为后端开发人员,我主要负责前后端接口的调用与数据处理。在这个项目中,我学会了如何设计高效的API,确保前端能够顺利获取数据。遇到的挑战让我对Django框架有了更深入的理解,也让我认识到良好文档的重要性。通过与前端团队的不断沟通,我体会到协作编程的乐趣与意义。
-
承宇豪 (后端开发)
在这次项目中,我负责编写账单添加和记录模块的工作。在实现账单添加功能时,我遇到了一些挑战,通过查阅文档和进行多次调试,我学会了使用Django中的序列化器来验证和处理数据。这不仅提高了我的编程技能,也让我更加重视代码的规范性和可维护性。与前端团队的合作让我认识到,良好的接口设计是保证数据顺利流动的关键,未来我会继续在这方面努力。
-
杨邑豪 (后端开发)
在编写账单查询模块的过程中,我负责实现查询功能并进行调试。这个阶段的工作让我意识到后端系统的复杂性,以及如何根据不同的条件快速过滤和返回数据。为了实现高效的查询功能,我深入学习了Django ORM的使用,并尝试优化查询逻辑。在测试阶段,我和前端团队密切合作,确保接口能够准确地响应请求。
-
刘宇杰 (前端设计)
在用户个人界面设计中,我提升了用户体验的思维,学会了如何更好地展现信息。
-
郭剑敏 (前端设计)
调试前端功能的过程让我认识到测试的重要性,发现问题并解决是提升项目质量的关键。
-
张诚坤 (后端开发)
调试整体功能让我意识到团队协作的重要性,沟通能有效提高工作效率和项目质量。
八、PSP:
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 5 | 5 |
Development | 开发 | 150 | 250 |
Analysis | 需求分析 | 10 | 20 |
Design | 具体设计 | 15 | 30 |
Coding | 具体编码 | 120 | 180 |
Test | 测试 | 20 | 20 |
合计 | 180 | 255 |
九、GitHub 仓库地址和 commit 记录:
仓库地址:man记账
十、组员贡献评分
组员 | 分数 | 组员 | 分数 |
---|---|---|---|
102202146 - 蓝敏龙 | 95 | 102202134 - 承宇豪 | 95 |
102201225 - 陈碧煌 | 96 | 102202117 - 杨邑豪 | 95 |
102202105 - 王梓铭 | 94 | 102202122 - 张诚坤 | 83 |
102202124 - 阿依娜孜 | 97 | 102201506 - 刘宇杰 | 89 |
102202135 - 施宇翔 | 98 | 102201137 - 郭剑敏 | 86 |