当前位置: 首页 > news >正文

第三天项目

苍穹外卖项目 - 第3天冲刺日志

日期:2025-11-28
冲刺周期:第3天/共7天
参与人员:李靖华 温尚熙 谢斯越 郑哲磊


一、站立会议照片

440be925a0c6a3541faec4ff732b18f5


二、会议内容记录

郑哲磊(后端负责人)

昨天已完成的工作

  • ✅ [WI-017] 完成员工管理CRUD接口
  • ✅ [WI-018] 实现JWT token认证机制
  • ✅ [WI-019] 完成菜品分类管理接口
  • ✅ [WI-020] 编写接口文档(Swagger)

今天计划完成的工作

  • [WI-033] 完成菜品管理CRUD接口
  • [WI-034] 实现图片上传功能(阿里云OSS)
  • [WI-035] 完成套餐管理接口
  • [WI-036] 实现微信用户登录接口

工作中遇到的困难

  • 阿里云OSS配置较复杂,需要申请AccessKey
  • 菜品口味数据结构设计需要优化

谢斯越(前端负责人)

昨天已完成的工作

  • ✅ [WI-021] 完成主页布局和导航菜单
  • ✅ [WI-022] 实现员工管理页面
  • ✅ [WI-023] 完成分类管理页面
  • ✅ [WI-024] 实现token自动刷新机制

今天计划完成的工作

  • [WI-037] 完成菜品管理页面
  • [WI-038] 实现图片上传组件
  • [WI-039] 完成套餐管理页面
  • [WI-040] 优化页面加载性能

工作中遇到的困难

  • 图片上传组件需要支持裁剪和压缩功能
  • 菜品口味选择器的交互设计需要优化

温尚熙(小程序开发)

昨天已完成的工作

  • ✅ [WI-025] 实现菜品列表页面
  • ✅ [WI-026] 完成菜品详情页面
  • ✅ [WI-027] 实现购物车功能
  • ✅ [WI-028] 完成用户授权登录

今天计划完成的工作

  • [WI-041] 完成地址管理功能
  • [WI-042] 实现订单确认页面
  • [WI-043] 完成订单提交功能
  • [WI-044] 实现订单列表页面

工作中遇到的困难

  • 地址选择器需要集成腾讯地图API
  • 订单金额计算逻辑较复杂,需要仔细测试

李靖华(测试与文档)

昨天已完成的工作

  • ✅ [WI-029] 执行登录接口测试
  • ✅ [WI-030] 编写员工管理测试用例
  • ✅ [WI-031] 更新API文档
  • ✅ [WI-032] 进行代码质量检查

今天计划完成的工作

  • [WI-045] 执行菜品管理接口测试
  • [WI-046] 编写集成测试用例
  • [WI-047] 进行性能测试
  • [WI-048] 更新用户手册

工作中遇到的困难

  • 性能测试工具JMeter配置需要学习
  • 部分接口响应时间超过预期,需要优化

三、燃尽图

剩余工作量(小时)
120 |●|  \
100 |    ●|      \\80 |          \|            ●60 |              \\|                  \40 |                    \|                      \20 |                        \|                          ●0 |____________________________1   2   3   4   5   6   7  (天数)图例:
● —— 实际进度(实线)
- - - 理想进度(虚线)

燃尽图说明

  • 当前剩余工作量:68小时(完成52小时)
  • 理想剩余工作量:77小时
  • 进度状态:✅ 快于预期进度
  • 燃尽速度:28小时/天

项目收敛分析

  • 第3天进入快速开发阶段,团队协作效率提升
  • 实际进度持续快于理想进度,项目风险降低
  • 核心功能模块开发进展顺利,预计可提前完成基础功能

四、代码/文档签入记录

郑哲磊 - 菜品管理与图片上传模块

  • 模块名称:sky-server 菜品管理模块
  • 提交内容
    • 完成菜品增删改查接口
    • 实现阿里云OSS图片上传
    • 添加菜品口味关联表设计

代码示例

// DishController.java - 菜品管理控制器
@RestController
@RequestMapping("/admin/dish")
@Slf4j
@Api(tags = "菜品相关接口")
public class DishController {@Autowiredprivate DishService dishService;@PostMapping@ApiOperation("新增菜品")public Result save(@RequestBody DishDTO dishDTO) {log.info("新增菜品:{}", dishDTO);dishService.saveWithFlavor(dishDTO);return Result.success();}@GetMapping("/page")@ApiOperation("菜品分页查询")public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {log.info("菜品分页查询:{}", dishPageQueryDTO);PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);return Result.success(pageResult);}@DeleteMapping@ApiOperation("批量删除菜品")public Result delete(@RequestParam List<Long> ids) {log.info("批量删除菜品:{}", ids);dishService.deleteBatch(ids);return Result.success();}
}
// AliOssUtil.java - 阿里云OSS工具类
@Component
@Slf4j
public class AliOssUtil {@Autowiredprivate AliOssProperties aliOssProperties;public String upload(byte[] bytes, String objectName) {// 创建OSSClient实例OSS ossClient = new OSSClientBuilder().build(aliOssProperties.getEndpoint(),aliOssProperties.getAccessKeyId(),aliOssProperties.getAccessKeySecret());try {// 上传文件ossClient.putObject(aliOssProperties.getBucketName(),objectName,new ByteArrayInputStream(bytes));// 返回文件访问路径String url = "https://" + aliOssProperties.getBucketName() + "." + aliOssProperties.getEndpoint() + "/" + objectName;return url;} finally {ossClient.shutdown();}}
}
// DishService.java - 菜品业务逻辑
@Service
public class DishServiceImpl implements DishService {@Autowiredprivate DishMapper dishMapper;@Autowiredprivate DishFlavorMapper dishFlavorMapper;@Transactionalpublic void saveWithFlavor(DishDTO dishDTO) {Dish dish = new Dish();BeanUtils.copyProperties(dishDTO, dish);// 向菜品表插入1条数据dishMapper.insert(dish);// 获取insert语句生成的主键值Long dishId = dish.getId();// 向口味表插入n条数据List<DishFlavor> flavors = dishDTO.getFlavors();if (flavors != null && flavors.size() > 0) {flavors.forEach(flavor -> {flavor.setDishId(dishId);});dishFlavorMapper.insertBatch(flavors);}}
}

谢斯越- 管理端菜品管理页面模块

  • 模块名称:sky-admin 菜品管理页面
  • 提交内容
    • 完成菜品管理页面CRUD功能
    • 实现图片上传和预览组件
    • 添加菜品口味动态表单

代码示例

<!-- DishEdit.vue - 菜品编辑页面 -->
<template><el-form :model="dishForm" :rules="rules" ref="formRef" label-width="100px"><el-form-item label="菜品名称" prop="name"><el-input v-model="dishForm.name" placeholder="请输入菜品名称" /></el-form-item><el-form-item label="菜品分类" prop="categoryId"><el-select v-model="dishForm.categoryId" placeholder="请选择分类"><el-option v-for="item in categoryList" :key="item.id" :label="item.name" :value="item.id" /></el-select></el-form-item><el-form-item label="菜品价格" prop="price"><el-input-number v-model="dishForm.price" :precision="2" :min="0" /></el-form-item><el-form-item label="菜品图片" prop="image"><el-uploadclass="avatar-uploader":action="uploadUrl":show-file-list="false":on-success="handleUploadSuccess":before-upload="beforeUpload"><img v-if="dishForm.image" :src="dishForm.image" class="avatar"><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item><el-form-item label="菜品口味"><div v-for="(flavor, index) in dishForm.flavors" :key="index" class="flavor-item"><el-input v-model="flavor.name" placeholder="口味名称" style="width: 150px" /><el-input v-model="flavor.value" placeholder="口味选项(逗号分隔)" style="width: 300px" /><el-button type="danger" @click="removeFlavor(index)">删除</el-button></div><el-button type="primary" @click="addFlavor">添加口味</el-button></el-form-item><el-form-item><el-button type="primary" @click="handleSubmit">提交</el-button><el-button @click="handleCancel">取消</el-button></el-form-item></el-form>
</template><script setup>
import { ref, reactive } from 'vue'
import { saveDishApi, updateDishApi } from '@/api/dish'
import { ElMessage } from 'element-plus'const dishForm = reactive({name: '',categoryId: null,price: 0,image: '',description: '',status: 1,flavors: []
})const uploadUrl = import.meta.env.VITE_API_URL + '/admin/common/upload'const handleUploadSuccess = (response) => {dishForm.image = response.dataElMessage.success('图片上传成功')
}const beforeUpload = (file) => {const isImage = file.type.startsWith('image/')const isLt2M = file.size / 1024 / 1024 < 2if (!isImage) {ElMessage.error('只能上传图片文件!')return false}if (!isLt2M) {ElMessage.error('图片大小不能超过 2MB!')return false}return true
}const addFlavor = () => {dishForm.flavors.push({ name: '', value: '' })
}const removeFlavor = (index) => {dishForm.flavors.splice(index, 1)
}const handleSubmit = async () => {if (dishForm.id) {await updateDishApi(dishForm)ElMessage.success('修改成功')} else {await saveDishApi(dishForm)ElMessage.success('新增成功')}
}
</script>

温尚熙 - 小程序地址管理模块

  • 模块名称:sky-user 地址管理模块
  • 提交内容
    • 完成地址管理页面
    • 实现地址选择器
    • 添加默认地址设置功能

代码示例

// pages/address/address.js - 地址管理页面
Page({data: {addressList: []},onLoad() {this.getAddressList()},// 获取地址列表getAddressList() {wx.request({url: getApp().globalData.baseUrl + '/user/addressBook/list',method: 'GET',header: {'token': wx.getStorageSync('token')},success: (res) => {this.setData({addressList: res.data.data})}})},// 新增地址onAddAddress() {wx.navigateTo({url: '/pages/address/edit/edit'})},// 编辑地址onEditAddress(e) {const id = e.currentTarget.dataset.idwx.navigateTo({url: `/pages/address/edit/edit?id=${id}`})},// 删除地址onDeleteAddress(e) {const id = e.currentTarget.dataset.idwx.showModal({title: '提示',content: '确定要删除该地址吗?',success: (res) => {if (res.confirm) {wx.request({url: getApp().globalData.baseUrl + '/user/addressBook',method: 'DELETE',data: { id },header: {'token': wx.getStorageSync('token')},success: () => {wx.showToast({ title: '删除成功', icon: 'success' })this.getAddressList()}})}}})},// 设置默认地址onSetDefault(e) {const id = e.currentTarget.dataset.idwx.request({url: getApp().globalData.baseUrl + '/user/addressBook/default',method: 'PUT',data: { id },header: {'token': wx.getStorageSync('token')},success: () => {wx.showToast({ title: '设置成功', icon: 'success' })this.getAddressList()}})}
})
<!-- pages/address/address.wxml - 地址列表页面 -->
<view class="container"><view class="address-list"><view wx:for="{{addressList}}" wx:key="id" class="address-item"><view class="address-info"><view class="address-header"><text class="consignee">{{item.consignee}}</text><text class="phone">{{item.phone}}</text><view wx:if="{{item.isDefault === 1}}" class="default-tag">默认</view></view><view class="address-detail">{{item.provinceName}}{{item.cityName}}{{item.districtName}}{{item.detail}}</view></view><view class="address-actions"><button class="btn-edit" data-id="{{item.id}}" bindtap="onEditAddress">编辑</button><button class="btn-delete" data-id="{{item.id}}" bindtap="onDeleteAddress">删除</button><button wx:if="{{item.isDefault !== 1}}" class="btn-default" data-id="{{item.id}}" bindtap="onSetDefault">设为默认</button></view></view></view><view class="add-btn" bindtap="onAddAddress"><text>+ 新增地址</text></view>
</view>
// pages/address/edit/edit.js - 地址编辑页面
Page({data: {addressForm: {consignee: '',phone: '',provinceName: '',cityName: '',districtName: '',detail: '',sex: '1',isDefault: 0}},// 选择地址onChooseLocation() {wx.chooseLocation({success: (res) => {this.setData({'addressForm.detail': res.address + res.name})}})},// 保存地址onSave() {const { addressForm } = this.dataconst url = addressForm.id ? '/user/addressBook' : '/user/addressBook'const method = addressForm.id ? 'PUT' : 'POST'wx.request({url: getApp().globalData.baseUrl + url,method: method,data: addressForm,header: {'token': wx.getStorageSync('token')},success: () => {wx.showToast({ title: '保存成功', icon: 'success' })setTimeout(() => {wx.navigateBack()}, 1500)}})}
})

李靖华 - 菜品接口测试与性能测试模块

  • 模块名称:菜品接口测试与性能测试
  • 提交内容
    • 完成菜品管理接口测试
    • 编写性能测试脚本
    • 生成测试报告v3.0

测试代码示例

// dish.test.js - 菜品管理接口测试
const request = require('supertest');
const app = require('../app');describe('菜品管理接口测试', () => {let token = '';let dishId = '';beforeAll(async () => {// 登录获取tokenconst response = await request(app).post('/admin/employee/login').send({ username: 'admin', password: '123456' });token = response.body.data.token;});// 测试新增菜品test('POST /admin/dish - 新增菜品', async () => {const response = await request(app).post('/admin/dish').set('token', token).send({name: '测试菜品',categoryId: 1,price: 38.00,image: 'http://example.com/dish.jpg',description: '这是一道测试菜品',status: 1,flavors: [{ name: '辣度', value: '不辣,微辣,中辣,特辣' },{ name: '温度', value: '热,冷' }]});expect(response.status).toBe(200);expect(response.body.code).toBe(1);dishId = response.body.data;});// 测试菜品分页查询test('GET /admin/dish/page - 菜品分页查询', async () => {const response = await request(app).get('/admin/dish/page').set('token', token).query({ page: 1, pageSize: 10, name: '测试' });expect(response.status).toBe(200);expect(response.body.code).toBe(1);expect(response.body.data).toHaveProperty('records');expect(response.body.data.records.length).toBeGreaterThan(0);});// 测试修改菜品test('PUT /admin/dish - 修改菜品', async () => {const response = await request(app).put('/admin/dish').set('token', token).send({id: dishId,name: '测试菜品(已修改)',price: 48.00});expect(response.status).toBe(200);expect(response.body.code).toBe(1);});// 测试删除菜品test('DELETE /admin/dish - 删除菜品', async () => {const response = await request(app).delete('/admin/dish').set('token', token).query({ ids: dishId });expect(response.status).toBe(200);expect(response.body.code).toBe(1);});
});

JMeter性能测试脚本

<!-- dish-performance-test.jmx - JMeter测试计划 -->
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2"><hashTree><TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="菜品接口性能测试"><elementProp name="TestPlan.user_defined_variables" elementType="Arguments"><collectionProp name="Arguments.arguments"><elementProp name="BASE_URL" elementType="Argument"><stringProp name="Argument.name">BASE_URL</stringProp><stringProp name="Argument.value">http://localhost:8080</stringProp></elementProp><elementProp name="TOKEN" elementType="Argument"><stringProp name="Argument.name">TOKEN</stringProp><stringProp name="Argument.value">${__P(token)}</stringProp></elementProp></collectionProp></elementProp></TestPlan><hashTree><ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="菜品查询并发测试"><intProp name="ThreadGroup.num_threads">100</intProp><intProp name="ThreadGroup.ramp_time">10</intProp><longProp name="ThreadGroup.duration">60</longProp></ThreadGroup><hashTree><HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="菜品分页查询"><stringProp name="HTTPSampler.domain">${BASE_URL}</stringProp><stringProp name="HTTPSampler.path">/admin/dish/page</stringProp><stringProp name="HTTPSampler.method">GET</stringProp><elementProp name="HTTPsampler.Arguments" elementType="Arguments"><collectionProp name="Arguments.arguments"><elementProp name="page" elementType="HTTPArgument"><stringProp name="Argument.value">1</stringProp></elementProp><elementProp name="pageSize" elementType="HTTPArgument"><stringProp name="Argument.value">10</stringProp></elementProp></collectionProp></elementProp><elementProp name="HTTPSampler.header_manager" elementType="HeaderManager"><collectionProp name="HeaderManager.headers"><elementProp name="token" elementType="Header"><stringProp name="Header.value">${TOKEN}</stringProp></elementProp></collectionProp></elementProp></HTTPSamplerProxy></hashTree></hashTree></hashTree>
</jmeterTestPlan>

测试报告示例

## 菜品管理接口性能测试报告### 测试环境
- 服务器:本地开发环境
- 数据库:MySQL 8.0
- 并发用户数:100
- 测试时长:60秒### 测试结果
| 接口 | 平均响应时间 | 最大响应时间 | TPS | 错误率 |
|------|------------|------------|-----|--------|
| 菜品分页查询 | 120ms | 350ms | 450 | 0% |
| 新增菜品 | 180ms | 420ms | 280 | 0% |
| 修改菜品 | 150ms | 380ms | 320 | 0% |
| 删除菜品 | 100ms | 280ms | 380 | 0% |### 结论
所有接口性能指标均符合要求,系统在100并发下运行稳定。
http://www.jsqmd.com/news/59863/

相关文章:

  • 第7篇Scrum冲刺博客
  • 2025年中国温度传感器主流品牌五大推荐:看哪家品牌适合实验
  • 递归算法设计与实现 - Invinc
  • 第二天项目
  • 一些md5绕过总结(长期补充)
  • Pytorch基础学习和实战,基于b站小土堆视频笔记 - 教程
  • 2025年中国仿石砖十大龙头厂家推荐:看哪家产品质量好?
  • 炫彩活体检测:金融科技的“生命感知”安全锁
  • 实用指南:Qt-VLC: 一个集成VLC的开源跨平台媒体播放库
  • Scrum3 冲刺博客
  • 团队作业四——项目冲刺
  • excel选中整列,设置单元格自动换行,为什么粘贴内容后还不换行,单独设置该单元格自动换行就可以,为什么整列设置没效果
  • Scrum1 冲刺博客
  • 实用指南:GitHub 全方位指南(续):实战进阶与生态拓展​
  • Scrum2 冲刺博客
  • Day6 Scrum 冲刺日志
  • Day3 Scrum 冲刺日志
  • Day2 Scrum 冲刺日志
  • mysqld_multi方式,多启动mysql
  • 2025年聚酰亚胺棒定制生产厂家排名,看哪家技术水平高?
  • 2025年五大行星减速机厂家推荐排行榜,专业供应商与大型厂家
  • 第6篇:Alpha阶段Day6冲刺日志
  • 第7篇:Alpha阶段Day7冲刺日志
  • 2025年口碑好的停经架配件批发厂家推荐,看看哪家价格实惠
  • 2025年诚信的便携式粒子计数器销售厂家排行榜,尘埃粒子计数器/0.1um尘埃粒子计数器/28.3L尘埃粒子计数器粒子计数器销售厂家电话
  • 第4篇:Alpha阶段Day4冲刺日志
  • LangChain 提示模板注入漏洞详解:通过属性访问实现攻击
  • 第2篇:Alpha阶段Day2冲刺日志
  • 第3篇:Alpha阶段Day3冲刺日志
  • 短连接和长连接